diff --git a/osu-framework b/osu-framework index 42ec8a62fb..51737ec132 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 42ec8a62fb697f1d967a927549dc51336cd66968 +Subproject commit 51737ec1320b4ea9dce4978f24e1d77a28f0a98e diff --git a/osu.Desktop.Deploy/Program.cs b/osu.Desktop.Deploy/Program.cs index 00ec215e8a..37776b329f 100644 --- a/osu.Desktop.Deploy/Program.cs +++ b/osu.Desktop.Deploy/Program.cs @@ -53,7 +53,7 @@ namespace osu.Desktop.Deploy private static string nupkgFilename(string ver) => $"{PackageName}.{ver}.nupkg"; private static string nupkgDistroFilename(string ver) => $"{PackageName}-{ver}-full.nupkg"; - private static Stopwatch sw = new Stopwatch(); + private static readonly Stopwatch sw = new Stopwatch(); private static string codeSigningPassword; diff --git a/osu.Desktop.VisualTests/Beatmaps/TestWorkingBeatmap.cs b/osu.Desktop.VisualTests/Beatmaps/TestWorkingBeatmap.cs index babca8ee06..f135a6affa 100644 --- a/osu.Desktop.VisualTests/Beatmaps/TestWorkingBeatmap.cs +++ b/osu.Desktop.VisualTests/Beatmaps/TestWorkingBeatmap.cs @@ -15,7 +15,7 @@ namespace osu.Desktop.VisualTests.Beatmaps this.beatmap = beatmap; } - private Beatmap beatmap; + private readonly Beatmap beatmap; protected override Beatmap GetBeatmap() => beatmap; protected override Texture GetBackground() => null; diff --git a/osu.Desktop.VisualTests/Benchmark.cs b/osu.Desktop.VisualTests/Benchmark.cs index 3847b6f00f..0a15b38fc2 100644 --- a/osu.Desktop.VisualTests/Benchmark.cs +++ b/osu.Desktop.VisualTests/Benchmark.cs @@ -10,7 +10,7 @@ namespace osu.Desktop.VisualTests { public class Benchmark : OsuGameBase { - private double timePerTest = 200; + private const double time_per_test = 200; [BackgroundDependencyLoader] private void load() @@ -27,7 +27,7 @@ namespace osu.Desktop.VisualTests TestBrowser f = new TestBrowser(); Add(f); - Console.WriteLine($@"{Time}: Running {f.TestCount} tests for {timePerTest}ms each..."); + Console.WriteLine($@"{Time}: Running {f.TestCount} tests for {time_per_test}ms each..."); for (int i = 1; i < f.TestCount; i++) { @@ -36,10 +36,10 @@ namespace osu.Desktop.VisualTests { f.LoadTest(loadableCase); Console.WriteLine($@"{Time}: Switching to test #{loadableCase}"); - }, loadableCase * timePerTest); + }, loadableCase * time_per_test); } - Scheduler.AddDelayed(Host.Exit, f.TestCount * timePerTest); + Scheduler.AddDelayed(Host.Exit, f.TestCount * time_per_test); } } } diff --git a/osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetailArea.cs b/osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetailArea.cs new file mode 100644 index 0000000000..bb7df19202 --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCaseBeatmapDetailArea.cs @@ -0,0 +1,27 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.Screens.Testing; +using osu.Game.Screens.Select; + +namespace osu.Desktop.VisualTests.Tests +{ + internal class TestCaseBeatmapDetailArea : TestCase + { + public override string Description => @"Beatmap details in song select"; + + public override void Reset() + { + base.Reset(); + + Add(new BeatmapDetailArea + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(550f, 450f), + }); + } + } +} \ No newline at end of file diff --git a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs b/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs index eeebc7faa8..2a20cad2db 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs @@ -21,7 +21,7 @@ namespace osu.Desktop.VisualTests.Tests { internal class TestCaseHitObjects : TestCase { - private FramedClock framedClock; + private readonly FramedClock framedClock; private bool auto; @@ -34,7 +34,7 @@ namespace osu.Desktop.VisualTests.Tests private HitObjectType mode = HitObjectType.Slider; - private BindableNumber playbackSpeed = new BindableDouble(0.5) { MinValue = 0, MaxValue = 1 }; + private readonly BindableNumber playbackSpeed = new BindableDouble(0.5) { MinValue = 0, MaxValue = 1 }; private Container playfieldContainer; private Container approachContainer; @@ -136,7 +136,7 @@ namespace osu.Desktop.VisualTests.Tests if (auto) { h.State = ArmedState.Hit; - h.Judgement = new OsuJudgementInfo { Result = HitResult.Hit }; + h.Judgement = new OsuJudgement { Result = HitResult.Hit }; } playfieldContainer.Add(h); diff --git a/osu.Desktop.VisualTests/Tests/TestCaseLeaderboard.cs b/osu.Desktop.VisualTests/Tests/TestCaseLeaderboard.cs index cc30e3de95..329d5c5687 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseLeaderboard.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseLeaderboard.cs @@ -4,9 +4,9 @@ using OpenTK; using osu.Framework.Graphics; using osu.Framework.Screens.Testing; -using osu.Game.Modes; using osu.Game.Modes.Mods; using osu.Game.Modes.Osu.Mods; +using osu.Game.Modes.Scoring; using osu.Game.Screens.Select.Leaderboards; using osu.Game.Users; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs b/osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs index c3a9064e69..13f89153e9 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs @@ -95,7 +95,7 @@ namespace osu.Desktop.VisualTests.Tests progressingNotifications.Add(n); } - private List progressingNotifications = new List(); + private readonly List progressingNotifications = new List(); private void sendProgress1() { diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs b/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs new file mode 100644 index 0000000000..395a0cab13 --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs @@ -0,0 +1,77 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.MathUtils; +using osu.Framework.Screens.Testing; +using osu.Game.Modes.Objects.Drawables; +using osu.Game.Modes.Taiko.Judgements; +using osu.Game.Modes.Taiko.Objects; +using osu.Game.Modes.Taiko.UI; + +namespace osu.Desktop.VisualTests.Tests +{ + internal class TestCaseTaikoPlayfield : TestCase + { + public override string Description => "Taiko playfield"; + + private TaikoPlayfield playfield; + + public override void Reset() + { + base.Reset(); + + AddButton("Hit!", addHitJudgement); + AddButton("Miss :(", addMissJudgement); + + Add(playfield = new TaikoPlayfield + { + Y = 200 + }); + } + + private void addHitJudgement() + { + TaikoHitResult hitResult = RNG.Next(2) == 0 ? TaikoHitResult.Good : TaikoHitResult.Great; + + playfield.OnJudgement(new DrawableTestHit(new Hit()) + { + X = RNG.NextSingle(hitResult == TaikoHitResult.Good ? -0.1f : -0.05f, hitResult == TaikoHitResult.Good ? 0.1f : 0.05f), + Judgement = new TaikoJudgement + { + Result = HitResult.Hit, + TaikoResult = hitResult, + TimeOffset = 0, + ComboAtHit = 1, + SecondHit = RNG.Next(10) == 0 + } + }); + } + + private void addMissJudgement() + { + playfield.OnJudgement(new DrawableTestHit(new Hit()) + { + Judgement = new TaikoJudgement + { + Result = HitResult.Miss, + TimeOffset = 0, + ComboAtHit = 0 + } + }); + } + + private class DrawableTestHit : DrawableHitObject + { + public DrawableTestHit(TaikoHitObject hitObject) + : base(hitObject) + { + } + + protected override TaikoJudgement CreateJudgement() => new TaikoJudgement(); + + protected override void UpdateState(ArmedState state) + { + } + } + } +} diff --git a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj index b67b4c4bb3..e63306300d 100644 --- a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj +++ b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj @@ -23,7 +23,7 @@ false LocalIntranet v4.5 - true + true publish\ true Disk @@ -194,6 +194,7 @@ + @@ -206,6 +207,7 @@ + diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index da3e7b704a..95870125e3 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -17,7 +17,7 @@ namespace osu.Desktop { internal class OsuGameDesktop : OsuGame { - private VersionManager versionManager; + private readonly VersionManager versionManager; public OsuGameDesktop(string[] args = null) : base(args) diff --git a/osu.Game.Modes.Catch/CatchRuleset.cs b/osu.Game.Modes.Catch/CatchRuleset.cs index 50224e3fdb..09d8bdb9e5 100644 --- a/osu.Game.Modes.Catch/CatchRuleset.cs +++ b/osu.Game.Modes.Catch/CatchRuleset.cs @@ -10,6 +10,8 @@ using osu.Game.Modes.Mods; using osu.Game.Modes.UI; using osu.Game.Screens.Play; using System.Collections.Generic; +using osu.Game.Modes.Catch.Scoring; +using osu.Game.Modes.Scoring; namespace osu.Game.Modes.Catch { diff --git a/osu.Game.Modes.Catch/Judgements/CatchJudgementInfo.cs b/osu.Game.Modes.Catch/Judgements/CatchJudgement.cs similarity index 57% rename from osu.Game.Modes.Catch/Judgements/CatchJudgementInfo.cs rename to osu.Game.Modes.Catch/Judgements/CatchJudgement.cs index 33e84d2f97..eaacedd7e0 100644 --- a/osu.Game.Modes.Catch/Judgements/CatchJudgementInfo.cs +++ b/osu.Game.Modes.Catch/Judgements/CatchJudgement.cs @@ -5,7 +5,10 @@ using osu.Game.Modes.Judgements; namespace osu.Game.Modes.Catch.Judgements { - public class CatchJudgementInfo : JudgementInfo + public class CatchJudgement : Judgement { + public override string ResultString => string.Empty; + + public override string MaxResultString => string.Empty; } } diff --git a/osu.Game.Modes.Catch/Objects/Drawable/DrawableFruit.cs b/osu.Game.Modes.Catch/Objects/Drawable/DrawableFruit.cs index 5d19d902b1..885048b938 100644 --- a/osu.Game.Modes.Catch/Objects/Drawable/DrawableFruit.cs +++ b/osu.Game.Modes.Catch/Objects/Drawable/DrawableFruit.cs @@ -12,7 +12,7 @@ namespace osu.Game.Modes.Catch.Objects.Drawable { internal class DrawableFruit : Sprite { - private CatchBaseHit h; + private readonly CatchBaseHit h; public DrawableFruit(CatchBaseHit h) { @@ -29,7 +29,7 @@ namespace osu.Game.Modes.Catch.Objects.Drawable { Texture = textures.Get(@"Menu/logo"); - double duration = 0; + const double duration = 0; Transforms.Add(new TransformPosition { StartTime = h.StartTime - 200, EndTime = h.StartTime, StartValue = new Vector2(h.Position, -0.1f), EndValue = new Vector2(h.Position, 0.9f) }); Transforms.Add(new TransformAlpha { StartTime = h.StartTime + duration + 200, EndTime = h.StartTime + duration + 400, StartValue = 1, EndValue = 0 }); diff --git a/osu.Game.Modes.Catch/CatchScoreProcessor.cs b/osu.Game.Modes.Catch/Scoring/CatchScoreProcessor.cs similarity index 71% rename from osu.Game.Modes.Catch/CatchScoreProcessor.cs rename to osu.Game.Modes.Catch/Scoring/CatchScoreProcessor.cs index be4ae8b799..766a492bf4 100644 --- a/osu.Game.Modes.Catch/CatchScoreProcessor.cs +++ b/osu.Game.Modes.Catch/Scoring/CatchScoreProcessor.cs @@ -3,22 +3,23 @@ using osu.Game.Modes.Catch.Judgements; using osu.Game.Modes.Catch.Objects; +using osu.Game.Modes.Scoring; using osu.Game.Modes.UI; -namespace osu.Game.Modes.Catch +namespace osu.Game.Modes.Catch.Scoring { - internal class CatchScoreProcessor : ScoreProcessor + internal class CatchScoreProcessor : ScoreProcessor { public CatchScoreProcessor() { } - public CatchScoreProcessor(HitRenderer hitRenderer) + public CatchScoreProcessor(HitRenderer hitRenderer) : base(hitRenderer) { } - protected override void UpdateCalculations(CatchJudgementInfo newJudgement) + protected override void OnNewJugement(CatchJudgement judgement) { } } diff --git a/osu.Game.Modes.Catch/UI/CatchHitRenderer.cs b/osu.Game.Modes.Catch/UI/CatchHitRenderer.cs index c02b6b6c49..90bd61a39f 100644 --- a/osu.Game.Modes.Catch/UI/CatchHitRenderer.cs +++ b/osu.Game.Modes.Catch/UI/CatchHitRenderer.cs @@ -5,12 +5,14 @@ using osu.Game.Beatmaps; using osu.Game.Modes.Catch.Beatmaps; using osu.Game.Modes.Catch.Judgements; using osu.Game.Modes.Catch.Objects; +using osu.Game.Modes.Catch.Scoring; using osu.Game.Modes.Objects.Drawables; +using osu.Game.Modes.Scoring; using osu.Game.Modes.UI; namespace osu.Game.Modes.Catch.UI { - public class CatchHitRenderer : HitRenderer + public class CatchHitRenderer : HitRenderer { public CatchHitRenderer(WorkingBeatmap beatmap) : base(beatmap) @@ -23,8 +25,8 @@ namespace osu.Game.Modes.Catch.UI protected override IBeatmapProcessor CreateBeatmapProcessor() => new CatchBeatmapProcessor(); - protected override Playfield CreatePlayfield() => new CatchPlayfield(); + protected override Playfield CreatePlayfield() => new CatchPlayfield(); - protected override DrawableHitObject GetVisualRepresentation(CatchBaseHit h) => null; + protected override DrawableHitObject GetVisualRepresentation(CatchBaseHit h) => null; } } diff --git a/osu.Game.Modes.Catch/UI/CatchPlayfield.cs b/osu.Game.Modes.Catch/UI/CatchPlayfield.cs index eba8734eaf..cf1a665470 100644 --- a/osu.Game.Modes.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Modes.Catch/UI/CatchPlayfield.cs @@ -10,7 +10,7 @@ using osu.Game.Modes.Catch.Judgements; namespace osu.Game.Modes.Catch.UI { - public class CatchPlayfield : Playfield + public class CatchPlayfield : Playfield { public CatchPlayfield() { diff --git a/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj b/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj index a32416173d..593d8db4f6 100644 --- a/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj +++ b/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj @@ -50,8 +50,8 @@ - - + + diff --git a/osu.Game.Modes.Mania/Judgements/ManiaJudgementInfo.cs b/osu.Game.Modes.Mania/Judgements/ManiaJudgement.cs similarity index 57% rename from osu.Game.Modes.Mania/Judgements/ManiaJudgementInfo.cs rename to osu.Game.Modes.Mania/Judgements/ManiaJudgement.cs index a75f95abe7..3ef5b0f29b 100644 --- a/osu.Game.Modes.Mania/Judgements/ManiaJudgementInfo.cs +++ b/osu.Game.Modes.Mania/Judgements/ManiaJudgement.cs @@ -5,7 +5,10 @@ using osu.Game.Modes.Judgements; namespace osu.Game.Modes.Mania.Judgements { - public class ManiaJudgementInfo : JudgementInfo + public class ManiaJudgement : Judgement { + public override string ResultString => string.Empty; + + public override string MaxResultString => string.Empty; } } diff --git a/osu.Game.Modes.Mania/ManiaRuleset.cs b/osu.Game.Modes.Mania/ManiaRuleset.cs index 27b3fcdf60..bd995d87d6 100644 --- a/osu.Game.Modes.Mania/ManiaRuleset.cs +++ b/osu.Game.Modes.Mania/ManiaRuleset.cs @@ -9,6 +9,8 @@ using osu.Game.Modes.Mods; using osu.Game.Modes.UI; using osu.Game.Screens.Play; using System.Collections.Generic; +using osu.Game.Modes.Mania.Scoring; +using osu.Game.Modes.Scoring; namespace osu.Game.Modes.Mania { diff --git a/osu.Game.Modes.Mania/Objects/Drawable/DrawableNote.cs b/osu.Game.Modes.Mania/Objects/Drawable/DrawableNote.cs index 9fd39e2f45..76999cef21 100644 --- a/osu.Game.Modes.Mania/Objects/Drawable/DrawableNote.cs +++ b/osu.Game.Modes.Mania/Objects/Drawable/DrawableNote.cs @@ -26,7 +26,7 @@ namespace osu.Game.Modes.Mania.Objects.Drawable { Texture = textures.Get(@"Menu/logo"); - double duration = 0; + const double duration = 0; Transforms.Add(new TransformPositionY { StartTime = note.StartTime - 200, EndTime = note.StartTime, StartValue = -0.1f, EndValue = 0.9f }); Transforms.Add(new TransformAlpha { StartTime = note.StartTime + duration + 200, EndTime = note.StartTime + duration + 400, StartValue = 1, EndValue = 0 }); diff --git a/osu.Game.Modes.Mania/ManiaScoreProcessor.cs b/osu.Game.Modes.Mania/Scoring/ManiaScoreProcessor.cs similarity index 71% rename from osu.Game.Modes.Mania/ManiaScoreProcessor.cs rename to osu.Game.Modes.Mania/Scoring/ManiaScoreProcessor.cs index 60ed336c8e..c6b223af6d 100644 --- a/osu.Game.Modes.Mania/ManiaScoreProcessor.cs +++ b/osu.Game.Modes.Mania/Scoring/ManiaScoreProcessor.cs @@ -3,22 +3,23 @@ using osu.Game.Modes.Mania.Judgements; using osu.Game.Modes.Mania.Objects; +using osu.Game.Modes.Scoring; using osu.Game.Modes.UI; -namespace osu.Game.Modes.Mania +namespace osu.Game.Modes.Mania.Scoring { - internal class ManiaScoreProcessor : ScoreProcessor + internal class ManiaScoreProcessor : ScoreProcessor { public ManiaScoreProcessor() { } - public ManiaScoreProcessor(HitRenderer hitRenderer) + public ManiaScoreProcessor(HitRenderer hitRenderer) : base(hitRenderer) { } - protected override void UpdateCalculations(ManiaJudgementInfo newJudgement) + protected override void OnNewJugement(ManiaJudgement judgement) { } } diff --git a/osu.Game.Modes.Mania/UI/ManiaHitRenderer.cs b/osu.Game.Modes.Mania/UI/ManiaHitRenderer.cs index 0f26c944a0..0415bc961a 100644 --- a/osu.Game.Modes.Mania/UI/ManiaHitRenderer.cs +++ b/osu.Game.Modes.Mania/UI/ManiaHitRenderer.cs @@ -5,12 +5,14 @@ using osu.Game.Beatmaps; using osu.Game.Modes.Mania.Beatmaps; using osu.Game.Modes.Mania.Judgements; using osu.Game.Modes.Mania.Objects; +using osu.Game.Modes.Mania.Scoring; using osu.Game.Modes.Objects.Drawables; +using osu.Game.Modes.Scoring; using osu.Game.Modes.UI; namespace osu.Game.Modes.Mania.UI { - public class ManiaHitRenderer : HitRenderer + public class ManiaHitRenderer : HitRenderer { private readonly int columns; @@ -26,8 +28,8 @@ namespace osu.Game.Modes.Mania.UI protected override IBeatmapProcessor CreateBeatmapProcessor() => new ManiaBeatmapProcessor(); - protected override Playfield CreatePlayfield() => new ManiaPlayfield(columns); + protected override Playfield CreatePlayfield() => new ManiaPlayfield(columns); - protected override DrawableHitObject GetVisualRepresentation(ManiaBaseHit h) => null; + protected override DrawableHitObject GetVisualRepresentation(ManiaBaseHit h) => null; } } diff --git a/osu.Game.Modes.Mania/UI/ManiaPlayfield.cs b/osu.Game.Modes.Mania/UI/ManiaPlayfield.cs index 3984fce626..670d18f71f 100644 --- a/osu.Game.Modes.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Modes.Mania/UI/ManiaPlayfield.cs @@ -11,7 +11,7 @@ using osu.Game.Modes.Mania.Judgements; namespace osu.Game.Modes.Mania.UI { - public class ManiaPlayfield : Playfield + public class ManiaPlayfield : Playfield { public ManiaPlayfield(int columns) { diff --git a/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj b/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj index 8b78929510..cc925d417a 100644 --- a/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj +++ b/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj @@ -49,9 +49,9 @@ - + - + diff --git a/osu.Game.Modes.Osu/Beatmaps/OsuBeatmapConverter.cs b/osu.Game.Modes.Osu/Beatmaps/OsuBeatmapConverter.cs index ea5143b08a..fec675be54 100644 --- a/osu.Game.Modes.Osu/Beatmaps/OsuBeatmapConverter.cs +++ b/osu.Game.Modes.Osu/Beatmaps/OsuBeatmapConverter.cs @@ -44,11 +44,8 @@ namespace osu.Game.Modes.Osu.Beatmaps { StartTime = original.StartTime, Sample = original.Sample, - CurveObject = curveData, - Position = positionData?.Position ?? Vector2.Zero, - NewCombo = comboData?.NewCombo ?? false }; } @@ -60,7 +57,6 @@ namespace osu.Game.Modes.Osu.Beatmaps StartTime = original.StartTime, Sample = original.Sample, Position = new Vector2(512, 384) / 2, - EndTime = endTimeData.EndTime }; } @@ -69,9 +65,7 @@ namespace osu.Game.Modes.Osu.Beatmaps { StartTime = original.StartTime, Sample = original.Sample, - Position = positionData?.Position ?? Vector2.Zero, - NewCombo = comboData?.NewCombo ?? false }; } @@ -81,7 +75,7 @@ namespace osu.Game.Modes.Osu.Beatmaps if (endIndex == -1) endIndex = hitObjects.Count - 1; - int stackDistance = 3; + const int stack_distance = 3; float stackThreshold = DrawableOsuHitObject.TIME_PREEMPT * stackLeniency; // Reset stacking inside the update range @@ -108,8 +102,8 @@ namespace osu.Game.Modes.Osu.Beatmaps //We are no longer within stacking range of the next object. break; - if (Vector2.Distance(stackBaseObject.Position, objectN.Position) < stackDistance || - stackBaseObject is Slider && Vector2.Distance(stackBaseObject.EndPosition, objectN.Position) < stackDistance) + if (Vector2.Distance(stackBaseObject.Position, objectN.Position) < stack_distance || + stackBaseObject is Slider && Vector2.Distance(stackBaseObject.EndPosition, objectN.Position) < stack_distance) { stackBaseIndex = n; @@ -174,14 +168,14 @@ namespace osu.Game.Modes.Osu.Beatmaps * o <- hitCircle has stack of -1 * o <- hitCircle has stack of -2 */ - if (objectN is Slider && Vector2.Distance(objectN.EndPosition, objectI.Position) < stackDistance) + if (objectN is Slider && Vector2.Distance(objectN.EndPosition, objectI.Position) < stack_distance) { int offset = objectI.StackHeight - objectN.StackHeight + 1; for (int j = n + 1; j <= i; j++) { //For each object which was declared under this slider, we will offset it to appear *below* the slider end (rather than above). OsuHitObject objectJ = hitObjects[j]; - if (Vector2.Distance(objectN.EndPosition, objectJ.Position) < stackDistance) + if (Vector2.Distance(objectN.EndPosition, objectJ.Position) < stack_distance) objectJ.StackHeight -= offset; } @@ -190,7 +184,7 @@ namespace osu.Game.Modes.Osu.Beatmaps break; } - if (Vector2.Distance(objectN.Position, objectI.Position) < stackDistance) + if (Vector2.Distance(objectN.Position, objectI.Position) < stack_distance) { //Keep processing as if there are no sliders. If we come across a slider, this gets cancelled out. //NOTE: Sliders with start positions stacking are a special case that is also handled here. @@ -214,7 +208,7 @@ namespace osu.Game.Modes.Osu.Beatmaps //We are no longer within stacking range of the previous object. break; - if (Vector2.Distance(objectN.EndPosition, objectI.Position) < stackDistance) + if (Vector2.Distance(objectN.EndPosition, objectI.Position) < stack_distance) { objectN.StackHeight = objectI.StackHeight + 1; objectI = objectN; diff --git a/osu.Game.Modes.Osu/Judgements/OsuJudgementInfo.cs b/osu.Game.Modes.Osu/Judgements/OsuJudgement.cs similarity index 82% rename from osu.Game.Modes.Osu/Judgements/OsuJudgementInfo.cs rename to osu.Game.Modes.Osu/Judgements/OsuJudgement.cs index 20d36efe55..e65d3dde3a 100644 --- a/osu.Game.Modes.Osu/Judgements/OsuJudgementInfo.cs +++ b/osu.Game.Modes.Osu/Judgements/OsuJudgement.cs @@ -4,10 +4,11 @@ using OpenTK; using osu.Game.Modes.Judgements; using osu.Game.Modes.Osu.Objects.Drawables; +using osu.Framework.Extensions; namespace osu.Game.Modes.Osu.Judgements { - public class OsuJudgementInfo : JudgementInfo + public class OsuJudgement : Judgement { /// /// The positional hit offset. @@ -24,6 +25,10 @@ namespace osu.Game.Modes.Osu.Judgements /// public OsuScoreResult MaxScore = OsuScoreResult.Hit300; + public override string ResultString => Score.GetDescription(); + + public override string MaxResultString => MaxScore.GetDescription(); + public int ScoreValue => scoreToInt(Score); public int MaxScoreValue => scoreToInt(MaxScore); diff --git a/osu.Game.Modes.Osu/Mods/OsuMod.cs b/osu.Game.Modes.Osu/Mods/OsuMod.cs index e80975aed1..db2ee26b7a 100644 --- a/osu.Game.Modes.Osu/Mods/OsuMod.cs +++ b/osu.Game.Modes.Osu/Mods/OsuMod.cs @@ -7,6 +7,7 @@ using osu.Game.Modes.Mods; using osu.Game.Modes.Osu.Objects; using System; using System.Linq; +using osu.Game.Modes.Scoring; namespace osu.Game.Modes.Osu.Mods { diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableHitCircle.cs index e8c74d4f8d..3ed3124e14 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -13,34 +13,30 @@ namespace osu.Game.Modes.Osu.Objects.Drawables { public class DrawableHitCircle : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach { - private OsuHitObject osuObject; - public ApproachCircle ApproachCircle; - private CirclePiece circle; - private RingPiece ring; - private FlashPiece flash; - private ExplodePiece explode; - private NumberPiece number; - private GlowPiece glow; + private readonly CirclePiece circle; + private readonly RingPiece ring; + private readonly FlashPiece flash; + private readonly ExplodePiece explode; + private readonly NumberPiece number; + private readonly GlowPiece glow; public DrawableHitCircle(OsuHitObject h) : base(h) { Origin = Anchor.Centre; - osuObject = h; - - Position = osuObject.StackedPosition; - Scale = new Vector2(osuObject.Scale); + Position = HitObject.StackedPosition; + Scale = new Vector2(HitObject.Scale); Children = new Drawable[] { glow = new GlowPiece { - Colour = osuObject.ComboColour + Colour = AccentColour }, circle = new CirclePiece { - Colour = osuObject.ComboColour, + Colour = AccentColour, Hit = () => { if (Judgement.Result.HasValue) return false; @@ -58,11 +54,11 @@ namespace osu.Game.Modes.Osu.Objects.Drawables flash = new FlashPiece(), explode = new ExplodePiece { - Colour = osuObject.ComboColour, + Colour = AccentColour, }, ApproachCircle = new ApproachCircle { - Colour = osuObject.ComboColour, + Colour = AccentColour, } }; @@ -115,8 +111,8 @@ namespace osu.Game.Modes.Osu.Objects.Drawables ApproachCircle.FadeOut(); - double endTime = (osuObject as IHasEndTime)?.EndTime ?? osuObject.StartTime; - double duration = endTime - osuObject.StartTime; + double endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; + double duration = endTime - HitObject.StartTime; glow.Delay(duration); glow.FadeOut(400); diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuHitObject.cs index b2af678cae..816faa0d98 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -7,18 +7,19 @@ using osu.Game.Modes.Osu.Judgements; namespace osu.Game.Modes.Osu.Objects.Drawables { - public class DrawableOsuHitObject : DrawableHitObject + public class DrawableOsuHitObject : DrawableHitObject { public const float TIME_PREEMPT = 600; public const float TIME_FADEIN = 400; public const float TIME_FADEOUT = 500; - public DrawableOsuHitObject(OsuHitObject hitObject) + protected DrawableOsuHitObject(OsuHitObject hitObject) : base(hitObject) { + AccentColour = HitObject.ComboColour; } - protected override OsuJudgementInfo CreateJudgementInfo() => new OsuJudgementInfo { MaxScore = OsuScoreResult.Hit300 }; + protected override OsuJudgement CreateJudgement() => new OsuJudgement { MaxScore = OsuScoreResult.Hit300 }; protected override void UpdateState(ArmedState state) { diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuJudgement.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuJudgement.cs new file mode 100644 index 0000000000..13937e3c39 --- /dev/null +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuJudgement.cs @@ -0,0 +1,26 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics.Transforms; +using osu.Game.Modes.Objects.Drawables; +using osu.Game.Modes.Osu.Judgements; +using OpenTK; +using osu.Game.Modes.Judgements; + +namespace osu.Game.Modes.Osu.Objects.Drawables +{ + public class DrawableOsuJudgement : DrawableJudgement + { + public DrawableOsuJudgement(OsuJudgement judgement) : base(judgement) + { + } + + protected override void LoadComplete() + { + if (Judgement.Result != HitResult.Miss) + JudgementText.TransformSpacingTo(new Vector2(14, 0), 1800, EasingTypes.OutQuint); + + base.LoadComplete(); + } + } +} \ No newline at end of file diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs index a2a52c7d94..e8f2154d7f 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs @@ -13,18 +13,18 @@ namespace osu.Game.Modes.Osu.Objects.Drawables { public class DrawableSlider : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach { - private Slider slider; + private readonly Slider slider; - private DrawableHitCircle initialCircle; + private readonly DrawableHitCircle initialCircle; - private List components = new List(); + private readonly List components = new List(); - private Container ticks; + private readonly Container ticks; - private SliderBody body; - private SliderBall ball; + private readonly SliderBody body; + private readonly SliderBall ball; - private SliderBouncer bouncer2; + private readonly SliderBouncer bouncer2; public DrawableSlider(Slider s) : base(s) { @@ -39,6 +39,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables { body = new SliderBody(s) { + AccentColour = AccentColour, Position = s.StackedPosition, PathWidth = s.Scale * 64, }, @@ -56,6 +57,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables ball = new SliderBall(s) { Scale = new Vector2(s.Scale), + AccentColour = AccentColour }, initialCircle = new DrawableHitCircle(new HitCircle { diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs index fff08b9f60..1c9f1e617c 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -18,7 +18,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables { public class DrawableSliderTick : DrawableOsuHitObject { - private SliderTick sliderTick; + private readonly SliderTick sliderTick; public double FadeInTime; public double FadeOutTime; @@ -27,7 +27,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables public override bool RemoveWhenNotAlive => false; - protected override OsuJudgementInfo CreateJudgementInfo() => new OsuJudgementInfo { MaxScore = OsuScoreResult.SliderTick }; + protected override OsuJudgement CreateJudgement() => new OsuJudgement { MaxScore = OsuScoreResult.SliderTick }; public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick) { @@ -48,7 +48,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables new Box { RelativeSizeAxes = Axes.Both, - Colour = sliderTick.ComboColour, + Colour = AccentColour, Alpha = 0.3f, } }; diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSpinner.cs index 8098e87b12..81bf9f0bf1 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSpinner.cs @@ -15,12 +15,12 @@ namespace osu.Game.Modes.Osu.Objects.Drawables { public class DrawableSpinner : DrawableOsuHitObject { - private Spinner spinner; + private readonly Spinner spinner; - private SpinnerDisc disc; - private SpinnerBackground background; - private Container circleContainer; - private DrawableHitCircle circle; + private readonly SpinnerDisc disc; + private readonly SpinnerBackground background; + private readonly Container circleContainer; + private readonly DrawableHitCircle circle; public DrawableSpinner(Spinner s) : base(s) { @@ -48,7 +48,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables Alpha = 0, Anchor = Anchor.Centre, Origin = Anchor.Centre, - DiscColour = s.ComboColour + DiscColour = AccentColour }, circleContainer = new Container { @@ -108,9 +108,9 @@ namespace osu.Game.Modes.Osu.Objects.Drawables private Vector2 scaleToCircle => circle.Scale * circle.DrawWidth / DrawWidth * 0.95f; - private float spinsPerMinuteNeeded = 100 + 5 * 15; //TODO: read per-map OD and place it on the 5 + private const float spins_per_minute_needed = 100 + 5 * 15; //TODO: read per-map OD and place it on the 5 - private float rotationsNeeded => (float)(spinsPerMinuteNeeded * (spinner.EndTime - spinner.StartTime) / 60000f); + private float rotationsNeeded => (float)(spins_per_minute_needed * (spinner.EndTime - spinner.StartTime) / 60000f); public float Progress => MathHelper.Clamp(disc.RotationAbsolute / 360 / rotationsNeeded, 0, 1); diff --git a/osu.Game.Modes.Osu/Objects/Drawables/HitExplosion.cs b/osu.Game.Modes.Osu/Objects/Drawables/HitExplosion.cs deleted file mode 100644 index ab34d49ecf..0000000000 --- a/osu.Game.Modes.Osu/Objects/Drawables/HitExplosion.cs +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Transforms; -using osu.Game.Graphics.Sprites; -using osu.Game.Modes.Objects.Drawables; -using osu.Game.Modes.Osu.Judgements; -using OpenTK; -using OpenTK.Graphics; - -namespace osu.Game.Modes.Osu.Objects.Drawables -{ - public class HitExplosion : FillFlowContainer - { - private readonly OsuJudgementInfo judgement; - private SpriteText line1; - private SpriteText line2; - - public HitExplosion(OsuJudgementInfo judgement, OsuHitObject h = null) - { - this.judgement = judgement; - AutoSizeAxes = Axes.Both; - Origin = Anchor.Centre; - - Direction = FillDirection.Vertical; - Spacing = new Vector2(0, 2); - Position = (h?.StackedEndPosition ?? Vector2.Zero) + judgement.PositionOffset; - - Children = new Drawable[] - { - line1 = new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Text = judgement.Score.GetDescription(), - Font = @"Venera", - TextSize = 16, - }, - line2 = new OsuSpriteText - { - Text = judgement.Combo.GetDescription(), - Font = @"Venera", - TextSize = 11, - } - }; - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - if (judgement.Result == HitResult.Miss) - { - FadeInFromZero(60); - - ScaleTo(1.6f); - ScaleTo(1, 100, EasingTypes.In); - - MoveToOffset(new Vector2(0, 100), 800, EasingTypes.InQuint); - RotateTo(40, 800, EasingTypes.InQuint); - - Delay(600); - FadeOut(200); - } - else - { - line1.TransformSpacingTo(new Vector2(14, 0), 1800, EasingTypes.OutQuint); - line2.TransformSpacingTo(new Vector2(14, 0), 1800, EasingTypes.OutQuint); - FadeOut(500); - } - - switch (judgement.Result) - { - case HitResult.Miss: - Colour = Color4.Red; - break; - } - - Expire(); - } - } -} \ No newline at end of file diff --git a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/ApproachCircle.cs b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/ApproachCircle.cs index b0b1e81fca..fd4ef64350 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/ApproachCircle.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/ApproachCircle.cs @@ -11,7 +11,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces { public class ApproachCircle : Container { - private Sprite approachCircle; + private readonly Sprite approachCircle; public ApproachCircle() { diff --git a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/CirclePiece.cs index 2a503e3dec..704a6b7490 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/CirclePiece.cs @@ -14,7 +14,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces { public class CirclePiece : Container { - private Sprite disc; + private readonly Sprite disc; public Func Hit; diff --git a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/GlowPiece.cs b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/GlowPiece.cs index 1a16eae48c..6cffa370cf 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/GlowPiece.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/GlowPiece.cs @@ -11,7 +11,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces { public class GlowPiece : Container { - private Sprite layer; + private readonly Sprite layer; public GlowPiece() { diff --git a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/NumberPiece.cs b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/NumberPiece.cs index 750f203a8d..0ebd274246 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/NumberPiece.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/NumberPiece.cs @@ -12,7 +12,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces { public class NumberPiece : Container { - private SpriteText number; + private readonly SpriteText number; public string Text { diff --git a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBall.cs index 0b1a3aa751..73a01dfce2 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -12,11 +12,27 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces { public class SliderBall : CircularContainer, ISliderProgress { - private readonly Slider slider; - private Box follow; - private const float width = 128; + private Color4 accentColour = Color4.Black; + /// + /// The colour that is used for the slider ball. + /// + public Color4 AccentColour + { + get { return accentColour; } + set + { + accentColour = value; + if (ball != null) + ball.Colour = value; + } + } + + private readonly Slider slider; + private readonly Box follow; + private readonly Box ball; + public SliderBall(Slider slider) { this.slider = slider; @@ -49,9 +65,9 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces Alpha = 1, Children = new[] { - new Box + ball = new Box { - Colour = slider.ComboColour, + Colour = AccentColour, Alpha = 0.4f, Width = width, Height = width, diff --git a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBody.cs index b923d05713..e7837471ee 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBody.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBody.cs @@ -13,27 +13,45 @@ using osu.Framework.Graphics.Textures; using osu.Game.Configuration; using OpenTK; using OpenTK.Graphics.ES30; +using OpenTK.Graphics; namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces { public class SliderBody : Container, ISliderProgress { - private Path path; - private BufferedContainer container; + private readonly Path path; + private readonly BufferedContainer container; public float PathWidth { get { return path.PathWidth; } - set - { - path.PathWidth = value; - } + set { path.PathWidth = value; } } public double? SnakedStart { get; private set; } public double? SnakedEnd { get; private set; } - private Slider slider; + private Color4 accentColour; + /// + /// Used to colour the path. + /// + public Color4 AccentColour + { + get { return accentColour; } + set + { + if (accentColour == value) + return; + accentColour = value; + + if (LoadState == LoadState.Loaded) + Schedule(reloadTexture); + } + } + + private int textureWidth => (int)PathWidth * 2; + + private readonly Slider slider; public SliderBody(Slider s) { slider = s; @@ -82,7 +100,12 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces snakingIn = config.GetBindable(OsuConfig.SnakingInSliders); snakingOut = config.GetBindable(OsuConfig.SnakingOutSliders); - int textureWidth = (int)PathWidth * 2; + reloadTexture(); + } + + private void reloadTexture() + { + var texture = new Texture(textureWidth, 1); //initialise background var upload = new TextureUpload(textureWidth * 4); @@ -110,19 +133,18 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces { progress -= border_portion; - bytes[i * 4] = (byte)(slider.ComboColour.R * 255); - bytes[i * 4 + 1] = (byte)(slider.ComboColour.G * 255); - bytes[i * 4 + 2] = (byte)(slider.ComboColour.B * 255); - bytes[i * 4 + 3] = (byte)((opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * (slider.ComboColour.A * 255)); + bytes[i * 4] = (byte)(AccentColour.R * 255); + bytes[i * 4 + 1] = (byte)(AccentColour.G * 255); + bytes[i * 4 + 2] = (byte)(AccentColour.B * 255); + bytes[i * 4 + 3] = (byte)((opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * (AccentColour.A * 255)); } } - var texture = new Texture(textureWidth, 1); texture.SetData(upload); path.Texture = texture; } - private List currentCurve = new List(); + private readonly List currentCurve = new List(); private bool updateSnaking(double p0, double p1) { if (SnakedStart == p0 && SnakedEnd == p1) return false; diff --git a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBouncer.cs b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBouncer.cs index 1a78e5d85f..196b9fb521 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBouncer.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBouncer.cs @@ -11,7 +11,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces { private readonly Slider slider; private readonly bool isEnd; - private TextAwesome icon; + private readonly TextAwesome icon; public SliderBouncer(Slider slider, bool isEnd) { diff --git a/osu.Game.Modes.Osu/Objects/OsuHitObject.cs b/osu.Game.Modes.Osu/Objects/OsuHitObject.cs index 2af669b4ef..fa422834db 100644 --- a/osu.Game.Modes.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Modes.Osu/Objects/OsuHitObject.cs @@ -36,7 +36,7 @@ namespace osu.Game.Modes.Osu.Objects public float Scale { get; set; } = 1; - public Color4 ComboColour { get; set; } + public Color4 ComboColour { get; set; } = Color4.Gray; public virtual bool NewCombo { get; set; } public int ComboIndex { get; set; } diff --git a/osu.Game.Modes.Osu/Objects/OsuHitObjectDifficulty.cs b/osu.Game.Modes.Osu/Objects/OsuHitObjectDifficulty.cs index 926e5132bd..322f6b077a 100644 --- a/osu.Game.Modes.Osu/Objects/OsuHitObjectDifficulty.cs +++ b/osu.Game.Modes.Osu/Objects/OsuHitObjectDifficulty.cs @@ -47,11 +47,11 @@ namespace osu.Game.Modes.Osu.Objects internal int MaxCombo = 1; - private float scalingFactor; + private readonly float scalingFactor; private float lazySliderLength; - private Vector2 startPosition; - private Vector2 endPosition; + private readonly Vector2 startPosition; + private readonly Vector2 endPosition; internal OsuHitObjectDifficulty(OsuHitObject baseHitObject) { diff --git a/osu.Game.Modes.Osu/OsuAutoReplay.cs b/osu.Game.Modes.Osu/OsuAutoReplay.cs index 6b543cb98f..61b7466d86 100644 --- a/osu.Game.Modes.Osu/OsuAutoReplay.cs +++ b/osu.Game.Modes.Osu/OsuAutoReplay.cs @@ -20,7 +20,7 @@ namespace osu.Game.Modes.Osu private const float spin_radius = 50; - private Beatmap beatmap; + private readonly Beatmap beatmap; public OsuAutoReplay(Beatmap beatmap) { @@ -37,11 +37,11 @@ namespace osu.Game.Modes.Osu } } - private static IComparer replayFrameComparer = new LegacyReplayFrameComparer(); + private static readonly IComparer replay_frame_comparer = new LegacyReplayFrameComparer(); private int findInsertionIndex(LegacyReplayFrame frame) { - int index = Frames.BinarySearch(frame, replayFrameComparer); + int index = Frames.BinarySearch(frame, replay_frame_comparer); if (index < 0) { diff --git a/osu.Game.Modes.Osu/OsuRuleset.cs b/osu.Game.Modes.Osu/OsuRuleset.cs index bbaf7eed5f..12df7d3f3c 100644 --- a/osu.Game.Modes.Osu/OsuRuleset.cs +++ b/osu.Game.Modes.Osu/OsuRuleset.cs @@ -12,6 +12,8 @@ using osu.Game.Modes.UI; using osu.Game.Screens.Play; using System.Collections.Generic; using System.Linq; +using osu.Game.Modes.Osu.Scoring; +using osu.Game.Modes.Scoring; namespace osu.Game.Modes.Osu { diff --git a/osu.Game.Modes.Osu/OsuScore.cs b/osu.Game.Modes.Osu/Scoring/OsuScore.cs similarity index 72% rename from osu.Game.Modes.Osu/OsuScore.cs rename to osu.Game.Modes.Osu/Scoring/OsuScore.cs index dddf826887..a0a639a59e 100644 --- a/osu.Game.Modes.Osu/OsuScore.cs +++ b/osu.Game.Modes.Osu/Scoring/OsuScore.cs @@ -1,7 +1,9 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -namespace osu.Game.Modes.Osu +using osu.Game.Modes.Scoring; + +namespace osu.Game.Modes.Osu.Scoring { internal class OsuScore : Score { diff --git a/osu.Game.Modes.Osu/OsuScoreProcessor.cs b/osu.Game.Modes.Osu/Scoring/OsuScoreProcessor.cs similarity index 82% rename from osu.Game.Modes.Osu/OsuScoreProcessor.cs rename to osu.Game.Modes.Osu/Scoring/OsuScoreProcessor.cs index 50eb685ecd..b71e7dbadd 100644 --- a/osu.Game.Modes.Osu/OsuScoreProcessor.cs +++ b/osu.Game.Modes.Osu/Scoring/OsuScoreProcessor.cs @@ -4,17 +4,18 @@ using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Osu.Judgements; using osu.Game.Modes.Osu.Objects; +using osu.Game.Modes.Scoring; using osu.Game.Modes.UI; -namespace osu.Game.Modes.Osu +namespace osu.Game.Modes.Osu.Scoring { - internal class OsuScoreProcessor : ScoreProcessor + internal class OsuScoreProcessor : ScoreProcessor { public OsuScoreProcessor() { } - public OsuScoreProcessor(HitRenderer hitRenderer) + public OsuScoreProcessor(HitRenderer hitRenderer) : base(hitRenderer) { } @@ -27,7 +28,7 @@ namespace osu.Game.Modes.Osu Accuracy.Value = 1; } - protected override void UpdateCalculations(OsuJudgementInfo judgement) + protected override void OnNewJugement(OsuJudgement judgement) { if (judgement != null) { @@ -47,9 +48,8 @@ namespace osu.Game.Modes.Osu int score = 0; int maxScore = 0; - foreach (var judgementInfo in Judgements) + foreach (var j in Judgements) { - var j = judgementInfo; score += j.ScoreValue; maxScore += j.MaxScoreValue; } diff --git a/osu.Game.Modes.Osu/UI/OsuHitRenderer.cs b/osu.Game.Modes.Osu/UI/OsuHitRenderer.cs index 7a1e83d3a5..ca9ff6fc61 100644 --- a/osu.Game.Modes.Osu/UI/OsuHitRenderer.cs +++ b/osu.Game.Modes.Osu/UI/OsuHitRenderer.cs @@ -7,12 +7,14 @@ using osu.Game.Modes.Osu.Beatmaps; using osu.Game.Modes.Osu.Judgements; using osu.Game.Modes.Osu.Objects; using osu.Game.Modes.Osu.Objects.Drawables; +using osu.Game.Modes.Osu.Scoring; +using osu.Game.Modes.Scoring; using osu.Game.Modes.UI; using osu.Game.Screens.Play; namespace osu.Game.Modes.Osu.UI { - public class OsuHitRenderer : HitRenderer + public class OsuHitRenderer : HitRenderer { public OsuHitRenderer(WorkingBeatmap beatmap) : base(beatmap) @@ -25,11 +27,11 @@ namespace osu.Game.Modes.Osu.UI protected override IBeatmapProcessor CreateBeatmapProcessor() => new OsuBeatmapProcessor(); - protected override Playfield CreatePlayfield() => new OsuPlayfield(); + protected override Playfield CreatePlayfield() => new OsuPlayfield(); protected override KeyConversionInputManager CreateKeyConversionInputManager() => new OsuKeyConversionInputManager(); - protected override DrawableHitObject GetVisualRepresentation(OsuHitObject h) + protected override DrawableHitObject GetVisualRepresentation(OsuHitObject h) { var circle = h as HitCircle; if (circle != null) diff --git a/osu.Game.Modes.Osu/UI/OsuPlayfield.cs b/osu.Game.Modes.Osu/UI/OsuPlayfield.cs index 5caaaafb13..8090263fe1 100644 --- a/osu.Game.Modes.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Modes.Osu/UI/OsuPlayfield.cs @@ -15,11 +15,11 @@ using osu.Game.Modes.Osu.Judgements; namespace osu.Game.Modes.Osu.UI { - public class OsuPlayfield : Playfield + public class OsuPlayfield : Playfield { - private Container approachCircles; - private Container judgementLayer; - private ConnectionRenderer connectionLayer; + private readonly Container approachCircles; + private readonly Container judgementLayer; + private readonly ConnectionRenderer connectionLayer; public override Vector2 Size { @@ -65,7 +65,7 @@ namespace osu.Game.Modes.Osu.UI AddInternal(new GameplayCursor()); } - public override void Add(DrawableHitObject h) + public override void Add(DrawableHitObject h) { h.Depth = (float)h.HitObject.StartTime; @@ -83,9 +83,13 @@ namespace osu.Game.Modes.Osu.UI .OrderBy(h => h.StartTime); } - public override void OnJudgement(DrawableHitObject judgedObject) + public override void OnJudgement(DrawableHitObject judgedObject) { - HitExplosion explosion = new HitExplosion(judgedObject.Judgement, judgedObject.HitObject); + DrawableOsuJudgement explosion = new DrawableOsuJudgement(judgedObject.Judgement) + { + Origin = Anchor.Centre, + Position = judgedObject.HitObject.StackedEndPosition + judgedObject.Judgement.PositionOffset + }; judgementLayer.Add(explosion); } diff --git a/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj b/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj index 12135a38fb..55322e855e 100644 --- a/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj +++ b/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj @@ -48,7 +48,7 @@ - + @@ -58,7 +58,7 @@ - + @@ -72,8 +72,8 @@ - - + + diff --git a/osu.Game.Modes.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Modes.Taiko/Beatmaps/TaikoBeatmapConverter.cs index d78c347f22..b2676bf28a 100644 --- a/osu.Game.Modes.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Modes.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -2,18 +2,80 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Legacy; +using osu.Game.Beatmaps.Samples; +using osu.Game.Modes.Objects; +using osu.Game.Modes.Objects.Types; using osu.Game.Modes.Taiko.Objects; using System.Collections.Generic; +using System.Linq; namespace osu.Game.Modes.Taiko.Beatmaps { internal class TaikoBeatmapConverter : IBeatmapConverter { + private const float legacy_velocity_scale = 1.4f; + private const float bash_convert_factor = 1.65f; + public Beatmap Convert(Beatmap original) { + if (original is LegacyBeatmap) + original.TimingInfo.ControlPoints.ForEach(c => c.VelocityAdjustment /= legacy_velocity_scale); + return new Beatmap(original) { - HitObjects = new List() // Todo: Implement + HitObjects = convertHitObjects(original.HitObjects) + }; + } + + private List convertHitObjects(List hitObjects) + { + return hitObjects.Select(convertHitObject).ToList(); + } + + private TaikoHitObject convertHitObject(HitObject original) + { + // Check if this HitObject is already a TaikoHitObject, and return it if so + TaikoHitObject originalTaiko = original as TaikoHitObject; + if (originalTaiko != null) + return originalTaiko; + + IHasDistance distanceData = original as IHasDistance; + IHasRepeats repeatsData = original as IHasRepeats; + IHasEndTime endTimeData = original as IHasEndTime; + + bool accented = ((original.Sample?.Type ?? SampleType.None) & SampleType.Finish) > 0; + + if (distanceData != null) + { + return new DrumRoll + { + StartTime = original.StartTime, + Sample = original.Sample, + Accented = accented, + + Distance = distanceData.Distance * (repeatsData?.RepeatCount ?? 1) + }; + } + + if (endTimeData != null) + { + // We compute the end time manually to add in the Bash convert factor + return new Bash + { + StartTime = original.StartTime, + Sample = original.Sample, + Accented = accented, + + EndTime = original.StartTime + endTimeData.Duration * bash_convert_factor + }; + } + + return new Hit + { + StartTime = original.StartTime, + Sample = original.Sample, + Accented = accented }; } } diff --git a/osu.Game.Modes.Taiko/Judgements/TaikoDrumRollTickJudgement.cs b/osu.Game.Modes.Taiko/Judgements/TaikoDrumRollTickJudgement.cs new file mode 100644 index 0000000000..b6a727aeb4 --- /dev/null +++ b/osu.Game.Modes.Taiko/Judgements/TaikoDrumRollTickJudgement.cs @@ -0,0 +1,34 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Modes.Taiko.Judgements +{ + public class TaikoDrumRollTickJudgement : TaikoJudgement + { + /// + /// Drum roll ticks don't display judgement text. + /// + public override string ResultString => string.Empty; + + /// + /// Drum roll ticks don't display judgement text. + /// + public override string MaxResultString => string.Empty; + + protected override int NumericResultForScore(TaikoHitResult result) + { + switch (result) + { + default: + return 0; + case TaikoHitResult.Great: + return 200; + } + } + + protected override int NumericResultForAccuracy(TaikoHitResult result) + { + return 0; + } + } +} \ No newline at end of file diff --git a/osu.Game.Modes.Taiko/Judgements/TaikoHitResult.cs b/osu.Game.Modes.Taiko/Judgements/TaikoHitResult.cs index d425616b66..cbc3919c4f 100644 --- a/osu.Game.Modes.Taiko/Judgements/TaikoHitResult.cs +++ b/osu.Game.Modes.Taiko/Judgements/TaikoHitResult.cs @@ -1,11 +1,15 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.ComponentModel; + namespace osu.Game.Modes.Taiko.Judgements { public enum TaikoHitResult { + [Description("GOOD")] Good, + [Description("GREAT")] Great } } diff --git a/osu.Game.Modes.Taiko/Judgements/TaikoJudgementInfo.cs b/osu.Game.Modes.Taiko/Judgements/TaikoJudgement.cs similarity index 56% rename from osu.Game.Modes.Taiko/Judgements/TaikoJudgementInfo.cs rename to osu.Game.Modes.Taiko/Judgements/TaikoJudgement.cs index d9e81d4d77..e50a685e24 100644 --- a/osu.Game.Modes.Taiko/Judgements/TaikoJudgementInfo.cs +++ b/osu.Game.Modes.Taiko/Judgements/TaikoJudgement.cs @@ -2,40 +2,45 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Modes.Judgements; +using osu.Framework.Extensions; namespace osu.Game.Modes.Taiko.Judgements { - public class TaikoJudgementInfo : JudgementInfo + public class TaikoJudgement : Judgement { /// - /// The maximum score value. + /// The maximum result. /// public const TaikoHitResult MAX_HIT_RESULT = TaikoHitResult.Great; /// - /// The score value. + /// The result. /// public TaikoHitResult TaikoResult; /// - /// The score value for the combo portion of the score. + /// The result value for the combo portion of the score. /// - public int ScoreValue => NumericResultForScore(TaikoResult); + public int ResultValueForScore => NumericResultForScore(TaikoResult); /// - /// The score value for the accuracy portion of the score. + /// The result value for the accuracy portion of the score. /// - public int AccuracyScoreValue => NumericResultForAccuracy(TaikoResult); + public int ResultValueForAccuracy => NumericResultForAccuracy(TaikoResult); /// - /// The maximum score value for the combo portion of the score. + /// The maximum result value for the combo portion of the score. /// - public int MaxScoreValue => NumericResultForScore(MAX_HIT_RESULT); + public int MaxResultValueForScore => NumericResultForScore(MAX_HIT_RESULT); /// - /// The maximum score value for the accuracy portion of the score. + /// The maximum result value for the accuracy portion of the score. /// - public int MaxAccuracyScoreValue => NumericResultForAccuracy(MAX_HIT_RESULT); + public int MaxResultValueForAccuracy => NumericResultForAccuracy(MAX_HIT_RESULT); + + public override string ResultString => TaikoResult.GetDescription(); + + public override string MaxResultString => MAX_HIT_RESULT.GetDescription(); /// /// Whether this Judgement has a secondary hit in the case of finishers. @@ -43,11 +48,11 @@ namespace osu.Game.Modes.Taiko.Judgements public bool SecondHit; /// - /// Computes the numeric score value for the combo portion of the score. + /// Computes the numeric result value for the combo portion of the score. /// For the accuracy portion of the score (including accuracy percentage), see . /// - /// The result to compute the score value for. - /// The numeric score value. + /// The result to compute the value for. + /// The numeric result value. protected virtual int NumericResultForScore(TaikoHitResult result) { switch (result) @@ -62,11 +67,11 @@ namespace osu.Game.Modes.Taiko.Judgements } /// - /// Computes the numeric score value for the accuracy portion of the score. + /// Computes the numeric result value for the accuracy portion of the score. /// For the combo portion of the score, see . /// - /// The result to compute the score value for. - /// The numeric score value. + /// The result to compute the value for. + /// The numeric result value. protected virtual int NumericResultForAccuracy(TaikoHitResult result) { switch (result) diff --git a/osu.Game.Modes.Taiko/Objects/Bash.cs b/osu.Game.Modes.Taiko/Objects/Bash.cs index 1b771cb1d5..b8b4eea6a9 100644 --- a/osu.Game.Modes.Taiko/Objects/Bash.cs +++ b/osu.Game.Modes.Taiko/Objects/Bash.cs @@ -17,7 +17,7 @@ namespace osu.Game.Modes.Taiko.Objects /// /// The number of hits required to complete the bash successfully. /// - public int RequiredHits; + public int RequiredHits { get; protected set; } public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty) { diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHitObject.cs b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHitObject.cs index 1cd02e8292..c77c7762e3 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHitObject.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHitObject.cs @@ -1,40 +1,32 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK.Graphics; using osu.Framework.Graphics; using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Taiko.Judgements; namespace osu.Game.Modes.Taiko.Objects.Drawable { - public class DrawableTaikoHitObject : DrawableHitObject + public abstract class DrawableTaikoHitObject : DrawableHitObject { - /// - /// The colour used for various elements of this DrawableHitObject. - /// - public virtual Color4 AccentColour { get; } - - public DrawableTaikoHitObject(TaikoHitObject hitObject) + protected DrawableTaikoHitObject(TaikoHitObject hitObject) : base(hitObject) { Anchor = Anchor.CentreLeft; Origin = Anchor.Centre; RelativePositionAxes = Axes.X; + } + protected override void LoadComplete() + { LifetimeStart = HitObject.StartTime - HitObject.PreEmpt * 2; LifetimeEnd = HitObject.StartTime + HitObject.PreEmpt; - // Todo: Remove (suppresses Resharper) - AccentColour = Color4.White; + base.LoadComplete(); } - protected override TaikoJudgementInfo CreateJudgementInfo() => new TaikoJudgementInfo(); - - protected override void UpdateState(ArmedState state) - { - } + protected override TaikoJudgement CreateJudgement() => new TaikoJudgement(); /// /// Sets the scroll position of the DrawableHitObject relative to the offset between diff --git a/osu.Game.Modes.Taiko/Objects/DrumRoll.cs b/osu.Game.Modes.Taiko/Objects/DrumRoll.cs new file mode 100644 index 0000000000..cdb8ef2405 --- /dev/null +++ b/osu.Game.Modes.Taiko/Objects/DrumRoll.cs @@ -0,0 +1,104 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps.Samples; +using osu.Game.Modes.Objects.Types; +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Game.Beatmaps.Timing; +using osu.Game.Database; + +namespace osu.Game.Modes.Taiko.Objects +{ + public class DrumRoll : TaikoHitObject, IHasDistance + { + public double EndTime => StartTime + Distance / Velocity; + + public double Duration => EndTime - StartTime; + + /// + /// Raw length of the drum roll in positional length units. + /// + public double Distance { get; set; } + + /// + /// Velocity of the drum roll in positional length units per millisecond. + /// + public double Velocity { get; protected set; } + + /// + /// The distance between ticks of this drumroll. + /// Half of this value is the hit window of the ticks. + /// + public double TickTimeDistance { get; protected set; } + + /// + /// Number of drum roll ticks required for a "Good" hit. + /// + public double RequiredGoodHits { get; protected set; } + + /// + /// Number of drum roll ticks required for a "Great" hit. + /// + public double RequiredGreatHits { get; protected set; } + + /// + /// Total number of drum roll ticks. + /// + public int TotalTicks => Ticks.Count(); + + /// + /// Initializes the drum roll ticks if not initialized and returns them. + /// + public IEnumerable Ticks => ticks ?? (ticks = createTicks()); + + private List ticks; + + public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty) + { + base.ApplyDefaults(timing, difficulty); + + Velocity = timing.SliderVelocityAt(StartTime) * difficulty.SliderMultiplier / 1000; + TickTimeDistance = timing.BeatLengthAt(StartTime); + + //TODO: move this to legacy conversion code to allow for direct division without special case. + if (difficulty.SliderTickRate == 3) + TickTimeDistance /= 3; + else + TickTimeDistance /= 4; + + RequiredGoodHits = TotalTicks * Math.Min(0.15, 0.05 + 0.10 / 6 * difficulty.OverallDifficulty); + RequiredGreatHits = TotalTicks * Math.Min(0.30, 0.10 + 0.20 / 6 * difficulty.OverallDifficulty); + } + + private List createTicks() + { + var ret = new List(); + + if (TickTimeDistance == 0) + return ret; + + bool first = true; + for (double t = StartTime; t < EndTime + (int)TickTimeDistance; t += TickTimeDistance) + { + ret.Add(new DrumRollTick + { + FirstTick = first, + PreEmpt = PreEmpt, + TickTimeDistance = TickTimeDistance, + StartTime = t, + Sample = new HitSampleInfo + { + Type = SampleType.None, + Set = SampleSet.Soft + } + }); + + first = false; + } + + return ret; + } + } +} diff --git a/osu.Game.Modes.Taiko/Objects/DrumRollTick.cs b/osu.Game.Modes.Taiko/Objects/DrumRollTick.cs new file mode 100644 index 0000000000..66a2d16fe1 --- /dev/null +++ b/osu.Game.Modes.Taiko/Objects/DrumRollTick.cs @@ -0,0 +1,19 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Modes.Taiko.Objects +{ + public class DrumRollTick : TaikoHitObject + { + /// + /// Whether this is the first (initial) tick of the slider. + /// + public bool FirstTick; + + /// + /// The distance between this tick and the next in milliseconds. + /// Half of this value is the hit window of the tick. + /// + public double TickTimeDistance; + } +} \ No newline at end of file diff --git a/osu.Game.Modes.Taiko/Objects/Hit.cs b/osu.Game.Modes.Taiko/Objects/Hit.cs new file mode 100644 index 0000000000..ad8d07d901 --- /dev/null +++ b/osu.Game.Modes.Taiko/Objects/Hit.cs @@ -0,0 +1,35 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps.Timing; +using osu.Game.Database; + +namespace osu.Game.Modes.Taiko.Objects +{ + public class Hit : TaikoHitObject + { + /// + /// The hit window that results in a "GREAT" hit. + /// + public double HitWindowGreat = 35; + + /// + /// The hit window that results in a "GOOD" hit. + /// + public double HitWindowGood = 80; + + /// + /// The hit window that results in a "MISS". + /// + public double HitWindowMiss = 95; + + public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty) + { + base.ApplyDefaults(timing, difficulty); + + HitWindowGreat = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 50, 35, 20); + HitWindowGood = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 120, 80, 50); + HitWindowMiss = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 135, 95, 70); + } + } +} diff --git a/osu.Game.Modes.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Modes.Taiko/Objects/TaikoHitObject.cs index 61d8ed5f01..0ec1c2b93c 100644 --- a/osu.Game.Modes.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Modes.Taiko/Objects/TaikoHitObject.cs @@ -7,37 +7,28 @@ using osu.Game.Modes.Objects; namespace osu.Game.Modes.Taiko.Objects { - public class TaikoHitObject : HitObject + public abstract class TaikoHitObject : HitObject { /// /// HitCircle radius. /// public const float CIRCLE_RADIUS = 64; - /// - /// The hit window that results in a "GREAT" hit. - /// - public double HitWindowGreat = 35; - - /// - /// The hit window that results in a "GOOD" hit. - /// - public double HitWindowGood = 80; - - /// - /// The hit window that results in a "MISS". - /// - public double HitWindowMiss = 95; - /// /// The time to scroll in the HitObject. /// public double PreEmpt; + /// + /// Whether this HitObject is accented. + /// Accented hit objects give more points for hitting the hit object with both keys. + /// + public bool Accented; + /// /// Whether this HitObject is in Kiai time. /// - public bool Kiai; + public bool Kiai { get; protected set; } public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty) { @@ -50,10 +41,6 @@ namespace osu.Game.Modes.Taiko.Objects if (overridePoint != null) Kiai |= overridePoint.KiaiMode; - - HitWindowGreat = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 50, 35, 20); - HitWindowGood = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 120, 80, 50); - HitWindowMiss = BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 135, 95, 70); } } } \ No newline at end of file diff --git a/osu.Game.Modes.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Modes.Taiko/Scoring/TaikoScoreProcessor.cs new file mode 100644 index 0000000000..3007411230 --- /dev/null +++ b/osu.Game.Modes.Taiko/Scoring/TaikoScoreProcessor.cs @@ -0,0 +1,263 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Beatmaps; +using osu.Game.Database; +using osu.Game.Modes.Objects.Drawables; +using osu.Game.Modes.Scoring; +using osu.Game.Modes.Taiko.Judgements; +using osu.Game.Modes.Taiko.Objects; +using osu.Game.Modes.UI; +using OpenTK; + +namespace osu.Game.Modes.Taiko.Scoring +{ + internal class TaikoScoreProcessor : ScoreProcessor + { + /// + /// The maximum score achievable. + /// Does _not_ include bonus score - for bonus score see . + /// + private const int max_score = 1000000; + + /// + /// The amount of the score attributed to combo. + /// + private const double combo_portion_max = max_score * 0.2; + + /// + /// The amount of the score attributed to accuracy. + /// + private const double accuracy_portion_max = max_score * 0.8; + + /// + /// The factor used to determine relevance of combos. + /// + private const double combo_base = 4; + + /// + /// The HP awarded by a hit. + /// + private const double hp_hit_great = 0.03; + + /// + /// The HP awarded for a hit. + /// + private const double hp_hit_good = 0.011; + + /// + /// The minimum HP deducted for a . + /// This occurs when HP Drain = 0. + /// + private const double hp_miss_min = -0.0018; + + /// + /// The median HP deducted for a . + /// This occurs when HP Drain = 5. + /// + private const double hp_miss_mid = -0.0075; + + /// + /// The maximum HP deducted for a . + /// This occurs when HP Drain = 10. + /// + private const double hp_miss_max = -0.12; + + /// + /// The HP awarded for a hit. + /// + /// hits award less HP as they're more spammable, although in hindsight + /// this probably awards too little HP and is kept at this value for now for compatibility. + /// + /// + private const double hp_hit_tick = 0.00000003; + + /// + /// Taiko fails at the end of the map if the player has not half-filled their HP bar. + /// + public override bool HasFailed => totalHits == maxTotalHits && Health.Value <= 0.5; + + /// + /// The cumulative combo portion of the score. + /// + private double comboScore => combo_portion_max * comboPortion / maxComboPortion; + + /// + /// The cumulative accuracy portion of the score. + /// + private double accuracyScore => accuracy_portion_max * Math.Pow(Accuracy, 3.6) * totalHits / maxTotalHits; + + /// + /// The cumulative bonus score. + /// This is added on top of , thus the total score can exceed . + /// + private double bonusScore; + + /// + /// The multiple of the original score added to the combo portion of the score + /// for correctly hitting an accented hit object with both keys. + /// + private double accentedHitScale; + + private double hpIncreaseTick; + private double hpIncreaseGreat; + private double hpIncreaseGood; + private double hpIncreaseMiss; + + private double maxComboPortion; + private double comboPortion; + private int maxTotalHits; + private int totalHits; + + public TaikoScoreProcessor() + { + } + + public TaikoScoreProcessor(HitRenderer hitRenderer) + : base(hitRenderer) + { + } + + protected override void ComputeTargets(Beatmap beatmap) + { + double hpMultiplierNormal = 1 / (hp_hit_great * beatmap.HitObjects.FindAll(o => o is Hit).Count * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.Difficulty.DrainRate, 0.5, 0.75, 0.98)); + + hpIncreaseTick = hp_hit_tick; + hpIncreaseGreat = hpMultiplierNormal * hp_hit_great; + hpIncreaseGood = hpMultiplierNormal * hp_hit_good; + hpIncreaseMiss = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.Difficulty.DrainRate, hp_miss_min, hp_miss_mid, hp_miss_max); + + var accentedHits = beatmap.HitObjects.FindAll(o => o is Hit && o.Accented); + + // This is a linear function that awards: + // 10 times bonus points for hitting an accented hit object with both keys with 30 accented hit objects in the map + // 3 times bonus points for hitting an accented hit object with both keys with 120 accented hit objects in the map + accentedHitScale = -7d / 90d * MathHelper.Clamp(accentedHits.Count, 30, 120) + 111d / 9d; + + foreach (var obj in beatmap.HitObjects) + { + if (obj is Hit) + { + AddJudgement(new TaikoJudgement + { + Result = HitResult.Hit, + TaikoResult = TaikoHitResult.Great, + SecondHit = obj.Accented + }); + } + else if (obj is DrumRoll) + { + for (int i = 0; i < ((DrumRoll)obj).TotalTicks; i++) + { + AddJudgement(new TaikoDrumRollTickJudgement + { + Result = HitResult.Hit, + TaikoResult = TaikoHitResult.Great, + SecondHit = obj.Accented + }); + } + + AddJudgement(new TaikoJudgement + { + Result = HitResult.Hit, + TaikoResult = TaikoHitResult.Great, + SecondHit = obj.Accented + }); + } + else if (obj is Bash) + { + AddJudgement(new TaikoJudgement + { + Result = HitResult.Hit, + TaikoResult = TaikoHitResult.Great + }); + } + } + + maxTotalHits = totalHits; + maxComboPortion = comboPortion; + } + + protected override void OnNewJugement(TaikoJudgement judgement) + { + bool isTick = judgement is TaikoDrumRollTickJudgement; + + // Don't consider ticks as a type of hit that counts towards map completion + if (!isTick) + totalHits++; + + // Apply score changes + if (judgement.Result == HitResult.Hit) + { + double baseValue = judgement.ResultValueForScore; + + // Add bonus points for hitting an accented hit object with the second key + if (judgement.SecondHit) + baseValue += baseValue * accentedHitScale; + + // Add score to portions + if (isTick) + bonusScore += baseValue; + else + { + Combo.Value++; + + // A relevance factor that needs to be applied to make higher combos more relevant + // Value is capped at 400 combo + double comboRelevance = Math.Min(Math.Log(400, combo_base), Math.Max(0.5, Math.Log(Combo.Value, combo_base))); + + comboPortion += baseValue * comboRelevance; + } + } + + // Apply HP changes + switch (judgement.Result) + { + case HitResult.Miss: + // Missing ticks shouldn't drop HP + if (!isTick) + Health.Value += hpIncreaseMiss; + break; + case HitResult.Hit: + switch (judgement.TaikoResult) + { + case TaikoHitResult.Good: + Health.Value += hpIncreaseGood; + break; + case TaikoHitResult.Great: + if (isTick) + Health.Value += hpIncreaseTick; + else + Health.Value += hpIncreaseGreat; + break; + } + break; + } + + // Compute the new score + accuracy + int scoreForAccuracy = 0; + int maxScoreForAccuracy = 0; + + foreach (var j in Judgements) + { + scoreForAccuracy += j.ResultValueForAccuracy; + maxScoreForAccuracy = j.MaxResultValueForAccuracy; + } + + Accuracy.Value = (double)scoreForAccuracy / maxScoreForAccuracy; + TotalScore.Value = comboScore + accuracyScore + bonusScore; + } + + protected override void Reset() + { + base.Reset(); + + Health.Value = 0; + + bonusScore = 0; + comboPortion = 0; + totalHits = 0; + } + } +} diff --git a/osu.Game.Modes.Taiko/TaikoRuleset.cs b/osu.Game.Modes.Taiko/TaikoRuleset.cs index ff481e9162..ce7e756e30 100644 --- a/osu.Game.Modes.Taiko/TaikoRuleset.cs +++ b/osu.Game.Modes.Taiko/TaikoRuleset.cs @@ -10,6 +10,8 @@ using osu.Game.Modes.Taiko.UI; using osu.Game.Modes.UI; using osu.Game.Screens.Play; using System.Collections.Generic; +using osu.Game.Modes.Scoring; +using osu.Game.Modes.Taiko.Scoring; namespace osu.Game.Modes.Taiko { diff --git a/osu.Game.Modes.Taiko/TaikoScoreProcessor.cs b/osu.Game.Modes.Taiko/TaikoScoreProcessor.cs deleted file mode 100644 index 849c0fa894..0000000000 --- a/osu.Game.Modes.Taiko/TaikoScoreProcessor.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Modes.Taiko.Judgements; -using osu.Game.Modes.Taiko.Objects; -using osu.Game.Modes.UI; - -namespace osu.Game.Modes.Taiko -{ - internal class TaikoScoreProcessor : ScoreProcessor - { - public TaikoScoreProcessor() - { - } - - public TaikoScoreProcessor(HitRenderer hitRenderer) - : base(hitRenderer) - { - } - - protected override void UpdateCalculations(TaikoJudgementInfo newJudgement) - { - } - } -} diff --git a/osu.Game.Modes.Taiko/UI/DrawableTaikoJudgement.cs b/osu.Game.Modes.Taiko/UI/DrawableTaikoJudgement.cs new file mode 100644 index 0000000000..78c9657b40 --- /dev/null +++ b/osu.Game.Modes.Taiko/UI/DrawableTaikoJudgement.cs @@ -0,0 +1,57 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Modes.Taiko.Judgements; +using osu.Game.Modes.Objects.Drawables; +using osu.Framework.Allocation; +using osu.Game.Graphics; +using osu.Game.Modes.Judgements; + +namespace osu.Game.Modes.Taiko.UI +{ + /// + /// Text that is shown as judgement when a hit object is hit or missed. + /// + public class DrawableTaikoJudgement : DrawableJudgement + { + /// + /// Creates a new judgement text. + /// + /// The judgement to visualise. + public DrawableTaikoJudgement(TaikoJudgement judgement) + : base(judgement) + { + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + switch (Judgement.Result) + { + case HitResult.Hit: + switch (Judgement.TaikoResult) + { + case TaikoHitResult.Good: + Colour = colours.GreenLight; + break; + case TaikoHitResult.Great: + Colour = colours.BlueLight; + break; + } + break; + } + } + + protected override void LoadComplete() + { + switch (Judgement.Result) + { + case HitResult.Hit: + MoveToY(-100, 500); + break; + } + + base.LoadComplete(); + } + } +} \ No newline at end of file diff --git a/osu.Game.Modes.Taiko/UI/HitExplosion.cs b/osu.Game.Modes.Taiko/UI/HitExplosion.cs new file mode 100644 index 0000000000..adc6a77c5d --- /dev/null +++ b/osu.Game.Modes.Taiko/UI/HitExplosion.cs @@ -0,0 +1,78 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Transforms; +using osu.Game.Graphics; +using osu.Game.Modes.Taiko.Judgements; +using osu.Game.Modes.Taiko.Objects; + +namespace osu.Game.Modes.Taiko.UI +{ + /// + /// A circle explodes from the hit target to indicate a hitobject has been hit. + /// + internal class HitExplosion : CircularContainer + { + private readonly TaikoJudgement judgement; + private readonly Box innerFill; + + public HitExplosion(TaikoJudgement judgement) + { + this.judgement = judgement; + + Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2); + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + RelativePositionAxes = Axes.Both; + + BorderColour = Color4.White; + BorderThickness = 1; + + Alpha = 0.15f; + Masking = true; + + Children = new[] + { + innerFill = new Box + { + RelativeSizeAxes = Axes.Both, + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + if (judgement.SecondHit) + Size *= 1.5f; + + switch (judgement.TaikoResult) + { + case TaikoHitResult.Good: + innerFill.Colour = colours.Green; + break; + case TaikoHitResult.Great: + innerFill.Colour = colours.Blue; + break; + } + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + ScaleTo(5f, 1000, EasingTypes.OutQuint); + FadeOut(500); + + Expire(); + } + } +} diff --git a/osu.Game.Modes.Taiko/UI/HitTarget.cs b/osu.Game.Modes.Taiko/UI/HitTarget.cs new file mode 100644 index 0000000000..d38af3390e --- /dev/null +++ b/osu.Game.Modes.Taiko/UI/HitTarget.cs @@ -0,0 +1,105 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Modes.Taiko.Objects; + +namespace osu.Game.Modes.Taiko.UI +{ + /// + /// A component that is displayed at the hit position in the taiko playfield. + /// + internal class HitTarget : Container + { + /// + /// Diameter of normal hit object circles. + /// + private const float normal_diameter = TaikoHitObject.CIRCLE_RADIUS * 2 * TaikoPlayfield.PLAYFIELD_SCALE; + + /// + /// Diameter of finisher hit object circles. + /// + private const float finisher_diameter = normal_diameter * 1.5f; + + /// + /// The 1px inner border of the taiko playfield. + /// + private const float border_offset = 1; + + /// + /// Thickness of all drawn line pieces. + /// + private const float border_thickness = 2.5f; + + public HitTarget() + { + RelativeSizeAxes = Axes.Y; + + Children = new Drawable[] + { + new Box + { + Name = "Bar Upper", + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Y = border_offset, + Size = new Vector2(border_thickness, (TaikoPlayfield.PlayfieldHeight - finisher_diameter) / 2f - border_offset), + Alpha = 0.1f + }, + new CircularContainer + { + Name = "Finisher Ring", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(finisher_diameter), + Masking = true, + BorderColour = Color4.White, + BorderThickness = border_thickness, + Alpha = 0.1f, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + } + }, + new CircularContainer + { + Name = "Normal Ring", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(normal_diameter), + Masking = true, + BorderColour = Color4.White, + BorderThickness = border_thickness, + Alpha = 0.5f, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + } + }, + new Box + { + Name = "Bar Lower", + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Y = -border_offset, + Size = new Vector2(border_thickness, (TaikoPlayfield.PlayfieldHeight - finisher_diameter) / 2f - border_offset), + Alpha = 0.1f + }, + }; + } + } +} diff --git a/osu.Game.Modes.Taiko/UI/InputDrum.cs b/osu.Game.Modes.Taiko/UI/InputDrum.cs new file mode 100644 index 0000000000..1787670c7a --- /dev/null +++ b/osu.Game.Modes.Taiko/UI/InputDrum.cs @@ -0,0 +1,149 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using OpenTK; +using OpenTK.Input; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Graphics.Transforms; +using osu.Framework.Input; +using osu.Game.Graphics; + +namespace osu.Game.Modes.Taiko.UI +{ + /// + /// A component of the playfield that captures input and displays input as a drum. + /// + internal class InputDrum : Container + { + public InputDrum() + { + Size = new Vector2(TaikoPlayfield.PlayfieldHeight); + + const float middle_split = 10; + + Children = new Drawable[] + { + new TaikoHalfDrum(false) + { + Name = "Left Half", + Anchor = Anchor.Centre, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.Both, + X = -middle_split / 2, + RimKey = Key.D, + CentreKey = Key.F + }, + new TaikoHalfDrum(true) + { + Name = "Right Half", + Anchor = Anchor.Centre, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + X = middle_split / 2, + Position = new Vector2(-1f, 0), + RimKey = Key.K, + CentreKey = Key.J + } + }; + } + + /// + /// A half-drum. Contains one centre and one rim hit. + /// + private class TaikoHalfDrum : Container + { + /// + /// The key to be used for the rim of the half-drum. + /// + public Key RimKey; + + /// + /// The key to be used for the centre of the half-drum. + /// + public Key CentreKey; + + private readonly Sprite rim; + private readonly Sprite rimHit; + private readonly Sprite centre; + private readonly Sprite centreHit; + + public TaikoHalfDrum(bool flipped) + { + Masking = true; + + Children = new Drawable[] + { + rim = new Sprite + { + Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both + }, + rimHit = new Sprite + { + Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Alpha = 0, + BlendingMode = BlendingMode.Additive, + }, + centre = new Sprite + { + Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.7f) + }, + centreHit = new Sprite + { + Anchor = flipped ? Anchor.CentreLeft : Anchor.CentreRight, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.7f), + Alpha = 0, + BlendingMode = BlendingMode.Additive + } + }; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures, OsuColour colours) + { + rim.Texture = textures.Get(@"Play/Taiko/taiko-drum-outer"); + rimHit.Texture = textures.Get(@"Play/Taiko/taiko-drum-outer-hit"); + centre.Texture = textures.Get(@"Play/Taiko/taiko-drum-inner"); + centreHit.Texture = textures.Get(@"Play/Taiko/taiko-drum-inner-hit"); + + rimHit.Colour = colours.Blue; + centreHit.Colour = colours.Pink; + } + + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (args.Repeat) + return false; + + Drawable target = null; + + if (args.Key == CentreKey) + target = centreHit; + else if (args.Key == RimKey) + target = rimHit; + + if (target != null) + { + target.FadeTo(Math.Min(target.Alpha + 0.4f, 1), 40, EasingTypes.OutQuint); + target.Delay(40); + target.FadeOut(600, EasingTypes.OutQuint); + } + + return false; + } + } + } +} diff --git a/osu.Game.Modes.Taiko/UI/TaikoHitRenderer.cs b/osu.Game.Modes.Taiko/UI/TaikoHitRenderer.cs index 80e42cb976..3266b5c030 100644 --- a/osu.Game.Modes.Taiko/UI/TaikoHitRenderer.cs +++ b/osu.Game.Modes.Taiko/UI/TaikoHitRenderer.cs @@ -3,14 +3,16 @@ using osu.Game.Beatmaps; using osu.Game.Modes.Objects.Drawables; +using osu.Game.Modes.Scoring; using osu.Game.Modes.Taiko.Beatmaps; using osu.Game.Modes.Taiko.Judgements; using osu.Game.Modes.Taiko.Objects; +using osu.Game.Modes.Taiko.Scoring; using osu.Game.Modes.UI; namespace osu.Game.Modes.Taiko.UI { - public class TaikoHitRenderer : HitRenderer + public class TaikoHitRenderer : HitRenderer { public TaikoHitRenderer(WorkingBeatmap beatmap) : base(beatmap) @@ -23,8 +25,8 @@ namespace osu.Game.Modes.Taiko.UI protected override IBeatmapProcessor CreateBeatmapProcessor() => new TaikoBeatmapProcessor(); - protected override Playfield CreatePlayfield() => new TaikoPlayfield(); + protected override Playfield CreatePlayfield() => new TaikoPlayfield(); - protected override DrawableHitObject GetVisualRepresentation(TaikoHitObject h) => null; + protected override DrawableHitObject GetVisualRepresentation(TaikoHitObject h) => null; } } diff --git a/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs index f3ae600501..b7fac507d6 100644 --- a/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs @@ -4,38 +4,192 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; using osu.Game.Modes.Taiko.Objects; using osu.Game.Modes.UI; using OpenTK; using OpenTK.Graphics; using osu.Game.Modes.Taiko.Judgements; +using osu.Game.Modes.Objects.Drawables; +using osu.Game.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics.Primitives; namespace osu.Game.Modes.Taiko.UI { - public class TaikoPlayfield : Playfield + public class TaikoPlayfield : Playfield { + /// + /// The default play field height. + /// + public const float PLAYFIELD_BASE_HEIGHT = 242; + + /// + /// The play field height scale. + /// + public const float PLAYFIELD_SCALE = 0.65f; + + /// + /// The play field height after scaling. + /// + public static float PlayfieldHeight => PLAYFIELD_BASE_HEIGHT * PLAYFIELD_SCALE; + + /// + /// The offset from which the center of the hit target lies at. + /// + private const float hit_target_offset = 80; + + /// + /// The size of the left area of the playfield. This area contains the input drum. + /// + private const float left_area_size = 240; + + protected override Container Content => hitObjectContainer; + + private readonly Container hitExplosionContainer; + //private Container barLineContainer; + private readonly Container judgementContainer; + + private readonly Container hitObjectContainer; + //private Container topLevelHitContainer; + private readonly Container leftBackgroundContainer; + private readonly Container rightBackgroundContainer; + private readonly Box leftBackground; + private readonly Box rightBackground; + public TaikoPlayfield() { RelativeSizeAxes = Axes.X; - Size = new Vector2(1, 100); - Anchor = Anchor.Centre; - Origin = Anchor.Centre; + Height = PlayfieldHeight; + + AddInternal(new Drawable[] + { + rightBackgroundContainer = new Container + { + RelativeSizeAxes = Axes.Both, + BorderThickness = 2, + Masking = true, + EdgeEffect = new EdgeEffect + { + Type = EdgeEffectType.Shadow, + Colour = Color4.Black.Opacity(0.2f), + Radius = 5, + }, + Children = new Drawable[] + { + rightBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.6f + }, + } + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = left_area_size }, + Children = new Drawable[] + { + new Container + { + Padding = new MarginPadding { Left = hit_target_offset }, + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + hitExplosionContainer = new Container + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.Centre, + Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2), + Scale = new Vector2(PLAYFIELD_SCALE), + BlendingMode = BlendingMode.Additive + }, + //barLineContainer = new Container + //{ + // RelativeSizeAxes = Axes.Both, + //}, + new HitTarget + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.Centre, + }, + hitObjectContainer = new Container + { + RelativeSizeAxes = Axes.Both, + }, + judgementContainer = new Container + { + RelativeSizeAxes = Axes.Both, + BlendingMode = BlendingMode.Additive + }, + }, + }, + } + }, + leftBackgroundContainer = new Container + { + Size = new Vector2(left_area_size, PlayfieldHeight), + BorderThickness = 1, + Children = new Drawable[] + { + leftBackground = new Box + { + RelativeSizeAxes = Axes.Both, + }, + new InputDrum + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativePositionAxes = Axes.X, + Position = new Vector2(0.10f, 0), + Scale = new Vector2(0.9f) + }, + new Box + { + Anchor = Anchor.TopRight, + RelativeSizeAxes = Axes.Y, + Width = 10, + ColourInfo = Framework.Graphics.Colour.ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.6f), Color4.Black.Opacity(0)), + }, + } + }, + //topLevelHitContainer = new Container + //{ + // RelativeSizeAxes = Axes.Both, + //} + }); } [BackgroundDependencyLoader] - private void load(TextureStore textures) + private void load(OsuColour colours) { - Add(new Box { RelativeSizeAxes = Axes.Both, Alpha = 0.5f }); + leftBackgroundContainer.BorderColour = colours.Gray0; + leftBackground.Colour = colours.Gray1; - Add(new Sprite + rightBackgroundContainer.BorderColour = colours.Gray1; + rightBackground.Colour = colours.Gray0; + } + + public override void Add(DrawableHitObject h) + { + h.Depth = (float)h.HitObject.StartTime; + + base.Add(h); + } + + public override void OnJudgement(DrawableHitObject judgedObject) + { + bool wasHit = judgedObject.Judgement.Result == HitResult.Hit; + + if (wasHit) + hitExplosionContainer.Add(new HitExplosion(judgedObject.Judgement)); + + judgementContainer.Add(new DrawableTaikoJudgement(judgedObject.Judgement) { - Texture = textures.Get(@"Menu/logo"), - Origin = Anchor.Centre, - Scale = new Vector2(0.2f), - RelativePositionAxes = Axes.Both, - Position = new Vector2(0.1f, 0.5f), - Colour = Color4.Gray + Anchor = wasHit ? Anchor.TopLeft : Anchor.CentreLeft, + Origin = wasHit ? Anchor.BottomCentre : Anchor.Centre, + RelativePositionAxes = Axes.X, + X = wasHit ? judgedObject.Position.X : 0, }); } } diff --git a/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj b/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj index a0ba6385db..a455167c6b 100644 --- a/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj +++ b/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj @@ -49,15 +49,23 @@ - + + + + + - + + + + + @@ -75,10 +83,6 @@ {C76BF5B3-985E-4D39-95FE-97C9C879B83A} osu.Framework - - {C92A607B-1FDD-4954-9F92-03FF547D9080} - osu.Game.Modes.Osu - {0D3FBF8A-7464-4CF7-8C90-3E7886DF2D4D} osu.Game diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index ae936f3f49..39fb1bfa8a 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -70,7 +70,7 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.IsTrue(File.Exists(temp)); var importer = new BeatmapIPCChannel(client); - if (!importer.ImportAsync(temp).Wait(1000)) + if (!importer.ImportAsync(temp).Wait(5000)) Assert.Fail(@"IPC took too long to send"); ensureLoaded(osu); diff --git a/osu.Game/Beatmaps/DifficultyCalculator.cs b/osu.Game/Beatmaps/DifficultyCalculator.cs index 9c057d5f77..a9da5c589c 100644 --- a/osu.Game/Beatmaps/DifficultyCalculator.cs +++ b/osu.Game/Beatmaps/DifficultyCalculator.cs @@ -15,8 +15,8 @@ namespace osu.Game.Beatmaps private void loadTiming() { // TODO: Handle mods - int audioRate = 100; - TimeRate = audioRate / 100.0; + const int audio_rate = 100; + TimeRate = audio_rate / 100.0; } public double Calculate(Dictionary categoryDifficulty = null) diff --git a/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs b/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs index 67ebb2fcb9..34e4ebf8ac 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs @@ -21,12 +21,12 @@ namespace osu.Game.Beatmaps.Drawables public class BeatmapPanel : Panel { public BeatmapInfo Beatmap; - private Sprite background; + private readonly Sprite background; public Action GainedSelection; public Action StartRequested; - private Triangles triangles; - private StarCounter starCounter; + private readonly Triangles triangles; + private readonly StarCounter starCounter; protected override void Selected() { diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs index 3dc5fdedc9..2d032f0ea4 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs @@ -20,11 +20,12 @@ namespace osu.Game.Beatmaps.Drawables public class BeatmapSetHeader : Panel { public Action GainedSelection; - private SpriteText title, artist; + private readonly SpriteText title; + private readonly SpriteText artist; private OsuConfigManager config; private Bindable preferUnicode; - private WorkingBeatmap beatmap; - private FillFlowContainer difficultyIcons; + private readonly WorkingBeatmap beatmap; + private readonly FillFlowContainer difficultyIcons; public BeatmapSetHeader(WorkingBeatmap beatmap) { diff --git a/osu.Game/Beatmaps/Drawables/Panel.cs b/osu.Game/Beatmaps/Drawables/Panel.cs index c51ae8680e..2a5564bc71 100644 --- a/osu.Game/Beatmaps/Drawables/Panel.cs +++ b/osu.Game/Beatmaps/Drawables/Panel.cs @@ -18,7 +18,7 @@ namespace osu.Game.Beatmaps.Drawables public override bool RemoveWhenNotAlive => false; - private Container nestedContainer; + private readonly Container nestedContainer; protected override Container Content => nestedContainer; diff --git a/osu.Game/Beatmaps/IO/OszArchiveReader.cs b/osu.Game/Beatmaps/IO/OszArchiveReader.cs index 8a1d071cfc..5c0f29fb86 100644 --- a/osu.Game/Beatmaps/IO/OszArchiveReader.cs +++ b/osu.Game/Beatmaps/IO/OszArchiveReader.cs @@ -21,9 +21,9 @@ namespace osu.Game.Beatmaps.IO OsuLegacyDecoder.Register(); } - private Stream archiveStream; - private ZipFile archive; - private Beatmap firstMap; + private readonly Stream archiveStream; + private readonly ZipFile archive; + private readonly Beatmap firstMap; public OszArchiveReader(Stream archiveStream) { diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 2a2de1b6ef..74c8866596 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -42,7 +42,7 @@ namespace osu.Game.Beatmaps protected abstract Track GetTrack(); private Beatmap beatmap; - private object beatmapLock = new object(); + private readonly object beatmapLock = new object(); public Beatmap Beatmap { get @@ -54,7 +54,7 @@ namespace osu.Game.Beatmaps } } - private object backgroundLock = new object(); + private readonly object backgroundLock = new object(); private Texture background; public Texture Background { @@ -68,7 +68,7 @@ namespace osu.Game.Beatmaps } private Track track; - private object trackLock = new object(); + private readonly object trackLock = new object(); public Track Track { get diff --git a/osu.Game/Database/BeatmapDatabase.cs b/osu.Game/Database/BeatmapDatabase.cs index 317467d792..dfc916a136 100644 --- a/osu.Game/Database/BeatmapDatabase.cs +++ b/osu.Game/Database/BeatmapDatabase.cs @@ -21,7 +21,7 @@ namespace osu.Game.Database public class BeatmapDatabase { private SQLiteConnection connection { get; } - private Storage storage; + private readonly Storage storage; public event Action BeatmapSetAdded; public event Action BeatmapSetRemoved; diff --git a/osu.Game/Database/ScoreDatabase.cs b/osu.Game/Database/ScoreDatabase.cs index cfa8e6ac7e..665ae8649e 100644 --- a/osu.Game/Database/ScoreDatabase.cs +++ b/osu.Game/Database/ScoreDatabase.cs @@ -8,6 +8,7 @@ using osu.Framework.Platform; using osu.Game.IO.Legacy; using osu.Game.IPC; using osu.Game.Modes; +using osu.Game.Modes.Scoring; using SharpCompress.Compressors.LZMA; namespace osu.Game.Database diff --git a/osu.Game/Graphics/Backgrounds/Background.cs b/osu.Game/Graphics/Backgrounds/Background.cs index 5bafc8cd64..7c47635276 100644 --- a/osu.Game/Graphics/Backgrounds/Background.cs +++ b/osu.Game/Graphics/Backgrounds/Background.cs @@ -14,7 +14,7 @@ namespace osu.Game.Graphics.Backgrounds { public Sprite Sprite; - private string textureName; + private readonly string textureName; public Background(string textureName = @"") { diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 4c50162194..8dff614f6c 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -89,25 +89,24 @@ namespace osu.Game.Graphics.Backgrounds protected virtual Triangle CreateTriangle() { - float stdDev = 0.16f; - float mean = 0.5f; + const float std_dev = 0.16f; + const float mean = 0.5f; float u1 = 1 - RNG.NextSingle(); //uniform(0,1] random floats float u2 = 1 - RNG.NextSingle(); float randStdNormal = (float)(Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2)); //random normal(0,1) - var scale = Math.Max(triangleScale * (mean + stdDev * randStdNormal), 0.1f); //random normal(mean,stdDev^2) + var scale = Math.Max(triangleScale * (mean + std_dev * randStdNormal), 0.1f); //random normal(mean,stdDev^2) const float size = 100; - return new Triangle + return new EquilateralTriangle { Origin = Anchor.TopCentre, RelativePositionAxes = Axes.Both, + Size = new Vector2(size), Scale = new Vector2(scale), EdgeSmoothness = new Vector2(1), Colour = GetTriangleShade(), - // Scaling height by 0.866 results in equiangular triangles (== 60° and equal side length) - Size = new Vector2(size, 0.866f * size), Depth = scale, }; } diff --git a/osu.Game/Graphics/Containers/ParallaxContainer.cs b/osu.Game/Graphics/Containers/ParallaxContainer.cs index c4cd1777e1..dc2f01a2bc 100644 --- a/osu.Game/Graphics/Containers/ParallaxContainer.cs +++ b/osu.Game/Graphics/Containers/ParallaxContainer.cs @@ -12,7 +12,7 @@ using osu.Framework.Configuration; namespace osu.Game.Graphics.Containers { - internal class ParallaxContainer : Container + internal class ParallaxContainer : Container, IRequireHighFrequencyMousePosition { public float ParallaxAmount = 0.02f; @@ -31,7 +31,7 @@ namespace osu.Game.Graphics.Containers }); } - private Container content; + private readonly Container content; private InputManager input; protected override Container Content => content; diff --git a/osu.Game/Graphics/Cursor/CursorTrail.cs b/osu.Game/Graphics/Cursor/CursorTrail.cs index ffd96d9622..4b5610e840 100644 --- a/osu.Game/Graphics/Cursor/CursorTrail.cs +++ b/osu.Game/Graphics/Cursor/CursorTrail.cs @@ -31,10 +31,10 @@ namespace osu.Game.Graphics.Cursor private float time; - private TrailDrawNodeSharedData trailDrawNodeSharedData = new TrailDrawNodeSharedData(); + private readonly TrailDrawNodeSharedData trailDrawNodeSharedData = new TrailDrawNodeSharedData(); private const int max_sprites = 2048; - private TrailPart[] parts = new TrailPart[max_sprites]; + private readonly TrailPart[] parts = new TrailPart[max_sprites]; private Vector2? lastPosition; @@ -88,10 +88,10 @@ namespace osu.Game.Graphics.Cursor Invalidate(Invalidation.DrawNode, shallPropagate: false); - int fadeClockResetThreshold = 1000000; + const int fade_clock_reset_threshold = 1000000; time = (float)(Time.Current - timeOffset) / 500f; - if (time > fadeClockResetThreshold) + if (time > fade_clock_reset_threshold) resetTime(); } @@ -163,7 +163,7 @@ namespace osu.Game.Graphics.Cursor public float Time; public TrailDrawNodeSharedData Shared; - public TrailPart[] Parts = new TrailPart[max_sprites]; + public readonly TrailPart[] Parts = new TrailPart[max_sprites]; public Vector2 Size; public TrailDrawNode() diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs index 49b5f1e509..6015f56d6c 100644 --- a/osu.Game/Graphics/UserInterface/DialogButton.cs +++ b/osu.Game/Graphics/UserInterface/DialogButton.cs @@ -81,9 +81,14 @@ namespace osu.Game.Graphics.UserInterface public SampleChannel SampleClick, SampleHover; - private Container backgroundContainer, colourContainer, glowContainer; - private Box leftGlow, centerGlow, rightGlow, background; - private SpriteText spriteText; + private readonly Container backgroundContainer; + private readonly Container colourContainer; + private readonly Container glowContainer; + private readonly Box leftGlow; + private readonly Box centerGlow; + private readonly Box rightGlow; + private readonly Box background; + private readonly SpriteText spriteText; private Vector2 hoverSpacing => new Vector2(3f, 0f); private bool didClick; // Used for making sure that the OnMouseDown animation can call instead of OnHoverLost's when clicking diff --git a/osu.Game/Graphics/UserInterface/Nub.cs b/osu.Game/Graphics/UserInterface/Nub.cs index a32e047af9..71060fbd09 100644 --- a/osu.Game/Graphics/UserInterface/Nub.cs +++ b/osu.Game/Graphics/UserInterface/Nub.cs @@ -18,7 +18,7 @@ namespace osu.Game.Graphics.UserInterface public const float COLLAPSED_SIZE = 20; public const float EXPANDED_SIZE = 40; - private Box fill; + private readonly Box fill; private const float border_width = 3; private Color4 glowingColour, idleColour; diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs index a2469afb53..2209c96689 100644 --- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs @@ -64,8 +64,8 @@ namespace osu.Game.Graphics.UserInterface } } - private Nub nub; - private SpriteText labelSpriteText; + private readonly Nub nub; + private readonly SpriteText labelSpriteText; private SampleChannel sampleChecked; private SampleChannel sampleUnchecked; diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index 7a31337660..3466fb1a60 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -85,7 +85,7 @@ namespace osu.Game.Graphics.UserInterface private Color4? accentColour; - private TextAwesome chevron; + private readonly TextAwesome chevron; protected override void FormatForeground(bool hover = false) { @@ -116,7 +116,7 @@ namespace osu.Game.Graphics.UserInterface protected class OsuDropdownHeader : DropdownHeader { - private SpriteText label; + private readonly SpriteText label; protected override string Label { get { return label.Text; } diff --git a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs index c9d8f936f6..d0072af0eb 100644 --- a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs +++ b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs @@ -18,7 +18,7 @@ namespace osu.Game.Graphics.UserInterface public class PasswordMaskChar : Container { - private CircularContainer circle; + private readonly CircularContainer circle; public PasswordMaskChar(float size) { diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index 58fe24a9e6..81a1b5ca1b 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -19,8 +19,9 @@ namespace osu.Game.Graphics.UserInterface private SampleChannel sample; private double lastSampleTime; - private Nub nub; - private Box leftBox, rightBox; + private readonly Nub nub; + private readonly Box leftBox; + private readonly Box rightBox; public OsuSliderBar() { diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs index 8283c1baa0..3e62780e5c 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs @@ -28,6 +28,8 @@ namespace osu.Game.Graphics.UserInterface public OsuTabControl() { + TabContainer.Spacing = new Vector2(10f, 0f); + if (!typeof(T).IsEnum) throw new InvalidOperationException("OsuTabControl only supports enums as the generic type argument"); @@ -59,8 +61,8 @@ namespace osu.Game.Graphics.UserInterface private class OsuTabItem : TabItem { - private SpriteText text; - private Box box; + private readonly SpriteText text; + private readonly Box box; private Color4? accentColour; public Color4 AccentColour @@ -142,7 +144,7 @@ namespace osu.Game.Graphics.UserInterface { text = new OsuSpriteText { - Margin = new MarginPadding(5), + Margin = new MarginPadding { Top = 5, Bottom = 5 }, Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, TextSize = 14, diff --git a/osu.Game/Graphics/UserInterface/OsuTabControlCheckBox.cs b/osu.Game/Graphics/UserInterface/OsuTabControlCheckBox.cs new file mode 100644 index 0000000000..7dc1212318 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/OsuTabControlCheckBox.cs @@ -0,0 +1,140 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Transforms; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// A checkbox styled to be placed in line with an + /// + public class OsuTabControlCheckBox : CheckBox + { + private readonly Box box; + private readonly SpriteText text; + private readonly TextAwesome icon; + + public event EventHandler Action; + + private Color4? accentColour; + public Color4 AccentColour + { + get { return accentColour.GetValueOrDefault(); } + set + { + accentColour = value; + + if (State != CheckBoxState.Checked) + { + text.Colour = AccentColour; + icon.Colour = AccentColour; + } + } + } + + public string Text + { + get { return text.Text; } + set { text.Text = value; } + } + + protected override void OnChecked() + { + fadeIn(); + icon.Icon = FontAwesome.fa_check_circle_o; + Action?.Invoke(this, State); + } + + protected override void OnUnchecked() + { + fadeOut(); + icon.Icon = FontAwesome.fa_circle_o; + Action?.Invoke(this, State); + } + + private const float transition_length = 500; + + private void fadeIn() + { + box.FadeIn(transition_length, EasingTypes.OutQuint); + text.FadeColour(Color4.White, transition_length, EasingTypes.OutQuint); + } + + private void fadeOut() + { + box.FadeOut(transition_length, EasingTypes.OutQuint); + text.FadeColour(AccentColour, transition_length, EasingTypes.OutQuint); + } + + protected override bool OnHover(InputState state) + { + fadeIn(); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + if (State == CheckBoxState.Unchecked) + fadeOut(); + + base.OnHoverLost(state); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + if (accentColour == null) + AccentColour = colours.Blue; + } + + public OsuTabControlCheckBox() + { + AutoSizeAxes = Axes.Both; + + Children = new Drawable[] + { + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Top = 5, Bottom = 5, }, + Spacing = new Vector2(5f, 0f), + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + text = new OsuSpriteText + { + TextSize = 14, + Font = @"Exo2.0-Bold", + }, + icon = new TextAwesome + { + TextSize = 14, + Icon = FontAwesome.fa_circle_o, + Shadow = true, + }, + }, + }, + box = new Box + { + RelativeSizeAxes = Axes.X, + Height = 1, + Alpha = 0, + Colour = Color4.White, + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + } + }; + } + } +} diff --git a/osu.Game/Graphics/UserInterface/StarCounter.cs b/osu.Game/Graphics/UserInterface/StarCounter.cs index c46eda6582..c0d55fa608 100644 --- a/osu.Game/Graphics/UserInterface/StarCounter.cs +++ b/osu.Game/Graphics/UserInterface/StarCounter.cs @@ -147,7 +147,7 @@ namespace osu.Game.Graphics.UserInterface private class Star : Container { - public TextAwesome Icon; + public readonly TextAwesome Icon; public Star() { Size = new Vector2(star_size); diff --git a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs index 0d4e72f92c..601970e36d 100644 --- a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs +++ b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs @@ -16,7 +16,7 @@ namespace osu.Game.Graphics.UserInterface { public class TwoLayerButton : ClickableContainer { - private TextAwesome icon; + private readonly TextAwesome icon; public Box IconLayer; public Box TextLayer; @@ -29,11 +29,11 @@ namespace osu.Game.Graphics.UserInterface public static readonly Vector2 SIZE_EXTENDED = new Vector2(140, 50); public static readonly Vector2 SIZE_RETRACTED = new Vector2(100, 50); public SampleChannel ActivationSound; - private SpriteText text; + private readonly SpriteText text; public Color4 HoverColour; - private Container c1; - private Container c2; + private readonly Container c1; + private readonly Container c2; public Color4 BackgroundColour { @@ -171,7 +171,7 @@ namespace osu.Game.Graphics.UserInterface IconLayer.FadeColour(HoverColour, transform_time, EasingTypes.OutElastic); - double offset = 0; //(1 - Game.Audio.SyncBeatProgress) * duration; + const double offset = 0; //(1 - Game.Audio.SyncBeatProgress) * duration; double startTime = Time.Current + offset; // basic pulse @@ -200,7 +200,7 @@ namespace osu.Game.Graphics.UserInterface int duration = 0; //(int)(Game.Audio.BeatLength); if (duration == 0) duration = pulse_length * 2; - double offset = 0; //(1 - Game.Audio.SyncBeatProgress) * duration; + const double offset = 0; //(1 - Game.Audio.SyncBeatProgress) * duration; double startTime = Time.Current + offset; // slow pulse diff --git a/osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs b/osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs index a9f09ce86c..374385e351 100644 --- a/osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs +++ b/osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs @@ -15,7 +15,7 @@ namespace osu.Game.Graphics.UserInterface.Volume { internal class VolumeControl : OverlayContainer { - private VolumeMeter volumeMeterMaster; + private readonly VolumeMeter volumeMeterMaster; protected override bool HideOnEscape => false; @@ -89,8 +89,8 @@ namespace osu.Game.Graphics.UserInterface.Volume private ScheduledDelegate popOutDelegate; - private VolumeMeter volumeMeterEffect; - private VolumeMeter volumeMeterMusic; + private readonly VolumeMeter volumeMeterEffect; + private readonly VolumeMeter volumeMeterMusic; protected override void PopIn() { diff --git a/osu.Game/Graphics/UserInterface/Volume/VolumeMeter.cs b/osu.Game/Graphics/UserInterface/Volume/VolumeMeter.cs index 4d68177fef..4d83805b83 100644 --- a/osu.Game/Graphics/UserInterface/Volume/VolumeMeter.cs +++ b/osu.Game/Graphics/UserInterface/Volume/VolumeMeter.cs @@ -15,7 +15,7 @@ namespace osu.Game.Graphics.UserInterface.Volume { internal class VolumeMeter : Container { - private Box meterFill; + private readonly Box meterFill; public BindableDouble Bindable { get; } = new BindableDouble(); public VolumeMeter(string meterName) diff --git a/osu.Game/IO/Legacy/SerializationReader.cs b/osu.Game/IO/Legacy/SerializationReader.cs index 6cab5c534d..6bb6c3bbbf 100644 --- a/osu.Game/IO/Legacy/SerializationReader.cs +++ b/osu.Game/IO/Legacy/SerializationReader.cs @@ -18,7 +18,7 @@ namespace osu.Game.IO.Legacy /// handle null strings and simplify use with ISerializable. public class SerializationReader : BinaryReader { - private Stream stream; + private readonly Stream stream; public SerializationReader(Stream s) : base(s, Encoding.UTF8) diff --git a/osu.Game/IPC/BeatmapIPCChannel.cs b/osu.Game/IPC/BeatmapIPCChannel.cs index 2b98d5254f..61e6cc76cc 100644 --- a/osu.Game/IPC/BeatmapIPCChannel.cs +++ b/osu.Game/IPC/BeatmapIPCChannel.cs @@ -10,7 +10,7 @@ namespace osu.Game.IPC { public class BeatmapIPCChannel : IpcChannel { - private BeatmapDatabase beatmaps; + private readonly BeatmapDatabase beatmaps; public BeatmapIPCChannel(IIpcHost host, BeatmapDatabase beatmaps = null) : base(host) diff --git a/osu.Game/IPC/ScoreIPCChannel.cs b/osu.Game/IPC/ScoreIPCChannel.cs index 0a49f4cd8f..7a509ee0e8 100644 --- a/osu.Game/IPC/ScoreIPCChannel.cs +++ b/osu.Game/IPC/ScoreIPCChannel.cs @@ -10,7 +10,7 @@ namespace osu.Game.IPC { public class ScoreIPCChannel : IpcChannel { - private ScoreDatabase scores; + private readonly ScoreDatabase scores; public ScoreIPCChannel(IIpcHost host, ScoreDatabase scores = null) : base(host) diff --git a/osu.Game/Modes/Judgements/DrawableJudgement.cs b/osu.Game/Modes/Judgements/DrawableJudgement.cs new file mode 100644 index 0000000000..76c73335dd --- /dev/null +++ b/osu.Game/Modes/Judgements/DrawableJudgement.cs @@ -0,0 +1,94 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Framework.Allocation; +using osu.Framework.Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Transforms; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Modes.Objects.Drawables; + +namespace osu.Game.Modes.Judgements +{ + /// + /// A drawable object which visualises the hit result of a . + /// + /// The type of judgement to visualise. + public class DrawableJudgement : Container + where TJudgement : Judgement + { + protected readonly TJudgement Judgement; + + protected readonly SpriteText JudgementText; + + /// + /// Creates a drawable which visualises a . + /// + /// The judgement to visualise. + public DrawableJudgement(TJudgement judgement) + { + Judgement = judgement; + + AutoSizeAxes = Axes.Both; + + string resultString = judgement.Result == HitResult.Hit ? judgement.ResultString : judgement.Result.GetDescription(); + + Children = new[] + { + JudgementText = new OsuSpriteText + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Text = resultString.ToUpper(), + Font = @"Venera", + TextSize = 16 + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + switch (Judgement.Result) + { + case HitResult.Miss: + Colour = colours.Red; + break; + } + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + FadeInFromZero(100, EasingTypes.OutQuint); + + switch (Judgement.Result) + { + case HitResult.Miss: + ScaleTo(1.6f); + ScaleTo(1, 100, EasingTypes.In); + + MoveToOffset(new Vector2(0, 100), 800, EasingTypes.InQuint); + RotateTo(40, 800, EasingTypes.InQuint); + + Delay(600); + FadeOut(200); + break; + case HitResult.Hit: + ScaleTo(0.9f); + ScaleTo(1, 500, EasingTypes.OutElastic); + + Delay(100); + FadeOut(400); + break; + } + + Expire(); + } + } +} diff --git a/osu.Game/Modes/Judgements/Judgement.cs b/osu.Game/Modes/Judgements/Judgement.cs new file mode 100644 index 0000000000..d916fc15de --- /dev/null +++ b/osu.Game/Modes/Judgements/Judgement.cs @@ -0,0 +1,35 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Modes.Objects.Drawables; + +namespace osu.Game.Modes.Judgements +{ + public abstract class Judgement + { + /// + /// Whether this judgement is the result of a hit or a miss. + /// + public HitResult? Result; + + /// + /// The offset at which this judgement occurred. + /// + public double TimeOffset; + + /// + /// The combo after this judgement was processed. + /// + public ulong? ComboAtHit; + + /// + /// The string representation for the result achieved. + /// + public abstract string ResultString { get; } + + /// + /// The string representation for the max result achievable. + /// + public abstract string MaxResultString { get; } + } +} \ No newline at end of file diff --git a/osu.Game/Modes/Judgements/JudgementInfo.cs b/osu.Game/Modes/Judgements/JudgementInfo.cs deleted file mode 100644 index 8e7539134e..0000000000 --- a/osu.Game/Modes/Judgements/JudgementInfo.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Modes.Objects.Drawables; - -namespace osu.Game.Modes.Judgements -{ - public class JudgementInfo - { - public ulong? ComboAtHit; - public HitResult? Result; - public double TimeOffset; - } -} \ No newline at end of file diff --git a/osu.Game/Modes/Mods/Mod.cs b/osu.Game/Modes/Mods/Mod.cs index 6a720a3574..8416ae6211 100644 --- a/osu.Game/Modes/Mods/Mod.cs +++ b/osu.Game/Modes/Mods/Mod.cs @@ -6,6 +6,7 @@ using osu.Game.Graphics; using osu.Game.Modes.Objects; using osu.Game.Modes.UI; using System; +using osu.Game.Modes.Scoring; namespace osu.Game.Modes.Mods { diff --git a/osu.Game/Modes/Objects/BezierApproximator.cs b/osu.Game/Modes/Objects/BezierApproximator.cs index b0f17afc7e..ee8e9b0e06 100644 --- a/osu.Game/Modes/Objects/BezierApproximator.cs +++ b/osu.Game/Modes/Objects/BezierApproximator.cs @@ -8,10 +8,10 @@ namespace osu.Game.Modes.Objects { public class BezierApproximator { - private int count; - private List controlPoints; - private Vector2[] subdivisionBuffer1; - private Vector2[] subdivisionBuffer2; + private readonly int count; + private readonly List controlPoints; + private readonly Vector2[] subdivisionBuffer1; + private readonly Vector2[] subdivisionBuffer2; private const float tolerance = 0.25f; private const float tolerance_sq = tolerance * tolerance; diff --git a/osu.Game/Modes/Objects/CircularArcApproximator.cs b/osu.Game/Modes/Objects/CircularArcApproximator.cs index af15f99e43..310b923b0b 100644 --- a/osu.Game/Modes/Objects/CircularArcApproximator.cs +++ b/osu.Game/Modes/Objects/CircularArcApproximator.cs @@ -10,9 +10,9 @@ namespace osu.Game.Modes.Objects { public class CircularArcApproximator { - private Vector2 a; - private Vector2 b; - private Vector2 c; + private readonly Vector2 a; + private readonly Vector2 b; + private readonly Vector2 c; private int amountPoints; diff --git a/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs index 6322614496..3998a3e385 100644 --- a/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs @@ -11,11 +11,12 @@ using osu.Game.Beatmaps.Samples; using osu.Game.Modes.Judgements; using Container = osu.Framework.Graphics.Containers.Container; using osu.Game.Modes.Objects.Types; +using OpenTK.Graphics; namespace osu.Game.Modes.Objects.Drawables { public abstract class DrawableHitObject : Container, IStateful - where TJudgement : JudgementInfo + where TJudgement : Judgement { public override bool HandleInput => Interactive; @@ -23,7 +24,7 @@ namespace osu.Game.Modes.Objects.Drawables public TJudgement Judgement; - protected abstract TJudgement CreateJudgementInfo(); + protected abstract TJudgement CreateJudgement(); protected abstract void UpdateState(ArmedState state); @@ -61,7 +62,7 @@ namespace osu.Game.Modes.Objects.Drawables //we may be setting a custom judgement in test cases or what not. if (Judgement == null) - Judgement = CreateJudgementInfo(); + Judgement = CreateJudgement(); //force application of the state that was set before we loaded. UpdateState(State); @@ -70,12 +71,17 @@ namespace osu.Game.Modes.Objects.Drawables public abstract class DrawableHitObject : DrawableHitObject where TObject : HitObject - where TJudgement : JudgementInfo + where TJudgement : Judgement { public event Action> OnJudgement; public TObject HitObject; + /// + /// The colour used for various elements of this DrawableHitObject. + /// + public Color4 AccentColour { get; protected set; } + protected DrawableHitObject(TObject hitObject) { HitObject = hitObject; diff --git a/osu.Game/Modes/Objects/SliderCurve.cs b/osu.Game/Modes/Objects/SliderCurve.cs index 8ab5097257..642a65af21 100644 --- a/osu.Game/Modes/Objects/SliderCurve.cs +++ b/osu.Game/Modes/Objects/SliderCurve.cs @@ -19,8 +19,8 @@ namespace osu.Game.Modes.Objects public Vector2 Offset; - private List calculatedPath = new List(); - private List cumulativeLength = new List(); + private readonly List calculatedPath = new List(); + private readonly List cumulativeLength = new List(); private List calculateSubpath(List subControlPoints) { diff --git a/osu.Game/Modes/Ruleset.cs b/osu.Game/Modes/Ruleset.cs index 013ea1d4fc..c97420fbe3 100644 --- a/osu.Game/Modes/Ruleset.cs +++ b/osu.Game/Modes/Ruleset.cs @@ -9,6 +9,7 @@ using osu.Game.Screens.Play; using System; using System.Collections.Concurrent; using System.Collections.Generic; +using osu.Game.Modes.Scoring; namespace osu.Game.Modes { @@ -21,9 +22,9 @@ namespace osu.Game.Modes public abstract class Ruleset { - private static ConcurrentDictionary availableRulesets = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary available_rulesets = new ConcurrentDictionary(); - public static IEnumerable PlayModes => availableRulesets.Keys; + public static IEnumerable PlayModes => available_rulesets.Keys; public virtual IEnumerable GetBeatmapStatistics(WorkingBeatmap beatmap) => new BeatmapStatistic[] { }; @@ -35,7 +36,7 @@ namespace osu.Game.Modes public abstract ScoreProcessor CreateScoreProcessor(); - public static void Register(Ruleset ruleset) => availableRulesets.TryAdd(ruleset.PlayMode, ruleset.GetType()); + public static void Register(Ruleset ruleset) => available_rulesets.TryAdd(ruleset.PlayMode, ruleset.GetType()); protected abstract PlayMode PlayMode { get; } @@ -49,7 +50,7 @@ namespace osu.Game.Modes { Type type; - if (!availableRulesets.TryGetValue(mode, out type)) + if (!available_rulesets.TryGetValue(mode, out type)) return null; return Activator.CreateInstance(type) as Ruleset; diff --git a/osu.Game/Modes/Score.cs b/osu.Game/Modes/Scoring/Score.cs similarity index 94% rename from osu.Game/Modes/Score.cs rename to osu.Game/Modes/Scoring/Score.cs index 55bbed8e77..fa74fba279 100644 --- a/osu.Game/Modes/Score.cs +++ b/osu.Game/Modes/Scoring/Score.cs @@ -3,11 +3,11 @@ using System; using Newtonsoft.Json; -using osu.Game.Users; using osu.Game.Database; using osu.Game.Modes.Mods; +using osu.Game.Users; -namespace osu.Game.Modes +namespace osu.Game.Modes.Scoring { public class Score { diff --git a/osu.Game/Modes/ScoreProcessor.cs b/osu.Game/Modes/Scoring/ScoreProcessor.cs similarity index 89% rename from osu.Game/Modes/ScoreProcessor.cs rename to osu.Game/Modes/Scoring/ScoreProcessor.cs index a6e29d53e1..f14b306e18 100644 --- a/osu.Game/Modes/ScoreProcessor.cs +++ b/osu.Game/Modes/Scoring/ScoreProcessor.cs @@ -1,15 +1,15 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Configuration; using System; using System.Collections.Generic; -using osu.Game.Modes.Judgements; -using osu.Game.Modes.UI; -using osu.Game.Modes.Objects; +using osu.Framework.Configuration; using osu.Game.Beatmaps; +using osu.Game.Modes.Judgements; +using osu.Game.Modes.Objects; +using osu.Game.Modes.UI; -namespace osu.Game.Modes +namespace osu.Game.Modes.Scoring { public abstract class ScoreProcessor { @@ -105,7 +105,7 @@ namespace osu.Game.Modes public abstract class ScoreProcessor : ScoreProcessor where TObject : HitObject - where TJudgement : JudgementInfo + where TJudgement : Judgement { /// /// All judgements held by this ScoreProcessor. @@ -122,7 +122,7 @@ namespace osu.Game.Modes { Judgements.Capacity = hitRenderer.Beatmap.HitObjects.Count; - hitRenderer.OnJudgement += addJudgement; + hitRenderer.OnJudgement += AddJudgement; ComputeTargets(hitRenderer.Beatmap); @@ -139,11 +139,11 @@ namespace osu.Game.Modes /// Adds a judgement to this ScoreProcessor. /// /// The judgement to add. - private void addJudgement(TJudgement judgement) + protected void AddJudgement(TJudgement judgement) { Judgements.Add(judgement); - UpdateCalculations(judgement); + OnNewJugement(judgement); judgement.ComboAtHit = (ulong)Combo.Value; @@ -158,7 +158,7 @@ namespace osu.Game.Modes /// /// Update any values that potentially need post-processing on a judgement change. /// - /// A new JudgementInfo that triggered this calculation. May be null. - protected abstract void UpdateCalculations(TJudgement newJudgement); + /// The judgement that triggered this calculation. + protected abstract void OnNewJugement(TJudgement judgement); } } \ No newline at end of file diff --git a/osu.Game/Modes/ScoreRank.cs b/osu.Game/Modes/Scoring/ScoreRank.cs similarity index 90% rename from osu.Game/Modes/ScoreRank.cs rename to osu.Game/Modes/Scoring/ScoreRank.cs index 433158f249..743f24ecd6 100644 --- a/osu.Game/Modes/ScoreRank.cs +++ b/osu.Game/Modes/Scoring/ScoreRank.cs @@ -3,7 +3,7 @@ using System.ComponentModel; -namespace osu.Game.Modes +namespace osu.Game.Modes.Scoring { public enum ScoreRank { diff --git a/osu.Game/Modes/UI/HitRenderer.cs b/osu.Game/Modes/UI/HitRenderer.cs index b82e3ada51..3d108b895d 100644 --- a/osu.Game/Modes/UI/HitRenderer.cs +++ b/osu.Game/Modes/UI/HitRenderer.cs @@ -14,6 +14,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using osu.Game.Modes.Scoring; namespace osu.Game.Modes.UI { @@ -143,7 +144,7 @@ namespace osu.Game.Modes.UI /// The type of Judgement of DrawableHitObjects contained by this HitRenderer. public abstract class HitRenderer : HitRenderer where TObject : HitObject - where TJudgement : JudgementInfo + where TJudgement : Judgement { public event Action OnJudgement; @@ -155,7 +156,7 @@ namespace osu.Game.Modes.UI /// protected Playfield Playfield; - private Container content; + private readonly Container content; protected HitRenderer(WorkingBeatmap beatmap) : base(beatmap) diff --git a/osu.Game/Modes/UI/HudOverlay.cs b/osu.Game/Modes/UI/HudOverlay.cs index 4b454797ce..a6c54e7f3a 100644 --- a/osu.Game/Modes/UI/HudOverlay.cs +++ b/osu.Game/Modes/UI/HudOverlay.cs @@ -9,6 +9,7 @@ using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Play; using System; +using osu.Game.Modes.Scoring; namespace osu.Game.Modes.UI { diff --git a/osu.Game/Modes/UI/ModIcon.cs b/osu.Game/Modes/UI/ModIcon.cs index 65a570fea7..35459985c9 100644 --- a/osu.Game/Modes/UI/ModIcon.cs +++ b/osu.Game/Modes/UI/ModIcon.cs @@ -10,7 +10,8 @@ namespace osu.Game.Modes.UI { public class ModIcon : Container { - private TextAwesome modIcon, background; + private readonly TextAwesome modIcon; + private readonly TextAwesome background; private float iconSize = 80; public float IconSize diff --git a/osu.Game/Modes/UI/Playfield.cs b/osu.Game/Modes/UI/Playfield.cs index e5babecad9..eff06ce80f 100644 --- a/osu.Game/Modes/UI/Playfield.cs +++ b/osu.Game/Modes/UI/Playfield.cs @@ -13,7 +13,7 @@ namespace osu.Game.Modes.UI { public abstract class Playfield : Container where TObject : HitObject - where TJudgement : JudgementInfo + where TJudgement : Judgement { /// /// The HitObjects contained in this Playfield. @@ -23,7 +23,7 @@ namespace osu.Game.Modes.UI internal Container ScaledContent; protected override Container Content => content; - private Container content; + private readonly Container content; /// /// A container for keeping track of DrawableHitObjects. diff --git a/osu.Game/Modes/UI/StandardHealthDisplay.cs b/osu.Game/Modes/UI/StandardHealthDisplay.cs index c639f3d482..58b44eabd3 100644 --- a/osu.Game/Modes/UI/StandardHealthDisplay.cs +++ b/osu.Game/Modes/UI/StandardHealthDisplay.cs @@ -15,7 +15,7 @@ namespace osu.Game.Modes.UI { public class StandardHealthDisplay : HealthDisplay { - private Container fill; + private readonly Container fill; public StandardHealthDisplay() { diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 6bb61ca83c..f39dec47e1 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -17,7 +17,7 @@ namespace osu.Game.Online.API { public class APIAccess : IUpdateable { - private OAuth authentication; + private readonly OAuth authentication; public string Endpoint = @"https://new.ppy.sh"; private const string client_id = @"5"; @@ -44,9 +44,9 @@ namespace osu.Game.Online.API protected bool HasLogin => Token != null || !string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(Password); // ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable (should dispose of this or at very least keep a reference). - private Thread thread; + private readonly Thread thread; - private Logger log; + private readonly Logger log; public APIAccess() { @@ -57,7 +57,7 @@ namespace osu.Game.Online.API thread.Start(); } - private List components = new List(); + private readonly List components = new List(); public void Register(IOnlineComponent component) { diff --git a/osu.Game/Online/API/Requests/GetMessagesRequest.cs b/osu.Game/Online/API/Requests/GetMessagesRequest.cs index af5a96a66a..cf52f9ccd3 100644 --- a/osu.Game/Online/API/Requests/GetMessagesRequest.cs +++ b/osu.Game/Online/API/Requests/GetMessagesRequest.cs @@ -9,7 +9,7 @@ namespace osu.Game.Online.API.Requests { public class GetMessagesRequest : APIRequest> { - private List channels; + private readonly List channels; private long? since; public GetMessagesRequest(List channels, long? sinceId) diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index 8c0fc4383a..66c5e6c72d 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Newtonsoft.Json; using osu.Framework.IO.Network; using osu.Game.Database; -using osu.Game.Modes; +using osu.Game.Modes.Scoring; namespace osu.Game.Online.API.Requests { diff --git a/osu.Game/Online/Chat/Drawables/DrawableChannel.cs b/osu.Game/Online/Chat/Drawables/DrawableChannel.cs index eb8653976a..e3101e8fd7 100644 --- a/osu.Game/Online/Chat/Drawables/DrawableChannel.cs +++ b/osu.Game/Online/Chat/Drawables/DrawableChannel.cs @@ -14,8 +14,8 @@ namespace osu.Game.Online.Chat.Drawables public class DrawableChannel : Container { private readonly Channel channel; - private FillFlowContainer flow; - private ScrollContainer scroll; + private readonly FillFlowContainer flow; + private readonly ScrollContainer scroll; public DrawableChannel(Channel channel) { diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 5e7c47c9a9..8ac809e591 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -26,6 +26,7 @@ using osu.Framework.Graphics.Primitives; using System.Threading.Tasks; using osu.Framework.Threading; using osu.Game.Graphics; +using osu.Game.Modes.Scoring; using osu.Game.Overlays.Notifications; using osu.Game.Screens.Play; @@ -60,7 +61,7 @@ namespace osu.Game public Bindable PlayMode; - private string[] args; + private readonly string[] args; private OptionsOverlay options; diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index efd366adb1..73ecf20891 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -31,11 +31,11 @@ namespace osu.Game.Overlays private ScheduledDelegate messageRequest; - private Container content; + private readonly Container content; protected override Container Content => content; - private FocusedTextBox inputTextBox; + private readonly FocusedTextBox inputTextBox; private APIAccess api; diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index 7936339e0c..bbac5ca786 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -27,10 +27,12 @@ namespace osu.Game.Overlays.Dialog private readonly Vector2 ringMinifiedSize = new Vector2(20f); private readonly Vector2 buttonsEnterSpacing = new Vector2(0f, 50f); - private Container content, ring; - private FillFlowContainer buttonsContainer; - private TextAwesome iconText; - private SpriteText header, body; + private readonly Container content; + private readonly Container ring; + private readonly FillFlowContainer buttonsContainer; + private readonly TextAwesome iconText; + private readonly SpriteText header; + private readonly SpriteText body; public FontAwesome Icon { diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index 12cca5275a..1769702c40 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -13,7 +13,7 @@ namespace osu.Game.Overlays { public class DialogOverlay : FocusedOverlayContainer { - private Container dialogContainer; + private readonly Container dialogContainer; private PopupDialog currentDialog; public void Push(PopupDialog dialog) diff --git a/osu.Game/Overlays/DragBar.cs b/osu.Game/Overlays/DragBar.cs index 2e8eb272d8..a9cf735171 100644 --- a/osu.Game/Overlays/DragBar.cs +++ b/osu.Game/Overlays/DragBar.cs @@ -11,7 +11,7 @@ namespace osu.Game.Overlays { public class DragBar : Container { - private Box fill; + private readonly Box fill; public Action SeekRequested; private bool isDragging; diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index acd5f37092..cd045aae41 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -23,8 +23,8 @@ namespace osu.Game.Overlays.Mods public class ModButton : FillFlowContainer { private ModIcon foregroundIcon { get; set; } - private SpriteText text; - private Container iconsContainer; + private readonly SpriteText text; + private readonly Container iconsContainer; private SampleChannel sampleOn, sampleOff; public Action Action; // Passed the selected mod or null if none diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 489af21ef4..0e93a5520d 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Mods { public abstract class ModSection : Container { - private OsuSpriteText headerLabel; + private readonly OsuSpriteText headerLabel; public FillFlowContainer ButtonsContainer { get; } diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 3a685be7fa..d4ceb29c23 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -30,10 +30,11 @@ namespace osu.Game.Overlays.Mods private Color4 lowMultiplierColour, highMultiplierColour; - private OsuSpriteText rankedLabel, multiplierLabel; - private FillFlowContainer rankedMultiplerContainer; + private readonly OsuSpriteText rankedLabel; + private readonly OsuSpriteText multiplierLabel; + private readonly FillFlowContainer rankedMultiplerContainer; - private FillFlowContainer modSectionsContainer; + private readonly FillFlowContainer modSectionsContainer; public readonly Bindable> SelectedMods = new Bindable>(); diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 32061974ad..a45dcaacb8 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -35,7 +35,7 @@ namespace osu.Game.Overlays private SpriteText title, artist; private List playList; - private List playHistory = new List(); + private readonly List playHistory = new List(); private int playListIndex; private int playHistoryIndex = -1; @@ -415,8 +415,8 @@ namespace osu.Game.Overlays private class MusicControllerBackground : BufferedContainer { - private Sprite sprite; - private WorkingBeatmap beatmap; + private readonly Sprite sprite; + private readonly WorkingBeatmap beatmap; public MusicControllerBackground(WorkingBeatmap beatmap = null) { diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index e003a0e9dd..961e83e958 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -35,9 +35,9 @@ namespace osu.Game.Overlays.Notifications public virtual bool DisplayOnTop => true; protected NotificationLight Light; - private CloseButton closeButton; + private readonly CloseButton closeButton; protected Container IconContent; - private Container content; + private readonly Container content; protected override Container Content => content; diff --git a/osu.Game/Overlays/Notifications/NotificationSection.cs b/osu.Game/Overlays/Notifications/NotificationSection.cs index e5debc9c9b..ee74c5e2cb 100644 --- a/osu.Game/Overlays/Notifications/NotificationSection.cs +++ b/osu.Game/Overlays/Notifications/NotificationSection.cs @@ -135,7 +135,7 @@ namespace osu.Game.Overlays.Notifications private class ClearAllButton : ClickableContainer { - private OsuSpriteText text; + private readonly OsuSpriteText text; public ClearAllButton() { diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index 9dcee5c8af..5179e85df1 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -104,12 +104,12 @@ namespace osu.Game.Overlays.Notifications public override bool DisplayOnTop => false; - private ProgressBar progressBar; + private readonly ProgressBar progressBar; private Color4 colourQueued; private Color4 colourActive; private Color4 colourCancelled; - private SpriteText textDrawable; + private readonly SpriteText textDrawable; public ProgressNotification() { diff --git a/osu.Game/Overlays/Notifications/SimpleNotification.cs b/osu.Game/Overlays/Notifications/SimpleNotification.cs index 19034d6d1c..ba2ec02651 100644 --- a/osu.Game/Overlays/Notifications/SimpleNotification.cs +++ b/osu.Game/Overlays/Notifications/SimpleNotification.cs @@ -34,8 +34,8 @@ namespace osu.Game.Overlays.Notifications } } - private SpriteText textDrawable; - private TextAwesome iconDrawable; + private readonly SpriteText textDrawable; + private readonly TextAwesome iconDrawable; protected Box IconBackgound; diff --git a/osu.Game/Overlays/Options/OptionDropdown.cs b/osu.Game/Overlays/Options/OptionDropdown.cs index d46e786bca..9ae02a17d3 100644 --- a/osu.Game/Overlays/Options/OptionDropdown.cs +++ b/osu.Game/Overlays/Options/OptionDropdown.cs @@ -15,8 +15,8 @@ namespace osu.Game.Overlays.Options { public class OptionDropdown : FillFlowContainer { - private Dropdown dropdown; - private SpriteText text; + private readonly Dropdown dropdown; + private readonly SpriteText text; public string LabelText { diff --git a/osu.Game/Overlays/Options/OptionSlider.cs b/osu.Game/Overlays/Options/OptionSlider.cs index 2f09c51655..772d2c37e6 100644 --- a/osu.Game/Overlays/Options/OptionSlider.cs +++ b/osu.Game/Overlays/Options/OptionSlider.cs @@ -14,8 +14,8 @@ namespace osu.Game.Overlays.Options { public class OptionSlider : FillFlowContainer where T : struct { - private SliderBar slider; - private SpriteText text; + private readonly SliderBar slider; + private readonly SpriteText text; public string LabelText { diff --git a/osu.Game/Overlays/Options/OptionsSection.cs b/osu.Game/Overlays/Options/OptionsSection.cs index 25e891b5c9..777a4fe703 100644 --- a/osu.Game/Overlays/Options/OptionsSection.cs +++ b/osu.Game/Overlays/Options/OptionsSection.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Options public abstract FontAwesome Icon { get; } public abstract string Header { get; } - private SpriteText headerLabel; + private readonly SpriteText headerLabel; protected OptionsSection() { diff --git a/osu.Game/Overlays/Options/OptionsSubsection.cs b/osu.Game/Overlays/Options/OptionsSubsection.cs index a6baf0a6cd..9fd2e8fb1e 100644 --- a/osu.Game/Overlays/Options/OptionsSubsection.cs +++ b/osu.Game/Overlays/Options/OptionsSubsection.cs @@ -13,7 +13,7 @@ namespace osu.Game.Overlays.Options { protected override Container Content => content; - private Container content; + private readonly Container content; protected abstract string Header { get; } diff --git a/osu.Game/Overlays/Options/Sidebar.cs b/osu.Game/Overlays/Options/Sidebar.cs index b0618ed3a6..e66c6de742 100644 --- a/osu.Game/Overlays/Options/Sidebar.cs +++ b/osu.Game/Overlays/Options/Sidebar.cs @@ -15,7 +15,7 @@ namespace osu.Game.Overlays.Options { public class Sidebar : Container { - private FillFlowContainer content; + private readonly FillFlowContainer content; internal const float DEFAULT_WIDTH = ToolbarButton.WIDTH; internal const int EXPANDED_WIDTH = 200; protected override Container Content => content; diff --git a/osu.Game/Overlays/Options/SidebarButton.cs b/osu.Game/Overlays/Options/SidebarButton.cs index 729a9be326..7c0049e8c7 100644 --- a/osu.Game/Overlays/Options/SidebarButton.cs +++ b/osu.Game/Overlays/Options/SidebarButton.cs @@ -16,11 +16,11 @@ namespace osu.Game.Overlays.Options { public class SidebarButton : Container { - private TextAwesome drawableIcon; - private SpriteText headerText; - private Box backgroundBox; - private Box selectionIndicator; - private Container text; + private readonly TextAwesome drawableIcon; + private readonly SpriteText headerText; + private readonly Box backgroundBox; + private readonly Box selectionIndicator; + private readonly Container text; public Action Action; private OptionsSection section; diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 657334d1bb..ff1e37aafb 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -23,8 +23,8 @@ namespace osu.Game.Overlays.Toolbar public Action OnHome; public Action OnPlayModeChange; - private ToolbarModeSelector modeSelector; - private ToolbarUserArea userArea; + private readonly ToolbarModeSelector modeSelector; + private readonly ToolbarUserArea userArea; protected override bool HideOnEscape => false; @@ -90,8 +90,8 @@ namespace osu.Game.Overlays.Toolbar public class ToolbarBackground : Container { - private Box solidBackground; - private Box gradientBackground; + private readonly Box solidBackground; + private readonly Box gradientBackground; public ToolbarBackground() { diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index be4a23722f..fa1e1abf2a 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -63,9 +63,9 @@ namespace osu.Game.Overlays.Toolbar protected TextAwesome DrawableIcon; protected SpriteText DrawableText; protected Box HoverBackground; - private FillFlowContainer tooltipContainer; - private SpriteText tooltip1; - private SpriteText tooltip2; + private readonly FillFlowContainer tooltipContainer; + private readonly SpriteText tooltip1; + private readonly SpriteText tooltip2; protected FillFlowContainer Flow; private SampleChannel sampleClick; diff --git a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs index 30474ad796..a6df237f20 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs @@ -19,8 +19,8 @@ namespace osu.Game.Overlays.Toolbar { private const float padding = 10; - private FillFlowContainer modeButtons; - private Drawable modeButtonLine; + private readonly FillFlowContainer modeButtons; + private readonly Drawable modeButtonLine; private ToolbarModeButton activeButton; public Action OnPlayModeChange; diff --git a/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs b/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs index 991c76e164..023d9cfea7 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs @@ -11,7 +11,7 @@ namespace osu.Game.Overlays.Toolbar { internal class ToolbarOverlayToggleButton : ToolbarButton { - private Box stateBackground; + private readonly Box stateBackground; private OverlayContainer stateContainer; diff --git a/osu.Game/Overlays/Toolbar/ToolbarUserArea.cs b/osu.Game/Overlays/Toolbar/ToolbarUserArea.cs index 7f28764b17..1928c0fc1f 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarUserArea.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarUserArea.cs @@ -11,7 +11,7 @@ namespace osu.Game.Overlays.Toolbar internal class ToolbarUserArea : Container { public LoginOverlay LoginOverlay; - private ToolbarUserButton button; + private readonly ToolbarUserButton button; public override RectangleF BoundingBox => button.BoundingBox; diff --git a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs index 52c9ab5aa4..7e266a2b43 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs @@ -14,7 +14,7 @@ namespace osu.Game.Overlays.Toolbar { internal class ToolbarUserButton : ToolbarButton, IOnlineComponent { - private Avatar avatar; + private readonly Avatar avatar; public ToolbarUserButton() { diff --git a/osu.Game/Overlays/WaveOverlayContainer.cs b/osu.Game/Overlays/WaveOverlayContainer.cs index 747b46aecc..07b76c188d 100644 --- a/osu.Game/Overlays/WaveOverlayContainer.cs +++ b/osu.Game/Overlays/WaveOverlayContainer.cs @@ -20,9 +20,12 @@ namespace osu.Game.Overlays private const EasingTypes easing_show = EasingTypes.OutSine; private const EasingTypes easing_hide = EasingTypes.InSine; - private Wave firstWave, secondWave, thirdWave, fourthWave; + private readonly Wave firstWave; + private readonly Wave secondWave; + private readonly Wave thirdWave; + private readonly Wave fourthWave; - private Container wavesContainer; + private readonly Container wavesContainer; private readonly Container contentContainer; diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index 9c352aad80..81319553ba 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -72,7 +72,7 @@ namespace osu.Game.Screens.Backgrounds private class BeatmapBackground : Background { - private WorkingBeatmap beatmap; + private readonly WorkingBeatmap beatmap; public BeatmapBackground(WorkingBeatmap beatmap) { diff --git a/osu.Game/Screens/GameScreenWhiteBox.cs b/osu.Game/Screens/GameScreenWhiteBox.cs index f659d3681e..60e8edb6e1 100644 --- a/osu.Game/Screens/GameScreenWhiteBox.cs +++ b/osu.Game/Screens/GameScreenWhiteBox.cs @@ -19,14 +19,14 @@ namespace osu.Game.Screens { public class ScreenWhiteBox : OsuScreen { - private BackButton popButton; + private readonly BackButton popButton; private const double transition_time = 1000; protected virtual IEnumerable PossibleChildren => null; - private Container textContainer; - private Box box; + private readonly Container textContainer; + private readonly Box box; protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg2"); diff --git a/osu.Game/Screens/Menu/Button.cs b/osu.Game/Screens/Menu/Button.cs index 52872c858b..fe9f7b4bf6 100644 --- a/osu.Game/Screens/Menu/Button.cs +++ b/osu.Game/Screens/Menu/Button.cs @@ -26,13 +26,13 @@ namespace osu.Game.Screens.Menu /// public class Button : Container, IStateful { - private Container iconText; - private Container box; - private Box boxHoverLayer; - private TextAwesome icon; - private string internalName; - private Action clickAction; - private Key triggerKey; + private readonly Container iconText; + private readonly Container box; + private readonly Box boxHoverLayer; + private readonly TextAwesome icon; + private readonly string internalName; + private readonly Action clickAction; + private readonly Key triggerKey; private SampleChannel sampleClick; protected override bool InternalContains(Vector2 screenSpacePos) => box.Contains(screenSpacePos); @@ -132,7 +132,7 @@ namespace osu.Game.Screens.Menu icon.ScaleTo(1, 500, EasingTypes.OutElasticHalf); - double offset = 0; //(1 - Game.Audio.SyncBeatProgress) * duration; + const double offset = 0; //(1 - Game.Audio.SyncBeatProgress) * duration; double startTime = Time.Current + offset; icon.RotateTo(10, offset, EasingTypes.InOutSine); diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 7903062929..ac7e419765 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -32,7 +32,7 @@ namespace osu.Game.Screens.Menu private Toolbar toolbar; - private FlowContainerWithOrigin buttonFlow; + private readonly FlowContainerWithOrigin buttonFlow; //todo: make these non-internal somehow. internal const float BUTTON_AREA_HEIGHT = 100; @@ -41,16 +41,16 @@ namespace osu.Game.Screens.Menu public const int EXIT_DELAY = 3000; - private OsuLogo osuLogo; - private Drawable iconFacade; - private Container buttonArea; - private Box buttonAreaBackground; + private readonly OsuLogo osuLogo; + private readonly Drawable iconFacade; + private readonly Container buttonArea; + private readonly Box buttonAreaBackground; - private Button backButton; - private Button settingsButton; + private readonly Button backButton; + private readonly Button settingsButton; - private List