diff --git a/.gitignore b/.gitignore index 6186cb870d..0e2850a01c 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ tools/** build/tools/** +fastlane/report.xml # Build results bin/[Dd]ebug/ diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000000..cdd3a6b349 --- /dev/null +++ b/Gemfile @@ -0,0 +1,6 @@ +source "https://rubygems.org" + +gem "fastlane" + +plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') +eval_gemfile(plugins_path) if File.exist?(plugins_path) diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000000..17c0db12e7 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,173 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.0) + addressable (2.6.0) + public_suffix (>= 2.0.2, < 4.0) + atomos (0.1.3) + babosa (1.0.2) + claide (1.0.2) + colored (1.2) + colored2 (3.1.2) + commander-fastlane (4.4.6) + highline (~> 1.7.2) + declarative (0.0.10) + declarative-option (0.1.0) + digest-crc (0.4.1) + domain_name (0.5.20180417) + unf (>= 0.0.5, < 1.0.0) + dotenv (2.7.1) + emoji_regex (1.0.1) + excon (0.62.0) + faraday (0.15.4) + multipart-post (>= 1.2, < 3) + faraday-cookie_jar (0.0.6) + faraday (>= 0.7.4) + http-cookie (~> 1.0.0) + faraday_middleware (0.13.1) + faraday (>= 0.7.4, < 1.0) + fastimage (2.1.5) + fastlane (2.117.0) + CFPropertyList (>= 2.3, < 4.0.0) + addressable (>= 2.3, < 3.0.0) + babosa (>= 1.0.2, < 2.0.0) + bundler (>= 1.12.0, < 3.0.0) + colored + commander-fastlane (>= 4.4.6, < 5.0.0) + dotenv (>= 2.1.1, < 3.0.0) + emoji_regex (>= 0.1, < 2.0) + excon (>= 0.45.0, < 1.0.0) + faraday (~> 0.9) + faraday-cookie_jar (~> 0.0.6) + faraday_middleware (~> 0.9) + fastimage (>= 2.1.0, < 3.0.0) + gh_inspector (>= 1.1.2, < 2.0.0) + google-api-client (>= 0.21.2, < 0.24.0) + google-cloud-storage (>= 1.15.0, < 2.0.0) + highline (>= 1.7.2, < 2.0.0) + json (< 3.0.0) + mini_magick (~> 4.5.1) + multi_json + multi_xml (~> 0.5) + multipart-post (~> 2.0.0) + plist (>= 3.1.0, < 4.0.0) + public_suffix (~> 2.0.0) + rubyzip (>= 1.2.2, < 2.0.0) + security (= 0.1.3) + simctl (~> 1.6.3) + slack-notifier (>= 2.0.0, < 3.0.0) + terminal-notifier (>= 1.6.2, < 2.0.0) + terminal-table (>= 1.4.5, < 2.0.0) + tty-screen (>= 0.6.3, < 1.0.0) + tty-spinner (>= 0.8.0, < 1.0.0) + word_wrap (~> 1.0.0) + xcodeproj (>= 1.6.0, < 2.0.0) + xcpretty (~> 0.3.0) + xcpretty-travis-formatter (>= 0.0.3) + fastlane-plugin-clean_testflight_testers (0.2.0) + fastlane-plugin-souyuz (0.8.1) + souyuz (>= 0.8.1) + fastlane-plugin-xamarin (0.6.3) + gh_inspector (1.1.3) + google-api-client (0.23.9) + addressable (~> 2.5, >= 2.5.1) + googleauth (>= 0.5, < 0.7.0) + httpclient (>= 2.8.1, < 3.0) + mime-types (~> 3.0) + representable (~> 3.0) + retriable (>= 2.0, < 4.0) + signet (~> 0.9) + google-cloud-core (1.3.0) + google-cloud-env (~> 1.0) + google-cloud-env (1.0.5) + faraday (~> 0.11) + google-cloud-storage (1.16.0) + digest-crc (~> 0.4) + google-api-client (~> 0.23) + google-cloud-core (~> 1.2) + googleauth (>= 0.6.2, < 0.10.0) + googleauth (0.6.7) + faraday (~> 0.12) + jwt (>= 1.4, < 3.0) + memoist (~> 0.16) + multi_json (~> 1.11) + os (>= 0.9, < 2.0) + signet (~> 0.7) + highline (1.7.10) + http-cookie (1.0.3) + domain_name (~> 0.5) + httpclient (2.8.3) + json (2.2.0) + jwt (2.1.0) + memoist (0.16.0) + mime-types (3.2.2) + mime-types-data (~> 3.2015) + mime-types-data (3.2018.0812) + mini_magick (4.5.1) + mini_portile2 (2.4.0) + multi_json (1.13.1) + multi_xml (0.6.0) + multipart-post (2.0.0) + nanaimo (0.2.6) + naturally (2.2.0) + nokogiri (1.10.1) + mini_portile2 (~> 2.4.0) + os (1.0.0) + plist (3.5.0) + public_suffix (2.0.5) + representable (3.0.4) + declarative (< 0.1.0) + declarative-option (< 0.2.0) + uber (< 0.2.0) + retriable (3.1.2) + rouge (2.0.7) + rubyzip (1.2.2) + security (0.1.3) + signet (0.11.0) + addressable (~> 2.3) + faraday (~> 0.9) + jwt (>= 1.5, < 3.0) + multi_json (~> 1.10) + simctl (1.6.5) + CFPropertyList + naturally + slack-notifier (2.3.2) + souyuz (0.8.1) + fastlane (>= 2.29.0) + highline (~> 1.7) + nokogiri (~> 1.7) + terminal-notifier (1.8.0) + terminal-table (1.8.0) + unicode-display_width (~> 1.1, >= 1.1.1) + tty-cursor (0.6.1) + tty-screen (0.6.5) + tty-spinner (0.9.0) + tty-cursor (~> 0.6.0) + uber (0.1.0) + unf (0.1.4) + unf_ext + unf_ext (0.0.7.5) + unicode-display_width (1.4.1) + word_wrap (1.0.0) + xcodeproj (1.8.1) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.2.6) + xcpretty (0.3.0) + rouge (~> 2.0.7) + xcpretty-travis-formatter (1.0.0) + xcpretty (~> 0.2, >= 0.0.7) + +PLATFORMS + ruby + +DEPENDENCIES + fastlane + fastlane-plugin-clean_testflight_testers + fastlane-plugin-souyuz + fastlane-plugin-xamarin + +BUNDLED WITH + 2.0.1 diff --git a/README.md b/README.md index 8cfc2ffebf..abddb1faa1 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,24 @@ -# osu! [![Build status](https://ci.appveyor.com/api/projects/status/u2p01nx7l6og8buh?svg=true)](https://ci.appveyor.com/project/peppy/osu) [![CodeFactor](https://www.codefactor.io/repository/github/ppy/osu/badge)](https://www.codefactor.io/repository/github/ppy/osu) [![dev chat](https://discordapp.com/api/guilds/188630481301012481/widget.png?style=shield)](https://discord.gg/ppy) +# osu! + +[![Build status](https://ci.appveyor.com/api/projects/status/u2p01nx7l6og8buh?svg=true)](https://ci.appveyor.com/project/peppy/osu) [![CodeFactor](https://www.codefactor.io/repository/github/ppy/osu/badge)](https://www.codefactor.io/repository/github/ppy/osu) [![dev chat](https://discordapp.com/api/guilds/188630481301012481/widget.png?style=shield)](https://discord.gg/ppy) Rhythm is just a *click* away. The future of [osu!](https://osu.ppy.sh) and the beginning of an open era! Commonly known by the codename "osu!lazer". Pew pew. -# Status +## Status This project is still heavily under development, but is in a state where users are encouraged to try it out and keep it installed alongside the stable osu! client. It will continue to evolve over the coming months and hopefully bring some new unique features to the table. We are accepting bug reports (please report with as much detail as possible). Feature requests are welcome as long as you read and understand the contribution guidelines listed below. -# Requirements +## Requirements - A desktop platform with the [.NET Core SDK 2.2](https://www.microsoft.com/net/learn/get-started) or higher installed. - When working with the codebase, we recommend using an IDE with intellisense and syntax highlighting, such as [Visual Studio 2017+](https://visualstudio.microsoft.com/vs/), [Jetbrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/). - Note that there are **[additional requirements for Windows 7 and Windows 8.1](https://docs.microsoft.com/en-us/dotnet/core/windows-prerequisites?tabs=netcore2x)** which you may need to manually install if your operating system is not up-to-date. -# Running osu! +## Running osu! -## Releases +### Releases If you are not interested in developing the game, please head over to the [releases](https://github.com/ppy/osu/releases) to download a precompiled build with automatic updating enabled. @@ -26,7 +28,7 @@ If you are not interested in developing the game, please head over to the [relea If your platform is not listed above, there is still a chance you can manually build it by following the instructions below. -## Downloading the source code +### Downloading the source code Clone the repository **including submodules**: @@ -41,7 +43,7 @@ To update the source code to the latest commit, run the following command inside git pull ``` -## Building +### Building Build configurations for the recommended IDEs (listed above) are included. You should use the provided Build/Run functionality of your IDE to get things going. When testing or building new components, it's highly encouraged you use the `VisualTests` project/configuration. More information on this provided below. @@ -57,7 +59,7 @@ If you are not interested in debugging osu!, you can add `-c Release` to gain pe If the build fails, try to restore nuget packages with `dotnet restore`. -### A note for Linux users +#### A note for Linux users On Linux, the environment variable `LD_LIBRARY_PATH` must point to the build directory, located at `osu.Desktop/bin/Debug/$NETCORE_VERSION`. @@ -69,15 +71,15 @@ For example, you can run osu! with the following command: LD_LIBRARY_PATH="$(pwd)/osu.Desktop/bin/Debug/netcoreapp2.2" dotnet run --project osu.Desktop ``` -## Testing with resource/framework modifications +### Testing with resource/framework modifications Sometimes it may be necessary to cross-test changes in [osu-resources](https://github.com/ppy/osu-resources) or [osu-framework](https://github.com/ppy/osu-framework). This can be achieved by running some commands as documented on the [osu-resources](https://github.com/ppy/osu-resources/wiki/Testing-local-resources-checkout-with-other-projects) and [osu-framework](https://github.com/ppy/osu-framework/wiki/Testing-local-framework-checkout-with-other-projects) wiki pages. -## Code analysis +### Code analysis Code analysis can be run with `powershell ./build.ps1` or `build.sh`. This is currently only supported under windows due to [resharper cli shortcomings](https://youtrack.jetbrains.com/issue/RSRP-410004). Alternatively, you can install resharper or use rider to get inline support in your IDE of choice. -# Contributing +## Contributing We welcome all contributions, but keep in mind that we already have a lot of the UI designed. If you wish to work on something with the intention on having it included in the official distribution, please open an issue for discussion and we will give you what you need from a design perspective to proceed. If you want to make *changes* to the design, we recommend you open an issue with your intentions before spending too much time, to ensure no effort is wasted. @@ -87,7 +89,7 @@ Contributions can be made via pull requests to this repository. We hope to credi Note that while we already have certain standards in place, nothing is set in stone. If you have an issue with the way code is structured; with any libraries we are using; with any processes involved with contributing, *please* bring it up. I welcome all feedback so we can make contributing to this project as pain-free as possible. -# Licence +## Licence The osu! client code and framework are licensed under the [MIT licence](https://opensource.org/licenses/MIT). Please see [the licence file](LICENCE) for more information. [tl;dr](https://tldrlegal.com/license/mit-license) you can do whatever you want as long as you include the original copyright and license notice in any copy of the software/source. diff --git a/fastlane/Appfile b/fastlane/Appfile new file mode 100644 index 0000000000..083de66985 --- /dev/null +++ b/fastlane/Appfile @@ -0,0 +1,2 @@ +app_identifier("sh.ppy.osulazer") # The bundle identifier of your app +apple_id("apple-dev@ppy.sh") # Your Apple email address diff --git a/fastlane/Fastfile b/fastlane/Fastfile new file mode 100644 index 0000000000..3f64bcdf19 --- /dev/null +++ b/fastlane/Fastfile @@ -0,0 +1,65 @@ +update_fastlane + +default_platform(:ios) + +platform :ios do + lane :testflight_prune_dry do + clean_testflight_testers(days_of_inactivity:45, dry_run: true) + end + + # Specify a custom number for what's "inactive" + lane :testflight_prune do + clean_testflight_testers(days_of_inactivity: 45) # 120 days, so about 4 months + end + + lane :update_version do |options| + options[:plist_path] = '../osu.iOS/Info.plist' + app_version(options) + end + + desc 'Deploy to testflight' + lane :beta do |options| + update_version(options) + + provision( + type: 'appstore' + ) + + build( + build_configuration: 'Release', + build_platform: 'iPhone' + ) + + client = HTTPClient.new + changelog = client.get_content 'https://gist.githubusercontent.com/peppy/ab89c29dcc0dce95f39eb218e8fad197/raw' + changelog.gsub!('$BUILD_ID', options[:build]) + + pilot( + wait_processing_interval: 900, + changelog: changelog, + ipa: './osu.iOS/bin/iPhone/Release/osu.iOS.ipa' + ) + end + + desc 'Compile the project' + lane :build do + nuget_restore( + project_path: 'osu.iOS.sln' + ) + + souyuz( + platform: "ios", + build_target: "osu_iOS", + plist_path: "../osu.iOS/Info.plist" + ) + end + + desc 'Install provisioning profiles using match' + lane :provision do |options| + if Helper.is_ci? + options[:readonly] = true + end + + match(options) + end +end diff --git a/fastlane/Matchfile b/fastlane/Matchfile new file mode 100644 index 0000000000..40c974b09e --- /dev/null +++ b/fastlane/Matchfile @@ -0,0 +1 @@ +git_url('https://github.com/peppy/apple-certificates') diff --git a/fastlane/Pluginfile b/fastlane/Pluginfile new file mode 100644 index 0000000000..9f4f47f213 --- /dev/null +++ b/fastlane/Pluginfile @@ -0,0 +1,7 @@ +# Autogenerated by fastlane +# +# Ensure this file is checked in to source control! + +gem 'fastlane-plugin-clean_testflight_testers' +gem 'fastlane-plugin-souyuz' +gem 'fastlane-plugin-xamarin' diff --git a/fastlane/README.md b/fastlane/README.md new file mode 100644 index 0000000000..53bbc62cae --- /dev/null +++ b/fastlane/README.md @@ -0,0 +1,54 @@ +fastlane documentation +================ +# Installation + +Make sure you have the latest version of the Xcode command line tools installed: + +``` +xcode-select --install +``` + +Install _fastlane_ using +``` +[sudo] gem install fastlane -NV +``` +or alternatively using `brew cask install fastlane` + +# Available Actions +## iOS +### ios testflight_prune_dry +``` +fastlane ios testflight_prune_dry +``` + +### ios testflight_prune +``` +fastlane ios testflight_prune +``` + +### ios update_version +``` +fastlane ios update_version +``` + +### ios beta +``` +fastlane ios beta +``` +Deploy to testflight +### ios build +``` +fastlane ios build +``` +Compile the project +### ios provision +``` +fastlane ios provision +``` +Install provisioning profiles using match + +---- + +This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. +More information about fastlane can be found on [fastlane.tools](https://fastlane.tools). +The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools). diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseHyperDash.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseHyperDash.cs index 0851fbed87..7451986a8b 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestCaseHyperDash.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestCaseHyperDash.cs @@ -1,9 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using NUnit.Framework; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Screens.Play; namespace osu.Game.Rulesets.Catch.Tests { @@ -17,13 +19,30 @@ namespace osu.Game.Rulesets.Catch.Tests protected override IBeatmap CreateBeatmap(Ruleset ruleset) { - var beatmap = new Beatmap { BeatmapInfo = { Ruleset = ruleset.RulesetInfo } }; + var beatmap = new Beatmap + { + BeatmapInfo = + { + Ruleset = ruleset.RulesetInfo, + BaseDifficulty = new BeatmapDifficulty { CircleSize = 3.6f } + } + }; + + // Should produce a hperdash + beatmap.HitObjects.Add(new Fruit { StartTime = 816, X = 308 / 512f, NewCombo = true }); + beatmap.HitObjects.Add(new Fruit { StartTime = 1008, X = 56 / 512f, }); for (int i = 0; i < 512; i++) if (i % 5 < 3) - beatmap.HitObjects.Add(new Fruit { X = i % 10 < 5 ? 0.02f : 0.98f, StartTime = i * 100, NewCombo = i % 8 == 0 }); + beatmap.HitObjects.Add(new Fruit { X = i % 10 < 5 ? 0.02f : 0.98f, StartTime = 2000 + i * 100, NewCombo = i % 8 == 0 }); return beatmap; } + + protected override void AddCheckSteps(Func player) + { + base.AddCheckSteps(player); + AddAssert("First note is hyperdash", () => Beatmap.Value.Beatmap.HitObjects[0] is Fruit f && f.HyperDash); + } } } diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 5f1e0b97da..78b5a510b2 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps CatchHitObject nextObject = objectWithDroplets[i + 1]; int thisDirection = nextObject.X > currentObject.X ? 1 : -1; - double timeToNext = nextObject.StartTime - currentObject.StartTime; + double timeToNext = nextObject.StartTime - currentObject.StartTime - 1000f / 60f / 4; // 1/4th of a frame of grace time, taken from osu-stable double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth); float distanceToHyper = (float)(timeToNext * CatcherArea.Catcher.BASE_SPEED - distanceToNext); if (distanceToHyper < 0) diff --git a/osu.Game.Rulesets.Mania.Tests/TestCaseEditor.cs b/osu.Game.Rulesets.Mania.Tests/TestCaseEditor.cs index 78e7768788..e721eb6fd9 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestCaseEditor.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestCaseEditor.cs @@ -25,8 +25,8 @@ namespace osu.Game.Rulesets.Mania.Tests [BackgroundDependencyLoader] private void load(RulesetConfigCache configCache) { - var config = (ManiaConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance()); - config.BindWith(ManiaSetting.ScrollDirection, direction); + var config = (ManiaRulesetConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance()); + config.BindWith(ManiaRulesetSetting.ScrollDirection, direction); } } } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index daefdf1128..71df68c087 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -27,6 +27,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps protected override IEnumerable ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) }; public int TargetColumns; + public bool Dual; public readonly bool IsForCurrentRuleset; // Internal for testing purposes @@ -45,7 +46,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps var roundedOverallDifficulty = Math.Round(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty); if (IsForCurrentRuleset) + { TargetColumns = (int)Math.Max(1, roundedCircleSize); + if (TargetColumns >= 10) + { + TargetColumns = TargetColumns / 2; + Dual = true; + } + } else { float percentSliderOrSpinner = (float)beatmap.HitObjects.Count(h => h is IHasEndTime) / beatmap.HitObjects.Count; @@ -70,7 +78,15 @@ namespace osu.Game.Rulesets.Mania.Beatmaps return base.ConvertBeatmap(original); } - protected override Beatmap CreateBeatmap() => beatmap = new ManiaBeatmap(new StageDefinition { Columns = TargetColumns }); + protected override Beatmap CreateBeatmap() + { + beatmap = new ManiaBeatmap(new StageDefinition { Columns = TargetColumns }); + + if (Dual) + beatmap.Stages.Add(new StageDefinition { Columns = TargetColumns }); + + return beatmap; + } protected override IEnumerable ConvertHitObject(HitObject original, IBeatmap beatmap) { diff --git a/osu.Game.Rulesets.Mania/Configuration/ManiaConfigManager.cs b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs similarity index 57% rename from osu.Game.Rulesets.Mania/Configuration/ManiaConfigManager.cs rename to osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs index 4e0ad31105..b591f9da22 100644 --- a/osu.Game.Rulesets.Mania/Configuration/ManiaConfigManager.cs +++ b/osu.Game.Rulesets.Mania/Configuration/ManiaRulesetConfigManager.cs @@ -8,9 +8,9 @@ using osu.Game.Rulesets.Mania.UI; namespace osu.Game.Rulesets.Mania.Configuration { - public class ManiaConfigManager : RulesetConfigManager + public class ManiaRulesetConfigManager : RulesetConfigManager { - public ManiaConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null) + public ManiaRulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null) : base(settings, ruleset, variant) { } @@ -19,17 +19,17 @@ namespace osu.Game.Rulesets.Mania.Configuration { base.InitialiseDefaults(); - Set(ManiaSetting.ScrollTime, 2250.0, 50.0, 10000.0, 50.0); - Set(ManiaSetting.ScrollDirection, ManiaScrollingDirection.Down); + Set(ManiaRulesetSetting.ScrollTime, 2250.0, 50.0, 10000.0, 50.0); + Set(ManiaRulesetSetting.ScrollDirection, ManiaScrollingDirection.Down); } public override TrackedSettings CreateTrackedSettings() => new TrackedSettings { - new TrackedSetting(ManiaSetting.ScrollTime, v => new SettingDescription(v, "Scroll Time", $"{v}ms")) + new TrackedSetting(ManiaRulesetSetting.ScrollTime, v => new SettingDescription(v, "Scroll Time", $"{v}ms")) }; } - public enum ManiaSetting + public enum ManiaRulesetSetting { ScrollTime, ScrollDirection diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 525bacee65..b40093844b 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -162,7 +162,7 @@ namespace osu.Game.Rulesets.Mania public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new ManiaReplayFrame(); - public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new ManiaConfigManager(settings, RulesetInfo); + public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new ManiaRulesetConfigManager(settings, RulesetInfo); public override RulesetSettingsSubsection CreateSettings() => new ManiaSettingsSubsection(this); diff --git a/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs b/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs index 2ab40b2bc6..2ebfd0cfc1 100644 --- a/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs +++ b/osu.Game.Rulesets.Mania/ManiaSettingsSubsection.cs @@ -22,19 +22,19 @@ namespace osu.Game.Rulesets.Mania [BackgroundDependencyLoader] private void load() { - var config = (ManiaConfigManager)Config; + var config = (ManiaRulesetConfigManager)Config; Children = new Drawable[] { new SettingsEnumDropdown { LabelText = "Scrolling direction", - Bindable = config.GetBindable(ManiaSetting.ScrollDirection) + Bindable = config.GetBindable(ManiaRulesetSetting.ScrollDirection) }, new SettingsSlider { LabelText = "Scroll speed", - Bindable = config.GetBindable(ManiaSetting.ScrollTime) + Bindable = config.GetBindable(ManiaRulesetSetting.ScrollTime) }, }; } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs index 7b2ee9a632..c78bf72979 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs @@ -1,15 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps; -using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Mania.Mods { - public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter, IApplicableToBeatmap + public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter { public override string Name => "Dual Stages"; public override string Acronym => "DS"; @@ -29,24 +27,7 @@ namespace osu.Game.Rulesets.Mania.Mods if (isForCurrentRuleset) return; - mbc.TargetColumns *= 2; - } - - public void ApplyToBeatmap(Beatmap beatmap) - { - if (isForCurrentRuleset) - return; - - var maniaBeatmap = (ManiaBeatmap)beatmap; - - var newDefinitions = new List(); - foreach (var existing in maniaBeatmap.Stages) - { - newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 }); - newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 }); - } - - maniaBeatmap.Stages = newDefinitions; + mbc.Dual = true; } public PlayfieldType PlayfieldType => PlayfieldType.Dual; diff --git a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs index b451e28088..d8b7dc0381 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs @@ -15,7 +15,6 @@ using osu.Game.Input.Handlers; using osu.Game.Replays; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Configuration; -using osu.Game.Rulesets.Mania.Mods; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Replays; @@ -35,7 +34,7 @@ namespace osu.Game.Rulesets.Mania.UI public IEnumerable BarLines; - protected new ManiaConfigManager Config => (ManiaConfigManager)base.Config; + protected new ManiaRulesetConfigManager Config => (ManiaRulesetConfigManager)base.Config; private readonly Bindable configDirection = new Bindable(); @@ -75,10 +74,10 @@ namespace osu.Game.Rulesets.Mania.UI { BarLines.ForEach(Playfield.Add); - Config.BindWith(ManiaSetting.ScrollDirection, configDirection); + Config.BindWith(ManiaRulesetSetting.ScrollDirection, configDirection); configDirection.BindValueChanged(direction => Direction.Value = (ScrollingDirection)direction.NewValue, true); - Config.BindWith(ManiaSetting.ScrollTime, TimeRange); + Config.BindWith(ManiaRulesetSetting.ScrollTime, TimeRange); } /// @@ -96,7 +95,7 @@ namespace osu.Game.Rulesets.Mania.UI public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this); - public override int Variant => (int)(Mods.OfType().FirstOrDefault()?.PlayfieldType ?? PlayfieldType.Single) + Beatmap.TotalColumns; + public override int Variant => (int)(Beatmap.Stages.Count == 1 ? PlayfieldType.Single : PlayfieldType.Dual) + Beatmap.TotalColumns; public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant); diff --git a/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs b/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs new file mode 100644 index 0000000000..f6edd062e9 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Configuration/OsuRulesetConfigManager.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Configuration; +using osu.Game.Rulesets.Configuration; + +namespace osu.Game.Rulesets.Osu.Configuration +{ + public class OsuRulesetConfigManager : RulesetConfigManager + { + public OsuRulesetConfigManager(SettingsStore settings, RulesetInfo ruleset, int? variant = null) + : base(settings, ruleset, variant) + { + } + + protected override void InitialiseDefaults() + { + base.InitialiseDefaults(); + + Set(OsuRulesetSetting.SnakingInSliders, true); + Set(OsuRulesetSetting.SnakingOutSliders, true); + } + } + + public enum OsuRulesetSetting + { + SnakingInSliders, + SnakingOutSliders + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 6595e53a6a..ff74954552 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -10,8 +10,8 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Containers; -using osu.Game.Configuration; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Scoring; using osuTK.Graphics; using osu.Game.Skinning; @@ -33,6 +33,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private readonly IBindable scaleBindable = new Bindable(); private readonly IBindable pathBindable = new Bindable(); + [Resolved(CanBeNull = true)] + private OsuRulesetConfigManager config { get; set; } + public DrawableSlider(Slider s) : base(s) { @@ -94,10 +97,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } [BackgroundDependencyLoader] - private void load(OsuConfigManager config) + private void load() { - config.BindWith(OsuSetting.SnakingInSliders, Body.SnakingIn); - config.BindWith(OsuSetting.SnakingOutSliders, Body.SnakingOut); + config?.BindWith(OsuRulesetSetting.SnakingInSliders, Body.SnakingIn); + config?.BindWith(OsuRulesetSetting.SnakingOutSliders, Body.SnakingOut); positionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition); scaleBindable.BindValueChanged(scale => diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 5ae7344996..20752517d5 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -16,8 +16,11 @@ using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.Replays.Types; using osu.Game.Beatmaps.Legacy; +using osu.Game.Configuration; +using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Osu.Beatmaps; +using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Difficulty; using osu.Game.Scoring; @@ -144,12 +147,14 @@ namespace osu.Game.Rulesets.Osu public override string ShortName => "osu"; - public override RulesetSettingsSubsection CreateSettings() => new OsuSettings(this); + public override RulesetSettingsSubsection CreateSettings() => new OsuSettingsSubsection(this); public override int? LegacyID => 0; public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new OsuReplayFrame(); + public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new OsuRulesetConfigManager(settings, RulesetInfo); + public OsuRuleset(RulesetInfo rulesetInfo = null) : base(rulesetInfo) { diff --git a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs index 935f9c5c0d..85b72cbb5b 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs @@ -8,6 +8,7 @@ using osu.Game.Beatmaps; using osu.Game.Input.Handlers; using osu.Game.Replays; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Replays; @@ -20,6 +21,8 @@ namespace osu.Game.Rulesets.Osu.UI { public class OsuRulesetContainer : RulesetContainer { + protected new OsuRulesetConfigManager Config => (OsuRulesetConfigManager)base.Config; + public OsuRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) : base(ruleset, beatmap) { diff --git a/osu.Game.Rulesets.Osu/UI/OsuSettings.cs b/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs similarity index 64% rename from osu.Game.Rulesets.Osu/UI/OsuSettings.cs rename to osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs index 3620145ced..ce3432c73d 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuSettings.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuSettingsSubsection.cs @@ -3,34 +3,36 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Game.Configuration; using osu.Game.Overlays.Settings; +using osu.Game.Rulesets.Osu.Configuration; namespace osu.Game.Rulesets.Osu.UI { - public class OsuSettings : RulesetSettingsSubsection + public class OsuSettingsSubsection : RulesetSettingsSubsection { protected override string Header => "osu!"; - public OsuSettings(Ruleset ruleset) + public OsuSettingsSubsection(Ruleset ruleset) : base(ruleset) { } [BackgroundDependencyLoader] - private void load(OsuConfigManager config) + private void load() { + var config = (OsuRulesetConfigManager)Config; + Children = new Drawable[] { new SettingsCheckbox { LabelText = "Snaking in sliders", - Bindable = config.GetBindable(OsuSetting.SnakingInSliders) + Bindable = config.GetBindable(OsuRulesetSetting.SnakingInSliders) }, new SettingsCheckbox { LabelText = "Snaking out sliders", - Bindable = config.GetBindable(OsuSetting.SnakingOutSliders) + Bindable = config.GetBindable(OsuRulesetSetting.SnakingOutSliders) }, }; } diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 171ba91ada..02dff6993d 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -333,6 +333,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual("hit_2.wav", getTestableSampleInfo(hitObjects[1]).LookupNames.First()); Assert.AreEqual("normal-hitnormal2", getTestableSampleInfo(hitObjects[2]).LookupNames.First()); Assert.AreEqual("hit_1.wav", getTestableSampleInfo(hitObjects[3]).LookupNames.First()); + Assert.AreEqual(70, getTestableSampleInfo(hitObjects[3]).Volume); } SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]); diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index b6a8b3b06c..5b8bdd8a51 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -101,7 +101,7 @@ namespace osu.Game.Tests.Beatmaps.IO int fireCount = 0; // ReSharper disable once AccessToModifiedClosure - manager.ItemAdded += (_, __, ___) => fireCount++; + manager.ItemAdded += (_, __) => fireCount++; manager.ItemRemoved += _ => fireCount++; var imported = LoadOszIntoOsu(osu); diff --git a/osu.Game.Tests/Resources/hitobject-file-samples.osu b/osu.Game.Tests/Resources/hitobject-file-samples.osu index 588672e2d9..7c69f259b8 100644 --- a/osu.Game.Tests/Resources/hitobject-file-samples.osu +++ b/osu.Game.Tests/Resources/hitobject-file-samples.osu @@ -13,4 +13,4 @@ SampleSet: Normal 255,193,2170,1,0,0:0:0:0:hit_1.wav 256,191,2638,5,0,0:0:0:0:hit_2.wav 255,193,3107,1,0,0:0:0:0: -256,191,3576,1,0,0:0:0:0:hit_1.wav +256,191,3576,1,0,0:0:0:70:hit_1.wav diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs b/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs index 6ad11ae6c4..bb55c0b1e8 100644 --- a/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs +++ b/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs @@ -23,9 +23,6 @@ namespace osu.Game.Tests.Visual [System.ComponentModel.Description("in BeatmapOverlay")] public class TestCaseBeatmapScoresContainer : OsuTestCase { - private readonly IEnumerable scores; - private readonly IEnumerable anotherScores; - private readonly APIScoreInfo topScoreInfo; private readonly Box background; public TestCaseBeatmapScoresContainer() @@ -47,15 +44,7 @@ namespace osu.Game.Tests.Visual } }; - AddStep("scores pack 1", () => scoresContainer.Scores = scores); - AddStep("scores pack 2", () => scoresContainer.Scores = anotherScores); - AddStep("only top score", () => scoresContainer.Scores = new[] { topScoreInfo }); - AddStep("remove scores", () => scoresContainer.Scores = null); - AddStep("resize to big", () => container.ResizeWidthTo(1, 300)); - AddStep("resize to normal", () => container.ResizeWidthTo(0.8f, 300)); - AddStep("online scores", () => scoresContainer.Beatmap = new BeatmapInfo { OnlineBeatmapID = 75, Ruleset = new OsuRuleset().RulesetInfo }); - - scores = new[] + IEnumerable scores = new[] { new APIScoreInfo { @@ -168,7 +157,7 @@ namespace osu.Game.Tests.Visual s.Statistics.Add(HitResult.Meh, RNG.Next(2000)); } - anotherScores = new[] + IEnumerable anotherScores = new[] { new APIScoreInfo { @@ -280,7 +269,7 @@ namespace osu.Game.Tests.Visual s.Statistics.Add(HitResult.Meh, RNG.Next(2000)); } - topScoreInfo = new APIScoreInfo + var topScoreInfo = new APIScoreInfo { User = new User { @@ -305,6 +294,14 @@ namespace osu.Game.Tests.Visual topScoreInfo.Statistics.Add(HitResult.Great, RNG.Next(2000)); topScoreInfo.Statistics.Add(HitResult.Good, RNG.Next(2000)); topScoreInfo.Statistics.Add(HitResult.Meh, RNG.Next(2000)); + + AddStep("scores pack 1", () => scoresContainer.Scores = scores); + AddStep("scores pack 2", () => scoresContainer.Scores = anotherScores); + AddStep("only top score", () => scoresContainer.Scores = new[] { topScoreInfo }); + AddStep("remove scores", () => scoresContainer.Scores = null); + AddStep("resize to big", () => container.ResizeWidthTo(1, 300)); + AddStep("resize to normal", () => container.ResizeWidthTo(0.8f, 300)); + AddStep("online scores", () => scoresContainer.Beatmap = new BeatmapInfo { OnlineBeatmapID = 75, Ruleset = new OsuRuleset().RulesetInfo }); } [BackgroundDependencyLoader] diff --git a/osu.Game.Tests/Visual/TestCaseDirectPanel.cs b/osu.Game.Tests/Visual/TestCaseDirectPanel.cs new file mode 100644 index 0000000000..beb88ac56c --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseDirectPanel.cs @@ -0,0 +1,47 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Overlays.Direct; +using osu.Game.Rulesets.Osu; +using osu.Game.Tests.Beatmaps; +using osuTK; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseDirectPanel : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(DirectGridPanel), + typeof(DirectListPanel), + typeof(IconPill) + }; + + [BackgroundDependencyLoader] + private void load() + { + var beatmap = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo, null); + beatmap.BeatmapSetInfo.OnlineInfo.HasVideo = true; + beatmap.BeatmapSetInfo.OnlineInfo.HasStoryboard = true; + + Child = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Padding = new MarginPadding(20), + Spacing = new Vector2(0, 20), + Children = new Drawable[] + { + new DirectGridPanel(beatmap.BeatmapSetInfo), + new DirectListPanel(beatmap.BeatmapSetInfo) + } + }; + } + } +} diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 2b559d5912..88f5e777e3 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -11,7 +11,6 @@ using Microsoft.EntityFrameworkCore; using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Extensions; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics.Textures; using osu.Framework.Logging; using osu.Framework.Platform; @@ -50,11 +49,6 @@ namespace osu.Game.Beatmaps /// public event Action BeatmapDownloadFailed; - /// - /// Fired when a beatmap load is requested (into the interactive game UI). - /// - public Action PresentBeatmap; - /// /// A default representation of a WorkingBeatmap to use when no beatmap is available. /// @@ -151,8 +145,7 @@ namespace osu.Game.Beatmaps var downloadNotification = new DownloadNotification { - CompletionText = $"Imported {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}!", - Text = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}", + Text = $"Downloading {beatmapSetInfo}", }; var request = new DownloadBeatmapSetRequest(beatmapSetInfo, noVideo); @@ -165,20 +158,10 @@ namespace osu.Game.Beatmaps request.Success += filename => { - downloadNotification.Text = $"Importing {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}"; - Task.Factory.StartNew(() => { // This gets scheduled back to the update thread, but we want the import to run in the background. - var importedBeatmap = Import(filename); - - downloadNotification.CompletionClickAction = () => - { - PresentCompletedImport(importedBeatmap.Yield()); - return true; - }; - downloadNotification.State = ProgressNotificationState.Completed; - + Import(downloadNotification, filename); currentDownloads.Remove(request); }, TaskCreationOptions.LongRunning); }; @@ -221,12 +204,6 @@ namespace osu.Game.Beatmaps return true; } - protected override void PresentCompletedImport(IEnumerable imported) - { - base.PresentCompletedImport(imported); - PresentBeatmap?.Invoke(imported.LastOrDefault()); - } - /// /// Get an existing download request if it exists. /// diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index 6786a780b6..f4b7b1d74f 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -12,7 +12,7 @@ namespace osu.Game.Beatmaps /// /// Handles the storage and retrieval of Beatmaps/BeatmapSets to the database backing /// - public class BeatmapStore : MutableDatabaseBackedStore + public class BeatmapStore : MutableDatabaseBackedStoreWithFileIncludes { public event Action BeatmapHidden; public event Action BeatmapRestored; @@ -64,18 +64,17 @@ namespace osu.Game.Beatmaps protected override IQueryable AddIncludesForDeletion(IQueryable query) => base.AddIncludesForDeletion(query) - .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata) - .Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty) .Include(s => s.Metadata) - .Include(s => s.Beatmaps).ThenInclude(b => b.Scores); + .Include(s => s.Beatmaps).ThenInclude(b => b.Scores) + .Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty) + .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata); protected override IQueryable AddIncludesForConsumption(IQueryable query) => base.AddIncludesForConsumption(query) .Include(s => s.Metadata) .Include(s => s.Beatmaps).ThenInclude(s => s.Ruleset) .Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty) - .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata) - .Include(s => s.Files).ThenInclude(f => f.FileInfo); + .Include(s => s.Beatmaps).ThenInclude(b => b.Metadata); protected override void Purge(List items, OsuDbContext context) { diff --git a/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs b/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs index ec52517197..f1607ad749 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyColouredContainer.cs @@ -48,11 +48,11 @@ namespace osu.Game.Beatmaps.Drawables var rating = beatmap.StarDifficulty; - if (rating < 1.5) return DifficultyRating.Easy; - if (rating < 2.25) return DifficultyRating.Normal; - if (rating < 3.75) return DifficultyRating.Hard; - if (rating < 5.25) return DifficultyRating.Insane; - if (rating < 6.75) return DifficultyRating.Expert; + if (rating < 2.0) return DifficultyRating.Easy; + if (rating < 2.7) return DifficultyRating.Normal; + if (rating < 4.0) return DifficultyRating.Hard; + if (rating < 5.3) return DifficultyRating.Insane; + if (rating < 6.5) return DifficultyRating.Expert; return DifficultyRating.ExpertPlus; } diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 1260a524b4..2d8cfa12ee 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -72,9 +72,6 @@ namespace osu.Game.Configuration Set(OsuSetting.MenuParallax, true); - Set(OsuSetting.SnakingInSliders, true); - Set(OsuSetting.SnakingOutSliders, true); - // Gameplay Set(OsuSetting.DimLevel, 0.3, 0, 1, 0.01); Set(OsuSetting.BlurLevel, 0, 0, 1, 0.01); @@ -150,8 +147,6 @@ namespace osu.Game.Configuration DisplayStarsMinimum, DisplayStarsMaximum, RandomSelectAlgorithm, - SnakingInSliders, - SnakingOutSliders, ShowFpsDisplay, ChatDisplayHeight, Version, diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs index 86c97df191..9ec184abd7 100644 --- a/osu.Game/Database/ArchiveModelManager.cs +++ b/osu.Game/Database/ArchiveModelManager.cs @@ -33,7 +33,7 @@ namespace osu.Game.Database where TModel : class, IHasFiles, IHasPrimaryKey, ISoftDelete where TFileModel : INamedFileInfo, new() { - public delegate void ItemAddedDelegate(TModel model, bool existing, bool silent); + public delegate void ItemAddedDelegate(TModel model, bool existing); /// /// Set an endpoint for notifications to be posted to. @@ -108,12 +108,12 @@ namespace osu.Game.Database a.Invoke(); } - protected ArchiveModelManager(Storage storage, IDatabaseContextFactory contextFactory, MutableDatabaseBackedStore modelStore, IIpcHost importHost = null) + protected ArchiveModelManager(Storage storage, IDatabaseContextFactory contextFactory, MutableDatabaseBackedStoreWithFileIncludes modelStore, IIpcHost importHost = null) { ContextFactory = contextFactory; ModelStore = modelStore; - ModelStore.ItemAdded += (item, silent) => handleEvent(() => ItemAdded?.Invoke(item, false, silent)); + ModelStore.ItemAdded += item => handleEvent(() => ItemAdded?.Invoke(item, false)); ModelStore.ItemRemoved += s => handleEvent(() => ItemRemoved?.Invoke(s)); Files = new FileStore(contextFactory, storage); @@ -131,14 +131,18 @@ namespace osu.Game.Database /// One or more archive locations on disk. public void Import(params string[] paths) { - var notification = new ProgressNotification - { - Text = "Import is initialising...", - Progress = 0, - State = ProgressNotificationState.Active, - }; + var notification = new ProgressNotification { State = ProgressNotificationState.Active }; PostNotification?.Invoke(notification); + Import(notification, paths); + } + + protected void Import(ProgressNotification notification, params string[] paths) + { + notification.Progress = 0; + notification.Text = "Import is initialising..."; + + var term = $"{typeof(TModel).Name.Replace("Info", "").ToLower()}"; List imported = new List(); @@ -151,7 +155,18 @@ namespace osu.Game.Database try { - notification.Text = $"Importing ({++current} of {paths.Length})\n{Path.GetFileName(path)}"; + var text = "Importing "; + + if (path.Length > 1) + text += $"{++current} of {paths.Length} {term}s.."; + else + text += $"{term}.."; + + // only show the filename if it isn't a temporary one (as those look ugly). + if (!path.Contains(Path.GetTempPath())) + text += $"\n{Path.GetFileName(path)}"; + + notification.Text = text; imported.Add(Import(path)); @@ -171,13 +186,20 @@ namespace osu.Game.Database } else { - notification.CompletionText = $"Imported {current} {typeof(TModel).Name.Replace("Info", "").ToLower()}s!"; - notification.CompletionClickAction += () => + notification.CompletionText = imported.Count == 1 + ? $"Imported {imported.First()}!" + : $"Imported {current} {term}s!"; + + if (imported.Count > 0 && PresentImport != null) { - if (imported.Count > 0) - PresentCompletedImport(imported); - return true; - }; + notification.CompletionText += " Click to view."; + notification.CompletionClickAction = () => + { + PresentImport?.Invoke(imported); + return true; + }; + } + notification.State = ProgressNotificationState.Completed; } } @@ -210,9 +232,10 @@ namespace osu.Game.Database return import; } - protected virtual void PresentCompletedImport(IEnumerable imported) - { - } + /// + /// Fired when the user requests to view the resulting import. + /// + public Action> PresentImport; /// /// Import an item from an . @@ -228,7 +251,7 @@ namespace osu.Game.Database model.Hash = computeHash(archive); - return Import(model, false, archive); + return Import(model, archive); } catch (Exception e) { @@ -262,9 +285,8 @@ namespace osu.Game.Database /// Import an item from a . /// /// The model to be imported. - /// Whether the user should be notified fo the import. /// An optional archive to use for model population. - public TModel Import(TModel item, bool silent = false, ArchiveReader archive = null) + public TModel Import(TModel item, ArchiveReader archive = null) { delayEvents(); @@ -284,7 +306,7 @@ namespace osu.Game.Database { Undelete(existing); Logger.Log($"Found existing {typeof(TModel)} for {item} (ID {existing.ID}). Skipping import.", LoggingTarget.Database); - handleEvent(() => ItemAdded?.Invoke(existing, true, silent)); + handleEvent(() => ItemAdded?.Invoke(existing, true)); return existing; } @@ -294,7 +316,7 @@ namespace osu.Game.Database Populate(item, archive); // import to store - ModelStore.Add(item, silent); + ModelStore.Add(item); } catch (Exception e) { diff --git a/osu.Game/Database/MutableDatabaseBackedStore.cs b/osu.Game/Database/MutableDatabaseBackedStore.cs index 8ed38fedb8..39a48b5be6 100644 --- a/osu.Game/Database/MutableDatabaseBackedStore.cs +++ b/osu.Game/Database/MutableDatabaseBackedStore.cs @@ -16,9 +16,7 @@ namespace osu.Game.Database public abstract class MutableDatabaseBackedStore : DatabaseBackedStore where T : class, IHasPrimaryKey, ISoftDelete { - public delegate void ItemAddedDelegate(T model, bool silent); - - public event ItemAddedDelegate ItemAdded; + public event Action ItemAdded; public event Action ItemRemoved; protected MutableDatabaseBackedStore(IDatabaseContextFactory contextFactory, Storage storage = null) @@ -35,8 +33,7 @@ namespace osu.Game.Database /// Add a to the database. /// /// The item to add. - /// Whether the user should be notified of the addition. - public void Add(T item, bool silent) + public void Add(T item) { using (var usage = ContextFactory.GetForWrite()) { @@ -44,7 +41,7 @@ namespace osu.Game.Database context.Attach(item); } - ItemAdded?.Invoke(item, silent); + ItemAdded?.Invoke(item); } /// @@ -57,7 +54,7 @@ namespace osu.Game.Database usage.Context.Update(item); ItemRemoved?.Invoke(item); - ItemAdded?.Invoke(item, true); + ItemAdded?.Invoke(item); } /// @@ -94,7 +91,7 @@ namespace osu.Game.Database item.DeletePending = false; } - ItemAdded?.Invoke(item, true); + ItemAdded?.Invoke(item); return true; } diff --git a/osu.Game/Database/MutableDatabaseBackedStoreWithFileIncludes.cs b/osu.Game/Database/MutableDatabaseBackedStoreWithFileIncludes.cs new file mode 100644 index 0000000000..5d6ff6b09b --- /dev/null +++ b/osu.Game/Database/MutableDatabaseBackedStoreWithFileIncludes.cs @@ -0,0 +1,27 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using Microsoft.EntityFrameworkCore; +using osu.Framework.Platform; + +namespace osu.Game.Database +{ + public abstract class MutableDatabaseBackedStoreWithFileIncludes : MutableDatabaseBackedStore + where T : class, IHasPrimaryKey, ISoftDelete, IHasFiles + where U : INamedFileInfo + { + protected MutableDatabaseBackedStoreWithFileIncludes(IDatabaseContextFactory contextFactory, Storage storage = null) + : base(contextFactory, storage) + { + } + + protected override IQueryable AddIncludesForConsumption(IQueryable query) => + base.AddIncludesForConsumption(query) + .Include(s => s.Files).ThenInclude(f => f.FileInfo); + + protected override IQueryable AddIncludesForDeletion(IQueryable query) => + base.AddIncludesForDeletion(query) + .Include(s => s.Files); // don't include FileInfo. these are handled by the FileStore itself. + } +} diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index 8e47bf2e99..c6ee91f961 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -24,6 +24,12 @@ namespace osu.Game.Graphics.Containers protected override bool BlockNonPositionalInput => true; + /// + /// Temporary to allow for overlays in the main screen content to not dim theirselves. + /// Should be eventually replaced by dimming which is aware of the target dim container (traverse parent for certain interface type?). + /// + protected virtual bool DimMainContent => true; + [Resolved(CanBeNull = true)] private OsuGame osuGame { get; set; } @@ -95,7 +101,7 @@ namespace osu.Game.Graphics.Containers if (OverlayActivationMode.Value != OverlayActivation.Disabled) { if (PlaySamplesOnStateChange) samplePopIn?.Play(); - if (BlockScreenWideMouse) osuGame?.AddBlockingOverlay(this); + if (BlockScreenWideMouse && DimMainContent) osuGame?.AddBlockingOverlay(this); } else State = Visibility.Hidden; diff --git a/osu.Game/Graphics/UserInterface/RollingCounter.cs b/osu.Game/Graphics/UserInterface/RollingCounter.cs index 47e12f5f15..cd244ed7e6 100644 --- a/osu.Game/Graphics/UserInterface/RollingCounter.cs +++ b/osu.Game/Graphics/UserInterface/RollingCounter.cs @@ -59,7 +59,6 @@ namespace osu.Game.Graphics.UserInterface public abstract void Increment(T amount); - public float TextSize { get => DisplayedCountSpriteText.Font.Size; diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index 01c91ca780..d9d05ff285 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -36,11 +36,12 @@ namespace osu.Game.Online.API.Requests [Description("Ranked & Approved")] RankedApproved = 0, - Approved = 1, + Qualified = 3, Loved = 8, Favourites = 2, - Qualified = 3, - Pending = 4, + + [Description("Pending & WIP")] + PendingWIP = 4, Graveyard = 5, [Description("My Maps")] diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index bf9b4ced3b..cf231f19ce 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -167,8 +167,6 @@ namespace osu.Game { this.frameworkConfig = frameworkConfig; - ScoreManager.ItemAdded += (score, _, silent) => Schedule(() => LoadScore(score, silent)); - if (!Host.IsPrimaryInstance) { Logger.Log(@"osu! does not support multiple running instances.", LoggingTarget.Runtime, LogLevel.Error); @@ -217,63 +215,12 @@ namespace osu.Game externalLinkOpener.OpenUrlExternally(url); } - private ScheduledDelegate scoreLoad; - /// /// Show a beatmap set as an overlay. /// /// The set to display. public void ShowBeatmapSet(int setId) => beatmapSetOverlay.FetchAndShowBeatmapSet(setId); - /// - /// Present a beatmap at song select. - /// - /// The beatmap to select. - public void PresentBeatmap(BeatmapSetInfo beatmap) - { - if (menuScreen == null) - { - Schedule(() => PresentBeatmap(beatmap)); - return; - } - - CloseAllOverlays(false); - - void setBeatmap() - { - if (Beatmap.Disabled) - { - Schedule(setBeatmap); - return; - } - - var databasedSet = beatmap.OnlineBeatmapSetID != null ? BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID) : BeatmapManager.QueryBeatmapSet(s => s.Hash == beatmap.Hash); - - if (databasedSet != null) - { - // Use first beatmap available for current ruleset, else switch ruleset. - var first = databasedSet.Beatmaps.Find(b => b.Ruleset == ruleset.Value) ?? databasedSet.Beatmaps.First(); - - ruleset.Value = first.Ruleset; - Beatmap.Value = BeatmapManager.GetWorkingBeatmap(first); - } - } - - switch (screenStack.CurrentScreen) - { - case SongSelect _: - break; - default: - // navigate to song select if we are not already there. - - menuScreen.MakeCurrent(); - menuScreen.LoadToSolo(); - break; - } - - setBeatmap(); - } - /// /// Show a user's profile as an overlay. /// @@ -286,19 +233,44 @@ namespace osu.Game /// The beatmap to show. public void ShowBeatmap(int beatmapId) => beatmapSetOverlay.FetchAndShowBeatmap(beatmapId); - protected void LoadScore(ScoreInfo score, bool silent) + /// + /// Present a beatmap at song select immediately. + /// The user should have already requested this interactively. + /// + /// The beatmap to select. + public void PresentBeatmap(BeatmapSetInfo beatmap) { - if (silent) - return; + var databasedSet = beatmap.OnlineBeatmapSetID != null + ? BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID) + : BeatmapManager.QueryBeatmapSet(s => s.Hash == beatmap.Hash); - scoreLoad?.Cancel(); - - if (menuScreen == null) + if (databasedSet == null) { - scoreLoad = Schedule(() => LoadScore(score, false)); + Logger.Log("The requested beatmap could not be loaded.", LoggingTarget.Information); return; } + performFromMainMenu(() => + { + // we might already be at song select, so a check is required before performing the load to solo. + if (menuScreen.IsCurrentScreen()) + menuScreen.LoadToSolo(); + + // Use first beatmap available for current ruleset, else switch ruleset. + var first = databasedSet.Beatmaps.Find(b => b.Ruleset == ruleset.Value) ?? databasedSet.Beatmaps.First(); + + ruleset.Value = first.Ruleset; + Beatmap.Value = BeatmapManager.GetWorkingBeatmap(first); + }, $"load {beatmap}", bypassScreenAllowChecks: true, targetScreen: typeof(PlaySongSelect)); + } + + /// + /// Present a score's replay immediately. + /// The user should have already requested this interactively. + /// + /// The beatmap to select. + public void PresentScore(ScoreInfo score) + { var databasedScore = ScoreManager.GetScore(score); var databasedScoreInfo = databasedScore.ScoreInfo; if (databasedScore.Replay == null) @@ -314,14 +286,40 @@ namespace osu.Game return; } - if ((screenStack.CurrentScreen as IOsuScreen)?.AllowExternalScreenChange == false) + performFromMainMenu(() => + { + ruleset.Value = databasedScoreInfo.Ruleset; + + Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap); + Beatmap.Value.Mods.Value = databasedScoreInfo.Mods; + + menuScreen.Push(new PlayerLoader(() => new ReplayPlayer(databasedScore))); + }, $"watch {databasedScoreInfo}", bypassScreenAllowChecks: true); + } + + private ScheduledDelegate performFromMainMenuTask; + + /// + /// Perform an action only after returning to the main menu. + /// Eagerly tries to exit the current screen until it succeeds. + /// + /// The action to perform once we are in the correct state. + /// The task name to display in a notification (if we can't immediately reach the main menu state). + /// An optional target screen type. If this screen is already current we can immediately perform the action without returning to the menu. + /// Whether checking should be bypassed. + private void performFromMainMenu(Action action, string taskName, Type targetScreen = null, bool bypassScreenAllowChecks = false) + { + performFromMainMenuTask?.Cancel(); + + // if the current screen does not allow screen changing, give the user an option to try again later. + if (!bypassScreenAllowChecks && (screenStack.CurrentScreen as IOsuScreen)?.AllowExternalScreenChange == false) { notifications.Post(new SimpleNotification { - Text = $"Click here to watch {databasedScoreInfo.User.Username} on {databasedScoreInfo.Beatmap}", + Text = $"Click here to {taskName}", Activated = () => { - loadScore(); + performFromMainMenu(action, taskName, targetScreen, true); return true; } }); @@ -329,24 +327,26 @@ namespace osu.Game return; } - loadScore(); + CloseAllOverlays(false); - void loadScore() + // we may already be at the target screen type. + if (targetScreen != null && screenStack.CurrentScreen?.GetType() == targetScreen) { - if (!menuScreen.IsCurrentScreen() || Beatmap.Disabled) - { - menuScreen.MakeCurrent(); - this.Delay(500).Schedule(loadScore, out scoreLoad); - return; - } - - ruleset.Value = databasedScoreInfo.Ruleset; - - Beatmap.Value = BeatmapManager.GetWorkingBeatmap(databasedBeatmap); - Beatmap.Value.Mods.Value = databasedScoreInfo.Mods; - - menuScreen.Push(new PlayerLoader(() => new ReplayPlayer(databasedScore))); + action(); + return; } + + // all conditions have been met to continue with the action. + if (menuScreen?.IsCurrentScreen() == true && !Beatmap.Disabled) + { + action(); + return; + } + + // menuScreen may not be initialised yet (null check required). + menuScreen?.MakeCurrent(); + + performFromMainMenuTask = Schedule(() => performFromMainMenu(action, taskName)); } protected override void Dispose(bool isDisposing) @@ -370,8 +370,10 @@ namespace osu.Game BeatmapManager.PostNotification = n => notifications?.Post(n); BeatmapManager.GetStableStorage = GetStorageForStableInstall; + BeatmapManager.PresentImport = items => PresentBeatmap(items.First()); - BeatmapManager.PresentBeatmap = PresentBeatmap; + ScoreManager.PostNotification = n => notifications?.Post(n); + ScoreManager.PresentImport = items => PresentScore(items.First()); Container logoContainer; diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 487ef10ffb..643a673faf 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -75,9 +75,9 @@ namespace osu.Game private Bindable fpsDisplayVisible; - protected AssemblyName AssemblyName => Assembly.GetEntryAssembly()?.GetName() ?? new AssemblyName { Version = new Version() }; + public virtual Version AssemblyVersion => Assembly.GetEntryAssembly()?.GetName().Version ?? new Version(); - public bool IsDeployedBuild => AssemblyName.Version.Major > 0; + public bool IsDeployedBuild => AssemblyVersion.Major > 0; public string Version { @@ -86,8 +86,8 @@ namespace osu.Game if (!IsDeployedBuild) return @"local " + (DebugUtils.IsDebug ? @"debug" : @"release"); - var assembly = AssemblyName; - return $@"{assembly.Version.Major}.{assembly.Version.Minor}.{assembly.Version.Build}"; + var version = AssemblyVersion; + return $@"{version.Major}.{version.Minor}.{version.Build}"; } } diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index a679f33e3a..908ec5f026 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -137,7 +137,7 @@ namespace osu.Game.Overlays.Chat { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Font = OsuFont.GetFont(size: TextSize * 0.75f, weight: FontWeight.Bold, fixedWidth: true) + Font = OsuFont.GetFont(size: TextSize * 0.75f, weight: FontWeight.SemiBold, fixedWidth: true) }, new MessageSender(message.Sender) { diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/Direct/DirectListPanel.cs index 7bf372dff7..01393ad98b 100644 --- a/osu.Game/Overlays/Direct/DirectListPanel.cs +++ b/osu.Game/Overlays/Direct/DirectListPanel.cs @@ -13,6 +13,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; namespace osu.Game.Overlays.Direct { @@ -23,6 +24,7 @@ namespace osu.Game.Overlays.Direct private const float vertical_padding = 5; private const float height = 70; + private FillFlowContainer statusContainer; private PlayButton playButton; private Box progressBar; @@ -108,10 +110,24 @@ namespace osu.Game.Overlays.Direct }, new FillFlowContainer { - AutoSizeAxes = Axes.X, - Height = 20, - Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding }, - Children = GetDifficultyIcons(), + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + statusContainer = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Vertical = vertical_padding, Horizontal = 5 }, + Spacing = new Vector2(5), + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.X, + Height = 20, + Margin = new MarginPadding { Top = vertical_padding, Bottom = vertical_padding }, + Children = GetDifficultyIcons(), + }, + }, }, }, }, @@ -194,6 +210,23 @@ namespace osu.Game.Overlays.Direct Colour = colours.Yellow, }, }); + + if (SetInfo.OnlineInfo?.HasVideo ?? false) + { + statusContainer.Add(new IconPill(FontAwesome.fa_film) { IconSize = new Vector2(20) }); + } + + if (SetInfo.OnlineInfo?.HasStoryboard ?? false) + { + statusContainer.Add(new IconPill(FontAwesome.fa_image) { IconSize = new Vector2(20) }); + } + + statusContainer.Add(new BeatmapSetOnlineStatusPill + { + TextSize = 12, + TextPadding = new MarginPadding { Horizontal = 10, Vertical = 4 }, + Status = SetInfo.OnlineInfo?.Status ?? BeatmapSetOnlineStatus.None, + }); } } } diff --git a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs index 7be9eadefb..37f13aefc8 100644 --- a/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs +++ b/osu.Game/Overlays/Direct/DownloadTrackingComposite.cs @@ -118,7 +118,7 @@ namespace osu.Game.Overlays.Direct private void onRequestFailure(Exception e) => Schedule(() => attachDownload(null)); - private void setAdded(BeatmapSetInfo s, bool existing, bool silent) => setDownloadStateFromManager(s, DownloadState.LocallyAvailable); + private void setAdded(BeatmapSetInfo s, bool existing) => setDownloadStateFromManager(s, DownloadState.LocallyAvailable); private void setRemoved(BeatmapSetInfo s) => setDownloadStateFromManager(s, DownloadState.NotDownloaded); diff --git a/osu.Game/Overlays/Direct/IconPill.cs b/osu.Game/Overlays/Direct/IconPill.cs index 83ee0cb6c5..e7f516f449 100644 --- a/osu.Game/Overlays/Direct/IconPill.cs +++ b/osu.Game/Overlays/Direct/IconPill.cs @@ -12,6 +12,14 @@ namespace osu.Game.Overlays.Direct { public class IconPill : CircularContainer { + public Vector2 IconSize + { + get => iconContainer.Size; + set => iconContainer.Size = value; + } + + private readonly Container iconContainer; + public IconPill(FontAwesome icon) { AutoSizeAxes = Axes.Both; @@ -25,16 +33,16 @@ namespace osu.Game.Overlays.Direct Colour = Color4.Black, Alpha = 0.5f, }, - new Container + iconContainer = new Container { - AutoSizeAxes = Axes.Both, - Margin = new MarginPadding(5), + Size = new Vector2(22), + Padding = new MarginPadding(5), Child = new SpriteIcon { Anchor = Anchor.Centre, Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, Icon = icon, - Size = new Vector2(12), }, }, }; diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 24faf36ef8..aa41723ca6 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -38,6 +38,8 @@ namespace osu.Game.Overlays.Mods protected override bool BlockNonPositionalInput => false; + protected override bool DimMainContent => false; + protected readonly FillFlowContainer ModSectionsContainer; protected readonly Bindable> SelectedMods = new Bindable>(new Mod[] { }); diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs index 7df09f1abe..7846e31725 100644 --- a/osu.Game/Overlays/Music/PlaylistList.cs +++ b/osu.Game/Overlays/Music/PlaylistList.cs @@ -75,7 +75,7 @@ namespace osu.Game.Overlays.Music [BackgroundDependencyLoader] private void load(BeatmapManager beatmaps, IBindable beatmap) { - beatmaps.GetAllUsableBeatmapSets().ForEach(b => addBeatmapSet(b, false, false)); + beatmaps.GetAllUsableBeatmapSets().ForEach(b => addBeatmapSet(b, false)); beatmaps.ItemAdded += addBeatmapSet; beatmaps.ItemRemoved += removeBeatmapSet; @@ -83,7 +83,7 @@ namespace osu.Game.Overlays.Music beatmapBacking.ValueChanged += _ => updateSelectedSet(); } - private void addBeatmapSet(BeatmapSetInfo obj, bool existing, bool silent) => Schedule(() => + private void addBeatmapSet(BeatmapSetInfo obj, bool existing) => Schedule(() => { if (existing) return; diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 6ac597451d..c1b742e4e5 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -212,7 +212,7 @@ namespace osu.Game.Overlays beatmapSets.Insert(index, beatmapSetInfo); } - private void handleBeatmapAdded(BeatmapSetInfo obj, bool existing, bool silent) + private void handleBeatmapAdded(BeatmapSetInfo obj, bool existing) { if (existing) return; diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index d1f47b6016..4b0147eb5d 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -82,7 +82,7 @@ namespace osu.Game.Overlays.Settings.Sections private void itemRemoved(SkinInfo s) => Schedule(() => skinDropdown.Items = skinDropdown.Items.Where(i => i.ID != s.ID).ToArray()); - private void itemAdded(SkinInfo s, bool existing, bool silent) + private void itemAdded(SkinInfo s, bool existing) { if (existing) return; diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index 4d8fbb99ac..855c7ad823 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -139,6 +139,7 @@ namespace osu.Game.Overlays.Toolbar protected override bool OnClick(ClickEvent e) { HoverBackground.FlashColour(Color4.White.Opacity(100), 500, Easing.OutQuint); + tooltipContainer.FadeOut(100); return base.OnClick(e); } diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index b9177a325c..e397139843 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -301,7 +301,16 @@ namespace osu.Game.Rulesets.Objects.Legacy { // Todo: This should return the normal SampleInfos if the specified sample file isn't found, but that's a pretty edge-case scenario if (!string.IsNullOrEmpty(bankInfo.Filename)) - return new List { new FileSampleInfo { Filename = bankInfo.Filename } }; + { + return new List + { + new FileSampleInfo + { + Filename = bankInfo.Filename, + Volume = bankInfo.Volume + } + }; + } var soundTypes = new List { diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 6ae51796e8..d36f963016 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -178,5 +178,7 @@ namespace osu.Game.Scoring { public string Acronym { get; set; } } + + public override string ToString() => $"{User} playing {Beatmap}"; } } diff --git a/osu.Game/Scoring/ScoreStore.cs b/osu.Game/Scoring/ScoreStore.cs index 745bb6cc29..9627481f4d 100644 --- a/osu.Game/Scoring/ScoreStore.cs +++ b/osu.Game/Scoring/ScoreStore.cs @@ -8,7 +8,7 @@ using osu.Game.Database; namespace osu.Game.Scoring { - public class ScoreStore : MutableDatabaseBackedStore + public class ScoreStore : MutableDatabaseBackedStoreWithFileIncludes { public ScoreStore(IDatabaseContextFactory factory, Storage storage) : base(factory, storage) @@ -17,7 +17,6 @@ namespace osu.Game.Scoring protected override IQueryable AddIncludesForConsumption(IQueryable query) => base.AddIncludesForConsumption(query) - .Include(s => s.Files).ThenInclude(f => f.FileInfo) .Include(s => s.Beatmap) .Include(s => s.Ruleset); } diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs index 42657ef3f7..3b24925f2c 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineArea.cs @@ -91,7 +91,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline RelativeSizeAxes = Axes.Y, Height = 0.5f, Icon = FontAwesome.fa_search_plus, - Action = () => timeline.Zoom++ + Action = () => changeZoom(1) }, new TimelineButton { @@ -100,7 +100,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline RelativeSizeAxes = Axes.Y, Height = 0.5f, Icon = FontAwesome.fa_search_minus, - Action = () => timeline.Zoom-- + Action = () => changeZoom(-1) }, } } @@ -124,5 +124,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline timeline.WaveformVisible.BindTo(waveformCheckbox.Current); } + + private void changeZoom(float change) => timeline.Zoom += change; } } diff --git a/osu.Game/Screens/IOsuScreen.cs b/osu.Game/Screens/IOsuScreen.cs index e665f401d9..b25bcdeab1 100644 --- a/osu.Game/Screens/IOsuScreen.cs +++ b/osu.Game/Screens/IOsuScreen.cs @@ -19,7 +19,7 @@ namespace osu.Game.Screens /// /// Whether a top-level component should be allowed to exit the current screen to, for example, - /// complete an import. + /// complete an import. Note that this can be overridden by a user if they specifically request. /// bool AllowExternalScreenChange { get; } diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs index 8c1cfdcda1..89f4f92092 100644 --- a/osu.Game/Screens/Menu/Disclaimer.cs +++ b/osu.Game/Screens/Menu/Disclaimer.cs @@ -5,14 +5,17 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Screens; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Online.API; using osuTK; using osuTK.Graphics; using osu.Game.Overlays; +using osu.Game.Users; namespace osu.Game.Screens.Menu { @@ -33,13 +36,15 @@ namespace osu.Game.Screens.Menu private const float icon_y = -85; + private readonly Bindable currentUser = new Bindable(); + public Disclaimer() { ValidForResume = false; } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, APIAccess api) { InternalChildren = new Drawable[] { @@ -70,7 +75,7 @@ namespace osu.Game.Screens.Menu textFlow.AddParagraph("Things may not work as expected", t => t.Font = t.Font.With(size: 20)); textFlow.NewParagraph(); - Action format = t => t.Font = OsuFont.GetFont(size: 15, weight: FontWeight.Bold); + Action format = t => t.Font = OsuFont.GetFont(size: 15, weight: FontWeight.SemiBold); textFlow.AddParagraph("Detailed bug reports are welcomed via github issues.", format); textFlow.NewParagraph(); @@ -96,6 +101,13 @@ namespace osu.Game.Screens.Menu }).First()); iconColour = colours.Yellow; + + currentUser.BindTo(api.LocalUser); + currentUser.BindValueChanged(e => + { + if (e.NewValue.IsSupporter) + supporterDrawables.ForEach(d => d.FadeOut(500, Easing.OutQuint).Expire()); + }, true); } protected override void LoadComplete() diff --git a/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs b/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs index b299e70962..86b4cfbfa7 100644 --- a/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs +++ b/osu.Game/Screens/Multi/Match/Components/ReadyButton.cs @@ -54,7 +54,7 @@ namespace osu.Game.Screens.Multi.Match.Components hasBeatmap = beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == beatmap.OnlineBeatmapID) != null; } - private void beatmapAdded(BeatmapSetInfo model, bool existing, bool silent) + private void beatmapAdded(BeatmapSetInfo model, bool existing) { if (Beatmap.Value == null) return; diff --git a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs index 229b9bde6a..a71106872e 100644 --- a/osu.Game/Screens/Multi/Match/MatchSubScreen.cs +++ b/osu.Game/Screens/Multi/Match/MatchSubScreen.cs @@ -202,7 +202,7 @@ namespace osu.Game.Screens.Multi.Match /// /// Handle the case where a beatmap is imported (and can be used by this match). /// - private void beatmapAdded(BeatmapSetInfo model, bool existing, bool silent) => Schedule(() => + private void beatmapAdded(BeatmapSetInfo model, bool existing) => Schedule(() => { if (Beatmap.Value != beatmapManager.DefaultBeatmap) return; diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index bb2211a533..ee59df0e98 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -298,7 +298,7 @@ namespace osu.Game.Screens.Play var score = CreateScore(); if (RulesetContainer.ReplayScore == null) - scoreManager.Import(score, true); + scoreManager.Import(score); this.Push(CreateResults(score)); diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 5c9acade7b..269baad955 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -17,6 +17,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Menu; +using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.PlayerSettings; using osuTK; using osuTK.Graphics; @@ -296,6 +297,7 @@ namespace osu.Game.Screens.Play private readonly WorkingBeatmap beatmap; private LoadingAnimation loading; private Sprite backgroundSprite; + private ModDisplay modDisplay; public bool Loading { @@ -322,7 +324,7 @@ namespace osu.Game.Screens.Play [BackgroundDependencyLoader] private void load() { - var metadata = beatmap?.BeatmapInfo?.Metadata ?? new BeatmapMetadata(); + var metadata = beatmap.BeatmapInfo?.Metadata ?? new BeatmapMetadata(); AutoSizeAxes = Axes.Both; Children = new Drawable[] @@ -391,6 +393,14 @@ namespace osu.Game.Screens.Play Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, }, + new ModDisplay + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + AutoSizeAxes = Axes.Both, + Margin = new MarginPadding { Top = 20 }, + Current = beatmap.Mods + } }, } }; diff --git a/osu.Game/Screens/Play/SquareGraph.cs b/osu.Game/Screens/Play/SquareGraph.cs index 95ac35baa7..d10034d552 100644 --- a/osu.Game/Screens/Play/SquareGraph.cs +++ b/osu.Game/Screens/Play/SquareGraph.cs @@ -242,7 +242,11 @@ namespace osu.Game.Screens.Play // Reverse drawableRows so when iterating through them they start at the bottom drawableRows.Reverse(); + } + protected override void LoadComplete() + { + base.LoadComplete(); fillActive(); } diff --git a/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs new file mode 100644 index 0000000000..a37327f2c3 --- /dev/null +++ b/osu.Game/Screens/Select/BeatmapClearScoresDialog.cs @@ -0,0 +1,48 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Overlays.Dialog; +using osu.Game.Scoring; +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace osu.Game.Screens.Select +{ + public class BeatmapClearScoresDialog : PopupDialog + { + private ScoreManager scoreManager; + + public BeatmapClearScoresDialog(BeatmapInfo beatmap, Action onCompletion) + { + BodyText = $@"{beatmap.Metadata?.Artist} - {beatmap.Metadata?.Title}"; + Icon = FontAwesome.fa_eraser; + HeaderText = @"Clearing all local scores. Are you sure?"; + Buttons = new PopupDialogButton[] + { + new PopupDialogOkButton + { + Text = @"Yes. Please.", + Action = () => + { + Task.Run(() => scoreManager.Delete(scoreManager.QueryScores(s => !s.DeletePending && s.Beatmap.ID == beatmap.ID).ToList())) + .ContinueWith(_ => onCompletion); + } + }, + new PopupDialogCancelButton + { + Text = @"No, I'm still attached.", + }, + }; + } + + [BackgroundDependencyLoader] + private void load(ScoreManager scoreManager) + { + this.scoreManager = scoreManager; + } + } +} diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs index adf989ee62..61370f7ce3 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboard.cs @@ -55,7 +55,7 @@ namespace osu.Game.Screens.Select.Leaderboards { if (Scope == BeatmapLeaderboardScope.Local) { - Scores = scoreManager.QueryScores(s => s.Beatmap.ID == Beatmap.ID).ToArray(); + Scores = scoreManager.QueryScores(s => !s.DeletePending && s.Beatmap.ID == Beatmap.ID).ToArray(); PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores; return null; } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 0c5871f483..5d4ead69d6 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -1,11 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Collections.Generic; -using System.Linq; -using osuTK; -using osuTK.Input; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; @@ -13,8 +8,8 @@ using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Logging; using osu.Framework.Input.Events; +using osu.Framework.Logging; using osu.Framework.Screens; using osu.Framework.Threading; using osu.Game.Beatmaps; @@ -31,7 +26,12 @@ using osu.Game.Screens.Menu; using osu.Game.Screens.Play; using osu.Game.Screens.Select.Options; using osu.Game.Skinning; +using osuTK; using osuTK.Graphics; +using osuTK.Input; +using System; +using System.Collections.Generic; +using System.Linq; namespace osu.Game.Screens.Select { @@ -226,7 +226,7 @@ namespace osu.Game.Screens.Select BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.fa_trash, colours.Pink, () => delete(Beatmap.Value.BeatmapSetInfo), Key.Number4, float.MaxValue); BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.fa_times_circle_o, colours.Purple, null, Key.Number1); - BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.fa_eraser, colours.Purple, null, Key.Number2); + BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.fa_eraser, colours.Purple, () => clearScores(Beatmap.Value.BeatmapInfo), Key.Number2); } if (this.beatmaps == null) @@ -582,7 +582,7 @@ namespace osu.Game.Screens.Select } } - private void onBeatmapSetAdded(BeatmapSetInfo s, bool existing, bool silent) => Carousel.UpdateBeatmapSet(s); + private void onBeatmapSetAdded(BeatmapSetInfo s, bool existing) => Carousel.UpdateBeatmapSet(s); private void onBeatmapSetRemoved(BeatmapSetInfo s) => Carousel.RemoveBeatmapSet(s); private void onBeatmapRestored(BeatmapInfo b) => Carousel.UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID)); private void onBeatmapHidden(BeatmapInfo b) => Carousel.UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID)); @@ -621,6 +621,15 @@ namespace osu.Game.Screens.Select dialogOverlay?.Push(new BeatmapDeleteDialog(beatmap)); } + private void clearScores(BeatmapInfo beatmap) + { + if (beatmap == null || beatmap.ID <= 0) return; + + dialogOverlay?.Push(new BeatmapClearScoresDialog(beatmap, () => + // schedule done here rather than inside the dialog as the dialog may fade out and never callback. + Schedule(() => BeatmapDetails.Leaderboard.RefreshScores()))); + } + public override bool OnPressed(GlobalAction action) { if (!this.IsCurrentScreen()) return false; diff --git a/osu.Game/Skinning/SkinStore.cs b/osu.Game/Skinning/SkinStore.cs index e80b03b935..31cadb0a24 100644 --- a/osu.Game/Skinning/SkinStore.cs +++ b/osu.Game/Skinning/SkinStore.cs @@ -1,22 +1,16 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; -using Microsoft.EntityFrameworkCore; using osu.Framework.Platform; using osu.Game.Database; namespace osu.Game.Skinning { - public class SkinStore : MutableDatabaseBackedStore + public class SkinStore : MutableDatabaseBackedStoreWithFileIncludes { public SkinStore(DatabaseContextFactory contextFactory, Storage storage = null) : base(contextFactory, storage) { } - - protected override IQueryable AddIncludesForConsumption(IQueryable query) => - base.AddIncludesForConsumption(query) - .Include(s => s.Files).ThenInclude(f => f.FileInfo); } } diff --git a/osu.Game/Tests/Beatmaps/TestBeatmap.cs b/osu.Game/Tests/Beatmaps/TestBeatmap.cs index d1263984ac..b77a8508ad 100644 --- a/osu.Game/Tests/Beatmaps/TestBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestBeatmap.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using System.IO; using System.Text; using osu.Game.Beatmaps; @@ -21,6 +22,18 @@ namespace osu.Game.Tests.Beatmaps HitObjects = baseBeatmap.HitObjects; BeatmapInfo.Ruleset = ruleset; + BeatmapInfo.BeatmapSet.Metadata = BeatmapInfo.Metadata; + BeatmapInfo.BeatmapSet.Beatmaps = new List { BeatmapInfo }; + BeatmapInfo.BeatmapSet.OnlineInfo = new BeatmapSetOnlineInfo + { + Status = BeatmapSetOnlineStatus.Ranked, + Covers = new BeatmapSetOnlineCovers + { + Cover = "https://assets.ppy.sh/beatmaps/163112/covers/cover.jpg", + Card = "https://assets.ppy.sh/beatmaps/163112/covers/card.jpg", + List = "https://assets.ppy.sh/beatmaps/163112/covers/list.jpg" + } + }; } private static Beatmap createTestBeatmap() diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index f8d89ec8a5..8f9a7cd14b 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -16,7 +16,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 16eeb46683..499878347b 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -35,7 +35,7 @@ prompt 4 - iPhone Developer + iPhone Distribution true true Entitlements.plist @@ -105,12 +105,12 @@ - - + + - \ No newline at end of file + diff --git a/osu.iOS/AppDelegate.cs b/osu.iOS/AppDelegate.cs index 97621e1e52..058e246ed8 100644 --- a/osu.iOS/AppDelegate.cs +++ b/osu.iOS/AppDelegate.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using Foundation; @@ -10,6 +10,6 @@ namespace osu.iOS [Register("AppDelegate")] public class AppDelegate : GameAppDelegate { - protected override Framework.Game CreateGame() => new OsuGame(); + protected override Framework.Game CreateGame() => new OsuGameIOS(); } } diff --git a/osu.iOS/Info.plist b/osu.iOS/Info.plist index fe711fc03c..0627feab78 100644 --- a/osu.iOS/Info.plist +++ b/osu.iOS/Info.plist @@ -2,6 +2,14 @@ + CFBundleIdentifier + sh.ppy.osulazer + CFBundleName + osu! + CFBundleShortVersionString + 0.1 + CFBundleVersion + 0.1.0 LSRequiresIPhoneOS MinimumOSVersion @@ -17,6 +25,10 @@ armv7 + UIRequiresFullScreen + + UIStatusBarHidden + UISupportedInterfaceOrientations UIInterfaceOrientationPortrait @@ -26,17 +38,5 @@ XSAppIconAssets Assets.xcassets/AppIcon.appiconset - UIStatusBarHidden - - UIRequiresFullScreen - - CFBundleIdentifier - sh.ppy.osulazer - CFBundleName - osu! - CFBundleShortVersionString - 0.1 - CFBundleVersion - 0.1.1 diff --git a/osu.iOS/OsuGameIOS.cs b/osu.iOS/OsuGameIOS.cs new file mode 100644 index 0000000000..6cf18df9a6 --- /dev/null +++ b/osu.iOS/OsuGameIOS.cs @@ -0,0 +1,14 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using Foundation; +using osu.Game; + +namespace osu.iOS +{ + public class OsuGameIOS : OsuGame + { + public override Version AssemblyVersion => new Version(NSBundle.MainBundle.InfoDictionary["CFBundleVersion"].ToString()); + } +} diff --git a/osu.iOS/osu.iOS.csproj b/osu.iOS/osu.iOS.csproj index 9c69dd40ba..9df3ad5516 100644 --- a/osu.iOS/osu.iOS.csproj +++ b/osu.iOS/osu.iOS.csproj @@ -48,6 +48,7 @@ + diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index a4551422cb..3c6a6dd2a9 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -219,7 +219,7 @@ Code Cleanup (peppy) ExpressionBody ExpressionBody - False + True True True True @@ -230,6 +230,8 @@ True NEXT_LINE NEXT_LINE + 1 + 1 True NEVER NEVER