diff --git a/.gitignore b/.gitignore
index 0e2850a01c..e60058ab35 100644
--- a/.gitignore
+++ b/.gitignore
@@ -198,6 +198,7 @@ ClientBin/
*.publishsettings
node_modules/
orleans.codegen.cs
+Resource.designer.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
diff --git a/README.md b/README.md
index 55f2eebec9..2c330e403c 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,7 @@
+
+
+
+
# osu!
[](https://ci.appveyor.com/project/peppy/osu) [](https://www.codefactor.io/repository/github/ppy/osu) [](https://discord.gg/ppy)
@@ -22,8 +26,6 @@ Detailed changelogs are published on the [official osu! site](https://osu.ppy.sh
### Releases
-
-
If you are not interested in developing the game, you can still consume our [binary releases](https://github.com/ppy/osu/releases).
**Latest build:**
@@ -92,11 +94,13 @@ Code analysis can be run with `powershell ./build.ps1` or `build.sh`. This is cu
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.
-Please make sure you are familiar with the [development and testing](https://github.com/ppy/osu-framework/wiki/Development-and-Testing) procedure we have set up. New component development, and where possible, bug fixing and debugging existing components **should always be done under VisualTests**.
+If you're unsure of what you can help with, check out the [list of open issues](https://github.com/ppy/osu/issues) (especially those with the ["good first issue"](https://github.com/ppy/osu/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3A%22good+first+issue%22) label).
-Contributions can be made via pull requests to this repository. We hope to credit and reward larger contributions via a [bounty system](https://www.bountysource.com/teams/ppy). If you're unsure of what you can help with, check out the [list of open issues](https://github.com/ppy/osu/issues).
+Before starting, please make sure you are familiar with the [development and testing](https://github.com/ppy/osu-framework/wiki/Development-and-Testing) procedure we have set up. New component development, and where possible, bug fixing and debugging existing components **should always be done under VisualTests**.
-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.
+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. We welcome all feedback so we can make contributing to this project as pain-free as possible.
+
+For those interested, we love to reward quality contributions via bounties, paid out via paypal or osu! supporter tags. Don't hesitate to [request a bounty](https://docs.google.com/forms/d/e/1FAIpQLSet_8iFAgPMG526pBZ2Kic6HSh7XPM3fE8xPcnWNkMzINDdYg/viewform) for your work on this project.
## Licence
diff --git a/assets/lazer.png b/assets/lazer.png
new file mode 100644
index 0000000000..075a8e7184
Binary files /dev/null and b/assets/lazer.png differ
diff --git a/osu.Android.props b/osu.Android.props
new file mode 100644
index 0000000000..400b43dbe6
--- /dev/null
+++ b/osu.Android.props
@@ -0,0 +1,69 @@
+
+
+ bin\$(Configuration)
+ 4
+ 2.0
+ false
+ false
+ default
+ Library
+ 512
+ Off
+ True
+ Xamarin.Android.Net.AndroidClientHandler
+ v8.1
+ false
+
+
+ True
+ portable
+ False
+ DEBUG;TRACE
+ prompt
+ false
+ false
+ SdkOnly
+ true
+ false
+ cjk,mideast,other,rare,west
+ true
+ armeabi-v7a;x86;arm64-v8a
+ true
+
+
+ false
+ None
+ True
+ prompt
+ true
+ false
+ SdkOnly
+ False
+ true
+ cjk,mideast,other,rare,west
+ true
+ armeabi-v7a;x86;arm64-v8a
+ true
+
+
+
+ osu.licenseheader
+
+
+ Always
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/osu.Android.sln b/osu.Android.sln
new file mode 100644
index 0000000000..ebf2c55cb4
--- /dev/null
+++ b/osu.Android.sln
@@ -0,0 +1,126 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.28516.95
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game", "osu.Game\osu.Game.csproj", "{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Osu", "osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj", "{C92A607B-1FDD-4954-9F92-03FF547D9080}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Catch", "osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj", "{58F6C80C-1253-4A0E-A465-B8C85EBEADF3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Taiko", "osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj", "{F167E17A-7DE6-4AF5-B920-A5112296C695}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "osu.Game.Rulesets.Mania", "osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj", "{48F4582B-7687-4621-9CBE-5C24197CB536}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Android", "osu.Android\osu.Android.csproj", "{D1D5F9A8-B40B-40E6-B02F-482D03346D3D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Catch.Tests.Android", "osu.Game.Rulesets.Catch.Tests.Android\osu.Game.Rulesets.Catch.Tests.Android.csproj", "{C5379ECB-3A94-4D2F-AC3B-2615AC23EB0D}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Mania.Tests.Android", "osu.Game.Rulesets.Mania.Tests.Android\osu.Game.Rulesets.Mania.Tests.Android.csproj", "{531F1092-DB27-445D-AA33-2A77C7187C99}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Osu.Tests.Android", "osu.Game.Rulesets.Osu.Tests.Android\osu.Game.Rulesets.Osu.Tests.Android.csproj", "{90CAB706-39CB-4B93-9629-3218A6FF8E9B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Rulesets.Taiko.Tests.Android", "osu.Game.Rulesets.Taiko.Tests.Android\osu.Game.Rulesets.Taiko.Tests.Android.csproj", "{3701A0A1-8476-42C6-B5C4-D24129B4A484}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "osu.Game.Tests.Android", "osu.Game.Tests.Android\osu.Game.Tests.Android.csproj", "{5CC222DC-5716-4499-B897-DCBDDA4A5CF9}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C92A607B-1FDD-4954-9F92-03FF547D9080}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C92A607B-1FDD-4954-9F92-03FF547D9080}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C92A607B-1FDD-4954-9F92-03FF547D9080}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C92A607B-1FDD-4954-9F92-03FF547D9080}.Release|Any CPU.Build.0 = Release|Any CPU
+ {58F6C80C-1253-4A0E-A465-B8C85EBEADF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {58F6C80C-1253-4A0E-A465-B8C85EBEADF3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {58F6C80C-1253-4A0E-A465-B8C85EBEADF3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {58F6C80C-1253-4A0E-A465-B8C85EBEADF3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F167E17A-7DE6-4AF5-B920-A5112296C695}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F167E17A-7DE6-4AF5-B920-A5112296C695}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F167E17A-7DE6-4AF5-B920-A5112296C695}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F167E17A-7DE6-4AF5-B920-A5112296C695}.Release|Any CPU.Build.0 = Release|Any CPU
+ {48F4582B-7687-4621-9CBE-5C24197CB536}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {48F4582B-7687-4621-9CBE-5C24197CB536}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {48F4582B-7687-4621-9CBE-5C24197CB536}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {48F4582B-7687-4621-9CBE-5C24197CB536}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D1D5F9A8-B40B-40E6-B02F-482D03346D3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D1D5F9A8-B40B-40E6-B02F-482D03346D3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D1D5F9A8-B40B-40E6-B02F-482D03346D3D}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {D1D5F9A8-B40B-40E6-B02F-482D03346D3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D1D5F9A8-B40B-40E6-B02F-482D03346D3D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D1D5F9A8-B40B-40E6-B02F-482D03346D3D}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {C5379ECB-3A94-4D2F-AC3B-2615AC23EB0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C5379ECB-3A94-4D2F-AC3B-2615AC23EB0D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C5379ECB-3A94-4D2F-AC3B-2615AC23EB0D}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {C5379ECB-3A94-4D2F-AC3B-2615AC23EB0D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C5379ECB-3A94-4D2F-AC3B-2615AC23EB0D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C5379ECB-3A94-4D2F-AC3B-2615AC23EB0D}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {531F1092-DB27-445D-AA33-2A77C7187C99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {531F1092-DB27-445D-AA33-2A77C7187C99}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {531F1092-DB27-445D-AA33-2A77C7187C99}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {531F1092-DB27-445D-AA33-2A77C7187C99}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {531F1092-DB27-445D-AA33-2A77C7187C99}.Release|Any CPU.Build.0 = Release|Any CPU
+ {531F1092-DB27-445D-AA33-2A77C7187C99}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {90CAB706-39CB-4B93-9629-3218A6FF8E9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {90CAB706-39CB-4B93-9629-3218A6FF8E9B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {90CAB706-39CB-4B93-9629-3218A6FF8E9B}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {90CAB706-39CB-4B93-9629-3218A6FF8E9B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {90CAB706-39CB-4B93-9629-3218A6FF8E9B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {90CAB706-39CB-4B93-9629-3218A6FF8E9B}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {3701A0A1-8476-42C6-B5C4-D24129B4A484}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3701A0A1-8476-42C6-B5C4-D24129B4A484}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3701A0A1-8476-42C6-B5C4-D24129B4A484}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {3701A0A1-8476-42C6-B5C4-D24129B4A484}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3701A0A1-8476-42C6-B5C4-D24129B4A484}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3701A0A1-8476-42C6-B5C4-D24129B4A484}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {5CC222DC-5716-4499-B897-DCBDDA4A5CF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5CC222DC-5716-4499-B897-DCBDDA4A5CF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5CC222DC-5716-4499-B897-DCBDDA4A5CF9}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
+ {5CC222DC-5716-4499-B897-DCBDDA4A5CF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5CC222DC-5716-4499-B897-DCBDDA4A5CF9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5CC222DC-5716-4499-B897-DCBDDA4A5CF9}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {671B0BEC-2403-45B0-9357-2C97CC517668}
+ EndGlobalSection
+ GlobalSection(MonoDevelopProperties) = preSolution
+ Policies = $0
+ $0.TextStylePolicy = $1
+ $1.EolMarker = Windows
+ $1.inheritsSet = VisualStudio
+ $1.inheritsScope = text/plain
+ $1.scope = text/x-csharp
+ $0.CSharpFormattingPolicy = $2
+ $2.IndentSwitchSection = True
+ $2.NewLinesForBracesInProperties = True
+ $2.NewLinesForBracesInAccessors = True
+ $2.NewLinesForBracesInAnonymousMethods = True
+ $2.NewLinesForBracesInControlBlocks = True
+ $2.NewLinesForBracesInAnonymousTypes = True
+ $2.NewLinesForBracesInObjectCollectionArrayInitializers = True
+ $2.NewLinesForBracesInLambdaExpressionBody = True
+ $2.NewLineForElse = True
+ $2.NewLineForCatch = True
+ $2.NewLineForFinally = True
+ $2.NewLineForMembersInObjectInit = True
+ $2.NewLineForMembersInAnonymousTypes = True
+ $2.NewLineForClausesInQuery = True
+ $2.SpacingAfterMethodDeclarationName = False
+ $2.SpaceAfterMethodCallName = False
+ $2.SpaceBeforeOpenSquareBracket = False
+ $2.inheritsSet = Mono
+ $2.inheritsScope = text/x-csharp
+ $2.scope = text/x-csharp
+ EndGlobalSection
+EndGlobal
diff --git a/osu.Android.sln.DotSettings b/osu.Android.sln.DotSettings
new file mode 100644
index 0000000000..3f5bd9d34d
--- /dev/null
+++ b/osu.Android.sln.DotSettings
@@ -0,0 +1,815 @@
+
+ True
+ True
+ True
+ True
+ ExplicitlyExcluded
+ ExplicitlyExcluded
+ SOLUTION
+ HINT
+ WARNING
+
+ True
+ WARNING
+ WARNING
+ HINT
+ HINT
+ HINT
+ HINT
+ WARNING
+ WARNING
+ WARNING
+ HINT
+ WARNING
+ HINT
+ SUGGESTION
+ HINT
+ HINT
+ HINT
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ HINT
+ WARNING
+ WARNING
+ HINT
+ WARNING
+ WARNING
+ DO_NOT_SHOW
+ HINT
+ WARNING
+ DO_NOT_SHOW
+ WARNING
+ HINT
+ HINT
+ HINT
+ ERROR
+ HINT
+ HINT
+ HINT
+ WARNING
+ WARNING
+ HINT
+ DO_NOT_SHOW
+ HINT
+ HINT
+ HINT
+ HINT
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ HINT
+ HINT
+ HINT
+ HINT
+ HINT
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ HINT
+ DO_NOT_SHOW
+ DO_NOT_SHOW
+ DO_NOT_SHOW
+ WARNING
+
+ WARNING
+ WARNING
+ WARNING
+ ERROR
+ WARNING
+ WARNING
+ HINT
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ HINT
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ HINT
+ DO_NOT_SHOW
+ DO_NOT_SHOW
+ DO_NOT_SHOW
+ WARNING
+ WARNING
+ HINT
+ WARNING
+ HINT
+ HINT
+ HINT
+ HINT
+ HINT
+ HINT
+ HINT
+
+ HINT
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ WARNING
+ HINT
+ WARNING
+ WARNING
+ HINT
+ HINT
+ WARNING
+ <?xml version="1.0" encoding="utf-16"?><Profile name="Code Cleanup (peppy)"><CSArrangeThisQualifier>True</CSArrangeThisQualifier><CSUseVar><BehavourStyle>CAN_CHANGE_TO_EXPLICIT</BehavourStyle><LocalVariableStyle>ALWAYS_EXPLICIT</LocalVariableStyle><ForeachVariableStyle>ALWAYS_EXPLICIT</ForeachVariableStyle></CSUseVar><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSShortenReferences>True</CSShortenReferences><CSReformatCode>True</CSReformatCode><CSUpdateFileHeader>True</CSUpdateFileHeader><CSCodeStyleAttributes ArrangeTypeAccessModifier="False" ArrangeTypeMemberAccessModifier="False" SortModifiers="True" RemoveRedundantParentheses="True" AddMissingParentheses="False" ArrangeBraces="False" ArrangeAttributes="False" ArrangeArgumentsStyle="False" /><XAMLCollapseEmptyTags>False</XAMLCollapseEmptyTags><CSFixBuiltinTypeReferences>True</CSFixBuiltinTypeReferences><CSArrangeQualifiers>True</CSArrangeQualifiers></Profile>
+ Code Cleanup (peppy)
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ NEXT_LINE
+ NEXT_LINE
+ True
+ NEVER
+ NEVER
+ False
+ NEVER
+ False
+ True
+ False
+ False
+ True
+ True
+ False
+ CHOP_IF_LONG
+ True
+ 200
+ CHOP_IF_LONG
+ False
+ False
+ AABB
+ API
+ BPM
+ GC
+ GL
+ GLSL
+ HID
+ HUD
+ ID
+ IP
+ IPC
+ LTRB
+ MD5
+ NS
+ OS
+ RGB
+ RNG
+ SHA
+ SRGB
+ TK
+ SS
+ PP
+ GMT
+ QAT
+ BNG
+ UI
+ HINT
+ <?xml version="1.0" encoding="utf-16"?>
+<Patterns xmlns="urn:schemas-jetbrains-com:member-reordering-patterns">
+ <TypePattern DisplayName="COM interfaces or structs">
+ <TypePattern.Match>
+ <Or>
+ <And>
+ <Kind Is="Interface" />
+ <Or>
+ <HasAttribute Name="System.Runtime.InteropServices.InterfaceTypeAttribute" />
+ <HasAttribute Name="System.Runtime.InteropServices.ComImport" />
+ </Or>
+ </And>
+ <Kind Is="Struct" />
+ </Or>
+ </TypePattern.Match>
+ </TypePattern>
+ <TypePattern DisplayName="NUnit Test Fixtures" RemoveRegions="All">
+ <TypePattern.Match>
+ <And>
+ <Kind Is="Class" />
+ <HasAttribute Name="NUnit.Framework.TestFixtureAttribute" Inherited="True" />
+ </And>
+ </TypePattern.Match>
+ <Entry DisplayName="Setup/Teardown Methods">
+ <Entry.Match>
+ <And>
+ <Kind Is="Method" />
+ <Or>
+ <HasAttribute Name="NUnit.Framework.SetUpAttribute" Inherited="True" />
+ <HasAttribute Name="NUnit.Framework.TearDownAttribute" Inherited="True" />
+ <HasAttribute Name="NUnit.Framework.FixtureSetUpAttribute" Inherited="True" />
+ <HasAttribute Name="NUnit.Framework.FixtureTearDownAttribute" Inherited="True" />
+ </Or>
+ </And>
+ </Entry.Match>
+ </Entry>
+ <Entry DisplayName="All other members" />
+ <Entry Priority="100" DisplayName="Test Methods">
+ <Entry.Match>
+ <And>
+ <Kind Is="Method" />
+ <HasAttribute Name="NUnit.Framework.TestAttribute" />
+ </And>
+ </Entry.Match>
+ <Entry.SortBy>
+ <Name />
+ </Entry.SortBy>
+ </Entry>
+ </TypePattern>
+ <TypePattern DisplayName="Default Pattern">
+ <Group DisplayName="Fields/Properties">
+ <Group DisplayName="Public Fields">
+ <Entry DisplayName="Constant Fields">
+ <Entry.Match>
+ <And>
+ <Access Is="Public" />
+ <Or>
+ <Kind Is="Constant" />
+ <Readonly />
+ <And>
+ <Static />
+ <Readonly />
+ </And>
+ </Or>
+ </And>
+ </Entry.Match>
+ </Entry>
+ <Entry DisplayName="Static Fields">
+ <Entry.Match>
+ <And>
+ <Access Is="Public" />
+ <Static />
+ <Not>
+ <Readonly />
+ </Not>
+ <Kind Is="Field" />
+ </And>
+ </Entry.Match>
+ </Entry>
+ <Entry DisplayName="Normal Fields">
+ <Entry.Match>
+ <And>
+ <Access Is="Public" />
+ <Not>
+ <Or>
+ <Static />
+ <Readonly />
+ </Or>
+ </Not>
+ <Kind Is="Field" />
+ </And>
+ </Entry.Match>
+ </Entry>
+ </Group>
+ <Entry DisplayName="Public Properties">
+ <Entry.Match>
+ <And>
+ <Access Is="Public" />
+ <Kind Is="Property" />
+ </And>
+ </Entry.Match>
+ </Entry>
+ <Group DisplayName="Internal Fields">
+ <Entry DisplayName="Constant Fields">
+ <Entry.Match>
+ <And>
+ <Access Is="Internal" />
+ <Or>
+ <Kind Is="Constant" />
+ <Readonly />
+ <And>
+ <Static />
+ <Readonly />
+ </And>
+ </Or>
+ </And>
+ </Entry.Match>
+ </Entry>
+ <Entry DisplayName="Static Fields">
+ <Entry.Match>
+ <And>
+ <Access Is="Internal" />
+ <Static />
+ <Not>
+ <Readonly />
+ </Not>
+ <Kind Is="Field" />
+ </And>
+ </Entry.Match>
+ </Entry>
+ <Entry DisplayName="Normal Fields">
+ <Entry.Match>
+ <And>
+ <Access Is="Internal" />
+ <Not>
+ <Or>
+ <Static />
+ <Readonly />
+ </Or>
+ </Not>
+ <Kind Is="Field" />
+ </And>
+ </Entry.Match>
+ </Entry>
+ </Group>
+ <Entry DisplayName="Internal Properties">
+ <Entry.Match>
+ <And>
+ <Access Is="Internal" />
+ <Kind Is="Property" />
+ </And>
+ </Entry.Match>
+ </Entry>
+ <Group DisplayName="Protected Fields">
+ <Entry DisplayName="Constant Fields">
+ <Entry.Match>
+ <And>
+ <Access Is="Protected" />
+ <Or>
+ <Kind Is="Constant" />
+ <Readonly />
+ <And>
+ <Static />
+ <Readonly />
+ </And>
+ </Or>
+ </And>
+ </Entry.Match>
+ </Entry>
+ <Entry DisplayName="Static Fields">
+ <Entry.Match>
+ <And>
+ <Access Is="Protected" />
+ <Static />
+ <Not>
+ <Readonly />
+ </Not>
+ <Kind Is="Field" />
+ </And>
+ </Entry.Match>
+ </Entry>
+ <Entry DisplayName="Normal Fields">
+ <Entry.Match>
+ <And>
+ <Access Is="Protected" />
+ <Not>
+ <Or>
+ <Static />
+ <Readonly />
+ </Or>
+ </Not>
+ <Kind Is="Field" />
+ </And>
+ </Entry.Match>
+ </Entry>
+ </Group>
+ <Entry DisplayName="Protected Properties">
+ <Entry.Match>
+ <And>
+ <Access Is="Protected" />
+ <Kind Is="Property" />
+ </And>
+ </Entry.Match>
+ </Entry>
+ <Group DisplayName="Private Fields">
+ <Entry DisplayName="Constant Fields">
+ <Entry.Match>
+ <And>
+ <Access Is="Private" />
+ <Or>
+ <Kind Is="Constant" />
+ <Readonly />
+ <And>
+ <Static />
+ <Readonly />
+ </And>
+ </Or>
+ </And>
+ </Entry.Match>
+ </Entry>
+ <Entry DisplayName="Static Fields">
+ <Entry.Match>
+ <And>
+ <Access Is="Private" />
+ <Static />
+ <Not>
+ <Readonly />
+ </Not>
+ <Kind Is="Field" />
+ </And>
+ </Entry.Match>
+ </Entry>
+ <Entry DisplayName="Normal Fields">
+ <Entry.Match>
+ <And>
+ <Access Is="Private" />
+ <Not>
+ <Or>
+ <Static />
+ <Readonly />
+ </Or>
+ </Not>
+ <Kind Is="Field" />
+ </And>
+ </Entry.Match>
+ </Entry>
+ </Group>
+ <Entry DisplayName="Private Properties">
+ <Entry.Match>
+ <And>
+ <Access Is="Private" />
+ <Kind Is="Property" />
+ </And>
+ </Entry.Match>
+ </Entry>
+ </Group>
+ <Group DisplayName="Constructor/Destructor">
+ <Entry DisplayName="Ctor">
+ <Entry.Match>
+ <Kind Is="Constructor" />
+ </Entry.Match>
+ </Entry>
+ <Region Name="Disposal">
+ <Entry DisplayName="Dtor">
+ <Entry.Match>
+ <Kind Is="Destructor" />
+ </Entry.Match>
+ </Entry>
+ <Entry DisplayName="Dispose()">
+ <Entry.Match>
+ <And>
+ <Access Is="Public" />
+ <Kind Is="Method" />
+ <Name Is="Dispose" />
+ </And>
+ </Entry.Match>
+ </Entry>
+ <Entry DisplayName="Dispose(true)">
+ <Entry.Match>
+ <And>
+ <Access Is="Protected" />
+ <Or>
+ <Virtual />
+ <Override />
+ </Or>
+ <Kind Is="Method" />
+ <Name Is="Dispose" />
+ </And>
+ </Entry.Match>
+ </Entry>
+ </Region>
+ </Group>
+ <Group DisplayName="Methods">
+ <Group DisplayName="Public">
+ <Entry DisplayName="Static Methods">
+ <Entry.Match>
+ <And>
+ <Access Is="Public" />
+ <Static />
+ <Kind Is="Method" />
+ </And>
+ </Entry.Match>
+ </Entry>
+ <Entry DisplayName="Methods">
+ <Entry.Match>
+ <And>
+ <Access Is="Public" />
+ <Not>
+ <Static />
+ </Not>
+ <Kind Is="Method" />
+ </And>
+ </Entry.Match>
+ </Entry>
+ </Group>
+ <Group DisplayName="Internal">
+ <Entry DisplayName="Static Methods">
+ <Entry.Match>
+ <And>
+ <Access Is="Internal" />
+ <Static />
+ <Kind Is="Method" />
+ </And>
+ </Entry.Match>
+ </Entry>
+ <Entry DisplayName="Methods">
+ <Entry.Match>
+ <And>
+ <Access Is="Internal" />
+ <Not>
+ <Static />
+ </Not>
+ <Kind Is="Method" />
+ </And>
+ </Entry.Match>
+ </Entry>
+ </Group>
+ <Group DisplayName="Protected">
+ <Entry DisplayName="Static Methods">
+ <Entry.Match>
+ <And>
+ <Access Is="Protected" />
+ <Static />
+ <Kind Is="Method" />
+ </And>
+ </Entry.Match>
+ </Entry>
+ <Entry DisplayName="Methods">
+ <Entry.Match>
+ <And>
+ <Access Is="Protected" />
+ <Not>
+ <Static />
+ </Not>
+ <Kind Is="Method" />
+ </And>
+ </Entry.Match>
+ </Entry>
+ </Group>
+ <Group DisplayName="Private">
+ <Entry DisplayName="Static Methods">
+ <Entry.Match>
+ <And>
+ <Access Is="Private" />
+ <Static />
+ <Kind Is="Method" />
+ </And>
+ </Entry.Match>
+ </Entry>
+ <Entry DisplayName="Methods">
+ <Entry.Match>
+ <And>
+ <Access Is="Private" />
+ <Not>
+ <Static />
+ </Not>
+ <Kind Is="Method" />
+ </And>
+ </Entry.Match>
+ </Entry>
+ </Group>
+ </Group>
+ </TypePattern>
+</Patterns>
+ Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
+See the LICENCE file in the repository root for full licence text.
+
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" />
+ <Policy Inspect="False" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb"><ExtraRule Prefix="_" Suffix="" Style="aaBb" /></Policy>
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aa_bb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Private" Description="private methods"><ElementKinds><Kind Name="ASYNC_METHOD" /><Kind Name="METHOD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy>
+ <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Protected, ProtectedInternal, Internal, Public" Description="internal/protected/public methods"><ElementKinds><Kind Name="ASYNC_METHOD" /><Kind Name="METHOD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy>
+ <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Private" Description="private properties"><ElementKinds><Kind Name="PROPERTY" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy>
+ <Policy><Descriptor Staticness="Static, Instance" AccessRightKinds="Protected, ProtectedInternal, Internal, Public" Description="internal/protected/public properties"><ElementKinds><Kind Name="PROPERTY" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /></Policy>
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ o!f – Object Initializer: Anchor&Origin
+ True
+ constant("Centre")
+ 0
+ True
+ True
+ 2.0
+ InCSharpFile
+ ofao
+ True
+ Anchor = Anchor.$anchor$,
+Origin = Anchor.$anchor$,
+ True
+ True
+ o!f – InternalChildren = []
+ True
+ True
+ 2.0
+ InCSharpFile
+ ofic
+ True
+ InternalChildren = new Drawable[]
+{
+ $END$
+};
+ True
+ True
+ o!f – new GridContainer { .. }
+ True
+ True
+ 2.0
+ InCSharpFile
+ ofgc
+ True
+ new GridContainer
+{
+ RelativeSizeAxes = Axes.Both,
+ Content = new[]
+ {
+ new Drawable[] { $END$ },
+ new Drawable[] { }
+ }
+};
+ True
+ True
+ o!f – new FillFlowContainer { .. }
+ True
+ True
+ 2.0
+ InCSharpFile
+ offf
+ True
+ new FillFlowContainer
+{
+ RelativeSizeAxes = Axes.Both,
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ $END$
+ }
+},
+ True
+ True
+ o!f – new Container { .. }
+ True
+ True
+ 2.0
+ InCSharpFile
+ ofcont
+ True
+ new Container
+{
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ $END$
+ }
+},
+ True
+ True
+ o!f – BackgroundDependencyLoader load()
+ True
+ True
+ 2.0
+ InCSharpFile
+ ofbdl
+ True
+ [BackgroundDependencyLoader]
+private void load()
+{
+ $END$
+}
+ True
+ True
+ o!f – new Box { .. }
+ True
+ True
+ 2.0
+ InCSharpFile
+ ofbox
+ True
+ new Box
+{
+ Colour = Color4.Black,
+ RelativeSizeAxes = Axes.Both,
+},
+ True
+ True
+ o!f – Children = []
+ True
+ True
+ 2.0
+ InCSharpFile
+ ofc
+ True
+ Children = new Drawable[]
+{
+ $END$
+};
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
diff --git a/osu.Android/OsuGameActivity.cs b/osu.Android/OsuGameActivity.cs
new file mode 100644
index 0000000000..762a9c418d
--- /dev/null
+++ b/osu.Android/OsuGameActivity.cs
@@ -0,0 +1,25 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using Android.App;
+using Android.Content.PM;
+using Android.OS;
+using Android.Views;
+using osu.Framework.Android;
+
+namespace osu.Android
+{
+ [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullSensor, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = true)]
+ public class OsuGameActivity : AndroidGameActivity
+ {
+ protected override Framework.Game CreateGame() => new OsuGameAndroid();
+
+ protected override void OnCreate(Bundle savedInstanceState)
+ {
+ base.OnCreate(savedInstanceState);
+
+ Window.AddFlags(WindowManagerFlags.Fullscreen);
+ Window.AddFlags(WindowManagerFlags.KeepScreenOn);
+ }
+ }
+}
diff --git a/osu.Android/OsuGameAndroid.cs b/osu.Android/OsuGameAndroid.cs
new file mode 100644
index 0000000000..d9bdd9c0c2
--- /dev/null
+++ b/osu.Android/OsuGameAndroid.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 Android.App;
+using osu.Game;
+
+namespace osu.Android
+{
+ public class OsuGameAndroid : OsuGame
+ {
+ public override Version AssemblyVersion => new Version(Application.Context.ApplicationContext.PackageManager.GetPackageInfo(Application.Context.ApplicationContext.PackageName, 0).VersionName);
+ }
+}
diff --git a/osu.Android/Properties/AndroidManifest.xml b/osu.Android/Properties/AndroidManifest.xml
new file mode 100644
index 0000000000..326d32f7ab
--- /dev/null
+++ b/osu.Android/Properties/AndroidManifest.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/osu.Android/Resources/drawable/lazer.png b/osu.Android/Resources/drawable/lazer.png
new file mode 100644
index 0000000000..075a8e7184
Binary files /dev/null and b/osu.Android/Resources/drawable/lazer.png differ
diff --git a/osu.Android/bass.dll b/osu.Android/bass.dll
new file mode 100644
index 0000000000..3cd403c3ac
Binary files /dev/null and b/osu.Android/bass.dll differ
diff --git a/osu.Android/lib/arm64-v8a/libbass.so b/osu.Android/lib/arm64-v8a/libbass.so
new file mode 100644
index 0000000000..d5c24b3e4b
Binary files /dev/null and b/osu.Android/lib/arm64-v8a/libbass.so differ
diff --git a/osu.Android/lib/arm64-v8a/libbass_fx.so b/osu.Android/lib/arm64-v8a/libbass_fx.so
new file mode 100644
index 0000000000..e498b10117
Binary files /dev/null and b/osu.Android/lib/arm64-v8a/libbass_fx.so differ
diff --git a/osu.Android/lib/armeabi-v7a/libbass.so b/osu.Android/lib/armeabi-v7a/libbass.so
new file mode 100644
index 0000000000..f7d23b9052
Binary files /dev/null and b/osu.Android/lib/armeabi-v7a/libbass.so differ
diff --git a/osu.Android/lib/armeabi-v7a/libbass_fx.so b/osu.Android/lib/armeabi-v7a/libbass_fx.so
new file mode 100644
index 0000000000..006e2feb30
Binary files /dev/null and b/osu.Android/lib/armeabi-v7a/libbass_fx.so differ
diff --git a/osu.Android/lib/x86/libbass.so b/osu.Android/lib/x86/libbass.so
new file mode 100644
index 0000000000..b0f758a42b
Binary files /dev/null and b/osu.Android/lib/x86/libbass.so differ
diff --git a/osu.Android/lib/x86/libbass_fx.so b/osu.Android/lib/x86/libbass_fx.so
new file mode 100644
index 0000000000..526dca39ca
Binary files /dev/null and b/osu.Android/lib/x86/libbass_fx.so differ
diff --git a/osu.Android/osu.Android.csproj b/osu.Android/osu.Android.csproj
new file mode 100644
index 0000000000..42a3185cd1
--- /dev/null
+++ b/osu.Android/osu.Android.csproj
@@ -0,0 +1,50 @@
+
+
+
+
+ Debug
+ AnyCPU
+ 8.0.30703
+ 2.0
+ {D1D5F9A8-B40B-40E6-B02F-482D03346D3D}
+ {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ {122416d6-6b49-4ee2-a1e8-b825f31c79fe}
+ osu.Android
+ osu.Android
+ Properties\AndroidManifest.xml
+ armeabi-v7a;x86;arm64-v8a
+
+
+
+
+
+
+
+
+
+
+ {58f6c80c-1253-4a0e-a465-b8c85ebeadf3}
+ osu.Game.Rulesets.Catch
+
+
+ {48f4582b-7687-4621-9cbe-5c24197cb536}
+ osu.Game.Rulesets.Mania
+
+
+ {c92a607b-1fdd-4954-9f92-03ff547d9080}
+ osu.Game.Rulesets.Osu
+
+
+ {f167e17a-7de6-4af5-b920-a5112296c695}
+ osu.Game.Rulesets.Taiko
+
+
+ {2a66dd92-adb1-4994-89e2-c94e04acda0d}
+ osu.Game
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/osu.Desktop/Updater/SquirrelUpdateManager.cs b/osu.Desktop/Updater/SquirrelUpdateManager.cs
index e2c7a5e892..78a1e680ec 100644
--- a/osu.Desktop/Updater/SquirrelUpdateManager.cs
+++ b/osu.Desktop/Updater/SquirrelUpdateManager.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 System;
@@ -25,11 +25,7 @@ namespace osu.Desktop.Updater
private UpdateManager updateManager;
private NotificationOverlay notificationOverlay;
- public void PrepareUpdate()
- {
- // Squirrel returns execution to us after the update process is started, so it's safe to use Wait() here
- UpdateManager.RestartAppWhenExited().Wait();
- }
+ public Task PrepareUpdateAsync() => UpdateManager.RestartAppWhenExited();
[BackgroundDependencyLoader]
private void load(NotificationOverlay notification, OsuGameBase game)
@@ -46,7 +42,7 @@ namespace osu.Desktop.Updater
private async void checkForUpdateAsync(bool useDeltaPatching = true, UpdateProgressNotification notification = null)
{
//should we schedule a retry on completion of this check?
- bool scheduleRetry = true;
+ bool scheduleRecheck = true;
try
{
@@ -86,10 +82,11 @@ namespace osu.Desktop.Updater
//could fail if deltas are unavailable for full update path (https://github.com/Squirrel/Squirrel.Windows/issues/959)
//try again without deltas.
checkForUpdateAsync(false, notification);
- scheduleRetry = false;
+ scheduleRecheck = false;
}
else
{
+ notification.State = ProgressNotificationState.Cancelled;
Logger.Error(e, @"update failed!");
}
}
@@ -100,11 +97,8 @@ namespace osu.Desktop.Updater
}
finally
{
- if (scheduleRetry)
+ if (scheduleRecheck)
{
- if (notification != null)
- notification.State = ProgressNotificationState.Cancelled;
-
//check again in 30 minutes.
Scheduler.AddDelayed(() => checkForUpdateAsync(), 60000 * 30);
}
@@ -134,8 +128,8 @@ namespace osu.Desktop.Updater
Text = @"Update ready to install. Click to restart!",
Activated = () =>
{
- updateManager.PrepareUpdate();
- game.GracefullyExit();
+ updateManager.PrepareUpdateAsync()
+ .ContinueWith(_ => updateManager.Schedule(() => game.GracefullyExit()));
return true;
}
};
diff --git a/osu.Game.Rulesets.Catch.Tests.Android/MainActivity.cs b/osu.Game.Rulesets.Catch.Tests.Android/MainActivity.cs
new file mode 100644
index 0000000000..d918305f3d
--- /dev/null
+++ b/osu.Game.Rulesets.Catch.Tests.Android/MainActivity.cs
@@ -0,0 +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 Android.App;
+using Android.Content.PM;
+using osu.Framework.Android;
+using osu.Game.Tests;
+
+namespace osu.Game.Rulesets.Catch.Tests.Android
+{
+ [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
+ public class MainActivity : AndroidGameActivity
+ {
+ protected override Framework.Game CreateGame() => new OsuTestBrowser();
+ }
+}
diff --git a/osu.Game.Rulesets.Catch.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Rulesets.Catch.Tests.Android/Properties/AndroidManifest.xml
new file mode 100644
index 0000000000..b04b0718f5
--- /dev/null
+++ b/osu.Game.Rulesets.Catch.Tests.Android/Properties/AndroidManifest.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj b/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj
new file mode 100644
index 0000000000..88b420ffad
--- /dev/null
+++ b/osu.Game.Rulesets.Catch.Tests.Android/osu.Game.Rulesets.Catch.Tests.Android.csproj
@@ -0,0 +1,39 @@
+
+
+
+
+ Debug
+ AnyCPU
+ 8.0.30703
+ 2.0
+ {C5379ECB-3A94-4D2F-AC3B-2615AC23EB0D}
+ {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ {122416d6-6b49-4ee2-a1e8-b825f31c79fe}
+ osu.Game.Rulesets.Catch.Tests
+ osu.Game.Rulesets.Catch.Tests.Android
+ Properties\AndroidManifest.xml
+ armeabi-v7a;x86;arm64-v8a
+
+
+
+
+
+
+
+
+
+ %(RecursiveDir)%(Filename)%(Extension)
+
+
+
+
+ {58f6c80c-1253-4a0e-a465-b8c85ebeadf3}
+ osu.Game.Rulesets.Catch
+
+
+ {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}
+ osu.Game
+
+
+
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs
new file mode 100644
index 0000000000..7a9b61c60c
--- /dev/null
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs
@@ -0,0 +1,160 @@
+// 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 osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Catch.Objects;
+using osu.Game.Rulesets.Catch.Objects.Drawable;
+using osu.Game.Rulesets.Catch.UI;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Tests.Visual;
+using osuTK;
+
+namespace osu.Game.Rulesets.Catch.Tests
+{
+ public class TestSceneDrawableHitObjects : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(CatcherArea.Catcher),
+ typeof(DrawableCatchRuleset),
+ typeof(DrawableFruit),
+ typeof(DrawableJuiceStream),
+ typeof(DrawableBanana)
+ };
+
+ private DrawableCatchRuleset drawableRuleset;
+ private double playfieldTime => drawableRuleset.Playfield.Time.Current;
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ var controlPointInfo = new ControlPointInfo();
+ controlPointInfo.TimingPoints.Add(new TimingControlPoint());
+
+ WorkingBeatmap beatmap = CreateWorkingBeatmap(new Beatmap
+ {
+ HitObjects = new List { new Fruit() },
+ BeatmapInfo = new BeatmapInfo
+ {
+ BaseDifficulty = new BeatmapDifficulty(),
+ Metadata = new BeatmapMetadata
+ {
+ Artist = @"Unknown",
+ Title = @"You're breathtaking",
+ AuthorString = @"Everyone",
+ },
+ Ruleset = new CatchRuleset().RulesetInfo
+ },
+ ControlPointInfo = controlPointInfo
+ });
+
+ Add(new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Children = new[]
+ {
+ drawableRuleset = new DrawableCatchRuleset(new CatchRuleset(), beatmap, Array.Empty())
+ }
+ });
+
+ AddStep("miss fruits", () => spawnFruits());
+ AddStep("hit fruits", () => spawnFruits(true));
+ AddStep("miss juicestream", () => spawnJuiceStream());
+ AddStep("hit juicestream", () => spawnJuiceStream(true));
+ AddStep("miss bananas", () => spawnBananas());
+ AddStep("hit bananas", () => spawnBananas(true));
+ }
+
+ private void spawnFruits(bool hit = false)
+ {
+ for (int i = 1; i <= 4; i++)
+ {
+ var fruit = new Fruit
+ {
+ X = getXCoords(hit),
+ LastInCombo = i % 4 == 0,
+ StartTime = playfieldTime + 800 + (200 * i)
+ };
+
+ fruit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
+
+ addToPlayfield(new DrawableFruit(fruit));
+ }
+ }
+
+ private void spawnJuiceStream(bool hit = false)
+ {
+ var xCoords = getXCoords(hit);
+
+ var juice = new JuiceStream
+ {
+ X = xCoords,
+ StartTime = playfieldTime + 1000,
+ Path = new SliderPath(PathType.Linear, new[]
+ {
+ Vector2.Zero,
+ new Vector2(0, 200)
+ })
+ };
+
+ juice.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
+
+ if (juice.NestedHitObjects.Last() is CatchHitObject tail)
+ tail.LastInCombo = true; // usually the (Catch)BeatmapProcessor would do this for us when necessary
+
+ addToPlayfield(new DrawableJuiceStream(juice, drawableRuleset.CreateDrawableRepresentation));
+ }
+
+ private void spawnBananas(bool hit = false)
+ {
+ for (int i = 1; i <= 4; i++)
+ {
+ var banana = new Banana
+ {
+ X = getXCoords(hit),
+ LastInCombo = i % 4 == 0,
+ StartTime = playfieldTime + 800 + (200 * i)
+ };
+
+ banana.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
+
+ addToPlayfield(new DrawableBanana(banana));
+ }
+ }
+
+ private float getXCoords(bool hit)
+ {
+ const float x_offset = 0.2f;
+ float xCoords = drawableRuleset.Playfield.Width / 2;
+
+ if (drawableRuleset.Playfield is CatchPlayfield catchPlayfield)
+ catchPlayfield.CatcherArea.MovableCatcher.X = xCoords - x_offset;
+
+ if (hit)
+ xCoords -= x_offset;
+ else
+ xCoords += x_offset;
+
+ return xCoords;
+ }
+
+ private void addToPlayfield(DrawableCatchHitObject drawable)
+ {
+ foreach (var mod in Mods.Value.OfType())
+ mod.ApplyToDrawableHitObjects(new[] { drawable });
+
+ drawableRuleset.Playfield.Add(drawable);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjectsHidden.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjectsHidden.cs
new file mode 100644
index 0000000000..f6d26addaa
--- /dev/null
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjectsHidden.cs
@@ -0,0 +1,20 @@
+// 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 osu.Game.Rulesets.Catch.Mods;
+
+namespace osu.Game.Rulesets.Catch.Tests
+{
+ public class TestSceneDrawableHitObjectsHidden : TestSceneDrawableHitObjects
+ {
+ public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(CatchModHidden) }).ToList();
+
+ public TestSceneDrawableHitObjectsHidden()
+ {
+ Mods.Value = new[] { new CatchModHidden() };
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs
index 9cbff8c5d3..a603d96201 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using NUnit.Framework;
-using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Tests.Visual;
@@ -17,8 +16,8 @@ namespace osu.Game.Rulesets.Catch.Tests
{
}
- [BackgroundDependencyLoader]
- private void load()
+ [Test]
+ public void TestHyperDash()
{
AddAssert("First note is hyperdash", () => Beatmap.Value.Beatmap.HitObjects[0] is Fruit f && f.HyperDash);
}
diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs
index 9990b01427..606a935229 100644
--- a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs
+++ b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs
@@ -1,7 +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.Linq;
+using osu.Framework.Graphics;
+using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Catch.Mods
{
@@ -9,5 +13,36 @@ namespace osu.Game.Rulesets.Catch.Mods
{
public override string Description => @"Play with fading fruits.";
public override double ScoreMultiplier => 1.06;
+
+ private const double fade_out_offset_multiplier = 0.6;
+ private const double fade_out_duration_multiplier = 0.44;
+
+ protected override void ApplyHiddenState(DrawableHitObject drawable, ArmedState state)
+ {
+ if (!(drawable is DrawableCatchHitObject catchDrawable))
+ return;
+
+ if (catchDrawable.NestedHitObjects.Any())
+ {
+ foreach (var nestedDrawable in catchDrawable.NestedHitObjects)
+ {
+ if (nestedDrawable is DrawableCatchHitObject nestedCatchDrawable)
+ fadeOutHitObject(nestedCatchDrawable);
+ }
+ }
+ else
+ fadeOutHitObject(catchDrawable);
+ }
+
+ private void fadeOutHitObject(DrawableCatchHitObject drawable)
+ {
+ var hitObject = drawable.HitObject;
+
+ var offset = hitObject.TimePreempt * fade_out_offset_multiplier;
+ var duration = offset - hitObject.TimePreempt * fade_out_duration_multiplier;
+
+ using (drawable.BeginAbsoluteSequence(hitObject.StartTime - offset, true))
+ drawable.FadeOut(duration);
+ }
}
}
diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
index 2153b8dc85..be76edc01b 100644
--- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
@@ -14,6 +14,8 @@ namespace osu.Game.Rulesets.Catch.Objects
public float X { get; set; }
+ public double TimePreempt = 1000;
+
public int IndexInBeatmap { get; set; }
public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(ComboIndex % 4);
@@ -54,6 +56,8 @@ namespace osu.Game.Rulesets.Catch.Objects
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
+ TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
+
Scale = 1.0f - 0.7f * (difficulty.CircleSize - 5) / 5;
}
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs
index 2f8ccec48b..5785d9a9ca 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs
@@ -68,11 +68,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
AccentColour = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White;
}
- private const float preempt = 1000;
-
protected override void UpdateState(ArmedState state)
{
- using (BeginAbsoluteSequence(HitObject.StartTime - preempt))
+ using (BeginAbsoluteSequence(HitObject.StartTime - HitObject.TimePreempt))
this.FadeIn(200);
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
index a9fd34455a..0952e8981a 100644
--- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
+++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
@@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Catch.Objects
{
base.CreateNestedHitObjects();
- var tickSamples = Samples.Select(s => new SampleInfo
+ var tickSamples = Samples.Select(s => new HitSampleInfo
{
Bank = s.Bank,
Name = @"slidertick",
@@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Catch.Objects
public double Distance => Path.Distance;
- public List> NodeSamples { get; set; } = new List>();
+ public List> NodeSamples { get; set; } = new List>();
public double? LegacyLastTickOffset { get; set; }
}
diff --git a/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs
index dba76eef49..26f20b223a 100644
--- a/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs
+++ b/osu.Game.Rulesets.Catch/Properties/AssemblyInfo.cs
@@ -10,3 +10,4 @@ using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("osu.Game.Rulesets.Catch.Tests")]
[assembly: InternalsVisibleTo("osu.Game.Rulesets.Catch.Tests.Dynamic")]
[assembly: InternalsVisibleTo("osu.Game.Rulesets.Catch.Tests.iOS")]
+[assembly: InternalsVisibleTo("osu.Game.Rulesets.Catch.Tests.Android")]
diff --git a/osu.Game.Rulesets.Mania.Tests.Android/MainActivity.cs b/osu.Game.Rulesets.Mania.Tests.Android/MainActivity.cs
new file mode 100644
index 0000000000..0a3f05ae54
--- /dev/null
+++ b/osu.Game.Rulesets.Mania.Tests.Android/MainActivity.cs
@@ -0,0 +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 Android.App;
+using Android.Content.PM;
+using osu.Framework.Android;
+using osu.Game.Tests;
+
+namespace osu.Game.Rulesets.Mania.Tests.Android
+{
+ [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
+ public class MainActivity : AndroidGameActivity
+ {
+ protected override Framework.Game CreateGame() => new OsuTestBrowser();
+ }
+}
diff --git a/osu.Game.Rulesets.Mania.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Rulesets.Mania.Tests.Android/Properties/AndroidManifest.xml
new file mode 100644
index 0000000000..c315581606
--- /dev/null
+++ b/osu.Game.Rulesets.Mania.Tests.Android/Properties/AndroidManifest.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj b/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj
new file mode 100644
index 0000000000..0e557cb260
--- /dev/null
+++ b/osu.Game.Rulesets.Mania.Tests.Android/osu.Game.Rulesets.Mania.Tests.Android.csproj
@@ -0,0 +1,39 @@
+
+
+
+
+ Debug
+ AnyCPU
+ 8.0.30703
+ 2.0
+ {531F1092-DB27-445D-AA33-2A77C7187C99}
+ {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ {122416d6-6b49-4ee2-a1e8-b825f31c79fe}
+ osu.Game.Rulesets.Mania.Tests
+ osu.Game.Rulesets.Mania.Tests.Android
+ Properties\AndroidManifest.xml
+ armeabi-v7a;x86;arm64-v8a
+
+
+
+
+
+
+
+
+
+ %(RecursiveDir)%(Filename)%(Extension)
+
+
+
+
+ {48f4582b-7687-4621-9cbe-5c24197cb536}
+ osu.Game.Rulesets.Mania
+
+
+ {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}
+ osu.Game
+
+
+
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
index 704deba78b..e10602312e 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
@@ -255,7 +255,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
///
/// The time to retrieve the sample info list from.
///
- private List sampleInfoListAt(double time)
+ private List sampleInfoListAt(double time)
{
var curveData = HitObject as IHasCurve;
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
index 1b6ff16388..ea418eedb4 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
@@ -364,7 +364,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
break;
}
- bool isDoubleSample(SampleInfo sample) => sample.Name == SampleInfo.HIT_CLAP || sample.Name == SampleInfo.HIT_FINISH;
+ bool isDoubleSample(HitSampleInfo sample) => sample.Name == HitSampleInfo.HIT_CLAP || sample.Name == HitSampleInfo.HIT_FINISH;
bool canGenerateTwoNotes = !convertType.HasFlag(PatternType.LowProbability);
canGenerateTwoNotes &= HitObject.Samples.Any(isDoubleSample) || sampleInfoListAt(HitObject.StartTime).Any(isDoubleSample);
@@ -443,7 +443,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
noteCount = 0;
noteCount = Math.Min(TotalColumns - 1, noteCount);
- bool ignoreHead = !sampleInfoListAt(startTime).Any(s => s.Name == SampleInfo.HIT_WHISTLE || s.Name == SampleInfo.HIT_FINISH || s.Name == SampleInfo.HIT_CLAP);
+ bool ignoreHead = !sampleInfoListAt(startTime).Any(s => s.Name == HitSampleInfo.HIT_WHISTLE || s.Name == HitSampleInfo.HIT_FINISH || s.Name == HitSampleInfo.HIT_CLAP);
var rowPattern = new Pattern();
@@ -472,7 +472,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
///
/// The time to retrieve the sample info list from.
///
- private List sampleInfoListAt(double time)
+ private List sampleInfoListAt(double time)
{
var curveData = HitObject as IHasCurve;
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
index 9e95be35fa..b3be08e1f7 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
@@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
switch (TotalColumns)
{
- case 8 when HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && endTime - HitObject.StartTime < 1000:
+ case 8 when HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH) && endTime - HitObject.StartTime < 1000:
addToPattern(pattern, 0, generateHold);
break;
@@ -72,9 +72,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
};
if (hold.Head.Samples == null)
- hold.Head.Samples = new List();
+ hold.Head.Samples = new List();
- hold.Head.Samples.Add(new SampleInfo { Name = SampleInfo.HIT_NORMAL });
+ hold.Head.Samples.Add(new HitSampleInfo { Name = HitSampleInfo.HIT_NORMAL });
hold.Tail.Samples = HitObject.Samples;
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
index d13b21183b..decd159ee9 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
@@ -79,9 +79,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (!convertType.HasFlag(PatternType.KeepSingle))
{
- if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && TotalColumns != 8)
+ if (HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH) && TotalColumns != 8)
convertType |= PatternType.Mirror;
- else if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP))
+ else if (HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP))
convertType |= PatternType.Gathered;
}
}
@@ -263,7 +263,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
///
/// Whether this hit object can generate a note in the special column.
///
- private bool hasSpecialColumn => HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP) && HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH);
+ private bool hasSpecialColumn => HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP) && HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH);
///
/// Generates a random pattern.
@@ -364,7 +364,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
break;
}
- if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP))
+ if (HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP))
p2 = 1;
return GetRandomNoteCount(p2, p3, p4, p5);
diff --git a/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs
index f3ea6c7b71..ca1f7036c7 100644
--- a/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs
+++ b/osu.Game.Rulesets.Mania/Properties/AssemblyInfo.cs
@@ -10,3 +10,4 @@ using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("osu.Game.Rulesets.Mania.Tests")]
[assembly: InternalsVisibleTo("osu.Game.Rulesets.Mania.Tests.Dynamic")]
[assembly: InternalsVisibleTo("osu.Game.Rulesets.Mania.Tests.iOS")]
+[assembly: InternalsVisibleTo("osu.Game.Rulesets.Mania.Tests.Android")]
diff --git a/osu.Game.Rulesets.Osu.Tests.Android/MainActivity.cs b/osu.Game.Rulesets.Osu.Tests.Android/MainActivity.cs
new file mode 100644
index 0000000000..e6c508d99e
--- /dev/null
+++ b/osu.Game.Rulesets.Osu.Tests.Android/MainActivity.cs
@@ -0,0 +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 Android.App;
+using Android.Content.PM;
+using osu.Framework.Android;
+using osu.Game.Tests;
+
+namespace osu.Game.Rulesets.Osu.Tests.Android
+{
+ [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
+ public class MainActivity : AndroidGameActivity
+ {
+ protected override Framework.Game CreateGame() => new OsuTestBrowser();
+ }
+}
diff --git a/osu.Game.Rulesets.Osu.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Rulesets.Osu.Tests.Android/Properties/AndroidManifest.xml
new file mode 100644
index 0000000000..dac9c19477
--- /dev/null
+++ b/osu.Game.Rulesets.Osu.Tests.Android/Properties/AndroidManifest.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj b/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj
new file mode 100644
index 0000000000..dcf1573522
--- /dev/null
+++ b/osu.Game.Rulesets.Osu.Tests.Android/osu.Game.Rulesets.Osu.Tests.Android.csproj
@@ -0,0 +1,39 @@
+
+
+
+
+ Debug
+ AnyCPU
+ 8.0.30703
+ 2.0
+ {90CAB706-39CB-4B93-9629-3218A6FF8E9B}
+ {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ {122416d6-6b49-4ee2-a1e8-b825f31c79fe}
+ osu.Game.Rulesets.Osu.Tests
+ osu.Game.Rulesets.Osu.Tests.Android
+ Properties\AndroidManifest.xml
+ armeabi-v7a;x86;arm64-v8a
+
+
+
+
+
+
+
+
+
+ %(RecursiveDir)%(Filename)%(Extension)
+
+
+
+
+ {c92a607b-1fdd-4954-9f92-03ff547d9080}
+ osu.Game.Rulesets.Osu
+
+
+ {2a66dd92-adb1-4994-89e2-c94e04acda0d}
+ osu.Game
+
+
+
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
index 1ba6d107be..c5a27205d6 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
@@ -248,9 +248,9 @@ namespace osu.Game.Rulesets.Osu.Tests
private void createCatmull(int repeats = 0)
{
- var repeatSamples = new List>();
+ var repeatSamples = new List>();
for (int i = 0; i < repeats; i++)
- repeatSamples.Add(new List());
+ repeatSamples.Add(new List());
var slider = new Slider
{
@@ -270,11 +270,11 @@ namespace osu.Game.Rulesets.Osu.Tests
addSlider(slider, 3, 1);
}
- private List> createEmptySamples(int repeats)
+ private List> createEmptySamples(int repeats)
{
- var repeatSamples = new List>();
+ var repeatSamples = new List>();
for (int i = 0; i < repeats; i++)
- repeatSamples.Add(new List());
+ repeatSamples.Add(new List());
return repeatSamples;
}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs
new file mode 100644
index 0000000000..adca95cf8a
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModDeflate.cs
@@ -0,0 +1,20 @@
+// 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.Graphics.Sprites;
+
+namespace osu.Game.Rulesets.Osu.Mods
+{
+ public class OsuModDeflate : OsuModeObjectScaleTween
+ {
+ public override string Name => "Deflate";
+
+ public override string Acronym => "DF";
+
+ public override IconUsage Icon => FontAwesome.Solid.CompressArrowsAlt;
+
+ public override string Description => "Hit them at the right size!";
+
+ protected override float StartScale => 2f;
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs
index 8072dc09c1..3c81203ad7 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModGrow.cs
@@ -1,20 +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.Collections.Generic;
-using System.Linq;
-using osu.Framework.Bindables;
-using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
-using osu.Game.Configuration;
-using osu.Game.Rulesets.Mods;
-using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Osu.Objects;
-using osu.Game.Rulesets.Osu.Objects.Drawables;
namespace osu.Game.Rulesets.Osu.Mods
{
- internal class OsuModGrow : Mod, IReadFromConfig, IApplicableToDrawableHitObjects
+ internal class OsuModGrow : OsuModeObjectScaleTween
{
public override string Name => "Grow";
@@ -22,65 +13,8 @@ namespace osu.Game.Rulesets.Osu.Mods
public override IconUsage Icon => FontAwesome.Solid.ArrowsAltV;
- public override ModType Type => ModType.Fun;
-
public override string Description => "Hit them at the right size!";
- public override double ScoreMultiplier => 1;
-
- private Bindable increaseFirstObjectVisibility = new Bindable();
-
- public void ReadFromConfig(OsuConfigManager config)
- {
- increaseFirstObjectVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility);
- }
-
- public void ApplyToDrawableHitObjects(IEnumerable drawables)
- {
- foreach (var drawable in drawables.Skip(increaseFirstObjectVisibility.Value ? 1 : 0))
- {
- switch (drawable)
- {
- case DrawableSpinner _:
- continue;
-
- default:
- drawable.ApplyCustomUpdateState += ApplyCustomState;
- break;
- }
- }
- }
-
- protected virtual void ApplyCustomState(DrawableHitObject drawable, ArmedState state)
- {
- var h = (OsuHitObject)drawable.HitObject;
-
- // apply grow effect
- switch (drawable)
- {
- case DrawableSliderHead _:
- case DrawableSliderTail _:
- // special cases we should *not* be scaling.
- break;
-
- case DrawableSlider _:
- case DrawableHitCircle _:
- {
- using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true))
- drawable.ScaleTo(0.5f).Then().ScaleTo(1, h.TimePreempt, Easing.OutSine);
- break;
- }
- }
-
- // remove approach circles
- switch (drawable)
- {
- case DrawableHitCircle circle:
- // we don't want to see the approach circle
- using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true))
- circle.ApproachCircle.Hide();
- break;
- }
- }
+ protected override float StartScale => 0.5f;
}
}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs b/osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs
new file mode 100644
index 0000000000..ad6a15718a
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModeObjectScaleTween.cs
@@ -0,0 +1,84 @@
+// 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.Linq;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Game.Configuration;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+
+namespace osu.Game.Rulesets.Osu.Mods
+{
+ ///
+ /// Adjusts the size of hit objects during their fade in animation.
+ ///
+ public abstract class OsuModeObjectScaleTween : Mod, IReadFromConfig, IApplicableToDrawableHitObjects
+ {
+ public override ModType Type => ModType.Fun;
+
+ public override double ScoreMultiplier => 1;
+
+ protected virtual float StartScale => 1;
+
+ protected virtual float EndScale => 1;
+
+ private Bindable increaseFirstObjectVisibility = new Bindable();
+
+ public void ReadFromConfig(OsuConfigManager config)
+ {
+ increaseFirstObjectVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility);
+ }
+
+ public void ApplyToDrawableHitObjects(IEnumerable drawables)
+ {
+ foreach (var drawable in drawables.Skip(increaseFirstObjectVisibility.Value ? 1 : 0))
+ {
+ switch (drawable)
+ {
+ case DrawableSpinner _:
+ continue;
+
+ default:
+ drawable.ApplyCustomUpdateState += ApplyCustomState;
+ break;
+ }
+ }
+ }
+
+ protected virtual void ApplyCustomState(DrawableHitObject drawable, ArmedState state)
+ {
+ var h = (OsuHitObject)drawable.HitObject;
+
+ // apply grow effect
+ switch (drawable)
+ {
+ case DrawableSliderHead _:
+ case DrawableSliderTail _:
+ // special cases we should *not* be scaling.
+ break;
+
+ case DrawableSlider _:
+ case DrawableHitCircle _:
+ {
+ using (drawable.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true))
+ drawable.ScaleTo(StartScale).Then().ScaleTo(EndScale, h.TimePreempt, Easing.OutSine);
+ break;
+ }
+ }
+
+ // remove approach circles
+ switch (drawable)
+ {
+ case DrawableHitCircle circle:
+ // we don't want to see the approach circle
+ using (circle.BeginAbsoluteSequence(h.StartTime - h.TimePreempt, true))
+ circle.ApproachCircle.Hide();
+ break;
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
index 10b37af957..f372cb65ce 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
@@ -18,6 +18,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
private readonly ShakeContainer shakeContainer;
+ // Must be set to update IsHovered as it's used in relax mdo to detect osu hit objects.
+ public override bool HandlePositionalInput => true;
+
protected DrawableOsuHitObject(OsuHitObject hitObject)
: base(hitObject)
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs
index a8aec005d1..a4638c31f2 100644
--- a/osu.Game.Rulesets.Osu/Objects/Slider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs
@@ -99,7 +99,7 @@ namespace osu.Game.Rulesets.Osu.Objects
///
internal float LazyTravelDistance;
- public List> NodeSamples { get; set; } = new List>();
+ public List> NodeSamples { get; set; } = new List>();
private int repeatCount;
@@ -157,12 +157,12 @@ namespace osu.Game.Rulesets.Osu.Objects
foreach (var e in
SliderEventGenerator.Generate(StartTime, SpanDuration, Velocity, TickDistance, Path.Distance, this.SpanCount(), LegacyLastTickOffset))
{
- var firstSample = Samples.Find(s => s.Name == SampleInfo.HIT_NORMAL)
+ var firstSample = Samples.Find(s => s.Name == HitSampleInfo.HIT_NORMAL)
?? Samples.FirstOrDefault(); // TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933)
- var sampleList = new List();
+ var sampleList = new List();
if (firstSample != null)
- sampleList.Add(new SampleInfo
+ sampleList.Add(new HitSampleInfo
{
Bank = firstSample.Bank,
Volume = firstSample.Volume,
@@ -225,7 +225,7 @@ namespace osu.Game.Rulesets.Osu.Objects
}
}
- private List getNodeSamples(int nodeIndex) =>
+ private List getNodeSamples(int nodeIndex) =>
nodeIndex < NodeSamples.Count ? NodeSamples[nodeIndex] : Samples;
public override Judgement CreateJudgement() => new OsuJudgement();
diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs
index 83d29c156d..baa4aff413 100644
--- a/osu.Game.Rulesets.Osu/OsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs
@@ -134,7 +134,7 @@ namespace osu.Game.Rulesets.Osu
{
new OsuModTransform(),
new OsuModWiggle(),
- new OsuModGrow(),
+ new MultiMod(new OsuModGrow(), new OsuModDeflate()),
new MultiMod(new ModWindUp(), new ModWindDown()),
};
diff --git a/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs
index b9a7096330..c842874635 100644
--- a/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs
+++ b/osu.Game.Rulesets.Osu/Properties/AssemblyInfo.cs
@@ -10,3 +10,4 @@ using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("osu.Game.Rulesets.Osu.Tests")]
[assembly: InternalsVisibleTo("osu.Game.Rulesets.Osu.Tests.Dynamic")]
[assembly: InternalsVisibleTo("osu.Game.Rulesets.Osu.Tests.iOS")]
+[assembly: InternalsVisibleTo("osu.Game.Rulesets.Osu.Tests.Android")]
diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/MainActivity.cs b/osu.Game.Rulesets.Taiko.Tests.Android/MainActivity.cs
new file mode 100644
index 0000000000..1128a0d37f
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko.Tests.Android/MainActivity.cs
@@ -0,0 +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 Android.App;
+using Android.Content.PM;
+using osu.Framework.Android;
+using osu.Game.Tests;
+
+namespace osu.Game.Rulesets.Taiko.Tests.Android
+{
+ [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
+ public class MainActivity : AndroidGameActivity
+ {
+ protected override Framework.Game CreateGame() => new OsuTestBrowser();
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AndroidManifest.xml
new file mode 100644
index 0000000000..f731042a4c
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko.Tests.Android/Properties/AndroidManifest.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj b/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj
new file mode 100644
index 0000000000..392442b713
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko.Tests.Android/osu.Game.Rulesets.Taiko.Tests.Android.csproj
@@ -0,0 +1,39 @@
+
+
+
+
+ Debug
+ AnyCPU
+ 8.0.30703
+ 2.0
+ {3701A0A1-8476-42C6-B5C4-D24129B4A484}
+ {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ {122416d6-6b49-4ee2-a1e8-b825f31c79fe}
+ osu.Game.Rulesets.Taiko.Tests
+ osu.Game.Rulesets.Taiko.Tests.Android
+ Properties\AndroidManifest.xml
+ armeabi-v7a;x86;arm64-v8a
+
+
+
+
+
+
+
+
+
+ %(RecursiveDir)%(Filename)%(Extension)
+
+
+
+
+ {f167e17a-7de6-4af5-b920-a5112296c695}
+ osu.Game.Rulesets.Taiko
+
+
+ {2a66dd92-adb1-4994-89e2-c94e04acda0d}
+ osu.Game
+
+
+
+
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneInputDrum.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneInputDrum.cs
index 02300a5dde..8c1b0c4c62 100644
--- a/osu.Game.Rulesets.Taiko.Tests/TestSceneInputDrum.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneInputDrum.cs
@@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
{
typeof(InputDrum),
typeof(DrumSampleMapping),
- typeof(SampleInfo),
+ typeof(HitSampleInfo),
typeof(SampleControlPoint)
};
diff --git a/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs b/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs
index d7fa661e8a..ad2596931d 100644
--- a/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs
+++ b/osu.Game.Rulesets.Taiko/Audio/DrumSampleMapping.cs
@@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Taiko.Audio
foreach (var s in samplePoints)
{
var centre = s.GetSampleInfo();
- var rim = s.GetSampleInfo(SampleInfo.HIT_CLAP);
+ var rim = s.GetSampleInfo(HitSampleInfo.HIT_CLAP);
// todo: this is ugly
centre.Namespace = "taiko";
@@ -43,9 +43,9 @@ namespace osu.Game.Rulesets.Taiko.Audio
}
}
- private SkinnableSound addSound(SampleInfo sampleInfo)
+ private SkinnableSound addSound(HitSampleInfo hitSampleInfo)
{
- var drawable = new SkinnableSound(sampleInfo);
+ var drawable = new SkinnableSound(hitSampleInfo);
Sounds.Add(drawable);
return drawable;
}
diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
index f8672037cd..f0cf8d9c7d 100644
--- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
@@ -79,9 +79,9 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
var curveData = obj as IHasCurve;
// Old osu! used hit sounding to determine various hit type information
- List samples = obj.Samples;
+ List samples = obj.Samples;
- bool strong = samples.Any(s => s.Name == SampleInfo.HIT_FINISH);
+ bool strong = samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH);
if (distanceData != null)
{
@@ -117,15 +117,15 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
{
- List> allSamples = curveData != null ? curveData.NodeSamples : new List>(new[] { samples });
+ List> allSamples = curveData != null ? curveData.NodeSamples : new List>(new[] { samples });
int i = 0;
for (double j = obj.StartTime; j <= obj.StartTime + taikoDuration + tickSpacing / 8; j += tickSpacing)
{
- List currentSamples = allSamples[i];
- bool isRim = currentSamples.Any(s => s.Name == SampleInfo.HIT_CLAP || s.Name == SampleInfo.HIT_WHISTLE);
- strong = currentSamples.Any(s => s.Name == SampleInfo.HIT_FINISH);
+ List currentSamples = allSamples[i];
+ bool isRim = currentSamples.Any(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE);
+ strong = currentSamples.Any(s => s.Name == HitSampleInfo.HIT_FINISH);
if (isRim)
{
@@ -175,7 +175,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
}
else
{
- bool isRim = samples.Any(s => s.Name == SampleInfo.HIT_CLAP || s.Name == SampleInfo.HIT_WHISTLE);
+ bool isRim = samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE);
if (isRim)
{
diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs
index 502dd54e9e..a6f902208c 100644
--- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs
+++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs
@@ -9,5 +9,6 @@ namespace osu.Game.Rulesets.Taiko.Mods
{
public override string Description => @"Beats fade out before you hit them!";
public override double ScoreMultiplier => 1.06;
+ public override bool HasImplementation => false;
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
index 119940536e..bd45b52d7b 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
@@ -122,7 +122,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
}
// Normal and clap samples are handled by the drum
- protected override IEnumerable GetSamples() => HitObject.Samples.Where(s => s.Name != SampleInfo.HIT_NORMAL && s.Name != SampleInfo.HIT_CLAP);
+ protected override IEnumerable GetSamples() => HitObject.Samples.Where(s => s.Name != HitSampleInfo.HIT_NORMAL && s.Name != HitSampleInfo.HIT_CLAP);
protected override string SampleNamespace => "Taiko";
diff --git a/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs b/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs
index 81f15fb293..ca7d04876e 100644
--- a/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs
+++ b/osu.Game.Rulesets.Taiko/Properties/AssemblyInfo.cs
@@ -10,3 +10,4 @@ using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("osu.Game.Rulesets.Taiko.Tests")]
[assembly: InternalsVisibleTo("osu.Game.Rulesets.Taiko.Tests.Dynamic")]
[assembly: InternalsVisibleTo("osu.Game.Rulesets.Taiko.Tests.iOS")]
+[assembly: InternalsVisibleTo("osu.Game.Rulesets.Taiko.Tests.Android")]
diff --git a/osu.Game.Tests.Android/MainActivity.cs b/osu.Game.Tests.Android/MainActivity.cs
new file mode 100644
index 0000000000..0695c8e37b
--- /dev/null
+++ b/osu.Game.Tests.Android/MainActivity.cs
@@ -0,0 +1,15 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using Android.App;
+using Android.Content.PM;
+using osu.Framework.Android;
+
+namespace osu.Game.Tests.Android
+{
+ [Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.SensorLandscape, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = true)]
+ public class MainActivity : AndroidGameActivity
+ {
+ protected override Framework.Game CreateGame() => new OsuTestBrowser();
+ }
+}
diff --git a/osu.Game.Tests.Android/Properties/AndroidManifest.xml b/osu.Game.Tests.Android/Properties/AndroidManifest.xml
new file mode 100644
index 0000000000..146f96c2a3
--- /dev/null
+++ b/osu.Game.Tests.Android/Properties/AndroidManifest.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj b/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj
new file mode 100644
index 0000000000..c2dd194e09
--- /dev/null
+++ b/osu.Game.Tests.Android/osu.Game.Tests.Android.csproj
@@ -0,0 +1,77 @@
+
+
+
+
+ Debug
+ AnyCPU
+ 8.0.30703
+ 2.0
+ {5CC222DC-5716-4499-B897-DCBDDA4A5CF9}
+ {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ {122416d6-6b49-4ee2-a1e8-b825f31c79fe}
+ osu.Game.Tests
+ osu.Game.Tests.Android
+ Properties\AndroidManifest.xml
+ armeabi-v7a;x86;arm64-v8a
+
+
+
+
+
+
+
+
+
+ %(RecursiveDir)%(Filename)%(Extension)
+
+
+ %(RecursiveDir)%(Filename)%(Extension)
+
+
+ %(RecursiveDir)%(Filename)%(Extension)
+
+
+ %(RecursiveDir)%(Filename)%(Extension)
+
+
+ %(RecursiveDir)%(Filename)%(Extension)
+
+
+ %(RecursiveDir)%(Filename)%(Extension)
+
+
+ %(RecursiveDir)%(Filename)%(Extension)
+
+
+ %(RecursiveDir)%(Filename)%(Extension)
+
+
+
+
+ {58f6c80c-1253-4a0e-a465-b8c85ebeadf3}
+ osu.Game.Rulesets.Catch
+
+
+ {48f4582b-7687-4621-9cbe-5c24197cb536}
+ osu.Game.Rulesets.Mania
+
+
+ {c92a607b-1fdd-4954-9f92-03ff547d9080}
+ osu.Game.Rulesets.Osu
+
+
+ {f167e17a-7de6-4af5-b920-a5112296c695}
+ osu.Game.Rulesets.Taiko
+
+
+ {2a66dd92-adb1-4994-89e2-c94e04acda0d}
+ osu.Game
+
+
+
+
+ 2.0.0
+
+
+
+
\ No newline at end of file
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
index 5fd5fe342d..d087251e7e 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
@@ -354,14 +354,14 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.IsNotNull(curveData);
Assert.AreEqual(new Vector2(192, 168), positionData.Position);
Assert.AreEqual(956, hitObjects[0].StartTime);
- Assert.IsTrue(hitObjects[0].Samples.Any(s => s.Name == SampleInfo.HIT_NORMAL));
+ Assert.IsTrue(hitObjects[0].Samples.Any(s => s.Name == HitSampleInfo.HIT_NORMAL));
positionData = hitObjects[1] as IHasPosition;
Assert.IsNotNull(positionData);
Assert.AreEqual(new Vector2(304, 56), positionData.Position);
Assert.AreEqual(1285, hitObjects[1].StartTime);
- Assert.IsTrue(hitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP));
+ Assert.IsTrue(hitObjects[1].Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP));
}
}
@@ -384,7 +384,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual("soft-hitnormal8", getTestableSampleInfo(hitObjects[4]).LookupNames.First());
}
- SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
+ HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
}
[Test]
@@ -402,7 +402,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual("normal-hitnormal3", getTestableSampleInfo(hitObjects[2]).LookupNames.First());
}
- SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
+ HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
}
[Test]
@@ -422,7 +422,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(70, getTestableSampleInfo(hitObjects[3]).Volume);
}
- SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
+ HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
}
[Test]
@@ -438,34 +438,34 @@ namespace osu.Game.Tests.Beatmaps.Formats
var slider1 = (ConvertSlider)hitObjects[0];
Assert.AreEqual(1, slider1.NodeSamples[0].Count);
- Assert.AreEqual(SampleInfo.HIT_NORMAL, slider1.NodeSamples[0][0].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider1.NodeSamples[0][0].Name);
Assert.AreEqual(1, slider1.NodeSamples[1].Count);
- Assert.AreEqual(SampleInfo.HIT_NORMAL, slider1.NodeSamples[1][0].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider1.NodeSamples[1][0].Name);
Assert.AreEqual(1, slider1.NodeSamples[2].Count);
- Assert.AreEqual(SampleInfo.HIT_NORMAL, slider1.NodeSamples[2][0].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider1.NodeSamples[2][0].Name);
var slider2 = (ConvertSlider)hitObjects[1];
Assert.AreEqual(2, slider2.NodeSamples[0].Count);
- Assert.AreEqual(SampleInfo.HIT_NORMAL, slider2.NodeSamples[0][0].Name);
- Assert.AreEqual(SampleInfo.HIT_CLAP, slider2.NodeSamples[0][1].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider2.NodeSamples[0][0].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_CLAP, slider2.NodeSamples[0][1].Name);
Assert.AreEqual(2, slider2.NodeSamples[1].Count);
- Assert.AreEqual(SampleInfo.HIT_NORMAL, slider2.NodeSamples[1][0].Name);
- Assert.AreEqual(SampleInfo.HIT_CLAP, slider2.NodeSamples[1][1].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider2.NodeSamples[1][0].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_CLAP, slider2.NodeSamples[1][1].Name);
Assert.AreEqual(2, slider2.NodeSamples[2].Count);
- Assert.AreEqual(SampleInfo.HIT_NORMAL, slider2.NodeSamples[2][0].Name);
- Assert.AreEqual(SampleInfo.HIT_CLAP, slider2.NodeSamples[2][1].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider2.NodeSamples[2][0].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_CLAP, slider2.NodeSamples[2][1].Name);
var slider3 = (ConvertSlider)hitObjects[2];
Assert.AreEqual(2, slider3.NodeSamples[0].Count);
- Assert.AreEqual(SampleInfo.HIT_NORMAL, slider3.NodeSamples[0][0].Name);
- Assert.AreEqual(SampleInfo.HIT_WHISTLE, slider3.NodeSamples[0][1].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider3.NodeSamples[0][0].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_WHISTLE, slider3.NodeSamples[0][1].Name);
Assert.AreEqual(1, slider3.NodeSamples[1].Count);
- Assert.AreEqual(SampleInfo.HIT_NORMAL, slider3.NodeSamples[1][0].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider3.NodeSamples[1][0].Name);
Assert.AreEqual(2, slider3.NodeSamples[2].Count);
- Assert.AreEqual(SampleInfo.HIT_NORMAL, slider3.NodeSamples[2][0].Name);
- Assert.AreEqual(SampleInfo.HIT_CLAP, slider3.NodeSamples[2][1].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_NORMAL, slider3.NodeSamples[2][0].Name);
+ Assert.AreEqual(HitSampleInfo.HIT_CLAP, slider3.NodeSamples[2][1].Name);
}
}
diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
index 39b7735a55..a725c58462 100644
--- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs
@@ -101,14 +101,14 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.IsNotNull(curveData);
Assert.AreEqual(new Vector2(192, 168), positionData.Position);
Assert.AreEqual(956, beatmap.HitObjects[0].StartTime);
- Assert.IsTrue(beatmap.HitObjects[0].Samples.Any(s => s.Name == SampleInfo.HIT_NORMAL));
+ Assert.IsTrue(beatmap.HitObjects[0].Samples.Any(s => s.Name == HitSampleInfo.HIT_NORMAL));
positionData = beatmap.HitObjects[1] as IHasPosition;
Assert.IsNotNull(positionData);
Assert.AreEqual(new Vector2(304, 56), positionData.Position);
Assert.AreEqual(1285, beatmap.HitObjects[1].StartTime);
- Assert.IsTrue(beatmap.HitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP));
+ Assert.IsTrue(beatmap.HitObjects[1].Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP));
}
[TestCase(normal)]
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs
new file mode 100644
index 0000000000..0dfcda122f
--- /dev/null
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneReplayDownloadButton.cs
@@ -0,0 +1,76 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using NUnit.Framework;
+using osu.Framework.Graphics;
+using osu.Game.Online;
+using osu.Game.Online.API.Requests.Responses;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Scoring;
+using osu.Game.Screens.Play;
+using osu.Game.Users;
+using osuTK;
+using System;
+using System.Collections.Generic;
+
+namespace osu.Game.Tests.Visual.Gameplay
+{
+ [TestFixture]
+ public class TestSceneReplayDownloadButton : OsuTestScene
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(ReplayDownloadButton)
+ };
+
+ private TestReplayDownloadButton downloadButton;
+
+ public TestSceneReplayDownloadButton()
+ {
+ createButton(true);
+ AddStep(@"downloading state", () => downloadButton.SetDownloadState(DownloadState.Downloading));
+ AddStep(@"locally available state", () => downloadButton.SetDownloadState(DownloadState.LocallyAvailable));
+ AddStep(@"not downloaded state", () => downloadButton.SetDownloadState(DownloadState.NotDownloaded));
+ createButton(false);
+ }
+
+ private void createButton(bool withReplay)
+ {
+ AddStep(withReplay ? @"create button with replay" : "create button without replay", () =>
+ {
+ Child = downloadButton = new TestReplayDownloadButton(getScoreInfo(withReplay))
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(80, 40),
+ };
+ });
+ }
+
+ private ScoreInfo getScoreInfo(bool replayAvailable)
+ {
+ return new APILegacyScoreInfo
+ {
+ ID = 1,
+ OnlineScoreID = 2553163309,
+ Ruleset = new OsuRuleset().RulesetInfo,
+ Replay = replayAvailable,
+ User = new User
+ {
+ Id = 39828,
+ Username = @"WubWoofWolf",
+ }
+ };
+ }
+
+ private class TestReplayDownloadButton : ReplayDownloadButton
+ {
+ public void SetDownloadState(DownloadState state) => State.Value = state;
+
+ public TestReplayDownloadButton(ScoreInfo score)
+ : base(score)
+ {
+ }
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
index c494f5ef33..a9c44c9020 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
@@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Online
typeof(BasicStats),
typeof(BeatmapPicker),
typeof(Details),
- typeof(DownloadButton),
+ typeof(HeaderDownloadButton),
typeof(FavouriteButton),
typeof(Header),
typeof(HeaderButton),
diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
index c75348112f..4d3992ce13 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs
@@ -3,19 +3,23 @@
using System;
using System.Collections.Generic;
-using System.ComponentModel;
+using NUnit.Framework;
using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.UserInterface;
using osu.Game.Online.Chat;
using osu.Game.Overlays;
using osu.Game.Overlays.Chat;
+using osu.Game.Overlays.Chat.Selection;
using osu.Game.Overlays.Chat.Tabs;
+using osu.Game.Users;
+using osuTK.Input;
namespace osu.Game.Tests.Visual.Online
{
- [Description("Testing chat api and overlay")]
- public class TestSceneChatOverlay : OsuTestScene
+ public class TestSceneChatOverlay : ManualInputManagerTestScene
{
public override IReadOnlyList RequiredTypes => new[]
{
@@ -28,17 +32,126 @@ namespace osu.Game.Tests.Visual.Online
typeof(TabCloseButton)
};
- [Cached]
- private readonly ChannelManager channelManager = new ChannelManager();
+ private TestChatOverlay chatOverlay;
+ private ChannelManager channelManager;
- [BackgroundDependencyLoader]
- private void load()
+ private readonly Channel channel1 = new Channel(new User()) { Name = "test1" };
+ private readonly Channel channel2 = new Channel(new User()) { Name = "test2" };
+
+ [SetUp]
+ public void Setup()
{
- Children = new Drawable[]
+ Schedule(() =>
{
- channelManager,
- new ChatOverlay { State = { Value = Visibility.Visible } }
- };
+ ChannelManagerContainer container;
+
+ Child = container = new ChannelManagerContainer(new List { channel1, channel2 })
+ {
+ RelativeSizeAxes = Axes.Both,
+ };
+
+ chatOverlay = container.ChatOverlay;
+ channelManager = container.ChannelManager;
+ });
+ }
+
+ [Test]
+ public void TestHideOverlay()
+ {
+ AddAssert("Chat overlay is visible", () => chatOverlay.State.Value == Visibility.Visible);
+ AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible);
+
+ AddStep("Close chat overlay", () => chatOverlay.Hide());
+
+ AddAssert("Chat overlay was hidden", () => chatOverlay.State.Value == Visibility.Hidden);
+ AddAssert("Channel selection overlay was hidden", () => chatOverlay.SelectionOverlayState == Visibility.Hidden);
+ }
+
+ [Test]
+ public void TestSelectingChannelClosesSelector()
+ {
+ AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible);
+
+ AddStep("Join channel 1", () => channelManager.JoinChannel(channel1));
+ AddStep("Switch to channel 1", () => clickDrawable(chatOverlay.TabMap[channel1]));
+
+ AddAssert("Current channel is channel 1", () => channelManager.CurrentChannel.Value == channel1);
+ AddAssert("Channel selector was closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden);
+ }
+
+ [Test]
+ public void TestCloseChannelWhileSelectorClosed()
+ {
+ AddStep("Join channel 1", () => channelManager.JoinChannel(channel1));
+ AddStep("Join channel 2", () => channelManager.JoinChannel(channel2));
+
+ AddStep("Switch to channel 2", () => clickDrawable(chatOverlay.TabMap[channel2]));
+ AddStep("Close channel 2", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel2]).CloseButton.Child));
+
+ AddAssert("Selector remained closed", () => chatOverlay.SelectionOverlayState == Visibility.Hidden);
+ AddAssert("Current channel is channel 1", () => channelManager.CurrentChannel.Value == channel1);
+
+ AddStep("Close channel 1", () => clickDrawable(((TestChannelTabItem)chatOverlay.TabMap[channel1]).CloseButton.Child));
+
+ AddAssert("Selector is visible", () => chatOverlay.SelectionOverlayState == Visibility.Visible);
+ }
+
+ private void clickDrawable(Drawable d)
+ {
+ InputManager.MoveMouseTo(d);
+ InputManager.Click(MouseButton.Left);
+ }
+
+ private class ChannelManagerContainer : Container
+ {
+ public TestChatOverlay ChatOverlay { get; private set; }
+
+ [Cached]
+ public ChannelManager ChannelManager { get; } = new ChannelManager();
+
+ private readonly List channels;
+
+ public ChannelManagerContainer(List channels)
+ {
+ this.channels = channels;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ ((BindableList)ChannelManager.AvailableChannels).AddRange(channels);
+
+ Child = ChatOverlay = new TestChatOverlay { RelativeSizeAxes = Axes.Both, };
+ ChatOverlay.Show();
+ }
+ }
+
+ private class TestChatOverlay : ChatOverlay
+ {
+ public Visibility SelectionOverlayState => ChannelSelectionOverlay.State.Value;
+
+ public new ChannelSelectionOverlay ChannelSelectionOverlay => base.ChannelSelectionOverlay;
+
+ protected override ChannelTabControl CreateChannelTabControl() => new TestTabControl();
+
+ public IReadOnlyDictionary> TabMap => ((TestTabControl)ChannelTabControl).TabMap;
+ }
+
+ private class TestTabControl : ChannelTabControl
+ {
+ protected override TabItem CreateTabItem(Channel value) => new TestChannelTabItem(value);
+
+ public new IReadOnlyDictionary> TabMap => base.TabMap;
+ }
+
+ private class TestChannelTabItem : PrivateChannelTabItem
+ {
+ public TestChannelTabItem(Channel channel)
+ : base(channel)
+ {
+ }
+
+ public new ClickableContainer CloseButton => base.CloseButton;
}
}
}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs
index ceb19a3ec6..5b0c2d3c67 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneDirectDownloadButton.cs
@@ -3,11 +3,15 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using NUnit.Framework;
+using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
+using osu.Game.Online;
using osu.Game.Overlays.Direct;
using osu.Game.Rulesets.Osu;
+using osu.Game.Tests.Resources;
using osuTK;
namespace osu.Game.Tests.Visual.Online
@@ -16,11 +20,14 @@ namespace osu.Game.Tests.Visual.Online
{
public override IReadOnlyList RequiredTypes => new[]
{
- typeof(DownloadButton)
+ typeof(PanelDownloadButton)
};
private TestDownloadButton downloadButton;
+ [Resolved]
+ private BeatmapManager beatmaps { get; set; }
+
[Test]
public void TestDownloadableBeatmap()
{
@@ -35,11 +42,66 @@ namespace osu.Game.Tests.Visual.Online
assertEnabled(false);
}
+ [Test]
+ public void TestDownloadState()
+ {
+ AddUntilStep("ensure manager loaded", () => beatmaps != null);
+ ensureSoleilyRemoved();
+ createButtonWithBeatmap(createSoleily());
+ AddAssert("button state not downloaded", () => downloadButton.DownloadState == DownloadState.NotDownloaded);
+ AddStep("import soleily", () => beatmaps.Import(new[] { TestResources.GetTestBeatmapForImport() }));
+ AddUntilStep("wait for beatmap import", () => beatmaps.GetAllUsableBeatmapSets().Any(b => b.OnlineBeatmapSetID == 241526));
+ createButtonWithBeatmap(createSoleily());
+ AddAssert("button state downloaded", () => downloadButton.DownloadState == DownloadState.LocallyAvailable);
+ ensureSoleilyRemoved();
+ AddAssert("button state not downloaded", () => downloadButton.DownloadState == DownloadState.NotDownloaded);
+ }
+
+ private void ensureSoleilyRemoved()
+ {
+ AddStep("remove soleily", () =>
+ {
+ var beatmap = beatmaps.QueryBeatmapSet(b => b.OnlineBeatmapSetID == 241526);
+
+ if (beatmap != null) beatmaps.Delete(beatmap);
+ });
+ }
+
private void assertEnabled(bool enabled)
{
AddAssert($"button {(enabled ? "enabled" : "disabled")}", () => downloadButton.DownloadEnabled == enabled);
}
+ private BeatmapSetInfo createSoleily()
+ {
+ return new BeatmapSetInfo
+ {
+ ID = 1,
+ OnlineBeatmapSetID = 241526,
+ OnlineInfo = new BeatmapSetOnlineInfo
+ {
+ Availability = new BeatmapSetOnlineAvailability
+ {
+ DownloadDisabled = false,
+ ExternalLink = string.Empty,
+ },
+ },
+ };
+ }
+
+ private void createButtonWithBeatmap(BeatmapSetInfo beatmap)
+ {
+ AddStep("create button", () =>
+ {
+ Child = downloadButton = new TestDownloadButton(beatmap)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(75, 50),
+ };
+ });
+ }
+
private void createButton(bool downloadable)
{
AddStep("create button", () =>
@@ -81,10 +143,12 @@ namespace osu.Game.Tests.Visual.Online
return beatmap;
}
- private class TestDownloadButton : DownloadButton
+ private class TestDownloadButton : PanelDownloadButton
{
public new bool DownloadEnabled => base.DownloadEnabled;
+ public DownloadState DownloadState => State.Value;
+
public TestDownloadButton(BeatmapSetInfo beatmapSet, bool noVideo = false)
: base(beatmapSet, noVideo)
{
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs
index 582303024b..0da256855a 100644
--- a/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneToolbarRulesetSelector.cs
@@ -7,7 +7,10 @@ using System;
using System.Collections.Generic;
using osu.Framework.Graphics;
using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Allocation;
using osu.Framework.MathUtils;
+using osu.Game.Rulesets;
namespace osu.Game.Tests.Visual.UserInterface
{
@@ -19,17 +22,24 @@ namespace osu.Game.Tests.Visual.UserInterface
typeof(ToolbarRulesetTabButton),
};
- public TestSceneToolbarRulesetSelector()
- {
- ToolbarRulesetSelector selector;
+ [Resolved]
+ private RulesetStore rulesets { get; set; }
- Add(new Container
+ [Test]
+ public void TestDisplay()
+ {
+ ToolbarRulesetSelector selector = null;
+
+ AddStep("create selector", () =>
{
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- AutoSizeAxes = Axes.X,
- Height = Toolbar.HEIGHT,
- Child = selector = new ToolbarRulesetSelector()
+ Child = new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ AutoSizeAxes = Axes.X,
+ Height = Toolbar.HEIGHT,
+ Child = selector = new ToolbarRulesetSelector()
+ };
});
AddStep("Select random", () =>
@@ -38,5 +48,32 @@ namespace osu.Game.Tests.Visual.UserInterface
});
AddStep("Toggle disabled state", () => selector.Current.Disabled = !selector.Current.Disabled);
}
+
+ [Test]
+ public void TestNonFirstRulesetInitialState()
+ {
+ TestSelector selector = null;
+
+ AddStep("create selector", () =>
+ {
+ Child = new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ AutoSizeAxes = Axes.X,
+ Height = Toolbar.HEIGHT,
+ Child = selector = new TestSelector()
+ };
+
+ selector.Current.Value = rulesets.GetRuleset(2);
+ });
+
+ AddAssert("mode line has moved", () => selector.ModeButtonLine.DrawPosition.X > 0);
+ }
+
+ private class TestSelector : ToolbarRulesetSelector
+ {
+ public new Drawable ModeButtonLine => base.ModeButtonLine;
+ }
}
}
diff --git a/osu.Game.Tests/WaveformTestBeatmap.cs b/osu.Game.Tests/WaveformTestBeatmap.cs
index fdb91b7c5b..3e0df8d45e 100644
--- a/osu.Game.Tests/WaveformTestBeatmap.cs
+++ b/osu.Game.Tests/WaveformTestBeatmap.cs
@@ -30,9 +30,9 @@ namespace osu.Game.Tests
trackStore = audioManager.GetTrackStore(reader);
}
- public override void Dispose()
+ protected override void Dispose(bool isDisposing)
{
- base.Dispose();
+ base.Dispose(isDisposing);
stream?.Dispose();
reader?.Dispose();
trackStore?.Dispose();
diff --git a/osu.Game/Audio/HitSampleInfo.cs b/osu.Game/Audio/HitSampleInfo.cs
new file mode 100644
index 0000000000..23a74d3fa6
--- /dev/null
+++ b/osu.Game/Audio/HitSampleInfo.cs
@@ -0,0 +1,70 @@
+// 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;
+
+namespace osu.Game.Audio
+{
+ ///
+ /// Describes a gameplay hit sample.
+ ///
+ [Serializable]
+ public class HitSampleInfo : ISampleInfo
+ {
+ public const string HIT_WHISTLE = @"hitwhistle";
+ public const string HIT_FINISH = @"hitfinish";
+ public const string HIT_NORMAL = @"hitnormal";
+ public const string HIT_CLAP = @"hitclap";
+
+ ///
+ /// An optional ruleset namespace.
+ ///
+ public string Namespace;
+
+ ///
+ /// The bank to load the sample from.
+ ///
+ public string Bank;
+
+ ///
+ /// The name of the sample to load.
+ ///
+ public string Name;
+
+ ///
+ /// An optional suffix to provide priority lookup. Falls back to non-suffixed .
+ ///
+ public string Suffix;
+
+ ///
+ /// The sample volume.
+ ///
+ public int Volume { get; set; }
+
+ ///
+ /// Retrieve all possible filenames that can be used as a source, returned in order of preference (highest first).
+ ///
+ public virtual IEnumerable LookupNames
+ {
+ get
+ {
+ if (!string.IsNullOrEmpty(Namespace))
+ {
+ if (!string.IsNullOrEmpty(Suffix))
+ yield return $"{Namespace}/{Bank}-{Name}{Suffix}";
+
+ yield return $"{Namespace}/{Bank}-{Name}";
+ }
+
+ // 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}";
+ }
+ }
+
+ public HitSampleInfo Clone() => (HitSampleInfo)MemberwiseClone();
+ }
+}
diff --git a/osu.Game/Audio/ISampleInfo.cs b/osu.Game/Audio/ISampleInfo.cs
new file mode 100644
index 0000000000..4f81d37e78
--- /dev/null
+++ b/osu.Game/Audio/ISampleInfo.cs
@@ -0,0 +1,17 @@
+// 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;
+
+namespace osu.Game.Audio
+{
+ public interface ISampleInfo
+ {
+ ///
+ /// Retrieve all possible filenames that can be used as a source, returned in order of preference (highest first).
+ ///
+ IEnumerable LookupNames { get; }
+
+ int Volume { get; }
+ }
+}
diff --git a/osu.Game/Audio/SampleInfo.cs b/osu.Game/Audio/SampleInfo.cs
index 5bc6dce60b..66c07209f3 100644
--- a/osu.Game/Audio/SampleInfo.cs
+++ b/osu.Game/Audio/SampleInfo.cs
@@ -1,67 +1,24 @@
-// 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 System;
using System.Collections.Generic;
namespace osu.Game.Audio
{
- [Serializable]
- public class SampleInfo
+ ///
+ /// Describes a gameplay sample.
+ ///
+ public class SampleInfo : ISampleInfo
{
- public const string HIT_WHISTLE = @"hitwhistle";
- public const string HIT_FINISH = @"hitfinish";
- public const string HIT_NORMAL = @"hitnormal";
- public const string HIT_CLAP = @"hitclap";
+ private readonly string sampleName;
- ///
- /// An optional ruleset namespace.
- ///
- public string Namespace;
-
- ///
- /// The bank to load the sample from.
- ///
- public string Bank;
-
- ///
- /// The name of the sample to load.
- ///
- public string Name;
-
- ///
- /// An optional suffix to provide priority lookup. Falls back to non-suffixed .
- ///
- public string Suffix;
-
- ///
- /// The sample volume.
- ///
- public int Volume;
-
- ///
- /// Retrieve all possible filenames that can be used as a source, returned in order of preference (highest first).
- ///
- public virtual IEnumerable LookupNames
+ public SampleInfo(string sampleName)
{
- get
- {
- if (!string.IsNullOrEmpty(Namespace))
- {
- if (!string.IsNullOrEmpty(Suffix))
- yield return $"{Namespace}/{Bank}-{Name}{Suffix}";
-
- yield return $"{Namespace}/{Bank}-{Name}";
- }
-
- // 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}";
- }
+ this.sampleName = sampleName;
}
- public SampleInfo Clone() => (SampleInfo)MemberwiseClone();
+ public IEnumerable LookupNames => new[] { sampleName };
+
+ public int Volume { get; set; } = 100;
}
}
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index cbaecdc8a7..643fef5904 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -13,6 +13,7 @@ using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Extensions;
using osu.Framework.Graphics.Textures;
+using osu.Framework.Lists;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Framework.Threading;
@@ -149,6 +150,8 @@ namespace osu.Game.Beatmaps
void resetIds() => beatmapSet.Beatmaps.ForEach(b => b.OnlineBeatmapID = null);
}
+ protected override bool CheckLocalAvailability(BeatmapSetInfo model, IQueryable items) => items.Any(b => b.OnlineBeatmapSetID == model.OnlineBeatmapSetID);
+
///
/// Delete a beatmap difficulty.
///
@@ -161,6 +164,8 @@ namespace osu.Game.Beatmaps
/// The beatmap difficulty to restore.
public void Restore(BeatmapInfo beatmap) => beatmaps.Restore(beatmap);
+ private readonly WeakList workingCache = new WeakList();
+
///
/// Retrieve a instance for the provided
///
@@ -175,12 +180,18 @@ namespace osu.Game.Beatmaps
if (beatmapInfo?.BeatmapSet == null || beatmapInfo == DefaultBeatmap?.BeatmapInfo)
return DefaultBeatmap;
+ var cached = workingCache.FirstOrDefault(w => w.BeatmapInfo?.ID == beatmapInfo.ID);
+
+ if (cached != null)
+ return cached;
+
if (beatmapInfo.Metadata == null)
beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata;
WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(Files.Store, new LargeTextureStore(host?.CreateTextureLoaderStore(Files.Store)), beatmapInfo, audioManager);
previous?.TransferTo(working);
+ workingCache.Add(working);
return working;
}
diff --git a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs
index 4c45bef862..7bc7a9056d 100644
--- a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs
+++ b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs
@@ -24,8 +24,8 @@ namespace osu.Game.Beatmaps.ControlPoints
/// Create a SampleInfo based on the sample settings in this control point.
///
/// The name of the same.
- /// A populated .
- public SampleInfo GetSampleInfo(string sampleName = SampleInfo.HIT_NORMAL) => new SampleInfo
+ /// A populated .
+ public HitSampleInfo GetSampleInfo(string sampleName = HitSampleInfo.HIT_NORMAL) => new HitSampleInfo
{
Bank = SampleBank,
Name = sampleName,
@@ -33,15 +33,15 @@ namespace osu.Game.Beatmaps.ControlPoints
};
///
- /// Applies and to a if necessary, returning the modified .
+ /// Applies and to a if necessary, returning the modified .
///
- /// The . This will not be modified.
- /// The modified . This does not share a reference with .
- public virtual SampleInfo ApplyTo(SampleInfo sampleInfo)
+ /// The . This will not be modified.
+ /// The modified . This does not share a reference with .
+ public virtual HitSampleInfo ApplyTo(HitSampleInfo hitSampleInfo)
{
- var newSampleInfo = sampleInfo.Clone();
- newSampleInfo.Bank = sampleInfo.Bank ?? SampleBank;
- newSampleInfo.Volume = sampleInfo.Volume > 0 ? sampleInfo.Volume : SampleVolume;
+ var newSampleInfo = hitSampleInfo.Clone();
+ newSampleInfo.Bank = hitSampleInfo.Bank ?? SampleBank;
+ newSampleInfo.Volume = hitSampleInfo.Volume > 0 ? hitSampleInfo.Volume : SampleVolume;
return newSampleInfo;
}
diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
index 7b7e0e7101..7999c82761 100644
--- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
@@ -193,9 +193,9 @@ namespace osu.Game.Beatmaps.Formats
{
public int CustomSampleBank;
- public override SampleInfo ApplyTo(SampleInfo sampleInfo)
+ public override HitSampleInfo ApplyTo(HitSampleInfo hitSampleInfo)
{
- var baseInfo = base.ApplyTo(sampleInfo);
+ var baseInfo = base.ApplyTo(hitSampleInfo);
if (string.IsNullOrEmpty(baseInfo.Suffix) && CustomSampleBank > 1)
baseInfo.Suffix = CustomSampleBank.ToString();
diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs
index 328763fc9f..37aa0024da 100644
--- a/osu.Game/Beatmaps/WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/WorkingBeatmap.cs
@@ -11,7 +11,9 @@ using osu.Framework.IO.File;
using System.IO;
using System.Linq;
using System.Threading;
+using System.Threading.Tasks;
using osu.Framework.Audio;
+using osu.Framework.Statistics;
using osu.Game.IO.Serialization;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects;
@@ -31,6 +33,8 @@ namespace osu.Game.Beatmaps
protected AudioManager AudioManager { get; }
+ private static readonly GlobalStatistic total_count = GlobalStatistics.Get(nameof(Beatmaps), $"Total {nameof(WorkingBeatmap)}s");
+
protected WorkingBeatmap(BeatmapInfo beatmapInfo, AudioManager audioManager)
{
AudioManager = audioManager;
@@ -38,24 +42,13 @@ namespace osu.Game.Beatmaps
BeatmapSetInfo = beatmapInfo.BeatmapSet;
Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
- beatmap = new RecyclableLazy(() =>
- {
- var b = GetBeatmap() ?? new Beatmap();
-
- // The original beatmap version needs to be preserved as the database doesn't contain it
- BeatmapInfo.BeatmapVersion = b.BeatmapInfo.BeatmapVersion;
-
- // Use the database-backed info for more up-to-date values (beatmap id, ranked status, etc)
- b.BeatmapInfo = BeatmapInfo;
-
- return b;
- });
-
track = new RecyclableLazy