1
0
mirror of https://github.com/ppy/osu.git synced 2025-03-14 05:47:20 +08:00

Merge branch 'master' into cognition

This commit is contained in:
Givikap120 2024-02-13 19:35:49 +02:00
commit 83d391e54b
353 changed files with 4332 additions and 1627 deletions

View File

@ -3,13 +3,13 @@
"isRoot": true,
"tools": {
"jetbrains.resharper.globaltools": {
"version": "2022.2.3",
"version": "2023.3.3",
"commands": [
"jb"
]
},
"nvika": {
"version": "2.2.0",
"version": "3.0.0",
"commands": [
"nvika"
]

View File

@ -15,17 +15,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v3
# FIXME: Tools won't run in .NET 6.0 unless you install 3.1.x LTS side by side.
# https://itnext.io/how-to-support-multiple-net-sdks-in-github-actions-workflows-b988daa884e
- name: Install .NET 3.1.x LTS
- name: Install .NET 8.0.x
uses: actions/setup-dotnet@v3
with:
dotnet-version: "3.1.x"
- name: Install .NET 6.0.x
uses: actions/setup-dotnet@v3
with:
dotnet-version: "6.0.x"
dotnet-version: "8.0.x"
- name: Restore Tools
run: dotnet tool restore
@ -79,10 +72,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v3
- name: Install .NET 6.0.x
- name: Install .NET 8.0.x
uses: actions/setup-dotnet@v3
with:
dotnet-version: "6.0.x"
dotnet-version: "8.0.x"
- name: Compile
run: dotnet build -c Debug -warnaserror osu.Desktop.slnf
@ -114,10 +107,10 @@ jobs:
distribution: microsoft
java-version: 11
- name: Install .NET 6.0.x
- name: Install .NET 8.0.x
uses: actions/setup-dotnet@v3
with:
dotnet-version: "6.0.x"
dotnet-version: "8.0.x"
- name: Install .NET workloads
run: dotnet workload install maui-android
@ -135,13 +128,16 @@ jobs:
- name: Checkout
uses: actions/checkout@v3
- name: Install .NET 6.0.x
- name: Install .NET 8.0.x
uses: actions/setup-dotnet@v3
with:
dotnet-version: "6.0.x"
dotnet-version: "8.0.x"
- name: Install .NET Workloads
run: dotnet workload install maui-ios
- name: Select Xcode 15.2
run: sudo xcode-select -s /Applications/Xcode_15.2.app/Contents/Developer
- name: Build
run: dotnet build -c Debug osu.iOS

View File

@ -12,10 +12,10 @@ jobs:
name: Update osu-web mod definitions
runs-on: ubuntu-latest
steps:
- name: Install .NET 6.0.x
- name: Install .NET 8.0.x
uses: actions/setup-dotnet@v3
with:
dotnet-version: "6.0.x"
dotnet-version: "8.0.x"
- name: Checkout ppy/osu
uses: actions/checkout@v3

View File

@ -1,5 +1,3 @@
is_global = true
# .NET Code Style
# IDE styles reference: https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/
@ -56,4 +54,4 @@ dotnet_diagnostic.RS0030.severity = error
# Temporarily disable analysing CanBeNull = true in NRT contexts due to mobile issues.
# See: https://github.com/ppy/osu/pull/19677
dotnet_diagnostic.OSUF001.severity = none
dotnet_diagnostic.OSUF001.severity = none

View File

@ -68,6 +68,7 @@ Aside from the above, below is a brief checklist of things to watch out when you
- Please do not make code changes via the GitHub web interface.
- Please add tests for your changes. We expect most new features and bugfixes to have test coverage, unless the effort of adding them is prohibitive. The visual testing methodology we use is described in more detail [here](https://github.com/ppy/osu-framework/wiki/Development-and-Testing).
- Please run tests and code style analysis (via `InspectCode.{ps1,sh}` scripts in the root of this repository) before opening the PR. This is particularly important if you're a first-time contributor, as CI will not run for your PR until we allow it to do so.
- **Do not run the game in release configuration at any point during your testing** (the sole exception to this being benchmarks). Using release is an unnecessary and harmful practice, and can even lead to you losing your local realm database if you start making changes to the schema. The debug configuration has a completely separated full-stack environment, including a development website instance at https://dev.ppy.sh/. It is permitted to register an account on that development instance for testing purposes and not worry about multi-accounting infractions.
After you're done with your changes and you wish to open the PR, please observe the following recommendations:

View File

@ -1,7 +1,7 @@
<!-- Contains required properties for osu!framework projects. -->
<Project>
<PropertyGroup Label="C#">
<LangVersion>10.0</LangVersion>
<LangVersion>12.0</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Nullable>enable</Nullable>
</PropertyGroup>

View File

@ -30,8 +30,8 @@ If you are just looking to give the game a whirl, you can grab the latest releas
### Latest release:
| [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) |
| ------------- | ------------- | ------------- | ------------- | ------------- |
| [Windows 10+ (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | macOS 12+ ([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) |
|--------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ------------- | ------------- | ------------- |
You can also generally download a version for your current device from the [osu! site](https://osu.ppy.sh/home/download).

View File

@ -0,0 +1,10 @@
<!-- Contains required properties for osu!framework projects. -->
<Project>
<PropertyGroup>
<ApplicationManifest>$(MSBuildThisFileDirectory)app.manifest</ApplicationManifest>
</PropertyGroup>
<PropertyGroup Label="Documentation">
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);CS1591</NoWarn>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<assemblyIdentity version="1.0.0.0" name="MyNewProject" />
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
<applicationRequestMinimum>
<defaultAssemblyRequest permissionSetReference="Custom" />
<PermissionSet class="System.Security.PermissionSet" version="1" Unrestricted="true" ID="Custom" SameSite="site" />
</applicationRequestMinimum>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
</asmv1:assembly>

View File

@ -18,7 +18,7 @@
</ItemGroup>
<PropertyGroup Label="Project">
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>osu.Game.Rulesets.EmptyFreeform.Tests</RootNamespace>
</PropertyGroup>
</Project>

View File

@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.EmptyFreeform.Objects.Drawables
{
if (timeOffset >= 0)
// todo: implement judgement logic
ApplyResult(r => r.Type = HitResult.Perfect);
ApplyResult(HitResult.Perfect);
}
protected override void UpdateHitStateTransforms(ArmedState state)

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Project">
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<AssemblyTitle>osu.Game.Rulesets.EmptyFreeform</AssemblyTitle>
<OutputType>Library</OutputType>
<PlatformTarget>AnyCPU</PlatformTarget>

View File

@ -0,0 +1,10 @@
<!-- Contains required properties for osu!framework projects. -->
<Project>
<PropertyGroup>
<ApplicationManifest>$(MSBuildThisFileDirectory)app.manifest</ApplicationManifest>
</PropertyGroup>
<PropertyGroup Label="Documentation">
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);CS1591</NoWarn>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<assemblyIdentity version="1.0.0.0" name="MyNewProject" />
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
<applicationRequestMinimum>
<defaultAssemblyRequest permissionSetReference="Custom" />
<PermissionSet class="System.Security.PermissionSet" version="1" Unrestricted="true" ID="Custom" SameSite="site" />
</applicationRequestMinimum>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
</asmv1:assembly>

View File

@ -18,7 +18,7 @@
</ItemGroup>
<PropertyGroup Label="Project">
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>osu.Game.Rulesets.Pippidon.Tests</RootNamespace>
</PropertyGroup>
</Project>

View File

@ -9,7 +9,6 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Audio;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osuTK;
using osuTK.Graphics;
@ -49,7 +48,12 @@ namespace osu.Game.Rulesets.Pippidon.Objects.Drawables
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (timeOffset >= 0)
ApplyResult(r => r.Type = IsHovered ? HitResult.Perfect : HitResult.Miss);
{
if (IsHovered)
ApplyMaxResult();
else
ApplyMinResult();
}
}
protected override double InitialLifetimeOffset => time_preempt;

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Project">
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<AssemblyTitle>osu.Game.Rulesets.Pippidon</AssemblyTitle>
<OutputType>Library</OutputType>
<PlatformTarget>AnyCPU</PlatformTarget>

View File

@ -0,0 +1,10 @@
<!-- Contains required properties for osu!framework projects. -->
<Project>
<PropertyGroup>
<ApplicationManifest>$(MSBuildThisFileDirectory)app.manifest</ApplicationManifest>
</PropertyGroup>
<PropertyGroup Label="Documentation">
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);CS1591</NoWarn>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<assemblyIdentity version="1.0.0.0" name="MyNewProject" />
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
<applicationRequestMinimum>
<defaultAssemblyRequest permissionSetReference="Custom" />
<PermissionSet class="System.Security.PermissionSet" version="1" Unrestricted="true" ID="Custom" SameSite="site" />
</applicationRequestMinimum>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
</asmv1:assembly>

View File

@ -18,7 +18,7 @@
</ItemGroup>
<PropertyGroup Label="Project">
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>osu.Game.Rulesets.EmptyScrolling.Tests</RootNamespace>
</PropertyGroup>
</Project>

View File

@ -3,7 +3,6 @@
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osuTK;
using osuTK.Graphics;
@ -24,7 +23,7 @@ namespace osu.Game.Rulesets.EmptyScrolling.Objects.Drawables
{
if (timeOffset >= 0)
// todo: implement judgement logic
ApplyResult(r => r.Type = HitResult.Perfect);
ApplyMaxResult();
}
protected override void UpdateHitStateTransforms(ArmedState state)

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Project">
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<AssemblyTitle>osu.Game.Rulesets.EmptyScrolling</AssemblyTitle>
<OutputType>Library</OutputType>
<PlatformTarget>AnyCPU</PlatformTarget>

View File

@ -0,0 +1,10 @@
<!-- Contains required properties for osu!framework projects. -->
<Project>
<PropertyGroup>
<ApplicationManifest>$(MSBuildThisFileDirectory)app.manifest</ApplicationManifest>
</PropertyGroup>
<PropertyGroup Label="Documentation">
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);CS1591</NoWarn>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<assemblyIdentity version="1.0.0.0" name="MyNewProject" />
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
<applicationRequestMinimum>
<defaultAssemblyRequest permissionSetReference="Custom" />
<PermissionSet class="System.Security.PermissionSet" version="1" Unrestricted="true" ID="Custom" SameSite="site" />
</applicationRequestMinimum>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
</asmv1:assembly>

View File

@ -18,7 +18,7 @@
</ItemGroup>
<PropertyGroup Label="Project">
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<RootNamespace>osu.Game.Rulesets.Pippidon.Tests</RootNamespace>
</PropertyGroup>
</Project>

View File

@ -10,7 +10,6 @@ using osu.Framework.Graphics.Textures;
using osu.Game.Audio;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Pippidon.UI;
using osu.Game.Rulesets.Scoring;
using osuTK;
using osuTK.Graphics;
@ -49,7 +48,12 @@ namespace osu.Game.Rulesets.Pippidon.Objects.Drawables
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (timeOffset >= 0)
ApplyResult(r => r.Type = currentLane.Value == HitObject.Lane ? HitResult.Perfect : HitResult.Miss);
{
if (currentLane.Value == HitObject.Lane)
ApplyMaxResult();
else
ApplyMinResult();
}
}
protected override void UpdateHitStateTransforms(ArmedState state)

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Project">
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<AssemblyTitle>osu.Game.Rulesets.Pippidon</AssemblyTitle>
<OutputType>Library</OutputType>
<PlatformTarget>AnyCPU</PlatformTarget>

View File

@ -1,7 +1,7 @@
{
"sdk": {
"version": "6.0.100",
"rollForward": "latestFeature"
"version": "8.0.100",
"rollForward": "latestFeature",
"allowPrerelease": false
}
}
}

