1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-15 11:43:21 +08:00

Merge branch 'master' into comment-editor-1

This commit is contained in:
Feodor0090 2022-12-28 17:46:01 +05:00 committed by GitHub
commit 930cd15649
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
375 changed files with 4425 additions and 2124 deletions

View File

@ -58,7 +58,8 @@ body:
The default places to find the logs on desktop platforms are as follows: The default places to find the logs on desktop platforms are as follows:
- `%AppData%/osu/logs` *on Windows* - `%AppData%/osu/logs` *on Windows*
- `~/.local/share/osu/logs` *on Linux & macOS* - `~/.local/share/osu/logs` *on Linux*
- `~/Library/Application Support/osu/logs` *on macOS*
If you have selected a custom location for the game files, you can find the `logs` folder there. If you have selected a custom location for the game files, you can find the `logs` folder there.

View File

@ -31,7 +31,7 @@ jobs:
run: dotnet tool restore run: dotnet tool restore
- name: Restore Packages - name: Restore Packages
run: dotnet restore run: dotnet restore osu.Desktop.slnf
- name: Restore inspectcode cache - name: Restore inspectcode cache
uses: actions/cache@v3 uses: actions/cache@v3
@ -113,27 +113,36 @@ jobs:
with: with:
dotnet-version: "6.0.x" dotnet-version: "6.0.x"
- name: Setup MSBuild - name: Install .NET workloads
uses: microsoft/setup-msbuild@v1 run: dotnet workload install maui-android
- name: Build - name: Compile
run: msbuild osu.Android/osu.Android.csproj /restore /p:Configuration=Debug run: dotnet build -c Debug osu.Android.slnf
build-only-ios: build-only-ios:
name: Build only (iOS) name: Build only (iOS)
runs-on: macos-latest # change to macos-latest once GitHub finishes migrating all repositories to macOS 12.
runs-on: macos-12
timeout-minutes: 60 timeout-minutes: 60
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v2 uses: actions/checkout@v2
# see https://github.com/actions/runner-images/issues/6771#issuecomment-1354713617
# remove once all workflow VMs use Xcode 14.1
- name: Set Xcode Version
shell: bash
run: |
sudo xcode-select -s "/Applications/Xcode_14.1.app"
echo "MD_APPLE_SDK_ROOT=/Applications/Xcode_14.1.app" >> $GITHUB_ENV
- name: Install .NET 6.0.x - name: Install .NET 6.0.x
uses: actions/setup-dotnet@v1 uses: actions/setup-dotnet@v1
with: with:
dotnet-version: "6.0.x" dotnet-version: "6.0.x"
# Contrary to seemingly any other msbuild, msbuild running on macOS/Mono - name: Install .NET Workloads
# cannot accept .sln(f) files as arguments. run: dotnet workload install maui-ios
# Build just the main game for now.
- name: Build - name: Build
run: msbuild osu.iOS/osu.iOS.csproj /restore /p:Configuration=Debug run: dotnet build -c Debug osu.iOS

View File

@ -28,7 +28,7 @@ jobs:
timeout-minutes: 5 timeout-minutes: 5
steps: steps:
- name: Annotate CI run with test results - name: Annotate CI run with test results
uses: dorny/test-reporter@v1.4.2 uses: dorny/test-reporter@v1.6.0
with: with:
artifact: osu-test-results-${{matrix.os.prettyname}}-${{matrix.threadingMode}} artifact: osu-test-results-${{matrix.os.prettyname}}-${{matrix.threadingMode}}
name: Test Results (${{matrix.os.prettyname}}, ${{matrix.threadingMode}}) name: Test Results (${{matrix.os.prettyname}}, ${{matrix.threadingMode}})

View File

