1
0
mirror of https://github.com/ppy/osu.git synced 2025-03-18 04:49:34 +08:00

Merge branch 'master' into cursor-size-trail

This commit is contained in:
Dean Herbert 2019-10-14 16:35:37 +09:00 committed by GitHub
commit 64a6ca28e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
94 changed files with 1303 additions and 754 deletions

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="CatchRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests/bin/Debug/netcoreapp2.2/osu.Game.Rulesets.Catch.Tests.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests/bin/Debug/netcoreapp3.0/osu.Game.Rulesets.Catch.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Catch.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
@ -12,7 +12,7 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v2.2" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.0" />
<browser url="http://localhost:5000" />
<method v="2">
<option name="Build" enabled="true" />

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="ManiaRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests/bin/Debug/netcoreapp2.2/osu.Game.Rulesets.Mania.Tests.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests/bin/Debug/netcoreapp3.0/osu.Game.Rulesets.Mania.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Mania.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
@ -12,7 +12,7 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v2.2" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.0" />
<browser url="http://localhost:5000" />
<method v="2">
<option name="Build" enabled="true" />

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="OsuRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests/bin/Debug/netcoreapp2.2/osu.Game.Rulesets.Osu.Tests.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests/bin/Debug/netcoreapp3.0/osu.Game.Rulesets.Osu.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Osu.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
@ -12,7 +12,7 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v2.2" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.0" />
<browser url="http://localhost:5000" />
<method v="2">
<option name="Build" enabled="true" />

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="TaikoRuleset (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="Ruleset">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests/bin/Debug/netcoreapp2.2/osu.Game.Rulesets.Taiko.Tests.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests/bin/Debug/netcoreapp3.0/osu.Game.Rulesets.Taiko.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Rulesets.Taiko.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
@ -12,7 +12,7 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v2.2" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.0" />
<browser url="http://localhost:5000" />
<method v="2">
<option name="Build" enabled="true" />

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Tournament" type="DotNetProject" factoryName=".NET Project" folderName="Tournament">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp2.2/osu!.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp3.0/osu!.dll" />
<option name="PROGRAM_PARAMETERS" value="--tournament" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop" />
<option name="PASS_PARENT_ENVS" value="1" />
@ -12,7 +12,7 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v2.2" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.0" />
<method v="2">
<option name="Build" enabled="true" />
</method>

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="osu!" type="DotNetProject" factoryName=".NET Project" folderName="osu!">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp2.2/osu!.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Desktop/bin/Debug/netcoreapp3.0/osu!.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Desktop" />
<option name="PASS_PARENT_ENVS" value="1" />
@ -12,7 +12,7 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v2.2" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.0" />
<method v="2">
<option name="Build" enabled="true" />
</method>

View File

@ -1,6 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="osu! (Tests)" type="DotNetProject" factoryName=".NET Project" folderName="osu!">
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Tests/bin/Debug/netcoreapp2.2/osu.Game.Tests.dll" />
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Tests/bin/Debug/netcoreapp3.0/osu.Game.Tests.dll" />
<option name="PROGRAM_PARAMETERS" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Tests" />
<option name="PASS_PARENT_ENVS" value="1" />
@ -12,7 +12,7 @@
<option name="PROJECT_ARGUMENTS_TRACKING" value="1" />
<option name="PROJECT_WORKING_DIRECTORY_TRACKING" value="1" />
<option name="PROJECT_KIND" value="DotNetCore" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v2.2" />
<option name="PROJECT_TFM" value=".NETCoreApp,Version=v3.0" />
<method v="2">
<option name="Build" enabled="true" />
</method>

32
.vscode/launch.json vendored
View File

