diff --git a/appveyor_deploy.yml b/appveyor_deploy.yml deleted file mode 100644 index 22a4859885..0000000000 --- a/appveyor_deploy.yml +++ /dev/null @@ -1,30 +0,0 @@ -clone_depth: 1 -version: '{build}' -skip_non_tags: true -image: Visual Studio 2017 -install: - - git clone https://github.com/ppy/osu-deploy -before_build: - - ps: if($env:appveyor_repo_tag -eq 'True') { Update-AppveyorBuild -Version $env:appveyor_repo_tag_name } - - cmd: git submodule update --init --recursive --depth=5 - - 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/BCrS8/7faccf7876.enc # signing certificate - - cmd: appveyor-tools\secure-file -decrypt 7faccf7876.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 - - msbuild osu.Desktop.Deploy.csproj - - cmd: ..\appveyor-tools\secure-file -decrypt ..\fdc6f19b04.enc -secret %decode_secret% -out bin\Debug\netcoreapp2.1\osu.Desktop.Deploy.dll.config - - dotnet bin/Debug/netcoreapp2.1/osu.Desktop.Deploy.dll %code_signing_password% %APPVEYOR_REPO_TAG_NAME% -environment: - decode_secret: - secure: i67IC2xj6DjjxmA6Oj2jing3+MwzLkq6CbGsjfZ7rdY= - code_signing_password: - secure: 34tLNqvjmmZEi97MLKfrnQ== -artifacts: - - path: 'osu-deploy/releases/*' -deploy: - - provider: Environment - name: github \ No newline at end of file diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 1e8bf05e01..e1e59804e5 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -27,7 +27,7 @@ - + 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/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.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/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs index 05ca1d4365..7bd39adb45 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs @@ -112,7 +112,7 @@ 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; diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseHitCirclePlacementMask.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCirclePlacementMask.cs index 4f94f88490..be0b94c4c8 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseHitCirclePlacementMask.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCirclePlacementMask.cs @@ -4,7 +4,7 @@ using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Edit.Masks.HitCircle; +using osu.Game.Rulesets.Osu.Edit.Masks.HitCircleMasks; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Tests.Visual; diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircleSelectionMask.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircleSelectionMask.cs index bafd7fa8bf..e3d61623bf 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircleSelectionMask.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircleSelectionMask.cs @@ -4,7 +4,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Osu.Edit.Masks.HitCircle; +using osu.Game.Rulesets.Osu.Edit.Masks.HitCircleMasks; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Tests.Visual; 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/TestCaseSliderSelectionMask.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSliderSelectionMask.cs index 0478946cb7..5e68d5cdc9 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestCaseSliderSelectionMask.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSliderSelectionMask.cs @@ -1,12 +1,11 @@ // 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.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects.Types; -using osu.Game.Rulesets.Osu.Edit.Masks.Slider; +using osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Tests.Visual; @@ -23,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Tests var slider = new Slider { Position = new Vector2(256, 192), - ControlPoints = new List + ControlPoints = new[] { Vector2.Zero, new Vector2(150, 150), diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs index 9e0e649eb2..b2914d4b82 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs @@ -43,7 +43,10 @@ namespace osu.Game.Rulesets.Osu.Beatmaps Position = positionData?.Position ?? Vector2.Zero, NewCombo = comboData?.NewCombo ?? false, ComboOffset = comboData?.ComboOffset ?? 0, - LegacyLastTickOffset = legacyOffset?.LegacyLastTickOffset + LegacyLastTickOffset = legacyOffset?.LegacyLastTickOffset, + // prior to v8, speed multipliers don't adjust for how many ticks are generated over the same distance. + // this results in more (or less) ticks being generated in ().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/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs index ccfcc1ef25..d6684f55af 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -75,15 +75,13 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing { computeSliderCursorPosition(lastSlider); lastCursorPosition = lastSlider.LazyEndPosition ?? lastCursorPosition; + + TravelDistance = lastSlider.LazyTravelDistance * 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() diff --git a/osu.Game.Rulesets.Osu/Edit/HitCircleCompositionTool.cs b/osu.Game.Rulesets.Osu/Edit/HitCircleCompositionTool.cs index 7f53409a32..767c7db5da 100644 --- a/osu.Game.Rulesets.Osu/Edit/HitCircleCompositionTool.cs +++ b/osu.Game.Rulesets.Osu/Edit/HitCircleCompositionTool.cs @@ -3,7 +3,7 @@ using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; -using osu.Game.Rulesets.Osu.Edit.Masks.HitCircle; +using osu.Game.Rulesets.Osu.Edit.Masks.HitCircleMasks; using osu.Game.Rulesets.Osu.Objects; namespace osu.Game.Rulesets.Osu.Edit diff --git a/osu.Game.Rulesets.Osu/Edit/Masks/HitCircle/Components/HitCirclePiece.cs b/osu.Game.Rulesets.Osu/Edit/Masks/HitCircleMasks/Components/HitCirclePiece.cs similarity index 81% rename from osu.Game.Rulesets.Osu/Edit/Masks/HitCircle/Components/HitCirclePiece.cs rename to osu.Game.Rulesets.Osu/Edit/Masks/HitCircleMasks/Components/HitCirclePiece.cs index 7f6ee32427..2c76a2e443 100644 --- a/osu.Game.Rulesets.Osu/Edit/Masks/HitCircle/Components/HitCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Masks/HitCircleMasks/Components/HitCirclePiece.cs @@ -9,19 +9,19 @@ using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using OpenTK; -namespace osu.Game.Rulesets.Osu.Edit.Masks.HitCircle.Components +namespace osu.Game.Rulesets.Osu.Edit.Masks.HitCircleMasks.Components { public class HitCirclePiece : CompositeDrawable { - private readonly Objects.HitCircle hitCircle; + private readonly HitCircle hitCircle; - public HitCirclePiece(Objects.HitCircle hitCircle) + public HitCirclePiece(HitCircle hitCircle) { this.hitCircle = hitCircle; - Origin = Anchor.Centre; Size = new Vector2((float)OsuHitObject.OBJECT_RADIUS * 2); + Scale = new Vector2(hitCircle.Scale); CornerRadius = Size.X / 2; InternalChild = new RingPiece(); @@ -39,12 +39,5 @@ namespace osu.Game.Rulesets.Osu.Edit.Masks.HitCircle.Components } protected virtual void UpdatePosition() => Position = hitCircle.StackedPosition; - - protected override void Update() - { - base.Update(); - - Scale = new Vector2(hitCircle.Scale); - } } } diff --git a/osu.Game.Rulesets.Osu/Edit/Masks/HitCircle/HitCirclePlacementMask.cs b/osu.Game.Rulesets.Osu/Edit/Masks/HitCircleMasks/HitCirclePlacementMask.cs similarity index 90% rename from osu.Game.Rulesets.Osu/Edit/Masks/HitCircle/HitCirclePlacementMask.cs rename to osu.Game.Rulesets.Osu/Edit/Masks/HitCircleMasks/HitCirclePlacementMask.cs index d172b87b44..930ccde814 100644 --- a/osu.Game.Rulesets.Osu/Edit/Masks/HitCircle/HitCirclePlacementMask.cs +++ b/osu.Game.Rulesets.Osu/Edit/Masks/HitCircleMasks/HitCirclePlacementMask.cs @@ -3,9 +3,9 @@ using osu.Framework.Input.Events; using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Osu.Edit.Masks.HitCircle.Components; +using osu.Game.Rulesets.Osu.Edit.Masks.HitCircleMasks.Components; -namespace osu.Game.Rulesets.Osu.Edit.Masks.HitCircle +namespace osu.Game.Rulesets.Osu.Edit.Masks.HitCircleMasks { public class HitCirclePlacementMask : PlacementMask { diff --git a/osu.Game.Rulesets.Osu/Edit/Masks/HitCircle/HitCircleSelectionMask.cs b/osu.Game.Rulesets.Osu/Edit/Masks/HitCircleMasks/HitCircleSelectionMask.cs similarity index 63% rename from osu.Game.Rulesets.Osu/Edit/Masks/HitCircle/HitCircleSelectionMask.cs rename to osu.Game.Rulesets.Osu/Edit/Masks/HitCircleMasks/HitCircleSelectionMask.cs index eb9696baa0..da46da92a5 100644 --- a/osu.Game.Rulesets.Osu/Edit/Masks/HitCircle/HitCircleSelectionMask.cs +++ b/osu.Game.Rulesets.Osu/Edit/Masks/HitCircleMasks/HitCircleSelectionMask.cs @@ -2,17 +2,18 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Osu.Edit.Masks.HitCircle.Components; +using osu.Game.Rulesets.Osu.Edit.Masks.HitCircleMasks.Components; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; -namespace osu.Game.Rulesets.Osu.Edit.Masks.HitCircle +namespace osu.Game.Rulesets.Osu.Edit.Masks.HitCircleMasks { public class HitCircleSelectionMask : SelectionMask { public HitCircleSelectionMask(DrawableHitCircle hitCircle) : base(hitCircle) { - InternalChild = new HitCirclePiece((Objects.HitCircle)hitCircle.HitObject); + InternalChild = new HitCirclePiece((HitCircle)hitCircle.HitObject); } } } diff --git a/osu.Game.Rulesets.Osu/Edit/Masks/Slider/Components/BodyPiece.cs b/osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/Components/SliderBodyPiece.cs similarity index 83% rename from osu.Game.Rulesets.Osu/Edit/Masks/Slider/Components/BodyPiece.cs rename to osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/Components/SliderBodyPiece.cs index ddf591b401..0595b046d1 100644 --- a/osu.Game.Rulesets.Osu/Edit/Masks/Slider/Components/BodyPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/Components/SliderBodyPiece.cs @@ -4,17 +4,18 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using OpenTK.Graphics; -namespace osu.Game.Rulesets.Osu.Edit.Masks.Slider.Components +namespace osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks.Components { - public class BodyPiece : CompositeDrawable + public class SliderBodyPiece : CompositeDrawable { - private readonly Objects.Slider slider; + private readonly Slider slider; private readonly SliderBody body; - public BodyPiece(Objects.Slider slider) + public SliderBodyPiece(Slider slider) { this.slider = slider; InternalChild = body = new SliderBody(slider) diff --git a/osu.Game.Rulesets.Osu/Edit/Masks/Slider/Components/SliderCirclePiece.cs b/osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/Components/SliderCirclePiece.cs similarity index 74% rename from osu.Game.Rulesets.Osu/Edit/Masks/Slider/Components/SliderCirclePiece.cs rename to osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/Components/SliderCirclePiece.cs index b1c05574d4..c5ecde5c4c 100644 --- a/osu.Game.Rulesets.Osu/Edit/Masks/Slider/Components/SliderCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/Components/SliderCirclePiece.cs @@ -1,16 +1,17 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Rulesets.Osu.Edit.Masks.HitCircle.Components; +using osu.Game.Rulesets.Osu.Edit.Masks.HitCircleMasks.Components; +using osu.Game.Rulesets.Osu.Objects; -namespace osu.Game.Rulesets.Osu.Edit.Masks.Slider.Components +namespace osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks.Components { public class SliderCirclePiece : HitCirclePiece { - private readonly Objects.Slider slider; + private readonly Slider slider; private readonly SliderPosition position; - public SliderCirclePiece(Objects.Slider slider, SliderPosition position) + public SliderCirclePiece(Slider slider, SliderPosition position) : base(slider.HeadCircle) { this.slider = slider; diff --git a/osu.Game.Rulesets.Osu/Edit/Masks/Slider/SliderCircleSelectionMask.cs b/osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/SliderCircleSelectionMask.cs similarity index 77% rename from osu.Game.Rulesets.Osu/Edit/Masks/Slider/SliderCircleSelectionMask.cs rename to osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/SliderCircleSelectionMask.cs index e65a3f2665..a1b3fd545c 100644 --- a/osu.Game.Rulesets.Osu/Edit/Masks/Slider/SliderCircleSelectionMask.cs +++ b/osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/SliderCircleSelectionMask.cs @@ -2,14 +2,15 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Osu.Edit.Masks.Slider.Components; +using osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks.Components; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; -namespace osu.Game.Rulesets.Osu.Edit.Masks.Slider +namespace osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks { public class SliderCircleSelectionMask : SelectionMask { - public SliderCircleSelectionMask(DrawableOsuHitObject hitObject, Objects.Slider slider, SliderPosition position) + public SliderCircleSelectionMask(DrawableOsuHitObject hitObject, Slider slider, SliderPosition position) : base(hitObject) { InternalChild = new SliderCirclePiece(slider, position); diff --git a/osu.Game.Rulesets.Osu/Edit/Masks/Slider/SliderPosition.cs b/osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/SliderPosition.cs similarity index 80% rename from osu.Game.Rulesets.Osu/Edit/Masks/Slider/SliderPosition.cs rename to osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/SliderPosition.cs index 07f45af3ef..01c1871131 100644 --- a/osu.Game.Rulesets.Osu/Edit/Masks/Slider/SliderPosition.cs +++ b/osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/SliderPosition.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 -namespace osu.Game.Rulesets.Osu.Edit.Masks.Slider +namespace osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks { public enum SliderPosition { diff --git a/osu.Game.Rulesets.Osu/Edit/Masks/Slider/SliderSelectionMask.cs b/osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/SliderSelectionMask.cs similarity index 76% rename from osu.Game.Rulesets.Osu/Edit/Masks/Slider/SliderSelectionMask.cs rename to osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/SliderSelectionMask.cs index e7a8652ed7..a411064f68 100644 --- a/osu.Game.Rulesets.Osu/Edit/Masks/Slider/SliderSelectionMask.cs +++ b/osu.Game.Rulesets.Osu/Edit/Masks/SliderMasks/SliderSelectionMask.cs @@ -3,11 +3,12 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.Edit; -using osu.Game.Rulesets.Osu.Edit.Masks.Slider.Components; +using osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks.Components; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using OpenTK; -namespace osu.Game.Rulesets.Osu.Edit.Masks.Slider +namespace osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks { public class SliderSelectionMask : SelectionMask { @@ -16,11 +17,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Masks.Slider public SliderSelectionMask(DrawableSlider slider) : base(slider) { - var sliderObject = (Objects.Slider)slider.HitObject; + var sliderObject = (Slider)slider.HitObject; InternalChildren = new Drawable[] { - new BodyPiece(sliderObject), + new SliderBodyPiece(sliderObject), headMask = new SliderCircleSelectionMask(slider.HeadCircle, sliderObject, SliderPosition.Start), new SliderCircleSelectionMask(slider.TailCircle, sliderObject, SliderPosition.End), }; diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index df72d2acca..ac41d6ef27 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -8,8 +8,8 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Edit.Masks.HitCircle; -using osu.Game.Rulesets.Osu.Edit.Masks.Slider; +using osu.Game.Rulesets.Osu.Edit.Masks.HitCircleMasks; +using osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.UI; 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/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 9a3bae96b0..a6f5bdb24e 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; } @@ -107,8 +107,21 @@ namespace osu.Game.Rulesets.Osu.Objects /// public double SpanDuration => Duration / this.SpanCount(); - public double Velocity; - public double TickDistance; + /// + /// Velocity of this . + /// + public double Velocity { get; private set; } + + /// + /// Spacing between s of this . + /// + public double TickDistance { get; private set; } + + /// + /// An extra multiplier that affects the number of s generated by this . + /// An increase in this value increases , which reduces the number of ticks generated. + /// + public double TickDistanceMultiplier = 1; public HitCircle HeadCircle; public SliderTailCircle TailCircle; @@ -123,7 +136,7 @@ namespace osu.Game.Rulesets.Osu.Objects double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier; Velocity = scoringDistance / timingPoint.BeatLength; - TickDistance = scoringDistance / difficulty.SliderTickRate; + TickDistance = scoringDistance / difficulty.SliderTickRate * TickDistanceMultiplier; } protected override void CreateNestedHitObjects() diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index af63a39662..f1ae366ee1 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -8,11 +8,13 @@ using OpenTK.Graphics; using osu.Game.Tests.Resources; using System.Linq; using osu.Game.Audio; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects.Types; using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Skinning; @@ -21,6 +23,25 @@ namespace osu.Game.Tests.Beatmaps.Formats [TestFixture] public class LegacyBeatmapDecoderTest { + [Test] + public void TestDecodeBeatmapVersion() + { + using (var resStream = Resource.OpenResource("beatmap-version.osu")) + using (var stream = new StreamReader(resStream)) + { + var decoder = Decoder.GetDecoder(stream); + + stream.BaseStream.Position = 0; + stream.DiscardBufferedData(); + + var working = new TestWorkingBeatmap(decoder.Decode(stream)); + + Assert.AreEqual(6, working.BeatmapInfo.BeatmapVersion); + Assert.AreEqual(6, working.Beatmap.BeatmapInfo.BeatmapVersion); + Assert.AreEqual(6, working.GetPlayableBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapVersion); + } + } + [Test] public void TestDecodeBeatmapGeneral() { diff --git a/osu.Game.Tests/Resources/beatmap-version.osu b/osu.Game.Tests/Resources/beatmap-version.osu new file mode 100644 index 0000000000..5749054ac4 --- /dev/null +++ b/osu.Game.Tests/Resources/beatmap-version.osu @@ -0,0 +1 @@ +osu file format v6 \ No newline at end of file diff --git a/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs b/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs index 482e801563..61647ffdc5 100644 --- a/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs @@ -13,8 +13,8 @@ using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Edit; -using osu.Game.Rulesets.Osu.Edit.Masks.HitCircle; -using osu.Game.Rulesets.Osu.Edit.Masks.HitCircle.Components; +using osu.Game.Rulesets.Osu.Edit.Masks.HitCircleMasks; +using osu.Game.Rulesets.Osu.Edit.Masks.HitCircleMasks.Components; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Screens.Edit.Screens.Compose; using osu.Game.Screens.Edit.Screens.Compose.Layers; @@ -53,14 +53,12 @@ namespace osu.Game.Tests.Visual new Slider { Position = new Vector2(128, 256), - ControlPoints = new List + ControlPoints = new[] { Vector2.Zero, new Vector2(216, 0), }, Distance = 216, - Velocity = 1, - TickDistance = 100, Scale = 0.5f, } }, 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 1aa4818393..3e1f3bdf54 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -19,7 +19,6 @@ namespace osu.Game.Beatmaps [JsonIgnore] public int ID { get; set; } - //TODO: should be in database public int BeatmapVersion; private int? onlineBeatmapID; diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 758a98e317..24c68d392b 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -148,11 +148,12 @@ namespace osu.Game.Beatmaps /// /// The to be downloaded. /// Whether the beatmap should be downloaded without video. Defaults to false. - public void Download(BeatmapSetInfo beatmapSetInfo, bool noVideo = false) + /// Downloading can happen + public bool Download(BeatmapSetInfo beatmapSetInfo, bool noVideo = false) { var existing = GetExistingDownload(beatmapSetInfo); - if (existing != null || api == null) return; + if (existing != null || api == null) return false; if (!api.LocalUser.Value.IsSupporter) { @@ -161,7 +162,7 @@ namespace osu.Game.Beatmaps Icon = FontAwesome.fa_superpowers, Text = "You gotta be an osu!supporter to download for now 'yo" }); - return; + return false; } var downloadNotification = new DownloadNotification @@ -227,6 +228,7 @@ namespace osu.Game.Beatmaps // don't run in the main api queue as this is a long-running task. Task.Factory.StartNew(() => request.Perform(api), TaskCreationOptions.LongRunning); BeatmapDownloadBegan?.Invoke(request); + return true; } protected override void PresentCompletedImport(IEnumerable imported) 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/Drawables/BeatmapSetDownloader.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetDownloader.cs index 6f4d4c0d6f..5b5dbec9c8 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapSetDownloader.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapSetDownloader.cs @@ -71,9 +71,11 @@ namespace osu.Game.Beatmaps.Drawables if (DownloadState.Value > DownloadStatus.NotDownloaded) return; - beatmaps.Download(set, noVideo); - - DownloadState.Value = DownloadStatus.Downloading; + if (beatmaps.Download(set, noVideo)) + { + // Only change state if download can happen + DownloadState.Value = DownloadStatus.Downloading; + } } private void setAdded(BeatmapSetInfo s) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 5b5bc5d936..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 diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index a9e1e4c55d..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; diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index a73a32325a..375c0b29c0 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -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 e0a22460ef..5b76122616 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -41,8 +41,13 @@ namespace osu.Game.Beatmaps beatmap = new RecyclableLazy(() => { var b = GetBeatmap() ?? new Beatmap(); - // use the database-backed info. + + // The original beatmap version needs to be preserved as the database doesn't contain it + BeatmapInfo.BeatmapVersion = b.BeatmapInfo.BeatmapVersion; + + // Use the database-backed info for more up-to-date values (beatmap id, ranked status, etc) b.BeatmapInfo = BeatmapInfo; + return b; }); diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 723bb90e7e..3686b702c4 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -404,7 +404,7 @@ 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) }); diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index 04a7cb64d3..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(); } diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs index 24183e07a0..e7d6a87349 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs @@ -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; } @@ -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; } 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/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 fde5c9fd82..663676a6d3 100644 --- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs +++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs @@ -14,7 +14,7 @@ namespace osu.Game.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "2.1.2-rtm-30932"); + .HasAnnotation("ProductVersion", "2.1.3-rtm-32065"); modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => { diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index 3c808d1bee..ffea7b83e1 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -1,16 +1,14 @@ // 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.ComponentModel; -using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Overlays.Direct; using osu.Game.Rulesets; namespace osu.Game.Online.API.Requests { - public class SearchBeatmapSetsRequest : APIRequest> + public class SearchBeatmapSetsRequest : APIRequest { private readonly string query; private readonly RulesetInfo ruleset; @@ -35,6 +33,7 @@ namespace osu.Game.Online.API.Requests public enum BeatmapSearchCategory { Any = 7, + [Description("Ranked & Approved")] RankedApproved = 0, Approved = 1, @@ -43,6 +42,7 @@ namespace osu.Game.Online.API.Requests Qualified = 3, Pending = 4, Graveyard = 5, + [Description("My Maps")] MyMaps = 6, } diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsResponse.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsResponse.cs new file mode 100644 index 0000000000..cf8b40d068 --- /dev/null +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsResponse.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 System.Collections.Generic; +using Newtonsoft.Json; +using osu.Game.Online.API.Requests.Responses; + +namespace osu.Game.Online.API.Requests +{ + public class SearchBeatmapSetsResponse + { + public IEnumerable BeatmapSets; + + /// + /// A collection of parameters which should be passed to the search endpoint to fetch the next page. + /// + [JsonProperty("cursor")] + public dynamic CursorJson; + } +} diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index f63d314053..641f57d25f 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -288,7 +288,7 @@ namespace osu.Game.Overlays { Task.Run(() => { - var sets = response.Select(r => r.ToBeatmapSet(rulesets)).ToList(); + var sets = response.BeatmapSets.Select(r => r.ToBeatmapSet(rulesets)).ToList(); // may not need scheduling; loads async internally. Schedule(() => diff --git a/osu.Game/Overlays/Profile/Header/RankGraph.cs b/osu.Game/Overlays/Profile/Header/RankGraph.cs index fc80370cf9..bfb01ce1c8 100644 --- a/osu.Game/Overlays/Profile/Header/RankGraph.cs +++ b/osu.Game/Overlays/Profile/Header/RankGraph.cs @@ -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, } 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/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 72168a4cd2..965e76d27a 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -72,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) { @@ -94,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); @@ -262,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/Storyboards/CommandTimeline.cs b/osu.Game/Storyboards/CommandTimeline.cs index 8a032064d3..64b0a45fcd 100644 --- a/osu.Game/Storyboards/CommandTimeline.cs +++ b/osu.Game/Storyboards/CommandTimeline.cs @@ -21,8 +21,8 @@ namespace osu.Game.Storyboards private Cached endTimeBacking; 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/osu.Game.csproj b/osu.Game/osu.Game.csproj index cc21f4f6a4..26004b513f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -18,7 +18,7 @@ - +