@ -23,7 +23,8 @@ Issues, bug reports and feature suggestions are welcomed, though please keep in
* the in-game logs, which are located at: * the in-game logs, which are located at:
* `%AppData%/osu/logs` (on Windows), * `%AppData%/osu/logs` (on Windows),
* `~/.local/share/osu/logs` (on Linux and macOS), * `~/.local/share/osu/logs` (on Linux),
* `~/Library/Application Support/osu/logs` (on macOS),
* `Android/data/sh.ppy.osulazer/files/logs` (on Android), * `Android/data/sh.ppy.osulazer/files/logs` (on Android),
* on iOS they can be obtained by connecting your device to your desktop and [copying the `logs` directory from the app's own document storage using iTunes](https://support.apple.com/en-us/HT201301#copy-to-computer), * on iOS they can be obtained by connecting your device to your desktop and [copying the `logs` directory from the app's own document storage using iTunes](https://support.apple.com/en-us/HT201301#copy-to-computer),
* your system specifications (including the operating system and platform you are playing on), * your system specifications (including the operating system and platform you are playing on),

View File

@ -32,7 +32,7 @@ If you are looking to install or test osu! without setting up a development envi
**Latest build:** **Latest build:**
| [Windows 8.1+ (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | macOS 10.15+ ([Intel](https://github.com/ppy/osu/releases/latest/download/osu.app.Intel.zip), [Apple Silicon](https://github.com/ppy/osu/releases/latest/download/osu.app.Apple.Silicon.zip)) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS 10+](https://osu.ppy.sh/home/testflight) | [Android 5+](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk) | | [Windows 8.1+ (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | macOS 10.15+ ([Intel](https://github.com/ppy/osu/releases/latest/download/osu.app.Intel.zip), [Apple Silicon](https://github.com/ppy/osu/releases/latest/download/osu.app.Apple.Silicon.zip)) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS 13.4+](https://osu.ppy.sh/home/testflight) | [Android 5+](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk) |
| ------------- | ------------- | ------------- | ------------- | ------------- | | ------------- | ------------- | ------------- | ------------- | ------------- |
- The iOS testflight link may fill up (Apple has a hard limit of 10,000 users). We reset it occasionally when this happens. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements of link resets. - The iOS testflight link may fill up (Apple has a hard limit of 10,000 users). We reset it occasionally when this happens. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements of link resets.

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyTitle>osu.Game.Rulesets.EmptyFreeform</AssemblyTitle> <AssemblyTitle>osu.Game.Rulesets.EmptyFreeform</AssemblyTitle>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
@ -12,4 +12,4 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\..\osu.Game\osu.Game.csproj" /> <ProjectReference Include="..\..\..\..\osu.Game\osu.Game.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyTitle>osu.Game.Rulesets.Pippidon</AssemblyTitle> <AssemblyTitle>osu.Game.Rulesets.Pippidon</AssemblyTitle>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
@ -12,4 +12,4 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\..\osu.Game\osu.Game.csproj" /> <ProjectReference Include="..\..\..\..\osu.Game\osu.Game.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyTitle>osu.Game.Rulesets.EmptyScrolling</AssemblyTitle> <AssemblyTitle>osu.Game.Rulesets.EmptyScrolling</AssemblyTitle>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
@ -12,4 +12,4 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\..\osu.Game\osu.Game.csproj" /> <ProjectReference Include="..\..\..\..\osu.Game\osu.Game.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<AssemblyTitle>osu.Game.Rulesets.Pippidon</AssemblyTitle> <AssemblyTitle>osu.Game.Rulesets.Pippidon</AssemblyTitle>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<PlatformTarget>AnyCPU</PlatformTarget> <PlatformTarget>AnyCPU</PlatformTarget>
@ -12,4 +12,4 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\..\..\osu.Game\osu.Game.csproj" /> <ProjectReference Include="..\..\..\..\osu.Game\osu.Game.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,6 +1,6 @@
clone_depth: 1 clone_depth: 1
version: '{branch}-{build}' version: '{branch}-{build}'
image: Visual Studio 2019 image: Visual Studio 2022
cache: cache:
- '%LOCALAPPDATA%\NuGet\v3-cache -> appveyor.yml' - '%LOCALAPPDATA%\NuGet\v3-cache -> appveyor.yml'
@ -11,6 +11,8 @@ dotnet_csproj:
before_build: before_build:
- cmd: dotnet --info # Useful when version mismatch between CI and local - cmd: dotnet --info # Useful when version mismatch between CI and local
- cmd: dotnet workload install maui-android # Change to `dotnet workload restore` once there's no old projects
- cmd: dotnet workload install maui-ios # Change to `dotnet workload restore` once there's no old projects
- cmd: nuget restore -verbosity quiet # Only nuget.exe knows both new (.NET Core) and old (Xamarin) projects - cmd: nuget restore -verbosity quiet # Only nuget.exe knows both new (.NET Core) and old (Xamarin) projects
build: build:

View File

@ -1,6 +1,6 @@
clone_depth: 1 clone_depth: 1
version: '{build}' version: '{build}'
image: Visual Studio 2019 image: Visual Studio 2022
test: off test: off
skip_non_tags: true skip_non_tags: true
configuration: Release configuration: Release
@ -83,4 +83,4 @@ artifacts:
deploy: deploy:
- provider: Environment - provider: Environment
name: nuget name: nuget

View File

@ -1,61 +1,20 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<LangVersion>8.0</LangVersion> <SupportedOSPlatformVersion>21.0</SupportedOSPlatformVersion>
<OutputPath>bin\$(Configuration)</OutputPath> <RuntimeIdentifiers>android-x86;android-arm;android-arm64</RuntimeIdentifiers>
<WarningLevel>4</WarningLevel> <AndroidPackageFormat>apk</AndroidPackageFormat>
<SchemaVersion>2.0</SchemaVersion> <MandroidI18n>CJK;Mideast;Rare;West;Other;</MandroidI18n>
<BundleAssemblies>false</BundleAssemblies> <AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidMessageHandler</AndroidHttpClientHandlerType>
<AotAssemblies>false</AotAssemblies> <!-- NullabilityInfoContextSupport is disabled by default for Android -->
<OutputType>Library</OutputType> <NullabilityInfoContextSupport>true</NullabilityInfoContextSupport>
<FileAlignment>512</FileAlignment>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidApplication>True</AndroidApplication>
<AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
<TargetFrameworkVersion>v10.0</TargetFrameworkVersion>
<AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<AndroidSupportedAbis>armeabi-v7a;x86;arm64-v8a</AndroidSupportedAbis>
<AndroidEnableSGenConcurrent>true</AndroidEnableSGenConcurrent>
<MandroidI18n>cjk,mideast,other,rare,west</MandroidI18n>
<AndroidLinkMode>SdkOnly</AndroidLinkMode>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<DebugSymbols>True</DebugSymbols>
<DebugType>portable</DebugType>
<Optimize>False</Optimize>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<AndroidManagedSymbols>false</AndroidManagedSymbols>
<AndroidUseSharedRuntime>true</AndroidUseSharedRuntime>
<EmbedAssembliesIntoApk>false</EmbedAssembliesIntoApk>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DebugSymbols>false</DebugSymbols>
<DebugType>None</DebugType>
<Optimize>True</Optimize>
<AndroidManagedSymbols>false</AndroidManagedSymbols>
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk> <EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="$(MSBuildThisFileDirectory)\osu.licenseheader"> <PackageReference Include="ppy.osu.Framework.Android" Version="2022.1226.0" />
<Link>osu.licenseheader</Link>
</None>
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="System.Core" />
<Reference Include="Mono.Android" />
<Reference Include="Java.Interop" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.1127.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.1130.0" />
</ItemGroup>
<ItemGroup Label="Transitive Dependencies">
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
<PackageReference Include="Realm" Version="10.18.0" />
</ItemGroup> </ItemGroup>
<PropertyGroup>
<!-- Fody does not handle Android build well, and warns when unchanged.
Since Realm objects are not declared directly in Android projects, simply disable Fody. -->
<DisableFody>true</DisableFody>
</PropertyGroup>
</Project> </Project>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" package="sh.ppy.osulazer" android:installLocation="auto" android:versionName="0.1.0"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="sh.ppy.osulazer" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" /> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!" android:icon="@drawable/lazer" /> <application android:allowBackup="true" android:supportsRtl="true" android:label="osu!" android:icon="@drawable/lazer" />
</manifest> </manifest>

View File

@ -7,6 +7,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using Android.App; using Android.App;
using Android.Content; using Android.Content;
@ -74,11 +75,23 @@ namespace osu.Android
Debug.Assert(Resources?.DisplayMetrics != null); Debug.Assert(Resources?.DisplayMetrics != null);
Point displaySize = new Point(); Point displaySize = new Point();
#pragma warning disable 618 // GetSize is deprecated
WindowManager.DefaultDisplay.GetSize(displaySize); WindowManager.DefaultDisplay.GetSize(displaySize);
#pragma warning restore 618
float smallestWidthDp = Math.Min(displaySize.X, displaySize.Y) / Resources.DisplayMetrics.Density; float smallestWidthDp = Math.Min(displaySize.X, displaySize.Y) / Resources.DisplayMetrics.Density;
bool isTablet = smallestWidthDp >= 600f; bool isTablet = smallestWidthDp >= 600f;
RequestedOrientation = DefaultOrientation = isTablet ? ScreenOrientation.FullUser : ScreenOrientation.SensorLandscape; RequestedOrientation = DefaultOrientation = isTablet ? ScreenOrientation.FullUser : ScreenOrientation.SensorLandscape;
// Currently (SDK 6.0.200), BundleAssemblies is not runnable for net6-android.
// The assembly files are not available as files either after native AOT.
// Manually load them so that they can be loaded by RulesetStore.loadFromAppDomain.
// REMEMBER to fully uninstall previous version every time when investigating this!
// Don't forget osu.Game.Tests.Android too.
Assembly.Load("osu.Game.Rulesets.Osu");
Assembly.Load("osu.Game.Rulesets.Taiko");
Assembly.Load("osu.Game.Rulesets.Catch");
Assembly.Load("osu.Game.Rulesets.Mania");
} }
protected override void OnNewIntent(Intent intent) => handleIntent(intent); protected override void OnNewIntent(Intent intent) => handleIntent(intent);
@ -127,7 +140,7 @@ namespace osu.Android
cursor.MoveToFirst(); cursor.MoveToFirst();
int filenameColumn = cursor.GetColumnIndex(OpenableColumns.DisplayName); int filenameColumn = cursor.GetColumnIndex(IOpenableColumns.DisplayName);
string filename = cursor.GetString(filenameColumn); string filename = cursor.GetString(filenameColumn);
// SharpCompress requires archive streams to be seekable, which the stream opened by // SharpCompress requires archive streams to be seekable, which the stream opened by

View File

@ -5,7 +5,7 @@
using System; using System;
using Android.App; using Android.App;
using Android.OS; using Microsoft.Maui.Devices;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Android.Input; using osu.Framework.Android.Input;
using osu.Framework.Input.Handlers; using osu.Framework.Input.Handlers;
@ -14,7 +14,6 @@ using osu.Game;
using osu.Game.Overlays.Settings; using osu.Game.Overlays.Settings;
using osu.Game.Updater; using osu.Game.Updater;
using osu.Game.Utils; using osu.Game.Utils;
using Xamarin.Essentials;
namespace osu.Android namespace osu.Android
{ {
@ -48,7 +47,7 @@ namespace osu.Android
// https://stackoverflow.com/questions/52977079/android-sdk-28-versioncode-in-packageinfo-has-been-deprecated // https://stackoverflow.com/questions/52977079/android-sdk-28-versioncode-in-packageinfo-has-been-deprecated
string versionName = string.Empty; string versionName = string.Empty;
if (Build.VERSION.SdkInt >= BuildVersionCodes.P) if (OperatingSystem.IsAndroidVersionAtLeast(28))
{ {
versionName = packageInfo.LongVersionCode.ToString(); versionName = packageInfo.LongVersionCode.ToString();
// ensure we only read the trailing portion of long (the part we are interested in). // ensure we only read the trailing portion of long (the part we are interested in).

View File

@ -1,73 +1,22 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\osu.Android.props" /> <Import Project="..\osu.Android.props" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <TargetFramework>net6.0-android</TargetFramework>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <OutputType>Exe</OutputType>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{D1D5F9A8-B40B-40E6-B02F-482D03346D3D}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TemplateGuid>{122416d6-6b49-4ee2-a1e8-b825f31c79fe}</TemplateGuid>
<RootNamespace>osu.Android</RootNamespace> <RootNamespace>osu.Android</RootNamespace>
<AssemblyName>osu.Android</AssemblyName> <AssemblyName>osu.Android</AssemblyName>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest> <UseMauiEssentials>true</UseMauiEssentials>
<AndroidSupportedAbis>armeabi-v7a;x86;arm64-v8a</AndroidSupportedAbis> <!-- This currently causes random lockups during gameplay. https://github.com/mono/mono/issues/18973 -->
<EnableLLVM>false</EnableLLVM> <!-- This currently causes random lockups during gameplay. https://github.com/mono/mono/issues/18973 --> <EnableLLVM>false</EnableLLVM>
</PropertyGroup> <Version>0.0.0</Version>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <ApplicationVersion Condition=" '$(ApplicationVersion)' == '' ">1</ApplicationVersion>
<MandroidI18n>cjk;mideast;other;rare;west</MandroidI18n> <ApplicationDisplayVersion Condition=" '$(ApplicationDisplayVersion)' == '' ">$(Version)</ApplicationDisplayVersion>
<AndroidDexTool>d8</AndroidDexTool>
<AndroidLinkTool>r8</AndroidLinkTool>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<AndroidLinkMode>None</AndroidLinkMode>
<MandroidI18n>cjk;mideast;other;rare;west</MandroidI18n>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="AndroidJoystickSettings.cs" /> <ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
<Compile Include="AndroidMouseSettings.cs" /> <ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
<Compile Include="GameplayScreenRotationLocker.cs" /> <ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />
<Compile Include="OsuGameActivity.cs" /> <ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
<Compile Include="OsuGameAndroid.cs" /> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> </Project>
<None Include="Properties\AndroidManifest.xml" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj">
<Project>{58f6c80c-1253-4a0e-a465-b8c85ebeadf3}</Project>
<Name>osu.Game.Rulesets.Catch</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj">
<Project>{48f4582b-7687-4621-9cbe-5c24197cb536}</Project>
<Name>osu.Game.Rulesets.Mania</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj">
<Project>{c92a607b-1fdd-4954-9f92-03ff547d9080}</Project>
<Name>osu.Game.Rulesets.Osu</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj">
<Project>{f167e17a-7de6-4af5-b920-a5112296c695}</Project>
<Name>osu.Game.Rulesets.Taiko</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game\osu.Game.csproj">
<Project>{2a66dd92-adb1-4994-89e2-c94e04acda0d}</Project>
<Name>osu.Game</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\lazer.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Formats.Asn1">
<Version>5.0.0</Version>
</PackageReference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Xamarin.Essentials" Version="1.7.0" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
</Project>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- using a different name because package name cannot contain 'catch' --> <!-- using a different name because package name cannot contain 'catch' -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Catch_Tests.Android" android:installLocation="auto"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Catch_Tests.Android" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" /> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!catch Test" /> <application android:allowBackup="true" android:supportsRtl="true" android:label="osu!catch Test" />
</manifest> </manifest>

View File

@ -1,49 +1,24 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\osu.Android.props" /> <Import Project="..\osu.Android.props" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <TargetFramework>net6.0-android</TargetFramework>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <OutputType>Exe</OutputType>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{C5379ECB-3A94-4D2F-AC3B-2615AC23EB0D}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TemplateGuid>{122416d6-6b49-4ee2-a1e8-b825f31c79fe}</TemplateGuid>
<RootNamespace>osu.Game.Rulesets.Catch.Tests</RootNamespace> <RootNamespace>osu.Game.Rulesets.Catch.Tests</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Catch.Tests.Android</AssemblyName> <AssemblyName>osu.Game.Rulesets.Catch.Tests.Android</AssemblyName>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<AndroidSupportedAbis>armeabi-v7a;x86;arm64-v8a</AndroidSupportedAbis>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<AndroidLinkMode>None</AndroidLinkMode>
<MandroidI18n>cjk;mideast;other;rare;west</MandroidI18n>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="MainActivity.cs" /> <Compile Include="..\osu.Game.Rulesets.Catch.Tests\**\*.cs" Exclude="**\obj\**">
</ItemGroup>
<ItemGroup>
<None Include="Properties\AndroidManifest.xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\osu.Game.Rulesets.Catch.Tests\*.cs">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link> <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile> </Compile>
<!-- TargetPath is relative to RootNamespace,
and DllResourceStore is relative to AssemblyName. -->
<EmbeddedResource Include="..\osu.Game.Rulesets.Catch.Tests\**\Resources\**\*">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
<TargetPath>Android\%(RecursiveDir)%(Filename)%(Extension)</TargetPath>
</EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj"> <ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
<Project>{58f6c80c-1253-4a0e-a465-b8c85ebeadf3}</Project> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
<Name>osu.Game.Rulesets.Catch</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game\osu.Game.csproj">
<Project>{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}</Project>
<Name>osu.Game</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="System.Formats.Asn1">
<Version>5.0.0</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
</Project> </Project>

View File

@ -3,7 +3,6 @@
#nullable disable #nullable disable
using osu.Framework.iOS;
using UIKit; using UIKit;
namespace osu.Game.Rulesets.Catch.Tests.iOS namespace osu.Game.Rulesets.Catch.Tests.iOS
@ -12,7 +11,7 @@ namespace osu.Game.Rulesets.Catch.Tests.iOS
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
UIApplication.Main(args, typeof(GameUIApplication), typeof(AppDelegate)); UIApplication.Main(args, null, typeof(AppDelegate));
} }
} }
} }

View File

@ -13,7 +13,7 @@
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>
<string>10.0</string> <string>13.4</string>
<key>UIDeviceFamily</key> <key>UIDeviceFamily</key>
<array> <array>
<integer>1</integer> <integer>1</integer>

View File

@ -1,35 +1,19 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform>
<ProjectGuid>{4004C7B7-1A62-43F1-9DF2-52450FA67E70}</ProjectGuid>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net6.0-ios</TargetFramework>
<SupportedOSPlatformVersion>13.4</SupportedOSPlatformVersion>
<RootNamespace>osu.Game.Rulesets.Catch.Tests</RootNamespace> <RootNamespace>osu.Game.Rulesets.Catch.Tests</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Catch.Tests.iOS</AssemblyName> <AssemblyName>osu.Game.Rulesets.Catch.Tests.iOS</AssemblyName>
</PropertyGroup> </PropertyGroup>
<Import Project="..\osu.iOS.props" /> <Import Project="..\osu.iOS.props" />
<ItemGroup> <ItemGroup>
<None Include="Info.plist" />
<None Include="Entitlements.plist" />
<LinkDescription Include="..\osu.iOS\Linker.xml">
<Link>Linker.xml</Link>
</LinkDescription>
<Compile Include="Application.cs" />
<Compile Include="AppDelegate.cs" />
<Compile Include="..\osu.Game.Rulesets.Catch.Tests\**\*.cs" Exclude="**\obj\**"> <Compile Include="..\osu.Game.Rulesets.Catch.Tests\**\*.cs" Exclude="**\obj\**">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link> <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game\osu.Game.csproj"> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
<Project>{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}</Project> <ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
<Name>osu.Game</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj">
<Project>{58F6C80C-1253-4A0E-A465-B8C85EBEADF3}</Project>
<Name>osu.Game.Rulesets.Catch</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" /> </Project>
</Project>

View File

@ -4,8 +4,10 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics.Cursor;
using osu.Game.Rulesets.Catch.Mods; using osu.Game.Rulesets.Catch.Mods;
using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Catch.UI;
@ -55,6 +57,21 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods
} }
}); });
[Test]
public void TestGameCursorHidden()
{
CreateModTest(new ModTestData
{
Mod = new CatchModRelax(),
Autoplay = false,
PassCondition = () =>
{
InputManager.MoveMouseTo(this.ChildrenOfType<DrawableCatchRuleset>().Single());
return this.ChildrenOfType<MenuCursorContainer>().Single().State.Value == Visibility.Hidden;
}
});
}
private bool passCondition() private bool passCondition()
{ {
var playfield = this.ChildrenOfType<CatchPlayfield>().Single(); var playfield = this.ChildrenOfType<CatchPlayfield>().Single();

View File

@ -121,9 +121,7 @@ namespace osu.Game.Rulesets.Catch.Edit
return new SnapResult(originPosition, StartTime); return new SnapResult(originPosition, StartTime);
} }
return enumerateSnappingCandidates(time) return enumerateSnappingCandidates(time).MinBy(pos => Vector2.DistanceSquared(screenSpacePosition, pos.ScreenSpacePosition));
.OrderBy(pos => Vector2.DistanceSquared(screenSpacePosition, pos.ScreenSpacePosition))
.FirstOrDefault();
} }
private IEnumerable<SnapResult> enumerateSnappingCandidates(double time) private IEnumerable<SnapResult> enumerateSnappingCandidates(double time)

View File

@ -1,193 +0,0 @@
// 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.
using System;
using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Utils;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Catch.Skinning.Argon
{
public partial class ArgonJudgementPiece : CompositeDrawable, IAnimatableJudgement
{
protected readonly HitResult Result;
protected SpriteText JudgementText { get; private set; } = null!;
private RingExplosion? ringExplosion;
[Resolved]
private OsuColour colours { get; set; } = null!;
public ArgonJudgementPiece(HitResult result)
{
Result = result;
Origin = Anchor.Centre;
Y = 160;
}
[BackgroundDependencyLoader]
private void load()
{
AutoSizeAxes = Axes.Both;
InternalChildren = new Drawable[]
{
JudgementText = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = Result.GetDescription().ToUpperInvariant(),
Colour = colours.ForHitResult(Result),
Blending = BlendingParameters.Additive,
Spacing = new Vector2(10, 0),
Font = OsuFont.Default.With(size: 28, weight: FontWeight.Regular),
},
};
if (Result.IsHit())
{
AddInternal(ringExplosion = new RingExplosion(Result)
{
Colour = colours.ForHitResult(Result),
});
}
}
/// <summary>
/// Plays the default animation for this judgement piece.
/// </summary>
/// <remarks>
/// The base implementation only handles fade (for all result types) and misses.
/// Individual rulesets are recommended to implement their appropriate hit animations.
/// </remarks>
public virtual void PlayAnimation()
{
switch (Result)
{
default:
JudgementText
.ScaleTo(Vector2.One)
.ScaleTo(new Vector2(1.4f), 1800, Easing.OutQuint);
break;
case HitResult.Miss:
this.ScaleTo(1.6f);
this.ScaleTo(1, 100, Easing.In);
this.MoveTo(Vector2.Zero);
this.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint);
this.RotateTo(0);
this.RotateTo(40, 800, Easing.InQuint);
break;
}
this.FadeOutFromOne(800);
ringExplosion?.PlayAnimation();
}
public Drawable? GetAboveHitObjectsProxiedContent() => null;
private partial class RingExplosion : CompositeDrawable
{
private readonly float travel = 52;
public RingExplosion(HitResult result)
{
const float thickness = 4;
const float small_size = 9;
const float large_size = 14;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Blending = BlendingParameters.Additive;
int countSmall = 0;
int countLarge = 0;
switch (result)
{
case HitResult.Meh:
countSmall = 3;
travel *= 0.3f;
break;
case HitResult.Ok:
case HitResult.Good:
countSmall = 4;
travel *= 0.6f;
break;
case HitResult.Great:
case HitResult.Perfect:
countSmall = 4;
countLarge = 4;
break;
}
for (int i = 0; i < countSmall; i++)
AddInternal(new RingPiece(thickness) { Size = new Vector2(small_size) });
for (int i = 0; i < countLarge; i++)
AddInternal(new RingPiece(thickness) { Size = new Vector2(large_size) });
}
public void PlayAnimation()
{
foreach (var c in InternalChildren)
{
const float start_position_ratio = 0.3f;
float direction = RNG.NextSingle(0, 360);
float distance = RNG.NextSingle(travel / 2, travel);
c.MoveTo(new Vector2(
MathF.Cos(direction) * distance * start_position_ratio,
MathF.Sin(direction) * distance * start_position_ratio
));
c.MoveTo(new Vector2(
MathF.Cos(direction) * distance,
MathF.Sin(direction) * distance
), 600, Easing.OutQuint);
}
this.FadeOutFromOne(1000, Easing.OutQuint);
}
public partial class RingPiece : CircularContainer
{
public RingPiece(float thickness = 9)
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Masking = true;
BorderThickness = thickness;
BorderColour = Color4.White;
Child = new Box
{
AlwaysPresent = true,
Alpha = 0,
RelativeSizeAxes = Axes.Both
};
}
}
}
}
}