View File

@ -10,7 +10,7 @@
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.127.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.205.0" />
</ItemGroup>
<PropertyGroup>
<!-- Fody does not handle Android build well, and warns when unchanged.

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="sh.ppy.osulazer" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" />
<application android:allowBackup="true"
android:supportsRtl="true"
android:label="osu!"

View File

@ -72,9 +72,9 @@ namespace osu.Android
Debug.Assert(Resources?.DisplayMetrics != null);
Point displaySize = new Point();
#pragma warning disable 618 // GetSize is deprecated
#pragma warning disable CA1422 // GetSize is deprecated
WindowManager.DefaultDisplay.GetSize(displaySize);
#pragma warning restore 618
#pragma warning restore CA1422
float smallestWidthDp = Math.Min(displaySize.X, displaySize.Y) / Resources.DisplayMetrics.Density;
bool isTablet = smallestWidthDp >= 600f;

View File

@ -1,13 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\osu.Android.props" />
<PropertyGroup>
<TargetFramework>net6.0-android</TargetFramework>
<TargetFramework>net8.0-android</TargetFramework>
<OutputType>Exe</OutputType>
<RootNamespace>osu.Android</RootNamespace>
<AssemblyName>osu.Android</AssemblyName>
<UseMauiEssentials>true</UseMauiEssentials>
<!-- This currently causes random lockups during gameplay. https://github.com/mono/mono/issues/18973 -->
<EnableLLVM>false</EnableLLVM>
<Version>0.0.0</Version>
<ApplicationVersion Condition=" '$(ApplicationVersion)' == '' ">1</ApplicationVersion>
<ApplicationDisplayVersion Condition=" '$(ApplicationDisplayVersion)' == '' ">$(Version)</ApplicationDisplayVersion>
@ -19,4 +16,7 @@
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Maui.Essentials" Version="8.0.3" />
</ItemGroup>
</Project>

