mirror of
https://github.com/ppy/osu.git
synced 2025-03-15 01:27:20 +08:00
Merge branch 'master' into mute_detection
This commit is contained in:
commit
df40f55c07
@ -3,13 +3,13 @@
|
|||||||
"isRoot": true,
|
"isRoot": true,
|
||||||
"tools": {
|
"tools": {
|
||||||
"jetbrains.resharper.globaltools": {
|
"jetbrains.resharper.globaltools": {
|
||||||
"version": "2022.2.3",
|
"version": "2023.3.3",
|
||||||
"commands": [
|
"commands": [
|
||||||
"jb"
|
"jb"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"nvika": {
|
"nvika": {
|
||||||
"version": "2.2.0",
|
"version": "3.0.0",
|
||||||
"commands": [
|
"commands": [
|
||||||
"nvika"
|
"nvika"
|
||||||
]
|
]
|
||||||
|
26
.github/workflows/ci.yml
vendored
26
.github/workflows/ci.yml
vendored
@ -15,17 +15,10 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
# FIXME: Tools won't run in .NET 6.0 unless you install 3.1.x LTS side by side.
|
- name: Install .NET 8.0.x
|
||||||
# https://itnext.io/how-to-support-multiple-net-sdks-in-github-actions-workflows-b988daa884e
|
|
||||||
- name: Install .NET 3.1.x LTS
|
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: "3.1.x"
|
dotnet-version: "8.0.x"
|
||||||
|
|
||||||
- name: Install .NET 6.0.x
|
|
||||||
uses: actions/setup-dotnet@v3
|
|
||||||
with:
|
|
||||||
dotnet-version: "6.0.x"
|
|
||||||
|
|
||||||
- name: Restore Tools
|
- name: Restore Tools
|
||||||
run: dotnet tool restore
|
run: dotnet tool restore
|
||||||
@ -79,10 +72,10 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install .NET 6.0.x
|
- name: Install .NET 8.0.x
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: "6.0.x"
|
dotnet-version: "8.0.x"
|
||||||
|
|
||||||
- name: Compile
|
- name: Compile
|
||||||
run: dotnet build -c Debug -warnaserror osu.Desktop.slnf
|
run: dotnet build -c Debug -warnaserror osu.Desktop.slnf
|
||||||
@ -114,10 +107,10 @@ jobs:
|
|||||||
distribution: microsoft
|
distribution: microsoft
|
||||||
java-version: 11
|
java-version: 11
|
||||||
|
|
||||||
- name: Install .NET 6.0.x
|
- name: Install .NET 8.0.x
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: "6.0.x"
|
dotnet-version: "8.0.x"
|
||||||
|
|
||||||
- name: Install .NET workloads
|
- name: Install .NET workloads
|
||||||
run: dotnet workload install maui-android
|
run: dotnet workload install maui-android
|
||||||
@ -135,13 +128,16 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install .NET 6.0.x
|
- name: Install .NET 8.0.x
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: "6.0.x"
|
dotnet-version: "8.0.x"
|
||||||
|
|
||||||
- name: Install .NET Workloads
|
- name: Install .NET Workloads
|
||||||
run: dotnet workload install maui-ios
|
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
|
- name: Build
|
||||||
run: dotnet build -c Debug osu.iOS
|
run: dotnet build -c Debug osu.iOS
|
||||||
|
@ -12,10 +12,10 @@ jobs:
|
|||||||
name: Update osu-web mod definitions
|
name: Update osu-web mod definitions
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Install .NET 6.0.x
|
- name: Install .NET 8.0.x
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: "6.0.x"
|
dotnet-version: "8.0.x"
|
||||||
|
|
||||||
- name: Checkout ppy/osu
|
- name: Checkout ppy/osu
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
is_global = true
|
|
||||||
|
|
||||||
# .NET Code Style
|
# .NET Code Style
|
||||||
# IDE styles reference: https://docs.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/
|
# 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.
|
# Temporarily disable analysing CanBeNull = true in NRT contexts due to mobile issues.
|
||||||
# See: https://github.com/ppy/osu/pull/19677
|
# See: https://github.com/ppy/osu/pull/19677
|
||||||
dotnet_diagnostic.OSUF001.severity = none
|
dotnet_diagnostic.OSUF001.severity = none
|
||||||
|
@ -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 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 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.
|
- 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:
|
After you're done with your changes and you wish to open the PR, please observe the following recommendations:
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<!-- Contains required properties for osu!framework projects. -->
|
<!-- Contains required properties for osu!framework projects. -->
|
||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup Label="C#">
|
<PropertyGroup Label="C#">
|
||||||
<LangVersion>10.0</LangVersion>
|
<LangVersion>12.0</LangVersion>
|
||||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@ -30,8 +30,8 @@ If you are just looking to give the game a whirl, you can grab the latest releas
|
|||||||
|
|
||||||
### Latest release:
|
### 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).
|
You can also generally download a version for your current device from the [osu! site](https://osu.ppy.sh/home/download).
|
||||||
|
|
||||||
|
10
Templates/Rulesets/ruleset-empty/Directory.Build.props
Normal file
10
Templates/Rulesets/ruleset-empty/Directory.Build.props
Normal 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>
|
46
Templates/Rulesets/ruleset-empty/app.manifest
Normal file
46
Templates/Rulesets/ruleset-empty/app.manifest
Normal 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>
|
@ -18,7 +18,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<RootNamespace>osu.Game.Rulesets.EmptyFreeform.Tests</RootNamespace>
|
<RootNamespace>osu.Game.Rulesets.EmptyFreeform.Tests</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.EmptyFreeform.Objects.Drawables
|
|||||||
{
|
{
|
||||||
if (timeOffset >= 0)
|
if (timeOffset >= 0)
|
||||||
// todo: implement judgement logic
|
// todo: implement judgement logic
|
||||||
ApplyResult(r => r.Type = HitResult.Perfect);
|
ApplyResult(HitResult.Perfect);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateHitStateTransforms(ArmedState state)
|
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.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>
|
||||||
|
10
Templates/Rulesets/ruleset-example/Directory.Build.props
Normal file
10
Templates/Rulesets/ruleset-example/Directory.Build.props
Normal 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>
|
46
Templates/Rulesets/ruleset-example/app.manifest
Normal file
46
Templates/Rulesets/ruleset-example/app.manifest
Normal 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>
|
@ -18,7 +18,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<RootNamespace>osu.Game.Rulesets.Pippidon.Tests</RootNamespace>
|
<RootNamespace>osu.Game.Rulesets.Pippidon.Tests</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -9,7 +9,6 @@ using osu.Framework.Graphics.Sprites;
|
|||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Scoring;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -49,7 +48,12 @@ namespace osu.Game.Rulesets.Pippidon.Objects.Drawables
|
|||||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||||
{
|
{
|
||||||
if (timeOffset >= 0)
|
if (timeOffset >= 0)
|
||||||
ApplyResult(r => r.Type = IsHovered ? HitResult.Perfect : HitResult.Miss);
|
{
|
||||||
|
if (IsHovered)
|
||||||
|
ApplyMaxResult();
|
||||||
|
else
|
||||||
|
ApplyMinResult();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override double InitialLifetimeOffset => time_preempt;
|
protected override double InitialLifetimeOffset => time_preempt;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.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>
|
||||||
|
@ -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>
|
46
Templates/Rulesets/ruleset-scrolling-empty/app.manifest
Normal file
46
Templates/Rulesets/ruleset-scrolling-empty/app.manifest
Normal 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>
|
@ -18,7 +18,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<RootNamespace>osu.Game.Rulesets.EmptyScrolling.Tests</RootNamespace>
|
<RootNamespace>osu.Game.Rulesets.EmptyScrolling.Tests</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Scoring;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -24,7 +23,7 @@ namespace osu.Game.Rulesets.EmptyScrolling.Objects.Drawables
|
|||||||
{
|
{
|
||||||
if (timeOffset >= 0)
|
if (timeOffset >= 0)
|
||||||
// todo: implement judgement logic
|
// todo: implement judgement logic
|
||||||
ApplyResult(r => r.Type = HitResult.Perfect);
|
ApplyMaxResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateHitStateTransforms(ArmedState state)
|
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.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>
|
||||||
|
@ -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>
|
46
Templates/Rulesets/ruleset-scrolling-example/app.manifest
Normal file
46
Templates/Rulesets/ruleset-scrolling-example/app.manifest
Normal 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>
|
@ -18,7 +18,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<RootNamespace>osu.Game.Rulesets.Pippidon.Tests</RootNamespace>
|
<RootNamespace>osu.Game.Rulesets.Pippidon.Tests</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -10,7 +10,6 @@ using osu.Framework.Graphics.Textures;
|
|||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Pippidon.UI;
|
using osu.Game.Rulesets.Pippidon.UI;
|
||||||
using osu.Game.Rulesets.Scoring;
|
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -49,7 +48,12 @@ namespace osu.Game.Rulesets.Pippidon.Objects.Drawables
|
|||||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||||
{
|
{
|
||||||
if (timeOffset >= 0)
|
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)
|
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.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>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"sdk": {
|
"sdk": {
|
||||||
"version": "6.0.100",
|
"version": "8.0.100",
|
||||||
"rollForward": "latestFeature"
|
"rollForward": "latestFeature",
|
||||||
|
"allowPrerelease": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -10,7 +10,7 @@
|
|||||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.131.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.215.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="sh.ppy.osulazer" android:installLocation="auto">
|
<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"
|
<application android:allowBackup="true"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:label="osu!"
|
android:label="osu!"
|
||||||
|
@ -72,9 +72,9 @@ 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
|
#pragma warning disable CA1422 // GetSize is deprecated
|
||||||
WindowManager.DefaultDisplay.GetSize(displaySize);
|
WindowManager.DefaultDisplay.GetSize(displaySize);
|
||||||
#pragma warning restore 618
|
#pragma warning restore CA1422
|
||||||
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;
|
||||||
|
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<Import Project="..\osu.Android.props" />
|
<Import Project="..\osu.Android.props" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0-android</TargetFramework>
|
<TargetFramework>net8.0-android</TargetFramework>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<RootNamespace>osu.Android</RootNamespace>
|
<RootNamespace>osu.Android</RootNamespace>
|
||||||
<AssemblyName>osu.Android</AssemblyName>
|
<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>
|
<Version>0.0.0</Version>
|
||||||
<ApplicationVersion Condition=" '$(ApplicationVersion)' == '' ">1</ApplicationVersion>
|
<ApplicationVersion Condition=" '$(ApplicationVersion)' == '' ">1</ApplicationVersion>
|
||||||
<ApplicationDisplayVersion Condition=" '$(ApplicationDisplayVersion)' == '' ">$(Version)</ApplicationDisplayVersion>
|
<ApplicationDisplayVersion Condition=" '$(ApplicationDisplayVersion)' == '' ">$(Version)</ApplicationDisplayVersion>
|
||||||
@ -19,4 +16,7 @@
|
|||||||
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
|
||||||
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
|
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Maui.Essentials" Version="8.0.3" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -138,7 +138,7 @@ namespace osu.Desktop
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Make sure that this is a laptop.
|
// Make sure that this is a laptop.
|
||||||
var gpus = new IntPtr[64];
|
IntPtr[] gpus = new IntPtr[64];
|
||||||
if (checkError(EnumPhysicalGPUs(gpus, out int gpuCount)))
|
if (checkError(EnumPhysicalGPUs(gpus, out int gpuCount)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -456,7 +456,7 @@ namespace osu.Desktop
|
|||||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NVAPI.UNICODE_STRING_MAX)]
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NVAPI.UNICODE_STRING_MAX)]
|
||||||
public string ProfileName;
|
public string ProfileName;
|
||||||
|
|
||||||
[MarshalAs(UnmanagedType.ByValArray)]
|
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
|
||||||
public uint[] GPUSupport;
|
public uint[] GPUSupport;
|
||||||
|
|
||||||
public uint IsPredefined;
|
public uint IsPredefined;
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 75 KiB |
@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<Description>A free-to-win rhythm game. Rhythm is just a *click* away!</Description>
|
<Description>A free-to-win rhythm game. Rhythm is just a *click* away!</Description>
|
||||||
|
31
osu.Game.Benchmarks/BenchmarkUnstableRate.cs
Normal file
31
osu.Game.Benchmarks/BenchmarkUnstableRate.cs
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@ -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="33" />
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" />
|
||||||
<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>
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<Import Project="..\osu.Android.props" />
|
<Import Project="..\osu.Android.props" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0-android</TargetFramework>
|
<TargetFramework>net8.0-android</TargetFramework>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<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>
|
||||||
@ -21,4 +21,4 @@
|
|||||||
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
|
||||||
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
|
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net6.0-ios</TargetFramework>
|
<TargetFramework>net8.0-ios</TargetFramework>
|
||||||
<SupportedOSPlatformVersion>13.4</SupportedOSPlatformVersion>
|
<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>
|
||||||
|
@ -1,111 +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.
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System.Linq;
|
|
||||||
using NUnit.Framework;
|
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Framework.Testing;
|
|
||||||
using osu.Framework.Utils;
|
|
||||||
using osu.Game.Beatmaps;
|
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
|
||||||
using osu.Game.Rulesets.Catch.Judgements;
|
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
|
||||||
using osu.Game.Rulesets.Catch.Skinning;
|
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
|
||||||
using osu.Game.Skinning;
|
|
||||||
using osu.Game.Tests.Visual;
|
|
||||||
using Direction = osu.Game.Rulesets.Catch.UI.Direction;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests
|
|
||||||
{
|
|
||||||
public partial class TestSceneCatchSkinConfiguration : OsuTestScene
|
|
||||||
{
|
|
||||||
private Catcher catcher;
|
|
||||||
|
|
||||||
private readonly Container container;
|
|
||||||
|
|
||||||
public TestSceneCatchSkinConfiguration()
|
|
||||||
{
|
|
||||||
Add(container = new Container { RelativeSizeAxes = Axes.Both });
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestCase(false)]
|
|
||||||
[TestCase(true)]
|
|
||||||
public void TestCatcherPlateFlipping(bool flip)
|
|
||||||
{
|
|
||||||
AddStep("setup catcher", () =>
|
|
||||||
{
|
|
||||||
var skin = new TestSkin { FlipCatcherPlate = flip };
|
|
||||||
container.Child = new SkinProvidingContainer(skin)
|
|
||||||
{
|
|
||||||
Child = catcher = new Catcher(new DroppedObjectContainer())
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
Fruit fruit = new Fruit();
|
|
||||||
|
|
||||||
AddStep("catch fruit", () => catchFruit(fruit, 20));
|
|
||||||
|
|
||||||
float position = 0;
|
|
||||||
|
|
||||||
AddStep("record fruit position", () => position = getCaughtObjectPosition(fruit));
|
|
||||||
|
|
||||||
AddStep("face left", () => catcher.VisualDirection = Direction.Left);
|
|
||||||
|
|
||||||
if (flip)
|
|
||||||
AddAssert("fruit position changed", () => !Precision.AlmostEquals(getCaughtObjectPosition(fruit), position));
|
|
||||||
else
|
|
||||||
AddAssert("fruit position unchanged", () => Precision.AlmostEquals(getCaughtObjectPosition(fruit), position));
|
|
||||||
|
|
||||||
AddStep("face right", () => catcher.VisualDirection = Direction.Right);
|
|
||||||
|
|
||||||
AddAssert("fruit position restored", () => Precision.AlmostEquals(getCaughtObjectPosition(fruit), position));
|
|
||||||
}
|
|
||||||
|
|
||||||
private float getCaughtObjectPosition(Fruit fruit)
|
|
||||||
{
|
|
||||||
var caughtObject = catcher.ChildrenOfType<CaughtObject>().Single(c => c.HitObject == fruit);
|
|
||||||
return caughtObject.Parent!.ToSpaceOfOtherDrawable(caughtObject.Position, catcher).X;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void catchFruit(Fruit fruit, float x)
|
|
||||||
{
|
|
||||||
fruit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
|
||||||
var drawableFruit = new DrawableFruit(fruit) { X = x };
|
|
||||||
var judgement = fruit.CreateJudgement();
|
|
||||||
catcher.OnNewResult(drawableFruit, new CatchJudgementResult(fruit, judgement)
|
|
||||||
{
|
|
||||||
Type = judgement.MaxResult
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TestSkin : TrianglesSkin
|
|
||||||
{
|
|
||||||
public bool FlipCatcherPlate { get; set; }
|
|
||||||
|
|
||||||
public TestSkin()
|
|
||||||
: base(null!)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
|
||||||
{
|
|
||||||
if (lookup is CatchSkinConfiguration config)
|
|
||||||
{
|
|
||||||
if (config == CatchSkinConfiguration.FlipCatcherPlate)
|
|
||||||
return SkinUtils.As<TValue>(new Bindable<bool>(FlipCatcherPlate));
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.GetConfig<TLookup, TValue>(lookup);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,7 +7,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Project References">
|
<ItemGroup Label="Project References">
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
|
||||||
|
@ -21,7 +21,6 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
public void ApplyToDrawableRuleset(DrawableRuleset<CatchHitObject> drawableRuleset)
|
public void ApplyToDrawableRuleset(DrawableRuleset<CatchHitObject> drawableRuleset)
|
||||||
{
|
{
|
||||||
drawableRuleset.PlayfieldAdjustmentContainer.Scale = new Vector2(1, -1);
|
drawableRuleset.PlayfieldAdjustmentContainer.Scale = new Vector2(1, -1);
|
||||||
drawableRuleset.PlayfieldAdjustmentContainer.Y = 1 - drawableRuleset.PlayfieldAdjustmentContainer.Y;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,12 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
|
|||||||
if (CheckPosition == null) return;
|
if (CheckPosition == null) return;
|
||||||
|
|
||||||
if (timeOffset >= 0 && Result != null)
|
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)
|
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||||
|
@ -1,13 +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.
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Skinning
|
|
||||||
{
|
|
||||||
public enum CatchSkinConfiguration
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Whether the contents of the catcher plate should be visually flipped when the catcher direction is changed.
|
|
||||||
/// </summary>
|
|
||||||
FlipCatcherPlate
|
|
||||||
}
|
|
||||||
}
|
|
@ -122,19 +122,6 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
|||||||
|
|
||||||
result.Value = LegacyColourCompatibility.DisallowZeroAlpha(result.Value);
|
result.Value = LegacyColourCompatibility.DisallowZeroAlpha(result.Value);
|
||||||
return (IBindable<TValue>)result;
|
return (IBindable<TValue>)result;
|
||||||
|
|
||||||
case CatchSkinConfiguration config:
|
|
||||||
switch (config)
|
|
||||||
{
|
|
||||||
case CatchSkinConfiguration.FlipCatcherPlate:
|
|
||||||
// Don't flip catcher plate contents if the catcher is provided by this legacy skin.
|
|
||||||
if (GetDrawableComponent(new CatchSkinComponentLookup(CatchSkinComponents.Catcher)) != null)
|
|
||||||
return (IBindable<TValue>)new Bindable<bool>();
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.GetConfig<TLookup, TValue>(lookup);
|
return base.GetConfig<TLookup, TValue>(lookup);
|
||||||
|
@ -17,24 +17,36 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
public CatchPlayfieldAdjustmentContainer()
|
public CatchPlayfieldAdjustmentContainer()
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre;
|
const float base_game_width = 1024f;
|
||||||
Origin = Anchor.TopCentre;
|
const float base_game_height = 768f;
|
||||||
|
|
||||||
// playfields in stable are positioned vertically at three fourths the difference between the playfield height and the window height in stable.
|
// extra bottom space for the catcher to not get cut off at tall resolutions lower than 4:3 (e.g. 5:4). number chosen based on testing with maximum catcher scale (i.e. CS 0).
|
||||||
// we can match that in lazer by using relative coordinates for Y and considering window height to be 1, and playfield height to be 0.8.
|
const float extra_bottom_space = 200f;
|
||||||
RelativePositionAxes = Axes.Y;
|
|
||||||
Y = (1 - playfield_size_adjust) / 4 * 3;
|
|
||||||
|
|
||||||
Size = new Vector2(playfield_size_adjust);
|
Anchor = Anchor.Centre;
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
|
||||||
InternalChild = new Container
|
InternalChild = new Container
|
||||||
{
|
{
|
||||||
|
// This container limits vertical visibility of the playfield to ensure fairness between wide and tall resolutions (i.e. tall resolutions should not see more fruits).
|
||||||
|
// Note that the container still extends across the screen horizontally, so that hit explosions at the sides of the playfield do not get cut off.
|
||||||
|
Name = "Visible area",
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.X,
|
||||||
FillMode = FillMode.Fit,
|
Height = base_game_height + extra_bottom_space,
|
||||||
FillAspectRatio = 4f / 3,
|
Y = extra_bottom_space / 2,
|
||||||
Child = content = new ScalingContainer { RelativeSizeAxes = Axes.Both, }
|
Masking = true,
|
||||||
|
Child = new Container
|
||||||
|
{
|
||||||
|
Name = "Playable area",
|
||||||
|
Anchor = Anchor.TopCentre,
|
||||||
|
Origin = Anchor.TopCentre,
|
||||||
|
// playfields in stable are positioned vertically at three fourths the difference between the playfield height and the window height in stable.
|
||||||
|
Y = base_game_height * ((1 - playfield_size_adjust) / 4 * 3),
|
||||||
|
Size = new Vector2(base_game_width, base_game_height) * playfield_size_adjust,
|
||||||
|
Child = content = new ScalingContainer { RelativeSizeAxes = Axes.Both }
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,11 +112,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
public Vector2 BodyScale => Scale * body.Scale;
|
public Vector2 BodyScale => Scale * body.Scale;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether the contents of the catcher plate should be visually flipped when the catcher direction is changed.
|
|
||||||
/// </summary>
|
|
||||||
private bool flipCatcherPlate;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Width of the area that can be used to attempt catches during gameplay.
|
/// Width of the area that can be used to attempt catches during gameplay.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -339,8 +334,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDash)?.Value ??
|
skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDash)?.Value ??
|
||||||
DEFAULT_HYPER_DASH_COLOUR;
|
DEFAULT_HYPER_DASH_COLOUR;
|
||||||
|
|
||||||
flipCatcherPlate = skin.GetConfig<CatchSkinConfiguration, bool>(CatchSkinConfiguration.FlipCatcherPlate)?.Value ?? true;
|
|
||||||
|
|
||||||
runHyperDashStateTransition(HyperDashing);
|
runHyperDashStateTransition(HyperDashing);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -352,8 +345,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
body.Scale = scaleFromDirection;
|
body.Scale = scaleFromDirection;
|
||||||
// Inverse of catcher scale is applied here, as catcher gets scaled by circle size and so do the incoming fruit.
|
// Inverse of catcher scale is applied here, as catcher gets scaled by circle size and so do the incoming fruit.
|
||||||
caughtObjectContainer.Scale = (1 / Scale.X) * (flipCatcherPlate ? scaleFromDirection : Vector2.One);
|
caughtObjectContainer.Scale = new Vector2(1 / Scale.X);
|
||||||
hitExplosionContainer.Scale = flipCatcherPlate ? scaleFromDirection : Vector2.One;
|
|
||||||
|
|
||||||
// Correct overshooting.
|
// Correct overshooting.
|
||||||
if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) ||
|
if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) ||
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.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>
|
||||||
|
@ -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="33" />
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" />
|
||||||
<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>
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<Import Project="..\osu.Android.props" />
|
<Import Project="..\osu.Android.props" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0-android</TargetFramework>
|
<TargetFramework>net8.0-android</TargetFramework>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<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>
|
||||||
@ -21,4 +21,4 @@
|
|||||||
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
|
||||||
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
|
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net6.0-ios</TargetFramework>
|
<TargetFramework>net8.0-ios</TargetFramework>
|
||||||
<SupportedOSPlatformVersion>13.4</SupportedOSPlatformVersion>
|
<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>
|
||||||
|
@ -1,8 +1,18 @@
|
|||||||
// 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.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.Timing;
|
||||||
using osu.Game.Rulesets.Mania.Mods;
|
using osu.Game.Rulesets.Mania.Mods;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests.Mods
|
namespace osu.Game.Rulesets.Mania.Tests.Mods
|
||||||
@ -11,9 +21,80 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods
|
|||||||
{
|
{
|
||||||
protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset();
|
protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset();
|
||||||
|
|
||||||
[TestCase(0.5f)]
|
[Test]
|
||||||
[TestCase(0.1f)]
|
public void TestMinCoverageFullWidth()
|
||||||
[TestCase(0.7f)]
|
{
|
||||||
public void TestCoverage(float coverage) => CreateModTest(new ModTestData { Mod = new ManiaModFadeIn { Coverage = { Value = coverage } }, PassCondition = () => true });
|
CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new ManiaModHidden(),
|
||||||
|
PassCondition = () => checkCoverage(ManiaModHidden.MIN_COVERAGE)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMinCoverageHalfWidth()
|
||||||
|
{
|
||||||
|
CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new ManiaModHidden(),
|
||||||
|
PassCondition = () => checkCoverage(ManiaModHidden.MIN_COVERAGE)
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("set playfield width to 0.5", () => Player.Width = 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMaxCoverageFullWidth()
|
||||||
|
{
|
||||||
|
CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new ManiaModHidden(),
|
||||||
|
PassCondition = () => checkCoverage(ManiaModHidden.MAX_COVERAGE)
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("set combo to 480", () => Player.ScoreProcessor.Combo.Value = 480);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMaxCoverageHalfWidth()
|
||||||
|
{
|
||||||
|
CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new ManiaModHidden(),
|
||||||
|
PassCondition = () => checkCoverage(ManiaModHidden.MAX_COVERAGE)
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("set combo to 480", () => Player.ScoreProcessor.Combo.Value = 480);
|
||||||
|
AddStep("set playfield width to 0.5", () => Player.Width = 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNoCoverageDuringBreak()
|
||||||
|
{
|
||||||
|
CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new ManiaModHidden(),
|
||||||
|
Beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
HitObjects = Enumerable.Range(1, 100).Select(i => (HitObject)new Note { StartTime = 1000 + 200 * i }).ToList(),
|
||||||
|
Breaks = { new BreakPeriod(2000, 28000) }
|
||||||
|
},
|
||||||
|
PassCondition = () => Player.IsBreakTime.Value && checkCoverage(0)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool checkCoverage(float expected)
|
||||||
|
{
|
||||||
|
Drawable? cover = this.ChildrenOfType<PlayfieldCoveringWrapper>().FirstOrDefault();
|
||||||
|
Drawable? filledArea = cover?.ChildrenOfType<Box>().LastOrDefault();
|
||||||
|
|
||||||
|
if (filledArea == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
float scale = cover!.DrawHeight / (768 - Stage.HIT_TARGET_POSITION);
|
||||||
|
|
||||||
|
// A bit of lenience because the test may end up hitting hitobjects before any assertions.
|
||||||
|
return Precision.AlmostEquals(filledArea.DrawHeight / scale, expected, 0.1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,18 @@
|
|||||||
// 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.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.Timing;
|
||||||
using osu.Game.Rulesets.Mania.Mods;
|
using osu.Game.Rulesets.Mania.Mods;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests.Mods
|
namespace osu.Game.Rulesets.Mania.Tests.Mods
|
||||||
@ -11,9 +21,80 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods
|
|||||||
{
|
{
|
||||||
protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset();
|
protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset();
|
||||||
|
|
||||||
[TestCase(0.5f)]
|
[Test]
|
||||||
[TestCase(0.2f)]
|
public void TestMinCoverageFullWidth()
|
||||||
[TestCase(0.8f)]
|
{
|
||||||
public void TestCoverage(float coverage) => CreateModTest(new ModTestData { Mod = new ManiaModHidden { Coverage = { Value = coverage } }, PassCondition = () => true });
|
CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new ManiaModHidden(),
|
||||||
|
PassCondition = () => checkCoverage(ManiaModHidden.MIN_COVERAGE)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMinCoverageHalfWidth()
|
||||||
|
{
|
||||||
|
CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new ManiaModHidden(),
|
||||||
|
PassCondition = () => checkCoverage(ManiaModHidden.MIN_COVERAGE)
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("set playfield width to 0.5", () => Player.Width = 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMaxCoverageFullWidth()
|
||||||
|
{
|
||||||
|
CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new ManiaModHidden(),
|
||||||
|
PassCondition = () => checkCoverage(ManiaModHidden.MAX_COVERAGE)
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("set combo to 480", () => Player.ScoreProcessor.Combo.Value = 480);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMaxCoverageHalfWidth()
|
||||||
|
{
|
||||||
|
CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new ManiaModHidden(),
|
||||||
|
PassCondition = () => checkCoverage(ManiaModHidden.MAX_COVERAGE)
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("set combo to 480", () => Player.ScoreProcessor.Combo.Value = 480);
|
||||||
|
AddStep("set playfield width to 0.5", () => Player.Width = 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNoCoverageDuringBreak()
|
||||||
|
{
|
||||||
|
CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new ManiaModHidden(),
|
||||||
|
Beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
HitObjects = Enumerable.Range(1, 100).Select(i => (HitObject)new Note { StartTime = 1000 + 200 * i }).ToList(),
|
||||||
|
Breaks = { new BreakPeriod(2000, 28000) }
|
||||||
|
},
|
||||||
|
PassCondition = () => Player.IsBreakTime.Value && checkCoverage(0)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool checkCoverage(float expected)
|
||||||
|
{
|
||||||
|
Drawable? cover = this.ChildrenOfType<PlayfieldCoveringWrapper>().FirstOrDefault();
|
||||||
|
Drawable? filledArea = cover?.ChildrenOfType<Box>().LastOrDefault();
|
||||||
|
|
||||||
|
if (filledArea == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
float scale = cover!.DrawHeight / (768 - Stage.HIT_TARGET_POSITION);
|
||||||
|
|
||||||
|
// A bit of lenience because the test may end up hitting hitobjects before any assertions.
|
||||||
|
return Precision.AlmostEquals(filledArea.DrawHeight / scale, expected, 0.1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,18 +39,18 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
public void TestScrollingDownwards()
|
public void TestScrollingDownwards()
|
||||||
{
|
{
|
||||||
AddStep("set down scroll", () => scrollingContainer.Direction = ScrollingDirection.Down);
|
AddStep("set down scroll", () => scrollingContainer.Direction = ScrollingDirection.Down);
|
||||||
AddStep("set coverage = 0.5", () => cover.Coverage = 0.5f);
|
AddStep("set coverage = 0.5", () => cover.Coverage.Value = 0.5f);
|
||||||
AddStep("set coverage = 0.8f", () => cover.Coverage = 0.8f);
|
AddStep("set coverage = 0.8f", () => cover.Coverage.Value = 0.8f);
|
||||||
AddStep("set coverage = 0.2f", () => cover.Coverage = 0.2f);
|
AddStep("set coverage = 0.2f", () => cover.Coverage.Value = 0.2f);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestScrollingUpwards()
|
public void TestScrollingUpwards()
|
||||||
{
|
{
|
||||||
AddStep("set up scroll", () => scrollingContainer.Direction = ScrollingDirection.Up);
|
AddStep("set up scroll", () => scrollingContainer.Direction = ScrollingDirection.Up);
|
||||||
AddStep("set coverage = 0.5", () => cover.Coverage = 0.5f);
|
AddStep("set coverage = 0.5", () => cover.Coverage.Value = 0.5f);
|
||||||
AddStep("set coverage = 0.8f", () => cover.Coverage = 0.8f);
|
AddStep("set coverage = 0.8f", () => cover.Coverage.Value = 0.8f);
|
||||||
AddStep("set coverage = 0.2f", () => cover.Coverage = 0.2f);
|
AddStep("set coverage = 0.2f", () => cover.Coverage.Value = 0.2f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Project References">
|
<ItemGroup Label="Project References">
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
|
||||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
private readonly bool isForCurrentRuleset;
|
private readonly bool isForCurrentRuleset;
|
||||||
private readonly double originalOverallDifficulty;
|
private readonly double originalOverallDifficulty;
|
||||||
|
|
||||||
public override int Version => 20220902;
|
public override int Version => 20230817;
|
||||||
|
|
||||||
public ManiaDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
|
public ManiaDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
|
||||||
: base(ruleset, beatmap)
|
: base(ruleset, beatmap)
|
||||||
|
@ -247,7 +247,7 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
new ManiaModHardRock(),
|
new ManiaModHardRock(),
|
||||||
new MultiMod(new ManiaModSuddenDeath(), new ManiaModPerfect()),
|
new MultiMod(new ManiaModSuddenDeath(), new ManiaModPerfect()),
|
||||||
new MultiMod(new ManiaModDoubleTime(), new ManiaModNightcore()),
|
new MultiMod(new ManiaModDoubleTime(), new ManiaModNightcore()),
|
||||||
new MultiMod(new ManiaModFadeIn(), new ManiaModHidden()),
|
new MultiMod(new ManiaModFadeIn(), new ManiaModHidden(), new ManiaModCover()),
|
||||||
new ManiaModFlashlight(),
|
new ManiaModFlashlight(),
|
||||||
new ModAccuracyChallenge(),
|
new ModAccuracyChallenge(),
|
||||||
};
|
};
|
||||||
@ -375,7 +375,7 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
/// <returns>The <see cref="PlayfieldType"/> that corresponds to <paramref name="variant"/>.</returns>
|
/// <returns>The <see cref="PlayfieldType"/> that corresponds to <paramref name="variant"/>.</returns>
|
||||||
private PlayfieldType getPlayfieldType(int variant)
|
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()
|
protected override IEnumerable<HitResult> GetValidHitResults()
|
||||||
|
@ -15,6 +15,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
public abstract int KeyCount { get; }
|
public abstract int KeyCount { get; }
|
||||||
public override ModType Type => ModType.Conversion;
|
public override ModType Type => ModType.Conversion;
|
||||||
public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier
|
public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier
|
||||||
|
public override bool Ranked => UsesDefaultConfiguration;
|
||||||
|
|
||||||
public void ApplyToBeatmapConverter(IBeatmapConverter beatmapConverter)
|
public void ApplyToBeatmapConverter(IBeatmapConverter beatmapConverter)
|
||||||
{
|
{
|
||||||
|
44
osu.Game.Rulesets.Mania/Mods/ManiaModCover.cs
Normal file
44
osu.Game.Rulesets.Mania/Mods/ManiaModCover.cs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// 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 System.Linq;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
|
{
|
||||||
|
public class ManiaModCover : ManiaModWithPlayfieldCover
|
||||||
|
{
|
||||||
|
public override string Name => "Cover";
|
||||||
|
public override string Acronym => "CO";
|
||||||
|
|
||||||
|
public override LocalisableString Description => @"Decrease the playfield's viewing area.";
|
||||||
|
|
||||||
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
|
protected override CoverExpandDirection ExpandDirection => Direction.Value;
|
||||||
|
|
||||||
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[]
|
||||||
|
{
|
||||||
|
typeof(ManiaModHidden),
|
||||||
|
typeof(ManiaModFadeIn)
|
||||||
|
}).ToArray();
|
||||||
|
|
||||||
|
public override bool Ranked => false;
|
||||||
|
|
||||||
|
[SettingSource("Coverage", "The proportion of playfield height that notes will be hidden for.")]
|
||||||
|
public override BindableNumber<float> Coverage { get; } = new BindableFloat(0.5f)
|
||||||
|
{
|
||||||
|
Precision = 0.1f,
|
||||||
|
MinValue = 0.2f,
|
||||||
|
MaxValue = 0.8f,
|
||||||
|
Default = 0.5f,
|
||||||
|
};
|
||||||
|
|
||||||
|
[SettingSource("Direction", "The direction on which the cover is applied")]
|
||||||
|
public Bindable<CoverExpandDirection> Direction { get; } = new Bindable<CoverExpandDirection>();
|
||||||
|
}
|
||||||
|
}
|
@ -3,29 +3,24 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModFadeIn : ManiaModPlayfieldCover
|
public class ManiaModFadeIn : ManiaModHidden
|
||||||
{
|
{
|
||||||
public override string Name => "Fade In";
|
public override string Name => "Fade In";
|
||||||
public override string Acronym => "FI";
|
public override string Acronym => "FI";
|
||||||
public override LocalisableString Description => @"Keys appear out of nowhere!";
|
public override LocalisableString Description => @"Keys appear out of nowhere!";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ManiaModHidden)).ToArray();
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[]
|
||||||
|
{
|
||||||
|
typeof(ManiaModHidden),
|
||||||
|
typeof(ManiaModCover)
|
||||||
|
}).ToArray();
|
||||||
|
|
||||||
protected override CoverExpandDirection ExpandDirection => CoverExpandDirection.AlongScroll;
|
protected override CoverExpandDirection ExpandDirection => CoverExpandDirection.AlongScroll;
|
||||||
|
|
||||||
public override BindableNumber<float> Coverage { get; } = new BindableFloat(0.5f)
|
|
||||||
{
|
|
||||||
Precision = 0.1f,
|
|
||||||
MinValue = 0.1f,
|
|
||||||
MaxValue = 0.7f,
|
|
||||||
Default = 0.5f,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,5 +8,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
public class ManiaModHardRock : ModHardRock
|
public class ManiaModHardRock : ModHardRock
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
public override bool Ranked => false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,27 +3,104 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Rulesets.Mania.Skinning;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModHidden : ManiaModPlayfieldCover
|
public partial class ManiaModHidden : ManiaModWithPlayfieldCover, IApplicableToPlayer, IUpdatableByPlayfield
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// osu!stable is referenced to 768px.
|
||||||
|
/// </summary>
|
||||||
|
private const float reference_playfield_height = 768;
|
||||||
|
|
||||||
|
public const float MIN_COVERAGE = 160f;
|
||||||
|
public const float MAX_COVERAGE = 400f;
|
||||||
|
private const float coverage_increase_per_combo = 0.5f;
|
||||||
|
|
||||||
public override LocalisableString Description => @"Keys fade out before you hit them!";
|
public override LocalisableString Description => @"Keys fade out before you hit them!";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
public override BindableNumber<float> Coverage { get; } = new BindableFloat(0.5f)
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[]
|
||||||
{
|
{
|
||||||
Precision = 0.1f,
|
typeof(ManiaModFadeIn),
|
||||||
MinValue = 0.2f,
|
typeof(ManiaModCover)
|
||||||
MaxValue = 0.8f,
|
}).ToArray();
|
||||||
Default = 0.5f,
|
|
||||||
};
|
|
||||||
|
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ManiaModFadeIn)).ToArray();
|
|
||||||
|
|
||||||
|
public override BindableNumber<float> Coverage { get; } = new BindableFloat(MIN_COVERAGE);
|
||||||
protected override CoverExpandDirection ExpandDirection => CoverExpandDirection.AgainstScroll;
|
protected override CoverExpandDirection ExpandDirection => CoverExpandDirection.AgainstScroll;
|
||||||
|
|
||||||
|
private readonly IBindable<bool> isBreakTime = new Bindable<bool>();
|
||||||
|
private readonly BindableInt combo = new BindableInt();
|
||||||
|
|
||||||
|
public override void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
|
||||||
|
{
|
||||||
|
base.ApplyToScoreProcessor(scoreProcessor);
|
||||||
|
|
||||||
|
combo.UnbindAll();
|
||||||
|
combo.BindTo(scoreProcessor.Combo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyToPlayer(Player player)
|
||||||
|
{
|
||||||
|
isBreakTime.UnbindAll();
|
||||||
|
isBreakTime.BindTo(player.IsBreakTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update(Playfield playfield)
|
||||||
|
{
|
||||||
|
Coverage.Value = isBreakTime.Value
|
||||||
|
? 0
|
||||||
|
: Math.Min(MAX_COVERAGE, MIN_COVERAGE + combo.Value * coverage_increase_per_combo) / reference_playfield_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override PlayfieldCoveringWrapper CreateCover(Drawable content) => new LegacyPlayfieldCover(content);
|
||||||
|
|
||||||
|
private partial class LegacyPlayfieldCover : PlayfieldCoveringWrapper
|
||||||
|
{
|
||||||
|
[Resolved]
|
||||||
|
private ISkinSource skin { get; set; } = null!;
|
||||||
|
|
||||||
|
private IBindable<float>? hitPosition;
|
||||||
|
|
||||||
|
public LegacyPlayfieldCover(Drawable content)
|
||||||
|
: base(content)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
skin.SourceChanged += onSkinChanged;
|
||||||
|
onSkinChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onSkinChanged()
|
||||||
|
{
|
||||||
|
hitPosition = skin.GetManiaSkinConfig<float>(LegacyManiaSkinConfigurationLookups.HitPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override float GetHeight(float coverage)
|
||||||
|
{
|
||||||
|
// In osu!stable, the cover is applied in absolute (x768) coordinates from the hit position.
|
||||||
|
float availablePlayfieldHeight = Math.Abs(reference_playfield_height - (hitPosition?.Value ?? Stage.HIT_TARGET_POSITION));
|
||||||
|
|
||||||
|
if (availablePlayfieldHeight == 0)
|
||||||
|
return base.GetHeight(coverage);
|
||||||
|
|
||||||
|
return base.GetHeight(coverage) * reference_playfield_height / availablePlayfieldHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,5 +11,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
public override string Name => "One Key";
|
public override string Name => "One Key";
|
||||||
public override string Acronym => "1K";
|
public override string Acronym => "1K";
|
||||||
public override LocalisableString Description => @"Play with one key.";
|
public override LocalisableString Description => @"Play with one key.";
|
||||||
|
public override bool Ranked => false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,5 +11,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
public override string Name => "Ten Keys";
|
public override string Name => "Ten Keys";
|
||||||
public override string Acronym => "10K";
|
public override string Acronym => "10K";
|
||||||
public override LocalisableString Description => @"Play with ten keys.";
|
public override LocalisableString Description => @"Play with ten keys.";
|
||||||
|
public override bool Ranked => false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,5 +11,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
public override string Name => "Two Keys";
|
public override string Name => "Two Keys";
|
||||||
public override string Acronym => "2K";
|
public override string Acronym => "2K";
|
||||||
public override LocalisableString Description => @"Play with two keys.";
|
public override LocalisableString Description => @"Play with two keys.";
|
||||||
|
public override bool Ranked => false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,5 +11,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
public override string Name => "Three Keys";
|
public override string Name => "Three Keys";
|
||||||
public override string Acronym => "3K";
|
public override string Acronym => "3K";
|
||||||
public override LocalisableString Description => @"Play with three keys.";
|
public override LocalisableString Description => @"Play with three keys.";
|
||||||
|
public override bool Ranked => false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
public class ManiaModMirror : ModMirror, IApplicableToBeatmap
|
public class ManiaModMirror : ModMirror, IApplicableToBeatmap
|
||||||
{
|
{
|
||||||
public override LocalisableString Description => "Notes are flipped horizontally.";
|
public override LocalisableString Description => "Notes are flipped horizontally.";
|
||||||
|
public override bool Ranked => UsesDefaultConfiguration;
|
||||||
|
|
||||||
public void ApplyToBeatmap(IBeatmap beatmap)
|
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||||
{
|
{
|
||||||
|
@ -6,7 +6,6 @@ using System.Linq;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Configuration;
|
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
@ -15,7 +14,7 @@ using osu.Game.Rulesets.UI;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public abstract class ManiaModPlayfieldCover : ModHidden, IApplicableToDrawableRuleset<ManiaHitObject>
|
public abstract class ManiaModWithPlayfieldCover : ModHidden, IApplicableToDrawableRuleset<ManiaHitObject>
|
||||||
{
|
{
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight<ManiaHitObject>) };
|
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight<ManiaHitObject>) };
|
||||||
|
|
||||||
@ -24,7 +23,9 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected abstract CoverExpandDirection ExpandDirection { get; }
|
protected abstract CoverExpandDirection ExpandDirection { get; }
|
||||||
|
|
||||||
[SettingSource("Coverage", "The proportion of playfield height that notes will be hidden for.")]
|
/// <summary>
|
||||||
|
/// The relative area that should be completely covered. This does not include the fade.
|
||||||
|
/// </summary>
|
||||||
public abstract BindableNumber<float> Coverage { get; }
|
public abstract BindableNumber<float> Coverage { get; }
|
||||||
|
|
||||||
public virtual void ApplyToDrawableRuleset(DrawableRuleset<ManiaHitObject> drawableRuleset)
|
public virtual void ApplyToDrawableRuleset(DrawableRuleset<ManiaHitObject> drawableRuleset)
|
||||||
@ -37,15 +38,17 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
Container hocParent = (Container)hoc.Parent!;
|
Container hocParent = (Container)hoc.Parent!;
|
||||||
|
|
||||||
hocParent.Remove(hoc, false);
|
hocParent.Remove(hoc, false);
|
||||||
hocParent.Add(new PlayfieldCoveringWrapper(hoc).With(c =>
|
hocParent.Add(CreateCover(hoc).With(c =>
|
||||||
{
|
{
|
||||||
c.RelativeSizeAxes = Axes.Both;
|
c.RelativeSizeAxes = Axes.Both;
|
||||||
c.Direction = ExpandDirection;
|
c.Direction = ExpandDirection;
|
||||||
c.Coverage = Coverage.Value;
|
c.Coverage.BindTo(Coverage);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected virtual PlayfieldCoveringWrapper CreateCover(Drawable content) => new PlayfieldCoveringWrapper(content);
|
||||||
|
|
||||||
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
||||||
{
|
{
|
||||||
}
|
}
|
@ -265,7 +265,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
if (Tail.AllJudged)
|
if (Tail.AllJudged)
|
||||||
{
|
{
|
||||||
if (Tail.IsHit)
|
if (Tail.IsHit)
|
||||||
ApplyResult(r => r.Type = r.Judgement.MaxResult);
|
ApplyMaxResult();
|
||||||
else
|
else
|
||||||
MissForcefully();
|
MissForcefully();
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
{
|
{
|
||||||
if (AllJudged) return;
|
if (AllJudged) return;
|
||||||
|
|
||||||
ApplyResult(r => r.Type = hit ? r.Judgement.MaxResult : r.Judgement.MinResult);
|
if (hit)
|
||||||
|
ApplyMaxResult();
|
||||||
|
else
|
||||||
|
ApplyMinResult();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Causes this <see cref="DrawableManiaHitObject"/> to get missed, disregarding all conditions in implementations of <see cref="DrawableHitObject.CheckForResult"/>.
|
/// Causes this <see cref="DrawableManiaHitObject"/> to get missed, disregarding all conditions in implementations of <see cref="DrawableHitObject.CheckForResult"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void MissForcefully() => ApplyResult(r => r.Type = r.Judgement.MinResult);
|
public virtual void MissForcefully() => ApplyMinResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract partial class DrawableManiaHitObject<TObject> : DrawableManiaHitObject
|
public abstract partial class DrawableManiaHitObject<TObject> : DrawableManiaHitObject
|
||||||
|
@ -89,18 +89,18 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
if (!userTriggered)
|
if (!userTriggered)
|
||||||
{
|
{
|
||||||
if (!HitObject.HitWindows.CanBeHit(timeOffset))
|
if (!HitObject.HitWindows.CanBeHit(timeOffset))
|
||||||
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
ApplyMinResult();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = HitObject.HitWindows.ResultFor(timeOffset);
|
var result = HitObject.HitWindows.ResultFor(timeOffset);
|
||||||
|
|
||||||
if (result == HitResult.None)
|
if (result == HitResult.None)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
result = GetCappedResult(result);
|
result = GetCappedResult(result);
|
||||||
|
ApplyResult(result);
|
||||||
ApplyResult(r => r.Type = result);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Mania.Scoring
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected override IEnumerable<HitObject> EnumerateHitObjects(IBeatmap beatmap)
|
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)
|
protected override double ComputeTotalScore(double comboProgress, double accuracyProgress, double bonusPortion)
|
||||||
{
|
{
|
||||||
|
@ -243,7 +243,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
|
|
||||||
bodySprite.FillMode = FillMode.Stretch;
|
bodySprite.FillMode = FillMode.Stretch;
|
||||||
// i dunno this looks about right??
|
// i dunno this looks about right??
|
||||||
bodySprite.Scale = new Vector2(1, scaleDirection * 32800 / sprite.DrawHeight);
|
// the guard against zero draw height is intended for zero-length hold notes. yes, such cases have been spotted in the wild.
|
||||||
|
if (sprite.DrawHeight > 0)
|
||||||
|
bodySprite.Scale = new Vector2(1, scaleDirection * 32800 / sprite.DrawHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio.Track;
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
|
using osu.Framework.Threading;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Input.Handlers;
|
using osu.Game.Input.Handlers;
|
||||||
@ -59,10 +59,9 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
// Stores the current speed adjustment active in gameplay.
|
// Stores the current speed adjustment active in gameplay.
|
||||||
private readonly Track speedAdjustmentTrack = new TrackVirtual(0);
|
private readonly Track speedAdjustmentTrack = new TrackVirtual(0);
|
||||||
|
|
||||||
[Resolved]
|
private ISkinSource currentSkin = null!;
|
||||||
private ISkinSource skin { get; set; }
|
|
||||||
|
|
||||||
public DrawableManiaRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods = null)
|
public DrawableManiaRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod>? mods = null)
|
||||||
: base(ruleset, beatmap, mods)
|
: base(ruleset, beatmap, mods)
|
||||||
{
|
{
|
||||||
BarLines = new BarLineGenerator<BarLine>(Beatmap).BarLines;
|
BarLines = new BarLineGenerator<BarLine>(Beatmap).BarLines;
|
||||||
@ -72,8 +71,12 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load(ISkinSource source)
|
||||||
{
|
{
|
||||||
|
currentSkin = source;
|
||||||
|
currentSkin.SourceChanged += onSkinChange;
|
||||||
|
skinChanged();
|
||||||
|
|
||||||
foreach (var mod in Mods.OfType<IApplicableToTrack>())
|
foreach (var mod in Mods.OfType<IApplicableToTrack>())
|
||||||
mod.ApplyToTrack(speedAdjustmentTrack);
|
mod.ApplyToTrack(speedAdjustmentTrack);
|
||||||
|
|
||||||
@ -109,12 +112,28 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
updateTimeRange();
|
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()
|
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;
|
const float length_to_default_hit_position = 768 - LegacyManiaSkinConfiguration.DEFAULT_HIT_POSITION;
|
||||||
float lengthToHitPosition = 768 - hitPosition;
|
float lengthToHitPosition = 768 - hitPosition;
|
||||||
|
|
||||||
@ -139,10 +158,18 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
protected override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant);
|
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 ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);
|
||||||
|
|
||||||
protected override ReplayRecorder CreateReplayRecorder(Score score) => new ManiaReplayRecorder(score);
|
protected override ReplayRecorder CreateReplayRecorder(Score score) => new ManiaReplayRecorder(score);
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
|
if (currentSkin.IsNotNull())
|
||||||
|
currentSkin.SourceChanged -= onSkinChange;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// 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 System.ComponentModel;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
@ -8,17 +10,24 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Colour;
|
using osu.Framework.Graphics.Colour;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
using Container = osu.Framework.Graphics.Containers.Container;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.UI
|
namespace osu.Game.Rulesets.Mania.UI
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A <see cref="Container"/> that has its contents partially hidden by an adjustable "cover". This is intended to be used in a playfield.
|
/// A <see cref="Framework.Graphics.Containers.Container"/> that has its contents partially hidden by an adjustable "cover". This is intended to be used in a playfield.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class PlayfieldCoveringWrapper : CompositeDrawable
|
public partial class PlayfieldCoveringWrapper : CompositeDrawable
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The relative area that should be completely covered. This does not include the fade.
|
||||||
|
/// </summary>
|
||||||
|
public readonly BindableFloat Coverage = new BindableFloat();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The complete cover, including gradient and fill.
|
/// The complete cover, including gradient and fill.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -36,6 +45,8 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
private readonly IBindable<ScrollingDirection> scrollDirection = new Bindable<ScrollingDirection>();
|
private readonly IBindable<ScrollingDirection> scrollDirection = new Bindable<ScrollingDirection>();
|
||||||
|
|
||||||
|
private float currentCoverageHeight;
|
||||||
|
|
||||||
public PlayfieldCoveringWrapper(Drawable content)
|
public PlayfieldCoveringWrapper(Drawable content)
|
||||||
{
|
{
|
||||||
InternalChild = new BufferedContainer
|
InternalChild = new BufferedContainer
|
||||||
@ -94,21 +105,46 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
scrollDirection.BindValueChanged(onScrollDirectionChanged, true);
|
scrollDirection.BindValueChanged(onScrollDirectionChanged, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
updateCoverSize(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Update()
|
||||||
|
{
|
||||||
|
base.Update();
|
||||||
|
updateCoverSize(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateCoverSize(bool instant)
|
||||||
|
{
|
||||||
|
float targetCoverage;
|
||||||
|
float targetAlpha;
|
||||||
|
|
||||||
|
if (instant)
|
||||||
|
{
|
||||||
|
targetCoverage = Coverage.Value;
|
||||||
|
targetAlpha = Coverage.Value > 0 ? 1 : 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
targetCoverage = (float)Interpolation.DampContinuously(currentCoverageHeight, Coverage.Value, 25, Math.Abs(Time.Elapsed));
|
||||||
|
targetAlpha = (float)Interpolation.DampContinuously(gradient.Alpha, Coverage.Value > 0 ? 1 : 0, 25, Math.Abs(Time.Elapsed));
|
||||||
|
}
|
||||||
|
|
||||||
|
filled.Height = GetHeight(targetCoverage);
|
||||||
|
gradient.Y = -GetHeight(targetCoverage);
|
||||||
|
gradient.Alpha = targetAlpha;
|
||||||
|
|
||||||
|
currentCoverageHeight = targetCoverage;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual float GetHeight(float coverage) => coverage;
|
||||||
|
|
||||||
private void onScrollDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
private void onScrollDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
||||||
=> cover.Rotation = direction.NewValue == ScrollingDirection.Up ? 0 : 180f;
|
=> cover.Rotation = direction.NewValue == ScrollingDirection.Up ? 0 : 180f;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The relative area that should be completely covered. This does not include the fade.
|
|
||||||
/// </summary>
|
|
||||||
public float Coverage
|
|
||||||
{
|
|
||||||
set
|
|
||||||
{
|
|
||||||
filled.Height = value;
|
|
||||||
gradient.Y = -value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The direction in which the cover expands.
|
/// The direction in which the cover expands.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -123,11 +159,13 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The cover expands along the scrolling direction.
|
/// The cover expands along the scrolling direction.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Description("Along scroll")]
|
||||||
AlongScroll,
|
AlongScroll,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The cover expands against the scrolling direction.
|
/// The cover expands against the scrolling direction.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Description("Against scroll")]
|
||||||
AgainstScroll
|
AgainstScroll
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.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>
|
||||||
|
@ -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="33" />
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="34" />
|
||||||
<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>
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<Import Project="..\osu.Android.props" />
|
<Import Project="..\osu.Android.props" />
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0-android</TargetFramework>
|
<TargetFramework>net8.0-android</TargetFramework>
|
||||||
<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.Android</AssemblyName>
|
<AssemblyName>osu.Game.Rulesets.Osu.Tests.Android</AssemblyName>
|
||||||
@ -24,4 +24,4 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Moq" Version="4.17.2" />
|
<PackageReference Include="Moq" Version="4.17.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net6.0-ios</TargetFramework>
|
<TargetFramework>net8.0-ios</TargetFramework>
|
||||||
<SupportedOSPlatformVersion>13.4</SupportedOSPlatformVersion>
|
<SupportedOSPlatformVersion>13.4</SupportedOSPlatformVersion>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<RootNamespace>osu.Game.Rulesets.Osu.Tests</RootNamespace>
|
<RootNamespace>osu.Game.Rulesets.Osu.Tests</RootNamespace>
|
||||||
|
@ -124,6 +124,113 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
AddAssert("selection preserved", () => EditorBeatmap.SelectedHitObjects.Count == 2);
|
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
|
private ComposeBlueprintContainer blueprintContainer
|
||||||
=> Editor.ChildrenOfType<ComposeBlueprintContainer>().First();
|
=> Editor.ChildrenOfType<ComposeBlueprintContainer>().First();
|
||||||
|
|
||||||
|
@ -206,7 +206,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
{
|
{
|
||||||
AddStep($"click context menu item \"{contextMenuText}\"", () =>
|
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();
|
item?.Action.Value?.Invoke();
|
||||||
});
|
});
|
||||||
|
@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = 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);
|
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)
|
protected override bool OnMouseDown(MouseDownEvent e)
|
||||||
{
|
{
|
||||||
accuracyHeatmap.AddPoint(object2.Position, object1.Position, background.ToLocalSpace(e.ScreenSpaceMouseDownPosition), 50);
|
accuracyHeatmap.AddPoint(object2.Position, object1.Position, background.ToLocalSpace(e.ScreenSpaceMouseDownPosition), 50);
|
||||||
|
@ -183,7 +183,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
hitObjectContainer.Add(drawableObject);
|
hitObjectContainer.Add(drawableObject!);
|
||||||
followPointRenderer.AddFollowPoints(objects[i]);
|
followPointRenderer.AddFollowPoints(objects[i]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -136,7 +136,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
if (auto && !userTriggered && timeOffset > hitOffset && CheckHittable?.Invoke(this, Time.Current, HitResult.Great) == ClickAction.Hit)
|
if (auto && !userTriggered && timeOffset > hitOffset && CheckHittable?.Invoke(this, Time.Current, HitResult.Great) == ClickAction.Hit)
|
||||||
{
|
{
|
||||||
// force success
|
// force success
|
||||||
ApplyResult(r => r.Type = HitResult.Great);
|
ApplyResult(HitResult.Great);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
base.CheckForResult(userTriggered, timeOffset);
|
base.CheckForResult(userTriggered, timeOffset);
|
||||||
|
@ -208,7 +208,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
if (shouldHit && !userTriggered && timeOffset >= 0)
|
if (shouldHit && !userTriggered && timeOffset >= 0)
|
||||||
{
|
{
|
||||||
// force success
|
// force success
|
||||||
ApplyResult(r => r.Type = HitResult.Great);
|
ApplyResult(HitResult.Great);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
base.CheckForResult(userTriggered, timeOffset);
|
base.CheckForResult(userTriggered, timeOffset);
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
@ -173,6 +174,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
public IEnumerable<ISkin> AllSources => new[] { this };
|
public IEnumerable<ISkin> AllSources => new[] { this };
|
||||||
|
|
||||||
|
[CanBeNull]
|
||||||
public event Action SourceChanged;
|
public event Action SourceChanged;
|
||||||
|
|
||||||
private bool enabled = true;
|
private bool enabled = true;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup Label="Project References">
|
<ItemGroup Label="Project References">
|
||||||
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />
|
<ProjectReference Include="..\osu.Game.Rulesets.Osu\osu.Game.Rulesets.Osu.csproj" />
|
||||||
|
@ -40,7 +40,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
double aimRatingNoSliders = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
|
double aimRatingNoSliders = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
|
||||||
double speedRating = Math.Sqrt(skills[2].DifficultyValue()) * difficulty_multiplier;
|
double speedRating = Math.Sqrt(skills[2].DifficultyValue()) * difficulty_multiplier;
|
||||||
double speedNotes = ((Speed)skills[2]).RelevantNoteCount();
|
double speedNotes = ((Speed)skills[2]).RelevantNoteCount();
|
||||||
double flashlightRating = Math.Sqrt(skills[3].DifficultyValue()) * difficulty_multiplier;
|
|
||||||
|
double flashlightRating = 0.0;
|
||||||
|
|
||||||
|
if (mods.Any(h => h is OsuModFlashlight))
|
||||||
|
flashlightRating = Math.Sqrt(skills[3].DifficultyValue()) * difficulty_multiplier;
|
||||||
|
|
||||||
double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1;
|
double sliderFactor = aimRating > 0 ? aimRatingNoSliders / aimRating : 1;
|
||||||
|
|
||||||
@ -126,13 +130,17 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
|
|
||||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate)
|
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, true),
|
||||||
new Aim(mods, false),
|
new Aim(mods, false),
|
||||||
new Speed(mods),
|
new Speed(mods)
|
||||||
new Flashlight(mods)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (mods.Any(h => h is OsuModFlashlight))
|
||||||
|
skills.Add(new Flashlight(mods));
|
||||||
|
|
||||||
|
return skills.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
|
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
|
||||||
|
@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
|||||||
// These sections will not contribute to the difficulty.
|
// These sections will not contribute to the difficulty.
|
||||||
var peaks = GetCurrentStrainPeaks().Where(p => p > 0);
|
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
|
// We are reducing the highest strains first to account for extreme difficulty spikes
|
||||||
for (int i = 0; i < Math.Min(strains.Count, ReducedSectionCount); i++)
|
for (int i = 0; i < Math.Min(strains.Count, ReducedSectionCount); i++)
|
||||||
@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
|||||||
|
|
||||||
// Difficulty is the weighted sum of the highest strains from every section.
|
// Difficulty is the weighted sum of the highest strains from every section.
|
||||||
// We're sorting from highest to lowest strain.
|
// 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;
|
difficulty += strain * weight;
|
||||||
weight *= DecayWeight;
|
weight *= DecayWeight;
|
||||||
|
@ -171,7 +171,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
return false; // Allow right click to be handled by context menu
|
return false; // Allow right click to be handled by context menu
|
||||||
|
|
||||||
case MouseButton.Left:
|
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();
|
changeHandler?.BeginChange();
|
||||||
placementControlPoint = addControlPoint(e.MousePosition);
|
placementControlPoint = addControlPoint(e.MousePosition);
|
||||||
|
@ -31,6 +31,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
|
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModFlashlight) };
|
public override Type[] IncompatibleMods => new[] { typeof(OsuModFlashlight) };
|
||||||
|
public override bool Ranked => true;
|
||||||
|
|
||||||
private DrawableOsuBlinds blinds = null!;
|
private DrawableOsuBlinds blinds = null!;
|
||||||
|
|
||||||
|
@ -75,8 +75,10 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
double time = playfield.Time.Current;
|
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)
|
switch (drawable)
|
||||||
{
|
{
|
||||||
case DrawableHitCircle circle:
|
case DrawableHitCircle circle:
|
||||||
|
@ -49,8 +49,10 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
var cursorPos = playfield.Cursor.AsNonNull().ActiveCursor.DrawPosition;
|
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)
|
switch (drawable)
|
||||||
{
|
{
|
||||||
case DrawableHitCircle circle:
|
case DrawableHitCircle circle:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// 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 System;
|
||||||
@ -38,12 +38,18 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
private ReplayState<OsuAction> state = null!;
|
private ReplayState<OsuAction> state = null!;
|
||||||
private double lastStateChangeTime;
|
private double lastStateChangeTime;
|
||||||
|
|
||||||
|
private DrawableOsuRuleset ruleset = null!;
|
||||||
|
private IPressHandler pressHandler = null!;
|
||||||
|
|
||||||
private bool hasReplay;
|
private bool hasReplay;
|
||||||
|
private bool legacyReplay;
|
||||||
|
|
||||||
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
||||||
{
|
{
|
||||||
|
ruleset = (DrawableOsuRuleset)drawableRuleset;
|
||||||
|
|
||||||
// grab the input manager for future use.
|
// grab the input manager for future use.
|
||||||
osuInputManager = ((DrawableOsuRuleset)drawableRuleset).KeyBindingInputManager;
|
osuInputManager = ruleset.KeyBindingInputManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ApplyToPlayer(Player player)
|
public void ApplyToPlayer(Player player)
|
||||||
@ -51,15 +57,22 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
if (osuInputManager.ReplayInputHandler != null)
|
if (osuInputManager.ReplayInputHandler != null)
|
||||||
{
|
{
|
||||||
hasReplay = true;
|
hasReplay = true;
|
||||||
|
|
||||||
|
Debug.Assert(ruleset.ReplayScore != null);
|
||||||
|
legacyReplay = ruleset.ReplayScore.ScoreInfo.IsLegacyScore;
|
||||||
|
|
||||||
|
pressHandler = legacyReplay ? new LegacyReplayPressHandler(this) : new PressHandler(this);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pressHandler = new PressHandler(this);
|
||||||
osuInputManager.AllowGameplayInputs = false;
|
osuInputManager.AllowGameplayInputs = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update(Playfield playfield)
|
public void Update(Playfield playfield)
|
||||||
{
|
{
|
||||||
if (hasReplay)
|
if (hasReplay && !legacyReplay)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool requiresHold = false;
|
bool requiresHold = false;
|
||||||
@ -88,7 +101,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
if (!slider.HeadCircle.IsHit)
|
if (!slider.HeadCircle.IsHit)
|
||||||
handleHitCircle(slider.HeadCircle);
|
handleHitCircle(slider.HeadCircle);
|
||||||
|
|
||||||
requiresHold |= slider.SliderInputManager.IsMouseInFollowArea(true);
|
requiresHold |= slider.SliderInputManager.IsMouseInFollowArea(slider.Tracking.Value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DrawableSpinner spinner:
|
case DrawableSpinner spinner:
|
||||||
@ -132,11 +145,62 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
if (down)
|
if (down)
|
||||||
{
|
{
|
||||||
state.PressedActions.Add(wasLeft ? OsuAction.LeftButton : OsuAction.RightButton);
|
pressHandler.HandlePress(wasLeft);
|
||||||
wasLeft = !wasLeft;
|
wasLeft = !wasLeft;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pressHandler.HandleRelease(wasLeft);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
state.Apply(osuInputManager.CurrentState, osuInputManager);
|
private interface IPressHandler
|
||||||
|
{
|
||||||
|
void HandlePress(bool wasLeft);
|
||||||
|
void HandleRelease(bool wasLeft);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PressHandler : IPressHandler
|
||||||
|
{
|
||||||
|
private readonly OsuModRelax mod;
|
||||||
|
|
||||||
|
public PressHandler(OsuModRelax mod)
|
||||||
|
{
|
||||||
|
this.mod = mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HandlePress(bool wasLeft)
|
||||||
|
{
|
||||||
|
mod.state.PressedActions.Add(wasLeft ? OsuAction.LeftButton : OsuAction.RightButton);
|
||||||
|
mod.state.Apply(mod.osuInputManager.CurrentState, mod.osuInputManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HandleRelease(bool wasLeft)
|
||||||
|
{
|
||||||
|
mod.state.Apply(mod.osuInputManager.CurrentState, mod.osuInputManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// legacy replays do not contain key-presses with Relax mod, so they need to be triggered by themselves.
|
||||||
|
private class LegacyReplayPressHandler : IPressHandler
|
||||||
|
{
|
||||||
|
private readonly OsuModRelax mod;
|
||||||
|
|
||||||
|
public LegacyReplayPressHandler(OsuModRelax mod)
|
||||||
|
{
|
||||||
|
this.mod = mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HandlePress(bool wasLeft)
|
||||||
|
{
|
||||||
|
mod.osuInputManager.KeyBindingContainer.TriggerPressed(wasLeft ? OsuAction.LeftButton : OsuAction.RightButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HandleRelease(bool wasLeft)
|
||||||
|
{
|
||||||
|
// this intentionally releases right when `wasLeft` is true because `wasLeft` is set at point of press and not at point of release
|
||||||
|
mod.osuInputManager.KeyBindingContainer.TriggerReleased(wasLeft ? OsuAction.RightButton : OsuAction.LeftButton);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,8 +48,10 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
var cursorPos = playfield.Cursor.AsNonNull().ActiveCursor.DrawPosition;
|
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);
|
var destination = Vector2.Clamp(2 * drawable.Position - cursorPos, Vector2.Zero, OsuPlayfield.BASE_SIZE);
|
||||||
|
|
||||||
if (drawable.HitObject is Slider thisSlider)
|
if (drawable.HitObject is Slider thisSlider)
|
||||||
|
@ -22,6 +22,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public override LocalisableString Description => @"Spinners will be automatically completed.";
|
public override LocalisableString Description => @"Spinners will be automatically completed.";
|
||||||
public override double ScoreMultiplier => 0.9;
|
public override double ScoreMultiplier => 0.9;
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot), typeof(OsuModTargetPractice) };
|
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot), typeof(OsuModTargetPractice) };
|
||||||
|
public override bool Ranked => UsesDefaultConfiguration;
|
||||||
|
|
||||||
public void ApplyToDrawableHitObject(DrawableHitObject hitObject)
|
public void ApplyToDrawableHitObject(DrawableHitObject hitObject)
|
||||||
{
|
{
|
||||||
|
@ -10,5 +10,6 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public class OsuModTouchDevice : ModTouchDevice
|
public class OsuModTouchDevice : ModTouchDevice
|
||||||
{
|
{
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray();
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray();
|
||||||
|
public override bool Ranked => UsesDefaultConfiguration;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -155,7 +155,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
if (!userTriggered)
|
if (!userTriggered)
|
||||||
{
|
{
|
||||||
if (!HitObject.HitWindows.CanBeHit(timeOffset))
|
if (!HitObject.HitWindows.CanBeHit(timeOffset))
|
||||||
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
ApplyMinResult();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -169,19 +169,22 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
if (result == HitResult.None || clickAction != ClickAction.Hit)
|
if (result == HitResult.None || clickAction != ClickAction.Hit)
|
||||||
return;
|
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;
|
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.
|
circleResult.Type = state.result;
|
||||||
if (result.IsHit())
|
circleResult.CursorPositionAtHit = state.position;
|
||||||
{
|
}, (result, hitPosition));
|
||||||
var localMousePosition = ToLocalSpace(inputManager.CurrentState.Mouse.Position);
|
|
||||||
circleResult.CursorPositionAtHit = HitObject.StackedPosition + (localMousePosition - DrawSize / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
circleResult.Type = result;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user