@ -6,13 +6,13 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.2/osu!.dll"
"${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp3.0/osu!.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.2:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp3.0:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
@ -23,13 +23,13 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.2/osu!.dll"
"${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp3.0/osu!.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.2:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp3.0:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
@ -40,13 +40,13 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp2.2/osu.Game.Tests.dll"
"${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp3.0/osu.Game.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build tests (Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp2.2:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tests/bin/Debug/netcoreapp3.0:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
@ -56,13 +56,13 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp2.2/osu.Game.Tests.dll"
"${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp3.0/osu.Game.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build tests (Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp2.2:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tests/bin/Release/netcoreapp3.0:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
@ -73,14 +73,14 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.2/osu!.dll",
"${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp3.0/osu!.dll",
"--tournament"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp2.2:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Debug/netcoreapp3.0:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
@ -91,14 +91,14 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.2/osu!.dll",
"${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp3.0/osu!.dll",
"--tournament"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build osu! (Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp2.2:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Desktop/bin/Release/netcoreapp3.0:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
@ -109,14 +109,14 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp2.2/osu.Game.Tournament.Tests.dll",
"${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp3.0/osu.Game.Tournament.Tests.dll",
"--tournament"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build tournament tests (Debug)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp2.2:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp3.0:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"
@ -127,14 +127,14 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp2.2/osu.Game.Tournament.Tests.dll",
"${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp3.0/osu.Game.Tournament.Tests.dll",
"--tournament"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build tournament tests (Release)",
"linux": {
"env": {
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp2.2:${env:LD_LIBRARY_PATH}"
"LD_LIBRARY_PATH": "${workspaceRoot}/osu.Game.Tournament.Tests/bin/Debug/netcoreapp3.0:${env:LD_LIBRARY_PATH}"
}
},
"console": "internalConsole"

2
.vscode/tasks.json vendored
View File

@ -95,7 +95,7 @@
"problemMatcher": "$msCompile"
},
{
"label": "Restore (netcoreapp2.2)",
"label": "Restore (netcoreapp3.0)",
"type": "shell",
"command": "dotnet",
"args": [

View File

@ -18,7 +18,7 @@ GEM
unf (>= 0.0.5, < 1.0.0)
dotenv (2.7.5)
emoji_regex (1.0.1)
excon (0.66.0)
excon (0.67.0)
faraday (0.15.4)
multipart-post (>= 1.2, < 3)
faraday-cookie_jar (0.0.6)
@ -27,7 +27,7 @@ GEM
faraday_middleware (0.13.1)
faraday (>= 0.7.4, < 1.0)
fastimage (2.1.7)
fastlane (2.131.0)
fastlane (2.133.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.3, < 3.0.0)
babosa (>= 1.0.2, < 2.0.0)
@ -37,9 +37,9 @@ GEM
dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (>= 0.1, < 2.0)
excon (>= 0.45.0, < 1.0.0)
faraday (~> 0.9)
faraday (< 0.16.0)
faraday-cookie_jar (~> 0.0.6)
faraday_middleware (~> 0.9)
faraday_middleware (< 0.16.0)
fastimage (>= 2.1.0, < 3.0.0)
gh_inspector (>= 1.1.2, < 2.0.0)
google-api-client (>= 0.21.2, < 0.24.0)
@ -52,7 +52,7 @@ GEM
multipart-post (~> 2.0.0)
plist (>= 3.1.0, < 4.0.0)
public_suffix (~> 2.0.0)
rubyzip (>= 1.2.2, < 2.0.0)
rubyzip (>= 1.3.0, < 2.0.0)
security (= 0.1.3)
simctl (~> 1.6.3)
slack-notifier (>= 2.0.0, < 3.0.0)
@ -102,7 +102,7 @@ GEM
memoist (0.16.0)
mime-types (3.3)
mime-types-data (~> 3.2015)
mime-types-data (3.2019.0904)
mime-types-data (3.2019.1009)
mini_magick (4.9.5)
mini_portile2 (2.4.0)
multi_json (1.13.1)
@ -121,9 +121,9 @@ GEM
uber (< 0.2.0)
retriable (3.1.2)
rouge (2.0.7)
rubyzip (1.2.4)
rubyzip (1.3.0)
security (0.1.3)
signet (0.11.0)
signet (0.12.0)
addressable (~> 2.3)
faraday (~> 0.9)
jwt (>= 1.5, < 3.0)

View File

@ -18,7 +18,7 @@ Detailed changelogs are published on the [official osu! site](https://osu.ppy.sh
## Requirements
- A desktop platform with the [.NET Core SDK 2.2](https://www.microsoft.com/net/learn/get-started) or higher installed.
- A desktop platform with the [.NET Core SDK 3.0](https://www.microsoft.com/net/learn/get-started) or higher installed.
- When running on linux, please have a system-wide ffmpeg installation available to support video decoding.
- When running on Windows 7 or 8.1, **[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/windows-prerequisites?tabs=netcore2x)** may be required to correctly run .NET Core applications if your operating system is not up-to-date with the latest service packs.
- When working with the codebase, we recommend using an IDE with intellisense and syntax highlighting, such as [Visual Studio 2017+](https://visualstudio.microsoft.com/vs/), [Jetbrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/).
@ -78,7 +78,7 @@ On Linux, the environment variable `LD_LIBRARY_PATH` must point to the build dir
For example, you can run osu! with the following command:
```shell
LD_LIBRARY_PATH="$(pwd)/osu.Desktop/bin/Debug/netcoreapp2.2" dotnet run --project osu.Desktop
LD_LIBRARY_PATH="$(pwd)/osu.Desktop/bin/Debug/netcoreapp3.0" dotnet run --project osu.Desktop
```
### Testing with resource/framework modifications

View File

@ -1,6 +1,6 @@
clone_depth: 1
version: '{branch}-{build}'
image: Previous Visual Studio 2017
image: Visual Studio 2019 Preview
test: off
build_script:
- cmd: PowerShell -Version 2.0 .\build.ps1

View File

@ -1,6 +1,6 @@
clone_depth: 1
version: '{build}'
image: Previous Visual Studio 2017
image: Visual Studio 2019 Preview
test: off
skip_non_tags: true
build_script:

65
build.ps1 Normal file → Executable file
View File

@ -1,39 +1,5 @@
##########################################################################
# This is a customized Cake bootstrapper script for PowerShell.
##########################################################################
<#
.SYNOPSIS
This is a Powershell script to bootstrap a Cake build.
.DESCRIPTION
This Powershell script restores NuGet tools (including Cake)
and execute your Cake build script with the parameters you provide.
.PARAMETER Script
The build script to execute.
.PARAMETER Target
The build script target to run.
.PARAMETER Configuration
The build configuration to use.
.PARAMETER Verbosity
Specifies the amount of information to be displayed.
.PARAMETER ShowDescription
Shows description about tasks.
.PARAMETER DryRun
Performs a dry run.
.PARAMETER ScriptArgs
Remaining arguments are added here.
.LINK
https://cakebuild.net
#>
[CmdletBinding()]
Param(
[string]$Script = "build.cake",
[string]$Target,
[string]$Configuration,
[ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")]
@ -45,27 +11,8 @@ Param(
[string[]]$ScriptArgs
)
Write-Host "Preparing to run build script..."
# Determine the script root for resolving other paths.
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"
# 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
# Build Cake arguments
$cakeArguments = @("$Script");
$cakeArguments = "";
if ($Target) { $cakeArguments += "-target=$Target" }
if ($Configuration) { $cakeArguments += "-configuration=$Configuration" }
if ($Verbosity) { $cakeArguments += "-verbosity=$Verbosity" }
@ -74,9 +21,7 @@ if ($DryRun) { $cakeArguments += "-dryrun" }
if ($Experimental) { $cakeArguments += "-experimental" }
$cakeArguments += $ScriptArgs
# Start Cake
Write-Host "Running build script..."
Push-Location -Path $BUILD_DIR
Invoke-Expression "dotnet `"$CAKE_EXECUTABLE`" $cakeArguments"
Pop-Location
exit $LASTEXITCODE
dotnet tool install Cake.Tool --global --version 0.35.0
dotnet cake ./build/build.cake --bootstrap
dotnet cake ./build/build.cake $cakeArguments
exit $LASTEXITCODE

View File

@ -1,18 +1,5 @@
#!/usr/bin/env bash
##########################################################################
# This is a customized Cake bootstrapper script for Shell.
##########################################################################
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"
echo "Installing Cake.Tool..."
dotnet tool install Cake.Tool --global --version 0.35.0
# Parse arguments.
CAKE_ARGUMENTS=()
@ -25,14 +12,6 @@ for i in "$@"; do
shift
done
# Install the required tools locally.
echo "Restoring cake tools..."
dotnet restore $CAKE_CSPROJ --packages $TOOLS_DIR > /dev/null 2>&1
# Search for the CakeBuild binary.
CAKE_BINARY=$(find $CAKE_BINARY_PATH -name "Cake.dll")
# Start Cake
echo "Running build script..."
dotnet "$CAKE_BINARY" $SCRIPT "${CAKE_ARGUMENTS[@]}"
dotnet cake ./build/build.cake --bootstrap
dotnet cake ./build/build.cake "${CAKE_ARGUMENTS[@]}"

View File

@ -1,5 +1,5 @@
#addin "nuget:?package=CodeFileSanity&version=0.0.21"
#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2019.1.1"
#addin "nuget:?package=CodeFileSanity&version=0.0.33"
#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2019.2.1"
#tool "nuget:?package=NVika.MSBuild&version=1.0.1"
var nVikaToolPath = GetFiles("./tools/NVika.MSBuild.*/tools/NVika.exe").First();

View File

@ -1,11 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<PackAsTool>true</PackAsTool>
<TargetFrameworks>netcoreapp2.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Cake" Version="0.34.1" />
<PackageReference Include="Cake.CoreCLR" Version="0.34.1" />
</ItemGroup>
</Project>

View File

@ -61,7 +61,7 @@
<Reference Include="Java.Interop" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.913.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.930.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2019.1011.0" />
</ItemGroup>
</Project>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\osu.Game.props" />
<PropertyGroup Label="Project">
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetFramework>netcoreapp3.0</TargetFramework>
<OutputType>WinExe</OutputType>
<PlatformTarget>AnyCPU</PlatformTarget>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>

View File

@ -7,7 +7,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Debug/netcoreapp2.2/osu.Game.Rulesets.Catch.Tests.dll"
"${workspaceRoot}/bin/Debug/netcoreapp3.0/osu.Game.Rulesets.Catch.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)",
@ -20,7 +20,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Release/netcoreapp2.2/osu.Game.Rulesets.Catch.Tests.dll"
"${workspaceRoot}/bin/Release/netcoreapp3.0/osu.Game.Rulesets.Catch.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)",

View File

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

View File

@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Catch.Objects
Scale = 1.0f - 0.7f * (difficulty.CircleSize - 5) / 5;
}
protected override HitWindows CreateHitWindows() => null;
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
public enum FruitVisualRepresentation

View File

@ -7,7 +7,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Debug/netcoreapp2.2/osu.Game.Rulesets.Mania.Tests.dll"
"${workspaceRoot}/bin/Debug/netcoreapp3.0/osu.Game.Rulesets.Mania.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)",
@ -20,7 +20,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Release/netcoreapp2.2/osu.Game.Rulesets.Mania.Tests.dll"
"${workspaceRoot}/bin/Release/netcoreapp3.0/osu.Game.Rulesets.Mania.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)",

View File

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

View File

@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
public Vector2 ScreenSpaceDragPosition { get; private set; }
public Vector2 DragPosition { get; private set; }
protected new DrawableManiaHitObject HitObject => (DrawableManiaHitObject)base.HitObject;
public new DrawableManiaHitObject HitObject => (DrawableManiaHitObject)base.HitObject;
protected IClock EditorClock { get; private set; }

View File

@ -3,9 +3,7 @@
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Input.Events;
using osu.Framework.Timing;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Edit.Blueprints;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.UI;
@ -31,13 +29,16 @@ namespace osu.Game.Rulesets.Mania.Edit
editorClock = clock;
}
public override void HandleDrag(SelectionBlueprint blueprint, DragEvent dragEvent)
public override void HandleMovement(MoveSelectionEvent moveEvent)
{
adjustOrigins((ManiaSelectionBlueprint)blueprint);
performDragMovement(dragEvent);
performColumnMovement(dragEvent);
var maniaBlueprint = (ManiaSelectionBlueprint)moveEvent.Blueprint;
int lastColumn = maniaBlueprint.HitObject.HitObject.Column;
base.HandleDrag(blueprint, dragEvent);
adjustOrigins(maniaBlueprint);
performDragMovement(moveEvent);
performColumnMovement(lastColumn, moveEvent);
base.HandleMovement(moveEvent);
}
/// <summary>
@ -62,7 +63,7 @@ namespace osu.Game.Rulesets.Mania.Edit
b.HitObject.Y += movementDelta;
}
private void performDragMovement(DragEvent dragEvent)
private void performDragMovement(MoveSelectionEvent moveEvent)
{
foreach (var b in SelectedBlueprints)
{
@ -72,7 +73,7 @@ namespace osu.Game.Rulesets.Mania.Edit
// Using the hitobject position is required since AdjustPosition can be invoked multiple times per frame
// without the position having been updated by the parenting ScrollingHitObjectContainer
hitObject.Y += dragEvent.Delta.Y;
hitObject.Y += moveEvent.InstantDelta.Y;
float targetPosition;
@ -94,14 +95,13 @@ namespace osu.Game.Rulesets.Mania.Edit
}
}
private void performColumnMovement(DragEvent dragEvent)
private void performColumnMovement(int lastColumn, MoveSelectionEvent moveEvent)
{
var lastColumn = composer.ColumnAt(dragEvent.ScreenSpaceLastMousePosition);
var currentColumn = composer.ColumnAt(dragEvent.ScreenSpaceMousePosition);
if (lastColumn == null || currentColumn == null)
var currentColumn = composer.ColumnAt(moveEvent.ScreenSpacePosition);
if (currentColumn == null)
return;
int columnDelta = currentColumn.Index - lastColumn.Index;
int columnDelta = currentColumn.Index - lastColumn;
if (columnDelta == 0)
return;

View File

@ -101,6 +101,6 @@ namespace osu.Game.Rulesets.Mania.Objects
public override Judgement CreateJudgement() => new HoldNoteJudgement();
protected override HitWindows CreateHitWindows() => null;
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -14,6 +14,6 @@ namespace osu.Game.Rulesets.Mania.Objects
{
public override Judgement CreateJudgement() => new HoldNoteTickJudgement();
protected override HitWindows CreateHitWindows() => null;
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -7,7 +7,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Debug/netcoreapp2.2/osu.Game.Rulesets.Osu.Tests.dll"
"${workspaceRoot}/bin/Debug/netcoreapp3.0/osu.Game.Rulesets.Osu.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)",
@ -20,7 +20,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Release/netcoreapp2.2/osu.Game.Rulesets.Osu.Tests.dll"
"${workspaceRoot}/bin/Release/netcoreapp3.0/osu.Game.Rulesets.Osu.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)",

View File

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

View File

@ -2,8 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Edit.Compose.Components;
@ -11,7 +9,7 @@ namespace osu.Game.Rulesets.Osu.Edit
{
public class OsuSelectionHandler : SelectionHandler
{
public override void HandleDrag(SelectionBlueprint blueprint, DragEvent dragEvent)
public override void HandleMovement(MoveSelectionEvent moveEvent)
{
foreach (var h in SelectedHitObjects.OfType<OsuHitObject>())
{
@ -21,10 +19,10 @@ namespace osu.Game.Rulesets.Osu.Edit
continue;
}
h.Position += dragEvent.Delta;
h.Position += moveEvent.InstantDelta;
}
base.HandleDrag(blueprint, dragEvent);
base.HandleMovement(moveEvent);
}
}
}

View File

@ -40,6 +40,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
positionBindable.BindValueChanged(_ => updatePosition());
pathBindable.BindValueChanged(_ => updatePosition(), true);
// TODO: This has no drawable content. Support for skins should be added.
}
protected override void CheckForResult(bool userTriggered, double timeOffset)

View File

@ -30,6 +30,6 @@ namespace osu.Game.Rulesets.Osu.Objects
public override Judgement CreateJudgement() => new OsuJudgement();
protected override HitWindows CreateHitWindows() => null;
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -205,6 +205,6 @@ namespace osu.Game.Rulesets.Osu.Objects
public override Judgement CreateJudgement() => new OsuJudgement();
protected override HitWindows CreateHitWindows() => null;
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -25,6 +25,6 @@ namespace osu.Game.Rulesets.Osu.Objects
public override Judgement CreateJudgement() => new OsuSliderTailJudgement();
protected override HitWindows CreateHitWindows() => null;
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -32,6 +32,6 @@ namespace osu.Game.Rulesets.Osu.Objects
public override Judgement CreateJudgement() => new OsuJudgement();
protected override HitWindows CreateHitWindows() => null;
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -33,6 +33,6 @@ namespace osu.Game.Rulesets.Osu.Objects
public override Judgement CreateJudgement() => new OsuJudgement();
protected override HitWindows CreateHitWindows() => null;
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -7,7 +7,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Debug/netcoreapp2.2/osu.Game.Rulesets.Taiko.Tests.dll"
"${workspaceRoot}/bin/Debug/netcoreapp3.0/osu.Game.Rulesets.Taiko.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Debug)",
@ -20,7 +20,7 @@
"request": "launch",
"program": "dotnet",
"args": [
"${workspaceRoot}/bin/Release/netcoreapp2.2/osu.Game.Rulesets.Taiko.Tests.dll"
"${workspaceRoot}/bin/Release/netcoreapp3.0/osu.Game.Rulesets.Taiko.Tests.dll"
],
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build (Release)",

View File

@ -9,7 +9,7 @@
</ItemGroup>
<PropertyGroup Label="Project">
<OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\osu.Game.Rulesets.Taiko\osu.Game.Rulesets.Taiko.csproj" />

View File

@ -88,6 +88,6 @@ namespace osu.Game.Rulesets.Taiko.Objects
public override Judgement CreateJudgement() => new TaikoDrumRollJudgement();
protected override HitWindows CreateHitWindows() => null;
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -27,6 +27,6 @@ namespace osu.Game.Rulesets.Taiko.Objects
public override Judgement CreateJudgement() => new TaikoDrumRollTickJudgement();
protected override HitWindows CreateHitWindows() => null;
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -11,6 +11,6 @@ namespace osu.Game.Rulesets.Taiko.Objects
{
public override Judgement CreateJudgement() => new TaikoStrongJudgement();
protected override HitWindows CreateHitWindows() => null;
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -35,6 +35,6 @@ namespace osu.Game.Rulesets.Taiko.Objects
public override Judgement CreateJudgement() => new TaikoSwellJudgement();
protected override HitWindows CreateHitWindows() => null;
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -11,6 +11,6 @@ namespace osu.Game.Rulesets.Taiko.Objects
{
public override Judgement CreateJudgement() => new TaikoSwellTickJudgement();
protected override HitWindows CreateHitWindows() => null;
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -29,7 +29,7 @@ namespace osu.Game.Tests.Beatmaps.IO
public async Task TestImportWhenClosed()
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportWhenClosed"))
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportWhenClosed)))
{
try
{
@ -46,7 +46,7 @@ namespace osu.Game.Tests.Beatmaps.IO
public async Task TestImportThenDelete()
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenDelete"))
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenDelete)))
{
try
{
@ -67,7 +67,7 @@ namespace osu.Game.Tests.Beatmaps.IO
public async Task TestImportThenImport()
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenImport"))
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImport)))
{
try
{
@ -94,7 +94,7 @@ namespace osu.Game.Tests.Beatmaps.IO
public async Task TestImportCorruptThenImport()
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenImport"))
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportCorruptThenImport)))
{
try
{
@ -136,7 +136,7 @@ namespace osu.Game.Tests.Beatmaps.IO
public async Task TestRollbackOnFailure()
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestRollbackOnFailure"))
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestRollbackOnFailure)))
{
try
{
@ -213,7 +213,7 @@ namespace osu.Game.Tests.Beatmaps.IO
public async Task TestImportThenImportDifferentHash()
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenImportDifferentHash"))
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenImportDifferentHash)))
{
try
{
@ -244,7 +244,7 @@ namespace osu.Game.Tests.Beatmaps.IO
public async Task TestImportThenDeleteThenImport()
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportThenDeleteThenImport"))
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportThenDeleteThenImport)))
{
try
{
@ -272,7 +272,7 @@ namespace osu.Game.Tests.Beatmaps.IO
public async Task TestImportThenDeleteThenImportWithOnlineIDMismatch(bool set)
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost($"TestImportThenDeleteThenImport-{set}"))
using (HeadlessGameHost host = new CleanRunHeadlessGameHost($"{nameof(TestImportThenDeleteThenImportWithOnlineIDMismatch)}-{set}"))
{
try
{
@ -306,7 +306,7 @@ namespace osu.Game.Tests.Beatmaps.IO
public async Task TestImportWithDuplicateBeatmapIDs()
{
//unfortunately for the time being we need to reference osu.Framework.Desktop for a game host here.
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportWithDuplicateBeatmapID"))
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportWithDuplicateBeatmapIDs)))
{
try
{
@ -392,7 +392,7 @@ namespace osu.Game.Tests.Beatmaps.IO
[Test]
public async Task TestImportWhenFileOpen()
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportWhenFileOpen"))
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportWhenFileOpen)))
{
try
{
@ -414,7 +414,7 @@ namespace osu.Game.Tests.Beatmaps.IO
[Test]
public async Task TestImportNestedStructure()
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost("TestImportNestedStructure"))
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportNestedStructure)))
{
try
{
@ -456,9 +456,63 @@ namespace osu.Game.Tests.Beatmaps.IO
}
}
public static async Task<BeatmapSetInfo> LoadOszIntoOsu(OsuGameBase osu, string path = null)
[Test]
public async Task TestImportWithIgnoredDirectoryInArchive()
{
var temp = path ?? TestResources.GetTestBeatmapForImport();
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportWithIgnoredDirectoryInArchive)))
{
try
{
var osu = loadOsu(host);
var temp = TestResources.GetTestBeatmapForImport();
string extractedFolder = $"{temp}_extracted";
string dataFolder = Path.Combine(extractedFolder, "actual_data");
string resourceForkFolder = Path.Combine(extractedFolder, "__MACOSX");
string resourceForkFilePath = Path.Combine(resourceForkFolder, ".extracted");
Directory.CreateDirectory(dataFolder);
Directory.CreateDirectory(resourceForkFolder);
using (var resourceForkFile = File.CreateText(resourceForkFilePath))
{
resourceForkFile.WriteLine("adding content so that it's not empty");
}
try
{
using (var zip = ZipArchive.Open(temp))
zip.WriteToDirectory(dataFolder);
using (var zip = ZipArchive.Create())
{
zip.AddAllFromDirectory(extractedFolder);
zip.SaveTo(temp, new ZipWriterOptions(CompressionType.Deflate));
}
var imported = await osu.Dependencies.Get<BeatmapManager>().Import(temp);
ensureLoaded(osu);
Assert.IsFalse(imported.Files.Any(f => f.Filename.Contains("__MACOSX")), "Files contain resource fork folder, which should be ignored");
Assert.IsFalse(imported.Files.Any(f => f.Filename.Contains("actual_data")), "Files contain common subfolder");
}
finally
{
Directory.Delete(extractedFolder, true);
}
}
finally
{
host.Exit();
}
}
}
public static async Task<BeatmapSetInfo> LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false)
{
var temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack);
var manager = osu.Dependencies.Get<BeatmapManager>();

View File

@ -1,6 +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 System;
using System.IO;
using System.Text;
using NUnit.Framework;
@ -14,9 +15,7 @@ namespace osu.Game.Tests.Beatmaps.IO
[Test]
public void TestReadLineByLine()
{
const string contents = @"line 1
line 2
line 3";
const string contents = "line 1\rline 2\nline 3";
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(contents)))
using (var bufferedReader = new LineBufferedReader(stream))
@ -31,9 +30,7 @@ line 3";
[Test]
public void TestPeekLineOnce()
{
const string contents = @"line 1
peek this
line 3";
const string contents = "line 1\r\npeek this\nline 3";
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(contents)))
using (var bufferedReader = new LineBufferedReader(stream))
@ -49,9 +46,7 @@ line 3";
[Test]
public void TestPeekLineMultipleTimes()
{
const string contents = @"peek this once
line 2
peek this a lot";
const string contents = "peek this once\nline 2\rpeek this a lot";
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(contents)))
using (var bufferedReader = new LineBufferedReader(stream))
@ -70,8 +65,7 @@ peek this a lot";
[Test]
public void TestPeekLineAtEndOfStream()
{
const string contents = @"first line
second line";
const string contents = "first line\r\nsecond line";
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(contents)))
using (var bufferedReader = new LineBufferedReader(stream))
@ -100,8 +94,7 @@ second line";
[Test]
public void TestReadToEndNoPeeks()
{
const string contents = @"first line
second line";
const string contents = "first line\r\nsecond line";
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(contents)))
using (var bufferedReader = new LineBufferedReader(stream))
@ -113,20 +106,19 @@ second line";
[Test]
public void TestReadToEndAfterReadsAndPeeks()
{
const string contents = @"this line is gone
this one shouldn't be
these ones
definitely not";
const string contents = "this line is gone\rthis one shouldn't be\r\nthese ones\ndefinitely not";
using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(contents)))
using (var bufferedReader = new LineBufferedReader(stream))
{
Assert.AreEqual("this line is gone", bufferedReader.ReadLine());
Assert.AreEqual("this one shouldn't be", bufferedReader.PeekLine());
const string ending = @"this one shouldn't be
these ones
definitely not";
Assert.AreEqual(ending, bufferedReader.ReadToEnd());
var endingLines = bufferedReader.ReadToEnd().Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
Assert.AreEqual(3, endingLines.Length);
Assert.AreEqual("this one shouldn't be", endingLines[0]);
Assert.AreEqual("these ones", endingLines[1]);
Assert.AreEqual("definitely not", endingLines[2]);
}
}
}

View File

@ -11,13 +11,13 @@ namespace osu.Game.Tests.Resources
{
public static Stream OpenResource(string name) => new DllResourceStore("osu.Game.Tests.dll").GetStream($"Resources/{name}");
public static Stream GetTestBeatmapStream() => new DllResourceStore("osu.Game.Resources.dll").GetStream("Beatmaps/241526 Soleily - Renatus.osz");
public static Stream GetTestBeatmapStream(bool virtualTrack = false) => new DllResourceStore("osu.Game.Resources.dll").GetStream($"Beatmaps/241526 Soleily - Renatus{(virtualTrack ? "_virtual" : "")}.osz");
public static string GetTestBeatmapForImport()
public static string GetTestBeatmapForImport(bool virtualTrack = false)
{
var temp = Path.GetTempFileName() + ".osz";
using (var stream = GetTestBeatmapStream())
using (var stream = GetTestBeatmapStream(virtualTrack))
using (var newFile = File.Create(temp))
stream.CopyTo(newFile);

View File

@ -0,0 +1,213 @@
// 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 NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Compose.Components;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Editor
{
public class TestSceneBeatSnapGrid : EditorClockTestScene
{
private const double beat_length = 100;
private static readonly Vector2 grid_position = new Vector2(512, 384);
[Cached(typeof(IEditorBeatmap))]
private readonly EditorBeatmap<OsuHitObject> editorBeatmap;
private TestBeatSnapGrid grid;
public TestSceneBeatSnapGrid()
{
editorBeatmap = new EditorBeatmap<OsuHitObject>(new OsuBeatmap());
editorBeatmap.ControlPointInfo.TimingPoints.Add(new TimingControlPoint { BeatLength = beat_length });
createGrid();
}
[SetUp]
public void Setup() => Schedule(() =>
{
Clear();
editorBeatmap.ControlPointInfo.TimingPoints.Clear();
editorBeatmap.ControlPointInfo.TimingPoints.Add(new TimingControlPoint { BeatLength = beat_length });
BeatDivisor.Value = 1;
});
[TestCase(1)]
[TestCase(2)]
[TestCase(3)]
[TestCase(4)]
[TestCase(6)]
[TestCase(8)]
[TestCase(12)]
[TestCase(16)]
public void TestInitialBeatDivisor(int divisor)
{
AddStep($"set beat divisor = {divisor}", () => BeatDivisor.Value = divisor);
createGrid();
float expectedDistance = (float)beat_length / divisor;
AddAssert($"spacing is {expectedDistance}", () => Precision.AlmostEquals(grid.DistanceSpacing, expectedDistance));
}
[Test]
public void TestChangeBeatDivisor()
{
createGrid();
AddStep("set beat divisor = 2", () => BeatDivisor.Value = 2);
const float expected_distance = (float)beat_length / 2;
AddAssert($"spacing is {expected_distance}", () => Precision.AlmostEquals(grid.DistanceSpacing, expected_distance));
}
[TestCase(100)]
[TestCase(200)]
public void TestBeatLength(double beatLength)
{
AddStep($"set beat length = {beatLength}", () =>
{
editorBeatmap.ControlPointInfo.TimingPoints.Clear();
editorBeatmap.ControlPointInfo.TimingPoints.Add(new TimingControlPoint { BeatLength = beatLength });
});
createGrid();
AddAssert($"spacing is {beatLength}", () => Precision.AlmostEquals(grid.DistanceSpacing, beatLength));
}
[TestCase(1)]
[TestCase(2)]
public void TestGridVelocity(float velocity)
{
createGrid(g => g.Velocity = velocity);
float expectedDistance = (float)beat_length * velocity;
AddAssert($"spacing is {expectedDistance}", () => Precision.AlmostEquals(grid.DistanceSpacing, expectedDistance));
}
[Test]
public void TestGetSnappedTime()
{
createGrid();
Vector2 snapPosition = Vector2.Zero;
AddStep("get first tick position", () => snapPosition = grid_position + new Vector2((float)beat_length, 0));
AddAssert("snap time is 1 beat away", () => Precision.AlmostEquals(beat_length, grid.GetSnapTime(snapPosition), 0.01));
createGrid(g => g.Velocity = 2, "with velocity = 2");
AddAssert("snap time is now 0.5 beats away", () => Precision.AlmostEquals(beat_length / 2, grid.GetSnapTime(snapPosition), 0.01));
}
private void createGrid(Action<TestBeatSnapGrid> func = null, string description = null)
{
AddStep($"create grid {description ?? string.Empty}", () =>
{
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.SlateGray
},
grid = new TestBeatSnapGrid(new HitObject(), grid_position)
};
func?.Invoke(grid);
});
}
private class TestBeatSnapGrid : BeatSnapGrid
{
public new float Velocity = 1;
public new float DistanceSpacing => base.DistanceSpacing;
public TestBeatSnapGrid(HitObject hitObject, Vector2 centrePosition)
: base(hitObject, centrePosition)
{
}
protected override void CreateContent(Vector2 centrePosition)
{
AddInternal(new Circle
{
Origin = Anchor.Centre,
Size = new Vector2(5),
Position = centrePosition
});
int beatIndex = 0;
for (float s = centrePosition.X + DistanceSpacing; s <= DrawWidth; s += DistanceSpacing, beatIndex++)
{
AddInternal(new Circle
{
Origin = Anchor.Centre,
Size = new Vector2(5, 10),
Position = new Vector2(s, centrePosition.Y),
Colour = GetColourForBeatIndex(beatIndex)
});
}
beatIndex = 0;
for (float s = centrePosition.X - DistanceSpacing; s >= 0; s -= DistanceSpacing, beatIndex++)
{
AddInternal(new Circle
{
Origin = Anchor.Centre,
Size = new Vector2(5, 10),
Position = new Vector2(s, centrePosition.Y),
Colour = GetColourForBeatIndex(beatIndex)
});
}
beatIndex = 0;
for (float s = centrePosition.Y + DistanceSpacing; s <= DrawHeight; s += DistanceSpacing, beatIndex++)
{
AddInternal(new Circle
{
Origin = Anchor.Centre,
Size = new Vector2(10, 5),
Position = new Vector2(centrePosition.X, s),
Colour = GetColourForBeatIndex(beatIndex)
});
}
beatIndex = 0;
for (float s = centrePosition.Y - DistanceSpacing; s >= 0; s -= DistanceSpacing, beatIndex++)
{
AddInternal(new Circle
{
Origin = Anchor.Centre,
Size = new Vector2(10, 5),
Position = new Vector2(centrePosition.X, s),
Colour = GetColourForBeatIndex(beatIndex)
});
}
}
protected override float GetVelocity(double time, ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
=> Velocity;
public override Vector2 GetSnapPosition(Vector2 screenSpacePosition)
=> Vector2.Zero;
}
}
}

View File

@ -1,8 +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 System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Rulesets.Osu;
@ -11,10 +9,8 @@ using osu.Game.Screens.Edit.Compose;
namespace osu.Game.Tests.Visual.Editor
{
[TestFixture]
public class TestSceneEditorCompose : EditorClockTestScene
public class TestSceneComposeScreen : EditorClockTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(ComposeScreen) };
[BackgroundDependencyLoader]
private void load()
{

View File

@ -5,12 +5,15 @@ using System;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
@ -18,7 +21,9 @@ using osu.Game.Overlays;
using osu.Game.Overlays.Mods;
using osu.Game.Screens;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Play;
using osu.Game.Screens.Select;
using osu.Game.Tests.Beatmaps.IO;
using osuTK;
using osuTK.Graphics;
using osuTK.Input;
@ -31,11 +36,11 @@ namespace osu.Game.Tests.Visual.Menus
private const float click_padding = 25;
private GameHost host;
private TestOsuGame osuGame;
private TestOsuGame game;
private Vector2 backButtonPosition => osuGame.ToScreenSpace(new Vector2(click_padding, osuGame.LayoutRectangle.Bottom - click_padding));
private Vector2 backButtonPosition => game.ToScreenSpace(new Vector2(click_padding, game.LayoutRectangle.Bottom - click_padding));
private Vector2 optionsButtonPosition => osuGame.ToScreenSpace(new Vector2(click_padding, click_padding));
private Vector2 optionsButtonPosition => game.ToScreenSpace(new Vector2(click_padding, click_padding));
[BackgroundDependencyLoader]
private void load(GameHost host)
@ -54,23 +59,23 @@ namespace osu.Game.Tests.Visual.Menus
{
AddStep("Create new game instance", () =>
{
if (osuGame != null)
if (game != null)
{
Remove(osuGame);
osuGame.Dispose();
Remove(game);
game.Dispose();
}
osuGame = new TestOsuGame(LocalStorage, API);
osuGame.SetHost(host);
game = new TestOsuGame(LocalStorage, API);
game.SetHost(host);
// todo: this can be removed once we can run audio trakcs without a device present
// see https://github.com/ppy/osu/issues/1302
osuGame.LocalConfig.Set(OsuSetting.IntroSequence, IntroSequence.Circles);
game.LocalConfig.Set(OsuSetting.IntroSequence, IntroSequence.Circles);
Add(osuGame);
Add(game);
});
AddUntilStep("Wait for load", () => osuGame.IsLoaded);
AddUntilStep("Wait for intro", () => osuGame.ScreenStack.CurrentScreen is IntroScreen);
AddUntilStep("Wait for load", () => game.IsLoaded);
AddUntilStep("Wait for intro", () => game.ScreenStack.CurrentScreen is IntroScreen);
confirmAtMainMenu();
}
@ -82,11 +87,43 @@ namespace osu.Game.Tests.Visual.Menus
pushAndConfirm(() => songSelect = new TestSongSelect());
AddStep("Show mods overlay", () => songSelect.ModSelectOverlay.Show());
AddAssert("Overlay was shown", () => songSelect.ModSelectOverlay.State.Value == Visibility.Visible);
AddStep("Press escape", () => pressAndRelease(Key.Escape));
pushEscape();
AddAssert("Overlay was hidden", () => songSelect.ModSelectOverlay.State.Value == Visibility.Hidden);
exitViaEscapeAndConfirm();
}
[TestCase(true)]
[TestCase(false)]
public void TestSongContinuesAfterExitPlayer(bool withUserPause)
{
Player player = null;
WorkingBeatmap beatmap() => game.Beatmap.Value;
Track track() => beatmap().Track;
pushAndConfirm(() => new TestSongSelect());
AddStep("import beatmap", () => ImportBeatmapTest.LoadOszIntoOsu(game, virtualTrack: true).Wait());
AddUntilStep("wait for selected", () => !game.Beatmap.IsDefault);
if (withUserPause)
AddStep("pause", () => game.Dependencies.Get<MusicController>().Stop());
AddStep("press enter", () => pressAndRelease(Key.Enter));
AddUntilStep("wait for player", () => (player = game.ScreenStack.CurrentScreen as Player) != null);
AddUntilStep("wait for fail", () => player.HasFailed);
AddUntilStep("wait for track stop", () => !track().IsRunning);
AddAssert("Ensure time before preview point", () => track().CurrentTime < beatmap().Metadata.PreviewTime);
pushEscape();
AddUntilStep("wait for track playing", () => track().IsRunning);
AddAssert("Ensure time wasn't reset to preview point", () => track().CurrentTime < beatmap().Metadata.PreviewTime);
}
[Test]
public void TestExitSongSelectWithClick()
{
@ -98,7 +135,7 @@ namespace osu.Game.Tests.Visual.Menus
AddStep("Move mouse to backButton", () => InputManager.MoveMouseTo(backButtonPosition));
// BackButton handles hover using its child button, so this checks whether or not any of BackButton's children are hovered.
AddUntilStep("Back button is hovered", () => InputManager.HoveredDrawables.Any(d => d.Parent == osuGame.BackButton));
AddUntilStep("Back button is hovered", () => InputManager.HoveredDrawables.Any(d => d.Parent == game.BackButton));
AddStep("Click back button", () => InputManager.Click(MouseButton.Left));
AddUntilStep("Overlay was hidden", () => songSelect.ModSelectOverlay.State.Value == Visibility.Hidden);
@ -122,25 +159,28 @@ namespace osu.Game.Tests.Visual.Menus
[Test]
public void TestOpenOptionsAndExitWithEscape()
{
AddUntilStep("Wait for options to load", () => osuGame.Settings.IsLoaded);
AddUntilStep("Wait for options to load", () => game.Settings.IsLoaded);
AddStep("Enter menu", () => pressAndRelease(Key.Enter));
AddStep("Move mouse to options overlay", () => InputManager.MoveMouseTo(optionsButtonPosition));
AddStep("Click options overlay", () => InputManager.Click(MouseButton.Left));
AddAssert("Options overlay was opened", () => osuGame.Settings.State.Value == Visibility.Visible);
AddAssert("Options overlay was opened", () => game.Settings.State.Value == Visibility.Visible);
AddStep("Hide options overlay using escape", () => pressAndRelease(Key.Escape));
AddAssert("Options overlay was closed", () => osuGame.Settings.State.Value == Visibility.Hidden);
AddAssert("Options overlay was closed", () => game.Settings.State.Value == Visibility.Hidden);
}
private void pushAndConfirm(Func<Screen> newScreen)
{
Screen screen = null;
AddStep("Push new screen", () => osuGame.ScreenStack.Push(screen = newScreen()));
AddUntilStep("Wait for new screen", () => osuGame.ScreenStack.CurrentScreen == screen && screen.IsLoaded);
AddStep("Push new screen", () => game.ScreenStack.Push(screen = newScreen()));
AddUntilStep("Wait for new screen", () => game.ScreenStack.CurrentScreen == screen && screen.IsLoaded);
}
private void pushEscape() =>
AddStep("Press escape", () => pressAndRelease(Key.Escape));
private void exitViaEscapeAndConfirm()
{
AddStep("Press escape", () => pressAndRelease(Key.Escape));
pushEscape();
confirmAtMainMenu();
}
@ -151,7 +191,7 @@ namespace osu.Game.Tests.Visual.Menus
confirmAtMainMenu();
}
private void confirmAtMainMenu() => AddUntilStep("Wait for main menu", () => osuGame.ScreenStack.CurrentScreen is MainMenu menu && menu.IsLoaded);
private void confirmAtMainMenu() => AddUntilStep("Wait for main menu", () => game.ScreenStack.CurrentScreen is MainMenu menu && menu.IsLoaded);
private void pressAndRelease(Key key)
{
@ -169,6 +209,8 @@ namespace osu.Game.Tests.Visual.Menus
public new OsuConfigManager LocalConfig => base.LocalConfig;
public new Bindable<WorkingBeatmap> Beatmap => base.Beatmap;
protected override Loader CreateLoader() => new TestLoader();
public TestOsuGame(Storage storage, IAPIProvider api)

View File

@ -16,6 +16,7 @@ using osu.Framework.Platform;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Overlays;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
@ -34,6 +35,8 @@ namespace osu.Game.Tests.Visual.SongSelect
private RulesetStore rulesets;
private MusicController music;
private WorkingBeatmap defaultBeatmap;
public override IReadOnlyList<Type> RequiredTypes => new[]
@ -79,6 +82,11 @@ namespace osu.Game.Tests.Visual.SongSelect
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, defaultBeatmap = Beatmap.Default));
Dependencies.Cache(music = new MusicController());
// required to get bindables attached
Add(music);
Beatmap.SetDefault();
Dependencies.Cache(config = new OsuConfigManager(LocalStorage));
@ -93,6 +101,57 @@ namespace osu.Game.Tests.Visual.SongSelect
manager?.Delete(manager.GetAllUsableBeatmapSets());
});
[Test]
public void TestAudioResuming()
{
createSongSelect();
addRulesetImportStep(0);
addRulesetImportStep(0);
checkMusicPlaying(true);
AddStep("select first", () => songSelect.Carousel.SelectBeatmap(songSelect.Carousel.BeatmapSets.First().Beatmaps.First()));
checkMusicPlaying(true);
AddStep("manual pause", () => music.TogglePause());
checkMusicPlaying(false);
AddStep("select next difficulty", () => songSelect.Carousel.SelectNext(skipDifficulties: false));
checkMusicPlaying(false);
AddStep("select next set", () => songSelect.Carousel.SelectNext());
checkMusicPlaying(true);
}
[TestCase(false)]
[TestCase(true)]
public void TestAudioRemainsCorrectOnRulesetChange(bool rulesetsInSameBeatmap)
{
createSongSelect();
// start with non-osu! to avoid convert confusion
changeRuleset(1);
if (rulesetsInSameBeatmap)
AddStep("import multi-ruleset map", () =>
{
var usableRulesets = rulesets.AvailableRulesets.Where(r => r.ID != 2).ToArray();
manager.Import(createTestBeatmapSet(0, usableRulesets)).Wait();
});
else
{
addRulesetImportStep(1);
addRulesetImportStep(0);
}
checkMusicPlaying(true);
AddStep("manual pause", () => music.TogglePause());
checkMusicPlaying(false);
changeRuleset(0);
checkMusicPlaying(!rulesetsInSameBeatmap);
}
[Test]
public void TestDummy()
{
@ -128,12 +187,10 @@ namespace osu.Game.Tests.Visual.SongSelect
}
[Test]
[Ignore("needs fixing")]
public void TestImportUnderDifferentRuleset()
{
createSongSelect();
changeRuleset(2);
addRulesetImportStep(0);
addRulesetImportStep(2);
AddUntilStep("no selection", () => songSelect.Carousel.SelectedBeatmap == null);
}
@ -224,6 +281,9 @@ namespace osu.Game.Tests.Visual.SongSelect
private static int importId;
private int getImportId() => ++importId;
private void checkMusicPlaying(bool playing) =>
AddUntilStep($"music {(playing ? "" : "not ")}playing", () => music.IsPlaying == playing);
private void changeMods(params Mod[] mods) => AddStep($"change mods to {string.Join(", ", mods.Select(m => m.Acronym))}", () => Mods.Value = mods);
private void changeRuleset(int id) => AddStep($"change ruleset to {id}", () => Ruleset.Value = rulesets.AvailableRulesets.First(r => r.ID == id));

View File

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

View File

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

View File

@ -1,15 +1,30 @@
// 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.IO;
using System.Linq;
using SharpCompress.Archives.Zip;
using SharpCompress.Common;
namespace osu.Game.IO.Archives
{
public sealed class ZipArchiveReader : ArchiveReader
{
/// <summary>
/// List of substrings that indicate a file should be ignored during the import process
/// (usually due to representing no useful data and being autogenerated by the OS).
/// </summary>
private static readonly string[] filename_ignore_list =
{
// Mac-specific
"__MACOSX",
".DS_Store",
// Windows-specific
"Thumbs.db"
};
private readonly Stream archiveStream;
private readonly ZipArchive archive;
@ -43,7 +58,9 @@ namespace osu.Game.IO.Archives
archiveStream.Dispose();
}
public override IEnumerable<string> Filenames => archive.Entries.Select(e => e.Key).ToArray();
private static bool canBeIgnored(IEntry entry) => filename_ignore_list.Any(ignoredName => entry.Key.IndexOf(ignoredName, StringComparison.OrdinalIgnoreCase) >= 0);
public override IEnumerable<string> Filenames => archive.Entries.Where(e => !canBeIgnored(e)).Select(e => e.Key).ToArray();
public override Stream GetUnderlyingStream() => archiveStream;
}

View File

@ -1,12 +1,9 @@
// 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.Online.API.Requests.Responses;
namespace osu.Game.Online.API.Requests
{
public class GetUsersRequest : APIRequest<List<APIUser>>
public class GetUsersRequest : APIRequest<GetUsersResponse>
{
protected override string Target => @"rankings/osu/performance";
}

View File

@ -0,0 +1,15 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using Newtonsoft.Json;
using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Online.API.Requests
{
public class GetUsersResponse : ResponseWithCursor
{
[JsonProperty("ranking")]
public List<APIUser> Users;
}
}

View File

@ -0,0 +1,16 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using Newtonsoft.Json;
namespace osu.Game.Online.API.Requests
{
public abstract class ResponseWithCursor
{
/// <summary>
/// A collection of parameters which should be passed to the search endpoint to fetch the next page.
/// </summary>
[JsonProperty("cursor")]
public dynamic CursorJson;
}
}

View File

@ -2,19 +2,12 @@
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using Newtonsoft.Json;
using osu.Game.Online.API.Requests.Responses;
namespace osu.Game.Online.API.Requests
{
public class SearchBeatmapSetsResponse
public class SearchBeatmapSetsResponse : ResponseWithCursor
{
public IEnumerable<APIBeatmapSet> BeatmapSets;
/// <summary>
/// A collection of parameters which should be passed to the search endpoint to fetch the next page.
/// </summary>
[JsonProperty("cursor")]
public dynamic CursorJson;
}
}

View File

@ -98,20 +98,13 @@ namespace osu.Game.Overlays
/// <summary>
/// Start playing the current track (if not already playing).
/// </summary>
public void Play()
{
if (!IsPlaying)
TogglePause();
}
/// <summary>
/// Toggle pause / play.
/// </summary>
/// <returns>Whether the operation was successful.</returns>
public bool TogglePause()
public bool Play(bool restart = false)
{
var track = current?.Track;
IsUserPaused = false;
if (track == null)
{
if (beatmap.Disabled)
@ -121,16 +114,38 @@ namespace osu.Game.Overlays
return true;
}
if (track.IsRunning)
{
IsUserPaused = true;
track.Stop();
}
else
{
if (restart)
track.Restart();
else if (!IsPlaying)
track.Start();
IsUserPaused = false;
}
return true;
}
/// <summary>
/// Stop playing the current track and pause at the current position.
/// </summary>
public void Stop()
{
var track = current?.Track;
IsUserPaused = true;
if (track?.IsRunning == true)
track.Stop();
}
/// <summary>
/// Toggle pause / play.
/// </summary>
/// <returns>Whether the operation was successful.</returns>
public bool TogglePause()
{
var track = current?.Track;
if (track?.IsRunning == true)
Stop();
else
Play();
return true;
}

View File

@ -118,7 +118,7 @@ namespace osu.Game.Overlays
default:
var userRequest = new GetUsersRequest(); // TODO filter arguments!
userRequest.Success += response => updateUsers(response.Select(r => r.User));
userRequest.Success += res => updateUsers(res.Users.Select(r => r.User));
API.Queue(getUsersRequest = userRequest);
break;
}

View File

@ -45,6 +45,11 @@ namespace osu.Game.Rulesets.Edit
/// </summary>
public readonly DrawableHitObject HitObject;
/// <summary>
/// The screen-space position of <see cref="HitObject"/> prior to handling a movement event.
/// </summary>
internal Vector2 ScreenSpaceMovementStartPosition { get; private set; }
protected override bool ShouldBeAlive => (HitObject.IsAlive && HitObject.IsPresent) || State == SelectionState.Selected;
public override bool HandlePositionalInput => ShouldBeAlive;
public override bool RemoveWhenNotAlive => false;
@ -131,7 +136,11 @@ namespace osu.Game.Rulesets.Edit
return base.OnClick(e);
}
protected override bool OnDragStart(DragStartEvent e) => true;
protected override bool OnDragStart(DragStartEvent e)
{
ScreenSpaceMovementStartPosition = HitObject.ToScreenSpace(HitObject.OriginPosition);
return true;
}
protected override bool OnDrag(DragEvent e)
{

View File

@ -173,7 +173,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
{
UpdateInitialTransforms();
var judgementOffset = Math.Min(HitObject.HitWindows?.WindowFor(HitResult.Miss) ?? double.MaxValue, Result?.TimeOffset ?? 0);
var judgementOffset = Result?.TimeOffset ?? 0;
using (BeginDelayedSequence(InitialLifetimeOffset + judgementOffset, true))
{
@ -379,7 +379,8 @@ namespace osu.Game.Rulesets.Objects.Drawables
// Ensure that the judgement is given a valid time offset, because this may not get set by the caller
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
Result.TimeOffset = Time.Current - endTime;
Result.TimeOffset = Math.Min(HitObject.HitWindows.WindowFor(HitResult.Miss), Time.Current - endTime);
switch (Result.Type)
{

View File

@ -66,7 +66,6 @@ namespace osu.Game.Rulesets.Objects
/// <summary>
/// The hit windows for this <see cref="HitObject"/>.
/// </summary>
[CanBeNull]
public HitWindows HitWindows { get; set; }
private readonly List<HitObject> nestedHitObjects = new List<HitObject>();
@ -113,10 +112,7 @@ namespace osu.Game.Rulesets.Objects
nestedHitObjects.Sort((h1, h2) => h1.StartTime.CompareTo(h2.StartTime));
foreach (var h in nestedHitObjects)
{
h.HitWindows = HitWindows;
h.ApplyDefaults(controlPointInfo, difficulty);
}
}
protected virtual void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
@ -147,7 +143,7 @@ namespace osu.Game.Rulesets.Objects
/// This will only be invoked if <see cref="HitWindows"/> hasn't been set externally (e.g. from a <see cref="BeatmapConverter{T}"/>.
/// </para>
/// </summary>
[CanBeNull]
[NotNull]
protected virtual HitWindows CreateHitWindows() => new HitWindows();
}
}

View File

@ -13,6 +13,6 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
{
public float X { get; set; }
protected override HitWindows CreateHitWindows() => null;
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -14,6 +14,6 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
public double Duration => EndTime - StartTime;
protected override HitWindows CreateHitWindows() => null;
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -13,6 +13,6 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
{
public float X { get; set; }
protected override HitWindows CreateHitWindows() => null;
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -17,6 +17,6 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
public float X { get; set; }
protected override HitWindows CreateHitWindows() => null;
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -22,6 +22,6 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
public int ComboOffset { get; set; }
protected override HitWindows CreateHitWindows() => null;
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -22,6 +22,6 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
public int ComboOffset { get; set; }
protected override HitWindows CreateHitWindows() => null;
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
public float Y => Position.Y;
protected override HitWindows CreateHitWindows() => null;
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
public bool NewCombo { get; set; }

View File

@ -10,6 +10,6 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
/// </summary>
internal sealed class ConvertHit : HitObject
{
protected override HitWindows CreateHitWindows() => null;
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -10,6 +10,6 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
/// </summary>
internal sealed class ConvertSlider : Legacy.ConvertSlider
{
protected override HitWindows CreateHitWindows() => null;
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -15,6 +15,6 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
public double Duration => EndTime - StartTime;
protected override HitWindows CreateHitWindows() => null;
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
}
}

View File

@ -3,6 +3,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects;
@ -30,6 +32,18 @@ namespace osu.Game.Rulesets.Scoring
private double meh;
private double miss;
/// <summary>
/// An empty <see cref="HitWindows"/> with only <see cref="HitResult.Miss"/> and <see cref="HitResult.Perfect"/>.
/// No time values are provided (meaning instantaneous hit or miss).
/// </summary>
public static HitWindows Empty => new EmptyHitWindows();
public HitWindows()
{
Debug.Assert(GetRanges().Any(r => r.Result == HitResult.Miss), $"{nameof(GetRanges)} should always contain {nameof(HitResult.Miss)}");
Debug.Assert(GetRanges().Any(r => r.Result != HitResult.Miss), $"{nameof(GetRanges)} should always contain at least one result type other than {nameof(HitResult.Miss)}.");
}
/// <summary>
/// Retrieves the <see cref="HitResult"/> with the largest hit window that produces a successful hit.
/// </summary>
@ -168,6 +182,29 @@ namespace osu.Game.Rulesets.Scoring
/// Defaults are provided but can be overridden to customise for a ruleset.
/// </summary>
protected virtual DifficultyRange[] GetRanges() => base_ranges;
public class EmptyHitWindows : HitWindows
{
private static readonly DifficultyRange[] ranges =
{
new DifficultyRange(HitResult.Perfect, 0, 0, 0),
new DifficultyRange(HitResult.Miss, 0, 0, 0),
};
public override bool IsHitResultAllowed(HitResult result)
{
switch (result)
{
case HitResult.Perfect:
case HitResult.Miss:
return true;
}
return false;
}
protected override DifficultyRange[] GetRanges() => ranges;
}
}
public struct DifficultyRange

View File

@ -426,11 +426,11 @@ namespace osu.Game.Rulesets.UI
{
foreach (var h in Objects)
{
if (h.HitWindows != null)
if (h.HitWindows.WindowFor(HitResult.Miss) > 0)
return h.HitWindows;
foreach (var n in h.NestedHitObjects)
if (n.HitWindows != null)
if (h.HitWindows.WindowFor(HitResult.Miss) > 0)
return n.HitWindows;
}

View File

@ -0,0 +1,153 @@
// 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.Allocation;
using osu.Framework.Caching;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Graphics;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osuTK;
namespace osu.Game.Screens.Edit.Compose.Components
{
public abstract class BeatSnapGrid : CompositeDrawable
{
/// <summary>
/// The velocity of the beatmap at the point of placement in pixels per millisecond.
/// </summary>
protected double Velocity { get; private set; }
/// <summary>
/// The spacing between each tick of the beat snapping grid.
/// </summary>
protected float DistanceSpacing { get; private set; }
/// <summary>
/// The position which the grid is centred on.
/// The first beat snapping tick is located at <see cref="CentrePosition"/> + <see cref="DistanceSpacing"/> in the desired direction.
/// </summary>
protected readonly Vector2 CentrePosition;
[Resolved]
private IEditorBeatmap beatmap { get; set; }
[Resolved]
private BindableBeatDivisor beatDivisor { get; set; }
[Resolved]
private OsuColour colours { get; set; }
private readonly Cached gridCache = new Cached();
private readonly HitObject hitObject;
private double startTime;
private double beatLength;
protected BeatSnapGrid(HitObject hitObject, Vector2 centrePosition)
{
this.hitObject = hitObject;
this.CentrePosition = centrePosition;
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load()
{
startTime = (hitObject as IHasEndTime)?.EndTime ?? hitObject.StartTime;
beatLength = beatmap.ControlPointInfo.TimingPointAt(startTime).BeatLength;
}
protected override void LoadComplete()
{
base.LoadComplete();
beatDivisor.BindValueChanged(_ => updateSpacing(), true);
}
private void updateSpacing()
{
Velocity = GetVelocity(startTime, beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty);
DistanceSpacing = (float)(beatLength / beatDivisor.Value * Velocity);
gridCache.Invalidate();
}
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
{
if ((invalidation & Invalidation.RequiredParentSizeToFit) > 0)
gridCache.Invalidate();
return base.Invalidate(invalidation, source, shallPropagate);
}
protected override void Update()
{
base.Update();
if (!gridCache.IsValid)
{
ClearInternal();
CreateContent(CentrePosition);
gridCache.Validate();
}
}
/// <summary>
/// Creates the content which visualises the grid ticks.
/// </summary>
protected abstract void CreateContent(Vector2 centrePosition);
/// <summary>
/// Retrieves the velocity of gameplay at a point in time in pixels per millisecond.
/// </summary>
/// <param name="time">The time to retrieve the velocity at.</param>
/// <param name="controlPointInfo">The beatmap's <see cref="ControlPointInfo"/> at the point in time.</param>
/// <param name="difficulty">The beatmap's <see cref="BeatmapDifficulty"/> at the point in time.</param>
/// <returns>The velocity.</returns>
protected abstract float GetVelocity(double time, ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty);
/// <summary>
/// Snaps a position to this grid.
/// </summary>
/// <param name="position">The original position in coordinate space local to this <see cref="BeatSnapGrid"/>.</param>
/// <returns>The snapped position in coordinate space local to this <see cref="BeatSnapGrid"/>.</returns>
public abstract Vector2 GetSnapPosition(Vector2 position);
/// <summary>
/// Retrieves the time at a snapped position.
/// </summary>
/// <param name="position">The snapped position in coordinate space local to this <see cref="BeatSnapGrid"/>.</param>
/// <returns>The time at the snapped position.</returns>
public double GetSnapTime(Vector2 position) => startTime + (position - CentrePosition).Length / Velocity;
/// <summary>
/// Retrieves the applicable colour for a beat index.
/// </summary>
/// <param name="index">The 0-based beat index.</param>
/// <returns>The applicable colour.</returns>
protected ColourInfo GetColourForBeatIndex(int index)
{
int beat = (index + 1) % beatDivisor.Value;
ColourInfo colour = colours.Gray5;
for (int i = 0; i < BindableBeatDivisor.VALID_DIVISORS.Length; i++)
{
int divisor = BindableBeatDivisor.VALID_DIVISORS[i];
if ((beat * divisor) % beatDivisor.Value == 0)
{
colour = BindableBeatDivisor.GetColourFor(divisor, colours);
break;
}
}
int repeatIndex = index / beatDivisor.Value;
return colour.MultiplyAlpha(0.5f / (repeatIndex + 1));
}
}
}

View File

@ -216,7 +216,12 @@ namespace osu.Game.Screens.Edit.Compose.Components
private void onSelectionRequested(SelectionBlueprint blueprint, InputState state) => selectionHandler.HandleSelectionRequested(blueprint, state);
private void onDragRequested(SelectionBlueprint blueprint, DragEvent dragEvent) => selectionHandler.HandleDrag(blueprint, dragEvent);
private void onDragRequested(SelectionBlueprint blueprint, DragEvent dragEvent)
{
var movePosition = blueprint.ScreenSpaceMovementStartPosition + dragEvent.ScreenSpaceMousePosition - dragEvent.ScreenSpaceMouseDownPosition;
selectionHandler.HandleMovement(new MoveSelectionEvent(blueprint, blueprint.ScreenSpaceMovementStartPosition, movePosition));
}
protected override void Dispose(bool isDisposing)
{

View File

@ -0,0 +1,46 @@
// 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.Edit;
using osuTK;
namespace osu.Game.Screens.Edit.Compose.Components
{
/// <summary>
/// An event which occurs when a <see cref="SelectionBlueprint"/> is moved.
/// </summary>
public class MoveSelectionEvent
{
/// <summary>
/// The <see cref="SelectionBlueprint"/> that triggered this <see cref="MoveSelectionEvent"/>.
/// </summary>
public readonly SelectionBlueprint Blueprint;
/// <summary>
/// The starting screen-space position of the hitobject.
/// </summary>
public readonly Vector2 ScreenSpaceStartPosition;
/// <summary>
/// The expected screen-space position of the hitobject at the current cursor position.
/// </summary>
public readonly Vector2 ScreenSpacePosition;
/// <summary>
/// The distance between <see cref="ScreenSpacePosition"/> and the hitobject's current position, in the coordinate-space of the hitobject's parent.
/// </summary>
/// <remarks>
/// This does not use <see cref="ScreenSpaceStartPosition"/> and does not represent the cumulative movement distance.
/// </remarks>
public readonly Vector2 InstantDelta;
public MoveSelectionEvent(SelectionBlueprint blueprint, Vector2 screenSpaceStartPosition, Vector2 screenSpacePosition)
{
Blueprint = blueprint;
ScreenSpaceStartPosition = screenSpaceStartPosition;
ScreenSpacePosition = screenSpacePosition;
InstantDelta = Blueprint.HitObject.Parent.ToLocalSpace(ScreenSpacePosition) - Blueprint.HitObject.Position;
}
}
}

View File

@ -65,11 +65,10 @@ namespace osu.Game.Screens.Edit.Compose.Components
#region User Input Handling
/// <summary>
/// Handles the selected <see cref="DrawableHitObject"/>s being dragged.
/// Handles the selected <see cref="DrawableHitObject"/>s being moved.
/// </summary>
/// <param name="blueprint">The <see cref="SelectionBlueprint"/> that received the drag event.</param>
/// <param name="dragEvent">The drag event.</param>
public virtual void HandleDrag(SelectionBlueprint blueprint, DragEvent dragEvent)
/// <param name="moveEvent">The move event.</param>
public virtual void HandleMovement(MoveSelectionEvent moveEvent)
{
}

View File

@ -1,132 +1,34 @@
// 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 JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Logging;
using osu.Game.Rulesets.Edit;
using osu.Game.Screens.Edit.Compose.Components;
using osu.Game.Screens.Edit.Compose.Components.Timeline;
using osu.Game.Skinning;
using osuTK.Graphics;
namespace osu.Game.Screens.Edit.Compose
{
public class ComposeScreen : EditorScreen
public class ComposeScreen : EditorScreenWithTimeline
{
private const float vertical_margins = 10;
private const float horizontal_margins = 20;
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
private HitObjectComposer composer;
[BackgroundDependencyLoader(true)]
private void load([CanBeNull] BindableBeatDivisor beatDivisor)
protected override Drawable CreateMainContent()
{
if (beatDivisor != null)
this.beatDivisor.BindTo(beatDivisor);
Container composerContainer;
Children = new Drawable[]
{
new GridContainer
{
RelativeSizeAxes = Axes.Both,
Content = new[]
{
new Drawable[]
{
new Container
{
Name = "Timeline",
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.5f)
},
new Container
{
Name = "Timeline content",
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = horizontal_margins, Vertical = vertical_margins },
Child = new GridContainer
{
RelativeSizeAxes = Axes.Both,
Content = new[]
{
new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Right = 5 },
Child = new TimelineArea { RelativeSizeAxes = Axes.Both }
},
new BeatDivisorControl(beatDivisor) { RelativeSizeAxes = Axes.Both }
},
},
ColumnDimensions = new[]
{
new Dimension(),
new Dimension(GridSizeMode.Absolute, 90),
}
},
}
}
}
},
new Drawable[]
{
composerContainer = new Container
{
Name = "Composer content",
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = horizontal_margins, Vertical = vertical_margins },
}
}
},
RowDimensions = new[] { new Dimension(GridSizeMode.Absolute, 110) }
},
};
var ruleset = Beatmap.Value.BeatmapInfo.Ruleset?.CreateInstance();
if (ruleset == null)
var composer = ruleset?.CreateHitObjectComposer();
if (composer != null)
{
Logger.Log("Beatmap doesn't have a ruleset assigned.");
// ExitRequested?.Invoke();
return;
var beatmapSkinProvider = new BeatmapSkinProvidingContainer(Beatmap.Value.Skin);
// the beatmapSkinProvider is used as the fallback source here to allow the ruleset-specific skin implementation
// full access to all skin sources.
var rulesetSkinProvider = new SkinProvidingContainer(ruleset.CreateLegacySkinProvider(beatmapSkinProvider));
// load the skinning hierarchy first.
// this is intentionally done in two stages to ensure things are in a loaded state before exposing the ruleset to skin sources.
return beatmapSkinProvider.WithChild(rulesetSkinProvider.WithChild(ruleset.CreateHitObjectComposer()));
}
composer = ruleset.CreateHitObjectComposer();
if (composer == null)
{
Logger.Log($"Ruleset {ruleset.Description} doesn't support hitobject composition.");
// ExitRequested?.Invoke();
return;
}
var beatmapSkinProvider = new BeatmapSkinProvidingContainer(Beatmap.Value.Skin);
// the beatmapSkinProvider is used as the fallback source here to allow the ruleset-specific skin implementation
// full access to all skin sources.
var rulesetSkinProvider = new SkinProvidingContainer(ruleset.CreateLegacySkinProvider(beatmapSkinProvider));
// load the skinning hierarchy first.
// this is intentionally done in two stages to ensure things are in a loaded state before exposing the ruleset to skin sources.
composerContainer.Add(
beatmapSkinProvider.WithChild(
rulesetSkinProvider.WithChild(composer)));
return new ScreenWhiteBox.UnderConstructionMessage(ruleset == null ? "This beatmap" : $"{ruleset.Description}'s composer");
}
}
}

View File

@ -1,52 +1,13 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Sprites;
using osuTK.Graphics;
namespace osu.Game.Screens.Edit.Design
{
public class DesignScreen : EditorScreen
{
public DesignScreen()
{
Add(new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.35f
},
new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.5f
},
new Container
{
AutoSizeAxes = Axes.Both,
Padding = new MarginPadding(20),
Child = new OsuSpriteText { Text = "Design screen" }
}
}
}
}
});
Child = new ScreenWhiteBox.UnderConstructionMessage("Design mode");
}
}
}

View File

@ -19,13 +19,15 @@ using osu.Framework.Timing;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Edit.Components;
using osu.Game.Screens.Edit.Components.Menus;
using osu.Game.Screens.Edit.Compose;
using osu.Game.Screens.Edit.Design;
using osuTK.Input;
using System.Collections.Generic;
using osu.Framework;
using osu.Framework.Input.Bindings;
using osu.Game.Input.Bindings;
using osu.Game.Screens.Edit.Compose;
using osu.Game.Screens.Edit.Setup;
using osu.Game.Screens.Edit.Timing;
using osu.Game.Users;
namespace osu.Game.Screens.Edit
@ -258,6 +260,10 @@ namespace osu.Game.Screens.Edit
switch (e.NewValue)
{
case EditorScreenMode.SongSetup:
currentScreen = new SetupScreen();
break;
case EditorScreenMode.Compose:
currentScreen = new ComposeScreen();
break;
@ -266,8 +272,8 @@ namespace osu.Game.Screens.Edit
currentScreen = new DesignScreen();
break;
default:
currentScreen = new EditorScreen();
case EditorScreenMode.Timing:
currentScreen = new TimingScreen();
break;
}

View File

@ -10,16 +10,17 @@ using osu.Game.Beatmaps;
namespace osu.Game.Screens.Edit
{
/// <summary>
/// TODO: eventually make this inherit Screen and add a local scren stack inside the Editor.
/// TODO: eventually make this inherit Screen and add a local screen stack inside the Editor.
/// </summary>
public class EditorScreen : Container
public abstract class EditorScreen : Container
{
protected readonly IBindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
[Resolved]
protected IBindable<WorkingBeatmap> Beatmap { get; private set; }
protected override Container<Drawable> Content => content;
private readonly Container content;
public EditorScreen()
protected EditorScreen()
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
@ -28,12 +29,6 @@ namespace osu.Game.Screens.Edit
InternalChild = content = new Container { RelativeSizeAxes = Axes.Both };
}
[BackgroundDependencyLoader]
private void load(IBindable<WorkingBeatmap> beatmap)
{
Beatmap.BindTo(beatmap);
}
protected override void LoadComplete()
{
base.LoadComplete();

View File

@ -0,0 +1,107 @@
// 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 JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Screens.Edit.Compose.Components;
using osu.Game.Screens.Edit.Compose.Components.Timeline;
using osuTK.Graphics;
namespace osu.Game.Screens.Edit
{
public abstract class EditorScreenWithTimeline : EditorScreen
{
private const float vertical_margins = 10;
private const float horizontal_margins = 20;
private readonly BindableBeatDivisor beatDivisor = new BindableBeatDivisor();
[BackgroundDependencyLoader(true)]
private void load([CanBeNull] BindableBeatDivisor beatDivisor)
{
if (beatDivisor != null)
this.beatDivisor.BindTo(beatDivisor);
Container mainContent;
Children = new Drawable[]
{
new GridContainer
{
RelativeSizeAxes = Axes.Both,
Content = new[]
{
new Drawable[]
{
new Container
{
Name = "Timeline",
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black.Opacity(0.5f)
},
new Container
{
Name = "Timeline content",
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = horizontal_margins, Vertical = vertical_margins },
Child = new GridContainer
{
RelativeSizeAxes = Axes.Both,
Content = new[]
{
new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Right = 5 },
Child = CreateTimeline()
},
new BeatDivisorControl(beatDivisor) { RelativeSizeAxes = Axes.Both }
},
},
ColumnDimensions = new[]
{
new Dimension(),
new Dimension(GridSizeMode.Absolute, 90),
}
},
}
}
}
},
new Drawable[]
{
mainContent = new Container
{
Name = "Main content",
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = horizontal_margins, Vertical = vertical_margins },
}
}
},
RowDimensions = new[] { new Dimension(GridSizeMode.Absolute, 110) }
},
};
LoadComponentAsync(CreateMainContent(), content =>
{
mainContent.Add(content);
content.FadeInFromZero(300, Easing.OutQuint);
});
}
protected abstract Drawable CreateMainContent();
protected virtual Drawable CreateTimeline() => new TimelineArea { RelativeSizeAxes = Axes.Both };
}
}

View File

@ -0,0 +1,13 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
namespace osu.Game.Screens.Edit.Setup
{
public class SetupScreen : EditorScreen
{
public SetupScreen()
{
Child = new ScreenWhiteBox.UnderConstructionMessage("Setup mode");
}
}
}

View File

@ -0,0 +1,13 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
namespace osu.Game.Screens.Edit.Timing
{
public class TimingScreen : EditorScreen
{
public TimingScreen()
{
Child = new ScreenWhiteBox.UnderConstructionMessage("Timing mode");
}
}
}

View File

@ -2,87 +2,42 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
using osu.Framework.Screens;
using osu.Framework.Graphics;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.IO.Archives;
namespace osu.Game.Screens.Menu
{
public class IntroCircles : IntroScreen
{
private const string menu_music_beatmap_hash = "3c8b1fcc9434dbb29e2fb613d3b9eada9d7bb6c125ceb32396c3b53437280c83";
protected override string BeatmapHash => "3c8b1fcc9434dbb29e2fb613d3b9eada9d7bb6c125ceb32396c3b53437280c83";
private SampleChannel welcome;
private Bindable<bool> menuMusic;
private Track track;
private WorkingBeatmap introBeatmap;
[BackgroundDependencyLoader]
private void load(OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game, ISampleStore samples)
{
menuMusic = config.GetBindable<bool>(OsuSetting.MenuMusic);
BeatmapSetInfo setInfo = null;
if (!menuMusic.Value)
{
var sets = beatmaps.GetAllUsableBeatmapSets();
if (sets.Count > 0)
setInfo = beatmaps.QueryBeatmapSet(s => s.ID == sets[RNG.Next(0, sets.Count - 1)].ID);
}
if (setInfo == null)
{
setInfo = beatmaps.QueryBeatmapSet(b => b.Hash == menu_music_beatmap_hash);
if (setInfo == null)
{
// we need to import the default menu background beatmap
setInfo = beatmaps.Import(new ZipArchiveReader(game.Resources.GetStream(@"Tracks/circles.osz"), "circles.osz")).Result;
setInfo.Protected = true;
beatmaps.Update(setInfo);
}
}
introBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]);
track = introBeatmap.Track;
if (config.Get<bool>(OsuSetting.MenuVoice))
welcome = samples.Get(@"welcome");
}
protected override string BeatmapFile => "circles.osz";
private const double delay_step_one = 2300;
private const double delay_step_two = 600;
private SampleChannel welcome;
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
if (MenuVoice.Value)
welcome = audio.Samples.Get(@"welcome");
}
protected override void LogoArriving(OsuLogo logo, bool resuming)
{
base.LogoArriving(logo, resuming);
if (!resuming)
{
Beatmap.Value = introBeatmap;
introBeatmap = null;
welcome?.Play();
Scheduler.AddDelayed(delegate
{
// Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Menu.
if (menuMusic.Value)
{
track.Restart();
track = null;
}
StartTrack();
PrepareMenuLoad();
@ -97,8 +52,6 @@ namespace osu.Game.Screens.Menu
public override void OnSuspending(IScreen next)
{
track = null;
this.FadeOut(300);
base.OnSuspending(next);
}

View File

@ -4,12 +4,16 @@
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.MathUtils;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.IO.Archives;
using osu.Game.Screens.Backgrounds;
using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
@ -17,51 +21,90 @@ namespace osu.Game.Screens.Menu
{
public abstract class IntroScreen : StartupScreen
{
private readonly BindableDouble exitingVolumeFade = new BindableDouble(1);
public const int EXIT_DELAY = 3000;
[Resolved]
private AudioManager audio { get; set; }
private SampleChannel seeya;
private Bindable<bool> menuVoice;
private LeasedBindable<WorkingBeatmap> beatmap;
public new Bindable<WorkingBeatmap> Beatmap => beatmap;
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBlack();
[BackgroundDependencyLoader]
private void load(OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game)
{
// prevent user from changing beatmap while the intro is still runnning.
beatmap = base.Beatmap.BeginLease(false);
menuVoice = config.GetBindable<bool>(OsuSetting.MenuVoice);
seeya = audio.Samples.Get(@"seeya");
}
/// <summary>
/// Whether we have loaded the menu previously.
/// </summary>
public bool DidLoadMenu { get; private set; }
public override bool OnExiting(IScreen next)
/// <summary>
/// A hash used to find the associated beatmap if already imported.
/// </summary>
protected abstract string BeatmapHash { get; }
/// <summary>
/// A source file to use as an import source if the intro beatmap is not yet present.
/// Should be within the "Tracks" namespace of game resources.
/// </summary>
protected abstract string BeatmapFile { get; }
protected IBindable<bool> MenuVoice { get; private set; }
protected IBindable<bool> MenuMusic { get; private set; }
private WorkingBeatmap introBeatmap;
protected Track Track { get; private set; }
private readonly BindableDouble exitingVolumeFade = new BindableDouble(1);
private const int exit_delay = 3000;
private SampleChannel seeya;
private LeasedBindable<WorkingBeatmap> beatmap;
private MainMenu mainMenu;
[Resolved]
private AudioManager audio { get; set; }
[BackgroundDependencyLoader]
private void load(OsuConfigManager config, SkinManager skinManager, BeatmapManager beatmaps, Framework.Game game)
{
//cancel exiting if we haven't loaded the menu yet.
return !DidLoadMenu;
// prevent user from changing beatmap while the intro is still runnning.
beatmap = Beatmap.BeginLease(false);
MenuVoice = config.GetBindable<bool>(OsuSetting.MenuVoice);
MenuMusic = config.GetBindable<bool>(OsuSetting.MenuMusic);
seeya = audio.Samples.Get(@"seeya");
BeatmapSetInfo setInfo = null;
if (!MenuMusic.Value)
{
var sets = beatmaps.GetAllUsableBeatmapSets();
if (sets.Count > 0)
setInfo = beatmaps.QueryBeatmapSet(s => s.ID == sets[RNG.Next(0, sets.Count - 1)].ID);
}
if (setInfo == null)
{
setInfo = beatmaps.QueryBeatmapSet(b => b.Hash == BeatmapHash);
if (setInfo == null)
{
// we need to import the default menu background beatmap
setInfo = beatmaps.Import(new ZipArchiveReader(game.Resources.GetStream($"Tracks/{BeatmapFile}"), BeatmapFile)).Result;
setInfo.Protected = true;
beatmaps.Update(setInfo);
}
}
introBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]);
Track = introBeatmap.Track;
}
public override bool OnExiting(IScreen next) => !DidLoadMenu;
public override void OnResuming(IScreen last)
{
this.FadeIn(300);
double fadeOutTime = EXIT_DELAY;
double fadeOutTime = exit_delay;
//we also handle the exit transition.
if (menuVoice.Value)
if (MenuVoice.Value)
seeya.Play();
else
fadeOutTime = 500;
@ -75,6 +118,21 @@ namespace osu.Game.Screens.Menu
base.OnResuming(last);
}
public override void OnSuspending(IScreen next)
{
base.OnSuspending(next);
Track = null;
}
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBlack();
protected void StartTrack()
{
// Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Menu.
if (MenuMusic.Value)
Track.Restart();
}
protected override void LogoArriving(OsuLogo logo, bool resuming)
{
base.LogoArriving(logo, resuming);
@ -85,6 +143,9 @@ namespace osu.Game.Screens.Menu
if (!resuming)
{
beatmap.Value = introBeatmap;
introBeatmap = null;
logo.MoveTo(new Vector2(0.5f));
logo.ScaleTo(Vector2.One);
logo.Hide();
@ -92,7 +153,7 @@ namespace osu.Game.Screens.Menu
else
{
const int quick_appear = 350;
int initialMovementTime = logo.Alpha > 0.2f ? quick_appear : 0;
var initialMovementTime = logo.Alpha > 0.2f ? quick_appear : 0;
logo.MoveTo(new Vector2(0.5f), initialMovementTime, Easing.OutQuint);
@ -100,17 +161,12 @@ namespace osu.Game.Screens.Menu
.ScaleTo(1, initialMovementTime, Easing.OutQuint)
.FadeIn(quick_appear, Easing.OutQuint)
.Then()
.RotateTo(20, EXIT_DELAY * 1.5f)
.FadeOut(EXIT_DELAY);
.RotateTo(20, exit_delay * 1.5f)
.FadeOut(exit_delay);
}
}
private MainMenu mainMenu;
protected void PrepareMenuLoad()
{
LoadComponentAsync(mainMenu = new MainMenu());
}
protected void PrepareMenuLoad() => LoadComponentAsync(mainMenu = new MainMenu());
protected void LoadMenu()
{

View File

@ -7,8 +7,6 @@ using System.IO;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables;
using osu.Framework.Screens;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@ -17,12 +15,9 @@ using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Video;
using osu.Framework.MathUtils;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.IO.Archives;
using osu.Game.Rulesets;
using osu.Game.Screens.Backgrounds;
using osuTK;
@ -32,9 +27,9 @@ namespace osu.Game.Screens.Menu
{
public class IntroTriangles : IntroScreen
{
private const string menu_music_beatmap_hash = "a1556d0801b3a6b175dda32ef546f0ec812b400499f575c44fccbe9c67f9b1e5";
protected override string BeatmapHash => "a1556d0801b3a6b175dda32ef546f0ec812b400499f575c44fccbe9c67f9b1e5";
private SampleChannel welcome;
protected override string BeatmapFile => "triangles.osz";
protected override BackgroundScreen CreateBackground() => background = new BackgroundScreenDefault(false)
{
@ -44,47 +39,14 @@ namespace osu.Game.Screens.Menu
[Resolved]
private AudioManager audio { get; set; }
private Bindable<bool> menuMusic;
private Track track;
private WorkingBeatmap introBeatmap;
private BackgroundScreenDefault background;
private SampleChannel welcome;
[BackgroundDependencyLoader]
private void load(OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game)
private void load(AudioManager audio)
{
menuMusic = config.GetBindable<bool>(OsuSetting.MenuMusic);
BeatmapSetInfo setInfo = null;
if (!menuMusic.Value)
{
var sets = beatmaps.GetAllUsableBeatmapSets();
if (sets.Count > 0)
setInfo = beatmaps.QueryBeatmapSet(s => s.ID == sets[RNG.Next(0, sets.Count - 1)].ID);
}
if (setInfo == null)
{
setInfo = beatmaps.QueryBeatmapSet(b => b.Hash == menu_music_beatmap_hash);
if (setInfo == null)
{
// we need to import the default menu background beatmap
setInfo = beatmaps.Import(new ZipArchiveReader(game.Resources.GetStream(@"Tracks/triangles.osz"), "triangles.osz")).Result;
setInfo.Protected = true;
beatmaps.Update(setInfo);
}
}
introBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]);
track = introBeatmap.Track;
track.Reset();
if (config.Get<bool>(OsuSetting.MenuVoice) && !menuMusic.Value)
// triangles has welcome sound included in the track. only play this if the user doesn't want menu music.
if (MenuVoice.Value && !MenuMusic.Value)
welcome = audio.Samples.Get(@"welcome");
}
@ -96,24 +58,19 @@ namespace osu.Game.Screens.Menu
if (!resuming)
{
Beatmap.Value = introBeatmap;
introBeatmap = null;
PrepareMenuLoad();
LoadComponentAsync(new TrianglesIntroSequence(logo, background)
{
RelativeSizeAxes = Axes.Both,
Clock = new FramedClock(menuMusic.Value ? track : null),
Clock = new FramedClock(MenuMusic.Value ? Track : null),
LoadMenu = LoadMenu
}, t =>
{
AddInternal(t);
welcome?.Play();
// Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Menu.
if (menuMusic.Value)
track.Start();
StartTrack();
});
}
}
@ -124,12 +81,6 @@ namespace osu.Game.Screens.Menu
background.FadeOut(100);
}
public override void OnSuspending(IScreen next)
{
track = null;
base.OnSuspending(next);
}
private class TrianglesIntroSequence : CompositeDrawable
{
private readonly OsuLogo logo;

View File

@ -48,7 +48,7 @@ namespace osu.Game.Screens.Play.HUD
private void onNewJudgement(JudgementResult result)
{
if (result.HitObject.HitWindows == null)
if (result.HitObject.HitWindows.WindowFor(HitResult.Miss) == 0)
return;
foreach (var c in Children)

View File

@ -20,38 +20,17 @@ namespace osu.Game.Screens
{
public class ScreenWhiteBox : OsuScreen
{
private readonly UnderConstructionMessage message;
private const double transition_time = 1000;
protected virtual IEnumerable<Type> PossibleChildren => null;
private readonly FillFlowContainer textContainer;
private readonly Container boxContainer;
protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg2");
public override void OnEntering(IScreen last)
{
base.OnEntering(last);
Alpha = 0;
textContainer.Position = new Vector2(DrawSize.X / 16, 0);
boxContainer.ScaleTo(0.2f);
boxContainer.RotateTo(-20);
using (BeginDelayedSequence(300, true))
{
boxContainer.ScaleTo(1, transition_time, Easing.OutElastic);
boxContainer.RotateTo(0, transition_time / 2, Easing.OutQuint);
textContainer.MoveTo(Vector2.Zero, transition_time, Easing.OutExpo);
this.FadeIn(transition_time, Easing.OutExpo);
}
}
public override bool OnExiting(IScreen next)
{
textContainer.MoveTo(new Vector2(DrawSize.X / 16, 0), transition_time, Easing.OutExpo);
message.TextContainer.MoveTo(new Vector2(DrawSize.X / 16, 0), transition_time, Easing.OutExpo);
this.FadeOut(transition_time, Easing.OutExpo);
return base.OnExiting(next);
@ -61,7 +40,7 @@ namespace osu.Game.Screens
{
base.OnSuspending(next);
textContainer.MoveTo(new Vector2(-(DrawSize.X / 16), 0), transition_time, Easing.OutExpo);
message.TextContainer.MoveTo(new Vector2(-(DrawSize.X / 16), 0), transition_time, Easing.OutExpo);
this.FadeOut(transition_time, Easing.OutExpo);
}
@ -69,7 +48,7 @@ namespace osu.Game.Screens
{
base.OnResuming(last);
textContainer.MoveTo(Vector2.Zero, transition_time, Easing.OutExpo);
message.TextContainer.MoveTo(Vector2.Zero, transition_time, Easing.OutExpo);
this.FadeIn(transition_time, Easing.OutExpo);
}
@ -79,65 +58,7 @@ namespace osu.Game.Screens
InternalChildren = new Drawable[]
{
boxContainer = new Container
{
Size = new Vector2(0.3f),
RelativeSizeAxes = Axes.Both,
CornerRadius = 20,
Masking = true,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = getColourFor(GetType()),
Alpha = 0.2f,
Blending = BlendingParameters.Additive,
},
textContainer = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new SpriteIcon
{
Icon = FontAwesome.Solid.UniversalAccess,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Size = new Vector2(50),
},
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = GetType().Name,
Colour = getColourFor(GetType()).Lighten(0.8f),
Font = OsuFont.GetFont(size: 50),
},
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "is not yet ready for use!",
Font = OsuFont.GetFont(size: 20),
},
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "please check back a bit later.",
Font = OsuFont.GetFont(size: 14),
},
}
},
}
},
message = new UnderConstructionMessage(GetType().Name),
childModeButtons = new FillFlowContainer
{
Direction = FillDirection.Vertical,
@ -155,24 +76,24 @@ namespace osu.Game.Screens
childModeButtons.Add(new ChildModeButton
{
Text = $@"{t.Name}",
BackgroundColour = getColourFor(t),
HoverColour = getColourFor(t).Lighten(0.2f),
BackgroundColour = getColourFor(t.Name),
HoverColour = getColourFor(t.Name).Lighten(0.2f),
Action = delegate { this.Push(Activator.CreateInstance(t) as Screen); }
});
}
}
}
private Color4 getColourFor(Type type)
private static Color4 getColourFor(object type)
{
int hash = type.Name.GetHashCode();
int hash = type.GetHashCode();
byte r = (byte)MathHelper.Clamp(((hash & 0xFF0000) >> 16) * 0.8f, 20, 255);
byte g = (byte)MathHelper.Clamp(((hash & 0x00FF00) >> 8) * 0.8f, 20, 255);
byte b = (byte)MathHelper.Clamp((hash & 0x0000FF) * 0.8f, 20, 255);
return new Color4(r, g, b, 255);
}
public class ChildModeButton : TwoLayerButton
private class ChildModeButton : TwoLayerButton
{
public ChildModeButton()
{
@ -181,5 +102,104 @@ namespace osu.Game.Screens
Origin = Anchor.BottomRight;
}
}
public class UnderConstructionMessage : CompositeDrawable
{
public FillFlowContainer TextContainer { get; }
private readonly Container boxContainer;
public UnderConstructionMessage(string name)
{
RelativeSizeAxes = Axes.Both;
Size = new Vector2(0.3f);
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
var colour = getColourFor(name);
InternalChildren = new Drawable[]
{
boxContainer = new Container
{
CornerRadius = 20,
Masking = true,
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colour,
Alpha = 0.2f,
Blending = BlendingParameters.Additive,
},
TextContainer = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new SpriteIcon
{
Icon = FontAwesome.Solid.UniversalAccess,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Size = new Vector2(50),
},
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = name,
Colour = colour.Lighten(0.8f),
Font = OsuFont.GetFont(size: 36),
},
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "is not yet ready for use!",
Font = OsuFont.GetFont(size: 20),
},
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "please check back a bit later.",
Font = OsuFont.GetFont(size: 14),
},
}
},
}
},
};
}
protected override void LoadComplete()
{
base.LoadComplete();
TextContainer.Position = new Vector2(DrawSize.X / 16, 0);
boxContainer.Hide();
boxContainer.ScaleTo(0.2f);
boxContainer.RotateTo(-20);
using (BeginDelayedSequence(300, true))
{
boxContainer.ScaleTo(1, transition_time, Easing.OutElastic);
boxContainer.RotateTo(0, transition_time / 2, Easing.OutQuint);
TextContainer.MoveTo(Vector2.Zero, transition_time, Easing.OutExpo);
boxContainer.FadeIn(transition_time, Easing.OutExpo);
}
}
}
}
}

View File

@ -66,11 +66,7 @@ namespace osu.Game.Screens.Select
/// </summary>
protected readonly Container FooterPanels;
protected override BackgroundScreen CreateBackground()
{
var background = new BackgroundScreenBeatmap();
return background;
}
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap();
protected readonly BeatmapCarousel Carousel;
private readonly BeatmapInfoWedge beatmapInfoWedge;
@ -412,9 +408,6 @@ namespace osu.Game.Screens.Select
WorkingBeatmap previous = Beatmap.Value;
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap, previous);
if (this.IsCurrentScreen() && Beatmap.Value?.Track != previous?.Track)
ensurePlayingSelected(true);
if (beatmap != null)
{
if (beatmap.BeatmapSetInfoID == beatmapNoDebounce?.BeatmapSetInfoID)
@ -424,6 +417,9 @@ namespace osu.Game.Screens.Select
}
}
if (this.IsCurrentScreen())
ensurePlayingSelected();
UpdateBeatmap(Beatmap.Value);
}
}
@ -490,7 +486,9 @@ namespace osu.Game.Screens.Select
if (Beatmap != null && !Beatmap.Value.BeatmapSetInfo.DeletePending)
{
UpdateBeatmap(Beatmap.Value);
ensurePlayingSelected();
// restart playback on returning to song select, regardless.
music?.Play();
}
base.OnResuming(last);
@ -581,19 +579,24 @@ namespace osu.Game.Screens.Select
beatmap.Track.Looping = true;
}
private void ensurePlayingSelected(bool restart = false)
private readonly WeakReference<Track> lastTrack = new WeakReference<Track>(null);
/// <summary>
/// Ensures some music is playing for the current track.
/// Will resume playback from a manual user pause if the track has changed.
/// </summary>
private void ensurePlayingSelected()
{
Track track = Beatmap.Value.Track;
if (!track.IsRunning)
{
track.RestartPoint = Beatmap.Value.Metadata.PreviewTime;
bool isNewTrack = !lastTrack.TryGetTarget(out var last) || last != track;
if (restart)
track.Restart();
else
track.Start();
}
track.RestartPoint = Beatmap.Value.Metadata.PreviewTime;
if (!track.IsRunning && (music?.IsUserPaused != true || isNewTrack))
music?.Play(true);
lastTrack.SetTarget(track);
}
private void onBeatmapSetAdded(BeatmapSetInfo s) => Carousel.UpdateBeatmapSet(s);

View File

@ -25,8 +25,8 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.913.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.930.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.1011.0" />
<PackageReference Include="SharpCompress" Version="0.24.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" />

View File

@ -117,13 +117,13 @@
<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.Game.Resources" Version="2019.913.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.930.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.930.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2019.1010.0" />
<PackageReference Include="ppy.osu.Framework" Version="2019.1011.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2019.1011.0" />
<PackageReference Include="SharpCompress" Version="0.24.0" />
<PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" />
<PackageReference Include="System.ComponentModel.Annotations" Version="4.6.0" />
<PackageReference Include="ppy.osu.Framework.NativeLibs" Version="2019.813.0" ExcludeAssets="all" />
<PackageReference Include="ppy.osu.Framework.NativeLibs" Version="2019.1010.0" ExcludeAssets="all" />
</ItemGroup>
</Project>