View File

@ -138,7 +138,7 @@ namespace osu.Desktop
return false;
// Make sure that this is a laptop.
var gpus = new IntPtr[64];
IntPtr[] gpus = new IntPtr[64];
if (checkError(EnumPhysicalGPUs(gpus, out int gpuCount)))
return false;
@ -456,7 +456,7 @@ namespace osu.Desktop
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NVAPI.UNICODE_STRING_MAX)]
public string ProfileName;
[MarshalAs(UnmanagedType.ByValArray)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public uint[] GPUSupport;
public uint IsPredefined;

View File

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup Label="Project">
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<OutputType>WinExe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Description>A free-to-win rhythm game. Rhythm is just a *click* away!</Description>

View File

@ -20,5 +20,6 @@
<file src="**.dll" target="lib\net45\"/>
<file src="**.config" target="lib\net45\"/>
<file src="**.json" target="lib\net45\"/>
<file src="icon.png" target=""/>
</files>
</package>

View File

@ -0,0 +1,31 @@
// 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.Collections.Generic;
using BenchmarkDotNet.Attributes;
using osu.Framework.Utils;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Benchmarks
{
public class BenchmarkUnstableRate : BenchmarkTest
{
private List<HitEvent> events = null!;
public override void SetUp()
{
base.SetUp();
events = new List<HitEvent>();
for (int i = 0; i < 1000; i++)
events.Add(new HitEvent(RNG.NextDouble(-200.0, 200.0), RNG.NextDouble(1.0, 2.0), HitResult.Great, new HitObject(), null, null));
}
[Benchmark]
public void CalculateUnstableRate()
{
_ = events.CalculateUnstableRate();
}
}
}

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
<OutputType>Exe</OutputType>
<IsPackable>false</IsPackable>
</PropertyGroup>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" />
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!catch Test" />
</manifest>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\osu.Android.props" />
<PropertyGroup>
<TargetFramework>net6.0-android</TargetFramework>
<TargetFramework>net8.0-android</TargetFramework>
<OutputType>Exe</OutputType>
<RootNamespace>osu.Game.Rulesets.Catch.Tests</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Catch.Tests.Android</AssemblyName>
@ -21,4 +21,4 @@
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
</ItemGroup>
</Project>
</Project>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0-ios</TargetFramework>
<TargetFramework>net8.0-ios</TargetFramework>
<SupportedOSPlatformVersion>13.4</SupportedOSPlatformVersion>
<RootNamespace>osu.Game.Rulesets.Catch.Tests</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Catch.Tests.iOS</AssemblyName>

View File

@ -7,7 +7,7 @@
</ItemGroup>
<PropertyGroup Label="Project">
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />

View File

@ -63,7 +63,12 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
if (CheckPosition == null) return;
if (timeOffset >= 0 && Result != null)
ApplyResult(r => r.Type = CheckPosition.Invoke(HitObject) ? r.Judgement.MaxResult : r.Judgement.MinResult);
{
if (CheckPosition.Invoke(HitObject))
ApplyMaxResult();
else
ApplyMinResult();
}
}
protected override void UpdateHitStateTransforms(ArmedState state)

View File

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

View File

@ -1,5 +1,5 @@
<?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">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" />
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!mania Test" />
</manifest>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\osu.Android.props" />
<PropertyGroup>
<TargetFramework>net6.0-android</TargetFramework>
<TargetFramework>net8.0-android</TargetFramework>
<OutputType>Exe</OutputType>
<RootNamespace>osu.Game.Rulesets.Mania.Tests</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Mania.Tests.Android</AssemblyName>
@ -21,4 +21,4 @@
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
</ItemGroup>
</Project>
</Project>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0-ios</TargetFramework>
<TargetFramework>net8.0-ios</TargetFramework>
<SupportedOSPlatformVersion>13.4</SupportedOSPlatformVersion>
<RootNamespace>osu.Game.Rulesets.Mania.Tests</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Mania.Tests.iOS</AssemblyName>

View File

@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Mania.Tests
{
foreach (var stage in stages)
{
for (int i = 0; i < stage.Columns.Count; i++)
for (int i = 0; i < stage.Columns.Length; i++)
{
var obj = new Note { Column = i, StartTime = Time.Current + 2000 };
obj.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.Mania.Tests
{
foreach (var stage in stages)
{
for (int i = 0; i < stage.Columns.Count; i++)
for (int i = 0; i < stage.Columns.Length; i++)
{
var obj = new HoldNote { Column = i, StartTime = Time.Current + 2000, Duration = 500 };
obj.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());

View File

@ -7,7 +7,7 @@
</ItemGroup>
<PropertyGroup Label="Project">
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />

View File

@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
private readonly bool isForCurrentRuleset;
private readonly double originalOverallDifficulty;
public override int Version => 20220902;
public override int Version => 20230817;
public ManiaDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
: base(ruleset, beatmap)

View File

@ -375,7 +375,7 @@ namespace osu.Game.Rulesets.Mania
/// <returns>The <see cref="PlayfieldType"/> that corresponds to <paramref name="variant"/>.</returns>
private PlayfieldType getPlayfieldType(int variant)
{
return (PlayfieldType)Enum.GetValues(typeof(PlayfieldType)).Cast<int>().OrderByDescending(i => i).First(v => variant >= v);
return (PlayfieldType)Enum.GetValues(typeof(PlayfieldType)).Cast<int>().OrderDescending().First(v => variant >= v);
}
protected override IEnumerable<HitResult> GetValidHitResults()

View File

@ -15,6 +15,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public abstract int KeyCount { get; }
public override ModType Type => ModType.Conversion;
public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier
public override bool Ranked => UsesDefaultConfiguration;
public void ApplyToBeatmapConverter(IBeatmapConverter beatmapConverter)
{

View File

@ -8,5 +8,6 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModHardRock : ModHardRock
{
public override double ScoreMultiplier => 1;
public override bool Ranked => false;
}
}

View File

@ -11,5 +11,6 @@ namespace osu.Game.Rulesets.Mania.Mods
public override string Name => "One Key";
public override string Acronym => "1K";
public override LocalisableString Description => @"Play with one key.";
public override bool Ranked => false;
}
}

View File

@ -11,5 +11,6 @@ namespace osu.Game.Rulesets.Mania.Mods
public override string Name => "Ten Keys";
public override string Acronym => "10K";
public override LocalisableString Description => @"Play with ten keys.";
public override bool Ranked => false;
}
}

View File

@ -11,5 +11,6 @@ namespace osu.Game.Rulesets.Mania.Mods
public override string Name => "Two Keys";
public override string Acronym => "2K";
public override LocalisableString Description => @"Play with two keys.";
public override bool Ranked => false;
}
}

