diff --git a/README.md b/README.md index a1f478e39a..dc36145337 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,9 @@ Clone the repository including submodules Build and run - Using Visual Studio 2017, Rider or Visual Studio Code (configurations are included) -- From command line using `dotnet run --project osu.Desktop` +- From command line using `dotnet run --project osu.Desktop`. When building for non-development purposes, add `-c Release` to gain higher performance. + +Note: If you run from command line under linux, you will need to prefix the output folder to your `LD_LIBRARY_PATH`. See `.vscode/launch.json` for an example If you run into issues building you may need to restore nuget packages (commonly via `dotnet restore`). Visual Studio Code users must run `Restore` task from debug tab before attempt to build. diff --git a/appveyor_deploy.yml b/appveyor_deploy.yml index 6d8d95e773..355f0603be 100644 --- a/appveyor_deploy.yml +++ b/appveyor_deploy.yml @@ -10,8 +10,8 @@ before_build: - cmd: nuget restore -verbosity quiet build_script: - ps: iex ((New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/appveyor/secure-file/master/install.ps1')) - - appveyor DownloadFile https://puu.sh/A6g5K/4d08705438.enc # signing certificate - - cmd: appveyor-tools\secure-file -decrypt 4d08705438.enc -secret %decode_secret% -out %HOMEPATH%\deanherbert.pfx + - appveyor DownloadFile https://www.dropbox.com/s/f7hv069mr5tqi2j/deanherbert.pfx.enc?dl=1 # signing certificate + - cmd: appveyor-tools\secure-file -decrypt deanherbert.pfx.enc -secret %decode_secret% -out %HOMEPATH%\deanherbert.pfx - appveyor DownloadFile https://puu.sh/A6g75/fdc6f19b04.enc # deploy configuration - cd osu-deploy - nuget restore -verbosity quiet diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index 1129969694..96857d6b4f 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -27,9 +27,6 @@ namespace osu.Desktop.Overlays private NotificationOverlay notificationOverlay; private GameHost host; - public override bool HandleKeyboardInput => false; - public override bool HandleMouseInput => false; - [BackgroundDependencyLoader] private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config, GameHost host) { diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 180abd7fec..2be352165e 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -27,9 +27,9 @@ - - - + + + diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs index 34e07170bd..5e68acde94 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; @@ -38,7 +37,7 @@ namespace osu.Game.Rulesets.Catch.Tests beatmap.HitObjects.Add(new JuiceStream { X = 0.5f - width / 2, - ControlPoints = new List + ControlPoints = new[] { Vector2.Zero, new Vector2(width * CatchPlayfield.BASE_WIDTH, 0) diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj index 51343d9e91..326791f506 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -2,9 +2,10 @@ - - + + + WinExe diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index ab0afb08d7..c7ea29f8c0 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -74,42 +74,42 @@ namespace osu.Game.Rulesets.Catch.Beatmaps private void initialiseHyperDash(List objects) { - // todo: add difficulty adjust. - double halfCatcherWidth = CatcherArea.CATCHER_SIZE * (objects.FirstOrDefault()?.Scale ?? 1) / CatchPlayfield.BASE_WIDTH / 2; + List objectWithDroplets = new List(); + foreach (var currentObject in objects) + { + if (currentObject is Fruit) + objectWithDroplets.Add(currentObject); + if (currentObject is JuiceStream) + foreach (var currentJuiceElement in currentObject.NestedHitObjects) + if (!(currentJuiceElement is TinyDroplet)) + objectWithDroplets.Add((CatchHitObject)currentJuiceElement); + } + + objectWithDroplets.Sort((h1, h2) => h1.StartTime.CompareTo(h2.StartTime)); + + double halfCatcherWidth = CatcherArea.GetCatcherSize(Beatmap.BeatmapInfo.BaseDifficulty) / 2; int lastDirection = 0; double lastExcess = halfCatcherWidth; - int objCount = objects.Count; - - for (int i = 0; i < objCount - 1; i++) + for (int i = 0; i < objectWithDroplets.Count - 1; i++) { - CatchHitObject currentObject = objects[i]; - - // not needed? - // if (currentObject is TinyDroplet) continue; - - CatchHitObject nextObject = objects[i + 1]; - - // while (nextObject is TinyDroplet) - // { - // if (++i == objCount - 1) break; - // nextObject = objects[i + 1]; - // } + CatchHitObject currentObject = objectWithDroplets[i]; + CatchHitObject nextObject = objectWithDroplets[i + 1]; int thisDirection = nextObject.X > currentObject.X ? 1 : -1; - double timeToNext = nextObject.StartTime - ((currentObject as IHasEndTime)?.EndTime ?? currentObject.StartTime) - 4; + double timeToNext = nextObject.StartTime - currentObject.StartTime; double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth); - - if (timeToNext * CatcherArea.Catcher.BASE_SPEED < distanceToNext) + float distanceToHyper = (float)(timeToNext * CatcherArea.Catcher.BASE_SPEED - distanceToNext); + if (distanceToHyper < 0) { currentObject.HyperDashTarget = nextObject; lastExcess = halfCatcherWidth; } else { - //currentObject.DistanceToHyperDash = timeToNext - distanceToNext; - lastExcess = MathHelper.Clamp(timeToNext - distanceToNext, 0, halfCatcherWidth); + currentObject.DistanceToHyperDash = distanceToHyper; + lastExcess = MathHelper.Clamp(distanceToHyper, 0, halfCatcherWidth); } lastDirection = thisDirection; diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index 621fc100c2..0c50dae9fb 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.Objects public int IndexInBeatmap { get; set; } - public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(IndexInBeatmap % 4); + public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(ComboIndex % 4); public virtual bool NewCombo { get; set; } @@ -27,7 +27,9 @@ namespace osu.Game.Rulesets.Catch.Objects public int ComboIndex { get; set; } /// - /// The distance for a fruit to to next hyper if it's not a hyper. + /// Difference between the distance to the next object + /// and the distance that would have triggered a hyper dash. + /// A value close to 0 indicates a difficult jump (for difficulty calculation). /// public float DistanceToHyperDash { get; set; } diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index 0344189af5..82e32d24d2 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -146,7 +146,7 @@ namespace osu.Game.Rulesets.Catch.Objects public SliderCurve Curve { get; } = new SliderCurve(); - public List ControlPoints + public Vector2[] ControlPoints { get { return Curve.ControlPoints; } set { Curve.ControlPoints = value; } diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index d49be69856..925e7aaac9 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -5,11 +5,13 @@ using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawable; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI.Scrolling; +using OpenTK; namespace osu.Game.Rulesets.Catch.UI { @@ -17,13 +19,13 @@ namespace osu.Game.Rulesets.Catch.UI { public const float BASE_WIDTH = 512; - protected override Container Content => content; - private readonly Container content; - private readonly CatcherArea catcherArea; + protected override bool UserScrollSpeedAdjustment => false; + + protected override SpeedChangeVisualisationMethod VisualisationMethod => SpeedChangeVisualisationMethod.Constant; + public CatchPlayfield(BeatmapDifficulty difficulty, Func> getVisualRepresentation) - : base(BASE_WIDTH) { Direction.Value = ScrollingDirection.Down; @@ -32,27 +34,29 @@ namespace osu.Game.Rulesets.Catch.UI Anchor = Anchor.TopCentre; Origin = Anchor.TopCentre; - base.Content.Anchor = Anchor.BottomLeft; - base.Content.Origin = Anchor.BottomLeft; + Size = new Vector2(0.86f); // matches stable's vertical offset for catcher plate - base.Content.AddRange(new Drawable[] + InternalChild = new PlayfieldAdjustmentContainer { - explodingFruitContainer = new Container + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - }, - catcherArea = new CatcherArea(difficulty) - { - GetVisualRepresentation = getVisualRepresentation, - ExplodingFruitTarget = explodingFruitContainer, - Anchor = Anchor.BottomLeft, - Origin = Anchor.TopLeft, - }, - content = new Container - { - RelativeSizeAxes = Axes.Both, - }, - }); + explodingFruitContainer = new Container + { + RelativeSizeAxes = Axes.Both, + }, + catcherArea = new CatcherArea(difficulty) + { + GetVisualRepresentation = getVisualRepresentation, + ExplodingFruitTarget = explodingFruitContainer, + Anchor = Anchor.BottomLeft, + Origin = Anchor.TopLeft, + }, + HitObjectContainer + } + }; + + VisibleTimeRange.Value = BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450); } public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.AttemptCatch(obj); diff --git a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs index 1ac052de4d..bd0fec43a1 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs @@ -13,7 +13,6 @@ using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; -using OpenTK; namespace osu.Game.Rulesets.Catch.UI { @@ -32,8 +31,6 @@ namespace osu.Game.Rulesets.Catch.UI public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo); - protected override Vector2 PlayfieldArea => new Vector2(0.86f); // matches stable's vertical offset for catcher plate - protected override DrawableHitObject GetVisualRepresentation(CatchHitObject h) { switch (h) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 4327abb96f..06453ac32d 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Catch.UI { public class CatcherArea : Container { - public const float CATCHER_SIZE = 84; + public const float CATCHER_SIZE = 100; protected readonly Catcher MovableCatcher; @@ -107,6 +107,11 @@ namespace osu.Game.Rulesets.Catch.UI public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj); + public static float GetCatcherSize(BeatmapDifficulty difficulty) + { + return CATCHER_SIZE / CatchPlayfield.BASE_WIDTH * (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5); + } + public class Catcher : Container, IKeyBindingHandler { /// @@ -407,9 +412,7 @@ namespace osu.Game.Rulesets.Catch.UI /// public void Explode() { - var fruit = caughtFruit.ToArray(); - - foreach (var f in fruit) + foreach (var f in caughtFruit.ToArray()) Explode(f); } @@ -422,15 +425,15 @@ namespace osu.Game.Rulesets.Catch.UI fruit.Anchor = Anchor.TopLeft; fruit.Position = caughtFruit.ToSpaceOfOtherDrawable(fruit.DrawPosition, ExplodingFruitTarget); - caughtFruit.Remove(fruit); + if (!caughtFruit.Remove(fruit)) + // we may have already been removed by a previous operation (due to the weird OnLoadComplete scheduling). + // this avoids a crash on potentially attempting to Add a fruit to ExplodingFruitTarget twice. + return; ExplodingFruitTarget.Add(fruit); } - fruit.MoveToY(fruit.Y - 50, 250, Easing.OutSine) - .Then() - .MoveToY(fruit.Y + 50, 500, Easing.InSine); - + fruit.MoveToY(fruit.Y - 50, 250, Easing.OutSine).Then().MoveToY(fruit.Y + 50, 500, Easing.InSine); fruit.MoveToX(fruit.X + originalX * 6, 1000); fruit.FadeOut(750); diff --git a/osu.Game.Rulesets.Catch/UI/PlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Catch/UI/PlayfieldAdjustmentContainer.cs new file mode 100644 index 0000000000..ad0073ff12 --- /dev/null +++ b/osu.Game.Rulesets.Catch/UI/PlayfieldAdjustmentContainer.cs @@ -0,0 +1,42 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using OpenTK; + +namespace osu.Game.Rulesets.Catch.UI +{ + public class PlayfieldAdjustmentContainer : Container + { + protected override Container Content => content; + private readonly Container content; + + public PlayfieldAdjustmentContainer() + { + InternalChild = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + FillAspectRatio = 4f / 3, + Child = content = new ScalingContainer { RelativeSizeAxes = Axes.Both } + }; + } + + /// + /// A which scales its content relative to a target width. + /// + private class ScalingContainer : Container + { + protected override void Update() + { + base.Update(); + + Scale = new Vector2(Parent.ChildSize.X / CatchPlayfield.BASE_WIDTH); + Size = Vector2.Divide(Vector2.One, Scale); + } + } + } +} diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj index 3165f69a6b..bf75ebbff8 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -2,9 +2,10 @@ - - + + + WinExe diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index c15b303048..5d8480d969 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps TargetColumns = (int)Math.Max(1, roundedCircleSize); else { - float percentSliderOrSpinner = (float)beatmap.HitObjects.Count(h => h is IHasEndTime) / beatmap.HitObjects.Count(); + float percentSliderOrSpinner = (float)beatmap.HitObjects.Count(h => h is IHasEndTime) / beatmap.HitObjects.Count; if (percentSliderOrSpinner < 0.2) TargetColumns = 7; else if (percentSliderOrSpinner < 0.3 || roundedCircleSize >= 5) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 010cf962cc..37a8062d75 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -173,26 +173,18 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy var pattern = new Pattern(); int usableColumns = TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects; - int nextColumn = Random.Next(RandomStart, TotalColumns); + int nextColumn = GetRandomColumn(); for (int i = 0; i < Math.Min(usableColumns, noteCount); i++) { // Find available column - RunWhile(() => pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn), () => - { - nextColumn = Random.Next(RandomStart, TotalColumns); - }); - + nextColumn = FindAvailableColumn(nextColumn, pattern, PreviousPattern); addToPattern(pattern, nextColumn, startTime, EndTime); } // This is can't be combined with the above loop due to RNG for (int i = 0; i < noteCount - usableColumns; i++) { - RunWhile(() => pattern.ColumnHasObject(nextColumn), () => - { - nextColumn = Random.Next(RandomStart, TotalColumns); - }); - + nextColumn = FindAvailableColumn(nextColumn, pattern); addToPattern(pattern, nextColumn, startTime, EndTime); } @@ -217,23 +209,13 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns) - { - RunWhile(() => PreviousPattern.ColumnHasObject(nextColumn), () => - { - nextColumn = Random.Next(RandomStart, TotalColumns); - }); - } + nextColumn = FindAvailableColumn(nextColumn, PreviousPattern); int lastColumn = nextColumn; for (int i = 0; i < noteCount; i++) { addToPattern(pattern, nextColumn, startTime, startTime); - - RunWhile(() => nextColumn == lastColumn, () => - { - nextColumn = Random.Next(RandomStart, TotalColumns); - }); - + nextColumn = FindAvailableColumn(nextColumn, validation: c => c != lastColumn); lastColumn = nextColumn; startTime += SegmentDuration; } @@ -325,7 +307,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy if (TotalColumns > 2) addToPattern(pattern, nextColumn, startTime, startTime); - nextColumn = Random.Next(RandomStart, TotalColumns); + nextColumn = GetRandomColumn(); startTime += SegmentDuration; } @@ -404,20 +386,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns) - { - RunWhile(() => PreviousPattern.ColumnHasObject(nextColumn), () => - { - nextColumn = Random.Next(RandomStart, TotalColumns); - }); - } + nextColumn = FindAvailableColumn(nextColumn, PreviousPattern); for (int i = 0; i < columnRepeat; i++) { - RunWhile(() => pattern.ColumnHasObject(nextColumn), () => - { - nextColumn = Random.Next(RandomStart, TotalColumns); - }); - + nextColumn = FindAvailableColumn(nextColumn, pattern); addToPattern(pattern, nextColumn, startTime, EndTime); startTime += SegmentDuration; } @@ -442,17 +415,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy int holdColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns) - { - RunWhile(() => PreviousPattern.ColumnHasObject(holdColumn), () => - { - holdColumn = Random.Next(RandomStart, TotalColumns); - }); - } + holdColumn = FindAvailableColumn(holdColumn, PreviousPattern); // Create the hold note addToPattern(pattern, holdColumn, startTime, EndTime); - int nextColumn = Random.Next(RandomStart, TotalColumns); + int nextColumn = GetRandomColumn(); int noteCount; if (ConversionDifficulty > 6.5) noteCount = GetRandomNoteCount(0.63, 0); @@ -473,11 +441,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy { for (int j = 0; j < noteCount; j++) { - RunWhile(() => rowPattern.ColumnHasObject(nextColumn) || nextColumn == holdColumn, () => - { - nextColumn = Random.Next(RandomStart, TotalColumns); - }); - + nextColumn = FindAvailableColumn(nextColumn, validation: c => c != holdColumn, patterns: rowPattern); addToPattern(rowPattern, nextColumn, startTime, startTime); } } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs index eae9a0fc3b..775a4145e6 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs @@ -39,34 +39,17 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy addToPattern(pattern, 0, generateHold); break; case 8: - addToPattern(pattern, getNextRandomColumn(RandomStart), generateHold); + addToPattern(pattern, FindAvailableColumn(GetRandomColumn(), PreviousPattern), generateHold); break; default: if (TotalColumns > 0) - addToPattern(pattern, getNextRandomColumn(0), generateHold); + addToPattern(pattern, GetRandomColumn(), generateHold); break; } return pattern; } - /// - /// Picks a random column after a column. - /// - /// The starting column. - /// A random column after . - private int getNextRandomColumn(int start) - { - int nextColumn = Random.Next(start, TotalColumns); - - RunWhile(() => PreviousPattern.ColumnHasObject(nextColumn), () => - { - nextColumn = Random.Next(start, TotalColumns); - }); - - return nextColumn; - } - /// /// Constructs and adds a note to a pattern. /// diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs index 5860480a91..da1dd62cf5 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs @@ -25,9 +25,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy PatternType lastStair, IBeatmap originalBeatmap) : base(random, hitObject, beatmap, previousPattern, originalBeatmap) { - if (previousTime > hitObject.StartTime) throw new ArgumentOutOfRangeException(nameof(previousTime)); - if (density < 0) throw new ArgumentOutOfRangeException(nameof(density)); - StairType = lastStair; TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime); @@ -234,22 +231,27 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); for (int i = 0; i < noteCount; i++) { - RunWhile(() => pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn) && !allowStacking, () => - { - if (convertType.HasFlag(PatternType.Gathered)) - { - nextColumn++; - if (nextColumn == TotalColumns) - nextColumn = RandomStart; - } - else - nextColumn = Random.Next(RandomStart, TotalColumns); - }); + nextColumn = allowStacking + ? FindAvailableColumn(nextColumn, nextColumn: getNextColumn, patterns: pattern) + : FindAvailableColumn(nextColumn, nextColumn: getNextColumn, patterns: new[] { pattern, PreviousPattern }); addToPattern(pattern, nextColumn); } return pattern; + + int getNextColumn(int last) + { + if (convertType.HasFlag(PatternType.Gathered)) + { + last++; + if (last == TotalColumns) + last = RandomStart; + } + else + last = GetRandomColumn(); + return last; + } } /// @@ -295,13 +297,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy int noteCount = getRandomNoteCountMirrored(centreProbability, p2, p3, out addToCentre); int columnLimit = (TotalColumns % 2 == 0 ? TotalColumns : TotalColumns - 1) / 2; - int nextColumn = Random.Next(RandomStart, columnLimit); + int nextColumn = GetRandomColumn(upperBound: columnLimit); for (int i = 0; i < noteCount; i++) { - RunWhile(() => pattern.ColumnHasObject(nextColumn), () => - { - nextColumn = Random.Next(RandomStart, columnLimit); - }); + nextColumn = FindAvailableColumn(nextColumn, upperBound: columnLimit, patterns: pattern); // Add normal note addToPattern(pattern, nextColumn); diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs index 55081e5822..7bd39adb45 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using JetBrains.Annotations; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Objects; @@ -90,6 +91,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy } private double? conversionDifficulty; + /// /// A difficulty factor used for various conversion methods from osu!stable. /// @@ -110,11 +112,88 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy drainTime = 10000; BeatmapDifficulty difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty; - conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + (double)OriginalBeatmap.HitObjects.Count() / drainTime * 9f) / 38f * 5f / 1.15; + conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + (double)OriginalBeatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15; conversionDifficulty = Math.Min(conversionDifficulty.Value, 12); return conversionDifficulty.Value; } } + + /// + /// Finds a new column in which a can be placed. + /// This uses to pick the next candidate column. + /// + /// The initial column to test. This may be returned if it is already a valid column. + /// A list of patterns for which the validity of a column should be checked against. + /// A column is not a valid candidate if a occupies the same column in any of the patterns. + /// A column for which there are no s in any of occupying the same column. + /// If there are no valid candidate columns. + protected int FindAvailableColumn(int initialColumn, params Pattern[] patterns) + => FindAvailableColumn(initialColumn, null, patterns: patterns); + + /// + /// Finds a new column in which a can be placed. + /// + /// The initial column to test. This may be returned if it is already a valid column. + /// A function to retrieve the next column. If null, a randomisation scheme will be used. + /// A function to perform additional validation checks to determine if a column is a valid candidate for a . + /// The minimum column index. If null, is used. + /// The maximum column index. If null, is used. + /// A list of patterns for which the validity of a column should be checked against. + /// A column is not a valid candidate if a occupies the same column in any of the patterns. + /// A column which has passed the check and for which there are no + /// s in any of occupying the same column. + /// If there are no valid candidate columns. + protected int FindAvailableColumn(int initialColumn, int? lowerBound = null, int? upperBound = null, Func nextColumn = null, [InstantHandle] Func validation = null, + params Pattern[] patterns) + { + lowerBound = lowerBound ?? RandomStart; + upperBound = upperBound ?? TotalColumns; + nextColumn = nextColumn ?? (_ => GetRandomColumn(lowerBound, upperBound)); + + // Check for the initial column + if (isValid(initialColumn)) + return initialColumn; + + // Ensure that we have at least one free column, so that an endless loop is avoided + bool hasValidColumns = false; + for (int i = lowerBound.Value; i < upperBound.Value; i++) + { + hasValidColumns = isValid(i); + if (hasValidColumns) + break; + } + + if (!hasValidColumns) + throw new NotEnoughColumnsException(); + + // Iterate until a valid column is found. This is a random iteration in the default case. + do + { + initialColumn = nextColumn(initialColumn); + } while (!isValid(initialColumn)); + + return initialColumn; + + bool isValid(int column) => validation?.Invoke(column) != false && !patterns.Any(p => p.ColumnHasObject(column)); + } + + /// + /// Returns a random column index in the range [, ). + /// + /// The minimum column index. If null, is used. + /// The maximum column index. If null, is used. + protected int GetRandomColumn(int? lowerBound = null, int? upperBound = null) => Random.Next(lowerBound ?? RandomStart, upperBound ?? TotalColumns); + + /// + /// Occurs when mania conversion is stuck in an infinite loop unable to find columns to place new hitobjects in. + /// + public class NotEnoughColumnsException : Exception + { + public NotEnoughColumnsException() + : base("There were not enough columns to complete conversion.") + { + } + } } } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs index e51cbcdc60..a42d57cdd1 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs @@ -3,9 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; -using JetBrains.Annotations; -using osu.Framework.Logging; using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns @@ -15,14 +12,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns /// internal abstract class PatternGenerator { - /// - /// An arbitrary maximum amount of iterations to perform in . - /// The specific value is not super important - enough such that no false-positives occur. - /// - /// /b/933228 requires at least 23 iterations. - /// - private const int max_rng_iterations = 30; - /// /// The last pattern. /// @@ -53,44 +42,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns TotalColumns = Beatmap.TotalColumns; } - protected void RunWhile([InstantHandle] Func condition, Action action) - { - int iterations = 0; - - while (condition()) - { - if (iterations++ >= max_rng_iterations) - { - // log an error but don't throw. we want to continue execution. - Logger.Error(new ExceededAllowedIterationsException(new StackTrace(0)), - "Conversion encountered errors. The beatmap may not be correctly converted."); - return; - } - - action(); - } - } - /// /// Generates the patterns for , each filled with hit objects. /// /// The s containing the hit objects. public abstract IEnumerable Generate(); - - /// - /// Denotes when a single conversion operation is in an infinitely looping state. - /// - public class ExceededAllowedIterationsException : Exception - { - private readonly string stackTrace; - - public ExceededAllowedIterationsException(StackTrace stackTrace) - { - this.stackTrace = stackTrace.ToString(); - } - - public override string StackTrace => stackTrace; - public override string ToString() => $"{GetType().Name}: {Message}\r\n{StackTrace}"; - } } } diff --git a/osu.Game.Rulesets.Mania/Configuration/ManiaConfigManager.cs b/osu.Game.Rulesets.Mania/Configuration/ManiaConfigManager.cs index 3e9c9feba1..1c9e1e4c73 100644 --- a/osu.Game.Rulesets.Mania/Configuration/ManiaConfigManager.cs +++ b/osu.Game.Rulesets.Mania/Configuration/ManiaConfigManager.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Configuration { base.InitialiseDefaults(); - Set(ManiaSetting.ScrollTime, 1500.0, 50.0, 10000.0, 50.0); + Set(ManiaSetting.ScrollTime, 2250.0, 50.0, 10000.0, 50.0); Set(ManiaSetting.ScrollDirection, ManiaScrollingDirection.Down); } diff --git a/osu.Game.Rulesets.Mania/Edit/Layers/Selection/Overlays/HoldNoteMask.cs b/osu.Game.Rulesets.Mania/Edit/Layers/Selection/Overlays/HoldNoteMask.cs index bfa6bc0a17..03d2ba19cb 100644 --- a/osu.Game.Rulesets.Mania/Edit/Layers/Selection/Overlays/HoldNoteMask.cs +++ b/osu.Game.Rulesets.Mania/Edit/Layers/Selection/Overlays/HoldNoteMask.cs @@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Layers.Selection.Overlays } // Todo: This is temporary, since the note masks don't do anything special yet. In the future they will handle input. - public override bool HandleMouseInput => false; + public override bool HandlePositionalInput => false; } } } diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaEditRulesetContainer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaEditRulesetContainer.cs index a01947a60b..138a2c0273 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaEditRulesetContainer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaEditRulesetContainer.cs @@ -20,8 +20,7 @@ namespace osu.Game.Rulesets.Mania.Edit { Anchor = Anchor.Centre, Origin = Anchor.Centre, + Size = Vector2.One }; - - protected override Vector2 PlayfieldArea => Vector2.One; } } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index af2a889f03..6a0457efc6 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -111,6 +111,18 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables bodyPiece.Height = DrawHeight - Head.Height / 2 + Tail.Height / 2; } + protected void BeginHold() + { + holdStartTime = Time.Current; + bodyPiece.Hitting = true; + } + + protected void EndHold() + { + holdStartTime = null; + bodyPiece.Hitting = false; + } + public bool OnPressed(ManiaAction action) { // Make sure the action happened within the body of the hold note @@ -123,8 +135,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables // The user has pressed during the body of the hold note, after the head note and its hit windows have passed // and within the limited range of the above if-statement. This state will be managed by the head note if the // user has pressed during the hit windows of the head note. - holdStartTime = Time.Current; - + BeginHold(); return true; } @@ -137,7 +148,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables if (action != Action.Value) return false; - holdStartTime = null; + EndHold(); // If the key has been released too early, the user should not receive full score for the release if (!Tail.IsHit) @@ -170,7 +181,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables // The head note also handles early hits before the body, but we want accurate early hits to count as the body being held // The body doesn't handle these early early hits, so we have to explicitly set the holding state here - holdNote.holdStartTime = Time.Current; + holdNote.BeginHold(); return true; } diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs index 4ab2da208a..619fe06c73 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs @@ -32,6 +32,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces background = new Box { RelativeSizeAxes = Axes.Both }, foreground = new BufferedContainer { + Blending = BlendingMode.Additive, RelativeSizeAxes = Axes.Both, CacheDrawnFrameBuffer = true, Children = new Drawable[] @@ -73,6 +74,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces } private Color4 accentColour; + public Color4 AccentColour { get { return accentColour; } @@ -86,6 +88,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces } } + public bool Hitting + { + get { return hitting; } + set + { + hitting = value; + updateAccentColour(); + } + } + private Cached subtractionCache = new Cached(); public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true) @@ -118,13 +130,26 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces } } + private bool hitting; + private void updateAccentColour() { if (!IsLoaded) return; - foreground.Colour = AccentColour.Opacity(0.9f); - background.Colour = AccentColour.Opacity(0.6f); + foreground.Colour = AccentColour.Opacity(0.5f); + background.Colour = AccentColour.Opacity(0.7f); + + const float animation_length = 50; + + foreground.ClearTransforms(false, nameof(foreground.Colour)); + if (hitting) + { + // wait for the next sync point + double synchronisedOffset = animation_length * 2 - Time.Current % (animation_length * 2); + using (foreground.BeginDelayedSequence(synchronisedOffset)) + foreground.FadeColour(AccentColour.Lighten(0.2f), animation_length).Then().FadeColour(foreground.Colour, animation_length).Loop(); + } subtractionCache.Invalidate(); } diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index d489d48fc3..09976e5994 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -30,8 +30,6 @@ namespace osu.Game.Rulesets.Mania.UI internal readonly Container TopLevelContainer; private readonly Container explosionContainer; - protected override Container Content => hitObjectArea; - public Column() { RelativeSizeAxes = Axes.Y; @@ -54,7 +52,10 @@ namespace osu.Game.Rulesets.Mania.UI RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - hitObjectArea = new ColumnHitObjectArea { RelativeSizeAxes = Axes.Both }, + hitObjectArea = new ColumnHitObjectArea(HitObjectContainer) + { + RelativeSizeAxes = Axes.Both, + }, explosionContainer = new Container { Name = "Hit explosions", diff --git a/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs index b5dfb0949a..5a4adfd72e 100644 --- a/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs +++ b/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs @@ -8,28 +8,24 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; +using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; using OpenTK.Graphics; namespace osu.Game.Rulesets.Mania.UI.Components { - public class ColumnHitObjectArea : Container, IHasAccentColour + public class ColumnHitObjectArea : CompositeDrawable, IHasAccentColour { private const float hit_target_height = 10; private const float hit_target_bar_height = 2; - private Container content; - protected override Container Content => content; - private readonly IBindable direction = new Bindable(); - private Container hitTargetLine; + private readonly Container hitTargetLine; + private readonly Drawable hitTargetBar; - [BackgroundDependencyLoader] - private void load(IScrollingInfo scrollingInfo) + public ColumnHitObjectArea(HitObjectContainer hitObjectContainer) { - Drawable hitTargetBar; - InternalChildren = new[] { hitTargetBar = new Box @@ -45,13 +41,13 @@ namespace osu.Game.Rulesets.Mania.UI.Components Masking = true, Child = new Box { RelativeSizeAxes = Axes.Both } }, - content = new Container - { - Name = "Hit objects", - RelativeSizeAxes = Axes.Both, - }, + hitObjectContainer }; + } + [BackgroundDependencyLoader] + private void load(IScrollingInfo scrollingInfo) + { direction.BindTo(scrollingInfo.Direction); direction.BindValueChanged(direction => { diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs index dc66249cd9..f78cfefab8 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs @@ -30,10 +30,11 @@ namespace osu.Game.Rulesets.Mania.UI if (Result.IsHit) { - this.ScaleTo(0.8f); - this.ScaleTo(1, 250, Easing.OutElastic); + JudgementBody.ScaleTo(0.8f); + JudgementBody.ScaleTo(1, 250, Easing.OutElastic); - this.Delay(50).FadeOut(200).ScaleTo(0.75f, 250); + JudgementBody.Delay(50).ScaleTo(0.75f, 250); + this.Delay(50).FadeOut(200); } Expire(); diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs index 6bf63443b5..5c3a618a19 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs @@ -10,6 +10,7 @@ using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Configuration; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Objects.Drawables; +using OpenTK; namespace osu.Game.Rulesets.Mania.UI { @@ -25,12 +26,14 @@ namespace osu.Game.Rulesets.Mania.UI if (stageDefinitions.Count <= 0) throw new ArgumentException("Can't have zero or fewer stages."); + Size = new Vector2(1, 0.8f); + GridContainer playfieldGrid; - InternalChild = playfieldGrid = new GridContainer + AddInternal(playfieldGrid = new GridContainer { RelativeSizeAxes = Axes.Both, Content = new[] { new Drawable[stageDefinitions.Count] } - }; + }); var normalColumnAction = ManiaAction.Key1; var specialColumnAction = ManiaAction.Special1; diff --git a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs index 09ebde2799..49874f6dc1 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs @@ -24,7 +24,6 @@ using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; -using OpenTK; namespace osu.Game.Rulesets.Mania.UI { @@ -110,8 +109,6 @@ namespace osu.Game.Rulesets.Mania.UI } } - protected override Vector2 PlayfieldArea => new Vector2(1, 0.8f); - protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay); } } diff --git a/osu.Game.Rulesets.Mania/UI/ManiaScrollingPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaScrollingPlayfield.cs index 4d6c5a747a..8ee0fbf7fe 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaScrollingPlayfield.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaScrollingPlayfield.cs @@ -7,7 +7,7 @@ using osu.Game.Rulesets.UI.Scrolling; namespace osu.Game.Rulesets.Mania.UI { - public class ManiaScrollingPlayfield : ScrollingPlayfield + public abstract class ManiaScrollingPlayfield : ScrollingPlayfield { private readonly IBindable direction = new Bindable(); diff --git a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs index f292d5ff16..8cf49686b9 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs @@ -30,8 +30,7 @@ namespace osu.Game.Rulesets.Mania.UI public IReadOnlyList Columns => columnFlow.Children; private readonly FillFlowContainer columnFlow; - protected override Container Content => barLineContainer; - private readonly Container barLineContainer; + private readonly Container barLineContainer; public Container Judgements => judgements; private readonly JudgementContainer judgements; @@ -105,6 +104,7 @@ namespace osu.Game.Rulesets.Mania.UI Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, RelativeSizeAxes = Axes.Y, + Child = HitObjectContainer } }, judgements = new JudgementContainer diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs index c2d3aab2ab..6b67188791 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs @@ -85,7 +85,7 @@ namespace osu.Game.Rulesets.Osu.Tests } } - private class TestDrawableHitCircle : DrawableHitCircle + protected class TestDrawableHitCircle : DrawableHitCircle { private readonly bool auto; @@ -94,6 +94,8 @@ namespace osu.Game.Rulesets.Osu.Tests this.auto = auto; } + public void TriggerJudgement() => UpdateResult(true); + protected override void CheckForResult(bool userTriggered, double timeOffset) { if (auto && !userTriggered && timeOffset > 0) diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseShaking.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseShaking.cs new file mode 100644 index 0000000000..97978cff1e --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseShaking.cs @@ -0,0 +1,23 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.MathUtils; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Osu.Tests +{ + public class TestCaseShaking : TestCaseHitCircle + { + public override void Add(Drawable drawable) + { + base.Add(drawable); + + if (drawable is TestDrawableHitCircle hitObject) + { + Scheduler.AddDelayed(() => hitObject.TriggerJudgement(), + hitObject.HitObject.StartTime - (hitObject.HitObject.HitWindows.HalfWindowFor(HitResult.Miss) + RNG.Next(0, 300)) - Time.Current); + } + } + } +} diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs index 3f9464a98f..300ac16155 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs @@ -108,7 +108,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + 1000, Position = new Vector2(239, 176), - ControlPoints = new List + ControlPoints = new[] { Vector2.Zero, new Vector2(154, 28), @@ -141,7 +141,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + 1000, Position = new Vector2(-(distance / 2), 0), - ControlPoints = new List + ControlPoints = new[] { Vector2.Zero, new Vector2(distance, 0), @@ -161,7 +161,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + 1000, Position = new Vector2(-200, 0), - ControlPoints = new List + ControlPoints = new[] { Vector2.Zero, new Vector2(200, 200), @@ -184,7 +184,7 @@ namespace osu.Game.Rulesets.Osu.Tests CurveType = CurveType.Linear, StartTime = Time.Current + 1000, Position = new Vector2(-200, 0), - ControlPoints = new List + ControlPoints = new[] { Vector2.Zero, new Vector2(150, 75), @@ -210,7 +210,7 @@ namespace osu.Game.Rulesets.Osu.Tests CurveType = CurveType.Bezier, StartTime = Time.Current + 1000, Position = new Vector2(-200, 0), - ControlPoints = new List + ControlPoints = new[] { Vector2.Zero, new Vector2(150, 75), @@ -235,7 +235,7 @@ namespace osu.Game.Rulesets.Osu.Tests CurveType = CurveType.Linear, StartTime = Time.Current + 1000, Position = new Vector2(0, 0), - ControlPoints = new List + ControlPoints = new[] { Vector2.Zero, new Vector2(-200, 0), @@ -265,7 +265,7 @@ namespace osu.Game.Rulesets.Osu.Tests StartTime = Time.Current + 1000, Position = new Vector2(-100, 0), CurveType = CurveType.Catmull, - ControlPoints = new List + ControlPoints = new[] { Vector2.Zero, new Vector2(50, -50), diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index 247d5e18c1..23c6150b6a 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -2,9 +2,10 @@ - - + + + WinExe diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs index cfb1b0f050..db80948c94 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs @@ -10,6 +10,8 @@ namespace osu.Game.Rulesets.Osu.Beatmaps { public class OsuBeatmapProcessor : BeatmapProcessor { + private const int stack_distance = 3; + public OsuBeatmapProcessor(IBeatmap beatmap) : base(beatmap) { @@ -18,17 +20,21 @@ namespace osu.Game.Rulesets.Osu.Beatmaps public override void PostProcess() { base.PostProcess(); - applyStacking((Beatmap)Beatmap); + + var osuBeatmap = (Beatmap)Beatmap; + + // Reset stacking + foreach (var h in osuBeatmap.HitObjects) + h.StackHeight = 0; + + if (Beatmap.BeatmapInfo.BeatmapVersion >= 6) + applyStacking(osuBeatmap); + else + applyStackingOld(osuBeatmap); } private void applyStacking(Beatmap beatmap) { - const int stack_distance = 3; - - // Reset stacking - for (int i = 0; i <= beatmap.HitObjects.Count - 1; i++) - beatmap.HitObjects[i].StackHeight = 0; - // Extend the end index to include objects they are stacked on int extendedEndIndex = beatmap.HitObjects.Count - 1; for (int i = beatmap.HitObjects.Count - 1; i >= 0; i--) @@ -167,5 +173,40 @@ namespace osu.Game.Rulesets.Osu.Beatmaps } } } + + private void applyStackingOld(Beatmap beatmap) + { + for (int i = 0; i < beatmap.HitObjects.Count; i++) + { + OsuHitObject currHitObject = beatmap.HitObjects[i]; + + if (currHitObject.StackHeight != 0 && !(currHitObject is Slider)) + continue; + + double startTime = (currHitObject as IHasEndTime)?.EndTime ?? currHitObject.StartTime; + int sliderStack = 0; + + for (int j = i + 1; j < beatmap.HitObjects.Count; j++) + { + double stackThreshold = beatmap.HitObjects[i].TimePreempt * beatmap.BeatmapInfo.StackLeniency; + + if (beatmap.HitObjects[j].StartTime - stackThreshold > startTime) + break; + + if (Vector2Extensions.Distance(beatmap.HitObjects[j].Position, currHitObject.Position) < stack_distance) + { + currHitObject.StackHeight++; + startTime = (beatmap.HitObjects[j] as IHasEndTime)?.EndTime ?? beatmap.HitObjects[i].StartTime; + } + else if (Vector2Extensions.Distance(beatmap.HitObjects[j].Position, currHitObject.EndPosition) < stack_distance) + { + //Case for sliders - bump notes down and right, rather than up and left. + sliderStack++; + beatmap.HitObjects[j].StackHeight -= sliderStack; + startTime = (beatmap.HitObjects[j] as IHasEndTime)?.EndTime ?? beatmap.HitObjects[i].StartTime; + } + } + } + } } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 5e91ed7a97..8fc2b69267 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty double sectionLength = section_length * timeRate; // The first object doesn't generate a strain, so we begin with an incremented section end - double currentSectionEnd = 2 * sectionLength; + double currentSectionEnd = Math.Ceiling(beatmap.HitObjects.First().StartTime / sectionLength) * sectionLength; foreach (OsuDifficultyHitObject h in difficultyBeatmap) { @@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty double hitWindowGreat = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate; double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate; - int maxCombo = beatmap.HitObjects.Count(); + int maxCombo = beatmap.HitObjects.Count; // Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above) maxCombo += beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.Count - 1); diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index d4a60dd52f..b0887ac72b 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty { countHitCircles = Beatmap.HitObjects.Count(h => h is HitCircle); - beatmapMaxCombo = Beatmap.HitObjects.Count(); + beatmapMaxCombo = Beatmap.HitObjects.Count; // Add the ticks + tail of the slider. 1 is subtracted because the "headcircle" would be counted twice (once for the slider itself in the line above) beatmapMaxCombo += Beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.Count - 1); } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyBeatmap.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyBeatmap.cs index 4443a0e66b..24d4677981 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyBeatmap.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyBeatmap.cs @@ -3,6 +3,7 @@ using System.Collections; using System.Collections.Generic; +using System.Linq; using osu.Game.Rulesets.Osu.Objects; namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing @@ -23,8 +24,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing { // 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, timeRate); + difficultyObjects = createDifficultyObjectEnumerator(objects.OrderBy(h => h.StartTime).ToList(), timeRate); } /// diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index 29de23406b..ccfcc1ef25 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -21,15 +21,25 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing public OsuHitObject BaseObject { get; } /// - /// Normalized distance from the of the previous . + /// Normalized distance from the end position of the previous to the start position of this . /// - public double Distance { get; private set; } + public double JumpDistance { get; private set; } + + /// + /// Normalized distance between the start and end position of the previous . + /// + public double TravelDistance { get; private set; } /// /// Milliseconds elapsed since the StartTime of the previous . /// public double DeltaTime { get; private set; } + /// + /// Milliseconds elapsed since the start time of the previous , with a minimum of 50ms. + /// + public double StrainTime { get; private set; } + private readonly OsuHitObject lastObject; private readonly double timeRate; @@ -51,31 +61,37 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing private void setDistances() { // We will scale distances by this factor, so we can assume a uniform CircleSize among beatmaps. - double scalingFactor = normalized_radius / BaseObject.Radius; + float scalingFactor = normalized_radius / (float)BaseObject.Radius; if (BaseObject.Radius < 30) { - double smallCircleBonus = Math.Min(30 - BaseObject.Radius, 5) / 50; + float smallCircleBonus = Math.Min(30 - (float)BaseObject.Radius, 5) / 50; scalingFactor *= 1 + smallCircleBonus; } Vector2 lastCursorPosition = lastObject.StackedPosition; - float lastTravelDistance = 0; var lastSlider = lastObject as Slider; if (lastSlider != null) { computeSliderCursorPosition(lastSlider); lastCursorPosition = lastSlider.LazyEndPosition ?? lastCursorPosition; - lastTravelDistance = lastSlider.LazyTravelDistance; } - Distance = (lastTravelDistance + (BaseObject.StackedPosition - lastCursorPosition).Length) * scalingFactor; + // Don't need to jump to reach spinners + if (!(BaseObject is Spinner)) + JumpDistance = (BaseObject.StackedPosition * scalingFactor - lastCursorPosition * scalingFactor).Length; + + // Todo: BUG!!! Last slider's travel distance is considered ONLY IF we ourselves are also a slider! + if (BaseObject is Slider) + TravelDistance = (lastSlider?.LazyTravelDistance ?? 0) * 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(50, (BaseObject.StartTime - lastObject.StartTime) / timeRate); + DeltaTime = (BaseObject.StartTime - lastObject.StartTime) / timeRate; + + // Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure + StrainTime = Math.Max(50, DeltaTime); } private void computeSliderCursorPosition(Slider slider) @@ -87,8 +103,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing float approxFollowCircleRadius = (float)(slider.Radius * 3); var computeVertex = new Action(t => { + double progress = ((int)t - (int)slider.StartTime) / (float)(int)slider.SpanDuration; + if (progress % 2 > 1) + progress = 1 - progress % 1; + else + progress = progress % 1; + // ReSharper disable once PossibleInvalidOperationException (bugged in current r# version) - var diff = slider.StackedPositionAt(t) - slider.LazyEndPosition.Value; + var diff = slider.StackedPosition + slider.Curve.PositionAt(progress) - slider.LazyEndPosition.Value; float dist = diff.Length; if (dist > approxFollowCircleRadius) diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs index 0a45c62671..f11b6d66f6 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs @@ -14,6 +14,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override double SkillMultiplier => 26.25; protected override double StrainDecayBase => 0.15; - protected override double StrainValueOf(OsuDifficultyHitObject current) => Math.Pow(current.Distance, 0.99) / current.DeltaTime; + protected override double StrainValueOf(OsuDifficultyHitObject current) + => (Math.Pow(current.TravelDistance, 0.99) + Math.Pow(current.JumpDistance, 0.99)) / current.StrainTime; } } diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs index b807f20037..1cde03624b 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills protected override double StrainValueOf(OsuDifficultyHitObject current) { - double distance = current.Distance; + double distance = current.TravelDistance + current.JumpDistance; double speedValue; if (distance > single_spacing_threshold) @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills else speedValue = 0.95; - return speedValue / current.DeltaTime; + return speedValue / current.StrainTime; } } } diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs index adb28289cf..151564a2a8 100644 --- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs +++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs @@ -56,6 +56,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays } // Todo: This is temporary, since the slider circle masks don't do anything special yet. In the future they will handle input. - public override bool HandleMouseInput => false; + public override bool HandlePositionalInput => false; } } diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderMask.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderMask.cs index 0f6143a83d..aff42dd233 100644 --- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderMask.cs +++ b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderMask.cs @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays body.UpdateProgress(0); } - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => body.ReceiveMouseInputAt(screenSpacePos); + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => body.ReceivePositionalInputAt(screenSpacePos); public override Vector2 SelectionPoint => ToScreenSpace(OriginPosition); public override Quad SelectionQuad => body.PathDrawQuad; diff --git a/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs b/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs index 6efa16bf56..8571de39f4 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs @@ -4,6 +4,7 @@ using osu.Framework.Graphics.Cursor; using osu.Game.Beatmaps; using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.UI; using OpenTK; namespace osu.Game.Rulesets.Osu.Edit @@ -15,8 +16,8 @@ namespace osu.Game.Rulesets.Osu.Edit { } - protected override Vector2 PlayfieldArea => Vector2.One; - protected override CursorContainer CreateCursor() => null; + + protected override Playfield CreatePlayfield() => new OsuPlayfield { Size = Vector2.One }; } } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index dce1fc2851..d6972d55d2 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; @@ -31,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Edit new HitObjectCompositionTool() }; - protected override ScalableContainer CreateLayerContainer() => new ScalableContainer(OsuPlayfield.BASE_SIZE.X) { RelativeSizeAxes = Axes.Both }; + protected override Container CreateLayerContainer() => new PlayfieldAdjustmentContainer { RelativeSizeAxes = Axes.Both }; public override HitObjectMask CreateMaskFor(DrawableHitObject hitObject) { diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs index 7a30e6b134..1b3725a15e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; using System.Linq; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Rulesets.Mods; @@ -33,8 +32,9 @@ namespace osu.Game.Rulesets.Osu.Mods slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); - var newControlPoints = new List(); - slider.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, -c.Y))); + var newControlPoints = new Vector2[slider.ControlPoints.Length]; + for (int i = 0; i < slider.ControlPoints.Length; i++) + newControlPoints[i] = new Vector2(slider.ControlPoints[i].X, -slider.ControlPoints[i].Y); slider.ControlPoints = newControlPoints; slider.Curve?.Calculate(); // Recalculate the slider curve diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs index 4047e057cb..8d27502b3c 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using osu.Framework.Input.States; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; @@ -28,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Mods const float relax_leniency = 3; - foreach (var drawable in playfield.HitObjects.AliveObjects) + foreach (var drawable in playfield.HitObjectContainer.AliveObjects) { if (!(drawable is DrawableOsuHitObject osuHit)) continue; @@ -77,7 +76,7 @@ namespace osu.Game.Rulesets.Osu.Mods wasLeft = !wasLeft; } - osuInputManager.HandleCustomInput(new InputState(), state); + state.Apply(osuInputManager.CurrentState, osuInputManager); } public void ApplyToRulesetContainer(RulesetContainer rulesetContainer) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs new file mode 100644 index 0000000000..440b314e5f --- /dev/null +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs @@ -0,0 +1,53 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects; +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Mods +{ + internal class OsuModTransform : Mod, IApplicableToDrawableHitObjects + { + public override string Name => "Transform"; + public override string ShortenedName => "TR"; + public override FontAwesome Icon => FontAwesome.fa_arrows; + public override ModType Type => ModType.Fun; + public override string Description => "Everything rotates. EVERYTHING."; + public override double ScoreMultiplier => 1; + public override Type[] IncompatibleMods => new[] { typeof(OsuModWiggle) }; + + private float theta; + + public void ApplyToDrawableHitObjects(IEnumerable drawables) + { + foreach (var drawable in drawables) + { + var hitObject = (OsuHitObject) drawable.HitObject; + + float appearDistance = (float)(hitObject.TimePreempt - hitObject.TimeFadeIn) / 2; + + Vector2 originalPosition = drawable.Position; + Vector2 appearOffset = new Vector2((float)Math.Cos(theta), (float)Math.Sin(theta)) * appearDistance; + + //the - 1 and + 1 prevents the hit objects to appear in the wrong position. + double appearTime = hitObject.StartTime - hitObject.TimePreempt - 1; + double moveDuration = hitObject.TimePreempt + 1; + + using (drawable.BeginAbsoluteSequence(appearTime, true)) + { + drawable + .MoveToOffset(appearOffset) + .MoveTo(originalPosition, moveDuration, Easing.InOutSine); + } + + theta += (float) hitObject.TimeFadeIn / 1000; + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs new file mode 100644 index 0000000000..e0a93453ce --- /dev/null +++ b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs @@ -0,0 +1,72 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu.Objects; +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Mods +{ + internal class OsuModWiggle : Mod, IApplicableToDrawableHitObjects + { + public override string Name => "Wiggle"; + public override string ShortenedName => "WG"; + public override FontAwesome Icon => FontAwesome.fa_certificate; + public override ModType Type => ModType.Fun; + public override string Description => "They just won't stay still..."; + public override double ScoreMultiplier => 1; + public override Type[] IncompatibleMods => new[] { typeof(OsuModTransform) }; + + private const int wiggle_duration = 90; // (ms) Higher = fewer wiggles + private const int wiggle_strength = 10; // Higher = stronger wiggles + + public void ApplyToDrawableHitObjects(IEnumerable drawables) + { + foreach (var drawable in drawables) + drawable.ApplyCustomUpdateState += drawableOnApplyCustomUpdateState; + } + + private void drawableOnApplyCustomUpdateState(DrawableHitObject drawable, ArmedState state) + { + var osuObject = (OsuHitObject)drawable.HitObject; + Vector2 origin = drawable.Position; + + // Wiggle the repeat points with the slider instead of independently. + // Also fixes an issue with repeat points being positioned incorrectly. + if (osuObject is RepeatPoint) + return; + + Random objRand = new Random((int)osuObject.StartTime); + + // Wiggle all objects during TimePreempt + int amountWiggles = (int)osuObject.TimePreempt / wiggle_duration; + + void wiggle() + { + float nextAngle = (float)(objRand.NextDouble() * 2 * Math.PI); + float nextDist = (float)(objRand.NextDouble() * wiggle_strength); + drawable.MoveTo(new Vector2((float)(nextDist * Math.Cos(nextAngle) + origin.X), (float)(nextDist * Math.Sin(nextAngle) + origin.Y)), wiggle_duration); + } + + for (int i = 0; i < amountWiggles; i++) + using (drawable.BeginAbsoluteSequence(osuObject.StartTime - osuObject.TimePreempt + i * wiggle_duration, true)) + wiggle(); + + // Keep wiggling sliders and spinners for their duration + if (!(osuObject is IHasEndTime endTime)) + return; + + amountWiggles = (int)(endTime.Duration / wiggle_duration); + + for (int i = 0; i < amountWiggles; i++) + using (drawable.BeginAbsoluteSequence(osuObject.StartTime + i * wiggle_duration, true)) + wiggle(); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 6344fbb770..4bdddcef11 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -88,7 +88,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables var result = HitObject.HitWindows.ResultFor(timeOffset); if (result == HitResult.None) + { + Shake(Math.Abs(timeOffset) - HitObject.HitWindows.HalfWindowFor(HitResult.Miss)); return; + } ApplyResult(r => r.Type = result); } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 0501f8b7a0..10cd246172 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -10,19 +10,29 @@ using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; using OpenTK.Graphics; +using osu.Game.Graphics.Containers; namespace osu.Game.Rulesets.Osu.Objects.Drawables { public class DrawableOsuHitObject : DrawableHitObject { - public override bool IsPresent => base.IsPresent || State.Value == ArmedState.Idle && Time.Current >= HitObject.StartTime - HitObject.TimePreempt; + public override bool IsPresent => base.IsPresent || State.Value == ArmedState.Idle && Clock?.CurrentTime >= HitObject.StartTime - HitObject.TimePreempt; + + private readonly ShakeContainer shakeContainer; protected DrawableOsuHitObject(OsuHitObject hitObject) : base(hitObject) { + base.AddInternal(shakeContainer = new ShakeContainer { RelativeSizeAxes = Axes.Both }); Alpha = 0; } + // Forward all internal management to shakeContainer. + // This is a bit ugly but we don't have the concept of InternalContent so it'll have to do for now. (https://github.com/ppy/osu-framework/issues/1690) + protected override void AddInternal(Drawable drawable) => shakeContainer.Add(drawable); + protected override void ClearInternal(bool disposeChildren = true) => shakeContainer.Clear(disposeChildren); + protected override bool RemoveInternal(Drawable drawable) => shakeContainer.Remove(drawable); + protected sealed override void UpdateState(ArmedState state) { double transformTime = HitObject.StartTime - HitObject.TimePreempt; @@ -68,6 +78,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private OsuInputManager osuActionInputManager; internal OsuInputManager OsuActionInputManager => osuActionInputManager ?? (osuActionInputManager = GetContainingInputManager() as OsuInputManager); + protected virtual void Shake(double maximumLength) => shakeContainer.Shake(maximumLength); + protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(judgement); } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index f48f03f197..89f380db4e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -44,14 +44,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables }, ticks = new Container { RelativeSizeAxes = Axes.Both }, repeatPoints = new Container { RelativeSizeAxes = Axes.Both }, - Ball = new SliderBall(s) + Ball = new SliderBall(s, this) { BypassAutoSizeAxes = Axes.Both, Scale = new Vector2(s.Scale), AlwaysPresent = true, Alpha = 0 }, - HeadCircle = new DrawableSliderHead(s, s.HeadCircle), + HeadCircle = new DrawableSliderHead(s, s.HeadCircle) + { + OnShake = Shake + }, TailCircle = new DrawableSliderTail(s, s.TailCircle) }; @@ -181,6 +184,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public Drawable ProxiedLayer => HeadCircle.ApproachCircle; - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Body.ReceiveMouseInputAt(screenSpacePos); + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Body.ReceivePositionalInputAt(screenSpacePos); } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs index e823c870f9..6d6cba4936 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using osu.Game.Rulesets.Objects.Types; using OpenTK; @@ -28,5 +29,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (!IsHit) Position = slider.CurvePositionAt(completionProgress); } + + public Action OnShake; + + protected override void Shake(double maximumLength) => OnShake?.Invoke(maximumLength); } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs index bd7a4ad3f6..6bb6991cc0 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs @@ -12,6 +12,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { public class CirclePiece : Container, IKeyBindingHandler { + // IsHovered is used + public override bool HandlePositionalInput => true; + public Func Hit; public CirclePiece() diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index b11e4fc971..3081ae49fc 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -5,11 +5,11 @@ using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Rulesets.Objects.Types; using OpenTK.Graphics; using osu.Game.Skinning; +using OpenTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { @@ -36,9 +36,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private readonly Slider slider; public readonly Drawable FollowCircle; private Drawable drawableBall; + private readonly DrawableSlider drawableSlider; - public SliderBall(Slider slider) + public SliderBall(Slider slider, DrawableSlider drawableSlider = null) { + this.drawableSlider = drawableSlider; this.slider = slider; Masking = true; AutoSizeAxes = Axes.Both; @@ -100,24 +102,24 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces }; } - private InputState lastState; + private Vector2? lastScreenSpaceMousePosition; - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(MouseDownEvent e) { - lastState = state; - return base.OnMouseDown(state, args); + lastScreenSpaceMousePosition = e.ScreenSpaceMousePosition; + return base.OnMouseDown(e); } - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + protected override bool OnMouseUp(MouseUpEvent e) { - lastState = state; - return base.OnMouseUp(state, args); + lastScreenSpaceMousePosition = e.ScreenSpaceMousePosition; + return base.OnMouseUp(e); } - protected override bool OnMouseMove(InputState state) + protected override bool OnMouseMove(MouseMoveEvent e) { - lastState = state; - return base.OnMouseMove(state); + lastScreenSpaceMousePosition = e.ScreenSpaceMousePosition; + return base.OnMouseMove(e); } public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null) @@ -151,11 +153,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces if (Time.Current < slider.EndTime) { - // Make sure to use the base version of ReceiveMouseInputAt so that we correctly check the position. + // Make sure to use the base version of ReceivePositionalInputAt so that we correctly check the position. Tracking = canCurrentlyTrack - && lastState != null - && ReceiveMouseInputAt(lastState.Mouse.NativeState.Position) - && ((Parent as DrawableSlider)?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false); + && lastScreenSpaceMousePosition.HasValue + && ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) + && (drawableSlider?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false); } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs index 283d6b91f6..f4ccf673e9 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs @@ -7,26 +7,24 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Lines; -using osu.Framework.Graphics.Textures; -using OpenTK; using OpenTK.Graphics.ES30; using OpenTK.Graphics; using osu.Framework.Graphics.Primitives; using osu.Game.Rulesets.Objects.Types; +using OpenTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { public class SliderBody : Container, ISliderProgress { - private readonly Path path; + private readonly SliderPath path; private readonly BufferedContainer container; public float PathWidth { - get { return path.PathWidth; } - set { path.PathWidth = value; } + get => path.PathWidth; + set => path.PathWidth = value; } /// @@ -42,49 +40,44 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces public double? SnakedStart { get; private set; } public double? SnakedEnd { get; private set; } - private Color4 accentColour = Color4.White; /// /// Used to colour the path. /// public Color4 AccentColour { - get { return accentColour; } + get => path.AccentColour; set { - if (accentColour == value) + if (path.AccentColour == value) return; - accentColour = value; + path.AccentColour = value; - if (LoadState >= LoadState.Ready) - reloadTexture(); + container.ForceRedraw(); } } - private Color4 borderColour = Color4.White; /// /// Used to colour the path border. /// public new Color4 BorderColour { - get { return borderColour; } + get => path.BorderColour; set { - if (borderColour == value) + if (path.BorderColour == value) return; - borderColour = value; + path.BorderColour = value; - if (LoadState >= LoadState.Ready) - reloadTexture(); + container.ForceRedraw(); } } public Quad PathDrawQuad => container.ScreenSpaceDrawQuad; - private int textureWidth => (int)PathWidth * 2; - private Vector2 topLeftOffset; private readonly Slider slider; + public SliderBody(Slider s) { slider = s; @@ -97,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces CacheDrawnFrameBuffer = true, Children = new Drawable[] { - path = new Path + path = new SliderPath { Blending = BlendingMode.None, }, @@ -108,7 +101,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces container.Attach(RenderbufferInternalFormat.DepthComponent16); } - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => path.ReceiveMouseInputAt(screenSpacePos); + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => path.ReceivePositionalInputAt(screenSpacePos); public void SetRange(double p0, double p1) { @@ -130,53 +123,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces [BackgroundDependencyLoader] private void load() { - reloadTexture(); computeSize(); } - private void reloadTexture() - { - var texture = new Texture(textureWidth, 1); - - //initialise background - var raw = new RawTexture(textureWidth, 1); - var bytes = raw.Data; - - const float aa_portion = 0.02f; - const float border_portion = 0.128f; - const float gradient_portion = 1 - border_portion; - - const float opacity_at_centre = 0.3f; - const float opacity_at_edge = 0.8f; - - for (int i = 0; i < textureWidth; i++) - { - float progress = (float)i / (textureWidth - 1); - - if (progress <= border_portion) - { - bytes[i * 4] = (byte)(BorderColour.R * 255); - bytes[i * 4 + 1] = (byte)(BorderColour.G * 255); - bytes[i * 4 + 2] = (byte)(BorderColour.B * 255); - bytes[i * 4 + 3] = (byte)(Math.Min(progress / aa_portion, 1) * (BorderColour.A * 255)); - } - else - { - progress -= border_portion; - - bytes[i * 4] = (byte)(AccentColour.R * 255); - bytes[i * 4 + 1] = (byte)(AccentColour.G * 255); - bytes[i * 4 + 2] = (byte)(AccentColour.B * 255); - bytes[i * 4 + 3] = (byte)((opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * (AccentColour.A * 255)); - } - } - - texture.SetData(new TextureUpload(raw)); - path.Texture = texture; - - container.ForceRedraw(); - } - private void computeSize() { // Generate the entire curve @@ -229,5 +178,53 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces SetRange(start, end); } + + private class SliderPath : SmoothPath + { + private const float border_portion = 0.128f; + private const float gradient_portion = 1 - border_portion; + + private const float opacity_at_centre = 0.3f; + private const float opacity_at_edge = 0.8f; + + private Color4 borderColour = Color4.White; + + public Color4 BorderColour + { + get => borderColour; + set + { + if (borderColour == value) + return; + borderColour = value; + + InvalidateTexture(); + } + } + + private Color4 accentColour = Color4.White; + + public Color4 AccentColour + { + get => accentColour; + set + { + if (accentColour == value) + return; + accentColour = value; + + InvalidateTexture(); + } + } + + protected override Color4 ColourAt(float position) + { + if (position <= border_portion) + return BorderColour; + + position -= border_portion; + return new Color4(AccentColour.R, AccentColour.G, AccentColour.B, (opacity_at_edge - (opacity_at_edge - opacity_at_centre) * position / gradient_portion) * AccentColour.A); + } + } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs index 1a7455838f..584fd93a70 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs @@ -11,9 +11,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { public class SpinnerBackground : CircularContainer, IHasAccentColour { - public override bool HandleKeyboardInput => false; - public override bool HandleMouseInput => false; - protected Box Disc; public Color4 AccentColour diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs index 5aba60ba03..4dd1c5f218 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs @@ -4,7 +4,7 @@ using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics; using OpenTK; using OpenTK.Graphics; @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces }; } - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; private bool tracking; public bool Tracking @@ -68,10 +68,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces } } - protected override bool OnMouseMove(InputState state) + protected override bool OnMouseMove(MouseMoveEvent e) { - mousePosition = Parent.ToLocalSpace(state.Mouse.NativeState.Position); - return base.OnMouseMove(state); + mousePosition = Parent.ToLocalSpace(e.ScreenSpaceMousePosition); + return base.OnMouseMove(e); } private Vector2 mousePosition; diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 7a0dcc77a6..c284335c98 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Objects public SliderCurve Curve { get; } = new SliderCurve(); - public List ControlPoints + public Vector2[] ControlPoints { get { return Curve.ControlPoints; } set { Curve.ControlPoints = value; } diff --git a/osu.Game.Rulesets.Osu/OsuInputManager.cs b/osu.Game.Rulesets.Osu/OsuInputManager.cs index e7bbe755a0..e2f4c43a23 100644 --- a/osu.Game.Rulesets.Osu/OsuInputManager.cs +++ b/osu.Game.Rulesets.Osu/OsuInputManager.cs @@ -4,8 +4,7 @@ using System.Collections.Generic; using System.ComponentModel; using osu.Framework.Input.Bindings; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Osu @@ -36,22 +35,20 @@ namespace osu.Game.Rulesets.Osu { } - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) => AllowUserPresses && base.OnKeyDown(state, args); - protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) => AllowUserPresses && base.OnKeyUp(state, args); - protected override bool OnJoystickPress(InputState state, JoystickEventArgs args) => AllowUserPresses && base.OnJoystickPress(state, args); - protected override bool OnJoystickRelease(InputState state, JoystickEventArgs args) => AllowUserPresses && base.OnJoystickRelease(state, args); - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => AllowUserPresses && base.OnMouseDown(state, args); - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) => AllowUserPresses && base.OnMouseUp(state, args); - protected override bool OnScroll(InputState state) => AllowUserPresses && base.OnScroll(state); + protected override bool Handle(UIEvent e) + { + if (!AllowUserPresses) return false; + return base.Handle(e); + } } } public enum OsuAction { - [Description("Left Button")] + [Description("Left button")] LeftButton, - [Description("Right Button")] + [Description("Right button")] RightButton } } diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index fa6e9a018a..6736d10dab 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -117,6 +117,11 @@ namespace osu.Game.Rulesets.Osu new OsuModRelax(), new OsuModAutopilot(), }; + case ModType.Fun: + return new Mod[] { + new OsuModTransform(), + new OsuModWiggle(), + }; default: return new Mod[] { }; } diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index abcd1ddbda..4b5513ff9c 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -12,7 +12,7 @@ using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shaders; using osu.Framework.Graphics.Textures; using osu.Framework.Input; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.Timing; using OpenTK; using OpenTK.Graphics; @@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor } } - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; [BackgroundDependencyLoader] private void load(ShaderManager shaders, TextureStore textures) @@ -117,15 +117,15 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor timeOffset = Time.Current; } - protected override bool OnMouseMove(InputState state) + protected override bool OnMouseMove(MouseMoveEvent e) { - Vector2 pos = state.Mouse.NativeState.Position; + Vector2 pos = e.ScreenSpaceMousePosition; if (lastPosition == null) { lastPosition = pos; resampler.AddPosition(lastPosition.Value); - return base.OnMouseMove(state); + return base.OnMouseMove(e); } foreach (Vector2 pos2 in resampler.AddPosition(pos)) @@ -147,7 +147,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor } } - return base.OnMouseMove(state); + return base.OnMouseMove(e); } private void addPosition(Vector2 pos) @@ -216,7 +216,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor Texture.DrawQuad( new Quad(pos.X - Size.X / 2, pos.Y - Size.Y / 2, Size.X, Size.Y), - DrawInfo.Colour, + DrawColourInfo.Colour, null, v => Shared.VertexBuffer.Vertices[end++] = new TexturedTrailVertex { diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs index 4d6722b61b..4a45d4fb31 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor return false; } - public override bool HandleMouseInput => true; // OverlayContainer will set this false when we go hidden, but we always want to receive input. + public override bool HandlePositionalInput => true; // OverlayContainer will set this false when we go hidden, but we always want to receive input. protected override void PopIn() { diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 703d8764fc..398680cb8d 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -23,29 +23,35 @@ namespace osu.Game.Rulesets.Osu.UI public static readonly Vector2 BASE_SIZE = new Vector2(512, 384); public OsuPlayfield() - : base(BASE_SIZE.X) { Anchor = Anchor.Centre; Origin = Anchor.Centre; - AddRange(new Drawable[] + Size = new Vector2(0.75f); + + InternalChild = new PlayfieldAdjustmentContainer { - connectionLayer = new FollowPointRenderer + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Depth = 2, - }, - judgementLayer = new JudgementContainer - { - RelativeSizeAxes = Axes.Both, - Depth = 1, - }, - approachCircles = new Container - { - RelativeSizeAxes = Axes.Both, - Depth = -1, - }, - }); + connectionLayer = new FollowPointRenderer + { + RelativeSizeAxes = Axes.Both, + Depth = 2, + }, + judgementLayer = new JudgementContainer + { + RelativeSizeAxes = Axes.Both, + Depth = 1, + }, + HitObjectContainer, + approachCircles = new Container + { + RelativeSizeAxes = Axes.Both, + Depth = -1, + }, + } + }; } public override void Add(DrawableHitObject h) @@ -61,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.UI public override void PostProcess() { - connectionLayer.HitObjects = HitObjects.Objects.Select(d => d.HitObject).OfType(); + connectionLayer.HitObjects = HitObjectContainer.Objects.Select(d => d.HitObject).OfType(); } private void onNewResult(DrawableHitObject judgedObject, JudgementResult result) @@ -72,7 +78,8 @@ namespace osu.Game.Rulesets.Osu.UI DrawableOsuJudgement explosion = new DrawableOsuJudgement(result, judgedObject) { Origin = Anchor.Centre, - Position = ((OsuHitObject)judgedObject.HitObject).StackedEndPosition + Position = ((OsuHitObject)judgedObject.HitObject).StackedEndPosition, + Scale = new Vector2(((OsuHitObject)judgedObject.HitObject).Scale * 1.65f) }; judgementLayer.Add(explosion); diff --git a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs index 4bc6992445..0ea8d3ede8 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs @@ -4,7 +4,6 @@ using System.Linq; using osu.Framework.Graphics.Cursor; using osu.Framework.Input; -using OpenTK; using osu.Game.Beatmaps; using osu.Game.Input.Handlers; using osu.Game.Rulesets.Objects.Drawables; @@ -58,12 +57,6 @@ namespace osu.Game.Rulesets.Osu.UI } } - protected override Vector2 GetAspectAdjustedSize() - { - var aspectSize = DrawSize.X * 0.75f < DrawSize.Y ? new Vector2(DrawSize.X, DrawSize.X * 0.75f) : new Vector2(DrawSize.Y * 4f / 3f, DrawSize.Y); - return new Vector2(aspectSize.X / DrawSize.X, aspectSize.Y / DrawSize.Y); - } - protected override CursorContainer CreateCursor() => new GameplayCursor(); } } diff --git a/osu.Game.Rulesets.Osu/UI/PlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Osu/UI/PlayfieldAdjustmentContainer.cs new file mode 100644 index 0000000000..00d5692fda --- /dev/null +++ b/osu.Game.Rulesets.Osu/UI/PlayfieldAdjustmentContainer.cs @@ -0,0 +1,42 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using OpenTK; + +namespace osu.Game.Rulesets.Osu.UI +{ + public class PlayfieldAdjustmentContainer : Container + { + protected override Container Content => content; + private readonly Container content; + + public PlayfieldAdjustmentContainer() + { + InternalChild = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + FillAspectRatio = 4f / 3, + Child = content = new ScalingContainer { RelativeSizeAxes = Axes.Both } + }; + } + + /// + /// A which scales its content relative to a target width. + /// + private class ScalingContainer : Container + { + protected override void Update() + { + base.Update(); + + Scale = new Vector2(Parent.ChildSize.X / OsuPlayfield.BASE_SIZE.X); + Size = Vector2.Divide(Vector2.One, Scale); + } + } + } +} diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj index 08a0579561..6ae9a018c5 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -2,9 +2,10 @@ - - + + + WinExe diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index acd6d43284..c2cde332e8 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps converted.HitObjects = converted.HitObjects.GroupBy(t => t.StartTime).Select(x => { TaikoHitObject first = x.First(); - if (x.Skip(1).Any()) + if (x.Skip(1).Any() && !(first is Swell)) first.IsStrong = true; return first; }).ToList(); diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index f59dc8c1ee..6f7264e23b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -24,6 +24,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables private bool validActionPressed; + private bool pressHandledThisFrame; + protected DrawableHit(Hit hit) : base(hit) { @@ -51,6 +53,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public override bool OnPressed(TaikoAction action) { + if (pressHandledThisFrame) + return true; + if (Judged) return false; @@ -62,6 +67,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables if (IsHit) HitAction = action; + // Regardless of whether we've hit or not, any secondary key presses in the same frame should be discarded + // E.g. hitting a non-strong centre as a strong should not fall through and perform a hit on the next note + pressHandledThisFrame = true; + return result; } @@ -76,6 +85,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { base.Update(); + // The input manager processes all input prior to us updating, so this is the perfect time + // for us to remove the extra press blocking, before input is handled in the next frame + pressHandledThisFrame = false; + Size = BaseSize * Parent.RelativeChildSize; } diff --git a/osu.Game.Rulesets.Taiko/TaikoInputManager.cs b/osu.Game.Rulesets.Taiko/TaikoInputManager.cs index dc683ae2f5..3b430e7ad1 100644 --- a/osu.Game.Rulesets.Taiko/TaikoInputManager.cs +++ b/osu.Game.Rulesets.Taiko/TaikoInputManager.cs @@ -17,13 +17,13 @@ namespace osu.Game.Rulesets.Taiko public enum TaikoAction { - [Description("Left (Rim)")] + [Description("Left (rim)")] LeftRim, - [Description("Left (Centre)")] + [Description("Left (centre)")] LeftCentre, - [Description("Right (Centre)")] + [Description("Right (centre)")] RightCentre, - [Description("Right (Rim)")] + [Description("Right (rim)")] RightRim } } diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs index 4d660918b8..c7eba91564 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs @@ -31,10 +31,10 @@ namespace osu.Game.Rulesets.Taiko.UI switch (Result.Type) { case HitResult.Good: - Colour = colours.GreenLight; + JudgementBody.Colour = colours.GreenLight; break; case HitResult.Great: - Colour = colours.BlueLight; + JudgementBody.Colour = colours.BlueLight; break; } } diff --git a/osu.Game.Rulesets.Taiko/UI/PlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Taiko/UI/PlayfieldAdjustmentContainer.cs new file mode 100644 index 0000000000..661a4e135c --- /dev/null +++ b/osu.Game.Rulesets.Taiko/UI/PlayfieldAdjustmentContainer.cs @@ -0,0 +1,22 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics.Containers; +using OpenTK; + +namespace osu.Game.Rulesets.Taiko.UI +{ + public class PlayfieldAdjustmentContainer : Container + { + private const float default_relative_height = TaikoPlayfield.DEFAULT_HEIGHT / 768; + private const float default_aspect = 16f / 9f; + + protected override void Update() + { + base.Update(); + + float aspectAdjust = MathHelper.Clamp(Parent.ChildSize.X / Parent.ChildSize.Y, 0.4f, 4) / default_aspect; + Size = new Vector2(1, default_relative_height * aspectAdjust); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 325beb38a5..40ed659bd6 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -1,23 +1,24 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Game.Rulesets.Taiko.Objects; -using OpenTK; -using OpenTK.Graphics; -using osu.Game.Rulesets.Taiko.Judgements; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Extensions.Color4Extensions; using System.Linq; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Taiko.Objects.Drawables; +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.Game.Beatmaps.ControlPoints; +using osu.Game.Configuration; +using osu.Game.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Objects.Drawables; +using osu.Game.Rulesets.Taiko.Judgements; +using OpenTK; +using OpenTK.Graphics; namespace osu.Game.Rulesets.Taiko.UI { @@ -40,13 +41,12 @@ namespace osu.Game.Rulesets.Taiko.UI protected override bool UserScrollSpeedAdjustment => false; + protected override SpeedChangeVisualisationMethod VisualisationMethod => SpeedChangeVisualisationMethod.Overlapping; + private readonly Container hitExplosionContainer; private readonly Container kiaiExplosionContainer; private readonly JudgementContainer judgementContainer; - protected override Container Content => content; - private readonly Container content; - private readonly Container topLevelHitContainer; private readonly Container barlineContainer; @@ -61,140 +61,147 @@ namespace osu.Game.Rulesets.Taiko.UI { Direction.Value = ScrollingDirection.Left; - AddRangeInternal(new Drawable[] + InternalChild = new PlayfieldAdjustmentContainer { - backgroundContainer = new Container + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] { - Name = "Transparent playfield background", - RelativeSizeAxes = Axes.Both, - Masking = true, - EdgeEffect = new EdgeEffectParameters + backgroundContainer = new Container { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(0.2f), - Radius = 5, - }, - Children = new Drawable[] - { - background = new Box + Name = "Transparent playfield background", + RelativeSizeAxes = Axes.Both, + Masking = true, + EdgeEffect = new EdgeEffectParameters { - RelativeSizeAxes = Axes.Both, - Alpha = 0.6f + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.2f), + Radius = 5, }, - } - }, - new Container - { - Name = "Right area", - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = left_area_size }, - Children = new Drawable[] - { - new Container + Children = new Drawable[] { - Name = "Masked elements before hit objects", - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = HIT_TARGET_OFFSET }, - Masking = true, - Children = new Drawable[] + background = new Box { - hitExplosionContainer = new Container - { - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit, - Blending = BlendingMode.Additive, - }, - new HitTarget - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit - } - } - }, - barlineContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = HIT_TARGET_OFFSET } - }, - content = new Container - { - Name = "Hit objects", - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Left = HIT_TARGET_OFFSET }, - Masking = true - }, - kiaiExplosionContainer = new Container - { - Name = "Kiai hit explosions", - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit, - Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, - Blending = BlendingMode.Additive - }, - judgementContainer = new JudgementContainer - { - Name = "Judgements", - RelativeSizeAxes = Axes.Y, - Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, - Blending = BlendingMode.Additive - }, - } - }, - overlayBackgroundContainer = new Container - { - Name = "Left overlay", - RelativeSizeAxes = Axes.Y, - Size = new Vector2(left_area_size, 1), - Children = new Drawable[] - { - overlayBackground = new Box - { - RelativeSizeAxes = Axes.Both, - }, - new InputDrum(controlPoints) - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Scale = new Vector2(0.9f), - Margin = new MarginPadding { Right = 20 } - }, - new Box - { - Anchor = Anchor.TopRight, - RelativeSizeAxes = Axes.Y, - Width = 10, - Colour = Framework.Graphics.Colour.ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.6f), Color4.Black.Opacity(0)), - }, - } - }, - new Container - { - Name = "Border", - RelativeSizeAxes = Axes.Both, - Masking = true, - MaskingSmoothness = 0, - BorderThickness = 2, - AlwaysPresent = true, - Children = new[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - AlwaysPresent = true + RelativeSizeAxes = Axes.Both, + Alpha = 0.6f + }, } + }, + new Container + { + Name = "Right area", + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = left_area_size }, + Children = new Drawable[] + { + new Container + { + Name = "Masked elements before hit objects", + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = HIT_TARGET_OFFSET }, + Masking = true, + Children = new Drawable[] + { + hitExplosionContainer = new Container + { + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + Blending = BlendingMode.Additive, + }, + new HitTarget + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit + } + } + }, + barlineContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = HIT_TARGET_OFFSET } + }, + new Container + { + Name = "Hit objects", + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = HIT_TARGET_OFFSET }, + Masking = true, + Child = HitObjectContainer + }, + kiaiExplosionContainer = new Container + { + Name = "Kiai hit explosions", + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, + Blending = BlendingMode.Additive + }, + judgementContainer = new JudgementContainer + { + Name = "Judgements", + RelativeSizeAxes = Axes.Y, + Margin = new MarginPadding { Left = HIT_TARGET_OFFSET }, + Blending = BlendingMode.Additive + }, + } + }, + overlayBackgroundContainer = new Container + { + Name = "Left overlay", + RelativeSizeAxes = Axes.Y, + Size = new Vector2(left_area_size, 1), + Children = new Drawable[] + { + overlayBackground = new Box + { + RelativeSizeAxes = Axes.Both, + }, + new InputDrum(controlPoints) + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Scale = new Vector2(0.9f), + Margin = new MarginPadding { Right = 20 } + }, + new Box + { + Anchor = Anchor.TopRight, + RelativeSizeAxes = Axes.Y, + Width = 10, + Colour = Framework.Graphics.Colour.ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.6f), Color4.Black.Opacity(0)), + }, + } + }, + new Container + { + Name = "Border", + RelativeSizeAxes = Axes.Both, + Masking = true, + MaskingSmoothness = 0, + BorderThickness = 2, + AlwaysPresent = true, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + } + }, + topLevelHitContainer = new Container + { + Name = "Top level hit objects", + RelativeSizeAxes = Axes.Both, } - }, - topLevelHitContainer = new Container - { - Name = "Top level hit objects", - RelativeSizeAxes = Axes.Both, } - }); + }; - VisibleTimeRange.Value = 6000; + VisibleTimeRange.Value = 7000; } [BackgroundDependencyLoader] diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs index 229ab69ceb..3d08bffe0f 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; -using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; @@ -13,7 +12,6 @@ using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Rulesets.Taiko.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.Taiko.Replays; -using OpenTK; using System.Linq; using osu.Framework.Input; using osu.Game.Input.Handlers; @@ -74,27 +72,11 @@ namespace osu.Game.Rulesets.Taiko.UI } } - protected override Vector2 GetAspectAdjustedSize() - { - const float default_relative_height = TaikoPlayfield.DEFAULT_HEIGHT / 768; - const float default_aspect = 16f / 9f; - - float aspectAdjust = MathHelper.Clamp(DrawWidth / DrawHeight, 0.4f, 4) / default_aspect; - - return new Vector2(1, default_relative_height * aspectAdjust); - } - - protected override Vector2 PlayfieldArea => Vector2.One; - public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this); public override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo); - protected override Playfield CreatePlayfield() => new TaikoPlayfield(Beatmap.ControlPointInfo) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft - }; + protected override Playfield CreatePlayfield() => new TaikoPlayfield(Beatmap.ControlPointInfo); protected override DrawableHitObject GetVisualRepresentation(TaikoHitObject h) { diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index d3351f86f8..af63a39662 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -165,7 +165,7 @@ namespace osu.Game.Tests.Beatmaps.Formats } [Test] - public void TestDecodeBeatmapColors() + public void TestDecodeBeatmapColours() { var decoder = new LegacySkinDecoder(); using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) @@ -181,6 +181,7 @@ namespace osu.Game.Tests.Beatmaps.Formats new Color4(128, 255, 128, 255), new Color4(255, 187, 255, 255), new Color4(255, 177, 140, 255), + new Color4(100, 100, 100, 100), }; Assert.AreEqual(expectedColors.Length, comboColors.Count); for (int i = 0; i < expectedColors.Length; i++) diff --git a/osu.Game.Tests/Resources/Soleily - Renatus (Gamu) [Insane].osu b/osu.Game.Tests/Resources/Soleily - Renatus (Gamu) [Insane].osu index 3e44dc0af8..67570ad21b 100644 --- a/osu.Game.Tests/Resources/Soleily - Renatus (Gamu) [Insane].osu +++ b/osu.Game.Tests/Resources/Soleily - Renatus (Gamu) [Insane].osu @@ -101,6 +101,7 @@ Combo3 : 128,255,255 Combo4 : 128,255,128 Combo5 : 255,187,255 Combo6 : 255,177,140 +Combo7 : 100,100,100,100 [HitObjects] 192,168,956,6,0,P|184:128|200:80,1,90,4|0,1:2|0:0,0:0:0:0: diff --git a/osu.Game.Tests/Visual/TestCaseCursors.cs b/osu.Game.Tests/Visual/TestCaseCursors.cs index 361e255894..1f409f043e 100644 --- a/osu.Game.Tests/Visual/TestCaseCursors.cs +++ b/osu.Game.Tests/Visual/TestCaseCursors.cs @@ -7,7 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.MathUtils; using osu.Game.Graphics.Cursor; using osu.Game.Graphics.Sprites; @@ -184,7 +184,7 @@ namespace osu.Game.Tests.Visual /// /// The cursor to check. private bool checkAtMouse(CursorContainer cursorContainer) - => Precision.AlmostEquals(InputManager.CurrentState.Mouse.NativeState.Position, cursorContainer.ToScreenSpace(cursorContainer.ActiveCursor.DrawPosition)); + => Precision.AlmostEquals(InputManager.CurrentState.Mouse.Position, cursorContainer.ToScreenSpace(cursorContainer.ActiveCursor.DrawPosition)); private class CustomCursorBox : Container, IProvideCursor { @@ -193,7 +193,7 @@ namespace osu.Game.Tests.Visual public CursorContainer Cursor { get; } public bool ProvidingUserCursor { get; } - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => base.ReceiveMouseInputAt(screenSpacePos) || SmoothTransition && !ProvidingUserCursor; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || SmoothTransition && !ProvidingUserCursor; private readonly Box background; @@ -224,16 +224,16 @@ namespace osu.Game.Tests.Visual }; } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { background.FadeTo(0.4f, 250, Easing.OutQuint); return false; } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { background.FadeTo(0.1f, 250); - base.OnHoverLost(state); + base.OnHoverLost(e); } } diff --git a/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs b/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs index 45b5ec2c11..417b0f94d7 100644 --- a/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs @@ -8,14 +8,14 @@ using System.Linq; using OpenTK.Input; using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; -using osu.Framework.Input.EventArgs; using osu.Framework.Logging; using osu.Game.Screens.Play; +using OpenTK; namespace osu.Game.Tests.Visual { [Description("player pause/fail screens")] - public class TestCaseGameplayMenuOverlay : OsuTestCase + public class TestCaseGameplayMenuOverlay : ManualInputManagerTestCase { public override IReadOnlyList RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseContainer) }; @@ -73,12 +73,18 @@ namespace osu.Game.Tests.Visual { AddStep("Show overlay", () => failOverlay.Show()); - AddStep("Hover first button", () => failOverlay.Buttons.First().TriggerOnMouseMove(null)); + AddStep("Hover first button", () => InputManager.MoveMouseTo(failOverlay.Buttons.First())); AddStep("Hide overlay", () => failOverlay.Hide()); AddAssert("Overlay state is reset", () => !failOverlay.Buttons.Any(b => b.Selected)); } + private void press(Key key) + { + InputManager.PressKey(key); + InputManager.ReleaseKey(key); + } + /// /// Tests that pressing enter after an overlay shows doesn't trigger an event because a selection hasn't occurred. /// @@ -86,7 +92,7 @@ namespace osu.Game.Tests.Visual { AddStep("Show overlay", () => pauseOverlay.Show()); - AddStep("Press enter", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Enter })); + AddStep("Press enter", () => press(Key.Enter)); AddAssert("Overlay still open", () => pauseOverlay.State == Visibility.Visible); AddStep("Hide overlay", () => pauseOverlay.Hide()); @@ -99,7 +105,7 @@ namespace osu.Game.Tests.Visual { AddStep("Show overlay", () => pauseOverlay.Show()); - AddStep("Up arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up })); + AddStep("Up arrow", () => press(Key.Up)); AddAssert("Last button selected", () => pauseOverlay.Buttons.Last().Selected); AddStep("Hide overlay", () => pauseOverlay.Hide()); @@ -112,7 +118,7 @@ namespace osu.Game.Tests.Visual { AddStep("Show overlay", () => pauseOverlay.Show()); - AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); + AddStep("Down arrow", () => press(Key.Down)); AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected); AddStep("Hide overlay", () => pauseOverlay.Hide()); @@ -125,11 +131,11 @@ namespace osu.Game.Tests.Visual { AddStep("Show overlay", () => failOverlay.Show()); - AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up })); + AddStep("Up arrow", () => press(Key.Up)); AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected); - AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up })); + AddStep("Up arrow", () => press(Key.Up)); AddAssert("First button selected", () => failOverlay.Buttons.First().Selected); - AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up })); + AddStep("Up arrow", () => press(Key.Up)); AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected); AddStep("Hide overlay", () => failOverlay.Hide()); @@ -142,11 +148,11 @@ namespace osu.Game.Tests.Visual { AddStep("Show overlay", () => failOverlay.Show()); - AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); + AddStep("Down arrow", () => press(Key.Down)); AddAssert("First button selected", () => failOverlay.Buttons.First().Selected); - AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); + AddStep("Down arrow", () => press(Key.Down)); AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected); - AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); + AddStep("Down arrow", () => press(Key.Down)); AddAssert("First button selected", () => failOverlay.Buttons.First().Selected); AddStep("Hide overlay", () => failOverlay.Hide()); @@ -161,8 +167,8 @@ namespace osu.Game.Tests.Visual var secondButton = pauseOverlay.Buttons.Skip(1).First(); - AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); - AddStep("Hover second button", () => secondButton.TriggerOnMouseMove(null)); + AddStep("Down arrow", () => press(Key.Down)); + AddStep("Hover second button", () => InputManager.MoveMouseTo(secondButton)); AddAssert("First button not selected", () => !pauseOverlay.Buttons.First().Selected); AddAssert("Second button selected", () => secondButton.Selected); @@ -174,12 +180,16 @@ namespace osu.Game.Tests.Visual /// private void testKeySelectionAfterMouseSelection() { - AddStep("Show overlay", () => pauseOverlay.Show()); + AddStep("Show overlay", () => + { + pauseOverlay.Show(); + InputManager.MoveMouseTo(Vector2.Zero); + }); var secondButton = pauseOverlay.Buttons.Skip(1).First(); - AddStep("Hover second button", () => secondButton.TriggerOnMouseMove(null)); - AddStep("Up arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up })); + AddStep("Hover second button", () => InputManager.MoveMouseTo(secondButton)); + AddStep("Up arrow", () => press(Key.Up)); AddAssert("Second button not selected", () => !secondButton.Selected); AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected); @@ -195,9 +205,9 @@ namespace osu.Game.Tests.Visual var secondButton = pauseOverlay.Buttons.Skip(1).First(); - AddStep("Hover second button", () => secondButton.TriggerOnMouseMove(null)); - AddStep("Unhover second button", () => secondButton.TriggerOnHoverLost(null)); - AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down })); + AddStep("Hover second button", () => InputManager.MoveMouseTo(secondButton)); + AddStep("Unhover second button", () => InputManager.MoveMouseTo(Vector2.Zero)); + AddStep("Down arrow", () => press(Key.Down)); AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected); // Initial state condition AddStep("Hide overlay", () => pauseOverlay.Hide()); @@ -218,7 +228,7 @@ namespace osu.Game.Tests.Visual var lastAction = pauseOverlay.OnRetry; pauseOverlay.OnRetry = () => triggered = true; - retryButton.TriggerOnClick(); + retryButton.Click(); pauseOverlay.OnRetry = lastAction; }); @@ -235,23 +245,28 @@ namespace osu.Game.Tests.Visual AddStep("Select second button", () => { - pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }); - pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }); + press(Key.Down); + press(Key.Down); }); - var retryButton = pauseOverlay.Buttons.Skip(1).First(); - bool triggered = false; + Action lastAction = null; AddStep("Press enter", () => { - var lastAction = pauseOverlay.OnRetry; + lastAction = pauseOverlay.OnRetry; pauseOverlay.OnRetry = () => triggered = true; - - retryButton.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Enter }); - pauseOverlay.OnRetry = lastAction; + press(Key.Enter); }); - AddAssert("Action was triggered", () => triggered); + AddAssert("Action was triggered", () => + { + if (lastAction != null) + { + pauseOverlay.OnRetry = lastAction; + lastAction = null; + } + return triggered; + }); AddAssert("Overlay is closed", () => pauseOverlay.State == Visibility.Hidden); } } diff --git a/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs b/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs index 5df371dd09..a2bc28bc3c 100644 --- a/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs @@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual new Slider { Position = new Vector2(128, 256), - ControlPoints = new List + ControlPoints = new[] { Vector2.Zero, new Vector2(216, 0), diff --git a/osu.Game.Tests/Visual/TestCaseKeyCounter.cs b/osu.Game.Tests/Visual/TestCaseKeyCounter.cs index b98875cd6a..178b47ed33 100644 --- a/osu.Game.Tests/Visual/TestCaseKeyCounter.cs +++ b/osu.Game.Tests/Visual/TestCaseKeyCounter.cs @@ -1,32 +1,45 @@ // Copyright (c) 2007-2018 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 NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.MathUtils; +using osu.Framework.Timing; using osu.Game.Screens.Play; using OpenTK.Input; namespace osu.Game.Tests.Visual { [TestFixture] - public class TestCaseKeyCounter : OsuTestCase + public class TestCaseKeyCounter : ManualInputManagerTestCase { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(KeyCounterKeyboard), + typeof(KeyCounterMouse), + typeof(KeyCounterCollection) + }; + public TestCaseKeyCounter() { + KeyCounterKeyboard rewindTestKeyCounterKeyboard; KeyCounterCollection kc = new KeyCounterCollection { Origin = Anchor.Centre, Anchor = Anchor.Centre, Children = new KeyCounter[] { - new KeyCounterKeyboard(Key.Z), + rewindTestKeyCounterKeyboard = new KeyCounterKeyboard(Key.X), new KeyCounterKeyboard(Key.X), new KeyCounterMouse(MouseButton.Left), new KeyCounterMouse(MouseButton.Right), }, }; + AddStep("Add random", () => { Key key = (Key)((int)Key.A + RNG.Next(26)); @@ -34,7 +47,57 @@ namespace osu.Game.Tests.Visual }); AddSliderStep("Fade time", 0, 200, 50, v => kc.FadeTime = v); + Key testKey = ((KeyCounterKeyboard)kc.Children.First()).Key; + double time1 = 0; + + AddStep($"Press {testKey} key", () => + { + InputManager.PressKey(testKey); + InputManager.ReleaseKey(testKey); + }); + + AddAssert($"Check {testKey} counter after keypress", () => rewindTestKeyCounterKeyboard.CountPresses == 1); + + AddStep($"Press {testKey} key", () => + { + InputManager.PressKey(testKey); + InputManager.ReleaseKey(testKey); + time1 = Clock.CurrentTime; + }); + + AddAssert($"Check {testKey} counter after keypress", () => rewindTestKeyCounterKeyboard.CountPresses == 2); + + IFrameBasedClock oldClock = null; + + AddStep($"Rewind {testKey} counter once", () => + { + oldClock = rewindTestKeyCounterKeyboard.Clock; + rewindTestKeyCounterKeyboard.Clock = new FramedOffsetClock(new FixedClock(time1 - 10)); + }); + + AddAssert($"Check {testKey} counter after rewind", () => rewindTestKeyCounterKeyboard.CountPresses == 1); + + AddStep($"Rewind {testKey} counter to zero", () => rewindTestKeyCounterKeyboard.Clock = new FramedOffsetClock(new FixedClock(0))); + + AddAssert($"Check {testKey} counter after rewind", () => rewindTestKeyCounterKeyboard.CountPresses == 0); + + AddStep("Restore clock", () => rewindTestKeyCounterKeyboard.Clock = oldClock); + Add(kc); } + + private class FixedClock : IClock + { + private readonly double time; + + public FixedClock(double time) + { + this.time = time; + } + + public double CurrentTime => time; + public double Rate => 1; + public bool IsRunning => false; + } } } diff --git a/osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs b/osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs index e4cb848d90..041fce6ce3 100644 --- a/osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs +++ b/osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual [BackgroundDependencyLoader] private void load() { - AddInternal(trackManager); + Add(trackManager); } [Test] @@ -75,7 +75,7 @@ namespace osu.Game.Tests.Visual TestTrackOwner owner = null; PreviewTrack track = null; - AddStep("get track", () => AddInternal(owner = new TestTrackOwner(track = getTrack()))); + AddStep("get track", () => Add(owner = new TestTrackOwner(track = getTrack()))); AddStep("start", () => track.Start()); AddStep("attempt stop", () => trackManager.StopAnyPlaying(this)); AddAssert("not stopped", () => track.IsRunning); @@ -89,7 +89,7 @@ namespace osu.Game.Tests.Visual { var track = getTrack(); - AddInternal(track); + Add(track); return track; } diff --git a/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs b/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs index bbc9d2b860..b254325472 100644 --- a/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs +++ b/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs @@ -121,14 +121,21 @@ namespace osu.Game.Tests.Visual Direction = direction; Padding = new MarginPadding(2); - Content.Masking = true; - AddInternal(new Box + InternalChildren = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Alpha = 0.5f, - Depth = float.MaxValue - }); + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.5f, + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + Child = HitObjectContainer + } + }; } } diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index d638af0c38..520e0b8940 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -2,9 +2,11 @@ - - + + + + WinExe diff --git a/osu.Game.props b/osu.Game.props index ec859e64a5..4bcac479a0 100644 --- a/osu.Game.props +++ b/osu.Game.props @@ -1,7 +1,7 @@ - 7 + 7.2 ..\app.manifest diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 9aabb434a3..4ef7b28d49 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -48,7 +48,7 @@ namespace osu.Game.Beatmaps [JsonConverter(typeof(TypedListConverter))] public List HitObjects = new List(); - IEnumerable IBeatmap.HitObjects => HitObjects; + IReadOnlyList IBeatmap.HitObjects => HitObjects; public virtual IEnumerable GetStatistics() => Enumerable.Empty(); diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs index a1bb70135a..3cb7833a12 100644 --- a/osu.Game/Beatmaps/BeatmapConverter.cs +++ b/osu.Game/Beatmaps/BeatmapConverter.cs @@ -55,39 +55,40 @@ namespace osu.Game.Beatmaps beatmap.BeatmapInfo = original.BeatmapInfo; beatmap.ControlPointInfo = original.ControlPointInfo; - beatmap.HitObjects = original.HitObjects.SelectMany(h => convert(h, original)).ToList(); + beatmap.HitObjects = convertHitObjects(original.HitObjects, original); beatmap.Breaks = original.Breaks; return beatmap; } - /// - /// Converts a hit object. - /// - /// The hit object to convert. - /// The un-converted Beatmap. - /// The converted hit object. - private IEnumerable convert(HitObject original, IBeatmap beatmap) + private List convertHitObjects(IReadOnlyList hitObjects, IBeatmap beatmap) { - // Check if the hitobject is already the converted type - T tObject = original as T; - if (tObject != null) - { - yield return tObject; - yield break; - } + var result = new List(hitObjects.Count); - var converted = ConvertHitObject(original, beatmap).ToList(); - ObjectConverted?.Invoke(original, converted); - - // Convert the hit object - foreach (var obj in converted) + foreach (var obj in hitObjects) { - if (obj == null) + if (obj is T tObj) + { + result.Add(tObj); continue; + } - yield return obj; + var converted = ConvertHitObject(obj, beatmap); + + if (ObjectConverted != null) + { + converted = converted.ToList(); + ObjectConverted.Invoke(obj, converted); + } + + foreach (var c in converted) + { + if (c != null) + result.Add(c); + } } + + return result; } /// diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 303a19aab3..1aa4818393 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -27,13 +27,15 @@ namespace osu.Game.Beatmaps [JsonProperty("id")] public int? OnlineBeatmapID { - get { return onlineBeatmapID; } - set { onlineBeatmapID = value > 0 ? value : null; } + get => onlineBeatmapID; + set => onlineBeatmapID = value > 0 ? value : null; } [JsonIgnore] public int BeatmapSetInfoID { get; set; } + public BeatmapSetOnlineStatus Status { get; set; } = BeatmapSetOnlineStatus.None; + [Required] public BeatmapSetInfo BeatmapSet { get; set; } @@ -82,7 +84,7 @@ namespace osu.Game.Beatmaps [JsonIgnore] public string StoredBookmarks { - get { return string.Join(",", Bookmarks); } + get => string.Join(",", Bookmarks); set { if (string.IsNullOrEmpty(value)) @@ -93,8 +95,7 @@ namespace osu.Game.Beatmaps Bookmarks = value.Split(',').Select(v => { - int val; - bool result = int.TryParse(v, out val); + bool result = int.TryParse(v, out int val); return new { result, val }; }).Where(p => p.result).Select(p => p.val).ToArray(); } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 1c28b533d2..aa653d88f9 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -1,4 +1,4 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . +// Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; @@ -11,6 +11,7 @@ using Microsoft.EntityFrameworkCore; using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Extensions; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics.Textures; using osu.Framework.Logging; using osu.Framework.Platform; @@ -62,6 +63,8 @@ namespace osu.Game.Beatmaps public override string[] HandledExtensions => new[] { ".osz" }; + protected override string ImportFromStablePath => "Songs"; + private readonly RulesetStore rulesets; private readonly BeatmapStore beatmaps; @@ -72,11 +75,6 @@ namespace osu.Game.Beatmaps private readonly List currentDownloads = new List(); - /// - /// Set a storage with access to an osu-stable install for import purposes. - /// - public Func GetStableStorage { private get; set; } - public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, APIAccess api, AudioManager audioManager, IIpcHost importHost = null) : base(storage, contextFactory, new BeatmapStore(contextFactory), importHost) { @@ -103,6 +101,11 @@ namespace osu.Game.Beatmaps b.BeatmapSet = beatmapSet; } + validateOnlineIds(beatmapSet.Beatmaps); + + foreach (BeatmapInfo b in beatmapSet.Beatmaps) + fetchAndPopulateOnlineValues(b, beatmapSet.Beatmaps); + // check if a set already exists with the same online id, delete if it does. if (beatmapSet.OnlineBeatmapSetID != null) { @@ -114,11 +117,6 @@ namespace osu.Game.Beatmaps Logger.Log($"Found existing beatmap set with same OnlineBeatmapSetID ({beatmapSet.OnlineBeatmapSetID}). It has been purged.", LoggingTarget.Database); } } - - validateOnlineIds(beatmapSet.Beatmaps); - - foreach (BeatmapInfo b in beatmapSet.Beatmaps) - fetchAndPopulateOnlineIDs(b, beatmapSet.Beatmaps); } private void validateOnlineIds(List beatmaps) @@ -195,7 +193,7 @@ namespace osu.Game.Beatmaps downloadNotification.CompletionClickAction = () => { - PresentBeatmap?.Invoke(importedBeatmap); + PresentCompletedImport(importedBeatmap.Yield()); return true; }; downloadNotification.State = ProgressNotificationState.Completed; @@ -231,6 +229,12 @@ namespace osu.Game.Beatmaps BeatmapDownloadBegan?.Invoke(request); } + protected override void PresentCompletedImport(IEnumerable imported) + { + base.PresentCompletedImport(imported); + PresentBeatmap?.Invoke(imported.LastOrDefault()); + } + /// /// Get an existing download request if it exists. /// @@ -311,27 +315,6 @@ namespace osu.Game.Beatmaps /// Results from the provided query. public IQueryable QueryBeatmaps(Expression> query) => beatmaps.Beatmaps.AsNoTracking().Where(query); - /// - /// Denotes whether an osu-stable installation is present to perform automated imports from. - /// - public bool StableInstallationAvailable => GetStableStorage?.Invoke() != null; - - /// - /// This is a temporary method and will likely be replaced by a full-fledged (and more correctly placed) migration process in the future. - /// - public async Task ImportFromStable() - { - var stable = GetStableStorage?.Invoke(); - - if (stable == null) - { - Logger.Log("No osu!stable installation available!", LoggingTarget.Information, LogLevel.Error); - return; - } - - await Task.Factory.StartNew(() => Import(stable.GetDirectories("Songs").Select(f => stable.GetFullPath(f)).ToArray()), TaskCreationOptions.LongRunning); - } - /// /// Create a SHA-2 hash from the provided archive based on contained beatmap (.osu) file content. /// @@ -350,7 +333,11 @@ namespace osu.Game.Beatmaps { // let's make sure there are actually .osu files to import. string mapName = reader.Filenames.FirstOrDefault(f => f.EndsWith(".osu")); - if (string.IsNullOrEmpty(mapName)) throw new InvalidOperationException("No beatmap files found in this beatmap archive."); + if (string.IsNullOrEmpty(mapName)) + { + Logger.Log($"No beatmap files found in the beatmap archive ({reader.Name}).", LoggingTarget.Database); + return null; + } Beatmap beatmap; using (var stream = new StreamReader(reader.GetStream(mapName))) @@ -401,21 +388,22 @@ namespace osu.Game.Beatmaps } /// - /// Query the API to populate mising OnlineBeatmapID / OnlineBeatmapSetID properties. + /// Query the API to populate missing values like OnlineBeatmapID / OnlineBeatmapSetID or (Rank-)Status. /// /// The beatmap to populate. /// The other beatmaps contained within this set. /// Whether to re-query if the provided beatmap already has populated values. /// True if population was successful. - private bool fetchAndPopulateOnlineIDs(BeatmapInfo beatmap, IEnumerable otherBeatmaps, bool force = false) + private bool fetchAndPopulateOnlineValues(BeatmapInfo beatmap, IEnumerable otherBeatmaps, bool force = false) { if (api?.State != APIState.Online) return false; - if (!force && beatmap.OnlineBeatmapID != null && beatmap.BeatmapSet.OnlineBeatmapSetID != null) + if (!force && beatmap.OnlineBeatmapID != null && beatmap.BeatmapSet.OnlineBeatmapSetID != null + && beatmap.Status != BeatmapSetOnlineStatus.None && beatmap.BeatmapSet.Status != BeatmapSetOnlineStatus.None) return true; - Logger.Log("Attempting online lookup for IDs...", LoggingTarget.Database); + Logger.Log("Attempting online lookup for the missing values...", LoggingTarget.Database); try { @@ -427,6 +415,9 @@ namespace osu.Game.Beatmaps Logger.Log($"Successfully mapped to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.", LoggingTarget.Database); + beatmap.Status = res.Status; + beatmap.BeatmapSet.Status = res.BeatmapSet.Status; + if (otherBeatmaps.Any(b => b.OnlineBeatmapID == res.OnlineBeatmapID)) { Logger.Log("Another beatmap in the same set already mapped to this ID. We'll skip adding it this time.", LoggingTarget.Database); @@ -435,6 +426,7 @@ namespace osu.Game.Beatmaps beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID; beatmap.OnlineBeatmapID = res.OnlineBeatmapID; + return true; } catch (Exception e) diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index 43ae30f780..77ff53b893 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Framework.Logging; using osu.Game.Beatmaps.Formats; -using osu.Game.Graphics.Textures; using osu.Game.Skinning; using osu.Game.Storyboards; @@ -45,6 +44,10 @@ namespace osu.Game.Beatmaps private string getPathForFile(string filename) => BeatmapSetInfo.Files.First(f => string.Equals(f.Filename, filename, StringComparison.InvariantCultureIgnoreCase)).FileInfo.StoragePath; + private LargeTextureStore textureStore; + + protected override bool BackgroundStillValid(Texture b) => false; // bypass lazy logic. we want to return a new background each time for refcounting purposes. + protected override Texture GetBackground() { if (Metadata?.BackgroundFile == null) @@ -52,7 +55,7 @@ namespace osu.Game.Beatmaps try { - return new LargeTextureStore(new RawTextureLoaderStore(store)).Get(getPathForFile(Metadata.BackgroundFile)); + return (textureStore ?? (textureStore = new LargeTextureStore(new TextureLoaderStore(store)))).Get(getPathForFile(Metadata.BackgroundFile)); } catch { @@ -73,6 +76,14 @@ namespace osu.Game.Beatmaps } } + public override void TransferTo(WorkingBeatmap other) + { + base.TransferTo(other); + + if (other is BeatmapManagerWorkingBeatmap owb && textureStore != null && BeatmapInfo.BackgroundEquals(other.BeatmapInfo)) + owb.textureStore = textureStore; + } + protected override Waveform GetWaveform() { try diff --git a/osu.Game/Beatmaps/BeatmapProcessor.cs b/osu.Game/Beatmaps/BeatmapProcessor.cs index 9d7cd673dc..9db2c5f08e 100644 --- a/osu.Game/Beatmaps/BeatmapProcessor.cs +++ b/osu.Game/Beatmaps/BeatmapProcessor.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Linq; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Beatmaps @@ -44,6 +45,25 @@ namespace osu.Game.Beatmaps public virtual void PostProcess() { + void updateNestedCombo(HitObject obj, int comboIndex, int indexInCurrentCombo) + { + if (obj is IHasComboInformation objectComboInfo) + { + objectComboInfo.ComboIndex = comboIndex; + objectComboInfo.IndexInCurrentCombo = indexInCurrentCombo; + foreach (var nestedObject in obj.NestedHitObjects) + updateNestedCombo(nestedObject, comboIndex, indexInCurrentCombo); + } + } + + foreach (var hitObject in Beatmap.HitObjects) + { + if (hitObject is IHasComboInformation objectComboInfo) + { + foreach (var nested in hitObject.NestedHitObjects) + updateNestedCombo(nested, objectComboInfo.ComboIndex, objectComboInfo.IndexInCurrentCombo); + } + } } } } diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index ebebe42097..7a7d010a31 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -17,10 +17,12 @@ namespace osu.Game.Beatmaps public int? OnlineBeatmapSetID { - get { return onlineBeatmapSetID; } - set { onlineBeatmapSetID = value > 0 ? value : null; } + get => onlineBeatmapSetID; + set => onlineBeatmapSetID = value > 0 ? value : null; } + public BeatmapSetOnlineStatus Status { get; set; } = BeatmapSetOnlineStatus.None; + public BeatmapMetadata Metadata { get; set; } public List Beatmaps { get; set; } diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index dad69321a5..f064d53358 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -55,14 +55,14 @@ namespace osu.Game.Beatmaps.ControlPoints /// /// The time to find the sound control point at. /// The sound control point. - public SampleControlPoint SamplePointAt(double time) => binarySearch(SamplePoints, time, SamplePoints.FirstOrDefault()); + public SampleControlPoint SamplePointAt(double time) => binarySearch(SamplePoints, time, SamplePoints.Count > 0 ? SamplePoints[0] : null); /// /// Finds the timing control point that is active at . /// /// The time to find the timing control point at. /// The timing control point. - public TimingControlPoint TimingPointAt(double time) => binarySearch(TimingPoints, time, TimingPoints.FirstOrDefault()); + public TimingControlPoint TimingPointAt(double time) => binarySearch(TimingPoints, time, TimingPoints.Count > 0 ? TimingPoints[0] : null); /// /// Finds the maximum BPM represented by any timing control point. @@ -104,17 +104,26 @@ namespace osu.Game.Beatmaps.ControlPoints if (time < list[0].Time) return prePoint ?? new T(); - int index = list.BinarySearch(new T { Time = time }); + if (time >= list[list.Count - 1].Time) + return list[list.Count - 1]; - // Check if we've found an exact match (t == time) - if (index >= 0) - return list[index]; + int l = 0; + int r = list.Count - 2; - index = ~index; + while (l <= r) + { + int pivot = l + ((r - l) >> 1); - // BinarySearch will return the index of the first element _greater_ than the search - // This is the inactive point - the active point is the one before it (index - 1) - return list[index - 1]; + if (list[pivot].Time < time) + l = pivot + 1; + else if (list[pivot].Time > time) + r = pivot - 1; + else + return list[pivot]; + } + + // l will be the first control point with Time > time, but we want the one before it + return list[l - 1]; } } } diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs index eb60133fed..81eddaa43a 100644 --- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs @@ -16,7 +16,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// /// The beat length at this control point. /// - public double BeatLength + public virtual double BeatLength { get => beatLength; set => beatLength = MathHelper.Clamp(value, 6, 60000); diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs index c7e97cef55..da281b4db3 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -14,20 +13,35 @@ namespace osu.Game.Beatmaps.Drawables { private readonly OsuSpriteText statusText; - private BeatmapSetOnlineStatus status = BeatmapSetOnlineStatus.None; + private BeatmapSetOnlineStatus status; + public BeatmapSetOnlineStatus Status { - get { return status; } + get => status; set { - if (value == status) return; + if (status == value) + return; status = value; - statusText.Text = Enum.GetName(typeof(BeatmapSetOnlineStatus), Status)?.ToUpperInvariant(); + Alpha = value == BeatmapSetOnlineStatus.None ? 0 : 1; + statusText.Text = value.ToString().ToUpperInvariant(); } } - public BeatmapSetOnlineStatusPill(float textSize, MarginPadding textPadding) + public float TextSize + { + get => statusText.TextSize; + set => statusText.TextSize = value; + } + + public MarginPadding TextPadding + { + get => statusText.Padding; + set => statusText.Padding = value; + } + + public BeatmapSetOnlineStatusPill() { AutoSizeAxes = Axes.Both; Masking = true; @@ -45,10 +59,10 @@ namespace osu.Game.Beatmaps.Drawables Anchor = Anchor.Centre, Origin = Anchor.Centre, Font = @"Exo2.0-Bold", - TextSize = textSize, - Padding = textPadding, }, }; + + Status = BeatmapSetOnlineStatus.None; } } } diff --git a/osu.Game/Beatmaps/Formats/Decoder.cs b/osu.Game/Beatmaps/Formats/Decoder.cs index 2927654f62..6f45718390 100644 --- a/osu.Game/Beatmaps/Formats/Decoder.cs +++ b/osu.Game/Beatmaps/Formats/Decoder.cs @@ -55,11 +55,11 @@ namespace osu.Game.Beatmaps.Formats } while (line != null && line.Length == 0); if (line == null) - throw new IOException(@"Unknown file format"); + throw new IOException(@"Unknown file format (null)"); - var decoder = typedDecoders.Select(d => line.StartsWith(d.Key) ? d.Value : null).FirstOrDefault(); + var decoder = typedDecoders.Select(d => line.StartsWith(d.Key, StringComparison.InvariantCulture) ? d.Value : null).FirstOrDefault(); if (decoder == null) - throw new IOException(@"Unknown file format"); + throw new IOException($@"Unknown file format ({line})"); return (Decoder)decoder.Invoke(line); } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 31a7698f50..71b7a65ccb 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -5,6 +5,7 @@ using System; using System.Globalization; using System.IO; using System.Linq; +using osu.Framework.IO.File; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Beatmaps.ControlPoints; @@ -58,7 +59,7 @@ namespace osu.Game.Beatmaps.Formats hitObject.ApplyDefaults(this.beatmap.ControlPointInfo, this.beatmap.BeatmapInfo.BaseDifficulty); } - protected override bool ShouldSkipLine(string line) => base.ShouldSkipLine(line) || line.StartsWith(" ") || line.StartsWith("_"); + protected override bool ShouldSkipLine(string line) => base.ShouldSkipLine(line) || line.StartsWith(" ", StringComparison.Ordinal) || line.StartsWith("_", StringComparison.Ordinal); protected override void ParseLine(Beatmap beatmap, Section section, string line) { @@ -100,7 +101,7 @@ namespace osu.Game.Beatmaps.Formats switch (pair.Key) { case @"AudioFilename": - metadata.AudioFile = pair.Value; + metadata.AudioFile = FileSafety.PathStandardise(pair.Value); break; case @"AudioLeadIn": beatmap.BeatmapInfo.AudioLeadIn = int.Parse(pair.Value); @@ -256,7 +257,7 @@ namespace osu.Game.Beatmaps.Formats { case EventType.Background: string filename = split[2].Trim('"'); - beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; + beatmap.BeatmapInfo.Metadata.BackgroundFile = FileSafety.PathStandardise(filename); break; case EventType.Break: var breakEvent = new BreakPeriod @@ -318,12 +319,12 @@ namespace osu.Game.Beatmaps.Formats if (timingChange) { - handleTimingControlPoint(new TimingControlPoint - { - Time = time, - BeatLength = beatLength, - TimeSignature = timeSignature - }); + var controlPoint = CreateTimingControlPoint(); + controlPoint.Time = time; + controlPoint.BeatLength = beatLength; + controlPoint.TimeSignature = timeSignature; + + handleTimingControlPoint(controlPoint); } handleDifficultyControlPoint(new DifficultyControlPoint @@ -408,11 +409,8 @@ namespace osu.Game.Beatmaps.Formats parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(getOffsetTime(), FormatVersion); var obj = parser.Parse(line); - if (obj != null) - { beatmap.HitObjects.Add(obj); - } } private int getOffsetTime(int time) => time + (ApplyOffsets ? offset : 0); @@ -421,6 +419,8 @@ namespace osu.Game.Beatmaps.Formats private double getOffsetTime(double time) => time + (ApplyOffsets ? offset : 0); + protected virtual TimingControlPoint CreateTimingControlPoint() => new TimingControlPoint(); + [Flags] internal enum EffectFlags { diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index e9f37e583b..2ef7c5de30 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -69,7 +69,7 @@ namespace osu.Game.Beatmaps.Formats protected string StripComments(string line) { - var index = line.IndexOf("//", StringComparison.Ordinal); + var index = line.AsSpan().IndexOf("//".AsSpan()); if (index > 0) return line.Substring(0, index); return line; @@ -85,13 +85,19 @@ namespace osu.Game.Beatmaps.Formats string[] split = pair.Value.Split(','); - if (split.Length != 3) - throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {pair.Value}"); + if (split.Length != 3 && split.Length != 4) + throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B or R,G,B,A): {pair.Value}"); - if (!byte.TryParse(split[0], out var r) || !byte.TryParse(split[1], out var g) || !byte.TryParse(split[2], out var b)) + Color4 colour; + + try + { + colour = new Color4(byte.Parse(split[0]), byte.Parse(split[1]), byte.Parse(split[2]), split.Length == 4 ? byte.Parse(split[3]) : (byte)255); + } + catch (Exception e) + { throw new InvalidOperationException(@"Color must be specified with 8-bit integer components"); - - Color4 colour = new Color4(r, g, b, 255); + } if (isCombo) { diff --git a/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs new file mode 100644 index 0000000000..13a71aac3d --- /dev/null +++ b/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs @@ -0,0 +1,37 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Game.Beatmaps.ControlPoints; + +namespace osu.Game.Beatmaps.Formats +{ + /// + /// A built for difficulty calculation of legacy s + /// + /// To use this, the decoder must be registered by the application through . + /// Doing so will override any existing decoders. + /// + /// + public class LegacyDifficultyCalculatorBeatmapDecoder : LegacyBeatmapDecoder + { + public LegacyDifficultyCalculatorBeatmapDecoder(int version = LATEST_VERSION) + : base(version) + { + ApplyOffsets = false; + } + + public new static void Register() + { + AddDecoder(@"osu file format v", m => new LegacyDifficultyCalculatorBeatmapDecoder(int.Parse(m.Split('v').Last()))); + } + + protected override TimingControlPoint CreateTimingControlPoint() + => new LegacyDifficultyCalculatorControlPoint(); + + private class LegacyDifficultyCalculatorControlPoint : TimingControlPoint + { + public override double BeatLength { get; set; } = 1000; + } + } +} diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 1063dfc923..375c0b29c0 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -269,9 +269,9 @@ namespace osu.Game.Beatmaps.Formats return Anchor.BottomCentre; case LegacyOrigins.BottomRight: return Anchor.BottomRight; + default: + return Anchor.TopLeft; } - - throw new InvalidDataException($@"Unknown origin: {value}"); } private void handleVariables(string line) @@ -298,6 +298,6 @@ namespace osu.Game.Beatmaps.Formats } } - private string cleanFilename(string path) => FileSafety.PathStandardise(FileSafety.PathSanitise(path.Trim('\"'))); + private string cleanFilename(string path) => FileSafety.PathStandardise(path.Trim('"')); } } diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs index fe20bce98a..3d8b94dc46 100644 --- a/osu.Game/Beatmaps/IBeatmap.cs +++ b/osu.Game/Beatmaps/IBeatmap.cs @@ -39,7 +39,7 @@ namespace osu.Game.Beatmaps /// /// The hitobjects contained by this beatmap. /// - IEnumerable HitObjects { get; } + IReadOnlyList HitObjects { get; } /// /// Returns statistics for the contained in this beatmap. diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 6c906bb1e4..e0a22460ef 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -8,10 +8,10 @@ using osu.Game.Rulesets.Mods; using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using osu.Game.Storyboards; using osu.Framework.IO.File; using System.IO; +using System.Threading; using osu.Game.IO.Serialization; using osu.Game.Rulesets; using osu.Game.Rulesets.Objects; @@ -38,12 +38,26 @@ namespace osu.Game.Beatmaps Mods.ValueChanged += mods => applyRateAdjustments(); - beatmap = new AsyncLazy(populateBeatmap); - background = new AsyncLazy(populateBackground, b => b == null || !b.IsDisposed); - track = new AsyncLazy(populateTrack); - waveform = new AsyncLazy(populateWaveform); - storyboard = new AsyncLazy(populateStoryboard); - skin = new AsyncLazy(populateSkin); + beatmap = new RecyclableLazy(() => + { + var b = GetBeatmap() ?? new Beatmap(); + // use the database-backed info. + b.BeatmapInfo = BeatmapInfo; + return b; + }); + + track = new RecyclableLazy(() => + { + // we want to ensure that we always have a track, even if it's a fake one. + var t = GetTrack() ?? new VirtualBeatmapTrack(Beatmap); + applyRateAdjustments(t); + return t; + }); + + background = new RecyclableLazy(GetBackground, BackgroundStillValid); + waveform = new RecyclableLazy(GetWaveform); + storyboard = new RecyclableLazy(GetStoryboard); + skin = new RecyclableLazy(GetSkin); } /// @@ -58,28 +72,6 @@ namespace osu.Game.Beatmaps return path; } - protected abstract IBeatmap GetBeatmap(); - protected abstract Texture GetBackground(); - protected abstract Track GetTrack(); - protected virtual Skin GetSkin() => new DefaultSkin(); - protected virtual Waveform GetWaveform() => new Waveform(); - protected virtual Storyboard GetStoryboard() => new Storyboard { BeatmapInfo = BeatmapInfo }; - - public bool BeatmapLoaded => beatmap.IsResultAvailable; - public IBeatmap Beatmap => beatmap.Value.Result; - public async Task GetBeatmapAsync() => await beatmap.Value; - private readonly AsyncLazy beatmap; - - private IBeatmap populateBeatmap() - { - var b = GetBeatmap() ?? new Beatmap(); - - // use the database-backed info. - b.BeatmapInfo = BeatmapInfo; - - return b; - } - /// /// Constructs a playable from using the applicable converters for a specific . /// @@ -136,62 +128,53 @@ namespace osu.Game.Beatmaps public override string ToString() => BeatmapInfo.ToString(); - public bool BackgroundLoaded => background.IsResultAvailable; - public Texture Background => background.Value.Result; - public async Task GetBackgroundAsync() => await background.Value; - private AsyncLazy background; + public bool BeatmapLoaded => beatmap.IsResultAvailable; + public IBeatmap Beatmap => beatmap.Value; + protected abstract IBeatmap GetBeatmap(); + private readonly RecyclableLazy beatmap; - private Texture populateBackground() => GetBackground(); + public bool BackgroundLoaded => background.IsResultAvailable; + public Texture Background => background.Value; + protected virtual bool BackgroundStillValid(Texture b) => b == null || b.Available; + protected abstract Texture GetBackground(); + private readonly RecyclableLazy background; public bool TrackLoaded => track.IsResultAvailable; - public Track Track => track.Value.Result; - public async Task GetTrackAsync() => await track.Value; - private AsyncLazy track; - - private Track populateTrack() - { - // we want to ensure that we always have a track, even if it's a fake one. - var t = GetTrack() ?? new VirtualBeatmapTrack(Beatmap); - applyRateAdjustments(t); - return t; - } + public Track Track => track.Value; + protected abstract Track GetTrack(); + private RecyclableLazy track; public bool WaveformLoaded => waveform.IsResultAvailable; - public Waveform Waveform => waveform.Value.Result; - public async Task GetWaveformAsync() => await waveform.Value; - private readonly AsyncLazy waveform; - - private Waveform populateWaveform() => GetWaveform(); + public Waveform Waveform => waveform.Value; + protected virtual Waveform GetWaveform() => new Waveform(); + private readonly RecyclableLazy waveform; public bool StoryboardLoaded => storyboard.IsResultAvailable; - public Storyboard Storyboard => storyboard.Value.Result; - public async Task GetStoryboardAsync() => await storyboard.Value; - private readonly AsyncLazy storyboard; - - private Storyboard populateStoryboard() => GetStoryboard(); + public Storyboard Storyboard => storyboard.Value; + protected virtual Storyboard GetStoryboard() => new Storyboard { BeatmapInfo = BeatmapInfo }; + private readonly RecyclableLazy storyboard; public bool SkinLoaded => skin.IsResultAvailable; - public Skin Skin => skin.Value.Result; - public async Task GetSkinAsync() => await skin.Value; - private readonly AsyncLazy skin; + public Skin Skin => skin.Value; + protected virtual Skin GetSkin() => new DefaultSkin(); + private readonly RecyclableLazy skin; - private Skin populateSkin() => GetSkin(); - - public void TransferTo(WorkingBeatmap other) + /// + /// Transfer pieces of a beatmap to a new one, where possible, to save on loading. + /// + /// The new beatmap which is being switched to. + public virtual void TransferTo(WorkingBeatmap other) { if (track.IsResultAvailable && Track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo)) other.track = track; - - if (background.IsResultAvailable && Background != null && BeatmapInfo.BackgroundEquals(other.BeatmapInfo)) - other.background = background; } public virtual void Dispose() { - if (BackgroundLoaded) Background?.Dispose(); - if (WaveformLoaded) Waveform?.Dispose(); - if (StoryboardLoaded) Storyboard?.Dispose(); - if (SkinLoaded) Skin?.Dispose(); + background.Recycle(); + waveform.Recycle(); + storyboard.Recycle(); + skin.Recycle(); } /// @@ -210,15 +193,15 @@ namespace osu.Game.Beatmaps mod.ApplyToClock(t); } - public class AsyncLazy + public class RecyclableLazy { - private Lazy> lazy; + private Lazy lazy; private readonly Func valueFactory; private readonly Func stillValidFunction; - private readonly object initLock = new object(); + private readonly object fetchLock = new object(); - public AsyncLazy(Func valueFactory, Func stillValidFunction = null) + public RecyclableLazy(Func valueFactory, Func stillValidFunction = null) { this.valueFactory = valueFactory; this.stillValidFunction = stillValidFunction; @@ -230,45 +213,28 @@ namespace osu.Game.Beatmaps { if (!IsResultAvailable) return; - (lazy.Value.Result as IDisposable)?.Dispose(); + (lazy.Value as IDisposable)?.Dispose(); recreate(); } - public bool IsResultAvailable + public bool IsResultAvailable => stillValid; + + public T Value { get { - recreateIfInvalid(); - return lazy.Value.IsCompleted; + lock (fetchLock) + { + if (!stillValid) + recreate(); + return lazy.Value; + } } } - public Task Value - { - get - { - recreateIfInvalid(); - return lazy.Value; - } - } + private bool stillValid => lazy.IsValueCreated && (stillValidFunction?.Invoke(lazy.Value) ?? true); - private void recreateIfInvalid() - { - lock (initLock) - { - if (!lazy.IsValueCreated || !lazy.Value.IsCompleted) - // we have not yet been initialised or haven't run the task. - return; - - if (stillValidFunction?.Invoke(lazy.Value.Result) ?? true) - // we are still in a valid state. - return; - - recreate(); - } - } - - private void recreate() => lazy = new Lazy>(() => Task.Run(valueFactory)); + private void recreate() => lazy = new Lazy(valueFactory, LazyThreadSafetyMode.ExecutionAndPublication); } } } diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index f7fe424aa9..9ac2cabe9f 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -83,8 +83,6 @@ namespace osu.Game.Configuration Set(OsuSetting.ScoreDisplayMode, ScoringMode.Standardised); - Set(OsuSetting.SpeedChangeVisualisation, SpeedChangeVisualisationMethod.Sequential); - Set(OsuSetting.IncreaseFirstObjectVisibility, true); // Update @@ -143,7 +141,6 @@ namespace osu.Game.Configuration ChatDisplayHeight, Version, ShowConvertedBeatmaps, - SpeedChangeVisualisation, Skin, ScreenshotFormat, ScreenshotCaptureMenuCursor, diff --git a/osu.Game/Configuration/SpeedChangeVisualisationMethod.cs b/osu.Game/Configuration/SpeedChangeVisualisationMethod.cs index d38b1a89c5..39c6e5649c 100644 --- a/osu.Game/Configuration/SpeedChangeVisualisationMethod.cs +++ b/osu.Game/Configuration/SpeedChangeVisualisationMethod.cs @@ -10,6 +10,8 @@ namespace osu.Game.Configuration [Description("Sequential")] Sequential, [Description("Overlapping")] - Overlapping + Overlapping, + [Description("Constant")] + Constant } } diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index ac79a8f565..3686b702c4 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; using osu.Framework.IO.File; @@ -58,7 +59,7 @@ namespace osu.Game.Database // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) private ArchiveImportIPCChannel ipc; - private readonly List cachedEvents = new List(); + private readonly List queuedEvents = new List(); /// /// Allows delaying of outwards events until an operation is confirmed (at a database level). @@ -76,20 +77,27 @@ namespace osu.Game.Database /// Whether the flushed events should be performed. private void flushEvents(bool perform) { + Action[] events; + lock (queuedEvents) + { + events = queuedEvents.ToArray(); + queuedEvents.Clear(); + } + if (perform) { - foreach (var a in cachedEvents) + foreach (var a in events) a.Invoke(); } - cachedEvents.Clear(); delayingEvents = false; } private void handleEvent(Action a) { if (delayingEvents) - cachedEvents.Add(a); + lock (queuedEvents) + queuedEvents.Add(a); else a.Invoke(); } @@ -129,7 +137,6 @@ namespace osu.Game.Database List imported = new List(); int current = 0; - int errors = 0; foreach (string path in paths) { if (notification.State == ProgressNotificationState.Cancelled) @@ -162,12 +169,29 @@ namespace osu.Game.Database { e = e.InnerException ?? e; Logger.Error(e, $@"Could not import ({Path.GetFileName(path)})"); - errors++; } } - notification.Text = errors > 0 ? $"Import complete with {errors} errors" : "Import successful!"; - notification.State = ProgressNotificationState.Completed; + if (imported.Count == 0) + { + notification.Text = "Import failed!"; + notification.State = ProgressNotificationState.Cancelled; + } + else + { + notification.CompletionText = $"Imported {current} {typeof(TModel).Name.Replace("Info", "").ToLower()}s!"; + notification.CompletionClickAction += () => + { + if (imported.Count > 0) + PresentCompletedImport(imported); + return true; + }; + notification.State = ProgressNotificationState.Completed; + } + } + + protected virtual void PresentCompletedImport(IEnumerable imported) + { } /// @@ -178,7 +202,8 @@ namespace osu.Game.Database { try { - return Import(CreateModel(archive), archive); + var model = CreateModel(archive); + return model == null ? null : Import(model, archive); } catch (Exception e) { @@ -257,17 +282,19 @@ namespace osu.Game.Database /// Is a no-op for already deleted items. /// /// The item to delete. - public void Delete(TModel item) + /// false if no operation was performed + public bool Delete(TModel item) { using (ContextFactory.GetForWrite()) { // re-fetch the model on the import context. - var foundModel = queryModel().Include(s => s.Files).ThenInclude(f => f.FileInfo).First(s => s.ID == item.ID); + var foundModel = queryModel().Include(s => s.Files).ThenInclude(f => f.FileInfo).FirstOrDefault(s => s.ID == item.ID); - if (foundModel.DeletePending) return; + if (foundModel == null || foundModel.DeletePending) return false; if (ModelStore.Delete(foundModel)) Files.Dereference(foundModel.Files.Select(f => f.FileInfo).ToArray()); + return true; } } @@ -282,7 +309,7 @@ namespace osu.Game.Database var notification = new ProgressNotification { Progress = 0, - CompletionText = "Deleted all beatmaps!", + CompletionText = $"Deleted all {typeof(TModel).Name.Replace("Info", "").ToLower()}s!", State = ProgressNotificationState.Active, }; @@ -377,19 +404,61 @@ namespace osu.Game.Database using (Stream s = reader.GetStream(file)) fileInfos.Add(new TFileModel { - Filename = FileSafety.PathSanitise(file), + Filename = FileSafety.PathStandardise(file), FileInfo = files.Add(s) }); return fileInfos; } + #region osu-stable import + + /// + /// Set a storage with access to an osu-stable install for import purposes. + /// + public Func GetStableStorage { private get; set; } + + /// + /// Denotes whether an osu-stable installation is present to perform automated imports from. + /// + public bool StableInstallationAvailable => GetStableStorage?.Invoke() != null; + + /// + /// The relative path from osu-stable's data directory to import items from. + /// + protected virtual string ImportFromStablePath => null; + + /// + /// This is a temporary method and will likely be replaced by a full-fledged (and more correctly placed) migration process in the future. + /// + public Task ImportFromStableAsync() + { + var stable = GetStableStorage?.Invoke(); + + if (stable == null) + { + Logger.Log("No osu!stable installation available!", LoggingTarget.Information, LogLevel.Error); + return Task.CompletedTask; + } + + if (!stable.ExistsDirectory(ImportFromStablePath)) + { + // This handles situations like when the user does not have a Skins folder + Logger.Log($"No {ImportFromStablePath} folder available in osu!stable installation", LoggingTarget.Information, LogLevel.Error); + return Task.CompletedTask; + } + + return Task.Factory.StartNew(() => Import(stable.GetDirectories(ImportFromStablePath).Select(f => stable.GetFullPath(f)).ToArray()), TaskCreationOptions.LongRunning); + } + + #endregion + /// /// Create a barebones model from the provided archive. /// Actual expensive population should be done in ; this should just prepare for duplicate checking. /// /// The archive to create the model for. - /// A model populated with minimal information. + /// A model populated with minimal information. Returning a null will abort importing silently. protected abstract TModel CreateModel(ArchiveReader archive); /// diff --git a/osu.Game/Database/DatabaseContextFactory.cs b/osu.Game/Database/DatabaseContextFactory.cs index e70d753114..2037612a09 100644 --- a/osu.Game/Database/DatabaseContextFactory.cs +++ b/osu.Game/Database/DatabaseContextFactory.cs @@ -5,7 +5,6 @@ using System; using System.Linq; using System.Threading; using Microsoft.EntityFrameworkCore.Storage; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Platform; namespace osu.Game.Database @@ -118,7 +117,9 @@ namespace osu.Game.Database private void recycleThreadContexts() { - threadContexts?.Values.ForEach(c => c.Dispose()); + // Contexts for other threads are not disposed as they may be in use elsewhere. Instead, fresh contexts are exposed + // for other threads to use, and we rely on the finalizer inside OsuDbContext to handle their previous contexts + threadContexts?.Value.Dispose(); threadContexts = new ThreadLocal(CreateContext, true); } diff --git a/osu.Game/Database/OsuDbContext.cs b/osu.Game/Database/OsuDbContext.cs index bf57644caf..20e144c033 100644 --- a/osu.Game/Database/OsuDbContext.cs +++ b/osu.Game/Database/OsuDbContext.cs @@ -75,6 +75,13 @@ namespace osu.Game.Database } } + ~OsuDbContext() + { + // DbContext does not contain a finalizer (https://github.com/aspnet/EntityFrameworkCore/issues/8872) + // This is used to clean up previous contexts when fresh contexts are exposed via DatabaseContextFactory + Dispose(); + } + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { base.OnConfiguring(optionsBuilder); diff --git a/osu.Game/Graphics/Backgrounds/Background.cs b/osu.Game/Graphics/Backgrounds/Background.cs index d5825a8c42..d3d530a540 100644 --- a/osu.Game/Graphics/Backgrounds/Background.cs +++ b/osu.Game/Graphics/Backgrounds/Background.cs @@ -5,8 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using OpenTK.Graphics; -using osu.Game.Graphics.Textures; +using osu.Framework.Graphics.Textures; namespace osu.Game.Graphics.Backgrounds { @@ -28,7 +27,6 @@ namespace osu.Game.Graphics.Backgrounds RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Colour = Color4.DarkGray, FillMode = FillMode.Fill, }); } diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 01b09c0a40..4a86d0e4f6 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -30,10 +30,6 @@ namespace osu.Game.Graphics.Backgrounds /// private const float edge_smoothness = 1; - public override bool HandleKeyboardInput => false; - public override bool HandleMouseInput => false; - - public Color4 ColourLight = Color4.White; public Color4 ColourDark = Color4.Black; @@ -116,7 +112,7 @@ namespace osu.Game.Graphics.Backgrounds float adjustedAlpha = HideAlphaDiscrepancies ? // Cubically scale alpha to make it drop off more sharply. - (float)Math.Pow(DrawInfo.Colour.AverageColour.Linear.A, 3) : + (float)Math.Pow(DrawColourInfo.Colour.AverageColour.Linear.A, 3) : 1; float elapsedSeconds = (float)Time.Elapsed / 1000; @@ -235,7 +231,7 @@ namespace osu.Game.Graphics.Backgrounds Vector2Extensions.Transform(particle.Position * Size + new Vector2(-offset.X, offset.Y), DrawInfo.Matrix) ); - ColourInfo colourInfo = DrawInfo.Colour; + ColourInfo colourInfo = DrawColourInfo.Colour; colourInfo.ApplyChild(particle.Colour); Texture.DrawTriangle( diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 9c5da71aff..e4e7828d0e 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -20,8 +20,6 @@ namespace osu.Game.Graphics.Containers { } - public override bool HandleMouseInput => true; - private OsuGame game; private Action showNotImplementedError; diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index d2ab8441eb..2aa3fede2c 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics.Containers; using OpenTK; using osu.Framework.Configuration; using osu.Framework.Input.Bindings; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Audio; using osu.Game.Input.Bindings; using osu.Game.Overlays; @@ -22,7 +22,7 @@ namespace osu.Game.Graphics.Containers protected virtual bool PlaySamplesOnStateChange => true; - protected override bool BlockPassThroughKeyboard => true; + protected override bool BlockNonPositionalInput => true; private PreviewTrackManager previewTrackManager; @@ -54,20 +54,20 @@ namespace osu.Game.Graphics.Containers /// Whether mouse input should be blocked screen-wide while this overlay is visible. /// Performing mouse actions outside of the valid extents will hide the overlay. /// - public virtual bool BlockScreenWideMouse => BlockPassThroughMouse; + public virtual bool BlockScreenWideMouse => BlockPositionalInput; // receive input outside our bounds so we can trigger a close event on ourselves. - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => BlockScreenWideMouse || base.ReceiveMouseInputAt(screenSpacePos); + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => BlockScreenWideMouse || base.ReceivePositionalInputAt(screenSpacePos); - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { - if (!base.ReceiveMouseInputAt(state.Mouse.NativeState.Position)) + if (!base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition)) { State = Visibility.Hidden; return true; } - return base.OnClick(state); + return base.OnClick(e); } public virtual bool OnPressed(GlobalAction action) diff --git a/osu.Game/Graphics/Containers/OsuHoverContainer.cs b/osu.Game/Graphics/Containers/OsuHoverContainer.cs index 12df19e7c0..577d889be3 100644 --- a/osu.Game/Graphics/Containers/OsuHoverContainer.cs +++ b/osu.Game/Graphics/Containers/OsuHoverContainer.cs @@ -6,7 +6,7 @@ using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; namespace osu.Game.Graphics.Containers { @@ -18,16 +18,16 @@ namespace osu.Game.Graphics.Containers protected virtual IEnumerable EffectTargets => new[] { Content }; - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { EffectTargets.ForEach(d => d.FadeColour(HoverColour, 500, Easing.OutQuint)); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { EffectTargets.ForEach(d => d.FadeColour(IdleColour, 500, Easing.OutQuint)); - base.OnHoverLost(state); + base.OnHoverLost(e); } [BackgroundDependencyLoader] diff --git a/osu.Game/Graphics/Containers/OsuScrollContainer.cs b/osu.Game/Graphics/Containers/OsuScrollContainer.cs index 6d42be6fca..4f18eb9e7b 100644 --- a/osu.Game/Graphics/Containers/OsuScrollContainer.cs +++ b/osu.Game/Graphics/Containers/OsuScrollContainer.cs @@ -2,8 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Graphics.Containers; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using OpenTK.Input; namespace osu.Game.Graphics.Containers @@ -21,7 +20,7 @@ namespace osu.Game.Graphics.Containers /// public double DistanceDecayOnRightMouseScrollbar = 0.02; - private bool shouldPerformRightMouseScroll(InputState state) => RightMouseScrollbar && state.Mouse.IsPressed(MouseButton.Right); + private bool shouldPerformRightMouseScroll(MouseButtonEvent e) => RightMouseScrollbar && e.Button == MouseButton.Right; private void scrollToRelative(float value) => ScrollTo(Clamp((value - Scrollbar.DrawSize[ScrollDim] / 2) / Scrollbar.Size[ScrollDim]), true, DistanceDecayOnRightMouseScrollbar); @@ -29,40 +28,40 @@ namespace osu.Game.Graphics.Containers protected override bool IsDragging => base.IsDragging || mouseScrollBarDragging; - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(MouseDownEvent e) { - if (shouldPerformRightMouseScroll(state)) + if (shouldPerformRightMouseScroll(e)) { - scrollToRelative(state.Mouse.Position[ScrollDim]); + scrollToRelative(e.MousePosition[ScrollDim]); return true; } - return base.OnMouseDown(state, args); + return base.OnMouseDown(e); } - protected override bool OnDrag(InputState state) + protected override bool OnDrag(DragEvent e) { if (mouseScrollBarDragging) { - scrollToRelative(state.Mouse.Position[ScrollDim]); + scrollToRelative(e.MousePosition[ScrollDim]); return true; } - return base.OnDrag(state); + return base.OnDrag(e); } - protected override bool OnDragStart(InputState state) + protected override bool OnDragStart(DragStartEvent e) { - if (shouldPerformRightMouseScroll(state)) + if (shouldPerformRightMouseScroll(e)) { mouseScrollBarDragging = true; return true; } - return base.OnDragStart(state); + return base.OnDragStart(e); } - protected override bool OnDragEnd(InputState state) + protected override bool OnDragEnd(DragEndEvent e) { if (mouseScrollBarDragging) { @@ -70,7 +69,7 @@ namespace osu.Game.Graphics.Containers return true; } - return base.OnDragEnd(state); + return base.OnDragEnd(e); } } } diff --git a/osu.Game/Graphics/Containers/ParallaxContainer.cs b/osu.Game/Graphics/Containers/ParallaxContainer.cs index 8e1e5d54fa..a6b79a20dc 100644 --- a/osu.Game/Graphics/Containers/ParallaxContainer.cs +++ b/osu.Game/Graphics/Containers/ParallaxContainer.cs @@ -67,7 +67,7 @@ namespace osu.Game.Graphics.Containers if (parallaxEnabled) { - Vector2 offset = (input.CurrentState.Mouse == null ? Vector2.Zero : ToLocalSpace(input.CurrentState.Mouse.NativeState.Position) - DrawSize / 2) * ParallaxAmount; + Vector2 offset = (input.CurrentState.Mouse == null ? Vector2.Zero : ToLocalSpace(input.CurrentState.Mouse.Position) - DrawSize / 2) * ParallaxAmount; double elapsed = MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 1000); diff --git a/osu.Game/Graphics/Containers/ShakeContainer.cs b/osu.Game/Graphics/Containers/ShakeContainer.cs new file mode 100644 index 0000000000..fde4d59f46 --- /dev/null +++ b/osu.Game/Graphics/Containers/ShakeContainer.cs @@ -0,0 +1,39 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Graphics.Containers +{ + /// + /// A container that adds the ability to shake its contents. + /// + public class ShakeContainer : Container + { + /// + /// Shake the contents of this container. + /// + /// The maximum length the shake should last. + public void Shake(double maximumLength) + { + const float shake_amount = 8; + const float shake_duration = 30; + + // if we don't have enough time, don't bother shaking. + if (maximumLength < shake_duration * 2) + return; + + var sequence = this.MoveToX(shake_amount, shake_duration / 2, Easing.OutSine).Then() + .MoveToX(-shake_amount, shake_duration, Easing.InOutSine).Then(); + + // if we don't have enough time for the second shake, skip it. + if (maximumLength > shake_duration * 4) + sequence = sequence + .MoveToX(shake_amount, shake_duration, Easing.InOutSine).Then() + .MoveToX(-shake_amount, shake_duration, Easing.InOutSine).Then(); + + sequence.MoveToX(0, shake_duration / 2, Easing.InSine); + } + } +} diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs index b55e1aa5dd..ba858bf52d 100644 --- a/osu.Game/Graphics/Cursor/MenuCursor.cs +++ b/osu.Game/Graphics/Cursor/MenuCursor.cs @@ -12,8 +12,7 @@ using osu.Game.Configuration; using System; using JetBrains.Annotations; using osu.Framework.Graphics.Textures; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using OpenTK.Input; namespace osu.Game.Graphics.Cursor @@ -40,11 +39,11 @@ namespace osu.Game.Graphics.Cursor screenshotCursorVisibility.BindTo(screenshotManager.CursorVisibility); } - protected override bool OnMouseMove(InputState state) + protected override bool OnMouseMove(MouseMoveEvent e) { if (dragRotationState != DragRotationState.NotDragging) { - var position = state.Mouse.Position; + var position = e.MousePosition; var distance = Vector2Extensions.Distance(position, positionMouseDown); // don't start rotating until we're moved a minimum distance away from the mouse down location, // else it can have an annoying effect. @@ -53,7 +52,7 @@ namespace osu.Game.Graphics.Cursor // don't rotate when distance is zero to avoid NaN if (dragRotationState == DragRotationState.Rotating && distance > 0) { - Vector2 offset = state.Mouse.Position - positionMouseDown; + Vector2 offset = e.MousePosition - positionMouseDown; float degrees = (float)MathHelper.RadiansToDegrees(Math.Atan2(-offset.X, offset.Y)) + 24.3f; // Always rotate in the direction of least distance @@ -66,13 +65,13 @@ namespace osu.Game.Graphics.Cursor } } - return base.OnMouseMove(state); + return base.OnMouseMove(e); } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(MouseDownEvent e) { // only trigger animation for main mouse buttons - if (args.Button <= MouseButton.Right) + if (e.Button <= MouseButton.Right) { activeCursor.Scale = new Vector2(1); activeCursor.ScaleTo(0.90f, 800, Easing.OutQuint); @@ -81,29 +80,29 @@ namespace osu.Game.Graphics.Cursor activeCursor.AdditiveLayer.FadeInFromZero(800, Easing.OutQuint); } - if (args.Button == MouseButton.Left && cursorRotate) + if (e.Button == MouseButton.Left && cursorRotate) { dragRotationState = DragRotationState.DragStarted; - positionMouseDown = state.Mouse.Position; + positionMouseDown = e.MousePosition; } - return base.OnMouseDown(state, args); + return base.OnMouseDown(e); } - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + protected override bool OnMouseUp(MouseUpEvent e) { - if (!state.Mouse.HasMainButtonPressed) + if (!e.IsPressed(MouseButton.Left) && !e.IsPressed(MouseButton.Right)) { activeCursor.AdditiveLayer.FadeOutFromOne(500, Easing.OutQuint); activeCursor.ScaleTo(1, 500, Easing.OutElastic); } - if (args.Button == MouseButton.Left) + if (e.Button == MouseButton.Left) { if (dragRotationState == DragRotationState.Rotating) activeCursor.RotateTo(0, 600 * (1 + Math.Abs(activeCursor.Rotation / 720)), Easing.OutElasticHalf); dragRotationState = DragRotationState.NotDragging; } - return base.OnMouseUp(state, args); + return base.OnMouseUp(e); } protected override void PopIn() diff --git a/osu.Game/Graphics/DrawableDate.cs b/osu.Game/Graphics/DrawableDate.cs index 406fc2ffd2..28f8bdf82f 100644 --- a/osu.Game/Graphics/DrawableDate.cs +++ b/osu.Game/Graphics/DrawableDate.cs @@ -4,7 +4,6 @@ using System; using Humanizer; using osu.Framework.Allocation; -using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; using osu.Game.Graphics.Sprites; @@ -16,7 +15,6 @@ namespace osu.Game.Graphics public DrawableDate(DateTimeOffset date) { - AutoSizeAxes = Axes.Both; Font = "Exo2.0-RegularItalic"; Date = date.ToLocalTime(); @@ -56,8 +54,6 @@ namespace osu.Game.Graphics Scheduler.AddDelayed(updateTimeWithReschedule, timeUntilNextUpdate); } - public override bool HandleMouseInput => true; - protected virtual string Format() => Date.Humanize(); private void updateTime() => Text = Format(); diff --git a/osu.Game/Graphics/ScreenshotManager.cs b/osu.Game/Graphics/ScreenshotManager.cs index bc30794298..be253f65c1 100644 --- a/osu.Game/Graphics/ScreenshotManager.cs +++ b/osu.Game/Graphics/ScreenshotManager.cs @@ -71,7 +71,7 @@ namespace osu.Game.Graphics private volatile int screenShotTasks; - public async Task TakeScreenshotAsync() => await Task.Run(async () => + public Task TakeScreenshotAsync() => Task.Run(async () => { Interlocked.Increment(ref screenShotTasks); diff --git a/osu.Game/Graphics/SpriteIcon.cs b/osu.Game/Graphics/SpriteIcon.cs index 6acd20719e..1b1df45c77 100644 --- a/osu.Game/Graphics/SpriteIcon.cs +++ b/osu.Game/Graphics/SpriteIcon.cs @@ -71,7 +71,7 @@ namespace osu.Game.Graphics if (loadableIcon == loadedIcon) return; - var texture = store?.Get(((char)loadableIcon).ToString()); + var texture = store.Get(((char)loadableIcon).ToString()); spriteMain.Texture = texture; spriteShadow.Texture = texture; @@ -95,7 +95,7 @@ namespace osu.Game.Graphics { //adjust shadow alpha based on highest component intensity to avoid muddy display of darker text. //squared result for quadratic fall-off seems to give the best result. - var avgColour = (Color4)DrawInfo.Colour.AverageColour; + var avgColour = (Color4)DrawColourInfo.Colour.AverageColour; spriteShadow.Alpha = (float)Math.Pow(Math.Max(Math.Max(avgColour.R, avgColour.G), avgColour.B), 2); @@ -129,7 +129,7 @@ namespace osu.Game.Graphics if (icon == value) return; icon = value; - if (IsLoaded) + if (LoadState == LoadState.Loaded) updateTexture(); } } diff --git a/osu.Game/Graphics/Sprites/OsuSpriteText.cs b/osu.Game/Graphics/Sprites/OsuSpriteText.cs index c9389bb9e2..8607d51e12 100644 --- a/osu.Game/Graphics/Sprites/OsuSpriteText.cs +++ b/osu.Game/Graphics/Sprites/OsuSpriteText.cs @@ -3,9 +3,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; -using osu.Framework.MathUtils; -using OpenTK; -using OpenTK.Graphics; using osu.Framework.Graphics.Transforms; namespace osu.Game.Graphics.Sprites @@ -19,27 +16,6 @@ namespace osu.Game.Graphics.Sprites Shadow = true; TextSize = FONT_SIZE; } - - protected override Drawable CreateFallbackCharacterDrawable() - { - var tex = GetTextureForCharacter('?'); - - if (tex != null) - { - float adjust = (RNG.NextSingle() - 0.5f) * 2; - return new Sprite - { - Texture = tex, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Scale = new Vector2(1 + adjust * 0.2f), - Rotation = adjust * 15, - Colour = Color4.White, - }; - } - - return base.CreateFallbackCharacterDrawable(); - } } public static class OsuSpriteTextTransformExtensions diff --git a/osu.Game/Graphics/Textures/LargeTextureStore.cs b/osu.Game/Graphics/Textures/LargeTextureStore.cs deleted file mode 100644 index 4dcbb1220d..0000000000 --- a/osu.Game/Graphics/Textures/LargeTextureStore.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics.Textures; -using osu.Framework.IO.Stores; - -namespace osu.Game.Graphics.Textures -{ - /// - /// A texture store that bypasses atlasing. - /// - public class LargeTextureStore : TextureStore - { - public LargeTextureStore(IResourceStore store = null) : base(store, false) - { - } - } -} diff --git a/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs b/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs index f5017de639..ebb7b686e4 100644 --- a/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs +++ b/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs @@ -47,10 +47,10 @@ namespace osu.Game.Graphics.UserInterface public readonly SpriteIcon Chevron; //don't allow clicking between transitions and don't make the chevron clickable - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Alpha == 1f && Text.ReceiveMouseInputAt(screenSpacePos); + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Alpha == 1f && Text.ReceivePositionalInputAt(screenSpacePos); - public override bool HandleKeyboardInput => State == Visibility.Visible; - public override bool HandleMouseInput => State == Visibility.Visible; + public override bool HandleNonPositionalInput => State == Visibility.Visible; + public override bool HandlePositionalInput => State == Visibility.Visible; public override bool IsRemovable => true; private Visibility state; diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs index ee2448ff02..b2220267ff 100644 --- a/osu.Game/Graphics/UserInterface/DialogButton.cs +++ b/osu.Game/Graphics/UserInterface/DialogButton.cs @@ -13,7 +13,7 @@ using osu.Game.Graphics.Sprites; using osu.Framework.Extensions.Color4Extensions; using osu.Game.Graphics.Containers; using osu.Framework.Configuration; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; namespace osu.Game.Graphics.UserInterface { @@ -211,9 +211,9 @@ namespace osu.Game.Graphics.UserInterface } } - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => backgroundContainer.ReceiveMouseInputAt(screenSpacePos); + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => backgroundContainer.ReceivePositionalInputAt(screenSpacePos); - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { colourContainer.ResizeTo(new Vector2(1.5f, 1f), click_duration, Easing.In); flash(); @@ -225,20 +225,20 @@ namespace osu.Game.Graphics.UserInterface glowContainer.FadeOut(); }); - return base.OnClick(state); + return base.OnClick(e); } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { - base.OnHover(state); + base.OnHover(e); Selected.Value = true; return true; } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { - base.OnHoverLost(state); + base.OnHoverLost(e); Selected.Value = false; } diff --git a/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs b/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs index fe8995f310..d82448e8a2 100644 --- a/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs +++ b/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs @@ -5,7 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.Platform; using OpenTK; using OpenTK.Graphics; @@ -37,19 +37,19 @@ namespace osu.Game.Graphics.UserInterface this.host = host; } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { InternalChild.FadeColour(hoverColour, 500, Easing.OutQuint); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { InternalChild.FadeColour(Color4.White, 500, Easing.OutQuint); - base.OnHoverLost(state); + base.OnHoverLost(e); } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { if(Link != null) host.OpenUrlExternally(Link); diff --git a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs index e4fd71e17e..122ac7b627 100644 --- a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs +++ b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs @@ -3,8 +3,7 @@ using OpenTK.Graphics; using System; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Input.Bindings; using OpenTK.Input; @@ -34,22 +33,22 @@ namespace osu.Game.Graphics.UserInterface } // We may not be focused yet, but we need to handle keyboard input to be able to request focus - public override bool HandleKeyboardInput => HoldFocus || base.HandleKeyboardInput; + public override bool HandleNonPositionalInput => HoldFocus || base.HandleNonPositionalInput; - protected override void OnFocus(InputState state) + protected override void OnFocus(FocusEvent e) { - base.OnFocus(state); + base.OnFocus(e); BorderThickness = 0; } - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + protected override bool OnKeyDown(KeyDownEvent e) { if (!HasFocus) return false; - if (args.Key == Key.Escape) + if (e.Key == Key.Escape) return false; // disable the framework-level handling of escape key for confority (we use GlobalAction.Back). - return base.OnKeyDown(state, args); + return base.OnKeyDown(e); } public override bool OnPressed(GlobalAction action) diff --git a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs index 27a06ba0b7..3641e251bc 100644 --- a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs @@ -5,7 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Extensions; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; namespace osu.Game.Graphics.UserInterface { @@ -21,10 +21,10 @@ namespace osu.Game.Graphics.UserInterface { } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { sampleClick?.Play(); - return base.OnClick(state); + return base.OnClick(e); } [BackgroundDependencyLoader] diff --git a/osu.Game/Graphics/UserInterface/HoverSounds.cs b/osu.Game/Graphics/UserInterface/HoverSounds.cs index 821305bc92..710948121e 100644 --- a/osu.Game/Graphics/UserInterface/HoverSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverSounds.cs @@ -8,7 +8,7 @@ using osu.Framework.Audio.Sample; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; namespace osu.Game.Graphics.UserInterface { @@ -28,10 +28,10 @@ namespace osu.Game.Graphics.UserInterface RelativeSizeAxes = Axes.Both; } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { sampleHover?.Play(); - return base.OnHover(state); + return base.OnHover(e); } [BackgroundDependencyLoader] diff --git a/osu.Game/Graphics/UserInterface/IconButton.cs b/osu.Game/Graphics/UserInterface/IconButton.cs index be60812ba6..f10f03873d 100644 --- a/osu.Game/Graphics/UserInterface/IconButton.cs +++ b/osu.Game/Graphics/UserInterface/IconButton.cs @@ -4,7 +4,7 @@ using OpenTK; using OpenTK.Graphics; using osu.Framework.Graphics; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; namespace osu.Game.Graphics.UserInterface { @@ -84,16 +84,16 @@ namespace osu.Game.Graphics.UserInterface }); } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { icon.FadeColour(IconHoverColour, 500, Easing.OutQuint); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { icon.FadeColour(IconColour, 500, Easing.OutQuint); - base.OnHoverLost(state); + base.OnHoverLost(e); } } } diff --git a/osu.Game/Graphics/UserInterface/LineGraph.cs b/osu.Game/Graphics/UserInterface/LineGraph.cs index 3cb2446acc..ff2c4cf7cd 100644 --- a/osu.Game/Graphics/UserInterface/LineGraph.cs +++ b/osu.Game/Graphics/UserInterface/LineGraph.cs @@ -69,7 +69,7 @@ namespace osu.Game.Graphics.UserInterface { Masking = true, RelativeSizeAxes = Axes.Both, - Child = path = new Path { RelativeSizeAxes = Axes.Both, PathWidth = 1 } + Child = path = new SmoothPath { RelativeSizeAxes = Axes.Both, PathWidth = 1 } }); } diff --git a/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs b/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs index 4428a058db..4516d7ce76 100644 --- a/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs @@ -6,8 +6,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; using OpenTK.Graphics; @@ -77,34 +76,34 @@ namespace osu.Game.Graphics.UserInterface Enabled.BindValueChanged(enabled => this.FadeColour(enabled ? Color4.White : colours.Gray9, 200, Easing.OutQuint), true); } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { hover.FadeIn(500, Easing.OutQuint); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { hover.FadeOut(500, Easing.OutQuint); - base.OnHoverLost(state); + base.OnHoverLost(e); } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { hover.FlashColour(FlashColour, 800, Easing.OutQuint); - return base.OnClick(state); + return base.OnClick(e); } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(MouseDownEvent e) { Content.ScaleTo(0.75f, 2000, Easing.OutQuint); - return base.OnMouseDown(state, args); + return base.OnMouseDown(e); } - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + protected override bool OnMouseUp(MouseUpEvent e) { Content.ScaleTo(1, 1000, Easing.OutElastic); - return base.OnMouseUp(state, args); + return base.OnMouseUp(e); } } } diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs index bf3805a44d..ab880cd473 100644 --- a/osu.Game/Graphics/UserInterface/OsuButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuButton.cs @@ -1,7 +1,15 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; +using osu.Game.Graphics.Sprites; +using OpenTK.Graphics; namespace osu.Game.Graphics.UserInterface { @@ -10,9 +18,73 @@ namespace osu.Game.Graphics.UserInterface /// public class OsuButton : Button { + private Box hover; + public OsuButton() { - Add(new HoverClickSounds(HoverSampleSet.Loud)); + Height = 40; + + Content.Masking = true; + Content.CornerRadius = 5; } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + BackgroundColour = colours.BlueDark; + + AddRange(new Drawable[] + { + hover = new Box + { + RelativeSizeAxes = Axes.Both, + Blending = BlendingMode.Additive, + Colour = Color4.White.Opacity(0.1f), + Alpha = 0, + Depth = -1 + }, + new HoverClickSounds(HoverSampleSet.Loud), + }); + + 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(HoverEvent e) + { + hover.FadeIn(200); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + hover.FadeOut(200); + base.OnHoverLost(e); + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + Content.ScaleTo(0.9f, 4000, Easing.OutQuint); + return base.OnMouseDown(e); + } + + protected override bool OnMouseUp(MouseUpEvent e) + { + Content.ScaleTo(1, 1000, Easing.OutElastic); + return base.OnMouseUp(e); + } + + protected override SpriteText CreateText() => new OsuSpriteText + { + Depth = -1, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Font = @"Exo2.0-Bold", + }; } } diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs index 68f59bd8cd..e267a7f848 100644 --- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs @@ -8,7 +8,7 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics.Sprites; using OpenTK.Graphics; @@ -95,18 +95,18 @@ namespace osu.Game.Graphics.UserInterface }; } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { Nub.Glowing = true; Nub.Expanded = true; - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { Nub.Glowing = false; Nub.Expanded = false; - base.OnHoverLost(state); + base.OnHoverLost(e); } [BackgroundDependencyLoader] diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index 830bde9dac..30803d1545 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -30,7 +30,7 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load(OsuColour colours) { - if (accentColour == default(Color4)) + if (accentColour == default) accentColour = colours.PinkDarker; updateAccentColour(); } @@ -51,6 +51,8 @@ namespace osu.Game.Graphics.UserInterface #region OsuDropdownMenu protected class OsuDropdownMenu : DropdownMenu, IHasAccentColour { + public override bool HandleNonPositionalInput => State == MenuState.Open; + // todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring public OsuDropdownMenu() { @@ -97,6 +99,9 @@ namespace osu.Game.Graphics.UserInterface #region DrawableOsuDropdownMenuItem public class DrawableOsuDropdownMenuItem : DrawableDropdownMenuItem, IHasAccentColour { + // IsHovered is used + public override bool HandlePositionalInput => true; + private Color4? accentColour; public Color4 AccentColour { diff --git a/osu.Game/Graphics/UserInterface/OsuMenu.cs b/osu.Game/Graphics/UserInterface/OsuMenu.cs index abb077e94f..fe3e866a70 100644 --- a/osu.Game/Graphics/UserInterface/OsuMenu.cs +++ b/osu.Game/Graphics/UserInterface/OsuMenu.cs @@ -10,7 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics.Sprites; using OpenTK; @@ -97,25 +97,25 @@ namespace osu.Game.Graphics.UserInterface } } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { sampleHover.Play(); text.BoldText.FadeIn(transition_length, Easing.OutQuint); text.NormalText.FadeOut(transition_length, Easing.OutQuint); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { text.BoldText.FadeOut(transition_length, Easing.OutQuint); text.NormalText.FadeIn(transition_length, Easing.OutQuint); - base.OnHoverLost(state); + base.OnHoverLost(e); } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { sampleClick.Play(); - return base.OnClick(state); + return base.OnClick(e); } protected sealed override Drawable CreateContent() => text = CreateTextContainer(); diff --git a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs index 75655ddb36..f4ec67db23 100644 --- a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs +++ b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs @@ -9,8 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.Platform; namespace osu.Game.Graphics.UserInterface @@ -43,23 +42,23 @@ namespace osu.Game.Graphics.UserInterface this.host = host; } - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + protected override bool OnKeyDown(KeyDownEvent e) { - if (args.Key == Key.CapsLock) + if (e.Key == Key.CapsLock) updateCapsWarning(host.CapsLockEnabled); - return base.OnKeyDown(state, args); + return base.OnKeyDown(e); } - protected override void OnFocus(InputState state) + protected override void OnFocus(FocusEvent e) { updateCapsWarning(host.CapsLockEnabled); - base.OnFocus(state); + base.OnFocus(e); } - protected override void OnFocusLost(InputState state) + protected override void OnFocusLost(FocusLostEvent e) { updateCapsWarning(false); - base.OnFocusLost(state); + base.OnFocusLost(e); } private void updateCapsWarning(bool visible) => warning.FadeTo(visible ? 1 : 0, 250, Easing.OutQuint); diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index b7b5319e06..eecc10469e 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -13,8 +13,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; namespace osu.Game.Graphics.UserInterface { @@ -125,16 +124,16 @@ namespace osu.Game.Graphics.UserInterface AccentColour = colours.Pink; } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { Nub.Glowing = true; - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { Nub.Glowing = false; - base.OnHoverLost(state); + base.OnHoverLost(e); } protected override void OnUserChange() @@ -164,16 +163,16 @@ namespace osu.Game.Graphics.UserInterface sample.Play(); } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(MouseDownEvent e) { Nub.Current.Value = true; - return base.OnMouseDown(state, args); + return base.OnMouseDown(e); } - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + protected override bool OnMouseUp(MouseUpEvent e) { Nub.Current.Value = false; - return base.OnMouseUp(state, args); + return base.OnMouseUp(e); } protected override void UpdateAfterChildren() diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs index e2a0b88b2a..e7d6a87349 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs @@ -14,7 +14,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.MathUtils; using osu.Game.Graphics.Sprites; @@ -53,7 +53,7 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load(OsuColour colours) { - if (accentColour == default(Color4)) + if (accentColour == default) AccentColour = colours.Blue; } @@ -126,14 +126,14 @@ namespace osu.Game.Graphics.UserInterface Text.FadeColour(AccentColour, transition_length, Easing.OutQuint); } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { if (!Active) fadeActive(); return true; } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { if (!Active) fadeInactive(); @@ -142,7 +142,7 @@ namespace osu.Game.Graphics.UserInterface [BackgroundDependencyLoader] private void load(OsuColour colours) { - if (accentColour == default(Color4)) + if (accentColour == default) AccentColour = colours.Blue; } @@ -265,16 +265,16 @@ namespace osu.Game.Graphics.UserInterface Padding = new MarginPadding { Left = 5, Right = 5 }; } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { Foreground.Colour = BackgroundColour; - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { Foreground.Colour = BackgroundColourHover; - base.OnHoverLost(state); + base.OnHoverLost(e); } } } diff --git a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs index 04ba111153..1355ffdb8e 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs @@ -10,7 +10,7 @@ using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.Sprites; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; namespace osu.Game.Graphics.UserInterface { @@ -59,18 +59,18 @@ namespace osu.Game.Graphics.UserInterface text.FadeColour(AccentColour, transition_length, Easing.OutQuint); } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { fadeIn(); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { if (!Current) fadeOut(); - base.OnHoverLost(state); + base.OnHoverLost(e); } [BackgroundDependencyLoader] diff --git a/osu.Game/Graphics/UserInterface/OsuTextBox.cs b/osu.Game/Graphics/UserInterface/OsuTextBox.cs index 7a45ffdd4b..37464faa73 100644 --- a/osu.Game/Graphics/UserInterface/OsuTextBox.cs +++ b/osu.Game/Graphics/UserInterface/OsuTextBox.cs @@ -9,7 +9,7 @@ using osu.Game.Graphics.Sprites; using OpenTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Input.Bindings; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Input.Bindings; namespace osu.Game.Graphics.UserInterface @@ -44,17 +44,17 @@ namespace osu.Game.Graphics.UserInterface BorderColour = colour.Yellow; } - protected override void OnFocus(InputState state) + protected override void OnFocus(FocusEvent e) { BorderThickness = 3; - base.OnFocus(state); + base.OnFocus(e); } - protected override void OnFocusLost(InputState state) + protected override void OnFocusLost(FocusLostEvent e) { BorderThickness = 0; - base.OnFocusLost(state); + base.OnFocusLost(e); } protected override Drawable GetDrawableCharacter(char c) => new OsuSpriteText { Text = c.ToString(), TextSize = CalculatedTextSize }; diff --git a/osu.Game/Graphics/UserInterface/PageTabControl.cs b/osu.Game/Graphics/UserInterface/PageTabControl.cs index d203532f9c..fb7b4c5676 100644 --- a/osu.Game/Graphics/UserInterface/PageTabControl.cs +++ b/osu.Game/Graphics/UserInterface/PageTabControl.cs @@ -10,7 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics.Sprites; namespace osu.Game.Graphics.UserInterface @@ -67,14 +67,14 @@ namespace osu.Game.Graphics.UserInterface box.Colour = colours.Yellow; } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { if (!Active) slideActive(); return true; } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { if (!Active) slideInactive(); diff --git a/osu.Game/Graphics/UserInterface/RollingCounter.cs b/osu.Game/Graphics/UserInterface/RollingCounter.cs index f84404a911..c2162b8a42 100644 --- a/osu.Game/Graphics/UserInterface/RollingCounter.cs +++ b/osu.Game/Graphics/UserInterface/RollingCounter.cs @@ -134,7 +134,7 @@ namespace osu.Game.Graphics.UserInterface /// public virtual void ResetCount() { - SetCountWithoutRolling(default(T)); + SetCountWithoutRolling(default); } /// diff --git a/osu.Game/Graphics/UserInterface/SearchTextBox.cs b/osu.Game/Graphics/UserInterface/SearchTextBox.cs index 6067481979..08e93fad18 100644 --- a/osu.Game/Graphics/UserInterface/SearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/SearchTextBox.cs @@ -2,8 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Graphics; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using OpenTK; using OpenTK.Input; @@ -33,11 +32,11 @@ namespace osu.Game.Graphics.UserInterface PlaceholderText = "type to search"; } - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + protected override bool OnKeyDown(KeyDownEvent e) { - if (!state.Keyboard.ControlPressed && !state.Keyboard.ShiftPressed) + if (!e.ControlPressed && !e.ShiftPressed) { - switch (args.Key) + switch (e.Key) { case Key.Left: case Key.Right: @@ -49,7 +48,7 @@ namespace osu.Game.Graphics.UserInterface if (!AllowCommit) { - switch (args.Key) + switch (e.Key) { case Key.KeypadEnter: case Key.Enter: @@ -57,16 +56,16 @@ namespace osu.Game.Graphics.UserInterface } } - if (state.Keyboard.ShiftPressed) + if (e.ShiftPressed) { - switch (args.Key) + switch (e.Key) { case Key.Delete: return false; } } - return base.OnKeyDown(state, args); + return base.OnKeyDown(e); } } } diff --git a/osu.Game/Graphics/UserInterface/TriangleButton.cs b/osu.Game/Graphics/UserInterface/TriangleButton.cs index bfdc0c3bef..683b442d93 100644 --- a/osu.Game/Graphics/UserInterface/TriangleButton.cs +++ b/osu.Game/Graphics/UserInterface/TriangleButton.cs @@ -2,17 +2,10 @@ // 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.EventArgs; -using osu.Framework.Input.States; using osu.Game.Graphics.Backgrounds; -using osu.Game.Graphics.Sprites; namespace osu.Game.Graphics.UserInterface { @@ -21,79 +14,17 @@ namespace osu.Game.Graphics.UserInterface /// 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", - }; + protected Triangles Triangles { get; private set; } [BackgroundDependencyLoader] private void load(OsuColour colours) { - BackgroundColour = colours.BlueDark; - - Content.Masking = true; - Content.CornerRadius = 5; - - AddRange(new Drawable[] + Add(Triangles = new Triangles { - 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, - }, + RelativeSizeAxes = Axes.Both, + ColourDark = colours.BlueDarker, + ColourLight = colours.Blue, }); - - 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 }; diff --git a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs index 4e6361d1ae..2000eb47e4 100644 --- a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs +++ b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs @@ -13,8 +13,7 @@ using osu.Game.Beatmaps.ControlPoints; using osu.Framework.Audio.Track; using System; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; namespace osu.Game.Graphics.UserInterface { @@ -170,9 +169,9 @@ namespace osu.Game.Graphics.UserInterface } } - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => IconLayer.ReceiveMouseInputAt(screenSpacePos) || TextLayer.ReceiveMouseInputAt(screenSpacePos); + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => IconLayer.ReceivePositionalInputAt(screenSpacePos) || TextLayer.ReceivePositionalInputAt(screenSpacePos); - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { this.ResizeTo(SIZE_EXTENDED, transform_time, Easing.OutElastic); IconLayer.FadeColour(HoverColour, transform_time, Easing.OutElastic); @@ -182,7 +181,7 @@ namespace osu.Game.Graphics.UserInterface return true; } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { this.ResizeTo(SIZE_RETRACTED, transform_time, Easing.OutElastic); IconLayer.FadeColour(TextLayer.Colour, transform_time, Easing.OutElastic); @@ -190,12 +189,12 @@ namespace osu.Game.Graphics.UserInterface bouncingIcon.ScaleTo(1, transform_time, Easing.OutElastic); } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(MouseDownEvent e) { return true; } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { var flash = new Box { @@ -209,7 +208,7 @@ namespace osu.Game.Graphics.UserInterface flash.FadeOut(500, Easing.OutQuint); flash.Expire(); - return base.OnClick(state); + return base.OnClick(e); } private class BouncingIcon : BeatSyncedContainer diff --git a/osu.Game/IO/Archives/ArchiveReader.cs b/osu.Game/IO/Archives/ArchiveReader.cs index 808ce159bb..24a5094586 100644 --- a/osu.Game/IO/Archives/ArchiveReader.cs +++ b/osu.Game/IO/Archives/ArchiveReader.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; +using System.Threading.Tasks; using osu.Framework.IO.Stores; namespace osu.Game.IO.Archives @@ -28,7 +29,9 @@ namespace osu.Game.IO.Archives public abstract IEnumerable Filenames { get; } - public virtual byte[] Get(string name) + public virtual byte[] Get(string name) => GetAsync(name).Result; + + public async Task GetAsync(string name) { using (Stream input = GetStream(name)) { @@ -36,7 +39,7 @@ namespace osu.Game.IO.Archives return null; byte[] buffer = new byte[input.Length]; - input.Read(buffer, 0, buffer.Length); + await input.ReadAsync(buffer, 0, buffer.Length); return buffer; } } diff --git a/osu.Game/IPC/ArchiveImportIPCChannel.cs b/osu.Game/IPC/ArchiveImportIPCChannel.cs index 6783b9712c..fa8168c1de 100644 --- a/osu.Game/IPC/ArchiveImportIPCChannel.cs +++ b/osu.Game/IPC/ArchiveImportIPCChannel.cs @@ -37,7 +37,7 @@ namespace osu.Game.IPC return; } - if (importer.HandledExtensions.Contains(Path.GetExtension(path))) + if (importer.HandledExtensions.Contains(Path.GetExtension(path)?.ToLowerInvariant())) importer.Import(path); } } diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index b21deff509..2f5f1aea3f 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -71,17 +71,17 @@ namespace osu.Game.Input.Bindings ToggleSettings, [Description("Toggle osu!direct")] ToggleDirect, - [Description("Increase Volume")] + [Description("Increase volume")] IncreaseVolume, - [Description("Decrease Volume")] + [Description("Decrease volume")] DecreaseVolume, [Description("Toggle mute")] ToggleMute, // In-Game Keybindings - [Description("Skip Cutscene")] + [Description("Skip cutscene")] SkipCutscene, - [Description("Quick Retry (Hold)")] + [Description("Quick retry (hold)")] QuickRetry, [Description("Take screenshot")] diff --git a/osu.Game/Input/Handlers/ReplayInputHandler.cs b/osu.Game/Input/Handlers/ReplayInputHandler.cs index 5f53868b28..0a62c6ca72 100644 --- a/osu.Game/Input/Handlers/ReplayInputHandler.cs +++ b/osu.Game/Input/Handlers/ReplayInputHandler.cs @@ -3,10 +3,13 @@ using System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Input.Handlers; using osu.Framework.Input.StateChanges; +using osu.Framework.Input.StateChanges.Events; using osu.Framework.Input.States; using osu.Framework.Platform; +using osu.Game.Rulesets.UI; using OpenTK; namespace osu.Game.Input.Handlers @@ -40,7 +43,29 @@ namespace osu.Game.Input.Handlers public void Apply(InputState state, IInputStateChangeHandler handler) { - handler.HandleCustomInput(state, this); + if (!(state is RulesetInputManagerInputState inputState)) + throw new InvalidOperationException($"{nameof(ReplayState)} should only be applied to a {nameof(RulesetInputManagerInputState)}"); + + var lastPressed = inputState.LastReplayState?.PressedActions ?? new List(); + var released = lastPressed.Except(PressedActions).ToArray(); + var pressed = PressedActions.Except(lastPressed).ToArray(); + + inputState.LastReplayState = this; + + handler.HandleInputStateChange(new ReplayStateChangeEvent(state, this, released, pressed)); + } + } + + public class ReplayStateChangeEvent : InputStateChangeEvent + { + public readonly T[] ReleasedActions; + public readonly T[] PressedActions; + + public ReplayStateChangeEvent(InputState state, IInput input, T[] releasedActions, T[] pressedActions) + : base(state, input) + { + ReleasedActions = releasedActions; + PressedActions = pressedActions; } } } diff --git a/osu.Game/Migrations/20180913080842_AddRankStatus.Designer.cs b/osu.Game/Migrations/20180913080842_AddRankStatus.Designer.cs new file mode 100644 index 0000000000..5ab43da046 --- /dev/null +++ b/osu.Game/Migrations/20180913080842_AddRankStatus.Designer.cs @@ -0,0 +1,380 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using osu.Game.Database; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + [Migration("20180913080842_AddRankStatus")] + partial class AddRankStatus + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.2-rtm-30932"); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("ApproachRate"); + + b.Property("CircleSize"); + + b.Property("DrainRate"); + + b.Property("OverallDifficulty"); + + b.Property("SliderMultiplier"); + + b.Property("SliderTickRate"); + + b.HasKey("ID"); + + b.ToTable("BeatmapDifficulty"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("AudioLeadIn"); + + b.Property("BaseDifficultyID"); + + b.Property("BeatDivisor"); + + b.Property("BeatmapSetInfoID"); + + b.Property("Countdown"); + + b.Property("DistanceSpacing"); + + b.Property("GridSize"); + + b.Property("Hash"); + + b.Property("Hidden"); + + b.Property("LetterboxInBreaks"); + + b.Property("MD5Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + + b.Property("Path"); + + b.Property("RulesetID"); + + b.Property("SpecialStyle"); + + b.Property("StackLeniency"); + + b.Property("StarDifficulty"); + + b.Property("Status"); + + b.Property("StoredBookmarks"); + + b.Property("TimelineZoom"); + + b.Property("Version"); + + b.Property("WidescreenStoryboard"); + + b.HasKey("ID"); + + b.HasIndex("BaseDifficultyID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("Hash"); + + b.HasIndex("MD5Hash"); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("BeatmapInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Artist"); + + b.Property("ArtistUnicode"); + + b.Property("AudioFile"); + + b.Property("AuthorString") + .HasColumnName("Author"); + + b.Property("BackgroundFile"); + + b.Property("PreviewTime"); + + b.Property("Source"); + + b.Property("Tags"); + + b.Property("Title"); + + b.Property("TitleUnicode"); + + b.HasKey("ID"); + + b.ToTable("BeatmapMetadata"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("BeatmapSetInfoID"); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("FileInfoID"); + + b.ToTable("BeatmapSetFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapSetID"); + + b.Property("Protected"); + + b.Property("Status"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapSetID") + .IsUnique(); + + b.ToTable("BeatmapSetInfo"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntKey") + .HasColumnName("Key"); + + b.Property("RulesetID"); + + b.Property("StringValue") + .HasColumnName("Value"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntAction") + .HasColumnName("Action"); + + b.Property("KeysString") + .HasColumnName("Keys"); + + b.Property("RulesetID"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("IntAction"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("KeyBinding"); + }); + + modelBuilder.Entity("osu.Game.IO.FileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Hash"); + + b.Property("ReferenceCount"); + + b.HasKey("ID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("ReferenceCount"); + + b.ToTable("FileInfo"); + }); + + modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Available"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.Property("ShortName"); + + b.HasKey("ID"); + + b.HasIndex("Available"); + + b.HasIndex("ShortName") + .IsUnique(); + + b.ToTable("RulesetInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("SkinInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("SkinInfoID"); + + b.ToTable("SkinFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Creator"); + + b.Property("DeletePending"); + + b.Property("Name"); + + b.HasKey("ID"); + + b.ToTable("SkinInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") + .WithMany("Beatmaps") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") + .WithMany("Files") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Files") + .HasForeignKey("SkinInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/osu.Game/Migrations/20180913080842_AddRankStatus.cs b/osu.Game/Migrations/20180913080842_AddRankStatus.cs new file mode 100644 index 0000000000..bba4944bb7 --- /dev/null +++ b/osu.Game/Migrations/20180913080842_AddRankStatus.cs @@ -0,0 +1,33 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace osu.Game.Migrations +{ + public partial class AddRankStatus : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "Status", + table: "BeatmapSetInfo", + nullable: false, + defaultValue: -3); // NONE + + migrationBuilder.AddColumn( + name: "Status", + table: "BeatmapInfo", + nullable: false, + defaultValue: -3); // NONE + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "Status", + table: "BeatmapSetInfo"); + + migrationBuilder.DropColumn( + name: "Status", + table: "BeatmapInfo"); + } + } +} diff --git a/osu.Game/Migrations/20181007180454_StandardizePaths.Designer.cs b/osu.Game/Migrations/20181007180454_StandardizePaths.Designer.cs new file mode 100644 index 0000000000..b387a45ecf --- /dev/null +++ b/osu.Game/Migrations/20181007180454_StandardizePaths.Designer.cs @@ -0,0 +1,380 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using osu.Game.Database; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + [Migration("20181007180454_StandardizePaths")] + partial class StandardizePaths + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.3-rtm-32065"); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("ApproachRate"); + + b.Property("CircleSize"); + + b.Property("DrainRate"); + + b.Property("OverallDifficulty"); + + b.Property("SliderMultiplier"); + + b.Property("SliderTickRate"); + + b.HasKey("ID"); + + b.ToTable("BeatmapDifficulty"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("AudioLeadIn"); + + b.Property("BaseDifficultyID"); + + b.Property("BeatDivisor"); + + b.Property("BeatmapSetInfoID"); + + b.Property("Countdown"); + + b.Property("DistanceSpacing"); + + b.Property("GridSize"); + + b.Property("Hash"); + + b.Property("Hidden"); + + b.Property("LetterboxInBreaks"); + + b.Property("MD5Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + + b.Property("Path"); + + b.Property("RulesetID"); + + b.Property("SpecialStyle"); + + b.Property("StackLeniency"); + + b.Property("StarDifficulty"); + + b.Property("Status"); + + b.Property("StoredBookmarks"); + + b.Property("TimelineZoom"); + + b.Property("Version"); + + b.Property("WidescreenStoryboard"); + + b.HasKey("ID"); + + b.HasIndex("BaseDifficultyID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("Hash"); + + b.HasIndex("MD5Hash"); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("BeatmapInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Artist"); + + b.Property("ArtistUnicode"); + + b.Property("AudioFile"); + + b.Property("AuthorString") + .HasColumnName("Author"); + + b.Property("BackgroundFile"); + + b.Property("PreviewTime"); + + b.Property("Source"); + + b.Property("Tags"); + + b.Property("Title"); + + b.Property("TitleUnicode"); + + b.HasKey("ID"); + + b.ToTable("BeatmapMetadata"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("BeatmapSetInfoID"); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("FileInfoID"); + + b.ToTable("BeatmapSetFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapSetID"); + + b.Property("Protected"); + + b.Property("Status"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapSetID") + .IsUnique(); + + b.ToTable("BeatmapSetInfo"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntKey") + .HasColumnName("Key"); + + b.Property("RulesetID"); + + b.Property("StringValue") + .HasColumnName("Value"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntAction") + .HasColumnName("Action"); + + b.Property("KeysString") + .HasColumnName("Keys"); + + b.Property("RulesetID"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("IntAction"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("KeyBinding"); + }); + + modelBuilder.Entity("osu.Game.IO.FileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Hash"); + + b.Property("ReferenceCount"); + + b.HasKey("ID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("ReferenceCount"); + + b.ToTable("FileInfo"); + }); + + modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Available"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.Property("ShortName"); + + b.HasKey("ID"); + + b.HasIndex("Available"); + + b.HasIndex("ShortName") + .IsUnique(); + + b.ToTable("RulesetInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("SkinInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("SkinInfoID"); + + b.ToTable("SkinFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Creator"); + + b.Property("DeletePending"); + + b.Property("Name"); + + b.HasKey("ID"); + + b.ToTable("SkinInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") + .WithMany("Beatmaps") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") + .WithMany("Files") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Files") + .HasForeignKey("SkinInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/osu.Game/Migrations/20181007180454_StandardizePaths.cs b/osu.Game/Migrations/20181007180454_StandardizePaths.cs new file mode 100644 index 0000000000..274b8030a9 --- /dev/null +++ b/osu.Game/Migrations/20181007180454_StandardizePaths.cs @@ -0,0 +1,26 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using System.IO; + +namespace osu.Game.Migrations +{ + public partial class StandardizePaths : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + string windowsStyle = @"\"; + string standardized = "/"; + + // Escaping \ does not seem to be needed. + migrationBuilder.Sql($"UPDATE `BeatmapInfo` SET `Path` = REPLACE(`Path`, '{windowsStyle}', '{standardized}')"); + migrationBuilder.Sql($"UPDATE `BeatmapMetadata` SET `AudioFile` = REPLACE(`AudioFile`, '{windowsStyle}', '{standardized}')"); + migrationBuilder.Sql($"UPDATE `BeatmapMetadata` SET `BackgroundFile` = REPLACE(`BackgroundFile`, '{windowsStyle}', '{standardized}')"); + migrationBuilder.Sql($"UPDATE `BeatmapSetFileInfo` SET `Filename` = REPLACE(`Filename`, '{windowsStyle}', '{standardized}')"); + migrationBuilder.Sql($"UPDATE `SkinFileInfo` SET `Filename` = REPLACE(`Filename`, '{windowsStyle}', '{standardized}')"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + } + } +} diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs index 6dbeaed62f..663676a6d3 100644 --- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs +++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs @@ -1,7 +1,8 @@ // - +using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using osu.Game.Database; namespace osu.Game.Migrations @@ -13,7 +14,7 @@ namespace osu.Game.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.1.1-rtm-30846"); + .HasAnnotation("ProductVersion", "2.1.3-rtm-32065"); modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => { @@ -78,6 +79,8 @@ namespace osu.Game.Migrations b.Property("StarDifficulty"); + b.Property("Status"); + b.Property("StoredBookmarks"); b.Property("TimelineZoom"); @@ -173,6 +176,8 @@ namespace osu.Game.Migrations b.Property("Protected"); + b.Property("Status"); + b.HasKey("ID"); b.HasIndex("DeletePending"); diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 12935a5ffe..1dda257c95 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -2,12 +2,10 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Net; using System.Threading; -using System.Threading.Tasks; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Logging; @@ -26,7 +24,7 @@ namespace osu.Game.Online.API private const string client_id = @"5"; private const string client_secret = @"FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk"; - private ConcurrentQueue queue = new ConcurrentQueue(); + private readonly Queue queue = new Queue(); /// /// The username/email provided by the user when initiating a login. @@ -55,7 +53,13 @@ namespace osu.Game.Online.API authentication.TokenString = config.Get(OsuSetting.Token); authentication.Token.ValueChanged += onTokenChanged; - Task.Factory.StartNew(run, cancellationToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); + var thread = new Thread(run) + { + Name = "APIAccess", + IsBackground = true + }; + + thread.Start(); } private void onTokenChanged(OAuthToken token) => config.Set(OsuSetting.Token, config.Get(OsuSetting.SavePassword) ? authentication.TokenString : string.Empty); @@ -75,10 +79,7 @@ namespace osu.Game.Online.API public void Unregister(IOnlineComponent component) { - Scheduler.Add(delegate - { - components.Remove(component); - }); + Scheduler.Add(delegate { components.Remove(component); }); } public string AccessToken => authentication.RequestAccessToken(); @@ -103,6 +104,7 @@ namespace osu.Game.Online.API log.Add(@"Queueing a ping request"); Queue(new ListChannelsRequest { Timeout = 5000 }); } + break; case APIState.Offline: case APIState.Connecting: @@ -161,18 +163,21 @@ namespace osu.Game.Online.API continue; } - //process the request queue. - APIRequest req; - while (queue.TryPeek(out req)) + while (true) { - if (handleRequest(req)) + APIRequest req; + + lock (queue) { - //we have succeeded, so let's unqueue. - queue.TryDequeue(out req); + if (queue.Count == 0) break; + req = queue.Dequeue(); } + + // TODO: handle failures better + handleRequest(req); } - Thread.Sleep(1); + Thread.Sleep(50); } } @@ -205,7 +210,8 @@ namespace osu.Game.Online.API } catch (WebException we) { - HttpStatusCode statusCode = (we.Response as HttpWebResponse)?.StatusCode ?? (we.Status == WebExceptionStatus.UnknownError ? HttpStatusCode.NotAcceptable : HttpStatusCode.RequestTimeout); + HttpStatusCode statusCode = (we.Response as HttpWebResponse)?.StatusCode + ?? (we.Status == WebExceptionStatus.UnknownError ? HttpStatusCode.NotAcceptable : HttpStatusCode.RequestTimeout); // special cases for un-typed but useful message responses. switch (we.Message) @@ -247,6 +253,7 @@ namespace osu.Game.Online.API } private APIState state; + public APIState State { get { return state; } @@ -271,7 +278,10 @@ namespace osu.Game.Online.API public bool IsLoggedIn => LocalUser.Value.Id > 1; - public void Queue(APIRequest request) => queue.Enqueue(request); + public void Queue(APIRequest request) + { + lock (queue) queue.Enqueue(request); + } public event StateChangeDelegate OnStateChange; @@ -279,16 +289,17 @@ namespace osu.Game.Online.API private void flushQueue(bool failOldRequests = true) { - var oldQueue = queue; - - //flush the queue. - queue = new ConcurrentQueue(); - - if (failOldRequests) + lock (queue) { - APIRequest req; - while (oldQueue.TryDequeue(out req)) - req.Fail(new WebException(@"Disconnected from server")); + var oldQueueRequests = queue.ToArray(); + + queue.Clear(); + + if (failOldRequests) + { + foreach (var req in oldQueueRequests) + req.Fail(new WebException(@"Disconnected from server")); + } } } diff --git a/osu.Game/Online/API/OAuth.cs b/osu.Game/Online/API/OAuth.cs index 67b908e894..7892df9aab 100644 --- a/osu.Game/Online/API/OAuth.cs +++ b/osu.Game/Online/API/OAuth.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Diagnostics; +using System.Net.Http; using osu.Framework.Configuration; using osu.Framework.IO.Network; @@ -40,7 +41,7 @@ namespace osu.Game.Online.API using (var req = new AccessTokenRequestPassword(username, password) { Url = $@"{endpoint}/oauth/token", - Method = HttpMethod.POST, + Method = HttpMethod.Post, ClientId = clientId, ClientSecret = clientSecret }) @@ -66,7 +67,7 @@ namespace osu.Game.Online.API using (var req = new AccessTokenRequestRefresh(refresh) { Url = $@"{endpoint}/oauth/token", - Method = HttpMethod.POST, + Method = HttpMethod.Post, ClientId = clientId, ClientSecret = clientSecret }) diff --git a/osu.Game/Online/API/Requests/GetMessagesRequest.cs b/osu.Game/Online/API/Requests/GetMessagesRequest.cs index 68de194bae..94f5a114ad 100644 --- a/osu.Game/Online/API/Requests/GetMessagesRequest.cs +++ b/osu.Game/Online/API/Requests/GetMessagesRequest.cs @@ -2,34 +2,19 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; -using System.Linq; -using osu.Framework.IO.Network; using osu.Game.Online.Chat; namespace osu.Game.Online.API.Requests { public class GetMessagesRequest : APIRequest> { - private readonly List channels; - private readonly long? since; + private readonly Channel channel; - public GetMessagesRequest(List channels, long? sinceId) + public GetMessagesRequest(Channel channel) { - this.channels = channels; - since = sinceId; + this.channel = channel; } - protected override WebRequest CreateWebRequest() - { - string channelString = string.Join(",", channels.Select(x => x.Id)); - - var req = base.CreateWebRequest(); - req.AddParameter(@"channels", channelString); - if (since.HasValue) req.AddParameter(@"since", since.Value.ToString()); - - return req; - } - - protected override string Target => @"chat/messages"; + protected override string Target => $@"chat/channels/{channel.Id}/messages"; } } diff --git a/osu.Game/Online/API/Requests/GetUpdatesRequest.cs b/osu.Game/Online/API/Requests/GetUpdatesRequest.cs new file mode 100644 index 0000000000..950ad93396 --- /dev/null +++ b/osu.Game/Online/API/Requests/GetUpdatesRequest.cs @@ -0,0 +1,32 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using JetBrains.Annotations; +using osu.Framework.IO.Network; +using osu.Game.Online.Chat; + +namespace osu.Game.Online.API.Requests +{ + public class GetUpdatesRequest : APIRequest + { + private readonly long since; + private readonly Channel channel; + + public GetUpdatesRequest(long sinceId, [CanBeNull] Channel channel = null) + { + this.channel = channel; + since = sinceId; + } + + protected override WebRequest CreateWebRequest() + { + var req = base.CreateWebRequest(); + if (channel != null) req.AddParameter(@"channel", channel.Id.ToString()); + req.AddParameter(@"since", since.ToString()); + + return req; + } + + protected override string Target => @"chat/updates"; + } +} diff --git a/osu.Game/Online/API/Requests/GetUpdatesResponse.cs b/osu.Game/Online/API/Requests/GetUpdatesResponse.cs new file mode 100644 index 0000000000..474d8e1501 --- /dev/null +++ b/osu.Game/Online/API/Requests/GetUpdatesResponse.cs @@ -0,0 +1,18 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using Newtonsoft.Json; +using osu.Game.Online.Chat; + +namespace osu.Game.Online.API.Requests +{ + public class GetUpdatesResponse + { + [JsonProperty] + public List Presence; + + [JsonProperty] + public List Messages; + } +} diff --git a/osu.Game/Online/API/Requests/JoinChannelRequest.cs b/osu.Game/Online/API/Requests/JoinChannelRequest.cs new file mode 100644 index 0000000000..a0a4667e98 --- /dev/null +++ b/osu.Game/Online/API/Requests/JoinChannelRequest.cs @@ -0,0 +1,31 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Net.Http; +using osu.Framework.IO.Network; +using osu.Game.Online.Chat; +using osu.Game.Users; + +namespace osu.Game.Online.API.Requests +{ + public class JoinChannelRequest : APIRequest + { + private readonly Channel channel; + private readonly User user; + + public JoinChannelRequest(Channel channel, User user) + { + this.channel = channel; + this.user = user; + } + + protected override WebRequest CreateWebRequest() + { + var req = base.CreateWebRequest(); + req.Method = HttpMethod.Put; + return req; + } + + protected override string Target => $@"chat/channels/{channel.Id}/users/{user.Id}"; + } +} diff --git a/osu.Game/Online/API/Requests/LeaveChannelRequest.cs b/osu.Game/Online/API/Requests/LeaveChannelRequest.cs new file mode 100644 index 0000000000..89bfa303c6 --- /dev/null +++ b/osu.Game/Online/API/Requests/LeaveChannelRequest.cs @@ -0,0 +1,31 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Net.Http; +using osu.Framework.IO.Network; +using osu.Game.Online.Chat; +using osu.Game.Users; + +namespace osu.Game.Online.API.Requests +{ + public class LeaveChannelRequest : APIRequest + { + private readonly Channel channel; + private readonly User user; + + public LeaveChannelRequest(Channel channel, User user) + { + this.channel = channel; + this.user = user; + } + + protected override WebRequest CreateWebRequest() + { + var req = base.CreateWebRequest(); + req.Method = HttpMethod.Delete; + return req; + } + + protected override string Target => $@"chat/channels/{channel.Id}/users/{user.Id}"; + } +} diff --git a/osu.Game/Online/API/Requests/PostMessageRequest.cs b/osu.Game/Online/API/Requests/PostMessageRequest.cs index e0a9fb83b2..8d9ba5dd5d 100644 --- a/osu.Game/Online/API/Requests/PostMessageRequest.cs +++ b/osu.Game/Online/API/Requests/PostMessageRequest.cs @@ -1,7 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Extensions; +using System.Net.Http; using osu.Framework.IO.Network; using osu.Game.Online.Chat; @@ -20,15 +20,13 @@ namespace osu.Game.Online.API.Requests { var req = base.CreateWebRequest(); - req.Method = HttpMethod.POST; - req.AddParameter(@"target_type", message.TargetType.GetDescription()); - req.AddParameter(@"target_id", message.TargetId.ToString()); + req.Method = HttpMethod.Post; req.AddParameter(@"is_action", message.IsAction.ToString().ToLowerInvariant()); req.AddParameter(@"message", message.Content); return req; } - protected override string Target => @"chat/messages"; + protected override string Target => $@"chat/channels/{message.ChannelId}/messages"; } } diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index 99e4392374..193ccf1f6b 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -15,6 +15,12 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"beatmapset_id")] public int OnlineBeatmapSetID { get; set; } + [JsonProperty(@"status")] + public BeatmapSetOnlineStatus Status { get; set; } + + [JsonProperty(@"beatmapset")] + public APIBeatmapSet BeatmapSet { get; set; } + [JsonProperty(@"playcount")] private int playCount { get; set; } @@ -59,11 +65,13 @@ namespace osu.Game.Online.API.Requests.Responses Ruleset = rulesets.GetRuleset(ruleset), StarDifficulty = starDifficulty, OnlineBeatmapID = OnlineBeatmapID, + Version = version, + Status = Status, BeatmapSet = new BeatmapSetInfo { OnlineBeatmapSetID = OnlineBeatmapSetID, + Status = BeatmapSet?.Status ?? BeatmapSetOnlineStatus.None }, - Version = version, BaseDifficulty = new BeatmapDifficulty { DrainRate = drainRate, diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs index bbaaa0c756..8446285070 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs @@ -20,10 +20,13 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"id")] public int? OnlineBeatmapSetID { - get { return onlineBeatmapSetID; } - set { onlineBeatmapSetID = value > 0 ? value : null; } + get => onlineBeatmapSetID; + set => onlineBeatmapSetID = value > 0 ? value : null; } + [JsonProperty(@"status")] + public BeatmapSetOnlineStatus Status { get; set; } + [JsonProperty(@"preview_url")] private string preview { get; set; } @@ -42,9 +45,6 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"storyboard")] private bool hasStoryboard { get; set; } - [JsonProperty(@"status")] - private BeatmapSetOnlineStatus status { get; set; } - [JsonProperty(@"submitted_date")] private DateTimeOffset submitted { get; set; } @@ -57,7 +57,7 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"user_id")] private long creatorId { - set { Author.Id = value; } + set => Author.Id = value; } [JsonProperty(@"beatmaps")] @@ -69,6 +69,7 @@ namespace osu.Game.Online.API.Requests.Responses { OnlineBeatmapSetID = OnlineBeatmapSetID, Metadata = this, + Status = Status, OnlineInfo = new BeatmapSetOnlineInfo { Covers = covers, @@ -76,7 +77,7 @@ namespace osu.Game.Online.API.Requests.Responses PlayCount = playCount, FavouriteCount = favouriteCount, BPM = bpm, - Status = status, + Status = Status, HasVideo = hasVideo, HasStoryboard = hasStoryboard, Submitted = submitted, diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs index e7aabad780..bbe74fcac0 100644 --- a/osu.Game/Online/Chat/Channel.cs +++ b/osu.Game/Online/Chat/Channel.cs @@ -19,11 +19,14 @@ namespace osu.Game.Online.Chat public string Topic; [JsonProperty(@"type")] - public string Type; + public ChannelType Type; [JsonProperty(@"channel_id")] public int Id; + [JsonProperty(@"last_message_id")] + public long? LastMessageId; + public readonly SortedList Messages = new SortedList(Comparer.Default); private readonly List pendingMessages = new List(); @@ -51,11 +54,20 @@ namespace osu.Game.Online.Chat NewMessagesArrived?.Invoke(new[] { message }); } + public bool MessagesLoaded { get; private set; } + public void AddNewMessages(params Message[] messages) { messages = messages.Except(Messages).ToArray(); + if (messages.Length == 0) return; + Messages.AddRange(messages); + MessagesLoaded = true; + + var maxMessageId = messages.Max(m => m.Id); + if (maxMessageId > LastMessageId) + LastMessageId = maxMessageId; purgeOldMessages(); diff --git a/osu.Game/Online/Chat/ChannelType.cs b/osu.Game/Online/Chat/ChannelType.cs new file mode 100644 index 0000000000..4ac0a99fc6 --- /dev/null +++ b/osu.Game/Online/Chat/ChannelType.cs @@ -0,0 +1,11 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Online.Chat +{ + public enum ChannelType + { + PM, + Public + } +} diff --git a/osu.Game/Online/Chat/DrawableLinkCompiler.cs b/osu.Game/Online/Chat/DrawableLinkCompiler.cs index 475363bd51..0148d1d2c3 100644 --- a/osu.Game/Online/Chat/DrawableLinkCompiler.cs +++ b/osu.Game/Online/Chat/DrawableLinkCompiler.cs @@ -24,7 +24,7 @@ namespace osu.Game.Online.Chat /// public List Parts; - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Parts.Any(d => d.ReceiveMouseInputAt(screenSpacePos)); + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Parts.Any(d => d.ReceivePositionalInputAt(screenSpacePos)); protected override HoverClickSounds CreateHoverClickSounds(HoverSampleSet sampleSet) => new LinkHoverSounds(sampleSet, Parts); @@ -53,7 +53,7 @@ namespace osu.Game.Online.Chat this.parts = parts; } - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => parts.Any(d => d.ReceiveMouseInputAt(screenSpacePos)); + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => parts.Any(d => d.ReceivePositionalInputAt(screenSpacePos)); } } } diff --git a/osu.Game/Online/Chat/InfoMessage.cs b/osu.Game/Online/Chat/InfoMessage.cs index 2ff901deb1..103a999591 100644 --- a/osu.Game/Online/Chat/InfoMessage.cs +++ b/osu.Game/Online/Chat/InfoMessage.cs @@ -15,11 +15,7 @@ namespace osu.Game.Online.Chat Timestamp = DateTimeOffset.Now; Content = message; - Sender = new User - { - Username = @"system", - Colour = @"0000ff", - }; + Sender = User.SYSTEM_USER; } } } diff --git a/osu.Game/Online/Chat/Message.cs b/osu.Game/Online/Chat/Message.cs index 535035e4fc..65e0415cd3 100644 --- a/osu.Game/Online/Chat/Message.cs +++ b/osu.Game/Online/Chat/Message.cs @@ -18,11 +18,8 @@ namespace osu.Game.Online.Chat [JsonProperty(@"sender_id")] public int UserId; - [JsonProperty(@"target_type")] - public TargetType TargetType; - - [JsonProperty(@"target_id")] - public int TargetId; + [JsonProperty(@"channel_id")] + public int ChannelId; [JsonProperty(@"is_action")] public bool IsAction; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 94678a9dde..b75a37e9df 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -65,7 +65,8 @@ namespace osu.Game private BeatmapSetOverlay beatmapSetOverlay; - private ScreenshotManager screenshotManager; + [Cached] + private readonly ScreenshotManager screenshotManager = new ScreenshotManager(); protected RavenLogger RavenLogger; @@ -75,10 +76,10 @@ namespace osu.Game { get { - Screen s = screenStack; - while (s != null && !(s is Intro)) - s = s.ChildScreen; - return s as Intro; + Screen screen = screenStack; + while (screen != null && !(screen is Intro)) + screen = screen.ChildScreen; + return screen as Intro; } } @@ -126,8 +127,8 @@ namespace osu.Game /// Whether the toolbar should also be hidden. public void CloseAllOverlays(bool toolbar = true) { - foreach (var o in overlays) - o.State = Visibility.Hidden; + foreach (var overlay in overlays) + overlay.State = Visibility.Hidden; if (toolbar) Toolbar.State = Visibility.Hidden; } @@ -244,7 +245,7 @@ namespace osu.Game /// The beatmap to show. public void ShowBeatmap(int beatmapId) => beatmapSetOverlay.FetchAndShowBeatmap(beatmapId); - protected void LoadScore(Score s) + protected void LoadScore(Score score) { scoreLoad?.Cancel(); @@ -252,18 +253,18 @@ namespace osu.Game if (menu == null) { - scoreLoad = Schedule(() => LoadScore(s)); + scoreLoad = Schedule(() => LoadScore(score)); return; } if (!menu.IsCurrentScreen) { menu.MakeCurrent(); - this.Delay(500).Schedule(() => LoadScore(s), out scoreLoad); + this.Delay(500).Schedule(() => LoadScore(score), out scoreLoad); return; } - if (s.Beatmap == null) + if (score.Beatmap == null) { notifications.Post(new SimpleNotification { @@ -273,12 +274,12 @@ namespace osu.Game return; } - ruleset.Value = s.Ruleset; + ruleset.Value = score.Ruleset; - Beatmap.Value = BeatmapManager.GetWorkingBeatmap(s.Beatmap); - Beatmap.Value.Mods.Value = s.Mods; + Beatmap.Value = BeatmapManager.GetWorkingBeatmap(score.Beatmap); + Beatmap.Value.Mods.Value = score.Mods; - menu.Push(new PlayerLoader(new ReplayPlayer(s.Replay))); + menu.Push(new PlayerLoader(new ReplayPlayer(score.Replay))); } protected override void Dispose(bool isDisposing) @@ -289,9 +290,6 @@ namespace osu.Game protected override void LoadComplete() { - // this needs to be cached before base.LoadComplete as it is used by MenuCursorContainer. - dependencies.Cache(screenshotManager = new ScreenshotManager()); - base.LoadComplete(); // The next time this is updated is in UpdateAfterChildren, which occurs too late and results @@ -299,11 +297,13 @@ namespace osu.Game // This prevents the cursor from showing until we have a screen with CursorVisible = true MenuCursorContainer.CanShowCursor = currentScreen?.CursorVisible ?? false; - // hook up notifications to components. + // todo: all archive managers should be able to be looped here. SkinManager.PostNotification = n => notifications?.Post(n); - BeatmapManager.PostNotification = n => notifications?.Post(n); + SkinManager.GetStableStorage = GetStorageForStableInstall; + BeatmapManager.PostNotification = n => notifications?.Post(n); BeatmapManager.GetStableStorage = GetStorageForStableInstall; + BeatmapManager.PresentBeatmap = PresentBeatmap; AddRange(new Drawable[] @@ -353,7 +353,7 @@ namespace osu.Game loadComponentSingleFile(beatmapSetOverlay = new BeatmapSetOverlay { Depth = -3 }, mainContent.Add); loadComponentSingleFile(musicController = new MusicController { - Depth = -4, + Depth = -5, Position = new Vector2(0, Toolbar.HEIGHT), Anchor = Anchor.TopRight, Origin = Anchor.TopRight, @@ -506,22 +506,24 @@ namespace osu.Game // we could avoid the need for scheduling altogether. Schedule(() => { - if (asyncLoadStream != null) + var previousLoadStream = asyncLoadStream; + + //chain with existing load stream + asyncLoadStream = Task.Run(async () => { - //chain with existing load stream - asyncLoadStream = asyncLoadStream.ContinueWith(async t => + if (previousLoadStream != null) + await previousLoadStream; + + try { - try - { - await LoadComponentAsync(d, add); - } - catch (OperationCanceledException) - { - } - }); - } - else - asyncLoadStream = LoadComponentAsync(d, add); + Logger.Log($"Loading {d}...", LoggingTarget.Debug); + await LoadComponentAsync(d, add); + Logger.Log($"Loaded {d}!", LoggingTarget.Debug); + } + catch (OperationCanceledException) + { + } + }); }); } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index bada2a794d..73c970ce5d 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -24,7 +24,6 @@ using osu.Framework.Input; using osu.Framework.Logging; using osu.Game.Audio; using osu.Game.Database; -using osu.Game.Graphics.Textures; using osu.Game.Input; using osu.Game.Input.Bindings; using osu.Game.IO; @@ -109,38 +108,16 @@ namespace osu.Game dependencies.Cache(contextFactory = new DatabaseContextFactory(Host.Storage)); - dependencies.Cache(new LargeTextureStore(new RawTextureLoaderStore(new NamespacedResourceStore(Resources, @"Textures")))); + var largeStore = new LargeTextureStore(new TextureLoaderStore(new NamespacedResourceStore(Resources, @"Textures"))); + largeStore.AddStore(new TextureLoaderStore(new OnlineStore())); + dependencies.Cache(largeStore); dependencies.CacheAs(this); dependencies.Cache(LocalConfig); - runMigrations(); - - dependencies.Cache(SkinManager = new SkinManager(Host.Storage, contextFactory, Host, Audio)); - dependencies.CacheAs(SkinManager); - - var api = new APIAccess(LocalConfig); - - dependencies.Cache(api); - dependencies.CacheAs(api); - - dependencies.Cache(RulesetStore = new RulesetStore(contextFactory)); - dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage)); - dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, api, Audio, Host)); - dependencies.Cache(ScoreStore = new ScoreStore(Host.Storage, contextFactory, Host, BeatmapManager, RulesetStore)); - dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore)); - dependencies.Cache(SettingsStore = new SettingsStore(contextFactory)); - dependencies.Cache(RulesetConfigCache = new RulesetConfigCache(SettingsStore)); - dependencies.Cache(new OsuColour()); - - fileImporters.Add(BeatmapManager); - fileImporters.Add(ScoreStore); - fileImporters.Add(SkinManager); - //this completely overrides the framework default. will need to change once we make a proper FontStore. - dependencies.Cache(Fonts = new FontStore { ScaleAdjust = 100 }); + dependencies.Cache(Fonts = new FontStore(new GlyphStore(Resources, @"Fonts/FontAwesome"))); - Fonts.AddStore(new GlyphStore(Resources, @"Fonts/FontAwesome")); Fonts.AddStore(new GlyphStore(Resources, @"Fonts/osuFont")); Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-Medium")); Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Exo2.0-MediumItalic")); @@ -164,6 +141,29 @@ namespace osu.Game Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Venera")); Fonts.AddStore(new GlyphStore(Resources, @"Fonts/Venera-Light")); + runMigrations(); + + dependencies.Cache(SkinManager = new SkinManager(Host.Storage, contextFactory, Host, Audio)); + dependencies.CacheAs(SkinManager); + + var api = new APIAccess(LocalConfig); + + dependencies.Cache(api); + dependencies.CacheAs(api); + + dependencies.Cache(RulesetStore = new RulesetStore(contextFactory)); + dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage)); + dependencies.Cache(BeatmapManager = new BeatmapManager(Host.Storage, contextFactory, RulesetStore, api, Audio, Host)); + dependencies.Cache(ScoreStore = new ScoreStore(contextFactory, Host, BeatmapManager, RulesetStore)); + dependencies.Cache(KeyBindingStore = new KeyBindingStore(contextFactory, RulesetStore)); + dependencies.Cache(SettingsStore = new SettingsStore(contextFactory)); + dependencies.Cache(RulesetConfigCache = new RulesetConfigCache(SettingsStore)); + dependencies.Cache(new OsuColour()); + + fileImporters.Add(BeatmapManager); + fileImporters.Add(ScoreStore); + fileImporters.Add(SkinManager); + var defaultBeatmap = new DummyWorkingBeatmap(this); beatmap = new OsuBindableBeatmap(defaultBeatmap, Audio); BeatmapManager.DefaultBeatmap = defaultBeatmap; @@ -243,7 +243,7 @@ namespace osu.Game public void Import(params string[] paths) { - var extension = Path.GetExtension(paths.First()); + var extension = Path.GetExtension(paths.First())?.ToLowerInvariant(); foreach (var importer in fileImporters) if (importer.HandledExtensions.Contains(extension)) importer.Import(paths); diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs index 8186bf8685..bd9d65ccd8 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs @@ -10,7 +10,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; @@ -174,9 +174,9 @@ namespace osu.Game.Overlays.BeatmapSet { public Action OnLostHover; - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { - base.OnHoverLost(state); + base.OnHoverLost(e); OnLostHover?.Invoke(); } } @@ -241,24 +241,24 @@ namespace osu.Game.Overlays.BeatmapSet }; } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { fadeIn(); OnHovered?.Invoke(Beatmap); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { if (State == DifficultySelectorState.NotSelected) fadeOut(); - base.OnHoverLost(state); + base.OnHoverLost(e); } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { OnClicked?.Invoke(Beatmap); - return base.OnClick(state); + return base.OnClick(e); } private void fadeIn() diff --git a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs index 18391c1177..1988b7d222 100644 --- a/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs +++ b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs @@ -7,7 +7,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Graphics; @@ -66,7 +66,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons }, }; - Action = () => playButton.TriggerOnClick(); + Action = () => playButton.Click(); Playing.ValueChanged += newValue => progress.FadeTo(newValue ? 1 : 0, 100); } @@ -89,16 +89,16 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons progress.Width = 0; } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { bg.FadeColour(Color4.Black.Opacity(0.5f), 100); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { bg.FadeColour(Color4.Black.Opacity(0.25f), 100); - base.OnHoverLost(state); + base.OnHoverLost(e); } } } diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index afba99f928..6f01fae92d 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -230,10 +230,12 @@ namespace osu.Game.Overlays.BeatmapSet Spacing = new Vector2(10), Children = new Drawable[] { - onlineStatusPill = new BeatmapSetOnlineStatusPill(14, new MarginPadding { Horizontal = 25, Vertical = 8 }) + onlineStatusPill = new BeatmapSetOnlineStatusPill { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, + TextSize = 14, + TextPadding = new MarginPadding { Horizontal = 25, Vertical = 8 } }, Details = new Details(), }, diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ClickableUsername.cs b/osu.Game/Overlays/BeatmapSet/Scores/ClickableUsername.cs index dc350d2c4c..0be83db39e 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ClickableUsername.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ClickableUsername.cs @@ -3,7 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Users; @@ -53,7 +53,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores this.profile = profile; } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { profile?.ShowUser(user); return true; diff --git a/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs b/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs index d9af81c19a..309d75f60a 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/DrawableScore.cs @@ -6,7 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.API.Requests.Responses; @@ -125,18 +125,18 @@ namespace osu.Game.Overlays.BeatmapSet.Scores background.Colour = colours.Gray4; } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { background.FadeIn(fade_duration, Easing.OutQuint); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { background.FadeOut(fade_duration, Easing.OutQuint); - base.OnHoverLost(state); + base.OnHoverLost(e); } - protected override bool OnClick(InputState state) => true; + protected override bool OnClick(ClickEvent e) => true; } } diff --git a/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs b/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs index a1ebab09b1..d954b48b86 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/DrawableTopScore.cs @@ -8,7 +8,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.API.Requests.Responses; @@ -184,16 +184,16 @@ namespace osu.Game.Overlays.BeatmapSet.Scores BorderColour = rankText.Colour = colours.Yellow; } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { background.FadeIn(fade_duration, Easing.OutQuint); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { background.FadeOut(fade_duration, Easing.OutQuint); - base.OnHoverLost(state); + base.OnHoverLost(e); } private class InfoColumn : FillFlowContainer diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs index 626de14c98..60811d8b12 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs @@ -62,7 +62,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores loading = true; getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset); - getScoresRequest.Success += r => Scores = r.Scores; + getScoresRequest.Success += r => Schedule(() => Scores = r.Scores); api.Queue(getScoresRequest); } } @@ -134,5 +134,10 @@ namespace osu.Game.Overlays.BeatmapSet.Scores this.api = api; updateDisplay(); } + + protected override void Dispose(bool isDisposing) + { + getScoresRequest?.Cancel(); + } } } diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs index 02cc89e57e..23c8ec3aab 100644 --- a/osu.Game/Overlays/BeatmapSetOverlay.cs +++ b/osu.Game/Overlays/BeatmapSetOverlay.cs @@ -7,7 +7,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -51,7 +51,7 @@ namespace osu.Game.Overlays } // receive input outside our bounds so we can trigger a close event on ourselves. - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; public BeatmapSetOverlay() { @@ -127,7 +127,7 @@ namespace osu.Game.Overlays FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out).OnComplete(_ => BeatmapSet = null); } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { State = Visibility.Hidden; return true; diff --git a/osu.Game/Overlays/Chat/ChannelListItem.cs b/osu.Game/Overlays/Chat/ChannelListItem.cs index 7a60bf9f47..8df29c89f2 100644 --- a/osu.Game/Overlays/Chat/ChannelListItem.cs +++ b/osu.Game/Overlays/Chat/ChannelListItem.cs @@ -9,7 +9,7 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Online.Chat; @@ -155,15 +155,15 @@ namespace osu.Game.Overlays.Chat FinishTransforms(true); } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { if (!channel.Joined.Value) name.FadeColour(hoverColour, 50, Easing.OutQuint); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { if (!channel.Joined.Value) name.FadeColour(Color4.White, transition_duration); diff --git a/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs b/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs index 4e4fe4f10a..a86465b77b 100644 --- a/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs +++ b/osu.Game/Overlays/Chat/ChannelSelectionOverlay.cs @@ -10,7 +10,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Sprites; @@ -150,10 +150,10 @@ namespace osu.Game.Overlays.Chat headerBg.Colour = colours.Gray2.Opacity(0.75f); } - protected override void OnFocus(InputState state) + protected override void OnFocus(FocusEvent e) { GetContainingInputManager().ChangeFocus(search); - base.OnFocus(state); + base.OnFocus(e); } protected override void PopIn() diff --git a/osu.Game/Overlays/Chat/ChatTabControl.cs b/osu.Game/Overlays/Chat/ChatTabControl.cs index 2e3c9f9c5f..ec4fd85901 100644 --- a/osu.Game/Overlays/Chat/ChatTabControl.cs +++ b/osu.Game/Overlays/Chat/ChatTabControl.cs @@ -13,11 +13,11 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Chat; using OpenTK; +using OpenTK.Input; using OpenTK.Graphics; using osu.Framework.Configuration; using System; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.Chat @@ -143,7 +143,18 @@ namespace osu.Game.Overlays.Chat textBold.FadeOut(transition_length, Easing.OutQuint); } - protected override bool OnHover(InputState state) + protected override bool OnMouseUp(MouseUpEvent e) + { + if (e.Button == MouseButton.Middle) + { + closeButton.Action(); + return true; + } + + return false; + } + + protected override bool OnHover(HoverEvent e) { if (IsRemovable) closeButton.FadeIn(200, Easing.OutQuint); @@ -153,7 +164,7 @@ namespace osu.Game.Overlays.Chat return true; } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { closeButton.FadeOut(200, Easing.OutQuint); updateState(); @@ -279,28 +290,28 @@ namespace osu.Game.Overlays.Chat }; } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(MouseDownEvent e) { icon.ScaleTo(0.5f, 1000, Easing.OutQuint); - return base.OnMouseDown(state, args); + return base.OnMouseDown(e); } - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + protected override bool OnMouseUp(MouseUpEvent e) { icon.ScaleTo(0.75f, 1000, Easing.OutElastic); - return base.OnMouseUp(state, args); + return base.OnMouseUp(e); } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { icon.FadeColour(Color4.Red, 200, Easing.OutQuint); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { icon.FadeColour(Color4.White, 200, Easing.OutQuint); - base.OnHoverLost(state); + base.OnHoverLost(e); } } diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 8e20d76914..ff2ff9af14 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using OpenTK; using OpenTK.Graphics; @@ -13,7 +12,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.Threading; using osu.Game.Configuration; using osu.Game.Graphics; @@ -47,12 +46,12 @@ namespace osu.Game.Overlays public const float TAB_AREA_HEIGHT = 50; - private GetMessagesRequest fetchReq; + private GetUpdatesRequest fetchReq; private readonly ChatTabControl channelTabs; private readonly Container chatContainer; - private readonly Container tabsArea; + private readonly TabsArea tabsArea; private readonly Box chatBackground; private readonly Box tabBackground; @@ -62,7 +61,7 @@ namespace osu.Game.Overlays private readonly Container channelSelectionContainer; private readonly ChannelSelectionOverlay channelSelection; - public override bool Contains(Vector2 screenSpacePos) => chatContainer.ReceiveMouseInputAt(screenSpacePos) || channelSelection.State == Visibility.Visible && channelSelection.ReceiveMouseInputAt(screenSpacePos); + public override bool Contains(Vector2 screenSpacePos) => chatContainer.ReceivePositionalInputAt(screenSpacePos) || channelSelection.State == Visibility.Visible && channelSelection.ReceivePositionalInputAt(screenSpacePos); public ChatOverlay() { @@ -146,11 +145,8 @@ namespace osu.Game.Overlays loading = new LoadingAnimation(), } }, - tabsArea = new Container + tabsArea = new TabsArea { - Name = @"tabs area", - RelativeSizeAxes = Axes.X, - Height = TAB_AREA_HEIGHT, Children = new Drawable[] { tabBackground = new Box @@ -191,25 +187,22 @@ namespace osu.Game.Overlays public void OpenChannel(Channel channel) => addChannel(channel); - protected override bool OnDragStart(InputState state) + protected override bool OnDragStart(DragStartEvent e) { isDragging = tabsArea.IsHovered; if (!isDragging) - return base.OnDragStart(state); + return base.OnDragStart(e); startDragChatHeight = ChatHeight.Value; return true; } - protected override bool OnDrag(InputState state) + protected override bool OnDrag(DragEvent e) { if (isDragging) { - Trace.Assert(state.Mouse.PositionMouseDown != null); - - // ReSharper disable once PossibleInvalidOperationException - double targetChatHeight = startDragChatHeight - (state.Mouse.Position.Y - state.Mouse.PositionMouseDown.Value.Y) / Parent.DrawSize.Y; + double targetChatHeight = startDragChatHeight - (e.MousePosition.Y - e.MouseDownPosition.Y) / Parent.DrawSize.Y; // If the channel selection screen is shown, mind its minimum height if (channelSelection.State == Visibility.Visible && targetChatHeight > 1f - channel_selection_min_height) @@ -221,10 +214,10 @@ namespace osu.Game.Overlays return true; } - protected override bool OnDragEnd(InputState state) + protected override bool OnDragEnd(DragEndEvent e) { isDragging = false; - return base.OnDragEnd(state); + return base.OnDragEnd(e); } public void APIStateChanged(APIAccess api, APIState state) @@ -242,11 +235,11 @@ namespace osu.Game.Overlays public override bool AcceptsFocus => true; - protected override void OnFocus(InputState state) + protected override void OnFocus(FocusEvent e) { //this is necessary as textbox is masked away and therefore can't get focus :( GetContainingInputManager().ChangeFocus(textbox); - base.OnFocus(state); + base.OnFocus(e); } protected override void PopIn() @@ -285,7 +278,7 @@ namespace osu.Game.Overlays chatBackground.Colour = colours.ChatBlue; } - private long? lastMessageId; + private long lastMessageId; private readonly List careChannels = new List(); @@ -298,15 +291,15 @@ namespace osu.Game.Overlays messageRequest?.Cancel(); ListChannelsRequest req = new ListChannelsRequest(); - req.Success += delegate (List channels) + req.Success += delegate(List channels) { AvailableChannels = channels; Scheduler.Add(delegate { + //todo: decide how to handle default channels for a user now that they are saved server-side. addChannel(channels.Find(c => c.Name == @"#lazer")); addChannel(channels.Find(c => c.Name == @"#osu")); - addChannel(channels.Find(c => c.Name == @"#lobby")); channelSelection.OnRequestJoin = addChannel; channelSelection.OnRequestLeave = removeChannel; @@ -320,7 +313,7 @@ namespace osu.Game.Overlays }; }); - messageRequest = Scheduler.AddDelayed(fetchNewMessages, 1000, true); + messageRequest = Scheduler.AddDelayed(fetchUpdates, 1000, true); }; api.Queue(req); @@ -330,10 +323,7 @@ namespace osu.Game.Overlays protected Channel CurrentChannel { - get - { - return currentChannel; - } + get { return currentChannel; } set { @@ -362,7 +352,7 @@ namespace osu.Game.Overlays loadedChannels.Add(loaded); LoadComponentAsync(loaded, l => { - if (currentChannel.Messages.Any()) + if (currentChannel.MessagesLoaded) loading.Hide(); currentChannelContainer.Clear(false); @@ -394,6 +384,15 @@ namespace osu.Game.Overlays { careChannels.Add(channel); channelTabs.AddItem(channel); + + if (channel.Type == ChannelType.Public && !channel.Joined) + { + var req = new JoinChannelRequest(channel, api.LocalUser); + req.Success += () => addChannel(channel); + req.Failure += ex => removeChannel(channel); + api.Queue(req); + return; + } } // let's fetch a small number of messages to bring us up-to-date with the backlog. @@ -415,49 +414,46 @@ namespace osu.Game.Overlays loadedChannels.Remove(loadedChannels.Find(c => c.Channel == channel)); channelTabs.RemoveItem(channel); + api.Queue(new LeaveChannelRequest(channel, api.LocalUser)); channel.Joined.Value = false; } private void fetchInitialMessages(Channel channel) { - var req = new GetMessagesRequest(new List { channel }, null); - - req.Success += delegate (List messages) + var req = new GetMessagesRequest(channel); + req.Success += messages => { - loading.Hide(); channel.AddNewMessages(messages.ToArray()); - Debug.Write("success!"); - }; - req.Failure += delegate - { - Debug.Write("failure!"); + if (channel == currentChannel) + loading.Hide(); }; api.Queue(req); } - private void fetchNewMessages() + private void fetchUpdates() { if (fetchReq != null) return; - fetchReq = new GetMessagesRequest(careChannels, lastMessageId); + fetchReq = new GetUpdatesRequest(lastMessageId); - fetchReq.Success += delegate (List messages) + fetchReq.Success += updates => { - foreach (var group in messages.Where(m => m.TargetType == TargetType.Channel).GroupBy(m => m.TargetId)) - careChannels.Find(c => c.Id == group.Key)?.AddNewMessages(group.ToArray()); + if (updates?.Presence != null) + { + foreach (var channel in updates.Presence) + addChannel(AvailableChannels.Find(c => c.Id == channel.Id)); - lastMessageId = messages.LastOrDefault()?.Id ?? lastMessageId; + foreach (var group in updates.Messages.GroupBy(m => m.ChannelId)) + careChannels.Find(c => c.Id == group.Key)?.AddNewMessages(group.ToArray()); + + lastMessageId = updates.Messages.LastOrDefault()?.Id ?? lastMessageId; + } - Debug.Write("success!"); fetchReq = null; }; - fetchReq.Failure += delegate - { - Debug.Write("failure!"); - fetchReq = null; - }; + fetchReq.Failure += delegate { fetchReq = null; }; api.Queue(fetchReq); } @@ -517,8 +513,7 @@ namespace osu.Game.Overlays { Sender = api.LocalUser.Value, Timestamp = DateTimeOffset.Now, - TargetType = TargetType.Channel, //TODO: read this from channel - TargetId = target.Id, + ChannelId = target.Id, IsAction = isAction, Content = postText }; @@ -531,5 +526,18 @@ namespace osu.Game.Overlays api.Queue(req); } + + private class TabsArea : Container + { + // IsHovered is used + public override bool HandlePositionalInput => true; + + public TabsArea() + { + Name = @"tabs area"; + RelativeSizeAxes = Axes.X; + Height = TAB_AREA_HEIGHT; + } + } } } diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index c9c90b4555..f421d2202c 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -8,8 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Containers; @@ -26,7 +25,7 @@ namespace osu.Game.Overlays.Dialog public static readonly float ENTER_DURATION = 500; public static readonly float EXIT_DURATION = 200; - protected override bool BlockPassThroughMouse => false; + protected override bool BlockPositionalInput => false; private readonly Vector2 ringSize = new Vector2(100f); private readonly Vector2 ringMinifiedSize = new Vector2(20f); @@ -199,19 +198,19 @@ namespace osu.Game.Overlays.Dialog switch (action) { case GlobalAction.Select: - Buttons.OfType().FirstOrDefault()?.TriggerOnClick(); + Buttons.OfType().FirstOrDefault()?.Click(); return true; } return base.OnPressed(action); } - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + protected override bool OnKeyDown(KeyDownEvent e) { - if (args.Repeat) return false; + if (e.Repeat) return false; // press button at number if 1-9 on number row or keypad are pressed - var k = args.Key; + var k = e.Key; if (k >= Key.Number1 && k <= Key.Number9) { pressButtonAtIndex(k - Key.Number1); @@ -224,7 +223,7 @@ namespace osu.Game.Overlays.Dialog return true; } - return base.OnKeyDown(state, args); + return base.OnKeyDown(e); } protected override void PopIn() @@ -252,7 +251,7 @@ namespace osu.Game.Overlays.Dialog if (!actionInvoked) // In the case a user did not choose an action before a hide was triggered, press the last button. // This is presumed to always be a sane default "cancel" action. - buttonsContainer.Last().TriggerOnClick(); + buttonsContainer.Last().Click(); base.PopOut(); content.FadeOut(EXIT_DURATION, Easing.InSine); @@ -261,7 +260,7 @@ namespace osu.Game.Overlays.Dialog private void pressButtonAtIndex(int index) { if (index < Buttons.Count()) - Buttons.Skip(index).First().TriggerOnClick(); + Buttons.Skip(index).First().Click(); } } } diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index c40f517023..dae502dbd9 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -43,7 +43,7 @@ namespace osu.Game.Overlays protected override bool PlaySamplesOnStateChange => false; - protected override bool BlockPassThroughKeyboard => true; + protected override bool BlockNonPositionalInput => true; private void onDialogOnStateChanged(VisibilityContainer dialog, Visibility v) { diff --git a/osu.Game/Overlays/Direct/DirectGridPanel.cs b/osu.Game/Overlays/Direct/DirectGridPanel.cs index eb940bff60..1c462e3a73 100644 --- a/osu.Game/Overlays/Direct/DirectGridPanel.cs +++ b/osu.Game/Overlays/Direct/DirectGridPanel.cs @@ -7,11 +7,11 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; @@ -44,7 +44,7 @@ namespace osu.Game.Overlays.Direct } [BackgroundDependencyLoader] - private void load(OsuColour colours, LocalisationEngine localisation) + private void load(OsuColour colours) { Content.CornerRadius = 4; @@ -74,13 +74,13 @@ namespace osu.Game.Overlays.Direct { new OsuSpriteText { - Text = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title), + Text = new LocalisedString((SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title)), TextSize = 18, Font = @"Exo2.0-BoldItalic", }, new OsuSpriteText { - Text = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist), + Text = new LocalisedString((SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist)), Font = @"Exo2.0-BoldItalic", }, }, @@ -217,23 +217,25 @@ namespace osu.Game.Overlays.Direct statusContainer.Add(new IconPill(FontAwesome.fa_image)); } - statusContainer.Add(new BeatmapSetOnlineStatusPill(12, new MarginPadding { Horizontal = 10, Vertical = 5 }) + statusContainer.Add(new BeatmapSetOnlineStatusPill { + TextSize = 12, + TextPadding = new MarginPadding { Horizontal = 10, Vertical = 5 }, Status = SetInfo.OnlineInfo?.Status ?? BeatmapSetOnlineStatus.None, }); PreviewPlaying.ValueChanged += _ => updateStatusContainer(); } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { updateStatusContainer(); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { - base.OnHoverLost(state); + base.OnHoverLost(e); updateStatusContainer(); } diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/Direct/DirectListPanel.cs index 850ead37f6..6cb5ebad4e 100644 --- a/osu.Game/Overlays/Direct/DirectListPanel.cs +++ b/osu.Game/Overlays/Direct/DirectListPanel.cs @@ -10,8 +10,8 @@ using osu.Framework.Graphics.Colour; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Framework.Allocation; -using osu.Framework.Localisation; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; using osu.Game.Beatmaps; namespace osu.Game.Overlays.Direct @@ -39,7 +39,7 @@ namespace osu.Game.Overlays.Direct } [BackgroundDependencyLoader] - private void load(LocalisationEngine localisation, OsuColour colours) + private void load(OsuColour colours) { Content.CornerRadius = 5; @@ -94,13 +94,13 @@ namespace osu.Game.Overlays.Direct { new OsuSpriteText { - Current = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title), + Text = new LocalisedString((SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title)), TextSize = 18, Font = @"Exo2.0-BoldItalic", }, new OsuSpriteText { - Current = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist), + Text = new LocalisedString((SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist)), Font = @"Exo2.0-BoldItalic", }, } diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs index 322db0b7d6..5c7c34a0ed 100644 --- a/osu.Game/Overlays/Direct/DirectPanel.cs +++ b/osu.Game/Overlays/Direct/DirectPanel.cs @@ -10,7 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; @@ -104,8 +104,6 @@ namespace osu.Game.Overlays.Direct beatmaps.ItemAdded += setAdded; } - public override bool DisposeOnDeathRemoval => true; - protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); @@ -123,27 +121,27 @@ namespace osu.Game.Overlays.Direct } } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { content.TweenEdgeEffectTo(edgeEffectHovered, hover_transition_time, Easing.OutQuint); content.MoveToY(-4, hover_transition_time, Easing.OutQuint); if (FadePlayButton) PlayButton.FadeIn(120, Easing.InOutQuint); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { content.TweenEdgeEffectTo(edgeEffectNormal, hover_transition_time, Easing.OutQuint); content.MoveToY(0, hover_transition_time, Easing.OutQuint); if (FadePlayButton && !PreviewPlaying) PlayButton.FadeOut(120, Easing.InOutQuint); - base.OnHoverLost(state); + base.OnHoverLost(e); } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { ShowInformation(); return true; @@ -187,7 +185,7 @@ namespace osu.Game.Overlays.Direct base.LoadComplete(); this.FadeInFromZero(200, Easing.Out); - PreviewPlaying.ValueChanged += newValue => PlayButton.FadeTo(newValue || IsHovered ? 1 : 0, 120, Easing.InOutQuint); + PreviewPlaying.ValueChanged += newValue => PlayButton.FadeTo(newValue || IsHovered || !FadePlayButton ? 1 : 0, 120, Easing.InOutQuint); PreviewPlaying.ValueChanged += newValue => PreviewBar.FadeTo(newValue ? 1 : 0, 120, Easing.InOutQuint); } diff --git a/osu.Game/Overlays/Direct/FilterControl.cs b/osu.Game/Overlays/Direct/FilterControl.cs index df98cc3c32..c4825f72fe 100644 --- a/osu.Game/Overlays/Direct/FilterControl.cs +++ b/osu.Game/Overlays/Direct/FilterControl.cs @@ -73,8 +73,8 @@ namespace osu.Game.Overlays.Direct iconContainer.FadeTo(Ruleset.ID == obj?.ID ? 1f : 0.5f, 100); } - public override bool HandleKeyboardInput => !bindable.Disabled && base.HandleKeyboardInput; - public override bool HandleMouseInput => !bindable.Disabled && base.HandleMouseInput; + public override bool HandleNonPositionalInput => !bindable.Disabled && base.HandleNonPositionalInput; + public override bool HandlePositionalInput => !bindable.Disabled && base.HandlePositionalInput; public RulesetToggleButton(Bindable bindable, RulesetInfo ruleset) { diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs index 092c9ddbea..ac7a26fca3 100644 --- a/osu.Game/Overlays/Direct/PlayButton.cs +++ b/osu.Game/Overlays/Direct/PlayButton.cs @@ -5,7 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Graphics; @@ -93,23 +93,23 @@ namespace osu.Game.Overlays.Direct hoverColour = colour.Yellow; } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { Playing.Toggle(); return true; } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { icon.FadeColour(hoverColour, 120, Easing.InOutQuint); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { if (!Playing.Value) icon.FadeColour(Color4.White, 120, Easing.InOutQuint); - base.OnHoverLost(state); + base.OnHoverLost(e); } private void playingStateChanged(bool playing) diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index 423211659d..f63d314053 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -240,6 +240,15 @@ namespace osu.Game.Overlays }); } + protected override void PopIn() + { + base.PopIn(); + + // Queries are allowed to be run only on the first pop-in + if (getSetsRequest == null) + Scheduler.AddOnce(updateSearch); + } + private SearchBeatmapSetsRequest getSetsRequest; private readonly Bindable currentQuery = new Bindable(); @@ -251,16 +260,22 @@ namespace osu.Game.Overlays { queryChangedDebounce?.Cancel(); - if (!IsLoaded) return; + if (!IsLoaded) + return; + + if (State == Visibility.Hidden) + return; BeatmapSets = null; ResultAmounts = null; getSetsRequest?.Cancel(); - if (api == null) return; + if (api == null) + return; - if (Header.Tabs.Current.Value == DirectTab.Search && (Filter.Search.Text == string.Empty || currentQuery == string.Empty)) return; + if (Header.Tabs.Current.Value == DirectTab.Search && (Filter.Search.Text == string.Empty || currentQuery == string.Empty)) + return; previewTrackManager.StopAnyPlaying(this); diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 86baaf3905..63ddc25fde 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -10,15 +10,13 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Bindings; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Input; using OpenTK.Graphics; using OpenTK.Input; -using JoystickEventArgs = osu.Framework.Input.EventArgs.JoystickEventArgs; namespace osu.Game.Overlays.KeyBinding { @@ -50,7 +48,7 @@ namespace osu.Game.Overlays.KeyBinding private FillFlowContainer buttons; - public IEnumerable FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend(text.Text); + public IEnumerable FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend((string)text.Text); public KeyBindingRow(object action, IEnumerable bindings) { @@ -125,18 +123,18 @@ namespace osu.Game.Overlays.KeyBinding } } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { FadeEdgeEffectTo(1, transition_time, Easing.OutQuint); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { FadeEdgeEffectTo(0, transition_time, Easing.OutQuint); - base.OnHoverLost(state); + base.OnHoverLost(e); } public override bool AcceptsFocus => bindTarget == null; @@ -149,16 +147,16 @@ namespace osu.Game.Overlays.KeyBinding private bool isModifier(Key k) => k < Key.F1; - protected override bool OnClick(InputState state) => true; + protected override bool OnClick(ClickEvent e) => true; - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(MouseDownEvent e) { if (!HasFocus || !bindTarget.IsHovered) - return base.OnMouseDown(state, args); + return base.OnMouseDown(e); if (!AllowMainMouseButtons) { - switch (args.Button) + switch (e.Button) { case MouseButton.Left: case MouseButton.Right: @@ -166,15 +164,15 @@ namespace osu.Game.Overlays.KeyBinding } } - bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state)); + bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState)); return true; } - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + protected override bool OnMouseUp(MouseUpEvent e) { // don't do anything until the last button is released. - if (!HasFocus || state.Mouse.Buttons.Any()) - return base.OnMouseUp(state, args); + if (!HasFocus || e.HasAnyButtonPressed) + return base.OnMouseUp(e); if (bindTarget.IsHovered) finalise(); @@ -183,31 +181,31 @@ namespace osu.Game.Overlays.KeyBinding return true; } - protected override bool OnScroll(InputState state) + protected override bool OnScroll(ScrollEvent e) { if (HasFocus) { if (bindTarget.IsHovered) { - bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state, state.Mouse.ScrollDelta)); + bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState, e.ScrollDelta)); finalise(); return true; } } - return base.OnScroll(state); + return base.OnScroll(e); } - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + protected override bool OnKeyDown(KeyDownEvent e) { if (!HasFocus) return false; - switch (args.Key) + switch (e.Key) { case Key.Delete: { - if (state.Keyboard.ShiftPressed) + if (e.ShiftPressed) { bindTarget.UpdateKeyCombination(InputKey.None); finalise(); @@ -218,35 +216,35 @@ namespace osu.Game.Overlays.KeyBinding } } - bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state)); - if (!isModifier(args.Key)) finalise(); + bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState)); + if (!isModifier(e.Key)) finalise(); return true; } - protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) + protected override bool OnKeyUp(KeyUpEvent e) { - if (!HasFocus) return base.OnKeyUp(state, args); + if (!HasFocus) return base.OnKeyUp(e); finalise(); return true; } - protected override bool OnJoystickPress(InputState state, JoystickEventArgs args) + protected override bool OnJoystickPress(JoystickPressEvent e) { if (!HasFocus) return false; - bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state)); + bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(e.CurrentState)); finalise(); return true; } - protected override bool OnJoystickRelease(InputState state, JoystickEventArgs args) + protected override bool OnJoystickRelease(JoystickReleaseEvent e) { if (!HasFocus) - return base.OnJoystickRelease(state, args); + return base.OnJoystickRelease(e); finalise(); return true; @@ -273,7 +271,7 @@ namespace osu.Game.Overlays.KeyBinding pressAKey.BypassAutoSizeAxes |= Axes.Y; } - protected override void OnFocus(InputState state) + protected override void OnFocus(FocusEvent e) { AutoSizeDuration = 500; AutoSizeEasing = Easing.OutQuint; @@ -282,13 +280,13 @@ namespace osu.Game.Overlays.KeyBinding pressAKey.BypassAutoSizeAxes &= ~Axes.Y; updateBindTarget(); - base.OnFocus(state); + base.OnFocus(e); } - protected override void OnFocusLost(InputState state) + protected override void OnFocusLost(FocusLostEvent e) { finalise(); - base.OnFocusLost(state); + base.OnFocusLost(e); } private void updateBindTarget() @@ -367,16 +365,16 @@ namespace osu.Game.Overlays.KeyBinding hoverColour = colours.YellowDark; } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { updateHoverState(); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { updateHoverState(); - base.OnHoverLost(state); + base.OnHoverLost(e); } private void updateHoverState() diff --git a/osu.Game/Overlays/MainSettings.cs b/osu.Game/Overlays/MainSettings.cs index 6e8b4494dc..736843ee4d 100644 --- a/osu.Game/Overlays/MainSettings.cs +++ b/osu.Game/Overlays/MainSettings.cs @@ -6,8 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -138,16 +137,16 @@ namespace osu.Game.Overlays }; } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(MouseDownEvent e) { aspect.ScaleTo(0.75f, 2000, Easing.OutQuint); - return base.OnMouseDown(state, args); + return base.OnMouseDown(e); } - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + protected override bool OnMouseUp(MouseUpEvent e) { aspect.ScaleTo(1, 1000, Easing.OutElastic); - return base.OnMouseUp(state, args); + return base.OnMouseUp(e); } public bool OnPressed(GlobalAction action) @@ -155,7 +154,7 @@ namespace osu.Game.Overlays switch (action) { case GlobalAction.Back: - TriggerOnClick(); + Click(); return true; } diff --git a/osu.Game/Overlays/MedalOverlay.cs b/osu.Game/Overlays/MedalOverlay.cs index bd67a718a7..dcd325490a 100644 --- a/osu.Game/Overlays/MedalOverlay.cs +++ b/osu.Game/Overlays/MedalOverlay.cs @@ -19,8 +19,7 @@ using osu.Framework.Graphics.Textures; using OpenTK.Input; using osu.Framework.Graphics.Shapes; using System; -using System.Linq; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.MathUtils; namespace osu.Game.Overlays @@ -176,15 +175,15 @@ namespace osu.Game.Overlays particleContainer.Add(new MedalParticle(RNG.Next(0, 359))); } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { dismiss(); return true; } - protected override void OnFocusLost(InputState state) + protected override void OnFocusLost(FocusLostEvent e) { - if (state.Keyboard.Keys.Contains(Key.Escape)) dismiss(); + if (e.CurrentState.Keyboard.Keys.IsPressed(Key.Escape)) dismiss(); } private const double initial_duration = 400; diff --git a/osu.Game/Overlays/MedalSplash/DrawableMedal.cs b/osu.Game/Overlays/MedalSplash/DrawableMedal.cs index a27278e002..629b6d6fa4 100644 --- a/osu.Game/Overlays/MedalSplash/DrawableMedal.cs +++ b/osu.Game/Overlays/MedalSplash/DrawableMedal.cs @@ -118,9 +118,9 @@ namespace osu.Game.Overlays.MedalSplash } [BackgroundDependencyLoader] - private void load(OsuColour colours, TextureStore textures) + private void load(OsuColour colours, TextureStore textures, LargeTextureStore largeTextures) { - medalSprite.Texture = textures.Get(medal.ImageUrl); + medalSprite.Texture = largeTextures.Get(medal.ImageUrl); medalGlow.Texture = textures.Get(@"MedalSplash/medal-glow"); description.Colour = colours.BlueLight; } diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index 8024f9a732..ecb65f6df2 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -13,8 +13,7 @@ using osu.Game.Rulesets.UI; using System; using System.Linq; using osu.Framework.Graphics.Cursor; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Mods @@ -149,20 +148,20 @@ namespace osu.Game.Overlays.Mods public virtual Mod SelectedMod => Mods.ElementAtOrDefault(selectedIndex); - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(MouseDownEvent e) { scaleContainer.ScaleTo(0.9f, 800, Easing.Out); - return base.OnMouseDown(state, args); + return base.OnMouseDown(e); } - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + protected override bool OnMouseUp(MouseUpEvent e) { scaleContainer.ScaleTo(1, 500, Easing.OutElastic); // only trigger the event if we are inside the area of the button - if (Contains(ToScreenSpace(state.Mouse.Position - Position))) + if (Contains(e.ScreenSpaceMousePosition)) { - switch (args.Button) + switch (e.Button) { case MouseButton.Left: SelectNext(1); diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 37bffaaf12..c0d2d889c6 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -10,8 +10,7 @@ using osu.Game.Rulesets.Mods; using System; using System.Linq; using System.Collections.Generic; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; namespace osu.Game.Overlays.Mods { @@ -55,16 +54,16 @@ namespace osu.Game.Overlays.Mods private ModButton[] buttons = { }; - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + protected override bool OnKeyDown(KeyDownEvent e) { if (ToggleKeys != null) { - var index = Array.IndexOf(ToggleKeys, args.Key); + var index = Array.IndexOf(ToggleKeys, e.Key); if (index > -1 && index < buttons.Length) - buttons[index].SelectNext(state.Keyboard.ShiftPressed ? -1 : 1); + buttons[index].SelectNext(e.ShiftPressed ? -1 : 1); } - return base.OnKeyDown(state, args); + return base.OnKeyDown(e); } public void DeselectAll() => DeselectTypes(buttons.Select(b => b.SelectedMod?.GetType()).Where(t => t != null)); diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index e83dedaf35..55d5d797e7 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -40,7 +40,7 @@ namespace osu.Game.Overlays.Mods protected readonly OsuSpriteText MultiplierLabel, UnrankedLabel; private readonly FillFlowContainer footerContainer; - protected override bool BlockPassThroughKeyboard => false; + protected override bool BlockNonPositionalInput => false; protected readonly FillFlowContainer ModSectionsContainer; diff --git a/osu.Game/Overlays/Music/PlaylistItem.cs b/osu.Game/Overlays/Music/PlaylistItem.cs index 62c9020160..c0a59df767 100644 --- a/osu.Game/Overlays/Music/PlaylistItem.cs +++ b/osu.Game/Overlays/Music/PlaylistItem.cs @@ -8,8 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Graphics; @@ -28,8 +27,8 @@ namespace osu.Game.Overlays.Music private SpriteIcon handle; private TextFlowContainer text; private IEnumerable titleSprites; - private UnicodeBindableString titleBind; - private UnicodeBindableString artistBind; + private ILocalisedBindableString titleBind; + private ILocalisedBindableString artistBind; public readonly BeatmapSetInfo BeatmapSetInfo; @@ -37,16 +36,16 @@ namespace osu.Game.Overlays.Music public bool IsDraggable { get; private set; } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(MouseDownEvent e) { IsDraggable = handle.IsHovered; - return base.OnMouseDown(state, args); + return base.OnMouseDown(e); } - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + protected override bool OnMouseUp(MouseUpEvent e) { IsDraggable = false; - return base.OnMouseUp(state, args); + return base.OnMouseUp(e); } private bool selected; @@ -74,7 +73,7 @@ namespace osu.Game.Overlays.Music } [BackgroundDependencyLoader] - private void load(OsuColour colours, LocalisationEngine localisation) + private void load(OsuColour colours, LocalisationManager localisation) { hoverColour = colours.Yellow; artistColour = colours.Gray9; @@ -97,11 +96,10 @@ namespace osu.Game.Overlays.Music }, }; - titleBind = localisation.GetUnicodePreference(metadata.TitleUnicode, metadata.Title); - artistBind = localisation.GetUnicodePreference(metadata.ArtistUnicode, metadata.Artist); + titleBind = localisation.GetLocalisedString(new LocalisedString((metadata.TitleUnicode, metadata.Title))); + artistBind = localisation.GetLocalisedString(new LocalisedString((metadata.ArtistUnicode, metadata.Artist))); - artistBind.ValueChanged += newText => recreateText(); - artistBind.TriggerChange(); + artistBind.BindValueChanged(newText => recreateText(), true); } private void recreateText() @@ -124,19 +122,19 @@ namespace osu.Game.Overlays.Music }); } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { handle.FadeIn(fade_duration); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { handle.FadeOut(fade_duration); } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { OnSelect?.Invoke(BeatmapSetInfo); return true; diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs index 32674f0a84..17c8d2f154 100644 --- a/osu.Game/Overlays/Music/PlaylistList.cs +++ b/osu.Game/Overlays/Music/PlaylistList.cs @@ -8,7 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; using OpenTK; @@ -115,25 +115,25 @@ namespace osu.Game.Overlays.Music private Vector2 nativeDragPosition; private PlaylistItem draggedItem; - protected override bool OnDragStart(InputState state) + protected override bool OnDragStart(DragStartEvent e) { - nativeDragPosition = state.Mouse.NativeState.Position; + nativeDragPosition = e.ScreenSpaceMousePosition; draggedItem = items.FirstOrDefault(d => d.IsDraggable); - return draggedItem != null || base.OnDragStart(state); + return draggedItem != null || base.OnDragStart(e); } - protected override bool OnDrag(InputState state) + protected override bool OnDrag(DragEvent e) { - nativeDragPosition = state.Mouse.NativeState.Position; + nativeDragPosition = e.ScreenSpaceMousePosition; if (draggedItem == null) - return base.OnDrag(state); + return base.OnDrag(e); return true; } - protected override bool OnDragEnd(InputState state) + protected override bool OnDragEnd(DragEndEvent e) { - nativeDragPosition = state.Mouse.NativeState.Position; - var handled = draggedItem != null || base.OnDragEnd(state); + nativeDragPosition = e.ScreenSpaceMousePosition; + var handled = draggedItem != null || base.OnDragEnd(e); draggedItem = null; return handled; diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 886b5fb95b..b32fd265cb 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -13,7 +13,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Framework.Threading; using osu.Game.Beatmaps; @@ -47,7 +47,6 @@ namespace osu.Game.Overlays private PlaylistOverlay playlist; private BeatmapManager beatmaps; - private LocalisationEngine localisation; private List beatmapSets; private BeatmapSetInfo currentSet; @@ -67,11 +66,10 @@ namespace osu.Game.Overlays } [BackgroundDependencyLoader] - private void load(BindableBeatmap beatmap, BeatmapManager beatmaps, OsuColour colours, LocalisationEngine localisation) + private void load(BindableBeatmap beatmap, BeatmapManager beatmaps, OsuColour colours) { this.beatmap.BindTo(beatmap); this.beatmaps = beatmaps; - this.localisation = localisation; Children = new Drawable[] { @@ -198,10 +196,16 @@ namespace osu.Game.Overlays playlist.StateChanged += s => playlistButton.FadeColour(s == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint); } + private ScheduledDelegate seekDelegate; + private void attemptSeek(double progress) { - if (!beatmap.Disabled) - current?.Track.Seek(progress); + seekDelegate?.Cancel(); + seekDelegate = Schedule(() => + { + if (!beatmap.Disabled) + current?.Track.Seek(progress); + }); } private void playlistOrderChanged(BeatmapSetInfo beatmapSetInfo, int index) @@ -351,17 +355,14 @@ namespace osu.Game.Overlays { if (beatmap?.Beatmap == null) //this is not needed if a placeholder exists { - title.Current = null; title.Text = @"Nothing to play"; - - artist.Current = null; artist.Text = @"Nothing to play"; } else { BeatmapMetadata metadata = beatmap.Metadata; - title.Current = localisation.GetUnicodePreference(metadata.TitleUnicode, metadata.Title); - artist.Current = localisation.GetUnicodePreference(metadata.ArtistUnicode, metadata.Artist); + title.Text = new LocalisedString((metadata.TitleUnicode, metadata.Title)); + artist.Text = new LocalisedString((metadata.ArtistUnicode, metadata.Artist)); } }); @@ -462,20 +463,14 @@ namespace osu.Game.Overlays private class DragContainer : Container { - private Vector2 dragStart; - - protected override bool OnDragStart(InputState state) + protected override bool OnDragStart(DragStartEvent e) { - base.OnDragStart(state); - dragStart = state.Mouse.Position; return true; } - protected override bool OnDrag(InputState state) + protected override bool OnDrag(DragEvent e) { - if (base.OnDrag(state)) return true; - - Vector2 change = state.Mouse.Position - dragStart; + Vector2 change = e.MousePosition - e.MouseDownPosition; // Diminish the drag distance as we go further to simulate "rubber band" feeling. change *= change.Length <= 0 ? 0 : (float)Math.Pow(change.Length, 0.7f) / change.Length; @@ -484,10 +479,10 @@ namespace osu.Game.Overlays return true; } - protected override bool OnDragEnd(InputState state) + protected override bool OnDragEnd(DragEndEvent e) { this.MoveTo(Vector2.Zero, 800, Easing.OutElastic); - return base.OnDragEnd(state); + return base.OnDragEnd(e); } } } diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index d891cd96e8..78f8f57343 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -96,8 +96,7 @@ namespace osu.Game.Overlays base.LoadComplete(); StateChanged += _ => updateProcessingMode(); - OverlayActivationMode.ValueChanged += _ => updateProcessingMode(); - OverlayActivationMode.TriggerChange(); + OverlayActivationMode.BindValueChanged(_ => updateProcessingMode(), true); } private int totalCount => sections.Select(c => c.DisplayedCount).Sum(); diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index 6798ae2bb2..aa2b248bc4 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -11,7 +11,7 @@ using osu.Game.Graphics; using OpenTK; using OpenTK.Graphics; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.Notifications @@ -118,19 +118,19 @@ namespace osu.Game.Overlays.Notifications }); } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { closeButton.FadeIn(75); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { closeButton.FadeOut(75); - base.OnHoverLost(state); + base.OnHoverLost(e); } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { if (Activated?.Invoke() ?? true) Close(); @@ -185,16 +185,16 @@ namespace osu.Game.Overlays.Notifications hoverColour = colours.Yellow; } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { this.FadeColour(hoverColour, 200); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { this.FadeColour(OsuColour.Gray(0.2f), 200); - base.OnHoverLost(state); + base.OnHoverLost(e); } } diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index 7a07fb970c..254258d098 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -169,7 +169,7 @@ namespace osu.Game.Overlays.Notifications public Action CompletionTarget { get; set; } /// - /// An action to complete when the completion notification is clicked. + /// An action to complete when the completion notification is clicked. Return true to close. /// public Func CompletionClickAction; diff --git a/osu.Game/Overlays/OnScreenDisplay.cs b/osu.Game/Overlays/OnScreenDisplay.cs index 041ceab365..97c6554908 100644 --- a/osu.Game/Overlays/OnScreenDisplay.cs +++ b/osu.Game/Overlays/OnScreenDisplay.cs @@ -25,9 +25,6 @@ namespace osu.Game.Overlays { private readonly Container box; - public override bool HandleKeyboardInput => false; - public override bool HandleMouseInput => false; - private readonly SpriteText textLine1; private readonly SpriteText textLine2; private readonly SpriteText textLine3; diff --git a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs index bfade5e45c..6a87db4211 100644 --- a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs @@ -9,7 +9,7 @@ using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Users; @@ -153,13 +153,13 @@ namespace osu.Game.Overlays.Profile.Header this.hoverLostAction = hoverLostAction; } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { hoverAction(); return true; } - protected override void OnHoverLost(InputState state) => hoverLostAction(); + protected override void OnHoverLost(HoverLostEvent e) => hoverLostAction(); } private class DrawableBadge : Container, IHasTooltip @@ -176,7 +176,7 @@ namespace osu.Game.Overlays.Profile.Header } [BackgroundDependencyLoader] - private void load(TextureStore textures) + private void load(LargeTextureStore textures) { Child = new Sprite { diff --git a/osu.Game/Overlays/Profile/Header/RankGraph.cs b/osu.Game/Overlays/Profile/Header/RankGraph.cs index a059792796..bfb01ce1c8 100644 --- a/osu.Game/Overlays/Profile/Header/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/RankGraph.cs @@ -10,7 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -74,7 +74,7 @@ namespace osu.Game.Overlays.Profile.Header Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, RelativeSizeAxes = Axes.X, - Height = 75, + Height = 60, Y = -secondary_textsize, Alpha = 0, } @@ -137,25 +137,25 @@ namespace osu.Game.Overlays.Profile.Header relativeText.Text = dayIndex + 1 == ranks.Length ? "Now" : $"{ranked_days - ranks[dayIndex].Key} days ago"; } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { if (ranks?.Length > 1) { - graph.UpdateBallPosition(state.Mouse.Position.X); + graph.UpdateBallPosition(e.MousePosition.X); graph.ShowBall(); } - return base.OnHover(state); + return base.OnHover(e); } - protected override bool OnMouseMove(InputState state) + protected override bool OnMouseMove(MouseMoveEvent e) { if (ranks?.Length > 1) - graph.UpdateBallPosition(state.Mouse.Position.X); + graph.UpdateBallPosition(e.MousePosition.X); - return base.OnMouseMove(state); + return base.OnMouseMove(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { if (ranks?.Length > 1) { @@ -163,7 +163,7 @@ namespace osu.Game.Overlays.Profile.Header updateRankTexts(); } - base.OnHoverLost(state); + base.OnHoverLost(e); } private class RankChartLineGraph : LineGraph diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 9d09836d25..4839348e0e 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -386,10 +386,13 @@ namespace osu.Game.Overlays.Profile infoTextLeft.AddText(new DrawableJoinDate(user.JoinDate), boldItalic); } - infoTextLeft.NewLine(); - infoTextLeft.AddText("Last seen ", lightText); - infoTextLeft.AddText(new DrawableDate(user.LastVisit), boldItalic); - infoTextLeft.NewParagraph(); + if (user.LastVisit.HasValue) + { + infoTextLeft.NewLine(); + infoTextLeft.AddText("Last seen ", lightText); + infoTextLeft.AddText(new DrawableDate(user.LastVisit.Value), boldItalic); + infoTextLeft.NewParagraph(); + } if (user.PlayStyle?.Length > 0) { diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs index 1a1f13933d..4278598bf5 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs @@ -29,7 +29,7 @@ namespace osu.Game.Overlays.Profile.Sections public string TooltipText { get; } [BackgroundDependencyLoader(true)] - private void load(LocalisationEngine locale, BeatmapSetOverlay beatmapSetOverlay) + private void load(BeatmapSetOverlay beatmapSetOverlay) { Action = () => { @@ -46,16 +46,14 @@ namespace osu.Game.Overlays.Profile.Sections { new OsuSpriteText { - Current = locale.GetUnicodePreference( - $"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} [{beatmap.Version}] ", - $"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} [{beatmap.Version}] " - ), + Text = new LocalisedString(($"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} [{beatmap.Version}] ", + $"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} [{beatmap.Version}] ")), TextSize = 15, Font = "Exo2.0-SemiBoldItalic", }, new OsuSpriteText { - Current = locale.GetUnicodePreference(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist), + Text = new LocalisedString((beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist)), TextSize = 12, Padding = new MarginPadding { Top = 3 }, Font = "Exo2.0-RegularItalic", diff --git a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs index 0b06acd426..621f752b9c 100644 --- a/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Beatmaps/PaginatedBeatmapContainer.cs @@ -14,8 +14,8 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps public class PaginatedBeatmapContainer : PaginatedContainer { private const float panel_padding = 10f; - private readonly BeatmapSetType type; + private GetUserBeatmapsRequest request; public PaginatedBeatmapContainer(BeatmapSetType type, Bindable user, string header, string missing = "None... yet.") : base(user, header, missing) @@ -31,9 +31,8 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps { base.ShowMore(); - var req = new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage); - - req.Success += sets => + request = new GetUserBeatmapsRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage); + request.Success += sets => Schedule(() => { ShowMoreButton.FadeTo(sets.Count == ItemsPerPage ? 1 : 0); ShowMoreLoading.Hide(); @@ -52,9 +51,15 @@ namespace osu.Game.Overlays.Profile.Sections.Beatmaps var panel = new DirectGridPanel(s.ToBeatmapSet(Rulesets)); ItemsContainer.Add(panel); } - }; + }); - Api.Queue(req); + Api.Queue(request); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + request?.Cancel(); } } } diff --git a/osu.Game/Overlays/Profile/Sections/DrawableProfileRow.cs b/osu.Game/Overlays/Profile/Sections/DrawableProfileRow.cs index 3a4bfc6804..165299e8c0 100644 --- a/osu.Game/Overlays/Profile/Sections/DrawableProfileRow.cs +++ b/osu.Game/Overlays/Profile/Sections/DrawableProfileRow.cs @@ -6,7 +6,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics; using OpenTK; using OpenTK.Graphics; @@ -105,20 +105,20 @@ namespace osu.Game.Overlays.Profile.Sections coloredBackground.Colour = underscoreLine.Colour = colour.Gray4; } - protected override bool OnClick(InputState state) => true; + protected override bool OnClick(ClickEvent e) => true; - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { background.FadeIn(fade_duration, Easing.OutQuint); underscoreLine.FadeOut(fade_duration, Easing.OutQuint); return true; } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { background.FadeOut(fade_duration, Easing.OutQuint); underscoreLine.FadeIn(fade_duration, Easing.OutQuint); - base.OnHoverLost(state); + base.OnHoverLost(e); } } } diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index 42784682be..ad886c363b 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -12,6 +12,8 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { public class PaginatedMostPlayedBeatmapContainer : PaginatedContainer { + private GetUserMostPlayedBeatmapsRequest request; + public PaginatedMostPlayedBeatmapContainer(Bindable user) :base(user, "Most Played Beatmaps", "No records. :(") { @@ -24,9 +26,8 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { base.ShowMore(); - var req = new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++ * ItemsPerPage); - - req.Success += beatmaps => + request = new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++ * ItemsPerPage); + request.Success += beatmaps => Schedule(() => { ShowMoreButton.FadeTo(beatmaps.Count == ItemsPerPage ? 1 : 0); ShowMoreLoading.Hide(); @@ -43,9 +44,16 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { ItemsContainer.Add(new DrawableMostPlayedRow(beatmap.GetBeatmapInfo(Rulesets), beatmap.PlayCount)); } - }; + }); - Api.Queue(req); + Api.Queue(request); + } + + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + request?.Cancel(); } } } diff --git a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs index 38bc419838..788041205b 100644 --- a/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs +++ b/osu.Game/Overlays/Profile/Sections/Kudosu/KudosuInfo.cs @@ -8,7 +8,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -70,7 +70,7 @@ namespace osu.Game.Overlays.Profile.Sections.Kudosu }; } - protected override bool OnClick(InputState state) => true; + protected override bool OnClick(ClickEvent e) => true; private class CountSection : Container { diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index 6dbb9b9ba3..db93fcbc1b 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -28,6 +28,7 @@ namespace osu.Game.Overlays.Profile.Sections protected readonly Bindable User = new Bindable(); protected APIAccess Api; + protected APIRequest RetrievalRequest; protected RulesetStore Rulesets; public PaginatedContainer(Bindable user, string header, string missing) diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs index 707de0a10f..ed82c62e5c 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/PaginatedScoreContainer.cs @@ -16,6 +16,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks { private readonly bool includeWeight; private readonly ScoreType type; + private GetUserScoresRequest request; public PaginatedScoreContainer(ScoreType type, Bindable user, string header, string missing, bool includeWeight = false) : base(user, header, missing) @@ -32,9 +33,8 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks { base.ShowMore(); - var req = new GetUserScoresRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage); - - req.Success += scores => + request = new GetUserScoresRequest(User.Value.Id, type, VisiblePages++ * ItemsPerPage); + request.Success += scores => Schedule(() => { foreach (var s in scores) s.ApplyRuleset(Rulesets.GetRuleset(s.OnlineRulesetID)); @@ -66,9 +66,15 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks ItemsContainer.Add(drawableScore); } - }; + }); - Api.Queue(req); + Api.Queue(request); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + request?.Cancel(); } } } diff --git a/osu.Game/Overlays/Profile/Sections/Recent/MedalIcon.cs b/osu.Game/Overlays/Profile/Sections/Recent/MedalIcon.cs index 0d354c728f..45569271df 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/MedalIcon.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/MedalIcon.cs @@ -30,7 +30,7 @@ namespace osu.Game.Overlays.Profile.Sections.Recent } [BackgroundDependencyLoader] - private void load(TextureStore textures) + private void load(LargeTextureStore textures) { sprite.Texture = textures.Get(url); } diff --git a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs index ee2f2f5973..fd5eda4e44 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/PaginatedRecentActivityContainer.cs @@ -12,6 +12,8 @@ namespace osu.Game.Overlays.Profile.Sections.Recent { public class PaginatedRecentActivityContainer : PaginatedContainer { + private GetUserRecentActivitiesRequest request; + public PaginatedRecentActivityContainer(Bindable user, string header, string missing) : base(user, header, missing) { @@ -22,9 +24,8 @@ namespace osu.Game.Overlays.Profile.Sections.Recent { base.ShowMore(); - var req = new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++ * ItemsPerPage); - - req.Success += activities => + request = new GetUserRecentActivitiesRequest(User.Value.Id, VisiblePages++ * ItemsPerPage); + request.Success += activities => Schedule(() => { ShowMoreButton.FadeTo(activities.Count == ItemsPerPage ? 1 : 0); ShowMoreLoading.Hide(); @@ -41,9 +42,15 @@ namespace osu.Game.Overlays.Profile.Sections.Recent { ItemsContainer.Add(new DrawableRecentActivity(activity)); } - }; + }); - Api.Queue(req); + Api.Queue(request); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + request?.Cancel(); } } } diff --git a/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs b/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs index f9e4a983bb..df249b0b88 100644 --- a/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs +++ b/osu.Game/Overlays/SearchableList/SearchableListOverlay.cs @@ -5,7 +5,7 @@ using OpenTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Containers; @@ -101,7 +101,7 @@ namespace osu.Game.Overlays.SearchableList scrollContainer.Padding = new MarginPadding { Top = Header.Height + Filter.Height }; } - protected override void OnFocus(InputState state) + protected override void OnFocus(FocusEvent e) { GetContainingInputManager().ChangeFocus(Filter.Search); } diff --git a/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs index 0fe41327db..da96c6ef30 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs @@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio { new SettingsSlider { - LabelText = "Audio Offset", + LabelText = "Audio offset", Bindable = config.GetBindable(OsuSetting.AudioOffset), KeyboardStep = 1f }, diff --git a/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs index 369c751448..fa4a714ba3 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs @@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio Children = new Drawable[] { new SettingsSlider { LabelText = "Master", Bindable = audio.Volume, KeyboardStep = 0.01f }, - new SettingsSlider { LabelText = "Master (Window Inactive)", Bindable = config.GetBindable(OsuSetting.VolumeInactive), KeyboardStep = 0.01f }, + new SettingsSlider { LabelText = "Master (window inactive)", Bindable = config.GetBindable(OsuSetting.VolumeInactive), KeyboardStep = 0.01f }, new SettingsSlider { LabelText = "Effect", Bindable = audio.VolumeSample, KeyboardStep = 0.01f }, new SettingsSlider { LabelText = "Music", Bindable = audio.VolumeTrack, KeyboardStep = 0.01f }, }; diff --git a/osu.Game/Overlays/Settings/Sections/Debug/GCSettings.cs b/osu.Game/Overlays/Settings/Sections/Debug/GCSettings.cs index 9f550413f3..b14a4b8773 100644 --- a/osu.Game/Overlays/Settings/Sections/Debug/GCSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Debug/GCSettings.cs @@ -13,15 +13,18 @@ namespace osu.Game.Overlays.Settings.Sections.Debug { protected override string Header => "Garbage Collector"; + private readonly Bindable latencyMode = new Bindable(); + private Bindable configLatencyMode; + [BackgroundDependencyLoader] private void load(FrameworkDebugConfigManager config) { Children = new Drawable[] { - new SettingsEnumDropdown + new SettingsEnumDropdown { LabelText = "Active mode", - Bindable = config.GetBindable(DebugSetting.ActiveGCMode) + Bindable = latencyMode }, new SettingsButton { @@ -29,6 +32,18 @@ namespace osu.Game.Overlays.Settings.Sections.Debug Action = GC.Collect }, }; + + configLatencyMode = config.GetBindable(DebugSetting.ActiveGCMode); + configLatencyMode.BindValueChanged(v => latencyMode.Value = (LatencyMode)v, true); + latencyMode.BindValueChanged(v => configLatencyMode.Value = (GCLatencyMode)v); + } + + private enum LatencyMode + { + Batch = GCLatencyMode.Batch, + Interactive = GCLatencyMode.Interactive, + LowLatency = GCLatencyMode.LowLatency, + SustainedLowLatency = GCLatencyMode.SustainedLowLatency } } } diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/ScrollingSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/ScrollingSettings.cs deleted file mode 100644 index 0e661aeba6..0000000000 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/ScrollingSettings.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Allocation; -using osu.Game.Configuration; - -namespace osu.Game.Overlays.Settings.Sections.Gameplay -{ - public class ScrollingSettings : SettingsSubsection - { - protected override string Header => "Scrolling"; - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - Children = new[] - { - new SettingsEnumDropdown - { - LabelText = "Visualise speed changes as", - Bindable = config.GetBindable(OsuSetting.SpeedChangeVisualisation), - } - }; - } - } -} diff --git a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs index 8add0b01ec..f565ff8556 100644 --- a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs +++ b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs @@ -21,7 +21,6 @@ namespace osu.Game.Overlays.Settings.Sections { new GeneralSettings(), new SongSelectSettings(), - new ScrollingSettings(), new ModsSettings(), }; } @@ -29,7 +28,7 @@ namespace osu.Game.Overlays.Settings.Sections [BackgroundDependencyLoader] private void load(RulesetStore rulesets) { - foreach(Ruleset ruleset in rulesets.AvailableRulesets.Select(info => info.CreateInstance())) + foreach (Ruleset ruleset in rulesets.AvailableRulesets.Select(info => info.CreateInstance())) { SettingsSubsection section = ruleset.CreateSettings(); if (section != null) diff --git a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs index c7f98f4107..11a3d36779 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LoginSettings.cs @@ -16,7 +16,7 @@ using System.ComponentModel; using osu.Game.Graphics; using OpenTK.Graphics; using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using RectangleF = osu.Framework.Graphics.Primitives.RectangleF; using Container = osu.Framework.Graphics.Containers.Container; @@ -175,12 +175,12 @@ namespace osu.Game.Overlays.Settings.Sections.General public override bool AcceptsFocus => true; - protected override bool OnClick(InputState state) => true; + protected override bool OnClick(ClickEvent e) => true; - protected override void OnFocus(InputState state) + protected override void OnFocus(FocusEvent e) { if (form != null) GetContainingInputManager().ChangeFocus(form); - base.OnFocus(state); + base.OnFocus(e); } private class LoginForm : FillFlowContainer @@ -244,9 +244,9 @@ namespace osu.Game.Overlays.Settings.Sections.General public override bool AcceptsFocus => true; - protected override bool OnClick(InputState state) => true; + protected override bool OnClick(ClickEvent e) => true; - protected override void OnFocus(InputState state) + protected override void OnFocus(FocusEvent e) { Schedule(() => { GetContainingInputManager().ChangeFocus(string.IsNullOrEmpty(username.Text) ? username : password); }); } diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 42028f6bd5..254de6c6f7 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -1,8 +1,12 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Collections.Generic; +using System.Drawing; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Configuration; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -15,21 +19,36 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics private FillFlowContainer letterboxSettings; private Bindable letterboxing; + private Bindable sizeFullscreen; + + private OsuGameBase game; + private SettingsDropdown resolutionDropdown; + private SettingsEnumDropdown windowModeDropdown; private const int transition_duration = 400; [BackgroundDependencyLoader] - private void load(FrameworkConfigManager config) + private void load(FrameworkConfigManager config, OsuGameBase game) { + this.game = game; + letterboxing = config.GetBindable(FrameworkSetting.Letterboxing); + sizeFullscreen = config.GetBindable(FrameworkSetting.SizeFullscreen); + + Container resolutionSettingsContainer; Children = new Drawable[] { - new SettingsEnumDropdown + windowModeDropdown = new SettingsEnumDropdown { LabelText = "Screen mode", Bindable = config.GetBindable(FrameworkSetting.WindowMode), }, + resolutionSettingsContainer = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }, new SettingsCheckbox { LabelText = "Letterboxing", @@ -62,15 +81,52 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics }, }; - letterboxing.ValueChanged += isVisible => + var resolutions = getResolutions(); + + if (resolutions.Count > 1) + { + resolutionSettingsContainer.Child = resolutionDropdown = new SettingsDropdown + { + LabelText = "Resolution", + ShowsDefaultIndicator = false, + Items = resolutions, + Bindable = sizeFullscreen + }; + + windowModeDropdown.Bindable.BindValueChanged(windowMode => + { + if (windowMode == WindowMode.Fullscreen) + { + resolutionDropdown.Show(); + sizeFullscreen.TriggerChange(); + } + else + resolutionDropdown.Hide(); + }, true); + } + + letterboxing.BindValueChanged(isVisible => { letterboxSettings.ClearTransforms(); letterboxSettings.AutoSizeAxes = isVisible ? Axes.Y : Axes.None; if (!isVisible) letterboxSettings.ResizeHeightTo(0, transition_duration, Easing.OutQuint); - }; - letterboxing.TriggerChange(); + }, true); + } + + private IReadOnlyList> getResolutions() + { + var resolutions = new KeyValuePair("Default", new Size(9999, 9999)).Yield(); + + if (game.Window != null) + resolutions = resolutions.Concat(game.Window.AvailableResolutions + .Where(r => r.Width >= 800 && r.Height >= 600) + .OrderByDescending(r => r.Width) + .ThenByDescending(r => r.Height) + .Select(res => new KeyValuePair($"{res.Width}x{res.Height}", new Size(res.Width, res.Height))) + .Distinct()); + return resolutions.ToList(); } } } diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs index 456d1c9a2f..51b0296ca4 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs @@ -15,7 +15,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input { new SettingsButton { - Text = "Key Configuration", + Text = "Key configuration", Action = keyConfig.ToggleVisibility }, }; diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index 71ab4d3782..7b9b7bf57f 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -5,7 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Input; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; @@ -26,12 +26,12 @@ namespace osu.Game.Overlays.Settings.Sections.Input { new SettingsCheckbox { - LabelText = "Raw Input", + LabelText = "Raw input", Bindable = rawInputToggle }, sensitivity = new SensitivitySetting { - LabelText = "Cursor Sensitivity", + LabelText = "Cursor sensitivity", Bindable = config.GetBindable(FrameworkSetting.CursorSensitivity) }, new SettingsCheckbox @@ -124,18 +124,18 @@ namespace osu.Game.Overlays.Settings.Sections.Input private bool isDragging; - protected override bool OnDragStart(InputState state) + protected override bool OnDragStart(DragStartEvent e) { isDragging = true; - return base.OnDragStart(state); + return base.OnDragStart(e); } - protected override bool OnDragEnd(InputState state) + protected override bool OnDragEnd(DragEndEvent e) { isDragging = false; Current.TriggerChange(); - return base.OnDragEnd(state); + return base.OnDragEnd(e); } public override string TooltipText => Current.Disabled ? "Enable raw input to adjust sensitivity" : Current.Value.ToString(@"0.##x"); diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index 93d1986e3a..835ec808bc 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -7,41 +7,65 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; +using osu.Game.Skinning; namespace osu.Game.Overlays.Settings.Sections.Maintenance { public class GeneralSettings : SettingsSubsection { - private TriangleButton importButton; - private TriangleButton deleteButton; + protected override string Header => "General"; + + private TriangleButton importBeatmapsButton; + private TriangleButton importSkinsButton; + private TriangleButton deleteSkinsButton; + private TriangleButton deleteBeatmapsButton; private TriangleButton restoreButton; private TriangleButton undeleteButton; - protected override string Header => "General"; - [BackgroundDependencyLoader] - private void load(BeatmapManager beatmaps, DialogOverlay dialogOverlay) + private void load(BeatmapManager beatmaps, SkinManager skins, DialogOverlay dialogOverlay) { Children = new Drawable[] { - importButton = new SettingsButton + importBeatmapsButton = new SettingsButton { Text = "Import beatmaps from stable", Action = () => { - importButton.Enabled.Value = false; - beatmaps.ImportFromStable().ContinueWith(t => Schedule(() => importButton.Enabled.Value = true)); + importBeatmapsButton.Enabled.Value = false; + beatmaps.ImportFromStableAsync().ContinueWith(t => Schedule(() => importBeatmapsButton.Enabled.Value = true)); } }, - deleteButton = new DangerousSettingsButton + deleteBeatmapsButton = new DangerousSettingsButton { Text = "Delete ALL beatmaps", Action = () => { dialogOverlay?.Push(new DeleteAllBeatmapsDialog(() => { - deleteButton.Enabled.Value = false; - Task.Run(() => beatmaps.Delete(beatmaps.GetAllUsableBeatmapSets())).ContinueWith(t => Schedule(() => deleteButton.Enabled.Value = true)); + deleteBeatmapsButton.Enabled.Value = false; + Task.Run(() => beatmaps.Delete(beatmaps.GetAllUsableBeatmapSets())).ContinueWith(t => Schedule(() => deleteBeatmapsButton.Enabled.Value = true)); + })); + } + }, + importSkinsButton = new SettingsButton + { + Text = "Import skins from stable", + Action = () => + { + importSkinsButton.Enabled.Value = false; + skins.ImportFromStableAsync().ContinueWith(t => Schedule(() => importSkinsButton.Enabled.Value = true)); + } + }, + deleteSkinsButton = new DangerousSettingsButton + { + Text = "Delete ALL skins", + Action = () => + { + dialogOverlay?.Push(new DeleteAllBeatmapsDialog(() => + { + deleteSkinsButton.Enabled.Value = false; + Task.Run(() => skins.Delete(skins.GetAllUserSkins())).ContinueWith(t => Schedule(() => deleteSkinsButton.Enabled.Value = true)); })); } }, diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index df8ebaf4aa..15787d29f7 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -51,10 +51,10 @@ namespace osu.Game.Overlays.Settings.Sections }, }; - skins.ItemAdded += onItemsChanged; - skins.ItemRemoved += onItemsChanged; + skins.ItemAdded += itemAdded; + skins.ItemRemoved += itemRemoved; - reloadSkins(); + skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair(s.ToString(), s.ID)); var skinBindable = config.GetBindable(OsuSetting.Skin); @@ -65,9 +65,8 @@ namespace osu.Game.Overlays.Settings.Sections skinDropdown.Bindable = skinBindable; } - private void reloadSkins() => skinDropdown.Items = skins.GetAllUsableSkins().Select(s => new KeyValuePair(s.ToString(), s.ID)); - - private void onItemsChanged(SkinInfo _) => Schedule(reloadSkins); + private void itemRemoved(SkinInfo s) => skinDropdown.Items = skinDropdown.Items.Where(i => i.Value != s.ID); + private void itemAdded(SkinInfo s) => skinDropdown.Items = skinDropdown.Items.Append(new KeyValuePair(s.ToString(), s.ID)); protected override void Dispose(bool isDisposing) { @@ -75,8 +74,8 @@ namespace osu.Game.Overlays.Settings.Sections if (skins != null) { - skins.ItemAdded -= onItemsChanged; - skins.ItemRemoved -= onItemsChanged; + skins.ItemAdded -= itemAdded; + skins.ItemRemoved -= itemRemoved; } } diff --git a/osu.Game/Overlays/Settings/SettingsItem.cs b/osu.Game/Overlays/Settings/SettingsItem.cs index 0f8d3aa2ac..4f947cd812 100644 --- a/osu.Game/Overlays/Settings/SettingsItem.cs +++ b/osu.Game/Overlays/Settings/SettingsItem.cs @@ -12,8 +12,7 @@ using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using OpenTK; @@ -168,25 +167,25 @@ namespace osu.Game.Overlays.Settings public string TooltipText => "Revert to default"; - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true; + protected override bool OnMouseDown(MouseDownEvent e) => true; - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) => true; + protected override bool OnMouseUp(MouseUpEvent e) => true; - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { if (bindable != null && !bindable.Disabled) bindable.SetDefault(); return true; } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { hovering = true; UpdateState(); return false; } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { hovering = false; UpdateState(); diff --git a/osu.Game/Overlays/Settings/SettingsSection.cs b/osu.Game/Overlays/Settings/SettingsSection.cs index 9555f0fbc5..58714208c0 100644 --- a/osu.Game/Overlays/Settings/SettingsSection.cs +++ b/osu.Game/Overlays/Settings/SettingsSection.cs @@ -73,7 +73,7 @@ namespace osu.Game.Overlays.Settings }, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Children = new[] + Children = new Drawable[] { new OsuSpriteText { diff --git a/osu.Game/Overlays/Settings/Sidebar.cs b/osu.Game/Overlays/Settings/Sidebar.cs index fdda5b870e..862011b6e2 100644 --- a/osu.Game/Overlays/Settings/Sidebar.cs +++ b/osu.Game/Overlays/Settings/Sidebar.cs @@ -9,7 +9,7 @@ using OpenTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.Threading; using osu.Game.Overlays.Toolbar; @@ -55,25 +55,25 @@ namespace osu.Game.Overlays.Settings private ScheduledDelegate expandEvent; private ExpandedState state; - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { queueExpandIfHovering(); return true; } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { expandEvent?.Cancel(); lastHoveredButton = null; State = ExpandedState.Contracted; - base.OnHoverLost(state); + base.OnHoverLost(e); } - protected override bool OnMouseMove(InputState state) + protected override bool OnMouseMove(MouseMoveEvent e) { queueExpandIfHovering(); - return base.OnMouseMove(state); + return base.OnMouseMove(e); } private class SidebarScrollContainer : ScrollContainer diff --git a/osu.Game/Overlays/Settings/SidebarButton.cs b/osu.Game/Overlays/Settings/SidebarButton.cs index ac2c840c94..b6d1cf609e 100644 --- a/osu.Game/Overlays/Settings/SidebarButton.cs +++ b/osu.Game/Overlays/Settings/SidebarButton.cs @@ -9,14 +9,15 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.States; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Settings { - public class SidebarButton : OsuButton + public class SidebarButton : Button { private readonly SpriteIcon drawableIcon; private readonly SpriteText headerText; @@ -97,7 +98,8 @@ namespace osu.Game.Overlays.Settings Width = 5, Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, - } + }, + new HoverClickSounds(HoverSampleSet.Loud), }); } @@ -107,22 +109,22 @@ namespace osu.Game.Overlays.Settings selectionIndicator.Colour = colours.Yellow; } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { Action?.Invoke(section); - return base.OnClick(state); + return base.OnClick(e); } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { Background.FadeTo(0.4f, 200); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { Background.FadeTo(0, 200); - base.OnHoverLost(state); + base.OnHoverLost(e); } } } diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs index 83e121e998..c971ab5005 100644 --- a/osu.Game/Overlays/SettingsOverlay.cs +++ b/osu.Game/Overlays/SettingsOverlay.cs @@ -11,7 +11,7 @@ using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Settings; @@ -177,10 +177,10 @@ namespace osu.Game.Overlays public override bool AcceptsFocus => true; - protected override void OnFocus(InputState state) + protected override void OnFocus(FocusEvent e) { GetContainingInputManager().ChangeFocus(searchTextBox); - base.OnFocus(state); + base.OnFocus(e); } protected override void UpdateAfterChildren() diff --git a/osu.Game/Overlays/Social/SocialPanel.cs b/osu.Game/Overlays/Social/SocialPanel.cs index b0455f7edd..cfee639d53 100644 --- a/osu.Game/Overlays/Social/SocialPanel.cs +++ b/osu.Game/Overlays/Social/SocialPanel.cs @@ -6,7 +6,7 @@ using OpenTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Users; namespace osu.Game.Overlays.Social @@ -35,20 +35,20 @@ namespace osu.Game.Overlays.Social Colour = Color4.Black.Opacity(0.3f), }; - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { Content.TweenEdgeEffectTo(edgeEffectHovered, hover_transition_time, Easing.OutQuint); Content.MoveToY(-4, hover_transition_time, Easing.OutQuint); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { Content.TweenEdgeEffectTo(edgeEffectNormal, hover_transition_time, Easing.OutQuint); Content.MoveToY(0, hover_transition_time, Easing.OutQuint); - base.OnHoverLost(state); + base.OnHoverLost(e); } protected override void LoadComplete() diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 2eabf1eb74..611b42383e 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -11,7 +11,7 @@ using OpenTK; using osu.Framework.Graphics.Shapes; using osu.Framework.Allocation; using osu.Framework.Configuration; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; namespace osu.Game.Overlays.Toolbar { @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Toolbar private readonly ToolbarUserArea userArea; - protected override bool BlockPassThroughMouse => false; + protected override bool BlockPositionalInput => false; private const double transition_time = 500; @@ -83,14 +83,14 @@ namespace osu.Game.Overlays.Toolbar [BackgroundDependencyLoader(true)] private void load(OsuGame osuGame) { - if (osuGame != null) - overlayActivationMode.BindTo(osuGame.OverlayActivationMode); - StateChanged += visibility => { if (overlayActivationMode == OverlayActivation.Disabled) State = Visibility.Hidden; }; + + if (osuGame != null) + overlayActivationMode.BindTo(osuGame.OverlayActivationMode); } public class ToolbarBackground : Container @@ -121,14 +121,14 @@ namespace osu.Game.Overlays.Toolbar }; } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { solidBackground.FadeTo(alpha_hovering, transition_time, Easing.OutQuint); gradientBackground.FadeIn(transition_time, Easing.OutQuint); return true; } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { solidBackground.FadeTo(alpha_normal, transition_time, Easing.OutQuint); gradientBackground.FadeOut(transition_time, Easing.OutQuint); diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index 74af5d7e9c..5cb26974e6 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -11,8 +11,7 @@ using osu.Game.Graphics.Sprites; using OpenTK; using OpenTK.Graphics; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; @@ -145,22 +144,22 @@ namespace osu.Game.Overlays.Toolbar }; } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true; + protected override bool OnMouseDown(MouseDownEvent e) => true; - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { HoverBackground.FlashColour(Color4.White.Opacity(100), 500, Easing.OutQuint); - return base.OnClick(state); + return base.OnClick(e); } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { HoverBackground.FadeIn(200); tooltipContainer.FadeIn(100); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { HoverBackground.FadeOut(200); tooltipContainer.FadeOut(100); diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs index f2744ae83f..fa35e53531 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs @@ -11,8 +11,7 @@ using OpenTK.Input; using OpenTK.Graphics; using osu.Framework.Configuration; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Rulesets; namespace osu.Game.Overlays.Toolbar @@ -86,13 +85,13 @@ namespace osu.Game.Overlays.Toolbar ruleset.BindTo(parentRuleset); } - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + protected override bool OnKeyDown(KeyDownEvent e) { - base.OnKeyDown(state, args); + base.OnKeyDown(e); - if (state.Keyboard.ControlPressed && !args.Repeat && args.Key >= Key.Number1 && args.Key <= Key.Number9) + if (e.ControlPressed && !e.Repeat && e.Key >= Key.Number1 && e.Key <= Key.Number9) { - int requested = args.Key - Key.Number1; + int requested = e.Key - Key.Number1; RulesetInfo found = rulesets.AvailableRulesets.Skip(requested).FirstOrDefault(); if (found != null) @@ -103,8 +102,10 @@ namespace osu.Game.Overlays.Toolbar return false; } - public override bool HandleKeyboardInput => !ruleset.Disabled && base.HandleKeyboardInput; - public override bool HandleMouseInput => !ruleset.Disabled && base.HandleMouseInput; + public override bool HandleNonPositionalInput => !ruleset.Disabled && base.HandleNonPositionalInput; + public override bool HandlePositionalInput => !ruleset.Disabled && base.HandlePositionalInput; + + public override bool PropagatePositionalInputSubTree => !ruleset.Disabled && base.PropagatePositionalInputSubTree; private void disabledChanged(bool isDisabled) => this.FadeColour(isDisabled ? Color4.Gray : Color4.White, 300); diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index 11b68b0e09..c106446fe0 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -73,16 +73,17 @@ namespace osu.Game.Overlays FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.Out); } - public void ShowUser(long userId) - { - if (userId == Header.User.Id) - return; - - ShowUser(new User { Id = userId }); - } + public void ShowUser(long userId) => ShowUser(new User { Id = userId }); public void ShowUser(User user, bool fetchOnline = true) { + if (user == User.SYSTEM_USER) return; + + Show(); + + if (user.Id == Header?.User?.Id) + return; + userReq?.Cancel(); Clear(); lastSection = null; @@ -97,6 +98,7 @@ namespace osu.Game.Overlays new BeatmapsSection(), new KudosuSection() }; + tabs = new ProfileTabControl { RelativeSizeAxes = Axes.X, @@ -161,7 +163,6 @@ namespace osu.Game.Overlays userLoadComplete(user); } - Show(); sectionsContainer.ScrollToTop(); } diff --git a/osu.Game/Overlays/Volume/MuteButton.cs b/osu.Game/Overlays/Volume/MuteButton.cs index 47169d7a7b..a099a10096 100644 --- a/osu.Game/Overlays/Volume/MuteButton.cs +++ b/osu.Game/Overlays/Volume/MuteButton.cs @@ -9,7 +9,7 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics; using OpenTK; using OpenTK.Graphics; @@ -63,18 +63,18 @@ namespace osu.Game.Overlays.Volume Current.TriggerChange(); } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { this.TransformTo("BorderColour", hoveredColour, 500, Easing.OutQuint); return false; } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { this.TransformTo("BorderColour", unhoveredColour, 500, Easing.OutQuint); } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { Current.Value = !Current.Value; return true; diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs index a6c98aa97e..86be652c8c 100644 --- a/osu.Game/Overlays/Volume/VolumeMeter.cs +++ b/osu.Game/Overlays/Volume/VolumeMeter.cs @@ -11,7 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.MathUtils; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -239,21 +239,21 @@ namespace osu.Game.Overlays.Volume adjustAccumulator = 0; } - protected override bool OnScroll(InputState state) + protected override bool OnScroll(ScrollEvent e) { - adjust(state.Mouse.ScrollDelta.Y, state.Mouse.HasPreciseScroll); + adjust(e.ScrollDelta.Y, e.IsPrecise); return true; } private const float transition_length = 500; - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { this.ScaleTo(1.04f, transition_length, Easing.OutExpo); return false; } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { this.ScaleTo(1f, transition_length, Easing.OutExpo); } diff --git a/osu.Game/Overlays/VolumeOverlay.cs b/osu.Game/Overlays/VolumeOverlay.cs index 4dcdd23768..d45d097a09 100644 --- a/osu.Game/Overlays/VolumeOverlay.cs +++ b/osu.Game/Overlays/VolumeOverlay.cs @@ -9,7 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.Threading; using osu.Game.Graphics; using osu.Game.Input.Bindings; @@ -28,7 +28,7 @@ namespace osu.Game.Overlays private VolumeMeter volumeMeterMusic; private MuteButton muteButton; - protected override bool BlockPassThroughMouse => false; + protected override bool BlockPositionalInput => false; private readonly BindableDouble muteAdjustment = new BindableDouble(); @@ -143,23 +143,23 @@ namespace osu.Game.Overlays this.FadeOut(100); } - protected override bool OnMouseMove(InputState state) + protected override bool OnMouseMove(MouseMoveEvent e) { // keep the scheduled event correctly timed as long as we have movement. schedulePopOut(); - return base.OnMouseMove(state); + return base.OnMouseMove(e); } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { schedulePopOut(); return true; } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { schedulePopOut(); - base.OnHoverLost(state); + base.OnHoverLost(e); } private void schedulePopOut() diff --git a/osu.Game/Overlays/WaveOverlayContainer.cs b/osu.Game/Overlays/WaveOverlayContainer.cs index 97f52d88f7..c5a4953c5e 100644 --- a/osu.Game/Overlays/WaveOverlayContainer.cs +++ b/osu.Game/Overlays/WaveOverlayContainer.cs @@ -11,7 +11,7 @@ namespace osu.Game.Overlays { protected readonly WaveContainer Waves; - protected override bool BlockPassThroughKeyboard => true; + protected override bool BlockNonPositionalInput => true; protected override Container Content => Waves; protected WaveOverlayContainer() diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index a3253250f2..8060ac742a 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -165,6 +165,6 @@ namespace osu.Game.Rulesets.Edit /// /// Creates a which provides a layer above or below the . /// - protected virtual ScalableContainer CreateLayerContainer() => new ScalableContainer { RelativeSizeAxes = Axes.Both }; + protected virtual Container CreateLayerContainer() => new Container { RelativeSizeAxes = Axes.Both }; } } diff --git a/osu.Game/Rulesets/Edit/HitObjectMask.cs b/osu.Game/Rulesets/Edit/HitObjectMask.cs index ada026b32f..636ea418f3 100644 --- a/osu.Game/Rulesets/Edit/HitObjectMask.cs +++ b/osu.Game/Rulesets/Edit/HitObjectMask.cs @@ -5,7 +5,7 @@ using System; using osu.Framework; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; -using osu.Framework.Input.EventArgs; +using osu.Framework.Input.Events; using osu.Framework.Input.States; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Objects.Drawables; @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Edit /// /// Invoked when this has requested drag. /// - public event Action DragRequested; + public event Action DragRequested; /// /// The which this applies to. @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Edit public readonly DrawableHitObject HitObject; protected override bool ShouldBeAlive => HitObject.IsAlive && HitObject.IsPresent || State == SelectionState.Selected; - public override bool HandleMouseInput => ShouldBeAlive; + public override bool HandlePositionalInput => ShouldBeAlive; public override bool RemoveWhenNotAlive => false; public HitObjectMask(DrawableHitObject hitObject) @@ -96,36 +96,36 @@ namespace osu.Game.Rulesets.Edit private bool selectionRequested; - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(MouseDownEvent e) { selectionRequested = false; if (State == SelectionState.NotSelected) { - SelectionRequested?.Invoke(this, state); + SelectionRequested?.Invoke(this, e.CurrentState); selectionRequested = true; } return IsSelected; } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { if (State == SelectionState.Selected && !selectionRequested) { selectionRequested = true; - SelectionRequested?.Invoke(this, state); + SelectionRequested?.Invoke(this, e.CurrentState); return true; } - return base.OnClick(state); + return base.OnClick(e); } - protected override bool OnDragStart(InputState state) => true; + protected override bool OnDragStart(DragStartEvent e) => true; - protected override bool OnDrag(InputState state) + protected override bool OnDrag(DragEvent e) { - DragRequested?.Invoke(this, state); + DragRequested?.Invoke(this, e.Delta, e.CurrentState); return true; } diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index 65b2ef75c4..e8e775a20d 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Judgements /// /// A drawable object which visualises the hit result of a . /// - public class DrawableJudgement : Container + public class DrawableJudgement : CompositeDrawable { private const float judgement_size = 80; @@ -29,6 +29,7 @@ namespace osu.Game.Rulesets.Judgements public readonly DrawableHitObject JudgedObject; + protected Container JudgementBody; protected SpriteText JudgementText; /// @@ -49,14 +50,20 @@ namespace osu.Game.Rulesets.Judgements { this.colours = colours; - Child = new SkinnableDrawable($"Play/{Result.Type}", _ => JudgementText = new OsuSpriteText + InternalChild = JudgementBody = new Container { - Text = Result.Type.GetDescription().ToUpperInvariant(), - Font = @"Venera", - Colour = judgementColour(Result.Type), - Scale = new Vector2(0.85f, 1), - TextSize = 12 - }, restrictSize: false); + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Child = new SkinnableDrawable($"Play/{Result.Type}", _ => JudgementText = new OsuSpriteText + { + Text = Result.Type.GetDescription().ToUpperInvariant(), + Font = @"Venera", + Colour = judgementColour(Result.Type), + Scale = new Vector2(0.85f, 1), + TextSize = 12 + }, restrictSize: false) + }; } protected override void LoadComplete() @@ -70,17 +77,17 @@ namespace osu.Game.Rulesets.Judgements case HitResult.None: break; case HitResult.Miss: - this.ScaleTo(1.6f); - this.ScaleTo(1, 100, Easing.In); + JudgementBody.ScaleTo(1.6f); + JudgementBody.ScaleTo(1, 100, Easing.In); - this.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint); - this.RotateTo(40, 800, Easing.InQuint); + JudgementBody.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint); + JudgementBody.RotateTo(40, 800, Easing.InQuint); this.Delay(600).FadeOut(200); break; default: - this.ScaleTo(0.9f); - this.ScaleTo(1, 500, Easing.OutElastic); + JudgementBody.ScaleTo(0.9f); + JudgementBody.ScaleTo(1, 500, Easing.OutElastic); this.Delay(100).FadeOut(400); break; @@ -103,9 +110,9 @@ namespace osu.Game.Rulesets.Judgements return colours.Yellow; case HitResult.Miss: return colours.Red; + default: + return Color4.White; } - - return Color4.White; } } } diff --git a/osu.Game/Rulesets/Mods/IApplicableToDrawableHitObject.cs b/osu.Game/Rulesets/Mods/IApplicableToDrawableHitObject.cs index d871cdd322..fca04ca513 100644 --- a/osu.Game/Rulesets/Mods/IApplicableToDrawableHitObject.cs +++ b/osu.Game/Rulesets/Mods/IApplicableToDrawableHitObject.cs @@ -13,6 +13,7 @@ namespace osu.Game.Rulesets.Mods { /// /// Applies this to a list of s. + /// This will only be invoked with top-level s. Access if adjusting nested objects is necessary. /// /// The list of s to apply to. void ApplyToDrawableHitObjects(IEnumerable drawables); diff --git a/osu.Game/Rulesets/Objects/BezierApproximator.cs b/osu.Game/Rulesets/Objects/BezierApproximator.cs index 6094934510..a1803e32f7 100644 --- a/osu.Game/Rulesets/Objects/BezierApproximator.cs +++ b/osu.Game/Rulesets/Objects/BezierApproximator.cs @@ -1,25 +1,26 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Collections.Generic; using OpenTK; namespace osu.Game.Rulesets.Objects { - public class BezierApproximator + public readonly ref struct BezierApproximator { private readonly int count; - private readonly List controlPoints; + private readonly ReadOnlySpan controlPoints; private readonly Vector2[] subdivisionBuffer1; private readonly Vector2[] subdivisionBuffer2; private const float tolerance = 0.25f; private const float tolerance_sq = tolerance * tolerance; - public BezierApproximator(List controlPoints) + public BezierApproximator(ReadOnlySpan controlPoints) { this.controlPoints = controlPoints; - count = controlPoints.Count; + count = controlPoints.Length; subdivisionBuffer1 = new Vector2[count]; subdivisionBuffer2 = new Vector2[count * 2 - 1]; diff --git a/osu.Game/Rulesets/Objects/CatmullApproximator.cs b/osu.Game/Rulesets/Objects/CatmullApproximator.cs index fabb55e8f6..78f8e471f3 100644 --- a/osu.Game/Rulesets/Objects/CatmullApproximator.cs +++ b/osu.Game/Rulesets/Objects/CatmullApproximator.cs @@ -1,40 +1,40 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Collections.Generic; using OpenTK; namespace osu.Game.Rulesets.Objects { - public class CatmullApproximator + public readonly ref struct CatmullApproximator { /// /// The amount of pieces to calculate for each controlpoint quadruplet. /// private const int detail = 50; - private readonly List controlPoints; + private readonly ReadOnlySpan controlPoints; - public CatmullApproximator(List controlPoints) + public CatmullApproximator(ReadOnlySpan controlPoints) { this.controlPoints = controlPoints; } - /// /// Creates a piecewise-linear approximation of a Catmull-Rom spline. /// /// A list of vectors representing the piecewise-linear approximation. public List CreateCatmull() { - var result = new List(); + var result = new List((controlPoints.Length - 1) * detail * 2); - for (int i = 0; i < controlPoints.Count - 1; i++) + for (int i = 0; i < controlPoints.Length - 1; i++) { var v1 = i > 0 ? controlPoints[i - 1] : controlPoints[i]; var v2 = controlPoints[i]; - var v3 = i < controlPoints.Count - 1 ? controlPoints[i + 1] : v2 + v2 - v1; - var v4 = i < controlPoints.Count - 2 ? controlPoints[i + 2] : v3 + v3 - v2; + var v3 = i < controlPoints.Length - 1 ? controlPoints[i + 1] : v2 + v2 - v1; + var v4 = i < controlPoints.Length - 2 ? controlPoints[i + 2] : v3 + v3 - v2; for (int c = 0; c < detail; c++) { diff --git a/osu.Game/Rulesets/Objects/CircularArcApproximator.cs b/osu.Game/Rulesets/Objects/CircularArcApproximator.cs index 7fb8d8a40e..28d7442aaf 100644 --- a/osu.Game/Rulesets/Objects/CircularArcApproximator.cs +++ b/osu.Game/Rulesets/Objects/CircularArcApproximator.cs @@ -8,21 +8,15 @@ using OpenTK; namespace osu.Game.Rulesets.Objects { - public class CircularArcApproximator + public readonly ref struct CircularArcApproximator { - private readonly Vector2 a; - private readonly Vector2 b; - private readonly Vector2 c; - - private int amountPoints; - private const float tolerance = 0.1f; - public CircularArcApproximator(Vector2 a, Vector2 b, Vector2 c) + private readonly ReadOnlySpan controlPoints; + + public CircularArcApproximator(ReadOnlySpan controlPoints) { - this.a = a; - this.b = b; - this.c = c; + this.controlPoints = controlPoints; } /// @@ -31,6 +25,10 @@ namespace osu.Game.Rulesets.Objects /// A list of vectors representing the piecewise-linear approximation. public List CreateArc() { + Vector2 a = controlPoints[0]; + Vector2 b = controlPoints[1]; + Vector2 c = controlPoints[2]; + float aSq = (b - c).LengthSquared; float bSq = (a - c).LengthSquared; float cSq = (a - b).LengthSquared; @@ -81,7 +79,7 @@ namespace osu.Game.Rulesets.Objects // is: 2 * Math.Acos(1 - TOLERANCE / r) // The special case is required for extremely short sliders where the radius is smaller than // the tolerance. This is a pathological rather than a realistic case. - amountPoints = 2 * r <= tolerance ? 2 : Math.Max(2, (int)Math.Ceiling(thetaRange / (2 * Math.Acos(1 - tolerance / r)))); + int amountPoints = 2 * r <= tolerance ? 2 : Math.Max(2, (int)Math.Ceiling(thetaRange / (2 * Math.Acos(1 - tolerance / r)))); List output = new List(amountPoints); diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 7e3e955740..bcf84b375f 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -7,6 +7,8 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Extensions.TypeExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Primitives; using osu.Game.Audio; using osu.Game.Graphics; using osu.Game.Rulesets.Judgements; @@ -76,8 +78,8 @@ namespace osu.Game.Rulesets.Objects.Drawables private bool judgementOccurred; public bool Interactive = true; - public override bool HandleKeyboardInput => Interactive; - public override bool HandleMouseInput => Interactive; + public override bool HandleNonPositionalInput => Interactive; + public override bool HandlePositionalInput => Interactive; public override bool RemoveWhenNotAlive => false; public override bool RemoveCompletedTransforms => false; @@ -143,7 +145,7 @@ namespace osu.Game.Rulesets.Objects.Drawables public event Action ApplyCustomUpdateState; /// - /// Plays all the hitsounds for this . + /// Plays all the hit sounds for this . /// public void PlaySamples() => Samples?.Play(); @@ -165,6 +167,14 @@ namespace osu.Game.Rulesets.Objects.Drawables } } + public override bool UpdateSubTreeMasking(Drawable source, RectangleF maskingBounds) + { + if (!AllJudged) + return false; + + return base.UpdateSubTreeMasking(source, maskingBounds); + } + protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index beb9620f78..f5613e927f 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -1,10 +1,8 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using System.Collections.Generic; using Newtonsoft.Json; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Lists; using osu.Game.Audio; using osu.Game.Beatmaps; @@ -58,10 +56,10 @@ namespace osu.Game.Rulesets.Objects /// public HitWindows HitWindows { get; set; } - private readonly Lazy> nestedHitObjects = new Lazy>(() => new SortedList((h1, h2) => h1.StartTime.CompareTo(h2.StartTime))); + private readonly SortedList nestedHitObjects = new SortedList(compareObjects); [JsonIgnore] - public IReadOnlyList NestedHitObjects => nestedHitObjects.Value; + public IReadOnlyList NestedHitObjects => nestedHitObjects; /// /// Applies default values to this HitObject. @@ -72,18 +70,14 @@ namespace osu.Game.Rulesets.Objects { ApplyDefaultsToSelf(controlPointInfo, difficulty); - if (nestedHitObjects.IsValueCreated) - nestedHitObjects.Value.Clear(); + nestedHitObjects.Clear(); CreateNestedHitObjects(); - if (nestedHitObjects.IsValueCreated) + foreach (var h in nestedHitObjects) { - nestedHitObjects.Value.ForEach(h => - { - h.HitWindows = HitWindows; - h.ApplyDefaults(controlPointInfo, difficulty); - }); + h.HitWindows = HitWindows; + h.ApplyDefaults(controlPointInfo, difficulty); } } @@ -104,7 +98,7 @@ namespace osu.Game.Rulesets.Objects { } - protected void AddNested(HitObject hitObject) => nestedHitObjects.Value.Add(hitObject); + protected void AddNested(HitObject hitObject) => nestedHitObjects.Add(hitObject); /// /// Creates the that represents the scoring information for this . @@ -120,5 +114,7 @@ namespace osu.Game.Rulesets.Objects /// /// protected virtual HitWindows CreateHitWindows() => new HitWindows(); + + private static int compareObjects(HitObject first, HitObject second) => first.StartTime.CompareTo(second.StartTime); } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs index 802080aedb..9c9fc2e742 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch }; } - protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples) + protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples) { newCombo |= forceNewCombo; comboOffset += extraComboOffset; diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 8236333a3f..965e76d27a 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -10,6 +10,8 @@ using System.IO; using osu.Game.Beatmaps.Formats; using osu.Game.Audio; using System.Linq; +using JetBrains.Annotations; +using osu.Framework.Logging; using osu.Framework.MathUtils; namespace osu.Game.Rulesets.Objects.Legacy @@ -37,6 +39,7 @@ namespace osu.Game.Rulesets.Objects.Legacy FormatVersion = formatVersion; } + [CanBeNull] public override HitObject Parse(string text) { try @@ -69,10 +72,18 @@ namespace osu.Game.Rulesets.Objects.Legacy { CurveType curveType = CurveType.Catmull; double length = 0; - var points = new List { Vector2.Zero }; - string[] pointsplit = split[5].Split('|'); - foreach (string t in pointsplit) + string[] pointSplit = split[5].Split('|'); + + int pointCount = 1; + foreach (var t in pointSplit) + if (t.Length > 1) + pointCount++; + + var points = new Vector2[pointCount]; + + int pointIndex = 1; + foreach (string t in pointSplit) { if (t.Length == 1) { @@ -91,16 +102,18 @@ namespace osu.Game.Rulesets.Objects.Legacy curveType = CurveType.PerfectCurve; break; } + continue; } string[] temp = t.Split(':'); - points.Add(new Vector2((int)Convert.ToDouble(temp[0], CultureInfo.InvariantCulture), (int)Convert.ToDouble(temp[1], CultureInfo.InvariantCulture)) - pos); + points[pointIndex++] = new Vector2((int)Convert.ToDouble(temp[0], CultureInfo.InvariantCulture), (int)Convert.ToDouble(temp[1], CultureInfo.InvariantCulture)) - pos; } // osu-stable special-cased colinear perfect curves to a CurveType.Linear - bool isLinear(List p) => Precision.AlmostEquals(0, (p[1].Y - p[0].Y) * (p[2].X - p[0].X) - (p[1].X - p[0].X) * (p[2].Y - p[0].Y)); - if (points.Count == 3 && curveType == CurveType.PerfectCurve && isLinear(points)) + bool isLinear(Vector2[] p) => Precision.AlmostEquals(0, (p[1].Y - p[0].Y) * (p[2].X - p[0].X) - (p[1].X - p[0].X) * (p[2].Y - p[0].Y)); + + if (points.Length == 3 && curveType == CurveType.PerfectCurve && isLinear(points)) curveType = CurveType.Linear; int repeatCount = Convert.ToInt32(split[6], CultureInfo.InvariantCulture); @@ -191,7 +204,10 @@ namespace osu.Game.Rulesets.Objects.Legacy } if (result == null) - throw new InvalidOperationException($@"Unknown hit object type {type}."); + { + Logger.Log($"Unknown hit object type: {type}. Skipped.", level: LogLevel.Error); + return null; + } result.StartTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture) + Offset; result.Samples = convertSoundType(soundType, bankInfo); @@ -256,7 +272,7 @@ namespace osu.Game.Rulesets.Objects.Legacy /// The slider repeat count. /// The samples to be played when the repeat nodes are hit. This includes the head and tail of the slider. /// The hit object. - protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples); + protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples); /// /// Creates a legacy Spinner-type hit object. diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs index ef1eecec3d..93c49ea3ce 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Objects.Legacy /// s don't need a curve since they're converted to ruleset-specific hitobjects. /// public SliderCurve Curve { get; } = null; - public List ControlPoints { get; set; } + public Vector2[] ControlPoints { get; set; } public CurveType CurveType { get; set; } public double Distance { get; set; } diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs index 6f59965e18..68e05f6223 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania }; } - protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples) + protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples) { return new ConvertSlider { diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs index acd0de8688..f3c815fc32 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu }; } - protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples) + protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples) { newCombo |= forceNewCombo; comboOffset += extraComboOffset; diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs index e5904825c2..985a032640 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko return new ConvertHit(); } - protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples) + protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples) { return new ConvertSlider { diff --git a/osu.Game/Rulesets/Objects/SliderCurve.cs b/osu.Game/Rulesets/Objects/SliderCurve.cs index 3932d8ed9d..dfccdf68f2 100644 --- a/osu.Game/Rulesets/Objects/SliderCurve.cs +++ b/osu.Game/Rulesets/Objects/SliderCurve.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 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.Framework.MathUtils; @@ -13,7 +14,7 @@ namespace osu.Game.Rulesets.Objects { public double Distance; - public List ControlPoints; + public Vector2[] ControlPoints; public CurveType CurveType = CurveType.PerfectCurve; @@ -22,19 +23,23 @@ namespace osu.Game.Rulesets.Objects private readonly List calculatedPath = new List(); private readonly List cumulativeLength = new List(); - private List calculateSubpath(List subControlPoints) + private List calculateSubpath(ReadOnlySpan subControlPoints) { switch (CurveType) { case CurveType.Linear: - return subControlPoints; + var result = new List(subControlPoints.Length); + foreach (var c in subControlPoints) + result.Add(c); + + return result; case CurveType.PerfectCurve: //we can only use CircularArc iff we have exactly three control points and no dissection. - if (ControlPoints.Count != 3 || subControlPoints.Count != 3) + if (ControlPoints.Length != 3 || subControlPoints.Length != 3) break; // Here we have exactly 3 control points. Attempt to fit a circular arc. - List subpath = new CircularArcApproximator(subControlPoints[0], subControlPoints[1], subControlPoints[2]).CreateArc(); + List subpath = new CircularArcApproximator(subControlPoints).CreateArc(); // If for some reason a circular arc could not be fit to the 3 given points, fall back to a numerically stable bezier approximation. if (subpath.Count == 0) @@ -55,18 +60,23 @@ namespace osu.Game.Rulesets.Objects // Sliders may consist of various subpaths separated by two consecutive vertices // with the same position. The following loop parses these subpaths and computes // their shape independently, consecutively appending them to calculatedPath. - List subControlPoints = new List(); - for (int i = 0; i < ControlPoints.Count; ++i) + + int start = 0; + int end = 0; + + for (int i = 0; i < ControlPoints.Length; ++i) { - subControlPoints.Add(ControlPoints[i]); - if (i == ControlPoints.Count - 1 || ControlPoints[i] == ControlPoints[i + 1]) + end++; + + if (i == ControlPoints.Length - 1 || ControlPoints[i] == ControlPoints[i + 1]) { - List subpath = calculateSubpath(subControlPoints); - foreach (Vector2 t in subpath) + ReadOnlySpan cpSpan = ControlPoints.AsSpan().Slice(start, end - start); + + foreach (Vector2 t in calculateSubpath(cpSpan)) if (calculatedPath.Count == 0 || calculatedPath.Last() != t) calculatedPath.Add(t); - subControlPoints.Clear(); + start = end; } } } @@ -166,7 +176,7 @@ namespace osu.Game.Rulesets.Objects /// End progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider). public void GetPathToProgress(List path, double p0, double p1) { - if (calculatedPath.Count == 0 && ControlPoints.Count > 0) + if (calculatedPath.Count == 0 && ControlPoints.Length > 0) Calculate(); double d0 = progressToDistance(p0); @@ -193,7 +203,7 @@ namespace osu.Game.Rulesets.Objects /// public Vector2 PositionAt(double progress) { - if (calculatedPath.Count == 0 && ControlPoints.Count > 0) + if (calculatedPath.Count == 0 && ControlPoints.Length > 0) Calculate(); double d = progressToDistance(progress); diff --git a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs index 54dcb26ae2..69b2f722e7 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; using OpenTK; namespace osu.Game.Rulesets.Objects.Types @@ -19,7 +18,7 @@ namespace osu.Game.Rulesets.Objects.Types /// /// The control points that shape the curve. /// - List ControlPoints { get; } + Vector2[] ControlPoints { get; } /// /// The type of curve. diff --git a/osu.Game/Rulesets/Scoring/Score.cs b/osu.Game/Rulesets/Scoring/Score.cs index dfe7ff0195..02f528791a 100644 --- a/osu.Game/Rulesets/Scoring/Score.cs +++ b/osu.Game/Rulesets/Scoring/Score.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using Newtonsoft.Json; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; using osu.Game.Users; @@ -32,6 +33,7 @@ namespace osu.Game.Rulesets.Scoring public User User; + [JsonIgnore] public Replay Replay; public BeatmapInfo Beatmap; diff --git a/osu.Game/Rulesets/Scoring/ScoreStore.cs b/osu.Game/Rulesets/Scoring/ScoreStore.cs index 69d25fcb67..091cb29a71 100644 --- a/osu.Game/Rulesets/Scoring/ScoreStore.cs +++ b/osu.Game/Rulesets/Scoring/ScoreStore.cs @@ -3,6 +3,7 @@ using System; using System.IO; +using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Beatmaps; using osu.Game.Database; @@ -13,8 +14,6 @@ namespace osu.Game.Rulesets.Scoring { public class ScoreStore : DatabaseBackedStore, ICanAcceptFiles { - private readonly Storage storage; - private readonly BeatmapManager beatmaps; private readonly RulesetStore rulesets; @@ -25,9 +24,8 @@ namespace osu.Game.Rulesets.Scoring // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) private ScoreIPCChannel ipc; - public ScoreStore(Storage storage, DatabaseContextFactory factory, IIpcHost importHost = null, BeatmapManager beatmaps = null, RulesetStore rulesets = null) : base(factory) + public ScoreStore(DatabaseContextFactory factory, IIpcHost importHost = null, BeatmapManager beatmaps = null, RulesetStore rulesets = null) : base(factory) { - this.storage = storage; this.beatmaps = beatmaps; this.rulesets = rulesets; @@ -49,8 +47,14 @@ namespace osu.Game.Rulesets.Scoring public Score ReadReplayFile(string replayFilename) { - using (Stream s = storage.GetStream(Path.Combine(replay_folder, replayFilename))) - return new DatabasedLegacyScoreParser(rulesets, beatmaps).Parse(s); + if (File.Exists(replayFilename)) + { + using (var stream = File.OpenRead(replayFilename)) + return new DatabasedLegacyScoreParser(rulesets, beatmaps).Parse(stream); + } + + Logger.Log($"Replay file {replayFilename} cannot be found", LoggingTarget.Information, LogLevel.Error); + return null; } } } diff --git a/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs b/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs index a5bd6bfde7..4988bac5ce 100644 --- a/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs +++ b/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs @@ -18,9 +18,14 @@ namespace osu.Game.Rulesets.Timing public double StartTime; /// - /// The multiplier which this provides. + /// The aggregate multiplier which this provides. /// - public double Multiplier => 1000 / TimingPoint.BeatLength * DifficultyPoint.SpeedMultiplier; + public double Multiplier => Velocity * DifficultyPoint.SpeedMultiplier * 1000 / TimingPoint.BeatLength; + + /// + /// The velocity multiplier. + /// + public double Velocity = 1; /// /// The that provides the timing information for this . @@ -48,18 +53,6 @@ namespace osu.Game.Rulesets.Timing StartTime = startTime; } - /// - /// Creates a by copying another . - /// - /// The start time of this . - /// The to copy. - public MultiplierControlPoint(double startTime, MultiplierControlPoint other) - : this(startTime) - { - TimingPoint = other.TimingPoint; - DifficultyPoint = other.DifficultyPoint; - } - // ReSharper disable once ImpureMethodCallOnReadonlyValueField public int CompareTo(MultiplierControlPoint other) => StartTime.CompareTo(other?.StartTime); } diff --git a/osu.Game/Rulesets/UI/HitObjectContainer.cs b/osu.Game/Rulesets/UI/HitObjectContainer.cs index af18d98561..261132c56b 100644 --- a/osu.Game/Rulesets/UI/HitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/HitObjectContainer.cs @@ -14,6 +14,11 @@ namespace osu.Game.Rulesets.UI public IEnumerable Objects => InternalChildren.Cast().OrderBy(h => h.HitObject.StartTime); public IEnumerable AliveObjects => AliveInternalChildren.Cast().OrderBy(h => h.HitObject.StartTime); + public HitObjectContainer() + { + RelativeSizeAxes = Axes.Both; + } + public virtual void Add(DrawableHitObject hitObject) => AddInternal(hitObject); public virtual bool Remove(DrawableHitObject hitObject) => RemoveInternal(hitObject); diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index da14fb54d6..886eb3ac0e 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -9,22 +9,31 @@ using osu.Game.Rulesets.Objects.Drawables; using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Configuration; +using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; +using OpenTK; namespace osu.Game.Rulesets.UI { - public abstract class Playfield : ScalableContainer + public abstract class Playfield : CompositeDrawable { /// /// The contained in this Playfield. /// - public HitObjectContainer HitObjects { get; private set; } + public HitObjectContainer HitObjectContainer => hitObjectContainerLazy.Value; + + private readonly Lazy hitObjectContainerLazy; + + /// + /// A function that converts gamefield coordinates to screen space. + /// + public Func GamefieldToScreenSpace => HitObjectContainer.ToScreenSpace; /// /// All the s contained in this and all . /// - public IEnumerable AllHitObjects => HitObjects?.Objects.Concat(NestedPlayfields.SelectMany(p => p.AllHitObjects)) ?? Enumerable.Empty(); + public IEnumerable AllHitObjects => HitObjectContainer?.Objects.Concat(NestedPlayfields.SelectMany(p => p.AllHitObjects)) ?? Enumerable.Empty(); /// /// All s nested inside this . @@ -39,18 +48,13 @@ namespace osu.Game.Rulesets.UI public readonly BindableBool DisplayJudgements = new BindableBool(true); /// - /// A container for keeping track of DrawableHitObjects. + /// Creates a new . /// - /// The width to scale the internal coordinate space to. - /// May be null if scaling based on is desired. If is also null, no scaling will occur. - /// - /// The height to scale the internal coordinate space to. - /// May be null if scaling based on is desired. If is also null, no scaling will occur. - /// - protected Playfield(float? customWidth = null, float? customHeight = null) - : base(customWidth, customHeight) + protected Playfield() { RelativeSizeAxes = Axes.Both; + + hitObjectContainerLazy = new Lazy(CreateHitObjectContainer); } private WorkingBeatmap beatmap; @@ -59,11 +63,6 @@ namespace osu.Game.Rulesets.UI private void load(IBindableBeatmap beatmap) { this.beatmap = beatmap.Value; - - HitObjects = CreateHitObjectContainer(); - HitObjects.RelativeSizeAxes = Axes.Both; - - Add(HitObjects); } /// @@ -75,13 +74,13 @@ namespace osu.Game.Rulesets.UI /// Adds a DrawableHitObject to this Playfield. /// /// The DrawableHitObject to add. - public virtual void Add(DrawableHitObject h) => HitObjects.Add(h); + public virtual void Add(DrawableHitObject h) => HitObjectContainer.Add(h); /// /// Remove a DrawableHitObject from this Playfield. /// /// The DrawableHitObject to remove. - public virtual void Remove(DrawableHitObject h) => HitObjects.Remove(h); + public virtual void Remove(DrawableHitObject h) => HitObjectContainer.Remove(h); /// /// Registers a as a nested . @@ -94,10 +93,14 @@ namespace osu.Game.Rulesets.UI nestedPlayfields.Value.Add(otherPlayfield); } - /// - /// Creates the container that will be used to contain the s. - /// - protected virtual HitObjectContainer CreateHitObjectContainer() => new HitObjectContainer(); + protected override void LoadComplete() + { + base.LoadComplete(); + + // in the case a consumer forgets to add the HitObjectContainer, we will add it here. + if (HitObjectContainer.Parent == null) + AddInternal(HitObjectContainer); + } protected override void Update() { @@ -108,5 +111,10 @@ namespace osu.Game.Rulesets.UI if (mod is IUpdatableByPlayfield updatable) updatable.Update(this); } + + /// + /// Creates the container that will be used to contain the s. + /// + protected virtual HitObjectContainer CreateHitObjectContainer() => new HitObjectContainer(); } } diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index 64ee680d45..a23a5a78f7 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -22,7 +22,6 @@ using osu.Game.Overlays; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; -using OpenTK; namespace osu.Game.Rulesets.UI { @@ -306,29 +305,9 @@ namespace osu.Game.Rulesets.UI Playfield.PostProcess(); foreach (var mod in Mods.OfType()) - mod.ApplyToDrawableHitObjects(Playfield.HitObjects.Objects); + mod.ApplyToDrawableHitObjects(Playfield.HitObjectContainer.Objects); } - protected override void Update() - { - base.Update(); - - Playfield.Size = GetAspectAdjustedSize() * PlayfieldArea; - } - - /// - /// Computes the size of the in relative coordinate space after aspect adjustments. - /// - /// The aspect-adjusted size. - protected virtual Vector2 GetAspectAdjustedSize() => Vector2.One; - - /// - /// The area of this that is available for the to use. - /// Must be specified in relative coordinate space to this . - /// This affects the final size of the but does not affect the 's scale. - /// - protected virtual Vector2 PlayfieldArea => new Vector2(0.75f); // A sane default - /// /// Creates a DrawableHitObject from a HitObject. /// diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index b05efce146..340833c090 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Configuration; @@ -9,8 +8,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Bindings; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.StateChanges; +using osu.Framework.Input.Events; +using osu.Framework.Input.StateChanges.Events; using osu.Framework.Input.States; using osu.Framework.Timing; using osu.Game.Configuration; @@ -19,6 +18,9 @@ using osu.Game.Input.Handlers; using osu.Game.Screens.Play; using OpenTK.Input; using static osu.Game.Input.Handlers.ReplayInputHandler; +using JoystickState = osu.Framework.Input.States.JoystickState; +using KeyboardState = osu.Framework.Input.States.KeyboardState; +using MouseState = osu.Framework.Input.States.MouseState; namespace osu.Game.Rulesets.UI { @@ -36,13 +38,7 @@ namespace osu.Game.Rulesets.UI protected override InputState CreateInitialState() { var state = base.CreateInitialState(); - return new RulesetInputManagerInputState - { - Mouse = state.Mouse, - Keyboard = state.Keyboard, - Joystick = state.Joystick, - LastReplayState = null - }; + return new RulesetInputManagerInputState(state.Mouse, state.Keyboard, state.Joystick); } protected readonly KeyBindingContainer KeyBindingContainer; @@ -56,33 +52,20 @@ namespace osu.Game.Rulesets.UI #region Action mapping (for replays) - private List lastPressedActions = new List(); - - public override void HandleCustomInput(InputState state, IInput input) + public override void HandleInputStateChange(InputStateChangeEvent inputStateChange) { - if (!(input is ReplayState replayState)) + if (inputStateChange is ReplayStateChangeEvent replayStateChanged) { - base.HandleCustomInput(state, input); - return; - } + foreach (var action in replayStateChanged.ReleasedActions) + KeyBindingContainer.TriggerReleased(action); - if (state is RulesetInputManagerInputState inputState) + foreach (var action in replayStateChanged.PressedActions) + KeyBindingContainer.TriggerPressed(action); + } + else { - inputState.LastReplayState = replayState; + base.HandleInputStateChange(inputStateChange); } - - // Here we handle states specifically coming from a replay source. - // These have extra action information rather than keyboard keys or mouse buttons. - - List newActions = replayState.PressedActions; - - foreach (var released in lastPressedActions.Except(newActions)) - KeyBindingContainer.TriggerReleased(released); - - foreach (var pressed in newActions.Except(lastPressedActions)) - KeyBindingContainer.TriggerPressed(pressed); - - lastPressedActions = newActions; } #endregion @@ -90,12 +73,10 @@ namespace osu.Game.Rulesets.UI #region IHasReplayHandler private ReplayInputHandler replayInputHandler; + public ReplayInputHandler ReplayInputHandler { - get - { - return replayInputHandler; - } + get => replayInputHandler; set { if (replayInputHandler != null) RemoveHandler(replayInputHandler); @@ -225,16 +206,20 @@ namespace osu.Game.Rulesets.UI mouseDisabled = config.GetBindable(OsuSetting.MouseDisableButtons); } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool Handle(UIEvent e) { - if (mouseDisabled.Value && (args.Button == MouseButton.Left || args.Button == MouseButton.Right)) return false; - return base.OnMouseDown(state, args); - } - - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) - { - if (!CurrentState.Mouse.IsPressed(args.Button)) return false; - return base.OnMouseUp(state, args); + switch (e) + { + case MouseDownEvent mouseDown when mouseDown.Button == MouseButton.Left || mouseDown.Button == MouseButton.Right: + if (mouseDisabled.Value) + return false; + break; + case MouseUpEvent mouseUp: + if (!CurrentState.Mouse.IsPressed(mouseUp.Button)) + return false; + break; + } + return base.Handle(e); } #endregion @@ -286,8 +271,13 @@ namespace osu.Game.Rulesets.UI } public class RulesetInputManagerInputState : InputState - where T : struct + where T : struct { public ReplayState LastReplayState; + + public RulesetInputManagerInputState(MouseState mouse = null, KeyboardState keyboard = null, JoystickState joystick = null) + : base(mouse, keyboard, joystick) + { + } } } diff --git a/osu.Game/Rulesets/UI/ScalableContainer.cs b/osu.Game/Rulesets/UI/ScalableContainer.cs deleted file mode 100644 index 5ee03be7ee..0000000000 --- a/osu.Game/Rulesets/UI/ScalableContainer.cs +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using OpenTK; - -namespace osu.Game.Rulesets.UI -{ - /// - /// A which can have its internal coordinate system scaled to a specific size. - /// - public class ScalableContainer : Container - { - /// - /// A function that converts coordinates from gamefield to screen space. - /// - public Func GamefieldToScreenSpace => scaledContent.GamefieldToScreenSpace; - - /// - /// The scaled content. - /// - private readonly ScaledContainer scaledContent; - protected override Container Content => scaledContent; - - /// - /// A which can have its internal coordinate system scaled to a specific size. - /// - /// The width to scale the internal coordinate space to. - /// May be null if scaling based on is desired. If is also null, no scaling will occur. - /// - /// The height to scale the internal coordinate space to. - /// May be null if scaling based on is desired. If is also null, no scaling will occur. - /// - public ScalableContainer(float? customWidth = null, float? customHeight = null) - { - AddInternal(scaledContent = new ScaledContainer - { - CustomWidth = customWidth, - CustomHeight = customHeight, - RelativeSizeAxes = Axes.Both, - }); - } - - private class ScaledContainer : Container - { - /// - /// A function that converts coordinates from gamefield to screen space. - /// - public Func GamefieldToScreenSpace => content.ToScreenSpace; - - /// - /// The value to scale the width of the content to match. - /// If null, is used. - /// - public float? CustomWidth; - - /// - /// The value to scale the height of the content to match. - /// if null, is used. - /// - public float? CustomHeight; - - private readonly Container content; - protected override Container Content => content; - - public ScaledContainer() - { - AddInternal(content = new Container { RelativeSizeAxes = Axes.Both }); - } - - protected override void Update() - { - base.Update(); - - content.Scale = sizeScale; - content.Size = Vector2.Divide(Vector2.One, sizeScale); - } - - /// - /// The scale that is required for the size of the content to match and . - /// - private Vector2 sizeScale - { - get - { - if (CustomWidth.HasValue && CustomHeight.HasValue) - return Vector2.Divide(DrawSize, new Vector2(CustomWidth.Value, CustomHeight.Value)); - if (CustomWidth.HasValue) - return new Vector2(DrawSize.X / CustomWidth.Value); - if (CustomHeight.HasValue) - return new Vector2(DrawSize.Y / CustomHeight.Value); - return Vector2.One; - } - } - } - } -} diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingDirection.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingDirection.cs index 4fe727cb84..d0db8f8835 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingDirection.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingDirection.cs @@ -6,19 +6,19 @@ namespace osu.Game.Rulesets.UI.Scrolling public enum ScrollingDirection { /// - /// Hitobjects will scroll vertically from the bottom of the hitobject container. + /// Hit objects will scroll vertically from the bottom of the hitobject container. /// Up, /// - /// Hitobjects will scroll vertically from the top of the hitobject container. + /// Hit objects will scroll vertically from the top of the hitobject container. /// Down, /// - /// Hitobjects will scroll horizontally from the right of the hitobject container. + /// Hit objects will scroll horizontally from the right of the hitobject container. /// Left, /// - /// Hitobjects will scroll horizontally from the left of the hitobject container. + /// Hit objects will scroll horizontally from the left of the hitobject container. /// Right } diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs index c64fca6eff..7307fc0ead 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Allocation; using osu.Framework.Caching; using osu.Framework.Configuration; using osu.Framework.Graphics; @@ -33,20 +32,16 @@ namespace osu.Game.Rulesets.UI.Scrolling private Cached initialStateCache = new Cached(); - public ScrollingHitObjectContainer() + private readonly ISpeedChangeVisualiser speedChangeVisualiser; + + public ScrollingHitObjectContainer(SpeedChangeVisualisationMethod visualisationMethod) { RelativeSizeAxes = Axes.Both; TimeRange.ValueChanged += _ => initialStateCache.Invalidate(); Direction.ValueChanged += _ => initialStateCache.Invalidate(); - } - private ISpeedChangeVisualiser speedChangeVisualiser; - - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - switch (config.Get(OsuSetting.SpeedChangeVisualisation)) + switch (visualisationMethod) { case SpeedChangeVisualisationMethod.Sequential: speedChangeVisualiser = new SequentialSpeedChangeVisualiser(ControlPoints); @@ -54,6 +49,9 @@ namespace osu.Game.Rulesets.UI.Scrolling case SpeedChangeVisualisationMethod.Overlapping: speedChangeVisualiser = new OverlappingSpeedChangeVisualiser(ControlPoints); break; + case SpeedChangeVisualisationMethod.Constant: + speedChangeVisualiser = new ConstantSpeedChangeVisualiser(); + break; } } diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs index 7146ad8064..a1fc13ce4d 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Input.Bindings; +using osu.Game.Configuration; using osu.Game.Input.Bindings; using osu.Game.Rulesets.Objects.Drawables; @@ -55,26 +56,14 @@ namespace osu.Game.Rulesets.UI.Scrolling /// /// The container that contains the s. /// - public new ScrollingHitObjectContainer HitObjects => (ScrollingHitObjectContainer)base.HitObjects; + public new ScrollingHitObjectContainer HitObjects => (ScrollingHitObjectContainer)HitObjectContainer; /// /// The direction in which s in this should scroll. /// protected readonly Bindable Direction = new Bindable(); - /// - /// Creates a new . - /// - /// The width to scale the internal coordinate space to. - /// May be null if scaling based on is desired. If is also null, no scaling will occur. - /// - /// The height to scale the internal coordinate space to. - /// May be null if scaling based on is desired. If is also null, no scaling will occur. - /// - protected ScrollingPlayfield(float? customWidth = null, float? customHeight = null) - : base(customWidth, customHeight) - { - } + protected virtual SpeedChangeVisualisationMethod VisualisationMethod => SpeedChangeVisualisationMethod.Sequential; [BackgroundDependencyLoader] private void load() @@ -104,7 +93,7 @@ namespace osu.Game.Rulesets.UI.Scrolling protected sealed override HitObjectContainer CreateHitObjectContainer() { - var container = new ScrollingHitObjectContainer(); + var container = new ScrollingHitObjectContainer(VisualisationMethod); container.Direction.BindTo(Direction); return container; } diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs index 3fc67e4e34..41cdd6c06f 100644 --- a/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs +++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs @@ -60,6 +60,7 @@ namespace osu.Game.Rulesets.UI.Scrolling return new MultiplierControlPoint(c.Time) { + Velocity = Beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier, TimingPoint = lastTimingPoint, DifficultyPoint = lastDifficultyPoint }; @@ -78,7 +79,7 @@ namespace osu.Game.Rulesets.UI.Scrolling // If we have no control points, add a default one if (DefaultControlPoints.Count == 0) - DefaultControlPoints.Add(new MultiplierControlPoint()); + DefaultControlPoints.Add(new MultiplierControlPoint { Velocity = Beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier }); DefaultControlPoints.ForEach(c => applySpeedAdjustment(c, Playfield)); } @@ -88,22 +89,5 @@ namespace osu.Game.Rulesets.UI.Scrolling playfield.HitObjects.AddControlPoint(controlPoint); playfield.NestedPlayfields?.OfType().ForEach(p => applySpeedAdjustment(controlPoint, p)); } - - /// - /// Generates a with the default timing change/difficulty change from the beatmap at a time. - /// - /// The time to create the control point at. - /// The default at . - public MultiplierControlPoint CreateControlPointAt(double time) - { - if (DefaultControlPoints.Count == 0) - return new MultiplierControlPoint(time); - - int index = DefaultControlPoints.BinarySearch(new MultiplierControlPoint(time)); - if (index < 0) - return new MultiplierControlPoint(time); - - return new MultiplierControlPoint(time, DefaultControlPoints[index]); - } } } diff --git a/osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantSpeedChangeVisualiser.cs b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantSpeedChangeVisualiser.cs new file mode 100644 index 0000000000..9e910d6b11 --- /dev/null +++ b/osu.Game/Rulesets/UI/Scrolling/Visualisers/ConstantSpeedChangeVisualiser.cs @@ -0,0 +1,67 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Objects.Types; +using OpenTK; + +namespace osu.Game.Rulesets.UI.Scrolling.Visualisers +{ + public class ConstantSpeedChangeVisualiser : ISpeedChangeVisualiser + { + public void ComputeInitialStates(IEnumerable hitObjects, ScrollingDirection direction, double timeRange, Vector2 length) + { + foreach (var obj in hitObjects) + { + obj.LifetimeStart = obj.HitObject.StartTime - timeRange; + + if (obj.HitObject is IHasEndTime endTime) + { + var hitObjectLength = (endTime.EndTime - obj.HitObject.StartTime) / timeRange; + + switch (direction) + { + case ScrollingDirection.Up: + case ScrollingDirection.Down: + obj.Height = (float)(hitObjectLength * length.Y); + break; + case ScrollingDirection.Left: + case ScrollingDirection.Right: + obj.Width = (float)(hitObjectLength * length.X); + break; + } + } + + ComputeInitialStates(obj.NestedHitObjects, direction, timeRange, length); + + // Nested hitobjects don't need to scroll, but they do need accurate positions + UpdatePositions(obj.NestedHitObjects, direction, obj.HitObject.StartTime, timeRange, length); + } + } + + public void UpdatePositions(IEnumerable hitObjects, ScrollingDirection direction, double currentTime, double timeRange, Vector2 length) + { + foreach (var obj in hitObjects) + { + var position = (obj.HitObject.StartTime - currentTime) / timeRange; + + switch (direction) + { + case ScrollingDirection.Up: + obj.Y = (float)(position * length.Y); + break; + case ScrollingDirection.Down: + obj.Y = (float)(-position * length.Y); + break; + case ScrollingDirection.Left: + obj.X = (float)(position * length.X); + break; + case ScrollingDirection.Right: + obj.X = (float)(-position * length.X); + break; + } + } + } + } +} diff --git a/osu.Game/Screens/BackgroundScreen.cs b/osu.Game/Screens/BackgroundScreen.cs index 46d1260410..7787b4ddc2 100644 --- a/osu.Game/Screens/BackgroundScreen.cs +++ b/osu.Game/Screens/BackgroundScreen.cs @@ -5,8 +5,7 @@ using System; using System.Threading; using osu.Framework.Screens; using osu.Framework.Graphics; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using OpenTK; namespace osu.Game.Screens @@ -21,7 +20,7 @@ namespace osu.Game.Screens private const float transition_length = 500; private const float x_movement_amount = 50; - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + protected override bool OnKeyDown(KeyDownEvent e) { //we don't want to handle escape key. return false; diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index 78561cecbf..e326cdb0ca 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -4,19 +4,14 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Textures; -using osu.Framework.Graphics.Transforms; -using OpenTK; using osu.Game.Beatmaps; using osu.Game.Graphics.Backgrounds; namespace osu.Game.Screens.Backgrounds { - public class BackgroundScreenBeatmap : BackgroundScreen + public class BackgroundScreenBeatmap : BlurrableBackgroundScreen { - private Background background; - private WorkingBeatmap beatmap; - private Vector2 blurTarget; public WorkingBeatmap Beatmap { @@ -30,21 +25,21 @@ namespace osu.Game.Screens.Backgrounds Schedule(() => { - LoadComponentAsync(new BeatmapBackground(beatmap), b => + LoadComponentAsync(new BeatmapBackground(beatmap), b => Schedule(() => { float newDepth = 0; - if (background != null) + if (Background != null) { - newDepth = background.Depth + 1; - background.FinishTransforms(); - background.FadeOut(250); - background.Expire(); + newDepth = Background.Depth + 1; + Background.FinishTransforms(); + Background.FadeOut(250); + Background.Expire(); } b.Depth = newDepth; - Add(background = b); - background.BlurSigma = blurTarget; - }); + Add(Background = b); + Background.BlurSigma = BlurTarget; + })); }); } } @@ -54,9 +49,6 @@ namespace osu.Game.Screens.Backgrounds Beatmap = beatmap; } - public TransformSequence BlurTo(Vector2 sigma, double duration, Easing easing = Easing.None) - => background?.BlurTo(blurTarget = sigma, duration, easing); - public override bool Equals(BackgroundScreen other) { var otherBeatmapBackground = other as BackgroundScreenBeatmap; diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs index 38df9b13ef..989883c8b3 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenDefault.cs @@ -3,32 +3,32 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.MathUtils; using osu.Framework.Threading; using osu.Game.Graphics.Backgrounds; namespace osu.Game.Screens.Backgrounds { - public class BackgroundScreenDefault : BackgroundScreen + public class BackgroundScreenDefault : BlurrableBackgroundScreen { private int currentDisplay; private const int background_count = 5; private string backgroundName => $@"Menu/menu-background-{currentDisplay % background_count + 1}"; - private Background current; - [BackgroundDependencyLoader] private void load() { + currentDisplay = RNG.Next(0, background_count); display(new Background(backgroundName)); } private void display(Background newBackground) { - current?.FadeOut(800, Easing.InOutSine); - current?.Expire(); + Background?.FadeOut(800, Easing.InOutSine); + Background?.Expire(); - Add(current = newBackground); + Add(Background = newBackground); currentDisplay++; } diff --git a/osu.Game/Screens/BlurrableBackgroundScreen.cs b/osu.Game/Screens/BlurrableBackgroundScreen.cs new file mode 100644 index 0000000000..92d32badc4 --- /dev/null +++ b/osu.Game/Screens/BlurrableBackgroundScreen.cs @@ -0,0 +1,20 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Transforms; +using osu.Game.Graphics.Backgrounds; +using OpenTK; + +namespace osu.Game.Screens +{ + public abstract class BlurrableBackgroundScreen : BackgroundScreen + { + protected Background Background; + + protected Vector2 BlurTarget; + + public TransformSequence BlurTo(Vector2 sigma, double duration, Easing easing = Easing.None) + => Background?.BlurTo(BlurTarget = sigma, duration, easing); + } +} diff --git a/osu.Game/Screens/Edit/Components/CircularButton.cs b/osu.Game/Screens/Edit/Components/CircularButton.cs new file mode 100644 index 0000000000..a8ad242772 --- /dev/null +++ b/osu.Game/Screens/Edit/Components/CircularButton.cs @@ -0,0 +1,25 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Graphics.UserInterface; +using OpenTK; + +namespace osu.Game.Screens.Edit.Components +{ + public class CircularButton : OsuButton + { + private const float width = 125; + private const float height = 30; + + public CircularButton() + { + Size = new Vector2(width, height); + } + + protected override void Update() + { + base.Update(); + Content.CornerRadius = DrawHeight / 2f; + } + } +} diff --git a/osu.Game/Screens/Edit/Components/PlaybackControl.cs b/osu.Game/Screens/Edit/Components/PlaybackControl.cs index 6cd7fd52d4..3c8288f04a 100644 --- a/osu.Game/Screens/Edit/Components/PlaybackControl.cs +++ b/osu.Game/Screens/Edit/Components/PlaybackControl.cs @@ -8,7 +8,7 @@ using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.Timing; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -44,7 +44,7 @@ namespace osu.Game.Screens.Edit.Components new OsuSpriteText { Origin = Anchor.BottomLeft, - Text = "Playback Speed", + Text = "Playback speed", RelativePositionAxes = Axes.Y, Y = 0.5f, Padding = new MarginPadding { Left = 45 } @@ -138,13 +138,13 @@ namespace osu.Game.Screens.Edit.Components textBold.Colour = hoveredColour = colours.Yellow; } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { updateState(); return true; } - protected override void OnHoverLost(InputState state) => updateState(); + protected override void OnHoverLost(HoverLostEvent e) => updateState(); protected override void OnActivated() => updateState(); protected override void OnDeactivated() => updateState(); diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs index 7ff3849361..4b57e1e92d 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs @@ -6,8 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Graphics; @@ -30,17 +29,17 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts Add(marker = new MarkerVisualisation()); } - protected override bool OnDragStart(InputState state) => true; - protected override bool OnDragEnd(InputState state) => true; - protected override bool OnDrag(InputState state) + protected override bool OnDragStart(DragStartEvent e) => true; + protected override bool OnDragEnd(DragEndEvent e) => true; + protected override bool OnDrag(DragEvent e) { - seekToPosition(state.Mouse.NativeState.Position); + seekToPosition(e.ScreenSpaceMousePosition); return true; } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(MouseDownEvent e) { - seekToPosition(state.Mouse.NativeState.Position); + seekToPosition(e.ScreenSpaceMousePosition); return true; } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 7159cd919c..62cf76ef69 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -12,7 +12,7 @@ using osu.Game.Screens.Edit.Menus; using osu.Game.Screens.Edit.Components.Timelines.Summary; using osu.Framework.Allocation; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.Platform; using osu.Framework.Timing; using osu.Game.Graphics.UserInterface; @@ -182,9 +182,9 @@ namespace osu.Game.Screens.Edit LoadComponentAsync(currentScreen, screenContainer.Add); } - protected override bool OnScroll(InputState state) + protected override bool OnScroll(ScrollEvent e) { - if (state.Mouse.ScrollDelta.X + state.Mouse.ScrollDelta.Y > 0) + if (e.ScrollDelta.X + e.ScrollDelta.Y > 0) clock.SeekBackward(!clock.IsRunning); else clock.SeekForward(!clock.IsRunning); diff --git a/osu.Game/Screens/Edit/Menus/EditorMenuBar.cs b/osu.Game/Screens/Edit/Menus/EditorMenuBar.cs index c6351c8520..af0a7b6694 100644 --- a/osu.Game/Screens/Edit/Menus/EditorMenuBar.cs +++ b/osu.Game/Screens/Edit/Menus/EditorMenuBar.cs @@ -11,7 +11,7 @@ using osu.Game.Graphics.UserInterface; using OpenTK; using OpenTK.Graphics; using osu.Framework.Configuration; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Screens.Edit.Screens; namespace osu.Game.Screens.Edit.Menus @@ -171,18 +171,18 @@ namespace osu.Game.Screens.Edit.Menus { } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { if (Item is EditorMenuItemSpacer) return true; - return base.OnHover(state); + return base.OnHover(e); } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { if (Item is EditorMenuItemSpacer) return true; - return base.OnClick(state); + return base.OnClick(e); } } } diff --git a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs index 63df143ca8..e46be9f7c1 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/BeatDivisorControl.cs @@ -12,8 +12,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using OpenTK; @@ -231,11 +230,11 @@ namespace osu.Game.Screens.Edit.Screens.Compose { } - public override bool HandleKeyboardInput => IsHovered && !CurrentNumber.Disabled; + public override bool HandleNonPositionalInput => IsHovered && !CurrentNumber.Disabled; - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + protected override bool OnKeyDown(KeyDownEvent e) { - switch (args.Key) + switch (e.Key) { case Key.Right: beatDivisor.Next(); @@ -250,34 +249,34 @@ namespace osu.Game.Screens.Edit.Screens.Compose } } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(MouseDownEvent e) { marker.Active = true; - return base.OnMouseDown(state, args); + return base.OnMouseDown(e); } - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + protected override bool OnMouseUp(MouseUpEvent e) { marker.Active = false; - return base.OnMouseUp(state, args); + return base.OnMouseUp(e); } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { - handleMouseInput(state); + handleMouseInput(e.ScreenSpaceMousePosition); return true; } - protected override bool OnDrag(InputState state) + protected override bool OnDrag(DragEvent e) { - handleMouseInput(state); + handleMouseInput(e.ScreenSpaceMousePosition); return true; } - private void handleMouseInput(InputState state) + private void handleMouseInput(Vector2 screenSpaceMousePosition) { // copied from SliderBar so we can do custom spacing logic. - var xPosition = (ToLocalSpace(state?.Mouse.NativeState.Position ?? Vector2.Zero).X - RangePadding) / UsableWidth; + var xPosition = (ToLocalSpace(screenSpaceMousePosition).X - RangePadding) / UsableWidth; CurrentNumber.Value = availableDivisors.OrderBy(d => Math.Abs(getMappedPosition(d) - xPosition)).First(); OnUserChange(); diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs index c4392bbc60..981ddd989c 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/DragLayer.cs @@ -7,7 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Rulesets.Edit; using OpenTK.Graphics; @@ -56,16 +56,16 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers }; } - protected override bool OnDragStart(InputState state) + protected override bool OnDragStart(DragStartEvent e) { this.FadeIn(250, Easing.OutQuint); return true; } - protected override bool OnDrag(InputState state) + protected override bool OnDrag(DragEvent e) { - var dragPosition = state.Mouse.NativeState.Position; - var dragStartPosition = state.Mouse.NativeState.PositionMouseDown ?? dragPosition; + var dragPosition = e.ScreenSpaceMousePosition; + var dragStartPosition = e.ScreenSpaceMouseDownPosition; var dragQuad = new Quad(dragStartPosition.X, dragStartPosition.Y, dragPosition.X - dragStartPosition.X, dragPosition.Y - dragStartPosition.Y); @@ -82,7 +82,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers return true; } - protected override bool OnDragEnd(InputState state) + protected override bool OnDragEnd(DragEndEvent e) { this.FadeOut(250, Easing.OutQuint); DragEnd?.Invoke(); diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs index d212bbe7dd..65f31dd56d 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs @@ -4,8 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects.Drawables; @@ -52,7 +51,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers addMask(obj); } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(MouseDownEvent e) { maskContainer.DeselectAll(); return true; diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs index 5ee31769a3..19258d669e 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskContainer.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.States; using osu.Game.Rulesets.Edit; +using OpenTK; using RectangleF = osu.Framework.Graphics.Primitives.RectangleF; namespace osu.Game.Screens.Edit.Screens.Compose.Layers @@ -32,7 +33,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers /// /// Invoked when any requests drag. /// - public event Action MaskDragRequested; + public event Action MaskDragRequested; private IEnumerable aliveMasks => AliveInternalChildren.Cast(); @@ -103,7 +104,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers } private void onSelectionRequested(HitObjectMask mask, InputState state) => MaskSelectionRequested?.Invoke(mask, state); - private void onDragRequested(HitObjectMask mask, InputState state) => MaskDragRequested?.Invoke(mask, state); + private void onDragRequested(HitObjectMask mask, Vector2 delta, InputState state) => MaskDragRequested?.Invoke(mask, delta, state); protected override int Compare(Drawable x, Drawable y) { diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs index 927e7a2342..635edf82da 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/MaskSelection.cs @@ -54,7 +54,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers #region User Input Handling - public void HandleDrag(HitObjectMask m, InputState state) + public void HandleDrag(HitObjectMask m, Vector2 delta, InputState state) { // Todo: Various forms of snapping @@ -63,7 +63,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers switch (mask.HitObject.HitObject) { case IHasEditablePosition editablePosition: - editablePosition.OffsetPosition(state.Mouse.Delta); + editablePosition.OffsetPosition(delta); break; } } diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs index 803a1275d7..2c7e2043fc 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs @@ -10,7 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -99,7 +99,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons bubble.Colour = button.Selected ? selectedBubbleColour : defaultBubbleColour; } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { if (button.Selected) return true; @@ -109,7 +109,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons button.Selected.Value = true; - return base.OnClick(state); + return base.OnClick(e); } protected override SpriteText CreateText() => new OsuSpriteText diff --git a/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs index 30205c5aa1..da95564975 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs @@ -7,8 +7,7 @@ using osu.Framework.Configuration; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Audio; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Graphics; @@ -133,9 +132,9 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline ScrollTo((float)(adjustableClock.CurrentTime / track.Length) * Content.DrawWidth, false); } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(MouseDownEvent e) { - if (base.OnMouseDown(state, args)) + if (base.OnMouseDown(e)) { beginUserDrag(); return true; @@ -144,10 +143,10 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline return false; } - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + protected override bool OnMouseUp(MouseUpEvent e) { endUserDrag(); - return base.OnMouseUp(state, args); + return base.OnMouseUp(e); } private void beginUserDrag() diff --git a/osu.Game/Screens/Edit/Screens/Compose/Timeline/TimelineArea.cs b/osu.Game/Screens/Edit/Screens/Compose/Timeline/TimelineArea.cs index 006317e57e..ecf760be8e 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Timeline/TimelineArea.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Timeline/TimelineArea.cs @@ -59,8 +59,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline Spacing = new Vector2(0, 4), Children = new[] { - hitObjectsCheckbox = new OsuCheckbox { LabelText = "Hitobjects" }, - hitSoundsCheckbox = new OsuCheckbox { LabelText = "Hitsounds" }, + hitObjectsCheckbox = new OsuCheckbox { LabelText = "Hit objects" }, + hitSoundsCheckbox = new OsuCheckbox { LabelText = "Hit sounds" }, waveformCheckbox = new OsuCheckbox { LabelText = "Waveform" } } } diff --git a/osu.Game/Screens/Edit/Screens/Compose/Timeline/ZoomableScrollContainer.cs b/osu.Game/Screens/Edit/Screens/Compose/Timeline/ZoomableScrollContainer.cs index d30aaacc6a..bbba439ca7 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Timeline/ZoomableScrollContainer.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Timeline/ZoomableScrollContainer.cs @@ -5,7 +5,7 @@ using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Transforms; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.MathUtils; using OpenTK; @@ -97,13 +97,13 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline zoomedContent.Width = DrawWidth * currentZoom; } - protected override bool OnScroll(InputState state) + protected override bool OnScroll(ScrollEvent e) { - if (state.Mouse.HasPreciseScroll) + if (e.IsPrecise) // for now, we don't support zoom when using a precision scroll device. this needs gesture support. - return base.OnScroll(state); + return base.OnScroll(e); - setZoomTarget(zoomTarget + state.Mouse.ScrollDelta.Y, zoomedContent.ToLocalSpace(state.Mouse.NativeState.Position).X); + setZoomTarget(zoomTarget + e.ScrollDelta.Y, zoomedContent.ToLocalSpace(e.ScreenSpaceMousePosition).X); return true; } diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs index c3b3e747fd..3cef20e510 100644 --- a/osu.Game/Screens/Loader.cs +++ b/osu.Game/Screens/Loader.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Shaders; using osu.Game.Screens.Menu; using OpenTK; using osu.Framework.Screens; +using osu.Game.Overlays; namespace osu.Game.Screens { @@ -18,6 +19,8 @@ namespace osu.Game.Screens protected override bool HideOverlaysOnEnter => true; + protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled; + protected override bool AllowBackButton => false; public Loader() diff --git a/osu.Game/Screens/Menu/Button.cs b/osu.Game/Screens/Menu/Button.cs index e53905a102..2b85ee6158 100644 --- a/osu.Game/Screens/Menu/Button.cs +++ b/osu.Game/Screens/Menu/Button.cs @@ -17,8 +17,7 @@ using OpenTK.Input; using osu.Framework.Extensions.Color4Extensions; using osu.Game.Graphics.Containers; using osu.Framework.Audio.Track; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Screens.Menu @@ -47,7 +46,7 @@ namespace osu.Game.Screens.Menu private SampleChannel sampleClick; private SampleChannel sampleHover; - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => box.ReceiveMouseInputAt(screenSpacePos); + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => box.ReceivePositionalInputAt(screenSpacePos); public Button(string text, string sampleName, FontAwesome symbol, Color4 colour, Action clickAction = null, float extraWidth = 0, Key triggerKey = Key.Unknown) { @@ -151,7 +150,7 @@ namespace osu.Game.Screens.Menu rightward = !rightward; } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { if (State != ButtonState.Expanded) return true; @@ -167,7 +166,7 @@ namespace osu.Game.Screens.Menu return true; } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { icon.ClearTransforms(); icon.RotateTo(0, 500, Easing.Out); @@ -186,30 +185,30 @@ namespace osu.Game.Screens.Menu sampleClick = audio.Sample.Get($@"Menu/{sampleName}"); } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(MouseDownEvent e) { boxHoverLayer.FadeTo(0.1f, 1000, Easing.OutQuint); - return base.OnMouseDown(state, args); + return base.OnMouseDown(e); } - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + protected override bool OnMouseUp(MouseUpEvent e) { boxHoverLayer.FadeTo(0, 1000, Easing.OutQuint); - return base.OnMouseUp(state, args); + return base.OnMouseUp(e); } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { trigger(); return true; } - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + protected override bool OnKeyDown(KeyDownEvent e) { - if (args.Repeat || state.Keyboard.ControlPressed || state.Keyboard.ShiftPressed || state.Keyboard.AltPressed) + if (e.Repeat || e.ControlPressed || e.ShiftPressed || e.AltPressed) return false; - if (triggerKey == args.Key && triggerKey != Key.Unknown) + if (triggerKey == e.Key && triggerKey != Key.Unknown) { trigger(); return true; @@ -229,8 +228,8 @@ namespace osu.Game.Screens.Menu boxHoverLayer.FadeOut(800, Easing.OutExpo); } - public override bool HandleKeyboardInput => state == ButtonState.Expanded; - public override bool HandleMouseInput => state != ButtonState.Exploded && box.Scale.X >= 0.8f; + public override bool HandleNonPositionalInput => state == ButtonState.Expanded; + public override bool HandlePositionalInput => state != ButtonState.Exploded && box.Scale.X >= 0.8f; protected override void Update() { diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 7f2bc1d357..5c17317fc1 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -115,7 +115,7 @@ namespace osu.Game.Screens.Menu case GlobalAction.Back: return goBack(); case GlobalAction.Select: - logo?.TriggerOnClick(); + logo?.Click(); return true; default: return false; @@ -133,7 +133,7 @@ namespace osu.Game.Screens.Menu sampleBack?.Play(); return true; case ButtonSystemState.Play: - backButton.TriggerOnClick(); + backButton.Click(); return true; default: return false; @@ -150,18 +150,18 @@ namespace osu.Game.Screens.Menu State = ButtonSystemState.TopLevel; return true; case ButtonSystemState.TopLevel: - buttonsTopLevel.First().TriggerOnClick(); + buttonsTopLevel.First().Click(); return false; case ButtonSystemState.Play: - buttonsPlay.First().TriggerOnClick(); + buttonsPlay.First().Click(); return false; } } private ButtonSystemState state = ButtonSystemState.Initial; - public override bool HandleKeyboardInput => state != ButtonSystemState.Exit; - public override bool HandleMouseInput => state != ButtonSystemState.Exit; + public override bool HandleNonPositionalInput => state != ButtonSystemState.Exit; + public override bool HandlePositionalInput => state != ButtonSystemState.Exit; public ButtonSystemState State { @@ -174,6 +174,9 @@ namespace osu.Game.Screens.Menu ButtonSystemState lastState = state; state = value; + if (game != null) + game.OverlayActivationMode.Value = state == ButtonSystemState.Exit ? OverlayActivation.Disabled : OverlayActivation.All; + updateLogoState(lastState); Logger.Log($"{nameof(ButtonSystem)}'s state changed from {lastState} to {state}"); @@ -205,11 +208,7 @@ namespace osu.Game.Screens.Menu { logoTracking = false; - if (game != null) - { - game.OverlayActivationMode.Value = state == ButtonSystemState.Exit ? OverlayActivation.Disabled : OverlayActivation.All; - game.Toolbar.Hide(); - } + game?.Toolbar.Hide(); logo.ClearTransforms(targetMember: nameof(Position)); logo.RelativePositionAxes = Axes.Both; @@ -243,11 +242,7 @@ namespace osu.Game.Screens.Menu if (impact) logo.Impact(); - if (game != null) - { - game.OverlayActivationMode.Value = OverlayActivation.All; - game.Toolbar.State = Visibility.Visible; - } + game?.Toolbar.Show(); }, 200); break; default: diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs index fb6130fa36..34fb0b196b 100644 --- a/osu.Game/Screens/Menu/LogoVisualisation.cs +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -64,9 +64,6 @@ namespace osu.Game.Screens.Menu private readonly float[] frequencyAmplitudes = new float[256]; - public override bool HandleKeyboardInput => false; - public override bool HandleMouseInput => false; - private Shader shader; private readonly Texture texture; @@ -176,7 +173,7 @@ namespace osu.Game.Screens.Menu Vector2 inflation = DrawInfo.MatrixInverse.ExtractScale().Xy; - ColourInfo colourInfo = DrawInfo.Colour; + ColourInfo colourInfo = DrawColourInfo.Colour; colourInfo.ApplyChild(Colour); if (AudioData != null) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 7e97859be6..2374d6a2fe 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -6,10 +6,10 @@ using OpenTK.Graphics; using OpenTK.Input; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.Screens; using osu.Game.Beatmaps; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Charts; @@ -64,6 +64,20 @@ namespace osu.Game.Screens.Menu }, sideFlashes = new MenuSideFlashes(), }; + + buttons.StateChanged += state => + { + switch (state) + { + case ButtonSystemState.Initial: + case ButtonSystemState.Exit: + background.FadeColour(Color4.White, 500, Easing.OutSine); + break; + default: + background.FadeColour(OsuColour.Gray(0.8f), 500, Easing.OutSine); + break; + } + }; } [BackgroundDependencyLoader(true)] @@ -185,15 +199,15 @@ namespace osu.Game.Screens.Menu return base.OnExiting(next); } - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + protected override bool OnKeyDown(KeyDownEvent e) { - if (!args.Repeat && state.Keyboard.ControlPressed && state.Keyboard.ShiftPressed && args.Key == Key.D) + if (!e.Repeat && e.ControlPressed && e.ShiftPressed && e.Key == Key.D) { Push(new Drawings()); return true; } - return base.OnKeyDown(state, args); + return base.OnKeyDown(e); } } } diff --git a/osu.Game/Screens/Menu/MenuSideFlashes.cs b/osu.Game/Screens/Menu/MenuSideFlashes.cs index 7d46aad089..3de68fe914 100644 --- a/osu.Game/Screens/Menu/MenuSideFlashes.cs +++ b/osu.Game/Screens/Menu/MenuSideFlashes.cs @@ -19,9 +19,6 @@ namespace osu.Game.Screens.Menu { public class MenuSideFlashes : BeatSyncedContainer { - public override bool HandleKeyboardInput => false; - public override bool HandleMouseInput => false; - private readonly IBindable beatmap = new Bindable(); private Box leftBox; diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index f0f765a4c9..4e6a107f91 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -11,8 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.MathUtils; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics; @@ -68,7 +67,7 @@ namespace osu.Game.Screens.Menu public bool BeatMatching = true; - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => logoContainer.ReceiveMouseInputAt(screenSpacePos); + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => logoContainer.ReceivePositionalInputAt(screenSpacePos); public bool Ripple { @@ -84,11 +83,10 @@ namespace osu.Game.Screens.Menu private const double early_activation = 60; + public override bool IsPresent => base.IsPresent || Scheduler.HasPendingTasks; + public OsuLogo() { - // Required to make Schedule calls run in OsuScreen even when we are not visible. - AlwaysPresent = true; - EarlyActivationMilliseconds = early_activation; Size = new Vector2(default_size); @@ -343,25 +341,25 @@ namespace osu.Game.Screens.Menu } } - public override bool HandleMouseInput => base.HandleMouseInput && Action != null && Alpha > 0.2f; + public override bool HandlePositionalInput => base.HandlePositionalInput && Action != null && Alpha > 0.2f; - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(MouseDownEvent e) { - if (args.Button != MouseButton.Left) return false; + if (e.Button != MouseButton.Left) return false; logoBounceContainer.ScaleTo(0.9f, 1000, Easing.Out); return true; } - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + protected override bool OnMouseUp(MouseUpEvent e) { - if (args.Button != MouseButton.Left) return false; + if (e.Button != MouseButton.Left) return false; logoBounceContainer.ScaleTo(1f, 500, Easing.OutElastic); return true; } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { if (Action?.Invoke() ?? true) sampleClick.Play(); @@ -372,13 +370,13 @@ namespace osu.Game.Screens.Menu return true; } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { logoHoverContainer.ScaleTo(1.1f, 500, Easing.OutElastic); return true; } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { logoHoverContainer.ScaleTo(1, 500, Easing.OutElastic); } diff --git a/osu.Game/Screens/Multi/Components/BeatmapTitle.cs b/osu.Game/Screens/Multi/Components/BeatmapTitle.cs index 42863754c5..6dc59f5cac 100644 --- a/osu.Game/Screens/Multi/Components/BeatmapTitle.cs +++ b/osu.Game/Screens/Multi/Components/BeatmapTitle.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; @@ -14,8 +13,6 @@ namespace osu.Game.Screens.Multi.Components { private readonly OsuSpriteText beatmapTitle, beatmapDash, beatmapArtist; - private LocalisationEngine localisation; - public float TextSize { set { beatmapTitle.TextSize = beatmapDash.TextSize = beatmapArtist.TextSize = value; } @@ -48,12 +45,6 @@ namespace osu.Game.Screens.Multi.Components }; } - [BackgroundDependencyLoader] - private void load(LocalisationEngine localisation) - { - this.localisation = localisation; - } - protected override void LoadComplete() { base.LoadComplete(); @@ -64,15 +55,14 @@ namespace osu.Game.Screens.Multi.Components { if (beatmap == null) { - beatmapTitle.Current = beatmapArtist.Current = null; beatmapTitle.Text = "Changing map"; beatmapDash.Text = beatmapArtist.Text = string.Empty; } else { - beatmapTitle.Current = localisation.GetUnicodePreference(beatmap.Metadata.TitleUnicode, beatmap.Metadata.Title); + beatmapTitle.Text = new LocalisedString((beatmap.Metadata.TitleUnicode, beatmap.Metadata.Title)); beatmapDash.Text = @" - "; - beatmapArtist.Current = localisation.GetUnicodePreference(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist); + beatmapArtist.Text = new LocalisedString((beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist)); } } } diff --git a/osu.Game/Screens/Multi/Components/DrawableRoom.cs b/osu.Game/Screens/Multi/Components/DrawableRoom.cs index 739346fabe..67db23263b 100644 --- a/osu.Game/Screens/Multi/Components/DrawableRoom.cs +++ b/osu.Game/Screens/Multi/Components/DrawableRoom.cs @@ -10,7 +10,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; @@ -246,7 +246,7 @@ namespace osu.Game.Screens.Multi.Components this.FadeInFromZero(transition_duration); } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { if (Enabled.Value) { diff --git a/osu.Game/Screens/Multi/Screens/Lounge/Lounge.cs b/osu.Game/Screens/Multi/Screens/Lounge/Lounge.cs index 1a47829ad7..3b3f789628 100644 --- a/osu.Game/Screens/Multi/Screens/Lounge/Lounge.cs +++ b/osu.Game/Screens/Multi/Screens/Lounge/Lounge.cs @@ -6,7 +6,7 @@ using System.Linq; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.Screens; using osu.Game.Graphics.UserInterface; using osu.Game.Online.Multiplayer; @@ -112,7 +112,7 @@ namespace osu.Game.Screens.Multi.Screens.Lounge }; } - protected override void OnFocus(InputState state) + protected override void OnFocus(FocusEvent e) { GetContainingInputManager().ChangeFocus(Filter.Search); } diff --git a/osu.Game/Screens/Multi/Screens/Match/Header.cs b/osu.Game/Screens/Multi/Screens/Match/Header.cs index cc31f10fd2..d469815d59 100644 --- a/osu.Game/Screens/Multi/Screens/Match/Header.cs +++ b/osu.Game/Screens/Multi/Screens/Match/Header.cs @@ -8,8 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; @@ -151,28 +150,28 @@ namespace osu.Game.Screens.Multi.Screens.Match border.BorderColour = colours.Yellow; } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { border.FadeIn(transition_duration); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { - base.OnHoverLost(state); + base.OnHoverLost(e); border.FadeOut(transition_duration); } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(MouseDownEvent e) { bg.FadeTo(0.75f, 1000, Easing.Out); - return base.OnMouseDown(state, args); + return base.OnMouseDown(e); } - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + protected override bool OnMouseUp(MouseUpEvent e) { bg.FadeTo(bg_opacity, transition_duration); - return base.OnMouseUp(state, args); + return base.OnMouseUp(e); } } } diff --git a/osu.Game/Screens/Play/Break/BreakArrows.cs b/osu.Game/Screens/Play/Break/BreakArrows.cs index 4a4a7960fa..1382aa9d9d 100644 --- a/osu.Game/Screens/Play/Break/BreakArrows.cs +++ b/osu.Game/Screens/Play/Break/BreakArrows.cs @@ -31,23 +31,30 @@ namespace osu.Game.Screens.Play.Break RelativeSizeAxes = Axes.Both; InternalChildren = new Drawable[] { - leftGlowIcon = new GlowIcon + new ParallaxContainer { - Anchor = Anchor.Centre, - Origin = Anchor.CentreRight, - X = -glow_icon_offscreen_offset, - Icon = Graphics.FontAwesome.fa_chevron_right, - BlurSigma = new Vector2(glow_icon_blur_sigma), - Size = new Vector2(glow_icon_size), - }, - rightGlowIcon = new GlowIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.CentreLeft, - X = glow_icon_offscreen_offset, - Icon = Graphics.FontAwesome.fa_chevron_left, - BlurSigma = new Vector2(glow_icon_blur_sigma), - Size = new Vector2(glow_icon_size), + ParallaxAmount = -0.01f, + Children = new Drawable[] + { + leftGlowIcon = new GlowIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.CentreRight, + X = -glow_icon_offscreen_offset, + Icon = Graphics.FontAwesome.fa_chevron_right, + BlurSigma = new Vector2(glow_icon_blur_sigma), + Size = new Vector2(glow_icon_size), + }, + rightGlowIcon = new GlowIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.CentreLeft, + X = glow_icon_offscreen_offset, + Icon = Graphics.FontAwesome.fa_chevron_left, + BlurSigma = new Vector2(glow_icon_blur_sigma), + Size = new Vector2(glow_icon_size), + }, + } }, new ParallaxContainer { diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index 1ca3bc2189..2c984e6135 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -16,8 +16,7 @@ using OpenTK.Input; using System.Collections.Generic; using System.Linq; using osu.Framework.Input.Bindings; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Input.Bindings; namespace osu.Game.Screens.Play @@ -28,9 +27,9 @@ namespace osu.Game.Screens.Play private const int button_height = 70; private const float background_alpha = 0.75f; - protected override bool BlockPassThroughKeyboard => true; + protected override bool BlockNonPositionalInput => true; - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; public Action OnRetry; public Action OnQuit; @@ -38,7 +37,7 @@ namespace osu.Game.Screens.Play /// /// Action that is invoked when is triggered. /// - protected virtual Action BackAction => () => InternalButtons.Children.Last().TriggerOnClick(); + protected virtual Action BackAction => () => InternalButtons.Children.Last().Click(); public abstract string Header { get; } public abstract string Description { get; } @@ -155,11 +154,11 @@ namespace osu.Game.Screens.Play protected override void PopOut() => this.FadeOut(transition_duration, Easing.In); // Don't let mouse down events through the overlay or people can click circles while paused. - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true; + protected override bool OnMouseDown(MouseDownEvent e) => true; - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) => true; + protected override bool OnMouseUp(MouseUpEvent e) => true; - protected override bool OnMouseMove(InputState state) => true; + protected override bool OnMouseMove(MouseMoveEvent e) => true; protected void AddButton(string text, Color4 colour, Action action) { @@ -204,11 +203,11 @@ namespace osu.Game.Screens.Play } } - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + protected override bool OnKeyDown(KeyDownEvent e) { - if (!args.Repeat) + if (!e.Repeat) { - switch (args.Key) + switch (e.Key) { case Key.Up: if (selectionIndex == -1 || selectionIndex == 0) @@ -225,7 +224,7 @@ namespace osu.Game.Screens.Play } } - return base.OnKeyDown(state, args); + return base.OnKeyDown(e); } public bool OnPressed(GlobalAction action) @@ -283,20 +282,20 @@ namespace osu.Game.Screens.Play private class Button : DialogButton { - protected override bool OnHover(InputState state) => true; + protected override bool OnHover(HoverEvent e) => true; - protected override bool OnMouseMove(InputState state) + protected override bool OnMouseMove(MouseMoveEvent e) { Selected.Value = true; - return base.OnMouseMove(state); + return base.OnMouseMove(e); } - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + protected override bool OnKeyDown(KeyDownEvent e) { - if (args.Repeat || args.Key != Key.Enter || !Selected) + if (e.Repeat || e.Key != Key.Enter || !Selected) return false; - OnClick(state); + Click(); return true; } } diff --git a/osu.Game/Screens/Play/HUD/ModDisplay.cs b/osu.Game/Screens/Play/HUD/ModDisplay.cs index 1a164b473d..04f086282e 100644 --- a/osu.Game/Screens/Play/HUD/ModDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ModDisplay.cs @@ -12,7 +12,7 @@ using osu.Game.Rulesets.UI; using OpenTK; using osu.Game.Graphics.Containers; using System.Linq; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; namespace osu.Game.Screens.Play.HUD { @@ -93,16 +93,16 @@ namespace osu.Game.Screens.Play.HUD private void contract() => iconsContainer.TransformSpacingTo(new Vector2(-25, 0), 500, Easing.OutQuint); - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { expand(); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { contract(); - base.OnHoverLost(state); + base.OnHoverLost(e); } } } diff --git a/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs b/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs index e5e2ed7ee0..debce8c680 100644 --- a/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs +++ b/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs @@ -3,8 +3,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using OpenTK; using osu.Game.Screens.Play.PlayerSettings; using OpenTK.Input; @@ -51,22 +50,22 @@ namespace osu.Game.Screens.Play.HUD protected override void PopOut() => this.FadeOut(fade_duration); //We want to handle keyboard inputs all the time in order to trigger ToggleVisibility() when not visible - public override bool HandleKeyboardInput => true; + public override bool HandleNonPositionalInput => true; - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + protected override bool OnKeyDown(KeyDownEvent e) { - if (args.Repeat) return false; + if (e.Repeat) return false; - if (state.Keyboard.ControlPressed) + if (e.ControlPressed) { - if (args.Key == Key.H && ReplayLoaded) + if (e.Key == Key.H && ReplayLoaded) { ToggleVisibility(); return true; } } - return base.OnKeyDown(state, args); + return base.OnKeyDown(e); } } } diff --git a/osu.Game/Screens/Play/HUD/QuitButton.cs b/osu.Game/Screens/Play/HUD/QuitButton.cs index 2a4b1f408d..88547e0169 100644 --- a/osu.Game/Screens/Play/HUD/QuitButton.cs +++ b/osu.Game/Screens/Play/HUD/QuitButton.cs @@ -8,8 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.MathUtils; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -20,7 +19,7 @@ namespace osu.Game.Screens.Play.HUD { public class QuitButton : FillFlowContainer { - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; private readonly Button button; @@ -62,10 +61,10 @@ namespace osu.Game.Screens.Play.HUD private float positionalAdjust; - protected override bool OnMouseMove(InputState state) + protected override bool OnMouseMove(MouseMoveEvent e) { - positionalAdjust = Vector2.Distance(state.Mouse.NativeState.Position, button.ScreenSpaceDrawQuad.Centre) / 200; - return base.OnMouseMove(state); + positionalAdjust = Vector2.Distance(e.ScreenSpaceMousePosition, button.ScreenSpaceDrawQuad.Centre) / 200; + return base.OnMouseMove(e); } protected override void Update() @@ -170,28 +169,28 @@ namespace osu.Game.Screens.Play.HUD }); } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { HoverGained?.Invoke(); return true; } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { HoverLost?.Invoke(); - base.OnHoverLost(state); + base.OnHoverLost(e); } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(MouseDownEvent e) { - if (!pendingAnimation && state.Mouse.Buttons.Count() == 1) + if (!pendingAnimation && e.CurrentState.Mouse.Buttons.Count() == 1) BeginConfirm(); return true; } - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + protected override bool OnMouseUp(MouseUpEvent e) { - if (!state.Mouse.Buttons.Any()) + if (!e.HasAnyButtonPressed) AbortConfirm(); return true; } diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index f51ea6fe3e..db0d7b6ccc 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -5,8 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Configuration; @@ -69,7 +68,7 @@ namespace osu.Game.Screens.Play Direction = FillDirection.Vertical, Children = new Drawable[] { - KeyCounter = CreateKeyCounter(), + KeyCounter = CreateKeyCounter(adjustableClock as IFrameBasedClock), HoldToQuit = CreateQuitButton(), } } @@ -152,13 +151,13 @@ namespace osu.Game.Screens.Play Progress.BindRulestContainer(rulesetContainer); } - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + protected override bool OnKeyDown(KeyDownEvent e) { - if (args.Repeat) return false; + if (e.Repeat) return false; - if (state.Keyboard.ShiftPressed) + if (e.ShiftPressed) { - switch (args.Key) + switch (e.Key) { case Key.Tab: showHud.Value = !showHud.Value; @@ -166,7 +165,7 @@ namespace osu.Game.Screens.Play } } - return base.OnKeyDown(state, args); + return base.OnKeyDown(e); } protected virtual RollingCounter CreateAccuracyCounter() => new PercentageCounter @@ -194,12 +193,13 @@ namespace osu.Game.Screens.Play Margin = new MarginPadding { Top = 20 } }; - protected virtual KeyCounterCollection CreateKeyCounter() => new KeyCounterCollection + protected virtual KeyCounterCollection CreateKeyCounter(IFrameBasedClock offsetClock) => new KeyCounterCollection { FadeTime = 50, Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, Margin = new MarginPadding(10), + AudioClock = offsetClock }; protected virtual ScoreCounter CreateScoreCounter() => new ScoreCounter(6) diff --git a/osu.Game/Screens/Play/KeyCounter.cs b/osu.Game/Screens/Play/KeyCounter.cs index 2c31e61114..d1efe4cab7 100644 --- a/osu.Game/Screens/Play/KeyCounter.cs +++ b/osu.Game/Screens/Play/KeyCounter.cs @@ -1,6 +1,8 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -19,6 +21,9 @@ namespace osu.Game.Screens.Play private Container textLayer; private SpriteText countSpriteText; + private readonly List states = new List(); + private KeyCounterState currentState; + public bool IsCounting { get; set; } = true; private int countPresses; public int CountPresses @@ -45,7 +50,10 @@ namespace osu.Game.Screens.Play isLit = value; updateGlowSprite(value); if (value && IsCounting) + { CountPresses++; + saveState(); + } } } } @@ -128,6 +136,32 @@ namespace osu.Game.Screens.Play } } - public void ResetCount() => CountPresses = 0; + public void ResetCount() + { + CountPresses = 0; + states.Clear(); + } + + protected override void Update() + { + base.Update(); + + if (currentState?.Time > Clock.CurrentTime) + restoreStateTo(Clock.CurrentTime); + } + + private void saveState() + { + if (currentState == null || currentState.Time < Clock.CurrentTime) + states.Add(currentState = new KeyCounterState(Clock.CurrentTime, CountPresses)); + } + + private void restoreStateTo(double time) + { + states.RemoveAll(state => state.Time > time); + + currentState = states.LastOrDefault(); + CountPresses = currentState?.Count ?? 0; + } } } diff --git a/osu.Game/Screens/Play/KeyCounterCollection.cs b/osu.Game/Screens/Play/KeyCounterCollection.cs index 5cb5bb152a..925f96f33b 100644 --- a/osu.Game/Screens/Play/KeyCounterCollection.cs +++ b/osu.Game/Screens/Play/KeyCounterCollection.cs @@ -3,15 +3,15 @@ using System; using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using OpenTK.Graphics; -using osu.Framework.Configuration; -using osu.Framework.Allocation; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; +using osu.Framework.Timing; using osu.Game.Configuration; using OpenTK; +using OpenTK.Graphics; namespace osu.Game.Screens.Play { @@ -37,6 +37,9 @@ namespace osu.Game.Screens.Play key.FadeTime = FadeTime; key.KeyDownTextColor = KeyDownTextColor; key.KeyUpTextColor = KeyUpTextColor; + // Use the same clock object as SongProgress for saving KeyCounter state + if (AudioClock != null) + key.Clock = AudioClock; } public void ResetCount() @@ -115,8 +118,10 @@ namespace osu.Game.Screens.Play private void updateVisibility() => this.FadeTo(Visible.Value || configVisibility.Value ? 1 : 0, duration); - public override bool HandleKeyboardInput => receptor == null; - public override bool HandleMouseInput => receptor == null; + public override bool HandleNonPositionalInput => receptor == null; + public override bool HandlePositionalInput => receptor == null; + + public IFrameBasedClock AudioClock { get; set; } private Receptor receptor; @@ -144,15 +149,20 @@ namespace osu.Game.Screens.Play Target = target; } - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) => Target.Children.Any(c => c.TriggerOnKeyDown(state, args)); - - protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) => Target.Children.Any(c => c.TriggerOnKeyUp(state, args)); - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => Target.Children.Any(c => c.TriggerOnMouseDown(state, args)); - - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) => Target.Children.Any(c => c.TriggerOnMouseUp(state, args)); + protected override bool Handle(UIEvent e) + { + switch (e) + { + case KeyDownEvent _: + case KeyUpEvent _: + case MouseDownEvent _: + case MouseUpEvent _: + return Target.Children.Any(c => c.TriggerEvent(e)); + } + return base.Handle(e); + } } } } diff --git a/osu.Game/Screens/Play/KeyCounterKeyboard.cs b/osu.Game/Screens/Play/KeyCounterKeyboard.cs index 1c2f2c0ac4..725eae0367 100644 --- a/osu.Game/Screens/Play/KeyCounterKeyboard.cs +++ b/osu.Game/Screens/Play/KeyCounterKeyboard.cs @@ -1,8 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using OpenTK.Input; namespace osu.Game.Screens.Play @@ -15,16 +14,16 @@ namespace osu.Game.Screens.Play Key = key; } - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + protected override bool OnKeyDown(KeyDownEvent e) { - if (args.Key == Key) IsLit = true; - return base.OnKeyDown(state, args); + if (e.Key == Key) IsLit = true; + return base.OnKeyDown(e); } - protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) + protected override bool OnKeyUp(KeyUpEvent e) { - if (args.Key == Key) IsLit = false; - return base.OnKeyUp(state, args); + if (e.Key == Key) IsLit = false; + return base.OnKeyUp(e); } } } diff --git a/osu.Game/Screens/Play/KeyCounterMouse.cs b/osu.Game/Screens/Play/KeyCounterMouse.cs index 20cc53caee..c1ed5f84f4 100644 --- a/osu.Game/Screens/Play/KeyCounterMouse.cs +++ b/osu.Game/Screens/Play/KeyCounterMouse.cs @@ -1,8 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using OpenTK.Input; using OpenTK; @@ -17,7 +16,7 @@ namespace osu.Game.Screens.Play Button = button; } - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; private static string getStringRepresentation(MouseButton button) { @@ -32,16 +31,16 @@ namespace osu.Game.Screens.Play } } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(MouseDownEvent e) { - if (args.Button == Button) IsLit = true; - return base.OnMouseDown(state, args); + if (e.Button == Button) IsLit = true; + return base.OnMouseDown(e); } - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + protected override bool OnMouseUp(MouseUpEvent e) { - if (args.Button == Button) IsLit = false; - return base.OnMouseUp(state, args); + if (e.Button == Button) IsLit = false; + return base.OnMouseUp(e); } } } diff --git a/osu.Game/Screens/Play/KeyCounterState.cs b/osu.Game/Screens/Play/KeyCounterState.cs new file mode 100644 index 0000000000..e5c0703319 --- /dev/null +++ b/osu.Game/Screens/Play/KeyCounterState.cs @@ -0,0 +1,17 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Screens.Play +{ + public class KeyCounterState + { + public KeyCounterState(double time, int count) + { + Time = time; + Count = count; + } + + public readonly double Time; + public readonly int Count; + } +} diff --git a/osu.Game/Screens/Play/PauseContainer.cs b/osu.Game/Screens/Play/PauseContainer.cs index d9677e5daf..25e701294a 100644 --- a/osu.Game/Screens/Play/PauseContainer.cs +++ b/osu.Game/Screens/Play/PauseContainer.cs @@ -132,7 +132,7 @@ namespace osu.Game.Screens.Play public override string Header => "paused"; public override string Description => "you're not going to do what i think you're going to do, are ya?"; - protected override Action BackAction => () => InternalButtons.Children.First().TriggerOnClick(); + protected override Action BackAction => () => InternalButtons.Children.First().Click(); [BackgroundDependencyLoader] private void load(OsuColour colours) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 2e23bb16f0..b3cbeb3850 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -12,7 +12,7 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.Logging; using osu.Framework.Screens; using osu.Framework.Threading; @@ -124,7 +124,7 @@ namespace osu.Game.Screens.Play if (!RulesetContainer.Objects.Any()) { - Logger.Error(new InvalidOperationException("Beatmap contains no hit objects!"), "Beatmap contains no hit objects!"); + Logger.Log("Beatmap contains no hit objects!", level: LogLevel.Error); return; } } @@ -370,7 +370,7 @@ namespace osu.Game.Screens.Play Content.FadeOut(fadeOutDuration); } - protected override bool OnScroll(InputState state) => mouseWheelDisabled.Value && !pauseContainer.IsPaused; + protected override bool OnScroll(ScrollEvent e) => mouseWheelDisabled.Value && !pauseContainer.IsPaused; private void initializeStoryboard(bool asyncLoad) { diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index fd4322c268..d87fb1db86 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -7,7 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Framework.Screens; using osu.Framework.Threading; @@ -136,21 +136,21 @@ namespace osu.Game.Screens.Play private bool readyForPush => player.LoadState == LoadState.Ready && IsHovered && GetContainingInputManager()?.DraggedDrawable == null; - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { // restore our screen defaults InitializeBackgroundElements(); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { if (GetContainingInputManager().HoveredDrawables.Contains(visualSettings)) { // show user setting preview UpdateBackgroundElements(); } - base.OnHoverLost(state); + base.OnHoverLost(e); } protected override void InitializeBackgroundElements() @@ -290,7 +290,7 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader] - private void load(LocalisationEngine localisation) + private void load() { var metadata = beatmap?.BeatmapInfo?.Metadata ?? new BeatmapMetadata(); @@ -307,7 +307,7 @@ namespace osu.Game.Screens.Play { new OsuSpriteText { - Current = localisation.GetUnicodePreference(metadata.TitleUnicode, metadata.Title), + Text = new LocalisedString((metadata.TitleUnicode, metadata.Title)), TextSize = 36, Font = @"Exo2.0-MediumItalic", Origin = Anchor.TopCentre, @@ -315,7 +315,7 @@ namespace osu.Game.Screens.Play }, new OsuSpriteText { - Current = localisation.GetUnicodePreference(metadata.ArtistUnicode, metadata.Artist), + Text = new LocalisedString((metadata.ArtistUnicode, metadata.Artist)), TextSize = 26, Font = @"Exo2.0-MediumItalic", Origin = Anchor.TopCentre, diff --git a/osu.Game/Screens/Play/PlayerSettings/PlayerSettingsGroup.cs b/osu.Game/Screens/Play/PlayerSettings/PlayerSettingsGroup.cs index 64c49099f2..6e317ccfc9 100644 --- a/osu.Game/Screens/Play/PlayerSettings/PlayerSettingsGroup.cs +++ b/osu.Game/Screens/Play/PlayerSettings/PlayerSettingsGroup.cs @@ -5,8 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -137,16 +136,16 @@ namespace osu.Game.Screens.Play.PlayerSettings this.Delay(600).FadeTo(inactive_alpha, fade_duration, Easing.OutQuint); } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { this.FadeIn(fade_duration, Easing.OutQuint); return true; } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { this.FadeTo(inactive_alpha, fade_duration, Easing.OutQuint); - base.OnHoverLost(state); + base.OnHoverLost(e); } [BackgroundDependencyLoader] @@ -161,6 +160,6 @@ namespace osu.Game.Screens.Play.PlayerSettings protected override Container Content => content; - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true; + protected override bool OnMouseDown(MouseDownEvent e) => true; } } diff --git a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs index 439e344020..f762597e81 100644 --- a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs @@ -38,7 +38,7 @@ namespace osu.Game.Screens.Play.PlayerSettings }, showStoryboardToggle = new PlayerCheckbox { LabelText = "Storyboards" }, beatmapSkinsToggle = new PlayerCheckbox { LabelText = "Beatmap skins" }, - beatmapHitsoundsToggle = new PlayerCheckbox { LabelText = "Beatmap hitsounds" } + beatmapHitsoundsToggle = new PlayerCheckbox { LabelText = "Beatmap hit sounds" } }; } diff --git a/osu.Game/Screens/Play/SkipOverlay.cs b/osu.Game/Screens/Play/SkipOverlay.cs index 06837c9274..cd34623951 100644 --- a/osu.Game/Screens/Play/SkipOverlay.cs +++ b/osu.Game/Screens/Play/SkipOverlay.cs @@ -18,8 +18,7 @@ using OpenTK.Graphics; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.Containers; using osu.Framework.Input.Bindings; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Input.Bindings; namespace osu.Game.Screens.Play @@ -37,8 +36,8 @@ namespace osu.Game.Screens.Play private FadeContainer fadeContainer; private double displayTime; - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true; - protected override bool BlockPassThroughMouse => false; + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; + protected override bool BlockPositionalInput => false; public SkipOverlay(double startTime) { @@ -129,11 +128,11 @@ namespace osu.Game.Screens.Play remainingTimeBox.ResizeWidthTo((float)Math.Max(0, 1 - (Time.Current - displayTime) / (beginFadeTime - displayTime)), 120, Easing.OutQuint); } - protected override bool OnMouseMove(InputState state) + protected override bool OnMouseMove(MouseMoveEvent e) { - if (!state.Mouse.HasAnyButtonPressed) + if (!e.HasAnyButtonPressed) fadeContainer.State = Visibility.Visible; - return base.OnMouseMove(state); + return base.OnMouseMove(e); } public bool OnPressed(GlobalAction action) @@ -141,7 +140,7 @@ namespace osu.Game.Screens.Play switch (action) { case GlobalAction.SkipCutscene: - button.TriggerOnClick(); + button.Click(); return true; } @@ -194,16 +193,16 @@ namespace osu.Game.Screens.Play State = Visibility.Visible; } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(MouseDownEvent e) { scheduledHide?.Cancel(); - return base.OnMouseDown(state, args); + return base.OnMouseDown(e); } - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + protected override bool OnMouseUp(MouseUpEvent e) { State = Visibility.Visible; - return base.OnMouseUp(state, args); + return base.OnMouseUp(e); } } @@ -284,7 +283,7 @@ namespace osu.Game.Screens.Play }; } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { flow.TransformSpacingTo(new Vector2(5), 500, Easing.OutQuint); box.FadeColour(colourHover, 500, Easing.OutQuint); @@ -292,27 +291,27 @@ namespace osu.Game.Screens.Play return true; } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { flow.TransformSpacingTo(new Vector2(0), 500, Easing.OutQuint); box.FadeColour(colourNormal, 500, Easing.OutQuint); background.FadeTo(0.2f, 500, Easing.OutQuint); - base.OnHoverLost(state); + base.OnHoverLost(e); } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(MouseDownEvent e) { aspect.ScaleTo(0.75f, 2000, Easing.OutQuint); - return base.OnMouseDown(state, args); + return base.OnMouseDown(e); } - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + protected override bool OnMouseUp(MouseUpEvent e) { aspect.ScaleTo(1, 1000, Easing.OutElastic); - return base.OnMouseUp(state, args); + return base.OnMouseUp(e); } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { if (!Enabled) return false; @@ -322,7 +321,7 @@ namespace osu.Game.Screens.Play box.FlashColour(Color4.White, 500, Easing.OutQuint); aspect.ScaleTo(1.2f, 2000, Easing.OutQuint); - bool result = base.OnClick(state); + bool result = base.OnClick(e); // for now, let's disable the skip button after the first press. // this will likely need to be contextual in the future (bound from external components). diff --git a/osu.Game/Screens/Play/SongProgress.cs b/osu.Game/Screens/Play/SongProgress.cs index 2ca471c5c1..2e2c77c1c8 100644 --- a/osu.Game/Screens/Play/SongProgress.cs +++ b/osu.Game/Screens/Play/SongProgress.cs @@ -31,8 +31,8 @@ namespace osu.Game.Screens.Play public Action OnSeek; - public override bool HandleKeyboardInput => AllowSeeking; - public override bool HandleMouseInput => AllowSeeking; + public override bool HandleNonPositionalInput => AllowSeeking; + public override bool HandlePositionalInput => AllowSeeking; private IClock audioClock; public IClock AudioClock { set { audioClock = info.AudioClock = value; } } diff --git a/osu.Game/Screens/Play/SquareGraph.cs b/osu.Game/Screens/Play/SquareGraph.cs index 8ffd04b35c..bc4c87e191 100644 --- a/osu.Game/Screens/Play/SquareGraph.cs +++ b/osu.Game/Screens/Play/SquareGraph.cs @@ -21,9 +21,6 @@ namespace osu.Game.Screens.Play public int ColumnCount => columns.Length; - public override bool HandleKeyboardInput => false; - public override bool HandleMouseInput => false; - private int progress; public int Progress { diff --git a/osu.Game/Screens/Ranking/ResultsPageScore.cs b/osu.Game/Screens/Ranking/ResultsPageScore.cs index 42d8af07b9..58c9cde9c7 100644 --- a/osu.Game/Screens/Ranking/ResultsPageScore.cs +++ b/osu.Game/Screens/Ranking/ResultsPageScore.cs @@ -13,7 +13,6 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -24,11 +23,13 @@ using osu.Game.Screens.Select.Leaderboards; using osu.Game.Users; using osu.Framework.Graphics.Shapes; using osu.Framework.Extensions; +using osu.Framework.Localisation; namespace osu.Game.Screens.Ranking { public class ResultsPageScore : ResultsPage { + private Container scoreContainer; private ScoreCounter scoreCounter; public ResultsPageScore(Score score, WorkingBeatmap beatmap) : base(score, beatmap) { } @@ -76,7 +77,7 @@ namespace osu.Game.Screens.Ranking Size = new Vector2(150, 60), Margin = new MarginPadding(20), }, - new Container + scoreContainer = new Container { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, @@ -92,8 +93,8 @@ namespace osu.Game.Screens.Ranking }, scoreCounter = new SlowScoreCounter(6) { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Colour = colours.PinkDarker, Y = 10, TextSize = 56, @@ -185,6 +186,13 @@ namespace osu.Game.Screens.Ranking }); } + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + scoreCounter.Scale = new Vector2(Math.Min(1f, (scoreContainer.DrawWidth - 20) / scoreCounter.DrawWidth)); + } + private class DrawableScoreStatistic : Container { private readonly KeyValuePair statistic; @@ -320,7 +328,7 @@ namespace osu.Game.Screens.Ranking } [BackgroundDependencyLoader] - private void load(OsuColour colours, LocalisationEngine localisation) + private void load(OsuColour colours) { title.Colour = artist.Colour = colours.BlueDarker; versionMapper.Colour = colours.Gray8; @@ -333,8 +341,8 @@ namespace osu.Game.Screens.Ranking versionMapper.Text = $"{beatmap.Version} - " + versionMapper.Text; } - title.Current = localisation.GetUnicodePreference(beatmap.Metadata.TitleUnicode, beatmap.Metadata.Title); - artist.Current = localisation.GetUnicodePreference(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist); + title.Text = new LocalisedString((beatmap.Metadata.TitleUnicode, beatmap.Metadata.Title)); + artist.Text = new LocalisedString((beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist)); } } @@ -368,7 +376,7 @@ namespace osu.Game.Screens.Ranking } [BackgroundDependencyLoader] - private void load(TextureStore textures) + private void load(LargeTextureStore textures) { if (!string.IsNullOrEmpty(user.CoverUrl)) cover.Texture = textures.Get(user.CoverUrl); diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 6e4454a311..7c9053d2f0 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -17,8 +17,7 @@ using osu.Framework.Caching; using osu.Framework.Threading; using osu.Framework.Configuration; using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; @@ -52,8 +51,8 @@ namespace osu.Game.Screens.Select /// public Action SelectionChanged; - public override bool HandleKeyboardInput => AllowSelection; - public override bool HandleMouseInput => AllowSelection; + public override bool HandleNonPositionalInput => AllowSelection; + public override bool HandlePositionalInput => AllowSelection; /// /// Used to avoid firing null selections before the initial beatmaps have been loaded via . @@ -64,32 +63,36 @@ namespace osu.Game.Screens.Select public IEnumerable BeatmapSets { - get { return beatmapSets.Select(g => g.BeatmapSet); } - set + get => beatmapSets.Select(g => g.BeatmapSet); + set => loadBeatmapSets(() => value); + } + + public void LoadBeatmapSetsFromManager(BeatmapManager manager) => loadBeatmapSets(manager.GetAllUsableBeatmapSetsEnumerable); + + private void loadBeatmapSets(Func> beatmapSets) + { + CarouselRoot newRoot = new CarouselRoot(this); + + Task.Run(() => { - CarouselRoot newRoot = new CarouselRoot(this); + beatmapSets().Select(createCarouselSet).Where(g => g != null).ForEach(newRoot.AddChild); + newRoot.Filter(activeCriteria); - Task.Run(() => + // preload drawables as the ctor overhead is quite high currently. + var _ = newRoot.Drawables; + }).ContinueWith(_ => Schedule(() => + { + root = newRoot; + scrollableContent.Clear(false); + itemsCache.Invalidate(); + scrollPositionCache.Invalidate(); + + Schedule(() => { - value.Select(createCarouselSet).Where(g => g != null).ForEach(newRoot.AddChild); - newRoot.Filter(activeCriteria); - - // preload drawables as the ctor overhead is quite high currently. - var _ = newRoot.Drawables; - }).ContinueWith(_ => Schedule(() => - { - root = newRoot; - scrollableContent.Clear(false); - itemsCache.Invalidate(); - scrollPositionCache.Invalidate(); - - Schedule(() => - { - BeatmapSetsChanged?.Invoke(); - initialLoadComplete = true; - }); - })); - } + BeatmapSetsChanged?.Invoke(); + initialLoadComplete = true; + }); + })); } private readonly List yPositions = new List(); @@ -377,12 +380,12 @@ namespace osu.Game.Screens.Select public void ScrollToSelected() => scrollPositionCache.Invalidate(); - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + protected override bool OnKeyDown(KeyDownEvent e) { int direction = 0; bool skipDifficulties = false; - switch (args.Key) + switch (e.Key) { case Key.Up: direction = -1; @@ -401,7 +404,7 @@ namespace osu.Game.Screens.Select } if (direction == 0) - return base.OnKeyDown(state, args); + return base.OnKeyDown(e); SelectNext(direction, skipDifficulties); return true; @@ -482,6 +485,15 @@ namespace osu.Game.Screens.Select updateItem(p, halfHeight); } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + // aggressively dispose "off-screen" items to reduce GC pressure. + foreach (var i in Items) + i.Dispose(); + } + private CarouselBeatmapSet createCarouselSet(BeatmapSetInfo beatmapSet) { if (beatmapSet.Beatmaps.All(b => b.Hidden)) diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index f1bd2b945f..f7b955941d 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -327,11 +327,6 @@ namespace osu.Game.Screens.Select TextSize = 14, }, }, - textFlow = new OsuTextFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - }, }, }; } @@ -350,6 +345,8 @@ namespace osu.Game.Screens.Select } } + public override bool IsPresent => base.IsPresent || textFlow == null; // Visibility is updated in the LoadComponentAsync callback + private void setTextAsync(string text) { LoadComponentAsync(new OsuTextFlowContainer(s => s.TextSize = 14) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index d26702fcf9..81455833e9 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -59,7 +59,7 @@ namespace osu.Game.Screens.Select ruleset.ValueChanged += _ => updateDisplay(); } - protected override bool BlockPassThroughMouse => false; + protected override bool BlockPositionalInput => false; protected override void PopIn() { @@ -89,6 +89,8 @@ namespace osu.Game.Screens.Select } } + public override bool IsPresent => base.IsPresent || Info == null; // Visibility is updated in the LoadComponentAsync callback + private BufferedWedgeInfo loadingInfo; private void updateDisplay() @@ -127,11 +129,12 @@ namespace osu.Game.Screens.Select public OsuSpriteText VersionLabel { get; private set; } public OsuSpriteText TitleLabel { get; private set; } public OsuSpriteText ArtistLabel { get; private set; } + public BeatmapSetOnlineStatusPill StatusPill { get; private set; } public FillFlowContainer MapperContainer { get; private set; } public FillFlowContainer InfoLabelContainer { get; private set; } - private UnicodeBindableString titleBinding; - private UnicodeBindableString artistBinding; + private ILocalisedBindableString titleBinding; + private ILocalisedBindableString artistBinding; private readonly WorkingBeatmap beatmap; private readonly RulesetInfo ruleset; @@ -143,7 +146,7 @@ namespace osu.Game.Screens.Select } [BackgroundDependencyLoader] - private void load(LocalisationEngine localisation) + private void load(LocalisationManager localisation) { var beatmapInfo = beatmap.BeatmapInfo; var metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata(); @@ -152,8 +155,8 @@ namespace osu.Game.Screens.Select CacheDrawnFrameBuffer = true; RelativeSizeAxes = Axes.Both; - titleBinding = localisation.GetUnicodePreference(metadata.TitleUnicode, metadata.Title); - artistBinding = localisation.GetUnicodePreference(metadata.ArtistUnicode, metadata.Artist); + titleBinding = localisation.GetLocalisedString(new LocalisedString((metadata.TitleUnicode, metadata.Title))); + artistBinding = localisation.GetLocalisedString(new LocalisedString((metadata.ArtistUnicode, metadata.Artist))); Children = new Drawable[] { @@ -190,7 +193,7 @@ namespace osu.Game.Screens.Select }, new FillFlowContainer { - Name = "Top-aligned metadata", + Name = "Topleft-aligned metadata", Anchor = Anchor.TopLeft, Origin = Anchor.TopLeft, Direction = FillDirection.Vertical, @@ -207,6 +210,24 @@ namespace osu.Game.Screens.Select } }, new FillFlowContainer + { + Name = "Topright-aligned metadata", + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Direction = FillDirection.Vertical, + Margin = new MarginPadding { Top = 14, Left = 10, Right = 18, Bottom = 20 }, + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + StatusPill = new BeatmapSetOnlineStatusPill + { + TextSize = 11, + TextPadding = new MarginPadding { Horizontal = 8, Vertical = 2 }, + Status = beatmapInfo.Status, + } + } + }, + new FillFlowContainer { Name = "Centre-aligned metadata", Anchor = Anchor.CentreLeft, @@ -244,8 +265,13 @@ namespace osu.Game.Screens.Select } } }; - artistBinding.ValueChanged += value => setMetadata(metadata.Source); - artistBinding.TriggerChange(); + + titleBinding.BindValueChanged(value => setMetadata(metadata.Source)); + artistBinding.BindValueChanged(value => setMetadata(metadata.Source), true); + + // no difficulty means it can't have a status to show + if (beatmapInfo.Version == null) + StatusPill.Hide(); } private void setMetadata(string source) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs index 6071e163cf..109ac25cd1 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs @@ -10,7 +10,7 @@ using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; @@ -155,12 +155,12 @@ namespace osu.Game.Screens.Select.Carousel triangles.Colour = OsuColour.Gray(0.5f); } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { if (Item.State == CarouselItemState.Selected) startRequested?.Invoke(beatmap); - return base.OnClick(state); + return base.OnClick(e); } protected override void ApplyState() diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index d554a22735..ec2622285f 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -38,11 +38,8 @@ namespace osu.Game.Screens.Select.Carousel } [BackgroundDependencyLoader(true)] - private void load(LocalisationEngine localisation, BeatmapManager manager, BeatmapSetOverlay beatmapOverlay, DialogOverlay overlay) + private void load(BeatmapManager manager, BeatmapSetOverlay beatmapOverlay, DialogOverlay overlay) { - if (localisation == null) - throw new ArgumentNullException(nameof(localisation)); - restoreHiddenRequested = s => s.Beatmaps.ForEach(manager.Restore); dialogOverlay = overlay; if (beatmapOverlay != null) @@ -50,12 +47,12 @@ namespace osu.Game.Screens.Select.Carousel Children = new Drawable[] { - new DelayedLoadWrapper( + new DelayedLoadUnloadWrapper(() => new PanelBackground(manager.GetWorkingBeatmap(beatmapSet.Beatmaps.FirstOrDefault())) { RelativeSizeAxes = Axes.Both, OnLoadComplete = d => d.FadeInFromZero(1000, Easing.OutQuint), - }, 300 + }, 300, 5000 ), new FillFlowContainer { @@ -67,22 +64,39 @@ namespace osu.Game.Screens.Select.Carousel new OsuSpriteText { Font = @"Exo2.0-BoldItalic", - Current = localisation.GetUnicodePreference(beatmapSet.Metadata.TitleUnicode, beatmapSet.Metadata.Title), + Text = new LocalisedString((beatmapSet.Metadata.TitleUnicode, beatmapSet.Metadata.Title)), TextSize = 22, Shadow = true, }, new OsuSpriteText { Font = @"Exo2.0-SemiBoldItalic", - Current = localisation.GetUnicodePreference(beatmapSet.Metadata.ArtistUnicode, beatmapSet.Metadata.Artist), + Text = new LocalisedString((beatmapSet.Metadata.ArtistUnicode, beatmapSet.Metadata.Artist)), TextSize = 17, Shadow = true, }, - new FillFlowContainer + new FillFlowContainer { - Margin = new MarginPadding { Top = 5 }, + Direction = FillDirection.Horizontal, AutoSizeAxes = Axes.Both, - Children = ((CarouselBeatmapSet)Item).Beatmaps.Select(b => new FilterableDifficultyIcon(b)).ToList() + Margin = new MarginPadding { Top = 5 }, + Children = new Drawable[] + { + new BeatmapSetOnlineStatusPill + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Margin = new MarginPadding { Right = 5 }, + TextSize = 11, + TextPadding = new MarginPadding { Horizontal = 8, Vertical = 2 }, + Status = beatmapSet.Status + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Children = ((CarouselBeatmapSet)Item).Beatmaps.Select(b => new FilterableDifficultyIcon(b)).ToList() + }, + } } } } diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs index 8a0052559e..e11b342efe 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselItem.cs @@ -8,7 +8,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.MathUtils; using osu.Game.Graphics; using OpenTK; @@ -72,18 +72,18 @@ namespace osu.Game.Screens.Select.Carousel hoverLayer.Colour = colours.Blue.Opacity(0.1f); } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { sampleHover?.Play(); hoverLayer.FadeIn(100, Easing.OutQuint); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { hoverLayer.FadeOut(1000, Easing.OutQuint); - base.OnHoverLost(state); + base.OnHoverLost(e); } public void SetMultiplicativeAlpha(float alpha) => borderContainer.Alpha = alpha; @@ -145,7 +145,7 @@ namespace osu.Game.Screens.Select.Carousel }; } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { Item.State.Value = CarouselItemState.Selected; return true; diff --git a/osu.Game/Screens/Select/Filter/GroupMode.cs b/osu.Game/Screens/Select/Filter/GroupMode.cs index 6e57843dfc..b3bd73ee59 100644 --- a/osu.Game/Screens/Select/Filter/GroupMode.cs +++ b/osu.Game/Screens/Select/Filter/GroupMode.cs @@ -21,8 +21,8 @@ namespace osu.Game.Screens.Select.Filter DateAdded, [Description("Difficulty")] Difficulty, - [Description("Favorites")] - Favorites, + [Description("Favourites")] + Favourites, [Description("Length")] Length, [Description("My Maps")] diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index 9ba8b085f3..fce7af1400 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -14,8 +14,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Select.Filter; using Container = osu.Framework.Graphics.Containers.Container; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Configuration; using osu.Game.Rulesets; @@ -72,8 +71,8 @@ namespace osu.Game.Screens.Select private readonly SearchTextBox searchTextBox; - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => - base.ReceiveMouseInputAt(screenSpacePos) || groupTabs.ReceiveMouseInputAt(screenSpacePos) || sortTabs.ReceiveMouseInputAt(screenSpacePos); + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => + base.ReceivePositionalInputAt(screenSpacePos) || groupTabs.ReceivePositionalInputAt(screenSpacePos) || sortTabs.ReceivePositionalInputAt(screenSpacePos); public FilterControl() { @@ -187,10 +186,10 @@ namespace osu.Game.Screens.Select private void updateCriteria() => FilterChanged?.Invoke(CreateCriteria()); - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true; + protected override bool OnMouseDown(MouseDownEvent e) => true; - protected override bool OnMouseMove(InputState state) => true; + protected override bool OnMouseMove(MouseMoveEvent e) => true; - protected override bool OnClick(InputState state) => true; + protected override bool OnClick(ClickEvent e) => true; } } diff --git a/osu.Game/Screens/Select/Footer.cs b/osu.Game/Screens/Select/Footer.cs index bc4d216f00..5fe1aa31ac 100644 --- a/osu.Game/Screens/Select/Footer.cs +++ b/osu.Game/Screens/Select/Footer.cs @@ -11,8 +11,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics.UserInterface; namespace osu.Game.Screens.Select @@ -139,8 +138,8 @@ namespace osu.Game.Screens.Select updateModeLight(); } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true; + protected override bool OnMouseDown(MouseDownEvent e) => true; - protected override bool OnClick(InputState state) => true; + protected override bool OnClick(ClickEvent e) => true; } } diff --git a/osu.Game/Screens/Select/FooterButton.cs b/osu.Game/Screens/Select/FooterButton.cs index 1b0e3a1620..4f12082e08 100644 --- a/osu.Game/Screens/Select/FooterButton.cs +++ b/osu.Game/Screens/Select/FooterButton.cs @@ -8,8 +8,7 @@ using OpenTK.Input; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Containers; @@ -56,7 +55,7 @@ namespace osu.Game.Screens.Select private readonly Box box; private readonly Box light; - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => box.ReceiveMouseInputAt(screenSpacePos); + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => box.ReceivePositionalInputAt(screenSpacePos); public FooterButton() { @@ -89,7 +88,7 @@ namespace osu.Game.Screens.Select public Action HoverLost; public Key? Hotkey; - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { Hovered?.Invoke(); light.ScaleTo(new Vector2(1, 2), Footer.TRANSITION_LENGTH, Easing.OutQuint); @@ -97,42 +96,42 @@ namespace osu.Game.Screens.Select return true; } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { HoverLost?.Invoke(); light.ScaleTo(new Vector2(1, 1), Footer.TRANSITION_LENGTH, Easing.OutQuint); light.FadeColour(DeselectedColour, Footer.TRANSITION_LENGTH, Easing.OutQuint); } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(MouseDownEvent e) { box.FadeTo(0.3f, Footer.TRANSITION_LENGTH * 2, Easing.OutQuint); - return base.OnMouseDown(state, args); + return base.OnMouseDown(e); } - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + protected override bool OnMouseUp(MouseUpEvent e) { box.FadeOut(Footer.TRANSITION_LENGTH, Easing.OutQuint); - return base.OnMouseUp(state, args); + return base.OnMouseUp(e); } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { box.ClearTransforms(); box.Alpha = 1; box.FadeOut(Footer.TRANSITION_LENGTH * 3, Easing.OutQuint); - return base.OnClick(state); + return base.OnClick(e); } - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + protected override bool OnKeyDown(KeyDownEvent e) { - if (!args.Repeat && args.Key == Hotkey) + if (!e.Repeat && e.Key == Hotkey) { - OnClick(state); + Click(); return true; } - return base.OnKeyDown(state, args); + return base.OnKeyDown(e); } } } diff --git a/osu.Game/Screens/Select/ImportFromStablePopup.cs b/osu.Game/Screens/Select/ImportFromStablePopup.cs index 7c3b98cc2e..a52b5bab2a 100644 --- a/osu.Game/Screens/Select/ImportFromStablePopup.cs +++ b/osu.Game/Screens/Select/ImportFromStablePopup.cs @@ -12,7 +12,7 @@ namespace osu.Game.Screens.Select public ImportFromStablePopup(Action importFromStable) { HeaderText = @"You have no beatmaps!"; - BodyText = "An existing copy of osu! was found, though.\nWould you like to import your beatmaps?"; + BodyText = "An existing copy of osu! was found, though.\nWould you like to import your beatmaps (and skins)?"; Icon = FontAwesome.fa_plane; diff --git a/osu.Game/Screens/Select/Leaderboards/DrawableRank.cs b/osu.Game/Screens/Select/Leaderboards/DrawableRank.cs index 0c4b369f36..0cf1e60304 100644 --- a/osu.Game/Screens/Select/Leaderboards/DrawableRank.cs +++ b/osu.Game/Screens/Select/Leaderboards/DrawableRank.cs @@ -41,7 +41,10 @@ namespace osu.Game.Screens.Select.Leaderboards updateTexture(); } - private void updateTexture() => rankSprite.Texture = textures.Get($@"Grades/{Rank.GetDescription()}"); + private void updateTexture() + { + rankSprite.Texture = textures.Get($@"Grades/{Rank.GetDescription()}"); + } public void UpdateRank(ScoreRank newRank) { diff --git a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs index 95e9dde68e..241f6da3d1 100644 --- a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs @@ -10,7 +10,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -251,16 +251,16 @@ namespace osu.Game.Screens.Select.Leaderboards } } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { background.FadeTo(0.5f, 300, Easing.OutQuint); - return base.OnHover(state); + return base.OnHover(e); } - protected override void OnHoverLost(InputState state) + protected override void OnHoverLost(HoverLostEvent e) { background.FadeTo(background_alpha, 200, Easing.OutQuint); - base.OnHoverLost(state); + base.OnHoverLost(e); } private class GlowingSpriteText : Container diff --git a/osu.Game/Screens/Select/Leaderboards/Placeholder.cs b/osu.Game/Screens/Select/Leaderboards/Placeholder.cs index 307986a299..468b43e54f 100644 --- a/osu.Game/Screens/Select/Leaderboards/Placeholder.cs +++ b/osu.Game/Screens/Select/Leaderboards/Placeholder.cs @@ -11,8 +11,6 @@ namespace osu.Game.Screens.Select.Leaderboards { protected const float TEXT_SIZE = 22; - public override bool HandleMouseInput => true; - protected Placeholder() : base(cp => cp.TextSize = TEXT_SIZE) { diff --git a/osu.Game/Screens/Select/Leaderboards/RetrievalFailurePlaceholder.cs b/osu.Game/Screens/Select/Leaderboards/RetrievalFailurePlaceholder.cs index 19cba72f1f..6601c9df8b 100644 --- a/osu.Game/Screens/Select/Leaderboards/RetrievalFailurePlaceholder.cs +++ b/osu.Game/Screens/Select/Leaderboards/RetrievalFailurePlaceholder.cs @@ -3,8 +3,7 @@ using System; using osu.Framework.Graphics; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using OpenTK; @@ -49,16 +48,16 @@ namespace osu.Game.Screens.Select.Leaderboards }; } - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(MouseDownEvent e) { icon.ScaleTo(0.8f, 4000, Easing.OutQuint); - return base.OnMouseDown(state, args); + return base.OnMouseDown(e); } - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + protected override bool OnMouseUp(MouseUpEvent e) { icon.ScaleTo(1, 1000, Easing.OutElastic); - return base.OnMouseUp(state, args); + return base.OnMouseUp(e); } } } diff --git a/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs index d4cd882433..cd775419be 100644 --- a/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs +++ b/osu.Game/Screens/Select/Options/BeatmapOptionsButton.cs @@ -5,8 +5,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using OpenTK; @@ -53,39 +52,39 @@ namespace osu.Game.Screens.Select.Options public Key? HotKey; - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + protected override bool OnMouseDown(MouseDownEvent e) { flash.FadeTo(0.1f, 1000, Easing.OutQuint); - return base.OnMouseDown(state, args); + return base.OnMouseDown(e); } - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + protected override bool OnMouseUp(MouseUpEvent e) { flash.FadeTo(0, 1000, Easing.OutQuint); - return base.OnMouseUp(state, args); + return base.OnMouseUp(e); } - protected override bool OnClick(InputState state) + protected override bool OnClick(ClickEvent e) { flash.ClearTransforms(); flash.Alpha = 0.9f; flash.FadeOut(800, Easing.OutExpo); - return base.OnClick(state); + return base.OnClick(e); } - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + protected override bool OnKeyDown(KeyDownEvent e) { - if (!args.Repeat && args.Key == HotKey) + if (!e.Repeat && e.Key == HotKey) { - OnClick(state); + Click(); return true; } return false; } - public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => box.ReceiveMouseInputAt(screenSpacePos); + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => box.ReceivePositionalInputAt(screenSpacePos); public BeatmapOptionsButton() { diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index e914eb365e..917a08d172 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using OpenTK.Input; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -20,6 +19,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Screens.Edit; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; +using osu.Game.Skinning; namespace osu.Game.Screens.Select { @@ -55,7 +55,7 @@ namespace osu.Game.Screens.Select private readonly Bindable> selectedMods = new Bindable>(new Mod[] { }); [BackgroundDependencyLoader(true)] - private void load(OsuColour colours, AudioManager audio, BeatmapManager beatmaps, DialogOverlay dialogOverlay, Bindable> selectedMods) + private void load(OsuColour colours, AudioManager audio, BeatmapManager beatmaps, SkinManager skins, DialogOverlay dialogOverlay, Bindable> selectedMods) { if (selectedMods != null) this.selectedMods.BindTo(selectedMods); @@ -65,7 +65,7 @@ namespace osu.Game.Screens.Select BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.fa_times_circle_o, colours.Purple, null, Key.Number1); BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.fa_eraser, colours.Purple, null, Key.Number2); - BeatmapOptions.AddButton(@"Edit", @"Beatmap", FontAwesome.fa_pencil, colours.Yellow, () => + BeatmapOptions.AddButton(@"Edit", @"beatmap", FontAwesome.fa_pencil, colours.Yellow, () => { ValidForResume = false; Push(new Editor()); @@ -78,7 +78,10 @@ namespace osu.Game.Screens.Select // if we have no beatmaps but osu-stable is found, let's prompt the user to import. if (!beatmaps.GetAllUsableBeatmapSets().Any() && beatmaps.StableInstallationAvailable) dialogOverlay.Push(new ImportFromStablePopup(() => - Task.Factory.StartNew(beatmaps.ImportFromStable, TaskCreationOptions.LongRunning))); + { + beatmaps.ImportFromStableAsync(); + skins.ImportFromStableAsync(); + })); }); } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index dcc0760262..b4f552ce93 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -13,8 +13,7 @@ using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Logging; -using osu.Framework.Input.EventArgs; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.Screens; using osu.Framework.Threading; using osu.Game.Beatmaps; @@ -205,7 +204,7 @@ namespace osu.Game.Screens.Select Footer.AddButton(@"random", colours.Green, triggerRandom, Key.F2); Footer.AddButton(@"options", colours.Blue, BeatmapOptions, Key.F3); - BeatmapOptions.AddButton(@"Delete", @"Beatmap", FontAwesome.fa_trash, colours.Pink, () => delete(Beatmap.Value.BeatmapSetInfo), Key.Number4, float.MaxValue); + BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.fa_trash, colours.Pink, () => delete(Beatmap.Value.BeatmapSetInfo), Key.Number4, float.MaxValue); } if (this.beatmaps == null) @@ -221,7 +220,7 @@ namespace osu.Game.Screens.Select sampleChangeDifficulty = audio.Sample.Get(@"SongSelect/select-difficulty"); sampleChangeBeatmap = audio.Sample.Get(@"SongSelect/select-expand"); - Carousel.BeatmapSets = this.beatmaps.GetAllUsableBeatmapSetsEnumerable(); + Carousel.LoadBeatmapSetsFromManager(this.beatmaps); } public void Edit(BeatmapInfo beatmap) @@ -536,7 +535,7 @@ namespace osu.Game.Screens.Select private void delete(BeatmapSetInfo beatmap) { - if (beatmap == null) return; + if (beatmap == null || beatmap.ID <= 0) return; dialogOverlay?.Push(new BeatmapDeleteDialog(beatmap)); } @@ -554,14 +553,14 @@ namespace osu.Game.Screens.Select return base.OnPressed(action); } - protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + protected override bool OnKeyDown(KeyDownEvent e) { - if (args.Repeat) return false; + if (e.Repeat) return false; - switch (args.Key) + switch (e.Key) { case Key.Delete: - if (state.Keyboard.ShiftPressed) + if (e.ShiftPressed) { if (!Beatmap.IsDefault) delete(Beatmap.Value.BeatmapSetInfo); @@ -571,7 +570,7 @@ namespace osu.Game.Screens.Select break; } - return base.OnKeyDown(state, args); + return base.OnKeyDown(e); } private class ResetScrollContainer : Container @@ -583,10 +582,10 @@ namespace osu.Game.Screens.Select this.onHoverAction = onHoverAction; } - protected override bool OnHover(InputState state) + protected override bool OnHover(HoverEvent e) { onHoverAction?.Invoke(); - return base.OnHover(state); + return base.OnHover(e); } } } diff --git a/osu.Game/Screens/Tournament/Drawings.cs b/osu.Game/Screens/Tournament/Drawings.cs index 63d29d5cd7..4e2109afd0 100644 --- a/osu.Game/Screens/Tournament/Drawings.cs +++ b/osu.Game/Screens/Tournament/Drawings.cs @@ -59,7 +59,7 @@ namespace osu.Game.Screens.Tournament TextureStore flagStore = new TextureStore(); // Local flag store - flagStore.AddStore(new RawTextureLoaderStore(new NamespacedResourceStore(new StorageBackedResourceStore(storage), "Drawings"))); + flagStore.AddStore(new TextureLoaderStore(new NamespacedResourceStore(new StorageBackedResourceStore(storage), "Drawings"))); // Default texture store flagStore.AddStore(textures); diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 45c6ec80aa..ce7edf8683 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -4,6 +4,7 @@ using System; using System.IO; using System.Linq; +using System.Threading.Tasks; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; @@ -36,7 +37,7 @@ namespace osu.Game.Skinning Configuration = new SkinConfiguration(); Samples = audioManager.GetSampleManager(storage); - Textures = new TextureStore(new RawTextureLoaderStore(storage)); + Textures = new TextureStore(new TextureLoaderStore(storage)); } public override Drawable GetDrawableComponent(string componentName) @@ -108,10 +109,12 @@ namespace osu.Game.Skinning return path == null ? null : underlyingStore.GetStream(path); } - byte[] IResourceStore.Get(string name) + byte[] IResourceStore.Get(string name) => GetAsync(name).Result; + + public Task GetAsync(string name) { string path = getPathForFile(name); - return path == null ? null : underlyingStore.Get(path); + return path == null ? Task.FromResult(null) : underlyingStore.GetAsync(path); } #region IDisposable Support diff --git a/osu.Game/Skinning/LocalSkinOverrideContainer.cs b/osu.Game/Skinning/LocalSkinOverrideContainer.cs index 3adf287cf7..25d9442e6f 100644 --- a/osu.Game/Skinning/LocalSkinOverrideContainer.cs +++ b/osu.Game/Skinning/LocalSkinOverrideContainer.cs @@ -85,12 +85,10 @@ namespace osu.Game.Skinning private void load(OsuConfigManager config) { beatmapSkins = config.GetBindable(OsuSetting.BeatmapSkins); - beatmapSkins.ValueChanged += val => onSourceChanged(); - beatmapSkins.TriggerChange(); + beatmapSkins.BindValueChanged(_ => onSourceChanged()); beatmapHitsounds = config.GetBindable(OsuSetting.BeatmapHitsounds); - beatmapHitsounds.ValueChanged += val => onSourceChanged(); - beatmapHitsounds.TriggerChange(); + beatmapHitsounds.BindValueChanged(_ => onSourceChanged(), true); } protected override void LoadComplete() diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 2387924cfa..bd694e443a 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -26,17 +26,25 @@ namespace osu.Game.Skinning public override string[] HandledExtensions => new[] { ".osk" }; + protected override string ImportFromStablePath => "Skins"; + /// - /// Returns a list of all usable s. + /// Returns a list of all usable s. Includes the special default skin plus all skins from . /// /// A list of available . public List GetAllUsableSkins() { - var userSkins = ModelStore.ConsumableItems.Where(s => !s.DeletePending).ToList(); + var userSkins = GetAllUserSkins(); userSkins.Insert(0, SkinInfo.Default); return userSkins; } + /// + /// Returns a list of all usable s that have been loaded by the user. + /// + /// A list of available . + public List GetAllUserSkins() => ModelStore.ConsumableItems.Where(s => !s.DeletePending).ToList(); + protected override SkinInfo CreateModel(ArchiveReader archive) => new SkinInfo { Name = archive.Name @@ -85,6 +93,13 @@ namespace osu.Game.Skinning { this.audio = audio; + ItemRemoved += removedInfo => + { + // check the removed skin is not the current user choice. if it is, switch back to default. + if (removedInfo.ID == CurrentSkinInfo.Value.ID) + CurrentSkinInfo.Value = SkinInfo.Default; + }; + CurrentSkinInfo.ValueChanged += info => CurrentSkin.Value = GetSkin(info); CurrentSkin.ValueChanged += skin => { @@ -93,16 +108,6 @@ namespace osu.Game.Skinning SourceChanged?.Invoke(); }; - - // migrate older imports which didn't have access to skin.ini - using (ContextFactory.GetForWrite()) - { - foreach (var skinInfo in ModelStore.ConsumableItems.Where(s => s.Name.EndsWith(".osk"))) - { - populate(skinInfo); - Update(skinInfo); - } - } } /// diff --git a/osu.Game/Skinning/SkinReloadableDrawable.cs b/osu.Game/Skinning/SkinReloadableDrawable.cs index f1ee0db6ce..0b94697405 100644 --- a/osu.Game/Skinning/SkinReloadableDrawable.cs +++ b/osu.Game/Skinning/SkinReloadableDrawable.cs @@ -52,5 +52,13 @@ namespace osu.Game.Skinning protected virtual void SkinChanged(ISkinSource skin, bool allowFallback) { } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (skin != null) + skin.SourceChanged -= onChange; + } } } diff --git a/osu.Game/Storyboards/CommandTimeline.cs b/osu.Game/Storyboards/CommandTimeline.cs index 7de0756dbd..64b0a45fcd 100644 --- a/osu.Game/Storyboards/CommandTimeline.cs +++ b/osu.Game/Storyboards/CommandTimeline.cs @@ -16,13 +16,13 @@ namespace osu.Game.Storyboards public bool HasCommands => commands.Count > 0; private Cached startTimeBacking; - public double StartTime => startTimeBacking.IsValid ? startTimeBacking : (startTimeBacking.Value = HasCommands ? commands.Min(c => c.StartTime) : double.MinValue); + public double StartTime => startTimeBacking.IsValid ? startTimeBacking : startTimeBacking.Value = HasCommands ? commands.Min(c => c.StartTime) : double.MinValue; private Cached endTimeBacking; - public double EndTime => endTimeBacking.IsValid ? endTimeBacking : (endTimeBacking.Value = HasCommands ? commands.Max(c => c.EndTime) : double.MaxValue); + public double EndTime => endTimeBacking.IsValid ? endTimeBacking : endTimeBacking.Value = HasCommands ? commands.Max(c => c.EndTime) : double.MaxValue; - public T StartValue => HasCommands ? commands.OrderBy(c => c.StartTime).First().StartValue : default(T); - public T EndValue => HasCommands ? commands.OrderByDescending(c => c.EndTime).First().EndValue : default(T); + public T StartValue => HasCommands ? commands.OrderBy(c => c.StartTime).First().StartValue : default; + public T EndValue => HasCommands ? commands.OrderByDescending(c => c.EndTime).First().EndValue : default; public void Add(Easing easing, double startTime, double endTime, T startValue, T endValue) { diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs index d746bb90c4..ef03539998 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs @@ -18,8 +18,6 @@ namespace osu.Game.Storyboards.Drawables protected override Container Content => content; protected override Vector2 DrawScale => new Vector2(Parent.DrawHeight / 480); - public override bool HandleKeyboardInput => false; - public override bool HandleMouseInput => false; private bool passing = true; public bool Passing @@ -57,7 +55,7 @@ namespace osu.Game.Storyboards.Drawables [BackgroundDependencyLoader] private void load(FileStore fileStore) { - dependencies.Cache(new TextureStore(new RawTextureLoaderStore(fileStore.Store), false) { ScaleAdjust = 1, }); + dependencies.Cache(new TextureStore(new TextureLoaderStore(fileStore.Store), false, scaleAdjust: 1)); foreach (var layer in Storyboard.Layers) Add(layer.CreateDrawable()); diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs index cdec8c042f..4f469ae593 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs @@ -41,7 +41,7 @@ namespace osu.Game.Storyboards.Drawables { base.Update(); - // TODO: this logic will need to be consolidated with other game samples like hitsounds. + // TODO: this logic will need to be consolidated with other game samples like hit sounds. if (Time.Current < sample.Time) { // We've rewound before the start time of the sample diff --git a/osu.Game/Tests/Visual/EditorClockTestCase.cs b/osu.Game/Tests/Visual/EditorClockTestCase.cs index 0ca7ff011f..ebede74171 100644 --- a/osu.Game/Tests/Visual/EditorClockTestCase.cs +++ b/osu.Game/Tests/Visual/EditorClockTestCase.cs @@ -2,7 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; -using osu.Framework.Input.States; +using osu.Framework.Input.Events; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -56,9 +56,9 @@ namespace osu.Game.Tests.Visual Clock.ProcessFrame(); } - protected override bool OnScroll(InputState state) + protected override bool OnScroll(ScrollEvent e) { - if (state.Mouse.ScrollDelta.Y > 0) + if (e.ScrollDelta.Y > 0) Clock.SeekBackward(true); else Clock.SeekForward(true); diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs index 9afb1dd6cd..9e499cb961 100644 --- a/osu.Game/Tests/Visual/TestCasePlayer.cs +++ b/osu.Game/Tests/Visual/TestCasePlayer.cs @@ -54,7 +54,7 @@ namespace osu.Game.Tests.Visual AddStep(r.Name, () => p = loadPlayerFor(r)); AddUntilStep(() => ContinueCondition(p)); - AddAssert("no leaked beatmaps", () => + AddUntilStep(() => { p = null; @@ -64,9 +64,9 @@ namespace osu.Game.Tests.Visual workingWeakReferences.ForEachAlive(_ => count++); return count == 1; - }); + }, "no leaked beatmaps"); - AddAssert("no leaked players", () => + AddUntilStep(() => { GC.Collect(); GC.WaitForPendingFinalizers(); @@ -74,7 +74,7 @@ namespace osu.Game.Tests.Visual playerWeakReferences.ForEachAlive(_ => count++); return count == 1; - }); + }, "no leaked players"); } } } diff --git a/osu.Game/Users/Avatar.cs b/osu.Game/Users/Avatar.cs index 9ba9549164..e6e1ba3c53 100644 --- a/osu.Game/Users/Avatar.cs +++ b/osu.Game/Users/Avatar.cs @@ -24,7 +24,7 @@ namespace osu.Game.Users } [BackgroundDependencyLoader] - private void load(TextureStore textures) + private void load(LargeTextureStore textures) { if (textures == null) throw new ArgumentNullException(nameof(textures)); diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index f42df4023f..a5d8c03a67 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -83,8 +83,8 @@ namespace osu.Game.Users [JsonProperty(@"location")] public string Location; - [JsonProperty(@"lastvisit")] - public DateTimeOffset LastVisit; + [JsonProperty(@"last_visit")] + public DateTimeOffset? LastVisit; [JsonProperty(@"twitter")] public string Twitter; @@ -144,5 +144,14 @@ namespace osu.Game.Users public Badge[] Badges; public override string ToString() => Username; + + /// + /// A user instance for displaying locally created system messages. + /// + public static readonly User SYSTEM_USER = new User + { + Username = "system", + Id = 0 + }; } } diff --git a/osu.Game/Users/UserCoverBackground.cs b/osu.Game/Users/UserCoverBackground.cs index 58b92b2750..fddbd57f9c 100644 --- a/osu.Game/Users/UserCoverBackground.cs +++ b/osu.Game/Users/UserCoverBackground.cs @@ -18,7 +18,7 @@ namespace osu.Game.Users } [BackgroundDependencyLoader] - private void load(TextureStore textures) + private void load(LargeTextureStore textures) { if (textures == null) throw new ArgumentNullException(nameof(textures)); diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 06fb1c4f82..cc21f4f6a4 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -14,13 +14,13 @@ - - - + + + - + - + diff --git a/osu.TestProject.props b/osu.TestProject.props index a73a4f8ce2..456ecfd468 100644 --- a/osu.TestProject.props +++ b/osu.TestProject.props @@ -10,10 +10,6 @@ - - - - VisualTestRunner.cs diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 1f1b6a79b1..345400305c 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -218,6 +218,7 @@ GMT QAT BNG + UI HINT <?xml version="1.0" encoding="utf-16"?> <Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns"> @@ -665,6 +666,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste True True True + True True True True