diff --git a/osu-framework b/osu-framework index 169f0a758c..476c8792ee 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 169f0a758c6b565ee42832f99bf4b5303f4413a6 +Subproject commit 476c8792ee6b42cf4e5f0362b4eb09c0230807d8 diff --git a/osu-resources b/osu-resources index 93eb5bf99b..9533590f83 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit 93eb5bf99bb642bf339d7dce09c2d946412dadd6 +Subproject commit 9533590f839aa6e27ed7f8b9064a0e7dc08ad861 diff --git a/osu.Desktop.Deploy/Program.cs b/osu.Desktop.Deploy/Program.cs index 7c6efa7f70..6c96459a92 100644 --- a/osu.Desktop.Deploy/Program.cs +++ b/osu.Desktop.Deploy/Program.cs @@ -40,7 +40,7 @@ namespace osu.Desktop.Deploy /// /// How many previous build deltas we want to keep when publishing. /// - const int keep_delta_count = 3; + private const int keep_delta_count = 3; private static string codeSigningCmd => string.IsNullOrEmpty(codeSigningPassword) ? "" : $"-n \"/a /f {codeSigningCertPath} /p {codeSigningPassword} /t http://timestamp.comodoca.com/authenticode\""; @@ -172,10 +172,10 @@ namespace osu.Desktop.Deploy } //remove excess deltas - var deltas = releaseLines.Where(l => l.Filename.Contains("-delta")); - if (deltas.Count() > keep_delta_count) + var deltas = releaseLines.Where(l => l.Filename.Contains("-delta")).ToArray(); + if (deltas.Length > keep_delta_count) { - foreach (var l in deltas.Take(deltas.Count() - keep_delta_count)) + foreach (var l in deltas.Take(deltas.Length - keep_delta_count)) { write($"- Removing old delta {l.Filename}", ConsoleColor.Yellow); File.Delete(Path.Combine(ReleasesFolder, l.Filename)); @@ -342,7 +342,10 @@ namespace osu.Desktop.Deploy }; Process p = Process.Start(psi); + if (p == null) return false; + string output = p.StandardOutput.ReadToEnd(); + if (p.ExitCode == 0) return true; write(output); diff --git a/osu.Desktop.VisualTests/Platform/TestStorage.cs b/osu.Desktop.VisualTests/Platform/TestStorage.cs index c09c955f9c..c5502d5d5d 100644 --- a/osu.Desktop.VisualTests/Platform/TestStorage.cs +++ b/osu.Desktop.VisualTests/Platform/TestStorage.cs @@ -23,7 +23,7 @@ namespace osu.Desktop.VisualTests.Platform platform = new SQLitePlatformWin32(); else platform = new SQLitePlatformGeneric(); - return new SQLiteConnection(platform, $@":memory:"); + return new SQLiteConnection(platform, @":memory:"); } } } \ No newline at end of file diff --git a/osu.Desktop.VisualTests/Tests/TestCaseBeatmapOptionsOverlay.cs b/osu.Desktop.VisualTests/Tests/TestCaseBeatmapOptionsOverlay.cs index be17f0c379..fea9eec6f8 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseBeatmapOptionsOverlay.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseBeatmapOptionsOverlay.cs @@ -4,9 +4,9 @@ using osu.Framework.Screens.Testing; using osu.Game.Screens.Select.Options; -namespace osu.Desktop.VisualTests +namespace osu.Desktop.VisualTests.Tests { - class TestCaseBeatmapOptionsOverlay : TestCase + internal class TestCaseBeatmapOptionsOverlay : TestCase { public override string Description => @"Beatmap options in song select"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseChatDisplay.cs b/osu.Desktop.VisualTests/Tests/TestCaseChatDisplay.cs index f2796a5404..08765a9b0f 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseChatDisplay.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseChatDisplay.cs @@ -8,7 +8,7 @@ using osu.Game.Overlays; namespace osu.Desktop.VisualTests.Tests { - class TestCaseChatDisplay : TestCase + internal class TestCaseChatDisplay : TestCase { private ScheduledDelegate messageRequest; @@ -18,7 +18,7 @@ namespace osu.Desktop.VisualTests.Tests { base.Reset(); - Add(new ChatOverlay() + Add(new ChatOverlay { State = Visibility.Visible }); diff --git a/osu.Desktop.VisualTests/Tests/TestCaseDialogOverlay.cs b/osu.Desktop.VisualTests/Tests/TestCaseDialogOverlay.cs index 44136b7999..c9edcb8a54 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseDialogOverlay.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseDialogOverlay.cs @@ -8,11 +8,11 @@ using osu.Game.Overlays.Dialog; namespace osu.Desktop.VisualTests.Tests { - class TestCaseDialogOverlay : TestCase + internal class TestCaseDialogOverlay : TestCase { public override string Description => @"Display dialogs"; - DialogOverlay overlay; + private DialogOverlay overlay; public override void Reset() { diff --git a/osu.Desktop.VisualTests/Tests/TestCaseDrawings.cs b/osu.Desktop.VisualTests/Tests/TestCaseDrawings.cs index eb34fd7a52..39e55f7174 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseDrawings.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseDrawings.cs @@ -2,23 +2,16 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE using System.Collections.Generic; -using osu.Framework.Allocation; -using osu.Framework.Platform; using osu.Framework.Screens.Testing; using osu.Game.Screens.Tournament; using osu.Game.Screens.Tournament.Teams; namespace osu.Desktop.VisualTests.Tests { - class TestCaseDrawings : TestCase + internal class TestCaseDrawings : TestCase { public override string Description => "Tournament drawings"; - [BackgroundDependencyLoader] - private void load(Storage storage) - { - } - public override void Reset() { base.Reset(); @@ -29,7 +22,7 @@ namespace osu.Desktop.VisualTests.Tests }); } - class TestTeamList : ITeamList + private class TestTeamList : ITeamList { public IEnumerable Teams { get; } = new[] { diff --git a/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs b/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs index 3c3ba7b0dc..79616d777f 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseGamefield.cs @@ -18,7 +18,7 @@ using OpenTK; namespace osu.Desktop.VisualTests.Tests { - class TestCaseGamefield : TestCase + internal class TestCaseGamefield : TestCase { public override string Description => @"Showing hitobjects and what not."; @@ -31,7 +31,7 @@ namespace osu.Desktop.VisualTests.Tests int time = 500; for (int i = 0; i < 100; i++) { - objects.Add(new HitCircle() + objects.Add(new HitCircle { StartTime = time, Position = new Vector2(RNG.Next(0, 512), RNG.Next(0, 384)), diff --git a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs b/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs index ba1b3b4f37..aa76d8f98a 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs @@ -17,12 +17,12 @@ using OpenTK.Graphics; namespace osu.Desktop.VisualTests.Tests { - class TestCaseHitObjects : TestCase + internal class TestCaseHitObjects : TestCase { private StopwatchClock rateAdjustClock; private FramedClock framedClock; - bool auto = false; + private bool auto; public TestCaseHitObjects() { @@ -31,9 +31,9 @@ namespace osu.Desktop.VisualTests.Tests playbackSpeed.ValueChanged += delegate { rateAdjustClock.Rate = playbackSpeed.Value; }; } - HitObjectType mode = HitObjectType.Slider; + private HitObjectType mode = HitObjectType.Slider; - BindableNumber playbackSpeed = new BindableDouble(0.5) { MinValue = 0, MaxValue = 1 }; + private BindableNumber playbackSpeed = new BindableDouble(0.5) { MinValue = 0, MaxValue = 1 }; private Container playfieldContainer; private Container approachContainer; @@ -61,7 +61,7 @@ namespace osu.Desktop.VisualTests.Tests add(new DrawableSlider(new Slider { StartTime = framedClock.CurrentTime + 600, - ControlPoints = new List() + ControlPoints = new List { new Vector2(-200, 0), new Vector2(400, 0), @@ -93,7 +93,7 @@ namespace osu.Desktop.VisualTests.Tests AddButton(@"slider", () => load(HitObjectType.Slider)); AddButton(@"spinner", () => load(HitObjectType.Spinner)); - AddToggle(@"auto", (state) => { auto = state; load(mode); }); + AddToggle(@"auto", state => { auto = state; load(mode); }); ButtonsContainer.Add(new SpriteText { Text = "Playback Speed" }); ButtonsContainer.Add(new BasicSliderBar @@ -122,8 +122,9 @@ namespace osu.Desktop.VisualTests.Tests load(mode); } - int depth; - void add(DrawableHitObject h) + private int depth; + + private void add(DrawableHitObject h) { h.Anchor = Anchor.Centre; h.Depth = depth++; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseKeyCounter.cs b/osu.Desktop.VisualTests/Tests/TestCaseKeyCounter.cs index cd4374336d..9ad439bfbe 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseKeyCounter.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseKeyCounter.cs @@ -15,7 +15,7 @@ using osu.Game.Screens.Play; namespace osu.Desktop.VisualTests.Tests { - class TestCaseKeyCounter : TestCase + internal class TestCaseKeyCounter : TestCase { public override string Description => @"Tests key counter"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseMenuButtonSystem.cs b/osu.Desktop.VisualTests/Tests/TestCaseMenuButtonSystem.cs index cb138c899a..36dc3945e2 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseMenuButtonSystem.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseMenuButtonSystem.cs @@ -9,7 +9,7 @@ using OpenTK.Graphics; namespace osu.Desktop.VisualTests.Tests { - class TestCaseMenuButtonSystem : TestCase + internal class TestCaseMenuButtonSystem : TestCase { public override string Description => @"Main menu button system"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseModSelectOverlay.cs b/osu.Desktop.VisualTests/Tests/TestCaseModSelectOverlay.cs index 3ac7b5f9aa..eaaa531691 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseModSelectOverlay.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseModSelectOverlay.cs @@ -8,7 +8,7 @@ using osu.Game.Modes; namespace osu.Desktop.VisualTests.Tests { - class TestCaseModSelectOverlay : TestCase + internal class TestCaseModSelectOverlay : TestCase { public override string Description => @"Tests the mod select overlay"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseMusicController.cs b/osu.Desktop.VisualTests/Tests/TestCaseMusicController.cs index 33ee7fa5a0..f44f662321 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseMusicController.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseMusicController.cs @@ -9,7 +9,7 @@ using osu.Framework.Graphics.Containers; namespace osu.Desktop.VisualTests.Tests { - class TestCaseMusicController : TestCase + internal class TestCaseMusicController : TestCase { public override string Description => @"Tests music controller ui."; @@ -30,7 +30,7 @@ namespace osu.Desktop.VisualTests.Tests Anchor = Anchor.Centre }; Add(mc); - AddToggle(@"Show", (state) => mc.State = state ? Visibility.Visible : Visibility.Hidden); + AddToggle(@"Show", state => mc.State = state ? Visibility.Visible : Visibility.Hidden); } } } diff --git a/osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs b/osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs index 52eb2f65d3..c3a9064e69 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseNotificationManager.cs @@ -12,11 +12,11 @@ using osu.Framework.Graphics.Containers; namespace osu.Desktop.VisualTests.Tests { - class TestCaseNotificationManager : TestCase + internal class TestCaseNotificationManager : TestCase { public override string Description => @"I handle notifications"; - NotificationManager manager; + private NotificationManager manager; public override void Reset() { @@ -30,7 +30,7 @@ namespace osu.Desktop.VisualTests.Tests Origin = Anchor.TopRight, }); - AddToggle(@"show", (state) => manager.State = state ? Visibility.Visible : Visibility.Hidden); + AddToggle(@"show", state => manager.State = state ? Visibility.Visible : Visibility.Hidden); AddButton(@"simple #1", sendNotification1); AddButton(@"simple #2", sendNotification2); @@ -95,7 +95,7 @@ namespace osu.Desktop.VisualTests.Tests progressingNotifications.Add(n); } - List progressingNotifications = new List(); + private List progressingNotifications = new List(); private void sendProgress1() { diff --git a/osu.Desktop.VisualTests/Tests/TestCaseOptions.cs b/osu.Desktop.VisualTests/Tests/TestCaseOptions.cs index dd58498383..1b4ecd726a 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseOptions.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseOptions.cs @@ -6,7 +6,7 @@ using osu.Game.Overlays; namespace osu.Desktop.VisualTests.Tests { - class TestCaseOptions : TestCase + internal class TestCaseOptions : TestCase { public override string Description => @"Tests the options overlay"; diff --git a/osu.Desktop.VisualTests/Tests/TestCasePauseOverlay.cs b/osu.Desktop.VisualTests/Tests/TestCasePauseOverlay.cs index cb85b2cbec..ad8039bc66 100644 --- a/osu.Desktop.VisualTests/Tests/TestCasePauseOverlay.cs +++ b/osu.Desktop.VisualTests/Tests/TestCasePauseOverlay.cs @@ -7,7 +7,7 @@ using osu.Game.Screens.Play; namespace osu.Desktop.VisualTests.Tests { - class TestCasePauseOverlay : TestCase + internal class TestCasePauseOverlay : TestCase { public override string Description => @"Tests the pause overlay"; diff --git a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs b/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs index cc2134eb55..84bb1cfde6 100644 --- a/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs +++ b/osu.Desktop.VisualTests/Tests/TestCasePlaySongSelect.cs @@ -11,7 +11,7 @@ using osu.Game.Screens.Select; namespace osu.Desktop.VisualTests.Tests { - class TestCasePlaySongSelect : TestCase + internal class TestCasePlaySongSelect : TestCase { private BeatmapDatabase db, oldDb; private TestStorage storage; diff --git a/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs b/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs index 1bb8144fc4..54d715ae64 100644 --- a/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs +++ b/osu.Desktop.VisualTests/Tests/TestCasePlayer.cs @@ -18,24 +18,30 @@ using OpenTK.Graphics; namespace osu.Desktop.VisualTests.Tests { - class TestCasePlayer : TestCase + internal class TestCasePlayer : TestCase { - private WorkingBeatmap beatmap; + protected Player Player; + private BeatmapDatabase db; + public override string Description => @"Showing everything to play the game."; [BackgroundDependencyLoader] private void load(BeatmapDatabase db) { - var beatmapInfo = db.Query().Where(b => b.Mode == PlayMode.Osu).FirstOrDefault(); - if (beatmapInfo != null) - beatmap = db.GetWorkingBeatmap(beatmapInfo); + this.db = db; } public override void Reset() { base.Reset(); + WorkingBeatmap beatmap = null; + + var beatmapInfo = db.Query().Where(b => b.Mode == PlayMode.Osu).FirstOrDefault(); + if (beatmapInfo != null) + beatmap = db.GetWorkingBeatmap(beatmapInfo); + if (beatmap?.Track == null) { var objects = new List(); @@ -43,7 +49,7 @@ namespace osu.Desktop.VisualTests.Tests int time = 1500; for (int i = 0; i < 50; i++) { - objects.Add(new HitCircle() + objects.Add(new HitCircle { StartTime = time, Position = new Vector2(i % 4 == 0 || i % 4 == 2 ? 0 : 512, @@ -81,17 +87,21 @@ namespace osu.Desktop.VisualTests.Tests Colour = Color4.Black, }); - Add(new PlayerLoader(new Player - { - PreferredPlayMode = PlayMode.Osu, - Beatmap = beatmap - }) + Add(new PlayerLoader(Player = CreatePlayer(beatmap)) { Beatmap = beatmap }); } - class TestWorkingBeatmap : WorkingBeatmap + protected virtual Player CreatePlayer(WorkingBeatmap beatmap) + { + return new Player + { + Beatmap = beatmap + }; + } + + private class TestWorkingBeatmap : WorkingBeatmap { public TestWorkingBeatmap(Beatmap beatmap) : base(beatmap.BeatmapInfo, beatmap.BeatmapInfo.BeatmapSet) diff --git a/osu.Desktop.VisualTests/Tests/TestCaseReplay.cs b/osu.Desktop.VisualTests/Tests/TestCaseReplay.cs new file mode 100644 index 0000000000..64b33a78c9 --- /dev/null +++ b/osu.Desktop.VisualTests/Tests/TestCaseReplay.cs @@ -0,0 +1,40 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.IO; +using osu.Framework.Allocation; +using osu.Framework.Input.Handlers; +using osu.Framework.Platform; +using osu.Game.Beatmaps; +using osu.Game.Database; +using osu.Game.Modes; +using osu.Game.Screens.Play; + +namespace osu.Desktop.VisualTests.Tests +{ + class TestCaseReplay : TestCasePlayer + { + private WorkingBeatmap beatmap; + + private InputHandler replay; + + private Func getReplayStream; + private ScoreDatabase scoreDatabase; + + public override string Description => @"Testing replay playback."; + + [BackgroundDependencyLoader] + private void load(Storage storage) + { + scoreDatabase = new ScoreDatabase(storage); + } + + protected override Player CreatePlayer(WorkingBeatmap beatmap) + { + var player = base.CreatePlayer(beatmap); + player.ReplayInputHandler = Ruleset.GetRuleset(beatmap.PlayMode).CreateAutoplayScore(beatmap.Beatmap)?.Replay?.GetInputHandler(); + return player; + } + } +} diff --git a/osu.Desktop.VisualTests/Tests/TestCaseScoreCounter.cs b/osu.Desktop.VisualTests/Tests/TestCaseScoreCounter.cs index ee6f7cd708..f4b2afee08 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseScoreCounter.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseScoreCounter.cs @@ -18,7 +18,7 @@ using osu.Framework.Graphics.Primitives; namespace osu.Desktop.VisualTests.Tests { - class TestCaseScoreCounter : TestCase + internal class TestCaseScoreCounter : TestCase { public override string Description => @"Tests multiple counters"; diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTextAwesome.cs b/osu.Desktop.VisualTests/Tests/TestCaseTextAwesome.cs index b787281932..3ba657d60a 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseTextAwesome.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseTextAwesome.cs @@ -12,7 +12,7 @@ using OpenTK.Graphics; namespace osu.Desktop.VisualTests.Tests { - class TestCaseTextAwesome : TestCase + internal class TestCaseTextAwesome : TestCase { public override string Description => @"Tests display of icons"; @@ -22,7 +22,7 @@ namespace osu.Desktop.VisualTests.Tests FillFlowContainer flow; - Add(flow = new FillFlowContainer() + Add(flow = new FillFlowContainer { RelativeSizeAxes = Axes.Both, Size = new Vector2(0.5f), diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTwoLayerButton.cs b/osu.Desktop.VisualTests/Tests/TestCaseTwoLayerButton.cs index 7f7b6e58dd..4694a6c6ea 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseTwoLayerButton.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseTwoLayerButton.cs @@ -7,7 +7,7 @@ using osu.Game.Screens.Play; namespace osu.Desktop.VisualTests.Tests { - class TestCaseTwoLayerButton : TestCase + internal class TestCaseTwoLayerButton : TestCase { public override string Description => @"Back and skip and what not"; diff --git a/osu.Desktop.VisualTests/VisualTestGame.cs b/osu.Desktop.VisualTests/VisualTestGame.cs index 3128824da2..6a51a892ad 100644 --- a/osu.Desktop.VisualTests/VisualTestGame.cs +++ b/osu.Desktop.VisualTests/VisualTestGame.cs @@ -7,13 +7,13 @@ using osu.Game.Screens.Backgrounds; namespace osu.Desktop.VisualTests { - class VisualTestGame : OsuGameBase + internal class VisualTestGame : OsuGameBase { protected override void LoadComplete() { base.LoadComplete(); - (new BackgroundScreenDefault() { Depth = 10 }).LoadAsync(this, AddInternal); + new BackgroundScreenDefault { Depth = 10 }.LoadAsync(this, AddInternal); // Have to construct this here, rather than in the constructor, because // we depend on some dependencies to be loaded within OsuGameBase.load(). diff --git a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj index 569a97e496..bea56b5cd0 100644 --- a/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj +++ b/osu.Desktop.VisualTests/osu.Desktop.VisualTests.csproj @@ -86,6 +86,10 @@ $(SolutionDir)\packages\ppy.OpenTK.2.0.50727.1340\lib\net45\OpenTK.dll True + + ..\packages\SharpCompress.0.15.1\lib\net45\SharpCompress.dll + True + False $(SolutionDir)\packages\SQLite.Net.Core-PCL.3.1.1\lib\portable-win8+net45+wp8+wpa81+MonoAndroid1+MonoTouch1\SQLite.Net.dll @@ -187,6 +191,7 @@ + diff --git a/osu.Desktop.VisualTests/packages.config b/osu.Desktop.VisualTests/packages.config index 3da209ee61..5a30c50600 100644 --- a/osu.Desktop.VisualTests/packages.config +++ b/osu.Desktop.VisualTests/packages.config @@ -6,6 +6,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste + diff --git a/osu.Desktop/Beatmaps/IO/LegacyFilesystemReader.cs b/osu.Desktop/Beatmaps/IO/LegacyFilesystemReader.cs index b8bfb63a08..0ef448cafe 100644 --- a/osu.Desktop/Beatmaps/IO/LegacyFilesystemReader.cs +++ b/osu.Desktop/Beatmaps/IO/LegacyFilesystemReader.cs @@ -17,16 +17,16 @@ namespace osu.Desktop.Beatmaps.IO { public static void Register() => AddReader((storage, path) => Directory.Exists(path)); - private string basePath { get; set; } - private Beatmap firstMap { get; set; } + private string basePath { get; } + private Beatmap firstMap { get; } public LegacyFilesystemReader(string path) { basePath = path; - BeatmapFilenames = Directory.GetFiles(basePath, @"*.osu").Select(f => Path.GetFileName(f)).ToArray(); + BeatmapFilenames = Directory.GetFiles(basePath, @"*.osu").Select(Path.GetFileName).ToArray(); if (BeatmapFilenames.Length == 0) throw new FileNotFoundException(@"This directory contains no beatmaps"); - StoryboardFilename = Directory.GetFiles(basePath, @"*.osb").Select(f => Path.GetFileName(f)).FirstOrDefault(); + StoryboardFilename = Directory.GetFiles(basePath, @"*.osb").Select(Path.GetFileName).FirstOrDefault(); using (var stream = new StreamReader(GetStream(BeatmapFilenames[0]))) { var decoder = BeatmapDecoder.GetDecoder(stream); diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index d9b9c31617..6d5cab14d6 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -9,16 +9,16 @@ using osu.Framework.Desktop.Platform; using osu.Desktop.Overlays; using System.Reflection; using System.Drawing; +using System.IO; +using System.Threading.Tasks; using osu.Game.Screens.Menu; namespace osu.Desktop { - class OsuGameDesktop : OsuGame + internal class OsuGameDesktop : OsuGame { private VersionManager versionManager; - public override bool IsDeployedBuild => versionManager.IsDeployedBuild; - public OsuGameDesktop(string[] args = null) : base(args) { @@ -44,7 +44,7 @@ namespace osu.Desktop if (desktopWindow != null) { desktopWindow.Icon = Icon.ExtractAssociatedIcon(Assembly.GetExecutingAssembly().Location); - desktopWindow.Title = @"osu!lazer"; + desktopWindow.Title = Name; desktopWindow.DragEnter += dragEnter; desktopWindow.DragDrop += dragDrop; @@ -54,22 +54,32 @@ namespace osu.Desktop private void dragDrop(DragEventArgs e) { // this method will only be executed if e.Effect in dragEnter gets set to something other that None. - var dropData = e.Data.GetData(DataFormats.FileDrop) as object[]; + var dropData = (object[])e.Data.GetData(DataFormats.FileDrop); var filePaths = dropData.Select(f => f.ToString()).ToArray(); - ImportBeatmapsAsync(filePaths); + + if (filePaths.All(f => Path.GetExtension(f) == @".osz")) + Task.Run(() => BeatmapDatabase.Import(filePaths)); + else if (filePaths.All(f => Path.GetExtension(f) == @".osr")) + Task.Run(() => + { + var score = ScoreDatabase.ReadReplayFile(filePaths.First()); + Schedule(() => LoadScore(score)); + }); } + static readonly string[] allowed_extensions = { @".osz", @".osr" }; + private void dragEnter(DragEventArgs e) { // dragDrop will only be executed if e.Effect gets set to something other that None in this method. bool isFile = e.Data.GetDataPresent(DataFormats.FileDrop); if (isFile) { - var paths = (e.Data.GetData(DataFormats.FileDrop) as object[]).Select(f => f.ToString()).ToArray(); - if (paths.Any(p => !p.EndsWith(".osz"))) - e.Effect = DragDropEffects.None; - else + var paths = ((object[])e.Data.GetData(DataFormats.FileDrop)).Select(f => f.ToString()).ToArray(); + if (allowed_extensions.Any(ext => paths.All(p => p.EndsWith(ext)))) e.Effect = DragDropEffects.Copy; + else + e.Effect = DragDropEffects.None; } } } diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index 789fbb6af3..e35616b51a 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; @@ -11,7 +10,6 @@ using osu.Game.Graphics.Sprites; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; using Squirrel; -using System.Reflection; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Graphics; @@ -19,6 +17,7 @@ using OpenTK; using OpenTK.Graphics; using System.Net.Http; using osu.Framework.Logging; +using osu.Game; namespace osu.Desktop.Overlays { @@ -27,16 +26,12 @@ namespace osu.Desktop.Overlays private UpdateManager updateManager; private NotificationManager notificationManager; - AssemblyName assembly = Assembly.GetEntryAssembly().GetName(); - - public bool IsDeployedBuild => assembly.Version.Major > 0; - protected override bool HideOnEscape => false; public override bool HandleInput => false; [BackgroundDependencyLoader] - private void load(NotificationManager notification, OsuColour colours, TextureStore textures) + private void load(NotificationManager notification, OsuColour colours, TextureStore textures, OsuGameBase game) { notificationManager = notification; @@ -45,17 +40,6 @@ namespace osu.Desktop.Overlays Origin = Anchor.BottomCentre; Alpha = 0; - bool isDebug = false; - Debug.Assert(isDebug = true); - - string version; - if (!IsDeployedBuild) - { - version = @"local " + (isDebug ? @"debug" : @"release"); - } - else - version = $@"{assembly.Version.Major}.{assembly.Version.Minor}.{assembly.Version.Build}"; - Children = new Drawable[] { new FillFlowContainer @@ -76,12 +60,12 @@ namespace osu.Desktop.Overlays new OsuSpriteText { Font = @"Exo2.0-Bold", - Text = $@"osu!lazer" + Text = game.Name }, new OsuSpriteText { - Colour = isDebug ? colours.Red : Color4.White, - Text = version + Colour = game.IsDebug ? colours.Red : Color4.White, + Text = game.Version }, } }, @@ -92,7 +76,7 @@ namespace osu.Desktop.Overlays TextSize = 12, Colour = colours.Yellow, Font = @"Venera", - Text = $@"Development Build" + Text = @"Development Build" }, new Sprite { @@ -104,7 +88,7 @@ namespace osu.Desktop.Overlays } }; - if (IsDeployedBuild) + if (game.IsDeployedBuild) checkForUpdateAsync(); } @@ -203,7 +187,7 @@ namespace osu.Desktop.Overlays { } - class UpdateProgressNotification : ProgressNotification + private class UpdateProgressNotification : ProgressNotification { protected override Notification CreateCompletionNotification() => new ProgressCompletionNotification(this) { @@ -228,6 +212,7 @@ namespace osu.Desktop.Overlays new TextAwesome { Anchor = Anchor.Centre, + Origin = Anchor.Centre, Icon = FontAwesome.fa_upload, Colour = Color4.White, } diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 3171e474dc..ddf58ac363 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -29,7 +29,7 @@ namespace osu.Desktop { if (!host.IsPrimaryInstance) { - var importer = new BeatmapImporter(host); + var importer = new BeatmapIPCChannel(host); // Restore the cwd so relative paths given at the command line work correctly Directory.SetCurrentDirectory(cwd); foreach (var file in args) diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 527a027ca2..7e85f09d8c 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -101,8 +101,8 @@ $(SolutionDir)\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.PatchApi.dll True - - $(SolutionDir)\packages\squirrel.windows.1.5.2\lib\Net45\ICSharpCode.SharpZipLib.dll + + ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll True diff --git a/osu.Desktop/packages.config b/osu.Desktop/packages.config index bdeaf1de89..35305fb5d8 100644 --- a/osu.Desktop/packages.config +++ b/osu.Desktop/packages.config @@ -7,6 +7,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste + \ No newline at end of file diff --git a/osu.Game.Modes.Catch/CatchMod.cs b/osu.Game.Modes.Catch/CatchMod.cs index 75b616a9ed..07d6d3469e 100644 --- a/osu.Game.Modes.Catch/CatchMod.cs +++ b/osu.Game.Modes.Catch/CatchMod.cs @@ -1,8 +1,6 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; - namespace osu.Game.Modes.Catch { public class CatchModNoFail : ModNoFail diff --git a/osu.Game.Modes.Catch/CatchRuleset.cs b/osu.Game.Modes.Catch/CatchRuleset.cs index b1d0780739..16c9114c66 100644 --- a/osu.Game.Modes.Catch/CatchRuleset.cs +++ b/osu.Game.Modes.Catch/CatchRuleset.cs @@ -8,6 +8,7 @@ using osu.Game.Modes.Objects; using osu.Game.Modes.Osu.UI; using osu.Game.Modes.UI; using osu.Game.Beatmaps; +using osu.Game.Screens.Play; namespace osu.Game.Modes.Catch { @@ -15,7 +16,11 @@ namespace osu.Game.Modes.Catch { public override ScoreOverlay CreateScoreOverlay() => new OsuScoreOverlay(); - public override HitRenderer CreateHitRendererWith(Beatmap beatmap) => new CatchHitRenderer { Beatmap = beatmap }; + public override HitRenderer CreateHitRendererWith(Beatmap beatmap, PlayerInputManager input = null) => new CatchHitRenderer + { + Beatmap = beatmap, + InputManager = input, + }; public override IEnumerable GetModsFor(ModType type) { @@ -76,7 +81,7 @@ namespace osu.Game.Modes.Catch public override FontAwesome Icon => FontAwesome.fa_osu_fruits_o; - public override ScoreProcessor CreateScoreProcessor(int hitObjectCount) => null; + public override ScoreProcessor CreateScoreProcessor(int hitObjectCount = 0) => null; public override HitObjectParser CreateHitObjectParser() => new NullHitObjectParser(); diff --git a/osu.Game.Modes.Catch/Objects/CatchConverter.cs b/osu.Game.Modes.Catch/Objects/CatchConverter.cs index d8c2df320f..31ddc73f61 100644 --- a/osu.Game.Modes.Catch/Objects/CatchConverter.cs +++ b/osu.Game.Modes.Catch/Objects/CatchConverter.cs @@ -8,7 +8,7 @@ using osu.Game.Beatmaps; namespace osu.Game.Modes.Catch.Objects { - class CatchConverter : HitObjectConverter + internal class CatchConverter : HitObjectConverter { public override List Convert(Beatmap beatmap) { diff --git a/osu.Game.Modes.Catch/Objects/Drawable/DrawableFruit.cs b/osu.Game.Modes.Catch/Objects/Drawable/DrawableFruit.cs index dea8bdfae2..542bdeb3c3 100644 --- a/osu.Game.Modes.Catch/Objects/Drawable/DrawableFruit.cs +++ b/osu.Game.Modes.Catch/Objects/Drawable/DrawableFruit.cs @@ -10,7 +10,7 @@ using OpenTK; namespace osu.Game.Modes.Catch.Objects.Drawable { - class DrawableFruit : Sprite + internal class DrawableFruit : Sprite { private CatchBaseHit h; diff --git a/osu.Game.Modes.Catch/UI/CatchHitRenderer.cs b/osu.Game.Modes.Catch/UI/CatchHitRenderer.cs index 0d06ce29a7..dd61fdd453 100644 --- a/osu.Game.Modes.Catch/UI/CatchHitRenderer.cs +++ b/osu.Game.Modes.Catch/UI/CatchHitRenderer.cs @@ -12,8 +12,8 @@ namespace osu.Game.Modes.Catch.UI { protected override HitObjectConverter Converter => new CatchConverter(); - protected override Playfield CreatePlayfield() => new CatchPlayfield(); + protected override Playfield CreatePlayfield() => new CatchPlayfield(); - protected override DrawableHitObject GetVisualRepresentation(CatchBaseHit h) => null;// new DrawableFruit(h); + protected override DrawableHitObject GetVisualRepresentation(CatchBaseHit h) => null;// new DrawableFruit(h); } } diff --git a/osu.Game.Modes.Catch/UI/CatchPlayfield.cs b/osu.Game.Modes.Catch/UI/CatchPlayfield.cs index cf69ab4fe2..c9c3df8197 100644 --- a/osu.Game.Modes.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Modes.Catch/UI/CatchPlayfield.cs @@ -3,12 +3,13 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Game.Modes.Catch.Objects; using osu.Game.Modes.UI; using OpenTK; namespace osu.Game.Modes.Catch.UI { - public class CatchPlayfield : Playfield + public class CatchPlayfield : Playfield { public CatchPlayfield() { diff --git a/osu.Game.Modes.Mania/ManiaRuleset.cs b/osu.Game.Modes.Mania/ManiaRuleset.cs index df8a48262e..d4b5ecad22 100644 --- a/osu.Game.Modes.Mania/ManiaRuleset.cs +++ b/osu.Game.Modes.Mania/ManiaRuleset.cs @@ -8,6 +8,7 @@ using osu.Game.Modes.Objects; using osu.Game.Modes.Osu.UI; using osu.Game.Modes.UI; using osu.Game.Beatmaps; +using osu.Game.Screens.Play; namespace osu.Game.Modes.Mania { @@ -15,7 +16,11 @@ namespace osu.Game.Modes.Mania { public override ScoreOverlay CreateScoreOverlay() => new OsuScoreOverlay(); - public override HitRenderer CreateHitRendererWith(Beatmap beatmap) => new ManiaHitRenderer { Beatmap = beatmap }; + public override HitRenderer CreateHitRendererWith(Beatmap beatmap, PlayerInputManager input = null) => new ManiaHitRenderer + { + Beatmap = beatmap, + InputManager = input, + }; public override IEnumerable GetModsFor(ModType type) { @@ -92,7 +97,7 @@ namespace osu.Game.Modes.Mania public override FontAwesome Icon => FontAwesome.fa_osu_mania_o; - public override ScoreProcessor CreateScoreProcessor(int hitObjectCount) => null; + public override ScoreProcessor CreateScoreProcessor(int hitObjectCount = 0) => null; public override HitObjectParser CreateHitObjectParser() => new NullHitObjectParser(); diff --git a/osu.Game.Modes.Mania/Objects/Drawable/DrawableNote.cs b/osu.Game.Modes.Mania/Objects/Drawable/DrawableNote.cs index c682a0dacb..92e73191a4 100644 --- a/osu.Game.Modes.Mania/Objects/Drawable/DrawableNote.cs +++ b/osu.Game.Modes.Mania/Objects/Drawable/DrawableNote.cs @@ -26,8 +26,8 @@ namespace osu.Game.Modes.Mania.Objects.Drawable { Texture = textures.Get(@"Menu/logo"); - Transforms.Add(new TransformPositionY() { StartTime = note.StartTime - 200, EndTime = note.StartTime, StartValue = -0.1f, EndValue = 0.9f }); - Transforms.Add(new TransformAlpha() { StartTime = note.StartTime + note.Duration + 200, EndTime = note.StartTime + note.Duration + 400, StartValue = 1, EndValue = 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 + note.Duration + 200, EndTime = note.StartTime + note.Duration + 400, StartValue = 1, EndValue = 0 }); Expire(true); } } diff --git a/osu.Game.Modes.Mania/Objects/ManiaConverter.cs b/osu.Game.Modes.Mania/Objects/ManiaConverter.cs index 654a8fa343..7e1183c0e3 100644 --- a/osu.Game.Modes.Mania/Objects/ManiaConverter.cs +++ b/osu.Game.Modes.Mania/Objects/ManiaConverter.cs @@ -9,7 +9,7 @@ using osu.Game.Beatmaps; namespace osu.Game.Modes.Mania.Objects { - class ManiaConverter : HitObjectConverter + internal class ManiaConverter : HitObjectConverter { private readonly int columns; diff --git a/osu.Game.Modes.Mania/UI/ManiaComboCounter.cs b/osu.Game.Modes.Mania/UI/ManiaComboCounter.cs index b0bd19d6eb..753199e9b9 100644 --- a/osu.Game.Modes.Mania/UI/ManiaComboCounter.cs +++ b/osu.Game.Modes.Mania/UI/ManiaComboCounter.cs @@ -13,7 +13,7 @@ namespace osu.Game.Modes.Mania.UI /// public class ManiaComboCounter : TaikoComboCounter { - protected ushort KeysHeld = 0; + protected ushort KeysHeld; protected Color4 OriginalColour; diff --git a/osu.Game.Modes.Mania/UI/ManiaHitRenderer.cs b/osu.Game.Modes.Mania/UI/ManiaHitRenderer.cs index 59c79f0d03..31bc4fffe4 100644 --- a/osu.Game.Modes.Mania/UI/ManiaHitRenderer.cs +++ b/osu.Game.Modes.Mania/UI/ManiaHitRenderer.cs @@ -19,9 +19,9 @@ namespace osu.Game.Modes.Mania.UI protected override HitObjectConverter Converter => new ManiaConverter(columns); - protected override Playfield CreatePlayfield() => new ManiaPlayfield(columns); + protected override Playfield CreatePlayfield() => new ManiaPlayfield(columns); - protected override DrawableHitObject GetVisualRepresentation(ManiaBaseHit h) + protected override DrawableHitObject GetVisualRepresentation(ManiaBaseHit h) { return null; //return new DrawableNote(h) diff --git a/osu.Game.Modes.Mania/UI/ManiaPlayfield.cs b/osu.Game.Modes.Mania/UI/ManiaPlayfield.cs index 2772b09e16..ab3c231917 100644 --- a/osu.Game.Modes.Mania/UI/ManiaPlayfield.cs +++ b/osu.Game.Modes.Mania/UI/ManiaPlayfield.cs @@ -3,19 +3,17 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Game.Modes.Mania.Objects; using osu.Game.Modes.UI; using OpenTK; using OpenTK.Graphics; namespace osu.Game.Modes.Mania.UI { - public class ManiaPlayfield : Playfield + public class ManiaPlayfield : Playfield { - private readonly int columns; - public ManiaPlayfield(int columns) { - this.columns = columns; RelativeSizeAxes = Axes.Both; Size = new Vector2(columns / 20f, 1f); Anchor = Anchor.BottomCentre; @@ -24,7 +22,7 @@ namespace osu.Game.Modes.Mania.UI Add(new Box { RelativeSizeAxes = Axes.Both, Alpha = 0.5f }); for (int i = 0; i < columns; i++) - Add(new Box() + Add(new Box { RelativeSizeAxes = Axes.Y, Size = new Vector2(2, 1), diff --git a/osu.Game.Modes.Osu/Objects/Drawables/Connections/FollowPoint.cs b/osu.Game.Modes.Osu/Objects/Drawables/Connections/FollowPoint.cs index 8e9d836a8b..935f3e01fd 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/Connections/FollowPoint.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/Connections/FollowPoint.cs @@ -17,7 +17,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Connections public double EndTime; public Vector2 EndPosition; - const float width = 8; + private const float width = 8; public FollowPoint() { diff --git a/osu.Game.Modes.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs b/osu.Game.Modes.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs index b57f0881bc..e11e0660d5 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs @@ -74,13 +74,13 @@ namespace osu.Game.Modes.Osu.Objects.Drawables for (int d = (int)(PointDistance * 1.5); d < distance - PointDistance; d += PointDistance) { - float fraction = ((float)d / distance); + float fraction = (float)d / distance; Vector2 pointStartPosition = startPosition + (fraction - 0.1f) * distanceVector; Vector2 pointEndPosition = startPosition + fraction * distanceVector; double fadeOutTime = startTime + fraction * duration; double fadeInTime = fadeOutTime - PreEmpt; - Add(new FollowPoint() + Add(new FollowPoint { StartTime = fadeInTime, EndTime = fadeOutTime, diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableHitCircle.cs index 9197472f92..e10fa91e6f 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -49,7 +49,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables return true; }, }, - number = new NumberPiece() + number = new NumberPiece { Text = h is Spinner ? "S" : (HitObject.ComboIndex + 1).ToString(), }, @@ -59,7 +59,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables { Colour = osuObject.Colour, }, - ApproachCircle = new ApproachCircle() + ApproachCircle = new ApproachCircle { Colour = osuObject.Colour, } @@ -69,33 +69,23 @@ namespace osu.Game.Modes.Osu.Objects.Drawables Size = circle.DrawSize; } - double hit50 = 150; - double hit100 = 80; - double hit300 = 30; - protected override void CheckJudgement(bool userTriggered) { if (!userTriggered) { - if (Judgement.TimeOffset > hit50) + if (Judgement.TimeOffset > HitObject.HitWindowFor(OsuScoreResult.Hit50)) Judgement.Result = HitResult.Miss; return; } double hitOffset = Math.Abs(Judgement.TimeOffset); - OsuJudgementInfo osuJudgement = Judgement as OsuJudgementInfo; + OsuJudgementInfo osuJudgement = (OsuJudgementInfo)Judgement; - if (hitOffset < hit50) + if (hitOffset < HitObject.HitWindowFor(OsuScoreResult.Hit50)) { Judgement.Result = HitResult.Hit; - - if (hitOffset < hit300) - osuJudgement.Score = OsuScoreResult.Hit300; - else if (hitOffset < hit100) - osuJudgement.Score = OsuScoreResult.Hit100; - else if (hitOffset < hit50) - osuJudgement.Score = OsuScoreResult.Hit50; + osuJudgement.Score = HitObject.ScoreResultForOffset(hitOffset); } else Judgement.Result = HitResult.Miss; diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuHitObject.cs index c7cc7cfdd7..4b436dd82f 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -2,12 +2,11 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.ComponentModel; -using osu.Game.Modes.Objects; using osu.Game.Modes.Objects.Drawables; 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; @@ -18,7 +17,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables { } - public override JudgementInfo CreateJudgementInfo() => new OsuJudgementInfo { MaxScore = OsuScoreResult.Hit300 }; + protected override JudgementInfo CreateJudgementInfo() => new OsuJudgementInfo { MaxScore = OsuScoreResult.Hit300 }; protected override void UpdateState(ArmedState state) { diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs index 907ce8da63..67acc7cc7e 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs @@ -21,10 +21,10 @@ namespace osu.Game.Modes.Osu.Objects.Drawables private Container ticks; - SliderBody body; - SliderBall ball; + private SliderBody body; + private SliderBall ball; - SliderBouncer bouncer1, bouncer2; + private SliderBouncer bouncer1, bouncer2; public DrawableSlider(Slider s) : base(s) { @@ -94,7 +94,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables // pass all input through. public override bool Contains(Vector2 screenSpacePos) => true; - int currentRepeat; + private int currentRepeat; protected override void Update() { @@ -102,8 +102,8 @@ namespace osu.Game.Modes.Osu.Objects.Drawables double progress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1); - int repeat = (int)(progress * slider.RepeatCount); - progress = (progress * slider.RepeatCount) % 1; + int repeat = slider.RepeatAt(progress); + progress = slider.CurveProgressAt(progress); if (repeat > currentRepeat) { @@ -112,9 +112,6 @@ namespace osu.Game.Modes.Osu.Objects.Drawables currentRepeat = repeat; } - if (repeat % 2 == 1) - progress = 1 - progress; - bouncer2.Position = slider.Curve.PositionAt(body.SnakedEnd ?? 0); //todo: we probably want to reconsider this before adding scoring, but it looks and feels nice. diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs index 5fe9e44b41..7c5fa4a092 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -26,7 +26,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables public override bool RemoveWhenNotAlive => false; - public override JudgementInfo CreateJudgementInfo() => new OsuJudgementInfo { MaxScore = OsuScoreResult.SliderTick }; + protected override JudgementInfo CreateJudgementInfo() => new OsuJudgementInfo { MaxScore = OsuScoreResult.SliderTick }; public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick) { @@ -71,7 +71,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables protected override void CheckJudgement(bool userTriggered) { - var j = Judgement as OsuJudgementInfo; + var j = (OsuJudgementInfo)Judgement; if (Judgement.TimeOffset >= 0) { diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSpinner.cs index 8c491dec9c..cfebd11809 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSpinner.cs @@ -75,7 +75,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables { if (Time.Current < HitObject.StartTime) return; - var j = Judgement as OsuJudgementInfo; + var j = (OsuJudgementInfo)Judgement; disc.ScaleTo(Interpolation.ValueAt(Math.Sqrt(Progress), scaleToCircle, Vector2.One, 0, 1), 100); @@ -108,9 +108,9 @@ namespace osu.Game.Modes.Osu.Objects.Drawables } } - private Vector2 scaleToCircle => (circle.Scale * circle.DrawWidth / DrawWidth) * 0.95f; + 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 float spinsPerMinuteNeeded = 100 + 5 * 15; //TODO: read per-map OD and place it on the 5 private float rotationsNeeded => (float)(spinsPerMinuteNeeded * (spinner.EndTime - spinner.StartTime) / 60000f); diff --git a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/CirclePiece.cs index 23f878d491..2a503e3dec 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/CirclePiece.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/CirclePiece.cs @@ -21,7 +21,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces public CirclePiece() { - Size = new Vector2(128); + Size = new Vector2((float)OsuHitObject.OBJECT_RADIUS * 2); Masking = true; CornerRadius = Size.X / 2; diff --git a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBall.cs index 1b9ddd2236..49785efd38 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -15,7 +15,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces private readonly Slider slider; private Box follow; - const float width = 128; + private const float width = 128; public SliderBall(Slider slider) { @@ -81,7 +81,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces return base.OnMouseMove(state); } - bool tracking; + private bool tracking; public bool Tracking { get { return tracking; } diff --git a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs index 65b878ae43..34dec34cb4 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs @@ -29,7 +29,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces set { Disc.Colour = value; } } - Color4 completeColour; + private Color4 completeColour; [BackgroundDependencyLoader] private void load(OsuColour colours) @@ -37,7 +37,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces completeColour = colours.YellowLight.Opacity(0.8f); } - class SpinnerBorder : Container + private class SpinnerBorder : Container { public SpinnerBorder() { @@ -116,7 +116,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces }; } - bool tracking; + private bool tracking; public bool Tracking { get { return tracking; } @@ -130,7 +130,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces } } - bool complete; + private bool complete; public bool Complete { get { return complete; } diff --git a/osu.Game.Modes.Osu/Objects/OsuHitObject.cs b/osu.Game.Modes.Osu/Objects/OsuHitObject.cs index cbf9a3de9c..ddcdae7be0 100644 --- a/osu.Game.Modes.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Modes.Osu/Objects/OsuHitObject.cs @@ -5,11 +5,19 @@ using System; using osu.Game.Modes.Objects; using OpenTK; using osu.Game.Beatmaps; +using osu.Game.Modes.Osu.Objects.Drawables; namespace osu.Game.Modes.Osu.Objects { public abstract class OsuHitObject : HitObject { + public const double OBJECT_RADIUS = 64; + + private const double hittable_range = 300; + private const double hit_window_50 = 150; + private const double hit_window_100 = 80; + private const double hit_window_300 = 30; + public Vector2 Position { get; set; } public Vector2 StackedPosition => Position + StackOffset; @@ -22,10 +30,38 @@ namespace osu.Game.Modes.Osu.Objects public Vector2 StackOffset => new Vector2(StackHeight * Scale * -6.4f); + public double Radius => OBJECT_RADIUS * Scale; + public float Scale { get; set; } = 1; public abstract HitObjectType Type { get; } + public double HitWindowFor(OsuScoreResult result) + { + switch (result) + { + default: + return 300; + case OsuScoreResult.Hit50: + return 150; + case OsuScoreResult.Hit100: + return 80; + case OsuScoreResult.Hit300: + return 30; + } + } + + public OsuScoreResult ScoreResultForOffset(double offset) + { + if (offset < HitWindowFor(OsuScoreResult.Hit300)) + return OsuScoreResult.Hit300; + if (offset < HitWindowFor(OsuScoreResult.Hit100)) + return OsuScoreResult.Hit100; + if (offset < HitWindowFor(OsuScoreResult.Hit50)) + return OsuScoreResult.Hit50; + return OsuScoreResult.Miss; + } + public override void SetDefaultsFromBeatmap(Beatmap beatmap) { base.SetDefaultsFromBeatmap(beatmap); diff --git a/osu.Game.Modes.Osu/Objects/OsuHitObjectDifficulty.cs b/osu.Game.Modes.Osu/Objects/OsuHitObjectDifficulty.cs index 4f0dac7407..c67c28b526 100644 --- a/osu.Game.Modes.Osu/Objects/OsuHitObjectDifficulty.cs +++ b/osu.Game.Modes.Osu/Objects/OsuHitObjectDifficulty.cs @@ -8,7 +8,7 @@ using System.Linq; namespace osu.Game.Modes.Osu.Objects { - class OsuHitObjectDifficulty + internal class OsuHitObjectDifficulty { /// /// Factor by how much speed / aim strain decays per second. @@ -63,7 +63,7 @@ namespace osu.Game.Modes.Osu.Objects MaxCombo += slider.Ticks.Count(); // We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps. - scalingFactor = (52.0f / circleRadius); + scalingFactor = 52.0f / circleRadius; if (circleRadius < 30) { float smallCircleBonus = Math.Min(30.0f - circleRadius, 5.0f) / 50.0f; @@ -130,7 +130,7 @@ namespace osu.Game.Modes.Osu.Objects else if (distance > almost_diameter) return 1.2 + 0.4 * (distance - almost_diameter) / (stream_spacing_threshold - almost_diameter); else if (distance > almost_diameter / 2) - return 0.95 + 0.25 * (distance - (almost_diameter / 2)) / (almost_diameter / 2); + return 0.95 + 0.25 * (distance - almost_diameter / 2) / (almost_diameter / 2); else return 0.95; diff --git a/osu.Game.Modes.Osu/Objects/OsuHitObjectParser.cs b/osu.Game.Modes.Osu/Objects/OsuHitObjectParser.cs index e8818db1d5..0dfd66bc8a 100644 --- a/osu.Game.Modes.Osu/Objects/OsuHitObjectParser.cs +++ b/osu.Game.Modes.Osu/Objects/OsuHitObjectParser.cs @@ -30,11 +30,8 @@ namespace osu.Game.Modes.Osu.Objects break; case HitObjectType.Slider: CurveTypes curveType = CurveTypes.Catmull; - int repeatCount; double length = 0; - List points = new List(); - - points.Add(new Vector2(int.Parse(split[0]), int.Parse(split[1]))); + List points = new List { new Vector2(int.Parse(split[0]), int.Parse(split[1])) }; string[] pointsplit = split[5].Split('|'); for (int i = 0; i < pointsplit.Length; i++) @@ -67,12 +64,10 @@ namespace osu.Game.Modes.Osu.Objects points.Add(v); } - repeatCount = Convert.ToInt32(split[6], CultureInfo.InvariantCulture); + int repeatCount = Convert.ToInt32(split[6], CultureInfo.InvariantCulture); if (repeatCount > 9000) - { - throw new ArgumentOutOfRangeException("wacky man"); - } + throw new ArgumentOutOfRangeException(nameof(repeatCount), @"Repeat count is way too high"); if (split.Length > 7) length = Convert.ToDouble(split[7], CultureInfo.InvariantCulture); diff --git a/osu.Game.Modes.Osu/Objects/Slider.cs b/osu.Game.Modes.Osu/Objects/Slider.cs index 5f6c50fd72..88abb68d0a 100644 --- a/osu.Game.Modes.Osu/Objects/Slider.cs +++ b/osu.Game.Modes.Osu/Objects/Slider.cs @@ -14,7 +14,32 @@ namespace osu.Game.Modes.Osu.Objects { public override double EndTime => StartTime + RepeatCount * Curve.Length / Velocity; - public override Vector2 EndPosition => RepeatCount % 2 == 0 ? Position : Curve.PositionAt(1); + public override Vector2 EndPosition => PositionAt(1); + + /// + /// Computes the position on the slider at a given progress that ranges from 0 (beginning of the slider) + /// to 1 (end of the slider). This includes repeat logic. + /// + /// Ranges from 0 (beginning of the slider) to 1 (end of the slider). + /// + public Vector2 PositionAt(double progress) => Curve.PositionAt(CurveProgressAt(progress)); + + /// + /// Find the current progress along the curve, accounting for repeat logic. + /// + public double CurveProgressAt(double progress) + { + var p = progress * RepeatCount % 1; + if (RepeatAt(progress) % 2 == 1) + p = 1 - p; + return p; + } + + /// + /// Determine which repeat of the slider we are on at a given progress. + /// Range is 0..RepeatCount where 0 is the first run. + /// + public int RepeatAt(double progress) => (int)(progress * RepeatCount); private int stackHeight; public override int StackHeight diff --git a/osu.Game.Modes.Osu/Objects/SliderCurve.cs b/osu.Game.Modes.Osu/Objects/SliderCurve.cs index 3b52a41b8c..6c65e7fa37 100644 --- a/osu.Game.Modes.Osu/Objects/SliderCurve.cs +++ b/osu.Game.Modes.Osu/Objects/SliderCurve.cs @@ -59,10 +59,9 @@ namespace osu.Game.Modes.Osu.Objects if (i == ControlPoints.Count - 1 || ControlPoints[i] == ControlPoints[i + 1]) { List subpath = calculateSubpath(subControlPoints); - for (int j = 0; j < subpath.Count; ++j) - // Only add those vertices that add a new segment to the path. - if (calculatedPath.Count == 0 || calculatedPath.Last() != subpath[j]) - calculatedPath.Add(subpath[j]); + foreach (Vector2 t in subpath) + if (calculatedPath.Count == 0 || calculatedPath.Last() != t) + calculatedPath.Add(t); subControlPoints.Clear(); } @@ -175,7 +174,7 @@ namespace osu.Game.Modes.Osu.Objects path.Clear(); int i = 0; - for (; i < calculatedPath.Count && cumulativeLength[i] < d0; ++i) ; + for (; i < calculatedPath.Count && cumulativeLength[i] < d0; ++i) { } path.Add(interpolateVertices(i, d0) + Offset); @@ -186,10 +185,10 @@ namespace osu.Game.Modes.Osu.Objects } /// - /// Computes the position on the slider at a given progress that ranges from 0 (beginning of the slider) - /// to 1 (end of the slider). + /// Computes the position on the slider at a given progress that ranges from 0 (beginning of the curve) + /// to 1 (end of the curve). /// - /// Ranges from 0 (beginning of the slider) to 1 (end of the slider). + /// Ranges from 0 (beginning of the curve) to 1 (end of the curve). /// public Vector2 PositionAt(double progress) { diff --git a/osu.Game.Modes.Osu/OsuAutoReplay.cs b/osu.Game.Modes.Osu/OsuAutoReplay.cs new file mode 100644 index 0000000000..a0fa0904af --- /dev/null +++ b/osu.Game.Modes.Osu/OsuAutoReplay.cs @@ -0,0 +1,298 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using osu.Game.Beatmaps; +using osu.Game.Modes.Osu.Objects; +using OpenTK; +using System; +using osu.Framework.Graphics.Transforms; +using osu.Game.Modes.Osu.Objects.Drawables; +using osu.Framework.MathUtils; +using System.Diagnostics; + +namespace osu.Game.Modes.Osu +{ + public class OsuAutoReplay : LegacyReplay + { + static readonly Vector2 spinner_centre = new Vector2(256, 192); + + const float spin_radius = 50; + + private Beatmap beatmap; + + public OsuAutoReplay(Beatmap beatmap) + { + this.beatmap = beatmap; + + createAutoReplay(); + } + + internal class LegacyReplayFrameComparer : IComparer + { + public int Compare(LegacyReplayFrame f1, LegacyReplayFrame f2) + { + return f1.Time.CompareTo(f2.Time); + } + } + + private static IComparer replayFrameComparer = new LegacyReplayFrameComparer(); + + private int findInsertionIndex(LegacyReplayFrame frame) + { + int index = Frames.BinarySearch(frame, replayFrameComparer); + + if (index < 0) + { + index = ~index; + } + else + { + // Go to the first index which is actually bigger + while (index < Frames.Count && frame.Time == Frames[index].Time) + { + ++index; + } + } + + return index; + } + + private void addFrameToReplay(LegacyReplayFrame frame) => Frames.Insert(findInsertionIndex(frame), frame); + + private static Vector2 circlePosition(double t, double radius) => new Vector2((float)(Math.Cos(t) * radius), (float)(Math.Sin(t) * radius)); + + private double applyModsToTime(double v) => v; + private double applyModsToRate(double v) => v; + + private void createAutoReplay() + { + int buttonIndex = 0; + + bool delayedMovements = false;// ModManager.CheckActive(Mods.Relax2); + EasingTypes preferredEasing = delayedMovements ? EasingTypes.InOutCubic : EasingTypes.Out; + + addFrameToReplay(new LegacyReplayFrame(-100000, 256, 500, LegacyButtonState.None)); + addFrameToReplay(new LegacyReplayFrame(beatmap.HitObjects[0].StartTime - 1500, 256, 500, LegacyButtonState.None)); + addFrameToReplay(new LegacyReplayFrame(beatmap.HitObjects[0].StartTime - 1000, 256, 192, LegacyButtonState.None)); + + // We are using ApplyModsToRate and not ApplyModsToTime to counteract the speed up / slow down from HalfTime / DoubleTime so that we remain at a constant framerate of 60 fps. + float frameDelay = (float)applyModsToRate(1000.0 / 60.0); + + // Already superhuman, but still somewhat realistic + int reactionTime = (int)applyModsToRate(100); + + + for (int i = 0; i < beatmap.HitObjects.Count; i++) + { + OsuHitObject h = beatmap.HitObjects[i] as OsuHitObject; + + //if (h.EndTime < InputManager.ReplayStartTime) + //{ + // h.IsHit = true; + // continue; + //} + + int endDelay = h is Spinner ? 1 : 0; + + if (delayedMovements && i > 0) + { + OsuHitObject last = beatmap.HitObjects[i - 1] as OsuHitObject; + + //Make the cursor stay at a hitObject as long as possible (mainly for autopilot). + if (h.StartTime - h.HitWindowFor(OsuScoreResult.Miss) > last.EndTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50) + { + if (!(last is Spinner) && h.StartTime - last.EndTime < 1000) addFrameToReplay(new LegacyReplayFrame(last.EndTime + h.HitWindowFor(OsuScoreResult.Hit50), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None)); + if (!(h is Spinner)) addFrameToReplay(new LegacyReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Miss), h.Position.X, h.Position.Y, LegacyButtonState.None)); + } + else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50) > last.EndTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50) + { + if (!(last is Spinner) && h.StartTime - last.EndTime < 1000) addFrameToReplay(new LegacyReplayFrame(last.EndTime + h.HitWindowFor(OsuScoreResult.Hit50), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None)); + if (!(h is Spinner)) addFrameToReplay(new LegacyReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50), h.Position.X, h.Position.Y, LegacyButtonState.None)); + } + else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100) > last.EndTime + h.HitWindowFor(OsuScoreResult.Hit100) + 50) + { + if (!(last is Spinner) && h.StartTime - last.EndTime < 1000) addFrameToReplay(new LegacyReplayFrame(last.EndTime + h.HitWindowFor(OsuScoreResult.Hit100), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None)); + if (!(h is Spinner)) addFrameToReplay(new LegacyReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.Position.X, h.Position.Y, LegacyButtonState.None)); + } + } + + + Vector2 targetPosition = h.Position; + EasingTypes easing = preferredEasing; + float spinnerDirection = -1; + + if (h is Spinner) + { + targetPosition.X = Frames[Frames.Count - 1].MouseX; + targetPosition.Y = Frames[Frames.Count - 1].MouseY; + + Vector2 difference = spinner_centre - targetPosition; + + float differenceLength = difference.Length; + float newLength = (float)Math.Sqrt(differenceLength * differenceLength - spin_radius * spin_radius); + + if (differenceLength > spin_radius) + { + float angle = (float)Math.Asin(spin_radius / differenceLength); + + if (angle > 0) + { + spinnerDirection = -1; + } + else + { + spinnerDirection = 1; + } + + difference.X = difference.X * (float)Math.Cos(angle) - difference.Y * (float)Math.Sin(angle); + difference.Y = difference.X * (float)Math.Sin(angle) + difference.Y * (float)Math.Cos(angle); + + difference.Normalize(); + difference *= newLength; + + targetPosition += difference; + + easing = EasingTypes.In; + } + else if (difference.Length > 0) + { + targetPosition = spinner_centre - difference * (spin_radius / difference.Length); + } + else + { + targetPosition = spinner_centre + new Vector2(0, -spin_radius); + } + } + + + // Do some nice easing for cursor movements + if (Frames.Count > 0) + { + LegacyReplayFrame lastFrame = Frames[Frames.Count - 1]; + + // Wait until Auto could "see and react" to the next note. + double waitTime = h.StartTime - Math.Max(0.0, DrawableOsuHitObject.TIME_PREEMPT - reactionTime); + if (waitTime > lastFrame.Time) + { + lastFrame = new LegacyReplayFrame(waitTime, lastFrame.MouseX, lastFrame.MouseY, lastFrame.ButtonState); + addFrameToReplay(lastFrame); + } + + Vector2 lastPosition = new Vector2(lastFrame.MouseX, lastFrame.MouseY); + + double timeDifference = applyModsToTime(h.StartTime - lastFrame.Time); + + // Only "snap" to hitcircles if they are far enough apart. As the time between hitcircles gets shorter the snapping threshold goes up. + if (timeDifference > 0 && // Sanity checks + ((lastPosition - targetPosition).Length > h.Radius * (1.5 + 100.0 / timeDifference) || // Either the distance is big enough + timeDifference >= 266)) // ... or the beats are slow enough to tap anyway. + { + // Perform eased movement + for (double time = lastFrame.Time + frameDelay; time < h.StartTime; time += frameDelay) + { + Vector2 currentPosition = Interpolation.ValueAt(time, lastPosition, targetPosition, lastFrame.Time, h.StartTime, easing); + addFrameToReplay(new LegacyReplayFrame((int)time, currentPosition.X, currentPosition.Y, lastFrame.ButtonState)); + } + + buttonIndex = 0; + } + else + { + buttonIndex++; + } + } + + LegacyButtonState button = buttonIndex % 2 == 0 ? LegacyButtonState.Left1 : LegacyButtonState.Right1; + + LegacyReplayFrame newFrame = new LegacyReplayFrame(h.StartTime, targetPosition.X, targetPosition.Y, button); + LegacyReplayFrame endFrame = new LegacyReplayFrame(h.EndTime + endDelay, h.EndPosition.X, h.EndPosition.Y, LegacyButtonState.None); + + // Decrement because we want the previous frame, not the next one + int index = findInsertionIndex(newFrame) - 1; + + // Do we have a previous frame? No need to check for < replay.Count since we decremented! + if (index >= 0) + { + LegacyReplayFrame previousFrame = Frames[index]; + var previousButton = previousFrame.ButtonState; + + // If a button is already held, then we simply alternate + if (previousButton != LegacyButtonState.None) + { + Debug.Assert(previousButton != (LegacyButtonState.Left1 | LegacyButtonState.Right1)); + + // Force alternation if we have the same button. Otherwise we can just keep the naturally to us assigned button. + if (previousButton == button) + { + button = (LegacyButtonState.Left1 | LegacyButtonState.Right1) & ~button; + newFrame.SetButtonStates(button); + } + + // We always follow the most recent slider / spinner, so remove any other frames that occur while it exists. + int endIndex = findInsertionIndex(endFrame); + + if (index < Frames.Count - 1) + Frames.RemoveRange(index + 1, Math.Max(0, endIndex - (index + 1))); + + // After alternating we need to keep holding the other button in the future rather than the previous one. + for (int j = index + 1; j < Frames.Count; ++j) + { + // Don't affect frames which stop pressing a button! + if (j < Frames.Count - 1 || Frames[j].ButtonState == previousButton) + Frames[j].SetButtonStates(button); + } + } + } + + addFrameToReplay(newFrame); + + // We add intermediate frames for spinning / following a slider here. + if (h is Spinner) + { + Vector2 difference = targetPosition - spinner_centre; + + float radius = difference.Length; + float angle = radius == 0 ? 0 : (float)Math.Atan2(difference.Y, difference.X); + + double t; + + for (double j = h.StartTime + frameDelay; j < h.EndTime; j += frameDelay) + { + t = applyModsToTime(j - h.StartTime) * spinnerDirection; + + Vector2 pos = spinner_centre + circlePosition(t / 20 + angle, spin_radius); + addFrameToReplay(new LegacyReplayFrame((int)j, pos.X, pos.Y, button)); + } + + t = applyModsToTime(h.EndTime - h.StartTime) * spinnerDirection; + Vector2 endPosition = spinner_centre + circlePosition(t / 20 + angle, spin_radius); + + addFrameToReplay(new LegacyReplayFrame(h.EndTime, endPosition.X, endPosition.Y, button)); + + endFrame.MouseX = endPosition.X; + endFrame.MouseY = endPosition.Y; + } + else if (h is Slider) + { + Slider s = h as Slider; + + for (double j = frameDelay; j < s.Duration; j += frameDelay) + { + Vector2 pos = s.PositionAt(j / s.Duration); + addFrameToReplay(new LegacyReplayFrame(h.StartTime + j, pos.X, pos.Y, button)); + } + + addFrameToReplay(new LegacyReplayFrame(h.EndTime, s.EndPosition.X, s.EndPosition.Y, button)); + } + + // We only want to let go of our button if we are at the end of the current replay. Otherwise something is still going on after us so we need to keep the button pressed! + if (Frames[Frames.Count - 1].Time <= endFrame.Time) + addFrameToReplay(endFrame); + } + + //Player.currentScore.Replay = InputManager.ReplayScore.Replay; + //Player.currentScore.PlayerName = "osu!"; + } + } +} diff --git a/osu.Game.Modes.Osu/OsuDifficultyCalculator.cs b/osu.Game.Modes.Osu/OsuDifficultyCalculator.cs index 9e2a311021..a42fe70cf3 100644 --- a/osu.Game.Modes.Osu/OsuDifficultyCalculator.cs +++ b/osu.Game.Modes.Osu/OsuDifficultyCalculator.cs @@ -100,22 +100,23 @@ namespace osu.Game.Modes.Osu protected bool CalculateStrainValues() { // Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment. - List.Enumerator hitObjectsEnumerator = DifficultyHitObjects.GetEnumerator(); - - if (!hitObjectsEnumerator.MoveNext()) return false; - - OsuHitObjectDifficulty currentHitObject = hitObjectsEnumerator.Current; - OsuHitObjectDifficulty nextHitObject; - - // First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject. - while (hitObjectsEnumerator.MoveNext()) + using (List.Enumerator hitObjectsEnumerator = DifficultyHitObjects.GetEnumerator()) { - nextHitObject = hitObjectsEnumerator.Current; - nextHitObject.CalculateStrains(currentHitObject, TimeRate); - currentHitObject = nextHitObject; - } - return true; + if (!hitObjectsEnumerator.MoveNext()) return false; + + OsuHitObjectDifficulty current = hitObjectsEnumerator.Current; + + // First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject. + while (hitObjectsEnumerator.MoveNext()) + { + var next = hitObjectsEnumerator.Current; + next?.CalculateStrains(current, TimeRate); + current = next; + } + + return true; + } } /// @@ -184,7 +185,7 @@ namespace osu.Game.Modes.Osu } // Those values are used as array indices. Be careful when changing them! - public enum DifficultyType : int + public enum DifficultyType { Speed = 0, Aim, diff --git a/osu.Game.Modes.Osu/OsuMod.cs b/osu.Game.Modes.Osu/OsuMod.cs index 2df44ab5de..1d59d6429b 100644 --- a/osu.Game.Modes.Osu/OsuMod.cs +++ b/osu.Game.Modes.Osu/OsuMod.cs @@ -85,7 +85,7 @@ namespace osu.Game.Modes.Osu public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModPerfect), typeof(ModNoFail), typeof(ModAutoplay), typeof(ModCinema) }; } - public class OsuModeAutoplay : ModAutoplay + public class OsuModAutoplay : ModAutoplay { public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray(); } diff --git a/osu.Game.Modes.Osu/OsuRuleset.cs b/osu.Game.Modes.Osu/OsuRuleset.cs index b7036cc044..b58dc70b16 100644 --- a/osu.Game.Modes.Osu/OsuRuleset.cs +++ b/osu.Game.Modes.Osu/OsuRuleset.cs @@ -9,6 +9,7 @@ using osu.Game.Modes.Objects; using osu.Game.Modes.Osu.Objects; using osu.Game.Modes.Osu.UI; using osu.Game.Modes.UI; +using osu.Game.Screens.Play; namespace osu.Game.Modes.Osu { @@ -16,7 +17,11 @@ namespace osu.Game.Modes.Osu { public override ScoreOverlay CreateScoreOverlay() => new OsuScoreOverlay(); - public override HitRenderer CreateHitRendererWith(Beatmap beatmap) => new OsuHitRenderer { Beatmap = beatmap }; + public override HitRenderer CreateHitRendererWith(Beatmap beatmap, PlayerInputManager input = null) => new OsuHitRenderer + { + Beatmap = beatmap, + InputManager = input + }; public override IEnumerable GetBeatmapStatistics(WorkingBeatmap beatmap) => new[] { @@ -81,7 +86,7 @@ namespace osu.Game.Modes.Osu { Mods = new Mod[] { - new OsuModeAutoplay(), + new OsuModAutoplay(), new ModCinema(), }, }, @@ -96,10 +101,17 @@ namespace osu.Game.Modes.Osu public override HitObjectParser CreateHitObjectParser() => new OsuHitObjectParser(); - public override ScoreProcessor CreateScoreProcessor(int hitObjectCount) => new OsuScoreProcessor(hitObjectCount); + public override ScoreProcessor CreateScoreProcessor(int hitObjectCount = 0) => new OsuScoreProcessor(hitObjectCount); public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new OsuDifficultyCalculator(beatmap); + public override Score CreateAutoplayScore(Beatmap beatmap) + { + var score = CreateScoreProcessor().GetScore(); + score.Replay = new OsuAutoReplay(beatmap); + return score; + } + protected override PlayMode PlayMode => PlayMode.Osu; } } diff --git a/osu.Game.Modes.Osu/OsuScore.cs b/osu.Game.Modes.Osu/OsuScore.cs index 6ea8eff890..dddf826887 100644 --- a/osu.Game.Modes.Osu/OsuScore.cs +++ b/osu.Game.Modes.Osu/OsuScore.cs @@ -3,7 +3,7 @@ namespace osu.Game.Modes.Osu { - class OsuScore : Score + internal class OsuScore : Score { } } diff --git a/osu.Game.Modes.Osu/OsuScoreProcessor.cs b/osu.Game.Modes.Osu/OsuScoreProcessor.cs index e73db8d901..7bc82c3c8b 100644 --- a/osu.Game.Modes.Osu/OsuScoreProcessor.cs +++ b/osu.Game.Modes.Osu/OsuScoreProcessor.cs @@ -6,9 +6,9 @@ using osu.Game.Modes.Osu.Objects.Drawables; namespace osu.Game.Modes.Osu { - class OsuScoreProcessor : ScoreProcessor + internal class OsuScoreProcessor : ScoreProcessor { - public OsuScoreProcessor(int hitObjectCount) + public OsuScoreProcessor(int hitObjectCount = 0) : base(hitObjectCount) { Health.Value = 1; @@ -35,8 +35,9 @@ namespace osu.Game.Modes.Osu int score = 0; int maxScore = 0; - foreach (OsuJudgementInfo j in Judgements) + foreach (var judgementInfo in Judgements) { + var j = (OsuJudgementInfo)judgementInfo; score += j.ScoreValue; maxScore += j.MaxScoreValue; } diff --git a/osu.Game.Modes.Osu/UI/OsuComboCounter.cs b/osu.Game.Modes.Osu/UI/OsuComboCounter.cs index 5ba5537b59..fe24b021a6 100644 --- a/osu.Game.Modes.Osu/UI/OsuComboCounter.cs +++ b/osu.Game.Modes.Osu/UI/OsuComboCounter.cs @@ -11,7 +11,7 @@ namespace osu.Game.Modes.Osu.UI /// public class OsuComboCounter : ComboCounter { - protected uint ScheduledPopOutCurrentId = 0; + protected uint ScheduledPopOutCurrentId; protected virtual float PopOutSmallScale => 1.1f; protected virtual bool CanPopOutWhileRolling => false; diff --git a/osu.Game.Modes.Osu/UI/OsuHitRenderer.cs b/osu.Game.Modes.Osu/UI/OsuHitRenderer.cs index fa222aafd7..4801862a91 100644 --- a/osu.Game.Modes.Osu/UI/OsuHitRenderer.cs +++ b/osu.Game.Modes.Osu/UI/OsuHitRenderer.cs @@ -13,16 +13,21 @@ namespace osu.Game.Modes.Osu.UI { protected override HitObjectConverter Converter => new OsuHitObjectConverter(); - protected override Playfield CreatePlayfield() => new OsuPlayfield(); + protected override Playfield CreatePlayfield() => new OsuPlayfield(); - protected override DrawableHitObject GetVisualRepresentation(OsuHitObject h) + protected override DrawableHitObject GetVisualRepresentation(OsuHitObject h) { - if (h is HitCircle) - return new DrawableHitCircle(h as HitCircle); - if (h is Slider) - return new DrawableSlider(h as Slider); - if (h is Spinner) - return new DrawableSpinner(h as Spinner); + var circle = h as HitCircle; + if (circle != null) + return new DrawableHitCircle(circle); + + var slider = h as Slider; + if (slider != null) + return new DrawableSlider(slider); + + var spinner = h as Spinner; + if (spinner != null) + return new DrawableSpinner(spinner); return null; } } diff --git a/osu.Game.Modes.Osu/UI/OsuPlayfield.cs b/osu.Game.Modes.Osu/UI/OsuPlayfield.cs index 20164060fe..41abb4736f 100644 --- a/osu.Game.Modes.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Modes.Osu/UI/OsuPlayfield.cs @@ -10,10 +10,12 @@ using osu.Game.Modes.Osu.Objects.Drawables; using osu.Game.Modes.Osu.Objects.Drawables.Connections; using osu.Game.Modes.UI; using System.Linq; +using osu.Game.Graphics.Cursor; +using OpenTK.Graphics; namespace osu.Game.Modes.Osu.UI { - public class OsuPlayfield : Playfield + public class OsuPlayfield : Playfield { private Container approachCircles; private Container judgementLayer; @@ -53,11 +55,18 @@ namespace osu.Game.Modes.Osu.UI { RelativeSizeAxes = Axes.Both, Depth = -1, - } + }, }); } - public override void Add(DrawableHitObject h) + protected override void LoadComplete() + { + base.LoadComplete(); + if (InputManager?.ReplayInputHandler != null) + Add(new OsuCursorContainer { Colour = Color4.LightYellow }); + } + + public override void Add(DrawableHitObject h) { h.Depth = (float)h.HitObject.StartTime; IDrawableHitObjectWithProxiedApproach c = h as IDrawableHitObjectWithProxiedApproach; @@ -78,9 +87,9 @@ namespace osu.Game.Modes.Osu.UI .OrderBy(h => h.StartTime); } - private void judgement(DrawableHitObject h, JudgementInfo j) + private void judgement(DrawableHitObject h, JudgementInfo j) { - HitExplosion explosion = new HitExplosion((OsuJudgementInfo)j, (OsuHitObject)h.HitObject); + HitExplosion explosion = new HitExplosion((OsuJudgementInfo)j, h.HitObject); judgementLayer.Add(explosion); } diff --git a/osu.Game.Modes.Osu/UI/OsuScoreOverlay.cs b/osu.Game.Modes.Osu/UI/OsuScoreOverlay.cs index a7141634c5..d91a751c26 100644 --- a/osu.Game.Modes.Osu/UI/OsuScoreOverlay.cs +++ b/osu.Game.Modes.Osu/UI/OsuScoreOverlay.cs @@ -22,7 +22,7 @@ namespace osu.Game.Modes.Osu.UI Margin = new MarginPadding { Right = 5 }, }; - protected override PercentageCounter CreateAccuracyCounter() => new PercentageCounter() + protected override PercentageCounter CreateAccuracyCounter() => new PercentageCounter { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, @@ -31,7 +31,7 @@ namespace osu.Game.Modes.Osu.UI Margin = new MarginPadding { Right = 5 }, }; - protected override ComboCounter CreateComboCounter() => new OsuComboCounter() + protected override ComboCounter CreateComboCounter() => new OsuComboCounter { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, diff --git a/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj b/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj index f53066ecde..0571ec2956 100644 --- a/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj +++ b/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj @@ -70,6 +70,7 @@ + diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHit.cs b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHit.cs index 6666e2ad97..a6af85b6fe 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHit.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHit.cs @@ -10,7 +10,7 @@ using OpenTK; namespace osu.Game.Modes.Taiko.Objects.Drawable { - class DrawableTaikoHit : Sprite + internal class DrawableTaikoHit : Sprite { private TaikoBaseHit h; diff --git a/osu.Game.Modes.Taiko/Objects/TaikoConverter.cs b/osu.Game.Modes.Taiko/Objects/TaikoConverter.cs index 5eb69b62ce..3a0e07e390 100644 --- a/osu.Game.Modes.Taiko/Objects/TaikoConverter.cs +++ b/osu.Game.Modes.Taiko/Objects/TaikoConverter.cs @@ -8,7 +8,7 @@ using osu.Game.Beatmaps; namespace osu.Game.Modes.Taiko.Objects { - class TaikoConverter : HitObjectConverter + internal class TaikoConverter : HitObjectConverter { public override List Convert(Beatmap beatmap) { diff --git a/osu.Game.Modes.Taiko/TaikoMod.cs b/osu.Game.Modes.Taiko/TaikoMod.cs index fd09e4528e..62db875991 100644 --- a/osu.Game.Modes.Taiko/TaikoMod.cs +++ b/osu.Game.Modes.Taiko/TaikoMod.cs @@ -1,8 +1,6 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; - namespace osu.Game.Modes.Taiko { public class TaikoModNoFail : ModNoFail diff --git a/osu.Game.Modes.Taiko/TaikoRuleset.cs b/osu.Game.Modes.Taiko/TaikoRuleset.cs index 71b747f895..73e65a2734 100644 --- a/osu.Game.Modes.Taiko/TaikoRuleset.cs +++ b/osu.Game.Modes.Taiko/TaikoRuleset.cs @@ -8,6 +8,7 @@ using osu.Game.Modes.Osu.UI; using osu.Game.Modes.Taiko.UI; using osu.Game.Modes.UI; using osu.Game.Beatmaps; +using osu.Game.Screens.Play; namespace osu.Game.Modes.Taiko { @@ -15,7 +16,11 @@ namespace osu.Game.Modes.Taiko { public override ScoreOverlay CreateScoreOverlay() => new OsuScoreOverlay(); - public override HitRenderer CreateHitRendererWith(Beatmap beatmap) => new TaikoHitRenderer { Beatmap = beatmap }; + public override HitRenderer CreateHitRendererWith(Beatmap beatmap, PlayerInputManager input = null) => new TaikoHitRenderer + { + Beatmap = beatmap, + InputManager = input, + }; public override IEnumerable GetModsFor(ModType type) { @@ -76,7 +81,7 @@ namespace osu.Game.Modes.Taiko public override FontAwesome Icon => FontAwesome.fa_osu_taiko_o; - public override ScoreProcessor CreateScoreProcessor(int hitObjectCount) => null; + public override ScoreProcessor CreateScoreProcessor(int hitObjectCount = 0) => null; public override HitObjectParser CreateHitObjectParser() => new NullHitObjectParser(); diff --git a/osu.Game.Modes.Taiko/UI/TaikoHitRenderer.cs b/osu.Game.Modes.Taiko/UI/TaikoHitRenderer.cs index 0e2b6cdc83..1b9bb682f1 100644 --- a/osu.Game.Modes.Taiko/UI/TaikoHitRenderer.cs +++ b/osu.Game.Modes.Taiko/UI/TaikoHitRenderer.cs @@ -12,8 +12,8 @@ namespace osu.Game.Modes.Taiko.UI { protected override HitObjectConverter Converter => new TaikoConverter(); - protected override Playfield CreatePlayfield() => new TaikoPlayfield(); + protected override Playfield CreatePlayfield() => new TaikoPlayfield(); - protected override DrawableHitObject GetVisualRepresentation(TaikoBaseHit h) => null;// new DrawableTaikoHit(h); + protected override DrawableHitObject GetVisualRepresentation(TaikoBaseHit h) => null;// new DrawableTaikoHit(h); } } diff --git a/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs index e8aa28e5b3..2fcfce4fa3 100644 --- a/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs @@ -5,13 +5,14 @@ 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; namespace osu.Game.Modes.Taiko.UI { - public class TaikoPlayfield : Playfield + public class TaikoPlayfield : Playfield { public TaikoPlayfield() { diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index 90d1aa542f..ae936f3f49 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -23,7 +23,7 @@ namespace osu.Game.Tests.Beatmaps.IO [TestFixture] public class ImportBeatmapTest { - const string osz_path = @"../../../osu-resources/osu.Game.Resources/Beatmaps/241526 Soleily - Renatus.osz"; + private const string osz_path = @"../../../osu-resources/osu.Game.Resources/Beatmaps/241526 Soleily - Renatus.osz"; [OneTimeSetUp] public void SetUp() @@ -69,7 +69,7 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.IsTrue(File.Exists(temp)); - var importer = new BeatmapImporter(client); + var importer = new BeatmapIPCChannel(client); if (!importer.ImportAsync(temp).Wait(1000)) Assert.Fail(@"IPC took too long to send"); @@ -91,7 +91,7 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.IsTrue(File.Exists(temp)); - using (FileStream stream = File.OpenRead(temp)) + using (File.OpenRead(temp)) osu.Dependencies.Get().Import(temp); ensureLoaded(osu); @@ -107,7 +107,7 @@ namespace osu.Game.Tests.Beatmaps.IO private string prepareTempCopy(string path) { var temp = Path.GetTempFileName(); - return new FileInfo(osz_path).CopyTo(temp, true).FullName; + return new FileInfo(path).CopyTo(temp, true).FullName; } private OsuGameBase loadOsu(GameHost host) @@ -130,13 +130,13 @@ namespace osu.Game.Tests.Beatmaps.IO Action waitAction = () => { - while ((resultSets = osu.Dependencies.Get() - .Query().Where(s => s.OnlineBeatmapSetID == 241526)).Count() == 0) + while (!(resultSets = osu.Dependencies.Get() + .Query().Where(s => s.OnlineBeatmapSetID == 241526)).Any()) Thread.Sleep(50); }; Assert.IsTrue(waitAction.BeginInvoke(null, null).AsyncWaitHandle.WaitOne(timeout), - $@"BeatmapSet did not import to the database in allocated time."); + @"BeatmapSet did not import to the database in allocated time."); //ensure we were stored to beatmap database backing... @@ -168,7 +168,7 @@ namespace osu.Game.Tests.Beatmaps.IO var beatmap = osu.Dependencies.Get().GetWorkingBeatmap(set.Beatmaps.First(b => b.Mode == PlayMode.Osu))?.Beatmap; - Assert.IsTrue(beatmap.HitObjects.Count > 0); + Assert.IsTrue(beatmap?.HitObjects.Count > 0); } } } diff --git a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs index faa83518ba..2a69be92ca 100644 --- a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs @@ -77,7 +77,7 @@ namespace osu.Game.Tests.Beatmaps.IO using (var stream = new StreamReader( reader.GetStream("Soleily - Renatus (Deif) [Platter].osu"))) { - Assert.AreEqual("osu file format v13", stream.ReadLine().Trim()); + Assert.AreEqual("osu file format v13", stream.ReadLine()?.Trim()); } } } diff --git a/osu.Game/Beatmaps/DifficultyCalculator.cs b/osu.Game/Beatmaps/DifficultyCalculator.cs index 8214496363..cb9d9f6cbc 100644 --- a/osu.Game/Beatmaps/DifficultyCalculator.cs +++ b/osu.Game/Beatmaps/DifficultyCalculator.cs @@ -37,7 +37,7 @@ namespace osu.Game.Beatmaps protected abstract HitObjectConverter Converter { get; } - public DifficultyCalculator(Beatmap beatmap) + protected DifficultyCalculator(Beatmap beatmap) { Objects = Converter.Convert(beatmap); PreprocessHitObjects(); diff --git a/osu.Game/Beatmaps/Drawables/BeatmapBackgroundSprite.cs b/osu.Game/Beatmaps/Drawables/BeatmapBackgroundSprite.cs index 5af2e7c197..9b897b4912 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapBackgroundSprite.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapBackgroundSprite.cs @@ -6,7 +6,7 @@ using osu.Framework.Graphics.Sprites; namespace osu.Game.Beatmaps.Drawables { - class BeatmapBackgroundSprite : Sprite + internal class BeatmapBackgroundSprite : Sprite { private readonly WorkingBeatmap working; diff --git a/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs b/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs index 9c1f1a02fc..2cccbc322a 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapGroup.cs @@ -10,7 +10,7 @@ using osu.Game.Database; namespace osu.Game.Beatmaps.Drawables { - class BeatmapGroup : IStateful + internal class BeatmapGroup : IStateful { public BeatmapPanel SelectedPanel; diff --git a/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs b/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs index f110fc37ac..aa5891c37e 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapPanel.cs @@ -18,7 +18,7 @@ using osu.Game.Graphics.Sprites; namespace osu.Game.Beatmaps.Drawables { - class BeatmapPanel : Panel + internal class BeatmapPanel : Panel { public BeatmapInfo Beatmap; private Sprite background; diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs index bbdc22e30d..c1ac93f70c 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapSetHeader.cs @@ -17,7 +17,7 @@ using OpenTK.Graphics; namespace osu.Game.Beatmaps.Drawables { - class BeatmapSetHeader : Panel + internal class BeatmapSetHeader : Panel { public Action GainedSelection; private SpriteText title, artist; @@ -96,7 +96,7 @@ namespace osu.Game.Beatmaps.Drawables base.Dispose(isDisposing); } - class PanelBackground : BufferedContainer + private class PanelBackground : BufferedContainer { private readonly WorkingBeatmap working; @@ -160,7 +160,7 @@ namespace osu.Game.Beatmaps.Drawables Anchor = Anchor.Centre, Origin = Anchor.Centre, FillMode = FillMode.Fill, - }.LoadAsync(game, (bg) => + }.LoadAsync(game, bg => { Add(bg); ForceRedraw(); diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index e0e390fcb1..47ae4d7985 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -12,7 +12,7 @@ using OpenTK.Graphics; namespace osu.Game.Beatmaps.Drawables { - class DifficultyIcon : Container + internal class DifficultyIcon : Container { private readonly BeatmapInfo beatmap; private OsuColour palette; @@ -34,6 +34,7 @@ namespace osu.Game.Beatmaps.Drawables new TextAwesome { Anchor = Anchor.Centre, + Origin = Anchor.Centre, TextSize = Size.X, Colour = getColour(beatmap), Icon = FontAwesome.fa_circle @@ -41,6 +42,7 @@ namespace osu.Game.Beatmaps.Drawables new TextAwesome { Anchor = Anchor.Centre, + Origin = Anchor.Centre, TextSize = Size.X, Colour = Color4.White, Icon = Ruleset.GetRuleset(beatmap.Mode).Icon @@ -48,7 +50,7 @@ namespace osu.Game.Beatmaps.Drawables }; } - enum DifficultyRating + private enum DifficultyRating { Easy, Normal, diff --git a/osu.Game/Beatmaps/Drawables/Panel.cs b/osu.Game/Beatmaps/Drawables/Panel.cs index be349f3378..a15d0c22f0 100644 --- a/osu.Game/Beatmaps/Drawables/Panel.cs +++ b/osu.Game/Beatmaps/Drawables/Panel.cs @@ -12,7 +12,7 @@ using osu.Framework.Extensions.Color4Extensions; namespace osu.Game.Beatmaps.Drawables { - class Panel : Container, IStateful + internal class Panel : Container, IStateful { public const float MAX_HEIGHT = 80; @@ -115,7 +115,7 @@ namespace osu.Game.Beatmaps.Drawables } } - enum PanelSelectedState + internal enum PanelSelectedState { Hidden, NotSelected, diff --git a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs index 7f55b3be1b..d13cb33554 100644 --- a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs @@ -17,8 +17,9 @@ namespace osu.Game.Beatmaps.Formats public static BeatmapDecoder GetDecoder(TextReader stream) { - var line = stream.ReadLine().Trim(); - if (!decoders.ContainsKey(line)) + var line = stream.ReadLine()?.Trim(); + + if (line == null || !decoders.ContainsKey(line)) throw new IOException(@"Unknown file format"); return (BeatmapDecoder)Activator.CreateInstance(decoders[line]); } diff --git a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs index aa2e2fbc75..9ce5a4cca0 100644 --- a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs @@ -197,7 +197,7 @@ namespace osu.Game.Beatmaps.Formats if (split.Length > 2) { - int kiaiFlags = split.Length > 7 ? Convert.ToInt32(split[7], NumberFormatInfo.InvariantInfo) : 0; + //int kiaiFlags = split.Length > 7 ? Convert.ToInt32(split[7], NumberFormatInfo.InvariantInfo) : 0; double beatLength = double.Parse(split[1].Trim(), NumberFormatInfo.InvariantInfo); cp = new ControlPoint { @@ -219,15 +219,18 @@ namespace osu.Game.Beatmaps.Formats throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {val}"); byte r, g, b; if (!byte.TryParse(split[0], out r) || !byte.TryParse(split[1], out g) || !byte.TryParse(split[2], out b)) - throw new InvalidOperationException($@"Color must be specified with 8-bit integer components"); + throw new InvalidOperationException(@"Color must be specified with 8-bit integer components"); // Note: the combo index specified in the beatmap is discarded - beatmap.ComboColors.Add(new Color4 + if (key.StartsWith(@"Combo")) { - R = r / 255f, - G = g / 255f, - B = b / 255f, - A = 1f, - }); + beatmap.ComboColors.Add(new Color4 + { + R = r / 255f, + G = g / 255f, + B = b / 255f, + A = 1f, + }); + } } protected override void ParseFile(TextReader stream, Beatmap beatmap) @@ -235,10 +238,9 @@ namespace osu.Game.Beatmaps.Formats HitObjectParser parser = null; var section = Section.None; - string line; while (true) { - line = stream.ReadLine(); + var line = stream.ReadLine(); if (line == null) break; if (string.IsNullOrEmpty(line)) diff --git a/osu.Game/Beatmaps/Timing/ControlPoint.cs b/osu.Game/Beatmaps/Timing/ControlPoint.cs index 88e2c2d0f4..bd53928a32 100644 --- a/osu.Game/Beatmaps/Timing/ControlPoint.cs +++ b/osu.Game/Beatmaps/Timing/ControlPoint.cs @@ -15,7 +15,8 @@ namespace osu.Game.Beatmaps.Timing public double BeatLength; public double VelocityAdjustment; public bool TimingChange; - + public bool KiaiMode; + } internal enum TimeSignatures diff --git a/osu.Game/Beatmaps/Timing/TimingChange.cs b/osu.Game/Beatmaps/Timing/TimingChange.cs index 74e855e157..b759fcd01c 100644 --- a/osu.Game/Beatmaps/Timing/TimingChange.cs +++ b/osu.Game/Beatmaps/Timing/TimingChange.cs @@ -3,7 +3,7 @@ namespace osu.Game.Beatmaps.Timing { - class TimingChange : ControlPoint + internal class TimingChange : ControlPoint { public TimingChange(double beatLength) { diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 9393c2b0e9..1d4047ea45 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -2,12 +2,15 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Collections.Generic; using System.IO; using osu.Framework.Audio.Track; +using osu.Framework.Configuration; using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.IO; using osu.Game.Database; +using osu.Game.Modes; namespace osu.Game.Beatmaps { @@ -17,6 +20,16 @@ namespace osu.Game.Beatmaps public readonly BeatmapSetInfo BeatmapSetInfo; + /// + /// A play mode that is preferred for this beatmap. PlayMode will become this mode where conversion is feasible, + /// or otherwise to the beatmap's default. + /// + public PlayMode? PreferredPlayMode; + + public PlayMode PlayMode => beatmap?.BeatmapInfo?.Mode > PlayMode.Osu ? beatmap.BeatmapInfo.Mode : PreferredPlayMode ?? PlayMode.Osu; + + public readonly Bindable> Mods = new Bindable>(); + public readonly bool WithStoryboard; protected abstract ArchiveReader GetReader(); diff --git a/osu.Game/Database/BeatmapDatabase.cs b/osu.Game/Database/BeatmapDatabase.cs index 03f904b7e8..871251b882 100644 --- a/osu.Game/Database/BeatmapDatabase.cs +++ b/osu.Game/Database/BeatmapDatabase.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Linq.Expressions; -using System.Security.Cryptography; +using osu.Framework.Extensions; using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Beatmaps; @@ -20,19 +20,20 @@ namespace osu.Game.Database { public class BeatmapDatabase { - private SQLiteConnection connection { get; set; } + private SQLiteConnection connection { get; } private Storage storage; public event Action BeatmapSetAdded; public event Action BeatmapSetRemoved; - private BeatmapImporter ipc; + // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) + private BeatmapIPCChannel ipc; - public BeatmapDatabase(Storage storage, GameHost importHost = null) + public BeatmapDatabase(Storage storage, IIpcHost importHost = null) { this.storage = storage; if (importHost != null) - ipc = new BeatmapImporter(importHost, this); + ipc = new BeatmapIPCChannel(importHost, this); if (connection == null) { @@ -73,7 +74,7 @@ namespace osu.Game.Database } catch (Exception e) { - Logger.Error(e, $@"Could not delete beatmap {b.ToString()}"); + Logger.Error(e, $@"Could not delete beatmap {b}"); } } @@ -149,9 +150,9 @@ namespace osu.Game.Database catch (Exception e) { e = e.InnerException ?? e; - Logger.Error(e, $@"Could not import beatmap set"); + Logger.Error(e, @"Could not import beatmap set"); } - + // Batch commit with multiple sets to database Import(sets); } @@ -162,7 +163,7 @@ namespace osu.Game.Database /// Location on disk public void Import(string path) { - Import(new [] { path }); + Import(new[] { path }); } /// @@ -181,10 +182,9 @@ namespace osu.Game.Database if (File.Exists(path)) // Not always the case, i.e. for LegacyFilesystemReader { - using (var md5 = MD5.Create()) using (var input = storage.GetStream(path)) { - hash = BitConverter.ToString(md5.ComputeHash(input)).Replace("-", "").ToLowerInvariant(); + hash = input.GetMd5Hash(); input.Seek(0, SeekOrigin.Begin); path = Path.Combine(@"beatmaps", hash.Remove(1), hash.Remove(2), hash); if (!storage.Exists(path)) @@ -216,22 +216,29 @@ namespace osu.Game.Database Metadata = metadata }; - using (var reader = ArchiveReader.GetReader(storage, path)) + using (var archive = ArchiveReader.GetReader(storage, path)) { - string[] mapNames = reader.BeatmapFilenames; + string[] mapNames = archive.BeatmapFilenames; foreach (var name in mapNames) - using (var stream = new StreamReader(reader.GetStream(name))) + using (var raw = archive.GetStream(name)) + using (var ms = new MemoryStream()) //we need a memory stream so we can seek and shit + using (var sr = new StreamReader(ms)) { - var decoder = BeatmapDecoder.GetDecoder(stream); - Beatmap beatmap = decoder.Decode(stream); + raw.CopyTo(ms); + ms.Position = 0; + + var decoder = BeatmapDecoder.GetDecoder(sr); + Beatmap beatmap = decoder.Decode(sr); + beatmap.BeatmapInfo.Path = name; + beatmap.BeatmapInfo.Hash = ms.GetMd5Hash(); // TODO: Diff beatmap metadata with set metadata and leave it here if necessary beatmap.BeatmapInfo.Metadata = null; beatmapSet.Beatmaps.Add(beatmap.BeatmapInfo); } - beatmapSet.StoryboardFile = reader.StoryboardFilename; + beatmapSet.StoryboardFile = archive.StoryboardFilename; } return beatmapSet; @@ -318,8 +325,7 @@ namespace osu.Game.Database return item; } - readonly Type[] validTypes = new[] - { + private readonly Type[] validTypes = { typeof(BeatmapSetInfo), typeof(BeatmapInfo), typeof(BeatmapMetadata), @@ -329,7 +335,7 @@ namespace osu.Game.Database public void Update(T record, bool cascade = true) where T : class { if (validTypes.All(t => t != typeof(T))) - throw new ArgumentException(nameof(T), "Must be a type managed by BeatmapDatabase"); + throw new ArgumentException("Must be a type managed by BeatmapDatabase", nameof(T)); if (cascade) connection.UpdateWithChildren(record); else diff --git a/osu.Game/Database/BeatmapInfo.cs b/osu.Game/Database/BeatmapInfo.cs index 1c2ae2bf78..28c9e51d2a 100644 --- a/osu.Game/Database/BeatmapInfo.cs +++ b/osu.Game/Database/BeatmapInfo.cs @@ -15,9 +15,9 @@ namespace osu.Game.Database [PrimaryKey, AutoIncrement] public int ID { get; set; } - public int? OnlineBeatmapID { get; set; } = null; + public int? OnlineBeatmapID { get; set; } - public int? OnlineBeatmapSetID { get; set; } = null; + public int? OnlineBeatmapSetID { get; set; } [ForeignKey(typeof(BeatmapSetInfo))] public int BeatmapSetInfoID { get; set; } @@ -39,6 +39,8 @@ namespace osu.Game.Database public string Path { get; set; } + public string Hash { get; set; } + // General public int AudioLeadIn { get; set; } public bool Countdown { get; set; } @@ -57,13 +59,14 @@ namespace osu.Game.Database { get { - return StoredBookmarks.Split(',').Select(b => int.Parse(b)).ToArray(); + return StoredBookmarks.Split(',').Select(int.Parse).ToArray(); } set { StoredBookmarks = string.Join(",", value); } } + public double DistanceSpacing { get; set; } public int BeatDivisor { get; set; } public int GridSize { get; set; } @@ -77,7 +80,7 @@ namespace osu.Game.Database { get { - return (starDifficulty < 0) ? (BaseDifficulty?.OverallDifficulty ?? 5) : starDifficulty; + return starDifficulty < 0 ? (BaseDifficulty?.OverallDifficulty ?? 5) : starDifficulty; } set { starDifficulty = value; } diff --git a/osu.Game/Database/BeatmapMetadata.cs b/osu.Game/Database/BeatmapMetadata.cs index 908fe3fd2b..d7dbb02cf9 100644 --- a/osu.Game/Database/BeatmapMetadata.cs +++ b/osu.Game/Database/BeatmapMetadata.cs @@ -10,7 +10,7 @@ namespace osu.Game.Database [PrimaryKey, AutoIncrement] public int ID { get; set; } - public int? OnlineBeatmapSetID { get; set; } = null; + public int? OnlineBeatmapSetID { get; set; } public string Title { get; set; } public string TitleUnicode { get; set; } diff --git a/osu.Game/Database/BeatmapSetInfo.cs b/osu.Game/Database/BeatmapSetInfo.cs index 07076d881e..3e05451bed 100644 --- a/osu.Game/Database/BeatmapSetInfo.cs +++ b/osu.Game/Database/BeatmapSetInfo.cs @@ -12,7 +12,7 @@ namespace osu.Game.Database [PrimaryKey, AutoIncrement] public int ID { get; set; } - public int? OnlineBeatmapSetID { get; set; } = null; + public int? OnlineBeatmapSetID { get; set; } [OneToOne(CascadeOperations = CascadeOperation.All)] public BeatmapMetadata Metadata { get; set; } diff --git a/osu.Game/Database/ScoreDatabase.cs b/osu.Game/Database/ScoreDatabase.cs new file mode 100644 index 0000000000..6fc65331ff --- /dev/null +++ b/osu.Game/Database/ScoreDatabase.cs @@ -0,0 +1,110 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.IO; +using osu.Framework.Platform; +using osu.Game.IO.Legacy; +using osu.Game.IPC; +using osu.Game.Modes; +using SharpCompress.Compressors.LZMA; + +namespace osu.Game.Database +{ + public class ScoreDatabase + { + private readonly Storage storage; + private readonly BeatmapDatabase beatmaps; + + private const string replay_folder = @"replays"; + + private ScoreIPCChannel ipc; + + public ScoreDatabase(Storage storage, IIpcHost importHost = null, BeatmapDatabase beatmaps = null) + { + this.storage = storage; + this.beatmaps = beatmaps; + + if (importHost != null) + ipc = new ScoreIPCChannel(importHost, this); + } + + public Score ReadReplayFile(string replayFilename) + { + Score score; + + using (Stream s = storage.GetStream(Path.Combine(replay_folder, replayFilename))) + using (SerializationReader sr = new SerializationReader(s)) + { + var ruleset = Ruleset.GetRuleset((PlayMode)sr.ReadByte()); + var processor = ruleset.CreateScoreProcessor(); + + score = processor.GetScore(); + + /* score.Pass = true;*/ + var version = sr.ReadInt32(); + /* score.FileChecksum = */ + var beatmapHash = sr.ReadString(); + score.Beatmap = beatmaps.Query().Where(b => b.Hash == beatmapHash).FirstOrDefault(); + /* score.PlayerName = */ + sr.ReadString(); + /* var localScoreChecksum = */ + sr.ReadString(); + /* score.Count300 = */ + sr.ReadUInt16(); + /* score.Count100 = */ + sr.ReadUInt16(); + /* score.Count50 = */ + sr.ReadUInt16(); + /* score.CountGeki = */ + sr.ReadUInt16(); + /* score.CountKatu = */ + sr.ReadUInt16(); + /* score.CountMiss = */ + sr.ReadUInt16(); + score.TotalScore = sr.ReadInt32(); + score.MaxCombo = sr.ReadUInt16(); + /* score.Perfect = */ + sr.ReadBoolean(); + /* score.EnabledMods = (Mods)*/ + sr.ReadInt32(); + /* score.HpGraphString = */ + sr.ReadString(); + /* score.Date = */ + sr.ReadDateTime(); + + var compressedReplay = sr.ReadByteArray(); + + if (version >= 20140721) + /*OnlineId =*/ + sr.ReadInt64(); + else if (version >= 20121008) + /*OnlineId =*/ + sr.ReadInt32(); + + using (var replayInStream = new MemoryStream(compressedReplay)) + { + byte[] properties = new byte[5]; + if (replayInStream.Read(properties, 0, 5) != 5) + throw (new Exception("input .lzma is too short")); + long outSize = 0; + for (int i = 0; i < 8; i++) + { + int v = replayInStream.ReadByte(); + if (v < 0) + throw (new Exception("Can't Read 1")); + outSize |= ((long)(byte)v) << (8 * i); + } + + long compressedSize = replayInStream.Length - replayInStream.Position; + + using (var lzma = new LzmaStream(properties, replayInStream, compressedSize, outSize)) + using (var reader = new StreamReader(lzma)) + score.Replay = new LegacyReplay(reader); + } + } + + return score; + } + } +} diff --git a/osu.Game/Graphics/Backgrounds/Background.cs b/osu.Game/Graphics/Backgrounds/Background.cs index c35006f416..5bafc8cd64 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; - string textureName; + private string textureName; public Background(string textureName = @"") { diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index f760919fb2..4c50162194 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -117,7 +117,7 @@ namespace osu.Game.Graphics.Backgrounds private void addTriangle(bool randomY) { var sprite = CreateTriangle(); - float triangleHeight = (sprite.DrawHeight / DrawHeight); + float triangleHeight = sprite.DrawHeight / DrawHeight; sprite.Position = new Vector2(RNG.NextSingle(), randomY ? RNG.NextSingle() * (1 + triangleHeight) - triangleHeight : 1); Add(sprite); } diff --git a/osu.Game/Graphics/Containers/ParallaxContainer.cs b/osu.Game/Graphics/Containers/ParallaxContainer.cs index 1908bd0aa2..88627dbd30 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 { - class ParallaxContainer : Container + internal class ParallaxContainer : Container { public float ParallaxAmount = 0.02f; @@ -51,7 +51,7 @@ namespace osu.Game.Graphics.Containers }; } - bool firstUpdate = true; + private bool firstUpdate = true; protected override void Update() { diff --git a/osu.Game/Graphics/Cursor/CursorTrail.cs b/osu.Game/Graphics/Cursor/CursorTrail.cs index 7e0937155c..09d1193661 100644 --- a/osu.Game/Graphics/Cursor/CursorTrail.cs +++ b/osu.Game/Graphics/Cursor/CursorTrail.cs @@ -16,8 +16,7 @@ using osu.Framework.Graphics.Colour; namespace osu.Game.Graphics.Cursor { - - class CursorTrail : Drawable + internal class CursorTrail : Drawable { public override bool Contains(Vector2 screenSpacePos) => true; public override bool HandleInput => true; @@ -32,7 +31,7 @@ namespace osu.Game.Graphics.Cursor private double timeOffset; private float time; - + private TrailDrawNodeSharedData trailDrawNodeSharedData = new TrailDrawNodeSharedData(); private const int max_sprites = 2048; @@ -46,7 +45,7 @@ namespace osu.Game.Graphics.Cursor { base.ApplyDrawNode(node); - TrailDrawNode tNode = node as TrailDrawNode; + TrailDrawNode tNode = (TrailDrawNode)node; tNode.Shader = shader; tNode.Texture = texture; tNode.Size = size; @@ -77,6 +76,12 @@ namespace osu.Game.Graphics.Cursor Scale = new Vector2(1 / texture.ScaleAdjust); } + protected override void LoadComplete() + { + base.LoadComplete(); + resetTime(); + } + protected override void Update() { base.Update(); @@ -117,7 +122,7 @@ namespace osu.Game.Graphics.Cursor float distance = diff.Length; Vector2 direction = diff / distance; - float interval = (size.X / 2) * 0.9f; + float interval = size.X / 2 * 0.9f; for (float d = interval; d < distance; d += interval) { @@ -137,7 +142,7 @@ namespace osu.Game.Graphics.Cursor currentIndex = (currentIndex + 1) % max_sprites; } - struct TrailPart + private struct TrailPart { public Vector2 Position; public float Time; @@ -145,12 +150,12 @@ namespace osu.Game.Graphics.Cursor public bool WasUpdated; } - class TrailDrawNodeSharedData + private class TrailDrawNodeSharedData { public VertexBuffer VertexBuffer; } - class TrailDrawNode : DrawNode + private class TrailDrawNode : DrawNode { public Shader Shader; public Texture Texture; diff --git a/osu.Game/Graphics/Cursor/OsuCursorContainer.cs b/osu.Game/Graphics/Cursor/OsuCursorContainer.cs index 4112672ccc..8b71182263 100644 --- a/osu.Game/Graphics/Cursor/OsuCursorContainer.cs +++ b/osu.Game/Graphics/Cursor/OsuCursorContainer.cs @@ -17,7 +17,7 @@ using System; namespace osu.Game.Graphics.Cursor { - class OsuCursorContainer : CursorContainer + public class OsuCursorContainer : CursorContainer { protected override Drawable CreateCursor() => new OsuCursor(); @@ -40,7 +40,7 @@ namespace osu.Game.Graphics.Cursor return base.OnMouseUp(state, args); } - class OsuCursor : Container + public class OsuCursor : Container { private Container cursorContainer; private Bindable cursorScale; diff --git a/osu.Game/Graphics/Processing/RatioAdjust.cs b/osu.Game/Graphics/Processing/RatioAdjust.cs index 72d81733e5..219d75c675 100644 --- a/osu.Game/Graphics/Processing/RatioAdjust.cs +++ b/osu.Game/Graphics/Processing/RatioAdjust.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics; namespace osu.Game.Graphics.Processing { - class RatioAdjust : Container + internal class RatioAdjust : Container { public override bool Contains(Vector2 screenSpacePos) => true; diff --git a/osu.Game/Graphics/TextAwesome.cs b/osu.Game/Graphics/TextAwesome.cs index 13d3cbc8c3..45f9ddeec9 100644 --- a/osu.Game/Graphics/TextAwesome.cs +++ b/osu.Game/Graphics/TextAwesome.cs @@ -26,11 +26,6 @@ namespace osu.Game.Graphics Text = ((char)icon).ToString(); } } - - public TextAwesome() - { - Origin = Framework.Graphics.Anchor.Centre; - } } public enum FontAwesome diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs index 6b43f023a6..590c5bf393 100644 --- a/osu.Game/Graphics/UserInterface/DialogButton.cs +++ b/osu.Game/Graphics/UserInterface/DialogButton.cs @@ -100,7 +100,7 @@ namespace osu.Game.Graphics.UserInterface Delay(click_duration); Schedule(delegate { - colourContainer.ResizeTo(new Vector2(0.8f, 1f), 0, EasingTypes.None); + colourContainer.ResizeTo(new Vector2(0.8f, 1f)); spriteText.Spacing = Vector2.Zero; glowContainer.FadeOut(); }); diff --git a/osu.Game/Graphics/UserInterface/Nub.cs b/osu.Game/Graphics/UserInterface/Nub.cs index a64b36208e..6f3529b67d 100644 --- a/osu.Game/Graphics/UserInterface/Nub.cs +++ b/osu.Game/Graphics/UserInterface/Nub.cs @@ -20,7 +20,7 @@ namespace osu.Game.Graphics.UserInterface private Box fill; - const float border_width = 3; + private const float border_width = 3; private Color4 glowingColour, idleColour; public Nub() diff --git a/osu.Game/Graphics/UserInterface/OsuDropDownMenu.cs b/osu.Game/Graphics/UserInterface/OsuDropDownMenu.cs index 875806346e..b9ebcff65a 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropDownMenu.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropDownMenu.cs @@ -36,10 +36,7 @@ namespace osu.Game.Graphics.UserInterface protected override void UpdateContentHeight() { - if (State == DropDownMenuState.Opened) - ContentContainer.ResizeTo(new Vector2(1, ContentHeight), 300, EasingTypes.OutQuint); - else - ContentContainer.ResizeTo(new Vector2(1, 0), 300, EasingTypes.OutQuint); + ContentContainer.ResizeTo(State == DropDownMenuState.Opened ? new Vector2(1, ContentHeight) : new Vector2(1, 0), 300, EasingTypes.OutQuint); } } } \ No newline at end of file diff --git a/osu.Game/Graphics/UserInterface/PercentageCounter.cs b/osu.Game/Graphics/UserInterface/PercentageCounter.cs index 1b29fcc88f..068b46c02b 100644 --- a/osu.Game/Graphics/UserInterface/PercentageCounter.cs +++ b/osu.Game/Graphics/UserInterface/PercentageCounter.cs @@ -49,7 +49,7 @@ namespace osu.Game.Graphics.UserInterface public override void Apply(Drawable d) { base.Apply(d); - (d as PercentageCounter).DisplayedCount = CurrentValue; + ((PercentageCounter)d).DisplayedCount = CurrentValue; } } } diff --git a/osu.Game/Graphics/UserInterface/RollingCounter.cs b/osu.Game/Graphics/UserInterface/RollingCounter.cs index 9d45b13ca9..447f07c3ae 100644 --- a/osu.Game/Graphics/UserInterface/RollingCounter.cs +++ b/osu.Game/Graphics/UserInterface/RollingCounter.cs @@ -18,7 +18,7 @@ namespace osu.Game.Graphics.UserInterface /// Type of the Transform to use. /// /// - /// Must be a subclass of Transform + /// Must be a subclass of Transform(T) /// protected virtual Type TransformType => typeof(Transform); @@ -107,7 +107,7 @@ namespace osu.Game.Graphics.UserInterface { Children = new Drawable[] { - DisplayedCountSpriteText = new OsuSpriteText() + DisplayedCountSpriteText = new OsuSpriteText { Font = @"Venera" }, diff --git a/osu.Game/Graphics/UserInterface/ScoreCounter.cs b/osu.Game/Graphics/UserInterface/ScoreCounter.cs index ebe5b63c5a..2961a6de40 100644 --- a/osu.Game/Graphics/UserInterface/ScoreCounter.cs +++ b/osu.Game/Graphics/UserInterface/ScoreCounter.cs @@ -66,7 +66,7 @@ namespace osu.Game.Graphics.UserInterface public override void Apply(Drawable d) { base.Apply(d); - (d as ScoreCounter).DisplayedCount = CurrentValue; + ((ScoreCounter)d).DisplayedCount = CurrentValue; } } } diff --git a/osu.Game/Graphics/UserInterface/StarCounter.cs b/osu.Game/Graphics/UserInterface/StarCounter.cs index d603493d38..a1da18e95a 100644 --- a/osu.Game/Graphics/UserInterface/StarCounter.cs +++ b/osu.Game/Graphics/UserInterface/StarCounter.cs @@ -32,7 +32,7 @@ namespace osu.Game.Graphics.UserInterface private float minStarAlpha => 0.5f; private const float star_size = 20; - private float star_spacing = 4; + private const float star_spacing = 4; private float count; @@ -145,7 +145,7 @@ namespace osu.Game.Graphics.UserInterface } } - class Star : Container + private class Star : Container { public TextAwesome Icon; public Star() diff --git a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs index 81cb94ce12..8221ef952f 100644 --- a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs +++ b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs @@ -98,6 +98,7 @@ namespace osu.Game.Graphics.UserInterface icon = new TextAwesome { Anchor = Anchor.Centre, + Origin = Anchor.Centre, TextSize = 25, }, } diff --git a/osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs b/osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs index 04ba2fcd2f..a9f09ce86c 100644 --- a/osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs +++ b/osu.Game/Graphics/UserInterface/Volume/VolumeControl.cs @@ -87,7 +87,7 @@ namespace osu.Game.Graphics.UserInterface.Volume volumeMeterMusic.Bindable.BindTo(audio.VolumeTrack); } - ScheduledDelegate popOutDelegate; + private ScheduledDelegate popOutDelegate; private VolumeMeter volumeMeterEffect; private VolumeMeter volumeMeterMusic; diff --git a/osu.Game/Graphics/UserInterface/Volume/VolumeControlReceptor.cs b/osu.Game/Graphics/UserInterface/Volume/VolumeControlReceptor.cs index c12f01fa7c..c155871f33 100644 --- a/osu.Game/Graphics/UserInterface/Volume/VolumeControlReceptor.cs +++ b/osu.Game/Graphics/UserInterface/Volume/VolumeControlReceptor.cs @@ -8,7 +8,7 @@ using OpenTK.Input; namespace osu.Game.Graphics.UserInterface.Volume { - class VolumeControlReceptor : Container + internal class VolumeControlReceptor : Container { public Action ActionRequested; diff --git a/osu.Game/Graphics/UserInterface/Volume/VolumeMeter.cs b/osu.Game/Graphics/UserInterface/Volume/VolumeMeter.cs index 1a2589a7a9..4d68177fef 100644 --- a/osu.Game/Graphics/UserInterface/Volume/VolumeMeter.cs +++ b/osu.Game/Graphics/UserInterface/Volume/VolumeMeter.cs @@ -16,7 +16,7 @@ namespace osu.Game.Graphics.UserInterface.Volume internal class VolumeMeter : Container { private Box meterFill; - public BindableDouble Bindable { get; private set; } = new BindableDouble(); + public BindableDouble Bindable { get; } = new BindableDouble(); public VolumeMeter(string meterName) { diff --git a/osu.Game/IO/Legacy/ILegacySerializable.cs b/osu.Game/IO/Legacy/ILegacySerializable.cs new file mode 100644 index 0000000000..a280a5d13a --- /dev/null +++ b/osu.Game/IO/Legacy/ILegacySerializable.cs @@ -0,0 +1,11 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.IO.Legacy +{ + public interface ILegacySerializable + { + void ReadFromStream(SerializationReader sr); + void WriteToStream(SerializationWriter sw); + } +} \ No newline at end of file diff --git a/osu.Game/IO/Legacy/SerializationReader.cs b/osu.Game/IO/Legacy/SerializationReader.cs new file mode 100644 index 0000000000..10ba95167b --- /dev/null +++ b/osu.Game/IO/Legacy/SerializationReader.cs @@ -0,0 +1,279 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters; +using System.Runtime.Serialization.Formatters.Binary; +using System.Text; +using System.Threading; + +namespace osu.Game.IO.Legacy +{ + /// SerializationReader. Extends BinaryReader to add additional data types, + /// handle null strings and simplify use with ISerializable. + public class SerializationReader : BinaryReader + { + Stream stream; + + public SerializationReader(Stream s) + : base(s, Encoding.UTF8) + { + stream = s; + } + + public int RemainingBytes => (int)(stream.Length - stream.Position); + + /// Static method to take a SerializationInfo object (an input to an ISerializable constructor) + /// and produce a SerializationReader from which serialized objects can be read . + public static SerializationReader GetReader(SerializationInfo info) + { + byte[] byteArray = (byte[])info.GetValue("X", typeof(byte[])); + MemoryStream ms = new MemoryStream(byteArray); + return new SerializationReader(ms); + } + + /// Reads a string from the buffer. Overrides the base implementation so it can cope with nulls. + public override string ReadString() + { + if (0 == ReadByte()) return null; + return base.ReadString(); + } + + /// Reads a byte array from the buffer, handling nulls and the array length. + public byte[] ReadByteArray() + { + int len = ReadInt32(); + if (len > 0) return ReadBytes(len); + if (len < 0) return null; + return new byte[0]; + } + + /// Reads a char array from the buffer, handling nulls and the array length. + public char[] ReadCharArray() + { + int len = ReadInt32(); + if (len > 0) return ReadChars(len); + if (len < 0) return null; + return new char[0]; + } + + /// Reads a DateTime from the buffer. + public DateTime ReadDateTime() + { + long ticks = ReadInt64(); + if (ticks < 0) throw new AbandonedMutexException("oops"); + return new DateTime(ticks, DateTimeKind.Utc); + } + + /// Reads a generic list from the buffer. + public IList ReadBList(bool skipErrors = false) where T : ILegacySerializable, new() + { + int count = ReadInt32(); + if (count < 0) return null; + IList d = new List(count); + + SerializationReader sr = new SerializationReader(BaseStream); + + for (int i = 0; i < count; i++) + { + T obj = new T(); + try + { + obj.ReadFromStream(sr); + } + catch (Exception) + { + if (skipErrors) + continue; + throw; + } + + d.Add(obj); + } + + return d; + } + + /// Reads a generic list from the buffer. + public IList ReadList() + { + int count = ReadInt32(); + if (count < 0) return null; + IList d = new List(count); + for (int i = 0; i < count; i++) d.Add((T)ReadObject()); + return d; + } + + /// Reads a generic Dictionary from the buffer. + public IDictionary ReadDictionary() + { + int count = ReadInt32(); + if (count < 0) return null; + IDictionary d = new Dictionary(); + for (int i = 0; i < count; i++) d[(T)ReadObject()] = (U)ReadObject(); + return d; + } + + /// Reads an object which was added to the buffer by WriteObject. + public object ReadObject() + { + ObjType t = (ObjType)ReadByte(); + switch (t) + { + case ObjType.boolType: + return ReadBoolean(); + case ObjType.byteType: + return ReadByte(); + case ObjType.uint16Type: + return ReadUInt16(); + case ObjType.uint32Type: + return ReadUInt32(); + case ObjType.uint64Type: + return ReadUInt64(); + case ObjType.sbyteType: + return ReadSByte(); + case ObjType.int16Type: + return ReadInt16(); + case ObjType.int32Type: + return ReadInt32(); + case ObjType.int64Type: + return ReadInt64(); + case ObjType.charType: + return ReadChar(); + case ObjType.stringType: + return base.ReadString(); + case ObjType.singleType: + return ReadSingle(); + case ObjType.doubleType: + return ReadDouble(); + case ObjType.decimalType: + return ReadDecimal(); + case ObjType.dateTimeType: + return ReadDateTime(); + case ObjType.byteArrayType: + return ReadByteArray(); + case ObjType.charArrayType: + return ReadCharArray(); + case ObjType.otherType: + return DynamicDeserializer.Deserialize(BaseStream); + default: + return null; + } + } + + public class DynamicDeserializer + { + private static VersionConfigToNamespaceAssemblyObjectBinder versionBinder; + private static BinaryFormatter formatter; + + private static void initialize() + { + versionBinder = new VersionConfigToNamespaceAssemblyObjectBinder(); + formatter = new BinaryFormatter(); + formatter.AssemblyFormat = FormatterAssemblyStyle.Simple; + formatter.Binder = versionBinder; + } + + public static object Deserialize(Stream stream) + { + if (formatter == null) + initialize(); + return formatter.Deserialize(stream); + } + + #region Nested type: VersionConfigToNamespaceAssemblyObjectBinder + + public sealed class VersionConfigToNamespaceAssemblyObjectBinder : SerializationBinder + { + private readonly Dictionary cache = new Dictionary(); + + public override Type BindToType(string assemblyName, string typeName) + { + Type typeToDeserialize; + + if (cache.TryGetValue(assemblyName + typeName, out typeToDeserialize)) + return typeToDeserialize; + + List tmpTypes = new List(); + Type genType = null; + + try + { + if (typeName.Contains("System.Collections.Generic") && typeName.Contains("[[")) + { + string[] splitTyps = typeName.Split('['); + + foreach (string typ in splitTyps) + { + if (typ.Contains("Version")) + { + string asmTmp = typ.Substring(typ.IndexOf(',') + 1); + string asmName = asmTmp.Remove(asmTmp.IndexOf(']')).Trim(); + string typName = typ.Remove(typ.IndexOf(',')); + tmpTypes.Add(BindToType(asmName, typName)); + } + else if (typ.Contains("Generic")) + { + genType = BindToType(assemblyName, typ); + } + } + if (genType != null && tmpTypes.Count > 0) + { + return genType.MakeGenericType(tmpTypes.ToArray()); + } + } + + string toAssemblyName = assemblyName.Split(',')[0]; + Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); + foreach (Assembly a in assemblies) + { + if (a.FullName.Split(',')[0] == toAssemblyName) + { + typeToDeserialize = a.GetType(typeName); + break; + } + } + } + catch (Exception exception) + { + throw exception; + } + + cache.Add(assemblyName + typeName, typeToDeserialize); + + return typeToDeserialize; + } + } + + #endregion + } + } + + public enum ObjType : byte + { + nullType, + boolType, + byteType, + uint16Type, + uint32Type, + uint64Type, + sbyteType, + int16Type, + int32Type, + int64Type, + charType, + stringType, + singleType, + doubleType, + decimalType, + dateTimeType, + byteArrayType, + charArrayType, + otherType, + ILegacySerializableType + } +} diff --git a/osu.Game/IO/Legacy/SerializationWriter.cs b/osu.Game/IO/Legacy/SerializationWriter.cs new file mode 100644 index 0000000000..df5facbc5b --- /dev/null +++ b/osu.Game/IO/Legacy/SerializationWriter.cs @@ -0,0 +1,255 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.Serialization; +using System.Runtime.Serialization.Formatters; +using System.Runtime.Serialization.Formatters.Binary; +using System.Text; + +namespace osu.Game.IO.Legacy +{ + /// SerializationWriter. Extends BinaryWriter to add additional data types, + /// handle null strings and simplify use with ISerializable. + public class SerializationWriter : BinaryWriter + { + public SerializationWriter(Stream s) + : base(s, Encoding.UTF8) + { + } + + /// Static method to initialise the writer with a suitable MemoryStream. + public static SerializationWriter GetWriter() + { + MemoryStream ms = new MemoryStream(1024); + return new SerializationWriter(ms); + } + + /// Writes a string to the buffer. Overrides the base implementation so it can cope with nulls + public override void Write(string str) + { + if (str == null) + { + Write((byte)ObjType.nullType); + } + else + { + Write((byte)ObjType.stringType); + base.Write(str); + } + } + + /// Writes a byte array to the buffer. Overrides the base implementation to + /// send the length of the array which is needed when it is retrieved + public override void Write(byte[] b) + { + if (b == null) + { + Write(-1); + } + else + { + int len = b.Length; + Write(len); + if (len > 0) base.Write(b); + } + } + + /// Writes a char array to the buffer. Overrides the base implementation to + /// sends the length of the array which is needed when it is read. + public override void Write(char[] c) + { + if (c == null) + { + Write(-1); + } + else + { + int len = c.Length; + Write(len); + if (len > 0) base.Write(c); + } + } + + /// Writes a DateTime to the buffer. + public void Write(DateTime dt) + { + Write(dt.ToUniversalTime().Ticks); + } + + /// Writes a generic ICollection (such as an IList) to the buffer. + public void Write(List c) where T : ILegacySerializable + { + if (c == null) + { + Write(-1); + } + else + { + int count = c.Count; + Write(count); + for (int i = 0; i < count; i++) + c[i].WriteToStream(this); + } + } + + /// Writes a generic IDictionary to the buffer. + public void Write(IDictionary d) + { + if (d == null) + { + Write(-1); + } + else + { + Write(d.Count); + foreach (KeyValuePair kvp in d) + { + WriteObject(kvp.Key); + WriteObject(kvp.Value); + } + } + } + + /// Writes an arbitrary object to the buffer. Useful where we have something of type "object" + /// and don't know how to treat it. This works out the best method to use to write to the buffer. + public void WriteObject(object obj) + { + if (obj == null) + { + Write((byte)ObjType.nullType); + } + else + { + switch (obj.GetType().Name) + { + case "Boolean": + Write((byte)ObjType.boolType); + Write((bool)obj); + break; + + case "Byte": + Write((byte)ObjType.byteType); + Write((byte)obj); + break; + + case "UInt16": + Write((byte)ObjType.uint16Type); + Write((ushort)obj); + break; + + case "UInt32": + Write((byte)ObjType.uint32Type); + Write((uint)obj); + break; + + case "UInt64": + Write((byte)ObjType.uint64Type); + Write((ulong)obj); + break; + + case "SByte": + Write((byte)ObjType.sbyteType); + Write((sbyte)obj); + break; + + case "Int16": + Write((byte)ObjType.int16Type); + Write((short)obj); + break; + + case "Int32": + Write((byte)ObjType.int32Type); + Write((int)obj); + break; + + case "Int64": + Write((byte)ObjType.int64Type); + Write((long)obj); + break; + + case "Char": + Write((byte)ObjType.charType); + base.Write((char)obj); + break; + + case "String": + Write((byte)ObjType.stringType); + base.Write((string)obj); + break; + + case "Single": + Write((byte)ObjType.singleType); + Write((float)obj); + break; + + case "Double": + Write((byte)ObjType.doubleType); + Write((double)obj); + break; + + case "Decimal": + Write((byte)ObjType.decimalType); + Write((decimal)obj); + break; + + case "DateTime": + Write((byte)ObjType.dateTimeType); + Write((DateTime)obj); + break; + + case "Byte[]": + Write((byte)ObjType.byteArrayType); + base.Write((byte[])obj); + break; + + case "Char[]": + Write((byte)ObjType.charArrayType); + base.Write((char[])obj); + break; + + default: + Write((byte)ObjType.otherType); + BinaryFormatter b = new BinaryFormatter(); + b.AssemblyFormat = FormatterAssemblyStyle.Simple; + b.TypeFormat = FormatterTypeStyle.TypesWhenNeeded; + b.Serialize(BaseStream, obj); + break; + } // switch + } // if obj==null + } // WriteObject + + /// Adds the SerializationWriter buffer to the SerializationInfo at the end of GetObjectData(). + public void AddToInfo(SerializationInfo info) + { + byte[] b = ((MemoryStream)BaseStream).ToArray(); + info.AddValue("X", b, typeof(byte[])); + } + + public void WriteRawBytes(byte[] b) + { + base.Write(b); + } + + public void WriteByteArray(byte[] b) + { + if (b == null) + { + Write(-1); + } + else + { + int len = b.Length; + Write(len); + if (len > 0) base.Write(b); + } + } + + public void WriteUtf8(string str) + { + WriteRawBytes(Encoding.UTF8.GetBytes(str)); + } + } +} \ No newline at end of file diff --git a/osu.Game/IPC/BeatmapIPCChannel.cs b/osu.Game/IPC/BeatmapIPCChannel.cs new file mode 100644 index 0000000000..8c53910146 --- /dev/null +++ b/osu.Game/IPC/BeatmapIPCChannel.cs @@ -0,0 +1,43 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Diagnostics; +using System.Threading.Tasks; +using osu.Framework.Platform; +using osu.Game.Database; + +namespace osu.Game.IPC +{ + public class BeatmapIPCChannel : IpcChannel + { + private BeatmapDatabase beatmaps; + + public BeatmapIPCChannel(IIpcHost host, BeatmapDatabase beatmaps = null) + : base(host) + { + this.beatmaps = beatmaps; + MessageReceived += (msg) => + { + Debug.Assert(beatmaps != null); + ImportAsync(msg.Path); + }; + } + + public async Task ImportAsync(string path) + { + if (beatmaps == null) + { + //we want to contact a remote osu! to handle the import. + await SendMessageAsync(new BeatmapImportMessage { Path = path }); + return; + } + + beatmaps.Import(path); + } + } + + public class BeatmapImportMessage + { + public string Path; + } +} diff --git a/osu.Game/IPC/BeatmapImporter.cs b/osu.Game/IPC/BeatmapImporter.cs deleted file mode 100644 index b6ce4d1e35..0000000000 --- a/osu.Game/IPC/BeatmapImporter.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Diagnostics; -using System.Threading.Tasks; -using osu.Framework.Platform; -using osu.Game.Database; - -namespace osu.Game.IPC -{ - public class BeatmapImporter - { - private IpcChannel channel; - private BeatmapDatabase beatmaps; - - public BeatmapImporter(GameHost host, BeatmapDatabase beatmaps = null) - { - this.beatmaps = beatmaps; - - channel = new IpcChannel(host); - channel.MessageReceived += messageReceived; - } - - public async Task ImportAsync(string path) - { - if (beatmaps != null) - beatmaps.Import(path); - else - { - await channel.SendMessageAsync(new BeatmapImportMessage { Path = path }); - } - } - - private void messageReceived(BeatmapImportMessage msg) - { - Debug.Assert(beatmaps != null); - - ImportAsync(msg.Path); - } - } - - public class BeatmapImportMessage - { - public string Path; - } -} diff --git a/osu.Game/IPC/ScoreIPCChannel.cs b/osu.Game/IPC/ScoreIPCChannel.cs new file mode 100644 index 0000000000..289f5454e6 --- /dev/null +++ b/osu.Game/IPC/ScoreIPCChannel.cs @@ -0,0 +1,43 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Diagnostics; +using System.Threading.Tasks; +using osu.Framework.Platform; +using osu.Game.Database; + +namespace osu.Game.IPC +{ + public class ScoreIPCChannel : IpcChannel + { + private ScoreDatabase scores; + + public ScoreIPCChannel(IIpcHost host, ScoreDatabase scores = null) + : base(host) + { + this.scores = scores; + MessageReceived += (msg) => + { + Debug.Assert(scores != null); + ImportAsync(msg.Path); + }; + } + + public async Task ImportAsync(string path) + { + if (scores == null) + { + //we want to contact a remote osu! to handle the import. + await SendMessageAsync(new ScoreImportMessage { Path = path }); + return; + } + + scores.ReadReplayFile(path); + } + } + + public class ScoreImportMessage + { + public string Path; + } +} diff --git a/osu.Game/Input/Handlers/ReplayInputHandler.cs b/osu.Game/Input/Handlers/ReplayInputHandler.cs new file mode 100644 index 0000000000..76e038048c --- /dev/null +++ b/osu.Game/Input/Handlers/ReplayInputHandler.cs @@ -0,0 +1,33 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Input.Handlers; +using osu.Framework.Platform; +using OpenTK; + +namespace osu.Game.Input.Handlers +{ + public abstract class ReplayInputHandler : InputHandler + { + /// + /// A function provided to convert replay coordinates from gamefield to screen space. + /// + public Func ToScreenSpace { protected get; set; } + + /// + /// Update the current frame based on an incoming time value. + /// There are cases where we return a "must-use" time value that is different from the input. + /// This is to ensure accurate playback of replay data. + /// + /// The time which we should use for finding the current frame. + /// The usable time value. If null, we should not advance time as we do not have enough data. + public abstract double? SetFrameFromTime(double time); + + public override bool Initialize(GameHost host) => true; + + public override bool IsActive => true; + + public override int Priority => 0; + } +} \ No newline at end of file diff --git a/osu.Game/Modes/LegacyReplay.cs b/osu.Game/Modes/LegacyReplay.cs new file mode 100644 index 0000000000..0c9fb6ffca --- /dev/null +++ b/osu.Game/Modes/LegacyReplay.cs @@ -0,0 +1,274 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using System.IO; +using osu.Framework.Input; +using osu.Framework.MathUtils; +using osu.Game.Input.Handlers; +using osu.Game.IO.Legacy; +using OpenTK; +using OpenTK.Input; +using KeyboardState = osu.Framework.Input.KeyboardState; +using MouseState = osu.Framework.Input.MouseState; + +namespace osu.Game.Modes +{ + public class LegacyReplay : Replay + { + protected List Frames = new List(); + + protected LegacyReplay() + { + + } + + public LegacyReplay(StreamReader reader) + { + float lastTime = 0; + + foreach (var l in reader.ReadToEnd().Split(',')) + { + var split = l.Split('|'); + + if (split.Length < 4 || float.Parse(split[0]) < 0) continue; + + lastTime += float.Parse(split[0]); + + Frames.Add(new LegacyReplayFrame( + lastTime, + float.Parse(split[1]), + 384 - float.Parse(split[2]), + (LegacyButtonState)int.Parse(split[3]) + )); + } + } + + public override ReplayInputHandler GetInputHandler() => new LegacyReplayInputHandler(Frames); + + /// + /// The ReplayHandler will take a replay and handle the propagation of updates to the input stack. + /// It handles logic of any frames which *must* be executed. + /// + public class LegacyReplayInputHandler : ReplayInputHandler + { + private readonly List replayContent; + + public LegacyReplayFrame CurrentFrame => !hasFrames ? null : replayContent[currentFrameIndex]; + public LegacyReplayFrame NextFrame => !hasFrames ? null : replayContent[nextFrameIndex]; + + int currentFrameIndex; + + private int nextFrameIndex => MathHelper.Clamp(currentFrameIndex + (currentDirection > 0 ? 1 : -1), 0, replayContent.Count - 1); + + public LegacyReplayInputHandler(List replayContent) + { + this.replayContent = replayContent; + } + + private bool advanceFrame() + { + int newFrame = nextFrameIndex; + + //ensure we aren't at an extent. + if (newFrame == currentFrameIndex) return false; + + currentFrameIndex = newFrame; + return true; + } + + public void SetPosition(Vector2 pos) + { + } + + private Vector2? position + { + get + { + if (!hasFrames) + return null; + + return Interpolation.ValueAt(currentTime, CurrentFrame.Position, NextFrame.Position, CurrentFrame.Time, NextFrame.Time); + } + } + + public override List GetPendingStates() + { + return new List + { + new InputState + { + Mouse = new ReplayMouseState( + ToScreenSpace(position ?? Vector2.Zero), + new List + { + new MouseState.ButtonState(MouseButton.Left) + { + State = CurrentFrame?.MouseLeft ?? false + }, + new MouseState.ButtonState(MouseButton.Right) + { + State = CurrentFrame?.MouseRight ?? false + }, + } + ), + Keyboard = new ReplayKeyboardState(new List()) + } + }; + } + + public bool AtLastFrame => currentFrameIndex == replayContent.Count - 1; + public bool AtFirstFrame => currentFrameIndex == 0; + + public Vector2 Size => new Vector2(512, 384); + + private const double sixty_frame_time = 1000.0 / 60; + + double currentTime; + int currentDirection; + + /// + /// When set, we will ensure frames executed by nested drawables are frame-accurate to replay data. + /// Disabling this can make replay playback smoother (useful for autoplay, currently). + /// + public bool FrameAccuratePlayback = true; + + private bool hasFrames => replayContent.Count > 0; + + bool inImportantSection => + FrameAccuratePlayback && + //a button is in a pressed state + (currentDirection > 0 ? CurrentFrame : NextFrame)?.ButtonState > LegacyButtonState.None && + //the next frame is within an allowable time span + Math.Abs(currentTime - NextFrame?.Time ?? 0) <= sixty_frame_time * 1.2; + + /// + /// Update the current frame based on an incoming time value. + /// There are cases where we return a "must-use" time value that is different from the input. + /// This is to ensure accurate playback of replay data. + /// + /// The time which we should use for finding the current frame. + /// The usable time value. If null, we should not advance time as we do not have enough data. + public override double? SetFrameFromTime(double time) + { + currentDirection = time.CompareTo(currentTime); + if (currentDirection == 0) currentDirection = 1; + + if (hasFrames) + { + //if we changed frames, we want to execute once *exactly* on the frame's time. + if (currentDirection == time.CompareTo(NextFrame.Time) && advanceFrame()) + return currentTime = CurrentFrame.Time; + + //if we didn't change frames, we need to ensure we are allowed to run frames in between, else return null. + if (inImportantSection) + return null; + } + + return currentTime = time; + } + + private class ReplayMouseState : MouseState + { + public ReplayMouseState(Vector2 position, List list) + { + Position = position; + ButtonStates = list; + } + } + + private class ReplayKeyboardState : KeyboardState + { + public ReplayKeyboardState(List keys) + { + Keys = keys; + } + } + } + + [Flags] + public enum LegacyButtonState + { + None = 0, + Left1 = 1, + Right1 = 2, + Left2 = 4, + Right2 = 8, + Smoke = 16 + } + + public class LegacyReplayFrame + { + public Vector2 Position => new Vector2(MouseX, MouseY); + + public float MouseX; + public float MouseY; + public bool MouseLeft; + public bool MouseRight; + public bool MouseLeft1; + public bool MouseRight1; + public bool MouseLeft2; + public bool MouseRight2; + public LegacyButtonState ButtonState; + public double Time; + + public LegacyReplayFrame(double time, float posX, float posY, LegacyButtonState buttonState) + { + MouseX = posX; + MouseY = posY; + ButtonState = buttonState; + SetButtonStates(buttonState); + Time = time; + } + + public void SetButtonStates(LegacyButtonState buttonState) + { + ButtonState = buttonState; + MouseLeft = (buttonState & (LegacyButtonState.Left1 | LegacyButtonState.Left2)) > 0; + MouseLeft1 = (buttonState & LegacyButtonState.Left1) > 0; + MouseLeft2 = (buttonState & LegacyButtonState.Left2) > 0; + MouseRight = (buttonState & (LegacyButtonState.Right1 | LegacyButtonState.Right2)) > 0; + MouseRight1 = (buttonState & LegacyButtonState.Right1) > 0; + MouseRight2 = (buttonState & LegacyButtonState.Right2) > 0; + } + + public LegacyReplayFrame(Stream s) : this(new SerializationReader(s)) + { + } + + public LegacyReplayFrame(SerializationReader sr) + { + ButtonState = (LegacyButtonState)sr.ReadByte(); + SetButtonStates(ButtonState); + + byte bt = sr.ReadByte(); + if (bt > 0)//Handle Pre-Taiko compatible replays. + SetButtonStates(LegacyButtonState.Right1); + + MouseX = sr.ReadSingle(); + MouseY = sr.ReadSingle(); + Time = sr.ReadInt32(); + } + + public void ReadFromStream(SerializationReader sr) + { + throw new System.NotImplementedException(); + } + + public void WriteToStream(SerializationWriter sw) + { + sw.Write((byte)ButtonState); + sw.Write((byte)0); + sw.Write(MouseX); + sw.Write(MouseY); + sw.Write(Time); + } + + public override string ToString() + { + return $"{Time}\t({MouseX},{MouseY})\t{MouseLeft}\t{MouseRight}\t{MouseLeft1}\t{MouseRight1}\t{MouseLeft2}\t{MouseRight2}\t{ButtonState}"; + } + } + } +} diff --git a/osu.Game/Modes/Mod.cs b/osu.Game/Modes/Mod.cs index 916d84cf2f..16c39ce044 100644 --- a/osu.Game/Modes/Mod.cs +++ b/osu.Game/Modes/Mod.cs @@ -2,8 +2,8 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using System.ComponentModel; using osu.Game.Graphics; +using osu.Game.Screens.Play; namespace osu.Game.Modes { @@ -41,6 +41,12 @@ namespace osu.Game.Modes /// The mods this mod cannot be enabled with. /// public virtual Type[] IncompatibleMods => new Type[] { }; + + /// + /// Direct access to the Player before load has run. + /// + /// + public virtual void PlayerLoading(Player player) { } } public class MultiMod : Mod @@ -69,6 +75,7 @@ namespace osu.Game.Modes public override string Description => "Reduces overall difficulty - larger circles, more forgiving HP drain, less accuracy required."; public override double ScoreMultiplier => 0.5; public override bool Ranked => true; + public override Type[] IncompatibleMods => new[] { typeof(ModHardRock) }; } public abstract class ModHidden : Mod @@ -144,6 +151,12 @@ namespace osu.Game.Modes public override string Description => "Watch a perfect automated play through the song"; public override double ScoreMultiplier => 0; public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModPerfect) }; + + public override void PlayerLoading(Player player) + { + base.PlayerLoading(player); + player.ReplayInputHandler = Ruleset.GetRuleset(player.Beatmap.PlayMode).CreateAutoplayScore(player.Beatmap.Beatmap)?.Replay?.GetInputHandler(); + } } public abstract class ModPerfect : ModSuddenDeath diff --git a/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs index ca1967904a..ac18ae5a04 100644 --- a/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs @@ -16,22 +16,15 @@ namespace osu.Game.Modes.Objects.Drawables { public abstract class DrawableHitObject : Container, IStateful { - public event Action OnJudgement; - public override bool HandleInput => Interactive; public bool Interactive = true; public JudgementInfo Judgement; - public abstract JudgementInfo CreateJudgementInfo(); + protected abstract JudgementInfo CreateJudgementInfo(); - public HitObject HitObject; - - public DrawableHitObject(HitObject hitObject) - { - HitObject = hitObject; - } + protected abstract void UpdateState(ArmedState state); private ArmedState state; public ArmedState State @@ -52,20 +45,11 @@ namespace osu.Game.Modes.Objects.Drawables } } - SampleChannel sample; - - [BackgroundDependencyLoader] - private void load(AudioManager audio) - { - string hitType = ((HitObject.Sample?.Type ?? SampleType.None) == SampleType.None ? SampleType.Normal : HitObject.Sample.Type).ToString().ToLower(); - string sampleSet = (HitObject.Sample?.Set ?? SampleSet.Normal).ToString().ToLower(); - - sample = audio.Sample.Get($@"Gameplay/{sampleSet}-hit{hitType}"); - } + protected SampleChannel Sample; protected virtual void PlaySample() { - sample?.Play(); + Sample?.Play(); } protected override void LoadComplete() @@ -81,24 +65,23 @@ namespace osu.Game.Modes.Objects.Drawables Expire(true); } + } - private List nestedHitObjects; + public abstract class DrawableHitObject : DrawableHitObject + where HitObjectType : HitObject + { + public event Action, JudgementInfo> OnJudgement; - protected IEnumerable NestedHitObjects => nestedHitObjects; + public HitObjectType HitObject; - protected void AddNested(DrawableHitObject h) + public DrawableHitObject(HitObjectType hitObject) { - if (nestedHitObjects == null) - nestedHitObjects = new List(); - - h.OnJudgement += (d, j) => { OnJudgement?.Invoke(d, j); } ; - nestedHitObjects.Add(h); + HitObject = hitObject; } /// /// Process a hit of this hitobject. Carries out judgement. /// - /// Preliminary judgement information provided by the hit source. /// Whether a hit was processed. protected bool UpdateJudgement(bool userTriggered) { @@ -143,7 +126,27 @@ namespace osu.Game.Modes.Objects.Drawables UpdateJudgement(false); } - protected abstract void UpdateState(ArmedState state); + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + string hitType = ((HitObject.Sample?.Type ?? SampleType.None) == SampleType.None ? SampleType.Normal : HitObject.Sample.Type).ToString().ToLower(); + string sampleSet = (HitObject.Sample?.Set ?? SampleSet.Normal).ToString().ToLower(); + + Sample = audio.Sample.Get($@"Gameplay/{sampleSet}-hit{hitType}"); + } + + private List> nestedHitObjects; + + protected IEnumerable> NestedHitObjects => nestedHitObjects; + + protected void AddNested(DrawableHitObject h) + { + if (nestedHitObjects == null) + nestedHitObjects = new List>(); + + h.OnJudgement += (d, j) => { OnJudgement?.Invoke(d, j); } ; + nestedHitObjects.Add(h); + } } public enum ArmedState diff --git a/osu.Game/Modes/Replay.cs b/osu.Game/Modes/Replay.cs new file mode 100644 index 0000000000..0a41a12335 --- /dev/null +++ b/osu.Game/Modes/Replay.cs @@ -0,0 +1,12 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Input.Handlers; + +namespace osu.Game.Modes +{ + public abstract class Replay + { + public virtual ReplayInputHandler GetInputHandler() => null; + } +} \ No newline at end of file diff --git a/osu.Game/Modes/Ruleset.cs b/osu.Game/Modes/Ruleset.cs index 8fb35dc1d4..e145e60b97 100644 --- a/osu.Game/Modes/Ruleset.cs +++ b/osu.Game/Modes/Ruleset.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Concurrent; using osu.Game.Beatmaps; using osu.Game.Graphics; +using osu.Game.Screens.Play; namespace osu.Game.Modes { @@ -28,9 +29,9 @@ namespace osu.Game.Modes public abstract IEnumerable GetModsFor(ModType type); - public abstract ScoreProcessor CreateScoreProcessor(int hitObjectCount); + public abstract ScoreProcessor CreateScoreProcessor(int hitObjectCount = 0); - public abstract HitRenderer CreateHitRendererWith(Beatmap beatmap); + public abstract HitRenderer CreateHitRendererWith(Beatmap beatmap, PlayerInputManager input = null); public abstract HitObjectParser CreateHitObjectParser(); @@ -42,6 +43,8 @@ namespace osu.Game.Modes public virtual FontAwesome Icon => FontAwesome.fa_question_circle; + public virtual Score CreateAutoplayScore(Beatmap beatmap) => null; + public static Ruleset GetRuleset(PlayMode mode) { Type type; @@ -51,5 +54,6 @@ namespace osu.Game.Modes return Activator.CreateInstance(type) as Ruleset; } + } } diff --git a/osu.Game/Modes/Score.cs b/osu.Game/Modes/Score.cs index 8da09cb974..1a761bea5d 100644 --- a/osu.Game/Modes/Score.cs +++ b/osu.Game/Modes/Score.cs @@ -1,6 +1,8 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Database; + namespace osu.Game.Modes { public class Score @@ -10,5 +12,8 @@ namespace osu.Game.Modes public double Combo { get; set; } public double MaxCombo { get; set; } public double Health { get; set; } + + public Replay Replay; + public BeatmapInfo Beatmap; } } diff --git a/osu.Game/Modes/ScoreProcesssor.cs b/osu.Game/Modes/ScoreProcesssor.cs index eb4514f40d..0433df66a9 100644 --- a/osu.Game/Modes/ScoreProcesssor.cs +++ b/osu.Game/Modes/ScoreProcesssor.cs @@ -10,7 +10,7 @@ namespace osu.Game.Modes { public abstract class ScoreProcessor { - public virtual Score GetScore() => new Score() + public virtual Score GetScore() => new Score { TotalScore = TotalScore, Combo = Combo, @@ -51,7 +51,7 @@ namespace osu.Game.Modes /// Initializes a new instance of the class. /// /// Number of HitObjects. It is used for specifying Judgements collection Capacity - public ScoreProcessor(int hitObjectCount = 0) + protected ScoreProcessor(int hitObjectCount = 0) { Combo.ValueChanged += delegate { HighestCombo.Value = Math.Max(HighestCombo.Value, Combo.Value); }; Judgements = new List(hitObjectCount); diff --git a/osu.Game/Modes/UI/ComboCounter.cs b/osu.Game/Modes/UI/ComboCounter.cs index f669efa673..ddff98db7c 100644 --- a/osu.Game/Modes/UI/ComboCounter.cs +++ b/osu.Game/Modes/UI/ComboCounter.cs @@ -260,7 +260,7 @@ namespace osu.Game.Modes.UI public override void Apply(Drawable d) { base.Apply(d); - (d as ComboCounter).DisplayedCount = CurrentValue; + ((ComboCounter)d).DisplayedCount = CurrentValue; } } diff --git a/osu.Game/Modes/UI/ComboResultCounter.cs b/osu.Game/Modes/UI/ComboResultCounter.cs index dc624b3660..03c8b5611f 100644 --- a/osu.Game/Modes/UI/ComboResultCounter.cs +++ b/osu.Game/Modes/UI/ComboResultCounter.cs @@ -51,7 +51,7 @@ namespace osu.Game.Modes.UI public override void Apply(Drawable d) { base.Apply(d); - (d as ComboResultCounter).DisplayedCount = CurrentValue; + ((ComboResultCounter)d).DisplayedCount = CurrentValue; } } } diff --git a/osu.Game/Modes/UI/HealthDisplay.cs b/osu.Game/Modes/UI/HealthDisplay.cs index e5a3ca74ba..ddd4c1db42 100644 --- a/osu.Game/Modes/UI/HealthDisplay.cs +++ b/osu.Game/Modes/UI/HealthDisplay.cs @@ -17,10 +17,9 @@ namespace osu.Game.Modes.UI { public class HealthDisplay : Container { - private Box background; private Container fill; - public BindableDouble Current = new BindableDouble() + public BindableDouble Current = new BindableDouble { MinValue = 0, MaxValue = 1 @@ -30,7 +29,7 @@ namespace osu.Game.Modes.UI { Children = new Drawable[] { - background = new Box + new Box { RelativeSizeAxes = Axes.Both, Colour = Color4.Black, diff --git a/osu.Game/Modes/UI/HitRenderer.cs b/osu.Game/Modes/UI/HitRenderer.cs index 14d9599be6..cf5c2460d2 100644 --- a/osu.Game/Modes/UI/HitRenderer.cs +++ b/osu.Game/Modes/UI/HitRenderer.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Modes.Objects; using osu.Game.Modes.Objects.Drawables; using osu.Game.Beatmaps; +using osu.Game.Screens.Play; namespace osu.Game.Modes.UI { @@ -19,24 +20,28 @@ namespace osu.Game.Modes.UI public event Action OnAllJudged; + public abstract bool AllObjectsJudged { get; } + protected void TriggerOnJudgement(JudgementInfo j) { OnJudgement?.Invoke(j); if (AllObjectsJudged) OnAllJudged?.Invoke(); } - - protected Playfield Playfield; - - public bool AllObjectsJudged => Playfield.HitObjects.Children.First()?.Judgement.Result != null; //reverse depth sort means First() instead of Last(). - - public IEnumerable DrawableObjects => Playfield.HitObjects.Children; } - public abstract class HitRenderer : HitRenderer - where T : HitObject + public abstract class HitRenderer : HitRenderer + where TObject : HitObject { - private List objects; + private List objects; + + public PlayerInputManager InputManager; + + protected Playfield Playfield; + + public override bool AllObjectsJudged => Playfield.HitObjects.Children.First()?.Judgement.Result != null; //reverse depth sort means First() instead of Last(). + + public IEnumerable DrawableObjects => Playfield.HitObjects.Children; public Beatmap Beatmap { @@ -48,13 +53,13 @@ namespace osu.Game.Modes.UI } } - protected abstract Playfield CreatePlayfield(); + protected abstract Playfield CreatePlayfield(); - protected abstract HitObjectConverter Converter { get; } + protected abstract HitObjectConverter Converter { get; } - protected virtual List Convert(Beatmap beatmap) => Converter.Convert(beatmap); + protected virtual List Convert(Beatmap beatmap) => Converter.Convert(beatmap); - public HitRenderer() + protected HitRenderer() { RelativeSizeAxes = Axes.Both; } @@ -62,10 +67,10 @@ namespace osu.Game.Modes.UI [BackgroundDependencyLoader] private void load() { - Children = new Drawable[] - { - Playfield = CreatePlayfield() - }; + Playfield = CreatePlayfield(); + Playfield.InputManager = InputManager; + + Add(Playfield); loadObjects(); } @@ -73,9 +78,9 @@ namespace osu.Game.Modes.UI private void loadObjects() { if (objects == null) return; - foreach (T h in objects) + foreach (TObject h in objects) { - var drawableObject = GetVisualRepresentation(h); + DrawableHitObject drawableObject = GetVisualRepresentation(h); if (drawableObject == null) continue; @@ -86,8 +91,8 @@ namespace osu.Game.Modes.UI Playfield.PostProcess(); } - private void onJudgement(DrawableHitObject o, JudgementInfo j) => TriggerOnJudgement(j); + private void onJudgement(DrawableHitObject o, JudgementInfo j) => TriggerOnJudgement(j); - protected abstract DrawableHitObject GetVisualRepresentation(T h); + protected abstract DrawableHitObject GetVisualRepresentation(TObject h); } } diff --git a/osu.Game/Modes/UI/Playfield.cs b/osu.Game/Modes/UI/Playfield.cs index 91eddce73c..502e072603 100644 --- a/osu.Game/Modes/UI/Playfield.cs +++ b/osu.Game/Modes/UI/Playfield.cs @@ -1,37 +1,72 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Allocation; using OpenTK; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Modes.Objects; using osu.Game.Modes.Objects.Drawables; +using osu.Game.Screens.Play; namespace osu.Game.Modes.UI { - public abstract class Playfield : Container + public abstract class Playfield : Container + where T : HitObject { - public HitObjectContainer HitObjects; - private Container content; + public HitObjectContainer> HitObjects; - public virtual void Add(DrawableHitObject h) => HitObjects.Add(h); + public virtual void Add(DrawableHitObject h) => HitObjects.Add(h); + + public class HitObjectContainer : Container + where U : Drawable + { + public override bool Contains(Vector2 screenSpacePos) => true; + } + + private Container scaledContent; public override bool Contains(Vector2 screenSpacePos) => true; - protected override Container Content => content; + protected override Container Content { get; } public Playfield() { - AddInternal(content = new ScaledContainer() + AddInternal(scaledContent = new ScaledContainer { RelativeSizeAxes = Axes.Both, + Children = new[] + { + Content = new Container + { + RelativeSizeAxes = Axes.Both, + } + } }); - Add(HitObjects = new HitObjectContainer + Add(HitObjects = new HitObjectContainer> { RelativeSizeAxes = Axes.Both, }); } + /// + /// An optional inputManager to provide interactivity etc. + /// + public PlayerInputManager InputManager; + + [BackgroundDependencyLoader] + private void load() + { + if (InputManager != null) + { + //if we've been provided an InputManager, we want it to sit inside the scaledcontainer + scaledContent.Remove(Content); + scaledContent.Add(InputManager); + InputManager.Add(Content); + } + } + public virtual void PostProcess() { } diff --git a/osu.Game/Modes/UI/ScoreOverlay.cs b/osu.Game/Modes/UI/ScoreOverlay.cs index d9283c52cb..66c361fcda 100644 --- a/osu.Game/Modes/UI/ScoreOverlay.cs +++ b/osu.Game/Modes/UI/ScoreOverlay.cs @@ -50,7 +50,7 @@ namespace osu.Game.Modes.UI AccuracyCounter?.Set(AccuracyCounter.Count - 0.01f); } - public ScoreOverlay() + protected ScoreOverlay() { RelativeSizeAxes = Axes.Both; diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index a9e0b2163b..799b761a8e 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -20,10 +20,10 @@ namespace osu.Game.Online.API private OAuth authentication; public string Endpoint = @"https://new.ppy.sh"; - const string client_id = @"5"; - const string client_secret = @"FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk"; + private const string client_id = @"5"; + private const string client_secret = @"FGc9GAtyHzeQDshWP5Ah7dega8hJACAJpQtw6OXk"; - ConcurrentQueue queue = new ConcurrentQueue(); + private ConcurrentQueue queue = new ConcurrentQueue(); public Scheduler Scheduler = new Scheduler(); @@ -38,22 +38,15 @@ namespace osu.Game.Online.API public string Token { get { return authentication.Token?.ToString(); } - - set - { - - if (string.IsNullOrEmpty(value)) - authentication.Token = null; - else - authentication.Token = OAuthToken.Parse(value); - } + set { authentication.Token = string.IsNullOrEmpty(value) ? null : OAuthToken.Parse(value); } } 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; - Logger log; + private Logger log; public APIAccess() { @@ -88,22 +81,22 @@ namespace osu.Game.Online.API /// /// Number of consecutive requests which failed due to network issues. /// - int failureCount = 0; + private int failureCount; private void run() { - while (true) + while (thread.IsAlive) { switch (State) { case APIState.Failing: //todo: replace this with a ping request. - log.Add($@"In a failing state, waiting a bit before we try again..."); + log.Add(@"In a failing state, waiting a bit before we try again..."); Thread.Sleep(5000); if (queue.Count == 0) { - log.Add($@"Queueing a ping request"); - Queue(new ListChannelsRequest() { Timeout = 5000 }); + log.Add(@"Queueing a ping request"); + Queue(new ListChannelsRequest { Timeout = 5000 }); } break; case APIState.Offline: @@ -131,7 +124,7 @@ namespace osu.Game.Online.API var userReq = new GetUserRequest(); - userReq.Success += (u) => { + userReq.Success += u => { LocalUser.Value = u; //we're connected! State = APIState.Online; @@ -291,7 +284,7 @@ namespace osu.Game.Online.API if (failOldRequests) { APIRequest req; - while (queue.TryDequeue(out req)) + while (oldQueue.TryDequeue(out req)) req.Fail(new Exception(@"Disconnected from server")); } } diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index 7b5f488c17..3654dac5d5 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -22,7 +22,7 @@ namespace osu.Game.Online.API private void onSuccess() { - Success?.Invoke((WebRequest as JsonWebRequest).ResponseObject); + Success?.Invoke(((JsonWebRequest)WebRequest).ResponseObject); } public new event APISuccessHandler Success; diff --git a/osu.Game/Online/API/OAuthToken.cs b/osu.Game/Online/API/OAuthToken.cs index c33701715e..4085e5602a 100644 --- a/osu.Game/Online/API/OAuthToken.cs +++ b/osu.Game/Online/API/OAuthToken.cs @@ -48,7 +48,7 @@ namespace osu.Game.Online.API try { string[] parts = value.Split('/'); - return new OAuthToken() + return new OAuthToken { AccessToken = parts[0], AccessTokenExpiry = long.Parse(parts[1], NumberFormatInfo.InvariantInfo), diff --git a/osu.Game/Online/API/Requests/GetMessagesRequest.cs b/osu.Game/Online/API/Requests/GetMessagesRequest.cs index ab309b2bfa..af5a96a66a 100644 --- a/osu.Game/Online/API/Requests/GetMessagesRequest.cs +++ b/osu.Game/Online/API/Requests/GetMessagesRequest.cs @@ -9,8 +9,8 @@ namespace osu.Game.Online.API.Requests { public class GetMessagesRequest : APIRequest> { - List channels; - long? since; + private List channels; + private long? since; public GetMessagesRequest(List channels, long? sinceId) { diff --git a/osu.Game/Online/API/SecurePassword.cs b/osu.Game/Online/API/SecurePassword.cs deleted file mode 100644 index e3e46ba6dc..0000000000 --- a/osu.Game/Online/API/SecurePassword.cs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Diagnostics; -using System.Security; -using osu.Framework.Extensions; - -namespace osu.Game.Online.API -{ - internal class SecurePassword - { - private readonly SecureString storage = new SecureString(); - private readonly Representation representation; - - //todo: move this to a central constants file. - private const string password_entropy = @"cu24180ncjeiu0ci1nwui"; - - public SecurePassword(string input, bool encrypted = false) - { - //if (encrypted) - //{ - // string rep; - // input = DPAPI.Decrypt(input, password_entropy, out rep); - // Enum.TryParse(rep, out representation); - //} - //else - { - representation = Representation.Raw; - } - - foreach (char c in input) - storage.AppendChar(c); - storage.MakeReadOnly(); - } - - internal string Get(Representation request = Representation.Raw) - { - Debug.Assert(representation == request); - - switch (request) - { - default: - return storage.UnsecureRepresentation(); - //case Representation.Encrypted: - // return DPAPI.Encrypt(DPAPI.KeyType.UserKey, storage.UnsecureRepresentation(), password_entropy, representation.ToString()); - } - } - } - - enum Representation - { - Raw, - Encrypted - } -} diff --git a/osu.Game/Online/Chat/Drawables/ChatLine.cs b/osu.Game/Online/Chat/Drawables/ChatLine.cs index c55dcc6620..9f78be92d1 100644 --- a/osu.Game/Online/Chat/Drawables/ChatLine.cs +++ b/osu.Game/Online/Chat/Drawables/ChatLine.cs @@ -59,8 +59,8 @@ namespace osu.Game.Online.Chat.Drawables return username_colours[message.UserId % username_colours.Length]; } - const float padding = 200; - const float text_size = 20; + private const float padding = 200; + private const float text_size = 20; public ChatLine(Message message) { diff --git a/osu.Game/Online/Chat/Drawables/DrawableChannel.cs b/osu.Game/Online/Chat/Drawables/DrawableChannel.cs index f5fac3d4c5..eb8653976a 100644 --- a/osu.Game/Online/Chat/Drawables/DrawableChannel.cs +++ b/osu.Game/Online/Chat/Drawables/DrawableChannel.cs @@ -7,7 +7,6 @@ using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; -using osu.Framework.Threading; using osu.Game.Graphics.Sprites; namespace osu.Game.Online.Chat.Drawables diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 9f7e3e04dc..79ffcfcadf 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -13,7 +13,6 @@ using osu.Game.Input; using OpenTK.Input; using osu.Framework.Logging; using osu.Game.Graphics.UserInterface.Volume; -using osu.Game.Database; using osu.Framework.Allocation; using osu.Framework.Graphics.Transforms; using osu.Framework.Timing; @@ -24,16 +23,16 @@ using osu.Game.Screens.Menu; using OpenTK; using System.Linq; using osu.Framework.Graphics.Primitives; -using System.Collections.Generic; using System.Threading.Tasks; +using osu.Framework.Threading; +using osu.Game.Graphics; using osu.Game.Overlays.Notifications; +using osu.Game.Screens.Play; namespace osu.Game { public class OsuGame : OsuGameBase { - public virtual bool IsDeployedBuild => false; - public Toolbar Toolbar; private ChatOverlay chat; @@ -61,7 +60,7 @@ namespace osu.Game public Bindable PlayMode; - string[] args; + private string[] args; private OptionsOverlay options; @@ -84,7 +83,7 @@ namespace osu.Game if (args?.Length > 0) { var paths = args.Where(a => !a.StartsWith(@"-")); - ImportBeatmapsAsync(paths); + Task.Run(() => BeatmapDatabase.Import(paths)); } Dependencies.Cache(this); @@ -92,9 +91,41 @@ namespace osu.Game PlayMode = LocalConfig.GetBindable(OsuConfig.PlayMode); } - protected async void ImportBeatmapsAsync(IEnumerable paths) + private ScheduledDelegate scoreLoad; + + protected void LoadScore(Score s) { - await Task.Run(() => BeatmapDatabase.Import(paths)); + scoreLoad?.Cancel(); + + var menu = intro.ChildScreen; + + if (menu == null) + { + scoreLoad = Schedule(() => LoadScore(s)); + return; + } + + if (!menu.IsCurrentScreen) + { + menu.MakeCurrent(); + Delay(500); + scoreLoad = Schedule(() => LoadScore(s)); + return; + } + + if (s.Beatmap == null) + { + notificationManager.Post(new SimpleNotification + { + Text = @"Tried to load a score for a beatmap we don't have!", + Icon = FontAwesome.fa_life_saver, + }); + return; + } + + Beatmap.Value = BeatmapDatabase.GetWorkingBeatmap(s.Beatmap); + + menu.Push(new PlayerLoader(new Player { ReplayInputHandler = s.Replay.GetInputHandler() })); } protected override void LoadComplete() @@ -129,7 +160,7 @@ namespace osu.Game //overlay elements (chat = new ChatOverlay { Depth = 0 }).LoadAsync(this, overlayContent.Add); (options = new OptionsOverlay { Depth = -1 }).LoadAsync(this, overlayContent.Add); - (musicController = new MusicController() + (musicController = new MusicController { Depth = -2, Position = new Vector2(0, Toolbar.HEIGHT), @@ -194,7 +225,7 @@ namespace osu.Game private bool globalHotkeyPressed(InputState state, KeyDownEventArgs args) { - if (args.Repeat) return false; + if (args.Repeat || intro == null) return false; switch (args.Key) { @@ -203,9 +234,11 @@ namespace osu.Game return true; case Key.PageUp: case Key.PageDown: - var rate = ((Clock as ThrottledFrameClock).Source as StopwatchClock).Rate * (args.Key == Key.PageUp ? 1.1f : 0.9f); - ((Clock as ThrottledFrameClock).Source as StopwatchClock).Rate = rate; - Logger.Log($@"Adjusting game clock to {rate}", LoggingTarget.Debug); + var swClock = (Clock as ThrottledFrameClock)?.Source as StopwatchClock; + if (swClock == null) return false; + + swClock.Rate *= args.Key == Key.PageUp ? 1.1f : 0.9f; + Logger.Log($@"Adjusting game clock to {swClock.Rate}", LoggingTarget.Debug); return true; } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 1c34743567..db792d4a7e 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -1,6 +1,9 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; +using System.Diagnostics; +using System.Reflection; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; @@ -25,6 +28,8 @@ namespace osu.Game protected BeatmapDatabase BeatmapDatabase; + protected ScoreDatabase ScoreDatabase; + protected override string MainResourceFile => @"osu.Game.Resources.dll"; public APIAccess API; @@ -33,16 +38,50 @@ namespace osu.Game private RatioAdjust ratioContainer; - public CursorContainer Cursor; + protected CursorContainer Cursor; public readonly Bindable Beatmap = new Bindable(); + protected AssemblyName AssemblyName => Assembly.GetEntryAssembly()?.GetName() ?? new AssemblyName { Version = new Version() }; + + public bool IsDeployedBuild => AssemblyName.Version.Major > 0; + + public bool IsDebug + { + get + { + bool isDebug = false; + // Debug.Assert conditions are only evaluated in debug mode + Debug.Assert(isDebug = true); + // ReSharper disable once ConditionIsAlwaysTrueOrFalse + return isDebug; + } + } + + public string Version + { + get + { + if (!IsDeployedBuild) + return @"local " + (IsDebug ? @"debug" : @"release"); + + var assembly = AssemblyName; + return $@"{assembly.Version.Major}.{assembly.Version.Minor}.{assembly.Version.Build}"; + } + } + + public OsuGameBase() + { + Name = @"osu!lazer"; + } + [BackgroundDependencyLoader] private void load() { Dependencies.Cache(this); Dependencies.Cache(LocalConfig); Dependencies.Cache(BeatmapDatabase = new BeatmapDatabase(Host.Storage, Host)); + Dependencies.Cache(ScoreDatabase = new ScoreDatabase(Host.Storage, Host, BeatmapDatabase)); Dependencies.Cache(new OsuColour()); //this completely overrides the framework default. will need to change once we make a proper FontStore. diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index ab5a255897..efd366adb1 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -27,7 +27,7 @@ namespace osu.Game.Overlays { public class ChatOverlay : FocusedOverlayContainer, IOnlineComponent { - const float textbox_height = 40; + private const float textbox_height = 40; private ScheduledDelegate messageRequest; diff --git a/osu.Game/Overlays/DragBar.cs b/osu.Game/Overlays/DragBar.cs index 7315cf38d1..2e8eb272d8 100644 --- a/osu.Game/Overlays/DragBar.cs +++ b/osu.Game/Overlays/DragBar.cs @@ -34,7 +34,7 @@ namespace osu.Game.Overlays Children = new Drawable[] { - fill = new Box() + fill = new Box { Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, diff --git a/osu.Game/Overlays/LoginOverlay.cs b/osu.Game/Overlays/LoginOverlay.cs index e1b72f4ed9..fec1c5ec6e 100644 --- a/osu.Game/Overlays/LoginOverlay.cs +++ b/osu.Game/Overlays/LoginOverlay.cs @@ -13,11 +13,11 @@ using OpenTK.Graphics; namespace osu.Game.Overlays { - class LoginOverlay : FocusedOverlayContainer + internal class LoginOverlay : FocusedOverlayContainer { private LoginOptions optionsSection; - const float transition_time = 400; + private const float transition_time = 400; public LoginOverlay() { diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index cb0ef7c39c..3e353ac071 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -105,25 +105,19 @@ namespace osu.Game.Overlays.Mods if (mod == value) return; mod = value; - if (mod is MultiMod) - { - mods = ((MultiMod)mod).Mods; - } - else - { - mods = new Mod[] { mod }; - } + Mods = (mod as MultiMod)?.Mods ?? new[] { mod }; createIcons(); - if (mods.Length > 0) + if (Mods.Length > 0) { - displayMod(mods[0]); + displayMod(Mods[0]); } } } - private Mod[] mods; - public Mod[] Mods => mods; // the mods from Mod, only multiple if Mod is a MultiMod + public Mod[] Mods { get; private set; } + + // the mods from Mod, only multiple if Mod is a MultiMod public Mod SelectedMod => Mods.ElementAtOrDefault(selectedIndex); @@ -178,7 +172,7 @@ namespace osu.Game.Overlays.Mods iconsContainer.Clear(); if (Mods.Length > 1) { - iconsContainer.Add(new[] + iconsContainer.Add(new ModIcon[] { new ModIcon { diff --git a/osu.Game/Overlays/Mods/ModSection.cs b/osu.Game/Overlays/Mods/ModSection.cs index 4def201f25..cb9083af69 100644 --- a/osu.Game/Overlays/Mods/ModSection.cs +++ b/osu.Game/Overlays/Mods/ModSection.cs @@ -60,7 +60,7 @@ namespace osu.Game.Overlays.Mods } private Color4 colour = Color4.White; - new public Color4 Colour + public new Color4 Colour { get { diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 86d7650681..d70aec28b2 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -59,7 +59,7 @@ namespace osu.Game.Overlays protected override bool OnDrag(InputState state) { - Vector2 change = (state.Mouse.Position - state.Mouse.PositionMouseDown.Value); + Vector2 change = state.Mouse.Position - state.Mouse.PositionMouseDown.Value; // Diminish the drag distance as we go further to simulate "rubber band" feeling. change *= (float)Math.Pow(change.Length, 0.7f) / change.Length; @@ -246,14 +246,14 @@ namespace osu.Game.Overlays } } - void preferUnicode_changed(object sender, EventArgs e) + private void preferUnicode_changed(object sender, EventArgs e) { updateDisplay(current, TransformDirection.None); } private void workingChanged(object sender = null, EventArgs e = null) { - progress.IsEnabled = (beatmapSource.Value != null); + progress.IsEnabled = beatmapSource.Value != null; if (beatmapSource.Value == current) return; bool audioEquals = current?.BeatmapInfo?.AudioEquals(beatmapSource?.Value?.BeatmapInfo) ?? false; current = beatmapSource.Value; @@ -323,7 +323,7 @@ namespace osu.Game.Overlays updateDisplay(current, isNext ? TransformDirection.Next : TransformDirection.Prev); } - Action pendingBeatmapSwitch; + private Action pendingBeatmapSwitch; private void updateDisplay(WorkingBeatmap beatmap, TransformDirection direction) { @@ -384,7 +384,7 @@ namespace osu.Game.Overlays base.Dispose(isDisposing); } - const float transition_length = 800; + private const float transition_length = 800; protected override void PopIn() { diff --git a/osu.Game/Overlays/NotificationManager.cs b/osu.Game/Overlays/NotificationManager.cs index c979b01af4..8f455d44e7 100644 --- a/osu.Game/Overlays/NotificationManager.cs +++ b/osu.Game/Overlays/NotificationManager.cs @@ -69,7 +69,7 @@ namespace osu.Game.Overlays }; } - int runningDepth = 0; + private int runningDepth; public void Post(Notification notification) { diff --git a/osu.Game/Overlays/Notifications/Notification.cs b/osu.Game/Overlays/Notifications/Notification.cs index 9a51c6d6ae..536a6a0a55 100644 --- a/osu.Game/Overlays/Notifications/Notification.cs +++ b/osu.Game/Overlays/Notifications/Notification.cs @@ -43,16 +43,7 @@ namespace osu.Game.Overlays.Notifications protected Container NotificationContent; - private bool read; - - public virtual bool Read - { - get { return read; } - set - { - read = value; - } - } + public virtual bool Read { get; set; } public Notification() { @@ -162,7 +153,7 @@ namespace osu.Game.Overlays.Notifications Expire(); } - class CloseButton : ClickableContainer + private class CloseButton : ClickableContainer { private Color4 hoverColour; @@ -176,6 +167,7 @@ namespace osu.Game.Overlays.Notifications new TextAwesome { Anchor = Anchor.Centre, + Origin = Anchor.Centre, Icon = FontAwesome.fa_times_circle, } }; diff --git a/osu.Game/Overlays/Notifications/NotificationSection.cs b/osu.Game/Overlays/Notifications/NotificationSection.cs index 8366c83527..e5debc9c9b 100644 --- a/osu.Game/Overlays/Notifications/NotificationSection.cs +++ b/osu.Game/Overlays/Notifications/NotificationSection.cs @@ -133,7 +133,7 @@ namespace osu.Game.Overlays.Notifications countText.Text = notifications.Children.Count(c => c.Alpha > 0.99f).ToString(); } - class ClearAllButton : ClickableContainer + private class ClearAllButton : ClickableContainer { private OsuSpriteText text; diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index 6124884add..7d61a79af4 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -168,7 +168,7 @@ namespace osu.Game.Overlays.Notifications /// public Func CompletionClickAction; - class ProgressBar : Container + private class ProgressBar : Container { private Box box; diff --git a/osu.Game/Overlays/Notifications/SimpleNotification.cs b/osu.Game/Overlays/Notifications/SimpleNotification.cs index c9c56e8d4a..0a33e2f416 100644 --- a/osu.Game/Overlays/Notifications/SimpleNotification.cs +++ b/osu.Game/Overlays/Notifications/SimpleNotification.cs @@ -51,6 +51,7 @@ namespace osu.Game.Overlays.Notifications iconDrawable = new TextAwesome { Anchor = Anchor.Centre, + Origin = Anchor.Centre, Icon = icon, } }); diff --git a/osu.Game/Overlays/Options/OptionDropDown.cs b/osu.Game/Overlays/Options/OptionDropDown.cs index 41e59e63a6..1878d12d43 100644 --- a/osu.Game/Overlays/Options/OptionDropDown.cs +++ b/osu.Game/Overlays/Options/OptionDropDown.cs @@ -46,12 +46,12 @@ namespace osu.Game.Overlays.Options private Bindable bindable; - void bindable_ValueChanged(object sender, EventArgs e) + private void bindable_ValueChanged(object sender, EventArgs e) { dropdown.SelectedValue = bindable.Value; } - void dropdown_ValueChanged(object sender, EventArgs e) + private void dropdown_ValueChanged(object sender, EventArgs e) { bindable.Value = dropdown.SelectedValue; } diff --git a/osu.Game/Overlays/Options/OptionLabel.cs b/osu.Game/Overlays/Options/OptionLabel.cs index bfe59eb035..4b0f1e4ec0 100644 --- a/osu.Game/Overlays/Options/OptionLabel.cs +++ b/osu.Game/Overlays/Options/OptionLabel.cs @@ -7,7 +7,7 @@ using osu.Game.Graphics.Sprites; namespace osu.Game.Overlays.Options { - class OptionLabel : OsuSpriteText + internal class OptionLabel : OsuSpriteText { [BackgroundDependencyLoader] private void load(OsuColour colour) diff --git a/osu.Game/Overlays/Options/OptionsFooter.cs b/osu.Game/Overlays/Options/OptionsFooter.cs new file mode 100644 index 0000000000..23622aef08 --- /dev/null +++ b/osu.Game/Overlays/Options/OptionsFooter.cs @@ -0,0 +1,68 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Modes; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.Options +{ + public class OptionsFooter : FillFlowContainer + { + [BackgroundDependencyLoader] + private void load(OsuGameBase game, OsuColour colours) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Direction = FillDirection.Vertical; + Padding = new MarginPadding { Top = 20, Bottom = 30 }; + + var modes = new List(); + + foreach (PlayMode m in Enum.GetValues(typeof(PlayMode))) + modes.Add(new TextAwesome + { + Icon = Ruleset.GetRuleset(m).Icon, + Colour = Color4.Gray, + }); + + Children = new Drawable[] + { + new FillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Direction = FillDirection.Full, + AutoSizeAxes = Axes.Both, + Children = modes, + Spacing = new Vector2(5), + Padding = new MarginPadding { Bottom = 10 }, + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = game.Name, + TextSize = 18, + Font = @"Exo2.0-Bold", + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + TextSize = 14, + Text = game.Version, + Colour = game.IsDebug ? colours.Red : Color4.White, + }, + }; + } + } +} \ No newline at end of file diff --git a/osu.Game/Overlays/Options/OptionsSubsection.cs b/osu.Game/Overlays/Options/OptionsSubsection.cs index 7d501ad68c..f21c34fa21 100644 --- a/osu.Game/Overlays/Options/OptionsSubsection.cs +++ b/osu.Game/Overlays/Options/OptionsSubsection.cs @@ -11,8 +11,7 @@ namespace osu.Game.Overlays.Options { public abstract class OptionsSubsection : FillFlowContainer { - private Container content; - protected override Container Content => content; + protected override Container Content { get; } protected abstract string Header { get; } @@ -29,7 +28,7 @@ namespace osu.Game.Overlays.Options Margin = new MarginPadding { Bottom = 10 }, Font = @"Exo2.0-Black", }, - content = new FillFlowContainer + Content = new FillFlowContainer { Direction = FillDirection.Vertical, Spacing = new Vector2(0, 5), diff --git a/osu.Game/Overlays/Options/Sections/Audio/AudioDevicesOptions.cs b/osu.Game/Overlays/Options/Sections/Audio/AudioDevicesOptions.cs index 06ddf584b2..25585845b0 100644 --- a/osu.Game/Overlays/Options/Sections/Audio/AudioDevicesOptions.cs +++ b/osu.Game/Overlays/Options/Sections/Audio/AudioDevicesOptions.cs @@ -32,12 +32,11 @@ namespace osu.Game.Overlays.Options.Sections.Audio private void updateItems() { - var deviceItems = new List>(); - deviceItems.Add(new KeyValuePair("Default", string.Empty)); + var deviceItems = new List> { new KeyValuePair("Default", string.Empty) }; deviceItems.AddRange(audio.AudioDeviceNames.Select(d => new KeyValuePair(d, d))); var preferredDeviceName = audio.AudioDevice.Value; - if (!deviceItems.Any(kv => kv.Value == preferredDeviceName)) + if (deviceItems.All(kv => kv.Value != preferredDeviceName)) deviceItems.Add(new KeyValuePair(preferredDeviceName, preferredDeviceName)); dropdown.Items = deviceItems; @@ -51,7 +50,7 @@ namespace osu.Game.Overlays.Options.Sections.Audio Children = new Drawable[] { - dropdown = new OptionDropDown() + dropdown = new OptionDropDown { Bindable = audio.AudioDevice }, diff --git a/osu.Game/Overlays/Options/Sections/Debug/GeneralOptions.cs b/osu.Game/Overlays/Options/Sections/Debug/GeneralOptions.cs new file mode 100644 index 0000000000..34901e1dac --- /dev/null +++ b/osu.Game/Overlays/Options/Sections/Debug/GeneralOptions.cs @@ -0,0 +1,28 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Options.Sections.Debug +{ + public class GeneralOptions : OptionsSubsection + { + protected override string Header => "General"; + + [BackgroundDependencyLoader] + private void load(FrameworkDebugConfigManager config) + { + Children = new Drawable[] + { + new OsuCheckbox + { + LabelText = "Bypass caching", + Bindable = config.GetBindable(FrameworkDebugConfig.BypassCaching) + } + }; + } + } +} diff --git a/osu.Game/Overlays/Options/Sections/DebugSection.cs b/osu.Game/Overlays/Options/Sections/DebugSection.cs index 0839088f08..a90558a319 100644 --- a/osu.Game/Overlays/Options/Sections/DebugSection.cs +++ b/osu.Game/Overlays/Options/Sections/DebugSection.cs @@ -16,6 +16,7 @@ namespace osu.Game.Overlays.Options.Sections { Children = new Drawable[] { + new GeneralOptions(), new GCOptions(), }; } diff --git a/osu.Game/Overlays/Options/Sections/General/LoginOptions.cs b/osu.Game/Overlays/Options/Sections/General/LoginOptions.cs index 45f7ac3d3e..3cf766b7af 100644 --- a/osu.Game/Overlays/Options/Sections/General/LoginOptions.cs +++ b/osu.Game/Overlays/Options/Sections/General/LoginOptions.cs @@ -84,7 +84,7 @@ namespace osu.Game.Overlays.Options.Sections.General } } - class LoginForm : FillFlowContainer + private class LoginForm : FillFlowContainer { private TextBox username; private TextBox password; diff --git a/osu.Game/Overlays/Options/Sections/MaintenanceSection.cs b/osu.Game/Overlays/Options/Sections/MaintenanceSection.cs index 6aa9b66f5c..638c1c9a64 100644 --- a/osu.Game/Overlays/Options/Sections/MaintenanceSection.cs +++ b/osu.Game/Overlays/Options/Sections/MaintenanceSection.cs @@ -5,7 +5,6 @@ using osu.Framework.Graphics; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using OpenTK; -using osu.Framework.Graphics.Containers; namespace osu.Game.Overlays.Options.Sections { @@ -39,20 +38,6 @@ namespace osu.Game.Overlays.Options.Sections RelativeSizeAxes = Axes.X, Text = "Run osu! updater", }, - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new[] - { - new OptionLabel - { - Text = "osu!lazer", - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - }, - } - } }; } } diff --git a/osu.Game/Overlays/OptionsOverlay.cs b/osu.Game/Overlays/OptionsOverlay.cs index 89a2d029f6..04048dcf98 100644 --- a/osu.Game/Overlays/OptionsOverlay.cs +++ b/osu.Game/Overlays/OptionsOverlay.cs @@ -100,7 +100,8 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.X, Direction = FillDirection.Vertical, Children = sections, - } + }, + new OptionsFooter() } } } diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 469efe8891..170740705c 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -50,7 +50,7 @@ namespace osu.Game.Overlays.Toolbar Children = new Drawable[] { new ToolbarSettingsButton(), - new ToolbarHomeButton() + new ToolbarHomeButton { Action = () => OnHome?.Invoke() }, @@ -145,7 +145,7 @@ namespace osu.Game.Overlays.Toolbar FadeOut(transition_time); } - class PassThroughFlowContainer : FillFlowContainer + private class PassThroughFlowContainer : FillFlowContainer { //needed to get input to the login overlay. public override bool Contains(Vector2 screenSpacePos) => true; diff --git a/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs b/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs index 4dca6b7a16..431cc73887 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs @@ -5,7 +5,7 @@ using osu.Game.Graphics; namespace osu.Game.Overlays.Toolbar { - class ToolbarHomeButton : ToolbarButton + internal class ToolbarHomeButton : ToolbarButton { public ToolbarHomeButton() { diff --git a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs index b2086b1c2d..3b83fc4e9a 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs @@ -15,9 +15,9 @@ using OpenTK.Graphics; namespace osu.Game.Overlays.Toolbar { - class ToolbarModeSelector : Container + internal class ToolbarModeSelector : Container { - const float padding = 10; + private const float padding = 10; private FillFlowContainer modeButtons; private Drawable modeButtonLine; @@ -29,7 +29,7 @@ namespace osu.Game.Overlays.Toolbar { RelativeSizeAxes = Axes.Y; - Children = new Drawable[] + Children = new[] { new OpaqueBackground(), modeButtons = new FillFlowContainer diff --git a/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs b/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs index d839cfab14..725af09ee7 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs @@ -6,7 +6,7 @@ using osu.Game.Graphics; namespace osu.Game.Overlays.Toolbar { - class ToolbarMusicButton : ToolbarOverlayToggleButton + internal class ToolbarMusicButton : ToolbarOverlayToggleButton { public ToolbarMusicButton() { diff --git a/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs b/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs index 973f9f2d8a..4da87a0568 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs @@ -7,7 +7,7 @@ using osu.Game.Graphics; namespace osu.Game.Overlays.Toolbar { - class ToolbarNotificationButton : ToolbarOverlayToggleButton + internal class ToolbarNotificationButton : ToolbarOverlayToggleButton { protected override Anchor TooltipAnchor => Anchor.TopRight; diff --git a/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs b/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs index 16cb5d9e6f..991c76e164 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs @@ -9,7 +9,7 @@ using osu.Game.Graphics; namespace osu.Game.Overlays.Toolbar { - class ToolbarOverlayToggleButton : ToolbarButton + internal class ToolbarOverlayToggleButton : ToolbarButton { private Box stateBackground; diff --git a/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs b/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs index bb72642a65..9f8170b604 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs @@ -6,7 +6,7 @@ using osu.Game.Graphics; namespace osu.Game.Overlays.Toolbar { - class ToolbarSettingsButton : ToolbarOverlayToggleButton + internal class ToolbarSettingsButton : ToolbarOverlayToggleButton { public ToolbarSettingsButton() { diff --git a/osu.Game/Overlays/Toolbar/ToolbarUserArea.cs b/osu.Game/Overlays/Toolbar/ToolbarUserArea.cs index 1955ef5c1c..9de0f290a5 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarUserArea.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarUserArea.cs @@ -8,7 +8,7 @@ using OpenTK; namespace osu.Game.Overlays.Toolbar { - class ToolbarUserArea : Container + internal class ToolbarUserArea : Container { public LoginOverlay LoginOverlay; private ToolbarUserButton button; diff --git a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs index be165d39b2..ae3d41e374 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs @@ -14,7 +14,7 @@ using OpenTK.Graphics; namespace osu.Game.Overlays.Toolbar { - class ToolbarUserButton : ToolbarButton, IOnlineComponent + internal class ToolbarUserButton : ToolbarButton, IOnlineComponent { private Avatar avatar; diff --git a/osu.Game/Overlays/WaveOverlayContainer.cs b/osu.Game/Overlays/WaveOverlayContainer.cs index 18971a532a..747b46aecc 100644 --- a/osu.Game/Overlays/WaveOverlayContainer.cs +++ b/osu.Game/Overlays/WaveOverlayContainer.cs @@ -134,19 +134,23 @@ namespace osu.Game.Overlays foreach (var w in wavesContainer.Children) w.State = Visibility.Visible; - contentContainer.FadeIn(APPEAR_DURATION, EasingTypes.OutQuint); + FadeIn(100, EasingTypes.OutQuint); contentContainer.MoveToY(0, APPEAR_DURATION, EasingTypes.OutQuint); + + FadeIn(100, EasingTypes.OutQuint); } protected override void PopOut() { base.PopOut(); - contentContainer.FadeOut(DISAPPEAR_DURATION, EasingTypes.In); + FadeOut(DISAPPEAR_DURATION, EasingTypes.InQuint); contentContainer.MoveToY(DrawHeight * 2f, DISAPPEAR_DURATION, EasingTypes.In); foreach (var w in wavesContainer.Children) w.State = Visibility.Hidden; + + FadeOut(DISAPPEAR_DURATION, EasingTypes.InQuint); } protected override void UpdateAfterChildren() diff --git a/osu.Game/Screens/BackgroundScreen.cs b/osu.Game/Screens/BackgroundScreen.cs index a71eac5fae..dd41294598 100644 --- a/osu.Game/Screens/BackgroundScreen.cs +++ b/osu.Game/Screens/BackgroundScreen.cs @@ -19,8 +19,8 @@ namespace osu.Game.Screens return other?.GetType() == GetType(); } - const float transition_length = 500; - const float x_movement_amount = 50; + private const float transition_length = 500; + private const float x_movement_amount = 50; protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) { @@ -28,7 +28,7 @@ namespace osu.Game.Screens return false; } - Framework.Game game; + private Framework.Game game; [BackgroundDependencyLoader] private void load(Framework.Game game) @@ -58,7 +58,7 @@ namespace osu.Game.Screens protected override void Update() { base.Update(); - Content.Scale = new Vector2(1 + (x_movement_amount / DrawSize.X) * 2); + Content.Scale = new Vector2(1 + x_movement_amount / DrawSize.X * 2); } protected override void OnEntering(Screen last) diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index 6a8328cf8c..95a263463a 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -68,10 +68,13 @@ namespace osu.Game.Screens.Backgrounds public override bool Equals(BackgroundScreen other) { - return base.Equals(other) && beatmap == ((BackgroundScreenBeatmap)other).Beatmap; + var otherBeatmapBackground = other as BackgroundScreenBeatmap; + if (otherBeatmapBackground == null) return false; + + return base.Equals(other) && beatmap == otherBeatmapBackground.Beatmap; } - class BeatmapBackground : Background + private class BeatmapBackground : Background { private WorkingBeatmap beatmap; diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenCustom.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenCustom.cs index 9ec8628c5f..dd377938a7 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenCustom.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenCustom.cs @@ -17,7 +17,10 @@ namespace osu.Game.Screens.Backgrounds public override bool Equals(BackgroundScreen other) { - return base.Equals(other) && textureName == ((BackgroundScreenCustom)other).textureName; + var backgroundScreenCustom = other as BackgroundScreenCustom; + if (backgroundScreenCustom == null) return false; + + return base.Equals(other) && textureName == backgroundScreenCustom.textureName; } } } \ No newline at end of file diff --git a/osu.Game/Screens/Charts/ChartInfo.cs b/osu.Game/Screens/Charts/ChartInfo.cs index f85d3acb69..b5ac5e4945 100644 --- a/osu.Game/Screens/Charts/ChartInfo.cs +++ b/osu.Game/Screens/Charts/ChartInfo.cs @@ -3,7 +3,7 @@ namespace osu.Game.Screens.Charts { - class ChartInfo : ScreenWhiteBox + internal class ChartInfo : ScreenWhiteBox { } } diff --git a/osu.Game/Screens/Charts/ChartListing.cs b/osu.Game/Screens/Charts/ChartListing.cs index b570b63c06..7bc6f0fa03 100644 --- a/osu.Game/Screens/Charts/ChartListing.cs +++ b/osu.Game/Screens/Charts/ChartListing.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; namespace osu.Game.Screens.Charts { - class ChartListing : ScreenWhiteBox + internal class ChartListing : ScreenWhiteBox { protected override IEnumerable PossibleChildren => new[] { typeof(ChartInfo) diff --git a/osu.Game/Screens/Direct/OnlineListing.cs b/osu.Game/Screens/Direct/OnlineListing.cs index 28de1c58d9..9ce23c2863 100644 --- a/osu.Game/Screens/Direct/OnlineListing.cs +++ b/osu.Game/Screens/Direct/OnlineListing.cs @@ -3,7 +3,7 @@ namespace osu.Game.Screens.Direct { - class OnlineListing : ScreenWhiteBox + internal class OnlineListing : ScreenWhiteBox { } } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 284a7f6f94..9d98a0f165 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -7,7 +7,7 @@ using OpenTK.Graphics; namespace osu.Game.Screens.Edit { - class Editor : ScreenWhiteBox + internal class Editor : ScreenWhiteBox { protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4"); diff --git a/osu.Game/Screens/GameScreenWhiteBox.cs b/osu.Game/Screens/GameScreenWhiteBox.cs index ec5ae13713..8e93ff3332 100644 --- a/osu.Game/Screens/GameScreenWhiteBox.cs +++ b/osu.Game/Screens/GameScreenWhiteBox.cs @@ -21,7 +21,7 @@ namespace osu.Game.Screens { private BackButton popButton; - const int transition_time = 1000; + private const int transition_time = 1000; protected virtual IEnumerable PossibleChildren => null; @@ -56,7 +56,7 @@ namespace osu.Game.Screens protected override bool OnExiting(Screen next) { - textContainer.MoveTo(new Vector2((DrawSize.X / 16), 0), transition_time, EasingTypes.OutExpo); + textContainer.MoveTo(new Vector2(DrawSize.X / 16, 0), transition_time, EasingTypes.OutExpo); Content.FadeOut(transition_time, EasingTypes.OutExpo); return base.OnExiting(next); diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs index a3c7be8453..41ca9df83b 100644 --- a/osu.Game/Screens/Loader.cs +++ b/osu.Game/Screens/Loader.cs @@ -7,7 +7,7 @@ using osu.Game.Screens.Menu; namespace osu.Game.Screens { - class Loader : OsuScreen + internal class Loader : OsuScreen { internal override bool ShowOverlays => false; diff --git a/osu.Game/Screens/Menu/Button.cs b/osu.Game/Screens/Menu/Button.cs index 4954fd9366..00ad1018fa 100644 --- a/osu.Game/Screens/Menu/Button.cs +++ b/osu.Game/Screens/Menu/Button.cs @@ -108,6 +108,7 @@ namespace osu.Game.Screens.Menu { Shadow = true, Anchor = Anchor.Centre, + Origin = Anchor.Centre, TextSize = 30, Position = new Vector2(0, 0), Icon = symbol @@ -291,7 +292,7 @@ namespace osu.Game.Screens.Menu public int ContractStyle; - ButtonState state; + private ButtonState state; public ButtonState State { diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index cad0ef4e47..bab6e24e96 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -49,8 +49,8 @@ namespace osu.Game.Screens.Menu private Button backButton; private Button settingsButton; - List