View File

@ -11,5 +11,6 @@ namespace osu.Game.Rulesets.Mania.Mods
public override string Name => "Three Keys";
public override string Acronym => "3K";
public override LocalisableString Description => @"Play with three keys.";
public override bool Ranked => false;
}
}

View File

@ -14,6 +14,7 @@ namespace osu.Game.Rulesets.Mania.Mods
public class ManiaModMirror : ModMirror, IApplicableToBeatmap
{
public override LocalisableString Description => "Notes are flipped horizontally.";
public override bool Ranked => UsesDefaultConfiguration;
public void ApplyToBeatmap(IBeatmap beatmap)
{

View File

@ -265,7 +265,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (Tail.AllJudged)
{
if (Tail.IsHit)
ApplyResult(r => r.Type = r.Judgement.MaxResult);
ApplyMaxResult();
else
MissForcefully();
}

View File

@ -25,7 +25,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
if (AllJudged) return;
ApplyResult(r => r.Type = hit ? r.Judgement.MaxResult : r.Judgement.MinResult);
if (hit)
ApplyMaxResult();
else
ApplyMinResult();
}
}
}

View File

@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
/// <summary>
/// Causes this <see cref="DrawableManiaHitObject"/> to get missed, disregarding all conditions in implementations of <see cref="DrawableHitObject.CheckForResult"/>.
/// </summary>
public virtual void MissForcefully() => ApplyResult(r => r.Type = r.Judgement.MinResult);
public virtual void MissForcefully() => ApplyMinResult();
}
public abstract partial class DrawableManiaHitObject<TObject> : DrawableManiaHitObject

View File

@ -89,18 +89,18 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
ApplyResult(r => r.Type = r.Judgement.MinResult);
ApplyMinResult();
return;
}
var result = HitObject.HitWindows.ResultFor(timeOffset);
if (result == HitResult.None)
return;
result = GetCappedResult(result);
ApplyResult(r => r.Type = result);
ApplyResult(result);
}
/// <summary>

View File

@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Mania.Scoring
}
protected override IEnumerable<HitObject> EnumerateHitObjects(IBeatmap beatmap)
=> base.EnumerateHitObjects(beatmap).OrderBy(ho => ho, JudgementOrderComparer.DEFAULT);
=> base.EnumerateHitObjects(beatmap).Order(JudgementOrderComparer.DEFAULT);
protected override double ComputeTotalScore(double comboProgress, double accuracyProgress, double bonusPortion)
{

View File

@ -17,7 +17,7 @@ using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.Skinning.Argon
{
public partial class ArgonJudgementPiece : JudgementPiece, IAnimatableJudgement
public partial class ArgonJudgementPiece : TextJudgementPiece, IAnimatableJudgement
{
private const float judgement_y_position = 160;

View File

@ -4,8 +4,6 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
@ -28,20 +26,21 @@ namespace osu.Game.Rulesets.Mania.UI
/// <summary>
/// All contents added to this <see cref="ColumnFlow{TContent}"/>.
/// </summary>
public IReadOnlyList<TContent> Content => columns.Children.Select(c => c.Count == 0 ? null : (TContent)c.Child).ToList();
public TContent[] Content { get; }
private readonly FillFlowContainer<Container> columns;
private readonly FillFlowContainer<Container<TContent>> columns;
private readonly StageDefinition stageDefinition;
public ColumnFlow(StageDefinition stageDefinition)
{
this.stageDefinition = stageDefinition;
Content = new TContent[stageDefinition.Columns];
AutoSizeAxes = Axes.X;
Masking = true;
InternalChild = columns = new FillFlowContainer<Container>
InternalChild = columns = new FillFlowContainer<Container<TContent>>
{
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X,
@ -49,7 +48,7 @@ namespace osu.Game.Rulesets.Mania.UI
};
for (int i = 0; i < stageDefinition.Columns; i++)
columns.Add(new Container { RelativeSizeAxes = Axes.Y });
columns.Add(new Container<TContent> { RelativeSizeAxes = Axes.Y });
}
private ISkinSource currentSkin;
@ -102,7 +101,10 @@ namespace osu.Game.Rulesets.Mania.UI
/// </summary>
/// <param name="column">The index of the column to set the content of.</param>
/// <param name="content">The content.</param>
public void SetContentForColumn(int column, TContent content) => columns[column].Child = content;
public void SetContentForColumn(int column, TContent content)
{
Content[column] = columns[column].Child = content;
}
private void updateMobileSizing()
{

View File

@ -1,16 +1,16 @@
// 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.
#nullable disable
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics;
using osu.Framework.Input;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Input.Handlers;
@ -59,10 +59,9 @@ namespace osu.Game.Rulesets.Mania.UI
// Stores the current speed adjustment active in gameplay.
private readonly Track speedAdjustmentTrack = new TrackVirtual(0);
[Resolved]
private ISkinSource skin { get; set; }
private ISkinSource currentSkin = null!;
public DrawableManiaRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
public DrawableManiaRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod>? mods = null)
: base(ruleset, beatmap, mods)
{
BarLines = new BarLineGenerator<BarLine>(Beatmap).BarLines;
@ -72,8 +71,12 @@ namespace osu.Game.Rulesets.Mania.UI
}
[BackgroundDependencyLoader]
private void load()
private void load(ISkinSource source)
{
currentSkin = source;
currentSkin.SourceChanged += onSkinChange;
skinChanged();
foreach (var mod in Mods.OfType<IApplicableToTrack>())
mod.ApplyToTrack(speedAdjustmentTrack);
@ -109,12 +112,28 @@ namespace osu.Game.Rulesets.Mania.UI
updateTimeRange();
}
private ScheduledDelegate? pendingSkinChange;
private float hitPosition;
private void onSkinChange()
{
// schedule required to avoid calls after disposed.
// note that this has the side-effect of components only performing a skin change when they are alive.
pendingSkinChange?.Cancel();
pendingSkinChange = Scheduler.Add(skinChanged);
}
private void skinChanged()
{
hitPosition = currentSkin.GetConfig<ManiaSkinConfigurationLookup, float>(
new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.HitPosition))?.Value
?? Stage.HIT_TARGET_POSITION;
pendingSkinChange = null;
}
private void updateTimeRange()
{
float hitPosition = skin.GetConfig<ManiaSkinConfigurationLookup, float>(
new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.HitPosition))?.Value
?? Stage.HIT_TARGET_POSITION;
const float length_to_default_hit_position = 768 - LegacyManiaSkinConfiguration.DEFAULT_HIT_POSITION;
float lengthToHitPosition = 768 - hitPosition;
@ -139,10 +158,18 @@ namespace osu.Game.Rulesets.Mania.UI
protected override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant);
public override DrawableHitObject<ManiaHitObject> CreateDrawableRepresentation(ManiaHitObject h) => null;
public override DrawableHitObject<ManiaHitObject>? CreateDrawableRepresentation(ManiaHitObject h) => null;
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);
protected override ReplayRecorder CreateReplayRecorder(Score score) => new ManiaReplayRecorder(score);
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (currentSkin.IsNotNull())
currentSkin.SourceChanged -= onSkinChange;
}
}
}