View File

@ -3,7 +3,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -32,7 +31,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(ISkinSource skin) private void load(ISkinSource skin)
{ {
foreach (var state in Enum.GetValues(typeof(CatcherAnimationState)).Cast<CatcherAnimationState>()) foreach (var state in Enum.GetValues<CatcherAnimationState>())
{ {
AddInternal(drawables[state] = getDrawableFor(state).With(d => AddInternal(drawables[state] = getDrawableFor(state).With(d =>
{ {

View File

@ -0,0 +1,15 @@
// 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.
using osu.Framework.Graphics;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Catch.UI
{
public partial class CatchCursorContainer : GameplayCursorContainer
{
// Just hide the cursor.
// The main goal here is to show that we have a cursor so the game never shows the global one.
protected override Drawable CreateCursor() => Empty();
}
}

View File

@ -10,6 +10,7 @@ using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using osuTK; using osuTK;
@ -49,6 +50,8 @@ namespace osu.Game.Rulesets.Catch.UI
this.difficulty = difficulty; this.difficulty = difficulty;
} }
protected override GameplayCursorContainer CreateCursor() => new CatchCursorContainer();
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Description>catch the fruit. to the beat.</Description> <Description>catch the fruit. to the beat.</Description>
@ -15,4 +15,4 @@
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game\osu.Game.csproj" /> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Mania.Tests.Android" android:installLocation="auto"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Mania.Tests.Android" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" /> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!mania Test" /> <application android:allowBackup="true" android:supportsRtl="true" android:label="osu!mania Test" />
</manifest> </manifest>

View File

@ -1,49 +1,24 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\osu.Android.props" /> <Import Project="..\osu.Android.props" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <TargetFramework>net6.0-android</TargetFramework>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <OutputType>Exe</OutputType>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{531F1092-DB27-445D-AA33-2A77C7187C99}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TemplateGuid>{122416d6-6b49-4ee2-a1e8-b825f31c79fe}</TemplateGuid>
<RootNamespace>osu.Game.Rulesets.Mania.Tests</RootNamespace> <RootNamespace>osu.Game.Rulesets.Mania.Tests</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Mania.Tests.Android</AssemblyName> <AssemblyName>osu.Game.Rulesets.Mania.Tests.Android</AssemblyName>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<AndroidSupportedAbis>armeabi-v7a;x86;arm64-v8a</AndroidSupportedAbis>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<AndroidLinkMode>None</AndroidLinkMode>
<MandroidI18n>cjk;mideast;other;rare;west</MandroidI18n>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="MainActivity.cs" /> <Compile Include="..\osu.Game.Rulesets.Mania.Tests\**\*.cs" Exclude="**\obj\**">
</ItemGroup>
<ItemGroup>
<None Include="Properties\AndroidManifest.xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\osu.Game.Rulesets.Mania.Tests\*.cs">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link> <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile> </Compile>
<!-- TargetPath is relative to RootNamespace,
and DllResourceStore is relative to AssemblyName. -->
<EmbeddedResource Include="..\osu.Game.Rulesets.Mania.Tests\**\Resources\**\*">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
<TargetPath>Android\%(RecursiveDir)%(Filename)%(Extension)</TargetPath>
</EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj"> <ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
<Project>{48f4582b-7687-4621-9cbe-5c24197cb536}</Project> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
<Name>osu.Game.Rulesets.Mania</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game\osu.Game.csproj">
<Project>{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}</Project>
<Name>osu.Game</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="System.Formats.Asn1">
<Version>5.0.0</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
</Project> </Project>

View File

@ -3,7 +3,6 @@
#nullable disable #nullable disable
using osu.Framework.iOS;
using UIKit; using UIKit;
namespace osu.Game.Rulesets.Mania.Tests.iOS namespace osu.Game.Rulesets.Mania.Tests.iOS
@ -12,7 +11,7 @@ namespace osu.Game.Rulesets.Mania.Tests.iOS
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
UIApplication.Main(args, typeof(GameUIApplication), typeof(AppDelegate)); UIApplication.Main(args, null, typeof(AppDelegate));
} }
} }
} }

View File

@ -13,7 +13,7 @@
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>
<string>10.0</string> <string>13.4</string>
<key>UIDeviceFamily</key> <key>UIDeviceFamily</key>
<array> <array>
<integer>1</integer> <integer>1</integer>

View File

@ -1,35 +1,19 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform>
<ProjectGuid>{39FD990E-B6CE-4B2A-999F-BC008CF2C64C}</ProjectGuid>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net6.0-ios</TargetFramework>
<SupportedOSPlatformVersion>13.4</SupportedOSPlatformVersion>
<RootNamespace>osu.Game.Rulesets.Mania.Tests</RootNamespace> <RootNamespace>osu.Game.Rulesets.Mania.Tests</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Mania.Tests.iOS</AssemblyName> <AssemblyName>osu.Game.Rulesets.Mania.Tests.iOS</AssemblyName>
</PropertyGroup> </PropertyGroup>
<Import Project="..\osu.iOS.props" /> <Import Project="..\osu.iOS.props" />
<ItemGroup> <ItemGroup>
<None Include="Info.plist" />
<None Include="Entitlements.plist" />
<LinkDescription Include="..\osu.iOS\Linker.xml">
<Link>Linker.xml</Link>
</LinkDescription>
<Compile Include="Application.cs" />
<Compile Include="AppDelegate.cs" />
<Compile Include="..\osu.Game.Rulesets.Mania.Tests\**\*.cs" Exclude="**\obj\**"> <Compile Include="..\osu.Game.Rulesets.Mania.Tests\**\*.cs" Exclude="**\obj\**">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link> <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game\osu.Game.csproj"> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
<Project>{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}</Project> <ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
<Name>osu.Game</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj">
<Project>{48F4582B-7687-4621-9CBE-5C24197CB536}</Project>
<Name>osu.Game.Rulesets.Mania</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" /> </Project>
</Project>

View File

@ -90,6 +90,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
public override bool CursorInPlacementArea => false; public override bool CursorInPlacementArea => false;
public TestHitObjectComposer(Playfield playfield) public TestHitObjectComposer(Playfield playfield)
: base(new ManiaRuleset())
{ {
Playfield = playfield; Playfield = playfield;
} }

View File

@ -35,8 +35,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
protected PatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) protected PatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap)
: base(hitObject, beatmap, previousPattern) : base(hitObject, beatmap, previousPattern)
{ {
if (random == null) throw new ArgumentNullException(nameof(random)); ArgumentNullException.ThrowIfNull(random);
if (originalBeatmap == null) throw new ArgumentNullException(nameof(originalBeatmap)); ArgumentNullException.ThrowIfNull(originalBeatmap);
Random = random; Random = random;
OriginalBeatmap = originalBeatmap; OriginalBeatmap = originalBeatmap;

View File

@ -33,9 +33,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
protected PatternGenerator(HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern) protected PatternGenerator(HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern)
{ {
if (hitObject == null) throw new ArgumentNullException(nameof(hitObject)); ArgumentNullException.ThrowIfNull(hitObject);
if (beatmap == null) throw new ArgumentNullException(nameof(beatmap)); ArgumentNullException.ThrowIfNull(beatmap);
if (previousPattern == null) throw new ArgumentNullException(nameof(previousPattern)); ArgumentNullException.ThrowIfNull(previousPattern);
HitObject = hitObject; HitObject = hitObject;
Beatmap = beatmap; Beatmap = beatmap;

View File

@ -22,8 +22,7 @@ namespace osu.Game.Rulesets.Mania.MathUtils
public static void Sort(T[] keys, IComparer<T> comparer) public static void Sort(T[] keys, IComparer<T> comparer)
{ {
if (keys == null) ArgumentNullException.ThrowIfNull(keys);
throw new ArgumentNullException(nameof(keys));
if (keys.Length == 0) if (keys.Length == 0)
return; return;

View File

@ -3,7 +3,6 @@
using System; using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
@ -18,20 +17,18 @@ using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.Skinning.Argon namespace osu.Game.Rulesets.Mania.Skinning.Argon
{ {
public partial class ArgonJudgementPiece : CompositeDrawable, IAnimatableJudgement public partial class ArgonJudgementPiece : JudgementPiece, IAnimatableJudgement
{ {
protected readonly HitResult Result;
protected SpriteText JudgementText { get; private set; } = null!;
private RingExplosion? ringExplosion; private RingExplosion? ringExplosion;
[Resolved] [Resolved]
private OsuColour colours { get; set; } = null!; private OsuColour colours { get; set; } = null!;
public ArgonJudgementPiece(HitResult result) public ArgonJudgementPiece(HitResult result)
: base(result)
{ {
Result = result; AutoSizeAxes = Axes.Both;
Origin = Anchor.Centre; Origin = Anchor.Centre;
Y = 160; Y = 160;
} }
@ -39,22 +36,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
AutoSizeAxes = Axes.Both;
InternalChildren = new Drawable[]
{
JudgementText = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = Result.GetDescription().ToUpperInvariant(),
Colour = colours.ForHitResult(Result),
Blending = BlendingParameters.Additive,
Spacing = new Vector2(10, 0),
Font = OsuFont.Default.With(size: 28, weight: FontWeight.Regular),
},
};
if (Result.IsHit()) if (Result.IsHit())
{ {
AddInternal(ringExplosion = new RingExplosion(Result) AddInternal(ringExplosion = new RingExplosion(Result)
@ -64,6 +45,16 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
} }
} }
protected override SpriteText CreateJudgementText() =>
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Blending = BlendingParameters.Additive,
Spacing = new Vector2(10, 0),
Font = OsuFont.Default.With(size: 28, weight: FontWeight.Regular),
};
/// <summary> /// <summary>
/// Plays the default animation for this judgement piece. /// Plays the default animation for this judgement piece.
/// </summary> /// </summary>

View File

@ -27,6 +27,10 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
switch (lookup) switch (lookup)
{ {
case GameplaySkinComponentLookup<HitResult> resultComponent: case GameplaySkinComponentLookup<HitResult> resultComponent:
// This should eventually be moved to a skin setting, when supported.
if (Skin is ArgonProSkin && resultComponent.Component >= HitResult.Great)
return Drawable.Empty();
return new ArgonJudgementPiece(resultComponent.Component); return new ArgonJudgementPiece(resultComponent.Component);
case ManiaSkinComponentLookup maniaComponent: case ManiaSkinComponentLookup maniaComponent:

View File

@ -134,6 +134,9 @@ namespace osu.Game.Rulesets.Mania.UI
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)
{ {
// must happen before children are disposed in base call to prevent illegal accesses to the hit explosion pool.
NewResult -= OnNewResult;
base.Dispose(isDisposing); base.Dispose(isDisposing);
if (skin != null) if (skin != null)

View File

@ -29,8 +29,7 @@ namespace osu.Game.Rulesets.Mania.UI
public ManiaPlayfield(List<StageDefinition> stageDefinitions) public ManiaPlayfield(List<StageDefinition> stageDefinitions)
{ {
if (stageDefinitions == null) ArgumentNullException.ThrowIfNull(stageDefinitions);
throw new ArgumentNullException(nameof(stageDefinitions));
if (stageDefinitions.Count <= 0) if (stageDefinitions.Count <= 0)
throw new ArgumentException("Can't have zero or fewer stages."); throw new ArgumentException("Can't have zero or fewer stages.");

View File

@ -156,6 +156,9 @@ namespace osu.Game.Rulesets.Mania.UI
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)
{ {
// must happen before children are disposed in base call to prevent illegal accesses to the judgement pool.
NewResult -= OnNewResult;
base.Dispose(isDisposing); base.Dispose(isDisposing);
if (currentSkin != null) if (currentSkin != null)

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Description>smash the keys. to the beat.</Description> <Description>smash the keys. to the beat.</Description>
@ -15,4 +15,4 @@
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game\osu.Game.csproj" /> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Osu.Tests.Android" android:installLocation="auto"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Osu.Tests.Android" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" /> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!standard Test" /> <application android:allowBackup="true" android:supportsRtl="true" android:label="osu!standard Test" />
</manifest> </manifest>

View File

@ -1,49 +1,27 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\osu.Android.props" /> <Import Project="..\osu.Android.props" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <TargetFramework>net6.0-android</TargetFramework>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <OutputType>Exe</OutputType>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{90CAB706-39CB-4B93-9629-3218A6FF8E9B}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TemplateGuid>{122416d6-6b49-4ee2-a1e8-b825f31c79fe}</TemplateGuid>
<RootNamespace>osu.Game.Rulesets.Osu.Tests</RootNamespace> <RootNamespace>osu.Game.Rulesets.Osu.Tests</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Osu.Tests.Android</AssemblyName> <AssemblyName>osu.Game.Rulesets.Osu.Tests.Android</AssemblyName>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<AndroidSupportedAbis>armeabi-v7a;x86;arm64-v8a</AndroidSupportedAbis>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<AndroidLinkMode>None</AndroidLinkMode>
<MandroidI18n>cjk;mideast;other;rare;west</MandroidI18n>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="MainActivity.cs" /> <Compile Include="..\osu.Game.Rulesets.Osu.Tests\**\*.cs" Exclude="**\obj\**">
</ItemGroup>
<ItemGroup>
<None Include="Properties\AndroidManifest.xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\osu.Game.Rulesets.Osu.Tests\*.cs">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link> <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile> </Compile>
<!-- TargetPath is relative to RootNamespace,
and DllResourceStore is relative to AssemblyName. -->
<EmbeddedResource Include="..\osu.Game.Rulesets.Osu.Tests\**\Resources\**\*">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
<TargetPath>Android\%(RecursiveDir)%(Filename)%(Extension)</TargetPath>
</EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj"> <ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />
<Project>{c92a607b-1fdd-4954-9f92-03ff547d9080}</Project> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
<Name>osu.Game.Rulesets.Osu</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game\osu.Game.csproj">
<Project>{2a66dd92-adb1-4994-89e2-c94e04acda0d}</Project>
<Name>osu.Game</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Formats.Asn1"> <PackageReference Include="Moq" Version="4.17.2" />
<Version>5.0.0</Version>
</PackageReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
</Project> </Project>

View File

@ -3,7 +3,6 @@
#nullable disable #nullable disable
using osu.Framework.iOS;
using UIKit; using UIKit;
namespace osu.Game.Rulesets.Osu.Tests.iOS namespace osu.Game.Rulesets.Osu.Tests.iOS
@ -12,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Tests.iOS
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
UIApplication.Main(args, typeof(GameUIApplication), typeof(AppDelegate)); UIApplication.Main(args, null, typeof(AppDelegate));
} }
} }
} }

View File

@ -13,7 +13,7 @@
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>
<string>10.0</string> <string>13.4</string>
<key>UIDeviceFamily</key> <key>UIDeviceFamily</key>
<array> <array>
<integer>1</integer> <integer>1</integer>

View File

@ -1,35 +1,20 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <OutputType>Exe</OutputType>
<Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform> <TargetFramework>net6.0-ios</TargetFramework>
<ProjectGuid>{6653CA6F-DB06-4604-A3FD-762E25C2AF96}</ProjectGuid> <SupportedOSPlatformVersion>13.4</SupportedOSPlatformVersion>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<RootNamespace>osu.Game.Rulesets.Osu.Tests</RootNamespace> <RootNamespace>osu.Game.Rulesets.Osu.Tests</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Osu.Tests.iOS</AssemblyName> <AssemblyName>osu.Game.Rulesets.Osu.Tests.iOS</AssemblyName>
</PropertyGroup> </PropertyGroup>
<Import Project="..\osu.iOS.props" /> <Import Project="..\osu.iOS.props" />
<ItemGroup> <ItemGroup>
<None Include="Info.plist" />
<None Include="Entitlements.plist" />
<LinkDescription Include="..\osu.iOS\Linker.xml">
<Link>Linker.xml</Link>
</LinkDescription>
<Compile Include="Application.cs" />
<Compile Include="AppDelegate.cs" />
<Compile Include="..\osu.Game.Rulesets.Osu.Tests\**\*.cs" Exclude="**\obj\**"> <Compile Include="..\osu.Game.Rulesets.Osu.Tests\**\*.cs" Exclude="**\obj\**">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link> <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game\osu.Game.csproj"> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
<Project>{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}</Project> <ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />
<Name>osu.Game</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj">
<Project>{C92A607B-1FDD-4954-9F92-03FF547D9080}</Project>
<Name>osu.Game.Rulesets.Osu</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" /> </Project>
</Project>

View File

@ -0,0 +1,91 @@
// 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.
using System.Linq;
using NUnit.Framework;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.Osu.Utils;
using osuTK;
namespace osu.Game.Rulesets.Osu.Tests
{
[TestFixture]
public class OsuHitObjectGenerationUtilsTest
{
private static Slider createTestSlider()
{
var slider = new Slider
{
Position = new Vector2(128, 128),
Path = new SliderPath
{
ControlPoints =
{
new PathControlPoint(new Vector2(), PathType.Linear),
new PathControlPoint(new Vector2(-64, -128), PathType.Linear), // absolute position: (64, 0)
new PathControlPoint(new Vector2(-128, 0), PathType.Linear) // absolute position: (0, 128)
}
},
RepeatCount = 1
};
slider.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
return slider;
}
[Test]
public void TestReflectSliderHorizontallyAlongPlayfield()
{
var slider = createTestSlider();
OsuHitObjectGenerationUtils.ReflectHorizontallyAlongPlayfield(slider);
Assert.That(slider.Position, Is.EqualTo(new Vector2(OsuPlayfield.BASE_SIZE.X - 128, 128)));
Assert.That(slider.NestedHitObjects.OfType<SliderRepeat>().Single().Position, Is.EqualTo(new Vector2(OsuPlayfield.BASE_SIZE.X - 0, 128)));
Assert.That(slider.Path.ControlPoints.Select(point => point.Position), Is.EquivalentTo(new[]
{
new Vector2(),
new Vector2(64, -128),
new Vector2(128, 0)
}));
}
[Test]
public void TestReflectSliderVerticallyAlongPlayfield()
{
var slider = createTestSlider();
OsuHitObjectGenerationUtils.ReflectVerticallyAlongPlayfield(slider);
Assert.That(slider.Position, Is.EqualTo(new Vector2(128, OsuPlayfield.BASE_SIZE.Y - 128)));
Assert.That(slider.NestedHitObjects.OfType<SliderRepeat>().Single().Position, Is.EqualTo(new Vector2(0, OsuPlayfield.BASE_SIZE.Y - 128)));
Assert.That(slider.Path.ControlPoints.Select(point => point.Position), Is.EquivalentTo(new[]
{
new Vector2(),
new Vector2(-64, 128),
new Vector2(-128, 0)
}));
}
[Test]
public void TestFlipSliderInPlaceHorizontally()
{
var slider = createTestSlider();
OsuHitObjectGenerationUtils.FlipSliderInPlaceHorizontally(slider);
Assert.That(slider.Position, Is.EqualTo(new Vector2(128, 128)));
Assert.That(slider.NestedHitObjects.OfType<SliderRepeat>().Single().Position, Is.EqualTo(new Vector2(256, 128)));
Assert.That(slider.Path.ControlPoints.Select(point => point.Position), Is.EquivalentTo(new[]
{
new Vector2(),
new Vector2(64, -128),
new Vector2(128, 0)
}));
}
}
}

View File

@ -160,9 +160,9 @@ namespace osu.Game.Rulesets.Osu.Tests
static bool assertSamples(HitObject hitObject) => hitObject.Samples.All(s => s.Name != HitSampleInfo.HIT_CLAP && s.Name != HitSampleInfo.HIT_WHISTLE); static bool assertSamples(HitObject hitObject) => hitObject.Samples.All(s => s.Name != HitSampleInfo.HIT_CLAP && s.Name != HitSampleInfo.HIT_WHISTLE);
} }
private Drawable testSimpleBig(int repeats = 0) => createSlider(2, repeats: repeats); private Drawable testSimpleBig(int repeats = 0) => createSlider(repeats: repeats);
private Drawable testSimpleBigLargeStackOffset(int repeats = 0) => createSlider(2, repeats: repeats, stackHeight: 10); private Drawable testSimpleBigLargeStackOffset(int repeats = 0) => createSlider(repeats: repeats, stackHeight: 10);
private Drawable testDistanceOverflow(int repeats = 0) private Drawable testDistanceOverflow(int repeats = 0)
{ {

View File

@ -133,6 +133,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
switch (e.Action) switch (e.Action)
{ {
case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Add:
Debug.Assert(e.NewItems != null);
// If inserting in the path (not appending), // If inserting in the path (not appending),
// update indices of existing connections after insert location // update indices of existing connections after insert location
if (e.NewStartingIndex < Pieces.Count) if (e.NewStartingIndex < Pieces.Count)
@ -164,6 +166,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
break; break;
case NotifyCollectionChangedAction.Remove: case NotifyCollectionChangedAction.Remove:
Debug.Assert(e.OldItems != null);
foreach (var point in e.OldItems.Cast<PathControlPoint>()) foreach (var point in e.OldItems.Cast<PathControlPoint>())
{ {
foreach (var piece in Pieces.Where(p => p.ControlPoint == point).ToArray()) foreach (var piece in Pieces.Where(p => p.ControlPoint == point).ToArray())

View File

@ -4,6 +4,8 @@
#nullable disable #nullable disable
using System; using System;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit;
@ -22,6 +24,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners
private bool isPlacingEnd; private bool isPlacingEnd;
[Resolved(CanBeNull = true)]
[CanBeNull]
private IBeatSnapProvider beatSnapProvider { get; set; }
public SpinnerPlacementBlueprint() public SpinnerPlacementBlueprint()
: base(new Spinner { Position = OsuPlayfield.BASE_SIZE / 2 }) : base(new Spinner { Position = OsuPlayfield.BASE_SIZE / 2 })
{ {
@ -33,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners
base.Update(); base.Update();
if (isPlacingEnd) if (isPlacingEnd)
HitObject.EndTime = Math.Max(HitObject.StartTime, EditorClock.CurrentTime); updateEndTimeFromCurrent();
piece.UpdateFrom(HitObject); piece.UpdateFrom(HitObject);
} }
@ -45,7 +51,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners
if (e.Button != MouseButton.Right) if (e.Button != MouseButton.Right)
return false; return false;
HitObject.EndTime = EditorClock.CurrentTime; updateEndTimeFromCurrent();
EndPlacement(true); EndPlacement(true);
} }
else else
@ -61,5 +67,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners
return true; return true;
} }
private void updateEndTimeFromCurrent()
{
HitObject.EndTime = beatSnapProvider == null
? Math.Max(HitObject.StartTime, EditorClock.CurrentTime)
: Math.Max(HitObject.StartTime + beatSnapProvider.GetBeatLengthAtTime(HitObject.StartTime), beatSnapProvider.SnapTime(EditorClock.CurrentTime));
}
} }
} }

View File

@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Mods
{ {
var osuObject = (OsuHitObject)hitObject; var osuObject = (OsuHitObject)hitObject;
OsuHitObjectGenerationUtils.ReflectVertically(osuObject); OsuHitObjectGenerationUtils.ReflectVerticallyAlongPlayfield(osuObject);
} }
} }
} }

