diff --git a/appveyor.yml b/appveyor.yml index ac6d6ebff8..7c08eb9e9c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,6 +2,9 @@ clone_depth: 1 version: '{branch}-{build}' image: Visual Studio 2017 configuration: Debug +cache: + - C:\ProgramData\chocolatey\bin -> appveyor.yml + - C:\ProgramData\chocolatey\lib -> appveyor.yml install: - cmd: git submodule update --init --recursive --depth=5 - cmd: choco install resharper-clt -y @@ -18,4 +21,4 @@ build: verbosity: minimal after_build: - cmd: inspectcode --o="inspectcodereport.xml" --projects:osu.Game* --caches-home="inspectcode" osu.sln > NUL - - cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors \ No newline at end of file + - cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index 64adcecba4..a4270f22b4 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -12,6 +12,7 @@ using osu.Framework.Platform; using osu.Game; using OpenTK.Input; using Microsoft.Win32; +using osu.Desktop.Updater; using osu.Framework.Platform.Windows; namespace osu.Desktop @@ -38,6 +39,52 @@ namespace osu.Desktop } } + protected override void LoadComplete() + { + base.LoadComplete(); + + if (!noVersionOverlay) + { + LoadComponentAsync(new VersionManager { Depth = int.MinValue }, v => + { + Add(v); + v.State = Visibility.Visible; + }); + +#if NET_FRAMEWORK + Add(new SquirrelUpdateManager()); +#else + Add(new SimpleUpdateManager()); +#endif + } + } + + public override void SetHost(GameHost host) + { + base.SetHost(host); + var desktopWindow = host.Window as DesktopGameWindow; + if (desktopWindow != null) + { + desktopWindow.CursorState |= CursorState.Hidden; + + desktopWindow.SetIconFromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico")); + desktopWindow.Title = Name; + + desktopWindow.FileDrop += fileDrop; + } + } + + private void fileDrop(object sender, FileDropEventArgs e) + { + var filePaths = new[] { e.FileName }; + + var firstExtension = Path.GetExtension(filePaths.First()); + + if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return; + + Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning); + } + /// /// A method of accessing an osu-stable install in a controlled fashion. /// @@ -77,45 +124,5 @@ namespace osu.Desktop { } } - - protected override void LoadComplete() - { - base.LoadComplete(); - - if (!noVersionOverlay) - { - LoadComponentAsync(new VersionManager { Depth = int.MinValue }, v => - { - Add(v); - v.State = Visibility.Visible; - }); - } - } - - public override void SetHost(GameHost host) - { - base.SetHost(host); - var desktopWindow = host.Window as DesktopGameWindow; - if (desktopWindow != null) - { - desktopWindow.CursorState |= CursorState.Hidden; - - desktopWindow.SetIconFromStream(Assembly.GetExecutingAssembly().GetManifestResourceStream(GetType(), "lazer.ico")); - desktopWindow.Title = Name; - - desktopWindow.FileDrop += fileDrop; - } - } - - private void fileDrop(object sender, FileDropEventArgs e) - { - var filePaths = new[] { e.FileName }; - - var firstExtension = Path.GetExtension(filePaths.First()); - - if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return; - - Task.Factory.StartNew(() => Import(filePaths), TaskCreationOptions.LongRunning); - } } } diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs index bc1faec822..1129969694 100644 --- a/osu.Desktop/Overlays/VersionManager.cs +++ b/osu.Desktop/Overlays/VersionManager.cs @@ -91,10 +91,6 @@ namespace osu.Desktop.Overlays } } }; - -#if NET_FRAMEWORK - Add(new SquirrelUpdateManager()); -#endif } protected override void LoadComplete() diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 61d2006315..7e6a07c89f 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -7,6 +7,7 @@ using System.Linq; using osu.Framework; using osu.Framework.Platform; using osu.Game.IPC; + #if NET_FRAMEWORK using System.Runtime; #endif @@ -18,10 +19,7 @@ namespace osu.Desktop [STAThread] public static int Main(string[] args) { - // required to initialise native SQLite libraries on some platforms. - - if (!RuntimeInfo.IsMono) - useMulticoreJit(); + useMultiCoreJit(); // Back up the cwd before DesktopGameHost changes it var cwd = Environment.CurrentDirectory; @@ -54,7 +52,7 @@ namespace osu.Desktop } } - private static void useMulticoreJit() + private static void useMultiCoreJit() { #if NET_FRAMEWORK var directory = Directory.CreateDirectory(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Profiles")); diff --git a/osu.Desktop/Updater/SimpleUpdateManager.cs b/osu.Desktop/Updater/SimpleUpdateManager.cs new file mode 100644 index 0000000000..6c363422f7 --- /dev/null +++ b/osu.Desktop/Updater/SimpleUpdateManager.cs @@ -0,0 +1,103 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Newtonsoft.Json; +using osu.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Containers; +using osu.Framework.IO.Network; +using osu.Framework.Platform; +using osu.Game; +using osu.Game.Graphics; +using osu.Game.Overlays; +using osu.Game.Overlays.Notifications; + +namespace osu.Desktop.Updater +{ + /// + /// An update manager that shows notifications if a newer release is detected. + /// Installation is left up to the user. + /// + internal class SimpleUpdateManager : CompositeDrawable + { + private NotificationOverlay notificationOverlay; + private string version; + private GameHost host; + + [BackgroundDependencyLoader] + private void load(NotificationOverlay notification, OsuGameBase game, GameHost host) + { + notificationOverlay = notification; + + this.host = host; + version = game.Version; + + if (game.IsDeployedBuild) + Schedule(() => Task.Run(() => checkForUpdateAsync())); + } + + private async void checkForUpdateAsync() + { + var releases = new JsonWebRequest("https://api.github.com/repos/ppy/osu/releases/latest"); + await releases.PerformAsync(); + + var latest = releases.ResponseObject; + + if (latest.TagName != version) + { + notificationOverlay.Post(new SimpleNotification + { + Text = $"A newer release of osu! has been found ({version} → {latest.TagName}).\n\n" + + "Click here to download the new version, which can be installed over the top of your existing installation", + Icon = FontAwesome.fa_upload, + Activated = () => + { + host.OpenUrlExternally(getBestUrl(latest)); + return true; + } + }); + } + } + + private string getBestUrl(GitHubRelease release) + { + GitHubAsset bestAsset = null; + + switch (RuntimeInfo.OS) + { + case RuntimeInfo.Platform.Windows: + bestAsset = release.Assets?.FirstOrDefault(f => f.Name.EndsWith(".exe")); + break; + case RuntimeInfo.Platform.MacOsx: + bestAsset = release.Assets?.FirstOrDefault(f => f.Name.EndsWith(".app.zip")); + break; + } + + return bestAsset?.BrowserDownloadUrl ?? release.HtmlUrl; + } + + public class GitHubRelease + { + [JsonProperty("html_url")] + public string HtmlUrl { get; set; } + + [JsonProperty("tag_name")] + public string TagName { get; set; } + + [JsonProperty("assets")] + public List Assets { get; set; } + } + + public class GitHubAsset + { + [JsonProperty("name")] + public string Name { get; set; } + + [JsonProperty("browser_download_url")] + public string BrowserDownloadUrl { get; set; } + } + } +} diff --git a/osu.Desktop/Overlays/SquirrelUpdateManager.cs b/osu.Desktop/Updater/SquirrelUpdateManager.cs similarity index 97% rename from osu.Desktop/Overlays/SquirrelUpdateManager.cs rename to osu.Desktop/Updater/SquirrelUpdateManager.cs index ea86d2f028..81da26cff2 100644 --- a/osu.Desktop/Overlays/SquirrelUpdateManager.cs +++ b/osu.Desktop/Updater/SquirrelUpdateManager.cs @@ -3,6 +3,7 @@ #if NET_FRAMEWORK using System; +using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; @@ -16,7 +17,7 @@ using OpenTK; using OpenTK.Graphics; using Squirrel; -namespace osu.Desktop.Overlays +namespace osu.Desktop.Updater { public class SquirrelUpdateManager : Component { @@ -35,7 +36,7 @@ namespace osu.Desktop.Overlays notificationOverlay = notification; if (game.IsDeployedBuild) - Schedule(() => checkForUpdateAsync()); + Schedule(() => Task.Run(() => checkForUpdateAsync())); } private async void checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null) diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseCatcherArea.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseCatcherArea.cs index 0ba6398ced..25f7ca108d 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestCaseCatcherArea.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestCaseCatcherArea.cs @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Catch.Tests { } - public void ToggleHyperDash(bool status) => MovableCatcher.SetHyperdashState(status ? 2 : 1); + public void ToggleHyperDash(bool status) => MovableCatcher.SetHyperDashState(status ? 2 : 1); } } } diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseHyperdash.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseHyperDash.cs similarity index 88% rename from osu.Game.Rulesets.Catch.Tests/TestCaseHyperdash.cs rename to osu.Game.Rulesets.Catch.Tests/TestCaseHyperDash.cs index 896582bf0a..14487b2c7f 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestCaseHyperdash.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestCaseHyperDash.cs @@ -8,9 +8,9 @@ using osu.Game.Rulesets.Catch.Objects; namespace osu.Game.Rulesets.Catch.Tests { [TestFixture] - public class TestCaseHyperdash : Game.Tests.Visual.TestCasePlayer + public class TestCaseHyperDash : Game.Tests.Visual.TestCasePlayer { - public TestCaseHyperdash() + public TestCaseHyperDash() : base(new CatchRuleset()) { } diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 3d1013aad3..31c56c37c4 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -61,12 +61,12 @@ namespace osu.Game.Rulesets.Catch.Difficulty return new CatchDifficultyAttributes(mods, 0); // this is the same as osu!, so there's potential to share the implementation... maybe - double preEmpt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate; + double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate; double starRating = Math.Sqrt(calculateDifficulty(difficultyHitObjects, timeRate)) * star_scaling_factor; return new CatchDifficultyAttributes(mods, starRating) { - ApproachRate = preEmpt > 1200.0 ? -(preEmpt - 1800.0) / 120.0 : -(preEmpt - 1200.0) / 150.0 + 5.0, + ApproachRate = preempt > 1200.0 ? -(preempt - 1800.0) / 120.0 : -(preempt - 1200.0) / 150.0 + 5.0, MaxCombo = difficultyHitObjects.Count }; } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 9c376f340a..2f42902fd0 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -255,11 +255,11 @@ namespace osu.Game.Rulesets.Catch.UI double positionDifference = target.X * CatchPlayfield.BASE_WIDTH - catcherPosition; double velocity = positionDifference / Math.Max(1.0, timeDifference - 1000.0 / 60.0); - SetHyperdashState(Math.Abs(velocity), target.X); + SetHyperDashState(Math.Abs(velocity), target.X); } else { - SetHyperdashState(); + SetHyperDashState(); } return validCatch; @@ -270,18 +270,18 @@ namespace osu.Game.Rulesets.Catch.UI private float hyperDashTargetPosition; /// - /// Whether we are hypderdashing or not. + /// Whether we are hyper-dashing or not. /// public bool HyperDashing => hyperDashModifier != 1; /// - /// Set hyperdash state. + /// Set hyper-dash state. /// - /// The speed multiplier. If this is less or equals to 1, this catcher will be non-hyperdashing state. - /// When this catcher crosses this position, this catcher ends hyperdashing. - public void SetHyperdashState(double modifier = 1, float targetPosition = -1) + /// The speed multiplier. If this is less or equals to 1, this catcher will be non-hyper-dashing state. + /// When this catcher crosses this position, this catcher ends hyper-dashing. + public void SetHyperDashState(double modifier = 1, float targetPosition = -1) { - const float hyperdash_transition_length = 180; + const float hyper_dash_transition_length = 180; bool previouslyHyperDashing = HyperDashing; if (modifier <= 1 || X == targetPosition) @@ -291,8 +291,8 @@ namespace osu.Game.Rulesets.Catch.UI if (previouslyHyperDashing) { - this.FadeColour(Color4.White, hyperdash_transition_length, Easing.OutQuint); - this.FadeTo(1, hyperdash_transition_length, Easing.OutQuint); + this.FadeColour(Color4.White, hyper_dash_transition_length, Easing.OutQuint); + this.FadeTo(1, hyper_dash_transition_length, Easing.OutQuint); } } else @@ -303,8 +303,8 @@ namespace osu.Game.Rulesets.Catch.UI if (!previouslyHyperDashing) { - this.FadeColour(Color4.OrangeRed, hyperdash_transition_length, Easing.OutQuint); - this.FadeTo(0.2f, hyperdash_transition_length, Easing.OutQuint); + this.FadeColour(Color4.OrangeRed, hyper_dash_transition_length, Easing.OutQuint); + this.FadeTo(0.2f, hyper_dash_transition_length, Easing.OutQuint); Trail = true; } } @@ -370,7 +370,7 @@ namespace osu.Game.Rulesets.Catch.UI hyperDashDirection < 0 && hyperDashTargetPosition > X) { X = hyperDashTargetPosition; - SetHyperdashState(); + SetHyperDashState(); } } diff --git a/osu.Game.Rulesets.Mania.Tests/ScrollingTestContainer.cs b/osu.Game.Rulesets.Mania.Tests/ScrollingTestContainer.cs index 78a98e83e8..5a93efb0dc 100644 --- a/osu.Game.Rulesets.Mania.Tests/ScrollingTestContainer.cs +++ b/osu.Game.Rulesets.Mania.Tests/ScrollingTestContainer.cs @@ -21,9 +21,9 @@ namespace osu.Game.Rulesets.Mania.Tests this.direction = direction; } - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { - var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); + var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); dependencies.CacheAs(new ScrollingInfo { Direction = { Value = direction }}); return dependencies; } diff --git a/osu.Game.Rulesets.Mania.Tests/TestCaseNotes.cs b/osu.Game.Rulesets.Mania.Tests/TestCaseNotes.cs index e827fa1fdc..64ed08373a 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestCaseNotes.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestCaseNotes.cs @@ -137,9 +137,9 @@ namespace osu.Game.Rulesets.Mania.Tests }; } - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { - var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); + var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); dependencies.CacheAs>(new Bindable()); return dependencies; } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs index 0f170e8540..b902ee63c6 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs @@ -19,9 +19,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, IBeatmap originalBeatmap) : base(random, hitObject, beatmap, new Pattern(), originalBeatmap) { - var endtimeData = HitObject as IHasEndTime; - - endTime = endtimeData?.EndTime ?? 0; + endTime = (HitObject as IHasEndTime)?.EndTime ?? 0; } public override IEnumerable Generate() diff --git a/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs b/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs index 54a7bf954d..783142fadc 100644 --- a/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs +++ b/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs @@ -19,14 +19,14 @@ namespace osu.Game.Rulesets.Mania } [BackgroundDependencyLoader] - private void load(ManiaConfigManager config) + private void load() { Children = new Drawable[] { new SettingsEnumDropdown { LabelText = "Scrolling direction", - Bindable = config.GetBindable(ManiaSetting.ScrollDirection) + Bindable = ((ManiaConfigManager)Config).GetBindable(ManiaSetting.ScrollDirection) } }; } diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index a19a6fb5d4..8465258055 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -118,9 +118,9 @@ namespace osu.Game.Rulesets.Mania.UI } } - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { - var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); + var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); dependencies.CacheAs>(Action); return dependencies; } diff --git a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs index e6ebf43c67..abc9705119 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs @@ -70,19 +70,19 @@ namespace osu.Game.Rulesets.Mania.UI } [BackgroundDependencyLoader] - private void load(ManiaConfigManager config) + private void load() { BarLines.ForEach(Playfield.Add); - config.BindWith(ManiaSetting.ScrollDirection, configDirection); + ((ManiaConfigManager)Config).BindWith(ManiaSetting.ScrollDirection, configDirection); configDirection.BindValueChanged(d => scrollingInfo.Direction.Value = (ScrollingDirection)d, true); } private DependencyContainer dependencies; - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { - dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); + dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); dependencies.CacheAs(scrollingInfo = new ScrollingInfo()); return dependencies; } diff --git a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs index 3fa039d946..3a551bbbcf 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs @@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Osu.Tests public struct ConvertValue : IEquatable { /// - /// A sane value to account for osu!stable using ints everwhere. + /// A sane value to account for osu!stable using s everywhere. /// private const double conversion_lenience = 2; diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 62fafd8196..5e91ed7a97 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -61,19 +61,19 @@ namespace osu.Game.Rulesets.Osu.Difficulty double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2; - // Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be remoevd in the future + // Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be removed in the future double hitWindowGreat = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate; - double preEmpt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate; + double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate; int maxCombo = beatmap.HitObjects.Count(); - // Add the ticks + tail of the slider. 1 is subtracted because the "headcircle" would be counted twice (once for the slider itself in the line above) + // Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above) maxCombo += beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.Count - 1); return new OsuDifficultyAttributes(mods, starRating) { AimStrain = aimRating, SpeedStrain = speedRating, - ApproachRate = preEmpt > 1200 ? (1800 - preEmpt) / 120 : (1200 - preEmpt) / 150 + 5, + ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5, OverallDifficulty = (80 - hitWindowGreat) / 6, MaxCombo = maxCombo }; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index 4220b72b16..4eff2a55c8 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override void ApplyToDrawableHitObjects(IEnumerable drawables) { - void adjustFadeIn(OsuHitObject h) => h.TimeFadein = h.TimePreempt * fade_in_duration_multiplier; + void adjustFadeIn(OsuHitObject h) => h.TimeFadeIn = h.TimePreempt * fade_in_duration_multiplier; foreach (var d in drawables.OfType()) { @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Mods var h = d.HitObject; - var fadeOutStartTime = h.StartTime - h.TimePreempt + h.TimeFadein; + var fadeOutStartTime = h.StartTime - h.TimePreempt + h.TimeFadeIn; var fadeOutDuration = h.TimePreempt * fade_out_duration_multiplier; // new duration from completed fade in to end (before fading out) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs index 4653f45149..4ac3b0c983 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs @@ -96,12 +96,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections using (fp.BeginAbsoluteSequence(fadeInTime)) { - fp.FadeIn(currHitObject.TimeFadein); - fp.ScaleTo(1, currHitObject.TimeFadein, Easing.Out); + fp.FadeIn(currHitObject.TimeFadeIn); + fp.ScaleTo(1, currHitObject.TimeFadeIn, Easing.Out); - fp.MoveTo(pointEndPosition, currHitObject.TimeFadein, Easing.Out); + fp.MoveTo(pointEndPosition, currHitObject.TimeFadeIn, Easing.Out); - fp.Delay(fadeOutTime - fadeInTime).FadeOut(currHitObject.TimeFadein); + fp.Delay(fadeOutTime - fadeInTime).FadeOut(currHitObject.TimeFadeIn); } fp.Expire(true); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 421c93d485..c525b4bd97 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.UpdatePreemptState(); - ApproachCircle.FadeIn(Math.Min(HitObject.TimeFadein * 2, HitObject.TimePreempt)); + ApproachCircle.FadeIn(Math.Min(HitObject.TimeFadeIn * 2, HitObject.TimePreempt)); ApproachCircle.ScaleTo(1.1f, HitObject.TimePreempt); } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 7c9503dfe2..02def2189f 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables AccentColour = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; } - protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadein); + protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadeIn); protected virtual void UpdateCurrentState(ArmedState state) { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 10539f85a2..1d3df69fb8 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -167,7 +167,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { Disc.Tracking = OsuActionInputManager.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton); if (!spmCounter.IsPresent && Disc.Tracking) - spmCounter.FadeIn(HitObject.TimeFadein); + spmCounter.FadeIn(HitObject.TimeFadeIn); base.Update(); } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs index 94a61e7904..283d6b91f6 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs @@ -212,7 +212,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces var spanProgress = slider.ProgressAt(completionProgress); double start = 0; - double end = SnakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadein, 0, 1) : 1; + double end = SnakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadeIn, 0, 1) : 1; if (span >= slider.SpanCount() - 1) { diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index befbc01f3c..48a6365c00 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Objects public event Action PositionChanged; public double TimePreempt = 600; - public double TimeFadein = 400; + public double TimeFadeIn = 400; private Vector2 position; @@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.Objects base.ApplyDefaultsToSelf(controlPointInfo, difficulty); TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450); - TimeFadein = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1200, 800, 300); + TimeFadeIn = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1200, 800, 300); Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2; } diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTick.cs b/osu.Game.Rulesets.Osu/Objects/SliderTick.cs index 4db6eb9883..54337a12be 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderTick.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Objects // This is so on repeats ticks don't appear too late to be visually processed by the player. offset = 200; else - offset = TimeFadein * 0.66f; + offset = TimeFadeIn * 0.66f; TimePreempt = (StartTime - SpanStartTime) / 2 + offset; } diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index ce80537b93..b8ba1e2945 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -31,8 +31,8 @@ namespace osu.Game.Rulesets.Osu public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[] { - new KeyBinding(InputKey.Z, OsuAction.LeftButton), - new KeyBinding(InputKey.X, OsuAction.RightButton), + new KeyBinding(InputKey.A, OsuAction.LeftButton), + new KeyBinding(InputKey.S, OsuAction.RightButton), new KeyBinding(InputKey.MouseLeft, OsuAction.LeftButton), new KeyBinding(InputKey.MouseRight, OsuAction.RightButton), }; diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index ad203d2107..2f5b4a13d9 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -222,10 +222,10 @@ namespace osu.Game.Tests.Beatmaps.Formats { var hitObjects = decoder.Decode(stream).HitObjects; - Assert.AreEqual("hitnormal", getTestableSampleInfo(hitObjects[0]).Name); - Assert.AreEqual("hitnormal", getTestableSampleInfo(hitObjects[1]).Name); - Assert.AreEqual("hitnormal2", getTestableSampleInfo(hitObjects[2]).Name); - Assert.AreEqual("hitnormal", getTestableSampleInfo(hitObjects[3]).Name); + Assert.AreEqual("normal-hitnormal", getTestableSampleInfo(hitObjects[0]).LookupNames.First()); + Assert.AreEqual("normal-hitnormal", getTestableSampleInfo(hitObjects[1]).LookupNames.First()); + Assert.AreEqual("normal-hitnormal2", getTestableSampleInfo(hitObjects[2]).LookupNames.First()); + Assert.AreEqual("normal-hitnormal", getTestableSampleInfo(hitObjects[3]).LookupNames.First()); } SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(new SampleInfo { Name = "hitnormal" }); @@ -242,7 +242,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual("hit_1.wav", hitObjects[0].Samples[0].LookupNames.First()); Assert.AreEqual("hit_2.wav", hitObjects[1].Samples[0].LookupNames.First()); - Assert.AreEqual("hitnormal2", getTestableSampleInfo(hitObjects[2]).Name); + Assert.AreEqual("normal-hitnormal2", getTestableSampleInfo(hitObjects[2]).LookupNames.First()); Assert.AreEqual("hit_1.wav", hitObjects[3].Samples[0].LookupNames.First()); } diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs index ee66f53ddc..b232180eba 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs @@ -110,8 +110,8 @@ namespace osu.Game.Tests.Visual private void testInfoLabels(int expectedCount) { - AddAssert("check infolabels exists", () => infoWedge.Info.InfoLabelContainer.Children.Any()); - AddAssert("check infolabels count", () => infoWedge.Info.InfoLabelContainer.Children.Count == expectedCount); + AddAssert("check info labels exists", () => infoWedge.Info.InfoLabelContainer.Children.Any()); + AddAssert("check info labels count", () => infoWedge.Info.InfoLabelContainer.Children.Count == expectedCount); } private void testNullBeatmap() @@ -121,7 +121,7 @@ namespace osu.Game.Tests.Visual AddAssert("check default title", () => infoWedge.Info.TitleLabel.Text == Beatmap.Default.BeatmapInfo.Metadata.Title); AddAssert("check default artist", () => infoWedge.Info.ArtistLabel.Text == Beatmap.Default.BeatmapInfo.Metadata.Artist); AddAssert("check empty author", () => !infoWedge.Info.MapperContainer.Children.Any()); - AddAssert("check no infolabels", () => !infoWedge.Info.InfoLabelContainer.Children.Any()); + AddAssert("check no info labels", () => !infoWedge.Info.InfoLabelContainer.Children.Any()); } private void selectBeatmap(IBeatmap b) diff --git a/osu.Game.Tests/Visual/TestCaseButtonSystem.cs b/osu.Game.Tests/Visual/TestCaseButtonSystem.cs index 5eb81cdf9f..7f8133d638 100644 --- a/osu.Game.Tests/Visual/TestCaseButtonSystem.cs +++ b/osu.Game.Tests/Visual/TestCaseButtonSystem.cs @@ -1,6 +1,9 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; +using System.Collections.Generic; +using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; @@ -13,6 +16,13 @@ namespace osu.Game.Tests.Visual [TestFixture] public class TestCaseButtonSystem : OsuTestCase { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(ButtonSystem), + typeof(ButtonArea), + typeof(Button) + }; + public TestCaseButtonSystem() { OsuLogo logo; @@ -30,6 +40,9 @@ namespace osu.Game.Tests.Visual }; buttons.SetOsuLogo(logo); + + foreach (var s in Enum.GetValues(typeof(ButtonSystemState)).OfType().Skip(1)) + AddStep($"State to {s}", () => buttons.State = s); } } } diff --git a/osu.Game.Tests/Visual/TestCaseDisclaimer.cs b/osu.Game.Tests/Visual/TestCaseDisclaimer.cs new file mode 100644 index 0000000000..a8253a991a --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseDisclaimer.cs @@ -0,0 +1,28 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Game.Screens.Menu; +using OpenTK.Graphics; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseDisclaimer : OsuTestCase + { + [BackgroundDependencyLoader] + private void load() + { + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + new Disclaimer() + }; + } + } +} diff --git a/osu.Game.Tests/Visual/TestCaseMods.cs b/osu.Game.Tests/Visual/TestCaseMods.cs index 73c37348d5..1a28442e38 100644 --- a/osu.Game.Tests/Visual/TestCaseMods.cs +++ b/osu.Game.Tests/Visual/TestCaseMods.cs @@ -114,7 +114,7 @@ namespace osu.Game.Tests.Visual testMultiplierTextColour(noFailMod, modSelect.LowMultiplierColour); testMultiplierTextColour(hiddenMod, modSelect.HighMultiplierColour); - testUnimplmentedMod(autoPilotMod); + testUnimplementedMod(autoPilotMod); } private void testManiaMods(ManiaRuleset ruleset) @@ -154,7 +154,7 @@ namespace osu.Game.Tests.Visual checkNotSelected(mod); } - private void testUnimplmentedMod(Mod mod) + private void testUnimplementedMod(Mod mod) { selectNext(mod); checkNotSelected(mod); diff --git a/osu.Game.Tests/Visual/TestCaseOnScreenDisplay.cs b/osu.Game.Tests/Visual/TestCaseOnScreenDisplay.cs index 123c1fe055..bc232d814d 100644 --- a/osu.Game.Tests/Visual/TestCaseOnScreenDisplay.cs +++ b/osu.Game.Tests/Visual/TestCaseOnScreenDisplay.cs @@ -88,7 +88,7 @@ namespace osu.Game.Tests.Visual private class TestOnScreenDisplay : OnScreenDisplay { - protected override void Display(Drawable toDisplay) => toDisplay.FadeIn().ResizeHeightTo(110); + protected override void DisplayTemporarily(Drawable toDisplay) => toDisplay.FadeIn().ResizeHeightTo(110); } } } diff --git a/osu.Game.Tests/Visual/TestCaseOsuGame.cs b/osu.Game.Tests/Visual/TestCaseOsuGame.cs index f1a21a58d5..7a4e4c1210 100644 --- a/osu.Game.Tests/Visual/TestCaseOsuGame.cs +++ b/osu.Game.Tests/Visual/TestCaseOsuGame.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; -using osu.Framework.Timing; using osu.Game.Screens; using osu.Game.Screens.Menu; using OpenTK.Graphics; @@ -23,19 +22,15 @@ namespace osu.Game.Tests.Visual public TestCaseOsuGame() { - var rateAdjustClock = new StopwatchClock(true); - var framedClock = new FramedClock(rateAdjustClock); - framedClock.ProcessFrame(); - - Add(new Box + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - }); - - Add(new Loader()); - - AddSliderStep("Playback speed", 0.0, 2.0, 1, v => rateAdjustClock.Rate = v); + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + }, + new Loader() + }; } } } diff --git a/osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs b/osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs index d711d501fe..e4cb848d90 100644 --- a/osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs +++ b/osu.Game.Tests/Visual/TestCasePreviewTrackManager.cs @@ -14,9 +14,9 @@ namespace osu.Game.Tests.Visual { private readonly PreviewTrackManager trackManager = new TestPreviewTrackManager(); - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { - var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); + var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); dependencies.CacheAs(trackManager); dependencies.CacheAs(this); return dependencies; @@ -101,9 +101,9 @@ namespace osu.Game.Tests.Visual AddInternal(track); } - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { - var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); + var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); dependencies.CacheAs(this); return dependencies; } diff --git a/osu.Game.Tests/Visual/TestCaseToolbar.cs b/osu.Game.Tests/Visual/TestCaseToolbar.cs index fd218af054..96f14e6b32 100644 --- a/osu.Game.Tests/Visual/TestCaseToolbar.cs +++ b/osu.Game.Tests/Visual/TestCaseToolbar.cs @@ -16,8 +16,8 @@ namespace osu.Game.Tests.Visual public override IReadOnlyList RequiredTypes => new[] { typeof(ToolbarButton), - typeof(ToolbarModeSelector), - typeof(ToolbarModeButton), + typeof(ToolbarRulesetSelector), + typeof(ToolbarRulesetButton), typeof(ToolbarNotificationButton), }; diff --git a/osu.Game.Tests/Visual/TestCaseUserProfile.cs b/osu.Game.Tests/Visual/TestCaseUserProfile.cs index aca832110a..cb281d045b 100644 --- a/osu.Game.Tests/Visual/TestCaseUserProfile.cs +++ b/osu.Game.Tests/Visual/TestCaseUserProfile.cs @@ -53,7 +53,6 @@ namespace osu.Game.Tests.Visual CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg", JoinDate = DateTimeOffset.Now.AddDays(-1), LastVisit = DateTimeOffset.Now, - Age = 1, ProfileOrder = new[] { "me" }, Statistics = new UserStatistics { diff --git a/osu.Game/Audio/SampleInfo.cs b/osu.Game/Audio/SampleInfo.cs index 7e329ac651..4345d09e05 100644 --- a/osu.Game/Audio/SampleInfo.cs +++ b/osu.Game/Audio/SampleInfo.cs @@ -29,6 +29,11 @@ namespace osu.Game.Audio /// public string Name; + /// + /// An optional suffix to provide priority lookup. Falls back to non-suffixed . + /// + public string Suffix; + /// /// The sample volume. /// @@ -42,9 +47,16 @@ namespace osu.Game.Audio get { if (!string.IsNullOrEmpty(Namespace)) + { + if (!string.IsNullOrEmpty(Suffix)) + yield return $"{Namespace}/{Bank}-{Name}{Suffix}"; yield return $"{Namespace}/{Bank}-{Name}"; + } - yield return $"{Bank}-{Name}"; // Without namespace as a fallback even when we have a namespace + // check non-namespace as a fallback even when we have a namespace + if (!string.IsNullOrEmpty(Suffix)) + yield return $"{Bank}-{Name}{Suffix}"; + yield return $"{Bank}-{Name}"; } } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index e488dacf80..fc4d43080e 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -92,7 +92,7 @@ namespace osu.Game.Beatmaps // by setting the model here, we can update the noline set id below. b.BeatmapSet = model; - fetchAndPopulateOnlineIDs(b); + fetchAndPopulateOnlineIDs(b, model.Beatmaps); } // check if a set already exists with the same online id, delete if it does. @@ -339,6 +339,8 @@ namespace osu.Game.Beatmaps { var beatmapInfos = new List(); + bool invalidateOnlineIDs = false; + foreach (var name in reader.Filenames.Where(f => f.EndsWith(".osu"))) { using (var raw = reader.GetStream(name)) @@ -355,9 +357,18 @@ namespace osu.Game.Beatmaps beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash(); beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash(); - // check that no existing beatmap exists that is imported with the same online beatmap ID. if so, give it precedence. - if (beatmap.BeatmapInfo.OnlineBeatmapID.HasValue && QueryBeatmap(b => b.OnlineBeatmapID.Value == beatmap.BeatmapInfo.OnlineBeatmapID.Value) != null) - beatmap.BeatmapInfo.OnlineBeatmapID = null; + if (beatmap.BeatmapInfo.OnlineBeatmapID.HasValue) + { + var ourId = beatmap.BeatmapInfo.OnlineBeatmapID; + + // check that no existing beatmap in database exists that is imported with the same online beatmap ID. if so, give it precedence. + if (QueryBeatmap(b => b.OnlineBeatmapID.Value == ourId) != null) + beatmap.BeatmapInfo.OnlineBeatmapID = null; + + // check that no other beatmap in this imported set has a conflicting online beatmap ID. If so, presume *all* are incorrect. + if (beatmapInfos.Any(b => b.OnlineBeatmapID == ourId)) + invalidateOnlineIDs = true; + } RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID); @@ -375,6 +386,9 @@ namespace osu.Game.Beatmaps } } + if (invalidateOnlineIDs) + beatmapInfos.ForEach(b => b.OnlineBeatmapID = null); + return beatmapInfos; } @@ -382,9 +396,10 @@ namespace osu.Game.Beatmaps /// Query the API to populate mising OnlineBeatmapID / OnlineBeatmapSetID properties. /// /// The beatmap to populate. + /// The other beatmaps contained within this set. /// Whether to re-query if the provided beatmap already has populated values. /// True if population was successful. - private bool fetchAndPopulateOnlineIDs(BeatmapInfo beatmap, bool force = false) + private bool fetchAndPopulateOnlineIDs(BeatmapInfo beatmap, IEnumerable otherBeatmaps, bool force = false) { if (!force && beatmap.OnlineBeatmapID != null && beatmap.BeatmapSet.OnlineBeatmapSetID != null) return true; @@ -404,6 +419,12 @@ namespace osu.Game.Beatmaps Logger.Log($"Successfully mapped to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.", LoggingTarget.Database); + if (otherBeatmaps.Any(b => b.OnlineBeatmapID == res.OnlineBeatmapID)) + { + Logger.Log("Another beatmap in the same set already mapped to this ID. We'll skip adding it this time.", LoggingTarget.Database); + return false; + } + beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID; beatmap.OnlineBeatmapID = res.OnlineBeatmapID; return true; diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs index 6c1bcd0531..57983ec568 100644 --- a/osu.Game/Beatmaps/BeatmapMetadata.cs +++ b/osu.Game/Beatmaps/BeatmapMetadata.cs @@ -14,7 +14,6 @@ namespace osu.Game.Beatmaps public class BeatmapMetadata : IEquatable { [DatabaseGenerated(DatabaseGeneratedOption.Identity)] - [JsonIgnore] public int ID { get; set; } public string Title { get; set; } diff --git a/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs b/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs index 025ab0037f..7d00c35862 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs @@ -9,14 +9,14 @@ using OpenTK.Graphics; namespace osu.Game.Beatmaps.Drawables { - public class DifficultyColouredContainer : Container, IHasAccentColour + public abstract class DifficultyColouredContainer : Container, IHasAccentColour { public Color4 AccentColour { get; set; } private readonly BeatmapInfo beatmap; private OsuColour palette; - public DifficultyColouredContainer(BeatmapInfo beatmap) + protected DifficultyColouredContainer(BeatmapInfo beatmap) { this.beatmap = beatmap; } diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 42c98aef24..24604711d4 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -3,10 +3,14 @@ using System; using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using OpenTK; +using OpenTK.Graphics; namespace osu.Game.Beatmaps.Drawables { @@ -14,7 +18,8 @@ namespace osu.Game.Beatmaps.Drawables { private readonly BeatmapInfo beatmap; - public DifficultyIcon(BeatmapInfo beatmap) : base(beatmap) + public DifficultyIcon(BeatmapInfo beatmap) + : base(beatmap) { if (beatmap == null) throw new ArgumentNullException(nameof(beatmap)); @@ -28,16 +33,29 @@ namespace osu.Game.Beatmaps.Drawables { Children = new Drawable[] { - new SpriteIcon + new CircularContainer { + RelativeSizeAxes = Axes.Both, + Scale = new Vector2(0.84f), Anchor = Anchor.Centre, Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Colour = AccentColour, - Icon = FontAwesome.fa_circle + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0.08f), + Type = EdgeEffectType.Shadow, + Radius = 5, + }, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = AccentColour, + }, }, new ConstrainedIconContainer { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, // the null coalesce here is only present to make unit tests work (ruleset dlls aren't copied correctly for testing at the moment) Icon = beatmap.Ruleset?.CreateInstance().CreateIcon() ?? new SpriteIcon { Icon = FontAwesome.fa_question_circle_o } diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 22a6acf459..c8874c3bc7 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -179,7 +179,7 @@ namespace osu.Game.Beatmaps.Formats var baseInfo = base.ApplyTo(sampleInfo); if (CustomSampleBank > 1) - baseInfo.Name += CustomSampleBank; + baseInfo.Suffix = CustomSampleBank.ToString(); return baseInfo; } diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index d6bc4a2095..0dc6297ad2 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -22,13 +22,16 @@ namespace osu.Game.Graphics.Containers protected virtual bool PlaySamplesOnStateChange => true; + protected override bool BlockPassThroughKeyboard => true; + private PreviewTrackManager previewTrackManager; + protected readonly Bindable OverlayActivationMode = new Bindable(OverlayActivation.All); - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { - var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); + var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); dependencies.CacheAs(this); return dependencies; } @@ -69,10 +72,13 @@ namespace osu.Game.Graphics.Containers public virtual bool OnPressed(GlobalAction action) { - if (action == GlobalAction.Back) + switch (action) { - State = Visibility.Hidden; - return true; + case GlobalAction.Back: + State = Visibility.Hidden; + return true; + case GlobalAction.Select: + return true; } return false; diff --git a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs index c0e331148d..44156f6e83 100644 --- a/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs +++ b/osu.Game/Graphics/Cursor/OsuTooltipContainer.cs @@ -21,6 +21,8 @@ namespace osu.Game.Graphics.Cursor { } + protected override double AppearDelay => (1 - CurrentTooltip.Alpha) * base.AppearDelay; // reduce appear delay if the tooltip is already partly visible. + public class OsuTooltip : Tooltip { private readonly Box background; diff --git a/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs b/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs index 98ce5def08..1f3c015614 100644 --- a/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs +++ b/osu.Game/Migrations/20180621044111_UpdateTaikoDefaultBindings.cs @@ -8,7 +8,6 @@ namespace osu.Game.Migrations protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.Sql("DELETE FROM KeyBinding WHERE RulesetID = 1"); - Logger.Log("osu!taiko bindings have been reset due to new defaults", LoggingTarget.Runtime, LogLevel.Important); } protected override void Down(MigrationBuilder migrationBuilder) diff --git a/osu.Game/Online/API/Requests/Responses/APIScore.cs b/osu.Game/Online/API/Requests/Responses/APIScore.cs index a398bf46ee..25eb32a79f 100644 --- a/osu.Game/Online/API/Requests/Responses/APIScore.cs +++ b/osu.Game/Online/API/Requests/Responses/APIScore.cs @@ -63,7 +63,14 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"beatmapset")] private BeatmapMetadata metadata { - set => Beatmap.Metadata = value; + set + { + // extract the set ID to its correct place. + Beatmap.BeatmapSet = new BeatmapSetInfo { OnlineBeatmapSetID = value.ID }; + value.ID = 0; + + Beatmap.Metadata = value; + } } [JsonProperty(@"statistics")] diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 922d228701..557b6e4469 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -19,6 +19,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using osu.Framework.Audio; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Platform; @@ -122,8 +123,8 @@ namespace osu.Game private DependencyContainer dependencies; - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => - dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => + dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); [BackgroundDependencyLoader] private void load(FrameworkConfigManager frameworkConfig) @@ -322,24 +323,6 @@ namespace osu.Game dependencies.Cache(notifications); dependencies.Cache(dialogOverlay); - // ensure only one of these overlays are open at once. - var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct }; - overlays.AddRange(singleDisplayOverlays); - - foreach (var overlay in singleDisplayOverlays) - { - overlay.StateChanged += state => - { - if (state == Visibility.Hidden) return; - - foreach (var c in singleDisplayOverlays) - { - if (c == overlay) continue; - c.State = Visibility.Hidden; - } - }; - } - var singleDisplaySideOverlays = new OverlayContainer[] { settings, notifications }; overlays.AddRange(singleDisplaySideOverlays); @@ -348,12 +331,7 @@ namespace osu.Game overlay.StateChanged += state => { if (state == Visibility.Hidden) return; - - foreach (var c in singleDisplaySideOverlays) - { - if (c == overlay) continue; - c.State = Visibility.Hidden; - } + singleDisplaySideOverlays.Where(o => o != overlay).ForEach(o => o.Hide()); }; } @@ -366,12 +344,24 @@ namespace osu.Game overlay.StateChanged += state => { if (state == Visibility.Hidden) return; + informationalOverlays.Where(o => o != overlay).ForEach(o => o.Hide()); + }; + } - foreach (var c in informationalOverlays) - { - if (c == overlay) continue; - c.State = Visibility.Hidden; - } + // ensure only one of these overlays are open at once. + var singleDisplayOverlays = new OverlayContainer[] { chat, social, direct }; + overlays.AddRange(singleDisplayOverlays); + + foreach (var overlay in singleDisplayOverlays) + { + overlay.StateChanged += state => + { + // informational overlays should be dismissed on a show or hide of a full overlay. + informationalOverlays.ForEach(o => o.Hide()); + + if (state == Visibility.Hidden) return; + + singleDisplayOverlays.Where(o => o != overlay).ForEach(o => o.Hide()); }; } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 246229a794..a9b74d6740 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -20,6 +20,7 @@ using osu.Game.Graphics.Cursor; using osu.Game.Online.API; using osu.Framework.Graphics.Performance; using osu.Framework.Graphics.Textures; +using osu.Framework.Input; using osu.Framework.Logging; using osu.Game.Audio; using osu.Game.Database; @@ -30,6 +31,7 @@ using osu.Game.IO; using osu.Game.Rulesets; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; +using OpenTK.Input; using DebugUtils = osu.Game.Utils.DebugUtils; namespace osu.Game @@ -93,11 +95,13 @@ namespace osu.Game private DependencyContainer dependencies; - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => - dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => + dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); private DatabaseContextFactory contextFactory; + protected override UserInputManager CreateUserInputManager() => new OsuUserInputManager(); + [BackgroundDependencyLoader] private void load() { @@ -267,5 +271,31 @@ namespace osu.Game return copy; } } + + private class OsuUserInputManager : UserInputManager + { + protected override MouseButtonEventManager CreateButtonManagerFor(MouseButton button) + { + switch (button) + { + case MouseButton.Right: + return new RightMouseManager(button); + } + + return base.CreateButtonManagerFor(button); + } + + private class RightMouseManager : MouseButtonEventManager + { + public RightMouseManager(MouseButton button) + : base(button) + { + } + + public override bool EnableDrag => true; // allow right-mouse dragging for absolute scroll in scroll containers. + public override bool EnableClick => false; + public override bool ChangeFocusOnClick => false; + } + } } } diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 5d4d627995..d407dc9cf9 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -102,6 +102,7 @@ namespace osu.Game.Overlays.KeyBinding RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Margin = new MarginPadding(padding), + Padding = new MarginPadding { Top = height }, Alpha = 0, Colour = colours.YellowDark } @@ -267,7 +268,7 @@ namespace osu.Game.Overlays.KeyBinding GetContainingInputManager().ChangeFocus(null); pressAKey.FadeOut(300, Easing.OutQuint); - pressAKey.Padding = new MarginPadding { Top = height, Bottom = -pressAKey.DrawHeight }; + pressAKey.BypassAutoSizeAxes |= Axes.Y; } protected override void OnFocus(InputState state) @@ -276,7 +277,7 @@ namespace osu.Game.Overlays.KeyBinding AutoSizeEasing = Easing.OutQuint; pressAKey.FadeIn(300, Easing.OutQuint); - pressAKey.Padding = new MarginPadding { Top = height }; + pressAKey.BypassAutoSizeAxes &= ~Axes.Y; updateBindTarget(); base.OnFocus(state); diff --git a/osu.Game/Overlays/OnScreenDisplay.cs b/osu.Game/Overlays/OnScreenDisplay.cs index 1c80f2e626..753cd33cc6 100644 --- a/osu.Game/Overlays/OnScreenDisplay.cs +++ b/osu.Game/Overlays/OnScreenDisplay.cs @@ -14,6 +14,8 @@ using osu.Game.Graphics; using OpenTK; using OpenTK.Graphics; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics.Transforms; +using osu.Framework.Threading; using osu.Game.Configuration; using osu.Game.Graphics.Sprites; @@ -135,7 +137,7 @@ namespace osu.Game.Overlays /// If is already being tracked from the same . public void BeginTracking(object source, ITrackableConfigManager configManager) { - if (configManager == null) throw new ArgumentNullException(nameof(configManager)); + if (configManager == null) throw new ArgumentNullException(nameof(configManager)); if (trackedConfigManagers.ContainsKey((source, configManager))) throw new InvalidOperationException($"{nameof(configManager)} is already registered."); @@ -159,7 +161,7 @@ namespace osu.Game.Overlays /// If is not being tracked from the same . public void StopTracking(object source, ITrackableConfigManager configManager) { - if (configManager == null) throw new ArgumentNullException(nameof(configManager)); + if (configManager == null) throw new ArgumentNullException(nameof(configManager)); if (!trackedConfigManagers.TryGetValue((source, configManager), out var existing)) throw new InvalidOperationException($"{nameof(configManager)} is not registered."); @@ -181,7 +183,7 @@ namespace osu.Game.Overlays if (string.IsNullOrEmpty(textLine3.Text)) textLine3.Text = "NO KEY BOUND"; - Display(box); + DisplayTemporarily(box); int optionCount = 0; int selectedOption = -1; @@ -213,15 +215,29 @@ namespace osu.Game.Overlays }); } - protected virtual void Display(Drawable toDisplay) + private TransformSequence fadeIn; + private ScheduledDelegate fadeOut; + + protected virtual void DisplayTemporarily(Drawable toDisplay) { - toDisplay.Animate( - b => b.FadeIn(500, Easing.OutQuint), - b => b.ResizeHeightTo(height, 500, Easing.OutQuint) - ).Then( - b => b.FadeOutFromOne(1500, Easing.InQuint), - b => b.ResizeHeightTo(height_contracted, 1500, Easing.InQuint) - ); + // avoid starting a new fade-in if one is already active. + if (fadeIn == null) + { + fadeIn = toDisplay.Animate( + b => b.FadeIn(500, Easing.OutQuint), + b => b.ResizeHeightTo(height, 500, Easing.OutQuint) + ); + + fadeIn.Finally(_ => fadeIn = null); + } + + fadeOut?.Cancel(); + fadeOut = Scheduler.AddDelayed(() => + { + toDisplay.Animate( + b => b.FadeOutFromOne(1500, Easing.InQuint), + b => b.ResizeHeightTo(height_contracted, 1500, Easing.InQuint)); + }, 500); } private class OptionLight : Container diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index c72ff6131b..9d09836d25 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -158,6 +158,13 @@ namespace osu.Game.Overlays.Profile } } }, + new Box // this is a temporary workaround for incorrect masking behaviour of FillMode.Fill used in UserCoverBackground (see https://github.com/ppy/osu-framework/issues/1675) + { + RelativeSizeAxes = Axes.X, + Height = 1, + Y = cover_height, + Colour = OsuColour.Gray(34), + }, infoTextLeft = new LinkFlowContainer(t => t.TextSize = 14) { X = UserProfileOverlay.CONTENT_X_MARGIN, @@ -360,11 +367,6 @@ namespace osu.Game.Overlays.Profile Text = text }; - if (user.Age != null) - { - infoTextLeft.AddText($"{user.Age} years old ", boldItalic); - } - if (user.Country != null) { infoTextLeft.AddText("From ", lightText); diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs index 359bfc7564..1a1f13933d 100644 --- a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs @@ -9,6 +9,7 @@ using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; + namespace osu.Game.Overlays.Profile.Sections { /// @@ -32,7 +33,10 @@ namespace osu.Game.Overlays.Profile.Sections { Action = () => { - if (beatmap.BeatmapSet?.OnlineBeatmapSetID != null) beatmapSetOverlay?.FetchAndShowBeatmapSet(beatmap.BeatmapSet.OnlineBeatmapSetID.Value); + if (beatmap.OnlineBeatmapID != null) + beatmapSetOverlay?.FetchAndShowBeatmap(beatmap.OnlineBeatmapID.Value); + else if (beatmap.BeatmapSet?.OnlineBeatmapSetID != null) + beatmapSetOverlay?.FetchAndShowBeatmapSet(beatmap.BeatmapSet.OnlineBeatmapSetID.Value); }; Child = new FillFlowContainer diff --git a/osu.Game/Overlays/Settings/RulesetSettingsSubsection.cs b/osu.Game/Overlays/Settings/RulesetSettingsSubsection.cs index 05104018cd..60a1c7c125 100644 --- a/osu.Game/Overlays/Settings/RulesetSettingsSubsection.cs +++ b/osu.Game/Overlays/Settings/RulesetSettingsSubsection.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Game.Rulesets; +using osu.Game.Rulesets.Configuration; namespace osu.Game.Overlays.Settings { @@ -14,6 +15,8 @@ namespace osu.Game.Overlays.Settings { private readonly Ruleset ruleset; + protected IRulesetConfigManager Config; + protected RulesetSettingsSubsection(Ruleset ruleset) { this.ruleset = ruleset; @@ -21,13 +24,13 @@ namespace osu.Game.Overlays.Settings private DependencyContainer dependencies; - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { - dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); + dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); - var config = dependencies.Get().GetConfigFor(ruleset); - if (config != null) - dependencies.Cache(config); + Config = dependencies.Get().GetConfigFor(ruleset); + if (Config != null) + dependencies.Cache(Config); return dependencies; } diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 48d0674b3d..eeace2f11c 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -50,7 +50,7 @@ namespace osu.Game.Overlays.Toolbar { Action = () => OnHome?.Invoke() }, - new ToolbarModeSelector() + new ToolbarRulesetSelector() } }, new FillFlowContainer diff --git a/osu.Game/Overlays/Toolbar/ToolbarModeButton.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs similarity index 96% rename from osu.Game/Overlays/Toolbar/ToolbarModeButton.cs rename to osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs index 90b9abb2e4..bbdf796e7a 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarModeButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetButton.cs @@ -7,7 +7,7 @@ using OpenTK.Graphics; namespace osu.Game.Overlays.Toolbar { - public class ToolbarModeButton : ToolbarButton + public class ToolbarRulesetButton : ToolbarButton { private RulesetInfo ruleset; public RulesetInfo Ruleset diff --git a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs similarity index 93% rename from osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs rename to osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs index dae4f84b1a..b1af3f0d62 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs @@ -16,18 +16,18 @@ using osu.Game.Rulesets; namespace osu.Game.Overlays.Toolbar { - public class ToolbarModeSelector : Container + public class ToolbarRulesetSelector : Container { private const float padding = 10; private readonly FillFlowContainer modeButtons; private readonly Drawable modeButtonLine; - private ToolbarModeButton activeButton; + private ToolbarRulesetButton activeButton; private RulesetStore rulesets; private readonly Bindable ruleset = new Bindable(); - public ToolbarModeSelector() + public ToolbarRulesetSelector() { RelativeSizeAxes = Axes.Y; @@ -73,7 +73,7 @@ namespace osu.Game.Overlays.Toolbar this.rulesets = rulesets; foreach (var r in rulesets.AvailableRulesets) { - modeButtons.Add(new ToolbarModeButton + modeButtons.Add(new ToolbarRulesetButton { Ruleset = r, Action = delegate { ruleset.Value = r; } @@ -115,7 +115,7 @@ namespace osu.Game.Overlays.Toolbar private void rulesetChanged(RulesetInfo ruleset) { - foreach (ToolbarModeButton m in modeButtons.Children.Cast()) + foreach (ToolbarRulesetButton m in modeButtons.Children.Cast()) { bool isActive = m.Ruleset.ID == ruleset.ID; m.Active = isActive; diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index fedb6abed3..0bfde148e7 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -70,7 +70,8 @@ namespace osu.Game.Rulesets.UI protected readonly Ruleset Ruleset; - private IRulesetConfigManager rulesetConfig; + protected IRulesetConfigManager Config { get; private set; } + private OnScreenDisplay onScreenDisplay; /// @@ -85,17 +86,17 @@ namespace osu.Game.Rulesets.UI Cursor = CreateCursor(); } - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { - var dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); + var dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); onScreenDisplay = dependencies.Get(); - rulesetConfig = dependencies.Get().GetConfigFor(Ruleset); - if (rulesetConfig != null) + Config = dependencies.Get().GetConfigFor(Ruleset); + if (Config != null) { - dependencies.Cache(rulesetConfig); - onScreenDisplay?.BeginTracking(this, rulesetConfig); + dependencies.Cache(Config); + onScreenDisplay?.BeginTracking(this, Config); } return dependencies; @@ -143,10 +144,10 @@ namespace osu.Game.Rulesets.UI { base.Dispose(isDisposing); - if (rulesetConfig != null) + if (Config != null) { - onScreenDisplay?.StopTracking(this, rulesetConfig); - rulesetConfig = null; + onScreenDisplay?.StopTracking(this, Config); + Config = null; } } } diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 8046e9f9b4..a3120179a4 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -222,23 +222,16 @@ namespace osu.Game.Rulesets.UI mouseDisabled = config.GetBindable(OsuSetting.MouseDisableButtons); } - protected override void TransformState(InputState state) + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) { - base.TransformState(state); + if (mouseDisabled.Value && (args.Button == MouseButton.Left || args.Button == MouseButton.Right)) return false; + return base.OnMouseDown(state, args); + } - // we don't want to transform the state if a replay is present (for now, at least). - if (replayInputHandler != null) return; - - var mouse = state.Mouse as Framework.Input.MouseState; - - if (mouse != null) - { - if (mouseDisabled.Value) - { - mouse.SetPressed(MouseButton.Left, false); - mouse.SetPressed(MouseButton.Right, false); - } - } + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + if (!CurrentState.Mouse.IsPressed(args.Button)) return false; + return base.OnMouseUp(state, args); } #endregion diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 7eeabd3e5e..a52fa3b462 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -42,8 +42,8 @@ namespace osu.Game.Screens.Edit private DependencyContainer dependencies; private GameHost host; - protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) - => dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); + protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) + => dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); [BackgroundDependencyLoader] private void load(OsuColour colours, GameHost host) diff --git a/osu.Game/Screens/Menu/Button.cs b/osu.Game/Screens/Menu/Button.cs index 542ddd2c92..29820f234d 100644 --- a/osu.Game/Screens/Menu/Button.cs +++ b/osu.Game/Screens/Menu/Button.cs @@ -35,6 +35,12 @@ namespace osu.Game.Screens.Menu private readonly Box boxHoverLayer; private readonly SpriteIcon icon; private readonly string sampleName; + + /// + /// The menu state for which we are visible for. + /// + public ButtonSystemState VisibleState = ButtonSystemState.TopLevel; + private readonly Action clickAction; private readonly Key triggerKey; private SampleChannel sampleClick; @@ -51,7 +57,7 @@ namespace osu.Game.Screens.Menu AutoSizeAxes = Axes.Both; Alpha = 0; - Vector2 boxSize = new Vector2(ButtonSystem.BUTTON_WIDTH + Math.Abs(extraWidth), ButtonSystem.BUTTON_AREA_HEIGHT); + Vector2 boxSize = new Vector2(ButtonSystem.BUTTON_WIDTH + Math.Abs(extraWidth), ButtonArea.BUTTON_AREA_HEIGHT); Children = new Drawable[] { @@ -260,6 +266,7 @@ namespace osu.Game.Screens.Menu this.FadeOut(800); break; } + break; case ButtonState.Expanded: const int expand_duration = 500; @@ -276,6 +283,33 @@ namespace osu.Game.Screens.Menu StateChanged?.Invoke(State); } } + + public ButtonSystemState ButtonSystemState + { + set + { + ContractStyle = 0; + + switch (value) + { + case ButtonSystemState.Initial: + State = ButtonState.Contracted; + break; + case ButtonSystemState.EnteringMode: + ContractStyle = 1; + State = ButtonState.Contracted; + break; + default: + if (value == VisibleState) + State = ButtonState.Expanded; + else if (value < VisibleState) + State = ButtonState.Contracted; + else + State = ButtonState.Exploded; + break; + } + } + } } public enum ButtonState diff --git a/osu.Game/Screens/Menu/ButtonArea.cs b/osu.Game/Screens/Menu/ButtonArea.cs new file mode 100644 index 0000000000..06004405b6 --- /dev/null +++ b/osu.Game/Screens/Menu/ButtonArea.cs @@ -0,0 +1,148 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using OpenTK; + +namespace osu.Game.Screens.Menu +{ + public class ButtonArea : Container, IStateful + { + public FlowContainerWithOrigin Flow; + + protected override Container Content => Flow; + + private readonly ButtonAreaBackground buttonAreaBackground; + private Visibility state; + + public const float BUTTON_AREA_HEIGHT = 100; + + public ButtonArea() + { + RelativeSizeAxes = Axes.Both; + InternalChild = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.X, + Size = new Vector2(1, BUTTON_AREA_HEIGHT), + Alpha = 0, + Children = new Drawable[] + { + buttonAreaBackground = new ButtonAreaBackground(), + Flow = new FlowContainerWithOrigin + { + Direction = FillDirection.Horizontal, + Spacing = new Vector2(-ButtonSystem.WEDGE_WIDTH, 0), + Anchor = Anchor.Centre, + AutoSizeAxes = Axes.Both, + } + } + }; + } + + public ButtonSystemState ButtonSystemState + { + set + { + switch (value) + { + case ButtonSystemState.Exit: + case ButtonSystemState.Initial: + case ButtonSystemState.EnteringMode: + State = Visibility.Hidden; + break; + case ButtonSystemState.TopLevel: + case ButtonSystemState.Play: + State = Visibility.Visible; + break; + } + + buttonAreaBackground.ButtonSystemState = value; + } + } + + public Visibility State + { + get => state; + set + { + if (value == state) return; + + state = value; + InternalChild.FadeTo(state == Visibility.Hidden ? 0 : 1, 300); + StateChanged?.Invoke(state); + } + } + + public event Action StateChanged; + + private class ButtonAreaBackground : Box, IStateful + { + private ButtonAreaBackgroundState state; + + public ButtonAreaBackground() + { + RelativeSizeAxes = Axes.Both; + Size = new Vector2(2, 1); + Colour = OsuColour.Gray(50); + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + } + + public ButtonAreaBackgroundState State + { + get => state; + set + { + if (value == state) return; + + state = value; + + switch (state) + { + case ButtonAreaBackgroundState.Flat: + this.ScaleTo(new Vector2(2, 0), 300, Easing.InSine); + break; + case ButtonAreaBackgroundState.Normal: + this.ScaleTo(Vector2.One, 400, Easing.OutQuint); + break; + } + + StateChanged?.Invoke(state); + } + } + + public ButtonSystemState ButtonSystemState + { + set + { + switch (value) + { + default: + State = ButtonAreaBackgroundState.Normal; + break; + case ButtonSystemState.Initial: + case ButtonSystemState.Exit: + case ButtonSystemState.EnteringMode: + State = ButtonAreaBackgroundState.Flat; + break; + } + } + } + + public event Action StateChanged; + } + + public enum ButtonAreaBackgroundState + { + Normal, + Flat + } + } +} diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 762f826425..ce00686c02 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -10,8 +10,8 @@ using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Bindings; +using osu.Framework.Logging; using osu.Framework.Threading; using osu.Game.Graphics; using osu.Game.Input.Bindings; @@ -22,9 +22,9 @@ using OpenTK.Input; namespace osu.Game.Screens.Menu { - public class ButtonSystem : Container, IStateful, IKeyBindingHandler + public class ButtonSystem : Container, IStateful, IKeyBindingHandler { - public event Action StateChanged; + public event Action StateChanged; public Action OnEdit; public Action OnExit; @@ -33,12 +33,6 @@ namespace osu.Game.Screens.Menu public Action OnSettings; public Action OnMulti; public Action OnChart; - public Action OnTest; - - private readonly FlowContainerWithOrigin buttonFlow; - - //todo: make these non-internal somehow. - public const float BUTTON_AREA_HEIGHT = 100; public const float BUTTON_WIDTH = 140f; public const float WEDGE_WIDTH = 20; @@ -54,18 +48,16 @@ namespace osu.Game.Screens.Menu this.logo.Action = onOsuLogo; // osuLogo.SizeForFlow relies on loading to be complete. - buttonFlow.Position = new Vector2(WEDGE_WIDTH * 2 - (BUTTON_WIDTH + this.logo.SizeForFlow / 4), 0); + buttonArea.Flow.Position = new Vector2(WEDGE_WIDTH * 2 - (BUTTON_WIDTH + this.logo.SizeForFlow / 4), 0); updateLogoState(); } } private readonly Drawable iconFacade; - private readonly Container buttonArea; - private readonly Box buttonAreaBackground; + private readonly ButtonArea buttonArea; private readonly Button backButton; - private readonly Button settingsButton; private readonly List