View File

@ -42,7 +42,16 @@ namespace osu.Game.Rulesets.Mania.UI
}
}
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => stages.Any(s => s.ReceivePositionalInputAt(screenSpacePos));
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos)
{
foreach (var s in stages)
{
if (s.ReceivePositionalInputAt(screenSpacePos))
return true;
}
return false;
}
public ManiaPlayfield(List<StageDefinition> stageDefinitions)
{
@ -71,7 +80,7 @@ namespace osu.Game.Rulesets.Mania.UI
stages.Add(newStage);
AddNested(newStage);
firstColumnIndex += newStage.Columns.Count;
firstColumnIndex += newStage.Columns.Length;
}
}
@ -125,9 +134,9 @@ namespace osu.Game.Rulesets.Mania.UI
foreach (var stage in stages)
{
if (index >= stage.Columns.Count)
if (index >= stage.Columns.Length)
{
index -= stage.Columns.Count;
index -= stage.Columns.Length;
continue;
}
@ -140,7 +149,7 @@ namespace osu.Game.Rulesets.Mania.UI
/// <summary>
/// Retrieves the total amount of columns across all stages in this playfield.
/// </summary>
public int TotalColumns => stages.Sum(s => s.Columns.Count);
public int TotalColumns => stages.Sum(s => s.Columns.Length);
private Stage getStageByColumn(int column)
{
@ -148,7 +157,7 @@ namespace osu.Game.Rulesets.Mania.UI
foreach (var stage in stages)
{
sum += stage.Columns.Count;
sum += stage.Columns.Length;
if (sum > column)
return stage;
}

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.ObjectExtensions;
@ -37,7 +36,7 @@ namespace osu.Game.Rulesets.Mania.UI
public const float HIT_TARGET_POSITION = 110;
public IReadOnlyList<Column> Columns => columnFlow.Content;
public Column[] Columns => columnFlow.Content;
private readonly ColumnFlow<Column> columnFlow;
private readonly JudgementContainer<DrawableManiaJudgement> judgements;
@ -45,7 +44,16 @@ namespace osu.Game.Rulesets.Mania.UI
private readonly Drawable barLineContainer;
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Columns.Any(c => c.ReceivePositionalInputAt(screenSpacePos));
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos)
{
foreach (var c in Columns)
{
if (c.ReceivePositionalInputAt(screenSpacePos))
return true;
}
return false;
}
private readonly int firstColumnIndex;
@ -184,13 +192,13 @@ namespace osu.Game.Rulesets.Mania.UI
NewResult += OnNewResult;
}
public override void Add(HitObject hitObject) => Columns.ElementAt(((ManiaHitObject)hitObject).Column - firstColumnIndex).Add(hitObject);
public override void Add(HitObject hitObject) => Columns[((ManiaHitObject)hitObject).Column - firstColumnIndex].Add(hitObject);
public override bool Remove(HitObject hitObject) => Columns.ElementAt(((ManiaHitObject)hitObject).Column - firstColumnIndex).Remove(hitObject);
public override bool Remove(HitObject hitObject) => Columns[((ManiaHitObject)hitObject).Column - firstColumnIndex].Remove(hitObject);
public override void Add(DrawableHitObject h) => Columns.ElementAt(((ManiaHitObject)h.HitObject).Column - firstColumnIndex).Add(h);
public override void Add(DrawableHitObject h) => Columns[((ManiaHitObject)h.HitObject).Column - firstColumnIndex].Add(h);
public override bool Remove(DrawableHitObject h) => Columns.ElementAt(((ManiaHitObject)h.HitObject).Column - firstColumnIndex).Remove(h);
public override bool Remove(DrawableHitObject h) => Columns[((ManiaHitObject)h.HitObject).Column - firstColumnIndex].Remove(h);
public void Add(BarLine barLine) => base.Add(barLine);

View File

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

View File

@ -1,5 +1,5 @@
<?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">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" />
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!standard Test" />
</manifest>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\osu.Android.props" />
<PropertyGroup>
<TargetFramework>net6.0-android</TargetFramework>
<TargetFramework>net8.0-android</TargetFramework>
<OutputType>Exe</OutputType>
<RootNamespace>osu.Game.Rulesets.Osu.Tests</RootNamespace>
<AssemblyName>osu.Game.Rulesets.Osu.Tests.Android</AssemblyName>
@ -24,4 +24,4 @@
<ItemGroup>
<PackageReference Include="Moq" Version="4.17.2" />
</ItemGroup>
</Project>
</Project>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0-ios</TargetFramework>
<TargetFramework>net8.0-ios</TargetFramework>
<SupportedOSPlatformVersion>13.4</SupportedOSPlatformVersion>
<OutputType>Exe</OutputType>
<RootNamespace>osu.Game.Rulesets.Osu.Tests</RootNamespace>

View File