View File

@ -27,16 +27,16 @@ namespace osu.Game.Rulesets.Osu.Mods
switch (Reflection.Value) switch (Reflection.Value)
{ {
case MirrorType.Horizontal: case MirrorType.Horizontal:
OsuHitObjectGenerationUtils.ReflectHorizontally(osuObject); OsuHitObjectGenerationUtils.ReflectHorizontallyAlongPlayfield(osuObject);
break; break;
case MirrorType.Vertical: case MirrorType.Vertical:
OsuHitObjectGenerationUtils.ReflectVertically(osuObject); OsuHitObjectGenerationUtils.ReflectVerticallyAlongPlayfield(osuObject);
break; break;
case MirrorType.Both: case MirrorType.Both:
OsuHitObjectGenerationUtils.ReflectHorizontally(osuObject); OsuHitObjectGenerationUtils.ReflectHorizontallyAlongPlayfield(osuObject);
OsuHitObjectGenerationUtils.ReflectVertically(osuObject); OsuHitObjectGenerationUtils.ReflectVerticallyAlongPlayfield(osuObject);
break; break;
} }
} }

View File

@ -65,6 +65,11 @@ namespace osu.Game.Rulesets.Osu.Mods
flowDirection = !flowDirection; flowDirection = !flowDirection;
} }
if (positionInfos[i].HitObject is Slider slider && random.NextDouble() < 0.5)
{
OsuHitObjectGenerationUtils.FlipSliderInPlaceHorizontally(slider);
}
if (i == 0) if (i == 0)
{ {
positionInfos[i].DistanceFromPrevious = (float)(random.NextDouble() * OsuPlayfield.BASE_SIZE.Y / 2); positionInfos[i].DistanceFromPrevious = (float)(random.NextDouble() * OsuPlayfield.BASE_SIZE.Y / 2);

View File

@ -17,7 +17,6 @@ using osu.Game.Configuration;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Overlays.Settings; using osu.Game.Overlays.Settings;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Beatmaps;
@ -196,8 +195,8 @@ namespace osu.Game.Rulesets.Osu.Mods
private IEnumerable<double> generateBeats(IBeatmap beatmap, IReadOnlyCollection<OsuHitObject> originalHitObjects) private IEnumerable<double> generateBeats(IBeatmap beatmap, IReadOnlyCollection<OsuHitObject> originalHitObjects)
{ {
double startTime = originalHitObjects.First().StartTime; double startTime = beatmap.HitObjects.First().StartTime;
double endTime = originalHitObjects.Last().GetEndTime(); double endTime = beatmap.GetLastObjectTime();
var beats = beatmap.ControlPointInfo.TimingPoints var beats = beatmap.ControlPointInfo.TimingPoints
// Ignore timing points after endTime // Ignore timing points after endTime

View File

@ -82,10 +82,10 @@ namespace osu.Game.Rulesets.Osu.Replays
private class ReplayFrameComparer : IComparer<ReplayFrame> private class ReplayFrameComparer : IComparer<ReplayFrame>
{ {
public int Compare(ReplayFrame f1, ReplayFrame f2) public int Compare(ReplayFrame? f1, ReplayFrame? f2)
{ {
if (f1 == null) throw new ArgumentNullException(nameof(f1)); ArgumentNullException.ThrowIfNull(f1);
if (f2 == null) throw new ArgumentNullException(nameof(f2)); ArgumentNullException.ThrowIfNull(f2);
return f1.Time.CompareTo(f2.Time); return f1.Time.CompareTo(f2.Time);
} }

View File

@ -3,7 +3,6 @@
using System; using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
@ -17,42 +16,24 @@ using osuTK;
namespace osu.Game.Rulesets.Osu.Skinning.Argon namespace osu.Game.Rulesets.Osu.Skinning.Argon
{ {
public partial class ArgonJudgementPiece : CompositeDrawable, IAnimatableJudgement public partial class ArgonJudgementPiece : JudgementPiece, IAnimatableJudgement
{ {
protected readonly HitResult Result;
protected SpriteText JudgementText { get; private set; } = null!;
private RingExplosion? ringExplosion; private RingExplosion? ringExplosion;
[Resolved] [Resolved]
private OsuColour colours { get; set; } = null!; private OsuColour colours { get; set; } = null!;
public ArgonJudgementPiece(HitResult result) public ArgonJudgementPiece(HitResult result)
: base(result)
{ {
Result = result; AutoSizeAxes = Axes.Both;
Origin = Anchor.Centre; Origin = Anchor.Centre;
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
AutoSizeAxes = Axes.Both;
InternalChildren = new Drawable[]
{
JudgementText = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = Result.GetDescription().ToUpperInvariant(),
Colour = colours.ForHitResult(Result),
Blending = BlendingParameters.Additive,
Spacing = new Vector2(5, 0),
Font = OsuFont.Default.With(size: 20, weight: FontWeight.Bold),
},
};
if (Result.IsHit()) if (Result.IsHit())
{ {
AddInternal(ringExplosion = new RingExplosion(Result) AddInternal(ringExplosion = new RingExplosion(Result)
@ -62,6 +43,16 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
} }
} }
protected override SpriteText CreateJudgementText() =>
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Blending = BlendingParameters.Additive,
Spacing = new Vector2(5, 0),
Font = OsuFont.Default.With(size: 20, weight: FontWeight.Bold),
};
/// <summary> /// <summary>
/// Plays the default animation for this judgement piece. /// Plays the default animation for this judgement piece.
/// </summary> /// </summary>

View File

@ -19,6 +19,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
switch (lookup) switch (lookup)
{ {
case GameplaySkinComponentLookup<HitResult> resultComponent: case GameplaySkinComponentLookup<HitResult> resultComponent:
// This should eventually be moved to a skin setting, when supported.
if (Skin is ArgonProSkin && resultComponent.Component >= HitResult.Great)
return Drawable.Empty();
return new ArgonJudgementPiece(resultComponent.Component); return new ArgonJudgementPiece(resultComponent.Component);
case OsuSkinComponentLookup osuComponent: case OsuSkinComponentLookup osuComponent:

View File

@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.UI
HitPolicy = new StartTimeOrderedHitPolicy(); HitPolicy = new StartTimeOrderedHitPolicy();
var hitWindows = new OsuHitWindows(); var hitWindows = new OsuHitWindows();
foreach (var result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Where(r => r > HitResult.None && hitWindows.IsHitResultAllowed(r))) foreach (var result in Enum.GetValues<HitResult>().Where(r => r > HitResult.None && hitWindows.IsHitResultAllowed(r)))
poolDictionary.Add(result, new DrawableJudgementPool(result, onJudgementLoaded)); poolDictionary.Add(result, new DrawableJudgementPool(result, onJudgementLoaded));
AddRangeInternal(poolDictionary.Values); AddRangeInternal(poolDictionary.Values);

View File

@ -112,44 +112,46 @@ namespace osu.Game.Rulesets.Osu.Utils
/// Reflects the position of the <see cref="OsuHitObject"/> in the playfield horizontally. /// Reflects the position of the <see cref="OsuHitObject"/> in the playfield horizontally.
/// </summary> /// </summary>
/// <param name="osuObject">The object to reflect.</param> /// <param name="osuObject">The object to reflect.</param>
public static void ReflectHorizontally(OsuHitObject osuObject) public static void ReflectHorizontallyAlongPlayfield(OsuHitObject osuObject)
{ {
osuObject.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - osuObject.X, osuObject.Position.Y); osuObject.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - osuObject.X, osuObject.Position.Y);
if (!(osuObject is Slider slider)) if (osuObject is not Slider slider)
return; return;
// No need to update the head and tail circles, since slider handles that when the new slider path is set void reflectNestedObject(OsuHitObject nested) => nested.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - nested.Position.X, nested.Position.Y);
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y)); static void reflectControlPoint(PathControlPoint point) => point.Position = new Vector2(-point.Position.X, point.Position.Y);
slider.NestedHitObjects.OfType<SliderRepeat>().ForEach(h => h.Position = new Vector2(OsuPlayfield.BASE_SIZE.X - h.Position.X, h.Position.Y));
var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position, p.Type)).ToArray(); modifySlider(slider, reflectNestedObject, reflectControlPoint);
foreach (var point in controlPoints)
point.Position = new Vector2(-point.Position.X, point.Position.Y);
slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value);
} }
/// <summary> /// <summary>
/// Reflects the position of the <see cref="OsuHitObject"/> in the playfield vertically. /// Reflects the position of the <see cref="OsuHitObject"/> in the playfield vertically.
/// </summary> /// </summary>
/// <param name="osuObject">The object to reflect.</param> /// <param name="osuObject">The object to reflect.</param>
public static void ReflectVertically(OsuHitObject osuObject) public static void ReflectVerticallyAlongPlayfield(OsuHitObject osuObject)
{ {
osuObject.Position = new Vector2(osuObject.Position.X, OsuPlayfield.BASE_SIZE.Y - osuObject.Y); osuObject.Position = new Vector2(osuObject.Position.X, OsuPlayfield.BASE_SIZE.Y - osuObject.Y);
if (!(osuObject is Slider slider)) if (osuObject is not Slider slider)
return; return;
// No need to update the head and tail circles, since slider handles that when the new slider path is set void reflectNestedObject(OsuHitObject nested) => nested.Position = new Vector2(nested.Position.X, OsuPlayfield.BASE_SIZE.Y - nested.Position.Y);
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); static void reflectControlPoint(PathControlPoint point) => point.Position = new Vector2(point.Position.X, -point.Position.Y);
slider.NestedHitObjects.OfType<SliderRepeat>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position, p.Type)).ToArray(); modifySlider(slider, reflectNestedObject, reflectControlPoint);
foreach (var point in controlPoints) }
point.Position = new Vector2(point.Position.X, -point.Position.Y);
slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value); /// <summary>
/// Flips the position of the <see cref="Slider"/> around its start position horizontally.
/// </summary>
/// <param name="slider">The slider to be flipped.</param>
public static void FlipSliderInPlaceHorizontally(Slider slider)
{
void flipNestedObject(OsuHitObject nested) => nested.Position = new Vector2(slider.X - (nested.X - slider.X), nested.Y);
static void flipControlPoint(PathControlPoint point) => point.Position = new Vector2(-point.Position.X, point.Position.Y);
modifySlider(slider, flipNestedObject, flipControlPoint);
} }
/// <summary> /// <summary>
@ -160,14 +162,20 @@ namespace osu.Game.Rulesets.Osu.Utils
public static void RotateSlider(Slider slider, float rotation) public static void RotateSlider(Slider slider, float rotation)
{ {
void rotateNestedObject(OsuHitObject nested) => nested.Position = rotateVector(nested.Position - slider.Position, rotation) + slider.Position; void rotateNestedObject(OsuHitObject nested) => nested.Position = rotateVector(nested.Position - slider.Position, rotation) + slider.Position;
void rotateControlPoint(PathControlPoint point) => point.Position = rotateVector(point.Position, rotation);
modifySlider(slider, rotateNestedObject, rotateControlPoint);
}
private static void modifySlider(Slider slider, Action<OsuHitObject> modifyNestedObject, Action<PathControlPoint> modifyControlPoint)
{
// No need to update the head and tail circles, since slider handles that when the new slider path is set // No need to update the head and tail circles, since slider handles that when the new slider path is set
slider.NestedHitObjects.OfType<SliderTick>().ForEach(rotateNestedObject); slider.NestedHitObjects.OfType<SliderTick>().ForEach(modifyNestedObject);
slider.NestedHitObjects.OfType<SliderRepeat>().ForEach(rotateNestedObject); slider.NestedHitObjects.OfType<SliderRepeat>().ForEach(modifyNestedObject);
var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position, p.Type)).ToArray(); var controlPoints = slider.Path.ControlPoints.Select(p => new PathControlPoint(p.Position, p.Type)).ToArray();
foreach (var point in controlPoints) foreach (var point in controlPoints)
point.Position = rotateVector(point.Position, rotation); modifyControlPoint(point);
slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value); slider.Path = new SliderPath(controlPoints, slider.Path.ExpectedDistance.Value);
} }

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Description>click the circles. to the beat.</Description> <Description>click the circles. to the beat.</Description>
@ -15,4 +15,4 @@
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game\osu.Game.csproj" /> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Taiko.Tests.Android" android:installLocation="auto"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Taiko.Tests.Android" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" /> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!taiko Test" /> <application android:allowBackup="true" android:supportsRtl="true" android:label="osu!taiko Test" />
</manifest> </manifest>

