mirror of
https://github.com/ppy/osu.git
synced 2026-06-08 10:53:52 +08:00
Compare commits
7 Commits
@@ -0,0 +1,8 @@
|
||||
Add any details pertaining to developers above the break.
|
||||
|
||||
- [ ] Depends on #PR
|
||||
- Closes #ISSUE
|
||||
|
||||
---
|
||||
|
||||
Add a sentence or two describing this change in plain english. This will be displayed on the [changelog](https://osu.ppy.sh/home/changelog). A single screenshot or short gif is also welcomed.
|
||||
+2
-4
@@ -11,10 +11,8 @@
|
||||
*.userprefs
|
||||
|
||||
### Cake ###
|
||||
tools/**
|
||||
build/tools/**
|
||||
|
||||
fastlane/report.xml
|
||||
tools/*
|
||||
!tools/cakebuild.csproj
|
||||
|
||||
# Build results
|
||||
bin/[Dd]ebug/
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "osu-resources"]
|
||||
path = osu-resources
|
||||
url = https://github.com/ppy/osu-resources
|
||||
Vendored
-14
@@ -68,20 +68,6 @@
|
||||
}
|
||||
},
|
||||
"console": "internalConsole"
|
||||
},
|
||||
{
|
||||
"name": "Cake: Debug Script",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"program": "${workspaceRoot}/build/tools/Cake.CoreCLR/0.30.0/Cake.dll",
|
||||
"args": [
|
||||
"${workspaceRoot}/build/build.cake",
|
||||
"--debug",
|
||||
"--verbosity=diagnostic"
|
||||
],
|
||||
"cwd": "${workspaceRoot}/build",
|
||||
"stopAtEntry": true,
|
||||
"externalConsole": false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Vendored
+1
-2
@@ -70,8 +70,7 @@
|
||||
"type": "shell",
|
||||
"command": "dotnet",
|
||||
"args": [
|
||||
"restore",
|
||||
"osu.sln"
|
||||
"restore"
|
||||
],
|
||||
"problemMatcher": []
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "fastlane"
|
||||
|
||||
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
|
||||
eval_gemfile(plugins_path) if File.exist?(plugins_path)
|
||||
-173
@@ -1,173 +0,0 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
CFPropertyList (3.0.0)
|
||||
addressable (2.6.0)
|
||||
public_suffix (>= 2.0.2, < 4.0)
|
||||
atomos (0.1.3)
|
||||
babosa (1.0.2)
|
||||
claide (1.0.2)
|
||||
colored (1.2)
|
||||
colored2 (3.1.2)
|
||||
commander-fastlane (4.4.6)
|
||||
highline (~> 1.7.2)
|
||||
declarative (0.0.10)
|
||||
declarative-option (0.1.0)
|
||||
digest-crc (0.4.1)
|
||||
domain_name (0.5.20180417)
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
dotenv (2.7.1)
|
||||
emoji_regex (1.0.1)
|
||||
excon (0.62.0)
|
||||
faraday (0.15.4)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
faraday-cookie_jar (0.0.6)
|
||||
faraday (>= 0.7.4)
|
||||
http-cookie (~> 1.0.0)
|
||||
faraday_middleware (0.13.1)
|
||||
faraday (>= 0.7.4, < 1.0)
|
||||
fastimage (2.1.5)
|
||||
fastlane (2.117.0)
|
||||
CFPropertyList (>= 2.3, < 4.0.0)
|
||||
addressable (>= 2.3, < 3.0.0)
|
||||
babosa (>= 1.0.2, < 2.0.0)
|
||||
bundler (>= 1.12.0, < 3.0.0)
|
||||
colored
|
||||
commander-fastlane (>= 4.4.6, < 5.0.0)
|
||||
dotenv (>= 2.1.1, < 3.0.0)
|
||||
emoji_regex (>= 0.1, < 2.0)
|
||||
excon (>= 0.45.0, < 1.0.0)
|
||||
faraday (~> 0.9)
|
||||
faraday-cookie_jar (~> 0.0.6)
|
||||
faraday_middleware (~> 0.9)
|
||||
fastimage (>= 2.1.0, < 3.0.0)
|
||||
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||
google-api-client (>= 0.21.2, < 0.24.0)
|
||||
google-cloud-storage (>= 1.15.0, < 2.0.0)
|
||||
highline (>= 1.7.2, < 2.0.0)
|
||||
json (< 3.0.0)
|
||||
mini_magick (~> 4.5.1)
|
||||
multi_json
|
||||
multi_xml (~> 0.5)
|
||||
multipart-post (~> 2.0.0)
|
||||
plist (>= 3.1.0, < 4.0.0)
|
||||
public_suffix (~> 2.0.0)
|
||||
rubyzip (>= 1.2.2, < 2.0.0)
|
||||
security (= 0.1.3)
|
||||
simctl (~> 1.6.3)
|
||||
slack-notifier (>= 2.0.0, < 3.0.0)
|
||||
terminal-notifier (>= 1.6.2, < 2.0.0)
|
||||
terminal-table (>= 1.4.5, < 2.0.0)
|
||||
tty-screen (>= 0.6.3, < 1.0.0)
|
||||
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||
word_wrap (~> 1.0.0)
|
||||
xcodeproj (>= 1.6.0, < 2.0.0)
|
||||
xcpretty (~> 0.3.0)
|
||||
xcpretty-travis-formatter (>= 0.0.3)
|
||||
fastlane-plugin-clean_testflight_testers (0.2.0)
|
||||
fastlane-plugin-souyuz (0.8.1)
|
||||
souyuz (>= 0.8.1)
|
||||
fastlane-plugin-xamarin (0.6.3)
|
||||
gh_inspector (1.1.3)
|
||||
google-api-client (0.23.9)
|
||||
addressable (~> 2.5, >= 2.5.1)
|
||||
googleauth (>= 0.5, < 0.7.0)
|
||||
httpclient (>= 2.8.1, < 3.0)
|
||||
mime-types (~> 3.0)
|
||||
representable (~> 3.0)
|
||||
retriable (>= 2.0, < 4.0)
|
||||
signet (~> 0.9)
|
||||
google-cloud-core (1.3.0)
|
||||
google-cloud-env (~> 1.0)
|
||||
google-cloud-env (1.0.5)
|
||||
faraday (~> 0.11)
|
||||
google-cloud-storage (1.16.0)
|
||||
digest-crc (~> 0.4)
|
||||
google-api-client (~> 0.23)
|
||||
google-cloud-core (~> 1.2)
|
||||
googleauth (>= 0.6.2, < 0.10.0)
|
||||
googleauth (0.6.7)
|
||||
faraday (~> 0.12)
|
||||
jwt (>= 1.4, < 3.0)
|
||||
memoist (~> 0.16)
|
||||
multi_json (~> 1.11)
|
||||
os (>= 0.9, < 2.0)
|
||||
signet (~> 0.7)
|
||||
highline (1.7.10)
|
||||
http-cookie (1.0.3)
|
||||
domain_name (~> 0.5)
|
||||
httpclient (2.8.3)
|
||||
json (2.2.0)
|
||||
jwt (2.1.0)
|
||||
memoist (0.16.0)
|
||||
mime-types (3.2.2)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2018.0812)
|
||||
mini_magick (4.5.1)
|
||||
mini_portile2 (2.4.0)
|
||||
multi_json (1.13.1)
|
||||
multi_xml (0.6.0)
|
||||
multipart-post (2.0.0)
|
||||
nanaimo (0.2.6)
|
||||
naturally (2.2.0)
|
||||
nokogiri (1.10.1)
|
||||
mini_portile2 (~> 2.4.0)
|
||||
os (1.0.0)
|
||||
plist (3.5.0)
|
||||
public_suffix (2.0.5)
|
||||
representable (3.0.4)
|
||||
declarative (< 0.1.0)
|
||||
declarative-option (< 0.2.0)
|
||||
uber (< 0.2.0)
|
||||
retriable (3.1.2)
|
||||
rouge (2.0.7)
|
||||
rubyzip (1.2.2)
|
||||
security (0.1.3)
|
||||
signet (0.11.0)
|
||||
addressable (~> 2.3)
|
||||
faraday (~> 0.9)
|
||||
jwt (>= 1.5, < 3.0)
|
||||
multi_json (~> 1.10)
|
||||
simctl (1.6.5)
|
||||
CFPropertyList
|
||||
naturally
|
||||
slack-notifier (2.3.2)
|
||||
souyuz (0.8.1)
|
||||
fastlane (>= 2.29.0)
|
||||
highline (~> 1.7)
|
||||
nokogiri (~> 1.7)
|
||||
terminal-notifier (1.8.0)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
tty-cursor (0.6.1)
|
||||
tty-screen (0.6.5)
|
||||
tty-spinner (0.9.0)
|
||||
tty-cursor (~> 0.6.0)
|
||||
uber (0.1.0)
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.5)
|
||||
unicode-display_width (1.4.1)
|
||||
word_wrap (1.0.0)
|
||||
xcodeproj (1.8.1)
|
||||
CFPropertyList (>= 2.3.3, < 4.0)
|
||||
atomos (~> 0.1.3)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
colored2 (~> 3.1)
|
||||
nanaimo (~> 0.2.6)
|
||||
xcpretty (0.3.0)
|
||||
rouge (~> 2.0.7)
|
||||
xcpretty-travis-formatter (1.0.0)
|
||||
xcpretty (~> 0.2, >= 0.0.7)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
fastlane
|
||||
fastlane-plugin-clean_testflight_testers
|
||||
fastlane-plugin-souyuz
|
||||
fastlane-plugin-xamarin
|
||||
|
||||
BUNDLED WITH
|
||||
2.0.1
|
||||
@@ -31,14 +31,18 @@ If your platform is not listed above, there is still a chance you can manually b
|
||||
Clone the repository **including submodules**:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/ppy/osu
|
||||
git clone --recurse-submodules https://github.com/ppy/osu
|
||||
cd osu
|
||||
```
|
||||
|
||||
> If you forgot the `--recurse-submodules` option, run this command inside the `osu` directory:
|
||||
>
|
||||
> `git submodule update --init --recursive`
|
||||
|
||||
To update the source code to the latest commit, run the following command inside the `osu` directory:
|
||||
|
||||
```shell
|
||||
git pull
|
||||
git pull --recurse-submodules
|
||||
```
|
||||
|
||||
## Building
|
||||
@@ -69,13 +73,9 @@ For example, you can run osu! with the following command:
|
||||
LD_LIBRARY_PATH="$(pwd)/osu.Desktop/bin/Debug/netcoreapp2.2" dotnet run --project osu.Desktop
|
||||
```
|
||||
|
||||
## Testing with resource/framework modifications
|
||||
|
||||
Sometimes it may be necessary to cross-test changes in [osu-resources](https://github.com/ppy/osu-resources) or [osu-framework](https://github.com/ppy/osu-framework). This can be achieved by running some commands as documented on the [osu-resources](https://github.com/ppy/osu-resources/wiki/Testing-local-resources-checkout-with-other-projects) and [osu-framework](https://github.com/ppy/osu-framework/wiki/Testing-local-framework-checkout-with-other-projects) wiki pages.
|
||||
|
||||
## Code analysis
|
||||
|
||||
Code analysis can be run with `powershell ./build.ps1` or `build.sh`. This is currently only supported under windows due to [resharper cli shortcomings](https://youtrack.jetbrains.com/issue/RSRP-410004). Alternatively, you can install resharper or use rider to get inline support in your IDE of choice.
|
||||
Code analysis can be run with `powershell ./build.ps1` or `build.sh`. This is currently only supported under windows due to [resharper cli shortcomings](https://youtrack.jetbrains.com/issue/RSRP-410004). Alternative, you can install resharper or use rider to get inline support in your IDE of choice.
|
||||
|
||||
# Contributing
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#addin "nuget:?package=CodeFileSanity&version=0.0.21"
|
||||
#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2018.2.2"
|
||||
#tool "nuget:?package=NVika.MSBuild&version=1.0.1"
|
||||
var nVikaToolPath = GetFiles("./tools/NVika.MSBuild.*/tools/NVika.exe").First();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// ARGUMENTS
|
||||
@@ -10,24 +9,30 @@ var nVikaToolPath = GetFiles("./tools/NVika.MSBuild.*/tools/NVika.exe").First();
|
||||
var target = Argument("target", "Build");
|
||||
var configuration = Argument("configuration", "Release");
|
||||
|
||||
var rootDirectory = new DirectoryPath("..");
|
||||
var solution = rootDirectory.CombineWithFilePath("osu.sln");
|
||||
var osuSolution = new FilePath("./osu.sln");
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// TASKS
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Task("Compile")
|
||||
Task("Restore")
|
||||
.Does(() => {
|
||||
DotNetCoreBuild(solution.FullPath, new DotNetCoreBuildSettings {
|
||||
DotNetCoreRestore(osuSolution.FullPath);
|
||||
});
|
||||
|
||||
Task("Compile")
|
||||
.IsDependentOn("Restore")
|
||||
.Does(() => {
|
||||
DotNetCoreBuild(osuSolution.FullPath, new DotNetCoreBuildSettings {
|
||||
Configuration = configuration,
|
||||
NoRestore = true,
|
||||
});
|
||||
});
|
||||
|
||||
Task("Test")
|
||||
.IsDependentOn("Compile")
|
||||
.Does(() => {
|
||||
var testAssemblies = GetFiles(rootDirectory + "/**/*.Tests/bin/**/*.Tests.dll");
|
||||
var testAssemblies = GetFiles("**/*.Tests/bin/**/*.Tests.dll");
|
||||
|
||||
DotNetCoreVSTest(testAssemblies, new DotNetCoreVSTestSettings {
|
||||
Logger = AppVeyor.IsRunningOnAppVeyor ? "Appveyor" : $"trx",
|
||||
@@ -41,7 +46,9 @@ Task("InspectCode")
|
||||
.WithCriteria(IsRunningOnWindows())
|
||||
.IsDependentOn("Compile")
|
||||
.Does(() => {
|
||||
InspectCode(solution, new InspectCodeSettings {
|
||||
var nVikaToolPath = GetFiles("./tools/NVika.MSBuild.*/tools/NVika.exe").First();
|
||||
|
||||
InspectCode(osuSolution, new InspectCodeSettings {
|
||||
CachesHome = "inspectcode",
|
||||
OutputFile = "inspectcodereport.xml",
|
||||
});
|
||||
@@ -52,7 +59,7 @@ Task("InspectCode")
|
||||
Task("CodeFileSanity")
|
||||
.Does(() => {
|
||||
ValidateCodeSanity(new ValidateCodeSanitySettings {
|
||||
RootDirectory = rootDirectory.FullPath,
|
||||
RootDirectory = ".",
|
||||
IsAppveyorBuild = AppVeyor.IsRunningOnAppVeyor
|
||||
});
|
||||
});
|
||||
@@ -41,28 +41,27 @@ Param(
|
||||
[switch]$ShowDescription,
|
||||
[Alias("WhatIf", "Noop")]
|
||||
[switch]$DryRun,
|
||||
[Parameter(Position = 0, Mandatory = $false, ValueFromRemainingArguments = $true)]
|
||||
[Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
|
||||
[string[]]$ScriptArgs
|
||||
)
|
||||
|
||||
Write-Host "Preparing to run build script..."
|
||||
|
||||
# Determine the script root for resolving other paths.
|
||||
if(!$PSScriptRoot) {
|
||||
if(!$PSScriptRoot){
|
||||
$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
|
||||
}
|
||||
|
||||
# Resolve the paths for resources used for debugging.
|
||||
$BUILD_DIR = Join-Path $PSScriptRoot "build"
|
||||
$TOOLS_DIR = Join-Path $BUILD_DIR "tools"
|
||||
$CAKE_CSPROJ = Join-Path $BUILD_DIR "cakebuild.csproj"
|
||||
$TOOLS_DIR = Join-Path $PSScriptRoot "tools"
|
||||
$CAKE_CSPROJ = Join-Path $TOOLS_DIR "cakebuild.csproj"
|
||||
|
||||
# Install the required tools locally.
|
||||
Write-Host "Restoring cake tools..."
|
||||
Invoke-Expression "dotnet restore `"$CAKE_CSPROJ`" --packages `"$TOOLS_DIR`"" | Out-Null
|
||||
|
||||
# Find the Cake executable
|
||||
$CAKE_EXECUTABLE = (Get-ChildItem -Path "$TOOLS_DIR/cake.coreclr/" -Filter Cake.dll -Recurse).FullName
|
||||
$CAKE_EXECUTABLE = (Get-ChildItem -Path ./tools/cake.coreclr/ -Filter Cake.dll -Recurse).FullName
|
||||
|
||||
# Build Cake arguments
|
||||
$cakeArguments = @("$Script");
|
||||
@@ -76,7 +75,5 @@ $cakeArguments += $ScriptArgs
|
||||
|
||||
# Start Cake
|
||||
Write-Host "Running build script..."
|
||||
Push-Location -Path $BUILD_DIR
|
||||
Invoke-Expression "dotnet `"$CAKE_EXECUTABLE`" $cakeArguments"
|
||||
Pop-Location
|
||||
exit $LASTEXITCODE
|
||||
|
||||
@@ -6,13 +6,12 @@
|
||||
|
||||
echo "Preparing to run build script..."
|
||||
|
||||
cd build
|
||||
SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||
TOOLS_DIR=$SCRIPT_DIR/tools
|
||||
CAKE_BINARY_PATH=$TOOLS_DIR/"cake.coreclr"
|
||||
|
||||
SCRIPT="build.cake"
|
||||
CAKE_CSPROJ=$SCRIPT_DIR/"cakebuild.csproj"
|
||||
CAKE_CSPROJ=$TOOLS_DIR/"cakebuild.csproj"
|
||||
|
||||
# Parse arguments.
|
||||
CAKE_ARGUMENTS=()
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
app_identifier("sh.ppy.osulazer") # The bundle identifier of your app
|
||||
apple_id("apple-dev@ppy.sh") # Your Apple email address
|
||||
@@ -1,65 +0,0 @@
|
||||
update_fastlane
|
||||
|
||||
default_platform(:ios)
|
||||
|
||||
platform :ios do
|
||||
lane :testflight_prune_dry do
|
||||
clean_testflight_testers(days_of_inactivity:45, dry_run: true)
|
||||
end
|
||||
|
||||
# Specify a custom number for what's "inactive"
|
||||
lane :testflight_prune do
|
||||
clean_testflight_testers(days_of_inactivity: 45) # 120 days, so about 4 months
|
||||
end
|
||||
|
||||
lane :update_version do |options|
|
||||
options[:plist_path] = '../osu.iOS/Info.plist'
|
||||
app_version(options)
|
||||
end
|
||||
|
||||
desc 'Deploy to testflight'
|
||||
lane :beta do |options|
|
||||
update_version(options)
|
||||
|
||||
provision(
|
||||
type: 'appstore'
|
||||
)
|
||||
|
||||
build(
|
||||
build_configuration: 'Release',
|
||||
build_platform: 'iPhone'
|
||||
)
|
||||
|
||||
client = HTTPClient.new
|
||||
changelog = client.get_content 'https://gist.githubusercontent.com/peppy/ab89c29dcc0dce95f39eb218e8fad197/raw'
|
||||
changelog.gsub!('$BUILD_ID', options[:build])
|
||||
|
||||
pilot(
|
||||
wait_processing_interval: 900,
|
||||
changelog: changelog,
|
||||
ipa: './osu.iOS/bin/iPhone/Release/osu.iOS.ipa'
|
||||
)
|
||||
end
|
||||
|
||||
desc 'Compile the project'
|
||||
lane :build do
|
||||
nuget_restore(
|
||||
project_path: 'osu.iOS.sln'
|
||||
)
|
||||
|
||||
souyuz(
|
||||
platform: "ios",
|
||||
build_target: "osu_iOS",
|
||||
plist_path: "../osu.iOS/Info.plist"
|
||||
)
|
||||
end
|
||||
|
||||
desc 'Install provisioning profiles using match'
|
||||
lane :provision do |options|
|
||||
if Helper.is_ci?
|
||||
options[:readonly] = true
|
||||
end
|
||||
|
||||
match(options)
|
||||
end
|
||||
end
|
||||
@@ -1 +0,0 @@
|
||||
git_url('https://github.com/peppy/apple-certificates')
|
||||
@@ -1,7 +0,0 @@
|
||||
# Autogenerated by fastlane
|
||||
#
|
||||
# Ensure this file is checked in to source control!
|
||||
|
||||
gem 'fastlane-plugin-clean_testflight_testers'
|
||||
gem 'fastlane-plugin-souyuz'
|
||||
gem 'fastlane-plugin-xamarin'
|
||||
@@ -1,54 +0,0 @@
|
||||
fastlane documentation
|
||||
================
|
||||
# Installation
|
||||
|
||||
Make sure you have the latest version of the Xcode command line tools installed:
|
||||
|
||||
```
|
||||
xcode-select --install
|
||||
```
|
||||
|
||||
Install _fastlane_ using
|
||||
```
|
||||
[sudo] gem install fastlane -NV
|
||||
```
|
||||
or alternatively using `brew cask install fastlane`
|
||||
|
||||
# Available Actions
|
||||
## iOS
|
||||
### ios testflight_prune_dry
|
||||
```
|
||||
fastlane ios testflight_prune_dry
|
||||
```
|
||||
|
||||
### ios testflight_prune
|
||||
```
|
||||
fastlane ios testflight_prune
|
||||
```
|
||||
|
||||
### ios update_version
|
||||
```
|
||||
fastlane ios update_version
|
||||
```
|
||||
|
||||
### ios beta
|
||||
```
|
||||
fastlane ios beta
|
||||
```
|
||||
Deploy to testflight
|
||||
### ios build
|
||||
```
|
||||
fastlane ios build
|
||||
```
|
||||
Compile the project
|
||||
### ios provision
|
||||
```
|
||||
fastlane ios provision
|
||||
```
|
||||
Install provisioning profiles using match
|
||||
|
||||
----
|
||||
|
||||
This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run.
|
||||
More information about fastlane can be found on [fastlane.tools](https://fastlane.tools).
|
||||
The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools).
|
||||
Submodule
+1
Submodule osu-resources added at 694cb03f19
@@ -16,6 +16,7 @@ using osu.Desktop.Updater;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Platform.Windows;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Screens.Menu;
|
||||
|
||||
namespace osu.Desktop
|
||||
@@ -62,10 +63,9 @@ namespace osu.Desktop
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ScreenChanged(IScreen lastScreen, IScreen newScreen)
|
||||
protected override void ScreenChanged(OsuScreen current, Screen newScreen)
|
||||
{
|
||||
base.ScreenChanged(lastScreen, newScreen);
|
||||
|
||||
base.ScreenChanged(current, newScreen);
|
||||
switch (newScreen)
|
||||
{
|
||||
case Intro _:
|
||||
@@ -83,7 +83,8 @@ namespace osu.Desktop
|
||||
public override void SetHost(GameHost host)
|
||||
{
|
||||
base.SetHost(host);
|
||||
if (host.Window is DesktopGameWindow desktopWindow)
|
||||
var desktopWindow = host.Window as DesktopGameWindow;
|
||||
if (desktopWindow != null)
|
||||
{
|
||||
desktopWindow.CursorState |= CursorState.Hidden;
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace osu.Desktop.Overlays
|
||||
{
|
||||
new OsuSpriteText
|
||||
{
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Bold),
|
||||
Font = @"Exo2.0-Bold",
|
||||
Text = game.Name
|
||||
},
|
||||
new OsuSpriteText
|
||||
@@ -74,8 +74,9 @@ namespace osu.Desktop.Overlays
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Font = OsuFont.Numeric.With(size: 12),
|
||||
TextSize = 12,
|
||||
Colour = colours.Yellow,
|
||||
Font = @"Venera",
|
||||
Text = @"Development Build"
|
||||
},
|
||||
new Sprite
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Catch\osu.Game.Rulesets.Catch.csproj" />
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Mania\osu.Game.Rulesets.Mania.csproj" />
|
||||
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />
|
||||
<ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj" />
|
||||
<PackageReference Include="Microsoft.Win32.Registry" Version="4.5.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// 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.
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using Foundation;
|
||||
using osu.Framework.iOS;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// 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.
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using UIKit;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>10.0</string>
|
||||
<string>11.0</string>
|
||||
<key>UIDeviceFamily</key>
|
||||
<array>
|
||||
<integer>1</integer>
|
||||
|
||||
@@ -26,7 +26,9 @@
|
||||
</LinkDescription>
|
||||
<Compile Include="Application.cs" />
|
||||
<Compile Include="AppDelegate.cs" />
|
||||
<Compile Include="..\osu.Game.Rulesets.Catch.Tests\**\*.cs" Exclude="**\obj\**">
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\osu.Game.Rulesets.Catch.Tests\*.cs">
|
||||
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
@@ -39,6 +41,32 @@
|
||||
<Project>{58F6C80C-1253-4A0E-A465-B8C85EBEADF3}</Project>
|
||||
<Name>osu.Game.Rulesets.Catch</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj">
|
||||
<Project>{D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}</Project>
|
||||
<Name>osu.Game.Resources</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="Xamarin.iOS" />
|
||||
<Reference Include="mscorlib" />
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Humanizer" Version="2.5.16" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2019.122.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.122.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.22.0" />
|
||||
<PackageReference Include="NUnit" Version="3.11.0" />
|
||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="4.5.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.NativeLibs" Version="2019.110.0" ExcludeAssets="all" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
|
||||
<Import Project="..\packages\NETStandard.Library.2.0.0\build\netstandard2.0\NETStandard.Library.targets" Condition="Exists('..\packages\NETStandard.Library.2.0.0\build\netstandard2.0\NETStandard.Library.targets')" />
|
||||
|
||||
@@ -21,7 +21,6 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
[TestCase("basic")]
|
||||
[TestCase("spinner")]
|
||||
[TestCase("spinner-and-circles")]
|
||||
[TestCase("slider")]
|
||||
public new void Test(string name)
|
||||
{
|
||||
base.Test(name);
|
||||
@@ -34,16 +33,13 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
case JuiceStream stream:
|
||||
foreach (var nested in stream.NestedHitObjects)
|
||||
yield return new ConvertValue((CatchHitObject)nested);
|
||||
|
||||
break;
|
||||
case BananaShower shower:
|
||||
foreach (var nested in shower.NestedHitObjects)
|
||||
yield return new ConvertValue((CatchHitObject)nested);
|
||||
|
||||
break;
|
||||
default:
|
||||
yield return new ConvertValue((CatchHitObject)hitObject);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Difficulty;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
public class CatchDifficultyCalculatorTest : DifficultyCalculatorTest
|
||||
{
|
||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
|
||||
|
||||
[TestCase(4.2038001515546597d, "diffcalc-test")]
|
||||
public void Test(double expected, string name)
|
||||
=> base.Test(expected, name);
|
||||
|
||||
protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new CatchDifficultyCalculator(new CatchRuleset(), beatmap);
|
||||
|
||||
protected override Ruleset CreateRuleset() => new CatchRuleset();
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
[TestFixture]
|
||||
public class TestCaseCatchPlayer : Game.Tests.Visual.TestCasePlayer
|
||||
{
|
||||
public TestCaseCatchPlayer()
|
||||
: base(new CatchRuleset())
|
||||
public TestCaseCatchPlayer() : base(new CatchRuleset())
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,6 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
rng.Next(); // osu!stable retrieved a random banana rotation
|
||||
rng.Next(); // osu!stable retrieved a random banana colour
|
||||
}
|
||||
|
||||
break;
|
||||
case JuiceStream juiceStream:
|
||||
foreach (var nested in juiceStream.NestedHitObjects)
|
||||
@@ -68,7 +67,6 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
rng.Next(); // osu!stable retrieved a random droplet rotation
|
||||
hitObject.X = MathHelper.Clamp(hitObject.X, 0, 1);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,8 @@ namespace osu.Game.Rulesets.Catch
|
||||
{
|
||||
[Description("Move left")]
|
||||
MoveLeft,
|
||||
|
||||
[Description("Move right")]
|
||||
MoveRight,
|
||||
|
||||
[Description("Engage dash")]
|
||||
Dash,
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
{
|
||||
@@ -9,5 +10,10 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
{
|
||||
public double ApproachRate;
|
||||
public int MaxCombo;
|
||||
|
||||
public CatchDifficultyAttributes(Mod[] mods, double starRating)
|
||||
: base(mods, starRating)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,92 +1,148 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Catch.Difficulty.Skills;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Difficulty.Skills;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
{
|
||||
public class CatchDifficultyCalculator : DifficultyCalculator
|
||||
{
|
||||
/// <summary>
|
||||
/// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size STRAIN_STEP.
|
||||
/// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain.
|
||||
/// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage.
|
||||
/// </summary>
|
||||
private const double strain_step = 750;
|
||||
|
||||
/// <summary>
|
||||
/// The weighting of each strain value decays to this number * it's previous value
|
||||
/// </summary>
|
||||
private const double decay_weight = 0.94;
|
||||
|
||||
private const double star_scaling_factor = 0.145;
|
||||
|
||||
protected override int SectionLength => 750;
|
||||
|
||||
private readonly float halfCatchWidth;
|
||||
|
||||
public CatchDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
|
||||
: base(ruleset, beatmap)
|
||||
{
|
||||
}
|
||||
|
||||
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
|
||||
{
|
||||
if (!beatmap.HitObjects.Any())
|
||||
return new CatchDifficultyAttributes(mods, 0);
|
||||
|
||||
var catcher = new CatcherArea.Catcher(beatmap.BeatmapInfo.BaseDifficulty);
|
||||
halfCatchWidth = catcher.CatchWidth * 0.5f;
|
||||
float halfCatchWidth = catcher.CatchWidth * 0.5f;
|
||||
|
||||
// We're only using 80% of the catcher's width to simulate imperfect gameplay.
|
||||
halfCatchWidth *= 0.8f;
|
||||
}
|
||||
var difficultyHitObjects = new List<CatchDifficultyHitObject>();
|
||||
|
||||
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
|
||||
{
|
||||
if (beatmap.HitObjects.Count == 0)
|
||||
return new CatchDifficultyAttributes { Mods = mods };
|
||||
|
||||
// this is the same as osu!, so there's potential to share the implementation... maybe
|
||||
double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;
|
||||
|
||||
return new CatchDifficultyAttributes
|
||||
foreach (var hitObject in beatmap.HitObjects)
|
||||
{
|
||||
StarRating = Math.Sqrt(skills[0].DifficultyValue()) * star_scaling_factor,
|
||||
Mods = mods,
|
||||
ApproachRate = preempt > 1200.0 ? -(preempt - 1800.0) / 120.0 : -(preempt - 1200.0) / 150.0 + 5.0,
|
||||
MaxCombo = beatmap.HitObjects.Count(h => h is Fruit) + beatmap.HitObjects.OfType<JuiceStream>().SelectMany(j => j.NestedHitObjects).Count(h => !(h is TinyDroplet))
|
||||
};
|
||||
}
|
||||
|
||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate)
|
||||
{
|
||||
CatchHitObject lastObject = null;
|
||||
|
||||
foreach (var hitObject in beatmap.HitObjects.OfType<CatchHitObject>())
|
||||
{
|
||||
if (lastObject == null)
|
||||
{
|
||||
lastObject = hitObject;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (hitObject)
|
||||
{
|
||||
// We want to only consider fruits that contribute to the combo. Droplets are addressed as accuracy and spinners are not relevant for "skill" calculations.
|
||||
case Fruit fruit:
|
||||
yield return new CatchDifficultyHitObject(fruit, lastObject, clockRate, halfCatchWidth);
|
||||
|
||||
lastObject = hitObject;
|
||||
difficultyHitObjects.Add(new CatchDifficultyHitObject(fruit, halfCatchWidth));
|
||||
break;
|
||||
case JuiceStream _:
|
||||
foreach (var nested in hitObject.NestedHitObjects.OfType<CatchHitObject>().Where(o => !(o is TinyDroplet)))
|
||||
{
|
||||
yield return new CatchDifficultyHitObject(nested, lastObject, clockRate, halfCatchWidth);
|
||||
|
||||
lastObject = nested;
|
||||
}
|
||||
|
||||
difficultyHitObjects.AddRange(hitObject.NestedHitObjects.OfType<CatchHitObject>().Where(o => !(o is TinyDroplet)).Select(o => new CatchDifficultyHitObject(o, halfCatchWidth)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
|
||||
|
||||
if (!calculateStrainValues(difficultyHitObjects, timeRate))
|
||||
return new CatchDifficultyAttributes(mods, 0);
|
||||
|
||||
// this is the same as osu!, so there's potential to share the implementation... maybe
|
||||
double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate;
|
||||
double starRating = Math.Sqrt(calculateDifficulty(difficultyHitObjects, timeRate)) * star_scaling_factor;
|
||||
|
||||
return new CatchDifficultyAttributes(mods, starRating)
|
||||
{
|
||||
ApproachRate = preempt > 1200.0 ? -(preempt - 1800.0) / 120.0 : -(preempt - 1200.0) / 150.0 + 5.0,
|
||||
MaxCombo = difficultyHitObjects.Count
|
||||
};
|
||||
}
|
||||
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap) => new Skill[]
|
||||
private bool calculateStrainValues(List<CatchDifficultyHitObject> objects, double timeRate)
|
||||
{
|
||||
new Movement(),
|
||||
};
|
||||
CatchDifficultyHitObject lastObject = null;
|
||||
|
||||
if (!objects.Any()) return false;
|
||||
|
||||
// Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment.
|
||||
foreach (var currentObject in objects)
|
||||
{
|
||||
if (lastObject != null)
|
||||
currentObject.CalculateStrains(lastObject, timeRate);
|
||||
|
||||
lastObject = currentObject;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private double calculateDifficulty(List<CatchDifficultyHitObject> objects, double timeRate)
|
||||
{
|
||||
// The strain step needs to be adjusted for the algorithm to be considered equal with speed changing mods
|
||||
double actualStrainStep = strain_step * timeRate;
|
||||
|
||||
// Find the highest strain value within each strain step
|
||||
var highestStrains = new List<double>();
|
||||
double intervalEndTime = actualStrainStep;
|
||||
double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval
|
||||
|
||||
CatchDifficultyHitObject previousHitObject = null;
|
||||
foreach (CatchDifficultyHitObject hitObject in objects)
|
||||
{
|
||||
// While we are beyond the current interval push the currently available maximum to our strain list
|
||||
while (hitObject.BaseHitObject.StartTime > intervalEndTime)
|
||||
{
|
||||
highestStrains.Add(maximumStrain);
|
||||
|
||||
// The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay
|
||||
// until the beginning of the next interval.
|
||||
if (previousHitObject == null)
|
||||
{
|
||||
maximumStrain = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
double decay = Math.Pow(CatchDifficultyHitObject.DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000);
|
||||
maximumStrain = previousHitObject.Strain * decay;
|
||||
}
|
||||
|
||||
// Go to the next time interval
|
||||
intervalEndTime += actualStrainStep;
|
||||
}
|
||||
|
||||
// Obtain maximum strain
|
||||
maximumStrain = Math.Max(hitObject.Strain, maximumStrain);
|
||||
|
||||
previousHitObject = hitObject;
|
||||
}
|
||||
|
||||
// Build the weighted sum over the highest strains for each interval
|
||||
double difficulty = 0;
|
||||
double weight = 1;
|
||||
highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain.
|
||||
|
||||
foreach (double strain in highestStrains)
|
||||
{
|
||||
difficulty += weight * strain;
|
||||
weight *= decay_weight;
|
||||
}
|
||||
|
||||
return difficulty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
{
|
||||
public class CatchDifficultyHitObject
|
||||
{
|
||||
internal static readonly double DECAY_BASE = 0.20;
|
||||
private const float normalized_hitobject_radius = 41.0f;
|
||||
private const float absolute_player_positioning_error = 16f;
|
||||
private readonly float playerPositioningError;
|
||||
|
||||
internal CatchHitObject BaseHitObject;
|
||||
|
||||
/// <summary>
|
||||
/// Measures jump difficulty. CtB doesn't have something like button pressing speed or accuracy
|
||||
/// </summary>
|
||||
internal double Strain = 1;
|
||||
|
||||
/// <summary>
|
||||
/// This is required to keep track of lazy player movement (always moving only as far as necessary)
|
||||
/// Without this quick repeat sliders / weirdly shaped streams might become ridiculously overrated
|
||||
/// </summary>
|
||||
internal float PlayerPositionOffset;
|
||||
internal float LastMovement;
|
||||
|
||||
internal float NormalizedPosition;
|
||||
internal float ActualNormalizedPosition => NormalizedPosition + PlayerPositionOffset;
|
||||
|
||||
internal CatchDifficultyHitObject(CatchHitObject baseHitObject, float catcherWidthHalf)
|
||||
{
|
||||
BaseHitObject = baseHitObject;
|
||||
|
||||
// We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps.
|
||||
float scalingFactor = normalized_hitobject_radius / catcherWidthHalf;
|
||||
|
||||
playerPositioningError = absolute_player_positioning_error; // * scalingFactor;
|
||||
NormalizedPosition = baseHitObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor;
|
||||
}
|
||||
|
||||
private const double direction_change_bonus = 12.5;
|
||||
internal void CalculateStrains(CatchDifficultyHitObject previousHitObject, double timeRate)
|
||||
{
|
||||
// Rather simple, but more specialized things are inherently inaccurate due to the big difference playstyles and opinions make.
|
||||
// See Taiko feedback thread.
|
||||
double timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate;
|
||||
double decay = Math.Pow(DECAY_BASE, timeElapsed / 1000);
|
||||
|
||||
// Update new position with lazy movement.
|
||||
PlayerPositionOffset =
|
||||
MathHelper.Clamp(
|
||||
previousHitObject.ActualNormalizedPosition,
|
||||
NormalizedPosition - (normalized_hitobject_radius - playerPositioningError),
|
||||
NormalizedPosition + (normalized_hitobject_radius - playerPositioningError)) // Obtain new lazy position, but be stricter by allowing for an error of a certain degree of the player.
|
||||
- NormalizedPosition; // Subtract HitObject position to obtain offset
|
||||
|
||||
LastMovement = DistanceTo(previousHitObject);
|
||||
double addition = spacingWeight(LastMovement);
|
||||
|
||||
if (NormalizedPosition < previousHitObject.NormalizedPosition)
|
||||
{
|
||||
LastMovement = -LastMovement;
|
||||
}
|
||||
|
||||
CatchHitObject previousHitCircle = previousHitObject.BaseHitObject;
|
||||
|
||||
double additionBonus = 0;
|
||||
double sqrtTime = Math.Sqrt(Math.Max(timeElapsed, 25));
|
||||
|
||||
// Direction changes give an extra point!
|
||||
if (Math.Abs(LastMovement) > 0.1)
|
||||
{
|
||||
if (Math.Abs(previousHitObject.LastMovement) > 0.1 && Math.Sign(LastMovement) != Math.Sign(previousHitObject.LastMovement))
|
||||
{
|
||||
double bonus = direction_change_bonus / sqrtTime;
|
||||
|
||||
// Weight bonus by how
|
||||
double bonusFactor = Math.Min(playerPositioningError, Math.Abs(LastMovement)) / playerPositioningError;
|
||||
|
||||
// We want time to play a role twice here!
|
||||
addition += bonus * bonusFactor;
|
||||
|
||||
// Bonus for tougher direction switches and "almost" hyperdashes at this point
|
||||
if (previousHitCircle != null && previousHitCircle.DistanceToHyperDash <= 10.0f / CatchPlayfield.BASE_WIDTH)
|
||||
{
|
||||
additionBonus += 0.3 * bonusFactor;
|
||||
}
|
||||
}
|
||||
|
||||
// Base bonus for every movement, giving some weight to streams.
|
||||
addition += 7.5 * Math.Min(Math.Abs(LastMovement), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtTime;
|
||||
}
|
||||
|
||||
// Bonus for "almost" hyperdashes at corner points
|
||||
if (previousHitCircle != null && previousHitCircle.DistanceToHyperDash <= 10.0f / CatchPlayfield.BASE_WIDTH)
|
||||
{
|
||||
if (!previousHitCircle.HyperDash)
|
||||
{
|
||||
additionBonus += 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// After a hyperdash we ARE in the correct position. Always!
|
||||
PlayerPositionOffset = 0;
|
||||
}
|
||||
|
||||
addition *= 1.0 + additionBonus * ((10 - previousHitCircle.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 10);
|
||||
}
|
||||
|
||||
addition *= 850.0 / Math.Max(timeElapsed, 25);
|
||||
|
||||
Strain = previousHitObject.Strain * decay + addition;
|
||||
}
|
||||
|
||||
private static double spacingWeight(float distance)
|
||||
{
|
||||
return Math.Pow(distance, 1.3) / 500;
|
||||
}
|
||||
|
||||
internal float DistanceTo(CatchDifficultyHitObject other)
|
||||
{
|
||||
return Math.Abs(ActualNormalizedPosition - other.ActualNormalizedPosition);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Difficulty.Preprocessing
|
||||
{
|
||||
public class CatchDifficultyHitObject : DifficultyHitObject
|
||||
{
|
||||
private const float normalized_hitobject_radius = 41.0f;
|
||||
|
||||
public new CatchHitObject BaseObject => (CatchHitObject)base.BaseObject;
|
||||
|
||||
public new CatchHitObject LastObject => (CatchHitObject)base.LastObject;
|
||||
|
||||
public readonly float NormalizedPosition;
|
||||
public readonly float LastNormalizedPosition;
|
||||
|
||||
/// <summary>
|
||||
/// Milliseconds elapsed since the start time of the previous <see cref="CatchDifficultyHitObject"/>, with a minimum of 25ms.
|
||||
/// </summary>
|
||||
public readonly double StrainTime;
|
||||
|
||||
public CatchDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate, float halfCatcherWidth)
|
||||
: base(hitObject, lastObject, clockRate)
|
||||
{
|
||||
// We will scale everything by this factor, so we can assume a uniform CircleSize among beatmaps.
|
||||
var scalingFactor = normalized_hitobject_radius / halfCatcherWidth;
|
||||
|
||||
NormalizedPosition = BaseObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor;
|
||||
LastNormalizedPosition = LastObject.X * CatchPlayfield.BASE_WIDTH * scalingFactor;
|
||||
|
||||
// Every strain interval is hard capped at the equivalent of 600 BPM streaming speed as a safety measure
|
||||
StrainTime = Math.Max(25, DeltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Game.Rulesets.Catch.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Difficulty.Skills;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
||||
{
|
||||
public class Movement : Skill
|
||||
{
|
||||
private const float absolute_player_positioning_error = 16f;
|
||||
private const float normalized_hitobject_radius = 41.0f;
|
||||
private const double direction_change_bonus = 12.5;
|
||||
|
||||
protected override double SkillMultiplier => 850;
|
||||
protected override double StrainDecayBase => 0.2;
|
||||
|
||||
protected override double DecayWeight => 0.94;
|
||||
|
||||
private float? lastPlayerPosition;
|
||||
private float lastDistanceMoved;
|
||||
|
||||
protected override double StrainValueOf(DifficultyHitObject current)
|
||||
{
|
||||
var catchCurrent = (CatchDifficultyHitObject)current;
|
||||
|
||||
if (lastPlayerPosition == null)
|
||||
lastPlayerPosition = catchCurrent.LastNormalizedPosition;
|
||||
|
||||
float playerPosition = MathHelper.Clamp(
|
||||
lastPlayerPosition.Value,
|
||||
catchCurrent.NormalizedPosition - (normalized_hitobject_radius - absolute_player_positioning_error),
|
||||
catchCurrent.NormalizedPosition + (normalized_hitobject_radius - absolute_player_positioning_error)
|
||||
);
|
||||
|
||||
float distanceMoved = playerPosition - lastPlayerPosition.Value;
|
||||
|
||||
double distanceAddition = Math.Pow(Math.Abs(distanceMoved), 1.3) / 500;
|
||||
double sqrtStrain = Math.Sqrt(catchCurrent.StrainTime);
|
||||
|
||||
double bonus = 0;
|
||||
|
||||
// Direction changes give an extra point!
|
||||
if (Math.Abs(distanceMoved) > 0.1)
|
||||
{
|
||||
if (Math.Abs(lastDistanceMoved) > 0.1 && Math.Sign(distanceMoved) != Math.Sign(lastDistanceMoved))
|
||||
{
|
||||
double bonusFactor = Math.Min(absolute_player_positioning_error, Math.Abs(distanceMoved)) / absolute_player_positioning_error;
|
||||
|
||||
distanceAddition += direction_change_bonus / sqrtStrain * bonusFactor;
|
||||
|
||||
// Bonus for tougher direction switches and "almost" hyperdashes at this point
|
||||
if (catchCurrent.LastObject.DistanceToHyperDash <= 10 / CatchPlayfield.BASE_WIDTH)
|
||||
bonus = 0.3 * bonusFactor;
|
||||
}
|
||||
|
||||
// Base bonus for every movement, giving some weight to streams.
|
||||
distanceAddition += 7.5 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain;
|
||||
}
|
||||
|
||||
// Bonus for "almost" hyperdashes at corner points
|
||||
if (catchCurrent.LastObject.DistanceToHyperDash <= 10.0f / CatchPlayfield.BASE_WIDTH)
|
||||
{
|
||||
if (!catchCurrent.LastObject.HyperDash)
|
||||
bonus += 1.0;
|
||||
else
|
||||
{
|
||||
// After a hyperdash we ARE in the correct position. Always!
|
||||
playerPosition = catchCurrent.NormalizedPosition;
|
||||
}
|
||||
|
||||
distanceAddition *= 1.0 + bonus * ((10 - catchCurrent.LastObject.DistanceToHyperDash * CatchPlayfield.BASE_WIDTH) / 10);
|
||||
}
|
||||
|
||||
lastPlayerPosition = playerPosition;
|
||||
lastDistanceMoved = distanceMoved;
|
||||
|
||||
return distanceAddition / catchCurrent.StrainTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,11 +33,11 @@ namespace osu.Game.Rulesets.Catch.MathUtils
|
||||
/// <returns>The random value.</returns>
|
||||
public uint NextUInt()
|
||||
{
|
||||
uint t = _x ^ (_x << 11);
|
||||
uint t = _x ^ _x << 11;
|
||||
_x = _y;
|
||||
_y = _z;
|
||||
_z = _w;
|
||||
return _w = _w ^ (_w >> 19) ^ t ^ (t >> 8);
|
||||
return _w = _w ^ _w >> 19 ^ t ^ t >> 8;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
@@ -56,9 +55,9 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
return default_flashlight_size;
|
||||
}
|
||||
|
||||
protected override void OnComboChange(ValueChangedEvent<int> e)
|
||||
protected override void OnComboChange(int newCombo)
|
||||
{
|
||||
this.TransformTo(nameof(FlashlightSize), new Vector2(0, getSizeFor(e.NewValue)), FLASHLIGHT_FADE_DURATION);
|
||||
this.TransformTo(nameof(FlashlightSize), new Vector2(0, getSizeFor(newCombo)), FLASHLIGHT_FADE_DURATION);
|
||||
}
|
||||
|
||||
protected override string FragmentShader => "CircularFlashlight";
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
base.SkinChanged(skin, allowFallback);
|
||||
|
||||
if (HitObject is IHasComboInformation combo)
|
||||
AccentColour = skin.GetValue<SkinConfiguration, Color4?>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White;
|
||||
AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : Color4.White);
|
||||
}
|
||||
|
||||
private const float preempt = 1000;
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
|
||||
|
||||
public override Color4 AccentColour
|
||||
{
|
||||
get => base.AccentColour;
|
||||
get { return base.AccentColour; }
|
||||
set
|
||||
{
|
||||
base.AccentColour = value;
|
||||
|
||||
@@ -23,10 +23,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable.Pieces
|
||||
}
|
||||
|
||||
private Color4 accentColour;
|
||||
|
||||
public Color4 AccentColour
|
||||
{
|
||||
get => accentColour;
|
||||
get { return accentColour; }
|
||||
set
|
||||
{
|
||||
accentColour = value;
|
||||
|
||||
@@ -55,13 +55,6 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
|
||||
var minDistanceFromEnd = Velocity * 0.01;
|
||||
|
||||
var tickSamples = Samples.Select(s => new SampleInfo
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
Volume = s.Volume
|
||||
}).ToList();
|
||||
|
||||
AddNested(new Fruit
|
||||
{
|
||||
Samples = Samples,
|
||||
@@ -69,22 +62,15 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
X = X
|
||||
});
|
||||
|
||||
double lastTickTime = StartTime;
|
||||
double lastDropletTime = StartTime;
|
||||
|
||||
for (int span = 0; span < this.SpanCount(); span++)
|
||||
{
|
||||
var spanStartTime = StartTime + span * spanDuration;
|
||||
var reversed = span % 2 == 1;
|
||||
|
||||
for (double d = tickDistance;; d += tickDistance)
|
||||
for (double d = 0; d <= length; d += tickDistance)
|
||||
{
|
||||
bool isLastTick = false;
|
||||
if (d + minDistanceFromEnd >= length)
|
||||
{
|
||||
d = length;
|
||||
isLastTick = true;
|
||||
}
|
||||
|
||||
var timeProgress = d / length;
|
||||
var distanceProgress = reversed ? 1 - timeProgress : timeProgress;
|
||||
|
||||
@@ -93,42 +79,47 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
if (LegacyLastTickOffset != null)
|
||||
{
|
||||
// If we're the last tick, apply the legacy offset
|
||||
if (span == this.SpanCount() - 1 && isLastTick)
|
||||
if (span == this.SpanCount() - 1 && d + tickDistance > length)
|
||||
time = Math.Max(StartTime + Duration / 2, time - LegacyLastTickOffset.Value);
|
||||
}
|
||||
|
||||
int tinyTickCount = 1;
|
||||
double tinyTickInterval = time - lastTickTime;
|
||||
while (tinyTickInterval > 100 && tinyTickCount < 10000)
|
||||
{
|
||||
double tinyTickInterval = time - lastDropletTime;
|
||||
while (tinyTickInterval > 100)
|
||||
tinyTickInterval /= 2;
|
||||
tinyTickCount *= 2;
|
||||
}
|
||||
|
||||
for (int tinyTickIndex = 0; tinyTickIndex < tinyTickCount - 1; tinyTickIndex++)
|
||||
for (double t = lastDropletTime + tinyTickInterval; t < time; t += tinyTickInterval)
|
||||
{
|
||||
var t = lastTickTime + (tinyTickIndex + 1) * tinyTickInterval;
|
||||
double progress = reversed ? 1 - (t - spanStartTime) / spanDuration : (t - spanStartTime) / spanDuration;
|
||||
|
||||
AddNested(new TinyDroplet
|
||||
{
|
||||
StartTime = t,
|
||||
X = X + Path.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = tickSamples
|
||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
Volume = s.Volume
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
lastTickTime = time;
|
||||
|
||||
if (isLastTick)
|
||||
break;
|
||||
|
||||
AddNested(new Droplet
|
||||
if (d > minDistanceFromEnd && Math.Abs(d - length) > minDistanceFromEnd)
|
||||
{
|
||||
StartTime = time,
|
||||
X = X + Path.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = tickSamples
|
||||
});
|
||||
AddNested(new Droplet
|
||||
{
|
||||
StartTime = time,
|
||||
X = X + Path.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
|
||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||
{
|
||||
Bank = s.Bank,
|
||||
Name = @"slidertick",
|
||||
Volume = s.Volume
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
lastDropletTime = time;
|
||||
}
|
||||
|
||||
AddNested(new Fruit
|
||||
|
||||
@@ -1,138 +0,0 @@
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
StackLeniency: 0.3
|
||||
Mode: 2
|
||||
|
||||
[Difficulty]
|
||||
CircleSize:4
|
||||
OverallDifficulty:7
|
||||
ApproachRate:8.3
|
||||
SliderMultiplier:1.6
|
||||
SliderTickRate:1
|
||||
|
||||
[TimingPoints]
|
||||
500,500,4,2,1,50,1,0
|
||||
34500,-50,4,2,1,50,0,0
|
||||
|
||||
[HitObjects]
|
||||
// fruits spaced 1/1 beat apart
|
||||
32,128,0,5,0,0:0:0:0:
|
||||
96,128,500,1,0,0:0:0:0:
|
||||
160,128,1000,1,0,0:0:0:0:
|
||||
224,128,1500,1,0,0:0:0:0:
|
||||
288,128,2000,1,0,0:0:0:0:
|
||||
352,128,2500,1,0,0:0:0:0:
|
||||
416,128,3000,1,0,0:0:0:0:
|
||||
480,128,3500,1,0,0:0:0:0:
|
||||
|
||||
// fruits spaced 1/2 beat apart
|
||||
32,160,4500,1,0,0:0:0:0:
|
||||
64,160,4750,1,0,0:0:0:0:
|
||||
96,160,5000,1,0,0:0:0:0:
|
||||
128,160,5250,1,0,0:0:0:0:
|
||||
160,160,5500,1,0,0:0:0:0:
|
||||
192,160,5750,1,0,0:0:0:0:
|
||||
224,160,6000,1,0,0:0:0:0:
|
||||
256,160,6250,1,0,0:0:0:0:
|
||||
288,160,6500,1,0,0:0:0:0:
|
||||
|
||||
// fruits spaced 1/4 beat apart
|
||||
96,128,7500,1,0,0:0:0:0:
|
||||
128,128,7625,1,0,0:0:0:0:
|
||||
160,128,7750,1,0,0:0:0:0:
|
||||
192,128,7875,1,0,0:0:0:0:
|
||||
224,128,8000,1,0,0:0:0:0:
|
||||
256,128,8125,1,0,0:0:0:0:
|
||||
288,128,8250,1,0,0:0:0:0:
|
||||
320,128,8375,1,0,0:0:0:0:
|
||||
352,128,8500,1,0,0:0:0:0:
|
||||
|
||||
// fruit hyperdashes, spaced 1/2 beat apart
|
||||
32,160,9500,1,0,0:0:0:0:
|
||||
480,160,9750,1,0,0:0:0:0:
|
||||
32,160,10000,1,0,0:0:0:0:
|
||||
480,160,10250,1,0,0:0:0:0:
|
||||
32,160,10500,1,0,0:0:0:0:
|
||||
480,160,10750,1,0,0:0:0:0:
|
||||
32,160,11000,1,0,0:0:0:0:
|
||||
|
||||
// fruit hyperdashes, spaced 1/4 beat apart
|
||||
32,192,12000,1,0,0:0:0:0:
|
||||
480,192,12125,1,0,0:0:0:0:
|
||||
32,192,12250,1,0,0:0:0:0:
|
||||
480,192,12375,1,0,0:0:0:0:
|
||||
32,192,12500,1,0,0:0:0:0:
|
||||
480,192,12625,1,0,0:0:0:0:
|
||||
32,192,12750,1,0,0:0:0:0:
|
||||
480,192,12875,1,0,0:0:0:0:
|
||||
32,192,13000,1,0,0:0:0:0:
|
||||
|
||||
// stream + hyperdash + stream, spaced 1/4 beat apart
|
||||
32,192,14000,1,0,0:0:0:0:
|
||||
64,192,14125,1,0,0:0:0:0:
|
||||
96,192,14250,1,0,0:0:0:0:
|
||||
128,192,14375,1,0,0:0:0:0:
|
||||
480,192,14500,1,0,0:0:0:0:
|
||||
448,192,14625,1,0,0:0:0:0:
|
||||
416,192,14750,1,0,0:0:0:0:
|
||||
384,192,14875,1,0,0:0:0:0:
|
||||
32,192,15000,1,0,0:0:0:0:
|
||||
|
||||
// basic sliders
|
||||
32,192,16000,2,0,L|192:192,1,160
|
||||
224,192,17000,2,0,L|384:192,1,160
|
||||
416,192,17875,2,0,L|480:192,1,40
|
||||
|
||||
// slider hyperdashes, spaced 1/4 beat apart
|
||||
32,192,19000,2,0,L|128:192,1,80
|
||||
480,192,19375,2,0,L|384:192,1,80
|
||||
352,192,19750,2,0,L|256:192,1,80
|
||||
0,192,20125,2,0,L|128:192,1,120
|
||||
|
||||
// stream + slider hyperdashes, spaced 1/4 beat apart
|
||||
32,192,21500,1,0,0:0:0:0:
|
||||
64,192,21625,1,0,0:0:0:0:
|
||||
96,192,21750,1,0,0:0:0:0:
|
||||
512,192,21875,2,0,L|320:192,1,160
|
||||
320,192,22500,1,0,0:0:0:0:
|
||||
288,192,22625,1,0,0:0:0:0:
|
||||
256,192,22750,1,0,0:0:0:0:
|
||||
0,192,22875,2,0,L|64:192,1,40
|
||||
|
||||
// streams, spaced 1/4 beat apart
|
||||
64,192,24000,1,0,0:0:0:0:
|
||||
160,192,24125,1,0,0:0:0:0:
|
||||
64,192,24250,1,0,0:0:0:0:
|
||||
160,192,24375,1,0,0:0:0:0:
|
||||
64,192,24500,1,0,0:0:0:0:
|
||||
160,192,24625,1,0,0:0:0:0:
|
||||
64,192,24750,1,0,0:0:0:0:
|
||||
160,192,24875,1,0,0:0:0:0:
|
||||
64,192,25000,1,0,0:0:0:0:
|
||||
160,192,25125,1,0,0:0:0:0:
|
||||
64,192,25250,1,0,0:0:0:0:
|
||||
160,192,25375,1,0,0:0:0:0:
|
||||
64,192,25500,1,0,0:0:0:0:
|
||||
|
||||
// stream + spinner combo, spaced 1/4 beat apart
|
||||
256,192,26500,12,0,27000,0:0:0:0:
|
||||
128,192,27250,5,0,0:0:0:0:
|
||||
128,192,27375,1,0,0:0:0:0:
|
||||
160,192,27500,1,0,0:0:0:0:
|
||||
192,192,27625,1,0,0:0:0:0:
|
||||
256,192,27750,12,0,28500,0:0:0:0:
|
||||
192,192,28625,5,0,0:0:0:0:
|
||||
224,192,28750,1,0,0:0:0:0:
|
||||
256,192,28875,1,0,0:0:0:0:
|
||||
256,192,29000,1,0,0:0:0:0:
|
||||
256,192,29125,12,0,29500,0:0:0:0:
|
||||
|
||||
// long slow slider
|
||||
0,192,30500,6,0,B|480:192|480:192|0:192,2,960
|
||||
|
||||
// long fast slider
|
||||
0,192,37500,6,0,B|480:192|480:192|0:192,2,960
|
||||
|
||||
// long hyperdash slider
|
||||
0,192,41500,2,0,P|544:192|544:192,5,480
|
||||
@@ -1 +0,0 @@
|
||||
{"Mappings":[{"StartTime":19184.0,"Objects":[{"StartTime":19184.0,"Position":320.0},{"StartTime":19263.0,"Position":311.730255},{"StartTime":19343.0,"Position":324.6205},{"StartTime":19423.0,"Position":343.0907},{"StartTime":19503.0,"Position":372.2917},{"StartTime":19582.0,"Position":385.194733},{"StartTime":19662.0,"Position":379.0426},{"StartTime":19742.0,"Position":385.1066},{"StartTime":19822.0,"Position":391.624664},{"StartTime":19919.0,"Position":386.27832},{"StartTime":20016.0,"Position":380.117035},{"StartTime":20113.0,"Position":381.664154},{"StartTime":20247.0,"Position":370.872864}]}]}
|
||||
@@ -1,18 +0,0 @@
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
Mode: 2
|
||||
|
||||
[Difficulty]
|
||||
HPDrainRate:3
|
||||
CircleSize:2
|
||||
OverallDifficulty:4
|
||||
ApproachRate:4
|
||||
SliderMultiplier:0.9
|
||||
SliderTickRate:1
|
||||
|
||||
[TimingPoints]
|
||||
35.4473684210527,638.298947368422,4,2,1,60,1,0
|
||||
|
||||
[HitObjects]
|
||||
320,176,19184,2,8,P|384:168|368:232,1,150
|
||||
@@ -43,6 +43,6 @@ namespace osu.Game.Rulesets.Catch.Scoring
|
||||
Health.Value += Math.Max(result.Judgement.HealthIncreaseFor(result) - hpDrainRate, 0) * harshness;
|
||||
}
|
||||
|
||||
public override HitWindows CreateHitWindows() => new CatchHitWindows();
|
||||
protected override HitWindows CreateHitWindows() => new CatchHitWindows();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
public class CatcherArea : Container
|
||||
{
|
||||
public const float CATCHER_SIZE = 106.75f;
|
||||
public const float CATCHER_SIZE = 100;
|
||||
|
||||
protected internal readonly Catcher MovableCatcher;
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
public Container ExplodingFruitTarget
|
||||
{
|
||||
set => MovableCatcher.ExplodingFruitTarget = value;
|
||||
set { MovableCatcher.ExplodingFruitTarget = value; }
|
||||
}
|
||||
|
||||
public CatcherArea(BeatmapDifficulty difficulty = null)
|
||||
@@ -158,7 +158,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
protected bool Dashing
|
||||
{
|
||||
get => dashing;
|
||||
get { return dashing; }
|
||||
set
|
||||
{
|
||||
if (value == dashing) return;
|
||||
@@ -176,7 +176,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
/// </summary>
|
||||
protected bool Trail
|
||||
{
|
||||
get => trail;
|
||||
get { return trail; }
|
||||
set
|
||||
{
|
||||
if (value == trail) return;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// 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.
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using Foundation;
|
||||
using osu.Framework.iOS;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// 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.
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using UIKit;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>10.0</string>
|
||||
<string>11.0</string>
|
||||
<key>UIDeviceFamily</key>
|
||||
<array>
|
||||
<integer>1</integer>
|
||||
|
||||
@@ -26,7 +26,9 @@
|
||||
</LinkDescription>
|
||||
<Compile Include="Application.cs" />
|
||||
<Compile Include="AppDelegate.cs" />
|
||||
<Compile Include="..\osu.Game.Rulesets.Mania.Tests\**\*.cs" Exclude="**\obj\**">
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\osu.Game.Rulesets.Mania.Tests\*.cs">
|
||||
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
@@ -39,6 +41,32 @@
|
||||
<Project>{48F4582B-7687-4621-9CBE-5C24197CB536}</Project>
|
||||
<Name>osu.Game.Rulesets.Mania</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj">
|
||||
<Project>{D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}</Project>
|
||||
<Name>osu.Game.Resources</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="Xamarin.iOS" />
|
||||
<Reference Include="mscorlib" />
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Humanizer" Version="2.5.16" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2019.122.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.122.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.22.0" />
|
||||
<PackageReference Include="NUnit" Version="3.11.0" />
|
||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="4.5.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.NativeLibs" Version="2019.110.0" ExcludeAssets="all" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
|
||||
<Import Project="..\packages\NETStandard.Library.2.0.0\build\netstandard2.0\NETStandard.Library.targets" Condition="Exists('..\packages\NETStandard.Library.2.0.0\build\netstandard2.0\NETStandard.Library.targets')" />
|
||||
|
||||
@@ -40,29 +40,29 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
protected override Ruleset CreateRuleset() => new ManiaRuleset();
|
||||
}
|
||||
|
||||
public class ManiaConvertMapping : ConvertMapping<ConvertValue>, IEquatable<ManiaConvertMapping>
|
||||
{
|
||||
public uint RandomW;
|
||||
public uint RandomX;
|
||||
public uint RandomY;
|
||||
public uint RandomZ;
|
||||
public class ManiaConvertMapping : ConvertMapping<ConvertValue>, IEquatable<ManiaConvertMapping>
|
||||
{
|
||||
public uint RandomW;
|
||||
public uint RandomX;
|
||||
public uint RandomY;
|
||||
public uint RandomZ;
|
||||
|
||||
public ManiaConvertMapping()
|
||||
{
|
||||
}
|
||||
public ManiaConvertMapping()
|
||||
{
|
||||
}
|
||||
|
||||
public ManiaConvertMapping(IBeatmapConverter converter)
|
||||
{
|
||||
var maniaConverter = (ManiaBeatmapConverter)converter;
|
||||
RandomW = maniaConverter.Random.W;
|
||||
RandomX = maniaConverter.Random.X;
|
||||
RandomY = maniaConverter.Random.Y;
|
||||
RandomZ = maniaConverter.Random.Z;
|
||||
}
|
||||
public ManiaConvertMapping(IBeatmapConverter converter)
|
||||
{
|
||||
var maniaConverter = (ManiaBeatmapConverter)converter;
|
||||
RandomW = maniaConverter.Random.W;
|
||||
RandomX = maniaConverter.Random.X;
|
||||
RandomY = maniaConverter.Random.Y;
|
||||
RandomZ = maniaConverter.Random.Z;
|
||||
}
|
||||
|
||||
public bool Equals(ManiaConvertMapping other) => other != null && RandomW == other.RandomW && RandomX == other.RandomX && RandomY == other.RandomY && RandomZ == other.RandomZ;
|
||||
public override bool Equals(ConvertMapping<ConvertValue> other) => base.Equals(other) && Equals(other as ManiaConvertMapping);
|
||||
}
|
||||
public bool Equals(ManiaConvertMapping other) => other != null && RandomW == other.RandomW && RandomX == other.RandomX && RandomY == other.RandomY && RandomZ == other.RandomZ;
|
||||
public override bool Equals(ConvertMapping<ConvertValue> other) => base.Equals(other) && Equals(other as ManiaConvertMapping);
|
||||
}
|
||||
|
||||
public struct ConvertValue : IEquatable<ConvertValue>
|
||||
{
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Mania.Difficulty;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
public class ManiaDifficultyCalculatorTest : DifficultyCalculatorTest
|
||||
{
|
||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
|
||||
|
||||
[TestCase(2.3683365342338796d, "diffcalc-test")]
|
||||
public void Test(double expected, string name)
|
||||
=> base.Test(expected, name);
|
||||
|
||||
protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new ManiaDifficultyCalculator(new ManiaRuleset(), beatmap);
|
||||
|
||||
protected override Ruleset CreateRuleset() => new ManiaRuleset();
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Rulesets.Mania.Configuration;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
@@ -6,7 +6,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@@ -14,7 +14,6 @@ using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
@@ -142,7 +141,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Font = OsuFont.GetFont(size: 14),
|
||||
TextSize = 14,
|
||||
Text = description
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
|
||||
|
||||
public int TargetColumns;
|
||||
public bool Dual;
|
||||
public readonly bool IsForCurrentRuleset;
|
||||
|
||||
// Internal for testing purposes
|
||||
@@ -46,14 +45,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
var roundedOverallDifficulty = Math.Round(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);
|
||||
|
||||
if (IsForCurrentRuleset)
|
||||
{
|
||||
TargetColumns = (int)Math.Max(1, roundedCircleSize);
|
||||
if (TargetColumns >= 10)
|
||||
{
|
||||
TargetColumns = TargetColumns / 2;
|
||||
Dual = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float percentSliderOrSpinner = (float)beatmap.HitObjects.Count(h => h is IHasEndTime) / beatmap.HitObjects.Count;
|
||||
@@ -78,22 +70,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
return base.ConvertBeatmap(original);
|
||||
}
|
||||
|
||||
protected override Beatmap<ManiaHitObject> CreateBeatmap()
|
||||
{
|
||||
beatmap = new ManiaBeatmap(new StageDefinition { Columns = TargetColumns });
|
||||
|
||||
if (Dual)
|
||||
beatmap.Stages.Add(new StageDefinition { Columns = TargetColumns });
|
||||
|
||||
return beatmap;
|
||||
}
|
||||
protected override Beatmap<ManiaHitObject> CreateBeatmap() => beatmap = new ManiaBeatmap(new StageDefinition { Columns = TargetColumns });
|
||||
|
||||
protected override IEnumerable<ManiaHitObject> ConvertHitObject(HitObject original, IBeatmap beatmap)
|
||||
{
|
||||
if (original is ManiaHitObject maniaOriginal)
|
||||
var maniaOriginal = original as ManiaHitObject;
|
||||
if (maniaOriginal != null)
|
||||
{
|
||||
yield return maniaOriginal;
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
@@ -108,7 +92,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
|
||||
private readonly List<double> prevNoteTimes = new List<double>(max_notes_for_density);
|
||||
private double density = int.MaxValue;
|
||||
|
||||
private void computeDensity(double newNoteTime)
|
||||
{
|
||||
if (prevNoteTimes.Count == max_notes_for_density)
|
||||
@@ -121,7 +104,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
private double lastTime;
|
||||
private Vector2 lastPosition;
|
||||
private PatternType lastStair = PatternType.Stair;
|
||||
|
||||
private void recordNote(double time, Vector2 position)
|
||||
{
|
||||
lastTime = time;
|
||||
|
||||
@@ -65,7 +65,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
if (originalPattern.HitObjects.Count() == 1)
|
||||
{
|
||||
yield return originalPattern;
|
||||
|
||||
yield break;
|
||||
}
|
||||
|
||||
@@ -136,7 +135,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
{
|
||||
if (convertType.HasFlag(PatternType.LowProbability))
|
||||
return generateNRandomNotes(HitObject.StartTime, 0.78, 0.3, 0);
|
||||
|
||||
return generateNRandomNotes(HitObject.StartTime, 0.85, 0.36, 0.03);
|
||||
}
|
||||
|
||||
@@ -144,7 +142,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
{
|
||||
if (convertType.HasFlag(PatternType.LowProbability))
|
||||
return generateNRandomNotes(HitObject.StartTime, 0.43, 0.08, 0);
|
||||
|
||||
return generateNRandomNotes(HitObject.StartTime, 0.56, 0.18, 0);
|
||||
}
|
||||
|
||||
@@ -152,13 +149,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
{
|
||||
if (convertType.HasFlag(PatternType.LowProbability))
|
||||
return generateNRandomNotes(HitObject.StartTime, 0.3, 0, 0);
|
||||
|
||||
return generateNRandomNotes(HitObject.StartTime, 0.37, 0.08, 0);
|
||||
}
|
||||
|
||||
if (convertType.HasFlag(PatternType.LowProbability))
|
||||
return generateNRandomNotes(HitObject.StartTime, 0.17, 0, 0);
|
||||
|
||||
return generateNRandomNotes(HitObject.StartTime, 0.27, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -116,10 +116,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
}
|
||||
|
||||
if (convertType.HasFlag(PatternType.Cycle) && PreviousPattern.HitObjects.Count() == 1
|
||||
// If we convert to 7K + 1, let's not overload the special key
|
||||
&& (TotalColumns != 8 || lastColumn != 0)
|
||||
// Make sure the last column was not the centre column
|
||||
&& (TotalColumns % 2 == 0 || lastColumn != TotalColumns / 2))
|
||||
// If we convert to 7K + 1, let's not overload the special key
|
||||
&& (TotalColumns != 8 || lastColumn != 0)
|
||||
// Make sure the last column was not the centre column
|
||||
&& (TotalColumns % 2 == 0 || lastColumn != TotalColumns / 2))
|
||||
{
|
||||
// Generate a new pattern by cycling backwards (similar to Reverse but for only one hit object)
|
||||
int column = RandomStart + TotalColumns - lastColumn - 1;
|
||||
@@ -172,7 +172,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
return pattern = generateRandomPatternWithMirrored(0.12, 0.38, 0.12);
|
||||
if (ConversionDifficulty > 4)
|
||||
return pattern = generateRandomPatternWithMirrored(0.12, 0.17, 0);
|
||||
|
||||
return pattern = generateRandomPatternWithMirrored(0.12, 0, 0);
|
||||
}
|
||||
|
||||
@@ -180,7 +179,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
{
|
||||
if (convertType.HasFlag(PatternType.LowProbability))
|
||||
return pattern = generateRandomPattern(0.78, 0.42, 0, 0);
|
||||
|
||||
return pattern = generateRandomPattern(1, 0.62, 0, 0);
|
||||
}
|
||||
|
||||
@@ -188,7 +186,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
{
|
||||
if (convertType.HasFlag(PatternType.LowProbability))
|
||||
return pattern = generateRandomPattern(0.35, 0.08, 0, 0);
|
||||
|
||||
return pattern = generateRandomPattern(0.52, 0.15, 0, 0);
|
||||
}
|
||||
|
||||
@@ -196,7 +193,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
{
|
||||
if (convertType.HasFlag(PatternType.LowProbability))
|
||||
return pattern = generateRandomPattern(0.18, 0, 0, 0);
|
||||
|
||||
return pattern = generateRandomPattern(0.45, 0, 0, 0);
|
||||
}
|
||||
|
||||
@@ -254,7 +250,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
}
|
||||
else
|
||||
last = GetRandomColumn();
|
||||
|
||||
return last;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
return 4;
|
||||
if (val >= 1 - p3)
|
||||
return 3;
|
||||
|
||||
return val >= 1 - p2 ? 2 : 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,63 +12,51 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
internal enum PatternType
|
||||
{
|
||||
None = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Keep the same as last row.
|
||||
/// </summary>
|
||||
ForceStack = 1 << 0,
|
||||
|
||||
/// <summary>
|
||||
/// Keep different from last row.
|
||||
/// </summary>
|
||||
ForceNotStack = 1 << 1,
|
||||
|
||||
/// <summary>
|
||||
/// Keep as single note at its original position.
|
||||
/// </summary>
|
||||
KeepSingle = 1 << 2,
|
||||
|
||||
/// <summary>
|
||||
/// Use a lower random value.
|
||||
/// </summary>
|
||||
LowProbability = 1 << 3,
|
||||
|
||||
/// <summary>
|
||||
/// Reserved.
|
||||
/// </summary>
|
||||
Alternate = 1 << 4,
|
||||
|
||||
/// <summary>
|
||||
/// Ignore the repeat count.
|
||||
/// </summary>
|
||||
ForceSigSlider = 1 << 5,
|
||||
|
||||
/// <summary>
|
||||
/// Convert slider to circle.
|
||||
/// </summary>
|
||||
ForceNotSlider = 1 << 6,
|
||||
|
||||
/// <summary>
|
||||
/// Notes gathered together.
|
||||
/// </summary>
|
||||
Gathered = 1 << 7,
|
||||
Mirror = 1 << 8,
|
||||
|
||||
/// <summary>
|
||||
/// Change 0 -> 6.
|
||||
/// </summary>
|
||||
Reverse = 1 << 9,
|
||||
|
||||
/// <summary>
|
||||
/// 1 -> 5 -> 1 -> 5 like reverse.
|
||||
/// </summary>
|
||||
Cycle = 1 << 10,
|
||||
|
||||
/// <summary>
|
||||
/// Next note will be at column + 1.
|
||||
/// </summary>
|
||||
Stair = 1 << 11,
|
||||
|
||||
/// <summary>
|
||||
/// Next note will be at column - 1.
|
||||
/// </summary>
|
||||
|
||||
@@ -2,11 +2,17 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
{
|
||||
public class ManiaDifficultyAttributes : DifficultyAttributes
|
||||
{
|
||||
public double GreatHitWindow;
|
||||
|
||||
public ManiaDifficultyAttributes(Mod[] mods, double starRating)
|
||||
: base(mods, starRating)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +1,34 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Difficulty.Skills;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Mania.Difficulty.Skills;
|
||||
using osu.Game.Rulesets.Mania.Mods;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
{
|
||||
public class ManiaDifficultyCalculator : DifficultyCalculator
|
||||
internal class ManiaDifficultyCalculator : DifficultyCalculator
|
||||
{
|
||||
private const double star_scaling_factor = 0.018;
|
||||
|
||||
/// <summary>
|
||||
/// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size strain_step.
|
||||
/// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain.
|
||||
/// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage.
|
||||
/// </summary>
|
||||
private const double strain_step = 400;
|
||||
|
||||
/// <summary>
|
||||
/// The weighting of each strain value decays to this number * it's previous value
|
||||
/// </summary>
|
||||
private const double decay_weight = 0.9;
|
||||
|
||||
private readonly bool isForCurrentRuleset;
|
||||
|
||||
public ManiaDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
|
||||
@@ -27,70 +37,108 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
isForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo);
|
||||
}
|
||||
|
||||
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
|
||||
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
|
||||
{
|
||||
if (beatmap.HitObjects.Count == 0)
|
||||
return new ManiaDifficultyAttributes { Mods = mods };
|
||||
if (!beatmap.HitObjects.Any())
|
||||
return new ManiaDifficultyAttributes(mods, 0);
|
||||
|
||||
return new ManiaDifficultyAttributes
|
||||
var difficultyHitObjects = new List<ManiaHitObjectDifficulty>();
|
||||
|
||||
int columnCount = ((ManiaBeatmap)beatmap).TotalColumns;
|
||||
|
||||
// Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure.
|
||||
// Note: Stable sort is done so that the ordering of hitobjects with equal start times doesn't change
|
||||
difficultyHitObjects.AddRange(beatmap.HitObjects.Select(h => new ManiaHitObjectDifficulty((ManiaHitObject)h, columnCount)).OrderBy(h => h.BaseHitObject.StartTime));
|
||||
|
||||
if (!calculateStrainValues(difficultyHitObjects, timeRate))
|
||||
return new DifficultyAttributes(mods, 0);
|
||||
|
||||
double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor;
|
||||
|
||||
return new ManiaDifficultyAttributes(mods, starRating)
|
||||
{
|
||||
StarRating = difficultyValue(skills) * star_scaling_factor,
|
||||
Mods = mods,
|
||||
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future
|
||||
GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / clockRate,
|
||||
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be remoevd in the future
|
||||
GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate
|
||||
};
|
||||
}
|
||||
|
||||
private double difficultyValue(Skill[] skills)
|
||||
private bool calculateStrainValues(List<ManiaHitObjectDifficulty> objects, double timeRate)
|
||||
{
|
||||
// Preprocess the strains to find the maximum overall + individual (aggregate) strain from each section
|
||||
var overall = skills.OfType<Overall>().Single();
|
||||
var aggregatePeaks = new List<double>(Enumerable.Repeat(0.0, overall.StrainPeaks.Count));
|
||||
|
||||
foreach (var individual in skills.OfType<Individual>())
|
||||
// Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment.
|
||||
using (var hitObjectsEnumerator = objects.GetEnumerator())
|
||||
{
|
||||
for (int i = 0; i < individual.StrainPeaks.Count; i++)
|
||||
{
|
||||
double aggregate = individual.StrainPeaks[i] + overall.StrainPeaks[i];
|
||||
if (!hitObjectsEnumerator.MoveNext())
|
||||
return false;
|
||||
|
||||
if (aggregate > aggregatePeaks[i])
|
||||
aggregatePeaks[i] = aggregate;
|
||||
ManiaHitObjectDifficulty current = hitObjectsEnumerator.Current;
|
||||
|
||||
// First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject.
|
||||
while (hitObjectsEnumerator.MoveNext())
|
||||
{
|
||||
var next = hitObjectsEnumerator.Current;
|
||||
next?.CalculateStrains(current, timeRate);
|
||||
current = next;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private double calculateDifficulty(List<ManiaHitObjectDifficulty> objects, double timeRate)
|
||||
{
|
||||
double actualStrainStep = strain_step * timeRate;
|
||||
|
||||
// Find the highest strain value within each strain step
|
||||
List<double> highestStrains = new List<double>();
|
||||
double intervalEndTime = actualStrainStep;
|
||||
double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval
|
||||
|
||||
ManiaHitObjectDifficulty previousHitObject = null;
|
||||
foreach (var hitObject in objects)
|
||||
{
|
||||
// While we are beyond the current interval push the currently available maximum to our strain list
|
||||
while (hitObject.BaseHitObject.StartTime > intervalEndTime)
|
||||
{
|
||||
highestStrains.Add(maximumStrain);
|
||||
|
||||
// The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay
|
||||
// until the beginning of the next interval.
|
||||
if (previousHitObject == null)
|
||||
{
|
||||
maximumStrain = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
double individualDecay = Math.Pow(ManiaHitObjectDifficulty.INDIVIDUAL_DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000);
|
||||
double overallDecay = Math.Pow(ManiaHitObjectDifficulty.OVERALL_DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000);
|
||||
maximumStrain = previousHitObject.IndividualStrain * individualDecay + previousHitObject.OverallStrain * overallDecay;
|
||||
}
|
||||
|
||||
// Go to the next time interval
|
||||
intervalEndTime += actualStrainStep;
|
||||
}
|
||||
|
||||
// Obtain maximum strain
|
||||
double strain = hitObject.IndividualStrain + hitObject.OverallStrain;
|
||||
maximumStrain = Math.Max(strain, maximumStrain);
|
||||
|
||||
previousHitObject = hitObject;
|
||||
}
|
||||
|
||||
aggregatePeaks.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain.
|
||||
|
||||
// Build the weighted sum over the highest strains for each interval
|
||||
double difficulty = 0;
|
||||
double weight = 1;
|
||||
highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain.
|
||||
|
||||
// Difficulty is the weighted sum of the highest strains from every section.
|
||||
foreach (double strain in aggregatePeaks)
|
||||
foreach (double strain in highestStrains)
|
||||
{
|
||||
difficulty += strain * weight;
|
||||
weight *= 0.9;
|
||||
difficulty += weight * strain;
|
||||
weight *= decay_weight;
|
||||
}
|
||||
|
||||
return difficulty;
|
||||
}
|
||||
|
||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate)
|
||||
{
|
||||
for (int i = 1; i < beatmap.HitObjects.Count; i++)
|
||||
yield return new ManiaDifficultyHitObject(beatmap.HitObjects[i], beatmap.HitObjects[i - 1], clockRate);
|
||||
}
|
||||
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap)
|
||||
{
|
||||
int columnCount = ((ManiaBeatmap)beatmap).TotalColumns;
|
||||
|
||||
var skills = new List<Skill> { new Overall(columnCount) };
|
||||
|
||||
for (int i = 0; i < columnCount; i++)
|
||||
skills.Add(new Individual(i, columnCount));
|
||||
|
||||
return skills.ToArray();
|
||||
}
|
||||
|
||||
protected override Mod[] DifficultyAdjustmentMods
|
||||
{
|
||||
get
|
||||
|
||||
@@ -114,8 +114,8 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
// Lots of arbitrary values from testing.
|
||||
// Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution
|
||||
double accuracyValue = Math.Max(0.0, 0.2 - (Attributes.GreatHitWindow - 34) * 0.006667)
|
||||
* strainValue
|
||||
* Math.Pow(Math.Max(0.0, scaledScore - 960000) / 40000, 1.1);
|
||||
* strainValue
|
||||
* Math.Pow(Math.Max(0.0, scaledScore - 960000) / 40000, 1.1);
|
||||
|
||||
// Bonus for many hitcircles - it's harder to keep good accuracy up for longer
|
||||
// accuracyValue *= Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Difficulty.Preprocessing
|
||||
{
|
||||
public class ManiaDifficultyHitObject : DifficultyHitObject
|
||||
{
|
||||
public new ManiaHitObject BaseObject => (ManiaHitObject)base.BaseObject;
|
||||
|
||||
public ManiaDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate)
|
||||
: base(hitObject, lastObject, clockRate)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Difficulty.Skills;
|
||||
using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
||||
{
|
||||
public class Individual : Skill
|
||||
{
|
||||
protected override double SkillMultiplier => 1;
|
||||
protected override double StrainDecayBase => 0.125;
|
||||
|
||||
private readonly double[] holdEndTimes;
|
||||
|
||||
private readonly int column;
|
||||
|
||||
public Individual(int column, int columnCount)
|
||||
{
|
||||
this.column = column;
|
||||
|
||||
holdEndTimes = new double[columnCount];
|
||||
}
|
||||
|
||||
protected override double StrainValueOf(DifficultyHitObject current)
|
||||
{
|
||||
var maniaCurrent = (ManiaDifficultyHitObject)current;
|
||||
var endTime = (maniaCurrent.BaseObject as HoldNote)?.EndTime ?? maniaCurrent.BaseObject.StartTime;
|
||||
|
||||
try
|
||||
{
|
||||
if (maniaCurrent.BaseObject.Column != column)
|
||||
return 0;
|
||||
|
||||
// We give a slight bonus if something is held meanwhile
|
||||
return holdEndTimes.Any(t => t > endTime) ? 2.5 : 2;
|
||||
}
|
||||
finally
|
||||
{
|
||||
holdEndTimes[maniaCurrent.BaseObject.Column] = endTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Difficulty.Skills;
|
||||
using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
||||
{
|
||||
public class Overall : Skill
|
||||
{
|
||||
protected override double SkillMultiplier => 1;
|
||||
protected override double StrainDecayBase => 0.3;
|
||||
|
||||
private readonly double[] holdEndTimes;
|
||||
|
||||
private readonly int columnCount;
|
||||
|
||||
public Overall(int columnCount)
|
||||
{
|
||||
this.columnCount = columnCount;
|
||||
|
||||
holdEndTimes = new double[columnCount];
|
||||
}
|
||||
|
||||
protected override double StrainValueOf(DifficultyHitObject current)
|
||||
{
|
||||
var maniaCurrent = (ManiaDifficultyHitObject)current;
|
||||
var endTime = (maniaCurrent.BaseObject as HoldNote)?.EndTime ?? maniaCurrent.BaseObject.StartTime;
|
||||
|
||||
double holdFactor = 1.0; // Factor in case something else is held
|
||||
double holdAddition = 0; // Addition to the current note in case it's a hold and has to be released awkwardly
|
||||
|
||||
for (int i = 0; i < columnCount; i++)
|
||||
{
|
||||
// If there is at least one other overlapping end or note, then we get an addition, buuuuuut...
|
||||
if (current.BaseObject.StartTime < holdEndTimes[i] && endTime > holdEndTimes[i])
|
||||
holdAddition = 1.0;
|
||||
|
||||
// ... this addition only is valid if there is _no_ other note with the same ending.
|
||||
// Releasing multiple notes at the same time is just as easy as releasing one
|
||||
if (endTime == holdEndTimes[i])
|
||||
holdAddition = 0;
|
||||
|
||||
// We give a slight bonus if something is held meanwhile
|
||||
if (holdEndTimes[i] > endTime)
|
||||
holdFactor = 1.25;
|
||||
}
|
||||
|
||||
holdEndTimes[maniaCurrent.BaseObject.Column] = endTime;
|
||||
|
||||
return (1 + holdAddition) * holdFactor;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
@@ -19,7 +19,6 @@ namespace osu.Game.Rulesets.Mania
|
||||
{
|
||||
[Description("Special 1")]
|
||||
Special1 = 1,
|
||||
|
||||
[Description("Special 2")]
|
||||
Special2,
|
||||
|
||||
@@ -27,55 +26,38 @@ namespace osu.Game.Rulesets.Mania
|
||||
// above at a later time, without breaking replays/configs.
|
||||
[Description("Key 1")]
|
||||
Key1 = 10,
|
||||
|
||||
[Description("Key 2")]
|
||||
Key2,
|
||||
|
||||
[Description("Key 3")]
|
||||
Key3,
|
||||
|
||||
[Description("Key 4")]
|
||||
Key4,
|
||||
|
||||
[Description("Key 5")]
|
||||
Key5,
|
||||
|
||||
[Description("Key 6")]
|
||||
Key6,
|
||||
|
||||
[Description("Key 7")]
|
||||
Key7,
|
||||
|
||||
[Description("Key 8")]
|
||||
Key8,
|
||||
|
||||
[Description("Key 9")]
|
||||
Key9,
|
||||
|
||||
[Description("Key 10")]
|
||||
Key10,
|
||||
|
||||
[Description("Key 11")]
|
||||
Key11,
|
||||
|
||||
[Description("Key 12")]
|
||||
Key12,
|
||||
|
||||
[Description("Key 13")]
|
||||
Key13,
|
||||
|
||||
[Description("Key 14")]
|
||||
Key14,
|
||||
|
||||
[Description("Key 15")]
|
||||
Key15,
|
||||
|
||||
[Description("Key 16")]
|
||||
Key16,
|
||||
|
||||
[Description("Key 17")]
|
||||
Key17,
|
||||
|
||||
[Description("Key 18")]
|
||||
Key18,
|
||||
}
|
||||
|
||||
@@ -330,12 +330,12 @@ namespace osu.Game.Rulesets.Mania
|
||||
for (int i = LeftKeys.Length - columns / 2; i < LeftKeys.Length; i++)
|
||||
bindings.Add(new KeyBinding(LeftKeys[i], currentNormalAction++));
|
||||
|
||||
if (columns % 2 == 1)
|
||||
bindings.Add(new KeyBinding(SpecialKey, SpecialAction));
|
||||
|
||||
for (int i = 0; i < columns / 2; i++)
|
||||
bindings.Add(new KeyBinding(RightKeys[i], currentNormalAction++));
|
||||
|
||||
if (columns % 2 == 1)
|
||||
bindings.Add(new KeyBinding(SpecialKey, SpecialAction));
|
||||
|
||||
nextNormalAction = currentNormalAction;
|
||||
return bindings;
|
||||
}
|
||||
@@ -349,7 +349,6 @@ namespace osu.Game.Rulesets.Mania
|
||||
/// Number of columns in this stage lies at (item - Single).
|
||||
/// </summary>
|
||||
Single = 0,
|
||||
|
||||
/// <summary>
|
||||
/// Columns are grouped into two stages.
|
||||
/// Overall number of columns lies at (item - Dual), further computation is required for
|
||||
|
||||
@@ -37,11 +37,11 @@ namespace osu.Game.Rulesets.Mania.MathUtils
|
||||
/// <returns>The random value.</returns>
|
||||
public uint NextUInt()
|
||||
{
|
||||
uint t = X ^ (X << 11);
|
||||
uint t = X ^ X << 11;
|
||||
X = Y;
|
||||
Y = Z;
|
||||
Z = W;
|
||||
return W = W ^ (W >> 19) ^ t ^ (t >> 8);
|
||||
return W = W ^ W >> 19 ^ t ^ t >> 8;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter
|
||||
public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter, IApplicableToBeatmap<ManiaHitObject>
|
||||
{
|
||||
public override string Name => "Dual Stages";
|
||||
public override string Acronym => "DS";
|
||||
@@ -27,7 +29,24 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
if (isForCurrentRuleset)
|
||||
return;
|
||||
|
||||
mbc.Dual = true;
|
||||
mbc.TargetColumns *= 2;
|
||||
}
|
||||
|
||||
public void ApplyToBeatmap(Beatmap<ManiaHitObject> beatmap)
|
||||
{
|
||||
if (isForCurrentRuleset)
|
||||
return;
|
||||
|
||||
var maniaBeatmap = (ManiaBeatmap)beatmap;
|
||||
|
||||
var newDefinitions = new List<StageDefinition>();
|
||||
foreach (var existing in maniaBeatmap.Stages)
|
||||
{
|
||||
newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 });
|
||||
newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 });
|
||||
}
|
||||
|
||||
maniaBeatmap.Stages = newDefinitions;
|
||||
}
|
||||
|
||||
public PlayfieldType PlayfieldType => PlayfieldType.Dual;
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Caching;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
@@ -52,7 +51,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnComboChange(ValueChangedEvent<int> e)
|
||||
protected override void OnComboChange(int newCombo)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
||||
@@ -76,16 +75,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
AddNested(Tail);
|
||||
}
|
||||
|
||||
protected override void OnDirectionChanged(ValueChangedEvent<ScrollingDirection> e)
|
||||
protected override void OnDirectionChanged(ScrollingDirection direction)
|
||||
{
|
||||
base.OnDirectionChanged(e);
|
||||
base.OnDirectionChanged(direction);
|
||||
|
||||
bodyPiece.Anchor = bodyPiece.Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
|
||||
bodyPiece.Anchor = bodyPiece.Origin = direction == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
|
||||
}
|
||||
|
||||
public override Color4 AccentColour
|
||||
{
|
||||
get => base.AccentColour;
|
||||
get { return base.AccentColour; }
|
||||
set
|
||||
{
|
||||
base.AccentColour = value;
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
|
||||
public override Color4 AccentColour
|
||||
{
|
||||
get => base.AccentColour;
|
||||
get { return base.AccentColour; }
|
||||
set
|
||||
{
|
||||
base.AccentColour = value;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
@@ -41,9 +41,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
|
||||
protected override bool ShouldBeAlive => AlwaysAlive || base.ShouldBeAlive;
|
||||
|
||||
protected virtual void OnDirectionChanged(ValueChangedEvent<ScrollingDirection> e)
|
||||
protected virtual void OnDirectionChanged(ScrollingDirection direction)
|
||||
{
|
||||
Anchor = Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
|
||||
Anchor = Origin = direction == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Graphics;
|
||||
@@ -32,16 +31,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
InternalChild = headPiece = new NotePiece();
|
||||
}
|
||||
|
||||
protected override void OnDirectionChanged(ValueChangedEvent<ScrollingDirection> e)
|
||||
protected override void OnDirectionChanged(ScrollingDirection direction)
|
||||
{
|
||||
base.OnDirectionChanged(e);
|
||||
base.OnDirectionChanged(direction);
|
||||
|
||||
headPiece.Anchor = headPiece.Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
|
||||
headPiece.Anchor = headPiece.Origin = direction == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
|
||||
}
|
||||
|
||||
public override Color4 AccentColour
|
||||
{
|
||||
get => base.AccentColour;
|
||||
get { return base.AccentColour; }
|
||||
set
|
||||
{
|
||||
base.AccentColour = value;
|
||||
|
||||
@@ -77,12 +77,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
|
||||
|
||||
public Color4 AccentColour
|
||||
{
|
||||
get => accentColour;
|
||||
get { return accentColour; }
|
||||
set
|
||||
{
|
||||
if (accentColour == value)
|
||||
return;
|
||||
|
||||
accentColour = value;
|
||||
|
||||
updateAccentColour();
|
||||
@@ -91,7 +90,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
|
||||
|
||||
public bool Hitting
|
||||
{
|
||||
get => hitting;
|
||||
get { return hitting; }
|
||||
set
|
||||
{
|
||||
hitting = value;
|
||||
|
||||
@@ -35,15 +35,13 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
|
||||
}
|
||||
|
||||
private Color4 accentColour;
|
||||
|
||||
public Color4 AccentColour
|
||||
{
|
||||
get => accentColour;
|
||||
get { return accentColour; }
|
||||
set
|
||||
{
|
||||
if (accentColour == value)
|
||||
return;
|
||||
|
||||
accentColour = value;
|
||||
|
||||
updateGlow();
|
||||
|
||||
@@ -78,8 +78,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
|
||||
|
||||
public Color4 AccentColour
|
||||
{
|
||||
get => Colour;
|
||||
set => Colour = value;
|
||||
get { return Colour; }
|
||||
set { Colour = value; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Configuration;
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
@@ -49,22 +49,20 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
|
||||
private void load(IScrollingInfo scrollingInfo)
|
||||
{
|
||||
direction.BindTo(scrollingInfo.Direction);
|
||||
direction.BindValueChanged(dir =>
|
||||
direction.BindValueChanged(direction =>
|
||||
{
|
||||
colouredBox.Anchor = colouredBox.Origin = dir.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
|
||||
colouredBox.Anchor = colouredBox.Origin = direction == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
|
||||
}, true);
|
||||
}
|
||||
|
||||
private Color4 accentColour;
|
||||
|
||||
public Color4 AccentColour
|
||||
{
|
||||
get => accentColour;
|
||||
get { return accentColour; }
|
||||
set
|
||||
{
|
||||
if (accentColour == value)
|
||||
return;
|
||||
|
||||
accentColour = value;
|
||||
|
||||
colouredBox.Colour = AccentColour.Lighten(0.9f);
|
||||
|
||||
@@ -17,10 +17,9 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
public double EndTime => StartTime + Duration;
|
||||
|
||||
private double duration;
|
||||
|
||||
public double Duration
|
||||
{
|
||||
get => duration;
|
||||
get { return duration; }
|
||||
set
|
||||
{
|
||||
duration = value;
|
||||
@@ -30,7 +29,7 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
|
||||
public override double StartTime
|
||||
{
|
||||
get => base.StartTime;
|
||||
get { return base.StartTime; }
|
||||
set
|
||||
{
|
||||
base.StartTime = value;
|
||||
@@ -41,7 +40,7 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
|
||||
public override int Column
|
||||
{
|
||||
get => base.Column;
|
||||
get { return base.Column; }
|
||||
set
|
||||
{
|
||||
base.Column = value;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Rulesets.Mania.Objects.Types;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
|
||||
public virtual int Column
|
||||
{
|
||||
get => ColumnBindable.Value;
|
||||
get => ColumnBindable;
|
||||
set => ColumnBindable.Value = value;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using System;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects
|
||||
{
|
||||
internal class ManiaHitObjectDifficulty
|
||||
{
|
||||
/// <summary>
|
||||
/// Factor by how much individual / overall strain decays per second.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// These values are results of tweaking a lot and taking into account general feedback.
|
||||
/// </remarks>
|
||||
internal const double INDIVIDUAL_DECAY_BASE = 0.125;
|
||||
internal const double OVERALL_DECAY_BASE = 0.30;
|
||||
|
||||
internal ManiaHitObject BaseHitObject;
|
||||
|
||||
private readonly int beatmapColumnCount;
|
||||
|
||||
private readonly double endTime;
|
||||
private readonly double[] heldUntil;
|
||||
|
||||
/// <summary>
|
||||
/// Measures jacks or more generally: repeated presses of the same button
|
||||
/// </summary>
|
||||
private readonly double[] individualStrains;
|
||||
|
||||
internal double IndividualStrain
|
||||
{
|
||||
get
|
||||
{
|
||||
return individualStrains[BaseHitObject.Column];
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
individualStrains[BaseHitObject.Column] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Measures note density in a way
|
||||
/// </summary>
|
||||
internal double OverallStrain = 1;
|
||||
|
||||
public ManiaHitObjectDifficulty(ManiaHitObject baseHitObject, int columnCount)
|
||||
{
|
||||
BaseHitObject = baseHitObject;
|
||||
|
||||
endTime = (baseHitObject as IHasEndTime)?.EndTime ?? baseHitObject.StartTime;
|
||||
|
||||
beatmapColumnCount = columnCount;
|
||||
heldUntil = new double[beatmapColumnCount];
|
||||
individualStrains = new double[beatmapColumnCount];
|
||||
|
||||
for (int i = 0; i < beatmapColumnCount; ++i)
|
||||
{
|
||||
individualStrains[i] = 0;
|
||||
heldUntil[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
internal void CalculateStrains(ManiaHitObjectDifficulty previousHitObject, double timeRate)
|
||||
{
|
||||
// TODO: Factor in holds
|
||||
double timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate;
|
||||
double individualDecay = Math.Pow(INDIVIDUAL_DECAY_BASE, timeElapsed / 1000);
|
||||
double overallDecay = Math.Pow(OVERALL_DECAY_BASE, timeElapsed / 1000);
|
||||
|
||||
double holdFactor = 1.0; // Factor to all additional strains in case something else is held
|
||||
double holdAddition = 0; // Addition to the current note in case it's a hold and has to be released awkwardly
|
||||
|
||||
// Fill up the heldUntil array
|
||||
for (int i = 0; i < beatmapColumnCount; ++i)
|
||||
{
|
||||
heldUntil[i] = previousHitObject.heldUntil[i];
|
||||
|
||||
// If there is at least one other overlapping end or note, then we get an addition, buuuuuut...
|
||||
if (BaseHitObject.StartTime < heldUntil[i] && endTime > heldUntil[i])
|
||||
{
|
||||
holdAddition = 1.0;
|
||||
}
|
||||
|
||||
// ... this addition only is valid if there is _no_ other note with the same ending. Releasing multiple notes at the same time is just as easy as releasing 1
|
||||
if (endTime == heldUntil[i])
|
||||
{
|
||||
holdAddition = 0;
|
||||
}
|
||||
|
||||
// We give a slight bonus to everything if something is held meanwhile
|
||||
if (heldUntil[i] > endTime)
|
||||
{
|
||||
holdFactor = 1.25;
|
||||
}
|
||||
|
||||
// Decay individual strains
|
||||
individualStrains[i] = previousHitObject.individualStrains[i] * individualDecay;
|
||||
}
|
||||
|
||||
heldUntil[BaseHitObject.Column] = endTime;
|
||||
|
||||
// Increase individual strain in own column
|
||||
IndividualStrain += 2.0 * holdFactor;
|
||||
|
||||
OverallStrain = previousHitObject.OverallStrain * overallDecay + (1.0 + holdAddition) * holdFactor;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
private static readonly IReadOnlyDictionary<HitResult, (double od0, double od5, double od10)> base_ranges = new Dictionary<HitResult, (double, double, double)>
|
||||
{
|
||||
{ HitResult.Perfect, (44.8, 38.8, 27.8) },
|
||||
{ HitResult.Great, (128, 98, 68) },
|
||||
{ HitResult.Great, (128, 98, 68 ) },
|
||||
{ HitResult.Good, (194, 164, 134) },
|
||||
{ HitResult.Ok, (254, 224, 194) },
|
||||
{ HitResult.Meh, (302, 272, 242) },
|
||||
|
||||
@@ -1,180 +0,0 @@
|
||||
osu file format v14
|
||||
|
||||
[General]
|
||||
Mode: 3
|
||||
|
||||
[Difficulty]
|
||||
CircleSize:4
|
||||
OverallDifficulty:7
|
||||
ApproachRate:8.3
|
||||
SliderMultiplier:1.6
|
||||
SliderTickRate:1
|
||||
|
||||
[TimingPoints]
|
||||
500,500,4,2,1,50,1,0
|
||||
37500,-50,4,2,1,50,0,0
|
||||
41500,-25,4,2,1,50,0,0
|
||||
|
||||
[HitObjects]
|
||||
// jacks spaced 1/1 beat apart
|
||||
64,192,0,1,0,0:0:0:0:
|
||||
64,192,500,1,0,0:0:0:0:
|
||||
64,192,1000,1,0,0:0:0:0:
|
||||
64,192,1500,1,0,0:0:0:0:
|
||||
64,192,2000,1,0,0:0:0:0:
|
||||
64,192,2500,1,0,0:0:0:0:
|
||||
|
||||
// jacks spaced 1/2 beat apart
|
||||
64,192,3500,1,0,0:0:0:0:
|
||||
64,192,3750,1,0,0:0:0:0:
|
||||
64,192,4000,1,0,0:0:0:0:
|
||||
64,192,4250,1,0,0:0:0:0:
|
||||
64,192,4500,1,0,0:0:0:0:
|
||||
64,192,4750,1,0,0:0:0:0:
|
||||
64,192,5000,1,0,0:0:0:0:
|
||||
64,192,6000,1,0,0:0:0:0:
|
||||
|
||||
// doubles jacks spaced 1/2 beat apart
|
||||
192,192,6000,1,0,0:0:0:0:
|
||||
64,192,6250,1,0,0:0:0:0:
|
||||
192,192,6250,1,0,0:0:0:0:
|
||||
64,192,6500,1,0,0:0:0:0:
|
||||
192,192,6500,1,0,0:0:0:0:
|
||||
64,192,6750,1,0,0:0:0:0:
|
||||
192,192,6750,1,0,0:0:0:0:
|
||||
64,192,7000,1,0,0:0:0:0:
|
||||
192,192,7000,1,0,0:0:0:0:
|
||||
64,192,7250,1,0,0:0:0:0:
|
||||
192,192,7250,1,0,0:0:0:0:
|
||||
64,192,7500,1,0,0:0:0:0:
|
||||
192,192,7500,1,0,0:0:0:0:
|
||||
|
||||
// trill spaced 1/2 beat apart
|
||||
64,192,8500,1,0,0:0:0:0:
|
||||
192,192,8750,1,0,0:0:0:0:
|
||||
64,192,9000,1,0,0:0:0:0:
|
||||
192,192,9250,1,0,0:0:0:0:
|
||||
64,192,9500,1,0,0:0:0:0:
|
||||
192,192,9750,1,0,0:0:0:0:
|
||||
64,192,10000,1,0,0:0:0:0:
|
||||
192,192,10250,1,0,0:0:0:0:
|
||||
64,192,10500,1,0,0:0:0:0:
|
||||
|
||||
// stair spaced 1/4 apart
|
||||
64,192,11500,1,0,0:0:0:0:
|
||||
192,192,11625,1,0,0:0:0:0:
|
||||
320,192,11750,1,0,0:0:0:0:
|
||||
448,192,11875,1,0,0:0:0:0:
|
||||
320,192,12000,1,0,0:0:0:0:
|
||||
192,192,12125,1,0,0:0:0:0:
|
||||
64,192,12250,1,0,0:0:0:0:
|
||||
192,192,12375,1,0,0:0:0:0:
|
||||
320,192,12500,1,0,0:0:0:0:
|
||||
448,192,12625,1,0,0:0:0:0:
|
||||
|
||||
// jumpstreams?
|
||||
64,192,13500,1,0,0:0:0:0:
|
||||
192,192,13625,1,0,0:0:0:0:
|
||||
320,192,13750,1,0,0:0:0:0:
|
||||
448,192,13875,1,0,0:0:0:0:
|
||||
320,192,14000,1,0,0:0:0:0:
|
||||
192,192,14000,1,0,0:0:0:0:
|
||||
64,192,14125,1,0,0:0:0:0:
|
||||
192,192,14250,1,0,0:0:0:0:
|
||||
320,192,14250,1,0,0:0:0:0:
|
||||
448,192,14250,1,0,0:0:0:0:
|
||||
64,192,14375,1,0,0:0:0:0:
|
||||
64,192,14500,1,0,0:0:0:0:
|
||||
320,192,14625,1,0,0:0:0:0:
|
||||
448,192,14625,1,0,0:0:0:0:
|
||||
192,192,14625,1,0,0:0:0:0:
|
||||
192,192,14750,1,0,0:0:0:0:
|
||||
64,192,14875,1,0,0:0:0:0:
|
||||
192,192,15000,1,0,0:0:0:0:
|
||||
320,192,15125,1,0,0:0:0:0:
|
||||
448,192,15125,1,0,0:0:0:0:
|
||||
|
||||
// double... jumps?
|
||||
64,192,16000,1,0,0:0:0:0:
|
||||
64,192,16250,1,0,0:0:0:0:
|
||||
192,192,16250,1,0,0:0:0:0:
|
||||
192,192,16500,1,0,0:0:0:0:
|
||||
320,192,16500,1,0,0:0:0:0:
|
||||
320,192,16750,1,0,0:0:0:0:
|
||||
448,192,16750,1,0,0:0:0:0:
|
||||
448,192,17000,1,0,0:0:0:0:
|
||||
|
||||
// notes alongside hold
|
||||
64,192,18000,128,0,18500:0:0:0:0:
|
||||
192,192,18000,1,0,0:0:0:0:
|
||||
192,192,18250,1,0,0:0:0:0:
|
||||
192,192,18500,1,0,0:0:0:0:
|
||||
|
||||
// notes overlapping hold
|
||||
64,192,19500,1,0,0:0:0:0:
|
||||
192,192,19625,128,0,20875:0:0:0:0:
|
||||
64,192,19750,1,0,0:0:0:0:
|
||||
64,192,20000,1,0,0:0:0:0:
|
||||
64,192,20250,1,0,0:0:0:0:
|
||||
64,192,20500,1,0,0:0:0:0:
|
||||
64,192,20750,1,0,0:0:0:0:
|
||||
64,192,21000,1,0,0:0:0:0:
|
||||
|
||||
// simultaneous holds
|
||||
64,192,22000,128,0,23000:0:0:0:0:
|
||||
192,192,22000,128,0,23000:0:0:0:0:
|
||||
320,192,22000,128,0,23000:0:0:0:0:
|
||||
448,192,22000,128,0,23000:0:0:0:0:
|
||||
|
||||
// hold stairs
|
||||
64,192,24500,128,0,25500:0:0:0:0:
|
||||
192,192,24625,128,0,25375:0:0:0:0:
|
||||
320,192,24750,128,0,25250:0:0:0:0:
|
||||
448,192,24875,128,0,25125:0:0:0:0:
|
||||
448,192,25375,128,0,26375:0:0:0:0:
|
||||
320,192,25500,128,0,26250:0:0:0:0:
|
||||
192,192,25625,128,0,26125:0:0:0:0:
|
||||
64,192,25750,128,0,26000:0:0:0:0:
|
||||
|
||||
// quads
|
||||
64,192,26500,1,0,0:0:0:0:
|
||||
64,192,27500,1,0,0:0:0:0:
|
||||
192,192,27500,1,0,0:0:0:0:
|
||||
320,192,27500,1,0,0:0:0:0:
|
||||
448,192,27500,1,0,0:0:0:0:
|
||||
64,192,27750,1,0,0:0:0:0:
|
||||
192,192,27750,1,0,0:0:0:0:
|
||||
320,192,27750,1,0,0:0:0:0:
|
||||
448,192,27750,1,0,0:0:0:0:
|
||||
64,192,28000,1,0,0:0:0:0:
|
||||
192,192,28000,1,0,0:0:0:0:
|
||||
320,192,28000,1,0,0:0:0:0:
|
||||
448,192,28000,1,0,0:0:0:0:
|
||||
64,192,28250,1,0,0:0:0:0:
|
||||
192,192,28250,1,0,0:0:0:0:
|
||||
320,192,28250,1,0,0:0:0:0:
|
||||
448,192,28250,1,0,0:0:0:0:
|
||||
64,192,28500,1,0,0:0:0:0:
|
||||
192,192,28500,1,0,0:0:0:0:
|
||||
320,192,28500,1,0,0:0:0:0:
|
||||
448,192,28500,1,0,0:0:0:0:
|
||||
|
||||
// double-trills
|
||||
64,192,29500,1,0,0:0:0:0:
|
||||
192,192,29500,1,0,0:0:0:0:
|
||||
320,192,29625,1,0,0:0:0:0:
|
||||
448,192,29625,1,0,0:0:0:0:
|
||||
64,192,29750,1,0,0:0:0:0:
|
||||
192,192,29750,1,0,0:0:0:0:
|
||||
320,192,29875,1,0,0:0:0:0:
|
||||
448,192,29875,1,0,0:0:0:0:
|
||||
64,192,30000,1,0,0:0:0:0:
|
||||
192,192,30000,1,0,0:0:0:0:
|
||||
320,192,30125,1,0,0:0:0:0:
|
||||
448,192,30125,1,0,0:0:0:0:
|
||||
64,192,30250,1,0,0:0:0:0:
|
||||
192,192,30250,1,0,0:0:0:0:
|
||||
320,192,30375,1,0,0:0:0:0:
|
||||
448,192,30375,1,0,0:0:0:0:
|
||||
64,192,30500,1,0,0:0:0:0:
|
||||
192,192,30500,1,0,0:0:0:0:
|
||||
@@ -159,6 +159,6 @@ namespace osu.Game.Rulesets.Mania.Scoring
|
||||
}
|
||||
}
|
||||
|
||||
public override HitWindows CreateHitWindows() => new ManiaHitWindows();
|
||||
protected override HitWindows CreateHitWindows() => new ManiaHitWindows();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Mania.UI.Components;
|
||||
@@ -82,30 +82,28 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
TopLevelContainer.Add(explosionContainer.CreateProxy());
|
||||
|
||||
Direction.BindValueChanged(dir =>
|
||||
Direction.BindValueChanged(d =>
|
||||
{
|
||||
hitTargetContainer.Padding = new MarginPadding
|
||||
{
|
||||
Top = dir.NewValue == ScrollingDirection.Up ? ManiaStage.HIT_TARGET_POSITION : 0,
|
||||
Bottom = dir.NewValue == ScrollingDirection.Down ? ManiaStage.HIT_TARGET_POSITION : 0,
|
||||
Top = d == ScrollingDirection.Up ? ManiaStage.HIT_TARGET_POSITION : 0,
|
||||
Bottom = d == ScrollingDirection.Down ? ManiaStage.HIT_TARGET_POSITION : 0,
|
||||
};
|
||||
|
||||
keyArea.Anchor = keyArea.Origin = dir.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
|
||||
keyArea.Anchor = keyArea.Origin= d == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
|
||||
}, true);
|
||||
}
|
||||
|
||||
public override Axes RelativeSizeAxes => Axes.Y;
|
||||
|
||||
private bool isSpecial;
|
||||
|
||||
public bool IsSpecial
|
||||
{
|
||||
get => isSpecial;
|
||||
get { return isSpecial; }
|
||||
set
|
||||
{
|
||||
if (isSpecial == value)
|
||||
return;
|
||||
|
||||
isSpecial = value;
|
||||
|
||||
Width = isSpecial ? special_column_width : column_width;
|
||||
@@ -113,15 +111,13 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
}
|
||||
|
||||
private Color4 accentColour;
|
||||
|
||||
public Color4 AccentColour
|
||||
{
|
||||
get => accentColour;
|
||||
get { return accentColour; }
|
||||
set
|
||||
{
|
||||
if (accentColour == value)
|
||||
return;
|
||||
|
||||
accentColour = value;
|
||||
|
||||
background.AccentColour = value;
|
||||
@@ -160,7 +156,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result)
|
||||
{
|
||||
if (!result.IsHit || !judgedObject.DisplayResult || !DisplayJudgements.Value)
|
||||
if (!result.IsHit || !judgedObject.DisplayResult || !DisplayJudgements)
|
||||
return;
|
||||
|
||||
explosionContainer.Add(new HitExplosion(judgedObject)
|
||||
@@ -171,7 +167,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
public bool OnPressed(ManiaAction action)
|
||||
{
|
||||
if (action != Action.Value)
|
||||
if (action != Action)
|
||||
return false;
|
||||
|
||||
var nextObject =
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
@@ -48,9 +48,9 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
};
|
||||
|
||||
direction.BindTo(scrollingInfo.Direction);
|
||||
direction.BindValueChanged(dir =>
|
||||
direction.BindValueChanged(direction =>
|
||||
{
|
||||
backgroundOverlay.Anchor = backgroundOverlay.Origin = dir.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
|
||||
backgroundOverlay.Anchor = backgroundOverlay.Origin = direction == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
|
||||
updateColours();
|
||||
}, true);
|
||||
}
|
||||
@@ -70,7 +70,6 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
{
|
||||
if (accentColour == value)
|
||||
return;
|
||||
|
||||
accentColour = value;
|
||||
|
||||
updateColours();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@@ -49,9 +49,9 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
private void load(IScrollingInfo scrollingInfo)
|
||||
{
|
||||
direction.BindTo(scrollingInfo.Direction);
|
||||
direction.BindValueChanged(dir =>
|
||||
direction.BindValueChanged(direction =>
|
||||
{
|
||||
Anchor anchor = dir.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
|
||||
Anchor anchor = direction == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
|
||||
|
||||
hitTargetBar.Anchor = hitTargetBar.Origin = anchor;
|
||||
hitTargetLine.Anchor = hitTargetLine.Origin = anchor;
|
||||
@@ -73,7 +73,6 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
{
|
||||
if (accentColour == value)
|
||||
return;
|
||||
|
||||
accentColour = value;
|
||||
|
||||
updateColours();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
@@ -64,11 +64,11 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
};
|
||||
|
||||
direction.BindTo(scrollingInfo.Direction);
|
||||
direction.BindValueChanged(dir =>
|
||||
direction.BindValueChanged(direction =>
|
||||
{
|
||||
gradient.Colour = ColourInfo.GradientVertical(
|
||||
dir.NewValue == ScrollingDirection.Up ? Color4.Black : Color4.Black.Opacity(0),
|
||||
dir.NewValue == ScrollingDirection.Up ? Color4.Black.Opacity(0) : Color4.Black);
|
||||
direction == ScrollingDirection.Up ? Color4.Black : Color4.Black.Opacity(0),
|
||||
direction == ScrollingDirection.Up ? Color4.Black.Opacity(0) : Color4.Black);
|
||||
}, true);
|
||||
}
|
||||
|
||||
@@ -87,7 +87,6 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
||||
{
|
||||
if (accentColour == value)
|
||||
return;
|
||||
|
||||
accentColour = value;
|
||||
|
||||
updateColours();
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
private void load()
|
||||
{
|
||||
if (JudgementText != null)
|
||||
JudgementText.Font = JudgementText.Font.With(size: 25);
|
||||
JudgementText.TextSize = 25;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input;
|
||||
@@ -15,6 +15,7 @@ using osu.Game.Input.Handlers;
|
||||
using osu.Game.Replays;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Configuration;
|
||||
using osu.Game.Rulesets.Mania.Mods;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.Replays;
|
||||
@@ -75,7 +76,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
BarLines.ForEach(Playfield.Add);
|
||||
|
||||
Config.BindWith(ManiaSetting.ScrollDirection, configDirection);
|
||||
configDirection.BindValueChanged(direction => Direction.Value = (ScrollingDirection)direction.NewValue, true);
|
||||
configDirection.BindValueChanged(v => Direction.Value = (ScrollingDirection)v, true);
|
||||
|
||||
Config.BindWith(ManiaSetting.ScrollTime, TimeRange);
|
||||
}
|
||||
@@ -95,7 +96,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
|
||||
|
||||
public override int Variant => (int)(Beatmap.Stages.Count == 1 ? PlayfieldType.Single : PlayfieldType.Dual) + Beatmap.TotalColumns;
|
||||
public override int Variant => (int)(Mods.OfType<IPlayfieldTypeMod>().FirstOrDefault()?.PlayfieldType ?? PlayfieldType.Single) + Beatmap.TotalColumns;
|
||||
|
||||
public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant);
|
||||
|
||||
|
||||
@@ -136,12 +136,12 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
AddColumn(column);
|
||||
}
|
||||
|
||||
Direction.BindValueChanged(dir =>
|
||||
Direction.BindValueChanged(d =>
|
||||
{
|
||||
barLineContainer.Padding = new MarginPadding
|
||||
{
|
||||
Top = dir.NewValue == ScrollingDirection.Up ? HIT_TARGET_POSITION : 0,
|
||||
Bottom = dir.NewValue == ScrollingDirection.Down ? HIT_TARGET_POSITION : 0,
|
||||
Top = d == ScrollingDirection.Up ? HIT_TARGET_POSITION : 0,
|
||||
Bottom = d == ScrollingDirection.Down ? HIT_TARGET_POSITION : 0,
|
||||
};
|
||||
}, true);
|
||||
}
|
||||
@@ -185,7 +185,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result)
|
||||
{
|
||||
if (!judgedObject.DisplayResult || !DisplayJudgements.Value)
|
||||
if (!judgedObject.DisplayResult || !DisplayJudgements)
|
||||
return;
|
||||
|
||||
judgements.Clear();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// 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.
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using Foundation;
|
||||
using osu.Framework.iOS;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// 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.
|
||||
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using UIKit;
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>10.0</string>
|
||||
<string>11.0</string>
|
||||
<key>UIDeviceFamily</key>
|
||||
<array>
|
||||
<integer>1</integer>
|
||||
|
||||
@@ -26,7 +26,9 @@
|
||||
</LinkDescription>
|
||||
<Compile Include="Application.cs" />
|
||||
<Compile Include="AppDelegate.cs" />
|
||||
<Compile Include="..\osu.Game.Rulesets.Osu.Tests\**\*.cs" Exclude="**\obj\**">
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="..\osu.Game.Rulesets.Osu.Tests\*.cs">
|
||||
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
@@ -39,6 +41,32 @@
|
||||
<Project>{C92A607B-1FDD-4954-9F92-03FF547D9080}</Project>
|
||||
<Name>osu.Game.Rulesets.Osu</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\osu-resources\osu.Game.Resources\osu.Game.Resources.csproj">
|
||||
<Project>{D9A367C9-4C1A-489F-9B05-A0CEA2B53B58}</Project>
|
||||
<Name>osu.Game.Resources</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="Xamarin.iOS" />
|
||||
<Reference Include="mscorlib" />
|
||||
<Reference Include="System.IO.Compression" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Humanizer" Version="2.5.16" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2019.122.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.122.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.22.0" />
|
||||
<PackageReference Include="NUnit" Version="3.11.0" />
|
||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||
<PackageReference Include="System.ComponentModel.Annotations" Version="4.5.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.NativeLibs" Version="2019.110.0" ExcludeAssets="all" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Xamarin\iOS\Xamarin.iOS.CSharp.targets" />
|
||||
<Import Project="..\packages\NETStandard.Library.2.0.0\build\netstandard2.0\NETStandard.Library.targets" Condition="Exists('..\packages\NETStandard.Library.2.0.0\build\netstandard2.0\NETStandard.Library.targets')" />
|
||||
|
||||
@@ -33,11 +33,9 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
case Slider slider:
|
||||
foreach (var nested in slider.NestedHitObjects)
|
||||
yield return createConvertValue(nested);
|
||||
|
||||
break;
|
||||
default:
|
||||
yield return createConvertValue(hitObject);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Difficulty;
|
||||
using osu.Game.Rulesets.Osu.Difficulty;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class OsuDifficultyCalculatorTest : DifficultyCalculatorTest
|
||||
{
|
||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
|
||||
|
||||
[TestCase(6.931145117263422, "diffcalc-test")]
|
||||
public void Test(double expected, string name)
|
||||
=> base.Test(expected, name);
|
||||
|
||||
protected override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuDifficultyCalculator(new OsuRuleset(), beatmap);
|
||||
|
||||
protected override Ruleset CreateRuleset() => new OsuRuleset();
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
private GameplayCursor cursor;
|
||||
|
||||
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(CursorTrail) };
|
||||
public override IReadOnlyList<Type> RequiredTypes => new [] { typeof(CursorTrail) };
|
||||
|
||||
public CursorContainer Cursor => cursor;
|
||||
|
||||
|
||||
@@ -89,8 +89,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
private readonly bool auto;
|
||||
|
||||
public TestDrawableHitCircle(HitCircle h, bool auto)
|
||||
: base(h)
|
||||
public TestDrawableHitCircle(HitCircle h, bool auto) : base(h)
|
||||
{
|
||||
this.auto = auto;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user