Compare commits
2456 Commits
@@ -1 +1,2 @@
|
||||
github: ppy
|
||||
custom: https://osu.ppy.sh/home/support
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Report a bug or crash to desktop
|
||||
---
|
||||
|
||||
<!--
|
||||
IMPORTANT: Your issue may already be reported.
|
||||
|
||||
Please check:
|
||||
- Pinned issues, at the top of https://github.com/ppy/osu/issues
|
||||
- Current priority 0 issues at https://github.com/ppy/osu/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Apriority%3A0
|
||||
- Search for your issue. If you find that it already exists, please respond with a reaction or add any further information that may be helpful.
|
||||
-->
|
||||
|
||||
|
||||
**Describe the bug:**
|
||||
|
||||
**Screenshots or videos showing encountered issue:**
|
||||
|
||||
**osu!lazer version:**
|
||||
|
||||
**Logs:**
|
||||
|
||||
<!--
|
||||
*please attach logs here, which are located at:*
|
||||
- `%AppData%/osu/logs` *(on Windows),*
|
||||
- `~/.local/share/osu/logs` *(on Linux & macOS).*
|
||||
- `Android/data/sh.ppy.osulazer/files/logs` *(on Android)*,
|
||||
- on iOS they can be obtained by connecting your device to your desktop and copying the `logs` directory from the app's own document storage using iTunes. (https://support.apple.com/en-us/HT201301#copy-to-computer)
|
||||
-->
|
||||
@@ -1,12 +1,12 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Suggestions or feature request
|
||||
url: https://github.com/ppy/osu/discussions/categories/ideas
|
||||
about: Got something you think should change or be added? Search for or start a new discussion!
|
||||
- name: Help
|
||||
url: https://github.com/ppy/osu/discussions/categories/q-a
|
||||
about: osu! not working as you'd expect? Not sure it's a bug? Check the Q&A section!
|
||||
- name: Suggestions or feature request
|
||||
url: https://github.com/ppy/osu/discussions/categories/ideas
|
||||
about: Got something you think should change or be added? Search for or start a new discussion!
|
||||
- name: osu!stable issues
|
||||
url: https://github.com/ppy/osu-stable-issues
|
||||
about: For osu!stable bugs (not osu!lazer), check out the dedicated repository. Note that we only accept serious bug reports.
|
||||
about: For osu!(stable) - ie. the current "live" game version, check out the dedicated repository. Note that this is for serious bug reports only, not tech support.
|
||||
|
||||
|
||||
@@ -79,9 +79,14 @@ jobs:
|
||||
run: |
|
||||
# TODO: Add ignore filters and GitHub Workflow Command Reporting in CFS. That way we don't have to do this workaround.
|
||||
# FIXME: Suppress warnings from templates project
|
||||
dotnet codefilesanity | while read -r line; do
|
||||
echo "::warning::$line"
|
||||
done
|
||||
exit_code=0
|
||||
while read -r line; do
|
||||
if [[ ! -z "$line" ]]; then
|
||||
echo "::error::$line"
|
||||
exit_code=1
|
||||
fi
|
||||
done <<< $(dotnet codefilesanity)
|
||||
exit $exit_code
|
||||
|
||||
# Temporarily disabled due to test failures, but it won't work anyway until the tool is upgraded.
|
||||
# - name: .NET Format (Dry Run)
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
# Listens for new PR comments containing !pp check [id], and runs a diffcalc comparison against master.
|
||||
# Usage:
|
||||
# !pp check 0 | Runs only the osu! ruleset.
|
||||
# !pp check 0 2 | Runs only the osu! and catch rulesets.
|
||||
#
|
||||
|
||||
name: Difficulty Calculation
|
||||
on:
|
||||
issue_comment:
|
||||
types: [ created ]
|
||||
|
||||
env:
|
||||
CONCURRENCY: 4
|
||||
ALLOW_DOWNLOAD: 1
|
||||
SAVE_DOWNLOADED: 1
|
||||
SKIP_INSERT_ATTRIBUTES: 1
|
||||
|
||||
jobs:
|
||||
metadata:
|
||||
name: Check for requests
|
||||
runs-on: self-hosted
|
||||
if: github.event.issue.pull_request && contains(github.event.comment.body, '!pp check') && (github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'OWNER')
|
||||
outputs:
|
||||
matrix: ${{ steps.generate-matrix.outputs.matrix }}
|
||||
continue: ${{ steps.generate-matrix.outputs.continue }}
|
||||
steps:
|
||||
- name: Construct build matrix
|
||||
id: generate-matrix
|
||||
run: |
|
||||
if [[ "${{ github.event.comment.body }}" =~ "osu" ]] ; then
|
||||
MATRIX_PROJECTS_JSON+='{ "name": "osu", "id": 0 },'
|
||||
fi
|
||||
if [[ "${{ github.event.comment.body }}" =~ "taiko" ]] ; then
|
||||
MATRIX_PROJECTS_JSON+='{ "name": "taiko", "id": 1 },'
|
||||
fi
|
||||
if [[ "${{ github.event.comment.body }}" =~ "catch" ]] ; then
|
||||
MATRIX_PROJECTS_JSON+='{ "name": "catch", "id": 2 },'
|
||||
fi
|
||||
if [[ "${{ github.event.comment.body }}" =~ "mania" ]] ; then
|
||||
MATRIX_PROJECTS_JSON+='{ "name": "mania", "id": 3 },'
|
||||
fi
|
||||
|
||||
if [[ "${MATRIX_PROJECTS_JSON}" != "" ]]; then
|
||||
MATRIX_JSON="{ \"ruleset\": [ ${MATRIX_PROJECTS_JSON} ] }"
|
||||
echo "${MATRIX_JSON}"
|
||||
CONTINUE="yes"
|
||||
else
|
||||
CONTINUE="no"
|
||||
fi
|
||||
|
||||
echo "::set-output name=continue::${CONTINUE}"
|
||||
echo "::set-output name=matrix::${MATRIX_JSON}"
|
||||
diffcalc:
|
||||
name: Run
|
||||
runs-on: self-hosted
|
||||
timeout-minutes: 1440
|
||||
if: needs.metadata.outputs.continue == 'yes'
|
||||
needs: metadata
|
||||
strategy:
|
||||
matrix: ${{ fromJson(needs.metadata.outputs.matrix) }}
|
||||
steps:
|
||||
- name: Verify MySQL connection from host
|
||||
run: |
|
||||
mysql -e "SHOW DATABASES"
|
||||
|
||||
- name: Drop previous databases
|
||||
run: |
|
||||
for db in osu_master osu_pr
|
||||
do
|
||||
mysql -e "DROP DATABASE IF EXISTS $db"
|
||||
done
|
||||
|
||||
- name: Create directory structure
|
||||
run: |
|
||||
mkdir -p $GITHUB_WORKSPACE/master/
|
||||
mkdir -p $GITHUB_WORKSPACE/pr/
|
||||
|
||||
- name: Get upstream branch # https://akaimo.hatenablog.jp/entry/2020/05/16/101251
|
||||
id: upstreambranch
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
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 "::set-output name=repo::$(curl -H "Authorization: token ${GITHUB_TOKEN}" ${{ github.event.issue.pull_request.url }} | jq '.head.repo.full_name' | sed 's/\"//g')"
|
||||
|
||||
# Checkout osu
|
||||
- name: Checkout osu (master)
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: 'master/osu'
|
||||
- name: Checkout osu (pr)
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
path: 'pr/osu'
|
||||
repository: ${{ steps.upstreambranch.outputs.repo }}
|
||||
ref: ${{ steps.upstreambranch.outputs.branchname }}
|
||||
|
||||
- name: Checkout osu-difficulty-calculator (master)
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: ppy/osu-difficulty-calculator
|
||||
path: 'master/osu-difficulty-calculator'
|
||||
- name: Checkout osu-difficulty-calculator (pr)
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: ppy/osu-difficulty-calculator
|
||||
path: 'pr/osu-difficulty-calculator'
|
||||
|
||||
- name: Install .NET 5.0.x
|
||||
uses: actions/setup-dotnet@v1
|
||||
with:
|
||||
dotnet-version: "5.0.x"
|
||||
|
||||
# Sanity checks to make sure diffcalc is not run when incompatible.
|
||||
- name: Build diffcalc (master)
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE/master/osu-difficulty-calculator
|
||||
./UseLocalOsu.sh
|
||||
dotnet build
|
||||
- name: Build diffcalc (pr)
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE/pr/osu-difficulty-calculator
|
||||
./UseLocalOsu.sh
|
||||
dotnet build
|
||||
|
||||
- name: Download + import data
|
||||
run: |
|
||||
PERFORMANCE_DATA_NAME=$(curl https://data.ppy.sh/ | grep performance_${{ matrix.ruleset.name }}_top_1000 | tail -1 | awk -F "\"" '{print $2}' | sed 's/\.tar\.bz2//g')
|
||||
BEATMAPS_DATA_NAME=$(curl https://data.ppy.sh/ | grep osu_files | tail -1 | awk -F "\"" '{print $2}' | sed 's/\.tar\.bz2//g')
|
||||
|
||||
# Set env variable for further steps.
|
||||
echo "BEATMAPS_PATH=$GITHUB_WORKSPACE/$BEATMAPS_DATA_NAME" >> $GITHUB_ENV
|
||||
|
||||
cd $GITHUB_WORKSPACE
|
||||
|
||||
echo "Downloading database dump $PERFORMANCE_DATA_NAME.."
|
||||
wget -q -nc https://data.ppy.sh/$PERFORMANCE_DATA_NAME.tar.bz2
|
||||
echo "Extracting.."
|
||||
tar -xf $PERFORMANCE_DATA_NAME.tar.bz2
|
||||
|
||||
echo "Downloading beatmap dump $BEATMAPS_DATA_NAME.."
|
||||
wget -q -nc https://data.ppy.sh/$BEATMAPS_DATA_NAME.tar.bz2
|
||||
echo "Extracting.."
|
||||
tar -xf $BEATMAPS_DATA_NAME.tar.bz2
|
||||
|
||||
cd $PERFORMANCE_DATA_NAME
|
||||
|
||||
for db in osu_master osu_pr
|
||||
do
|
||||
echo "Setting up database $db.."
|
||||
|
||||
mysql -e "CREATE DATABASE $db"
|
||||
|
||||
echo "Importing beatmaps.."
|
||||
cat osu_beatmaps.sql | mysql $db
|
||||
echo "Importing beatmapsets.."
|
||||
cat osu_beatmapsets.sql | mysql $db
|
||||
|
||||
echo "Creating table structure.."
|
||||
mysql $db -e 'CREATE TABLE `osu_beatmap_difficulty` (
|
||||
`beatmap_id` int unsigned NOT NULL,
|
||||
`mode` tinyint NOT NULL DEFAULT 0,
|
||||
`mods` int unsigned NOT NULL,
|
||||
`diff_unified` float NOT NULL,
|
||||
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`beatmap_id`,`mode`,`mods`),
|
||||
KEY `diff_sort` (`mode`,`mods`,`diff_unified`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;'
|
||||
done
|
||||
|
||||
- name: Run diffcalc (master)
|
||||
env:
|
||||
DB_NAME: osu_master
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE/master/osu-difficulty-calculator/osu.Server.DifficultyCalculator
|
||||
dotnet run -c:Release -- all -m ${{ matrix.ruleset.id }} -ac -c ${{ env.CONCURRENCY }}
|
||||
- name: Run diffcalc (pr)
|
||||
env:
|
||||
DB_NAME: osu_pr
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE/pr/osu-difficulty-calculator/osu.Server.DifficultyCalculator
|
||||
dotnet run -c:Release -- all -m ${{ matrix.ruleset.id }} -ac -c ${{ env.CONCURRENCY }}
|
||||
|
||||
- name: Print diffs
|
||||
run: |
|
||||
mysql -e "
|
||||
SELECT
|
||||
m.beatmap_id,
|
||||
m.mods,
|
||||
b.filename,
|
||||
m.diff_unified as 'sr_master',
|
||||
p.diff_unified as 'sr_pr',
|
||||
(p.diff_unified - m.diff_unified) as 'diff'
|
||||
FROM osu_master.osu_beatmap_difficulty m
|
||||
JOIN osu_pr.osu_beatmap_difficulty p
|
||||
ON m.beatmap_id = p.beatmap_id
|
||||
AND m.mode = p.mode
|
||||
AND m.mods = p.mods
|
||||
JOIN osu_pr.osu_beatmaps b
|
||||
ON b.beatmap_id = p.beatmap_id
|
||||
WHERE abs(m.diff_unified - p.diff_unified) > 0.1
|
||||
ORDER BY abs(m.diff_unified - p.diff_unified)
|
||||
DESC
|
||||
LIMIT 10000;"
|
||||
|
||||
# Todo: Run ppcalc
|
||||
@@ -30,3 +30,5 @@ jobs:
|
||||
name: Test Results (${{matrix.os.prettyname}}, ${{matrix.threadingMode}})
|
||||
path: "*.trx"
|
||||
reporter: dotnet-trx
|
||||
list-suites: 'failed'
|
||||
list-tests: 'failed'
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Benchmarks" type="DotNetProject" factoryName=".NET Project">
|
||||
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Benchmarks/bin/Release/net5.0/osu.Game.Benchmarks.dll" />
|
||||
<option name="PROGRAM_PARAMETERS" value="--filter *" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Benchmarks/bin/Release/net5.0" />
|
||||
<option name="EXE_PATH" value="$PROJECT_DIR$/osu.Game.Benchmarks/bin/Debug/net5.0/osu.Game.Benchmarks.dll" />
|
||||
<option name="PROGRAM_PARAMETERS" value="" />
|
||||
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/osu.Game.Benchmarks/bin/Debug/net5.0" />
|
||||
<option name="PASS_PARENT_ENVS" value="1" />
|
||||
<option name="USE_EXTERNAL_CONSOLE" value="0" />
|
||||
<option name="USE_MONO" value="0" />
|
||||
@@ -14,7 +14,7 @@
|
||||
<option name="PROJECT_KIND" value="DotNetCore" />
|
||||
<option name="PROJECT_TFM" value="net5.0" />
|
||||
<method v="2">
|
||||
<option name="Build" enabled="true" />
|
||||
<option name="Build" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ContentModelUserStore">
|
||||
<component name="UserContentModel">
|
||||
<attachedFolders />
|
||||
<explicitIncludes />
|
||||
<explicitExcludes />
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
# osu!
|
||||
|
||||
[](https://ci.appveyor.com/project/peppy/osu)
|
||||
[](https://github.com/ppy/osu/actions/workflows/ci.yml)
|
||||
[](https://github.com/ppy/osu/releases/latest)
|
||||
[](https://www.codefactor.io/repository/github/ppy/osu)
|
||||
[](https://discord.gg/ppy)
|
||||
@@ -31,12 +31,11 @@ If you are looking to install or test osu! without setting up a development envi
|
||||
|
||||
**Latest build:**
|
||||
|
||||
| [Windows (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | [macOS 10.12+](https://github.com/ppy/osu/releases/latest/download/osu.app.zip) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS(iOS 10+)](https://osu.ppy.sh/home/testflight) | [Android (5+)](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk)
|
||||
| [Windows 8.1+ (x64)](https://github.com/ppy/osu/releases/latest/download/install.exe) | [macOS 10.12+](https://github.com/ppy/osu/releases/latest/download/osu.app.zip) | [Linux (x64)](https://github.com/ppy/osu/releases/latest/download/osu.AppImage) | [iOS 10+](https://osu.ppy.sh/home/testflight) | [Android 5+](https://github.com/ppy/osu/releases/latest/download/sh.ppy.osulazer.apk)
|
||||
| ------------- | ------------- | ------------- | ------------- | ------------- |
|
||||
|
||||
- The iOS testflight link may fill up (Apple has a hard limit of 10,000 users). We reset it occasionally when this happens. Please do not ask about this. Check back regularly for link resets or follow [peppy](https://twitter.com/ppy) on twitter for announcements of link resets.
|
||||
|
||||
- When running on Windows 7 or 8.1, *[additional prerequisites](https://docs.microsoft.com/en-us/dotnet/core/install/windows?tabs=net50&pivots=os-windows#dependencies)** may be required to correctly run .NET 5 applications if your operating system is not up-to-date with the latest service packs.
|
||||
If your platform is not listed above, there is still a chance you can manually build it by following the instructions below.
|
||||
|
||||
## Developing a custom ruleset
|
||||
|
||||
@@ -15,9 +15,6 @@ namespace osu.Game.Rulesets.EmptyFreeform.Tests
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host, OsuGameBase gameBase)
|
||||
{
|
||||
OsuGame game = new OsuGame();
|
||||
game.SetHost(host);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
@@ -25,8 +22,9 @@ namespace osu.Game.Rulesets.EmptyFreeform.Tests
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
},
|
||||
game
|
||||
};
|
||||
|
||||
AddGame(new OsuGame());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
|
||||
@@ -15,9 +15,6 @@ namespace osu.Game.Rulesets.Pippidon.Tests
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host, OsuGameBase gameBase)
|
||||
{
|
||||
OsuGame game = new OsuGame();
|
||||
game.SetHost(host);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
@@ -25,8 +22,9 @@ namespace osu.Game.Rulesets.Pippidon.Tests
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
},
|
||||
game
|
||||
};
|
||||
|
||||
AddGame(new OsuGame());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
|
||||
@@ -15,9 +15,6 @@ namespace osu.Game.Rulesets.EmptyScrolling.Tests
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host, OsuGameBase gameBase)
|
||||
{
|
||||
OsuGame game = new OsuGame();
|
||||
game.SetHost(host);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
@@ -25,8 +22,9 @@ namespace osu.Game.Rulesets.EmptyScrolling.Tests
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
},
|
||||
game
|
||||
};
|
||||
|
||||
AddGame(new OsuGame());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
|
||||
@@ -15,9 +15,6 @@ namespace osu.Game.Rulesets.Pippidon.Tests
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(GameHost host, OsuGameBase gameBase)
|
||||
{
|
||||
OsuGame game = new OsuGame();
|
||||
game.SetHost(host);
|
||||
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
@@ -25,8 +22,9 @@ namespace osu.Game.Rulesets.Pippidon.Tests
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
},
|
||||
game
|
||||
};
|
||||
|
||||
AddGame(new OsuGame());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
|
||||
@@ -8,6 +8,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osuTK;
|
||||
@@ -61,9 +62,9 @@ namespace osu.Game.Rulesets.Pippidon.UI
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnPressed(PippidonAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<PippidonAction> e)
|
||||
{
|
||||
switch (action)
|
||||
switch (e.Action)
|
||||
{
|
||||
case PippidonAction.MoveUp:
|
||||
changeLane(-1);
|
||||
@@ -78,7 +79,7 @@ namespace osu.Game.Rulesets.Pippidon.UI
|
||||
}
|
||||
}
|
||||
|
||||
public void OnReleased(PippidonAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<PippidonAction> e)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -51,11 +51,11 @@
|
||||
<Reference Include="Java.Interop" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.803.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.803.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2021.1015.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2021.1014.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Transitive Dependencies">
|
||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||
<PackageReference Include="Realm" Version="10.3.0" />
|
||||
<PackageReference Include="Realm" Version="10.6.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -20,8 +20,21 @@ namespace osu.Android
|
||||
[Activity(Theme = "@android:style/Theme.NoTitleBar", MainLauncher = true, ScreenOrientation = ScreenOrientation.FullUser, SupportsPictureInPicture = false, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize, HardwareAccelerated = false, LaunchMode = LaunchMode.SingleInstance, Exported = true)]
|
||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osz", DataHost = "*", DataMimeType = "*/*")]
|
||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataPathPattern = ".*\\\\.osk", DataHost = "*", DataMimeType = "*/*")]
|
||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-archive")]
|
||||
[IntentFilter(new[] { Intent.ActionSend, Intent.ActionSendMultiple }, Categories = new[] { Intent.CategoryDefault }, DataMimeTypes = new[] { "application/zip", "application/octet-stream", "application/download", "application/x-zip", "application/x-zip-compressed", "application/x-osu-archive" })]
|
||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-beatmap-archive")]
|
||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-skin-archive")]
|
||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryDefault }, DataScheme = "content", DataMimeType = "application/x-osu-replay")]
|
||||
[IntentFilter(new[] { Intent.ActionSend, Intent.ActionSendMultiple }, Categories = new[] { Intent.CategoryDefault }, DataMimeTypes = new[]
|
||||
{
|
||||
"application/zip",
|
||||
"application/octet-stream",
|
||||
"application/download",
|
||||
"application/x-zip",
|
||||
"application/x-zip-compressed",
|
||||
// newer official mime types (see https://osu.ppy.sh/wiki/en/osu%21_File_Formats).
|
||||
"application/x-osu-beatmap-archive",
|
||||
"application/x-osu-skin-archive",
|
||||
"application/x-osu-replay",
|
||||
})]
|
||||
[IntentFilter(new[] { Intent.ActionView }, Categories = new[] { Intent.CategoryBrowsable, Intent.CategoryDefault }, DataSchemes = new[] { "osu", "osump" })]
|
||||
public class OsuGameActivity : AndroidGameActivity
|
||||
{
|
||||
@@ -66,12 +79,14 @@ namespace osu.Android
|
||||
case Intent.ActionSendMultiple:
|
||||
{
|
||||
var uris = new List<Uri>();
|
||||
|
||||
for (int i = 0; i < intent.ClipData?.ItemCount; i++)
|
||||
{
|
||||
var content = intent.ClipData?.GetItemAt(i);
|
||||
if (content != null)
|
||||
uris.Add(content.Uri);
|
||||
}
|
||||
|
||||
handleImportFromUris(uris.ToArray());
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -139,11 +139,11 @@ namespace osu.Desktop
|
||||
{
|
||||
switch (activity)
|
||||
{
|
||||
case UserActivity.SoloGame solo:
|
||||
return solo.Beatmap.ToString();
|
||||
case UserActivity.InGame game:
|
||||
return game.BeatmapInfo.ToString();
|
||||
|
||||
case UserActivity.Editing edit:
|
||||
return edit.Beatmap.ToString();
|
||||
return edit.BeatmapInfo.ToString();
|
||||
|
||||
case UserActivity.InLobby lobby:
|
||||
return privacyMode.Value == DiscordRichPresenceMode.Limited ? string.Empty : lobby.Room.Name.Value;
|
||||
|
||||
@@ -74,7 +74,10 @@ namespace osu.Desktop
|
||||
|
||||
// we want to allow multiple instances to be started when in debug.
|
||||
if (!DebugUtils.IsDebugBuild)
|
||||
{
|
||||
Logger.Log(@"osu! does not support multiple running instances.", LoggingTarget.Runtime, LogLevel.Error);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (tournamentClient)
|
||||
|
||||
@@ -5,23 +5,23 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Screens.Play;
|
||||
|
||||
namespace osu.Desktop.Windows
|
||||
{
|
||||
public class GameplayWinKeyBlocker : Component
|
||||
{
|
||||
private Bindable<bool> disableWinKey;
|
||||
private Bindable<bool> localUserPlaying;
|
||||
private IBindable<bool> localUserPlaying;
|
||||
|
||||
[Resolved]
|
||||
private GameHost host { get; set; }
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGame game, OsuConfigManager config)
|
||||
private void load(ILocalUserPlayInfo localUserInfo, OsuConfigManager config)
|
||||
{
|
||||
localUserPlaying = game.LocalUserPlaying.GetBoundCopy();
|
||||
localUserPlaying = localUserInfo.IsPlaying.GetBoundCopy();
|
||||
localUserPlaying.BindValueChanged(_ => updateBlocking());
|
||||
|
||||
disableWinKey = config.GetBindable<bool>(OsuSetting.GameplayDisableWinKey);
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
|
||||
namespace osu.Game.Benchmarks
|
||||
{
|
||||
public class BenchmarkMod : BenchmarkTest
|
||||
{
|
||||
private OsuModDoubleTime mod;
|
||||
|
||||
[Params(1, 10, 100)]
|
||||
public int Times { get; set; }
|
||||
|
||||
public override void SetUp()
|
||||
{
|
||||
base.SetUp();
|
||||
mod = new OsuModDoubleTime();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public int ModHashCode()
|
||||
{
|
||||
var hashCode = new HashCode();
|
||||
|
||||
for (int i = 0; i < Times; i++)
|
||||
hashCode.Add(mod);
|
||||
|
||||
return hashCode.ToHashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// 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 BenchmarkDotNet.Engines;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
|
||||
namespace osu.Game.Benchmarks
|
||||
{
|
||||
public class BenchmarkRuleset : BenchmarkTest
|
||||
{
|
||||
private OsuRuleset ruleset;
|
||||
private APIMod apiModDoubleTime;
|
||||
private APIMod apiModDifficultyAdjust;
|
||||
|
||||
public override void SetUp()
|
||||
{
|
||||
base.SetUp();
|
||||
ruleset = new OsuRuleset();
|
||||
apiModDoubleTime = new APIMod { Acronym = "DT" };
|
||||
apiModDifficultyAdjust = new APIMod { Acronym = "DA" };
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void BenchmarkToModDoubleTime()
|
||||
{
|
||||
apiModDoubleTime.ToMod(ruleset);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void BenchmarkToModDifficultyAdjust()
|
||||
{
|
||||
apiModDifficultyAdjust.ToMod(ruleset);
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void BenchmarkGetAllMods()
|
||||
{
|
||||
ruleset.CreateAllMods().Consume(new Consumer());
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void BenchmarkGetAllModsForReference()
|
||||
{
|
||||
ruleset.AllMods.Consume(new Consumer());
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void BenchmarkGetForAcronym()
|
||||
{
|
||||
ruleset.CreateModFromAcronym("DT");
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void BenchmarkGetForType()
|
||||
{
|
||||
ruleset.CreateMod<ModDoubleTime>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using BenchmarkDotNet.Configs;
|
||||
using BenchmarkDotNet.Running;
|
||||
|
||||
namespace osu.Game.Benchmarks
|
||||
@@ -11,7 +12,7 @@ namespace osu.Game.Benchmarks
|
||||
{
|
||||
BenchmarkSwitcher
|
||||
.FromAssembly(typeof(Program).Assembly)
|
||||
.Run(args);
|
||||
.Run(args, DefaultConfig.Instance.WithOption(ConfigOptions.DisableOptimizationsValidator, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.0" />
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
|
||||
<PackageReference Include="nunit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -14,11 +14,11 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
|
||||
|
||||
[TestCase(4.050601681491468d, "diffcalc-test")]
|
||||
[TestCase(4.0505463516206195d, "diffcalc-test")]
|
||||
public void Test(double expected, string name)
|
||||
=> base.Test(expected, name);
|
||||
|
||||
[TestCase(5.169743871843191d, "diffcalc-test")]
|
||||
[TestCase(5.1696411260785498d, "diffcalc-test")]
|
||||
public void TestClockRateAdjusted(double expected, string name)
|
||||
=> Test(expected, name, new CatchModDoubleTime());
|
||||
|
||||
|
||||
@@ -29,8 +29,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
|
||||
|
||||
protected CatchSelectionBlueprintTestScene()
|
||||
{
|
||||
EditorBeatmap = new EditorBeatmap(new CatchBeatmap());
|
||||
EditorBeatmap.BeatmapInfo.BaseDifficulty.CircleSize = 0;
|
||||
EditorBeatmap = new EditorBeatmap(new CatchBeatmap()) { Difficulty = { CircleSize = 0 } };
|
||||
EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint
|
||||
{
|
||||
BeatLength = 100
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Catch.Edit.Blueprints;
|
||||
using osu.Game.Rulesets.Catch.Edit.Blueprints.Components;
|
||||
@@ -26,7 +27,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
|
||||
protected override void AddHitObject(DrawableHitObject hitObject)
|
||||
{
|
||||
// Create nested bananas (but positions are not randomized because beatmap processing is not done).
|
||||
hitObject.HitObject.ApplyDefaults(new ControlPointInfo(), Beatmap.Value.BeatmapInfo.BaseDifficulty);
|
||||
hitObject.HitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
|
||||
base.AddHitObject(hitObject);
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Edit.Blueprints;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||
@@ -23,11 +23,12 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
|
||||
|
||||
private JuiceStream lastObject => LastObject?.HitObject as JuiceStream;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
protected override IBeatmap GetPlayableBeatmap()
|
||||
{
|
||||
Beatmap.Value.BeatmapInfo.BaseDifficulty.SliderTickRate = 5;
|
||||
Beatmap.Value.BeatmapInfo.BaseDifficulty.SliderMultiplier = velocity * 10;
|
||||
var playable = base.GetPlayableBeatmap();
|
||||
playable.Difficulty.SliderTickRate = 5;
|
||||
playable.Difficulty.SliderMultiplier = velocity * 10;
|
||||
return playable;
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -210,9 +210,9 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
|
||||
new Vector2(50, 200),
|
||||
}), 0.5);
|
||||
AddAssert("1 vertex per 1 nested HO", () => getVertices().Count == hitObject.NestedHitObjects.Count);
|
||||
AddAssert("slider path not yet changed", () => hitObject.Path.ControlPoints[0].Type.Value == PathType.PerfectCurve);
|
||||
AddAssert("slider path not yet changed", () => hitObject.Path.ControlPoints[0].Type == PathType.PerfectCurve);
|
||||
addAddVertexSteps(150, 150);
|
||||
AddAssert("slider path change to linear", () => hitObject.Path.ControlPoints[0].Type.Value == PathType.Linear);
|
||||
AddAssert("slider path change to linear", () => hitObject.Path.ControlPoints[0].Type == PathType.Linear);
|
||||
}
|
||||
|
||||
private void addBlueprintStep(double time, float x, SliderPath sliderPath, double velocity) => AddStep("add selection blueprint", () =>
|
||||
@@ -223,7 +223,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor
|
||||
X = x,
|
||||
Path = sliderPath,
|
||||
};
|
||||
EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = velocity;
|
||||
EditorBeatmap.Difficulty.SliderMultiplier = velocity;
|
||||
EditorBeatmap.Add(hitObject);
|
||||
EditorBeatmap.Update(hitObject);
|
||||
Assert.That(hitObject.Velocity, Is.EqualTo(velocity));
|
||||
|
||||
@@ -154,7 +154,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
} while (rng.Next(2) != 0);
|
||||
|
||||
int length = sliderPath.ControlPoints.Count - start + 1;
|
||||
sliderPath.ControlPoints[start].Type.Value = length <= 2 ? PathType.Linear : length == 3 ? PathType.PerfectCurve : PathType.Bezier;
|
||||
sliderPath.ControlPoints[start].Type = length <= 2 ? PathType.Linear : length == 3 ? PathType.PerfectCurve : PathType.Bezier;
|
||||
} while (rng.Next(3) != 0);
|
||||
|
||||
if (rng.Next(5) == 0)
|
||||
@@ -210,13 +210,13 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
|
||||
path.ConvertToSliderPath(sliderPath, sliderStartY);
|
||||
Assert.That(sliderPath.Distance, Is.EqualTo(path.Distance).Within(1e-3));
|
||||
Assert.That(sliderPath.ControlPoints[0].Position.Value.X, Is.EqualTo(path.Vertices[0].X));
|
||||
Assert.That(sliderPath.ControlPoints[0].Position.X, Is.EqualTo(path.Vertices[0].X));
|
||||
assertInvariants(path.Vertices, true);
|
||||
|
||||
foreach (var point in sliderPath.ControlPoints)
|
||||
{
|
||||
Assert.That(point.Type.Value, Is.EqualTo(PathType.Linear).Or.Null);
|
||||
Assert.That(sliderStartY + point.Position.Value.Y, Is.InRange(0, JuiceStreamPath.OSU_PLAYFIELD_HEIGHT));
|
||||
Assert.That(point.Type, Is.EqualTo(PathType.Linear).Or.Null);
|
||||
Assert.That(sliderStartY + point.Position.Y, Is.InRange(0, JuiceStreamPath.OSU_PLAYFIELD_HEIGHT));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Mods;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests.Mods
|
||||
{
|
||||
[TestFixture]
|
||||
public class CatchModMirrorTest
|
||||
{
|
||||
[Test]
|
||||
public void TestModMirror()
|
||||
{
|
||||
IBeatmap original = createBeatmap(false);
|
||||
IBeatmap mirrored = createBeatmap(true);
|
||||
|
||||
assertEffectivePositionsMirrored(original, mirrored);
|
||||
}
|
||||
|
||||
private static IBeatmap createBeatmap(bool withMirrorMod)
|
||||
{
|
||||
var beatmap = createRawBeatmap();
|
||||
var mirrorMod = new CatchModMirror();
|
||||
|
||||
var beatmapProcessor = new CatchBeatmapProcessor(beatmap);
|
||||
beatmapProcessor.PreProcess();
|
||||
|
||||
foreach (var hitObject in beatmap.HitObjects)
|
||||
hitObject.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
|
||||
beatmapProcessor.PostProcess();
|
||||
|
||||
if (withMirrorMod)
|
||||
mirrorMod.ApplyToBeatmap(beatmap);
|
||||
|
||||
return beatmap;
|
||||
}
|
||||
|
||||
private static IBeatmap createRawBeatmap() => new Beatmap
|
||||
{
|
||||
HitObjects = new List<HitObject>
|
||||
{
|
||||
new Fruit
|
||||
{
|
||||
OriginalX = 150,
|
||||
StartTime = 0
|
||||
},
|
||||
new Fruit
|
||||
{
|
||||
OriginalX = 450,
|
||||
StartTime = 500
|
||||
},
|
||||
new JuiceStream
|
||||
{
|
||||
OriginalX = 250,
|
||||
Path = new SliderPath
|
||||
{
|
||||
ControlPoints =
|
||||
{
|
||||
new PathControlPoint(new Vector2(-100, 1)),
|
||||
new PathControlPoint(new Vector2(0, 2)),
|
||||
new PathControlPoint(new Vector2(100, 3)),
|
||||
new PathControlPoint(new Vector2(0, 4))
|
||||
}
|
||||
},
|
||||
StartTime = 1000,
|
||||
},
|
||||
new BananaShower
|
||||
{
|
||||
StartTime = 5000,
|
||||
Duration = 5000
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private static void assertEffectivePositionsMirrored(IBeatmap original, IBeatmap mirrored)
|
||||
{
|
||||
if (original.HitObjects.Count != mirrored.HitObjects.Count)
|
||||
Assert.Fail($"Top-level object count mismatch (original: {original.HitObjects.Count}, mirrored: {mirrored.HitObjects.Count})");
|
||||
|
||||
for (int i = 0; i < original.HitObjects.Count; ++i)
|
||||
{
|
||||
var originalObject = (CatchHitObject)original.HitObjects[i];
|
||||
var mirroredObject = (CatchHitObject)mirrored.HitObjects[i];
|
||||
|
||||
// banana showers themselves are exempt, as we only really care about their nested bananas' positions.
|
||||
if (!effectivePositionMirrored(originalObject, mirroredObject) && !(originalObject is BananaShower))
|
||||
Assert.Fail($"{originalObject.GetType().Name} at time {originalObject.StartTime} is not mirrored ({printEffectivePositions(originalObject, mirroredObject)})");
|
||||
|
||||
if (originalObject.NestedHitObjects.Count != mirroredObject.NestedHitObjects.Count)
|
||||
Assert.Fail($"{originalObject.GetType().Name} nested object count mismatch (original: {originalObject.NestedHitObjects.Count}, mirrored: {mirroredObject.NestedHitObjects.Count})");
|
||||
|
||||
for (int j = 0; j < originalObject.NestedHitObjects.Count; ++j)
|
||||
{
|
||||
var originalNested = (CatchHitObject)originalObject.NestedHitObjects[j];
|
||||
var mirroredNested = (CatchHitObject)mirroredObject.NestedHitObjects[j];
|
||||
|
||||
if (!effectivePositionMirrored(originalNested, mirroredNested))
|
||||
Assert.Fail($"{originalObject.GetType().Name}'s nested {originalNested.GetType().Name} at time {originalObject.StartTime} is not mirrored ({printEffectivePositions(originalNested, mirroredNested)})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string printEffectivePositions(CatchHitObject original, CatchHitObject mirrored)
|
||||
=> $"original X: {original.EffectiveX}, mirrored X is: {mirrored.EffectiveX}, mirrored X should be: {CatchPlayfield.WIDTH - original.EffectiveX}";
|
||||
|
||||
private static bool effectivePositionMirrored(CatchHitObject original, CatchHitObject mirrored)
|
||||
=> Precision.AlmostEquals(original.EffectiveX, CatchPlayfield.WIDTH - mirrored.EffectiveX);
|
||||
}
|
||||
}
|
||||
@@ -290,7 +290,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
{
|
||||
public IEnumerable<CaughtObject> CaughtObjects => this.ChildrenOfType<CaughtObject>();
|
||||
|
||||
public TestCatcher(DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty)
|
||||
public TestCatcher(DroppedObjectContainer droppedObjectTarget, IBeatmapDifficultyInfo difficulty)
|
||||
: base(droppedObjectTarget, difficulty)
|
||||
{
|
||||
}
|
||||
@@ -298,7 +298,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
|
||||
public class TestKiaiFruit : Fruit
|
||||
{
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
|
||||
{
|
||||
controlPointInfo.Add(0, new EffectControlPoint { KiaiMode = true });
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
@@ -34,12 +34,13 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
|
||||
private ScheduledDelegate addManyFruit;
|
||||
|
||||
private BeatmapDifficulty beatmapDifficulty;
|
||||
private IBeatmapDifficultyInfo beatmapDifficulty;
|
||||
|
||||
public TestSceneCatcherArea()
|
||||
{
|
||||
AddSliderStep<float>("circle size", 0, 8, 5, createCatcher);
|
||||
AddToggleStep("hyper dash", t => this.ChildrenOfType<TestCatcherArea>().ForEach(area => area.ToggleHyperDash(t)));
|
||||
AddToggleStep("toggle hit lighting", lighting => config.SetValue(OsuSetting.HitLighting, lighting));
|
||||
|
||||
AddStep("catch centered fruit", () => attemptCatch(new Fruit()));
|
||||
AddStep("catch many random fruit", () =>
|
||||
@@ -119,7 +120,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
||||
|
||||
private class TestCatcherArea : CatcherArea
|
||||
{
|
||||
public TestCatcherArea(BeatmapDifficulty beatmapDifficulty)
|
||||
public TestCatcherArea(IBeatmapDifficultyInfo beatmapDifficulty)
|
||||
{
|
||||
var droppedObjectContainer = new DroppedObjectContainer();
|
||||
Add(droppedObjectContainer);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
|
||||
case JuiceStream juiceStream:
|
||||
// Todo: BUG!! Stable used the last control point as the final position of the path, but it should use the computed path instead.
|
||||
lastPosition = juiceStream.OriginalX + juiceStream.Path.ControlPoints[^1].Position.Value.X;
|
||||
lastPosition = juiceStream.OriginalX + juiceStream.Path.ControlPoints[^1].Position.X;
|
||||
|
||||
// Todo: BUG!! Stable attempted to use the end time of the stream, but referenced it too early in execution and used the start time instead.
|
||||
lastStartTime = juiceStream.StartTime;
|
||||
@@ -211,7 +211,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
|
||||
palpableObjects.Sort((h1, h2) => h1.StartTime.CompareTo(h2.StartTime));
|
||||
|
||||
double halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.BeatmapInfo.BaseDifficulty) / 2;
|
||||
double halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.Difficulty) / 2;
|
||||
|
||||
// Todo: This is wrong. osu!stable calculated hyperdashes using the full catcher size, excluding the margins.
|
||||
// This should theoretically cause impossible scenarios, but practically, likely due to the size of the playfield, it doesn't seem possible.
|
||||
|
||||
@@ -117,6 +117,7 @@ namespace osu.Game.Rulesets.Catch
|
||||
{
|
||||
new CatchModDifficultyAdjust(),
|
||||
new CatchModClassic(),
|
||||
new CatchModMirror(),
|
||||
};
|
||||
|
||||
case ModType.Automation:
|
||||
|
||||
@@ -9,6 +9,7 @@ namespace osu.Game.Rulesets.Catch
|
||||
Banana,
|
||||
Droplet,
|
||||
Catcher,
|
||||
CatchComboCounter
|
||||
CatchComboCounter,
|
||||
HitExplosion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
return new CatchDifficultyAttributes { Mods = mods, Skills = skills };
|
||||
|
||||
// this is the same as osu!, so there's potential to share the implementation... maybe
|
||||
double preempt = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / clockRate;
|
||||
double preempt = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450) / clockRate;
|
||||
|
||||
return new CatchDifficultyAttributes
|
||||
{
|
||||
@@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
|
||||
// In 2B beatmaps, it is possible that a normal Fruit is placed in the middle of a JuiceStream.
|
||||
foreach (var hitObject in beatmap.HitObjects
|
||||
.SelectMany(obj => obj is JuiceStream stream ? stream.NestedHitObjects : new[] { obj })
|
||||
.SelectMany(obj => obj is JuiceStream stream ? stream.NestedHitObjects.AsEnumerable() : new[] { obj })
|
||||
.Cast<CatchHitObject>()
|
||||
.OrderBy(x => x.StartTime))
|
||||
{
|
||||
@@ -69,10 +69,10 @@ namespace osu.Game.Rulesets.Catch.Difficulty
|
||||
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate)
|
||||
{
|
||||
halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.BeatmapInfo.BaseDifficulty) * 0.5f;
|
||||
halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.Difficulty) * 0.5f;
|
||||
|
||||
// For circle sizes above 5.5, reduce the catcher width further to simulate imperfect gameplay.
|
||||
halfCatcherWidth *= 1 - (Math.Max(0, beatmap.BeatmapInfo.BaseDifficulty.CircleSize - 5.5f) * 0.0625f);
|
||||
halfCatcherWidth *= 1 - (Math.Max(0, beatmap.Difficulty.CircleSize - 5.5f) * 0.0625f);
|
||||
|
||||
return new Skill[]
|
||||
{
|
||||
|
||||
@@ -9,7 +9,7 @@ using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Difficulty.Skills
|
||||
{
|
||||
public class Movement : StrainSkill
|
||||
public class Movement : StrainDecaySkill
|
||||
{
|
||||
private const float absolute_player_positioning_error = 16f;
|
||||
private const float normalized_hitobject_radius = 41.0f;
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components
|
||||
path.ConvertFromSliderPath(sliderPath);
|
||||
|
||||
// If the original slider path has non-linear type segments, resample the vertices at nested hit object times to reduce the number of vertices.
|
||||
if (sliderPath.ControlPoints.Any(p => p.Type.Value != null && p.Type.Value != PathType.Linear))
|
||||
if (sliderPath.ControlPoints.Any(p => p.Type != null && p.Type != PathType.Linear))
|
||||
{
|
||||
path.ResampleVertices(hitObject.NestedHitObjects
|
||||
.Skip(1).TakeWhile(h => !(h is Fruit)) // Only droplets in the first span are used.
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Catch.Edit
|
||||
public class CatchEditorPlayfield : CatchPlayfield
|
||||
{
|
||||
// TODO fixme: the size of the catcher is not changed when circle size is changed in setup screen.
|
||||
public CatchEditorPlayfield(BeatmapDifficulty difficulty)
|
||||
public CatchEditorPlayfield(IBeatmapDifficultyInfo difficulty)
|
||||
: base(difficulty)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ namespace osu.Game.Rulesets.Catch.Edit
|
||||
juiceStream.OriginalX = selectionRange.GetFlippedPosition(juiceStream.OriginalX);
|
||||
|
||||
foreach (var point in juiceStream.Path.ControlPoints)
|
||||
point.Position.Value *= new Vector2(-1, 1);
|
||||
point.Position *= new Vector2(-1, 1);
|
||||
|
||||
EditorBeatmap.Update(juiceStream);
|
||||
return true;
|
||||
|
||||
@@ -16,6 +16,6 @@ namespace osu.Game.Rulesets.Catch.Edit
|
||||
{
|
||||
}
|
||||
|
||||
protected override Playfield CreatePlayfield() => new CatchEditorPlayfield(Beatmap.BeatmapInfo.BaseDifficulty);
|
||||
protected override Playfield CreatePlayfield() => new CatchEditorPlayfield(Beatmap.Difficulty);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Mods
|
||||
{
|
||||
public class CatchModMirror : ModMirror, IApplicableToBeatmap
|
||||
{
|
||||
public override string Description => "Fruits are flipped horizontally.";
|
||||
|
||||
/// <remarks>
|
||||
/// <see cref="IApplicableToBeatmap"/> is used instead of <see cref="IApplicableToHitObject"/>,
|
||||
/// as <see cref="CatchBeatmapProcessor"/> applies offsets in <see cref="CatchBeatmapProcessor.PostProcess"/>.
|
||||
/// <see cref="IApplicableToBeatmap"/> runs after post-processing, while <see cref="IApplicableToHitObject"/> runs before it.
|
||||
/// </remarks>
|
||||
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||
{
|
||||
foreach (var hitObject in beatmap.HitObjects)
|
||||
applyToHitObject(hitObject);
|
||||
}
|
||||
|
||||
private void applyToHitObject(HitObject hitObject)
|
||||
{
|
||||
var catchObject = (CatchHitObject)hitObject;
|
||||
|
||||
switch (catchObject)
|
||||
{
|
||||
case Fruit fruit:
|
||||
mirrorEffectiveX(fruit);
|
||||
break;
|
||||
|
||||
case JuiceStream juiceStream:
|
||||
mirrorEffectiveX(juiceStream);
|
||||
mirrorJuiceStreamPath(juiceStream);
|
||||
break;
|
||||
|
||||
case BananaShower bananaShower:
|
||||
mirrorBananaShower(bananaShower);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mirrors the effective X position of <paramref name="catchObject"/> and its nested hit objects.
|
||||
/// </summary>
|
||||
private static void mirrorEffectiveX(CatchHitObject catchObject)
|
||||
{
|
||||
catchObject.OriginalX = CatchPlayfield.WIDTH - catchObject.OriginalX;
|
||||
catchObject.XOffset = -catchObject.XOffset;
|
||||
|
||||
foreach (var nested in catchObject.NestedHitObjects.Cast<CatchHitObject>())
|
||||
{
|
||||
nested.OriginalX = CatchPlayfield.WIDTH - nested.OriginalX;
|
||||
nested.XOffset = -nested.XOffset;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mirrors the path of the <paramref name="juiceStream"/>.
|
||||
/// </summary>
|
||||
private static void mirrorJuiceStreamPath(JuiceStream juiceStream)
|
||||
{
|
||||
var controlPoints = juiceStream.Path.ControlPoints.Select(p => new PathControlPoint(p.Position, p.Type)).ToArray();
|
||||
foreach (var point in controlPoints)
|
||||
point.Position = new Vector2(-point.Position.X, point.Position.Y);
|
||||
|
||||
juiceStream.Path = new SliderPath(controlPoints, juiceStream.Path.ExpectedDistance.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mirrors X positions of all bananas in the <paramref name="bananaShower"/>.
|
||||
/// </summary>
|
||||
private static void mirrorBananaShower(BananaShower bananaShower)
|
||||
{
|
||||
foreach (var banana in bananaShower.NestedHitObjects.OfType<Banana>())
|
||||
banana.XOffset = CatchPlayfield.WIDTH - banana.XOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -44,9 +44,9 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
}
|
||||
|
||||
// disable keyboard controls
|
||||
public bool OnPressed(CatchAction action) => true;
|
||||
public bool OnPressed(KeyBindingPressEvent<CatchAction> e) => true;
|
||||
|
||||
public void OnReleased(CatchAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<CatchAction> e)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -128,11 +128,11 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
/// </summary>
|
||||
public int RandomSeed => (int)StartTime;
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
|
||||
TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
|
||||
|
||||
Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
|
||||
}
|
||||
|
||||
@@ -37,14 +37,13 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
/// </summary>
|
||||
public double SpanDuration => Duration / this.SpanCount();
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
|
||||
DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime);
|
||||
|
||||
double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier;
|
||||
double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * DifficultyControlPoint.SliderVelocity;
|
||||
|
||||
Velocity = scoringDistance / timingPoint.BeatLength;
|
||||
TickDistance = scoringDistance / difficulty.SliderTickRate;
|
||||
@@ -138,7 +137,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position.Value, c.Type.Value)));
|
||||
path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position, c.Type)));
|
||||
path.ExpectedDistance.Value = value.ExpectedDistance.Value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,7 +234,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
|
||||
for (int i = 1; i < vertices.Count; i++)
|
||||
{
|
||||
sliderPath.ControlPoints[^1].Type.Value = PathType.Linear;
|
||||
sliderPath.ControlPoints[^1].Type = PathType.Linear;
|
||||
|
||||
float deltaX = vertices[i].X - lastPosition.X;
|
||||
double length = vertices[i].Distance - currentDistance;
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Utils;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Default
|
||||
{
|
||||
public class DefaultHitExplosion : CompositeDrawable, IHitExplosion
|
||||
{
|
||||
private CircularContainer largeFaint;
|
||||
private CircularContainer smallFaint;
|
||||
private CircularContainer directionalGlow1;
|
||||
private CircularContainer directionalGlow2;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Size = new Vector2(20);
|
||||
Anchor = Anchor.BottomCentre;
|
||||
Origin = Anchor.BottomCentre;
|
||||
|
||||
// scale roughly in-line with visual appearance of notes
|
||||
const float initial_height = 10;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
largeFaint = new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Blending = BlendingParameters.Additive,
|
||||
},
|
||||
smallFaint = new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Blending = BlendingParameters.Additive,
|
||||
},
|
||||
directionalGlow1 = new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Size = new Vector2(0.01f, initial_height),
|
||||
Blending = BlendingParameters.Additive,
|
||||
},
|
||||
directionalGlow2 = new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Size = new Vector2(0.01f, initial_height),
|
||||
Blending = BlendingParameters.Additive,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void Animate(HitExplosionEntry entry)
|
||||
{
|
||||
X = entry.Position;
|
||||
Scale = new Vector2(entry.HitObject.Scale);
|
||||
setColour(entry.ObjectColour);
|
||||
|
||||
using (BeginAbsoluteSequence(entry.LifetimeStart))
|
||||
applyTransforms(entry.HitObject.RandomSeed);
|
||||
}
|
||||
|
||||
private void applyTransforms(int randomSeed)
|
||||
{
|
||||
const double duration = 400;
|
||||
|
||||
// we want our size to be very small so the glow dominates it.
|
||||
largeFaint.Size = new Vector2(0.8f);
|
||||
largeFaint
|
||||
.ResizeTo(largeFaint.Size * new Vector2(5, 1), duration, Easing.OutQuint)
|
||||
.FadeOut(duration * 2);
|
||||
|
||||
const float angle_variangle = 15; // should be less than 45
|
||||
directionalGlow1.Rotation = StatelessRNG.NextSingle(-angle_variangle, angle_variangle, randomSeed, 4);
|
||||
directionalGlow2.Rotation = StatelessRNG.NextSingle(-angle_variangle, angle_variangle, randomSeed, 5);
|
||||
|
||||
this.FadeInFromZero(50).Then().FadeOut(duration, Easing.Out);
|
||||
}
|
||||
|
||||
private void setColour(Color4 objectColour)
|
||||
{
|
||||
const float roundness = 100;
|
||||
|
||||
largeFaint.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = Interpolation.ValueAt(0.1f, objectColour, Color4.White, 0, 1).Opacity(0.3f),
|
||||
Roundness = 160,
|
||||
Radius = 200,
|
||||
};
|
||||
|
||||
smallFaint.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = Interpolation.ValueAt(0.6f, objectColour, Color4.White, 0, 1),
|
||||
Roundness = 20,
|
||||
Radius = 50,
|
||||
};
|
||||
|
||||
directionalGlow1.EdgeEffect = directionalGlow2.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = Interpolation.ValueAt(0.4f, objectColour, Color4.White, 0, 1),
|
||||
Roundness = roundness,
|
||||
Radius = 40,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -70,13 +70,11 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
|
||||
if (version < 2.3m)
|
||||
{
|
||||
if (GetTexture(@"fruit-ryuuta") != null ||
|
||||
GetTexture(@"fruit-ryuuta-0") != null)
|
||||
if (hasOldStyleCatcherSprite())
|
||||
return new LegacyCatcherOld();
|
||||
}
|
||||
|
||||
if (GetTexture(@"fruit-catcher-idle") != null ||
|
||||
GetTexture(@"fruit-catcher-idle-0") != null)
|
||||
if (hasNewStyleCatcherSprite())
|
||||
return new LegacyCatcherNew();
|
||||
|
||||
return null;
|
||||
@@ -86,12 +84,26 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
return new LegacyCatchComboCounter(Skin);
|
||||
|
||||
return null;
|
||||
|
||||
case CatchSkinComponents.HitExplosion:
|
||||
if (hasOldStyleCatcherSprite() || hasNewStyleCatcherSprite())
|
||||
return new LegacyHitExplosion();
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return base.GetDrawableComponent(component);
|
||||
}
|
||||
|
||||
private bool hasOldStyleCatcherSprite() =>
|
||||
GetTexture(@"fruit-ryuuta") != null
|
||||
|| GetTexture(@"fruit-ryuuta-0") != null;
|
||||
|
||||
private bool hasNewStyleCatcherSprite() =>
|
||||
GetTexture(@"fruit-catcher-idle") != null
|
||||
|| GetTexture(@"fruit-catcher-idle-0") != null;
|
||||
|
||||
public override IBindable<TValue> GetConfig<TLookup, TValue>(TLookup lookup)
|
||||
{
|
||||
switch (lookup)
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Catch.UI;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Skinning.Legacy
|
||||
{
|
||||
public class LegacyHitExplosion : CompositeDrawable, IHitExplosion
|
||||
{
|
||||
[Resolved]
|
||||
private Catcher catcher { get; set; }
|
||||
|
||||
private const float catch_margin = (1 - Catcher.ALLOWED_CATCH_RANGE) / 2;
|
||||
|
||||
private readonly Sprite explosion1;
|
||||
private readonly Sprite explosion2;
|
||||
|
||||
public LegacyHitExplosion()
|
||||
{
|
||||
Anchor = Anchor.BottomCentre;
|
||||
Origin = Anchor.BottomCentre;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Scale = new Vector2(0.5f);
|
||||
|
||||
InternalChildren = new[]
|
||||
{
|
||||
explosion1 = new Sprite
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Alpha = 0,
|
||||
Blending = BlendingParameters.Additive,
|
||||
Rotation = -90
|
||||
},
|
||||
explosion2 = new Sprite
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Alpha = 0,
|
||||
Blending = BlendingParameters.Additive,
|
||||
Rotation = -90
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(SkinManager skins)
|
||||
{
|
||||
var defaultLegacySkin = skins.DefaultLegacySkin;
|
||||
|
||||
// sprite names intentionally swapped to match stable member naming / ease of cross-referencing
|
||||
explosion1.Texture = defaultLegacySkin.GetTexture("scoreboard-explosion-2");
|
||||
explosion2.Texture = defaultLegacySkin.GetTexture("scoreboard-explosion-1");
|
||||
}
|
||||
|
||||
public void Animate(HitExplosionEntry entry)
|
||||
{
|
||||
Colour = entry.ObjectColour;
|
||||
|
||||
using (BeginAbsoluteSequence(entry.LifetimeStart))
|
||||
{
|
||||
float halfCatchWidth = catcher.CatchWidth / 2;
|
||||
float explosionOffset = Math.Clamp(entry.Position, -halfCatchWidth + catch_margin * 3, halfCatchWidth - catch_margin * 3);
|
||||
|
||||
if (!(entry.HitObject is Droplet))
|
||||
{
|
||||
float scale = Math.Clamp(entry.JudgementResult.ComboAtJudgement / 200f, 0.35f, 1.125f);
|
||||
|
||||
explosion1.Scale = new Vector2(1, 0.9f);
|
||||
explosion1.Position = new Vector2(explosionOffset, 0);
|
||||
|
||||
explosion1.FadeOutFromOne(300);
|
||||
explosion1.ScaleTo(new Vector2(16 * scale, 1.1f), 160, Easing.Out);
|
||||
}
|
||||
|
||||
explosion2.Scale = new Vector2(0.9f, 1);
|
||||
explosion2.Position = new Vector2(explosionOffset, 0);
|
||||
|
||||
explosion2.FadeOutFromOne(700);
|
||||
explosion2.ScaleTo(new Vector2(0.9f, 1.3f), 500, Easing.Out);
|
||||
|
||||
this.Delay(700).FadeOutFromOne();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,9 +34,9 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
internal CatcherArea CatcherArea { get; private set; }
|
||||
|
||||
private readonly BeatmapDifficulty difficulty;
|
||||
private readonly IBeatmapDifficultyInfo difficulty;
|
||||
|
||||
public CatchPlayfield(BeatmapDifficulty difficulty)
|
||||
public CatchPlayfield(IBeatmapDifficultyInfo difficulty)
|
||||
{
|
||||
this.difficulty = difficulty;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
[Cached]
|
||||
public class Catcher : SkinReloadableDrawable
|
||||
{
|
||||
/// <summary>
|
||||
@@ -106,7 +107,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
/// <summary>
|
||||
/// Width of the area that can be used to attempt catches during gameplay.
|
||||
/// </summary>
|
||||
private readonly float catchWidth;
|
||||
public readonly float CatchWidth;
|
||||
|
||||
private readonly SkinnableCatcher body;
|
||||
|
||||
@@ -123,7 +124,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
private readonly DrawablePool<CaughtBanana> caughtBananaPool;
|
||||
private readonly DrawablePool<CaughtDroplet> caughtDropletPool;
|
||||
|
||||
public Catcher([NotNull] DroppedObjectContainer droppedObjectTarget, BeatmapDifficulty difficulty = null)
|
||||
public Catcher([NotNull] DroppedObjectContainer droppedObjectTarget, IBeatmapDifficultyInfo difficulty = null)
|
||||
{
|
||||
this.droppedObjectTarget = droppedObjectTarget;
|
||||
|
||||
@@ -133,7 +134,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
if (difficulty != null)
|
||||
Scale = calculateScale(difficulty);
|
||||
|
||||
catchWidth = CalculateCatchWidth(Scale);
|
||||
CatchWidth = CalculateCatchWidth(Scale);
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
@@ -171,7 +172,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
/// <summary>
|
||||
/// Calculates the scale of the catcher based off the provided beatmap difficulty.
|
||||
/// </summary>
|
||||
private static Vector2 calculateScale(BeatmapDifficulty difficulty) => new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5);
|
||||
private static Vector2 calculateScale(IBeatmapDifficultyInfo difficulty) => new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the width of the area used for attempting catches in gameplay.
|
||||
@@ -183,7 +184,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
/// Calculates the width of the area used for attempting catches in gameplay.
|
||||
/// </summary>
|
||||
/// <param name="difficulty">The beatmap difficulty.</param>
|
||||
public static float CalculateCatchWidth(BeatmapDifficulty difficulty) => CalculateCatchWidth(calculateScale(difficulty));
|
||||
public static float CalculateCatchWidth(IBeatmapDifficultyInfo difficulty) => CalculateCatchWidth(calculateScale(difficulty));
|
||||
|
||||
/// <summary>
|
||||
/// Determine if this catcher can catch a <see cref="CatchHitObject"/> in the current position.
|
||||
@@ -193,7 +194,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
if (!(hitObject is PalpableCatchHitObject fruit))
|
||||
return false;
|
||||
|
||||
float halfCatchWidth = catchWidth * 0.5f;
|
||||
float halfCatchWidth = CatchWidth * 0.5f;
|
||||
return fruit.EffectiveX >= X - halfCatchWidth &&
|
||||
fruit.EffectiveX <= X + halfCatchWidth;
|
||||
}
|
||||
@@ -216,7 +217,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
placeCaughtObject(palpableObject, positionInStack);
|
||||
|
||||
if (hitLighting.Value)
|
||||
addLighting(hitObject, positionInStack.X, drawableObject.AccentColour.Value);
|
||||
addLighting(result, drawableObject.AccentColour.Value, positionInStack.X);
|
||||
}
|
||||
|
||||
// droplet doesn't affect the catcher state
|
||||
@@ -365,8 +366,8 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
return position;
|
||||
}
|
||||
|
||||
private void addLighting(CatchHitObject hitObject, float x, Color4 colour) =>
|
||||
hitExplosionContainer.Add(new HitExplosionEntry(Time.Current, x, hitObject.Scale, colour, hitObject.RandomSeed));
|
||||
private void addLighting(JudgementResult judgementResult, Color4 colour, float x) =>
|
||||
hitExplosionContainer.Add(new HitExplosionEntry(Time.Current, judgementResult, colour, x));
|
||||
|
||||
private CaughtObject getCaughtObject(PalpableCatchHitObject source)
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@ using System;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Catch.Judgements;
|
||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Catch.Replays;
|
||||
@@ -144,9 +145,9 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
Catcher.VisualDirection = Direction.Left;
|
||||
}
|
||||
|
||||
public bool OnPressed(CatchAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<CatchAction> e)
|
||||
{
|
||||
switch (action)
|
||||
switch (e.Action)
|
||||
{
|
||||
case CatchAction.MoveLeft:
|
||||
currentDirection--;
|
||||
@@ -164,9 +165,9 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(CatchAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<CatchAction> e)
|
||||
{
|
||||
switch (action)
|
||||
switch (e.Action)
|
||||
{
|
||||
case CatchAction.MoveLeft:
|
||||
currentDirection++;
|
||||
|
||||
@@ -27,14 +27,14 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
: base(ruleset, beatmap, mods)
|
||||
{
|
||||
Direction.Value = ScrollingDirection.Down;
|
||||
TimeRange.Value = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450);
|
||||
TimeRange.Value = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.ApproachRate, 1800, 1200, 450);
|
||||
}
|
||||
|
||||
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
|
||||
|
||||
protected override ReplayRecorder CreateReplayRecorder(Score score) => new CatchReplayRecorder(score, (CatchPlayfield)Playfield);
|
||||
|
||||
protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty);
|
||||
protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.Difficulty);
|
||||
|
||||
public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new CatchPlayfieldAdjustmentContainer();
|
||||
|
||||
|
||||
@@ -1,129 +1,56 @@
|
||||
// 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.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Rulesets.Catch.Skinning.Default;
|
||||
using osu.Game.Rulesets.Objects.Pooling;
|
||||
using osu.Game.Utils;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
public class HitExplosion : PoolableDrawableWithLifetime<HitExplosionEntry>
|
||||
{
|
||||
private readonly CircularContainer largeFaint;
|
||||
private readonly CircularContainer smallFaint;
|
||||
private readonly CircularContainer directionalGlow1;
|
||||
private readonly CircularContainer directionalGlow2;
|
||||
private readonly SkinnableDrawable skinnableExplosion;
|
||||
|
||||
public HitExplosion()
|
||||
{
|
||||
Size = new Vector2(20);
|
||||
Anchor = Anchor.TopCentre;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Anchor = Anchor.BottomCentre;
|
||||
Origin = Anchor.BottomCentre;
|
||||
|
||||
// scale roughly in-line with visual appearance of notes
|
||||
const float initial_height = 10;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
InternalChild = skinnableExplosion = new SkinnableDrawable(new CatchSkinComponent(CatchSkinComponents.HitExplosion), _ => new DefaultHitExplosion())
|
||||
{
|
||||
largeFaint = new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Blending = BlendingParameters.Additive,
|
||||
},
|
||||
smallFaint = new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Blending = BlendingParameters.Additive,
|
||||
},
|
||||
directionalGlow1 = new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Size = new Vector2(0.01f, initial_height),
|
||||
Blending = BlendingParameters.Additive,
|
||||
},
|
||||
directionalGlow2 = new CircularContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Size = new Vector2(0.01f, initial_height),
|
||||
Blending = BlendingParameters.Additive,
|
||||
}
|
||||
CentreComponent = false,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre
|
||||
};
|
||||
}
|
||||
|
||||
protected override void OnApply(HitExplosionEntry entry)
|
||||
{
|
||||
X = entry.Position;
|
||||
Scale = new Vector2(entry.Scale);
|
||||
setColour(entry.ObjectColour);
|
||||
|
||||
using (BeginAbsoluteSequence(entry.LifetimeStart))
|
||||
applyTransforms(entry.RNGSeed);
|
||||
base.OnApply(entry);
|
||||
if (IsLoaded)
|
||||
apply(entry);
|
||||
}
|
||||
|
||||
private void applyTransforms(int randomSeed)
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
apply(Entry);
|
||||
}
|
||||
|
||||
private void apply(HitExplosionEntry? entry)
|
||||
{
|
||||
if (entry == null)
|
||||
return;
|
||||
|
||||
ApplyTransformsAt(double.MinValue, true);
|
||||
ClearTransforms(true);
|
||||
|
||||
const double duration = 400;
|
||||
|
||||
// we want our size to be very small so the glow dominates it.
|
||||
largeFaint.Size = new Vector2(0.8f);
|
||||
largeFaint
|
||||
.ResizeTo(largeFaint.Size * new Vector2(5, 1), duration, Easing.OutQuint)
|
||||
.FadeOut(duration * 2);
|
||||
|
||||
const float angle_variangle = 15; // should be less than 45
|
||||
directionalGlow1.Rotation = StatelessRNG.NextSingle(-angle_variangle, angle_variangle, randomSeed, 4);
|
||||
directionalGlow2.Rotation = StatelessRNG.NextSingle(-angle_variangle, angle_variangle, randomSeed, 5);
|
||||
|
||||
this.FadeInFromZero(50).Then().FadeOut(duration, Easing.Out).Expire();
|
||||
}
|
||||
|
||||
private void setColour(Color4 objectColour)
|
||||
{
|
||||
const float roundness = 100;
|
||||
|
||||
largeFaint.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = Interpolation.ValueAt(0.1f, objectColour, Color4.White, 0, 1).Opacity(0.3f),
|
||||
Roundness = 160,
|
||||
Radius = 200,
|
||||
};
|
||||
|
||||
smallFaint.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = Interpolation.ValueAt(0.6f, objectColour, Color4.White, 0, 1),
|
||||
Roundness = 20,
|
||||
Radius = 50,
|
||||
};
|
||||
|
||||
directionalGlow1.EdgeEffect = directionalGlow2.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = Interpolation.ValueAt(0.4f, objectColour, Color4.White, 0, 1),
|
||||
Roundness = roundness,
|
||||
Radius = 40,
|
||||
};
|
||||
(skinnableExplosion.Drawable as IHitExplosion)?.Animate(entry);
|
||||
LifetimeEnd = skinnableExplosion.Drawable.LatestTransformEndTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Pooling;
|
||||
using osu.Game.Rulesets.Objects.Pooling;
|
||||
|
||||
@@ -14,6 +15,8 @@ namespace osu.Game.Rulesets.Catch.UI
|
||||
|
||||
public HitExplosionContainer()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
AddInternal(pool = new DrawablePool<HitExplosion>(10));
|
||||
}
|
||||
|
||||
|
||||
@@ -2,24 +2,42 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Graphics.Performance;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osuTK.Graphics;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
public class HitExplosionEntry : LifetimeEntry
|
||||
{
|
||||
public readonly float Position;
|
||||
public readonly float Scale;
|
||||
public readonly Color4 ObjectColour;
|
||||
public readonly int RNGSeed;
|
||||
/// <summary>
|
||||
/// The judgement result that triggered this explosion.
|
||||
/// </summary>
|
||||
public JudgementResult JudgementResult { get; }
|
||||
|
||||
public HitExplosionEntry(double startTime, float position, float scale, Color4 objectColour, int rngSeed)
|
||||
/// <summary>
|
||||
/// The hitobject which triggered this explosion.
|
||||
/// </summary>
|
||||
public CatchHitObject HitObject => (CatchHitObject)JudgementResult.HitObject;
|
||||
|
||||
/// <summary>
|
||||
/// The accent colour of the object caught.
|
||||
/// </summary>
|
||||
public Color4 ObjectColour { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The position at which the object was caught.
|
||||
/// </summary>
|
||||
public float Position { get; }
|
||||
|
||||
public HitExplosionEntry(double startTime, JudgementResult judgementResult, Color4 objectColour, float position)
|
||||
{
|
||||
LifetimeStart = startTime;
|
||||
Position = position;
|
||||
Scale = scale;
|
||||
JudgementResult = judgementResult;
|
||||
ObjectColour = objectColour;
|
||||
RNGSeed = rngSeed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// 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 enable
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Common interface for all hit explosion skinnables.
|
||||
/// </summary>
|
||||
public interface IHitExplosion
|
||||
{
|
||||
/// <summary>
|
||||
/// Begins animating this <see cref="IHitExplosion"/>.
|
||||
/// </summary>
|
||||
void Animate(HitExplosionEntry entry);
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Edit;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
@@ -101,27 +102,27 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override float GetBeatSnapDistanceAt(double referenceTime)
|
||||
public override float GetBeatSnapDistanceAt(HitObject referenceObject)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override float DurationToDistance(double referenceTime, double duration)
|
||||
public override float DurationToDistance(HitObject referenceObject, double duration)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override double DistanceToDuration(double referenceTime, float distance)
|
||||
public override double DistanceToDuration(HitObject referenceObject, float distance)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override double GetSnappedDurationFromDistance(double referenceTime, float distance)
|
||||
public override double GetSnappedDurationFromDistance(HitObject referenceObject, float distance)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
public override float GetSnappedDistanceFromDistance(double referenceTime, float distance)
|
||||
public override float GetSnappedDistanceFromDistance(HitObject referenceObject, float distance)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
|
||||
@@ -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 System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Edit.Compose;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests.Editor
|
||||
{
|
||||
public class TestSceneManiaComposeScreen : EditorClockTestScene
|
||||
{
|
||||
[Resolved]
|
||||
private SkinManager skins { get; set; }
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("setup compose screen", () =>
|
||||
{
|
||||
var editorBeatmap = new EditorBeatmap(new ManiaBeatmap(new StageDefinition { Columns = 4 }))
|
||||
{
|
||||
BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo },
|
||||
};
|
||||
|
||||
Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap);
|
||||
|
||||
Child = new DependencyProvidingContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
CachedDependencies = new (Type, object)[]
|
||||
{
|
||||
(typeof(EditorBeatmap), editorBeatmap),
|
||||
(typeof(IBeatSnapProvider), editorBeatmap),
|
||||
},
|
||||
Child = new ComposeScreen { State = { Value = Visibility.Visible } },
|
||||
};
|
||||
});
|
||||
|
||||
AddUntilStep("wait for composer", () => this.ChildrenOfType<HitObjectComposer>().SingleOrDefault()?.IsLoaded == true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDefaultSkin()
|
||||
{
|
||||
AddStep("set default skin", () => skins.CurrentSkinInfo.Value = SkinInfo.Default);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestLegacySkin()
|
||||
{
|
||||
AddStep("set legacy skin", () => skins.CurrentSkinInfo.Value = DefaultLegacySkin.Info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
@@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
{
|
||||
c.Add(CreateHitObject().With(h =>
|
||||
{
|
||||
h.HitObject.StartTime = START_TIME;
|
||||
h.HitObject.StartTime = Time.Current + 5000;
|
||||
h.AccentColour.Value = Color4.Orange;
|
||||
}));
|
||||
})
|
||||
@@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
{
|
||||
c.Add(CreateHitObject().With(h =>
|
||||
{
|
||||
h.HitObject.StartTime = START_TIME;
|
||||
h.HitObject.StartTime = Time.Current + 5000;
|
||||
h.AccentColour.Value = Color4.Orange;
|
||||
}));
|
||||
})
|
||||
|
||||
@@ -19,8 +19,6 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
/// </summary>
|
||||
public abstract class ManiaSkinnableTestScene : SkinnableTestScene
|
||||
{
|
||||
protected const double START_TIME = 1000000000;
|
||||
|
||||
[Cached(Type = typeof(IScrollingInfo))]
|
||||
private readonly TestScrollingInfo scrollingInfo = new TestScrollingInfo();
|
||||
|
||||
@@ -55,27 +53,8 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||
public readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
|
||||
|
||||
IBindable<ScrollingDirection> IScrollingInfo.Direction => Direction;
|
||||
IBindable<double> IScrollingInfo.TimeRange { get; } = new Bindable<double>(1000);
|
||||
IScrollAlgorithm IScrollingInfo.Algorithm { get; } = new ZeroScrollAlgorithm();
|
||||
}
|
||||
|
||||
private class ZeroScrollAlgorithm : IScrollAlgorithm
|
||||
{
|
||||
public double GetDisplayStartTime(double originTime, float offset, double timeRange, float scrollLength)
|
||||
=> double.MinValue;
|
||||
|
||||
public float GetLength(double startTime, double endTime, double timeRange, float scrollLength)
|
||||
=> scrollLength;
|
||||
|
||||
public float PositionAt(double time, double currentTime, double timeRange, float scrollLength)
|
||||
=> (float)((time - START_TIME) / timeRange) * scrollLength;
|
||||
|
||||
public double TimeAt(float position, double currentTime, double timeRange, float scrollLength)
|
||||
=> 0;
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
}
|
||||
IBindable<double> IScrollingInfo.TimeRange { get; } = new Bindable<double>(5000);
|
||||
IScrollAlgorithm IScrollingInfo.Algorithm { get; } = new ConstantScrollAlgorithm();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
@@ -58,7 +59,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
AddStep("Hold key", () =>
|
||||
{
|
||||
clock.CurrentTime = 0;
|
||||
note.OnPressed(ManiaAction.Key1);
|
||||
note.OnPressed(new KeyBindingPressEvent<ManiaAction>(GetContainingInputManager().CurrentState, ManiaAction.Key1));
|
||||
});
|
||||
AddStep("progress time", () => clock.CurrentTime = 500);
|
||||
AddAssert("head is visible", () => note.Head.Alpha == 1);
|
||||
|
||||
@@ -388,7 +388,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
},
|
||||
};
|
||||
|
||||
beatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SpeedMultiplier = 0.1f });
|
||||
beatmap.ControlPointInfo.Add(0, new EffectControlPoint { ScrollSpeed = 0.1f });
|
||||
}
|
||||
|
||||
AddStep("load player", () =>
|
||||
|
||||
@@ -148,7 +148,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
},
|
||||
});
|
||||
|
||||
Beatmap.Value.Beatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SpeedMultiplier = 0.1f });
|
||||
Beatmap.Value.Beatmap.ControlPointInfo.Add(0, new EffectControlPoint { ScrollSpeed = 0.1f });
|
||||
|
||||
var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } });
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
@@ -13,6 +14,10 @@ using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Configuration;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
@@ -22,14 +27,65 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
[Resolved]
|
||||
private RulesetConfigCache configCache { get; set; }
|
||||
|
||||
private readonly Bindable<bool> configTimingBasedNoteColouring = new Bindable<bool>();
|
||||
private Bindable<bool> configTimingBasedNoteColouring;
|
||||
|
||||
protected override void LoadComplete()
|
||||
private ManualClock clock;
|
||||
private DrawableManiaRuleset drawableRuleset;
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("setup hierarchy", () => Child = new Container
|
||||
{
|
||||
Clock = new FramedClock(clock = new ManualClock()),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Children = new[]
|
||||
{
|
||||
drawableRuleset = (DrawableManiaRuleset)Ruleset.Value.CreateInstance().CreateDrawableRulesetWith(createTestBeatmap())
|
||||
}
|
||||
});
|
||||
AddStep("retrieve config bindable", () =>
|
||||
{
|
||||
var config = (ManiaRulesetConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance());
|
||||
configTimingBasedNoteColouring = config.GetBindable<bool>(ManiaRulesetSetting.TimingBasedNoteColouring);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSimple()
|
||||
{
|
||||
AddStep("enable", () => configTimingBasedNoteColouring.Value = true);
|
||||
AddStep("disable", () => configTimingBasedNoteColouring.Value = false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestToggleOffScreen()
|
||||
{
|
||||
AddStep("enable", () => configTimingBasedNoteColouring.Value = true);
|
||||
|
||||
seekTo(10000);
|
||||
AddStep("disable", () => configTimingBasedNoteColouring.Value = false);
|
||||
seekTo(0);
|
||||
AddAssert("all notes not coloured", () => this.ChildrenOfType<DrawableNote>().All(note => note.Colour == Colour4.White));
|
||||
|
||||
seekTo(10000);
|
||||
AddStep("enable again", () => configTimingBasedNoteColouring.Value = true);
|
||||
seekTo(0);
|
||||
AddAssert("some notes coloured", () => this.ChildrenOfType<DrawableNote>().Any(note => note.Colour != Colour4.White));
|
||||
}
|
||||
|
||||
private void seekTo(double time)
|
||||
{
|
||||
AddStep($"seek to {time}", () => clock.CurrentTime = time);
|
||||
AddUntilStep("wait for seek", () => Precision.AlmostEquals(drawableRuleset.FrameStableClock.CurrentTime, time, 1));
|
||||
}
|
||||
|
||||
private ManiaBeatmap createTestBeatmap()
|
||||
{
|
||||
const double beat_length = 500;
|
||||
|
||||
var ruleset = new ManiaRuleset();
|
||||
|
||||
var beatmap = new ManiaBeatmap(new StageDefinition { Columns = 1 })
|
||||
{
|
||||
HitObjects =
|
||||
@@ -45,7 +101,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
new Note { StartTime = beat_length }
|
||||
},
|
||||
ControlPointInfo = new ControlPointInfo(),
|
||||
BeatmapInfo = { Ruleset = ruleset.RulesetInfo },
|
||||
BeatmapInfo = { Ruleset = Ruleset.Value },
|
||||
};
|
||||
|
||||
foreach (var note in beatmap.HitObjects)
|
||||
@@ -57,24 +113,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
BeatLength = beat_length
|
||||
});
|
||||
|
||||
Child = new Container
|
||||
{
|
||||
Clock = new FramedClock(new ManualClock()),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Children = new[]
|
||||
{
|
||||
ruleset.CreateDrawableRulesetWith(beatmap)
|
||||
}
|
||||
};
|
||||
|
||||
var config = (ManiaRulesetConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance());
|
||||
config.BindWith(ManiaRulesetSetting.TimingBasedNoteColouring, configTimingBasedNoteColouring);
|
||||
|
||||
AddStep("Enable", () => configTimingBasedNoteColouring.Value = true);
|
||||
AddStep("Disable", () => configTimingBasedNoteColouring.Value = false);
|
||||
return beatmap;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<Import Project="..\osu.TestProject.props" />
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.2" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.0.0" />
|
||||
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
|
||||
@@ -42,8 +42,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
{
|
||||
IsForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo);
|
||||
|
||||
var roundedCircleSize = Math.Round(beatmap.BeatmapInfo.BaseDifficulty.CircleSize);
|
||||
var roundedOverallDifficulty = Math.Round(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);
|
||||
var roundedCircleSize = Math.Round(beatmap.Difficulty.CircleSize);
|
||||
var roundedOverallDifficulty = Math.Round(beatmap.Difficulty.OverallDifficulty);
|
||||
|
||||
if (IsForCurrentRuleset)
|
||||
{
|
||||
@@ -71,9 +71,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
originalTargetColumns = TargetColumns;
|
||||
}
|
||||
|
||||
public static int GetColumnCountForNonConvert(BeatmapInfo beatmap)
|
||||
public static int GetColumnCountForNonConvert(BeatmapInfo beatmapInfo)
|
||||
{
|
||||
var roundedCircleSize = Math.Round(beatmap.BaseDifficulty.CircleSize);
|
||||
var roundedCircleSize = Math.Round(beatmapInfo.BaseDifficulty.CircleSize);
|
||||
return (int)Math.Max(1, roundedCircleSize);
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
|
||||
protected override Beatmap<ManiaHitObject> ConvertBeatmap(IBeatmap original, CancellationToken cancellationToken)
|
||||
{
|
||||
BeatmapDifficulty difficulty = original.BeatmapInfo.BaseDifficulty;
|
||||
IBeatmapDifficultyInfo difficulty = original.Difficulty;
|
||||
|
||||
int seed = (int)MathF.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)MathF.Round(difficulty.ApproachRate);
|
||||
Random = new FastRandom(seed);
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
Debug.Assert(distanceData != null);
|
||||
|
||||
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);
|
||||
DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(hitObject.StartTime);
|
||||
DifficultyControlPoint difficultyPoint = hitObject.DifficultyControlPoint;
|
||||
|
||||
double beatLength;
|
||||
#pragma warning disable 618
|
||||
@@ -55,13 +55,13 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
#pragma warning restore 618
|
||||
beatLength = timingPoint.BeatLength * legacyDifficultyPoint.BpmMultiplier;
|
||||
else
|
||||
beatLength = timingPoint.BeatLength / difficultyPoint.SpeedMultiplier;
|
||||
beatLength = timingPoint.BeatLength / difficultyPoint.SliderVelocity;
|
||||
|
||||
SpanCount = repeatsData?.SpanCount() ?? 1;
|
||||
StartTime = (int)Math.Round(hitObject.StartTime);
|
||||
|
||||
// This matches stable's calculation.
|
||||
EndTime = (int)Math.Floor(StartTime + distanceData.Distance * beatLength * SpanCount * 0.01 / beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier);
|
||||
EndTime = (int)Math.Floor(StartTime + distanceData.Distance * beatLength * SpanCount * 0.01 / beatmap.Difficulty.SliderMultiplier);
|
||||
|
||||
SegmentDuration = (EndTime - StartTime) / SpanCount;
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
if (drainTime == 0)
|
||||
drainTime = 10000;
|
||||
|
||||
BeatmapDifficulty difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty;
|
||||
IBeatmapDifficultyInfo difficulty = OriginalBeatmap.Difficulty;
|
||||
conversionDifficulty = ((difficulty.DrainRate + Math.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + (double)OriginalBeatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15;
|
||||
conversionDifficulty = Math.Min(conversionDifficulty.Value, 12);
|
||||
|
||||
@@ -149,7 +149,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
{
|
||||
lowerBound ??= RandomStart;
|
||||
upperBound ??= TotalColumns;
|
||||
nextColumn ??= (_ => GetRandomColumn(lowerBound, upperBound));
|
||||
nextColumn ??= _ => GetRandomColumn(lowerBound, upperBound);
|
||||
|
||||
// Check for the initial column
|
||||
if (isValid(initialColumn))
|
||||
@@ -176,7 +176,19 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
|
||||
return initialColumn;
|
||||
|
||||
bool isValid(int column) => validation?.Invoke(column) != false && !patterns.Any(p => p.ColumnHasObject(column));
|
||||
bool isValid(int column)
|
||||
{
|
||||
if (validation?.Invoke(column) == false)
|
||||
return false;
|
||||
|
||||
foreach (var p in patterns)
|
||||
{
|
||||
if (p.ColumnHasObject(column))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -12,46 +12,68 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
|
||||
/// </summary>
|
||||
internal class Pattern
|
||||
{
|
||||
private readonly List<ManiaHitObject> hitObjects = new List<ManiaHitObject>();
|
||||
private List<ManiaHitObject> hitObjects;
|
||||
private HashSet<int> containedColumns;
|
||||
|
||||
/// <summary>
|
||||
/// All the hit objects contained in this pattern.
|
||||
/// </summary>
|
||||
public IEnumerable<ManiaHitObject> HitObjects => hitObjects;
|
||||
public IEnumerable<ManiaHitObject> HitObjects => hitObjects ?? Enumerable.Empty<ManiaHitObject>();
|
||||
|
||||
/// <summary>
|
||||
/// Check whether a column of this patterns contains a hit object.
|
||||
/// </summary>
|
||||
/// <param name="column">The column index.</param>
|
||||
/// <returns>Whether the column with index <paramref name="column"/> contains a hit object.</returns>
|
||||
public bool ColumnHasObject(int column) => hitObjects.Exists(h => h.Column == column);
|
||||
public bool ColumnHasObject(int column) => containedColumns?.Contains(column) == true;
|
||||
|
||||
/// <summary>
|
||||
/// Amount of columns taken up by hit objects in this pattern.
|
||||
/// </summary>
|
||||
public int ColumnWithObjects => HitObjects.GroupBy(h => h.Column).Count();
|
||||
public int ColumnWithObjects => containedColumns?.Count ?? 0;
|
||||
|
||||
/// <summary>
|
||||
/// Adds a hit object to this pattern.
|
||||
/// </summary>
|
||||
/// <param name="hitObject">The hit object to add.</param>
|
||||
public void Add(ManiaHitObject hitObject) => hitObjects.Add(hitObject);
|
||||
public void Add(ManiaHitObject hitObject)
|
||||
{
|
||||
prepareStorage();
|
||||
|
||||
hitObjects.Add(hitObject);
|
||||
containedColumns.Add(hitObject.Column);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies hit object from another pattern to this one.
|
||||
/// </summary>
|
||||
/// <param name="other">The other pattern.</param>
|
||||
public void Add(Pattern other) => hitObjects.AddRange(other.HitObjects);
|
||||
public void Add(Pattern other)
|
||||
{
|
||||
prepareStorage();
|
||||
|
||||
if (other.hitObjects != null)
|
||||
{
|
||||
hitObjects.AddRange(other.hitObjects);
|
||||
|
||||
foreach (var h in other.hitObjects)
|
||||
containedColumns.Add(h.Column);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears this pattern, removing all hit objects.
|
||||
/// </summary>
|
||||
public void Clear() => hitObjects.Clear();
|
||||
public void Clear()
|
||||
{
|
||||
hitObjects?.Clear();
|
||||
containedColumns?.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a hit object from this pattern.
|
||||
/// </summary>
|
||||
/// <param name="hitObject">The hit object to remove.</param>
|
||||
public bool Remove(ManiaHitObject hitObject) => hitObjects.Remove(hitObject);
|
||||
private void prepareStorage()
|
||||
{
|
||||
hitObjects ??= new List<ManiaHitObject>();
|
||||
containedColumns ??= new HashSet<int>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,15 +41,14 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
return new ManiaDifficultyAttributes { Mods = mods, Skills = skills };
|
||||
|
||||
HitWindows hitWindows = new ManiaHitWindows();
|
||||
hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);
|
||||
hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty);
|
||||
|
||||
return new ManiaDifficultyAttributes
|
||||
{
|
||||
StarRating = skills[0].DifficultyValue() * star_scaling_factor,
|
||||
Mods = mods,
|
||||
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future
|
||||
GreatHitWindow = (int)Math.Ceiling(getHitWindow300(mods) / clockRate),
|
||||
ScoreMultiplier = getScoreMultiplier(beatmap, mods),
|
||||
GreatHitWindow = Math.Ceiling(getHitWindow300(mods) / clockRate),
|
||||
ScoreMultiplier = getScoreMultiplier(mods),
|
||||
MaxCombo = beatmap.HitObjects.Sum(h => h is HoldNote ? 2 : 1),
|
||||
Skills = skills
|
||||
};
|
||||
@@ -70,7 +69,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
|
||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[]
|
||||
{
|
||||
new Strain(mods, ((ManiaBeatmap)beatmap).TotalColumns)
|
||||
new Strain(mods, ((ManiaBeatmap)Beatmap).TotalColumns)
|
||||
};
|
||||
|
||||
protected override Mod[] DifficultyAdjustmentMods
|
||||
@@ -138,7 +137,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
}
|
||||
}
|
||||
|
||||
private double getScoreMultiplier(IBeatmap beatmap, Mod[] mods)
|
||||
private double getScoreMultiplier(Mod[] mods)
|
||||
{
|
||||
double scoreMultiplier = 1;
|
||||
|
||||
@@ -154,7 +153,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
||||
}
|
||||
}
|
||||
|
||||
var maniaBeatmap = (ManiaBeatmap)beatmap;
|
||||
var maniaBeatmap = (ManiaBeatmap)Beatmap;
|
||||
int diff = maniaBeatmap.TotalColumns - maniaBeatmap.OriginalTotalColumns;
|
||||
|
||||
if (diff > 0)
|
||||
|
||||
@@ -10,7 +10,7 @@ using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
||||
{
|
||||
public class Strain : StrainSkill
|
||||
public class Strain : StrainDecaySkill
|
||||
{
|
||||
private const double individual_decay_base = 0.125;
|
||||
private const double overall_decay_base = 0.30;
|
||||
@@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
||||
return individualStrain + overallStrain - CurrentStrain;
|
||||
}
|
||||
|
||||
protected override double GetPeakStrain(double offset)
|
||||
protected override double CalculateInitialStrain(double offset)
|
||||
=> applyDecay(individualStrain, offset - Previous[0].StartTime, individual_decay_base)
|
||||
+ applyDecay(overallStrain, offset - Previous[0].StartTime, overall_decay_base);
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Screens.Edit.Setup;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Edit.Setup
|
||||
{
|
||||
public class ManiaSetupSection : RulesetSetupSection
|
||||
{
|
||||
private LabelledSwitchButton specialStyle;
|
||||
|
||||
public ManiaSetupSection()
|
||||
: base(new ManiaRuleset().RulesetInfo)
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
specialStyle = new LabelledSwitchButton
|
||||
{
|
||||
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.",
|
||||
Current = { Value = Beatmap.BeatmapInfo.SpecialStyle }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
specialStyle.Current.BindValueChanged(_ => updateBeatmap());
|
||||
}
|
||||
|
||||
private void updateBeatmap()
|
||||
{
|
||||
Beatmap.BeatmapInfo.SpecialStyle = specialStyle.Current.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,9 +13,9 @@ namespace osu.Game.Rulesets.Mania
|
||||
{
|
||||
private FilterCriteria.OptionalRange<float> keys;
|
||||
|
||||
public bool Matches(BeatmapInfo beatmap)
|
||||
public bool Matches(BeatmapInfo beatmapInfo)
|
||||
{
|
||||
return !keys.HasFilter || (beatmap.RulesetID == new ManiaRuleset().LegacyID && keys.IsInRange(ManiaBeatmapConverter.GetColumnCountForNonConvert(beatmap)));
|
||||
return !keys.HasFilter || (beatmapInfo.RulesetID == new ManiaRuleset().LegacyID && keys.IsInRange(ManiaBeatmapConverter.GetColumnCountForNonConvert(beatmapInfo)));
|
||||
}
|
||||
|
||||
public bool TryParseCustomKeywordCriteria(string key, Operator op, string value)
|
||||
|
||||
@@ -27,11 +27,13 @@ using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Configuration;
|
||||
using osu.Game.Rulesets.Mania.Difficulty;
|
||||
using osu.Game.Rulesets.Mania.Edit;
|
||||
using osu.Game.Rulesets.Mania.Edit.Setup;
|
||||
using osu.Game.Rulesets.Mania.Scoring;
|
||||
using osu.Game.Rulesets.Mania.Skinning.Legacy;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Edit.Setup;
|
||||
using osu.Game.Screens.Ranking.Statistics;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania
|
||||
@@ -390,6 +392,8 @@ namespace osu.Game.Rulesets.Mania
|
||||
{
|
||||
return new ManiaFilterCriteria();
|
||||
}
|
||||
|
||||
public override RulesetSetupSection CreateEditorSetupSection() => new ManiaSetupSection();
|
||||
}
|
||||
|
||||
public enum PlayfieldType
|
||||
|
||||
@@ -7,6 +7,7 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Mania.Skinning.Default;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
@@ -253,12 +254,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
HoldBrokenTime = Time.Current;
|
||||
}
|
||||
|
||||
public bool OnPressed(ManiaAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<ManiaAction> e)
|
||||
{
|
||||
if (AllJudged)
|
||||
return false;
|
||||
|
||||
if (action != Action.Value)
|
||||
if (e.Action != Action.Value)
|
||||
return false;
|
||||
|
||||
// do not run any of this logic when rewinding, as it inverts order of presses/releases.
|
||||
@@ -275,9 +276,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
return false;
|
||||
|
||||
beginHoldAt(Time.Current - Head.HitObject.StartTime);
|
||||
Head.UpdateResult();
|
||||
|
||||
return true;
|
||||
return Head.UpdateResult();
|
||||
}
|
||||
|
||||
private void beginHoldAt(double timeOffset)
|
||||
@@ -289,12 +289,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
isHitting.Value = true;
|
||||
}
|
||||
|
||||
public void OnReleased(ManiaAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
|
||||
{
|
||||
if (AllJudged)
|
||||
return;
|
||||
|
||||
if (action != Action.Value)
|
||||
if (e.Action != Action.Value)
|
||||
return;
|
||||
|
||||
// do not run any of this logic when rewinding, as it inverts order of presses/releases.
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
@@ -25,7 +26,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
Origin = Anchor.TopCentre;
|
||||
}
|
||||
|
||||
public void UpdateResult() => base.UpdateResult(true);
|
||||
public bool UpdateResult() => base.UpdateResult(true);
|
||||
|
||||
protected override void UpdateInitialTransforms()
|
||||
{
|
||||
@@ -43,9 +44,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
// it will be hidden along with its parenting hold note when required.
|
||||
}
|
||||
|
||||
public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note
|
||||
public override bool OnPressed(KeyBindingPressEvent<ManiaAction> e) => false; // Handled by the hold note
|
||||
|
||||
public override void OnReleased(ManiaAction action)
|
||||
public override void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using System.Diagnostics;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
@@ -68,9 +69,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
});
|
||||
}
|
||||
|
||||
public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note
|
||||
public override bool OnPressed(KeyBindingPressEvent<ManiaAction> e) => false; // Handled by the hold note
|
||||
|
||||
public override void OnReleased(ManiaAction action)
|
||||
public override void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
@@ -29,11 +28,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
[Resolved(canBeNull: true)]
|
||||
private ManiaPlayfield playfield { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the samples that are played by this object during gameplay.
|
||||
/// </summary>
|
||||
public ISampleInfo[] GetGameplaySamples() => Samples.Samples;
|
||||
|
||||
protected override float SamplePlaybackPosition
|
||||
{
|
||||
get
|
||||
|
||||
@@ -6,6 +6,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Mania.Configuration;
|
||||
@@ -66,6 +67,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
StartTimeBindable.BindValueChanged(_ => updateSnapColour(), true);
|
||||
}
|
||||
|
||||
protected override void OnApply()
|
||||
{
|
||||
base.OnApply();
|
||||
updateSnapColour();
|
||||
}
|
||||
|
||||
protected override void OnDirectionChanged(ValueChangedEvent<ScrollingDirection> e)
|
||||
{
|
||||
base.OnDirectionChanged(e);
|
||||
@@ -91,9 +98,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
ApplyResult(r => r.Type = result);
|
||||
}
|
||||
|
||||
public virtual bool OnPressed(ManiaAction action)
|
||||
public virtual bool OnPressed(KeyBindingPressEvent<ManiaAction> e)
|
||||
{
|
||||
if (action != Action.Value)
|
||||
if (e.Action != Action.Value)
|
||||
return false;
|
||||
|
||||
if (CheckHittable?.Invoke(this, Time.Current) == false)
|
||||
@@ -102,7 +109,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
return UpdateResult(true);
|
||||
}
|
||||
|
||||
public virtual void OnReleased(ManiaAction action)
|
||||
public virtual void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
/// </summary>
|
||||
private double tickSpacing = 50;
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ SliderTickRate:1
|
||||
|
||||
[TimingPoints]
|
||||
0,500,4,1,0,100,1,0
|
||||
10000,-150,4,1,0,100,1,0
|
||||
|
||||
[HitObjects]
|
||||
51,192,500,128,0,1500:1:0:0:0:
|
||||
|
||||
@@ -7,6 +7,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
@@ -76,9 +77,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnPressed(ManiaAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<ManiaAction> e)
|
||||
{
|
||||
if (action == Column.Action.Value)
|
||||
if (e.Action == Column.Action.Value)
|
||||
{
|
||||
light.FadeIn();
|
||||
light.ScaleTo(Vector2.One);
|
||||
@@ -87,12 +88,12 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(ManiaAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
|
||||
{
|
||||
// Todo: Should be 400 * 100 / CurrentBPM
|
||||
const double animation_length = 250;
|
||||
|
||||
if (action == Column.Action.Value)
|
||||
if (e.Action == Column.Action.Value)
|
||||
{
|
||||
light.FadeTo(0, animation_length);
|
||||
light.ScaleTo(new Vector2(1, 0), animation_length);
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
// 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.Textures;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
{
|
||||
public class LegacyHoldNoteHeadPiece : LegacyNotePiece
|
||||
{
|
||||
protected override Texture GetTexture(ISkinSource skin)
|
||||
protected override Drawable GetAnimation(ISkinSource skin)
|
||||
{
|
||||
// TODO: Should fallback to the head from default legacy skin instead of note.
|
||||
return GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteHeadImage)
|
||||
?? GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.NoteImage);
|
||||
return GetAnimationFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteHeadImage)
|
||||
?? GetAnimationFromLookup(skin, LegacyManiaSkinConfigurationLookups.NoteImage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
@@ -18,12 +18,12 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
: new ValueChangedEvent<ScrollingDirection>(ScrollingDirection.Up, ScrollingDirection.Up));
|
||||
}
|
||||
|
||||
protected override Texture GetTexture(ISkinSource skin)
|
||||
protected override Drawable GetAnimation(ISkinSource skin)
|
||||
{
|
||||
// TODO: Should fallback to the head from default legacy skin instead of note.
|
||||
return GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteTailImage)
|
||||
?? GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteHeadImage)
|
||||
?? GetTextureFromLookup(skin, LegacyManiaSkinConfigurationLookups.NoteImage);
|
||||
return GetAnimationFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteTailImage)
|
||||
?? GetAnimationFromLookup(skin, LegacyManiaSkinConfigurationLookups.HoldNoteHeadImage)
|
||||
?? GetAnimationFromLookup(skin, LegacyManiaSkinConfigurationLookups.NoteImage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Skinning;
|
||||
@@ -86,9 +87,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnPressed(ManiaAction action)
|
||||
public bool OnPressed(KeyBindingPressEvent<ManiaAction> e)
|
||||
{
|
||||
if (action == column.Action.Value)
|
||||
if (e.Action == column.Action.Value)
|
||||
{
|
||||
upSprite.FadeTo(0);
|
||||
downSprite.FadeTo(1);
|
||||
@@ -97,9 +98,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(ManiaAction action)
|
||||
public void OnReleased(KeyBindingReleaseEvent<ManiaAction> e)
|
||||
{
|
||||
if (action == column.Action.Value)
|
||||
if (e.Action == column.Action.Value)
|
||||
{
|
||||
upSprite.Delay(LegacyHitExplosion.FADE_IN_DURATION).FadeTo(1);
|
||||
downSprite.Delay(LegacyHitExplosion.FADE_IN_DURATION).FadeTo(0);
|
||||
|
||||