View File

@ -1,49 +1,24 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\osu.Android.props" /> <Import Project="..\osu.Android.props" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <TargetFramework>net6.0-android</TargetFramework>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <OutputType>Exe</OutputType>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{3701A0A1-8476-42C6-B5C4-D24129B4A484}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TemplateGuid>{122416d6-6b49-4ee2-a1e8-b825f31c79fe}</TemplateGuid>
<RootNamespace>osu.Game.Rulesets.Taiko.Tests</RootNamespace> <RootNamespace>osu.Game.Rulesets.Taiko.Tests</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Taiko.Tests.Android</AssemblyName> <AssemblyName>osu.Game.Rulesets.Taiko.Tests.Android</AssemblyName>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<AndroidSupportedAbis>armeabi-v7a;x86;arm64-v8a</AndroidSupportedAbis>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<AndroidLinkMode>None</AndroidLinkMode>
<MandroidI18n>cjk;mideast;other;rare;west</MandroidI18n>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="MainActivity.cs" /> <Compile Include="..\osu.Game.Rulesets.Taiko.Tests\**\*.cs" Exclude="**\obj\**">
</ItemGroup>
<ItemGroup>
<None Include="Properties\AndroidManifest.xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\osu.Game.Rulesets.Taiko.Tests\*.cs">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link> <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile> </Compile>
<!-- TargetPath is relative to RootNamespace,
and DllResourceStore is relative to AssemblyName. -->
<EmbeddedResource Include="..\osu.Game.Rulesets.Taiko.Tests\**\Resources\**\*">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
<TargetPath>Android\%(RecursiveDir)%(Filename)%(Extension)</TargetPath>
</EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj"> <ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
<Project>{f167e17a-7de6-4af5-b920-a5112296c695}</Project> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
<Name>osu.Game.Rulesets.Taiko</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game\osu.Game.csproj">
<Project>{2a66dd92-adb1-4994-89e2-c94e04acda0d}</Project>
<Name>osu.Game</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="System.Formats.Asn1">
<Version>5.0.0</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
</Project> </Project>

View File

@ -3,7 +3,6 @@
#nullable disable #nullable disable
using osu.Framework.iOS;
using UIKit; using UIKit;
namespace osu.Game.Rulesets.Taiko.Tests.iOS namespace osu.Game.Rulesets.Taiko.Tests.iOS
@ -12,7 +11,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.iOS
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
UIApplication.Main(args, typeof(GameUIApplication), typeof(AppDelegate)); UIApplication.Main(args, null, typeof(AppDelegate));
} }
} }
} }

View File

@ -13,7 +13,7 @@
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>
<string>10.0</string> <string>13.4</string>
<key>UIDeviceFamily</key> <key>UIDeviceFamily</key>
<array> <array>
<integer>1</integer> <integer>1</integer>

View File

@ -1,35 +1,19 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform>
<ProjectGuid>{7E408809-66AC-49D1-AF4D-98834F9B979A}</ProjectGuid>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net6.0-ios</TargetFramework>
<SupportedOSPlatformVersion>13.4</SupportedOSPlatformVersion>
<RootNamespace>osu.Game.Rulesets.Taiko.Tests</RootNamespace> <RootNamespace>osu.Game.Rulesets.Taiko.Tests</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Taiko.Tests.iOS</AssemblyName> <AssemblyName>osu.Game.Rulesets.Taiko.Tests.iOS</AssemblyName>
</PropertyGroup> </PropertyGroup>
<Import Project="..\osu.iOS.props" /> <Import Project="..\osu.iOS.props" />
<ItemGroup> <ItemGroup>
<None Include="Info.plist" />
<None Include="Entitlements.plist" />
<LinkDescription Include="..\osu.iOS\Linker.xml">
<Link>Linker.xml</Link>
</LinkDescription>
<Compile Include="Application.cs" />
<Compile Include="AppDelegate.cs" />
<Compile Include="..\osu.Game.Rulesets.Taiko.Tests\**\*.cs" Exclude="**\obj\**"> <Compile Include="..\osu.Game.Rulesets.Taiko.Tests\**\*.cs" Exclude="**\obj\**">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link> <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game\osu.Game.csproj"> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
<Project>{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}</Project> <ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
<Name>osu.Game</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj">
<Project>{F167E17A-7DE6-4AF5-B920-A5112296C695}</Project>
<Name>osu.Game.Rulesets.Taiko</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" /> </Project>
</Project>

View File

@ -0,0 +1,37 @@
// 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.
using NUnit.Framework;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Taiko.Skinning.Legacy;
namespace osu.Game.Rulesets.Taiko.Tests.Skinning
{
public partial class TestSceneTaikoKiaiGlow : TaikoSkinnableTestScene
{
[Test]
public void TestKiaiGlow()
{
AddStep("Create kiai glow", () => SetContents(_ => new LegacyKiaiGlow()));
AddToggleStep("Toggle kiai mode", setUpBeatmap);
}
private void setUpBeatmap(bool withKiai)
{
var controlPointInfo = new ControlPointInfo();
controlPointInfo.Add(0, new TimingControlPoint { BeatLength = 500 });
if (withKiai)
controlPointInfo.Add(0, new EffectControlPoint { KiaiMode = true });
Beatmap.Value = CreateWorkingBeatmap(new Beatmap
{
ControlPointInfo = controlPointInfo
});
Beatmap.Value.Track.Start();
}
}
}

View File

