mirror of
https://github.com/ppy/osu.git
synced 2024-12-15 08:22:56 +08:00
Merge branch 'master' into fix-menu-state-display
This commit is contained in:
commit
67c044773b
@ -191,6 +191,8 @@ csharp_style_prefer_index_operator = false:silent
|
|||||||
csharp_style_prefer_range_operator = false:silent
|
csharp_style_prefer_range_operator = false:silent
|
||||||
csharp_style_prefer_switch_expression = false:none
|
csharp_style_prefer_switch_expression = false:none
|
||||||
|
|
||||||
|
csharp_style_namespace_declarations = block_scoped:warning
|
||||||
|
|
||||||
[*.{yaml,yml}]
|
[*.{yaml,yml}]
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
indent_style = space
|
indent_style = space
|
||||||
|
34
.github/workflows/ci.yml
vendored
34
.github/workflows/ci.yml
vendored
@ -13,17 +13,17 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
# FIXME: Tools won't run in .NET 6.0 unless you install 3.1.x LTS side by side.
|
# FIXME: Tools won't run in .NET 6.0 unless you install 3.1.x LTS side by side.
|
||||||
# https://itnext.io/how-to-support-multiple-net-sdks-in-github-actions-workflows-b988daa884e
|
# https://itnext.io/how-to-support-multiple-net-sdks-in-github-actions-workflows-b988daa884e
|
||||||
- name: Install .NET 3.1.x LTS
|
- name: Install .NET 3.1.x LTS
|
||||||
uses: actions/setup-dotnet@v1
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: "3.1.x"
|
dotnet-version: "3.1.x"
|
||||||
|
|
||||||
- name: Install .NET 6.0.x
|
- name: Install .NET 6.0.x
|
||||||
uses: actions/setup-dotnet@v1
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: "6.0.x"
|
dotnet-version: "6.0.x"
|
||||||
|
|
||||||
@ -77,10 +77,10 @@ jobs:
|
|||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install .NET 6.0.x
|
- name: Install .NET 6.0.x
|
||||||
uses: actions/setup-dotnet@v1
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: "6.0.x"
|
dotnet-version: "6.0.x"
|
||||||
|
|
||||||
@ -94,7 +94,7 @@ jobs:
|
|||||||
# Attempt to upload results even if test fails.
|
# Attempt to upload results even if test fails.
|
||||||
# https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#always
|
# https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#always
|
||||||
- name: Upload Test Results
|
- name: Upload Test Results
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v3
|
||||||
if: ${{ always() }}
|
if: ${{ always() }}
|
||||||
with:
|
with:
|
||||||
name: osu-test-results-${{matrix.os.prettyname}}-${{matrix.threadingMode}}
|
name: osu-test-results-${{matrix.os.prettyname}}-${{matrix.threadingMode}}
|
||||||
@ -106,10 +106,10 @@ jobs:
|
|||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install .NET 6.0.x
|
- name: Install .NET 6.0.x
|
||||||
uses: actions/setup-dotnet@v1
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: "6.0.x"
|
dotnet-version: "6.0.x"
|
||||||
|
|
||||||
@ -121,14 +121,26 @@ jobs:
|
|||||||
|
|
||||||
build-only-ios:
|
build-only-ios:
|
||||||
name: Build only (iOS)
|
name: Build only (iOS)
|
||||||
runs-on: macos-latest
|
# `macos-13` is required, because Xcode 14.3 is required (see below).
|
||||||
|
# TODO: can be changed to `macos-latest` once `macos-13` becomes latest (currently in beta)
|
||||||
|
runs-on: macos-13
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
# newest Microsoft.iOS.Sdk versions require Xcode 14.3.
|
||||||
|
# 14.3 is currently not the default Xcode version (https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md#xcode),
|
||||||
|
# so set it manually.
|
||||||
|
# TODO: remove when 14.3 becomes the default Xcode version.
|
||||||
|
- name: Set Xcode version
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
sudo xcode-select -s "/Applications/Xcode_14.3.app"
|
||||||
|
echo "MD_APPLE_SDK_ROOT=/Applications/Xcode_14.3.app" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Install .NET 6.0.x
|
- name: Install .NET 6.0.x
|
||||||
uses: actions/setup-dotnet@v1
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: "6.0.x"
|
dotnet-version: "6.0.x"
|
||||||
|
|
||||||
|
18
.github/workflows/diffcalc.yml
vendored
18
.github/workflows/diffcalc.yml
vendored
@ -48,8 +48,8 @@ jobs:
|
|||||||
CONTINUE="no"
|
CONTINUE="no"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "::set-output name=continue::${CONTINUE}"
|
echo "continue=${CONTINUE}" >> $GITHUB_OUTPUT
|
||||||
echo "::set-output name=matrix::${MATRIX_JSON}"
|
echo "matrix=${MATRIX_JSON}" >> $GITHUB_OUTPUT
|
||||||
diffcalc:
|
diffcalc:
|
||||||
name: Run
|
name: Run
|
||||||
runs-on: self-hosted
|
runs-on: self-hosted
|
||||||
@ -80,34 +80,34 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
echo "::set-output name=branchname::$(curl -H "Authorization: token ${GITHUB_TOKEN}" ${{ github.event.issue.pull_request.url }} | jq '.head.ref' | sed 's/\"//g')"
|
echo "branchname=$(curl -H "Authorization: token ${GITHUB_TOKEN}" ${{ github.event.issue.pull_request.url }} | jq '.head.ref' | sed 's/\"//g')" >> $GITHUB_OUTPUT
|
||||||
echo "::set-output name=repo::$(curl -H "Authorization: token ${GITHUB_TOKEN}" ${{ github.event.issue.pull_request.url }} | jq '.head.repo.full_name' | sed 's/\"//g')"
|
echo "repo=$(curl -H "Authorization: token ${GITHUB_TOKEN}" ${{ github.event.issue.pull_request.url }} | jq '.head.repo.full_name' | sed 's/\"//g')" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
# Checkout osu
|
# Checkout osu
|
||||||
- name: Checkout osu (master)
|
- name: Checkout osu (master)
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
path: 'master/osu'
|
path: 'master/osu'
|
||||||
- name: Checkout osu (pr)
|
- name: Checkout osu (pr)
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
path: 'pr/osu'
|
path: 'pr/osu'
|
||||||
repository: ${{ steps.upstreambranch.outputs.repo }}
|
repository: ${{ steps.upstreambranch.outputs.repo }}
|
||||||
ref: ${{ steps.upstreambranch.outputs.branchname }}
|
ref: ${{ steps.upstreambranch.outputs.branchname }}
|
||||||
|
|
||||||
- name: Checkout osu-difficulty-calculator (master)
|
- name: Checkout osu-difficulty-calculator (master)
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: ppy/osu-difficulty-calculator
|
repository: ppy/osu-difficulty-calculator
|
||||||
path: 'master/osu-difficulty-calculator'
|
path: 'master/osu-difficulty-calculator'
|
||||||
- name: Checkout osu-difficulty-calculator (pr)
|
- name: Checkout osu-difficulty-calculator (pr)
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
repository: ppy/osu-difficulty-calculator
|
repository: ppy/osu-difficulty-calculator
|
||||||
path: 'pr/osu-difficulty-calculator'
|
path: 'pr/osu-difficulty-calculator'
|
||||||
|
|
||||||
- name: Install .NET 5.0.x
|
- name: Install .NET 5.0.x
|
||||||
uses: actions/setup-dotnet@v1
|
uses: actions/setup-dotnet@v3
|
||||||
with:
|
with:
|
||||||
dotnet-version: "5.0.x"
|
dotnet-version: "5.0.x"
|
||||||
|
|
||||||
|
2
.github/workflows/sentry-release.yml
vendored
2
.github/workflows/sentry-release.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
53
.github/workflows/update-web-mod-definitions.yml
vendored
Normal file
53
.github/workflows/update-web-mod-definitions.yml
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
name: Update osu-web mod definitions
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read # to fetch code (actions/checkout)
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update-mod-definitions:
|
||||||
|
name: Update osu-web mod definitions
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Install .NET 6.0.x
|
||||||
|
uses: actions/setup-dotnet@v3
|
||||||
|
with:
|
||||||
|
dotnet-version: "6.0.x"
|
||||||
|
|
||||||
|
- name: Checkout ppy/osu
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
path: osu
|
||||||
|
|
||||||
|
- name: Checkout ppy/osu-tools
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: ppy/osu-tools
|
||||||
|
path: osu-tools
|
||||||
|
|
||||||
|
- name: Checkout ppy/osu-web
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
repository: ppy/osu-web
|
||||||
|
path: osu-web
|
||||||
|
|
||||||
|
- name: Setup local game checkout for tools
|
||||||
|
run: ./UseLocalOsu.sh
|
||||||
|
working-directory: ./osu-tools
|
||||||
|
|
||||||
|
- name: Regenerate mod definitions
|
||||||
|
run: dotnet run --project PerformanceCalculator -- mods > ../osu-web/database/mods.json
|
||||||
|
working-directory: ./osu-tools
|
||||||
|
|
||||||
|
- name: Create pull request with changes
|
||||||
|
uses: peter-evans/create-pull-request@v5
|
||||||
|
with:
|
||||||
|
title: Update mod definitions
|
||||||
|
body: "This PR has been auto-generated to update the mod definitions to match ppy/osu@${{ github.ref_name }}."
|
||||||
|
branch: update-mod-definitions
|
||||||
|
commit-message: Update mod definitions
|
||||||
|
path: osu-web
|
||||||
|
token: ${{ secrets.OSU_WEB_PULL_REQUEST_PAT }}
|
@ -1,7 +1,7 @@
|
|||||||
<!-- Contains required properties for osu!framework projects. -->
|
<!-- Contains required properties for osu!framework projects. -->
|
||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup Label="C#">
|
<PropertyGroup Label="C#">
|
||||||
<LangVersion>9.0</LangVersion>
|
<LangVersion>10.0</LangVersion>
|
||||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
@ -105,7 +105,7 @@ When it comes to contributing to the project, the two main things you can do to
|
|||||||
|
|
||||||
If you wish to help with localisation efforts, head over to [crowdin](https://crowdin.com/project/osu-web).
|
If you wish to help with localisation efforts, head over to [crowdin](https://crowdin.com/project/osu-web).
|
||||||
|
|
||||||
For those interested, we love to reward quality contributions via [bounties](https://docs.google.com/spreadsheets/d/1jNXfj_S3Pb5PErA-czDdC9DUu4IgUbe1Lt8E7CYUJuE/view?&rm=minimal#gid=523803337), paid out via PayPal or osu!supporter tags. Don't hesitate to [request a bounty](https://docs.google.com/forms/d/e/1FAIpQLSet_8iFAgPMG526pBZ2Kic6HSh7XPM3fE8xPcnWNkMzINDdYg/viewform) for your work on this project.
|
We love to reward quality contributions. If you have made a large contribution, or are a regular contributor, you are welcome to [submit an expense via opencollective](https://opencollective.com/ppy/expenses/new). If you have any questions, feel free to [reach out to peppy](mailto:pe@ppy.sh) before doing so.
|
||||||
|
|
||||||
## Licence
|
## Licence
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -44,7 +43,7 @@ namespace osu.Game.Rulesets.Pippidon.Objects.Drawables
|
|||||||
|
|
||||||
public override IEnumerable<HitSampleInfo> GetSamples() => new[]
|
public override IEnumerable<HitSampleInfo> GetSamples() => new[]
|
||||||
{
|
{
|
||||||
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK)
|
new HitSampleInfo(HitSampleInfo.HIT_NORMAL)
|
||||||
};
|
};
|
||||||
|
|
||||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||||
|
@ -8,7 +8,6 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Pippidon.UI;
|
using osu.Game.Rulesets.Pippidon.UI;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
@ -44,7 +43,7 @@ namespace osu.Game.Rulesets.Pippidon.Objects.Drawables
|
|||||||
|
|
||||||
public override IEnumerable<HitSampleInfo> GetSamples() => new[]
|
public override IEnumerable<HitSampleInfo> GetSamples() => new[]
|
||||||
{
|
{
|
||||||
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK)
|
new HitSampleInfo(HitSampleInfo.HIT_NORMAL)
|
||||||
};
|
};
|
||||||
|
|
||||||
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||||
|
@ -3,15 +3,53 @@
|
|||||||
#
|
#
|
||||||
# https://github.com/ppy/osu-framework/wiki/Testing-local-framework-checkout-with-other-projects
|
# https://github.com/ppy/osu-framework/wiki/Testing-local-framework-checkout-with-other-projects
|
||||||
|
|
||||||
$CSPROJ="osu.Game/osu.Game.csproj"
|
$GAME_CSPROJ="osu.Game/osu.Game.csproj"
|
||||||
|
$ANDROID_PROPS="osu.Android.props"
|
||||||
|
$IOS_PROPS="osu.iOS.props"
|
||||||
$SLN="osu.sln"
|
$SLN="osu.sln"
|
||||||
|
|
||||||
dotnet remove $CSPROJ package ppy.osu.Framework;
|
dotnet remove $GAME_CSPROJ reference ppy.osu.Framework;
|
||||||
dotnet sln $SLN add ../osu-framework/osu.Framework/osu.Framework.csproj ../osu-framework/osu.Framework.NativeLibs/osu.Framework.NativeLibs.csproj;
|
dotnet remove $ANDROID_PROPS reference ppy.osu.Framework.Android;
|
||||||
dotnet add $CSPROJ reference ../osu-framework/osu.Framework/osu.Framework.csproj
|
dotnet remove $IOS_PROPS reference ppy.osu.Framework.iOS;
|
||||||
|
|
||||||
|
dotnet sln $SLN add ../osu-framework/osu.Framework/osu.Framework.csproj `
|
||||||
|
../osu-framework/osu.Framework.NativeLibs/osu.Framework.NativeLibs.csproj `
|
||||||
|
../osu-framework/osu.Framework.Android/osu.Framework.Android.csproj `
|
||||||
|
../osu-framework/osu.Framework.iOS/osu.Framework.iOS.csproj;
|
||||||
|
|
||||||
|
dotnet add $GAME_CSPROJ reference ../osu-framework/osu.Framework/osu.Framework.csproj;
|
||||||
|
dotnet add $ANDROID_PROPS reference ../osu-framework/osu.Framework.Android/osu.Framework.Android.csproj;
|
||||||
|
dotnet add $IOS_PROPS reference ../osu-framework/osu.Framework.iOS/osu.Framework.iOS.csproj;
|
||||||
|
|
||||||
|
# workaround for dotnet add not inserting $(MSBuildThisFileDirectory) on props files
|
||||||
|
(Get-Content "osu.Android.props") -replace "`"..\\osu-framework", "`"`$(MSBuildThisFileDirectory)..\osu-framework" | Set-Content "osu.Android.props"
|
||||||
|
(Get-Content "osu.iOS.props") -replace "`"..\\osu-framework", "`"`$(MSBuildThisFileDirectory)..\osu-framework" | Set-Content "osu.iOS.props"
|
||||||
|
|
||||||
|
# needed because iOS framework nupkg includes a set of properties to work around certain issues during building,
|
||||||
|
# and those get ignored when referencing framework via project, threfore we have to manually include it via props reference.
|
||||||
|
(Get-Content "osu.iOS.props") |
|
||||||
|
Foreach-Object {
|
||||||
|
if ($_ -match "</Project>")
|
||||||
|
{
|
||||||
|
" <Import Project=`"`$(MSBuildThisFileDirectory)../osu-framework/osu.Framework.iOS.props`"/>"
|
||||||
|
}
|
||||||
|
|
||||||
|
$_
|
||||||
|
} | Set-Content "osu.iOS.props"
|
||||||
|
|
||||||
|
$TMP=New-TemporaryFile
|
||||||
|
|
||||||
$SLNF=Get-Content "osu.Desktop.slnf" | ConvertFrom-Json
|
$SLNF=Get-Content "osu.Desktop.slnf" | ConvertFrom-Json
|
||||||
$TMP=New-TemporaryFile
|
|
||||||
$SLNF.solution.projects += ("../osu-framework/osu.Framework/osu.Framework.csproj", "../osu-framework/osu.Framework.NativeLibs/osu.Framework.NativeLibs.csproj")
|
$SLNF.solution.projects += ("../osu-framework/osu.Framework/osu.Framework.csproj", "../osu-framework/osu.Framework.NativeLibs/osu.Framework.NativeLibs.csproj")
|
||||||
ConvertTo-Json $SLNF | Out-File $TMP -Encoding UTF8
|
ConvertTo-Json $SLNF | Out-File $TMP -Encoding UTF8
|
||||||
Move-Item -Path $TMP -Destination "osu.Desktop.slnf" -Force
|
Move-Item -Path $TMP -Destination "osu.Desktop.slnf" -Force
|
||||||
|
|
||||||
|
$SLNF=Get-Content "osu.Android.slnf" | ConvertFrom-Json
|
||||||
|
$SLNF.solution.projects += ("../osu-framework/osu.Framework/osu.Framework.csproj", "../osu-framework/osu.Framework.NativeLibs/osu.Framework.NativeLibs.csproj", "../osu-framework/osu.Framework.Android/osu.Framework.Android.csproj")
|
||||||
|
ConvertTo-Json $SLNF | Out-File $TMP -Encoding UTF8
|
||||||
|
Move-Item -Path $TMP -Destination "osu.Android.slnf" -Force
|
||||||
|
|
||||||
|
$SLNF=Get-Content "osu.iOS.slnf" | ConvertFrom-Json
|
||||||
|
$SLNF.solution.projects += ("../osu-framework/osu.Framework/osu.Framework.csproj", "../osu-framework/osu.Framework.NativeLibs/osu.Framework.NativeLibs.csproj", "../osu-framework/osu.Framework.iOS/osu.Framework.iOS.csproj")
|
||||||
|
ConvertTo-Json $SLNF | Out-File $TMP -Encoding UTF8
|
||||||
|
Move-Item -Path $TMP -Destination "osu.iOS.slnf" -Force
|
||||||
|
@ -5,14 +5,41 @@
|
|||||||
#
|
#
|
||||||
# https://github.com/ppy/osu-framework/wiki/Testing-local-framework-checkout-with-other-projects
|
# https://github.com/ppy/osu-framework/wiki/Testing-local-framework-checkout-with-other-projects
|
||||||
|
|
||||||
CSPROJ="osu.Game/osu.Game.csproj"
|
GAME_CSPROJ="osu.Game/osu.Game.csproj"
|
||||||
|
ANDROID_PROPS="osu.Android.props"
|
||||||
|
IOS_PROPS="osu.iOS.props"
|
||||||
SLN="osu.sln"
|
SLN="osu.sln"
|
||||||
|
|
||||||
dotnet remove $CSPROJ package ppy.osu.Framework
|
dotnet remove $GAME_CSPROJ reference ppy.osu.Framework
|
||||||
dotnet sln $SLN add ../osu-framework/osu.Framework/osu.Framework.csproj ../osu-framework/osu.Framework.NativeLibs/osu.Framework.NativeLibs.csproj
|
dotnet remove $ANDROID_PROPS reference ppy.osu.Framework.Android
|
||||||
dotnet add $CSPROJ reference ../osu-framework/osu.Framework/osu.Framework.csproj
|
dotnet remove $IOS_PROPS reference ppy.osu.Framework.iOS
|
||||||
|
|
||||||
|
dotnet sln $SLN add ../osu-framework/osu.Framework/osu.Framework.csproj \
|
||||||
|
../osu-framework/osu.Framework.NativeLibs/osu.Framework.NativeLibs.csproj \
|
||||||
|
../osu-framework/osu.Framework.Android/osu.Framework.Android.csproj \
|
||||||
|
../osu-framework/osu.Framework.iOS/osu.Framework.iOS.csproj
|
||||||
|
|
||||||
|
dotnet add $GAME_CSPROJ reference ../osu-framework/osu.Framework/osu.Framework.csproj
|
||||||
|
dotnet add $ANDROID_PROPS reference ../osu-framework/osu.Framework.Android/osu.Framework.Android.csproj
|
||||||
|
dotnet add $IOS_PROPS reference ../osu-framework/osu.Framework.iOS/osu.Framework.iOS.csproj
|
||||||
|
|
||||||
|
# workaround for dotnet add not inserting $(MSBuildThisFileDirectory) on props files
|
||||||
|
sed -i.bak 's:"..\\osu-framework:"$(MSBuildThisFileDirectory)..\\osu-framework:g' ./osu.Android.props && rm osu.Android.props.bak
|
||||||
|
sed -i.bak 's:"..\\osu-framework:"$(MSBuildThisFileDirectory)..\\osu-framework:g' ./osu.iOS.props && rm osu.iOS.props.bak
|
||||||
|
|
||||||
|
# needed because iOS framework nupkg includes a set of properties to work around certain issues during building,
|
||||||
|
# and those get ignored when referencing framework via project, threfore we have to manually include it via props reference.
|
||||||
|
sed -i.bak '/<\/Project>/i\
|
||||||
|
<Import Project=\"$(MSBuildThisFileDirectory)../osu-framework/osu.Framework.iOS.props\"/>\
|
||||||
|
' ./osu.iOS.props && rm osu.iOS.props.bak
|
||||||
|
|
||||||
SLNF="osu.Desktop.slnf"
|
|
||||||
tmp=$(mktemp)
|
tmp=$(mktemp)
|
||||||
|
|
||||||
jq '.solution.projects += ["../osu-framework/osu.Framework/osu.Framework.csproj", "../osu-framework/osu.Framework.NativeLibs/osu.Framework.NativeLibs.csproj"]' osu.Desktop.slnf > $tmp
|
jq '.solution.projects += ["../osu-framework/osu.Framework/osu.Framework.csproj", "../osu-framework/osu.Framework.NativeLibs/osu.Framework.NativeLibs.csproj"]' osu.Desktop.slnf > $tmp
|
||||||
mv -f $tmp $SLNF
|
mv -f $tmp osu.Desktop.slnf
|
||||||
|
|
||||||
|
jq '.solution.projects += ["../osu-framework/osu.Framework/osu.Framework.csproj", "../osu-framework/osu.Framework.NativeLibs/osu.Framework.NativeLibs.csproj", "../osu-framework/osu.Framework.Android/osu.Framework.Android.csproj"]' osu.Android.slnf > $tmp
|
||||||
|
mv -f $tmp osu.Android.slnf
|
||||||
|
|
||||||
|
jq '.solution.projects += ["../osu-framework/osu.Framework/osu.Framework.csproj", "../osu-framework/osu.Framework.NativeLibs/osu.Framework.NativeLibs.csproj", "../osu-framework/osu.Framework.iOS/osu.Framework.iOS.csproj"]' osu.iOS.slnf > $tmp
|
||||||
|
mv -f $tmp osu.iOS.slnf
|
||||||
|
7
global.json
Normal file
7
global.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"sdk": {
|
||||||
|
"version": "6.0.100",
|
||||||
|
"rollForward": "latestFeature"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,9 +8,13 @@
|
|||||||
<!-- NullabilityInfoContextSupport is disabled by default for Android -->
|
<!-- NullabilityInfoContextSupport is disabled by default for Android -->
|
||||||
<NullabilityInfoContextSupport>true</NullabilityInfoContextSupport>
|
<NullabilityInfoContextSupport>true</NullabilityInfoContextSupport>
|
||||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||||
|
<AndroidManifestMerger>manifestmerger.jar</AndroidManifestMerger>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.131.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.513.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AndroidManifestOverlay Include="$(MSBuildThisFileDirectory)osu.Android\Properties\AndroidManifestOverlay.xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||||
|
15
osu.Android/Properties/AndroidManifestOverlay.xml
Normal file
15
osu.Android/Properties/AndroidManifestOverlay.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<queries>
|
||||||
|
<intent>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="https" />
|
||||||
|
</intent>
|
||||||
|
<intent>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="mailto" />
|
||||||
|
</intent>
|
||||||
|
</queries>
|
||||||
|
</manifest>
|
@ -5,7 +5,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -139,7 +138,17 @@ namespace osu.Desktop
|
|||||||
|
|
||||||
desktopWindow.CursorState |= CursorState.Hidden;
|
desktopWindow.CursorState |= CursorState.Hidden;
|
||||||
desktopWindow.Title = Name;
|
desktopWindow.Title = Name;
|
||||||
desktopWindow.DragDrop += f => fileDrop(new[] { f });
|
desktopWindow.DragDrop += f =>
|
||||||
|
{
|
||||||
|
// on macOS, URL associations are handled via SDL_DROPFILE events.
|
||||||
|
if (f.StartsWith(OSU_PROTOCOL, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
HandleLink(f);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fileDrop(new[] { f });
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override BatteryInfo CreateBatteryInfo() => new SDL2BatteryInfo();
|
protected override BatteryInfo CreateBatteryInfo() => new SDL2BatteryInfo();
|
||||||
@ -151,10 +160,6 @@ namespace osu.Desktop
|
|||||||
{
|
{
|
||||||
lock (importableFiles)
|
lock (importableFiles)
|
||||||
{
|
{
|
||||||
string firstExtension = Path.GetExtension(filePaths.First());
|
|
||||||
|
|
||||||
if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return;
|
|
||||||
|
|
||||||
importableFiles.AddRange(filePaths);
|
importableFiles.AddRange(filePaths);
|
||||||
|
|
||||||
Logger.Log($"Adding {filePaths.Length} files for import");
|
Logger.Log($"Adding {filePaths.Length} files for import");
|
||||||
|
123
osu.Game.Benchmarks/BenchmarkCarouselFilter.cs
Normal file
123
osu.Game.Benchmarks/BenchmarkCarouselFilter.cs
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
// 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 BenchmarkDotNet.Attributes;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Screens.Select;
|
||||||
|
using osu.Game.Screens.Select.Carousel;
|
||||||
|
|
||||||
|
namespace osu.Game.Benchmarks
|
||||||
|
{
|
||||||
|
public class BenchmarkCarouselFilter : BenchmarkTest
|
||||||
|
{
|
||||||
|
private BeatmapInfo getExampleBeatmap() => new BeatmapInfo
|
||||||
|
{
|
||||||
|
Ruleset = new RulesetInfo
|
||||||
|
{
|
||||||
|
ShortName = "osu",
|
||||||
|
OnlineID = 0
|
||||||
|
},
|
||||||
|
StarRating = 4.0d,
|
||||||
|
Difficulty = new BeatmapDifficulty
|
||||||
|
{
|
||||||
|
ApproachRate = 5.0f,
|
||||||
|
DrainRate = 3.0f,
|
||||||
|
CircleSize = 2.0f,
|
||||||
|
},
|
||||||
|
Metadata = new BeatmapMetadata
|
||||||
|
{
|
||||||
|
Artist = "The Artist",
|
||||||
|
ArtistUnicode = "check unicode too",
|
||||||
|
Title = "Title goes here",
|
||||||
|
TitleUnicode = "Title goes here",
|
||||||
|
Author = { Username = "The Author" },
|
||||||
|
Source = "unit tests",
|
||||||
|
Tags = "look for tags too",
|
||||||
|
},
|
||||||
|
DifficultyName = "version as well",
|
||||||
|
Length = 2500,
|
||||||
|
BPM = 160,
|
||||||
|
BeatDivisor = 12,
|
||||||
|
Status = BeatmapOnlineStatus.Loved
|
||||||
|
};
|
||||||
|
|
||||||
|
private CarouselBeatmap carouselBeatmap = null!;
|
||||||
|
private FilterCriteria criteria1 = null!;
|
||||||
|
private FilterCriteria criteria2 = null!;
|
||||||
|
private FilterCriteria criteria3 = null!;
|
||||||
|
private FilterCriteria criteria4 = null!;
|
||||||
|
private FilterCriteria criteria5 = null!;
|
||||||
|
private FilterCriteria criteria6 = null!;
|
||||||
|
|
||||||
|
public override void SetUp()
|
||||||
|
{
|
||||||
|
var beatmap = getExampleBeatmap();
|
||||||
|
beatmap.OnlineID = 20201010;
|
||||||
|
beatmap.BeatmapSet = new BeatmapSetInfo { OnlineID = 1535 };
|
||||||
|
carouselBeatmap = new CarouselBeatmap(beatmap);
|
||||||
|
criteria1 = new FilterCriteria();
|
||||||
|
criteria2 = new FilterCriteria
|
||||||
|
{
|
||||||
|
Ruleset = new RulesetInfo { ShortName = "catch" }
|
||||||
|
};
|
||||||
|
criteria3 = new FilterCriteria
|
||||||
|
{
|
||||||
|
Ruleset = new RulesetInfo { OnlineID = 6 },
|
||||||
|
AllowConvertedBeatmaps = true,
|
||||||
|
BPM = new FilterCriteria.OptionalRange<double>
|
||||||
|
{
|
||||||
|
IsUpperInclusive = false,
|
||||||
|
Max = 160d
|
||||||
|
}
|
||||||
|
};
|
||||||
|
criteria4 = new FilterCriteria
|
||||||
|
{
|
||||||
|
Ruleset = new RulesetInfo { OnlineID = 6 },
|
||||||
|
AllowConvertedBeatmaps = true,
|
||||||
|
SearchText = "an artist"
|
||||||
|
};
|
||||||
|
criteria5 = new FilterCriteria
|
||||||
|
{
|
||||||
|
Creator = new FilterCriteria.OptionalTextFilter { SearchTerm = "the author AND then something else" }
|
||||||
|
};
|
||||||
|
criteria6 = new FilterCriteria { SearchText = "20201010" };
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void CarouselBeatmapFilter()
|
||||||
|
{
|
||||||
|
carouselBeatmap.Filter(criteria1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void CriteriaMatchingSpecificRuleset()
|
||||||
|
{
|
||||||
|
carouselBeatmap.Filter(criteria2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void CriteriaMatchingRangeMax()
|
||||||
|
{
|
||||||
|
carouselBeatmap.Filter(criteria3);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void CriteriaMatchingTerms()
|
||||||
|
{
|
||||||
|
carouselBeatmap.Filter(criteria4);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void CriteriaMatchingCreator()
|
||||||
|
{
|
||||||
|
carouselBeatmap.Filter(criteria5);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Benchmark]
|
||||||
|
public void CriteriaMatchingBeatmapIDs()
|
||||||
|
{
|
||||||
|
carouselBeatmap.Filter(criteria6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,17 +0,0 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using Foundation;
|
|
||||||
using osu.Framework.iOS;
|
|
||||||
using osu.Game.Tests;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests.iOS
|
|
||||||
{
|
|
||||||
[Register("AppDelegate")]
|
|
||||||
public class AppDelegate : GameAppDelegate
|
|
||||||
{
|
|
||||||
protected override Framework.Game CreateGame() => new OsuTestBrowser();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
using osu.Framework.iOS;
|
||||||
|
using osu.Game.Tests;
|
||||||
using UIKit;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Tests.iOS
|
namespace osu.Game.Rulesets.Catch.Tests.iOS
|
||||||
{
|
{
|
||||||
@ -11,7 +10,7 @@ namespace osu.Game.Rulesets.Catch.Tests.iOS
|
|||||||
{
|
{
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
UIApplication.Main(args, null, typeof(AppDelegate));
|
GameApplication.Main(new OsuTestBrowser());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
|
|||||||
AddAssert("end time is correct", () => Precision.AlmostEquals(lastObject.EndTime, times[1]));
|
AddAssert("end time is correct", () => Precision.AlmostEquals(lastObject.EndTime, times[1]));
|
||||||
AddAssert("start position is correct", () => Precision.AlmostEquals(lastObject.OriginalX, positions[0]));
|
AddAssert("start position is correct", () => Precision.AlmostEquals(lastObject.OriginalX, positions[0]));
|
||||||
AddAssert("end position is correct", () => Precision.AlmostEquals(lastObject.EndX, positions[1]));
|
AddAssert("end position is correct", () => Precision.AlmostEquals(lastObject.EndX, positions[1]));
|
||||||
AddAssert("default slider velocity", () => lastObject.DifficultyControlPoint.SliderVelocityBindable.IsDefault);
|
AddAssert("default slider velocity", () => lastObject.SliderVelocityBindable.IsDefault);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
|
|||||||
addPlacementSteps(times, positions);
|
addPlacementSteps(times, positions);
|
||||||
addPathCheckStep(times, positions);
|
addPathCheckStep(times, positions);
|
||||||
|
|
||||||
AddAssert("slider velocity changed", () => !lastObject.DifficultyControlPoint.SliderVelocityBindable.IsDefault);
|
AddAssert("slider velocity changed", () => !lastObject.SliderVelocityBindable.IsDefault);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -108,11 +108,11 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
|
|||||||
double[] times = { 100, 300 };
|
double[] times = { 100, 300 };
|
||||||
float[] positions = { 200, 300 };
|
float[] positions = { 200, 300 };
|
||||||
addBlueprintStep(times, positions);
|
addBlueprintStep(times, positions);
|
||||||
AddAssert("default slider velocity", () => hitObject.DifficultyControlPoint.SliderVelocityBindable.IsDefault);
|
AddAssert("default slider velocity", () => hitObject.SliderVelocityBindable.IsDefault);
|
||||||
|
|
||||||
addDragStartStep(times[1], positions[1]);
|
addDragStartStep(times[1], positions[1]);
|
||||||
AddMouseMoveStep(times[1], 400);
|
AddMouseMoveStep(times[1], 400);
|
||||||
AddAssert("slider velocity changed", () => !hitObject.DifficultyControlPoint.SliderVelocityBindable.IsDefault);
|
AddAssert("slider velocity changed", () => !hitObject.SliderVelocityBindable.IsDefault);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
NewCombo = i % 8 == 0,
|
NewCombo = i % 8 == 0,
|
||||||
Samples = new List<HitSampleInfo>(new[]
|
Samples = new List<HitSampleInfo>(new[]
|
||||||
{
|
{
|
||||||
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "normal", volume: 100)
|
new HitSampleInfo(HitSampleInfo.HIT_NORMAL)
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
var xPositionData = obj as IHasXPosition;
|
var xPositionData = obj as IHasXPosition;
|
||||||
var yPositionData = obj as IHasYPosition;
|
var yPositionData = obj as IHasYPosition;
|
||||||
var comboData = obj as IHasCombo;
|
var comboData = obj as IHasCombo;
|
||||||
|
var sliderVelocityData = obj as IHasSliderVelocity;
|
||||||
|
|
||||||
switch (obj)
|
switch (obj)
|
||||||
{
|
{
|
||||||
@ -41,7 +42,8 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
NewCombo = comboData?.NewCombo ?? false,
|
NewCombo = comboData?.NewCombo ?? false,
|
||||||
ComboOffset = comboData?.ComboOffset ?? 0,
|
ComboOffset = comboData?.ComboOffset ?? 0,
|
||||||
LegacyLastTickOffset = (obj as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0,
|
LegacyLastTickOffset = (obj as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0,
|
||||||
LegacyConvertedY = yPositionData?.Y ?? CatchHitObject.DEFAULT_LEGACY_CONVERT_Y
|
LegacyConvertedY = yPositionData?.Y ?? CatchHitObject.DEFAULT_LEGACY_CONVERT_Y,
|
||||||
|
SliderVelocity = sliderVelocityData?.SliderVelocity ?? 1
|
||||||
}.Yield();
|
}.Yield();
|
||||||
|
|
||||||
case IHasDuration endTime:
|
case IHasDuration endTime:
|
||||||
|
@ -17,6 +17,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|||||||
private double placementStartTime;
|
private double placementStartTime;
|
||||||
private double placementEndTime;
|
private double placementEndTime;
|
||||||
|
|
||||||
|
protected override bool IsValidForPlacement => HitObject.Duration > 0;
|
||||||
|
|
||||||
public BananaShowerPlacementBlueprint()
|
public BananaShowerPlacementBlueprint()
|
||||||
{
|
{
|
||||||
InternalChild = outline = new TimeSpanOutline();
|
InternalChild = outline = new TimeSpanOutline();
|
||||||
@ -49,7 +51,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|||||||
case PlacementState.Active:
|
case PlacementState.Active:
|
||||||
if (e.Button != MouseButton.Right) break;
|
if (e.Button != MouseButton.Right) break;
|
||||||
|
|
||||||
EndPlacement(HitObject.Duration > 0);
|
EndPlacement(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components
|
|||||||
public void UpdateHitObjectFromPath(JuiceStream hitObject)
|
public void UpdateHitObjectFromPath(JuiceStream hitObject)
|
||||||
{
|
{
|
||||||
// The SV setting may need to be changed for the current path.
|
// The SV setting may need to be changed for the current path.
|
||||||
var svBindable = hitObject.DifficultyControlPoint.SliderVelocityBindable;
|
var svBindable = hitObject.SliderVelocityBindable;
|
||||||
double svToVelocityFactor = hitObject.Velocity / svBindable.Value;
|
double svToVelocityFactor = hitObject.Velocity / svBindable.Value;
|
||||||
double requiredVelocity = path.ComputeRequiredVelocity();
|
double requiredVelocity = path.ComputeRequiredVelocity();
|
||||||
|
|
||||||
|
@ -24,6 +24,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|||||||
|
|
||||||
private InputManager inputManager = null!;
|
private InputManager inputManager = null!;
|
||||||
|
|
||||||
|
protected override bool IsValidForPlacement => HitObject.Duration > 0;
|
||||||
|
|
||||||
public JuiceStreamPlacementBlueprint()
|
public JuiceStreamPlacementBlueprint()
|
||||||
{
|
{
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
@ -70,7 +72,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
case MouseButton.Right:
|
case MouseButton.Right:
|
||||||
EndPlacement(HitObject.Duration > 0);
|
EndPlacement(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,6 @@ namespace osu.Game.Rulesets.Catch.Edit
|
|||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
// todo: enable distance spacing once catch supports applying it to its existing distance snap grid implementation.
|
// todo: enable distance spacing once catch supports applying it to its existing distance snap grid implementation.
|
||||||
RightSideToolboxContainer.Alpha = 0;
|
|
||||||
DistanceSpacingMultiplier.Disabled = true;
|
DistanceSpacingMultiplier.Disabled = true;
|
||||||
|
|
||||||
LayerBelowRuleset.Add(new PlayfieldBorder
|
LayerBelowRuleset.Add(new PlayfieldBorder
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
{
|
{
|
||||||
public class CatchModDaycore : ModDaycore
|
public class CatchModDaycore : ModDaycore
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 0.3;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
public DifficultyBindable CircleSize { get; } = new DifficultyBindable
|
public DifficultyBindable CircleSize { get; } = new DifficultyBindable
|
||||||
{
|
{
|
||||||
Precision = 0.1f,
|
Precision = 0.1f,
|
||||||
MinValue = 1,
|
MinValue = 0,
|
||||||
MaxValue = 10,
|
MaxValue = 10,
|
||||||
ExtendedMaxValue = 11,
|
ExtendedMaxValue = 11,
|
||||||
ReadCurrentFromDifficulty = diff => diff.CircleSize,
|
ReadCurrentFromDifficulty = diff => diff.CircleSize,
|
||||||
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
public DifficultyBindable ApproachRate { get; } = new DifficultyBindable
|
public DifficultyBindable ApproachRate { get; } = new DifficultyBindable
|
||||||
{
|
{
|
||||||
Precision = 0.1f,
|
Precision = 0.1f,
|
||||||
MinValue = 1,
|
MinValue = 0,
|
||||||
MaxValue = 10,
|
MaxValue = 10,
|
||||||
ExtendedMaxValue = 11,
|
ExtendedMaxValue = 11,
|
||||||
ReadCurrentFromDifficulty = diff => diff.ApproachRate,
|
ReadCurrentFromDifficulty = diff => diff.ApproachRate,
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
{
|
{
|
||||||
public class CatchModDoubleTime : ModDoubleTime
|
public class CatchModDoubleTime : ModDoubleTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
{
|
{
|
||||||
public class CatchModHalfTime : ModHalfTime
|
public class CatchModHalfTime : ModHalfTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 0.3;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,5 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
{
|
{
|
||||||
public class CatchModNightcore : ModNightcore<CatchHitObject>
|
public class CatchModNightcore : ModNightcore<CatchHitObject>
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,11 +22,11 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
|
|
||||||
public override Judgement CreateJudgement() => new CatchBananaJudgement();
|
public override Judgement CreateJudgement() => new CatchBananaJudgement();
|
||||||
|
|
||||||
private static readonly List<HitSampleInfo> samples = new List<HitSampleInfo> { new BananaHitSampleInfo() };
|
private static readonly IList<HitSampleInfo> default_banana_samples = new List<HitSampleInfo> { new BananaHitSampleInfo() }.AsReadOnly();
|
||||||
|
|
||||||
public Banana()
|
public Banana()
|
||||||
{
|
{
|
||||||
Samples = samples;
|
Samples = default_banana_samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
// override any external colour changes with banananana
|
// override any external colour changes with banananana
|
||||||
@ -47,18 +47,18 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class BananaHitSampleInfo : HitSampleInfo, IEquatable<BananaHitSampleInfo>
|
public class BananaHitSampleInfo : HitSampleInfo, IEquatable<BananaHitSampleInfo>
|
||||||
{
|
{
|
||||||
private static readonly string[] lookup_names = { "Gameplay/metronomelow", "Gameplay/catch-banana" };
|
private static readonly string[] lookup_names = { "Gameplay/metronomelow", "Gameplay/catch-banana" };
|
||||||
|
|
||||||
public override IEnumerable<string> LookupNames => lookup_names;
|
public override IEnumerable<string> LookupNames => lookup_names;
|
||||||
|
|
||||||
public BananaHitSampleInfo(int volume = 0)
|
public BananaHitSampleInfo(int volume = 100)
|
||||||
: base(string.Empty, volume: volume)
|
: base(string.Empty, volume: volume)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed override HitSampleInfo With(Optional<string> newName = default, Optional<string?> newBank = default, Optional<string?> newSuffix = default, Optional<int> newVolume = default)
|
public sealed override HitSampleInfo With(Optional<string> newName = default, Optional<string> newBank = default, Optional<string?> newSuffix = default, Optional<int> newVolume = default)
|
||||||
=> new BananaHitSampleInfo(newVolume.GetOr(Volume));
|
=> new BananaHitSampleInfo(newVolume.GetOr(Volume));
|
||||||
|
|
||||||
public bool Equals(BananaHitSampleInfo? other)
|
public bool Equals(BananaHitSampleInfo? other)
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using osu.Game.Audio;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
|
||||||
@ -39,6 +41,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
{
|
{
|
||||||
StartTime = time,
|
StartTime = time,
|
||||||
BananaIndex = i,
|
BananaIndex = i,
|
||||||
|
Samples = new List<HitSampleInfo> { new Banana.BananaHitSampleInfo(CreateHitSampleInfo().Volume) }
|
||||||
});
|
});
|
||||||
|
|
||||||
time += spacing;
|
time += spacing;
|
||||||
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
@ -16,7 +17,7 @@ using osu.Game.Rulesets.Objects.Types;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects
|
namespace osu.Game.Rulesets.Catch.Objects
|
||||||
{
|
{
|
||||||
public class JuiceStream : CatchHitObject, IHasPathWithRepeats
|
public class JuiceStream : CatchHitObject, IHasPathWithRepeats, IHasSliderVelocity
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Positional distance that results in a duration of one second, before any speed adjustments.
|
/// Positional distance that results in a duration of one second, before any speed adjustments.
|
||||||
@ -27,6 +28,19 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
|
|
||||||
public int RepeatCount { get; set; }
|
public int RepeatCount { get; set; }
|
||||||
|
|
||||||
|
public BindableNumber<double> SliderVelocityBindable { get; } = new BindableDouble(1)
|
||||||
|
{
|
||||||
|
Precision = 0.01,
|
||||||
|
MinValue = 0.1,
|
||||||
|
MaxValue = 10
|
||||||
|
};
|
||||||
|
|
||||||
|
public double SliderVelocity
|
||||||
|
{
|
||||||
|
get => SliderVelocityBindable.Value;
|
||||||
|
set => SliderVelocityBindable.Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
private double velocityFactor;
|
private double velocityFactor;
|
||||||
|
|
||||||
@ -34,10 +48,10 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
private double tickDistanceFactor;
|
private double tickDistanceFactor;
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public double Velocity => velocityFactor * DifficultyControlPoint.SliderVelocity;
|
public double Velocity => velocityFactor * SliderVelocity;
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public double TickDistance => tickDistanceFactor * DifficultyControlPoint.SliderVelocity;
|
public double TickDistance => tickDistanceFactor * SliderVelocity;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The length of one span of this <see cref="JuiceStream"/>.
|
/// The length of one span of this <see cref="JuiceStream"/>.
|
||||||
|
@ -136,6 +136,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
Origin = Anchor.TopCentre;
|
Origin = Anchor.TopCentre;
|
||||||
|
|
||||||
Size = new Vector2(BASE_SIZE);
|
Size = new Vector2(BASE_SIZE);
|
||||||
|
|
||||||
if (difficulty != null)
|
if (difficulty != null)
|
||||||
Scale = calculateScale(difficulty);
|
Scale = calculateScale(difficulty);
|
||||||
|
|
||||||
@ -333,8 +334,11 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
var scaleFromDirection = new Vector2((int)VisualDirection, 1);
|
var scaleFromDirection = new Vector2((int)VisualDirection, 1);
|
||||||
|
|
||||||
body.Scale = scaleFromDirection;
|
body.Scale = scaleFromDirection;
|
||||||
caughtObjectContainer.Scale = hitExplosionContainer.Scale = flipCatcherPlate ? scaleFromDirection : Vector2.One;
|
// Inverse of catcher scale is applied here, as catcher gets scaled by circle size and so do the incoming fruit.
|
||||||
|
caughtObjectContainer.Scale = (1 / Scale.X) * (flipCatcherPlate ? scaleFromDirection : Vector2.One);
|
||||||
|
hitExplosionContainer.Scale = flipCatcherPlate ? scaleFromDirection : Vector2.One;
|
||||||
|
|
||||||
// Correct overshooting.
|
// Correct overshooting.
|
||||||
if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) ||
|
if ((hyperDashDirection > 0 && hyperDashTargetPosition < X) ||
|
||||||
@ -414,10 +418,13 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
private void clearPlate(DroppedObjectAnimation animation)
|
private void clearPlate(DroppedObjectAnimation animation)
|
||||||
{
|
{
|
||||||
var droppedObjects = caughtObjectContainer.Children.Select(getDroppedObject).ToArray();
|
var caughtObjects = caughtObjectContainer.Children.ToArray();
|
||||||
|
|
||||||
caughtObjectContainer.Clear(false);
|
caughtObjectContainer.Clear(false);
|
||||||
|
|
||||||
|
// Use the already returned PoolableDrawables for new objects
|
||||||
|
var droppedObjects = caughtObjects.Select(getDroppedObject).ToArray();
|
||||||
|
|
||||||
droppedObjectTarget.AddRange(droppedObjects);
|
droppedObjectTarget.AddRange(droppedObjects);
|
||||||
|
|
||||||
foreach (var droppedObject in droppedObjects)
|
foreach (var droppedObject in droppedObjects)
|
||||||
@ -426,10 +433,10 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
private void removeFromPlate(CaughtObject caughtObject, DroppedObjectAnimation animation)
|
private void removeFromPlate(CaughtObject caughtObject, DroppedObjectAnimation animation)
|
||||||
{
|
{
|
||||||
var droppedObject = getDroppedObject(caughtObject);
|
|
||||||
|
|
||||||
caughtObjectContainer.Remove(caughtObject, false);
|
caughtObjectContainer.Remove(caughtObject, false);
|
||||||
|
|
||||||
|
var droppedObject = getDroppedObject(caughtObject);
|
||||||
|
|
||||||
droppedObjectTarget.Add(droppedObject);
|
droppedObjectTarget.Add(droppedObject);
|
||||||
|
|
||||||
applyDropAnimation(droppedObject, animation);
|
applyDropAnimation(droppedObject, animation);
|
||||||
@ -452,6 +459,8 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Define lifetime start for dropped objects to be disposed correctly when rewinding replay
|
||||||
|
d.LifetimeStart = Clock.CurrentTime;
|
||||||
d.Expire();
|
d.Expire();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using Foundation;
|
|
||||||
using osu.Framework.iOS;
|
|
||||||
using osu.Game.Tests;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests.iOS
|
|
||||||
{
|
|
||||||
[Register("AppDelegate")]
|
|
||||||
public class AppDelegate : GameAppDelegate
|
|
||||||
{
|
|
||||||
protected override Framework.Game CreateGame() => new OsuTestBrowser();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
using osu.Framework.iOS;
|
||||||
|
using osu.Game.Tests;
|
||||||
using UIKit;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests.iOS
|
namespace osu.Game.Rulesets.Mania.Tests.iOS
|
||||||
{
|
{
|
||||||
@ -11,7 +10,7 @@ namespace osu.Game.Rulesets.Mania.Tests.iOS
|
|||||||
{
|
{
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
UIApplication.Main(args, null, typeof(AppDelegate));
|
GameApplication.Main(new OsuTestBrowser());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
@ -40,7 +38,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
private const double time_tail = 4000;
|
private const double time_tail = 4000;
|
||||||
private const double time_after_tail = 5250;
|
private const double time_after_tail = 5250;
|
||||||
|
|
||||||
private List<JudgementResult> judgementResults;
|
private List<JudgementResult> judgementResults = new List<JudgementResult>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// -----[ ]-----
|
/// -----[ ]-----
|
||||||
@ -61,6 +59,44 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
assertNoteJudgement(HitResult.IgnoreMiss);
|
assertNoteJudgement(HitResult.IgnoreMiss);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// -----[ ]-----
|
||||||
|
/// x o
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestCorrectInput()
|
||||||
|
{
|
||||||
|
performTest(new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new ManiaReplayFrame(time_head, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(time_tail),
|
||||||
|
});
|
||||||
|
|
||||||
|
assertHeadJudgement(HitResult.Perfect);
|
||||||
|
assertTickJudgement(HitResult.LargeTickHit);
|
||||||
|
assertTailJudgement(HitResult.Perfect);
|
||||||
|
assertNoteJudgement(HitResult.IgnoreHit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// -----[ ]-----
|
||||||
|
/// x o
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestLateRelease()
|
||||||
|
{
|
||||||
|
performTest(new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new ManiaReplayFrame(time_head, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(time_after_tail),
|
||||||
|
});
|
||||||
|
|
||||||
|
assertHeadJudgement(HitResult.Perfect);
|
||||||
|
assertTickJudgement(HitResult.LargeTickHit);
|
||||||
|
assertTailJudgement(HitResult.Miss);
|
||||||
|
assertNoteJudgement(HitResult.IgnoreMiss);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// -----[ ]-----
|
/// -----[ ]-----
|
||||||
/// x o
|
/// x o
|
||||||
@ -521,9 +557,9 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
private void assertLastTickJudgement(HitResult result)
|
private void assertLastTickJudgement(HitResult result)
|
||||||
=> AddAssert($"last tick judged as {result}", () => judgementResults.Last(j => j.HitObject is HoldNoteTick).Type, () => Is.EqualTo(result));
|
=> AddAssert($"last tick judged as {result}", () => judgementResults.Last(j => j.HitObject is HoldNoteTick).Type, () => Is.EqualTo(result));
|
||||||
|
|
||||||
private ScoreAccessibleReplayPlayer currentPlayer;
|
private ScoreAccessibleReplayPlayer currentPlayer = null!;
|
||||||
|
|
||||||
private void performTest(List<ReplayFrame> frames, Beatmap<ManiaHitObject> beatmap = null)
|
private void performTest(List<ReplayFrame> frames, Beatmap<ManiaHitObject>? beatmap = null)
|
||||||
{
|
{
|
||||||
if (beatmap == null)
|
if (beatmap == null)
|
||||||
{
|
{
|
||||||
@ -569,15 +605,13 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0);
|
AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0);
|
||||||
AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen());
|
AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen());
|
||||||
|
|
||||||
AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor?.HasCompleted.Value == true);
|
AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private partial class ScoreAccessibleReplayPlayer : ReplayPlayer
|
private partial class ScoreAccessibleReplayPlayer : ReplayPlayer
|
||||||
{
|
{
|
||||||
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
|
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
|
||||||
|
|
||||||
public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer;
|
|
||||||
|
|
||||||
protected override bool PauseOnFocusLost => false;
|
protected override bool PauseOnFocusLost => false;
|
||||||
|
|
||||||
public ScoreAccessibleReplayPlayer(Score score)
|
public ScoreAccessibleReplayPlayer(Score score)
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
|
using osu.Game.Rulesets.Mania.Configuration;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests
|
namespace osu.Game.Rulesets.Mania.Tests
|
||||||
@ -10,5 +13,19 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
public partial class TestSceneManiaPlayer : PlayerTestScene
|
public partial class TestSceneManiaPlayer : PlayerTestScene
|
||||||
{
|
{
|
||||||
protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset();
|
protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset();
|
||||||
|
|
||||||
|
public override void SetUpSteps()
|
||||||
|
{
|
||||||
|
base.SetUpSteps();
|
||||||
|
|
||||||
|
AddStep("change direction to down", () => changeDirectionTo(ManiaScrollingDirection.Down));
|
||||||
|
AddStep("change direction to up", () => changeDirectionTo(ManiaScrollingDirection.Up));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void changeDirectionTo(ManiaScrollingDirection direction)
|
||||||
|
{
|
||||||
|
var rulesetConfig = (ManiaRulesetConfigManager)RulesetConfigs.GetConfigFor(new ManiaRuleset()).AsNonNull();
|
||||||
|
rulesetConfig.SetValue(ManiaRulesetSetting.ScrollDirection, direction);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@ using osu.Game.Rulesets.Objects;
|
|||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Beatmaps.Formats;
|
|
||||||
using osu.Game.Utils;
|
using osu.Game.Utils;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||||
@ -49,15 +48,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
Debug.Assert(distanceData != null);
|
Debug.Assert(distanceData != null);
|
||||||
|
|
||||||
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);
|
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);
|
||||||
DifficultyControlPoint difficultyPoint = hitObject.DifficultyControlPoint;
|
|
||||||
|
|
||||||
double beatLength;
|
double beatLength;
|
||||||
#pragma warning disable 618
|
if (hitObject.LegacyBpmMultiplier.HasValue)
|
||||||
if (difficultyPoint is LegacyBeatmapDecoder.LegacyDifficultyControlPoint legacyDifficultyPoint)
|
beatLength = timingPoint.BeatLength * hitObject.LegacyBpmMultiplier.Value;
|
||||||
#pragma warning restore 618
|
else if (hitObject is IHasSliderVelocity hasSliderVelocity)
|
||||||
beatLength = timingPoint.BeatLength * legacyDifficultyPoint.BpmMultiplier;
|
beatLength = timingPoint.BeatLength / hasSliderVelocity.SliderVelocity;
|
||||||
else
|
else
|
||||||
beatLength = timingPoint.BeatLength / difficultyPoint.SliderVelocity;
|
beatLength = timingPoint.BeatLength;
|
||||||
|
|
||||||
SpanCount = repeatsData?.SpanCount() ?? 1;
|
SpanCount = repeatsData?.SpanCount() ?? 1;
|
||||||
StartTime = (int)Math.Round(hitObject.StartTime);
|
StartTime = (int)Math.Round(hitObject.StartTime);
|
||||||
|
@ -25,6 +25,8 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private IScrollingInfo scrollingInfo { get; set; }
|
private IScrollingInfo scrollingInfo { get; set; }
|
||||||
|
|
||||||
|
protected override bool IsValidForPlacement => HitObject.Duration > 0;
|
||||||
|
|
||||||
public HoldNotePlacementBlueprint()
|
public HoldNotePlacementBlueprint()
|
||||||
: base(new HoldNote())
|
: base(new HoldNote())
|
||||||
{
|
{
|
||||||
@ -75,7 +77,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
base.OnMouseUp(e);
|
base.OnMouseUp(e);
|
||||||
EndPlacement(HitObject.Duration > 0);
|
EndPlacement(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private double originalStartTime;
|
private double originalStartTime;
|
||||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Setup
|
|||||||
specialStyle = new LabelledSwitchButton
|
specialStyle = new LabelledSwitchButton
|
||||||
{
|
{
|
||||||
Label = "Use special (N+1) style",
|
Label = "Use special (N+1) style",
|
||||||
Description = "Changes one column to act as a classic \"scratch\" or \"special\" column, which can be moved around by the user's skin (to the left/right/centre). Generally used in 5k (4+1) or 8key (7+1) configurations.",
|
Description = "Changes one column to act as a classic \"scratch\" or \"special\" column, which can be moved around by the user's skin (to the left/right/centre). Generally used in 6K (5+1) or 8K (7+1) configurations.",
|
||||||
Current = { Value = Beatmap.BeatmapInfo.SpecialStyle }
|
Current = { Value = Beatmap.BeatmapInfo.SpecialStyle }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
{
|
{
|
||||||
public class ManiaModDaycore : ModDaycore
|
public class ManiaModDaycore : ModDaycore
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 0.5;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
{
|
{
|
||||||
public class ManiaModDoubleTime : ModDoubleTime
|
public class ManiaModDoubleTime : ModDoubleTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
{
|
{
|
||||||
public class ManiaModHalfTime : ModHalfTime
|
public class ManiaModHalfTime : ModHalfTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 0.5;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,5 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
{
|
{
|
||||||
public class ManiaModNightcore : ModNightcore<ManiaHitObject>
|
public class ManiaModNightcore : ModNightcore<ManiaHitObject>
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -219,6 +218,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
if (Time.Current < releaseTime)
|
if (Time.Current < releaseTime)
|
||||||
releaseTime = null;
|
releaseTime = null;
|
||||||
|
|
||||||
|
if (Time.Current < HoldStartTime)
|
||||||
|
endHold();
|
||||||
|
|
||||||
// Pad the full size container so its contents (i.e. the masking container) reach under the tail.
|
// Pad the full size container so its contents (i.e. the masking container) reach under the tail.
|
||||||
// This is required for the tail to not be masked away, since it lies outside the bounds of the hold note.
|
// This is required for the tail to not be masked away, since it lies outside the bounds of the hold note.
|
||||||
sizingContainer.Padding = new MarginPadding
|
sizingContainer.Padding = new MarginPadding
|
||||||
@ -236,6 +238,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Position and resize the body to lie half-way under the head and the tail notes.
|
// Position and resize the body to lie half-way under the head and the tail notes.
|
||||||
|
// The rationale for this is account for heads/tails with corner radius.
|
||||||
bodyPiece.Y = (Direction.Value == ScrollingDirection.Up ? 1 : -1) * Head.Height / 2;
|
bodyPiece.Y = (Direction.Value == ScrollingDirection.Up ? 1 : -1) * Head.Height / 2;
|
||||||
bodyPiece.Height = DrawHeight - Head.Height / 2 + Tail.Height / 2;
|
bodyPiece.Height = DrawHeight - Head.Height / 2 + Tail.Height / 2;
|
||||||
|
|
||||||
@ -244,9 +247,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
// 2. The head note will move along with the new "head position" in the container.
|
// 2. The head note will move along with the new "head position" in the container.
|
||||||
if (Head.IsHit && releaseTime == null && DrawHeight > 0)
|
if (Head.IsHit && releaseTime == null && DrawHeight > 0)
|
||||||
{
|
{
|
||||||
// How far past the hit target this hold note is. Always a positive value.
|
// How far past the hit target this hold note is.
|
||||||
float yOffset = Math.Max(0, Direction.Value == ScrollingDirection.Up ? -Y : Y);
|
float yOffset = Direction.Value == ScrollingDirection.Up ? -Y : Y;
|
||||||
sizingContainer.Height = Math.Clamp(1 - yOffset / DrawHeight, 0, 1);
|
sizingContainer.Height = 1 - yOffset / DrawHeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,14 +324,14 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
if (e.Action != Action.Value)
|
if (e.Action != Action.Value)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// do not run any of this logic when rewinding, as it inverts order of presses/releases.
|
|
||||||
if (Time.Elapsed < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Make sure a hold was started
|
// Make sure a hold was started
|
||||||
if (HoldStartTime == null)
|
if (HoldStartTime == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// do not run any of this logic when rewinding, as it inverts order of presses/releases.
|
||||||
|
if (Time.Elapsed < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
Tail.UpdateResult();
|
Tail.UpdateResult();
|
||||||
endHold();
|
endHold();
|
||||||
|
|
||||||
@ -349,13 +352,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
{
|
{
|
||||||
// Note: base.LoadSamples() isn't called since the slider plays the tail's hitsounds for the time being.
|
// Note: base.LoadSamples() isn't called since the slider plays the tail's hitsounds for the time being.
|
||||||
|
|
||||||
if (HitObject.SampleControlPoint == null)
|
slidingSample.Samples = HitObject.CreateSlidingSamples().Cast<ISampleInfo>().ToArray();
|
||||||
{
|
|
||||||
throw new InvalidOperationException($"{nameof(HitObject)}s must always have an attached {nameof(HitObject.SampleControlPoint)}."
|
|
||||||
+ $" This is an indication that {nameof(HitObject.ApplyDefaults)} has not been invoked on {this}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
slidingSample.Samples = HitObject.CreateSlidingSamples().Select(s => HitObject.SampleControlPoint.ApplyTo(s)).Cast<ISampleInfo>().ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void StopAllSamples()
|
public override void StopAllSamples()
|
||||||
|
@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
{
|
{
|
||||||
protected override ManiaSkinComponents Component => ManiaSkinComponents.HoldNoteTail;
|
protected override ManiaSkinComponents Component => ManiaSkinComponents.HoldNoteTail;
|
||||||
|
|
||||||
protected DrawableHoldNote HoldNote => (DrawableHoldNote)ParentHitObject;
|
protected internal DrawableHoldNote HoldNote => (DrawableHoldNote)ParentHitObject;
|
||||||
|
|
||||||
public DrawableHoldNoteTail()
|
public DrawableHoldNoteTail()
|
||||||
: this(null)
|
: this(null)
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
["Gameplay/soft-hitnormal"],
|
["Gameplay/soft-hitnormal"],
|
||||||
["Gameplay/drum-hitnormal"]
|
["Gameplay/drum-hitnormal"]
|
||||||
],
|
],
|
||||||
"Samples": ["Gameplay/-hitnormal"]
|
"Samples": ["Gameplay/normal-hitnormal"]
|
||||||
}, {
|
}, {
|
||||||
"StartTime": 1875.0,
|
"StartTime": 1875.0,
|
||||||
"EndTime": 2750.0,
|
"EndTime": 2750.0,
|
||||||
@ -19,7 +19,7 @@
|
|||||||
["Gameplay/soft-hitnormal"],
|
["Gameplay/soft-hitnormal"],
|
||||||
["Gameplay/drum-hitnormal"]
|
["Gameplay/drum-hitnormal"]
|
||||||
],
|
],
|
||||||
"Samples": ["Gameplay/-hitnormal"]
|
"Samples": ["Gameplay/normal-hitnormal"]
|
||||||
}]
|
}]
|
||||||
}, {
|
}, {
|
||||||
"StartTime": 3750.0,
|
"StartTime": 3750.0,
|
||||||
|
@ -43,9 +43,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
{
|
{
|
||||||
largeFaint = new Container
|
largeFaint = new Container
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Height = ArgonNotePiece.NOTE_ACCENT_RATIO,
|
||||||
Masking = true,
|
Masking = true,
|
||||||
CornerRadius = ArgonNotePiece.CORNER_RADIUS,
|
CornerRadius = ArgonNotePiece.CORNER_RADIUS,
|
||||||
Blending = BlendingParameters.Additive,
|
Blending = BlendingParameters.Additive,
|
||||||
@ -80,11 +79,15 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
if (direction.NewValue == ScrollingDirection.Up)
|
if (direction.NewValue == ScrollingDirection.Up)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.TopCentre;
|
Anchor = Anchor.TopCentre;
|
||||||
|
largeFaint.Anchor = Anchor.TopCentre;
|
||||||
|
largeFaint.Origin = Anchor.TopCentre;
|
||||||
Y = ArgonNotePiece.NOTE_HEIGHT / 2;
|
Y = ArgonNotePiece.NOTE_HEIGHT / 2;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomCentre;
|
Anchor = Anchor.BottomCentre;
|
||||||
|
largeFaint.Anchor = Anchor.BottomCentre;
|
||||||
|
largeFaint.Origin = Anchor.BottomCentre;
|
||||||
Y = -ArgonNotePiece.NOTE_HEIGHT / 2;
|
Y = -ArgonNotePiece.NOTE_HEIGHT / 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
private void load(IScrollingInfo scrollingInfo)
|
private void load(IScrollingInfo scrollingInfo)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
Height = ArgonNotePiece.NOTE_HEIGHT;
|
Height = ArgonNotePiece.NOTE_HEIGHT * ArgonNotePiece.NOTE_ACCENT_RATIO;
|
||||||
|
|
||||||
Masking = true;
|
Masking = true;
|
||||||
CornerRadius = ArgonNotePiece.CORNER_RADIUS;
|
CornerRadius = ArgonNotePiece.CORNER_RADIUS;
|
||||||
|
@ -20,10 +20,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
public partial class ArgonHoldBodyPiece : CompositeDrawable, IHoldNoteBody
|
public partial class ArgonHoldBodyPiece : CompositeDrawable, IHoldNoteBody
|
||||||
{
|
{
|
||||||
protected readonly Bindable<Color4> AccentColour = new Bindable<Color4>();
|
protected readonly Bindable<Color4> AccentColour = new Bindable<Color4>();
|
||||||
protected readonly IBindable<bool> IsHitting = new Bindable<bool>();
|
|
||||||
|
|
||||||
private Drawable background = null!;
|
private Drawable background = null!;
|
||||||
private Box foreground = null!;
|
private ArgonHoldNoteHittingLayer hittingLayer = null!;
|
||||||
|
|
||||||
public ArgonHoldBodyPiece()
|
public ArgonHoldBodyPiece()
|
||||||
{
|
{
|
||||||
@ -32,7 +31,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
// Without this, the width of the body will be slightly larger than the head/tail.
|
// Without this, the width of the body will be slightly larger than the head/tail.
|
||||||
Masking = true;
|
Masking = true;
|
||||||
CornerRadius = ArgonNotePiece.CORNER_RADIUS;
|
CornerRadius = ArgonNotePiece.CORNER_RADIUS;
|
||||||
Blending = BlendingParameters.Additive;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
@ -41,12 +39,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
InternalChildren = new[]
|
InternalChildren = new[]
|
||||||
{
|
{
|
||||||
background = new Box { RelativeSizeAxes = Axes.Both },
|
background = new Box { RelativeSizeAxes = Axes.Both },
|
||||||
foreground = new Box
|
hittingLayer = new ArgonHoldNoteHittingLayer()
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Blending = BlendingParameters.Additive,
|
|
||||||
Alpha = 0,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (drawableObject != null)
|
if (drawableObject != null)
|
||||||
@ -54,44 +47,19 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
var holdNote = (DrawableHoldNote)drawableObject;
|
var holdNote = (DrawableHoldNote)drawableObject;
|
||||||
|
|
||||||
AccentColour.BindTo(holdNote.AccentColour);
|
AccentColour.BindTo(holdNote.AccentColour);
|
||||||
IsHitting.BindTo(holdNote.IsHitting);
|
hittingLayer.AccentColour.BindTo(holdNote.AccentColour);
|
||||||
|
((IBindable<bool>)hittingLayer.IsHitting).BindTo(holdNote.IsHitting);
|
||||||
}
|
}
|
||||||
|
|
||||||
AccentColour.BindValueChanged(colour =>
|
AccentColour.BindValueChanged(colour =>
|
||||||
{
|
{
|
||||||
background.Colour = colour.NewValue.Darken(1.2f);
|
background.Colour = colour.NewValue.Darken(0.6f);
|
||||||
foreground.Colour = colour.NewValue.Opacity(0.2f);
|
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
IsHitting.BindValueChanged(hitting =>
|
|
||||||
{
|
|
||||||
const float animation_length = 50;
|
|
||||||
|
|
||||||
foreground.ClearTransforms();
|
|
||||||
|
|
||||||
if (hitting.NewValue)
|
|
||||||
{
|
|
||||||
// wait for the next sync point
|
|
||||||
double synchronisedOffset = animation_length * 2 - Time.Current % (animation_length * 2);
|
|
||||||
|
|
||||||
using (foreground.BeginDelayedSequence(synchronisedOffset))
|
|
||||||
{
|
|
||||||
foreground.FadeTo(1, animation_length).Then()
|
|
||||||
.FadeTo(0.5f, animation_length)
|
|
||||||
.Loop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreground.FadeOut(animation_length);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Recycle()
|
public void Recycle()
|
||||||
{
|
{
|
||||||
foreground.ClearTransforms();
|
hittingLayer.Recycle();
|
||||||
foreground.Alpha = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
// 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.Shapes;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||||
|
{
|
||||||
|
internal partial class ArgonHoldNoteHeadPiece : ArgonNotePiece
|
||||||
|
{
|
||||||
|
protected override Drawable CreateIcon() => new Circle
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Y = 2,
|
||||||
|
Size = new Vector2(20, 5),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,64 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
using Box = osu.Framework.Graphics.Shapes.Box;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
||||||
|
{
|
||||||
|
public partial class ArgonHoldNoteHittingLayer : Box
|
||||||
|
{
|
||||||
|
public readonly Bindable<Color4> AccentColour = new Bindable<Color4>();
|
||||||
|
public readonly Bindable<bool> IsHitting = new Bindable<bool>();
|
||||||
|
|
||||||
|
public ArgonHoldNoteHittingLayer()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
Blending = BlendingParameters.Additive;
|
||||||
|
Alpha = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
AccentColour.BindValueChanged(colour =>
|
||||||
|
{
|
||||||
|
Colour = colour.NewValue.Lighten(0.2f).Opacity(0.3f);
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
IsHitting.BindValueChanged(hitting =>
|
||||||
|
{
|
||||||
|
const float animation_length = 80;
|
||||||
|
|
||||||
|
ClearTransforms();
|
||||||
|
|
||||||
|
if (hitting.NewValue)
|
||||||
|
{
|
||||||
|
// wait for the next sync point
|
||||||
|
double synchronisedOffset = animation_length * 2 - Time.Current % (animation_length * 2);
|
||||||
|
|
||||||
|
using (BeginDelayedSequence(synchronisedOffset))
|
||||||
|
{
|
||||||
|
this.FadeTo(1, animation_length, Easing.OutSine).Then()
|
||||||
|
.FadeTo(0.5f, animation_length, Easing.InSine)
|
||||||
|
.Loop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.FadeOut(animation_length);
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Recycle()
|
||||||
|
{
|
||||||
|
ClearTransforms();
|
||||||
|
Alpha = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,8 +5,10 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Colour;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -16,47 +18,68 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
{
|
{
|
||||||
internal partial class ArgonHoldNoteTailPiece : CompositeDrawable
|
internal partial class ArgonHoldNoteTailPiece : CompositeDrawable
|
||||||
{
|
{
|
||||||
|
[Resolved]
|
||||||
|
private DrawableHitObject? drawableObject { get; set; }
|
||||||
|
|
||||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||||
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
||||||
|
|
||||||
private readonly Box shadeBackground;
|
private readonly Box foreground;
|
||||||
private readonly Box shadeForeground;
|
private readonly ArgonHoldNoteHittingLayer hittingLayer;
|
||||||
|
private readonly Box foregroundAdditive;
|
||||||
|
|
||||||
public ArgonHoldNoteTailPiece()
|
public ArgonHoldNoteTailPiece()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
Height = ArgonNotePiece.NOTE_HEIGHT;
|
Height = ArgonNotePiece.NOTE_HEIGHT;
|
||||||
|
|
||||||
CornerRadius = ArgonNotePiece.CORNER_RADIUS;
|
|
||||||
Masking = true;
|
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
shadeBackground = new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
},
|
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = ArgonNotePiece.NOTE_ACCENT_RATIO,
|
Height = ArgonNotePiece.NOTE_HEIGHT,
|
||||||
Anchor = Anchor.BottomCentre,
|
|
||||||
Origin = Anchor.BottomCentre,
|
|
||||||
CornerRadius = ArgonNotePiece.CORNER_RADIUS,
|
CornerRadius = ArgonNotePiece.CORNER_RADIUS,
|
||||||
Masking = true,
|
Masking = true,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
shadeForeground = new Box
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0), Colour4.Black),
|
||||||
|
// Avoid ugly single pixel overlap.
|
||||||
|
Height = 0.9f,
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.BottomCentre,
|
||||||
|
Origin = Anchor.BottomCentre,
|
||||||
|
Height = ArgonNotePiece.NOTE_ACCENT_RATIO,
|
||||||
|
CornerRadius = ArgonNotePiece.CORNER_RADIUS,
|
||||||
|
Masking = true,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
foreground = new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
},
|
},
|
||||||
|
hittingLayer = new ArgonHoldNoteHittingLayer(),
|
||||||
|
foregroundAdditive = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Blending = BlendingParameters.Additive,
|
||||||
|
Height = 0.5f,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load(IScrollingInfo scrollingInfo, DrawableHitObject? drawableObject)
|
private void load(IScrollingInfo scrollingInfo)
|
||||||
{
|
{
|
||||||
direction.BindTo(scrollingInfo.Direction);
|
direction.BindTo(scrollingInfo.Direction);
|
||||||
direction.BindValueChanged(onDirectionChanged, true);
|
direction.BindValueChanged(onDirectionChanged, true);
|
||||||
@ -65,9 +88,24 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
{
|
{
|
||||||
accentColour.BindTo(drawableObject.AccentColour);
|
accentColour.BindTo(drawableObject.AccentColour);
|
||||||
accentColour.BindValueChanged(onAccentChanged, true);
|
accentColour.BindValueChanged(onAccentChanged, true);
|
||||||
|
|
||||||
|
drawableObject.HitObjectApplied += hitObjectApplied;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void hitObjectApplied(DrawableHitObject drawableHitObject)
|
||||||
|
{
|
||||||
|
var holdNoteTail = (DrawableHoldNoteTail)drawableHitObject;
|
||||||
|
|
||||||
|
hittingLayer.Recycle();
|
||||||
|
|
||||||
|
hittingLayer.AccentColour.UnbindBindings();
|
||||||
|
hittingLayer.AccentColour.BindTo(holdNoteTail.HoldNote.AccentColour);
|
||||||
|
|
||||||
|
hittingLayer.IsHitting.UnbindBindings();
|
||||||
|
((IBindable<bool>)hittingLayer.IsHitting).BindTo(holdNoteTail.HoldNote.IsHitting);
|
||||||
|
}
|
||||||
|
|
||||||
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
||||||
{
|
{
|
||||||
Scale = new Vector2(1, direction.NewValue == ScrollingDirection.Up ? -1 : 1);
|
Scale = new Vector2(1, direction.NewValue == ScrollingDirection.Up ? -1 : 1);
|
||||||
@ -75,8 +113,20 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
|
|
||||||
private void onAccentChanged(ValueChangedEvent<Color4> accent)
|
private void onAccentChanged(ValueChangedEvent<Color4> accent)
|
||||||
{
|
{
|
||||||
shadeBackground.Colour = accent.NewValue.Darken(1.7f);
|
foreground.Colour = accent.NewValue.Darken(0.6f); // matches body
|
||||||
shadeForeground.Colour = accent.NewValue.Darken(1.1f);
|
|
||||||
|
foregroundAdditive.Colour = ColourInfo.GradientVertical(
|
||||||
|
accent.NewValue.Opacity(0.4f),
|
||||||
|
accent.NewValue.Opacity(0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
|
if (drawableObject != null)
|
||||||
|
drawableObject.HitObjectApplied -= hitObjectApplied;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
private readonly IBindable<Color4> accentColour = new Bindable<Color4>();
|
||||||
|
|
||||||
private readonly Box colouredBox;
|
private readonly Box colouredBox;
|
||||||
private readonly Box shadow;
|
|
||||||
|
|
||||||
public ArgonNotePiece()
|
public ArgonNotePiece()
|
||||||
{
|
{
|
||||||
@ -36,11 +35,12 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
CornerRadius = CORNER_RADIUS;
|
CornerRadius = CORNER_RADIUS;
|
||||||
Masking = true;
|
Masking = true;
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new[]
|
||||||
{
|
{
|
||||||
shadow = new Box
|
new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0), Colour4.Black)
|
||||||
},
|
},
|
||||||
new Container
|
new Container
|
||||||
{
|
{
|
||||||
@ -65,17 +65,21 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = CORNER_RADIUS * 2,
|
Height = CORNER_RADIUS * 2,
|
||||||
},
|
},
|
||||||
new SpriteIcon
|
CreateIcon(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual Drawable CreateIcon() => new SpriteIcon
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Y = 4,
|
Y = 4,
|
||||||
|
// TODO: replace with a non-squashed version.
|
||||||
|
// The 0.7f height scale should be removed.
|
||||||
Icon = FontAwesome.Solid.AngleDown,
|
Icon = FontAwesome.Solid.AngleDown,
|
||||||
Size = new Vector2(20),
|
Size = new Vector2(20),
|
||||||
Scale = new Vector2(1, 0.7f)
|
Scale = new Vector2(1, 0.7f)
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
private void load(IScrollingInfo scrollingInfo, DrawableHitObject? drawableObject)
|
private void load(IScrollingInfo scrollingInfo, DrawableHitObject? drawableObject)
|
||||||
@ -105,8 +109,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
accent.NewValue.Lighten(0.1f),
|
accent.NewValue.Lighten(0.1f),
|
||||||
accent.NewValue
|
accent.NewValue
|
||||||
);
|
);
|
||||||
|
|
||||||
shadow.Colour = accent.NewValue.Darken(0.5f);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
return new ArgonHoldNoteTailPiece();
|
return new ArgonHoldNoteTailPiece();
|
||||||
|
|
||||||
case ManiaSkinComponents.HoldNoteHead:
|
case ManiaSkinComponents.HoldNoteHead:
|
||||||
|
return new ArgonHoldNoteHeadPiece();
|
||||||
|
|
||||||
case ManiaSkinComponents.Note:
|
case ManiaSkinComponents.Note:
|
||||||
return new ArgonNotePiece();
|
return new ArgonNotePiece();
|
||||||
|
|
||||||
@ -69,12 +71,23 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
return base.GetDrawableComponent(lookup);
|
return base.GetDrawableComponent(lookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly Color4 colour_special_column = new Color4(169, 106, 255, 255);
|
||||||
|
|
||||||
|
private const int total_colours = 6;
|
||||||
|
|
||||||
|
private static readonly Color4 colour_yellow = new Color4(255, 197, 40, 255);
|
||||||
|
private static readonly Color4 colour_orange = new Color4(252, 109, 1, 255);
|
||||||
|
private static readonly Color4 colour_pink = new Color4(213, 35, 90, 255);
|
||||||
|
private static readonly Color4 colour_purple = new Color4(203, 60, 236, 255);
|
||||||
|
private static readonly Color4 colour_cyan = new Color4(72, 198, 255, 255);
|
||||||
|
private static readonly Color4 colour_green = new Color4(100, 192, 92, 255);
|
||||||
|
|
||||||
public override IBindable<TValue>? GetConfig<TLookup, TValue>(TLookup lookup)
|
public override IBindable<TValue>? GetConfig<TLookup, TValue>(TLookup lookup)
|
||||||
{
|
{
|
||||||
if (lookup is ManiaSkinConfigurationLookup maniaLookup)
|
if (lookup is ManiaSkinConfigurationLookup maniaLookup)
|
||||||
{
|
{
|
||||||
int column = maniaLookup.ColumnIndex ?? 0;
|
int columnIndex = maniaLookup.ColumnIndex ?? 0;
|
||||||
var stage = beatmap.GetStageForColumnIndex(column);
|
var stage = beatmap.GetStageForColumnIndex(columnIndex);
|
||||||
|
|
||||||
switch (maniaLookup.Lookup)
|
switch (maniaLookup.Lookup)
|
||||||
{
|
{
|
||||||
@ -87,53 +100,12 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
|
|
||||||
case LegacyManiaSkinConfigurationLookups.ColumnWidth:
|
case LegacyManiaSkinConfigurationLookups.ColumnWidth:
|
||||||
return SkinUtils.As<TValue>(new Bindable<float>(
|
return SkinUtils.As<TValue>(new Bindable<float>(
|
||||||
stage.IsSpecialColumn(column) ? 120 : 60
|
stage.IsSpecialColumn(columnIndex) ? 120 : 60
|
||||||
));
|
));
|
||||||
|
|
||||||
case LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour:
|
case LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour:
|
||||||
|
|
||||||
Color4 colour;
|
var colour = getColourForLayout(columnIndex, stage);
|
||||||
|
|
||||||
const int total_colours = 7;
|
|
||||||
|
|
||||||
if (stage.IsSpecialColumn(column))
|
|
||||||
colour = new Color4(159, 101, 255, 255);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
switch (column % total_colours)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
colour = new Color4(240, 216, 0, 255);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
colour = new Color4(240, 101, 0, 255);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
colour = new Color4(240, 0, 130, 255);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
colour = new Color4(192, 0, 240, 255);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4:
|
|
||||||
colour = new Color4(0, 96, 240, 255);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 5:
|
|
||||||
colour = new Color4(0, 226, 240, 255);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 6:
|
|
||||||
colour = new Color4(0, 240, 96, 255);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new ArgumentOutOfRangeException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return SkinUtils.As<TValue>(new Bindable<Color4>(colour));
|
return SkinUtils.As<TValue>(new Bindable<Color4>(colour));
|
||||||
}
|
}
|
||||||
@ -141,5 +113,203 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon
|
|||||||
|
|
||||||
return base.GetConfig<TLookup, TValue>(lookup);
|
return base.GetConfig<TLookup, TValue>(lookup);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Color4 getColourForLayout(int columnIndex, StageDefinition stage)
|
||||||
|
{
|
||||||
|
// Account for cases like dual-stage (assume that all stages have the same column count for now).
|
||||||
|
columnIndex %= stage.Columns;
|
||||||
|
|
||||||
|
// For now, these are defined per column count as per https://user-images.githubusercontent.com/50823728/218038463-b450f46c-ef21-4551-b133-f866be59970c.png
|
||||||
|
// See https://github.com/ppy/osu/discussions/21996 for discussion.
|
||||||
|
switch (stage.Columns)
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return colour_yellow;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
switch (columnIndex)
|
||||||
|
{
|
||||||
|
case 0: return colour_green;
|
||||||
|
|
||||||
|
case 1: return colour_cyan;
|
||||||
|
|
||||||
|
default: throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
switch (columnIndex)
|
||||||
|
{
|
||||||
|
case 0: return colour_pink;
|
||||||
|
|
||||||
|
case 1: return colour_orange;
|
||||||
|
|
||||||
|
case 2: return colour_yellow;
|
||||||
|
|
||||||
|
default: throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
case 4:
|
||||||
|
switch (columnIndex)
|
||||||
|
{
|
||||||
|
case 0: return colour_yellow;
|
||||||
|
|
||||||
|
case 1: return colour_orange;
|
||||||
|
|
||||||
|
case 2: return colour_pink;
|
||||||
|
|
||||||
|
case 3: return colour_purple;
|
||||||
|
|
||||||
|
default: throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
switch (columnIndex)
|
||||||
|
{
|
||||||
|
case 0: return colour_pink;
|
||||||
|
|
||||||
|
case 1: return colour_orange;
|
||||||
|
|
||||||
|
case 2: return colour_yellow;
|
||||||
|
|
||||||
|
case 3: return colour_green;
|
||||||
|
|
||||||
|
case 4: return colour_cyan;
|
||||||
|
|
||||||
|
default: throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
case 6:
|
||||||
|
switch (columnIndex)
|
||||||
|
{
|
||||||
|
case 0: return colour_pink;
|
||||||
|
|
||||||
|
case 1: return colour_orange;
|
||||||
|
|
||||||
|
case 2: return colour_yellow;
|
||||||
|
|
||||||
|
case 3: return colour_cyan;
|
||||||
|
|
||||||
|
case 4: return colour_purple;
|
||||||
|
|
||||||
|
case 5: return colour_pink;
|
||||||
|
|
||||||
|
default: throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
case 7:
|
||||||
|
switch (columnIndex)
|
||||||
|
{
|
||||||
|
case 0: return colour_pink;
|
||||||
|
|
||||||
|
case 1: return colour_cyan;
|
||||||
|
|
||||||
|
case 2: return colour_pink;
|
||||||
|
|
||||||
|
case 3: return colour_special_column;
|
||||||
|
|
||||||
|
case 4: return colour_green;
|
||||||
|
|
||||||
|
case 5: return colour_cyan;
|
||||||
|
|
||||||
|
case 6: return colour_green;
|
||||||
|
|
||||||
|
default: throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
case 8:
|
||||||
|
switch (columnIndex)
|
||||||
|
{
|
||||||
|
case 0: return colour_purple;
|
||||||
|
|
||||||
|
case 1: return colour_pink;
|
||||||
|
|
||||||
|
case 2: return colour_orange;
|
||||||
|
|
||||||
|
case 3: return colour_yellow;
|
||||||
|
|
||||||
|
case 4: return colour_yellow;
|
||||||
|
|
||||||
|
case 5: return colour_orange;
|
||||||
|
|
||||||
|
case 6: return colour_pink;
|
||||||
|
|
||||||
|
case 7: return colour_purple;
|
||||||
|
|
||||||
|
default: throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
case 9:
|
||||||
|
switch (columnIndex)
|
||||||
|
{
|
||||||
|
case 0: return colour_purple;
|
||||||
|
|
||||||
|
case 1: return colour_pink;
|
||||||
|
|
||||||
|
case 2: return colour_orange;
|
||||||
|
|
||||||
|
case 3: return colour_yellow;
|
||||||
|
|
||||||
|
case 4: return colour_special_column;
|
||||||
|
|
||||||
|
case 5: return colour_yellow;
|
||||||
|
|
||||||
|
case 6: return colour_orange;
|
||||||
|
|
||||||
|
case 7: return colour_pink;
|
||||||
|
|
||||||
|
case 8: return colour_purple;
|
||||||
|
|
||||||
|
default: throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
case 10:
|
||||||
|
switch (columnIndex)
|
||||||
|
{
|
||||||
|
case 0: return colour_purple;
|
||||||
|
|
||||||
|
case 1: return colour_pink;
|
||||||
|
|
||||||
|
case 2: return colour_orange;
|
||||||
|
|
||||||
|
case 3: return colour_yellow;
|
||||||
|
|
||||||
|
case 4: return colour_cyan;
|
||||||
|
|
||||||
|
case 5: return colour_green;
|
||||||
|
|
||||||
|
case 6: return colour_yellow;
|
||||||
|
|
||||||
|
case 7: return colour_orange;
|
||||||
|
|
||||||
|
case 8: return colour_pink;
|
||||||
|
|
||||||
|
case 9: return colour_purple;
|
||||||
|
|
||||||
|
default: throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback for unhandled scenarios
|
||||||
|
|
||||||
|
if (stage.IsSpecialColumn(columnIndex))
|
||||||
|
return colour_special_column;
|
||||||
|
|
||||||
|
switch (columnIndex % total_colours)
|
||||||
|
{
|
||||||
|
case 0: return colour_yellow;
|
||||||
|
|
||||||
|
case 1: return colour_orange;
|
||||||
|
|
||||||
|
case 2: return colour_pink;
|
||||||
|
|
||||||
|
case 3: return colour_purple;
|
||||||
|
|
||||||
|
case 4: return colour_cyan;
|
||||||
|
|
||||||
|
case 5: return colour_green;
|
||||||
|
|
||||||
|
default: throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -167,8 +167,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
{
|
{
|
||||||
if (bodySprite != null)
|
if (bodySprite != null)
|
||||||
{
|
{
|
||||||
bodySprite.Origin = Anchor.BottomCentre;
|
bodySprite.Origin = Anchor.TopCentre;
|
||||||
bodySprite.Scale = new Vector2(bodySprite.Scale.X, Math.Abs(bodySprite.Scale.Y) * -1);
|
bodySprite.Anchor = Anchor.BottomCentre; // needs to be flipped due to scale flip in Update.
|
||||||
}
|
}
|
||||||
|
|
||||||
if (light != null)
|
if (light != null)
|
||||||
@ -179,7 +179,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
if (bodySprite != null)
|
if (bodySprite != null)
|
||||||
{
|
{
|
||||||
bodySprite.Origin = Anchor.TopCentre;
|
bodySprite.Origin = Anchor.TopCentre;
|
||||||
bodySprite.Scale = new Vector2(bodySprite.Scale.X, Math.Abs(bodySprite.Scale.Y));
|
bodySprite.Anchor = Anchor.TopCentre;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (light != null)
|
if (light != null)
|
||||||
@ -211,11 +211,15 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
base.Update();
|
base.Update();
|
||||||
missFadeTime.Value ??= holdNote.HoldBrokenTime;
|
missFadeTime.Value ??= holdNote.HoldBrokenTime;
|
||||||
|
|
||||||
|
int scaleDirection = (direction.Value == ScrollingDirection.Down ? 1 : -1);
|
||||||
|
|
||||||
// here we go...
|
// here we go...
|
||||||
switch (bodyStyle)
|
switch (bodyStyle)
|
||||||
{
|
{
|
||||||
case LegacyNoteBodyStyle.Stretch:
|
case LegacyNoteBodyStyle.Stretch:
|
||||||
// this is how lazer works by default. nothing required.
|
// this is how lazer works by default. nothing required.
|
||||||
|
if (bodySprite != null)
|
||||||
|
bodySprite.Scale = new Vector2(1, scaleDirection);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -228,7 +232,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
|
|
||||||
bodySprite.FillMode = FillMode.Stretch;
|
bodySprite.FillMode = FillMode.Stretch;
|
||||||
// i dunno this looks about right??
|
// i dunno this looks about right??
|
||||||
bodySprite.Scale = new Vector2(1, 32800 / sprite.DrawHeight);
|
bodySprite.Scale = new Vector2(1, scaleDirection * 32800 / sprite.DrawHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using Foundation;
|
|
||||||
using osu.Framework.iOS;
|
|
||||||
using osu.Game.Tests;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests.iOS
|
|
||||||
{
|
|
||||||
[Register("AppDelegate")]
|
|
||||||
public class AppDelegate : GameAppDelegate
|
|
||||||
{
|
|
||||||
protected override Framework.Game CreateGame() => new OsuTestBrowser();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
using osu.Framework.iOS;
|
||||||
|
using osu.Game.Tests;
|
||||||
using UIKit;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests.iOS
|
namespace osu.Game.Rulesets.Osu.Tests.iOS
|
||||||
{
|
{
|
||||||
@ -11,7 +10,7 @@ namespace osu.Game.Rulesets.Osu.Tests.iOS
|
|||||||
{
|
{
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
UIApplication.Main(args, null, typeof(AppDelegate));
|
GameApplication.Main(new OsuTestBrowser());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,8 +138,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
var mergedSlider = (Slider)EditorBeatmap.SelectedHitObjects.First();
|
var mergedSlider = (Slider)EditorBeatmap.SelectedHitObjects.First();
|
||||||
return slider1 is not null && mergedSlider.HeadCircle.Samples.SequenceEqual(slider1.HeadCircle.Samples)
|
return slider1 is not null && mergedSlider.HeadCircle.Samples.SequenceEqual(slider1.HeadCircle.Samples)
|
||||||
&& mergedSlider.TailCircle.Samples.SequenceEqual(slider1.TailCircle.Samples)
|
&& mergedSlider.TailCircle.Samples.SequenceEqual(slider1.TailCircle.Samples)
|
||||||
&& mergedSlider.Samples.SequenceEqual(slider1.Samples)
|
&& mergedSlider.Samples.SequenceEqual(slider1.Samples);
|
||||||
&& mergedSlider.SampleControlPoint.IsRedundant(slider1.SampleControlPoint);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("slider end is at same completion for last slider", () =>
|
AddAssert("slider end is at same completion for last slider", () =>
|
||||||
|
@ -181,10 +181,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
{
|
{
|
||||||
if (slider is null) return;
|
if (slider is null) return;
|
||||||
|
|
||||||
slider.SampleControlPoint.SampleBank = "soft";
|
sample = new HitSampleInfo("hitwhistle", "soft", volume: 70);
|
||||||
slider.SampleControlPoint.SampleVolume = 70;
|
slider.Samples.Add(sample.With());
|
||||||
sample = new HitSampleInfo("hitwhistle");
|
|
||||||
slider.Samples.Add(sample);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
AddStep("select added slider", () =>
|
AddStep("select added slider", () =>
|
||||||
@ -207,9 +205,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
AddAssert("sliders have hitsounds", hasHitsounds);
|
AddAssert("sliders have hitsounds", hasHitsounds);
|
||||||
|
|
||||||
bool hasHitsounds() => sample is not null &&
|
bool hasHitsounds() => sample is not null &&
|
||||||
EditorBeatmap.HitObjects.All(o => o.SampleControlPoint.SampleBank == "soft" &&
|
EditorBeatmap.HitObjects.All(o => o.Samples.Contains(sample));
|
||||||
o.SampleControlPoint.SampleVolume == 70 &&
|
|
||||||
o.Samples.Contains(sample));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool sliderCreatedFor(Slider s, double startTime, double endTime, params (Vector2 pos, PathType? pathType)[] expectedControlPoints)
|
private bool sliderCreatedFor(Slider s, double startTime, double endTime, params (Vector2 pos, PathType? pathType)[] expectedControlPoints)
|
||||||
|
@ -199,8 +199,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
Precision.AlmostEquals(circle.StartTime, time, 1)
|
Precision.AlmostEquals(circle.StartTime, time, 1)
|
||||||
&& Precision.AlmostEquals(circle.Position, position, 0.01f)
|
&& Precision.AlmostEquals(circle.Position, position, 0.01f)
|
||||||
&& circle.NewCombo == startsNewCombo
|
&& circle.NewCombo == startsNewCombo
|
||||||
&& circle.Samples.SequenceEqual(slider.HeadCircle.Samples)
|
&& circle.Samples.SequenceEqual(slider.HeadCircle.Samples);
|
||||||
&& circle.SampleControlPoint.IsRedundant(slider.SampleControlPoint);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool sliderRestored(Slider slider)
|
private bool sliderRestored(Slider slider)
|
||||||
|
19
osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModBubbles.cs
Normal file
19
osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModBubbles.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Tests.Mods
|
||||||
|
{
|
||||||
|
public partial class TestSceneOsuModBubbles : OsuModTestScene
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void TestOsuModBubbles() => CreateModTest(new ModTestData
|
||||||
|
{
|
||||||
|
Mod = new OsuModBubbles(),
|
||||||
|
Autoplay = true,
|
||||||
|
PassCondition = () => true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
@ -8,6 +8,7 @@ using NUnit.Framework;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
@ -18,6 +19,7 @@ using osu.Framework.Testing.Input;
|
|||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Rulesets.Osu.Configuration;
|
||||||
using osu.Game.Rulesets.Osu.Skinning;
|
using osu.Game.Rulesets.Osu.Skinning;
|
||||||
using osu.Game.Rulesets.Osu.UI.Cursor;
|
using osu.Game.Rulesets.Osu.UI.Cursor;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
@ -40,6 +42,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
private Drawable background;
|
private Drawable background;
|
||||||
|
|
||||||
|
private readonly Bindable<bool> ripples = new Bindable<bool>();
|
||||||
|
|
||||||
public TestSceneGameplayCursor()
|
public TestSceneGameplayCursor()
|
||||||
{
|
{
|
||||||
var ruleset = new OsuRuleset();
|
var ruleset = new OsuRuleset();
|
||||||
@ -57,6 +61,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AddToggleStep("ripples", v => ripples.Value = v);
|
||||||
|
|
||||||
AddSliderStep("circle size", 0f, 10f, 0f, val =>
|
AddSliderStep("circle size", 0f, 10f, 0f, val =>
|
||||||
{
|
{
|
||||||
config.SetValue(OsuSetting.AutoCursorSize, true);
|
config.SetValue(OsuSetting.AutoCursorSize, true);
|
||||||
@ -67,6 +73,13 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
AddStep("test cursor container", () => loadContent(false));
|
AddStep("test cursor container", () => loadContent(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
var rulesetConfig = (OsuRulesetConfigManager)RulesetConfigs.GetConfigFor(Ruleset.Value.CreateInstance()).AsNonNull();
|
||||||
|
rulesetConfig.BindWith(OsuRulesetSetting.ShowCursorRipples, ripples);
|
||||||
|
}
|
||||||
|
|
||||||
[TestCase(1, 1)]
|
[TestCase(1, 1)]
|
||||||
[TestCase(5, 1)]
|
[TestCase(5, 1)]
|
||||||
[TestCase(10, 1)]
|
[TestCase(10, 1)]
|
||||||
|
@ -439,7 +439,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
public TestSlider()
|
public TestSlider()
|
||||||
{
|
{
|
||||||
DifficultyControlPoint = new DifficultyControlPoint { SliderVelocity = 0.1f };
|
SliderVelocity = 0.1f;
|
||||||
|
|
||||||
DefaultsApplied += _ =>
|
DefaultsApplied += _ =>
|
||||||
{
|
{
|
||||||
|
@ -21,7 +21,7 @@ using osu.Game.Configuration;
|
|||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.UI.Cursor;
|
using osu.Game.Rulesets.Osu.UI.Cursor;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play.HUD;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
@ -34,9 +34,9 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private OsuConfigManager config { get; set; } = null!;
|
private OsuConfigManager config { get; set; } = null!;
|
||||||
|
|
||||||
private TestActionKeyCounter leftKeyCounter = null!;
|
private DefaultKeyCounter leftKeyCounter = null!;
|
||||||
|
|
||||||
private TestActionKeyCounter rightKeyCounter = null!;
|
private DefaultKeyCounter rightKeyCounter = null!;
|
||||||
|
|
||||||
private OsuInputManager osuInputManager = null!;
|
private OsuInputManager osuInputManager = null!;
|
||||||
|
|
||||||
@ -59,14 +59,14 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
leftKeyCounter = new TestActionKeyCounter(OsuAction.LeftButton)
|
leftKeyCounter = new DefaultKeyCounter(new TestActionKeyCounterTrigger(OsuAction.LeftButton))
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.CentreRight,
|
Origin = Anchor.CentreRight,
|
||||||
Depth = float.MinValue,
|
Depth = float.MinValue,
|
||||||
X = -100,
|
X = -100,
|
||||||
},
|
},
|
||||||
rightKeyCounter = new TestActionKeyCounter(OsuAction.RightButton)
|
rightKeyCounter = new DefaultKeyCounter(new TestActionKeyCounterTrigger(OsuAction.RightButton))
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
@ -150,6 +150,42 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
assertKeyCounter(1, 1);
|
assertKeyCounter(1, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestPositionalTrackingAfterLongDistanceTravelled()
|
||||||
|
{
|
||||||
|
// When a single touch has already travelled enough distance on screen, it should remain as the positional
|
||||||
|
// tracking touch until released (unless a direct touch occurs).
|
||||||
|
|
||||||
|
beginTouch(TouchSource.Touch1);
|
||||||
|
|
||||||
|
assertKeyCounter(1, 0);
|
||||||
|
checkPressed(OsuAction.LeftButton);
|
||||||
|
checkPosition(TouchSource.Touch1);
|
||||||
|
|
||||||
|
// cover some distance
|
||||||
|
beginTouch(TouchSource.Touch1, new Vector2(0));
|
||||||
|
beginTouch(TouchSource.Touch1, new Vector2(9999));
|
||||||
|
beginTouch(TouchSource.Touch1, new Vector2(0));
|
||||||
|
beginTouch(TouchSource.Touch1, new Vector2(9999));
|
||||||
|
beginTouch(TouchSource.Touch1);
|
||||||
|
|
||||||
|
beginTouch(TouchSource.Touch2);
|
||||||
|
|
||||||
|
assertKeyCounter(1, 1);
|
||||||
|
checkNotPressed(OsuAction.LeftButton);
|
||||||
|
checkPressed(OsuAction.RightButton);
|
||||||
|
// in this case, touch 2 should not become the positional tracking touch.
|
||||||
|
checkPosition(TouchSource.Touch1);
|
||||||
|
|
||||||
|
// even if the second touch moves on the screen, the original tracking touch is retained.
|
||||||
|
beginTouch(TouchSource.Touch2, new Vector2(0));
|
||||||
|
beginTouch(TouchSource.Touch2, new Vector2(9999));
|
||||||
|
beginTouch(TouchSource.Touch2, new Vector2(0));
|
||||||
|
beginTouch(TouchSource.Touch2, new Vector2(9999));
|
||||||
|
|
||||||
|
checkPosition(TouchSource.Touch1);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestPositionalInputUpdatesOnlyFromMostRecentTouch()
|
public void TestPositionalInputUpdatesOnlyFromMostRecentTouch()
|
||||||
{
|
{
|
||||||
@ -562,8 +598,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
private void assertKeyCounter(int left, int right)
|
private void assertKeyCounter(int left, int right)
|
||||||
{
|
{
|
||||||
AddAssert($"The left key was pressed {left} times", () => leftKeyCounter.CountPresses, () => Is.EqualTo(left));
|
AddAssert($"The left key was pressed {left} times", () => leftKeyCounter.CountPresses.Value, () => Is.EqualTo(left));
|
||||||
AddAssert($"The right key was pressed {right} times", () => rightKeyCounter.CountPresses, () => Is.EqualTo(right));
|
AddAssert($"The right key was pressed {right} times", () => rightKeyCounter.CountPresses.Value, () => Is.EqualTo(right));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void releaseAllTouches()
|
private void releaseAllTouches()
|
||||||
@ -579,11 +615,11 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
private void checkNotPressed(OsuAction action) => AddAssert($"Not pressing {action}", () => !osuInputManager.PressedActions.Contains(action));
|
private void checkNotPressed(OsuAction action) => AddAssert($"Not pressing {action}", () => !osuInputManager.PressedActions.Contains(action));
|
||||||
private void checkPressed(OsuAction action) => AddAssert($"Is pressing {action}", () => osuInputManager.PressedActions.Contains(action));
|
private void checkPressed(OsuAction action) => AddAssert($"Is pressing {action}", () => osuInputManager.PressedActions.Contains(action));
|
||||||
|
|
||||||
public partial class TestActionKeyCounter : KeyCounter, IKeyBindingHandler<OsuAction>
|
public partial class TestActionKeyCounterTrigger : InputTrigger, IKeyBindingHandler<OsuAction>
|
||||||
{
|
{
|
||||||
public OsuAction Action { get; }
|
public OsuAction Action { get; }
|
||||||
|
|
||||||
public TestActionKeyCounter(OsuAction action)
|
public TestActionKeyCounterTrigger(OsuAction action)
|
||||||
: base(action.ToString())
|
: base(action.ToString())
|
||||||
{
|
{
|
||||||
Action = action;
|
Action = action;
|
||||||
@ -593,8 +629,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
if (e.Action == Action)
|
if (e.Action == Action)
|
||||||
{
|
{
|
||||||
IsLit = true;
|
Activate();
|
||||||
Increment();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -602,7 +637,8 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
|
|
||||||
public void OnReleased(KeyBindingReleaseEvent<OsuAction> e)
|
public void OnReleased(KeyBindingReleaseEvent<OsuAction> e)
|
||||||
{
|
{
|
||||||
if (e.Action == Action) IsLit = false;
|
if (e.Action == Action)
|
||||||
|
Deactivate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,10 @@ using osuTK.Graphics;
|
|||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps.Legacy;
|
using osu.Game.Beatmaps.Legacy;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
@ -22,6 +26,7 @@ using osu.Game.Rulesets.Judgements;
|
|||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
using osu.Game.Rulesets.Osu.Configuration;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Tests
|
namespace osu.Game.Rulesets.Osu.Tests
|
||||||
{
|
{
|
||||||
@ -30,6 +35,27 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
private int depthIndex;
|
private int depthIndex;
|
||||||
|
|
||||||
|
private readonly BindableBool snakingIn = new BindableBool();
|
||||||
|
private readonly BindableBool snakingOut = new BindableBool();
|
||||||
|
|
||||||
|
[SetUpSteps]
|
||||||
|
public void SetUpSteps()
|
||||||
|
{
|
||||||
|
AddToggleStep("toggle snaking", v =>
|
||||||
|
{
|
||||||
|
snakingIn.Value = v;
|
||||||
|
snakingOut.Value = v;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
var config = (OsuRulesetConfigManager)RulesetConfigs.GetConfigFor(Ruleset.Value.CreateInstance()).AsNonNull();
|
||||||
|
config.BindWith(OsuRulesetSetting.SnakingInSliders, snakingIn);
|
||||||
|
config.BindWith(OsuRulesetSetting.SnakingOutSliders, snakingOut);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestVariousSliders()
|
public void TestVariousSliders()
|
||||||
{
|
{
|
||||||
|
@ -7,7 +7,6 @@ using NUnit.Framework;
|
|||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
|
||||||
using osu.Game.Replays;
|
using osu.Game.Replays;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
@ -47,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
StartTime = time_slider_start,
|
StartTime = time_slider_start,
|
||||||
Position = new Vector2(0, 0),
|
Position = new Vector2(0, 0),
|
||||||
DifficultyControlPoint = new DifficultyControlPoint { SliderVelocity = velocity },
|
SliderVelocity = velocity,
|
||||||
Path = new SliderPath(PathType.Linear, new[]
|
Path = new SliderPath(PathType.Linear, new[]
|
||||||
{
|
{
|
||||||
Vector2.Zero,
|
Vector2.Zero,
|
||||||
|
@ -8,7 +8,6 @@ using System.Linq;
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
|
||||||
using osu.Game.Replays;
|
using osu.Game.Replays;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
@ -350,7 +349,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
StartTime = time_slider_start,
|
StartTime = time_slider_start,
|
||||||
Position = new Vector2(0, 0),
|
Position = new Vector2(0, 0),
|
||||||
DifficultyControlPoint = new DifficultyControlPoint { SliderVelocity = 0.1f },
|
SliderVelocity = 0.1f,
|
||||||
Path = new SliderPath(PathType.PerfectCurve, new[]
|
Path = new SliderPath(PathType.PerfectCurve, new[]
|
||||||
{
|
{
|
||||||
Vector2.Zero,
|
Vector2.Zero,
|
||||||
|
@ -74,7 +74,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
EndTime = Time.Current + delay + length,
|
EndTime = Time.Current + delay + length,
|
||||||
Samples = new List<HitSampleInfo>
|
Samples = new List<HitSampleInfo>
|
||||||
{
|
{
|
||||||
new HitSampleInfo("hitnormal")
|
new HitSampleInfo(HitSampleInfo.HIT_NORMAL)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -399,7 +399,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
public TestSlider()
|
public TestSlider()
|
||||||
{
|
{
|
||||||
DifficultyControlPoint = new DifficultyControlPoint { SliderVelocity = 0.1f };
|
SliderVelocity = 0.1f;
|
||||||
|
|
||||||
DefaultsApplied += _ =>
|
DefaultsApplied += _ =>
|
||||||
{
|
{
|
||||||
|
@ -30,6 +30,8 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
{
|
{
|
||||||
var positionData = original as IHasPosition;
|
var positionData = original as IHasPosition;
|
||||||
var comboData = original as IHasCombo;
|
var comboData = original as IHasCombo;
|
||||||
|
var sliderVelocityData = original as IHasSliderVelocity;
|
||||||
|
var generateTicksData = original as IHasGenerateTicks;
|
||||||
|
|
||||||
switch (original)
|
switch (original)
|
||||||
{
|
{
|
||||||
@ -47,7 +49,9 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
LegacyLastTickOffset = (original as IHasLegacyLastTickOffset)?.LegacyLastTickOffset,
|
LegacyLastTickOffset = (original as IHasLegacyLastTickOffset)?.LegacyLastTickOffset,
|
||||||
// prior to v8, speed multipliers don't adjust for how many ticks are generated over the same distance.
|
// prior to v8, speed multipliers don't adjust for how many ticks are generated over the same distance.
|
||||||
// this results in more (or less) ticks being generated in <v8 maps for the same time duration.
|
// this results in more (or less) ticks being generated in <v8 maps for the same time duration.
|
||||||
TickDistanceMultiplier = beatmap.BeatmapInfo.BeatmapVersion < 8 ? 1f / ((LegacyControlPointInfo)beatmap.ControlPointInfo).DifficultyPointAt(original.StartTime).SliderVelocity : 1
|
TickDistanceMultiplier = beatmap.BeatmapInfo.BeatmapVersion < 8 ? 1f / ((LegacyControlPointInfo)beatmap.ControlPointInfo).DifficultyPointAt(original.StartTime).SliderVelocity : 1,
|
||||||
|
GenerateTicks = generateTicksData?.GenerateTicks ?? true,
|
||||||
|
SliderVelocity = sliderVelocityData?.SliderVelocity ?? 1,
|
||||||
}.Yield();
|
}.Yield();
|
||||||
|
|
||||||
case IHasDuration endTimeData:
|
case IHasDuration endTimeData:
|
||||||
|
@ -22,6 +22,7 @@ namespace osu.Game.Rulesets.Osu.Configuration
|
|||||||
SetDefault(OsuRulesetSetting.SnakingInSliders, true);
|
SetDefault(OsuRulesetSetting.SnakingInSliders, true);
|
||||||
SetDefault(OsuRulesetSetting.SnakingOutSliders, true);
|
SetDefault(OsuRulesetSetting.SnakingOutSliders, true);
|
||||||
SetDefault(OsuRulesetSetting.ShowCursorTrail, true);
|
SetDefault(OsuRulesetSetting.ShowCursorTrail, true);
|
||||||
|
SetDefault(OsuRulesetSetting.ShowCursorRipples, false);
|
||||||
SetDefault(OsuRulesetSetting.PlayfieldBorderStyle, PlayfieldBorderStyle.None);
|
SetDefault(OsuRulesetSetting.PlayfieldBorderStyle, PlayfieldBorderStyle.None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -31,6 +32,7 @@ namespace osu.Game.Rulesets.Osu.Configuration
|
|||||||
SnakingInSliders,
|
SnakingInSliders,
|
||||||
SnakingOutSliders,
|
SnakingOutSliders,
|
||||||
ShowCursorTrail,
|
ShowCursorTrail,
|
||||||
|
ShowCursorRipples,
|
||||||
PlayfieldBorderStyle,
|
PlayfieldBorderStyle,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints
|
|||||||
protected override bool AlwaysShowWhenSelected => true;
|
protected override bool AlwaysShowWhenSelected => true;
|
||||||
|
|
||||||
protected override bool ShouldBeAlive => base.ShouldBeAlive
|
protected override bool ShouldBeAlive => base.ShouldBeAlive
|
||||||
|| (ShowHitMarkers.Value && editorClock.CurrentTime >= Item.StartTime && editorClock.CurrentTime - Item.GetEndTime() < HitCircleOverlapMarker.FADE_OUT_EXTENSION);
|
|| (DrawableObject is not DrawableSpinner && ShowHitMarkers.Value && editorClock.CurrentTime >= Item.StartTime && editorClock.CurrentTime - Item.GetEndTime() < HitCircleOverlapMarker.FADE_OUT_EXTENSION);
|
||||||
|
|
||||||
protected OsuSelectionBlueprint(T hitObject)
|
protected OsuSelectionBlueprint(T hitObject)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
|
@ -10,7 +10,6 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
@ -42,6 +41,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
private IDistanceSnapProvider snapProvider { get; set; }
|
private IDistanceSnapProvider snapProvider { get; set; }
|
||||||
|
|
||||||
|
protected override bool IsValidForPlacement => HitObject.Path.HasValidLength;
|
||||||
|
|
||||||
public SliderPlacementBlueprint()
|
public SliderPlacementBlueprint()
|
||||||
: base(new Slider())
|
: base(new Slider())
|
||||||
{
|
{
|
||||||
@ -83,11 +84,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
case SliderPlacementState.Initial:
|
case SliderPlacementState.Initial:
|
||||||
BeginPlacement();
|
BeginPlacement();
|
||||||
|
|
||||||
var nearestDifficultyPoint = editorBeatmap.HitObjects
|
double? nearestSliderVelocity = (editorBeatmap.HitObjects
|
||||||
.LastOrDefault(h => h is Slider && h.GetEndTime() < HitObject.StartTime)?
|
.LastOrDefault(h => h is Slider && h.GetEndTime() < HitObject.StartTime) as Slider)?.SliderVelocity;
|
||||||
.DifficultyControlPoint?.DeepClone() as DifficultyControlPoint;
|
|
||||||
|
|
||||||
HitObject.DifficultyControlPoint = nearestDifficultyPoint ?? new DifficultyControlPoint();
|
HitObject.SliderVelocity = nearestSliderVelocity ?? 1;
|
||||||
HitObject.Position = ToLocalSpace(result.ScreenSpacePosition);
|
HitObject.Position = ToLocalSpace(result.ScreenSpacePosition);
|
||||||
|
|
||||||
// Replacing the DifficultyControlPoint above doesn't trigger any kind of invalidation.
|
// Replacing the DifficultyControlPoint above doesn't trigger any kind of invalidation.
|
||||||
@ -152,7 +152,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
private void endCurve()
|
private void endCurve()
|
||||||
{
|
{
|
||||||
updateSlider();
|
updateSlider();
|
||||||
EndPlacement(HitObject.Path.HasValidLength);
|
EndPlacement(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
|
@ -14,7 +14,6 @@ using osu.Framework.Graphics.UserInterface;
|
|||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
@ -311,17 +310,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
var splitControlPoints = controlPoints.Take(index + 1).ToList();
|
var splitControlPoints = controlPoints.Take(index + 1).ToList();
|
||||||
controlPoints.RemoveRange(0, index);
|
controlPoints.RemoveRange(0, index);
|
||||||
|
|
||||||
// Turn the control points which were split off into a new slider.
|
|
||||||
var samplePoint = (SampleControlPoint)HitObject.SampleControlPoint.DeepClone();
|
|
||||||
var difficultyPoint = (DifficultyControlPoint)HitObject.DifficultyControlPoint.DeepClone();
|
|
||||||
|
|
||||||
var newSlider = new Slider
|
var newSlider = new Slider
|
||||||
{
|
{
|
||||||
StartTime = HitObject.StartTime,
|
StartTime = HitObject.StartTime,
|
||||||
Position = HitObject.Position + splitControlPoints[0].Position,
|
Position = HitObject.Position + splitControlPoints[0].Position,
|
||||||
NewCombo = HitObject.NewCombo,
|
NewCombo = HitObject.NewCombo,
|
||||||
SampleControlPoint = samplePoint,
|
|
||||||
DifficultyControlPoint = difficultyPoint,
|
|
||||||
LegacyLastTickOffset = HitObject.LegacyLastTickOffset,
|
LegacyLastTickOffset = HitObject.LegacyLastTickOffset,
|
||||||
Samples = HitObject.Samples.Select(s => s.With()).ToList(),
|
Samples = HitObject.Samples.Select(s => s.With()).ToList(),
|
||||||
RepeatCount = HitObject.RepeatCount,
|
RepeatCount = HitObject.RepeatCount,
|
||||||
@ -378,15 +371,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
|
|
||||||
Vector2 position = HitObject.Position + HitObject.Path.PositionAt(pathPosition);
|
Vector2 position = HitObject.Position + HitObject.Path.PositionAt(pathPosition);
|
||||||
|
|
||||||
var samplePoint = (SampleControlPoint)HitObject.SampleControlPoint.DeepClone();
|
|
||||||
samplePoint.Time = time;
|
|
||||||
|
|
||||||
editorBeatmap.Add(new HitCircle
|
editorBeatmap.Add(new HitCircle
|
||||||
{
|
{
|
||||||
StartTime = time,
|
StartTime = time,
|
||||||
Position = position,
|
Position = position,
|
||||||
NewCombo = i == 0 && HitObject.NewCombo,
|
NewCombo = i == 0 && HitObject.NewCombo,
|
||||||
SampleControlPoint = samplePoint,
|
|
||||||
Samples = HitObject.HeadCircle.Samples.Select(s => s.With()).ToList()
|
Samples = HitObject.HeadCircle.Samples.Select(s => s.With()).ToList()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
@ -24,9 +21,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners
|
|||||||
|
|
||||||
private bool isPlacingEnd;
|
private bool isPlacingEnd;
|
||||||
|
|
||||||
[Resolved(CanBeNull = true)]
|
[Resolved]
|
||||||
[CanBeNull]
|
private IBeatSnapProvider? beatSnapProvider { get; set; }
|
||||||
private IBeatSnapProvider beatSnapProvider { get; set; }
|
|
||||||
|
|
||||||
public SpinnerPlacementBlueprint()
|
public SpinnerPlacementBlueprint()
|
||||||
: base(new Spinner { Position = OsuPlayfield.BASE_SIZE / 2 })
|
: base(new Spinner { Position = OsuPlayfield.BASE_SIZE / 2 })
|
||||||
|
@ -13,8 +13,8 @@ using osu.Framework.Extensions.EnumExtensions;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Utils;
|
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
@ -62,7 +62,12 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
// Give a bit of breathing room around the playfield content.
|
// Give a bit of breathing room around the playfield content.
|
||||||
PlayfieldContentContainer.Padding = new MarginPadding(10);
|
PlayfieldContentContainer.Padding = new MarginPadding
|
||||||
|
{
|
||||||
|
Vertical = 10,
|
||||||
|
Left = TOOLBOX_CONTRACTED_SIZE_LEFT + 10,
|
||||||
|
Right = TOOLBOX_CONTRACTED_SIZE_RIGHT + 10,
|
||||||
|
};
|
||||||
|
|
||||||
LayerBelowRuleset.AddRange(new Drawable[]
|
LayerBelowRuleset.AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
|
@ -362,7 +362,6 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
StartTime = firstHitObject.StartTime,
|
StartTime = firstHitObject.StartTime,
|
||||||
Position = firstHitObject.Position,
|
Position = firstHitObject.Position,
|
||||||
NewCombo = firstHitObject.NewCombo,
|
NewCombo = firstHitObject.NewCombo,
|
||||||
SampleControlPoint = firstHitObject.SampleControlPoint,
|
|
||||||
Samples = firstHitObject.Samples,
|
Samples = firstHitObject.Samples,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ using osu.Game.Beatmaps.Timing;
|
|||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
@ -26,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
private const double flash_duration = 1000;
|
private const double flash_duration = 1000;
|
||||||
|
|
||||||
private DrawableRuleset<OsuHitObject> ruleset = null!;
|
private DrawableOsuRuleset ruleset = null!;
|
||||||
|
|
||||||
protected OsuAction? LastAcceptedAction { get; private set; }
|
protected OsuAction? LastAcceptedAction { get; private set; }
|
||||||
|
|
||||||
@ -42,8 +43,8 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
||||||
{
|
{
|
||||||
ruleset = drawableRuleset;
|
ruleset = (DrawableOsuRuleset)drawableRuleset;
|
||||||
drawableRuleset.KeyBindingInputManager.Add(new InputInterceptor(this));
|
ruleset.KeyBindingInputManager.Add(new InputInterceptor(this));
|
||||||
|
|
||||||
var periods = new List<Period>();
|
var periods = new List<Period>();
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ using osu.Game.Graphics;
|
|||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Replays;
|
using osu.Game.Rulesets.Osu.Replays;
|
||||||
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
@ -55,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
||||||
{
|
{
|
||||||
// Grab the input manager to disable the user's cursor, and for future use
|
// Grab the input manager to disable the user's cursor, and for future use
|
||||||
inputManager = (OsuInputManager)drawableRuleset.KeyBindingInputManager;
|
inputManager = ((DrawableOsuRuleset)drawableRuleset).KeyBindingInputManager;
|
||||||
inputManager.AllowUserCursorMovement = false;
|
inputManager.AllowUserCursorMovement = false;
|
||||||
|
|
||||||
// Generate the replay frames the cursor should follow
|
// Generate the replay frames the cursor should follow
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
@ -10,6 +11,8 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public class OsuModBarrelRoll : ModBarrelRoll<OsuHitObject>, IApplicableToDrawableHitObject
|
public class OsuModBarrelRoll : ModBarrelRoll<OsuHitObject>, IApplicableToDrawableHitObject
|
||||||
{
|
{
|
||||||
|
public override Type[] IncompatibleMods => new[] { typeof(OsuModBubbles) };
|
||||||
|
|
||||||
public void ApplyToDrawableHitObject(DrawableHitObject d)
|
public void ApplyToDrawableHitObject(DrawableHitObject d)
|
||||||
{
|
{
|
||||||
d.OnUpdate += _ =>
|
d.OnUpdate += _ =>
|
||||||
|
214
osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
Normal file
214
osu.Game.Rulesets.Osu/Mods/OsuModBubbles.cs
Normal file
@ -0,0 +1,214 @@
|
|||||||
|
// 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.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Colour;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Effects;
|
||||||
|
using osu.Framework.Graphics.Pooling;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
|
{
|
||||||
|
public partial class OsuModBubbles : Mod, IApplicableToDrawableRuleset<OsuHitObject>, IApplicableToDrawableHitObject, IApplicableToScoreProcessor
|
||||||
|
{
|
||||||
|
public override string Name => "Bubbles";
|
||||||
|
|
||||||
|
public override string Acronym => "BU";
|
||||||
|
|
||||||
|
public override LocalisableString Description => "Don't let their popping distract you!";
|
||||||
|
|
||||||
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
|
public override ModType Type => ModType.Fun;
|
||||||
|
|
||||||
|
// Compatibility with these seems potentially feasible in the future, blocked for now because they don't work as one would expect
|
||||||
|
public override Type[] IncompatibleMods => new[] { typeof(OsuModBarrelRoll), typeof(OsuModMagnetised), typeof(OsuModRepel) };
|
||||||
|
|
||||||
|
private PlayfieldAdjustmentContainer bubbleContainer = null!;
|
||||||
|
|
||||||
|
private readonly Bindable<int> currentCombo = new BindableInt();
|
||||||
|
|
||||||
|
private float maxSize;
|
||||||
|
private float bubbleSize;
|
||||||
|
private double bubbleFade;
|
||||||
|
|
||||||
|
private readonly DrawablePool<BubbleDrawable> bubblePool = new DrawablePool<BubbleDrawable>(100);
|
||||||
|
|
||||||
|
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
|
||||||
|
|
||||||
|
public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
|
||||||
|
{
|
||||||
|
currentCombo.BindTo(scoreProcessor.Combo);
|
||||||
|
currentCombo.BindValueChanged(combo =>
|
||||||
|
maxSize = Math.Min(1.75f, (float)(1.25 + 0.005 * combo.NewValue)), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
||||||
|
{
|
||||||
|
// Multiplying by 2 results in an initial size that is too large, hence 1.90 has been chosen
|
||||||
|
// Also avoids the HitObject bleeding around the edges of the bubble drawable at minimum size
|
||||||
|
bubbleSize = (float)(drawableRuleset.Beatmap.HitObjects.OfType<HitCircle>().First().Radius * 1.90f);
|
||||||
|
bubbleFade = drawableRuleset.Beatmap.HitObjects.OfType<HitCircle>().First().TimePreempt * 2;
|
||||||
|
|
||||||
|
// We want to hide the judgements since they are obscured by the BubbleDrawable (due to layering)
|
||||||
|
drawableRuleset.Playfield.DisplayJudgements.Value = false;
|
||||||
|
|
||||||
|
bubbleContainer = drawableRuleset.CreatePlayfieldAdjustmentContainer();
|
||||||
|
|
||||||
|
drawableRuleset.Overlays.Add(bubbleContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyToDrawableHitObject(DrawableHitObject drawableObject)
|
||||||
|
{
|
||||||
|
drawableObject.OnNewResult += (drawable, _) =>
|
||||||
|
{
|
||||||
|
if (drawable is not DrawableOsuHitObject drawableOsuHitObject) return;
|
||||||
|
|
||||||
|
switch (drawableOsuHitObject.HitObject)
|
||||||
|
{
|
||||||
|
case Slider:
|
||||||
|
case SpinnerTick:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
addBubble();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addBubble()
|
||||||
|
{
|
||||||
|
BubbleDrawable bubble = bubblePool.Get();
|
||||||
|
|
||||||
|
bubble.DrawableOsuHitObject = drawableOsuHitObject;
|
||||||
|
bubble.InitialSize = new Vector2(bubbleSize);
|
||||||
|
bubble.FadeTime = bubbleFade;
|
||||||
|
bubble.MaxSize = maxSize;
|
||||||
|
|
||||||
|
bubbleContainer.Add(bubble);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
drawableObject.OnRevertResult += (drawable, _) =>
|
||||||
|
{
|
||||||
|
if (drawable.HitObject is SpinnerTick or Slider) return;
|
||||||
|
|
||||||
|
BubbleDrawable? lastBubble = bubbleContainer.OfType<BubbleDrawable>().LastOrDefault();
|
||||||
|
|
||||||
|
lastBubble?.ClearTransforms();
|
||||||
|
lastBubble?.Expire(true);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Pooled Bubble drawable
|
||||||
|
|
||||||
|
private partial class BubbleDrawable : PoolableDrawable
|
||||||
|
{
|
||||||
|
public DrawableOsuHitObject? DrawableOsuHitObject { get; set; }
|
||||||
|
|
||||||
|
public Vector2 InitialSize { get; set; }
|
||||||
|
|
||||||
|
public float MaxSize { get; set; }
|
||||||
|
|
||||||
|
public double FadeTime { get; set; }
|
||||||
|
|
||||||
|
private readonly Box colourBox;
|
||||||
|
private readonly CircularContainer content;
|
||||||
|
|
||||||
|
public BubbleDrawable()
|
||||||
|
{
|
||||||
|
Origin = Anchor.Centre;
|
||||||
|
InternalChild = content = new CircularContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
MaskingSmoothness = 2,
|
||||||
|
BorderThickness = 0,
|
||||||
|
BorderColour = Colour4.White,
|
||||||
|
Masking = true,
|
||||||
|
EdgeEffect = new EdgeEffectParameters
|
||||||
|
{
|
||||||
|
Type = EdgeEffectType.Shadow,
|
||||||
|
Radius = 3,
|
||||||
|
Colour = Colour4.Black.Opacity(0.05f),
|
||||||
|
},
|
||||||
|
Child = colourBox = new Box { RelativeSizeAxes = Axes.Both, }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PrepareForUse()
|
||||||
|
{
|
||||||
|
Debug.Assert(DrawableOsuHitObject.IsNotNull());
|
||||||
|
|
||||||
|
Colour = DrawableOsuHitObject.IsHit ? Colour4.White : Colour4.Black;
|
||||||
|
Scale = new Vector2(1);
|
||||||
|
Position = getPosition(DrawableOsuHitObject);
|
||||||
|
Size = InitialSize;
|
||||||
|
|
||||||
|
//We want to fade to a darker colour to avoid colours such as white hiding the "ripple" effect.
|
||||||
|
ColourInfo colourDarker = DrawableOsuHitObject.AccentColour.Value.Darken(0.1f);
|
||||||
|
|
||||||
|
// The absolute length of the bubble's animation, can be used in fractions for animations of partial length
|
||||||
|
double duration = 1700 + Math.Pow(FadeTime, 1.07f);
|
||||||
|
|
||||||
|
// Main bubble scaling based on combo
|
||||||
|
this.FadeTo(1)
|
||||||
|
.ScaleTo(MaxSize, duration * 0.8f)
|
||||||
|
.Then()
|
||||||
|
// Pop at the end of the bubbles life time
|
||||||
|
.ScaleTo(MaxSize * 1.5f, duration * 0.2f, Easing.OutQuint)
|
||||||
|
.FadeOut(duration * 0.2f, Easing.OutCirc).Expire();
|
||||||
|
|
||||||
|
if (!DrawableOsuHitObject.IsHit) return;
|
||||||
|
|
||||||
|
content.BorderThickness = InitialSize.X / 3.5f;
|
||||||
|
content.BorderColour = Colour4.White;
|
||||||
|
|
||||||
|
colourBox.FadeColour(colourDarker);
|
||||||
|
|
||||||
|
content.TransformTo(nameof(BorderColour), colourDarker, duration * 0.3f, Easing.OutQuint);
|
||||||
|
// Ripple effect utilises the border to reduce drawable count
|
||||||
|
content.TransformTo(nameof(BorderThickness), 2f, duration * 0.3f, Easing.OutQuint)
|
||||||
|
.Then()
|
||||||
|
// Avoids transparency overlap issues during the bubble "pop"
|
||||||
|
.TransformTo(nameof(BorderThickness), 0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector2 getPosition(DrawableOsuHitObject drawableObject)
|
||||||
|
{
|
||||||
|
switch (drawableObject)
|
||||||
|
{
|
||||||
|
// SliderHeads are derived from HitCircles,
|
||||||
|
// so we must handle them before to avoid them using the wrong positioning logic
|
||||||
|
case DrawableSliderHead:
|
||||||
|
return drawableObject.HitObject.Position;
|
||||||
|
|
||||||
|
// Using hitobject position will cause issues with HitCircle placement due to stack leniency.
|
||||||
|
case DrawableHitCircle:
|
||||||
|
return drawableObject.Position;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return drawableObject.HitObject.Position;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public class OsuModDaycore : ModDaycore
|
public class OsuModDaycore : ModDaycore
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 0.3;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public class OsuModDoubleTime : ModDoubleTime
|
public class OsuModDoubleTime : ModDoubleTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,5 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public class OsuModHalfTime : ModHalfTime
|
public class OsuModHalfTime : ModHalfTime
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 0.3;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public override ModType Type => ModType.Fun;
|
public override ModType Type => ModType.Fun;
|
||||||
public override LocalisableString Description => "No need to chase the circles – your cursor is a magnet!";
|
public override LocalisableString Description => "No need to chase the circles – your cursor is a magnet!";
|
||||||
public override double ScoreMultiplier => 0.5;
|
public override double ScoreMultiplier => 0.5;
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModRelax), typeof(OsuModRepel) };
|
public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModRelax), typeof(OsuModRepel), typeof(OsuModBubbles) };
|
||||||
|
|
||||||
[SettingSource("Attraction strength", "How strong the pull is.", 0)]
|
[SettingSource("Attraction strength", "How strong the pull is.", 0)]
|
||||||
public BindableFloat AttractionStrength { get; } = new BindableFloat(0.5f)
|
public BindableFloat AttractionStrength { get; } = new BindableFloat(0.5f)
|
||||||
|
@ -8,6 +8,5 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public class OsuModNightcore : ModNightcore<OsuHitObject>
|
public class OsuModNightcore : ModNightcore<OsuHitObject>
|
||||||
{
|
{
|
||||||
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.12 : 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ using osu.Framework.Localisation;
|
|||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Overlays.Settings;
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||||
@ -28,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModTargetPractice)).ToArray();
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModTargetPractice)).ToArray();
|
||||||
|
|
||||||
[SettingSource("Angle sharpness", "How sharp angles should be", SettingControlType = typeof(SettingsSlider<float>))]
|
[SettingSource("Angle sharpness", "How sharp angles should be")]
|
||||||
public BindableFloat AngleSharpness { get; } = new BindableFloat(7)
|
public BindableFloat AngleSharpness { get; } = new BindableFloat(7)
|
||||||
{
|
{
|
||||||
MinValue = 1,
|
MinValue = 1,
|
||||||
|
@ -10,6 +10,7 @@ using osu.Game.Rulesets.Mods;
|
|||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
using osu.Game.Rulesets.Replays;
|
using osu.Game.Rulesets.Replays;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
@ -42,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
||||||
{
|
{
|
||||||
// grab the input manager for future use.
|
// grab the input manager for future use.
|
||||||
osuInputManager = (OsuInputManager)drawableRuleset.KeyBindingInputManager;
|
osuInputManager = ((DrawableOsuRuleset)drawableRuleset).KeyBindingInputManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ApplyToPlayer(Player player)
|
public void ApplyToPlayer(Player player)
|
||||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public override ModType Type => ModType.Fun;
|
public override ModType Type => ModType.Fun;
|
||||||
public override LocalisableString Description => "Hit objects run away!";
|
public override LocalisableString Description => "Hit objects run away!";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModMagnetised) };
|
public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModMagnetised), typeof(OsuModBubbles) };
|
||||||
|
|
||||||
[SettingSource("Repulsion strength", "How strong the repulsion is.", 0)]
|
[SettingSource("Repulsion strength", "How strong the repulsion is.", 0)]
|
||||||
public BindableFloat RepulsionStrength { get; } = new BindableFloat(0.5f)
|
public BindableFloat RepulsionStrength { get; } = new BindableFloat(0.5f)
|
||||||
|
@ -98,6 +98,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
ComboOffset = original.ComboOffset;
|
ComboOffset = original.ComboOffset;
|
||||||
LegacyLastTickOffset = original.LegacyLastTickOffset;
|
LegacyLastTickOffset = original.LegacyLastTickOffset;
|
||||||
TickDistanceMultiplier = original.TickDistanceMultiplier;
|
TickDistanceMultiplier = original.TickDistanceMultiplier;
|
||||||
|
SliderVelocity = original.SliderVelocity;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
|
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
|
||||||
|
@ -133,14 +133,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
// Note: base.LoadSamples() isn't called since the slider plays the tail's hitsounds for the time being.
|
// Note: base.LoadSamples() isn't called since the slider plays the tail's hitsounds for the time being.
|
||||||
|
|
||||||
if (HitObject.SampleControlPoint == null)
|
Samples.Samples = HitObject.TailSamples.Cast<ISampleInfo>().ToArray();
|
||||||
{
|
slidingSample.Samples = HitObject.CreateSlidingSamples().Cast<ISampleInfo>().ToArray();
|
||||||
throw new InvalidOperationException($"{nameof(HitObject)}s must always have an attached {nameof(HitObject.SampleControlPoint)}."
|
|
||||||
+ $" This is an indication that {nameof(HitObject.ApplyDefaults)} has not been invoked on {this}.");
|
|
||||||
}
|
|
||||||
|
|
||||||
Samples.Samples = HitObject.TailSamples.Select(s => HitObject.SampleControlPoint.ApplyTo(s)).Cast<ISampleInfo>().ToArray();
|
|
||||||
slidingSample.Samples = HitObject.CreateSlidingSamples().Select(s => HitObject.SampleControlPoint.ApplyTo(s)).Cast<ISampleInfo>().ToArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void StopAllSamples()
|
public override void StopAllSamples()
|
||||||
|
@ -87,12 +87,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
protected override void UpdateInitialTransforms()
|
protected override void UpdateInitialTransforms()
|
||||||
{
|
{
|
||||||
|
// When snaking in is enabled, the first end circle needs to be delayed until the snaking completes.
|
||||||
|
bool delayFadeIn = DrawableSlider.SliderBody?.SnakingIn.Value == true && HitObject.RepeatIndex == 0;
|
||||||
|
|
||||||
animDuration = Math.Min(300, HitObject.SpanDuration);
|
animDuration = Math.Min(300, HitObject.SpanDuration);
|
||||||
|
|
||||||
this.Animate(
|
this
|
||||||
d => d.FadeIn(animDuration),
|
.FadeOut()
|
||||||
d => d.ScaleTo(0.5f).ScaleTo(1f, animDuration * 2, Easing.OutElasticHalf)
|
.Delay(delayFadeIn ? (Slider?.TimePreempt ?? 0) / 3 : 0)
|
||||||
);
|
.FadeIn(HitObject.RepeatIndex == 0 ? HitObject.TimeFadeIn : animDuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateHitStateTransforms(ArmedState state)
|
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||||
|
@ -91,7 +91,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
base.UpdateInitialTransforms();
|
base.UpdateInitialTransforms();
|
||||||
|
|
||||||
CirclePiece.FadeInFromZero(HitObject.TimeFadeIn);
|
// When snaking in is enabled, the first end circle needs to be delayed until the snaking completes.
|
||||||
|
bool delayFadeIn = DrawableSlider.SliderBody?.SnakingIn.Value == true && HitObject.RepeatIndex == 0;
|
||||||
|
|
||||||
|
CirclePiece
|
||||||
|
.FadeOut()
|
||||||
|
.Delay(delayFadeIn ? (Slider?.TimePreempt ?? 0) / 3 : 0)
|
||||||
|
.FadeIn(HitObject.TimeFadeIn);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateHitStateTransforms(ArmedState state)
|
protected override void UpdateHitStateTransforms(ArmedState state)
|
||||||
|
@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
base.LoadSamples();
|
base.LoadSamples();
|
||||||
|
|
||||||
spinningSample.Samples = HitObject.CreateSpinningSamples().Select(s => HitObject.SampleControlPoint.ApplyTo(s)).Cast<ISampleInfo>().ToArray();
|
spinningSample.Samples = HitObject.CreateSpinningSamples().Cast<ISampleInfo>().ToArray();
|
||||||
spinningSample.Frequency.Value = spinning_sample_initial_frequency;
|
spinningSample.Frequency.Value = spinning_sample_initial_frequency;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,18 +10,18 @@ using osu.Game.Rulesets.Objects;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Caching;
|
using osu.Framework.Caching;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Beatmaps.Formats;
|
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Osu.Judgements;
|
using osu.Game.Rulesets.Osu.Judgements;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects
|
namespace osu.Game.Rulesets.Osu.Objects
|
||||||
{
|
{
|
||||||
public class Slider : OsuHitObject, IHasPathWithRepeats
|
public class Slider : OsuHitObject, IHasPathWithRepeats, IHasSliderVelocity, IHasGenerateTicks
|
||||||
{
|
{
|
||||||
public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity;
|
public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity;
|
||||||
|
|
||||||
@ -134,6 +134,21 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool OnlyJudgeNestedObjects = true;
|
public bool OnlyJudgeNestedObjects = true;
|
||||||
|
|
||||||
|
public BindableNumber<double> SliderVelocityBindable { get; } = new BindableDouble(1)
|
||||||
|
{
|
||||||
|
Precision = 0.01,
|
||||||
|
MinValue = 0.1,
|
||||||
|
MaxValue = 10
|
||||||
|
};
|
||||||
|
|
||||||
|
public double SliderVelocity
|
||||||
|
{
|
||||||
|
get => SliderVelocityBindable.Value;
|
||||||
|
set => SliderVelocityBindable.Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool GenerateTicks { get; set; } = true;
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public SliderHeadCircle HeadCircle { get; protected set; }
|
public SliderHeadCircle HeadCircle { get; protected set; }
|
||||||
|
|
||||||
@ -151,15 +166,11 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||||
|
|
||||||
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
|
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
|
||||||
#pragma warning disable 618
|
|
||||||
var legacyDifficultyPoint = DifficultyControlPoint as LegacyBeatmapDecoder.LegacyDifficultyControlPoint;
|
|
||||||
#pragma warning restore 618
|
|
||||||
|
|
||||||
double scoringDistance = BASE_SCORING_DISTANCE * difficulty.SliderMultiplier * DifficultyControlPoint.SliderVelocity;
|
double scoringDistance = BASE_SCORING_DISTANCE * difficulty.SliderMultiplier * SliderVelocity;
|
||||||
bool generateTicks = legacyDifficultyPoint?.GenerateTicks ?? true;
|
|
||||||
|
|
||||||
Velocity = scoringDistance / timingPoint.BeatLength;
|
Velocity = scoringDistance / timingPoint.BeatLength;
|
||||||
TickDistance = generateTicks ? (scoringDistance / difficulty.SliderTickRate * TickDistanceMultiplier) : double.PositiveInfinity;
|
TickDistance = GenerateTicks ? (scoringDistance / difficulty.SliderTickRate * TickDistanceMultiplier) : double.PositiveInfinity;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
|
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
|
||||||
|
@ -39,11 +39,8 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// taken from osu-stable
|
|
||||||
const float first_end_circle_preempt_adjust = 2 / 3f;
|
|
||||||
|
|
||||||
// The first end circle should fade in with the slider.
|
// The first end circle should fade in with the slider.
|
||||||
TimePreempt = (StartTime - slider.StartTime) + slider.TimePreempt * first_end_circle_preempt_adjust;
|
TimePreempt += StartTime - slider.StartTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user