diff --git a/osu.Android.props b/osu.Android.props
index c4cdffa8a5..3b2e6574ac 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -62,7 +62,7 @@
-
-
+
+
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 9acf47a67c..4100404da6 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
@@ -4,7 +4,7 @@
-
+
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 df5131dd8b..013d2a71d4 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
@@ -4,7 +4,7 @@
-
+
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternType.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternType.cs
index a3cd455886..e4a28167ec 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternType.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternType.cs
@@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
///
/// Keep the same as last row.
///
- ForceStack = 1 << 0,
+ ForceStack = 1,
///
/// Keep different from last row.
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs
index 8102718edf..a92e56d3c3 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/Pieces/BodyPiece.cs
@@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
}
}
- private Cached subtractionCache = new Cached();
+ private readonly Cached subtractionCache = new Cached();
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
{
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 bb3e5a66f3..92c5c77aac 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
@@ -4,7 +4,7 @@
-
+
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs
index 0590ca1d96..70a1bad4a3 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs
@@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
var spanProgress = slider.ProgressAt(completionProgress);
double start = 0;
- double end = SnakingIn.Value ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadeIn, 0, 1) : 1;
+ double end = SnakingIn.Value ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / (slider.TimePreempt / 3), 0, 1) : 1;
if (span >= slider.SpanCount() - 1)
{
diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
index d1221fd2d3..b52bfcd181 100644
--- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
@@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Osu.Objects
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
- TimeFadeIn = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1200, 800, 300);
+ TimeFadeIn = 400; // as per osu-stable
Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs
index a4638c31f2..d3279652c7 100644
--- a/osu.Game.Rulesets.Osu/Objects/Slider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs
@@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Objects
public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity;
public double Duration => EndTime - StartTime;
- private Cached endPositionCache;
+ private readonly Cached endPositionCache = new Cached();
public override Vector2 EndPosition => endPositionCache.IsValid ? endPositionCache.Value : endPositionCache.Value = Position + this.CurvePositionAt(1);
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 5510c3a9d9..82055ecaee 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
@@ -4,7 +4,7 @@
-
+
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
index d087251e7e..535320530d 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
@@ -482,5 +482,17 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(hitObjects[0].Samples[0].Bank, hitObjects[0].Samples[1].Bank);
}
}
+
+ [Test]
+ public void TestInvalidEventStillPasses()
+ {
+ var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
+
+ using (var badResStream = TestResources.OpenResource("invalid-events.osu"))
+ using (var badStream = new StreamReader(badResStream))
+ {
+ Assert.DoesNotThrow(() => decoder.Decode(badStream));
+ }
+ }
}
}
diff --git a/osu.Game.Tests/Resources/invalid-events.osu b/osu.Game.Tests/Resources/invalid-events.osu
new file mode 100644
index 0000000000..df86b26dba
--- /dev/null
+++ b/osu.Game.Tests/Resources/invalid-events.osu
@@ -0,0 +1,14 @@
+osu file format v14
+
+[Events]
+bad,event,this,should,fail
+//Background and Video events
+0,0,"machinetop_background.jpg",0,0
+//Break Periods
+2,122474,140135
+//Storyboard Layer 0 (Background)
+this,is,also,bad
+//Storyboard Layer 1 (Fail)
+//Storyboard Layer 2 (Pass)
+//Storyboard Layer 3 (Foreground)
+//Storyboard Sound Samples
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs
index 3cd1b8307a..879e15c548 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBreakOverlay.cs
@@ -1,8 +1,11 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using System.Collections.Generic;
+using System.Linq;
using NUnit.Framework;
+using osu.Framework.Timing;
using osu.Game.Beatmaps.Timing;
using osu.Game.Screens.Play;
@@ -11,78 +14,172 @@ namespace osu.Game.Tests.Visual.Gameplay
[TestFixture]
public class TestSceneBreakOverlay : OsuTestScene
{
- private readonly BreakOverlay breakOverlay;
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(BreakOverlay),
+ };
+
+ private readonly TestBreakOverlay breakOverlay;
+
+ private readonly IReadOnlyList testBreaks = new List
+ {
+ new BreakPeriod
+ {
+ StartTime = 1000,
+ EndTime = 5000,
+ },
+ new BreakPeriod
+ {
+ StartTime = 6000,
+ EndTime = 13500,
+ },
+ };
public TestSceneBreakOverlay()
{
- Child = breakOverlay = new BreakOverlay(true);
-
- AddStep("2s break", () => startBreak(2000));
- AddStep("5s break", () => startBreak(5000));
- AddStep("10s break", () => startBreak(10000));
- AddStep("15s break", () => startBreak(15000));
- AddStep("2s, 2s", startMultipleBreaks);
- AddStep("0.5s, 0.7s, 1s, 2s", startAnotherMultipleBreaks);
+ Add(breakOverlay = new TestBreakOverlay(true));
}
- private void startBreak(double duration)
+ [Test]
+ public void TestShowBreaks()
{
- breakOverlay.Breaks = new List
+ setClock(false);
+
+ addShowBreakStep(2);
+ addShowBreakStep(5);
+ addShowBreakStep(15);
+ }
+
+ [Test]
+ public void TestNoEffectsBreak()
+ {
+ var shortBreak = new BreakPeriod { EndTime = 500 };
+
+ setClock(true);
+ loadBreaksStep("short break", new[] { shortBreak });
+
+ addBreakSeeks(shortBreak, false);
+ }
+
+ [Test]
+ public void TestMultipleBreaks()
+ {
+ setClock(true);
+ loadBreaksStep("multiple breaks", testBreaks);
+
+ foreach (var b in testBreaks)
+ addBreakSeeks(b, false);
+ }
+
+ [Test]
+ public void TestRewindBreaks()
+ {
+ setClock(true);
+ loadBreaksStep("multiple breaks", testBreaks);
+
+ foreach (var b in testBreaks.Reverse())
+ addBreakSeeks(b, true);
+ }
+
+ [Test]
+ public void TestSkipBreaks()
+ {
+ setClock(true);
+ loadBreaksStep("multiple breaks", testBreaks);
+
+ seekAndAssertBreak("seek to break start", testBreaks[1].StartTime, true);
+ AddAssert("is skipped to break #2", () => breakOverlay.CurrentBreakIndex == 1);
+
+ seekAndAssertBreak("seek to break middle", testBreaks[1].StartTime + testBreaks[1].Duration / 2, true);
+ seekAndAssertBreak("seek to break end", testBreaks[1].EndTime, false);
+ seekAndAssertBreak("seek to break after end", testBreaks[1].EndTime + 500, false);
+ }
+
+ private void addShowBreakStep(double seconds)
+ {
+ AddStep($"show '{seconds}s' break", () => breakOverlay.Breaks = new List
{
new BreakPeriod
{
StartTime = Clock.CurrentTime,
- EndTime = Clock.CurrentTime + duration,
+ EndTime = Clock.CurrentTime + seconds * 1000,
}
- };
+ });
}
- private void startMultipleBreaks()
+ private void setClock(bool useManual)
{
- double currentTime = Clock.CurrentTime;
-
- breakOverlay.Breaks = new List
- {
- new BreakPeriod
- {
- StartTime = currentTime,
- EndTime = currentTime + 2000,
- },
- new BreakPeriod
- {
- StartTime = currentTime + 4000,
- EndTime = currentTime + 6000,
- }
- };
+ AddStep($"set {(useManual ? "manual" : "realtime")} clock", () => breakOverlay.SwitchClock(useManual));
}
- private void startAnotherMultipleBreaks()
+ private void loadBreaksStep(string breakDescription, IReadOnlyList breaks)
{
- double currentTime = Clock.CurrentTime;
+ AddStep($"load {breakDescription}", () => breakOverlay.Breaks = breaks);
+ seekAndAssertBreak("seek back to 0", 0, false);
+ }
- breakOverlay.Breaks = new List
+ private void addBreakSeeks(BreakPeriod b, bool isReversed)
+ {
+ if (isReversed)
{
- new BreakPeriod // Duration is less than 650 - too short to appear
- {
- StartTime = currentTime,
- EndTime = currentTime + 500,
- },
- new BreakPeriod
- {
- StartTime = currentTime + 1500,
- EndTime = currentTime + 2200,
- },
- new BreakPeriod
- {
- StartTime = currentTime + 3200,
- EndTime = currentTime + 4200,
- },
- new BreakPeriod
- {
- StartTime = currentTime + 5200,
- EndTime = currentTime + 7200,
- }
- };
+ seekAndAssertBreak("seek to break after end", b.EndTime + 500, false);
+ seekAndAssertBreak("seek to break end", b.EndTime, false);
+ seekAndAssertBreak("seek to break middle", b.StartTime + b.Duration / 2, b.HasEffect);
+ seekAndAssertBreak("seek to break start", b.StartTime, b.HasEffect);
+ }
+ else
+ {
+ seekAndAssertBreak("seek to break start", b.StartTime, b.HasEffect);
+ seekAndAssertBreak("seek to break middle", b.StartTime + b.Duration / 2, b.HasEffect);
+ seekAndAssertBreak("seek to break end", b.EndTime, false);
+ seekAndAssertBreak("seek to break after end", b.EndTime + 500, false);
+ }
+ }
+
+ private void seekAndAssertBreak(string seekStepDescription, double time, bool shouldBeBreak)
+ {
+ AddStep(seekStepDescription, () => breakOverlay.ManualClockTime = time);
+ AddAssert($"is{(!shouldBeBreak ? " not" : string.Empty)} break time", () =>
+ {
+ breakOverlay.ProgressTime();
+ return breakOverlay.IsBreakTime.Value == shouldBeBreak;
+ });
+ }
+
+ private class TestBreakOverlay : BreakOverlay
+ {
+ private readonly FramedClock framedManualClock;
+ private readonly ManualClock manualClock;
+ private IFrameBasedClock originalClock;
+
+ public new int CurrentBreakIndex => base.CurrentBreakIndex;
+
+ public double ManualClockTime
+ {
+ get => manualClock.CurrentTime;
+ set => manualClock.CurrentTime = value;
+ }
+
+ public TestBreakOverlay(bool letterboxing)
+ : base(letterboxing)
+ {
+ framedManualClock = new FramedClock(manualClock = new ManualClock());
+ ProcessCustomClock = false;
+ }
+
+ public void ProgressTime()
+ {
+ framedManualClock.ProcessFrame();
+ Update();
+ }
+
+ public void SwitchClock(bool setManual) => Clock = setManual ? framedManualClock : originalClock;
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ originalClock = Clock;
+ }
}
}
}
diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj
index 659f5415c3..50530088c2 100644
--- a/osu.Game.Tests/osu.Game.Tests.csproj
+++ b/osu.Game.Tests/osu.Game.Tests.csproj
@@ -5,7 +5,7 @@
-
+
diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj
index dad2fe0877..257db89a20 100644
--- a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj
+++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj
@@ -7,7 +7,7 @@
-
+
WinExe
diff --git a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs
index 67531ce5d3..83a41a662f 100644
--- a/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs
+++ b/osu.Game.Tournament/Screens/Ladder/LadderScreen.cs
@@ -88,7 +88,7 @@ namespace osu.Game.Tournament.Screens.Ladder
};
}
- private Cached layout = new Cached();
+ private readonly Cached layout = new Cached();
protected override void Update()
{
diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs
index 4ebeee40bf..a09a1bb9cb 100644
--- a/osu.Game/Beatmaps/Beatmap.cs
+++ b/osu.Game/Beatmaps/Beatmap.cs
@@ -60,5 +60,7 @@ namespace osu.Game.Beatmaps
public class Beatmap : Beatmap
{
public new Beatmap Clone() => (Beatmap)base.Clone();
+
+ public override string ToString() => BeatmapInfo?.ToString() ?? base.ToString();
}
}
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index 3cd425ea44..02d969b571 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -5,7 +5,6 @@ using System;
using System.IO;
using System.Linq;
using osu.Framework.IO.File;
-using osu.Framework.Logging;
using osu.Game.Beatmaps.Timing;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Beatmaps.ControlPoints;
@@ -290,8 +289,9 @@ namespace osu.Game.Beatmaps.Formats
string[] split = line.Split(',');
EventType type;
+
if (!Enum.TryParse(split[0], out type))
- throw new InvalidDataException($@"Unknown event type {split[0]}");
+ throw new InvalidDataException($@"Unknown event type: {split[0]}");
switch (type)
{
@@ -319,90 +319,79 @@ namespace osu.Game.Beatmaps.Formats
private void handleTimingPoint(string line)
{
- try
+ string[] split = line.Split(',');
+
+ double time = getOffsetTime(Parsing.ParseDouble(split[0].Trim()));
+ double beatLength = Parsing.ParseDouble(split[1].Trim());
+ double speedMultiplier = beatLength < 0 ? 100.0 / -beatLength : 1;
+
+ TimeSignatures timeSignature = TimeSignatures.SimpleQuadruple;
+ if (split.Length >= 3)
+ timeSignature = split[2][0] == '0' ? TimeSignatures.SimpleQuadruple : (TimeSignatures)Parsing.ParseInt(split[2]);
+
+ LegacySampleBank sampleSet = defaultSampleBank;
+ if (split.Length >= 4)
+ sampleSet = (LegacySampleBank)Parsing.ParseInt(split[3]);
+
+ int customSampleBank = 0;
+ if (split.Length >= 5)
+ customSampleBank = Parsing.ParseInt(split[4]);
+
+ int sampleVolume = defaultSampleVolume;
+ if (split.Length >= 6)
+ sampleVolume = Parsing.ParseInt(split[5]);
+
+ bool timingChange = true;
+ if (split.Length >= 7)
+ timingChange = split[6][0] == '1';
+
+ bool kiaiMode = false;
+ bool omitFirstBarSignature = false;
+
+ if (split.Length >= 8)
{
- string[] split = line.Split(',');
-
- double time = getOffsetTime(Parsing.ParseDouble(split[0].Trim()));
- double beatLength = Parsing.ParseDouble(split[1].Trim());
- double speedMultiplier = beatLength < 0 ? 100.0 / -beatLength : 1;
-
- TimeSignatures timeSignature = TimeSignatures.SimpleQuadruple;
- if (split.Length >= 3)
- timeSignature = split[2][0] == '0' ? TimeSignatures.SimpleQuadruple : (TimeSignatures)Parsing.ParseInt(split[2]);
-
- LegacySampleBank sampleSet = defaultSampleBank;
- if (split.Length >= 4)
- sampleSet = (LegacySampleBank)Parsing.ParseInt(split[3]);
-
- int customSampleBank = 0;
- if (split.Length >= 5)
- customSampleBank = Parsing.ParseInt(split[4]);
-
- int sampleVolume = defaultSampleVolume;
- if (split.Length >= 6)
- sampleVolume = Parsing.ParseInt(split[5]);
-
- bool timingChange = true;
- if (split.Length >= 7)
- timingChange = split[6][0] == '1';
-
- bool kiaiMode = false;
- bool omitFirstBarSignature = false;
-
- if (split.Length >= 8)
- {
- EffectFlags effectFlags = (EffectFlags)Parsing.ParseInt(split[7]);
- kiaiMode = effectFlags.HasFlag(EffectFlags.Kiai);
- omitFirstBarSignature = effectFlags.HasFlag(EffectFlags.OmitFirstBarLine);
- }
-
- string stringSampleSet = sampleSet.ToString().ToLowerInvariant();
- if (stringSampleSet == @"none")
- stringSampleSet = @"normal";
-
- if (timingChange)
- {
- var controlPoint = CreateTimingControlPoint();
- controlPoint.Time = time;
- controlPoint.BeatLength = beatLength;
- controlPoint.TimeSignature = timeSignature;
-
- handleTimingControlPoint(controlPoint);
- }
-
- handleDifficultyControlPoint(new DifficultyControlPoint
- {
- Time = time,
- SpeedMultiplier = speedMultiplier,
- AutoGenerated = timingChange
- });
-
- handleEffectControlPoint(new EffectControlPoint
- {
- Time = time,
- KiaiMode = kiaiMode,
- OmitFirstBarLine = omitFirstBarSignature,
- AutoGenerated = timingChange
- });
-
- handleSampleControlPoint(new LegacySampleControlPoint
- {
- Time = time,
- SampleBank = stringSampleSet,
- SampleVolume = sampleVolume,
- CustomSampleBank = customSampleBank,
- AutoGenerated = timingChange
- });
+ EffectFlags effectFlags = (EffectFlags)Parsing.ParseInt(split[7]);
+ kiaiMode = effectFlags.HasFlag(EffectFlags.Kiai);
+ omitFirstBarSignature = effectFlags.HasFlag(EffectFlags.OmitFirstBarLine);
}
- catch (FormatException)
+
+ string stringSampleSet = sampleSet.ToString().ToLowerInvariant();
+ if (stringSampleSet == @"none")
+ stringSampleSet = @"normal";
+
+ if (timingChange)
{
- Logger.Log("A timing point could not be parsed correctly and will be ignored", LoggingTarget.Runtime, LogLevel.Important);
+ var controlPoint = CreateTimingControlPoint();
+ controlPoint.Time = time;
+ controlPoint.BeatLength = beatLength;
+ controlPoint.TimeSignature = timeSignature;
+
+ handleTimingControlPoint(controlPoint);
}
- catch (OverflowException)
+
+ handleDifficultyControlPoint(new DifficultyControlPoint
{
- Logger.Log("A timing point could not be parsed correctly and will be ignored", LoggingTarget.Runtime, LogLevel.Important);
- }
+ Time = time,
+ SpeedMultiplier = speedMultiplier,
+ AutoGenerated = timingChange
+ });
+
+ handleEffectControlPoint(new EffectControlPoint
+ {
+ Time = time,
+ KiaiMode = kiaiMode,
+ OmitFirstBarLine = omitFirstBarSignature,
+ AutoGenerated = timingChange
+ });
+
+ handleSampleControlPoint(new LegacySampleControlPoint
+ {
+ Time = time,
+ SampleBank = stringSampleSet,
+ SampleVolume = sampleVolume,
+ CustomSampleBank = customSampleBank,
+ AutoGenerated = timingChange
+ });
}
private void handleTimingControlPoint(TimingControlPoint newPoint)
diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
index 7999c82761..9a8197ad82 100644
--- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
@@ -36,7 +36,7 @@ namespace osu.Game.Beatmaps.Formats
{
if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section))
{
- Logger.Log($"Unknown section \"{line}\" in {output}");
+ Logger.Log($"Unknown section \"{line}\" in \"{output}\"");
section = Section.None;
}
@@ -49,7 +49,7 @@ namespace osu.Game.Beatmaps.Formats
}
catch (Exception e)
{
- Logger.Error(e, $"Failed to process line \"{line}\" into {output}");
+ Logger.Log($"Failed to process line \"{line}\" into \"{output}\": {e.Message}", LoggingTarget.Runtime, LogLevel.Important);
}
}
}
diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
index f6e2bf6966..3ae1c3ef12 100644
--- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
@@ -82,8 +82,9 @@ namespace osu.Game.Beatmaps.Formats
storyboardSprite = null;
EventType type;
+
if (!Enum.TryParse(split[0], out type))
- throw new InvalidDataException($@"Unknown event type {split[0]}");
+ throw new InvalidDataException($@"Unknown event type: {split[0]}");
switch (type)
{
diff --git a/osu.Game/Beatmaps/Legacy/LegacyMods.cs b/osu.Game/Beatmaps/Legacy/LegacyMods.cs
index 8e53c24e7b..583e950e49 100644
--- a/osu.Game/Beatmaps/Legacy/LegacyMods.cs
+++ b/osu.Game/Beatmaps/Legacy/LegacyMods.cs
@@ -9,7 +9,7 @@ namespace osu.Game.Beatmaps.Legacy
public enum LegacyMods
{
None = 0,
- NoFail = 1 << 0,
+ NoFail = 1,
Easy = 1 << 1,
TouchDevice = 1 << 2,
Hidden = 1 << 3,
diff --git a/osu.Game/Beatmaps/Timing/BreakPeriod.cs b/osu.Game/Beatmaps/Timing/BreakPeriod.cs
index 856a5fefd4..5d79c7a86b 100644
--- a/osu.Game/Beatmaps/Timing/BreakPeriod.cs
+++ b/osu.Game/Beatmaps/Timing/BreakPeriod.cs
@@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using osu.Game.Screens.Play;
+
namespace osu.Game.Beatmaps.Timing
{
public class BreakPeriod
@@ -35,6 +37,6 @@ namespace osu.Game.Beatmaps.Timing
///
/// The time to check in milliseconds.
/// Whether the time falls within this .
- public bool Contains(double time) => time >= StartTime && time <= EndTime;
+ public bool Contains(double time) => time >= StartTime && time <= EndTime - BreakOverlay.BREAK_FADE_DURATION;
}
}
diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs
index efb76deff8..52d3f013ce 100644
--- a/osu.Game/Database/ArchiveModelManager.cs
+++ b/osu.Game/Database/ArchiveModelManager.cs
@@ -31,10 +31,21 @@ namespace osu.Game.Database
///
/// The model type.
/// The associated file join type.
- public abstract class ArchiveModelManager : ArchiveModelManager, ICanAcceptFiles, IModelManager
+ public abstract class ArchiveModelManager : ICanAcceptFiles, IModelManager
where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete
where TFileModel : INamedFileInfo, new()
{
+ private const int import_queue_request_concurrency = 1;
+
+ ///
+ /// A singleton scheduler shared by all .
+ ///
+ ///
+ /// This scheduler generally performs IO and CPU intensive work so concurrency is limited harshly.
+ /// It is mainly being used as a queue mechanism for large imports.
+ ///
+ private static readonly ThreadedTaskScheduler import_scheduler = new ThreadedTaskScheduler(import_queue_request_concurrency, nameof(ArchiveModelManager));
+
///
/// Set an endpoint for notifications to be posted to.
///
@@ -336,7 +347,7 @@ namespace osu.Game.Database
flushEvents(true);
return item;
- }, cancellationToken, TaskCreationOptions.HideScheduler, IMPORT_SCHEDULER).Unwrap();
+ }, cancellationToken, TaskCreationOptions.HideScheduler, import_scheduler).Unwrap();
///
/// Perform an update of the specified item.
@@ -646,18 +657,4 @@ namespace osu.Game.Database
#endregion
}
-
- public abstract class ArchiveModelManager
- {
- private const int import_queue_request_concurrency = 1;
-
- ///
- /// A singleton scheduler shared by all .
- ///
- ///
- /// This scheduler generally performs IO and CPU intensive work so concurrency is limited harshly.
- /// It is mainly being used as a queue mechanism for large imports.
- ///
- protected static readonly ThreadedTaskScheduler IMPORT_SCHEDULER = new ThreadedTaskScheduler(import_queue_request_concurrency, nameof(ArchiveModelManager));
- }
}
diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs
index 2b68e8530d..dffa0c4fd5 100644
--- a/osu.Game/Graphics/Backgrounds/Triangles.cs
+++ b/osu.Game/Graphics/Backgrounds/Triangles.cs
@@ -191,7 +191,7 @@ namespace osu.Game.Graphics.Backgrounds
private readonly List parts = new List();
private Vector2 size;
- private TriangleBatch vertexBatch;
+ private QuadBatch vertexBatch;
public TrianglesDrawNode(Triangles source)
: base(source)
@@ -217,7 +217,7 @@ namespace osu.Game.Graphics.Backgrounds
if (Source.AimCount > 0 && (vertexBatch == null || vertexBatch.Size != Source.AimCount))
{
vertexBatch?.Dispose();
- vertexBatch = new TriangleBatch(Source.AimCount, 1);
+ vertexBatch = new QuadBatch(Source.AimCount, 1);
}
shader.Bind();
diff --git a/osu.Game/Graphics/UserInterface/Bar.cs b/osu.Game/Graphics/UserInterface/Bar.cs
index 2a858ccbcf..f8d5955503 100644
--- a/osu.Game/Graphics/UserInterface/Bar.cs
+++ b/osu.Game/Graphics/UserInterface/Bar.cs
@@ -110,7 +110,7 @@ namespace osu.Game.Graphics.UserInterface
[Flags]
public enum BarDirection
{
- LeftToRight = 1 << 0,
+ LeftToRight = 1,
RightToLeft = 1 << 1,
TopToBottom = 1 << 2,
BottomToTop = 1 << 3,
diff --git a/osu.Game/Graphics/UserInterface/LineGraph.cs b/osu.Game/Graphics/UserInterface/LineGraph.cs
index 714e953816..6d65b77cbf 100644
--- a/osu.Game/Graphics/UserInterface/LineGraph.cs
+++ b/osu.Game/Graphics/UserInterface/LineGraph.cs
@@ -93,7 +93,7 @@ namespace osu.Game.Graphics.UserInterface
return base.Invalidate(invalidation, source, shallPropagate);
}
- private Cached pathCached = new Cached();
+ private readonly Cached pathCached = new Cached();
protected override void Update()
{
diff --git a/osu.Game/Graphics/UserInterface/ScreenTitle.cs b/osu.Game/Graphics/UserInterface/ScreenTitle.cs
index 7b39238e5e..10fc312d8b 100644
--- a/osu.Game/Graphics/UserInterface/ScreenTitle.cs
+++ b/osu.Game/Graphics/UserInterface/ScreenTitle.cs
@@ -15,7 +15,7 @@ namespace osu.Game.Graphics.UserInterface
{
public const float ICON_WIDTH = ICON_SIZE + icon_spacing;
- protected const float ICON_SIZE = 25;
+ public const float ICON_SIZE = 25;
private SpriteIcon iconSprite;
private readonly OsuSpriteText titleText, pageText;
diff --git a/osu.Game/Graphics/UserInterface/ScreenTitleTextureIcon.cs b/osu.Game/Graphics/UserInterface/ScreenTitleTextureIcon.cs
new file mode 100644
index 0000000000..f590e7e357
--- /dev/null
+++ b/osu.Game/Graphics/UserInterface/ScreenTitleTextureIcon.cs
@@ -0,0 +1,64 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Textures;
+using osuTK;
+
+namespace osu.Game.Graphics.UserInterface
+{
+ ///
+ /// A custom icon class for use with based off a texture resource.
+ ///
+ public class ScreenTitleTextureIcon : CompositeDrawable
+ {
+ private const float circle_allowance = 0.8f;
+
+ private readonly string textureName;
+
+ public ScreenTitleTextureIcon(string textureName)
+ {
+ this.textureName = textureName;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(TextureStore textures, OsuColour colours)
+ {
+ Size = new Vector2(ScreenTitle.ICON_SIZE / circle_allowance);
+
+ InternalChildren = new Drawable[]
+ {
+ new CircularContainer
+ {
+ Masking = true,
+ BorderColour = colours.Violet,
+ BorderThickness = 3,
+ MaskingSmoothness = 1,
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new Sprite
+ {
+ RelativeSizeAxes = Axes.Both,
+ Texture = textures.Get(textureName),
+ Size = new Vector2(circle_allowance),
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ },
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = colours.Violet,
+ Alpha = 0,
+ AlwaysPresent = true,
+ },
+ }
+ },
+ };
+ }
+ }
+}
diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs
index 3af11ff20f..4f6066cab1 100644
--- a/osu.Game/Online/Chat/ChannelManager.cs
+++ b/osu.Game/Online/Chat/ChannelManager.cs
@@ -213,8 +213,27 @@ namespace osu.Game.Online.Chat
PostMessage(content, true);
break;
+ case "join":
+ if (string.IsNullOrWhiteSpace(content))
+ {
+ target.AddNewMessages(new ErrorMessage("Usage: /join [channel]"));
+ break;
+ }
+
+ var channel = availableChannels.Where(c => c.Name == content || c.Name == $"#{content}").FirstOrDefault();
+
+ if (channel == null)
+ {
+ target.AddNewMessages(new ErrorMessage($"Channel '{content}' not found."));
+ break;
+ }
+
+ JoinChannel(channel);
+ CurrentChannel.Value = channel;
+ break;
+
case "help":
- target.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action]"));
+ target.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action], /join [channel]"));
break;
default:
diff --git a/osu.Game/Online/Chat/ErrorMessage.cs b/osu.Game/Online/Chat/ErrorMessage.cs
index a8ff0e9a98..87a65fb3f1 100644
--- a/osu.Game/Online/Chat/ErrorMessage.cs
+++ b/osu.Game/Online/Chat/ErrorMessage.cs
@@ -8,7 +8,7 @@ namespace osu.Game.Online.Chat
public ErrorMessage(string message)
: base(message)
{
- Sender.Colour = @"ff0000";
+ // todo: this should likely be styled differently in the future.
}
}
}
diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs
index fca62fbb44..b2e9be24b3 100644
--- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs
+++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs
@@ -7,13 +7,11 @@ using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests.Responses;
-using osuTK;
namespace osu.Game.Overlays.Changelog
{
@@ -123,48 +121,7 @@ namespace osu.Game.Overlays.Changelog
AccentColour = colours.Violet;
}
- protected override Drawable CreateIcon() => new ChangelogIcon();
-
- internal class ChangelogIcon : CompositeDrawable
- {
- private const float circle_allowance = 0.8f;
-
- [BackgroundDependencyLoader]
- private void load(TextureStore textures, OsuColour colours)
- {
- Size = new Vector2(ICON_SIZE / circle_allowance);
-
- InternalChildren = new Drawable[]
- {
- new CircularContainer
- {
- Masking = true,
- BorderColour = colours.Violet,
- BorderThickness = 3,
- MaskingSmoothness = 1,
- RelativeSizeAxes = Axes.Both,
- Children = new Drawable[]
- {
- new Sprite
- {
- RelativeSizeAxes = Axes.Both,
- Texture = textures.Get(@"Icons/changelog"),
- Size = new Vector2(circle_allowance),
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- },
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = colours.Violet,
- Alpha = 0,
- AlwaysPresent = true,
- },
- }
- },
- };
- }
- }
+ protected override Drawable CreateIcon() => new ScreenTitleTextureIcon(@"Icons/changelog");
}
}
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
index d70c1bf7d3..e990938291 100644
--- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
@@ -10,7 +10,6 @@ using osu.Game.Beatmaps.Formats;
using osu.Game.Audio;
using System.Linq;
using JetBrains.Annotations;
-using osu.Framework.Logging;
using osu.Framework.MathUtils;
namespace osu.Game.Rulesets.Objects.Legacy
@@ -41,208 +40,192 @@ namespace osu.Game.Rulesets.Objects.Legacy
[CanBeNull]
public override HitObject Parse(string text)
{
- try
+ string[] split = text.Split(',');
+
+ Vector2 pos = new Vector2((int)Parsing.ParseFloat(split[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseFloat(split[1], Parsing.MAX_COORDINATE_VALUE));
+
+ double startTime = Parsing.ParseDouble(split[2]) + Offset;
+
+ ConvertHitObjectType type = (ConvertHitObjectType)Parsing.ParseInt(split[3]);
+
+ int comboOffset = (int)(type & ConvertHitObjectType.ComboOffset) >> 4;
+ type &= ~ConvertHitObjectType.ComboOffset;
+
+ bool combo = type.HasFlag(ConvertHitObjectType.NewCombo);
+ type &= ~ConvertHitObjectType.NewCombo;
+
+ var soundType = (LegacySoundType)Parsing.ParseInt(split[4]);
+ var bankInfo = new SampleBankInfo();
+
+ HitObject result = null;
+
+ if (type.HasFlag(ConvertHitObjectType.Circle))
{
- string[] split = text.Split(',');
+ result = CreateHit(pos, combo, comboOffset);
- Vector2 pos = new Vector2((int)Parsing.ParseFloat(split[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseFloat(split[1], Parsing.MAX_COORDINATE_VALUE));
+ if (split.Length > 5)
+ readCustomSampleBanks(split[5], bankInfo);
+ }
+ else if (type.HasFlag(ConvertHitObjectType.Slider))
+ {
+ PathType pathType = PathType.Catmull;
+ double? length = null;
- double startTime = Parsing.ParseDouble(split[2]) + Offset;
+ string[] pointSplit = split[5].Split('|');
- ConvertHitObjectType type = (ConvertHitObjectType)Parsing.ParseInt(split[3]);
+ int pointCount = 1;
+ foreach (var t in pointSplit)
+ if (t.Length > 1)
+ pointCount++;
- int comboOffset = (int)(type & ConvertHitObjectType.ComboOffset) >> 4;
- type &= ~ConvertHitObjectType.ComboOffset;
+ var points = new Vector2[pointCount];
- bool combo = type.HasFlag(ConvertHitObjectType.NewCombo);
- type &= ~ConvertHitObjectType.NewCombo;
+ int pointIndex = 1;
- var soundType = (LegacySoundType)Parsing.ParseInt(split[4]);
- var bankInfo = new SampleBankInfo();
-
- HitObject result = null;
-
- if (type.HasFlag(ConvertHitObjectType.Circle))
+ foreach (string t in pointSplit)
{
- result = CreateHit(pos, combo, comboOffset);
-
- if (split.Length > 5)
- readCustomSampleBanks(split[5], bankInfo);
- }
- else if (type.HasFlag(ConvertHitObjectType.Slider))
- {
- PathType pathType = PathType.Catmull;
- double? length = null;
-
- 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)
{
- if (t.Length == 1)
+ switch (t)
{
- switch (t)
- {
- case @"C":
- pathType = PathType.Catmull;
- break;
-
- case @"B":
- pathType = PathType.Bezier;
- break;
-
- case @"L":
- pathType = PathType.Linear;
- break;
-
- case @"P":
- pathType = PathType.PerfectCurve;
- break;
- }
-
- continue;
- }
-
- string[] temp = t.Split(':');
- points[pointIndex++] = new Vector2((int)Parsing.ParseDouble(temp[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseDouble(temp[1], Parsing.MAX_COORDINATE_VALUE)) - pos;
- }
-
- // osu-stable special-cased colinear perfect curves to a CurveType.Linear
- 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 && pathType == PathType.PerfectCurve && isLinear(points))
- pathType = PathType.Linear;
-
- int repeatCount = Parsing.ParseInt(split[6]);
-
- if (repeatCount > 9000)
- throw new ArgumentOutOfRangeException(nameof(repeatCount), @"Repeat count is way too high");
-
- // osu-stable treated the first span of the slider as a repeat, but no repeats are happening
- repeatCount = Math.Max(0, repeatCount - 1);
-
- if (split.Length > 7)
- {
- length = Math.Max(0, Parsing.ParseDouble(split[7]));
- if (length == 0)
- length = null;
- }
-
- if (split.Length > 10)
- readCustomSampleBanks(split[10], bankInfo);
-
- // One node for each repeat + the start and end nodes
- int nodes = repeatCount + 2;
-
- // Populate node sample bank infos with the default hit object sample bank
- var nodeBankInfos = new List();
- for (int i = 0; i < nodes; i++)
- nodeBankInfos.Add(bankInfo.Clone());
-
- // Read any per-node sample banks
- if (split.Length > 9 && split[9].Length > 0)
- {
- string[] sets = split[9].Split('|');
-
- for (int i = 0; i < nodes; i++)
- {
- if (i >= sets.Length)
+ case @"C":
+ pathType = PathType.Catmull;
break;
- SampleBankInfo info = nodeBankInfos[i];
- readCustomSampleBanks(sets[i], info);
- }
- }
-
- // Populate node sound types with the default hit object sound type
- var nodeSoundTypes = new List();
- for (int i = 0; i < nodes; i++)
- nodeSoundTypes.Add(soundType);
-
- // Read any per-node sound types
- if (split.Length > 8 && split[8].Length > 0)
- {
- string[] adds = split[8].Split('|');
-
- for (int i = 0; i < nodes; i++)
- {
- if (i >= adds.Length)
+ case @"B":
+ pathType = PathType.Bezier;
break;
- int sound;
- int.TryParse(adds[i], out sound);
- nodeSoundTypes[i] = (LegacySoundType)sound;
+ case @"L":
+ pathType = PathType.Linear;
+ break;
+
+ case @"P":
+ pathType = PathType.PerfectCurve;
+ break;
}
+
+ continue;
}
- // Generate the final per-node samples
- var nodeSamples = new List>(nodes);
+ string[] temp = t.Split(':');
+ points[pointIndex++] = new Vector2((int)Parsing.ParseDouble(temp[0], Parsing.MAX_COORDINATE_VALUE), (int)Parsing.ParseDouble(temp[1], Parsing.MAX_COORDINATE_VALUE)) - pos;
+ }
+
+ // osu-stable special-cased colinear perfect curves to a CurveType.Linear
+ 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 && pathType == PathType.PerfectCurve && isLinear(points))
+ pathType = PathType.Linear;
+
+ int repeatCount = Parsing.ParseInt(split[6]);
+
+ if (repeatCount > 9000)
+ throw new ArgumentOutOfRangeException(nameof(repeatCount), @"Repeat count is way too high");
+
+ // osu-stable treated the first span of the slider as a repeat, but no repeats are happening
+ repeatCount = Math.Max(0, repeatCount - 1);
+
+ if (split.Length > 7)
+ {
+ length = Math.Max(0, Parsing.ParseDouble(split[7]));
+ if (length == 0)
+ length = null;
+ }
+
+ if (split.Length > 10)
+ readCustomSampleBanks(split[10], bankInfo);
+
+ // One node for each repeat + the start and end nodes
+ int nodes = repeatCount + 2;
+
+ // Populate node sample bank infos with the default hit object sample bank
+ var nodeBankInfos = new List();
+ for (int i = 0; i < nodes; i++)
+ nodeBankInfos.Add(bankInfo.Clone());
+
+ // Read any per-node sample banks
+ if (split.Length > 9 && split[9].Length > 0)
+ {
+ string[] sets = split[9].Split('|');
+
for (int i = 0; i < nodes; i++)
- nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i]));
-
- result = CreateSlider(pos, combo, comboOffset, points, length, pathType, repeatCount, nodeSamples);
-
- // The samples are played when the slider ends, which is the last node
- result.Samples = nodeSamples[nodeSamples.Count - 1];
- }
- else if (type.HasFlag(ConvertHitObjectType.Spinner))
- {
- double endTime = Math.Max(startTime, Parsing.ParseDouble(split[5]) + Offset);
-
- result = CreateSpinner(new Vector2(512, 384) / 2, combo, comboOffset, endTime);
-
- if (split.Length > 6)
- readCustomSampleBanks(split[6], bankInfo);
- }
- else if (type.HasFlag(ConvertHitObjectType.Hold))
- {
- // Note: Hold is generated by BMS converts
-
- double endTime = Math.Max(startTime, Parsing.ParseDouble(split[2]));
-
- if (split.Length > 5 && !string.IsNullOrEmpty(split[5]))
{
- string[] ss = split[5].Split(':');
- endTime = Math.Max(startTime, Parsing.ParseDouble(ss[0]));
- readCustomSampleBanks(string.Join(":", ss.Skip(1)), bankInfo);
+ if (i >= sets.Length)
+ break;
+
+ SampleBankInfo info = nodeBankInfos[i];
+ readCustomSampleBanks(sets[i], info);
}
-
- result = CreateHold(pos, combo, comboOffset, endTime + Offset);
}
- if (result == null)
+ // Populate node sound types with the default hit object sound type
+ var nodeSoundTypes = new List();
+ for (int i = 0; i < nodes; i++)
+ nodeSoundTypes.Add(soundType);
+
+ // Read any per-node sound types
+ if (split.Length > 8 && split[8].Length > 0)
{
- Logger.Log($"Unknown hit object type: {type}. Skipped.", level: LogLevel.Error);
- return null;
+ string[] adds = split[8].Split('|');
+
+ for (int i = 0; i < nodes; i++)
+ {
+ if (i >= adds.Length)
+ break;
+
+ int sound;
+ int.TryParse(adds[i], out sound);
+ nodeSoundTypes[i] = (LegacySoundType)sound;
+ }
}
- result.StartTime = startTime;
+ // Generate the final per-node samples
+ var nodeSamples = new List>(nodes);
+ for (int i = 0; i < nodes; i++)
+ nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i]));
- if (result.Samples.Count == 0)
- result.Samples = convertSoundType(soundType, bankInfo);
+ result = CreateSlider(pos, combo, comboOffset, points, length, pathType, repeatCount, nodeSamples);
- FirstObject = false;
-
- return result;
+ // The samples are played when the slider ends, which is the last node
+ result.Samples = nodeSamples[nodeSamples.Count - 1];
}
- catch (FormatException)
+ else if (type.HasFlag(ConvertHitObjectType.Spinner))
{
- Logger.Log("A hitobject could not be parsed correctly and will be ignored", LoggingTarget.Runtime, LogLevel.Important);
+ double endTime = Math.Max(startTime, Parsing.ParseDouble(split[5]) + Offset);
+
+ result = CreateSpinner(new Vector2(512, 384) / 2, combo, comboOffset, endTime);
+
+ if (split.Length > 6)
+ readCustomSampleBanks(split[6], bankInfo);
}
- catch (OverflowException)
+ else if (type.HasFlag(ConvertHitObjectType.Hold))
{
- Logger.Log("A hitobject could not be parsed correctly and will be ignored", LoggingTarget.Runtime, LogLevel.Important);
+ // Note: Hold is generated by BMS converts
+
+ double endTime = Math.Max(startTime, Parsing.ParseDouble(split[2]));
+
+ if (split.Length > 5 && !string.IsNullOrEmpty(split[5]))
+ {
+ string[] ss = split[5].Split(':');
+ endTime = Math.Max(startTime, Parsing.ParseDouble(ss[0]));
+ readCustomSampleBanks(string.Join(":", ss.Skip(1)), bankInfo);
+ }
+
+ result = CreateHold(pos, combo, comboOffset, endTime + Offset);
}
- return null;
+ if (result == null)
+ throw new InvalidDataException($"Unknown hit object type: {split[3]}");
+
+ result.StartTime = startTime;
+
+ if (result.Samples.Count == 0)
+ result.Samples = convertSoundType(soundType, bankInfo);
+
+ FirstObject = false;
+
+ return result;
}
private void readCustomSampleBanks(string str, SampleBankInfo bankInfo)
diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectType.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectType.cs
index c9f7224643..eab37b682c 100644
--- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectType.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectType.cs
@@ -8,7 +8,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
[Flags]
internal enum ConvertHitObjectType
{
- Circle = 1 << 0,
+ Circle = 1,
Slider = 1 << 1,
NewCombo = 1 << 2,
Spinner = 1 << 3,
diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
index 2e863f7edb..ba2375bec1 100644
--- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
+++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
@@ -313,6 +313,9 @@ namespace osu.Game.Rulesets.Scoring
///
/// Applies the score change of a to this .
///
+ ///
+ /// Any changes applied via this method can be reverted via .
+ ///
/// The to apply.
protected virtual void ApplyResult(JudgementResult result)
{
@@ -357,7 +360,7 @@ namespace osu.Game.Rulesets.Scoring
}
///
- /// Reverts the score change of a that was applied to this .
+ /// Reverts the score change of a that was applied to this via .
///
/// The judgement scoring result.
protected virtual void RevertResult(JudgementResult result)
diff --git a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs
index 069e2d1a0b..19247d8a37 100644
--- a/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs
+++ b/osu.Game/Rulesets/UI/Scrolling/ScrollingHitObjectContainer.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.UI.Scrolling
[Resolved]
private IScrollingInfo scrollingInfo { get; set; }
- private Cached initialStateCache = new Cached();
+ private readonly Cached initialStateCache = new Cached();
public ScrollingHitObjectContainer()
{
diff --git a/osu.Game/Screens/Direct/OnlineListing.cs b/osu.Game/Screens/Direct/OnlineListing.cs
deleted file mode 100644
index 8376383674..0000000000
--- a/osu.Game/Screens/Direct/OnlineListing.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
-// See the LICENCE file in the repository root for full licence text.
-
-namespace osu.Game.Screens.Direct
-{
- public class OnlineListing : ScreenWhiteBox
- {
- }
-}
diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs
index fc8ddd8bd4..499b5089f6 100644
--- a/osu.Game/Screens/Menu/MainMenu.cs
+++ b/osu.Game/Screens/Menu/MainMenu.cs
@@ -14,7 +14,6 @@ using osu.Game.Graphics.Containers;
using osu.Game.Overlays;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Charts;
-using osu.Game.Screens.Direct;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Multi;
using osu.Game.Screens.Select;
@@ -65,7 +64,6 @@ namespace osu.Game.Screens.Menu
buttons = new ButtonSystem
{
OnChart = delegate { this.Push(new ChartListing()); },
- OnDirect = delegate { this.Push(new OnlineListing()); },
OnEdit = delegate { this.Push(new Editor()); },
OnSolo = onSolo,
OnMulti = delegate { this.Push(new Multiplayer()); },
diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs
index 2b401778a6..6fdee85f45 100644
--- a/osu.Game/Screens/Play/BreakOverlay.cs
+++ b/osu.Game/Screens/Play/BreakOverlay.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@@ -15,7 +16,11 @@ namespace osu.Game.Screens.Play
{
public class BreakOverlay : Container
{
- private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2;
+ ///
+ /// The duration of the break overlay fading.
+ ///
+ public const double BREAK_FADE_DURATION = BreakPeriod.MIN_BREAK_DURATION / 2;
+
private const float remaining_time_container_max_size = 0.3f;
private const int vertical_margin = 25;
@@ -29,12 +34,27 @@ namespace osu.Game.Screens.Play
set
{
breaks = value;
- initializeBreaks();
+
+ // reset index in case the new breaks list is smaller than last one
+ isBreakTime.Value = false;
+ CurrentBreakIndex = 0;
+
+ if (IsLoaded)
+ initializeBreaks();
}
}
public override bool RemoveCompletedTransforms => false;
+ ///
+ /// Whether the gameplay is currently in a break.
+ ///
+ public IBindable IsBreakTime => isBreakTime;
+
+ protected int CurrentBreakIndex;
+
+ private readonly BindableBool isBreakTime = new BindableBool();
+
private readonly Container remainingTimeAdjustmentBox;
private readonly Container remainingTimeBox;
private readonly RemainingTimeCounter remainingTimeCounter;
@@ -109,10 +129,36 @@ namespace osu.Game.Screens.Play
initializeBreaks();
}
+ protected override void Update()
+ {
+ base.Update();
+ updateBreakTimeBindable();
+ }
+
+ private void updateBreakTimeBindable()
+ {
+ if (breaks == null || breaks.Count == 0)
+ return;
+
+ var time = Clock.CurrentTime;
+
+ if (time > breaks[CurrentBreakIndex].EndTime)
+ {
+ while (time > breaks[CurrentBreakIndex].EndTime && CurrentBreakIndex < breaks.Count - 1)
+ CurrentBreakIndex++;
+ }
+ else
+ {
+ while (time < breaks[CurrentBreakIndex].StartTime && CurrentBreakIndex > 0)
+ CurrentBreakIndex--;
+ }
+
+ var currentBreak = breaks[CurrentBreakIndex];
+ isBreakTime.Value = currentBreak.HasEffect && currentBreak.Contains(time);
+ }
+
private void initializeBreaks()
{
- if (!IsLoaded) return; // we need a clock.
-
FinishTransforms(true);
Scheduler.CancelDelayedTasks();
@@ -125,25 +171,25 @@ namespace osu.Game.Screens.Play
using (BeginAbsoluteSequence(b.StartTime, true))
{
- fadeContainer.FadeIn(fade_duration);
- breakArrows.Show(fade_duration);
+ fadeContainer.FadeIn(BREAK_FADE_DURATION);
+ breakArrows.Show(BREAK_FADE_DURATION);
remainingTimeAdjustmentBox
- .ResizeWidthTo(remaining_time_container_max_size, fade_duration, Easing.OutQuint)
- .Delay(b.Duration - fade_duration)
+ .ResizeWidthTo(remaining_time_container_max_size, BREAK_FADE_DURATION, Easing.OutQuint)
+ .Delay(b.Duration - BREAK_FADE_DURATION)
.ResizeWidthTo(0);
remainingTimeBox
- .ResizeWidthTo(0, b.Duration - fade_duration)
+ .ResizeWidthTo(0, b.Duration - BREAK_FADE_DURATION)
.Then()
.ResizeWidthTo(1);
remainingTimeCounter.CountTo(b.Duration).CountTo(0, b.Duration);
- using (BeginDelayedSequence(b.Duration - fade_duration, true))
+ using (BeginDelayedSequence(b.Duration - BREAK_FADE_DURATION, true))
{
- fadeContainer.FadeOut(fade_duration);
- breakArrows.Hide(fade_duration);
+ fadeContainer.FadeOut(BREAK_FADE_DURATION);
+ breakArrows.Hide(BREAK_FADE_DURATION);
}
}
}
diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index 96b8073d11..e7398be176 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -66,6 +66,8 @@ namespace osu.Game.Screens.Play
private SampleChannel sampleRestart;
+ private BreakOverlay breakOverlay;
+
protected ScoreProcessor ScoreProcessor { get; private set; }
protected DrawableRuleset DrawableRuleset { get; private set; }
@@ -134,7 +136,7 @@ namespace osu.Game.Screens.Play
}
}
},
- new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor)
+ breakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -411,8 +413,7 @@ namespace osu.Game.Screens.Play
PauseOverlay.Hide();
// breaks and time-based conditions may allow instant resume.
- double time = GameplayClockContainer.GameplayClock.CurrentTime;
- if (Beatmap.Value.Beatmap.Breaks.Any(b => b.Contains(time)) || time < Beatmap.Value.Beatmap.HitObjects.First().StartTime)
+ if (breakOverlay.IsBreakTime.Value || GameplayClockContainer.GameplayClock.CurrentTime < Beatmap.Value.Beatmap.HitObjects.First().StartTime)
completeResume();
else
DrawableRuleset.RequestResume(completeResume);
diff --git a/osu.Game/Screens/Play/SquareGraph.cs b/osu.Game/Screens/Play/SquareGraph.cs
index 5b7a9574b6..9c56725c4e 100644
--- a/osu.Game/Screens/Play/SquareGraph.cs
+++ b/osu.Game/Screens/Play/SquareGraph.cs
@@ -75,7 +75,7 @@ namespace osu.Game.Screens.Play
return base.Invalidate(invalidation, source, shallPropagate);
}
- private Cached layout = new Cached();
+ private readonly Cached layout = new Cached();
private ScheduledDelegate scheduledCreate;
protected override void Update()
diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs
index cf88b697d9..7366fa8c17 100644
--- a/osu.Game/Screens/Select/BeatmapCarousel.cs
+++ b/osu.Game/Screens/Select/BeatmapCarousel.cs
@@ -93,8 +93,8 @@ namespace osu.Game.Screens.Select
}
private readonly List yPositions = new List();
- private Cached itemsCache = new Cached();
- private Cached scrollPositionCache = new Cached();
+ private readonly Cached itemsCache = new Cached();
+ private readonly Cached scrollPositionCache = new Cached();
private readonly Container scrollableContent;
diff --git a/osu.Game/Storyboards/CommandTimeline.cs b/osu.Game/Storyboards/CommandTimeline.cs
index aa1f137cf3..bcf642b4ea 100644
--- a/osu.Game/Storyboards/CommandTimeline.cs
+++ b/osu.Game/Storyboards/CommandTimeline.cs
@@ -15,10 +15,10 @@ namespace osu.Game.Storyboards
public IEnumerable Commands => commands.OrderBy(c => c.StartTime);
public bool HasCommands => commands.Count > 0;
- private Cached startTimeBacking;
+ private readonly Cached startTimeBacking = new Cached();
public double StartTime => startTimeBacking.IsValid ? startTimeBacking : startTimeBacking.Value = HasCommands ? commands.Min(c => c.StartTime) : double.MinValue;
- private Cached endTimeBacking;
+ private readonly Cached endTimeBacking = new Cached();
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;
diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs
index b738eff4a6..a5f3578711 100644
--- a/osu.Game/Users/User.cs
+++ b/osu.Game/Users/User.cs
@@ -187,6 +187,7 @@ namespace osu.Game.Users
public static readonly User SYSTEM_USER = new User
{
Username = "system",
+ Colour = @"9c0101",
Id = 0
};
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 02bf053468..1dd19ac7ed 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -14,8 +14,8 @@
-
-
+
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 5b43a6f46f..1c3faeed39 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -104,9 +104,9 @@
-
-
-
+
+
+