@ -3,7 +3,6 @@
using System; using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
@ -18,20 +17,16 @@ using osuTK.Graphics;
namespace osu.Game.Rulesets.Taiko.Skinning.Argon namespace osu.Game.Rulesets.Taiko.Skinning.Argon
{ {
public partial class ArgonJudgementPiece : CompositeDrawable, IAnimatableJudgement public partial class ArgonJudgementPiece : JudgementPiece, IAnimatableJudgement
{ {
protected readonly HitResult Result;
protected SpriteText JudgementText { get; private set; } = null!;
private RingExplosion? ringExplosion; private RingExplosion? ringExplosion;
[Resolved] [Resolved]
private OsuColour colours { get; set; } = null!; private OsuColour colours { get; set; } = null!;
public ArgonJudgementPiece(HitResult result) public ArgonJudgementPiece(HitResult result)
: base(result)
{ {
Result = result;
RelativePositionAxes = Axes.Both; RelativePositionAxes = Axes.Both;
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
} }
@ -39,21 +34,6 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
InternalChildren = new Drawable[]
{
JudgementText = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = Result.GetDescription().ToUpperInvariant(),
Colour = colours.ForHitResult(Result),
Blending = BlendingParameters.Additive,
Spacing = new Vector2(10, 0),
RelativePositionAxes = Axes.Both,
Font = OsuFont.Default.With(size: 20, weight: FontWeight.Regular),
},
};
if (Result.IsHit()) if (Result.IsHit())
{ {
AddInternal(ringExplosion = new RingExplosion(Result) AddInternal(ringExplosion = new RingExplosion(Result)
@ -64,6 +44,17 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon
} }
} }
protected override SpriteText CreateJudgementText() =>
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Blending = BlendingParameters.Additive,
Spacing = new Vector2(10, 0),
RelativePositionAxes = Axes.Both,
Font = OsuFont.Default.With(size: 20, weight: FontWeight.Regular),
};
/// <summary> /// <summary>
/// Plays the default animation for this judgement piece. /// Plays the default animation for this judgement piece.
/// </summary> /// </summary>

View File

@ -19,6 +19,10 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon
switch (component) switch (component)
{ {
case GameplaySkinComponentLookup<HitResult> resultComponent: case GameplaySkinComponentLookup<HitResult> resultComponent:
// This should eventually be moved to a skin setting, when supported.
if (Skin is ArgonProSkin && resultComponent.Component >= HitResult.Great)
return Drawable.Empty();
return new ArgonJudgementPiece(resultComponent.Component); return new ArgonJudgementPiece(resultComponent.Component);
case TaikoSkinComponentLookup taikoComponent: case TaikoSkinComponentLookup taikoComponent:

View File

@ -0,0 +1,65 @@
// 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.
using System;
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics.Containers;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using osuTK;
namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
{
internal partial class LegacyKiaiGlow : BeatSyncedContainer
{
private bool isKiaiActive;
private Sprite sprite = null!;
[BackgroundDependencyLoader(true)]
private void load(ISkinSource skin, HealthProcessor? healthProcessor)
{
Child = sprite = new Sprite
{
Texture = skin.GetTexture("taiko-glow"),
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Alpha = 0,
Scale = new Vector2(0.7f),
Colour = new Colour4(255, 228, 0, 255),
};
if (healthProcessor != null)
healthProcessor.NewJudgement += onNewJudgement;
}
protected override void Update()
{
base.Update();
if (isKiaiActive)
sprite.Alpha = (float)Math.Min(1, sprite.Alpha + Math.Abs(Clock.ElapsedFrameTime) / 100f);
else
sprite.Alpha = (float)Math.Max(0, sprite.Alpha - Math.Abs(Clock.ElapsedFrameTime) / 600f);
}
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
{
isKiaiActive = effectPoint.KiaiMode;
}
private void onNewJudgement(JudgementResult result)
{
if (!result.IsHit || !isKiaiActive)
return;
sprite.ScaleTo(0.85f).Then()
.ScaleTo(0.7f, 80, Easing.OutQuad);
}
}
}

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio.Track; using osu.Framework.Audio.Track;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -16,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
{ {
private Sprite kiai = null!; private Sprite kiai = null!;
private bool kiaiDisplayed; private bool isKiaiActive;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(ISkinSource skin) private void load(ISkinSource skin)
@ -41,17 +42,19 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
}; };
} }
protected override void Update()
{
base.Update();
if (isKiaiActive)
kiai.Alpha = (float)Math.Min(1, kiai.Alpha + Math.Abs(Clock.ElapsedFrameTime) / 200f);
else
kiai.Alpha = (float)Math.Max(0, kiai.Alpha - Math.Abs(Clock.ElapsedFrameTime) / 200f);
}
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
{ {
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); isKiaiActive = effectPoint.KiaiMode;
if (effectPoint.KiaiMode != kiaiDisplayed)
{
kiaiDisplayed = effectPoint.KiaiMode;
kiai.ClearTransforms();
kiai.FadeTo(kiaiDisplayed ? 1 : 0, 200);
}
} }
} }
} }

View File

@ -129,6 +129,12 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
case TaikoSkinComponents.Mascot: case TaikoSkinComponents.Mascot:
return new DrawableTaikoMascot(); return new DrawableTaikoMascot();
case TaikoSkinComponents.KiaiGlow:
if (GetTexture("taiko-glow") != null)
return new LegacyKiaiGlow();
return null;
default: default:
throw new UnsupportedSkinComponentException(lookup); throw new UnsupportedSkinComponentException(lookup);
} }

View File

@ -21,5 +21,6 @@ namespace osu.Game.Rulesets.Taiko
TaikoExplosionKiai, TaikoExplosionKiai,
Scroller, Scroller,
Mascot, Mascot,
KiaiGlow
} }
} }

View File

@ -112,6 +112,10 @@ namespace osu.Game.Rulesets.Taiko.UI
FillMode = FillMode.Fit, FillMode = FillMode.Fit,
Children = new[] Children = new[]
{ {
new SkinnableDrawable(new TaikoSkinComponentLookup(TaikoSkinComponents.KiaiGlow), _ => Empty())
{
RelativeSizeAxes = Axes.Both,
},
hitExplosionContainer = new Container<HitExplosion> hitExplosionContainer = new Container<HitExplosion>
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
@ -186,7 +190,7 @@ namespace osu.Game.Rulesets.Taiko.UI
var hitWindows = new TaikoHitWindows(); var hitWindows = new TaikoHitWindows();
foreach (var result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Where(r => hitWindows.IsHitResultAllowed(r))) foreach (var result in Enum.GetValues<HitResult>().Where(r => hitWindows.IsHitResultAllowed(r)))
{ {
judgementPools.Add(result, new DrawablePool<DrawableTaikoJudgement>(15)); judgementPools.Add(result, new DrawablePool<DrawableTaikoJudgement>(15));
explosionPools.Add(result, new HitExplosionPool(result)); explosionPools.Add(result, new HitExplosionPool(result));

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<TargetFramework>netstandard2.1</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<OutputType>Library</OutputType> <OutputType>Library</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Description>bash the drum. to the beat.</Description> <Description>bash the drum. to the beat.</Description>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Tests.Android" android:installLocation="auto"> <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Tests.Android" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="29" /> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!visual Test" /> <application android:allowBackup="true" android:supportsRtl="true" android:label="osu!visual Test" />
</manifest> </manifest>

View File

@ -3,7 +3,9 @@
#nullable disable #nullable disable
using System.Reflection;
using Android.App; using Android.App;
using Android.OS;
using osu.Framework.Android; using osu.Framework.Android;
namespace osu.Game.Tests.Android namespace osu.Game.Tests.Android
@ -12,5 +14,16 @@ namespace osu.Game.Tests.Android
public class MainActivity : AndroidGameActivity public class MainActivity : AndroidGameActivity
{ {
protected override Framework.Game CreateGame() => new OsuTestBrowser(); protected override Framework.Game CreateGame() => new OsuTestBrowser();
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// See the comment in OsuGameActivity
Assembly.Load("osu.Game.Rulesets.Osu");
Assembly.Load("osu.Game.Rulesets.Taiko");
Assembly.Load("osu.Game.Rulesets.Catch");
Assembly.Load("osu.Game.Rulesets.Mania");
}
} }
} }

View File

@ -1,88 +1,34 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\osu.Android.props" /> <Import Project="..\osu.Android.props" />
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> <TargetFramework>net6.0-android</TargetFramework>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> <OutputType>Exe</OutputType>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{5CC222DC-5716-4499-B897-DCBDDA4A5CF9}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<TemplateGuid>{122416d6-6b49-4ee2-a1e8-b825f31c79fe}</TemplateGuid>
<RootNamespace>osu.Game.Tests</RootNamespace> <RootNamespace>osu.Game.Tests</RootNamespace>
<AssemblyName>osu.Game.Tests.Android</AssemblyName> <AssemblyName>osu.Game.Tests.Android</AssemblyName>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<AndroidSupportedAbis>armeabi-v7a;x86;arm64-v8a</AndroidSupportedAbis>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Compile Include="MainActivity.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Properties\AndroidManifest.xml" />
</ItemGroup>
<PropertyGroup> <PropertyGroup>
<NoWarn>$(NoWarn);CA2007</NoWarn> <NoWarn>$(NoWarn);CA2007</NoWarn>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<AndroidLinkMode>None</AndroidLinkMode>
<MandroidI18n>cjk;mideast;other;rare;west</MandroidI18n>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\osu.Game.Tests\**\Beatmaps\**\*.cs"> <Compile Include="..\osu.Game.Tests\**\*.cs" Exclude="**\obj\**">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link> <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile> </Compile>
<Compile Remove="..\osu.Game.Tests\Beatmaps\Formats\OsuJsonDecoderTest.cs" /> <!-- TargetPath is relative to RootNamespace,
<Compile Include="..\osu.Game.Tests\**\Chat\**\*.cs"> and DllResourceStore is relative to AssemblyName. -->
<EmbeddedResource Include="..\osu.Game.Tests\**\Resources\**\*">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link> <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile> <TargetPath>Android\%(RecursiveDir)%(Filename)%(Extension)</TargetPath>
<Compile Include="..\osu.Game.Tests\**\NonVisual\**\*.cs"> </EmbeddedResource>
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile>
<Compile Include="..\osu.Game.Tests\**\Scores\**\*.cs">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile>
<Compile Include="..\osu.Game.Tests\**\ScrollAlgorithms\**\*.cs">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile>
<Compile Include="..\osu.Game.Tests\**\Visual\**\*.cs">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile>
<Compile Include="..\osu.Game.Tests\**\Resources\**\*.cs">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile>
<Compile Include="..\osu.Game.Tests\*.cs">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj"> <ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
<Project>{58f6c80c-1253-4a0e-a465-b8c85ebeadf3}</Project> <ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
<Name>osu.Game.Rulesets.Catch</Name> <ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />
</ProjectReference> <ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj"> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
<Project>{48f4582b-7687-4621-9cbe-5c24197cb536}</Project>
<Name>osu.Game.Rulesets.Mania</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj">
<Project>{c92a607b-1fdd-4954-9f92-03ff547d9080}</Project>
<Name>osu.Game.Rulesets.Osu</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj">
<Project>{f167e17a-7de6-4af5-b920-a5112296c695}</Project>
<Name>osu.Game.Rulesets.Taiko</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game\osu.Game.csproj">
<Project>{2a66dd92-adb1-4994-89e2-c94e04acda0d}</Project>
<Name>osu.Game</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="DeepEqual" Version="2.0.0" /> <PackageReference Include="DeepEqual" Version="2.0.0" />
<PackageReference Include="Moq" Version="4.17.2" /> <PackageReference Include="Moq" Version="4.17.2" />
<PackageReference Include="System.Formats.Asn1">
<Version>5.0.0</Version>
</PackageReference>
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
</Project> </Project>

View File

@ -3,7 +3,6 @@
#nullable disable #nullable disable
using osu.Framework.iOS;
using UIKit; using UIKit;
namespace osu.Game.Tests.iOS namespace osu.Game.Tests.iOS
@ -12,7 +11,7 @@ namespace osu.Game.Tests.iOS
{ {
public static void Main(string[] args) public static void Main(string[] args)
{ {
UIApplication.Main(args, typeof(GameUIApplication), typeof(AppDelegate)); UIApplication.Main(args, null, typeof(AppDelegate));
} }
} }
} }

View File

@ -13,7 +13,7 @@
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>MinimumOSVersion</key> <key>MinimumOSVersion</key>
<string>10.0</string> <string>13.4</string>
<key>UIDeviceFamily</key> <key>UIDeviceFamily</key>
<array> <array>
<integer>1</integer> <integer>1</integer>

View File

@ -1,54 +1,26 @@
<?xml version="1.0" encoding="utf-8"?> <Project Sdk="Microsoft.NET.Sdk">
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup> <PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<ProjectGuid>{65FF8E19-6934-469B-B690-23C6D6E56A17}</ProjectGuid> <TargetFramework>net6.0-ios</TargetFramework>
<SupportedOSPlatformVersion>13.4</SupportedOSPlatformVersion>
<RootNamespace>osu.Game.Tests</RootNamespace> <RootNamespace>osu.Game.Tests</RootNamespace>
<AssemblyName>osu.Game.Tests.iOS</AssemblyName> <AssemblyName>osu.Game.Tests.iOS</AssemblyName>
</PropertyGroup> </PropertyGroup>
<Import Project="..\osu.iOS.props" /> <Import Project="..\osu.iOS.props" />
<ItemGroup> <ItemGroup>
<None Include="Info.plist" />
<None Include="Entitlements.plist" />
<LinkDescription Include="..\osu.iOS\Linker.xml">
<Link>Linker.xml</Link>
</LinkDescription>
<Compile Include="Application.cs" />
<Compile Include="AppDelegate.cs" />
<Compile Include="..\osu.Game.Tests\**\*.cs" Exclude="**\obj\**"> <Compile Include="..\osu.Game.Tests\**\*.cs" Exclude="**\obj\**">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link> <Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<PropertyGroup>
<NoWarn>$(NoWarn);CA2007</NoWarn>
</PropertyGroup>
<ItemGroup Label="Project References"> <ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game\osu.Game.csproj"> <ProjectReference Include="..\osu.Game\osu.Game.csproj" />
<Project>{2A66DD92-ADB1-4994-89E2-C94E04ACDA0D}</Project> <ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />
<Name>osu.Game</Name> <ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
</ProjectReference> <ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj"> <ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
<Project>{C92A607B-1FDD-4954-9F92-03FF547D9080}</Project>
<Name>osu.Game.Rulesets.Osu</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj">
<Project>{58F6C80C-1253-4A0E-A465-B8C85EBEADF3}</Project>
<Name>osu.Game.Rulesets.Catch</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj">
<Project>{48F4582B-7687-4621-9CBE-5C24197CB536}</Project>
<Name>osu.Game.Rulesets.Mania</Name>
</ProjectReference>
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj">
<Project>{F167E17A-7DE6-4AF5-B920-A5112296C695}</Project>
<Name>osu.Game.Rulesets.Taiko</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="DeepEqual" Version="2.0.0" /> <PackageReference Include="DeepEqual" Version="2.0.0" />
<PackageReference Include="Moq" Version="4.17.2" /> <PackageReference Include="Moq" Version="4.17.2" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
</Project> </Project>

