diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index 84673fe2ba..b3f7c67c51 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -2,12 +2,6 @@
"version": 1,
"isRoot": true,
"tools": {
- "cake.tool": {
- "version": "0.35.0",
- "commands": [
- "dotnet-cake"
- ]
- },
"dotnet-format": {
"version": "3.1.37601",
"commands": [
@@ -20,14 +14,14 @@
"jb"
]
},
- "nvika": {
- "version": "2.0.0",
+ "smoogipoo.nvika": {
+ "version": "1.0.1",
"commands": [
"nvika"
]
},
"codefilesanity": {
- "version": "15.0.0",
+ "version": "0.0.36",
"commands": [
"CodeFileSanity"
]
@@ -39,4 +33,4 @@
]
}
}
-}
+}
\ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000000..ed3e99cb61
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,93 @@
+on: [push, pull_request]
+name: Continuous Integration
+
+jobs:
+ test:
+ name: Test
+ runs-on: ${{matrix.os.fullname}}
+ env:
+ OSU_EXECUTION_MODE: ${{matrix.threadingMode}}
+ strategy:
+ fail-fast: false
+ matrix:
+ os:
+ - { prettyname: Windows, fullname: windows-latest }
+ - { prettyname: macOS, fullname: macos-latest }
+ - { prettyname: Linux, fullname: ubuntu-latest }
+ threadingMode: ['SingleThread', 'MultiThreaded']
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+
+ - name: Install .NET 5.0.x
+ uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: "5.0.x"
+
+ # FIXME: libavformat is not included in Ubuntu. Let's fix that.
+ # https://github.com/ppy/osu-framework/issues/4349
+ # Remove this once https://github.com/actions/virtual-environments/issues/3306 has been resolved.
+ - name: Install libavformat-dev
+ if: ${{matrix.os.fullname == 'ubuntu-latest'}}
+ run: |
+ sudo apt-get update && \
+ sudo apt-get -y install libavformat-dev
+
+ - name: Compile
+ run: dotnet build -c Debug -warnaserror osu.Desktop.slnf
+
+ - name: Test
+ run: dotnet test $pwd/*.Tests/bin/Debug/*/*.Tests.dll --logger "trx;LogFileName=TestResults-${{matrix.os.prettyname}}-${{matrix.threadingMode}}.trx"
+ shell: pwsh
+
+ # Attempt to upload results even if test fails.
+ # https://docs.github.com/en/actions/reference/context-and-expression-syntax-for-github-actions#always
+ - name: Upload Test Results
+ uses: actions/upload-artifact@v2
+ if: ${{ always() }}
+ with:
+ name: osu-test-results-${{matrix.os.prettyname}}-${{matrix.threadingMode}}
+ path: ${{github.workspace}}/TestResults/TestResults-${{matrix.os.prettyname}}-${{matrix.threadingMode}}.trx
+
+ inspect-code:
+ name: Code Quality
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v2
+
+ # FIXME: Tools won't run in .NET 5.0 unless you install 3.1.x LTS side by side.
+ # https://itnext.io/how-to-support-multiple-net-sdks-in-github-actions-workflows-b988daa884e
+ - name: Install .NET 3.1.x LTS
+ uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: "3.1.x"
+
+ - name: Install .NET 5.0.x
+ uses: actions/setup-dotnet@v1
+ with:
+ dotnet-version: "5.0.x"
+
+ - name: Restore Tools
+ run: dotnet tool restore
+
+ - name: Restore Packages
+ run: dotnet restore
+
+ - name: CodeFileSanity
+ 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
+
+ # Temporarily disabled due to test failures, but it won't work anyway until the tool is upgraded.
+ # - name: .NET Format (Dry Run)
+ # run: dotnet format --dry-run --check
+
+ - name: InspectCode
+ run: dotnet jb inspectcode $(pwd)/osu.Desktop.slnf --output=$(pwd)/inspectcodereport.xml --cachesDir=$(pwd)/inspectcode --verbosity=WARN
+
+ - name: NVika
+ run: dotnet nvika parsereport "${{github.workspace}}/inspectcodereport.xml" --treatwarningsaserrors
diff --git a/.github/workflows/report-nunit.yml b/.github/workflows/report-nunit.yml
new file mode 100644
index 0000000000..381d2d49c5
--- /dev/null
+++ b/.github/workflows/report-nunit.yml
@@ -0,0 +1,31 @@
+# This is a workaround to allow PRs to report their coverage. This will run inside the base repository.
+# See:
+# * https://github.com/dorny/test-reporter#recommended-setup-for-public-repositories
+# * https://docs.github.com/en/actions/reference/authentication-in-a-workflow#permissions-for-the-github_token
+name: Annotate CI run with test results
+on:
+ workflow_run:
+ workflows: ["Continuous Integration"]
+ types:
+ - completed
+jobs:
+ annotate:
+ name: Annotate CI run with test results
+ runs-on: ubuntu-latest
+ if: ${{ github.event.workflow_run.conclusion != 'cancelled' }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os:
+ - { prettyname: Windows }
+ - { prettyname: macOS }
+ - { prettyname: Linux }
+ threadingMode: ['SingleThread', 'MultiThreaded']
+ steps:
+ - name: Annotate CI run with test results
+ uses: dorny/test-reporter@v1.4.2
+ with:
+ artifact: osu-test-results-${{matrix.os.prettyname}}-${{matrix.threadingMode}}
+ name: Test Results (${{matrix.os.prettyname}}, ${{matrix.threadingMode}})
+ path: "*.trx"
+ reporter: dotnet-trx
diff --git a/.vscode/launch.json b/.vscode/launch.json
index afd997f91d..1b590008cd 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -113,20 +113,6 @@
"cwd": "${workspaceRoot}",
"preLaunchTask": "Build benchmarks",
"console": "internalConsole"
- },
- {
- "name": "Cake: Debug Script",
- "type": "coreclr",
- "request": "launch",
- "program": "${workspaceRoot}/build/tools/Cake.CoreCLR/0.30.0/Cake.dll",
- "args": [
- "${workspaceRoot}/build/build.cake",
- "--debug",
- "--verbosity=diagnostic"
- ],
- "cwd": "${workspaceRoot}/build",
- "stopAtEntry": true,
- "externalConsole": false
}
]
}
diff --git a/InspectCode.ps1 b/InspectCode.ps1
index 6ed935fdbb..8316f48ff3 100644
--- a/InspectCode.ps1
+++ b/InspectCode.ps1
@@ -1,27 +1,11 @@
-[CmdletBinding()]
-Param(
- [string]$Target,
- [string]$Configuration,
- [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")]
- [string]$Verbosity,
- [switch]$ShowDescription,
- [Alias("WhatIf", "Noop")]
- [switch]$DryRun,
- [Parameter(Position = 0, Mandatory = $false, ValueFromRemainingArguments = $true)]
- [string[]]$ScriptArgs
-)
-
-# Build Cake arguments
-$cakeArguments = "";
-if ($Target) { $cakeArguments += "-target=$Target" }
-if ($Configuration) { $cakeArguments += "-configuration=$Configuration" }
-if ($Verbosity) { $cakeArguments += "-verbosity=$Verbosity" }
-if ($ShowDescription) { $cakeArguments += "-showdescription" }
-if ($DryRun) { $cakeArguments += "-dryrun" }
-if ($Experimental) { $cakeArguments += "-experimental" }
-$cakeArguments += $ScriptArgs
-
dotnet tool restore
-dotnet cake ./build/InspectCode.cake --bootstrap
-dotnet cake ./build/InspectCode.cake $cakeArguments
-exit $LASTEXITCODE
\ No newline at end of file
+
+# Temporarily disabled until the tool is upgraded to 5.0.
+ # The version specified in .config/dotnet-tools.json (3.1.37601) won't run on .NET hosts >=5.0.7.
+ # - cmd: dotnet format --dry-run --check
+
+dotnet CodeFileSanity
+dotnet jb inspectcode "osu.Desktop.slnf" --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN
+dotnet nvika parsereport "inspectcodereport.xml" --treatwarningsaserrors
+
+exit $LASTEXITCODE
diff --git a/InspectCode.sh b/InspectCode.sh
new file mode 100755
index 0000000000..cf2bc18175
--- /dev/null
+++ b/InspectCode.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+dotnet tool restore
+dotnet CodeFileSanity
+dotnet jb inspectcode "osu.Desktop.slnf" --output="inspectcodereport.xml" --caches-home="inspectcode" --verbosity=WARN
+dotnet nvika parsereport "inspectcodereport.xml" --treatwarningsaserrors
diff --git a/appveyor.yml b/appveyor.yml
index 845751ef07..5be73f9875 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,28 +1,27 @@
clone_depth: 1
version: '{branch}-{build}'
image: Visual Studio 2019
+cache:
+ - '%LOCALAPPDATA%\NuGet\v3-cache -> appveyor.yml'
+
dotnet_csproj:
patch: true
file: 'osu.Game\osu.Game.csproj' # Use wildcard when it's able to exclude Xamarin projects
version: '0.0.{build}'
-cache:
- - '%LOCALAPPDATA%\NuGet\v3-cache -> appveyor.yml'
+
before_build:
- - ps: dotnet --info # Useful when version mismatch between CI and local
- - ps: nuget restore -verbosity quiet # Only nuget.exe knows both new (.NET Core) and old (Xamarin) projects
+ - cmd: dotnet --info # Useful when version mismatch between CI and local
+ - cmd: nuget restore -verbosity quiet # Only nuget.exe knows both new (.NET Core) and old (Xamarin) projects
+
build:
project: osu.sln
parallel: true
verbosity: minimal
publish_nuget: true
+
after_build:
- - ps: dotnet tool restore
-
- # Temporarily disabled until the tool is upgraded to 5.0.
- # The version specified in .config/dotnet-tools.json (3.1.37601) won't run on .NET hosts >=5.0.7.
- # - ps: dotnet format --dry-run --check
-
- ps: .\InspectCode.ps1
+
test:
assemblies:
except:
diff --git a/build/Desktop.proj b/build/Desktop.proj
deleted file mode 100644
index b1c6b065e8..0000000000
--- a/build/Desktop.proj
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/build/InspectCode.cake b/build/InspectCode.cake
deleted file mode 100644
index 6836d9071b..0000000000
--- a/build/InspectCode.cake
+++ /dev/null
@@ -1,41 +0,0 @@
-#addin "nuget:?package=CodeFileSanity&version=0.0.36"
-
-///////////////////////////////////////////////////////////////////////////////
-// ARGUMENTS
-///////////////////////////////////////////////////////////////////////////////
-
-var target = Argument("target", "CodeAnalysis");
-var configuration = Argument("configuration", "Release");
-
-var rootDirectory = new DirectoryPath("..");
-var sln = rootDirectory.CombineWithFilePath("osu.sln");
-var desktopSlnf = rootDirectory.CombineWithFilePath("osu.Desktop.slnf");
-
-///////////////////////////////////////////////////////////////////////////////
-// TASKS
-///////////////////////////////////////////////////////////////////////////////
-
-Task("InspectCode")
- .Does(() => {
- var inspectcodereport = "inspectcodereport.xml";
- var cacheDir = "inspectcode";
- var verbosity = AppVeyor.IsRunningOnAppVeyor ? "WARN" : "INFO"; // Don't flood CI output
-
- DotNetCoreTool(rootDirectory.FullPath,
- "jb", $@"inspectcode ""{desktopSlnf}"" --output=""{inspectcodereport}"" --caches-home=""{cacheDir}"" --verbosity={verbosity}");
- DotNetCoreTool(rootDirectory.FullPath, "nvika", $@"parsereport ""{inspectcodereport}"" --treatwarningsaserrors");
- });
-
-Task("CodeFileSanity")
- .Does(() => {
- ValidateCodeSanity(new ValidateCodeSanitySettings {
- RootDirectory = rootDirectory.FullPath,
- IsAppveyorBuild = AppVeyor.IsRunningOnAppVeyor
- });
- });
-
-Task("CodeAnalysis")
- .IsDependentOn("CodeFileSanity")
- .IsDependentOn("InspectCode");
-
-RunTarget(target);
\ No newline at end of file
diff --git a/cake.config b/cake.config
deleted file mode 100644
index 187d825591..0000000000
--- a/cake.config
+++ /dev/null
@@ -1,5 +0,0 @@
-
-[Nuget]
-Source=https://api.nuget.org/v3/index.json
-UseInProcessClient=true
-LoadDependencies=true
diff --git a/osu.Android.props b/osu.Android.props
index c020b1d783..1f60f02fb1 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -51,7 +51,7 @@
-
-
+
+
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModApproachDifferent.cs b/osu.Game.Rulesets.Osu/Mods/OsuModApproachDifferent.cs
new file mode 100644
index 0000000000..3e638c4833
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModApproachDifferent.cs
@@ -0,0 +1,102 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using osu.Framework.Bindables;
+using osu.Framework.Extensions.IEnumerableExtensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Configuration;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+
+namespace osu.Game.Rulesets.Osu.Mods
+{
+ public class OsuModApproachDifferent : Mod, IApplicableToDrawableHitObjects
+ {
+ public override string Name => "Approach Different";
+ public override string Acronym => "AD";
+ public override string Description => "Never trust the approach circles...";
+ public override double ScoreMultiplier => 1;
+ public override IconUsage? Icon { get; } = FontAwesome.Regular.Circle;
+
+ [SettingSource("Initial size", "Change the initial size of the approach circle, relative to hit circles.", 0)]
+ public BindableFloat Scale { get; } = new BindableFloat(4)
+ {
+ Precision = 0.1f,
+ MinValue = 2,
+ MaxValue = 10,
+ };
+
+ [SettingSource("Style", "Change the animation style of the approach circles.", 1)]
+ public Bindable Style { get; } = new Bindable();
+
+ public void ApplyToDrawableHitObjects(IEnumerable drawables)
+ {
+ drawables.ForEach(drawable =>
+ {
+ drawable.ApplyCustomUpdateState += (drawableObject, state) =>
+ {
+ if (!(drawableObject is DrawableHitCircle drawableHitCircle)) return;
+
+ var hitCircle = drawableHitCircle.HitObject;
+
+ drawableHitCircle.ApproachCircle.ClearTransforms(targetMember: nameof(Scale));
+
+ using (drawableHitCircle.BeginAbsoluteSequence(hitCircle.StartTime - hitCircle.TimePreempt))
+ drawableHitCircle.ApproachCircle.ScaleTo(Scale.Value).ScaleTo(1f, hitCircle.TimePreempt, getEasing(Style.Value));
+ };
+ });
+ }
+
+ private Easing getEasing(AnimationStyle style)
+ {
+ switch (style)
+ {
+ default:
+ return Easing.None;
+
+ case AnimationStyle.Accelerate1:
+ return Easing.In;
+
+ case AnimationStyle.Accelerate2:
+ return Easing.InCubic;
+
+ case AnimationStyle.Accelerate3:
+ return Easing.InQuint;
+
+ case AnimationStyle.Gravity:
+ return Easing.InBack;
+
+ case AnimationStyle.Decelerate1:
+ return Easing.Out;
+
+ case AnimationStyle.Decelerate2:
+ return Easing.OutCubic;
+
+ case AnimationStyle.Decelerate3:
+ return Easing.OutQuint;
+
+ case AnimationStyle.InOut1:
+ return Easing.InOutCubic;
+
+ case AnimationStyle.InOut2:
+ return Easing.InOutQuint;
+ }
+ }
+
+ public enum AnimationStyle
+ {
+ Gravity,
+ InOut1,
+ InOut2,
+ Accelerate1,
+ Accelerate2,
+ Accelerate3,
+ Decelerate1,
+ Decelerate2,
+ Decelerate3,
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs
index b50d3ad2b4..1b9bcd19fd 100644
--- a/osu.Game.Rulesets.Osu/OsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs
@@ -187,6 +187,7 @@ namespace osu.Game.Rulesets.Osu
new MultiMod(new ModWindUp(), new ModWindDown()),
new OsuModTraceable(),
new OsuModBarrelRoll(),
+ new OsuModApproachDifferent(),
};
case ModType.System:
diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyOldStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyOldStyleSpinner.cs
index 19cb55c16e..d80e061662 100644
--- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyOldStyleSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyOldStyleSpinner.cs
@@ -12,6 +12,7 @@ using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Skinning;
using osuTK;
+using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Skinning.Legacy
{
@@ -40,6 +41,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
Anchor = Anchor.TopCentre,
Origin = Anchor.Centre,
Texture = source.GetTexture("spinner-background"),
+ Colour = source.GetConfig(OsuSkinColour.SpinnerBackground)?.Value ?? new Color4(100, 100, 100, 255),
Scale = new Vector2(SPRITE_SCALE),
Y = SPINNER_Y_CENTRE,
},
diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs b/osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs
index 4e6d3ef0e4..f7ba8b9fc4 100644
--- a/osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/OsuSkinColour.cs
@@ -7,6 +7,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
{
SliderTrackOverride,
SliderBorder,
- SliderBall
+ SliderBall,
+ SpinnerBackground,
}
}
diff --git a/osu.Game.Rulesets.Taiko.Tests/Mods/TaikoModTestScene.cs b/osu.Game.Rulesets.Taiko.Tests/Mods/TaikoModTestScene.cs
new file mode 100644
index 0000000000..3090facf8c
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko.Tests/Mods/TaikoModTestScene.cs
@@ -0,0 +1,12 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Game.Tests.Visual;
+
+namespace osu.Game.Rulesets.Taiko.Tests.Mods
+{
+ public abstract class TaikoModTestScene : ModTestScene
+ {
+ protected sealed override Ruleset CreatePlayerRuleset() => new TaikoRuleset();
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModHidden.cs b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModHidden.cs
new file mode 100644
index 0000000000..7abbb9d186
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModHidden.cs
@@ -0,0 +1,24 @@
+// Copyright (c) ppy Pty Ltd . 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.Game.Rulesets.Taiko.Mods;
+
+namespace osu.Game.Rulesets.Taiko.Tests.Mods
+{
+ public class TestSceneTaikoModHidden : TaikoModTestScene
+ {
+ [Test]
+ public void TestDefaultBeatmapTest() => CreateModTest(new ModTestData
+ {
+ Mod = new TaikoModHidden(),
+ Autoplay = true,
+ PassCondition = checkSomeAutoplayHits
+ });
+
+ private bool checkSomeAutoplayHits()
+ => Player.ScoreProcessor.JudgedHits >= 4
+ && Player.Results.All(result => result.Type == result.Judgement.MaxResult);
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs
index 7739ecaf5b..0fd3625a93 100644
--- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs
+++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHidden.cs
@@ -1,23 +1,93 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using osu.Framework.Graphics;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Scoring;
+using osu.Game.Rulesets.Taiko.Objects.Drawables;
namespace osu.Game.Rulesets.Taiko.Mods
{
- public class TaikoModHidden : ModHidden
+ public class TaikoModHidden : ModHidden, IApplicableToDifficulty
{
public override string Description => @"Beats fade out before you hit them!";
public override double ScoreMultiplier => 1.06;
- public override bool HasImplementation => false;
+
+ ///
+ /// In osu-stable, the hit position is 160, so the active playfield is essentially 160 pixels shorter
+ /// than the actual screen width. The normalized playfield height is 480, so on a 4:3 screen the
+ /// playfield ratio of the active area up to the hit position will actually be (640 - 160) / 480 = 1.
+ /// For custom resolutions/aspect ratios (x:y), the screen width given the normalized height becomes 480 * x / y instead,
+ /// and the playfield ratio becomes (480 * x / y - 160) / 480 = x / y - 1/3.
+ /// This constant is equal to the playfield ratio on 4:3 screens divided by the playfield ratio on 16:9 screens.
+ ///
+ private const double hd_sv_scale = (4.0 / 3.0 - 1.0 / 3.0) / (16.0 / 9.0 - 1.0 / 3.0);
+
+ private double originalSliderMultiplier;
+
+ private ControlPointInfo controlPointInfo;
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
{
+ ApplyNormalVisibilityState(hitObject, state);
+ }
+
+ protected double MultiplierAt(double position)
+ {
+ double beatLength = controlPointInfo.TimingPointAt(position).BeatLength;
+ double speedMultiplier = controlPointInfo.DifficultyPointAt(position).SpeedMultiplier;
+
+ return originalSliderMultiplier * speedMultiplier * TimingControlPoint.DEFAULT_BEAT_LENGTH / beatLength;
}
protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state)
{
+ switch (hitObject)
+ {
+ case DrawableDrumRollTick _:
+ case DrawableHit _:
+ double preempt = 10000 / MultiplierAt(hitObject.HitObject.StartTime);
+ double start = hitObject.HitObject.StartTime - preempt * 0.6;
+ double duration = preempt * 0.3;
+
+ using (hitObject.BeginAbsoluteSequence(start))
+ {
+ hitObject.FadeOut(duration);
+
+ // DrawableHitObject sets LifetimeEnd to LatestTransformEndTime if it isn't manually changed.
+ // in order for the object to not be killed before its actual end time (as the latest transform ends earlier), set lifetime end explicitly.
+ hitObject.LifetimeEnd = state == ArmedState.Idle || !hitObject.AllJudged
+ ? hitObject.HitObject.GetEndTime() + hitObject.HitObject.HitWindows.WindowFor(HitResult.Miss)
+ : hitObject.HitStateUpdateTime;
+ }
+
+ break;
+ }
+ }
+
+ public void ReadFromDifficulty(BeatmapDifficulty difficulty)
+ {
+ }
+
+ public void ApplyToDifficulty(BeatmapDifficulty difficulty)
+ {
+ // needs to be read after all processing has been run (TaikoBeatmapConverter applies an adjustment which would otherwise be omitted).
+ originalSliderMultiplier = difficulty.SliderMultiplier;
+
+ // osu-stable has an added playfield cover that essentially forces a 4:3 playfield ratio, by cutting off all objects past that size.
+ // This is not yet implemented; instead a playfield adjustment container is present which maintains a 16:9 ratio.
+ // For now, increase the slider multiplier proportionally so that the notes stay on the screen for the same amount of time as on stable.
+ // Note that this means that the notes will scroll faster as they have a longer distance to travel on the screen in that same amount of time.
+ difficulty.SliderMultiplier /= hd_sv_scale;
+ }
+
+ public override void ApplyToBeatmap(IBeatmap beatmap)
+ {
+ controlPointInfo = beatmap.ControlPointInfo;
}
}
}
diff --git a/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs b/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs
index a47631a83b..8f5ebf53bd 100644
--- a/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs
+++ b/osu.Game.Tests/Collections/IO/ImportCollectionsTest.cs
@@ -113,7 +113,6 @@ namespace osu.Game.Tests.Collections.IO
await importCollectionsFromStream(osu, ms);
}
- Assert.That(host.UpdateThread.Running, Is.True);
Assert.That(exceptionThrown, Is.False);
Assert.That(osu.CollectionManager.Collections.Count, Is.EqualTo(0));
}
diff --git a/osu.Game.Tests/Visual/Background/TestSceneSeasonalBackgroundLoader.cs b/osu.Game.Tests/Visual/Background/TestSceneSeasonalBackgroundLoader.cs
index dc5a4f4a3e..0bd1263076 100644
--- a/osu.Game.Tests/Visual/Background/TestSceneSeasonalBackgroundLoader.cs
+++ b/osu.Game.Tests/Visual/Background/TestSceneSeasonalBackgroundLoader.cs
@@ -161,15 +161,18 @@ namespace osu.Game.Tests.Visual.Background
private void loadNextBackground()
{
+ SeasonalBackground previousBackground = null;
SeasonalBackground background = null;
AddStep("create next background", () =>
{
+ previousBackground = (SeasonalBackground)backgroundContainer.SingleOrDefault();
background = backgroundLoader.LoadNextBackground();
LoadComponentAsync(background, bg => backgroundContainer.Child = bg);
});
AddUntilStep("background loaded", () => background.IsLoaded);
+ AddAssert("background is different", () => !background.Equals(previousBackground));
}
private void assertAnyBackground()
diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs
index d3a4b635f5..25d0843a71 100644
--- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs
+++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using JetBrains.Annotations;
using Newtonsoft.Json;
using osu.Framework.Bindables;
using osu.Framework.Lists;
@@ -66,6 +67,7 @@ namespace osu.Game.Beatmaps.ControlPoints
///
/// The time to find the difficulty control point at.
/// The difficulty control point.
+ [NotNull]
public DifficultyControlPoint DifficultyPointAt(double time) => binarySearchWithFallback(DifficultyPoints, time, DifficultyControlPoint.DEFAULT);
///
@@ -73,6 +75,7 @@ namespace osu.Game.Beatmaps.ControlPoints
///
/// The time to find the effect control point at.
/// The effect control point.
+ [NotNull]
public EffectControlPoint EffectPointAt(double time) => binarySearchWithFallback(EffectPoints, time, EffectControlPoint.DEFAULT);
///
@@ -80,6 +83,7 @@ namespace osu.Game.Beatmaps.ControlPoints
///
/// The time to find the sound control point at.
/// The sound control point.
+ [NotNull]
public SampleControlPoint SamplePointAt(double time) => binarySearchWithFallback(SamplePoints, time, SamplePoints.Count > 0 ? SamplePoints[0] : SampleControlPoint.DEFAULT);
///
@@ -87,6 +91,7 @@ namespace osu.Game.Beatmaps.ControlPoints
///
/// The time to find the timing control point at.
/// The timing control point.
+ [NotNull]
public TimingControlPoint TimingPointAt(double time) => binarySearchWithFallback(TimingPoints, time, TimingPoints.Count > 0 ? TimingPoints[0] : TimingControlPoint.DEFAULT);
///
diff --git a/osu.Game/Beatmaps/DifficultyRecommender.cs b/osu.Game/Beatmaps/DifficultyRecommender.cs
index 340c47d89b..ca910e70b8 100644
--- a/osu.Game/Beatmaps/DifficultyRecommender.cs
+++ b/osu.Game/Beatmaps/DifficultyRecommender.cs
@@ -101,10 +101,20 @@ namespace osu.Game.Beatmaps
/// Rulesets ordered descending by their respective recommended difficulties.
/// The currently selected ruleset will always be first.
///
- private IEnumerable orderedRulesets =>
- recommendedDifficultyMapping
- .OrderByDescending(pair => pair.Value).Select(pair => pair.Key).Where(r => !r.Equals(ruleset.Value))
- .Prepend(ruleset.Value);
+ private IEnumerable orderedRulesets
+ {
+ get
+ {
+ if (LoadState < LoadState.Ready || ruleset.Value == null)
+ return Enumerable.Empty();
+
+ return recommendedDifficultyMapping
+ .OrderByDescending(pair => pair.Value)
+ .Select(pair => pair.Key)
+ .Where(r => !r.Equals(ruleset.Value))
+ .Prepend(ruleset.Value);
+ }
+ }
private void onlineStateChanged(ValueChangedEvent state) => Schedule(() =>
{
diff --git a/osu.Game/Extensions/LanguageExtensions.cs b/osu.Game/Extensions/LanguageExtensions.cs
new file mode 100644
index 0000000000..b67e7fb6fc
--- /dev/null
+++ b/osu.Game/Extensions/LanguageExtensions.cs
@@ -0,0 +1,33 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using System.Globalization;
+using osu.Game.Localisation;
+
+namespace osu.Game.Extensions
+{
+ ///
+ /// Conversion utilities for the enum.
+ ///
+ public static class LanguageExtensions
+ {
+ ///
+ /// Returns the culture code of the that corresponds to the supplied .
+ ///
+ ///
+ /// This is required as enum member names are not allowed to contain hyphens.
+ ///
+ public static string ToCultureCode(this Language language)
+ => language.ToString().Replace("_", "-");
+
+ ///
+ /// Attempts to parse the supplied to a value.
+ ///
+ /// The code of the culture to parse.
+ /// The parsed . Valid only if the return value of the method is .
+ /// Whether the parsing succeeded.
+ public static bool TryParseCultureCode(string cultureCode, out Language language)
+ => Enum.TryParse(cultureCode.Replace("-", "_"), out language);
+ }
+}
diff --git a/osu.Game/Graphics/Backgrounds/SeasonalBackgroundLoader.cs b/osu.Game/Graphics/Backgrounds/SeasonalBackgroundLoader.cs
index a48da37804..f01a26a3a8 100644
--- a/osu.Game/Graphics/Backgrounds/SeasonalBackgroundLoader.cs
+++ b/osu.Game/Graphics/Backgrounds/SeasonalBackgroundLoader.cs
@@ -99,5 +99,14 @@ namespace osu.Game.Graphics.Backgrounds
// ensure we're not loading in without a transition.
this.FadeInFromZero(200, Easing.InOutSine);
}
+
+ public override bool Equals(Background other)
+ {
+ if (ReferenceEquals(null, other)) return false;
+ if (ReferenceEquals(this, other)) return true;
+
+ return other.GetType() == GetType()
+ && ((SeasonalBackground)other).url == url;
+ }
}
}
diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs
index dbcce9a84a..0c220336a5 100644
--- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs
+++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs
@@ -160,7 +160,7 @@ namespace osu.Game.Graphics.UserInterface
Margin = new MarginPadding { Top = 5, Bottom = 5 },
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
- Text = (value as IHasDescription)?.Description ?? (value as Enum)?.GetDescription() ?? value.ToString(),
+ Text = (value as IHasDescription)?.Description ?? (value as Enum)?.GetLocalisableDescription() ?? value.ToString(),
Font = OsuFont.GetFont(size: 14)
},
Bar = new Box
diff --git a/osu.Game/Graphics/UserInterface/PageTabControl.cs b/osu.Game/Graphics/UserInterface/PageTabControl.cs
index d05a08108a..1ba9ad53bb 100644
--- a/osu.Game/Graphics/UserInterface/PageTabControl.cs
+++ b/osu.Game/Graphics/UserInterface/PageTabControl.cs
@@ -11,6 +11,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
+using osu.Framework.Localisation;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Graphics.UserInterface
@@ -81,7 +82,7 @@ namespace osu.Game.Graphics.UserInterface
Active.BindValueChanged(active => Text.Font = Text.Font.With(Typeface.Torus, weight: active.NewValue ? FontWeight.Bold : FontWeight.Medium), true);
}
- protected virtual string CreateText() => (Value as Enum)?.GetDescription() ?? Value.ToString();
+ protected virtual LocalisableString CreateText() => (Value as Enum)?.GetLocalisableDescription() ?? Value.ToString();
protected override bool OnHover(HoverEvent e)
{
diff --git a/osu.Game/Localisation/Language.cs b/osu.Game/Localisation/Language.cs
index a3e845f229..96bfde8596 100644
--- a/osu.Game/Localisation/Language.cs
+++ b/osu.Game/Localisation/Language.cs
@@ -10,7 +10,104 @@ namespace osu.Game.Localisation
[Description(@"English")]
en,
+ // TODO: Requires Arabic glyphs to be added to resources (and possibly also RTL support).
+ // [Description(@"اَلْعَرَبِيَّةُ")]
+ // ar,
+
+ // TODO: Some accented glyphs are missing. Revisit when adding Inter.
+ // [Description(@"Беларуская мова")]
+ // be,
+
+ [Description(@"Български")]
+ bg,
+
+ [Description(@"Česky")]
+ cs,
+
+ [Description(@"Dansk")]
+ da,
+
+ [Description(@"Deutsch")]
+ de,
+
+ // TODO: Some accented glyphs are missing. Revisit when adding Inter.
+ // [Description(@"Ελληνικά")]
+ // el,
+
+ [Description(@"español")]
+ es,
+
+ [Description(@"Suomi")]
+ fi,
+
+ [Description(@"français")]
+ fr,
+
+ [Description(@"Magyar")]
+ hu,
+
+ [Description(@"Bahasa Indonesia")]
+ id,
+
+ [Description(@"Italiano")]
+ it,
+
[Description(@"日本語")]
- ja
+ ja,
+
+ [Description(@"한국어")]
+ ko,
+
+ [Description(@"Nederlands")]
+ nl,
+
+ [Description(@"Norsk")]
+ no,
+
+ [Description(@"polski")]
+ pl,
+
+ [Description(@"Português")]
+ pt,
+
+ [Description(@"Português (Brasil)")]
+ pt_br,
+
+ [Description(@"Română")]
+ ro,
+
+ [Description(@"Русский")]
+ ru,
+
+ [Description(@"Slovenčina")]
+ sk,
+
+ [Description(@"Svenska")]
+ sv,
+
+ [Description(@"ไทย")]
+ th,
+
+ [Description(@"Tagalog")]
+ tl,
+
+ [Description(@"Türkçe")]
+ tr,
+
+ // TODO: Some accented glyphs are missing. Revisit when adding Inter.
+ // [Description(@"Українська мова")]
+ // uk,
+
+ [Description(@"Tiếng Việt")]
+ vi,
+
+ [Description(@"简体中文")]
+ zh,
+
+ [Description(@"繁體中文(香港)")]
+ zh_hk,
+
+ [Description(@"繁體中文(台灣)")]
+ zh_tw
}
}
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index 019d3b3cd0..0c4d035728 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -50,8 +50,10 @@ using osu.Game.Updater;
using osu.Game.Utils;
using LogLevel = osu.Framework.Logging.LogLevel;
using osu.Game.Database;
+using osu.Game.Extensions;
using osu.Game.IO;
using osu.Game.Localisation;
+using osu.Game.Performance;
using osu.Game.Skinning.Editor;
namespace osu.Game
@@ -426,9 +428,12 @@ namespace osu.Game
{
// The given ScoreInfo may have missing properties if it was retrieved from online data. Re-retrieve it from the database
// to ensure all the required data for presenting a replay are present.
- var databasedScoreInfo = score.OnlineScoreID != null
- ? ScoreManager.Query(s => s.OnlineScoreID == score.OnlineScoreID)
- : ScoreManager.Query(s => s.Hash == score.Hash);
+ ScoreInfo databasedScoreInfo = null;
+
+ if (score.OnlineScoreID != null)
+ databasedScoreInfo = ScoreManager.Query(s => s.OnlineScoreID == score.OnlineScoreID);
+
+ databasedScoreInfo ??= ScoreManager.Query(s => s.Hash == score.Hash);
if (databasedScoreInfo == null)
{
@@ -484,6 +489,8 @@ namespace osu.Game
protected virtual UpdateManager CreateUpdateManager() => new UpdateManager();
+ protected virtual HighPerformanceSession CreateHighPerformanceSession() => new HighPerformanceSession();
+
protected override Container CreateScalingContainer() => new ScalingContainer(ScalingMode.Everything);
#region Beatmap progression
@@ -577,7 +584,7 @@ namespace osu.Game
foreach (var language in Enum.GetValues(typeof(Language)).OfType())
{
- var cultureCode = language.ToString();
+ var cultureCode = language.ToCultureCode();
Localisation.AddLanguage(cultureCode, new ResourceManagerLocalisationStore(cultureCode));
}
@@ -712,7 +719,6 @@ namespace osu.Game
PostNotification = n => notifications.Post(n),
}, Add, true);
- loadComponentSingleFile(difficultyRecommender, Add);
loadComponentSingleFile(stableImportManager, Add);
loadComponentSingleFile(screenshotManager, Add);
@@ -753,8 +759,11 @@ namespace osu.Game
loadComponentSingleFile(new AccountCreationOverlay(), topMostOverlayContent.Add, true);
loadComponentSingleFile(new DialogOverlay(), topMostOverlayContent.Add, true);
+ loadComponentSingleFile(CreateHighPerformanceSession(), Add);
+
chatOverlay.State.ValueChanged += state => channelManager.HighPollRate.Value = state.NewValue == Visibility.Visible;
+ Add(difficultyRecommender);
Add(externalLinkOpener = new ExternalLinkOpener());
Add(new MusicKeyBindingHandler());
diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs
index 97ccb66599..0626f236b8 100644
--- a/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs
+++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingSearchControl.cs
@@ -14,6 +14,7 @@ using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
+using osu.Game.Resources.Localisation.Web;
using osuTK.Graphics;
using osu.Game.Rulesets;
using osu.Game.Scoring;
@@ -126,15 +127,15 @@ namespace osu.Game.Overlays.BeatmapListing
Padding = new MarginPadding { Horizontal = 10 },
Children = new Drawable[]
{
- generalFilter = new BeatmapSearchMultipleSelectionFilterRow(@"General"),
+ generalFilter = new BeatmapSearchMultipleSelectionFilterRow(BeatmapsStrings.ListingSearchFiltersGeneral),
modeFilter = new BeatmapSearchRulesetFilterRow(),
- categoryFilter = new BeatmapSearchFilterRow(@"Categories"),
- genreFilter = new BeatmapSearchFilterRow(@"Genre"),
- languageFilter = new BeatmapSearchFilterRow(@"Language"),
- extraFilter = new BeatmapSearchMultipleSelectionFilterRow(@"Extra"),
+ categoryFilter = new BeatmapSearchFilterRow(BeatmapsStrings.ListingSearchFiltersStatus),
+ genreFilter = new BeatmapSearchFilterRow(BeatmapsStrings.ListingSearchFiltersGenre),
+ languageFilter = new BeatmapSearchFilterRow(BeatmapsStrings.ListingSearchFiltersLanguage),
+ extraFilter = new BeatmapSearchMultipleSelectionFilterRow(BeatmapsStrings.ListingSearchFiltersExtra),
ranksFilter = new BeatmapSearchScoreFilterRow(),
- playedFilter = new BeatmapSearchFilterRow(@"Played"),
- explicitContentFilter = new BeatmapSearchFilterRow(@"Explicit Content"),
+ playedFilter = new BeatmapSearchFilterRow(BeatmapsStrings.ListingSearchFiltersPlayed),
+ explicitContentFilter = new BeatmapSearchFilterRow(BeatmapsStrings.ListingSearchFiltersNsfw),
}
}
}
@@ -172,7 +173,7 @@ namespace osu.Game.Overlays.BeatmapListing
public BeatmapSearchTextBox()
{
- PlaceholderText = @"type in keywords...";
+ PlaceholderText = BeatmapsStrings.ListingSearchPrompt;
}
protected override bool OnKeyDown(KeyDownEvent e)
diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapSearchFilterRow.cs b/osu.Game/Overlays/BeatmapListing/BeatmapSearchFilterRow.cs
index 01bcbd3244..4c831543fe 100644
--- a/osu.Game/Overlays/BeatmapListing/BeatmapSearchFilterRow.cs
+++ b/osu.Game/Overlays/BeatmapListing/BeatmapSearchFilterRow.cs
@@ -11,8 +11,8 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osuTK;
-using Humanizer;
using osu.Framework.Extensions.EnumExtensions;
+using osu.Framework.Localisation;
namespace osu.Game.Overlays.BeatmapListing
{
@@ -26,7 +26,7 @@ namespace osu.Game.Overlays.BeatmapListing
set => current.Current = value;
}
- public BeatmapSearchFilterRow(string headerName)
+ public BeatmapSearchFilterRow(LocalisableString header)
{
Drawable filter;
AutoSizeAxes = Axes.Y;
@@ -53,7 +53,7 @@ namespace osu.Game.Overlays.BeatmapListing
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Font = OsuFont.GetFont(size: 13),
- Text = headerName.Titleize()
+ Text = header
},
filter = CreateFilter()
}
diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapSearchMultipleSelectionFilterRow.cs b/osu.Game/Overlays/BeatmapListing/BeatmapSearchMultipleSelectionFilterRow.cs
index 5dfa8e6109..e0632ace58 100644
--- a/osu.Game/Overlays/BeatmapListing/BeatmapSearchMultipleSelectionFilterRow.cs
+++ b/osu.Game/Overlays/BeatmapListing/BeatmapSearchMultipleSelectionFilterRow.cs
@@ -9,6 +9,7 @@ using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
+using osu.Framework.Localisation;
using osuTK;
namespace osu.Game.Overlays.BeatmapListing
@@ -19,8 +20,8 @@ namespace osu.Game.Overlays.BeatmapListing
private MultipleSelectionFilter filter;
- public BeatmapSearchMultipleSelectionFilterRow(string headerName)
- : base(headerName)
+ public BeatmapSearchMultipleSelectionFilterRow(LocalisableString header)
+ : base(header)
{
Current.BindTo(filter.Current);
}
diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapSearchRulesetFilterRow.cs b/osu.Game/Overlays/BeatmapListing/BeatmapSearchRulesetFilterRow.cs
index a8dc088e52..c2d0eea80c 100644
--- a/osu.Game/Overlays/BeatmapListing/BeatmapSearchRulesetFilterRow.cs
+++ b/osu.Game/Overlays/BeatmapListing/BeatmapSearchRulesetFilterRow.cs
@@ -3,6 +3,7 @@
using osu.Framework.Allocation;
using osu.Framework.Graphics;
+using osu.Game.Resources.Localisation.Web;
using osu.Game.Rulesets;
namespace osu.Game.Overlays.BeatmapListing
@@ -10,7 +11,7 @@ namespace osu.Game.Overlays.BeatmapListing
public class BeatmapSearchRulesetFilterRow : BeatmapSearchFilterRow
{
public BeatmapSearchRulesetFilterRow()
- : base(@"Mode")
+ : base(BeatmapsStrings.ListingSearchFiltersMode)
{
}
diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapSearchScoreFilterRow.cs b/osu.Game/Overlays/BeatmapListing/BeatmapSearchScoreFilterRow.cs
index 804962adfb..b39934b56f 100644
--- a/osu.Game/Overlays/BeatmapListing/BeatmapSearchScoreFilterRow.cs
+++ b/osu.Game/Overlays/BeatmapListing/BeatmapSearchScoreFilterRow.cs
@@ -4,6 +4,8 @@
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Extensions;
+using osu.Framework.Localisation;
+using osu.Game.Resources.Localisation.Web;
using osu.Game.Scoring;
namespace osu.Game.Overlays.BeatmapListing
@@ -11,7 +13,7 @@ namespace osu.Game.Overlays.BeatmapListing
public class BeatmapSearchScoreFilterRow : BeatmapSearchMultipleSelectionFilterRow
{
public BeatmapSearchScoreFilterRow()
- : base(@"Rank Achieved")
+ : base(BeatmapsStrings.ListingSearchFiltersRank)
{
}
@@ -31,20 +33,7 @@ namespace osu.Game.Overlays.BeatmapListing
{
}
- protected override string LabelFor(ScoreRank value)
- {
- switch (value)
- {
- case ScoreRank.XH:
- return @"Silver SS";
-
- case ScoreRank.SH:
- return @"Silver S";
-
- default:
- return value.GetDescription();
- }
- }
+ protected override LocalisableString LabelFor(ScoreRank value) => value.GetLocalisableDescription();
}
}
}
diff --git a/osu.Game/Overlays/BeatmapListing/FilterTabItem.cs b/osu.Game/Overlays/BeatmapListing/FilterTabItem.cs
index f02b515755..46cb1e822f 100644
--- a/osu.Game/Overlays/BeatmapListing/FilterTabItem.cs
+++ b/osu.Game/Overlays/BeatmapListing/FilterTabItem.cs
@@ -7,6 +7,7 @@ using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events;
+using osu.Framework.Localisation;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
@@ -66,7 +67,7 @@ namespace osu.Game.Overlays.BeatmapListing
///
/// Returns the label text to be used for the supplied .
///
- protected virtual string LabelFor(T value) => (value as Enum)?.GetDescription() ?? value.ToString();
+ protected virtual LocalisableString LabelFor(T value) => (value as Enum)?.GetLocalisableDescription() ?? value.ToString();
private void updateState()
{
diff --git a/osu.Game/Overlays/BeatmapListing/SearchCategory.cs b/osu.Game/Overlays/BeatmapListing/SearchCategory.cs
index 84859bf5b5..8a9df76af3 100644
--- a/osu.Game/Overlays/BeatmapListing/SearchCategory.cs
+++ b/osu.Game/Overlays/BeatmapListing/SearchCategory.cs
@@ -1,10 +1,14 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using System.ComponentModel;
+using osu.Framework.Localisation;
+using osu.Game.Resources.Localisation.Web;
namespace osu.Game.Overlays.BeatmapListing
{
+ [LocalisableEnum(typeof(SearchCategoryEnumLocalisationMapper))]
public enum SearchCategory
{
Any,
@@ -23,4 +27,43 @@ namespace osu.Game.Overlays.BeatmapListing
[Description("My Maps")]
Mine,
}
+
+ public class SearchCategoryEnumLocalisationMapper : EnumLocalisationMapper
+ {
+ public override LocalisableString Map(SearchCategory value)
+ {
+ switch (value)
+ {
+ case SearchCategory.Any:
+ return BeatmapsStrings.StatusAny;
+
+ case SearchCategory.Leaderboard:
+ return BeatmapsStrings.StatusLeaderboard;
+
+ case SearchCategory.Ranked:
+ return BeatmapsStrings.StatusRanked;
+
+ case SearchCategory.Qualified:
+ return BeatmapsStrings.StatusQualified;
+
+ case SearchCategory.Loved:
+ return BeatmapsStrings.StatusLoved;
+
+ case SearchCategory.Favourites:
+ return BeatmapsStrings.StatusFavourites;
+
+ case SearchCategory.Pending:
+ return BeatmapsStrings.StatusPending;
+
+ case SearchCategory.Graveyard:
+ return BeatmapsStrings.StatusGraveyard;
+
+ case SearchCategory.Mine:
+ return BeatmapsStrings.StatusMine;
+
+ default:
+ throw new ArgumentOutOfRangeException(nameof(value), value, null);
+ }
+ }
+ }
}
diff --git a/osu.Game/Overlays/BeatmapListing/SearchExplicit.cs b/osu.Game/Overlays/BeatmapListing/SearchExplicit.cs
index 3e57cdd48c..78e6a4e094 100644
--- a/osu.Game/Overlays/BeatmapListing/SearchExplicit.cs
+++ b/osu.Game/Overlays/BeatmapListing/SearchExplicit.cs
@@ -1,11 +1,34 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
+using osu.Framework.Localisation;
+using osu.Game.Resources.Localisation.Web;
+
namespace osu.Game.Overlays.BeatmapListing
{
+ [LocalisableEnum(typeof(SearchExplicitEnumLocalisationMapper))]
public enum SearchExplicit
{
Hide,
Show
}
+
+ public class SearchExplicitEnumLocalisationMapper : EnumLocalisationMapper
+ {
+ public override LocalisableString Map(SearchExplicit value)
+ {
+ switch (value)
+ {
+ case SearchExplicit.Hide:
+ return BeatmapsStrings.NsfwExclude;
+
+ case SearchExplicit.Show:
+ return BeatmapsStrings.NsfwInclude;
+
+ default:
+ throw new ArgumentOutOfRangeException(nameof(value), value, null);
+ }
+ }
+ }
}
diff --git a/osu.Game/Overlays/BeatmapListing/SearchExtra.cs b/osu.Game/Overlays/BeatmapListing/SearchExtra.cs
index af37e3264f..4b3fb6e833 100644
--- a/osu.Game/Overlays/BeatmapListing/SearchExtra.cs
+++ b/osu.Game/Overlays/BeatmapListing/SearchExtra.cs
@@ -1,10 +1,14 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using System.ComponentModel;
+using osu.Framework.Localisation;
+using osu.Game.Resources.Localisation.Web;
namespace osu.Game.Overlays.BeatmapListing
{
+ [LocalisableEnum(typeof(SearchExtraEnumLocalisationMapper))]
public enum SearchExtra
{
[Description("Has Video")]
@@ -13,4 +17,22 @@ namespace osu.Game.Overlays.BeatmapListing
[Description("Has Storyboard")]
Storyboard
}
+
+ public class SearchExtraEnumLocalisationMapper : EnumLocalisationMapper
+ {
+ public override LocalisableString Map(SearchExtra value)
+ {
+ switch (value)
+ {
+ case SearchExtra.Video:
+ return BeatmapsStrings.ExtraVideo;
+
+ case SearchExtra.Storyboard:
+ return BeatmapsStrings.ExtraStoryboard;
+
+ default:
+ throw new ArgumentOutOfRangeException(nameof(value), value, null);
+ }
+ }
+ }
}
diff --git a/osu.Game/Overlays/BeatmapListing/SearchGeneral.cs b/osu.Game/Overlays/BeatmapListing/SearchGeneral.cs
index 175942c626..b4c629f7fa 100644
--- a/osu.Game/Overlays/BeatmapListing/SearchGeneral.cs
+++ b/osu.Game/Overlays/BeatmapListing/SearchGeneral.cs
@@ -1,10 +1,14 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using System.ComponentModel;
+using osu.Framework.Localisation;
+using osu.Game.Resources.Localisation.Web;
namespace osu.Game.Overlays.BeatmapListing
{
+ [LocalisableEnum(typeof(SearchGeneralEnumLocalisationMapper))]
public enum SearchGeneral
{
[Description("Recommended difficulty")]
@@ -16,4 +20,25 @@ namespace osu.Game.Overlays.BeatmapListing
[Description("Subscribed mappers")]
Follows
}
+
+ public class SearchGeneralEnumLocalisationMapper : EnumLocalisationMapper
+ {
+ public override LocalisableString Map(SearchGeneral value)
+ {
+ switch (value)
+ {
+ case SearchGeneral.Recommended:
+ return BeatmapsStrings.GeneralRecommended;
+
+ case SearchGeneral.Converts:
+ return BeatmapsStrings.GeneralConverts;
+
+ case SearchGeneral.Follows:
+ return BeatmapsStrings.GeneralFollows;
+
+ default:
+ throw new ArgumentOutOfRangeException(nameof(value), value, null);
+ }
+ }
+ }
}
diff --git a/osu.Game/Overlays/BeatmapListing/SearchGenre.cs b/osu.Game/Overlays/BeatmapListing/SearchGenre.cs
index de437fac3e..b2709ecd2e 100644
--- a/osu.Game/Overlays/BeatmapListing/SearchGenre.cs
+++ b/osu.Game/Overlays/BeatmapListing/SearchGenre.cs
@@ -1,10 +1,14 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using System.ComponentModel;
+using osu.Framework.Localisation;
+using osu.Game.Resources.Localisation.Web;
namespace osu.Game.Overlays.BeatmapListing
{
+ [LocalisableEnum(typeof(SearchGenreEnumLocalisationMapper))]
public enum SearchGenre
{
Any = 0,
@@ -26,4 +30,58 @@ namespace osu.Game.Overlays.BeatmapListing
Folk = 13,
Jazz = 14
}
+
+ public class SearchGenreEnumLocalisationMapper : EnumLocalisationMapper
+ {
+ public override LocalisableString Map(SearchGenre value)
+ {
+ switch (value)
+ {
+ case SearchGenre.Any:
+ return BeatmapsStrings.GenreAny;
+
+ case SearchGenre.Unspecified:
+ return BeatmapsStrings.GenreUnspecified;
+
+ case SearchGenre.VideoGame:
+ return BeatmapsStrings.GenreVideoGame;
+
+ case SearchGenre.Anime:
+ return BeatmapsStrings.GenreAnime;
+
+ case SearchGenre.Rock:
+ return BeatmapsStrings.GenreRock;
+
+ case SearchGenre.Pop:
+ return BeatmapsStrings.GenrePop;
+
+ case SearchGenre.Other:
+ return BeatmapsStrings.GenreOther;
+
+ case SearchGenre.Novelty:
+ return BeatmapsStrings.GenreNovelty;
+
+ case SearchGenre.HipHop:
+ return BeatmapsStrings.GenreHipHop;
+
+ case SearchGenre.Electronic:
+ return BeatmapsStrings.GenreElectronic;
+
+ case SearchGenre.Metal:
+ return BeatmapsStrings.GenreMetal;
+
+ case SearchGenre.Classical:
+ return BeatmapsStrings.GenreClassical;
+
+ case SearchGenre.Folk:
+ return BeatmapsStrings.GenreFolk;
+
+ case SearchGenre.Jazz:
+ return BeatmapsStrings.GenreJazz;
+
+ default:
+ throw new ArgumentOutOfRangeException(nameof(value), value, null);
+ }
+ }
+ }
}
diff --git a/osu.Game/Overlays/BeatmapListing/SearchLanguage.cs b/osu.Game/Overlays/BeatmapListing/SearchLanguage.cs
index 015cee8ce3..fc176c305a 100644
--- a/osu.Game/Overlays/BeatmapListing/SearchLanguage.cs
+++ b/osu.Game/Overlays/BeatmapListing/SearchLanguage.cs
@@ -1,10 +1,14 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
+using osu.Framework.Localisation;
using osu.Framework.Utils;
+using osu.Game.Resources.Localisation.Web;
namespace osu.Game.Overlays.BeatmapListing
{
+ [LocalisableEnum(typeof(SearchLanguageEnumLocalisationMapper))]
[HasOrderedElements]
public enum SearchLanguage
{
@@ -53,4 +57,61 @@ namespace osu.Game.Overlays.BeatmapListing
[Order(13)]
Other
}
+
+ public class SearchLanguageEnumLocalisationMapper : EnumLocalisationMapper
+ {
+ public override LocalisableString Map(SearchLanguage value)
+ {
+ switch (value)
+ {
+ case SearchLanguage.Any:
+ return BeatmapsStrings.LanguageAny;
+
+ case SearchLanguage.Unspecified:
+ return BeatmapsStrings.LanguageUnspecified;
+
+ case SearchLanguage.English:
+ return BeatmapsStrings.LanguageEnglish;
+
+ case SearchLanguage.Japanese:
+ return BeatmapsStrings.LanguageJapanese;
+
+ case SearchLanguage.Chinese:
+ return BeatmapsStrings.LanguageChinese;
+
+ case SearchLanguage.Instrumental:
+ return BeatmapsStrings.LanguageInstrumental;
+
+ case SearchLanguage.Korean:
+ return BeatmapsStrings.LanguageKorean;
+
+ case SearchLanguage.French:
+ return BeatmapsStrings.LanguageFrench;
+
+ case SearchLanguage.German:
+ return BeatmapsStrings.LanguageGerman;
+
+ case SearchLanguage.Swedish:
+ return BeatmapsStrings.LanguageSwedish;
+
+ case SearchLanguage.Spanish:
+ return BeatmapsStrings.LanguageSpanish;
+
+ case SearchLanguage.Italian:
+ return BeatmapsStrings.LanguageItalian;
+
+ case SearchLanguage.Russian:
+ return BeatmapsStrings.LanguageRussian;
+
+ case SearchLanguage.Polish:
+ return BeatmapsStrings.LanguagePolish;
+
+ case SearchLanguage.Other:
+ return BeatmapsStrings.LanguageOther;
+
+ default:
+ throw new ArgumentOutOfRangeException(nameof(value), value, null);
+ }
+ }
+ }
}
diff --git a/osu.Game/Overlays/BeatmapListing/SearchPlayed.cs b/osu.Game/Overlays/BeatmapListing/SearchPlayed.cs
index eb7fb46158..f24cf46c2d 100644
--- a/osu.Game/Overlays/BeatmapListing/SearchPlayed.cs
+++ b/osu.Game/Overlays/BeatmapListing/SearchPlayed.cs
@@ -1,12 +1,38 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
+using osu.Framework.Localisation;
+using osu.Game.Resources.Localisation.Web;
+
namespace osu.Game.Overlays.BeatmapListing
{
+ [LocalisableEnum(typeof(SearchPlayedEnumLocalisationMapper))]
public enum SearchPlayed
{
Any,
Played,
Unplayed
}
+
+ public class SearchPlayedEnumLocalisationMapper : EnumLocalisationMapper
+ {
+ public override LocalisableString Map(SearchPlayed value)
+ {
+ switch (value)
+ {
+ case SearchPlayed.Any:
+ return BeatmapsStrings.PlayedAny;
+
+ case SearchPlayed.Played:
+ return BeatmapsStrings.PlayedPlayed;
+
+ case SearchPlayed.Unplayed:
+ return BeatmapsStrings.PlayedUnplayed;
+
+ default:
+ throw new ArgumentOutOfRangeException(nameof(value), value, null);
+ }
+ }
+ }
}
diff --git a/osu.Game/Overlays/BeatmapListing/SortCriteria.cs b/osu.Game/Overlays/BeatmapListing/SortCriteria.cs
index e409cbdda7..5ea885eecc 100644
--- a/osu.Game/Overlays/BeatmapListing/SortCriteria.cs
+++ b/osu.Game/Overlays/BeatmapListing/SortCriteria.cs
@@ -1,8 +1,13 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
+using osu.Framework.Localisation;
+using osu.Game.Resources.Localisation.Web;
+
namespace osu.Game.Overlays.BeatmapListing
{
+ [LocalisableEnum(typeof(SortCriteriaLocalisationMapper))]
public enum SortCriteria
{
Title,
@@ -14,4 +19,40 @@ namespace osu.Game.Overlays.BeatmapListing
Favourites,
Relevance
}
+
+ public class SortCriteriaLocalisationMapper : EnumLocalisationMapper
+ {
+ public override LocalisableString Map(SortCriteria value)
+ {
+ switch (value)
+ {
+ case SortCriteria.Title:
+ return BeatmapsStrings.ListingSearchSortingTitle;
+
+ case SortCriteria.Artist:
+ return BeatmapsStrings.ListingSearchSortingArtist;
+
+ case SortCriteria.Difficulty:
+ return BeatmapsStrings.ListingSearchSortingDifficulty;
+
+ case SortCriteria.Ranked:
+ return BeatmapsStrings.ListingSearchSortingRanked;
+
+ case SortCriteria.Rating:
+ return BeatmapsStrings.ListingSearchSortingRating;
+
+ case SortCriteria.Plays:
+ return BeatmapsStrings.ListingSearchSortingPlays;
+
+ case SortCriteria.Favourites:
+ return BeatmapsStrings.ListingSearchSortingFavourites;
+
+ case SortCriteria.Relevance:
+ return BeatmapsStrings.ListingSearchSortingRelevance;
+
+ default:
+ throw new ArgumentOutOfRangeException(nameof(value), value, null);
+ }
+ }
+ }
}
diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs
index 5df7a4650e..5e65cd9488 100644
--- a/osu.Game/Overlays/BeatmapListingOverlay.cs
+++ b/osu.Game/Overlays/BeatmapListingOverlay.cs
@@ -18,6 +18,7 @@ using osu.Game.Beatmaps;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays.BeatmapListing;
using osu.Game.Overlays.BeatmapListing.Panels;
+using osu.Game.Resources.Localisation.Web;
using osuTK;
using osuTK.Graphics;
@@ -232,7 +233,7 @@ namespace osu.Game.Overlays
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- Text = @"... nope, nothing found.",
+ Text = BeatmapsStrings.ListingSearchNotFoundQuote,
}
}
});
diff --git a/osu.Game/Overlays/OverlaySortTabControl.cs b/osu.Game/Overlays/OverlaySortTabControl.cs
index 0ebabd424f..b230acca11 100644
--- a/osu.Game/Overlays/OverlaySortTabControl.cs
+++ b/osu.Game/Overlays/OverlaySortTabControl.cs
@@ -18,6 +18,7 @@ using JetBrains.Annotations;
using System;
using osu.Framework.Extensions;
using osu.Framework.Localisation;
+using osu.Game.Resources.Localisation.Web;
namespace osu.Game.Overlays
{
@@ -54,7 +55,7 @@ namespace osu.Game.Overlays
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold),
- Text = @"Sort by"
+ Text = SortStrings.Default
},
CreateControl().With(c =>
{
@@ -143,7 +144,7 @@ namespace osu.Game.Overlays
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold),
- Text = (value as Enum)?.GetDescription() ?? value.ToString()
+ Text = (value as Enum)?.GetLocalisableDescription() ?? value.ToString()
}
}
});
diff --git a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs
index c2767f61b4..dfcdb8e340 100644
--- a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs
@@ -1,11 +1,11 @@
// Copyright (c) ppy Pty Ltd . 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.Bindables;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
+using osu.Game.Extensions;
using osu.Game.Localisation;
namespace osu.Game.Overlays.Settings.Sections.General
@@ -35,11 +35,11 @@ namespace osu.Game.Overlays.Settings.Sections.General
},
};
- if (!Enum.TryParse(frameworkLocale.Value, out var locale))
+ if (!LanguageExtensions.TryParseCultureCode(frameworkLocale.Value, out var locale))
locale = Language.en;
languageSelection.Current.Value = locale;
- languageSelection.Current.BindValueChanged(val => frameworkLocale.Value = val.NewValue.ToString());
+ languageSelection.Current.BindValueChanged(val => frameworkLocale.Value = val.NewValue.ToCultureCode());
}
}
}
diff --git a/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs b/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs
index c77d14632b..9987a0c607 100644
--- a/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs
+++ b/osu.Game/Overlays/Settings/SettingsEnumDropdown.cs
@@ -19,6 +19,8 @@ namespace osu.Game.Overlays.Settings
Margin = new MarginPadding { Top = 5 };
RelativeSizeAxes = Axes.X;
}
+
+ protected override DropdownMenu CreateMenu() => base.CreateMenu().With(m => m.MaxHeight = 200);
}
}
}
diff --git a/osu.Game/Overlays/Settings/SettingsNumberBox.cs b/osu.Game/Overlays/Settings/SettingsNumberBox.cs
index cb7e63ae6f..ca9a8e9c08 100644
--- a/osu.Game/Overlays/Settings/SettingsNumberBox.cs
+++ b/osu.Game/Overlays/Settings/SettingsNumberBox.cs
@@ -2,16 +2,20 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
-using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Settings
{
public class SettingsNumberBox : SettingsItem
{
- protected override Drawable CreateControl() => new OsuNumberBox
+ protected override Drawable CreateControl() => new NumberBox
{
Margin = new MarginPadding { Top = 5 },
RelativeSizeAxes = Axes.X,
};
+
+ public class NumberBox : SettingsTextBox.TextBox
+ {
+ protected override bool CanAddCharacter(char character) => char.IsNumber(character);
+ }
}
}
diff --git a/osu.Game/Overlays/Settings/SettingsTextBox.cs b/osu.Game/Overlays/Settings/SettingsTextBox.cs
index 5e700a1d6b..25424e85a1 100644
--- a/osu.Game/Overlays/Settings/SettingsTextBox.cs
+++ b/osu.Game/Overlays/Settings/SettingsTextBox.cs
@@ -1,18 +1,60 @@
// Copyright (c) ppy Pty Ltd . 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.Input.Events;
+using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
+using osuTK.Graphics;
namespace osu.Game.Overlays.Settings
{
public class SettingsTextBox : SettingsItem
{
- protected override Drawable CreateControl() => new OsuTextBox
+ protected override Drawable CreateControl() => new TextBox
{
Margin = new MarginPadding { Top = 5 },
RelativeSizeAxes = Axes.X,
CommitOnFocusLost = true,
};
+
+ public class TextBox : OsuTextBox
+ {
+ private const float border_thickness = 3;
+
+ private Color4 borderColourFocused;
+ private Color4 borderColourUnfocused;
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colour)
+ {
+ borderColourUnfocused = colour.Gray4.Opacity(0.5f);
+ borderColourFocused = BorderColour;
+
+ updateBorder();
+ }
+
+ protected override void OnFocus(FocusEvent e)
+ {
+ base.OnFocus(e);
+
+ updateBorder();
+ }
+
+ protected override void OnFocusLost(FocusLostEvent e)
+ {
+ base.OnFocusLost(e);
+
+ updateBorder();
+ }
+
+ private void updateBorder()
+ {
+ BorderThickness = border_thickness;
+ BorderColour = HasFocus ? borderColourFocused : borderColourUnfocused;
+ }
+ }
}
}
diff --git a/osu.Game/Overlays/TabControlOverlayHeader.cs b/osu.Game/Overlays/TabControlOverlayHeader.cs
index 7798dfa576..e6f7e250a7 100644
--- a/osu.Game/Overlays/TabControlOverlayHeader.cs
+++ b/osu.Game/Overlays/TabControlOverlayHeader.cs
@@ -106,7 +106,19 @@ namespace osu.Game.Overlays
public OverlayHeaderTabItem(T value)
: base(value)
{
- Text.Text = ((Value as Enum)?.GetDescription() ?? Value.ToString()).ToLower();
+ if (!(Value is Enum enumValue))
+ Text.Text = Value.ToString().ToLower();
+ else
+ {
+ var localisableDescription = enumValue.GetLocalisableDescription();
+ var nonLocalisableDescription = enumValue.GetDescription();
+
+ // If localisable == non-localisable, then we must have a basic string, so .ToLower() is used.
+ Text.Text = localisableDescription.Equals(nonLocalisableDescription)
+ ? nonLocalisableDescription.ToLower()
+ : localisableDescription;
+ }
+
Text.Font = OsuFont.GetFont(size: 14);
Text.Margin = new MarginPadding { Vertical = 16.5f }; // 15px padding + 1.5px line-height difference compensation
Bar.Margin = new MarginPadding { Bottom = bar_height };
diff --git a/osu.Game/Performance/HighPerformanceSession.cs b/osu.Game/Performance/HighPerformanceSession.cs
new file mode 100644
index 0000000000..96e67669c5
--- /dev/null
+++ b/osu.Game/Performance/HighPerformanceSession.cs
@@ -0,0 +1,47 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Runtime;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+
+namespace osu.Game.Performance
+{
+ public class HighPerformanceSession : Component
+ {
+ private readonly IBindable localUserPlaying = new Bindable();
+ private GCLatencyMode originalGCMode;
+
+ [BackgroundDependencyLoader]
+ private void load(OsuGame game)
+ {
+ localUserPlaying.BindTo(game.LocalUserPlaying);
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ localUserPlaying.BindValueChanged(playing =>
+ {
+ if (playing.NewValue)
+ EnableHighPerformanceSession();
+ else
+ DisableHighPerformanceSession();
+ }, true);
+ }
+
+ protected virtual void EnableHighPerformanceSession()
+ {
+ originalGCMode = GCSettings.LatencyMode;
+ GCSettings.LatencyMode = GCLatencyMode.LowLatency;
+ }
+
+ protected virtual void DisableHighPerformanceSession()
+ {
+ if (GCSettings.LatencyMode == GCLatencyMode.LowLatency)
+ GCSettings.LatencyMode = originalGCMode;
+ }
+ }
+}
diff --git a/osu.Game/Rulesets/Mods/ModRandom.cs b/osu.Game/Rulesets/Mods/ModRandom.cs
index 3f14263420..e0c3008ae8 100644
--- a/osu.Game/Rulesets/Mods/ModRandom.cs
+++ b/osu.Game/Rulesets/Mods/ModRandom.cs
@@ -8,7 +8,6 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Configuration;
using osu.Game.Graphics;
-using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Settings;
namespace osu.Game.Rulesets.Mods
@@ -50,7 +49,7 @@ namespace osu.Game.Rulesets.Mods
}
}
- private readonly OsuNumberBox seedNumberBox;
+ private readonly SettingsNumberBox.NumberBox seedNumberBox;
public SeedControl()
{
@@ -76,7 +75,7 @@ namespace osu.Game.Rulesets.Mods
{
new Drawable[]
{
- seedNumberBox = new OsuNumberBox
+ seedNumberBox = new SettingsNumberBox.NumberBox
{
RelativeSizeAxes = Axes.X,
CommitOnFocusLost = true
diff --git a/osu.Game/Scoring/ScoreRank.cs b/osu.Game/Scoring/ScoreRank.cs
index 696d493830..f3b4551ff8 100644
--- a/osu.Game/Scoring/ScoreRank.cs
+++ b/osu.Game/Scoring/ScoreRank.cs
@@ -1,10 +1,14 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using System.ComponentModel;
+using osu.Framework.Localisation;
+using osu.Game.Resources.Localisation.Web;
namespace osu.Game.Scoring
{
+ [LocalisableEnum(typeof(ScoreRankEnumLocalisationMapper))]
public enum ScoreRank
{
[Description(@"D")]
@@ -31,4 +35,40 @@ namespace osu.Game.Scoring
[Description(@"SS+")]
XH,
}
+
+ public class ScoreRankEnumLocalisationMapper : EnumLocalisationMapper
+ {
+ public override LocalisableString Map(ScoreRank value)
+ {
+ switch (value)
+ {
+ case ScoreRank.XH:
+ return BeatmapsStrings.RankXH;
+
+ case ScoreRank.X:
+ return BeatmapsStrings.RankX;
+
+ case ScoreRank.SH:
+ return BeatmapsStrings.RankSH;
+
+ case ScoreRank.S:
+ return BeatmapsStrings.RankS;
+
+ case ScoreRank.A:
+ return BeatmapsStrings.RankA;
+
+ case ScoreRank.B:
+ return BeatmapsStrings.RankB;
+
+ case ScoreRank.C:
+ return BeatmapsStrings.RankC;
+
+ case ScoreRank.D:
+ return BeatmapsStrings.RankD;
+
+ default:
+ throw new ArgumentOutOfRangeException(nameof(value), value, null);
+ }
+ }
+ }
}
diff --git a/osu.Game/Skinning/SkinnableTargetContainer.cs b/osu.Game/Skinning/SkinnableTargetContainer.cs
index 1338462dd6..53b142f09a 100644
--- a/osu.Game/Skinning/SkinnableTargetContainer.cs
+++ b/osu.Game/Skinning/SkinnableTargetContainer.cs
@@ -18,6 +18,8 @@ namespace osu.Game.Skinning
private readonly BindableList components = new BindableList();
+ public bool ComponentsLoaded { get; private set; }
+
public SkinnableTargetContainer(SkinnableTarget target)
{
Target = target;
@@ -30,6 +32,7 @@ namespace osu.Game.Skinning
{
ClearInternal();
components.Clear();
+ ComponentsLoaded = false;
content = CurrentSkin.GetDrawableComponent(new SkinnableTargetComponent(Target)) as SkinnableTargetComponentsContainer;
@@ -39,8 +42,11 @@ namespace osu.Game.Skinning
{
AddInternal(wrapper);
components.AddRange(wrapper.Children.OfType());
+ ComponentsLoaded = true;
});
}
+ else
+ ComponentsLoaded = true;
}
///
diff --git a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs
index b810bbf6ae..d74be70df8 100644
--- a/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs
+++ b/osu.Game/Tests/Visual/LegacySkinPlayerTestScene.cs
@@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd . 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.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
@@ -47,6 +48,8 @@ namespace osu.Game.Tests.Visual
LegacySkin.ResetDrawableTarget(t);
t.Reload();
}));
+
+ AddUntilStep("wait for components to load", () => this.ChildrenOfType().All(t => t.ComponentsLoaded));
}
public class SkinProvidingPlayer : TestPlayer
diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs
index a4c78f24e3..98aad821ce 100644
--- a/osu.Game/Tests/Visual/OsuTestScene.cs
+++ b/osu.Game/Tests/Visual/OsuTestScene.cs
@@ -350,7 +350,7 @@ namespace osu.Game.Tests.Visual
if (CurrentTime >= Length)
{
Stop();
- RaiseCompleted();
+ // `RaiseCompleted` is not called here to prevent transitioning to the next song.
}
}
}
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index a7bd5f2e9f..68ffb87c6c 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -34,8 +34,8 @@
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 5b3efb4ba4..8aa79762fc 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -70,8 +70,8 @@
-
-
+
+
@@ -93,7 +93,7 @@
-
+