@ -124,6 +124,113 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
AddAssert("selection preserved", () => EditorBeatmap.SelectedHitObjects.Count == 2);
}
[Test]
public void TestControlClickAddsControlPointsIfSingleSliderSelected()
{
var firstSlider = new Slider
{
StartTime = 0,
Position = new Vector2(0, 0),
Path = new SliderPath
{
ControlPoints =
{
new PathControlPoint(),
new PathControlPoint(new Vector2(100))
}
}
};
var secondSlider = new Slider
{
StartTime = 1000,
Position = new Vector2(200, 200),
Path = new SliderPath
{
ControlPoints =
{
new PathControlPoint(),
new PathControlPoint(new Vector2(100, -100))
}
}
};
AddStep("add objects", () => EditorBeatmap.AddRange(new HitObject[] { firstSlider, secondSlider }));
AddStep("select first slider", () => EditorBeatmap.SelectedHitObjects.AddRange(new HitObject[] { secondSlider }));
AddStep("move mouse to middle of slider", () =>
{
var pos = blueprintContainer.SelectionBlueprints
.First(s => s.Item == secondSlider)
.ChildrenOfType<SliderBodyPiece>().First()
.ScreenSpaceDrawQuad.Centre;
InputManager.MoveMouseTo(pos);
});
AddStep("control-click left mouse", () =>
{
InputManager.PressKey(Key.ControlLeft);
InputManager.Click(MouseButton.Left);
InputManager.ReleaseKey(Key.ControlLeft);
});
AddAssert("selection preserved", () => EditorBeatmap.SelectedHitObjects.Count, () => Is.EqualTo(1));
AddAssert("slider has 3 anchors", () => secondSlider.Path.ControlPoints.Count, () => Is.EqualTo(3));
}
[Test]
public void TestControlClickDoesNotAddSliderControlPointsIfMultipleObjectsSelected()
{
var firstSlider = new Slider
{
StartTime = 0,
Position = new Vector2(0, 0),
Path = new SliderPath
{
ControlPoints =
{
new PathControlPoint(),
new PathControlPoint(new Vector2(100))
}
}
};
var secondSlider = new Slider
{
StartTime = 1000,
Position = new Vector2(200, 200),
Path = new SliderPath
{
ControlPoints =
{
new PathControlPoint(),
new PathControlPoint(new Vector2(100, -100))
}
}
};
AddStep("add objects", () => EditorBeatmap.AddRange(new HitObject[] { firstSlider, secondSlider }));
AddStep("select first slider", () => EditorBeatmap.SelectedHitObjects.AddRange(new HitObject[] { firstSlider, secondSlider }));
AddStep("move mouse to middle of slider", () =>
{
var pos = blueprintContainer.SelectionBlueprints
.First(s => s.Item == secondSlider)
.ChildrenOfType<SliderBodyPiece>().First()
.ScreenSpaceDrawQuad.Centre;
InputManager.MoveMouseTo(pos);
});
AddStep("control-click left mouse", () =>
{
InputManager.PressKey(Key.ControlLeft);
InputManager.Click(MouseButton.Left);
InputManager.ReleaseKey(Key.ControlLeft);
});
AddAssert("selection not preserved", () => EditorBeatmap.SelectedHitObjects.Count, () => Is.EqualTo(1));
AddAssert("second slider not selected",
() => blueprintContainer.SelectionBlueprints.First(s => s.Item == secondSlider).IsSelected,
() => Is.False);
AddAssert("slider still has 2 anchors", () => secondSlider.Path.ControlPoints.Count, () => Is.EqualTo(2));
}
private ComposeBlueprintContainer blueprintContainer
=> Editor.ChildrenOfType<ComposeBlueprintContainer>().First();

View File

@ -206,7 +206,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
{
AddStep($"click context menu item \"{contextMenuText}\"", () =>
{
MenuItem item = visualiser.ContextMenuItems.FirstOrDefault(menuItem => menuItem.Text.Value == "Curve type")?.Items.FirstOrDefault(menuItem => menuItem.Text.Value == contextMenuText);
MenuItem item = visualiser.ContextMenuItems!.FirstOrDefault(menuItem => menuItem.Text.Value == "Curve type")?.Items.FirstOrDefault(menuItem => menuItem.Text.Value == contextMenuText);
item?.Action.Value?.Invoke();
});

View File

@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Tests
new PathControlPoint(new Vector2(-128, 0), PathType.LINEAR) // absolute position: (0, 128)
}
},
RepeatCount = 1
RepeatCount = 2
};
slider.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
return slider;
@ -45,7 +45,9 @@ namespace osu.Game.Rulesets.Osu.Tests
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.NestedHitObjects.OfType<SliderHeadCircle>().Single().Position, Is.EqualTo(new Vector2(OsuPlayfield.BASE_SIZE.X - 128, 128)));
Assert.That(slider.NestedHitObjects.OfType<SliderRepeat>().First().Position, Is.EqualTo(new Vector2(OsuPlayfield.BASE_SIZE.X - 0, 128)));
Assert.That(slider.NestedHitObjects.OfType<SliderTailCircle>().Single().Position, Is.EqualTo(new Vector2(OsuPlayfield.BASE_SIZE.X, 128)));
Assert.That(slider.Path.ControlPoints.Select(point => point.Position), Is.EquivalentTo(new[]
{
new Vector2(),
@ -62,7 +64,9 @@ namespace osu.Game.Rulesets.Osu.Tests
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.NestedHitObjects.OfType<SliderHeadCircle>().Single().Position, Is.EqualTo(new Vector2(128, OsuPlayfield.BASE_SIZE.Y - 128)));
Assert.That(slider.NestedHitObjects.OfType<SliderRepeat>().First().Position, Is.EqualTo(new Vector2(0, OsuPlayfield.BASE_SIZE.Y - 128)));
Assert.That(slider.NestedHitObjects.OfType<SliderTailCircle>().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(),
@ -79,7 +83,9 @@ namespace osu.Game.Rulesets.Osu.Tests
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.NestedHitObjects.OfType<SliderHeadCircle>().Single().Position, Is.EqualTo(new Vector2(128, 128)));
Assert.That(slider.NestedHitObjects.OfType<SliderRepeat>().First().Position, Is.EqualTo(new Vector2(256, 128)));
Assert.That(slider.NestedHitObjects.OfType<SliderTailCircle>().Single().Position, Is.EqualTo(new Vector2(256, 128)));
Assert.That(slider.Path.ControlPoints.Select(point => point.Position), Is.EquivalentTo(new[]
{
new Vector2(),

View File

@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(130)
Size = new Vector2(300)
}
};
});
@ -85,6 +85,30 @@ namespace osu.Game.Rulesets.Osu.Tests
AddStep("return user input", () => InputManager.UseParentInput = true);
}
[Test]
public void TestAllPoints()
{
AddStep("add points", () =>
{
float minX = object1.DrawPosition.X - object1.DrawSize.X / 2;
float maxX = object1.DrawPosition.X + object1.DrawSize.X / 2;
float minY = object1.DrawPosition.Y - object1.DrawSize.Y / 2;
float maxY = object1.DrawPosition.Y + object1.DrawSize.Y / 2;
for (int i = 0; i < 10; i++)
{
for (float x = minX; x <= maxX; x += 0.5f)
{
for (float y = minY; y <= maxY; y += 0.5f)
{
accuracyHeatmap.AddPoint(object2.Position, object1.Position, new Vector2(x, y), RNG.NextSingle(10, 500));
}
}
}
});
}
protected override bool OnMouseDown(MouseDownEvent e)
{
accuracyHeatmap.AddPoint(object2.Position, object1.Position, background.ToLocalSpace(e.ScreenSpaceMouseDownPosition), 50);

View File

@ -183,7 +183,7 @@ namespace osu.Game.Rulesets.Osu.Tests
break;
}
hitObjectContainer.Add(drawableObject);
hitObjectContainer.Add(drawableObject!);
followPointRenderer.AddFollowPoints(objects[i]);
}
});

