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 @@
-
+