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 803927bc6f..e1e59804e5 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -27,9 +27,9 @@
-
-
-
+
+
+
diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs
index 34e07170bd..5e68acde94 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs
@@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
@@ -38,7 +37,7 @@ namespace osu.Game.Rulesets.Catch.Tests
beatmap.HitObjects.Add(new JuiceStream
{
X = 0.5f - width / 2,
- ControlPoints = new List
+ ControlPoints = new[]
{
Vector2.Zero,
new Vector2(width * CatchPlayfield.BASE_WIDTH, 0)
diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
index 51343d9e91..326791f506 100644
--- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
+++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
@@ -2,9 +2,10 @@
-
-
+
+
+
WinExe
diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
index 0344189af5..82e32d24d2 100644
--- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
+++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
@@ -146,7 +146,7 @@ namespace osu.Game.Rulesets.Catch.Objects
public SliderCurve Curve { get; } = new SliderCurve();
- public List ControlPoints
+ public Vector2[] ControlPoints
{
get { return Curve.ControlPoints; }
set { Curve.ControlPoints = value; }
diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
index 970dc2d630..925e7aaac9 100644
--- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
@@ -11,6 +11,7 @@ using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI.Scrolling;
+using OpenTK;
namespace osu.Game.Rulesets.Catch.UI
{
@@ -18,9 +19,6 @@ namespace osu.Game.Rulesets.Catch.UI
{
public const float BASE_WIDTH = 512;
- protected override Container Content => content;
- private readonly Container content;
-
private readonly CatcherArea catcherArea;
protected override bool UserScrollSpeedAdjustment => false;
@@ -28,7 +26,6 @@ namespace osu.Game.Rulesets.Catch.UI
protected override SpeedChangeVisualisationMethod VisualisationMethod => SpeedChangeVisualisationMethod.Constant;
public CatchPlayfield(BeatmapDifficulty difficulty, Func> getVisualRepresentation)
- : base(BASE_WIDTH)
{
Direction.Value = ScrollingDirection.Down;
@@ -37,27 +34,27 @@ namespace osu.Game.Rulesets.Catch.UI
Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
- base.Content.Anchor = Anchor.BottomLeft;
- base.Content.Origin = Anchor.BottomLeft;
+ Size = new Vector2(0.86f); // matches stable's vertical offset for catcher plate
- base.Content.AddRange(new Drawable[]
+ InternalChild = new PlayfieldAdjustmentContainer
{
- explodingFruitContainer = new Container
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
{
- RelativeSizeAxes = Axes.Both,
- },
- catcherArea = new CatcherArea(difficulty)
- {
- GetVisualRepresentation = getVisualRepresentation,
- ExplodingFruitTarget = explodingFruitContainer,
- Anchor = Anchor.BottomLeft,
- Origin = Anchor.TopLeft,
- },
- content = new Container
- {
- RelativeSizeAxes = Axes.Both,
- },
- });
+ explodingFruitContainer = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ catcherArea = new CatcherArea(difficulty)
+ {
+ GetVisualRepresentation = getVisualRepresentation,
+ ExplodingFruitTarget = explodingFruitContainer,
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.TopLeft,
+ },
+ HitObjectContainer
+ }
+ };
VisibleTimeRange.Value = BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
}
diff --git a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs
index 1ac052de4d..bd0fec43a1 100644
--- a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs
@@ -13,7 +13,6 @@ using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
-using OpenTK;
namespace osu.Game.Rulesets.Catch.UI
{
@@ -32,8 +31,6 @@ namespace osu.Game.Rulesets.Catch.UI
public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo);
- protected override Vector2 PlayfieldArea => new Vector2(0.86f); // matches stable's vertical offset for catcher plate
-
protected override DrawableHitObject GetVisualRepresentation(CatchHitObject h)
{
switch (h)
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
index be56ccf8c1..06453ac32d 100644
--- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
@@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Catch.UI
{
public class CatcherArea : Container
{
- public const float CATCHER_SIZE = 84;
+ public const float CATCHER_SIZE = 100;
protected readonly Catcher MovableCatcher;
diff --git a/osu.Game.Rulesets.Catch/UI/PlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Catch/UI/PlayfieldAdjustmentContainer.cs
new file mode 100644
index 0000000000..ad0073ff12
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/UI/PlayfieldAdjustmentContainer.cs
@@ -0,0 +1,42 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using OpenTK;
+
+namespace osu.Game.Rulesets.Catch.UI
+{
+ public class PlayfieldAdjustmentContainer : Container
+ {
+ protected override Container Content => content;
+ private readonly Container content;
+
+ public PlayfieldAdjustmentContainer()
+ {
+ InternalChild = new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ FillMode = FillMode.Fit,
+ FillAspectRatio = 4f / 3,
+ Child = content = new ScalingContainer { RelativeSizeAxes = Axes.Both }
+ };
+ }
+
+ ///
+ /// A which scales its content relative to a target width.
+ ///
+ private class ScalingContainer : Container
+ {
+ protected override void Update()
+ {
+ base.Update();
+
+ Scale = new Vector2(Parent.ChildSize.X / CatchPlayfield.BASE_WIDTH);
+ Size = Vector2.Divide(Vector2.One, Scale);
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
index 3165f69a6b..bf75ebbff8 100644
--- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
+++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
@@ -2,9 +2,10 @@
-
-
+
+
+
WinExe
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
index c15b303048..5d8480d969 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
@@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
TargetColumns = (int)Math.Max(1, roundedCircleSize);
else
{
- float percentSliderOrSpinner = (float)beatmap.HitObjects.Count(h => h is IHasEndTime) / beatmap.HitObjects.Count();
+ float percentSliderOrSpinner = (float)beatmap.HitObjects.Count(h => h is IHasEndTime) / beatmap.HitObjects.Count;
if (percentSliderOrSpinner < 0.2)
TargetColumns = 7;
else if (percentSliderOrSpinner < 0.3 || roundedCircleSize >= 5)
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/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.Mania/Configuration/ManiaConfigManager.cs b/osu.Game.Rulesets.Mania/Configuration/ManiaConfigManager.cs
index 3e9c9feba1..1c9e1e4c73 100644
--- a/osu.Game.Rulesets.Mania/Configuration/ManiaConfigManager.cs
+++ b/osu.Game.Rulesets.Mania/Configuration/ManiaConfigManager.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Configuration
{
base.InitialiseDefaults();
- Set(ManiaSetting.ScrollTime, 1500.0, 50.0, 10000.0, 50.0);
+ Set(ManiaSetting.ScrollTime, 2250.0, 50.0, 10000.0, 50.0);
Set(ManiaSetting.ScrollDirection, ManiaScrollingDirection.Down);
}
diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaEditRulesetContainer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaEditRulesetContainer.cs
index a01947a60b..138a2c0273 100644
--- a/osu.Game.Rulesets.Mania/Edit/ManiaEditRulesetContainer.cs
+++ b/osu.Game.Rulesets.Mania/Edit/ManiaEditRulesetContainer.cs
@@ -20,8 +20,7 @@ namespace osu.Game.Rulesets.Mania.Edit
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
+ Size = Vector2.One
};
-
- protected override Vector2 PlayfieldArea => Vector2.One;
}
}
diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs
index d489d48fc3..09976e5994 100644
--- a/osu.Game.Rulesets.Mania/UI/Column.cs
+++ b/osu.Game.Rulesets.Mania/UI/Column.cs
@@ -30,8 +30,6 @@ namespace osu.Game.Rulesets.Mania.UI
internal readonly Container TopLevelContainer;
private readonly Container explosionContainer;
- protected override Container Content => hitObjectArea;
-
public Column()
{
RelativeSizeAxes = Axes.Y;
@@ -54,7 +52,10 @@ namespace osu.Game.Rulesets.Mania.UI
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
- hitObjectArea = new ColumnHitObjectArea { RelativeSizeAxes = Axes.Both },
+ hitObjectArea = new ColumnHitObjectArea(HitObjectContainer)
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
explosionContainer = new Container
{
Name = "Hit explosions",
diff --git a/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs
index b5dfb0949a..5a4adfd72e 100644
--- a/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs
+++ b/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs
@@ -8,28 +8,24 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
+using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Mania.UI.Components
{
- public class ColumnHitObjectArea : Container, IHasAccentColour
+ public class ColumnHitObjectArea : CompositeDrawable, IHasAccentColour
{
private const float hit_target_height = 10;
private const float hit_target_bar_height = 2;
- private Container content;
- protected override Container Content => content;
-
private readonly IBindable direction = new Bindable();
- private Container hitTargetLine;
+ private readonly Container hitTargetLine;
+ private readonly Drawable hitTargetBar;
- [BackgroundDependencyLoader]
- private void load(IScrollingInfo scrollingInfo)
+ public ColumnHitObjectArea(HitObjectContainer hitObjectContainer)
{
- Drawable hitTargetBar;
-
InternalChildren = new[]
{
hitTargetBar = new Box
@@ -45,13 +41,13 @@ namespace osu.Game.Rulesets.Mania.UI.Components
Masking = true,
Child = new Box { RelativeSizeAxes = Axes.Both }
},
- content = new Container
- {
- Name = "Hit objects",
- RelativeSizeAxes = Axes.Both,
- },
+ hitObjectContainer
};
+ }
+ [BackgroundDependencyLoader]
+ private void load(IScrollingInfo scrollingInfo)
+ {
direction.BindTo(scrollingInfo.Direction);
direction.BindValueChanged(direction =>
{
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
index 999f84ed8e..5c3a618a19 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
@@ -10,6 +10,7 @@ using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects.Drawables;
+using OpenTK;
namespace osu.Game.Rulesets.Mania.UI
{
@@ -25,6 +26,8 @@ namespace osu.Game.Rulesets.Mania.UI
if (stageDefinitions.Count <= 0)
throw new ArgumentException("Can't have zero or fewer stages.");
+ Size = new Vector2(1, 0.8f);
+
GridContainer playfieldGrid;
AddInternal(playfieldGrid = new GridContainer
{
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs
index 09ebde2799..49874f6dc1 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs
@@ -24,7 +24,6 @@ using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
-using OpenTK;
namespace osu.Game.Rulesets.Mania.UI
{
@@ -110,8 +109,6 @@ namespace osu.Game.Rulesets.Mania.UI
}
}
- protected override Vector2 PlayfieldArea => new Vector2(1, 0.8f);
-
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);
}
}
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaScrollingPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaScrollingPlayfield.cs
index 4d6c5a747a..8ee0fbf7fe 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaScrollingPlayfield.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaScrollingPlayfield.cs
@@ -7,7 +7,7 @@ using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Mania.UI
{
- public class ManiaScrollingPlayfield : ScrollingPlayfield
+ public abstract class ManiaScrollingPlayfield : ScrollingPlayfield
{
private readonly IBindable direction = new Bindable();
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs
index f292d5ff16..8cf49686b9 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs
@@ -30,8 +30,7 @@ namespace osu.Game.Rulesets.Mania.UI
public IReadOnlyList Columns => columnFlow.Children;
private readonly FillFlowContainer columnFlow;
- protected override Container Content => barLineContainer;
- private readonly Container barLineContainer;
+ private readonly Container barLineContainer;
public Container Judgements => judgements;
private readonly JudgementContainer judgements;
@@ -105,6 +104,7 @@ namespace osu.Game.Rulesets.Mania.UI
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Y,
+ Child = HitObjectContainer
}
},
judgements = new JudgementContainer
diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs
index 3f9464a98f..300ac16155 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs
@@ -108,7 +108,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = Time.Current + 1000,
Position = new Vector2(239, 176),
- ControlPoints = new List
+ ControlPoints = new[]
{
Vector2.Zero,
new Vector2(154, 28),
@@ -141,7 +141,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = Time.Current + 1000,
Position = new Vector2(-(distance / 2), 0),
- ControlPoints = new List
+ ControlPoints = new[]
{
Vector2.Zero,
new Vector2(distance, 0),
@@ -161,7 +161,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = Time.Current + 1000,
Position = new Vector2(-200, 0),
- ControlPoints = new List
+ ControlPoints = new[]
{
Vector2.Zero,
new Vector2(200, 200),
@@ -184,7 +184,7 @@ namespace osu.Game.Rulesets.Osu.Tests
CurveType = CurveType.Linear,
StartTime = Time.Current + 1000,
Position = new Vector2(-200, 0),
- ControlPoints = new List
+ ControlPoints = new[]
{
Vector2.Zero,
new Vector2(150, 75),
@@ -210,7 +210,7 @@ namespace osu.Game.Rulesets.Osu.Tests
CurveType = CurveType.Bezier,
StartTime = Time.Current + 1000,
Position = new Vector2(-200, 0),
- ControlPoints = new List
+ ControlPoints = new[]
{
Vector2.Zero,
new Vector2(150, 75),
@@ -235,7 +235,7 @@ namespace osu.Game.Rulesets.Osu.Tests
CurveType = CurveType.Linear,
StartTime = Time.Current + 1000,
Position = new Vector2(0, 0),
- ControlPoints = new List
+ ControlPoints = new[]
{
Vector2.Zero,
new Vector2(-200, 0),
@@ -265,7 +265,7 @@ namespace osu.Game.Rulesets.Osu.Tests
StartTime = Time.Current + 1000,
Position = new Vector2(-100, 0),
CurveType = CurveType.Catmull,
- ControlPoints = new List
+ ControlPoints = new[]
{
Vector2.Zero,
new Vector2(50, -50),
diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
index 247d5e18c1..23c6150b6a 100644
--- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
+++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
@@ -2,9 +2,10 @@
-
-
+
+
+
WinExe
diff --git a/osu.Game.Rulesets.Osu/Beatmaps/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 )Beatmap);
+
+ var osuBeatmap = (Beatmap)Beatmap;
+
+ // Reset stacking
+ foreach (var h in osuBeatmap.HitObjects)
+ h.StackHeight = 0;
+
+ if (Beatmap.BeatmapInfo.BeatmapVersion >= 6)
+ applyStacking(osuBeatmap);
+ else
+ applyStackingOld(osuBeatmap);
}
private void applyStacking(Beatmap beatmap)
{
- const int stack_distance = 3;
-
- // Reset stacking
- for (int i = 0; i <= beatmap.HitObjects.Count - 1; i++)
- beatmap.HitObjects[i].StackHeight = 0;
-
// Extend the end index to include objects they are stacked on
int extendedEndIndex = beatmap.HitObjects.Count - 1;
for (int i = beatmap.HitObjects.Count - 1; i >= 0; i--)
@@ -167,5 +173,40 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
}
}
}
+
+ private void applyStackingOld(Beatmap beatmap)
+ {
+ for (int i = 0; i < beatmap.HitObjects.Count; i++)
+ {
+ OsuHitObject currHitObject = beatmap.HitObjects[i];
+
+ if (currHitObject.StackHeight != 0 && !(currHitObject is Slider))
+ continue;
+
+ double startTime = (currHitObject as IHasEndTime)?.EndTime ?? currHitObject.StartTime;
+ int sliderStack = 0;
+
+ for (int j = i + 1; j < beatmap.HitObjects.Count; j++)
+ {
+ double stackThreshold = beatmap.HitObjects[i].TimePreempt * beatmap.BeatmapInfo.StackLeniency;
+
+ if (beatmap.HitObjects[j].StartTime - stackThreshold > startTime)
+ break;
+
+ if (Vector2Extensions.Distance(beatmap.HitObjects[j].Position, currHitObject.Position) < stack_distance)
+ {
+ currHitObject.StackHeight++;
+ startTime = (beatmap.HitObjects[j] as IHasEndTime)?.EndTime ?? beatmap.HitObjects[i].StartTime;
+ }
+ else if (Vector2Extensions.Distance(beatmap.HitObjects[j].Position, currHitObject.EndPosition) < stack_distance)
+ {
+ //Case for sliders - bump notes down and right, rather than up and left.
+ sliderStack++;
+ beatmap.HitObjects[j].StackHeight -= sliderStack;
+ startTime = (beatmap.HitObjects[j] as IHasEndTime)?.EndTime ?? beatmap.HitObjects[i].StartTime;
+ }
+ }
+ }
+ }
}
}
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
index 5e91ed7a97..8fc2b69267 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
@@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double sectionLength = section_length * timeRate;
// The first object doesn't generate a strain, so we begin with an incremented section end
- double currentSectionEnd = 2 * sectionLength;
+ double currentSectionEnd = Math.Ceiling(beatmap.HitObjects.First().StartTime / sectionLength) * sectionLength;
foreach (OsuDifficultyHitObject h in difficultyBeatmap)
{
@@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double hitWindowGreat = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate;
double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate;
- int maxCombo = beatmap.HitObjects.Count();
+ int maxCombo = beatmap.HitObjects.Count;
// Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above)
maxCombo += beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.Count - 1);
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index d4a60dd52f..b0887ac72b 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
{
countHitCircles = Beatmap.HitObjects.Count(h => h is HitCircle);
- beatmapMaxCombo = Beatmap.HitObjects.Count();
+ beatmapMaxCombo = Beatmap.HitObjects.Count;
// Add the ticks + tail of the slider. 1 is subtracted because the "headcircle" would be counted twice (once for the slider itself in the line above)
beatmapMaxCombo += Beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.Count - 1);
}
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyBeatmap.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyBeatmap.cs
index 4443a0e66b..24d4677981 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyBeatmap.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyBeatmap.cs
@@ -3,6 +3,7 @@
using System.Collections;
using System.Collections.Generic;
+using System.Linq;
using osu.Game.Rulesets.Osu.Objects;
namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
@@ -23,8 +24,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
{
// Sort OsuHitObjects by StartTime - they are not correctly ordered in some cases.
// This should probably happen before the objects reach the difficulty calculator.
- objects.Sort((a, b) => a.StartTime.CompareTo(b.StartTime));
- difficultyObjects = createDifficultyObjectEnumerator(objects, timeRate);
+ difficultyObjects = createDifficultyObjectEnumerator(objects.OrderBy(h => h.StartTime).ToList(), timeRate);
}
///
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
index 29de23406b..d6684f55af 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
@@ -21,15 +21,25 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
public OsuHitObject BaseObject { get; }
///
- /// Normalized distance from the of the previous .
+ /// Normalized distance from the end position of the previous to the start position of this .
///
- public double Distance { get; private set; }
+ public double JumpDistance { get; private set; }
+
+ ///
+ /// Normalized distance between the start and end position of the previous .
+ ///
+ public double TravelDistance { get; private set; }
///
/// Milliseconds elapsed since the StartTime of the previous .
///
public double DeltaTime { get; private set; }
+ ///
+ /// Milliseconds elapsed since the start time of the previous , with a minimum of 50ms.
+ ///
+ public double StrainTime { get; private set; }
+
private readonly OsuHitObject lastObject;
private readonly double timeRate;
@@ -51,31 +61,35 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
private void setDistances()
{
// We will scale distances by this factor, so we can assume a uniform CircleSize among beatmaps.
- double scalingFactor = normalized_radius / BaseObject.Radius;
+ float scalingFactor = normalized_radius / (float)BaseObject.Radius;
if (BaseObject.Radius < 30)
{
- double smallCircleBonus = Math.Min(30 - BaseObject.Radius, 5) / 50;
+ float smallCircleBonus = Math.Min(30 - (float)BaseObject.Radius, 5) / 50;
scalingFactor *= 1 + smallCircleBonus;
}
Vector2 lastCursorPosition = lastObject.StackedPosition;
- float lastTravelDistance = 0;
var lastSlider = lastObject as Slider;
if (lastSlider != null)
{
computeSliderCursorPosition(lastSlider);
lastCursorPosition = lastSlider.LazyEndPosition ?? lastCursorPosition;
- lastTravelDistance = lastSlider.LazyTravelDistance;
+
+ TravelDistance = lastSlider.LazyTravelDistance * scalingFactor;
}
- Distance = (lastTravelDistance + (BaseObject.StackedPosition - lastCursorPosition).Length) * scalingFactor;
+ // Don't need to jump to reach spinners
+ if (!(BaseObject is Spinner))
+ JumpDistance = (BaseObject.StackedPosition * scalingFactor - lastCursorPosition * scalingFactor).Length;
}
private void setTimingValues()
{
- // Every timing inverval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure.
- DeltaTime = Math.Max(50, (BaseObject.StartTime - lastObject.StartTime) / timeRate);
+ DeltaTime = (BaseObject.StartTime - lastObject.StartTime) / timeRate;
+
+ // Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure
+ StrainTime = Math.Max(50, DeltaTime);
}
private void computeSliderCursorPosition(Slider slider)
@@ -87,8 +101,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
float approxFollowCircleRadius = (float)(slider.Radius * 3);
var computeVertex = new Action(t =>
{
+ double progress = ((int)t - (int)slider.StartTime) / (float)(int)slider.SpanDuration;
+ if (progress % 2 > 1)
+ progress = 1 - progress % 1;
+ else
+ progress = progress % 1;
+
// ReSharper disable once PossibleInvalidOperationException (bugged in current r# version)
- var diff = slider.StackedPositionAt(t) - slider.LazyEndPosition.Value;
+ var diff = slider.StackedPosition + slider.Curve.PositionAt(progress) - slider.LazyEndPosition.Value;
float dist = diff.Length;
if (dist > approxFollowCircleRadius)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs
index 0a45c62671..f11b6d66f6 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs
@@ -14,6 +14,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
protected override double SkillMultiplier => 26.25;
protected override double StrainDecayBase => 0.15;
- protected override double StrainValueOf(OsuDifficultyHitObject current) => Math.Pow(current.Distance, 0.99) / current.DeltaTime;
+ protected override double StrainValueOf(OsuDifficultyHitObject current)
+ => (Math.Pow(current.TravelDistance, 0.99) + Math.Pow(current.JumpDistance, 0.99)) / current.StrainTime;
}
}
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
index b807f20037..1cde03624b 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
protected override double StrainValueOf(OsuDifficultyHitObject current)
{
- double distance = current.Distance;
+ double distance = current.TravelDistance + current.JumpDistance;
double speedValue;
if (distance > single_spacing_threshold)
@@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
else
speedValue = 0.95;
- return speedValue / current.DeltaTime;
+ return speedValue / current.StrainTime;
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs b/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs
index 6efa16bf56..8571de39f4 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs
@@ -4,6 +4,7 @@
using osu.Framework.Graphics.Cursor;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu.UI;
+using osu.Game.Rulesets.UI;
using OpenTK;
namespace osu.Game.Rulesets.Osu.Edit
@@ -15,8 +16,8 @@ namespace osu.Game.Rulesets.Osu.Edit
{
}
- protected override Vector2 PlayfieldArea => Vector2.One;
-
protected override CursorContainer CreateCursor() => null;
+
+ protected override Playfield CreatePlayfield() => new OsuPlayfield { Size = Vector2.One };
}
}
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
index dce1fc2851..d6972d55d2 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
@@ -31,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Edit
new HitObjectCompositionTool()
};
- protected override ScalableContainer CreateLayerContainer() => new ScalableContainer(OsuPlayfield.BASE_SIZE.X) { RelativeSizeAxes = Axes.Both };
+ protected override Container CreateLayerContainer() => new PlayfieldAdjustmentContainer { RelativeSizeAxes = Axes.Both };
public override HitObjectMask CreateMaskFor(DrawableHitObject hitObject)
{
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs
index 7a30e6b134..1b3725a15e 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs
@@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using System.Collections.Generic;
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Rulesets.Mods;
@@ -33,8 +32,9 @@ namespace osu.Game.Rulesets.Osu.Mods
slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
- var newControlPoints = new List();
- slider.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, -c.Y)));
+ var newControlPoints = new Vector2[slider.ControlPoints.Length];
+ for (int i = 0; i < slider.ControlPoints.Length; i++)
+ newControlPoints[i] = new Vector2(slider.ControlPoints[i].X, -slider.ControlPoints[i].Y);
slider.ControlPoints = newControlPoints;
slider.Curve?.Calculate(); // Recalculate the slider curve
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs
index 2e601c9078..e0a93453ce 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs
@@ -37,6 +37,11 @@ namespace osu.Game.Rulesets.Osu.Mods
var osuObject = (OsuHitObject)drawable.HitObject;
Vector2 origin = drawable.Position;
+ // Wiggle the repeat points with the slider instead of independently.
+ // Also fixes an issue with repeat points being positioned incorrectly.
+ if (osuObject is RepeatPoint)
+ return;
+
Random objRand = new Random((int)osuObject.StartTime);
// Wiggle all objects during TimePreempt
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
index f3924ec43b..f4ccf673e9 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
@@ -8,26 +8,23 @@ using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Lines;
-using osu.Framework.Graphics.Textures;
using OpenTK.Graphics.ES30;
using OpenTK.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Game.Rulesets.Objects.Types;
using OpenTK;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.PixelFormats;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
public class SliderBody : Container, ISliderProgress
{
- private readonly Path path;
+ private readonly SliderPath path;
private readonly BufferedContainer container;
public float PathWidth
{
- get { return path.PathWidth; }
- set { path.PathWidth = value; }
+ get => path.PathWidth;
+ set => path.PathWidth = value;
}
///
@@ -43,48 +40,40 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public double? SnakedStart { get; private set; }
public double? SnakedEnd { get; private set; }
- private Color4 accentColour = Color4.White;
-
///
/// Used to colour the path.
///
public Color4 AccentColour
{
- get { return accentColour; }
+ get => path.AccentColour;
set
{
- if (accentColour == value)
+ if (path.AccentColour == value)
return;
- accentColour = value;
+ path.AccentColour = value;
- if (LoadState >= LoadState.Ready)
- reloadTexture();
+ container.ForceRedraw();
}
}
- private Color4 borderColour = Color4.White;
-
///
/// Used to colour the path border.
///
public new Color4 BorderColour
{
- get { return borderColour; }
+ get => path.BorderColour;
set
{
- if (borderColour == value)
+ if (path.BorderColour == value)
return;
- borderColour = value;
+ path.BorderColour = value;
- if (LoadState >= LoadState.Ready)
- reloadTexture();
+ container.ForceRedraw();
}
}
public Quad PathDrawQuad => container.ScreenSpaceDrawQuad;
- private int textureWidth => (int)PathWidth * 2;
-
private Vector2 topLeftOffset;
private readonly Slider slider;
@@ -101,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
CacheDrawnFrameBuffer = true,
Children = new Drawable[]
{
- path = new Path
+ path = new SliderPath
{
Blending = BlendingMode.None,
},
@@ -134,46 +123,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
[BackgroundDependencyLoader]
private void load()
{
- reloadTexture();
computeSize();
}
- private void reloadTexture()
- {
- var texture = new Texture(textureWidth, 1);
-
- //initialise background
- var raw = new Image(textureWidth, 1);
-
- const float aa_portion = 0.02f;
- const float border_portion = 0.128f;
- const float gradient_portion = 1 - border_portion;
-
- const float opacity_at_centre = 0.3f;
- const float opacity_at_edge = 0.8f;
-
- for (int i = 0; i < textureWidth; i++)
- {
- float progress = (float)i / (textureWidth - 1);
-
- if (progress <= border_portion)
- {
- raw[i, 0] = new Rgba32(BorderColour.R, BorderColour.G, BorderColour.B, Math.Min(progress / aa_portion, 1) * BorderColour.A);
- }
- else
- {
- progress -= border_portion;
- raw[i, 0] = new Rgba32(AccentColour.R, AccentColour.G, AccentColour.B,
- (opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * AccentColour.A);
- }
- }
-
- texture.SetData(new TextureUpload(raw));
- path.Texture = texture;
-
- container.ForceRedraw();
- }
-
private void computeSize()
{
// Generate the entire curve
@@ -226,5 +178,53 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
SetRange(start, end);
}
+
+ private class SliderPath : SmoothPath
+ {
+ private const float border_portion = 0.128f;
+ private const float gradient_portion = 1 - border_portion;
+
+ private const float opacity_at_centre = 0.3f;
+ private const float opacity_at_edge = 0.8f;
+
+ private Color4 borderColour = Color4.White;
+
+ public Color4 BorderColour
+ {
+ get => borderColour;
+ set
+ {
+ if (borderColour == value)
+ return;
+ borderColour = value;
+
+ InvalidateTexture();
+ }
+ }
+
+ private Color4 accentColour = Color4.White;
+
+ public Color4 AccentColour
+ {
+ get => accentColour;
+ set
+ {
+ if (accentColour == value)
+ return;
+ accentColour = value;
+
+ InvalidateTexture();
+ }
+ }
+
+ protected override Color4 ColourAt(float position)
+ {
+ if (position <= border_portion)
+ return BorderColour;
+
+ position -= border_portion;
+ return new Color4(AccentColour.R, AccentColour.G, AccentColour.B, (opacity_at_edge - (opacity_at_edge - opacity_at_centre) * position / gradient_portion) * AccentColour.A);
+ }
+ }
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs
index 7a0dcc77a6..80be192b77 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; }
@@ -92,8 +92,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;
@@ -108,7 +121,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.Rulesets.Osu/OsuInputManager.cs b/osu.Game.Rulesets.Osu/OsuInputManager.cs
index 0f7bc30888..e2f4c43a23 100644
--- a/osu.Game.Rulesets.Osu/OsuInputManager.cs
+++ b/osu.Game.Rulesets.Osu/OsuInputManager.cs
@@ -45,10 +45,10 @@ namespace osu.Game.Rulesets.Osu
public enum OsuAction
{
- [Description("Left Button")]
+ [Description("Left button")]
LeftButton,
- [Description("Right Button")]
+ [Description("Right button")]
RightButton
}
}
diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
index b0010ccbf6..398680cb8d 100644
--- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
+++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
@@ -23,29 +23,35 @@ namespace osu.Game.Rulesets.Osu.UI
public static readonly Vector2 BASE_SIZE = new Vector2(512, 384);
public OsuPlayfield()
- : base(BASE_SIZE.X)
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
- AddRange(new Drawable[]
+ Size = new Vector2(0.75f);
+
+ InternalChild = new PlayfieldAdjustmentContainer
{
- connectionLayer = new FollowPointRenderer
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
{
- RelativeSizeAxes = Axes.Both,
- Depth = 2,
- },
- judgementLayer = new JudgementContainer
- {
- RelativeSizeAxes = Axes.Both,
- Depth = 1,
- },
- approachCircles = new Container
- {
- RelativeSizeAxes = Axes.Both,
- Depth = -1,
- },
- });
+ connectionLayer = new FollowPointRenderer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Depth = 2,
+ },
+ judgementLayer = new JudgementContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Depth = 1,
+ },
+ HitObjectContainer,
+ approachCircles = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Depth = -1,
+ },
+ }
+ };
}
public override void Add(DrawableHitObject h)
diff --git a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs
index 4bc6992445..0ea8d3ede8 100644
--- a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs
+++ b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs
@@ -4,7 +4,6 @@
using System.Linq;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Input;
-using OpenTK;
using osu.Game.Beatmaps;
using osu.Game.Input.Handlers;
using osu.Game.Rulesets.Objects.Drawables;
@@ -58,12 +57,6 @@ namespace osu.Game.Rulesets.Osu.UI
}
}
- protected override Vector2 GetAspectAdjustedSize()
- {
- var aspectSize = DrawSize.X * 0.75f < DrawSize.Y ? new Vector2(DrawSize.X, DrawSize.X * 0.75f) : new Vector2(DrawSize.Y * 4f / 3f, DrawSize.Y);
- return new Vector2(aspectSize.X / DrawSize.X, aspectSize.Y / DrawSize.Y);
- }
-
protected override CursorContainer CreateCursor() => new GameplayCursor();
}
}
diff --git a/osu.Game.Rulesets.Osu/UI/PlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Osu/UI/PlayfieldAdjustmentContainer.cs
new file mode 100644
index 0000000000..00d5692fda
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/UI/PlayfieldAdjustmentContainer.cs
@@ -0,0 +1,42 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using OpenTK;
+
+namespace osu.Game.Rulesets.Osu.UI
+{
+ public class PlayfieldAdjustmentContainer : Container
+ {
+ protected override Container Content => content;
+ private readonly Container content;
+
+ public PlayfieldAdjustmentContainer()
+ {
+ InternalChild = new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ FillMode = FillMode.Fit,
+ FillAspectRatio = 4f / 3,
+ Child = content = new ScalingContainer { RelativeSizeAxes = Axes.Both }
+ };
+ }
+
+ ///
+ /// A which scales its content relative to a target width.
+ ///
+ private class ScalingContainer : Container
+ {
+ protected override void Update()
+ {
+ base.Update();
+
+ Scale = new Vector2(Parent.ChildSize.X / OsuPlayfield.BASE_SIZE.X);
+ Size = Vector2.Divide(Vector2.One, Scale);
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
index 08a0579561..6ae9a018c5 100644
--- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
+++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
@@ -2,9 +2,10 @@
-
-
+
+
+
WinExe
diff --git a/osu.Game.Rulesets.Taiko/TaikoInputManager.cs b/osu.Game.Rulesets.Taiko/TaikoInputManager.cs
index dc683ae2f5..3b430e7ad1 100644
--- a/osu.Game.Rulesets.Taiko/TaikoInputManager.cs
+++ b/osu.Game.Rulesets.Taiko/TaikoInputManager.cs
@@ -17,13 +17,13 @@ namespace osu.Game.Rulesets.Taiko
public enum TaikoAction
{
- [Description("Left (Rim)")]
+ [Description("Left (rim)")]
LeftRim,
- [Description("Left (Centre)")]
+ [Description("Left (centre)")]
LeftCentre,
- [Description("Right (Centre)")]
+ [Description("Right (centre)")]
RightCentre,
- [Description("Right (Rim)")]
+ [Description("Right (rim)")]
RightRim
}
}
diff --git a/osu.Game.Rulesets.Taiko/UI/PlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Taiko/UI/PlayfieldAdjustmentContainer.cs
new file mode 100644
index 0000000000..661a4e135c
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/UI/PlayfieldAdjustmentContainer.cs
@@ -0,0 +1,22 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics.Containers;
+using OpenTK;
+
+namespace osu.Game.Rulesets.Taiko.UI
+{
+ public class PlayfieldAdjustmentContainer : Container
+ {
+ private const float default_relative_height = TaikoPlayfield.DEFAULT_HEIGHT / 768;
+ private const float default_aspect = 16f / 9f;
+
+ protected override void Update()
+ {
+ base.Update();
+
+ float aspectAdjust = MathHelper.Clamp(Parent.ChildSize.X / Parent.ChildSize.Y, 0.4f, 4) / default_aspect;
+ Size = new Vector2(1, default_relative_height * aspectAdjust);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
index 1219ccc0e8..40ed659bd6 100644
--- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
+++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
@@ -47,9 +47,6 @@ namespace osu.Game.Rulesets.Taiko.UI
private readonly Container kiaiExplosionContainer;
private readonly JudgementContainer judgementContainer;
- protected override Container Content => content;
- private readonly Container content;
-
private readonly Container topLevelHitContainer;
private readonly Container barlineContainer;
@@ -64,140 +61,147 @@ namespace osu.Game.Rulesets.Taiko.UI
{
Direction.Value = ScrollingDirection.Left;
- AddRangeInternal(new Drawable[]
+ InternalChild = new PlayfieldAdjustmentContainer
{
- backgroundContainer = new Container
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
{
- Name = "Transparent playfield background",
- RelativeSizeAxes = Axes.Both,
- Masking = true,
- EdgeEffect = new EdgeEffectParameters
+ backgroundContainer = new Container
{
- Type = EdgeEffectType.Shadow,
- Colour = Color4.Black.Opacity(0.2f),
- Radius = 5,
- },
- Children = new Drawable[]
- {
- background = new Box
+ Name = "Transparent playfield background",
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ EdgeEffect = new EdgeEffectParameters
{
- RelativeSizeAxes = Axes.Both,
- Alpha = 0.6f
+ Type = EdgeEffectType.Shadow,
+ Colour = Color4.Black.Opacity(0.2f),
+ Radius = 5,
},
- }
- },
- new Container
- {
- Name = "Right area",
- RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding { Left = left_area_size },
- Children = new Drawable[]
- {
- new Container
+ Children = new Drawable[]
{
- Name = "Masked elements before hit objects",
- RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding { Left = HIT_TARGET_OFFSET },
- Masking = true,
- Children = new Drawable[]
+ background = new Box
{
- hitExplosionContainer = new Container
- {
- RelativeSizeAxes = Axes.Both,
- FillMode = FillMode.Fit,
- Blending = BlendingMode.Additive,
- },
- new HitTarget
- {
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.Centre,
- RelativeSizeAxes = Axes.Both,
- FillMode = FillMode.Fit
- }
- }
- },
- barlineContainer = new Container
- {
- RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding { Left = HIT_TARGET_OFFSET }
- },
- content = new Container
- {
- Name = "Hit objects",
- RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding { Left = HIT_TARGET_OFFSET },
- Masking = true
- },
- kiaiExplosionContainer = new Container
- {
- Name = "Kiai hit explosions",
- RelativeSizeAxes = Axes.Both,
- FillMode = FillMode.Fit,
- Margin = new MarginPadding { Left = HIT_TARGET_OFFSET },
- Blending = BlendingMode.Additive
- },
- judgementContainer = new JudgementContainer
- {
- Name = "Judgements",
- RelativeSizeAxes = Axes.Y,
- Margin = new MarginPadding { Left = HIT_TARGET_OFFSET },
- Blending = BlendingMode.Additive
- },
- }
- },
- overlayBackgroundContainer = new Container
- {
- Name = "Left overlay",
- RelativeSizeAxes = Axes.Y,
- Size = new Vector2(left_area_size, 1),
- Children = new Drawable[]
- {
- overlayBackground = new Box
- {
- RelativeSizeAxes = Axes.Both,
- },
- new InputDrum(controlPoints)
- {
- Anchor = Anchor.CentreRight,
- Origin = Anchor.CentreRight,
- Scale = new Vector2(0.9f),
- Margin = new MarginPadding { Right = 20 }
- },
- new Box
- {
- Anchor = Anchor.TopRight,
- RelativeSizeAxes = Axes.Y,
- Width = 10,
- Colour = Framework.Graphics.Colour.ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.6f), Color4.Black.Opacity(0)),
- },
- }
- },
- new Container
- {
- Name = "Border",
- RelativeSizeAxes = Axes.Both,
- Masking = true,
- MaskingSmoothness = 0,
- BorderThickness = 2,
- AlwaysPresent = true,
- Children = new[]
- {
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Alpha = 0,
- AlwaysPresent = true
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0.6f
+ },
}
+ },
+ new Container
+ {
+ Name = "Right area",
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Left = left_area_size },
+ Children = new Drawable[]
+ {
+ new Container
+ {
+ Name = "Masked elements before hit objects",
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Left = HIT_TARGET_OFFSET },
+ Masking = true,
+ Children = new Drawable[]
+ {
+ hitExplosionContainer = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ FillMode = FillMode.Fit,
+ Blending = BlendingMode.Additive,
+ },
+ new HitTarget
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ FillMode = FillMode.Fit
+ }
+ }
+ },
+ barlineContainer = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Left = HIT_TARGET_OFFSET }
+ },
+ new Container
+ {
+ Name = "Hit objects",
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Left = HIT_TARGET_OFFSET },
+ Masking = true,
+ Child = HitObjectContainer
+ },
+ kiaiExplosionContainer = new Container
+ {
+ Name = "Kiai hit explosions",
+ RelativeSizeAxes = Axes.Both,
+ FillMode = FillMode.Fit,
+ Margin = new MarginPadding { Left = HIT_TARGET_OFFSET },
+ Blending = BlendingMode.Additive
+ },
+ judgementContainer = new JudgementContainer
+ {
+ Name = "Judgements",
+ RelativeSizeAxes = Axes.Y,
+ Margin = new MarginPadding { Left = HIT_TARGET_OFFSET },
+ Blending = BlendingMode.Additive
+ },
+ }
+ },
+ overlayBackgroundContainer = new Container
+ {
+ Name = "Left overlay",
+ RelativeSizeAxes = Axes.Y,
+ Size = new Vector2(left_area_size, 1),
+ Children = new Drawable[]
+ {
+ overlayBackground = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ new InputDrum(controlPoints)
+ {
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.CentreRight,
+ Scale = new Vector2(0.9f),
+ Margin = new MarginPadding { Right = 20 }
+ },
+ new Box
+ {
+ Anchor = Anchor.TopRight,
+ RelativeSizeAxes = Axes.Y,
+ Width = 10,
+ Colour = Framework.Graphics.Colour.ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.6f), Color4.Black.Opacity(0)),
+ },
+ }
+ },
+ new Container
+ {
+ Name = "Border",
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ MaskingSmoothness = 0,
+ BorderThickness = 2,
+ AlwaysPresent = true,
+ Children = new[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0,
+ AlwaysPresent = true
+ }
+ }
+ },
+ topLevelHitContainer = new Container
+ {
+ Name = "Top level hit objects",
+ RelativeSizeAxes = Axes.Both,
}
- },
- topLevelHitContainer = new Container
- {
- Name = "Top level hit objects",
- RelativeSizeAxes = Axes.Both,
}
- });
+ };
- VisibleTimeRange.Value = 6000;
+ VisibleTimeRange.Value = 7000;
}
[BackgroundDependencyLoader]
diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs
index 229ab69ceb..3d08bffe0f 100644
--- a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs
+++ b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
-using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
@@ -13,7 +12,6 @@ using osu.Game.Rulesets.Taiko.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.Taiko.Replays;
-using OpenTK;
using System.Linq;
using osu.Framework.Input;
using osu.Game.Input.Handlers;
@@ -74,27 +72,11 @@ namespace osu.Game.Rulesets.Taiko.UI
}
}
- protected override Vector2 GetAspectAdjustedSize()
- {
- const float default_relative_height = TaikoPlayfield.DEFAULT_HEIGHT / 768;
- const float default_aspect = 16f / 9f;
-
- float aspectAdjust = MathHelper.Clamp(DrawWidth / DrawHeight, 0.4f, 4) / default_aspect;
-
- return new Vector2(1, default_relative_height * aspectAdjust);
- }
-
- protected override Vector2 PlayfieldArea => Vector2.One;
-
public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this);
public override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo);
- protected override Playfield CreatePlayfield() => new TaikoPlayfield(Beatmap.ControlPointInfo)
- {
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.CentreLeft
- };
+ protected override Playfield CreatePlayfield() => new TaikoPlayfield(Beatmap.ControlPointInfo);
protected override DrawableHitObject GetVisualRepresentation(TaikoHitObject h)
{
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
index d3351f86f8..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()
{
@@ -165,7 +186,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
}
[Test]
- public void TestDecodeBeatmapColors()
+ public void TestDecodeBeatmapColours()
{
var decoder = new LegacySkinDecoder();
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
@@ -181,6 +202,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
new Color4(128, 255, 128, 255),
new Color4(255, 187, 255, 255),
new Color4(255, 177, 140, 255),
+ new Color4(100, 100, 100, 100),
};
Assert.AreEqual(expectedColors.Length, comboColors.Count);
for (int i = 0; i < expectedColors.Length; i++)
diff --git a/osu.Game.Tests/Resources/Soleily - Renatus (Gamu) [Insane].osu b/osu.Game.Tests/Resources/Soleily - Renatus (Gamu) [Insane].osu
index 3e44dc0af8..67570ad21b 100644
--- a/osu.Game.Tests/Resources/Soleily - Renatus (Gamu) [Insane].osu
+++ b/osu.Game.Tests/Resources/Soleily - Renatus (Gamu) [Insane].osu
@@ -101,6 +101,7 @@ Combo3 : 128,255,255
Combo4 : 128,255,128
Combo5 : 255,187,255
Combo6 : 255,177,140
+Combo7 : 100,100,100,100
[HitObjects]
192,168,956,6,0,P|184:128|200:80,1,90,4|0,1:2|0:0,0:0:0:0:
diff --git a/osu.Game.Tests/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 5df371dd09..825aef75c9 100644
--- a/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs
+++ b/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs
@@ -44,14 +44,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 = 400,
- Velocity = 1,
- TickDistance = 100,
+ Distance = 216,
Scale = 0.5f,
}
},
diff --git a/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs b/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs
index bbc9d2b860..b254325472 100644
--- a/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs
+++ b/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs
@@ -121,14 +121,21 @@ namespace osu.Game.Tests.Visual
Direction = direction;
Padding = new MarginPadding(2);
- Content.Masking = true;
- AddInternal(new Box
+ InternalChildren = new Drawable[]
{
- RelativeSizeAxes = Axes.Both,
- Alpha = 0.5f,
- Depth = float.MaxValue
- });
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0.5f,
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ Child = HitObjectContainer
+ }
+ };
}
}
diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj
index d638af0c38..520e0b8940 100644
--- a/osu.Game.Tests/osu.Game.Tests.csproj
+++ b/osu.Game.Tests/osu.Game.Tests.csproj
@@ -2,9 +2,11 @@
-
-
+
+
+
+
WinExe
diff --git a/osu.Game.props b/osu.Game.props
index ec859e64a5..4bcac479a0 100644
--- a/osu.Game.props
+++ b/osu.Game.props
@@ -1,7 +1,7 @@
- 7
+ 7.2
..\app.manifest
diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs
index 9aabb434a3..4ef7b28d49 100644
--- a/osu.Game/Beatmaps/Beatmap.cs
+++ b/osu.Game/Beatmaps/Beatmap.cs
@@ -48,7 +48,7 @@ namespace osu.Game.Beatmaps
[JsonConverter(typeof(TypedListConverter))]
public List HitObjects = new List();
- IEnumerable IBeatmap.HitObjects => HitObjects;
+ IReadOnlyList IBeatmap.HitObjects => HitObjects;
public virtual IEnumerable GetStatistics() => Enumerable.Empty();
diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs
index a1bb70135a..3cb7833a12 100644
--- a/osu.Game/Beatmaps/BeatmapConverter.cs
+++ b/osu.Game/Beatmaps/BeatmapConverter.cs
@@ -55,39 +55,40 @@ namespace osu.Game.Beatmaps
beatmap.BeatmapInfo = original.BeatmapInfo;
beatmap.ControlPointInfo = original.ControlPointInfo;
- beatmap.HitObjects = original.HitObjects.SelectMany(h => convert(h, original)).ToList();
+ beatmap.HitObjects = convertHitObjects(original.HitObjects, original);
beatmap.Breaks = original.Breaks;
return beatmap;
}
- ///
- /// Converts a hit object.
- ///
- /// The hit object to convert.
- /// The un-converted Beatmap.
- /// The converted hit object.
- private IEnumerable convert(HitObject original, IBeatmap beatmap)
+ private List convertHitObjects(IReadOnlyList hitObjects, IBeatmap beatmap)
{
- // Check if the hitobject is already the converted type
- T tObject = original as T;
- if (tObject != null)
- {
- yield return tObject;
- yield break;
- }
+ var result = new List(hitObjects.Count);
- var converted = ConvertHitObject(original, beatmap).ToList();
- ObjectConverted?.Invoke(original, converted);
-
- // Convert the hit object
- foreach (var obj in converted)
+ foreach (var obj in hitObjects)
{
- if (obj == null)
+ if (obj is T tObj)
+ {
+ result.Add(tObj);
continue;
+ }
- yield return obj;
+ var converted = ConvertHitObject(obj, beatmap);
+
+ if (ObjectConverted != null)
+ {
+ converted = converted.ToList();
+ ObjectConverted.Invoke(obj, converted);
+ }
+
+ foreach (var c in converted)
+ {
+ if (c != null)
+ result.Add(c);
+ }
}
+
+ return result;
}
///
diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
index 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 aa653d88f9..fad94dcdfd 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/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs
index eb60133fed..81eddaa43a 100644
--- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs
+++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs
@@ -16,7 +16,7 @@ namespace osu.Game.Beatmaps.ControlPoints
///
/// The beat length at this control point.
///
- public double BeatLength
+ public virtual double BeatLength
{
get => beatLength;
set => beatLength = MathHelper.Clamp(value, 6, 60000);
diff --git a/osu.Game/Beatmaps/Drawables/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 181d17932d..71b7a65ccb 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -5,6 +5,7 @@ using System;
using System.Globalization;
using System.IO;
using System.Linq;
+using osu.Framework.IO.File;
using osu.Game.Beatmaps.Timing;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Beatmaps.ControlPoints;
@@ -58,7 +59,7 @@ namespace osu.Game.Beatmaps.Formats
hitObject.ApplyDefaults(this.beatmap.ControlPointInfo, this.beatmap.BeatmapInfo.BaseDifficulty);
}
- protected override bool ShouldSkipLine(string line) => base.ShouldSkipLine(line) || line.StartsWith(" ") || line.StartsWith("_");
+ protected override bool ShouldSkipLine(string line) => base.ShouldSkipLine(line) || line.StartsWith(" ", StringComparison.Ordinal) || line.StartsWith("_", StringComparison.Ordinal);
protected override void ParseLine(Beatmap beatmap, Section section, string line)
{
@@ -100,7 +101,7 @@ namespace osu.Game.Beatmaps.Formats
switch (pair.Key)
{
case @"AudioFilename":
- metadata.AudioFile = pair.Value;
+ metadata.AudioFile = FileSafety.PathStandardise(pair.Value);
break;
case @"AudioLeadIn":
beatmap.BeatmapInfo.AudioLeadIn = int.Parse(pair.Value);
@@ -256,7 +257,7 @@ namespace osu.Game.Beatmaps.Formats
{
case EventType.Background:
string filename = split[2].Trim('"');
- beatmap.BeatmapInfo.Metadata.BackgroundFile = filename;
+ beatmap.BeatmapInfo.Metadata.BackgroundFile = FileSafety.PathStandardise(filename);
break;
case EventType.Break:
var breakEvent = new BreakPeriod
@@ -318,12 +319,12 @@ namespace osu.Game.Beatmaps.Formats
if (timingChange)
{
- handleTimingControlPoint(new TimingControlPoint
- {
- Time = time,
- BeatLength = beatLength,
- TimeSignature = timeSignature
- });
+ var controlPoint = CreateTimingControlPoint();
+ controlPoint.Time = time;
+ controlPoint.BeatLength = beatLength;
+ controlPoint.TimeSignature = timeSignature;
+
+ handleTimingControlPoint(controlPoint);
}
handleDifficultyControlPoint(new DifficultyControlPoint
@@ -418,6 +419,8 @@ namespace osu.Game.Beatmaps.Formats
private double getOffsetTime(double time) => time + (ApplyOffsets ? offset : 0);
+ protected virtual TimingControlPoint CreateTimingControlPoint() => new TimingControlPoint();
+
[Flags]
internal enum EffectFlags
{
diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
index e9f37e583b..2ef7c5de30 100644
--- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
@@ -69,7 +69,7 @@ namespace osu.Game.Beatmaps.Formats
protected string StripComments(string line)
{
- var index = line.IndexOf("//", StringComparison.Ordinal);
+ var index = line.AsSpan().IndexOf("//".AsSpan());
if (index > 0)
return line.Substring(0, index);
return line;
@@ -85,13 +85,19 @@ namespace osu.Game.Beatmaps.Formats
string[] split = pair.Value.Split(',');
- if (split.Length != 3)
- throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {pair.Value}");
+ if (split.Length != 3 && split.Length != 4)
+ throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B or R,G,B,A): {pair.Value}");
- if (!byte.TryParse(split[0], out var r) || !byte.TryParse(split[1], out var g) || !byte.TryParse(split[2], out var b))
+ Color4 colour;
+
+ try
+ {
+ colour = new Color4(byte.Parse(split[0]), byte.Parse(split[1]), byte.Parse(split[2]), split.Length == 4 ? byte.Parse(split[3]) : (byte)255);
+ }
+ catch (Exception e)
+ {
throw new InvalidOperationException(@"Color must be specified with 8-bit integer components");
-
- Color4 colour = new Color4(r, g, b, 255);
+ }
if (isCombo)
{
diff --git a/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs
new file mode 100644
index 0000000000..13a71aac3d
--- /dev/null
+++ b/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs
@@ -0,0 +1,37 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.Linq;
+using osu.Game.Beatmaps.ControlPoints;
+
+namespace osu.Game.Beatmaps.Formats
+{
+ ///
+ /// A built for difficulty calculation of legacy s
+ ///
+ /// To use this, the decoder must be registered by the application through .
+ /// Doing so will override any existing decoders.
+ ///
+ ///
+ public class LegacyDifficultyCalculatorBeatmapDecoder : LegacyBeatmapDecoder
+ {
+ public LegacyDifficultyCalculatorBeatmapDecoder(int version = LATEST_VERSION)
+ : base(version)
+ {
+ ApplyOffsets = false;
+ }
+
+ public new static void Register()
+ {
+ AddDecoder(@"osu file format v", m => new LegacyDifficultyCalculatorBeatmapDecoder(int.Parse(m.Split('v').Last())));
+ }
+
+ protected override TimingControlPoint CreateTimingControlPoint()
+ => new LegacyDifficultyCalculatorControlPoint();
+
+ private class LegacyDifficultyCalculatorControlPoint : TimingControlPoint
+ {
+ public override double BeatLength { get; set; } = 1000;
+ }
+ }
+}
diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
index 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/LineGraph.cs b/osu.Game/Graphics/UserInterface/LineGraph.cs
index 3cb2446acc..ff2c4cf7cd 100644
--- a/osu.Game/Graphics/UserInterface/LineGraph.cs
+++ b/osu.Game/Graphics/UserInterface/LineGraph.cs
@@ -69,7 +69,7 @@ namespace osu.Game.Graphics.UserInterface
{
Masking = true,
RelativeSizeAxes = Axes.Both,
- Child = path = new Path { RelativeSizeAxes = Axes.Both, PathWidth = 1 }
+ Child = path = new SmoothPath { RelativeSizeAxes = Axes.Both, PathWidth = 1 }
});
}
diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs
index 26caf09b1b..30803d1545 100644
--- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs
+++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs
@@ -30,7 +30,7 @@ namespace osu.Game.Graphics.UserInterface
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
- if (accentColour == default(Color4))
+ if (accentColour == default)
accentColour = colours.PinkDarker;
updateAccentColour();
}
@@ -51,6 +51,8 @@ namespace osu.Game.Graphics.UserInterface
#region OsuDropdownMenu
protected class OsuDropdownMenu : DropdownMenu, IHasAccentColour
{
+ public override bool HandleNonPositionalInput => State == MenuState.Open;
+
// todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring
public OsuDropdownMenu()
{
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/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs
index b21deff509..2f5f1aea3f 100644
--- a/osu.Game/Input/Bindings/GlobalActionContainer.cs
+++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs
@@ -71,17 +71,17 @@ namespace osu.Game.Input.Bindings
ToggleSettings,
[Description("Toggle osu!direct")]
ToggleDirect,
- [Description("Increase Volume")]
+ [Description("Increase volume")]
IncreaseVolume,
- [Description("Decrease Volume")]
+ [Description("Decrease volume")]
DecreaseVolume,
[Description("Toggle mute")]
ToggleMute,
// In-Game Keybindings
- [Description("Skip Cutscene")]
+ [Description("Skip cutscene")]
SkipCutscene,
- [Description("Quick Retry (Hold)")]
+ [Description("Quick retry (hold)")]
QuickRetry,
[Description("Take screenshot")]
diff --git a/osu.Game/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/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs
index eeadac8e8c..ff2ff9af14 100644
--- a/osu.Game/Overlays/ChatOverlay.cs
+++ b/osu.Game/Overlays/ChatOverlay.cs
@@ -291,7 +291,7 @@ namespace osu.Game.Overlays
messageRequest?.Cancel();
ListChannelsRequest req = new ListChannelsRequest();
- req.Success += delegate (List channels)
+ req.Success += delegate(List channels)
{
AvailableChannels = channels;
@@ -323,10 +323,7 @@ namespace osu.Game.Overlays
protected Channel CurrentChannel
{
- get
- {
- return currentChannel;
- }
+ get { return currentChannel; }
set
{
@@ -445,13 +442,7 @@ namespace osu.Game.Overlays
if (updates?.Presence != null)
{
foreach (var channel in updates.Presence)
- {
- if (careChannels.Find(c => c.Id == channel.Id) == null)
- {
- channel.Joined.Value = true;
- addChannel(channel);
- }
- }
+ addChannel(AvailableChannels.Find(c => c.Id == channel.Id));
foreach (var group in updates.Messages.GroupBy(m => m.ChannelId))
careChannels.Find(c => c.Id == group.Key)?.AddNewMessages(group.ToArray());
@@ -462,10 +453,7 @@ namespace osu.Game.Overlays
fetchReq = null;
};
- fetchReq.Failure += delegate
- {
- fetchReq = null;
- };
+ fetchReq.Failure += delegate { fetchReq = null; };
api.Queue(fetchReq);
}
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/MusicController.cs b/osu.Game/Overlays/MusicController.cs
index e3dc504e4d..b32fd265cb 100644
--- a/osu.Game/Overlays/MusicController.cs
+++ b/osu.Game/Overlays/MusicController.cs
@@ -196,10 +196,16 @@ namespace osu.Game.Overlays
playlist.StateChanged += s => playlistButton.FadeColour(s == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint);
}
+ private ScheduledDelegate seekDelegate;
+
private void attemptSeek(double progress)
{
- if (!beatmap.Disabled)
- current?.Track.Seek(progress);
+ seekDelegate?.Cancel();
+ seekDelegate = Schedule(() =>
+ {
+ if (!beatmap.Disabled)
+ current?.Track.Seek(progress);
+ });
}
private void playlistOrderChanged(BeatmapSetInfo beatmapSetInfo, int index)
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/Overlays/Settings/Sections/Audio/OffsetSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs
index 0fe41327db..da96c6ef30 100644
--- a/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
{
new SettingsSlider
{
- LabelText = "Audio Offset",
+ LabelText = "Audio offset",
Bindable = config.GetBindable(OsuSetting.AudioOffset),
KeyboardStep = 1f
},
diff --git a/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs
index 369c751448..fa4a714ba3 100644
--- a/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Audio/VolumeSettings.cs
@@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
Children = new Drawable[]
{
new SettingsSlider { LabelText = "Master", Bindable = audio.Volume, KeyboardStep = 0.01f },
- new SettingsSlider { LabelText = "Master (Window Inactive)", Bindable = config.GetBindable(OsuSetting.VolumeInactive), KeyboardStep = 0.01f },
+ new SettingsSlider { LabelText = "Master (window inactive)", Bindable = config.GetBindable(OsuSetting.VolumeInactive), KeyboardStep = 0.01f },
new SettingsSlider { LabelText = "Effect", Bindable = audio.VolumeSample, KeyboardStep = 0.01f },
new SettingsSlider { LabelText = "Music", Bindable = audio.VolumeTrack, KeyboardStep = 0.01f },
};
diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs
index 456d1c9a2f..51b0296ca4 100644
--- a/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Input/KeyboardSettings.cs
@@ -15,7 +15,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
{
new SettingsButton
{
- Text = "Key Configuration",
+ Text = "Key configuration",
Action = keyConfig.ToggleVisibility
},
};
diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
index 2b643b6805..7b9b7bf57f 100644
--- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs
@@ -26,12 +26,12 @@ namespace osu.Game.Overlays.Settings.Sections.Input
{
new SettingsCheckbox
{
- LabelText = "Raw Input",
+ LabelText = "Raw input",
Bindable = rawInputToggle
},
sensitivity = new SensitivitySetting
{
- LabelText = "Cursor Sensitivity",
+ LabelText = "Cursor sensitivity",
Bindable = config.GetBindable(FrameworkSetting.CursorSensitivity)
},
new SettingsCheckbox
diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs
index 7470dd0cd5..fa35e53531 100644
--- a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs
+++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs
@@ -105,6 +105,8 @@ namespace osu.Game.Overlays.Toolbar
public override bool HandleNonPositionalInput => !ruleset.Disabled && base.HandleNonPositionalInput;
public override bool HandlePositionalInput => !ruleset.Disabled && base.HandlePositionalInput;
+ public override bool PropagatePositionalInputSubTree => !ruleset.Disabled && base.PropagatePositionalInputSubTree;
+
private void disabledChanged(bool isDisabled) => this.FadeColour(isDisabled ? Color4.Gray : Color4.White, 300);
protected override void Update()
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index a3253250f2..8060ac742a 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -165,6 +165,6 @@ namespace osu.Game.Rulesets.Edit
///
/// Creates a which provides a layer above or below the .
///
- protected virtual ScalableContainer CreateLayerContainer() => new ScalableContainer { RelativeSizeAxes = Axes.Both };
+ protected virtual Container CreateLayerContainer() => new Container { RelativeSizeAxes = Axes.Both };
}
}
diff --git a/osu.Game/Rulesets/Mods/IApplicableToDrawableHitObject.cs b/osu.Game/Rulesets/Mods/IApplicableToDrawableHitObject.cs
index d871cdd322..fca04ca513 100644
--- a/osu.Game/Rulesets/Mods/IApplicableToDrawableHitObject.cs
+++ b/osu.Game/Rulesets/Mods/IApplicableToDrawableHitObject.cs
@@ -13,6 +13,7 @@ namespace osu.Game.Rulesets.Mods
{
///
/// Applies this to a list of s.
+ /// This will only be invoked with top-level s. Access if adjusting nested objects is necessary.
///
/// The list of s to apply to.
void ApplyToDrawableHitObjects(IEnumerable drawables);
diff --git a/osu.Game/Rulesets/Objects/BezierApproximator.cs b/osu.Game/Rulesets/Objects/BezierApproximator.cs
index 6094934510..a1803e32f7 100644
--- a/osu.Game/Rulesets/Objects/BezierApproximator.cs
+++ b/osu.Game/Rulesets/Objects/BezierApproximator.cs
@@ -1,25 +1,26 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
using System.Collections.Generic;
using OpenTK;
namespace osu.Game.Rulesets.Objects
{
- public class BezierApproximator
+ public readonly ref struct BezierApproximator
{
private readonly int count;
- private readonly List controlPoints;
+ private readonly ReadOnlySpan controlPoints;
private readonly Vector2[] subdivisionBuffer1;
private readonly Vector2[] subdivisionBuffer2;
private const float tolerance = 0.25f;
private const float tolerance_sq = tolerance * tolerance;
- public BezierApproximator(List controlPoints)
+ public BezierApproximator(ReadOnlySpan controlPoints)
{
this.controlPoints = controlPoints;
- count = controlPoints.Count;
+ count = controlPoints.Length;
subdivisionBuffer1 = new Vector2[count];
subdivisionBuffer2 = new Vector2[count * 2 - 1];
diff --git a/osu.Game/Rulesets/Objects/CatmullApproximator.cs b/osu.Game/Rulesets/Objects/CatmullApproximator.cs
index fabb55e8f6..78f8e471f3 100644
--- a/osu.Game/Rulesets/Objects/CatmullApproximator.cs
+++ b/osu.Game/Rulesets/Objects/CatmullApproximator.cs
@@ -1,40 +1,40 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
using System.Collections.Generic;
using OpenTK;
namespace osu.Game.Rulesets.Objects
{
- public class CatmullApproximator
+ public readonly ref struct CatmullApproximator
{
///
/// The amount of pieces to calculate for each controlpoint quadruplet.
///
private const int detail = 50;
- private readonly List controlPoints;
+ private readonly ReadOnlySpan controlPoints;
- public CatmullApproximator(List controlPoints)
+ public CatmullApproximator(ReadOnlySpan controlPoints)
{
this.controlPoints = controlPoints;
}
-
///
/// Creates a piecewise-linear approximation of a Catmull-Rom spline.
///
/// A list of vectors representing the piecewise-linear approximation.
public List CreateCatmull()
{
- var result = new List();
+ var result = new List((controlPoints.Length - 1) * detail * 2);
- for (int i = 0; i < controlPoints.Count - 1; i++)
+ for (int i = 0; i < controlPoints.Length - 1; i++)
{
var v1 = i > 0 ? controlPoints[i - 1] : controlPoints[i];
var v2 = controlPoints[i];
- var v3 = i < controlPoints.Count - 1 ? controlPoints[i + 1] : v2 + v2 - v1;
- var v4 = i < controlPoints.Count - 2 ? controlPoints[i + 2] : v3 + v3 - v2;
+ var v3 = i < controlPoints.Length - 1 ? controlPoints[i + 1] : v2 + v2 - v1;
+ var v4 = i < controlPoints.Length - 2 ? controlPoints[i + 2] : v3 + v3 - v2;
for (int c = 0; c < detail; c++)
{
diff --git a/osu.Game/Rulesets/Objects/CircularArcApproximator.cs b/osu.Game/Rulesets/Objects/CircularArcApproximator.cs
index 7fb8d8a40e..28d7442aaf 100644
--- a/osu.Game/Rulesets/Objects/CircularArcApproximator.cs
+++ b/osu.Game/Rulesets/Objects/CircularArcApproximator.cs
@@ -8,21 +8,15 @@ using OpenTK;
namespace osu.Game.Rulesets.Objects
{
- public class CircularArcApproximator
+ public readonly ref struct CircularArcApproximator
{
- private readonly Vector2 a;
- private readonly Vector2 b;
- private readonly Vector2 c;
-
- private int amountPoints;
-
private const float tolerance = 0.1f;
- public CircularArcApproximator(Vector2 a, Vector2 b, Vector2 c)
+ private readonly ReadOnlySpan controlPoints;
+
+ public CircularArcApproximator(ReadOnlySpan controlPoints)
{
- this.a = a;
- this.b = b;
- this.c = c;
+ this.controlPoints = controlPoints;
}
///
@@ -31,6 +25,10 @@ namespace osu.Game.Rulesets.Objects
/// A list of vectors representing the piecewise-linear approximation.
public List CreateArc()
{
+ Vector2 a = controlPoints[0];
+ Vector2 b = controlPoints[1];
+ Vector2 c = controlPoints[2];
+
float aSq = (b - c).LengthSquared;
float bSq = (a - c).LengthSquared;
float cSq = (a - b).LengthSquared;
@@ -81,7 +79,7 @@ namespace osu.Game.Rulesets.Objects
// is: 2 * Math.Acos(1 - TOLERANCE / r)
// The special case is required for extremely short sliders where the radius is smaller than
// the tolerance. This is a pathological rather than a realistic case.
- amountPoints = 2 * r <= tolerance ? 2 : Math.Max(2, (int)Math.Ceiling(thetaRange / (2 * Math.Acos(1 - tolerance / r))));
+ int amountPoints = 2 * r <= tolerance ? 2 : Math.Max(2, (int)Math.Ceiling(thetaRange / (2 * Math.Acos(1 - tolerance / r))));
List output = new List(amountPoints);
diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
index 8489f0b19e..bcf84b375f 100644
--- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
+++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
@@ -145,7 +145,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
public event Action ApplyCustomUpdateState;
///
- /// Plays all the hitsounds for this .
+ /// Plays all the hit sounds for this .
///
public void PlaySamples() => Samples?.Play();
diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs
index beb9620f78..f5613e927f 100644
--- a/osu.Game/Rulesets/Objects/HitObject.cs
+++ b/osu.Game/Rulesets/Objects/HitObject.cs
@@ -1,10 +1,8 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using System;
using System.Collections.Generic;
using Newtonsoft.Json;
-using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Lists;
using osu.Game.Audio;
using osu.Game.Beatmaps;
@@ -58,10 +56,10 @@ namespace osu.Game.Rulesets.Objects
///
public HitWindows HitWindows { get; set; }
- private readonly Lazy> nestedHitObjects = new Lazy>(() => new SortedList((h1, h2) => h1.StartTime.CompareTo(h2.StartTime)));
+ private readonly SortedList nestedHitObjects = new SortedList(compareObjects);
[JsonIgnore]
- public IReadOnlyList NestedHitObjects => nestedHitObjects.Value;
+ public IReadOnlyList NestedHitObjects => nestedHitObjects;
///
/// Applies default values to this HitObject.
@@ -72,18 +70,14 @@ namespace osu.Game.Rulesets.Objects
{
ApplyDefaultsToSelf(controlPointInfo, difficulty);
- if (nestedHitObjects.IsValueCreated)
- nestedHitObjects.Value.Clear();
+ nestedHitObjects.Clear();
CreateNestedHitObjects();
- if (nestedHitObjects.IsValueCreated)
+ foreach (var h in nestedHitObjects)
{
- nestedHitObjects.Value.ForEach(h =>
- {
- h.HitWindows = HitWindows;
- h.ApplyDefaults(controlPointInfo, difficulty);
- });
+ h.HitWindows = HitWindows;
+ h.ApplyDefaults(controlPointInfo, difficulty);
}
}
@@ -104,7 +98,7 @@ namespace osu.Game.Rulesets.Objects
{
}
- protected void AddNested(HitObject hitObject) => nestedHitObjects.Value.Add(hitObject);
+ protected void AddNested(HitObject hitObject) => nestedHitObjects.Add(hitObject);
///
/// Creates the that represents the scoring information for this .
@@ -120,5 +114,7 @@ namespace osu.Game.Rulesets.Objects
///
///
protected virtual HitWindows CreateHitWindows() => new HitWindows();
+
+ private static int compareObjects(HitObject first, HitObject second) => first.StartTime.CompareTo(second.StartTime);
}
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs
index 802080aedb..9c9fc2e742 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs
@@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
};
}
- protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples)
+ protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples)
{
newCombo |= forceNewCombo;
comboOffset += extraComboOffset;
diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
index 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/Rulesets/Timing/MultiplierControlPoint.cs b/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs
index a5bd6bfde7..4988bac5ce 100644
--- a/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs
+++ b/osu.Game/Rulesets/Timing/MultiplierControlPoint.cs
@@ -18,9 +18,14 @@ namespace osu.Game.Rulesets.Timing
public double StartTime;
///
- /// The multiplier which this provides.
+ /// The aggregate multiplier which this provides.
///
- public double Multiplier => 1000 / TimingPoint.BeatLength * DifficultyPoint.SpeedMultiplier;
+ public double Multiplier => Velocity * DifficultyPoint.SpeedMultiplier * 1000 / TimingPoint.BeatLength;
+
+ ///
+ /// The velocity multiplier.
+ ///
+ public double Velocity = 1;
///
/// The that provides the timing information for this .
@@ -48,18 +53,6 @@ namespace osu.Game.Rulesets.Timing
StartTime = startTime;
}
- ///
- /// Creates a by copying another .
- ///
- /// The start time of this .
- /// The to copy.
- public MultiplierControlPoint(double startTime, MultiplierControlPoint other)
- : this(startTime)
- {
- TimingPoint = other.TimingPoint;
- DifficultyPoint = other.DifficultyPoint;
- }
-
// ReSharper disable once ImpureMethodCallOnReadonlyValueField
public int CompareTo(MultiplierControlPoint other) => StartTime.CompareTo(other?.StartTime);
}
diff --git a/osu.Game/Rulesets/UI/HitObjectContainer.cs b/osu.Game/Rulesets/UI/HitObjectContainer.cs
index af18d98561..261132c56b 100644
--- a/osu.Game/Rulesets/UI/HitObjectContainer.cs
+++ b/osu.Game/Rulesets/UI/HitObjectContainer.cs
@@ -14,6 +14,11 @@ namespace osu.Game.Rulesets.UI
public IEnumerable Objects => InternalChildren.Cast().OrderBy(h => h.HitObject.StartTime);
public IEnumerable AliveObjects => AliveInternalChildren.Cast().OrderBy(h => h.HitObject.StartTime);
+ public HitObjectContainer()
+ {
+ RelativeSizeAxes = Axes.Both;
+ }
+
public virtual void Add(DrawableHitObject hitObject) => AddInternal(hitObject);
public virtual bool Remove(DrawableHitObject hitObject) => RemoveInternal(hitObject);
diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs
index e090a18eda..886eb3ac0e 100644
--- a/osu.Game/Rulesets/UI/Playfield.cs
+++ b/osu.Game/Rulesets/UI/Playfield.cs
@@ -9,17 +9,26 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Configuration;
+using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
+using OpenTK;
namespace osu.Game.Rulesets.UI
{
- public abstract class Playfield : ScalableContainer
+ public abstract class Playfield : CompositeDrawable
{
///
/// The contained in this Playfield.
///
- public HitObjectContainer HitObjectContainer { get; private set; }
+ public HitObjectContainer HitObjectContainer => hitObjectContainerLazy.Value;
+
+ private readonly Lazy hitObjectContainerLazy;
+
+ ///
+ /// A function that converts gamefield coordinates to screen space.
+ ///
+ public Func GamefieldToScreenSpace => HitObjectContainer.ToScreenSpace;
///
/// All the s contained in this and all .
@@ -39,18 +48,13 @@ namespace osu.Game.Rulesets.UI
public readonly BindableBool DisplayJudgements = new BindableBool(true);
///
- /// A container for keeping track of DrawableHitObjects.
+ /// Creates a new .
///
- /// The width to scale the internal coordinate space to.
- /// May be null if scaling based on is desired. If is also null, no scaling will occur.
- ///
- /// The height to scale the internal coordinate space to.
- /// May be null if scaling based on is desired. If is also null, no scaling will occur.
- ///
- protected Playfield(float? customWidth = null, float? customHeight = null)
- : base(customWidth, customHeight)
+ protected Playfield()
{
RelativeSizeAxes = Axes.Both;
+
+ hitObjectContainerLazy = new Lazy(CreateHitObjectContainer);
}
private WorkingBeatmap beatmap;
@@ -59,11 +63,6 @@ namespace osu.Game.Rulesets.UI
private void load(IBindableBeatmap beatmap)
{
this.beatmap = beatmap.Value;
-
- HitObjectContainer = CreateHitObjectContainer();
- HitObjectContainer.RelativeSizeAxes = Axes.Both;
-
- Add(HitObjectContainer);
}
///
@@ -94,10 +93,14 @@ namespace osu.Game.Rulesets.UI
nestedPlayfields.Value.Add(otherPlayfield);
}
- ///
- /// Creates the container that will be used to contain the s.
- ///
- protected virtual HitObjectContainer CreateHitObjectContainer() => new HitObjectContainer();
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ // in the case a consumer forgets to add the HitObjectContainer, we will add it here.
+ if (HitObjectContainer.Parent == null)
+ AddInternal(HitObjectContainer);
+ }
protected override void Update()
{
@@ -108,5 +111,10 @@ namespace osu.Game.Rulesets.UI
if (mod is IUpdatableByPlayfield updatable)
updatable.Update(this);
}
+
+ ///
+ /// Creates the container that will be used to contain the s.
+ ///
+ protected virtual HitObjectContainer CreateHitObjectContainer() => new HitObjectContainer();
}
}
diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs
index a830803fb1..a23a5a78f7 100644
--- a/osu.Game/Rulesets/UI/RulesetContainer.cs
+++ b/osu.Game/Rulesets/UI/RulesetContainer.cs
@@ -22,7 +22,6 @@ using osu.Game.Overlays;
using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
-using OpenTK;
namespace osu.Game.Rulesets.UI
{
@@ -309,26 +308,6 @@ namespace osu.Game.Rulesets.UI
mod.ApplyToDrawableHitObjects(Playfield.HitObjectContainer.Objects);
}
- protected override void Update()
- {
- base.Update();
-
- Playfield.Size = GetAspectAdjustedSize() * PlayfieldArea;
- }
-
- ///
- /// Computes the size of the in relative coordinate space after aspect adjustments.
- ///
- /// The aspect-adjusted size.
- protected virtual Vector2 GetAspectAdjustedSize() => Vector2.One;
-
- ///
- /// The area of this that is available for the to use.
- /// Must be specified in relative coordinate space to this .
- /// This affects the final size of the but does not affect the 's scale.
- ///
- protected virtual Vector2 PlayfieldArea => new Vector2(0.75f); // A sane default
-
///
/// Creates a DrawableHitObject from a HitObject.
///
diff --git a/osu.Game/Rulesets/UI/ScalableContainer.cs b/osu.Game/Rulesets/UI/ScalableContainer.cs
deleted file mode 100644
index 5ee03be7ee..0000000000
--- a/osu.Game/Rulesets/UI/ScalableContainer.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using System;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Containers;
-using OpenTK;
-
-namespace osu.Game.Rulesets.UI
-{
- ///
- /// A which can have its internal coordinate system scaled to a specific size.
- ///
- public class ScalableContainer : Container
- {
- ///
- /// A function that converts coordinates from gamefield to screen space.
- ///
- public Func GamefieldToScreenSpace => scaledContent.GamefieldToScreenSpace;
-
- ///
- /// The scaled content.
- ///
- private readonly ScaledContainer scaledContent;
- protected override Container Content => scaledContent;
-
- ///
- /// A which can have its internal coordinate system scaled to a specific size.
- ///
- /// The width to scale the internal coordinate space to.
- /// May be null if scaling based on is desired. If is also null, no scaling will occur.
- ///
- /// The height to scale the internal coordinate space to.
- /// May be null if scaling based on is desired. If is also null, no scaling will occur.
- ///
- public ScalableContainer(float? customWidth = null, float? customHeight = null)
- {
- AddInternal(scaledContent = new ScaledContainer
- {
- CustomWidth = customWidth,
- CustomHeight = customHeight,
- RelativeSizeAxes = Axes.Both,
- });
- }
-
- private class ScaledContainer : Container
- {
- ///
- /// A function that converts coordinates from gamefield to screen space.
- ///
- public Func GamefieldToScreenSpace => content.ToScreenSpace;
-
- ///
- /// The value to scale the width of the content to match.
- /// If null, is used.
- ///
- public float? CustomWidth;
-
- ///
- /// The value to scale the height of the content to match.
- /// if null, is used.
- ///
- public float? CustomHeight;
-
- private readonly Container content;
- protected override Container Content => content;
-
- public ScaledContainer()
- {
- AddInternal(content = new Container { RelativeSizeAxes = Axes.Both });
- }
-
- protected override void Update()
- {
- base.Update();
-
- content.Scale = sizeScale;
- content.Size = Vector2.Divide(Vector2.One, sizeScale);
- }
-
- ///
- /// The scale that is required for the size of the content to match and .
- ///
- private Vector2 sizeScale
- {
- get
- {
- if (CustomWidth.HasValue && CustomHeight.HasValue)
- return Vector2.Divide(DrawSize, new Vector2(CustomWidth.Value, CustomHeight.Value));
- if (CustomWidth.HasValue)
- return new Vector2(DrawSize.X / CustomWidth.Value);
- if (CustomHeight.HasValue)
- return new Vector2(DrawSize.Y / CustomHeight.Value);
- return Vector2.One;
- }
- }
- }
- }
-}
diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingDirection.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingDirection.cs
index 4fe727cb84..d0db8f8835 100644
--- a/osu.Game/Rulesets/UI/Scrolling/ScrollingDirection.cs
+++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingDirection.cs
@@ -6,19 +6,19 @@ namespace osu.Game.Rulesets.UI.Scrolling
public enum ScrollingDirection
{
///
- /// Hitobjects will scroll vertically from the bottom of the hitobject container.
+ /// Hit objects will scroll vertically from the bottom of the hitobject container.
///
Up,
///
- /// Hitobjects will scroll vertically from the top of the hitobject container.
+ /// Hit objects will scroll vertically from the top of the hitobject container.
///
Down,
///
- /// Hitobjects will scroll horizontally from the right of the hitobject container.
+ /// Hit objects will scroll horizontally from the right of the hitobject container.
///
Left,
///
- /// Hitobjects will scroll horizontally from the left of the hitobject container.
+ /// Hit objects will scroll horizontally from the left of the hitobject container.
///
Right
}
diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs
index b85531909c..a1fc13ce4d 100644
--- a/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs
+++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingPlayfield.cs
@@ -65,20 +65,6 @@ namespace osu.Game.Rulesets.UI.Scrolling
protected virtual SpeedChangeVisualisationMethod VisualisationMethod => SpeedChangeVisualisationMethod.Sequential;
- ///
- /// Creates a new .
- ///
- /// The width to scale the internal coordinate space to.
- /// May be null if scaling based on is desired. If is also null, no scaling will occur.
- ///
- /// The height to scale the internal coordinate space to.
- /// May be null if scaling based on is desired. If is also null, no scaling will occur.
- ///
- protected ScrollingPlayfield(float? customWidth = null, float? customHeight = null)
- : base(customWidth, customHeight)
- {
- }
-
[BackgroundDependencyLoader]
private void load()
{
diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs
index 3fc67e4e34..41cdd6c06f 100644
--- a/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs
+++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingRulesetContainer.cs
@@ -60,6 +60,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
return new MultiplierControlPoint(c.Time)
{
+ Velocity = Beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier,
TimingPoint = lastTimingPoint,
DifficultyPoint = lastDifficultyPoint
};
@@ -78,7 +79,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
// If we have no control points, add a default one
if (DefaultControlPoints.Count == 0)
- DefaultControlPoints.Add(new MultiplierControlPoint());
+ DefaultControlPoints.Add(new MultiplierControlPoint { Velocity = Beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier });
DefaultControlPoints.ForEach(c => applySpeedAdjustment(c, Playfield));
}
@@ -88,22 +89,5 @@ namespace osu.Game.Rulesets.UI.Scrolling
playfield.HitObjects.AddControlPoint(controlPoint);
playfield.NestedPlayfields?.OfType().ForEach(p => applySpeedAdjustment(controlPoint, p));
}
-
- ///
- /// Generates a with the default timing change/difficulty change from the beatmap at a time.
- ///
- /// The time to create the control point at.
- /// The default at .
- public MultiplierControlPoint CreateControlPointAt(double time)
- {
- if (DefaultControlPoints.Count == 0)
- return new MultiplierControlPoint(time);
-
- int index = DefaultControlPoints.BinarySearch(new MultiplierControlPoint(time));
- if (index < 0)
- return new MultiplierControlPoint(time);
-
- return new MultiplierControlPoint(time, DefaultControlPoints[index]);
- }
}
}
diff --git a/osu.Game/Screens/Edit/Components/PlaybackControl.cs b/osu.Game/Screens/Edit/Components/PlaybackControl.cs
index c5b6251216..3c8288f04a 100644
--- a/osu.Game/Screens/Edit/Components/PlaybackControl.cs
+++ b/osu.Game/Screens/Edit/Components/PlaybackControl.cs
@@ -44,7 +44,7 @@ namespace osu.Game.Screens.Edit.Components
new OsuSpriteText
{
Origin = Anchor.BottomLeft,
- Text = "Playback Speed",
+ Text = "Playback speed",
RelativePositionAxes = Axes.Y,
Y = 0.5f,
Padding = new MarginPadding { Left = 45 }
diff --git a/osu.Game/Screens/Edit/Screens/Compose/Timeline/TimelineArea.cs b/osu.Game/Screens/Edit/Screens/Compose/Timeline/TimelineArea.cs
index 006317e57e..ecf760be8e 100644
--- a/osu.Game/Screens/Edit/Screens/Compose/Timeline/TimelineArea.cs
+++ b/osu.Game/Screens/Edit/Screens/Compose/Timeline/TimelineArea.cs
@@ -59,8 +59,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline
Spacing = new Vector2(0, 4),
Children = new[]
{
- hitObjectsCheckbox = new OsuCheckbox { LabelText = "Hitobjects" },
- hitSoundsCheckbox = new OsuCheckbox { LabelText = "Hitsounds" },
+ hitObjectsCheckbox = new OsuCheckbox { LabelText = "Hit objects" },
+ hitSoundsCheckbox = new OsuCheckbox { LabelText = "Hit sounds" },
waveformCheckbox = new OsuCheckbox { LabelText = "Waveform" }
}
}
diff --git a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs
index 439e344020..f762597e81 100644
--- a/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs
+++ b/osu.Game/Screens/Play/PlayerSettings/VisualSettings.cs
@@ -38,7 +38,7 @@ namespace osu.Game.Screens.Play.PlayerSettings
},
showStoryboardToggle = new PlayerCheckbox { LabelText = "Storyboards" },
beatmapSkinsToggle = new PlayerCheckbox { LabelText = "Beatmap skins" },
- beatmapHitsoundsToggle = new PlayerCheckbox { LabelText = "Beatmap hitsounds" }
+ beatmapHitsoundsToggle = new PlayerCheckbox { LabelText = "Beatmap hit sounds" }
};
}
diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs
index f1bd2b945f..3999adfede 100644
--- a/osu.Game/Screens/Select/BeatmapDetails.cs
+++ b/osu.Game/Screens/Select/BeatmapDetails.cs
@@ -307,10 +307,10 @@ namespace osu.Game.Screens.Select
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
- Alpha = 0;
InternalChild = textContainer = new FillFlowContainer
{
+ Alpha = 0,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(spacing / 2),
@@ -327,11 +327,6 @@ namespace osu.Game.Screens.Select
TextSize = 14,
},
},
- textFlow = new OsuTextFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- },
},
};
}
@@ -342,7 +337,7 @@ namespace osu.Game.Screens.Select
{
if (string.IsNullOrEmpty(value))
{
- this.FadeOut(transition_duration);
+ textContainer.FadeOut(transition_duration);
return;
}
@@ -364,7 +359,7 @@ namespace osu.Game.Screens.Select
textContainer.Add(textFlow = loaded);
// fade in if we haven't yet.
- this.FadeIn(transition_duration);
+ textContainer.FadeIn(transition_duration);
});
}
}
diff --git a/osu.Game/Screens/Select/Filter/GroupMode.cs b/osu.Game/Screens/Select/Filter/GroupMode.cs
index 6e57843dfc..b3bd73ee59 100644
--- a/osu.Game/Screens/Select/Filter/GroupMode.cs
+++ b/osu.Game/Screens/Select/Filter/GroupMode.cs
@@ -21,8 +21,8 @@ namespace osu.Game.Screens.Select.Filter
DateAdded,
[Description("Difficulty")]
Difficulty,
- [Description("Favorites")]
- Favorites,
+ [Description("Favourites")]
+ Favourites,
[Description("Length")]
Length,
[Description("My Maps")]
diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs
index 2c43b333aa..917a08d172 100644
--- a/osu.Game/Screens/Select/PlaySongSelect.cs
+++ b/osu.Game/Screens/Select/PlaySongSelect.cs
@@ -65,7 +65,7 @@ namespace osu.Game.Screens.Select
BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.fa_times_circle_o, colours.Purple, null, Key.Number1);
BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.fa_eraser, colours.Purple, null, Key.Number2);
- BeatmapOptions.AddButton(@"Edit", @"Beatmap", FontAwesome.fa_pencil, colours.Yellow, () =>
+ BeatmapOptions.AddButton(@"Edit", @"beatmap", FontAwesome.fa_pencil, colours.Yellow, () =>
{
ValidForResume = false;
Push(new Editor());
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index 7402cf4da0..b4f552ce93 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -204,7 +204,7 @@ namespace osu.Game.Screens.Select
Footer.AddButton(@"random", colours.Green, triggerRandom, Key.F2);
Footer.AddButton(@"options", colours.Blue, BeatmapOptions, Key.F3);
- BeatmapOptions.AddButton(@"Delete", @"Beatmap", FontAwesome.fa_trash, colours.Pink, () => delete(Beatmap.Value.BeatmapSetInfo), Key.Number4, float.MaxValue);
+ BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.fa_trash, colours.Pink, () => delete(Beatmap.Value.BeatmapSetInfo), Key.Number4, float.MaxValue);
}
if (this.beatmaps == null)
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/Storyboards/Drawables/DrawableStoryboardSample.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs
index cdec8c042f..4f469ae593 100644
--- a/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs
+++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardSample.cs
@@ -41,7 +41,7 @@ namespace osu.Game.Storyboards.Drawables
{
base.Update();
- // TODO: this logic will need to be consolidated with other game samples like hitsounds.
+ // TODO: this logic will need to be consolidated with other game samples like hit sounds.
if (Time.Current < sample.Time)
{
// We've rewound before the start time of the sample
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 66bae277b3..26004b513f 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -14,13 +14,13 @@
-
-
-
+
+
+
-
+
-
+
diff --git a/osu.TestProject.props b/osu.TestProject.props
index 506d634555..456ecfd468 100644
--- a/osu.TestProject.props
+++ b/osu.TestProject.props
@@ -10,10 +10,6 @@
-
-
-
-
VisualTestRunner.cs
diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings
index 404b19deda..345400305c 100644
--- a/osu.sln.DotSettings
+++ b/osu.sln.DotSettings
@@ -666,6 +666,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
True
True
True
+ True
True
True
True