View File

@ -136,7 +136,7 @@ namespace osu.Game.Rulesets.Osu.Tests
if (auto && !userTriggered && timeOffset > hitOffset && CheckHittable?.Invoke(this, Time.Current, HitResult.Great) == ClickAction.Hit)
{
// force success
ApplyResult(r => r.Type = HitResult.Great);
ApplyResult(HitResult.Great);
}
else
base.CheckForResult(userTriggered, timeOffset);

View File

@ -208,7 +208,7 @@ namespace osu.Game.Rulesets.Osu.Tests
if (shouldHit && !userTriggered && timeOffset >= 0)
{
// force success
ApplyResult(r => r.Type = HitResult.Great);
ApplyResult(HitResult.Great);
}
else
base.CheckForResult(userTriggered, timeOffset);

View File

@ -6,6 +6,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio;
@ -173,6 +174,7 @@ namespace osu.Game.Rulesets.Osu.Tests
public IEnumerable<ISkin> AllSources => new[] { this };
[CanBeNull]
public event Action SourceChanged;
private bool enabled = true;

View File

@ -8,7 +8,7 @@
</ItemGroup>
<PropertyGroup Label="Project">
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />

View File

@ -159,7 +159,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate)
{
return new Skill[]
var skills = new List<Skill>
{
new Aim(mods, true),
new Aim(mods, false),
@ -169,6 +169,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty
new ReadingHighAR(mods),
new ReadingHidden(mods),
};
if (mods.Any(h => h is OsuModFlashlight))
skills.Add(new Flashlight(mods));
return skills.ToArray();
}
protected override Mod[] DifficultyAdjustmentMods => new Mod[]

View File