View File

@ -314,6 +314,24 @@ namespace osu.Game.Tests.Beatmaps.Formats
} }
} }
[Test]
public void TestGetLastObjectTime()
{
var decoder = new LegacyBeatmapDecoder();
using (var resStream = TestResources.OpenResource("mania-last-object-not-latest.osu"))
using (var stream = new LineBufferedReader(resStream))
{
var beatmap = decoder.Decode(stream);
Assert.That(beatmap.HitObjects.Last().StartTime, Is.EqualTo(2494));
Assert.That(beatmap.HitObjects.Last().GetEndTime(), Is.EqualTo(2494));
Assert.That(beatmap.HitObjects.Max(h => h.GetEndTime()), Is.EqualTo(2582));
Assert.That(beatmap.GetLastObjectTime(), Is.EqualTo(2582));
}
}
[Test] [Test]
public void TestDecodeBeatmapComboOffsetsOsu() public void TestDecodeBeatmapComboOffsetsOsu()
{ {

View File

@ -16,7 +16,9 @@ using osu.Game.Rulesets;
using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Mania.Mods; using osu.Game.Rulesets.Mania.Mods;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays;
@ -179,6 +181,40 @@ namespace osu.Game.Tests.Beatmaps.Formats
}); });
} }
[Test]
public void TestSoloScoreData()
{
var ruleset = new OsuRuleset().RulesetInfo;
var scoreInfo = TestResources.CreateTestScoreInfo(ruleset);
scoreInfo.Mods = new Mod[]
{
new OsuModDoubleTime { SpeedChange = { Value = 1.1 } }
};
var beatmap = new TestBeatmap(ruleset);
var score = new Score
{
ScoreInfo = scoreInfo,
Replay = new Replay
{
Frames = new List<ReplayFrame>
{
new OsuReplayFrame(2000, OsuPlayfield.BASE_SIZE / 2, OsuAction.LeftButton)
}
}
};
var decodedAfterEncode = encodeThenDecode(LegacyBeatmapDecoder.LATEST_VERSION, score, beatmap);
Assert.Multiple(() =>
{
Assert.That(decodedAfterEncode.ScoreInfo.Statistics, Is.EqualTo(scoreInfo.Statistics));
Assert.That(decodedAfterEncode.ScoreInfo.MaximumStatistics, Is.EqualTo(scoreInfo.MaximumStatistics));
Assert.That(decodedAfterEncode.ScoreInfo.Mods, Is.EqualTo(scoreInfo.Mods));
});
}
private static Score encodeThenDecode(int beatmapVersion, Score score, TestBeatmap beatmap) private static Score encodeThenDecode(int beatmapVersion, Score score, TestBeatmap beatmap)
{ {
var encodeStream = new MemoryStream(); var encodeStream = new MemoryStream();

View File

@ -26,6 +26,16 @@ namespace osu.Game.Tests.Chat
MessageFormatter.WebsiteRootUrl = originalWebsiteRootUrl; MessageFormatter.WebsiteRootUrl = originalWebsiteRootUrl;
} }
[Test]
public void TestUnsupportedProtocolLink()
{
Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a gopher://really-old-protocol we don't support." });
Assert.AreEqual(result.Content, result.DisplayContent);
Assert.AreEqual(1, result.Links.Count);
Assert.AreEqual("gopher://really-old-protocol", result.Links[0].Url);
}
[Test] [Test]
public void TestBareLink() public void TestBareLink()
{ {

View File

@ -564,7 +564,7 @@ namespace osu.Game.Tests.Database
var imported = await importer.Import( var imported = await importer.Import(
progressNotification, progressNotification,
new ImportTask(zipStream, string.Empty) new[] { new ImportTask(zipStream, string.Empty) }
); );
realm.Run(r => r.Refresh()); realm.Run(r => r.Refresh());
@ -1052,7 +1052,7 @@ namespace osu.Game.Tests.Database
{ {
string? temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack); string? temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack);
var importedSet = await importer.Import(new ImportTask(temp), batchImport); var importedSet = await importer.Import(new ImportTask(temp), new ImportParameters { Batch = batchImport });
Assert.NotNull(importedSet); Assert.NotNull(importedSet);
Debug.Assert(importedSet != null); Debug.Assert(importedSet != null);

View File

@ -8,6 +8,7 @@ using osu.Framework.Graphics;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
@ -137,6 +138,31 @@ namespace osu.Game.Tests.Gameplay
AddAssert("DHO state is correct", () => dho.State.Value == ArmedState.Miss); AddAssert("DHO state is correct", () => dho.State.Value == ArmedState.Miss);
} }
[Test]
public void TestResultSetBeforeLoadComplete()
{
TestDrawableHitObject dho = null;
HitObjectLifetimeEntry lifetimeEntry = null;
AddStep("Create lifetime entry", () =>
{
var hitObject = new HitObject { StartTime = Time.Current };
lifetimeEntry = new HitObjectLifetimeEntry(hitObject)
{
Result = new JudgementResult(hitObject, hitObject.CreateJudgement())
{
Type = HitResult.Great
}
};
});
AddStep("Create DHO and apply entry", () =>
{
dho = new TestDrawableHitObject();
dho.Apply(lifetimeEntry);
Child = dho;
});
AddAssert("DHO state is correct", () => dho.State.Value, () => Is.EqualTo(ArmedState.Hit));
}
private partial class TestDrawableHitObject : DrawableHitObject private partial class TestDrawableHitObject : DrawableHitObject
{ {
public const double INITIAL_LIFETIME_OFFSET = 100; public const double INITIAL_LIFETIME_OFFSET = 100;

View File

@ -60,6 +60,6 @@ namespace osu.Game.Tests.Mods
/// This local helper is used rather than <see cref="Ruleset.CreateAllMods"/>, because the aforementioned method flattens multi mods. /// This local helper is used rather than <see cref="Ruleset.CreateAllMods"/>, because the aforementioned method flattens multi mods.
/// </remarks>> /// </remarks>>
private static IEnumerable<MultiMod> getMultiMods(Ruleset ruleset) private static IEnumerable<MultiMod> getMultiMods(Ruleset ruleset)
=> Enum.GetValues(typeof(ModType)).Cast<ModType>().SelectMany(ruleset.GetModsFor).OfType<MultiMod>(); => Enum.GetValues<ModType>().SelectMany(ruleset.GetModsFor).OfType<MultiMod>();
} }
} }

View File

@ -226,12 +226,12 @@ namespace osu.Game.Tests.Online
this.testBeatmapManager = testBeatmapManager; this.testBeatmapManager = testBeatmapManager;
} }
public override Live<BeatmapSetInfo> ImportModel(BeatmapSetInfo item, ArchiveReader archive = null, bool batchImport = false, CancellationToken cancellationToken = default) public override Live<BeatmapSetInfo> ImportModel(BeatmapSetInfo item, ArchiveReader archive = null, ImportParameters parameters = default, CancellationToken cancellationToken = default)
{ {
if (!testBeatmapManager.AllowImport.Wait(TimeSpan.FromSeconds(10), cancellationToken)) if (!testBeatmapManager.AllowImport.Wait(TimeSpan.FromSeconds(10), cancellationToken))
throw new TimeoutException("Timeout waiting for import to be allowed."); throw new TimeoutException("Timeout waiting for import to be allowed.");
return (testBeatmapManager.CurrentImport = base.ImportModel(item, archive, batchImport, cancellationToken)); return (testBeatmapManager.CurrentImport = base.ImportModel(item, archive, parameters, cancellationToken));
} }
} }
} }

View File

@ -0,0 +1,39 @@
osu file format v14
[General]
SampleSet: Normal
StackLeniency: 0.7
Mode: 3
[Difficulty]
HPDrainRate:3
CircleSize:5
OverallDifficulty:8
ApproachRate:8
SliderMultiplier:3.59999990463257
SliderTickRate:2
[TimingPoints]
24,352.941176470588,4,1,1,100,1,0
6376,-50,4,1,1,100,0,0
[HitObjects]
51,192,24,1,0,0:0:0:0:
153,192,200,1,0,0:0:0:0:
358,192,376,1,0,0:0:0:0:
460,192,553,1,0,0:0:0:0:
460,192,729,128,0,1435:0:0:0:0:
358,192,906,128,0,1612:0:0:0:0:
256,192,1082,128,0,1788:0:0:0:0:
153,192,1259,128,0,1965:0:0:0:0:
51,192,1435,128,0,2141:0:0:0:0:
51,192,2318,1,12,0:0:0:0:
153,192,2318,1,4,0:0:0:0:
256,192,2318,1,6,0:0:0:0:
358,192,2318,1,14,0:0:0:0:
460,192,2318,1,0,0:0:0:0:
51,192,2494,128,0,2582:0:0:0:0:
153,192,2494,128,14,2582:0:0:0:0:
256,192,2494,128,6,2582:0:0:0:0:
358,192,2494,128,4,2582:0:0:0:0:
460,192,2494,1,12,0:0:0:0:0:

View File

@ -7,6 +7,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Utils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Difficulty;
@ -355,6 +356,28 @@ namespace osu.Game.Tests.Rulesets.Scoring
} }
#pragma warning restore CS0618 #pragma warning restore CS0618
[Test]
public void TestAccuracyWhenNearPerfect()
{
const int count_judgements = 1000;
const int count_misses = 1;
double actual = new TestScoreProcessor().ComputeAccuracy(new ScoreInfo
{
Statistics = new Dictionary<HitResult, int>
{
{ HitResult.Great, count_judgements - count_misses },
{ HitResult.Miss, count_misses }
}
});
const double expected = (count_judgements - count_misses) / (double)count_judgements;
Assert.That(actual, Is.Not.EqualTo(0.0));
Assert.That(actual, Is.Not.EqualTo(1.0));
Assert.That(actual, Is.EqualTo(expected).Within(Precision.FLOAT_EPSILON));
}
private class TestRuleset : Ruleset private class TestRuleset : Ruleset
{ {
public override IEnumerable<Mod> GetModsFor(ModType type) => throw new NotImplementedException(); public override IEnumerable<Mod> GetModsFor(ModType type) => throw new NotImplementedException();

View File

@ -360,7 +360,7 @@ namespace osu.Game.Tests.Skins.IO
private async Task<Live<SkinInfo>> loadSkinIntoOsu(OsuGameBase osu, ImportTask import, bool batchImport = false) private async Task<Live<SkinInfo>> loadSkinIntoOsu(OsuGameBase osu, ImportTask import, bool batchImport = false)
{ {
var skinManager = osu.Dependencies.Get<SkinManager>(); var skinManager = osu.Dependencies.Get<SkinManager>();
return await skinManager.Import(import, batchImport); return await skinManager.Import(import, new ImportParameters { Batch = batchImport });
} }
} }
} }

View File