@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
// These sections will not contribute to the difficulty.
var peaks = GetCurrentStrainPeaks().Where(p => p > 0);
List<double> strains = peaks.OrderByDescending(d => d).ToList();
List<double> strains = peaks.OrderDescending().ToList();
// We are reducing the highest strains first to account for extreme difficulty spikes
for (int i = 0; i < Math.Min(strains.Count, ReducedSectionCount); i++)
@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
// Difficulty is the weighted sum of the highest strains from every section.
// We're sorting from highest to lowest strain.
foreach (double strain in strains.OrderByDescending(d => d))
foreach (double strain in strains.OrderDescending())
{
difficulty += strain * weight;
weight *= DecayWeight;

View File

@ -171,7 +171,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
return false; // Allow right click to be handled by context menu
case MouseButton.Left:
if (e.ControlPressed && IsSelected)
// If there's more than two objects selected, ctrl+click should deselect
if (e.ControlPressed && IsSelected && selectedObjects.Count < 2)
{
changeHandler?.BeginChange();
placementControlPoint = addControlPoint(e.MousePosition);

View File

@ -31,6 +31,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
public override Type[] IncompatibleMods => new[] { typeof(OsuModFlashlight) };
public override bool Ranked => true;
private DrawableOsuBlinds blinds = null!;

View File

@ -75,8 +75,10 @@ namespace osu.Game.Rulesets.Osu.Mods
{
double time = playfield.Time.Current;
foreach (var drawable in playfield.HitObjectContainer.AliveObjects)
foreach (var entry in playfield.HitObjectContainer.AliveEntries)
{
var drawable = entry.Value;
switch (drawable)
{
case DrawableHitCircle circle:

View File

@ -49,8 +49,10 @@ namespace osu.Game.Rulesets.Osu.Mods
{
var cursorPos = playfield.Cursor.AsNonNull().ActiveCursor.DrawPosition;
foreach (var drawable in playfield.HitObjectContainer.AliveObjects)
foreach (var entry in playfield.HitObjectContainer.AliveEntries)
{
var drawable = entry.Value;
switch (drawable)
{
case DrawableHitCircle circle:

View File

@ -88,7 +88,7 @@ namespace osu.Game.Rulesets.Osu.Mods
if (!slider.HeadCircle.IsHit)
handleHitCircle(slider.HeadCircle);
requiresHold |= slider.SliderInputManager.IsMouseInFollowArea(true);
requiresHold |= slider.SliderInputManager.IsMouseInFollowArea(slider.Tracking.Value);
break;
case DrawableSpinner spinner:

View File

@ -48,8 +48,10 @@ namespace osu.Game.Rulesets.Osu.Mods
{
var cursorPos = playfield.Cursor.AsNonNull().ActiveCursor.DrawPosition;
foreach (var drawable in playfield.HitObjectContainer.AliveObjects)
foreach (var entry in playfield.HitObjectContainer.AliveEntries)
{
var drawable = entry.Value;
var destination = Vector2.Clamp(2 * drawable.Position - cursorPos, Vector2.Zero, OsuPlayfield.BASE_SIZE);
if (drawable.HitObject is Slider thisSlider)

View File

@ -22,6 +22,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public override LocalisableString Description => @"Spinners will be automatically completed.";
public override double ScoreMultiplier => 0.9;
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot), typeof(OsuModTargetPractice) };
public override bool Ranked => UsesDefaultConfiguration;
public void ApplyToDrawableHitObject(DrawableHitObject hitObject)
{

View File

@ -10,5 +10,6 @@ namespace osu.Game.Rulesets.Osu.Mods
public class OsuModTouchDevice : ModTouchDevice
{
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray();
public override bool Ranked => UsesDefaultConfiguration;
}
}

View File

@ -155,7 +155,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
ApplyResult(r => r.Type = r.Judgement.MinResult);
ApplyMinResult();
return;
}
@ -169,19 +169,22 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (result == HitResult.None || clickAction != ClickAction.Hit)
return;
ApplyResult(r =>
Vector2? hitPosition = null;
// Todo: This should also consider misses, but they're a little more interesting to handle, since we don't necessarily know the position at the time of a miss.
if (result.IsHit())
{
var localMousePosition = ToLocalSpace(inputManager.CurrentState.Mouse.Position);
hitPosition = HitObject.StackedPosition + (localMousePosition - DrawSize / 2);
}
ApplyResult<(HitResult result, Vector2? position)>((r, state) =>
{
var circleResult = (OsuHitCircleJudgementResult)r;
// Todo: This should also consider misses, but they're a little more interesting to handle, since we don't necessarily know the position at the time of a miss.
if (result.IsHit())
{
var localMousePosition = ToLocalSpace(inputManager.CurrentState.Mouse.Position);
circleResult.CursorPositionAtHit = HitObject.StackedPosition + (localMousePosition - DrawSize / 2);
}
circleResult.Type = result;
});
circleResult.Type = state.result;
circleResult.CursorPositionAtHit = state.position;
}, (result, hitPosition));
}
/// <summary>

View File

@ -100,12 +100,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
/// <summary>
/// Causes this <see cref="DrawableOsuHitObject"/> to get hit, disregarding all conditions in implementations of <see cref="DrawableHitObject.CheckForResult"/>.
/// </summary>
public void HitForcefully() => ApplyResult(r => r.Type = r.Judgement.MaxResult);
public void HitForcefully() => ApplyMaxResult();
/// <summary>
/// Causes this <see cref="DrawableOsuHitObject"/> to get missed, disregarding all conditions in implementations of <see cref="DrawableHitObject.CheckForResult"/>.
/// </summary>
public void MissForcefully() => ApplyResult(r => r.Type = r.Judgement.MinResult);
public void MissForcefully() => ApplyMinResult();
private RectangleF parentScreenSpaceRectangle => ((DrawableOsuHitObject)ParentHitObject)?.parentScreenSpaceRectangle ?? Parent!.ScreenSpaceDrawQuad.AABBFloat;

View File

@ -239,11 +239,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (Tracking.Value && Time.Current >= HitObject.StartTime)
{
// keep the sliding sample playing at the current tracking position
if (!slidingSample.IsPlaying)
if (!slidingSample.RequestedPlaying)
slidingSample.Play();
slidingSample.Balance.Value = CalculateSamplePlaybackBalance(CalculateDrawableRelativePosition(Ball));
}
else if (slidingSample.IsPlaying)
else if (slidingSample.IsPlaying || slidingSample.RequestedPlaying)
slidingSample.Stop();
}
}
@ -292,10 +292,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (HitObject.ClassicSliderBehaviour)
{
// Classic behaviour means a slider is judged proportionally to the number of nested hitobjects hit. This is the classic osu!stable scoring.
ApplyResult(r =>
ApplyResult(static (r, hitObject) =>
{
int totalTicks = NestedHitObjects.Count;
int hitTicks = NestedHitObjects.Count(h => h.IsHit);
int totalTicks = hitObject.NestedHitObjects.Count;
int hitTicks = hitObject.NestedHitObjects.Count(h => h.IsHit);
if (hitTicks == totalTicks)
r.Type = HitResult.Great;
@ -312,7 +312,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
// If only the nested hitobjects are judged, then the slider's own judgement is ignored for scoring purposes.
// But the slider needs to still be judged with a reasonable hit/miss result for visual purposes (hit/miss transforms, etc).
ApplyResult(r => r.Type = NestedHitObjects.Any(h => h.Result.IsHit) ? r.Judgement.MaxResult : r.Judgement.MinResult);
ApplyResult(static (r, hitObject) =>
{
r.Type = hitObject.NestedHitObjects.Any(h => h.Result.IsHit) ? r.Judgement.MaxResult : r.Judgement.MinResult;
});
}
}

View File

@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public const double ANIM_DURATION = 150;
private const float default_tick_size = 16;
public const float DEFAULT_TICK_SIZE = 16;
protected DrawableSlider DrawableSlider => (DrawableSlider)ParentHitObject;
@ -44,8 +44,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
Masking = true,
Origin = Anchor.Centre,
Size = new Vector2(default_tick_size),
BorderThickness = default_tick_size / 4,
Size = new Vector2(DEFAULT_TICK_SIZE),
BorderThickness = DEFAULT_TICK_SIZE / 4,
BorderColour = Color4.White,
Child = new Box
{
@ -88,8 +88,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
break;
case ArmedState.Miss:
this.FadeOut(ANIM_DURATION);
this.TransformBindableTo(AccentColour, Color4.Red, 0);
this.FadeOut(ANIM_DURATION, Easing.OutQuint);
break;
case ArmedState.Hit:

View File

@ -153,7 +153,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
if (tracking.NewValue)
{
if (!spinningSample.IsPlaying)
if (!spinningSample.RequestedPlaying)
spinningSample.Play();
spinningSample.VolumeTo(1, 300);
@ -258,15 +258,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
foreach (var tick in ticks.Where(t => !t.Result.HasResult))
tick.TriggerResult(false);
ApplyResult(r =>
ApplyResult(static (r, hitObject) =>
{
if (Progress >= 1)
var spinner = (DrawableSpinner)hitObject;
if (spinner.Progress >= 1)
r.Type = HitResult.Great;
else if (Progress > .9)
else if (spinner.Progress > .9)
r.Type = HitResult.Ok;
else if (Progress > .75)
else if (spinner.Progress > .75)
r.Type = HitResult.Meh;
else if (Time.Current >= HitObject.EndTime)
else if (spinner.Time.Current >= spinner.HitObject.EndTime)
r.Type = r.Judgement.MinResult;
});
}

View File

@ -35,6 +35,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
/// Apply a judgement result.
/// </summary>
/// <param name="hit">Whether this tick was reached.</param>
internal void TriggerResult(bool hit) => ApplyResult(r => r.Type = hit ? r.Judgement.MaxResult : r.Judgement.MinResult);
internal void TriggerResult(bool hit)
{
if (hit)
ApplyMaxResult();
else
ApplyMinResult();
}
}
}

View File

@ -30,7 +30,6 @@ namespace osu.Game.Rulesets.Osu.Objects
public class TailJudgement : SliderEndJudgement
{
public override HitResult MaxResult => HitResult.SliderTailHit;
public override HitResult MinResult => HitResult.IgnoreMiss;
}
}
}

View File

@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Osu
base.ReloadMappings(realmKeyBindings);
if (!AllowGameplayInputs)
KeyBindings = KeyBindings.Where(b => b.GetAction<OsuAction>() == OsuAction.Smoke).ToList();
KeyBindings = KeyBindings.Where(static b => b.GetAction<OsuAction>() == OsuAction.Smoke).ToList();
}
}
}

View File

@ -28,6 +28,7 @@ using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.Osu.Scoring;
using osu.Game.Rulesets.Osu.Skinning.Argon;
using osu.Game.Rulesets.Osu.Skinning.Default;
using osu.Game.Rulesets.Osu.Skinning.Legacy;
using osu.Game.Rulesets.Osu.Statistics;
using osu.Game.Rulesets.Osu.UI;
@ -254,6 +255,9 @@ namespace osu.Game.Rulesets.Osu
case ArgonSkin:
return new OsuArgonSkinTransformer(skin);
case TrianglesSkin:
return new OsuTrianglesSkinTransformer(skin);
}
return null;

View File

@ -16,7 +16,7 @@ using osuTK;
namespace osu.Game.Rulesets.Osu.Skinning.Argon
{
public partial class ArgonJudgementPiece : JudgementPiece, IAnimatableJudgement
public partial class ArgonJudgementPiece : TextJudgementPiece, IAnimatableJudgement
{
private RingExplosion? ringExplosion;

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