@ -11,7 +11,7 @@ namespace osu.Game.Tests.Utils
public class NamingUtilsTest public class NamingUtilsTest
{ {
[Test] [Test]
public void TestEmptySet() public void TestNextBestNameEmptySet()
{ {
string nextBestName = NamingUtils.GetNextBestName(Enumerable.Empty<string>(), "New Difficulty"); string nextBestName = NamingUtils.GetNextBestName(Enumerable.Empty<string>(), "New Difficulty");
@ -19,7 +19,7 @@ namespace osu.Game.Tests.Utils
} }
[Test] [Test]
public void TestNotTaken() public void TestNextBestNameNotTaken()
{ {
string[] existingNames = string[] existingNames =
{ {
@ -34,7 +34,7 @@ namespace osu.Game.Tests.Utils
} }
[Test] [Test]
public void TestNotTakenButClose() public void TestNextBestNameNotTakenButClose()
{ {
string[] existingNames = string[] existingNames =
{ {
@ -49,7 +49,7 @@ namespace osu.Game.Tests.Utils
} }
[Test] [Test]
public void TestAlreadyTaken() public void TestNextBestNameAlreadyTaken()
{ {
string[] existingNames = string[] existingNames =
{ {
@ -62,7 +62,7 @@ namespace osu.Game.Tests.Utils
} }
[Test] [Test]
public void TestAlreadyTakenWithDifferentCase() public void TestNextBestNameAlreadyTakenWithDifferentCase()
{ {
string[] existingNames = string[] existingNames =
{ {
@ -75,7 +75,7 @@ namespace osu.Game.Tests.Utils
} }
[Test] [Test]
public void TestAlreadyTakenWithBrackets() public void TestNextBestNameAlreadyTakenWithBrackets()
{ {
string[] existingNames = string[] existingNames =
{ {
@ -88,7 +88,7 @@ namespace osu.Game.Tests.Utils
} }
[Test] [Test]
public void TestMultipleAlreadyTaken() public void TestNextBestNameMultipleAlreadyTaken()
{ {
string[] existingNames = string[] existingNames =
{ {
@ -104,7 +104,7 @@ namespace osu.Game.Tests.Utils
} }
[Test] [Test]
public void TestEvenMoreAlreadyTaken() public void TestNextBestNameEvenMoreAlreadyTaken()
{ {
string[] existingNames = Enumerable.Range(1, 30).Select(i => $"New Difficulty ({i})").Append("New Difficulty").ToArray(); string[] existingNames = Enumerable.Range(1, 30).Select(i => $"New Difficulty ({i})").Append("New Difficulty").ToArray();
@ -114,7 +114,7 @@ namespace osu.Game.Tests.Utils
} }
[Test] [Test]
public void TestMultipleAlreadyTakenWithGaps() public void TestNextBestNameMultipleAlreadyTakenWithGaps()
{ {
string[] existingNames = string[] existingNames =
{ {
@ -128,5 +128,153 @@ namespace osu.Game.Tests.Utils
Assert.AreEqual("New Difficulty (2)", nextBestName); Assert.AreEqual("New Difficulty (2)", nextBestName);
} }
[Test]
public void TestNextBestFilenameEmptySet()
{
string nextBestFilename = NamingUtils.GetNextBestFilename(Enumerable.Empty<string>(), "test_file.osr");
Assert.AreEqual("test_file.osr", nextBestFilename);
}
[Test]
public void TestNextBestFilenameNotTaken()
{
string[] existingFiles =
{
"this file exists.zip",
"that file exists.too",
"three.4",
};
string nextBestFilename = NamingUtils.GetNextBestFilename(existingFiles, "test_file.osr");
Assert.AreEqual("test_file.osr", nextBestFilename);
}
[Test]
public void TestNextBestFilenameNotTakenButClose()
{
string[] existingFiles =
{
"replay_file(1).osr",
"replay_file (not a number).zip",
"replay_file (1 <- now THAT is a number right here).lol",
};
string nextBestFilename = NamingUtils.GetNextBestFilename(existingFiles, "replay_file.osr");
Assert.AreEqual("replay_file.osr", nextBestFilename);
}
[Test]
public void TestNextBestFilenameAlreadyTaken()
{
string[] existingFiles =
{
"replay_file.osr",
};
string nextBestFilename = NamingUtils.GetNextBestFilename(existingFiles, "replay_file.osr");
Assert.AreEqual("replay_file (1).osr", nextBestFilename);
}
[Test]
public void TestNextBestFilenameAlreadyTakenDifferentCase()
{
string[] existingFiles =
{
"replay_file.osr",
"RePlAy_FiLe (1).OsR",
"REPLAY_FILE (2).OSR",
};
string nextBestFilename = NamingUtils.GetNextBestFilename(existingFiles, "replay_file.osr");
Assert.AreEqual("replay_file (3).osr", nextBestFilename);
}
[Test]
public void TestNextBestFilenameAlreadyTakenWithBrackets()
{
string[] existingFiles =
{
"replay_file.osr",
"replay_file (copy).osr",
};
string nextBestFilename = NamingUtils.GetNextBestFilename(existingFiles, "replay_file.osr");
Assert.AreEqual("replay_file (1).osr", nextBestFilename);
nextBestFilename = NamingUtils.GetNextBestFilename(existingFiles, "replay_file (copy).osr");
Assert.AreEqual("replay_file (copy) (1).osr", nextBestFilename);
}
[Test]
public void TestNextBestFilenameMultipleAlreadyTaken()
{
string[] existingFiles =
{
"replay_file.osr",
"replay_file (1).osr",
"replay_file (2).osr",
"replay_file (3).osr",
};
string nextBestFilename = NamingUtils.GetNextBestFilename(existingFiles, "replay_file.osr");
Assert.AreEqual("replay_file (4).osr", nextBestFilename);
}
[Test]
public void TestNextBestFilenameMultipleAlreadyTakenWithGaps()
{
string[] existingFiles =
{
"replay_file.osr",
"replay_file (1).osr",
"replay_file (2).osr",
"replay_file (4).osr",
"replay_file (5).osr",
};
string nextBestFilename = NamingUtils.GetNextBestFilename(existingFiles, "replay_file.osr");
Assert.AreEqual("replay_file (3).osr", nextBestFilename);
}
[Test]
public void TestNextBestFilenameNoExtensions()
{
string[] existingFiles =
{
"those",
"are definitely",
"files",
};
string nextBestFilename = NamingUtils.GetNextBestFilename(existingFiles, "surely");
Assert.AreEqual("surely", nextBestFilename);
nextBestFilename = NamingUtils.GetNextBestFilename(existingFiles, "those");
Assert.AreEqual("those (1)", nextBestFilename);
}
[Test]
public void TestNextBestFilenameDifferentExtensions()
{
string[] existingFiles =
{
"replay_file.osr",
"replay_file (1).osr",
"replay_file.txt",
};
string nextBestFilename = NamingUtils.GetNextBestFilename(existingFiles, "replay_file.osr");
Assert.AreEqual("replay_file (2).osr", nextBestFilename);
nextBestFilename = NamingUtils.GetNextBestFilename(existingFiles, "replay_file.txt");
Assert.AreEqual("replay_file (1).txt", nextBestFilename);
}
} }
} }

View File

@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.Editing
{ {
public partial class TestSceneZoomableScrollContainer : OsuManualInputManagerTestScene public partial class TestSceneZoomableScrollContainer : OsuManualInputManagerTestScene
{ {
private ZoomableScrollContainer scrollContainer; private TestZoomableScrollContainer scrollContainer;
private Drawable innerBox; private Drawable innerBox;
[SetUpSteps] [SetUpSteps]
@ -47,7 +47,7 @@ namespace osu.Game.Tests.Visual.Editing
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(30) Colour = OsuColour.Gray(30)
}, },
scrollContainer = new ZoomableScrollContainer(1, 60, 1) scrollContainer = new TestZoomableScrollContainer(1, 60, 1)
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
@ -93,6 +93,14 @@ namespace osu.Game.Tests.Visual.Editing
AddAssert("Inner container width matches scroll container", () => innerBox.DrawWidth == scrollContainer.DrawWidth); AddAssert("Inner container width matches scroll container", () => innerBox.DrawWidth == scrollContainer.DrawWidth);
} }
[Test]
public void TestWidthUpdatesOnSecondZoomSetup()
{
AddAssert("Inner container width = 1x", () => innerBox.DrawWidth == scrollContainer.DrawWidth);
AddStep("reload zoom", () => scrollContainer.SetupZoom(10, 10, 60));
AddAssert("Inner container width = 10x", () => innerBox.DrawWidth == scrollContainer.DrawWidth * 10);
}
[Test] [Test]
public void TestZoom0() public void TestZoom0()
{ {
@ -190,5 +198,15 @@ namespace osu.Game.Tests.Visual.Editing
private Quad scrollQuad => scrollContainer.ScreenSpaceDrawQuad; private Quad scrollQuad => scrollContainer.ScreenSpaceDrawQuad;
private Quad boxQuad => innerBox.ScreenSpaceDrawQuad; private Quad boxQuad => innerBox.ScreenSpaceDrawQuad;
private partial class TestZoomableScrollContainer : ZoomableScrollContainer
{
public TestZoomableScrollContainer(int minimum, float maximum, float initial)
: base(minimum, maximum, initial)
{
}
public new void SetupZoom(float initial, float minimum, float maximum) => base.SetupZoom(initial, minimum, maximum);
}
} }
} }

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Diagnostics;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
@ -187,18 +188,22 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test] [Test]
public void TestInputDoesntWorkWhenHUDHidden() public void TestInputDoesntWorkWhenHUDHidden()
{ {
SongProgressBar getSongProgress() => hudOverlay.ChildrenOfType<SongProgressBar>().Single(); SongProgressBar? getSongProgress() => hudOverlay.ChildrenOfType<SongProgressBar>().SingleOrDefault();
bool seeked = false; bool seeked = false;
createNew(); createNew();
AddUntilStep("wait for song progress", () => getSongProgress() != null);
AddStep("bind seek", () => AddStep("bind seek", () =>
{ {
seeked = false; seeked = false;
var progress = getSongProgress(); var progress = getSongProgress();
Debug.Assert(progress != null);
progress.ShowHandle = true; progress.ShowHandle = true;
progress.OnSeek += _ => seeked = true; progress.OnSeek += _ => seeked = true;
}); });

View File

@ -257,7 +257,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
prepareTestAPI(true); prepareTestAPI(true);
createPlayerTest(false, createRuleset: () => new OsuRuleset createPlayerTest(createRuleset: () => new OsuRuleset
{ {
RulesetInfo = RulesetInfo =
{ {

View File

@ -107,7 +107,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddUntilStep("wait for load", () => downloadButton.IsLoaded); AddUntilStep("wait for load", () => downloadButton.IsLoaded);
AddAssert("state is available", () => downloadButton.State.Value == DownloadState.NotDownloaded); checkState(DownloadState.NotDownloaded);
AddStep("click button", () => AddStep("click button", () =>
{ {
@ -133,7 +133,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddUntilStep("wait for load", () => downloadButton.IsLoaded); AddUntilStep("wait for load", () => downloadButton.IsLoaded);
AddAssert("state is not downloaded", () => downloadButton.State.Value == DownloadState.NotDownloaded); checkState(DownloadState.NotDownloaded);
AddAssert("button is not enabled", () => !downloadButton.ChildrenOfType<DownloadButton>().First().Enabled.Value); AddAssert("button is not enabled", () => !downloadButton.ChildrenOfType<DownloadButton>().First().Enabled.Value);
} }
@ -155,7 +155,7 @@ namespace osu.Game.Tests.Visual.Gameplay
AddUntilStep("wait for load", () => downloadButton.IsLoaded); AddUntilStep("wait for load", () => downloadButton.IsLoaded);
AddUntilStep("state is not downloaded", () => downloadButton.State.Value == DownloadState.NotDownloaded); checkState(DownloadState.NotDownloaded);
AddAssert("button is not enabled", () => !downloadButton.ChildrenOfType<DownloadButton>().First().Enabled.Value); AddAssert("button is not enabled", () => !downloadButton.ChildrenOfType<DownloadButton>().First().Enabled.Value);
} }
@ -174,17 +174,16 @@ namespace osu.Game.Tests.Visual.Gameplay
}); });
AddUntilStep("wait for load", () => downloadButton.IsLoaded); AddUntilStep("wait for load", () => downloadButton.IsLoaded);
checkState(DownloadState.NotDownloaded);
AddUntilStep("state is not downloaded", () => downloadButton.State.Value == DownloadState.NotDownloaded);
AddStep("import score", () => imported = scoreManager.Import(getScoreInfo(true))); AddStep("import score", () => imported = scoreManager.Import(getScoreInfo(true)));
AddUntilStep("state is available", () => downloadButton.State.Value == DownloadState.LocallyAvailable); checkState(DownloadState.LocallyAvailable);
AddAssert("button is enabled", () => downloadButton.ChildrenOfType<DownloadButton>().First().Enabled.Value); AddAssert("button is enabled", () => downloadButton.ChildrenOfType<DownloadButton>().First().Enabled.Value);
AddStep("delete score", () => scoreManager.Delete(imported.Value)); AddStep("delete score", () => scoreManager.Delete(imported.Value));
AddUntilStep("state is not downloaded", () => downloadButton.State.Value == DownloadState.NotDownloaded); checkState(DownloadState.NotDownloaded);
AddAssert("button is not enabled", () => !downloadButton.ChildrenOfType<DownloadButton>().First().Enabled.Value); AddAssert("button is not enabled", () => !downloadButton.ChildrenOfType<DownloadButton>().First().Enabled.Value);
} }
@ -202,10 +201,13 @@ namespace osu.Game.Tests.Visual.Gameplay
AddUntilStep("wait for load", () => downloadButton.IsLoaded); AddUntilStep("wait for load", () => downloadButton.IsLoaded);
AddAssert("state is unknown", () => downloadButton.State.Value == DownloadState.Unknown); checkState(DownloadState.Unknown);
AddAssert("button is not enabled", () => !downloadButton.ChildrenOfType<DownloadButton>().First().Enabled.Value); AddAssert("button is not enabled", () => !downloadButton.ChildrenOfType<DownloadButton>().First().Enabled.Value);
} }
private void checkState(DownloadState expectedState) =>
AddUntilStep($"state is {expectedState}", () => downloadButton.State.Value, () => Is.EqualTo(expectedState));
private ScoreInfo getScoreInfo(bool replayAvailable, bool hasOnlineId = true) => new ScoreInfo private ScoreInfo getScoreInfo(bool replayAvailable, bool hasOnlineId = true) => new ScoreInfo
{ {
OnlineID = hasOnlineId ? online_score_id : 0, OnlineID = hasOnlineId ? online_score_id : 0,

View File

@ -261,7 +261,7 @@ namespace osu.Game.Tests.Visual.Gameplay
[Test] [Test]
public void TestFinalFramesPurgedBeforeEndingPlay() public void TestFinalFramesPurgedBeforeEndingPlay()
{ {
AddStep("begin playing", () => spectatorClient.BeginPlaying(TestGameplayState.Create(new OsuRuleset()), new Score())); AddStep("begin playing", () => spectatorClient.BeginPlaying(0, TestGameplayState.Create(new OsuRuleset()), new Score()));
AddStep("send frames and finish play", () => AddStep("send frames and finish play", () =>
{ {

View File

@ -147,7 +147,7 @@ namespace osu.Game.Tests.Visual.Gameplay
} }
}; };
spectatorClient.BeginPlaying(TestGameplayState.Create(new OsuRuleset()), recordingScore); spectatorClient.BeginPlaying(0, TestGameplayState.Create(new OsuRuleset()), recordingScore);
spectatorClient.OnNewFrames += onNewFrames; spectatorClient.OnNewFrames += onNewFrames;
}); });
} }

View File

@ -117,11 +117,9 @@ namespace osu.Game.Tests.Visual.Multiplayer
BeatmapID = 0, BeatmapID = 0,
RulesetID = 0, RulesetID = 0,
Mods = user.Mods, Mods = user.Mods,
MaximumScoringValues = new ScoringValues MaximumStatistics = new Dictionary<HitResult, int>
{ {
BaseScore = 10000, { HitResult.Perfect, 100 }
MaxCombo = 1000,
CountBasicHitObjects = 1000
} }
}; };
} }

Some files were not shown because too many files have changed in this diff Show More