diff --git a/.github/ISSUE_TEMPLATE/bug-issues.md b/.github/ISSUE_TEMPLATE/bug-issues.md
new file mode 100644
index 0000000000..8d85c92fec
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug-issues.md
@@ -0,0 +1,14 @@
+---
+name: Bug Report
+about: For issues regarding encountered game bugs
+---
+
+
+
+**Describe your problem:**
+
+**Screenshots or videos showing encountered issue:**
+
+**osu!lazer version:**
+
+**Logs:**
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/crash-issues.md b/.github/ISSUE_TEMPLATE/crash-issues.md
new file mode 100644
index 0000000000..849f042c1f
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/crash-issues.md
@@ -0,0 +1,16 @@
+---
+name: Crash Report
+about: For issues regarding game crashes or permanent freezes
+---
+
+
+
+**Describe your problem:**
+
+**Screenshots or videos showing encountered issue:**
+
+**osu!lazer version:**
+
+**Logs:**
+
+**Computer Specifications:**
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE/feature-request-issues.md b/.github/ISSUE_TEMPLATE/feature-request-issues.md
new file mode 100644
index 0000000000..73c4f37a3e
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature-request-issues.md
@@ -0,0 +1,10 @@
+---
+name: Feature Request
+about: Let us know what you would like to see in the game!
+---
+
+
+
+**Describe the feature:**
+
+**Proposal designs of the feature:**
diff --git a/.github/ISSUE_TEMPLATE/missing-for-live-issues.md b/.github/ISSUE_TEMPLATE/missing-for-live-issues.md
new file mode 100644
index 0000000000..ae3cf20a8c
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/missing-for-live-issues.md
@@ -0,0 +1,10 @@
+---
+name: Missing for Live
+about: Let us know the features you need which are available in osu-stable but not lazer
+---
+
+
+
+**Describe the feature:**
+
+**Designs:**
diff --git a/.gitignore b/.gitignore
index 5138e940ed..f95a04e517 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,10 @@
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
+### Cake ###
+tools/*
+!tools/cakebuild.csproj
+
# Build results
bin/[Dd]ebug/
[Dd]ebugPublic/
@@ -98,6 +102,7 @@ $tf/
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
+inspectcode
# JustCode is a .NET coding add-in
.JustCode
@@ -247,7 +252,11 @@ paket-files/
.fake/
# JetBrains Rider
-.idea/
+.idea/.idea.osu/.idea/*.xml
+.idea/.idea.osu/.idea/codeStyles/*.xml
+.idea/.idea.osu/.idea/dataSources/*.xml
+.idea/.idea.osu/.idea/dictionaries/*.xml
+.idea/.idea.osu/*.iml
*.sln.iml
# CodeRush
@@ -257,3 +266,5 @@ paket-files/
__pycache__/
*.pyc
Staging/
+
+inspectcodereport.xml
diff --git a/README.md b/README.md
index dc36145337..baaba22726 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,7 @@ Build and run
- Using Visual Studio 2017, Rider or Visual Studio Code (configurations are included)
- From command line using `dotnet run --project osu.Desktop`. When building for non-development purposes, add `-c Release` to gain higher performance.
+- To run with code analysis, instead use `powershell ./build.ps1` or `build.sh`. This is currently only supported under windows due to [resharper cli shortcomings](https://youtrack.jetbrains.com/issue/RSRP-410004). Alternative, you can install resharper or use rider to get inline support in your IDE of choice.
Note: If you run from command line under linux, you will need to prefix the output folder to your `LD_LIBRARY_PATH`. See `.vscode/launch.json` for an example
diff --git a/appveyor.yml b/appveyor.yml
index 8c06b42fd2..1f485485da 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,22 +1,8 @@
clone_depth: 1
version: '{branch}-{build}'
image: Visual Studio 2017
-configuration: Debug
-cache:
- - C:\ProgramData\chocolatey\bin -> appveyor.yml
- - C:\ProgramData\chocolatey\lib -> appveyor.yml
+test: off
install:
- cmd: git submodule update --init --recursive --depth=5
- - cmd: choco install resharper-clt -y
- - cmd: choco install nvika -y
- - cmd: dotnet tool install CodeFileSanity --version 0.0.16 --global
-before_build:
- - cmd: CodeFileSanity
- - cmd: nuget restore -verbosity quiet
-build:
- project: osu.sln
- parallel: true
- verbosity: minimal
-after_build:
- - cmd: inspectcode --o="inspectcodereport.xml" --projects:osu.Game* --caches-home="inspectcode" osu.sln > NUL
- - cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors
+build_script:
+ - cmd: PowerShell -Version 2.0 .\build.ps1
diff --git a/appveyor_deploy.yml b/appveyor_deploy.yml
deleted file mode 100644
index 6d8d95e773..0000000000
--- a/appveyor_deploy.yml
+++ /dev/null
@@ -1,30 +0,0 @@
-clone_depth: 1
-version: '{build}'
-skip_non_tags: true
-image: Visual Studio 2017
-install:
- - git clone https://github.com/ppy/osu-deploy
-before_build:
- - ps: if($env:appveyor_repo_tag -eq 'True') { Update-AppveyorBuild -Version $env:appveyor_repo_tag_name }
- - cmd: git submodule update --init --recursive --depth=5
- - cmd: nuget restore -verbosity quiet
-build_script:
- - ps: iex ((New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/appveyor/secure-file/master/install.ps1'))
- - appveyor DownloadFile https://puu.sh/A6g5K/4d08705438.enc # signing certificate
- - cmd: appveyor-tools\secure-file -decrypt 4d08705438.enc -secret %decode_secret% -out %HOMEPATH%\deanherbert.pfx
- - appveyor DownloadFile https://puu.sh/A6g75/fdc6f19b04.enc # deploy configuration
- - cd osu-deploy
- - nuget restore -verbosity quiet
- - msbuild osu.Desktop.Deploy.csproj
- - cmd: ..\appveyor-tools\secure-file -decrypt ..\fdc6f19b04.enc -secret %decode_secret% -out bin\Debug\netcoreapp2.1\osu.Desktop.Deploy.dll.config
- - dotnet bin/Debug/netcoreapp2.1/osu.Desktop.Deploy.dll %code_signing_password% %APPVEYOR_REPO_TAG_NAME%
-environment:
- decode_secret:
- secure: i67IC2xj6DjjxmA6Oj2jing3+MwzLkq6CbGsjfZ7rdY=
- code_signing_password:
- secure: 34tLNqvjmmZEi97MLKfrnQ==
-artifacts:
- - path: 'osu-deploy/releases/*'
-deploy:
- - provider: Environment
- name: github
\ No newline at end of file
diff --git a/build.cake b/build.cake
new file mode 100644
index 0000000000..bc7dfafb8c
--- /dev/null
+++ b/build.cake
@@ -0,0 +1,72 @@
+#addin "nuget:?package=CodeFileSanity&version=0.0.21"
+#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2018.2.2"
+#tool "nuget:?package=NVika.MSBuild&version=1.0.1"
+
+///////////////////////////////////////////////////////////////////////////////
+// ARGUMENTS
+///////////////////////////////////////////////////////////////////////////////
+
+var target = Argument("target", "Build");
+var configuration = Argument("configuration", "Release");
+
+var osuSolution = new FilePath("./osu.sln");
+
+///////////////////////////////////////////////////////////////////////////////
+// TASKS
+///////////////////////////////////////////////////////////////////////////////
+
+Task("Restore")
+ .Does(() => {
+ DotNetCoreRestore(osuSolution.FullPath);
+ });
+
+Task("Compile")
+ .IsDependentOn("Restore")
+ .Does(() => {
+ DotNetCoreBuild(osuSolution.FullPath, new DotNetCoreBuildSettings {
+ Configuration = configuration,
+ NoRestore = true,
+ });
+ });
+
+Task("Test")
+ .IsDependentOn("Compile")
+ .Does(() => {
+ var testAssemblies = GetFiles("**/*.Tests/bin/**/*.Tests.dll");
+
+ DotNetCoreVSTest(testAssemblies, new DotNetCoreVSTestSettings {
+ Logger = AppVeyor.IsRunningOnAppVeyor ? "Appveyor" : $"trx",
+ Parallel = true,
+ ToolTimeout = TimeSpan.FromMinutes(10),
+ });
+ });
+
+// windows only because both inspectcore and nvika depend on net45
+Task("InspectCode")
+ .WithCriteria(IsRunningOnWindows())
+ .IsDependentOn("Compile")
+ .Does(() => {
+ var nVikaToolPath = GetFiles("./tools/NVika.MSBuild.*/tools/NVika.exe").First();
+
+ InspectCode(osuSolution, new InspectCodeSettings {
+ CachesHome = "inspectcode",
+ OutputFile = "inspectcodereport.xml",
+ });
+
+ StartProcess(nVikaToolPath, @"parsereport ""inspectcodereport.xml"" --treatwarningsaserrors");
+ });
+
+Task("CodeFileSanity")
+ .Does(() => {
+ ValidateCodeSanity(new ValidateCodeSanitySettings {
+ RootDirectory = ".",
+ IsAppveyorBuild = AppVeyor.IsRunningOnAppVeyor
+ });
+ });
+
+Task("Build")
+ .IsDependentOn("CodeFileSanity")
+ .IsDependentOn("InspectCode")
+ .IsDependentOn("Test");
+
+RunTarget(target);
\ No newline at end of file
diff --git a/build.ps1 b/build.ps1
new file mode 100644
index 0000000000..9968673c90
--- /dev/null
+++ b/build.ps1
@@ -0,0 +1,79 @@
+##########################################################################
+# This is a customized Cake bootstrapper script for PowerShell.
+##########################################################################
+
+<#
+
+.SYNOPSIS
+This is a Powershell script to bootstrap a Cake build.
+
+.DESCRIPTION
+This Powershell script restores NuGet tools (including Cake)
+and execute your Cake build script with the parameters you provide.
+
+.PARAMETER Script
+The build script to execute.
+.PARAMETER Target
+The build script target to run.
+.PARAMETER Configuration
+The build configuration to use.
+.PARAMETER Verbosity
+Specifies the amount of information to be displayed.
+.PARAMETER ShowDescription
+Shows description about tasks.
+.PARAMETER DryRun
+Performs a dry run.
+.PARAMETER ScriptArgs
+Remaining arguments are added here.
+
+.LINK
+https://cakebuild.net
+
+#>
+
+[CmdletBinding()]
+Param(
+ [string]$Script = "build.cake",
+ [string]$Target,
+ [string]$Configuration,
+ [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")]
+ [string]$Verbosity,
+ [switch]$ShowDescription,
+ [Alias("WhatIf", "Noop")]
+ [switch]$DryRun,
+ [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
+ [string[]]$ScriptArgs
+)
+
+Write-Host "Preparing to run build script..."
+
+# Determine the script root for resolving other paths.
+if(!$PSScriptRoot){
+ $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
+}
+
+# Resolve the paths for resources used for debugging.
+$TOOLS_DIR = Join-Path $PSScriptRoot "tools"
+$CAKE_CSPROJ = Join-Path $TOOLS_DIR "cakebuild.csproj"
+
+# Install the required tools locally.
+Write-Host "Restoring cake tools..."
+Invoke-Expression "dotnet restore `"$CAKE_CSPROJ`" --packages `"$TOOLS_DIR`"" | Out-Null
+
+# Find the Cake executable
+$CAKE_EXECUTABLE = (Get-ChildItem -Path ./tools/cake.coreclr/ -Filter Cake.dll -Recurse).FullName
+
+# Build Cake arguments
+$cakeArguments = @("$Script");
+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
+
+# Start Cake
+Write-Host "Running build script..."
+Invoke-Expression "dotnet `"$CAKE_EXECUTABLE`" $cakeArguments"
+exit $LASTEXITCODE
diff --git a/build.sh b/build.sh
new file mode 100755
index 0000000000..caf1702f41
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,37 @@
+#!/usr/bin/env bash
+
+##########################################################################
+# This is a customized Cake bootstrapper script for Shell.
+##########################################################################
+
+echo "Preparing to run build script..."
+
+SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
+TOOLS_DIR=$SCRIPT_DIR/tools
+CAKE_BINARY_PATH=$TOOLS_DIR/"cake.coreclr"
+
+SCRIPT="build.cake"
+CAKE_CSPROJ=$TOOLS_DIR/"cakebuild.csproj"
+
+# Parse arguments.
+CAKE_ARGUMENTS=()
+for i in "$@"; do
+ case $1 in
+ -s|--script) SCRIPT="$2"; shift ;;
+ --) shift; CAKE_ARGUMENTS+=("$@"); break ;;
+ *) CAKE_ARGUMENTS+=("$1") ;;
+ esac
+ shift
+done
+
+# Install the required tools locally.
+echo "Restoring cake tools..."
+dotnet restore $CAKE_CSPROJ --packages $TOOLS_DIR > /dev/null 2>&1
+
+# Search for the CakeBuild binary.
+CAKE_BINARY=$(find $CAKE_BINARY_PATH -name "Cake.dll")
+
+# Start Cake
+echo "Running build script..."
+
+dotnet "$CAKE_BINARY" $SCRIPT "${CAKE_ARGUMENTS[@]}"
diff --git a/cake.config b/cake.config
new file mode 100644
index 0000000000..187d825591
--- /dev/null
+++ b/cake.config
@@ -0,0 +1,5 @@
+
+[Nuget]
+Source=https://api.nuget.org/v3/index.json
+UseInProcessClient=true
+LoadDependencies=true
diff --git a/osu-resources b/osu-resources
index c3848d8b1c..9ee64e369f 160000
--- a/osu-resources
+++ b/osu-resources
@@ -1 +1 @@
-Subproject commit c3848d8b1c84966abe851d915bcca878415614b4
+Subproject commit 9ee64e369fe6fdafc6aed40f5a35b5f01eb82c53
diff --git a/osu.Desktop/Overlays/VersionManager.cs b/osu.Desktop/Overlays/VersionManager.cs
index 1129969694..96857d6b4f 100644
--- a/osu.Desktop/Overlays/VersionManager.cs
+++ b/osu.Desktop/Overlays/VersionManager.cs
@@ -27,9 +27,6 @@ namespace osu.Desktop.Overlays
private NotificationOverlay notificationOverlay;
private GameHost host;
- public override bool HandleKeyboardInput => false;
- public override bool HandleMouseInput => false;
-
[BackgroundDependencyLoader]
private void load(NotificationOverlay notification, OsuColour colours, TextureStore textures, OsuGameBase game, OsuConfigManager config, GameHost host)
{
diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index e2fc4d14f6..e1e59804e5 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -27,9 +27,9 @@
-
-
-
+
+
+
diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs
index 34e07170bd..cac1356c81 100644
--- a/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs
+++ b/osu.Game.Rulesets.Catch.Tests/TestCaseAutoJuiceStream.cs
@@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
@@ -38,12 +37,12 @@ namespace osu.Game.Rulesets.Catch.Tests
beatmap.HitObjects.Add(new JuiceStream
{
X = 0.5f - width / 2,
- ControlPoints = new List
+ ControlPoints = new[]
{
Vector2.Zero,
new Vector2(width * CatchPlayfield.BASE_WIDTH, 0)
},
- CurveType = CurveType.Linear,
+ PathType = PathType.Linear,
Distance = width * CatchPlayfield.BASE_WIDTH,
StartTime = i * 2000,
NewCombo = i % 8 == 0
diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
index 51343d9e91..b76f591239 100644
--- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
+++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj
@@ -2,9 +2,10 @@
-
-
-
+
+
+
+
WinExe
diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs
index 15d4edc411..fed65c42af 100644
--- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs
@@ -35,9 +35,9 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
StartTime = obj.StartTime,
Samples = obj.Samples,
ControlPoints = curveData.ControlPoints,
- CurveType = curveData.CurveType,
+ PathType = curveData.PathType,
Distance = curveData.Distance,
- RepeatSamples = curveData.RepeatSamples,
+ NodeSamples = curveData.NodeSamples,
RepeatCount = curveData.RepeatCount,
X = (positionData?.X ?? 0) / CatchPlayfield.BASE_WIDTH,
NewCombo = comboData?.NewCombo ?? false,
diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
index 773bf576dd..0c50dae9fb 100644
--- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
@@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.Objects
public int IndexInBeatmap { get; set; }
- public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(IndexInBeatmap % 4);
+ public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(ComboIndex % 4);
public virtual bool NewCombo { get; set; }
diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
index 0344189af5..f1e131932b 100644
--- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
+++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
@@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Catch.Objects
if (TickDistance == 0)
return;
- var length = Curve.Distance;
+ var length = Path.Distance;
var tickDistance = Math.Min(TickDistance, length);
var spanDuration = length / Velocity;
@@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Catch.Objects
AddNested(new TinyDroplet
{
StartTime = t,
- X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
+ X = X + Path.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
Samples = new List(Samples.Select(s => new SampleInfo
{
Bank = s.Bank,
@@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Catch.Objects
AddNested(new Droplet
{
StartTime = time,
- X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
+ X = X + Path.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
Samples = new List(Samples.Select(s => new SampleInfo
{
Bank = s.Bank,
@@ -127,12 +127,12 @@ namespace osu.Game.Rulesets.Catch.Objects
{
Samples = Samples,
StartTime = spanStartTime + spanDuration,
- X = X + Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
+ X = X + Path.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
});
}
}
- public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity;
+ public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity;
public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH;
@@ -140,24 +140,24 @@ namespace osu.Game.Rulesets.Catch.Objects
public double Distance
{
- get { return Curve.Distance; }
- set { Curve.Distance = value; }
+ get { return Path.Distance; }
+ set { Path.Distance = value; }
}
- public SliderCurve Curve { get; } = new SliderCurve();
+ public SliderPath Path { get; } = new SliderPath();
- public List ControlPoints
+ public Vector2[] ControlPoints
{
- get { return Curve.ControlPoints; }
- set { Curve.ControlPoints = value; }
+ get { return Path.ControlPoints; }
+ set { Path.ControlPoints = value; }
}
- public List> RepeatSamples { get; set; } = new List>();
+ public List> NodeSamples { get; set; } = new List>();
- public CurveType CurveType
+ public PathType PathType
{
- get { return Curve.CurveType; }
- set { Curve.CurveType = value; }
+ get { return Path.PathType; }
+ set { Path.PathType = value; }
}
public double? LegacyLastTickOffset { get; set; }
diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
index d49be69856..925e7aaac9 100644
--- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
@@ -5,11 +5,13 @@ using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
+using osu.Game.Configuration;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI.Scrolling;
+using OpenTK;
namespace osu.Game.Rulesets.Catch.UI
{
@@ -17,13 +19,13 @@ namespace osu.Game.Rulesets.Catch.UI
{
public const float BASE_WIDTH = 512;
- protected override Container Content => content;
- private readonly Container content;
-
private readonly CatcherArea catcherArea;
+ protected override bool UserScrollSpeedAdjustment => false;
+
+ protected override SpeedChangeVisualisationMethod VisualisationMethod => SpeedChangeVisualisationMethod.Constant;
+
public CatchPlayfield(BeatmapDifficulty difficulty, Func> getVisualRepresentation)
- : base(BASE_WIDTH)
{
Direction.Value = ScrollingDirection.Down;
@@ -32,27 +34,29 @@ namespace osu.Game.Rulesets.Catch.UI
Anchor = Anchor.TopCentre;
Origin = Anchor.TopCentre;
- base.Content.Anchor = Anchor.BottomLeft;
- base.Content.Origin = Anchor.BottomLeft;
+ Size = new Vector2(0.86f); // matches stable's vertical offset for catcher plate
- base.Content.AddRange(new Drawable[]
+ InternalChild = new PlayfieldAdjustmentContainer
{
- explodingFruitContainer = new Container
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
{
- RelativeSizeAxes = Axes.Both,
- },
- catcherArea = new CatcherArea(difficulty)
- {
- GetVisualRepresentation = getVisualRepresentation,
- ExplodingFruitTarget = explodingFruitContainer,
- Anchor = Anchor.BottomLeft,
- Origin = Anchor.TopLeft,
- },
- content = new Container
- {
- RelativeSizeAxes = Axes.Both,
- },
- });
+ explodingFruitContainer = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ catcherArea = new CatcherArea(difficulty)
+ {
+ GetVisualRepresentation = getVisualRepresentation,
+ ExplodingFruitTarget = explodingFruitContainer,
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.TopLeft,
+ },
+ HitObjectContainer
+ }
+ };
+
+ VisibleTimeRange.Value = BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
}
public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.AttemptCatch(obj);
diff --git a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs
index 1ac052de4d..94233bc9f0 100644
--- a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs
@@ -13,7 +13,6 @@ using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
-using OpenTK;
namespace osu.Game.Rulesets.Catch.UI
{
@@ -32,9 +31,7 @@ namespace osu.Game.Rulesets.Catch.UI
public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo);
- protected override Vector2 PlayfieldArea => new Vector2(0.86f); // matches stable's vertical offset for catcher plate
-
- protected override DrawableHitObject GetVisualRepresentation(CatchHitObject h)
+ public override DrawableHitObject GetVisualRepresentation(CatchHitObject h)
{
switch (h)
{
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
index be56ccf8c1..06453ac32d 100644
--- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
@@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Catch.UI
{
public class CatcherArea : Container
{
- public const float CATCHER_SIZE = 84;
+ public const float CATCHER_SIZE = 100;
protected readonly Catcher MovableCatcher;
diff --git a/osu.Game.Rulesets.Catch/UI/PlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Catch/UI/PlayfieldAdjustmentContainer.cs
new file mode 100644
index 0000000000..ad0073ff12
--- /dev/null
+++ b/osu.Game.Rulesets.Catch/UI/PlayfieldAdjustmentContainer.cs
@@ -0,0 +1,42 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using OpenTK;
+
+namespace osu.Game.Rulesets.Catch.UI
+{
+ public class PlayfieldAdjustmentContainer : Container
+ {
+ protected override Container Content => content;
+ private readonly Container content;
+
+ public PlayfieldAdjustmentContainer()
+ {
+ InternalChild = new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ FillMode = FillMode.Fit,
+ FillAspectRatio = 4f / 3,
+ Child = content = new ScalingContainer { RelativeSizeAxes = Axes.Both }
+ };
+ }
+
+ ///
+ /// A which scales its content relative to a target width.
+ ///
+ private class ScalingContainer : Container
+ {
+ protected override void Update()
+ {
+ base.Update();
+
+ Scale = new Vector2(Parent.ChildSize.X / CatchPlayfield.BASE_WIDTH);
+ Size = Vector2.Divide(Vector2.One, Scale);
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
index 3165f69a6b..98ad086c66 100644
--- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
+++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj
@@ -2,9 +2,10 @@
-
-
-
+
+
+
+
WinExe
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
index c15b303048..d86ebc9a09 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs
@@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
TargetColumns = (int)Math.Max(1, roundedCircleSize);
else
{
- float percentSliderOrSpinner = (float)beatmap.HitObjects.Count(h => h is IHasEndTime) / beatmap.HitObjects.Count();
+ float percentSliderOrSpinner = (float)beatmap.HitObjects.Count(h => h is IHasEndTime) / beatmap.HitObjects.Count;
if (percentSliderOrSpinner < 0.2)
TargetColumns = 7;
else if (percentSliderOrSpinner < 0.3 || roundedCircleSize >= 5)
@@ -247,7 +247,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.SpanCount();
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
- return curveData.RepeatSamples[index];
+ return curveData.NodeSamples[index];
}
}
}
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
index 37a8062d75..635004d2f6 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
@@ -470,7 +470,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
double segmentTime = (EndTime - HitObject.StartTime) / spanCount;
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
- return curveData.RepeatSamples[index];
+ return curveData.NodeSamples[index];
}
///
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs
index 05ca1d4365..7bd39adb45 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs
@@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
drainTime = 10000;
BeatmapDifficulty difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty;
- conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + (double)OriginalBeatmap.HitObjects.Count() / drainTime * 9f) / 38f * 5f / 1.15;
+ conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + (double)OriginalBeatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15;
conversionDifficulty = Math.Min(conversionDifficulty.Value, 12);
return conversionDifficulty.Value;
diff --git a/osu.Game.Rulesets.Mania/Configuration/ManiaConfigManager.cs b/osu.Game.Rulesets.Mania/Configuration/ManiaConfigManager.cs
index 3e9c9feba1..1c9e1e4c73 100644
--- a/osu.Game.Rulesets.Mania/Configuration/ManiaConfigManager.cs
+++ b/osu.Game.Rulesets.Mania/Configuration/ManiaConfigManager.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Configuration
{
base.InitialiseDefaults();
- Set(ManiaSetting.ScrollTime, 1500.0, 50.0, 10000.0, 50.0);
+ Set(ManiaSetting.ScrollTime, 2250.0, 50.0, 10000.0, 50.0);
Set(ManiaSetting.ScrollDirection, ManiaScrollingDirection.Down);
}
diff --git a/osu.Game.Rulesets.Mania/Edit/Layers/Selection/Overlays/HoldNoteMask.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs
similarity index 82%
rename from osu.Game.Rulesets.Mania/Edit/Layers/Selection/Overlays/HoldNoteMask.cs
rename to osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs
index bfa6bc0a17..3da2538497 100644
--- a/osu.Game.Rulesets.Mania/Edit/Layers/Selection/Overlays/HoldNoteMask.cs
+++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNoteSelectionBlueprint.cs
@@ -5,7 +5,6 @@ using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Game.Graphics;
-using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Mania.UI;
@@ -13,9 +12,9 @@ using osu.Game.Rulesets.UI.Scrolling;
using OpenTK;
using OpenTK.Graphics;
-namespace osu.Game.Rulesets.Mania.Edit.Layers.Selection.Overlays
+namespace osu.Game.Rulesets.Mania.Edit.Blueprints
{
- public class HoldNoteMask : HitObjectMask
+ public class HoldNoteSelectionBlueprint : ManiaSelectionBlueprint
{
public new DrawableHoldNote HitObject => (DrawableHoldNote)base.HitObject;
@@ -23,13 +22,13 @@ namespace osu.Game.Rulesets.Mania.Edit.Layers.Selection.Overlays
private readonly BodyPiece body;
- public HoldNoteMask(DrawableHoldNote hold)
+ public HoldNoteSelectionBlueprint(DrawableHoldNote hold)
: base(hold)
{
InternalChildren = new Drawable[]
{
- new HoldNoteNoteMask(hold.Head),
- new HoldNoteNoteMask(hold.Tail),
+ new HoldNoteNoteSelectionBlueprint(hold.Head),
+ new HoldNoteNoteSelectionBlueprint(hold.Tail),
body = new BodyPiece
{
AccentColour = Color4.Transparent
@@ -59,9 +58,9 @@ namespace osu.Game.Rulesets.Mania.Edit.Layers.Selection.Overlays
Y -= HitObject.Tail.DrawHeight;
}
- private class HoldNoteNoteMask : NoteMask
+ private class HoldNoteNoteSelectionBlueprint : NoteSelectionBlueprint
{
- public HoldNoteNoteMask(DrawableNote note)
+ public HoldNoteNoteSelectionBlueprint(DrawableNote note)
: base(note)
{
Select();
@@ -78,7 +77,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Layers.Selection.Overlays
}
// Todo: This is temporary, since the note masks don't do anything special yet. In the future they will handle input.
- public override bool HandleMouseInput => false;
+ public override bool HandlePositionalInput => false;
}
}
}
diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs
new file mode 100644
index 0000000000..474b8c662e
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/ManiaSelectionBlueprint.cs
@@ -0,0 +1,21 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Input.Events;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Objects.Drawables;
+
+namespace osu.Game.Rulesets.Mania.Edit.Blueprints
+{
+ public class ManiaSelectionBlueprint : SelectionBlueprint
+ {
+ public ManiaSelectionBlueprint(DrawableHitObject hitObject)
+ : base(hitObject)
+ {
+ }
+
+ public override void AdjustPosition(DragEvent dragEvent)
+ {
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Edit/Layers/Selection/Overlays/NoteMask.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/NoteSelectionBlueprint.cs
similarity index 82%
rename from osu.Game.Rulesets.Mania/Edit/Layers/Selection/Overlays/NoteMask.cs
rename to osu.Game.Rulesets.Mania/Edit/Blueprints/NoteSelectionBlueprint.cs
index 78f876cb14..7c0337dc4e 100644
--- a/osu.Game.Rulesets.Mania/Edit/Layers/Selection/Overlays/NoteMask.cs
+++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/NoteSelectionBlueprint.cs
@@ -3,15 +3,14 @@
using osu.Framework.Allocation;
using osu.Game.Graphics;
-using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
-namespace osu.Game.Rulesets.Mania.Edit.Layers.Selection.Overlays
+namespace osu.Game.Rulesets.Mania.Edit.Blueprints
{
- public class NoteMask : HitObjectMask
+ public class NoteSelectionBlueprint : ManiaSelectionBlueprint
{
- public NoteMask(DrawableNote note)
+ public NoteSelectionBlueprint(DrawableNote note)
: base(note)
{
Scale = note.Scale;
diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaEditRulesetContainer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaEditRulesetContainer.cs
index a01947a60b..138a2c0273 100644
--- a/osu.Game.Rulesets.Mania/Edit/ManiaEditRulesetContainer.cs
+++ b/osu.Game.Rulesets.Mania/Edit/ManiaEditRulesetContainer.cs
@@ -20,8 +20,7 @@ namespace osu.Game.Rulesets.Mania.Edit
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
+ Size = Vector2.One
};
-
- protected override Vector2 PlayfieldArea => Vector2.One;
}
}
diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
index f37d8134ce..06d67821a9 100644
--- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
+++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
@@ -1,22 +1,23 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
-using osu.Game.Rulesets.Mania.Edit.Layers.Selection.Overlays;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.UI;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Game.Rulesets.Mania.Configuration;
+using osu.Game.Rulesets.Mania.Edit.Blueprints;
using osu.Game.Rulesets.Mania.UI;
+using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.Edit
{
- public class ManiaHitObjectComposer : HitObjectComposer
+ public class ManiaHitObjectComposer : HitObjectComposer
{
protected new ManiaConfigManager Config => (ManiaConfigManager)base.Config;
@@ -32,25 +33,22 @@ namespace osu.Game.Rulesets.Mania.Edit
return dependencies;
}
- protected override RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => new ManiaEditRulesetContainer(ruleset, beatmap);
+ protected override RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
+ => new ManiaEditRulesetContainer(ruleset, beatmap);
- protected override IReadOnlyList CompositionTools => new ICompositionTool[]
- {
- new HitObjectCompositionTool("Note"),
- new HitObjectCompositionTool("Hold"),
- };
+ protected override IReadOnlyList CompositionTools => Array.Empty();
- public override HitObjectMask CreateMaskFor(DrawableHitObject hitObject)
+ public override SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject)
{
switch (hitObject)
{
case DrawableNote note:
- return new NoteMask(note);
+ return new NoteSelectionBlueprint(note);
case DrawableHoldNote holdNote:
- return new HoldNoteMask(holdNote);
+ return new HoldNoteSelectionBlueprint(holdNote);
}
- return base.CreateMaskFor(hitObject);
+ return base.CreateBlueprintFor(hitObject);
}
}
}
diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs
index aecfb50fbe..4790a77cc0 100644
--- a/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs
+++ b/osu.Game.Rulesets.Mania/Mods/ManiaModDualStages.cs
@@ -5,13 +5,11 @@ using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
-using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
-using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.Mods
{
- public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter, IApplicableToRulesetContainer
+ public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter, IApplicableToBeatmap
{
public override string Name => "Dual Stages";
public override string ShortenedName => "DS";
@@ -34,22 +32,21 @@ namespace osu.Game.Rulesets.Mania.Mods
mbc.TargetColumns *= 2;
}
- public void ApplyToRulesetContainer(RulesetContainer rulesetContainer)
+ public void ApplyToBeatmap(Beatmap beatmap)
{
- var mrc = (ManiaRulesetContainer)rulesetContainer;
-
- // Although this can work, for now let's not allow keymods for mania-specific beatmaps
if (isForCurrentRuleset)
return;
+ var maniaBeatmap = (ManiaBeatmap)beatmap;
+
var newDefinitions = new List();
- foreach (var existing in mrc.Beatmap.Stages)
+ foreach (var existing in maniaBeatmap.Stages)
{
newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 });
newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 });
}
- mrc.Beatmap.Stages = newDefinitions;
+ maniaBeatmap.Stages = newDefinitions;
}
public PlayfieldType PlayfieldType => PlayfieldType.Dual;
diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs
index d489d48fc3..09976e5994 100644
--- a/osu.Game.Rulesets.Mania/UI/Column.cs
+++ b/osu.Game.Rulesets.Mania/UI/Column.cs
@@ -30,8 +30,6 @@ namespace osu.Game.Rulesets.Mania.UI
internal readonly Container TopLevelContainer;
private readonly Container explosionContainer;
- protected override Container Content => hitObjectArea;
-
public Column()
{
RelativeSizeAxes = Axes.Y;
@@ -54,7 +52,10 @@ namespace osu.Game.Rulesets.Mania.UI
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
- hitObjectArea = new ColumnHitObjectArea { RelativeSizeAxes = Axes.Both },
+ hitObjectArea = new ColumnHitObjectArea(HitObjectContainer)
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
explosionContainer = new Container
{
Name = "Hit explosions",
diff --git a/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs b/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs
index b5dfb0949a..5a4adfd72e 100644
--- a/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs
+++ b/osu.Game.Rulesets.Mania/UI/Components/ColumnHitObjectArea.cs
@@ -8,28 +8,24 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
+using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Mania.UI.Components
{
- public class ColumnHitObjectArea : Container, IHasAccentColour
+ public class ColumnHitObjectArea : CompositeDrawable, IHasAccentColour
{
private const float hit_target_height = 10;
private const float hit_target_bar_height = 2;
- private Container content;
- protected override Container Content => content;
-
private readonly IBindable direction = new Bindable();
- private Container hitTargetLine;
+ private readonly Container hitTargetLine;
+ private readonly Drawable hitTargetBar;
- [BackgroundDependencyLoader]
- private void load(IScrollingInfo scrollingInfo)
+ public ColumnHitObjectArea(HitObjectContainer hitObjectContainer)
{
- Drawable hitTargetBar;
-
InternalChildren = new[]
{
hitTargetBar = new Box
@@ -45,13 +41,13 @@ namespace osu.Game.Rulesets.Mania.UI.Components
Masking = true,
Child = new Box { RelativeSizeAxes = Axes.Both }
},
- content = new Container
- {
- Name = "Hit objects",
- RelativeSizeAxes = Axes.Both,
- },
+ hitObjectContainer
};
+ }
+ [BackgroundDependencyLoader]
+ private void load(IScrollingInfo scrollingInfo)
+ {
direction.BindTo(scrollingInfo.Direction);
direction.BindValueChanged(direction =>
{
diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs
index dc66249cd9..f78cfefab8 100644
--- a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs
+++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs
@@ -30,10 +30,11 @@ namespace osu.Game.Rulesets.Mania.UI
if (Result.IsHit)
{
- this.ScaleTo(0.8f);
- this.ScaleTo(1, 250, Easing.OutElastic);
+ JudgementBody.ScaleTo(0.8f);
+ JudgementBody.ScaleTo(1, 250, Easing.OutElastic);
- this.Delay(50).FadeOut(200).ScaleTo(0.75f, 250);
+ JudgementBody.Delay(50).ScaleTo(0.75f, 250);
+ this.Delay(50).FadeOut(200);
}
Expire();
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
index 999f84ed8e..5c3a618a19 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
@@ -10,6 +10,7 @@ using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects.Drawables;
+using OpenTK;
namespace osu.Game.Rulesets.Mania.UI
{
@@ -25,6 +26,8 @@ namespace osu.Game.Rulesets.Mania.UI
if (stageDefinitions.Count <= 0)
throw new ArgumentException("Can't have zero or fewer stages.");
+ Size = new Vector2(1, 0.8f);
+
GridContainer playfieldGrid;
AddInternal(playfieldGrid = new GridContainer
{
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs
index 09ebde2799..500fb5a631 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs
@@ -24,7 +24,6 @@ using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
-using OpenTK;
namespace osu.Game.Rulesets.Mania.UI
{
@@ -97,7 +96,7 @@ namespace osu.Game.Rulesets.Mania.UI
public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant);
- protected override DrawableHitObject GetVisualRepresentation(ManiaHitObject h)
+ public override DrawableHitObject GetVisualRepresentation(ManiaHitObject h)
{
switch (h)
{
@@ -110,8 +109,6 @@ namespace osu.Game.Rulesets.Mania.UI
}
}
- protected override Vector2 PlayfieldArea => new Vector2(1, 0.8f);
-
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);
}
}
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaScrollingPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaScrollingPlayfield.cs
index 4d6c5a747a..8ee0fbf7fe 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaScrollingPlayfield.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaScrollingPlayfield.cs
@@ -7,7 +7,7 @@ using osu.Game.Rulesets.UI.Scrolling;
namespace osu.Game.Rulesets.Mania.UI
{
- public class ManiaScrollingPlayfield : ScrollingPlayfield
+ public abstract class ManiaScrollingPlayfield : ScrollingPlayfield
{
private readonly IBindable direction = new Bindable();
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs
index f292d5ff16..8cf49686b9 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs
@@ -30,8 +30,7 @@ namespace osu.Game.Rulesets.Mania.UI
public IReadOnlyList Columns => columnFlow.Children;
private readonly FillFlowContainer columnFlow;
- protected override Container Content => barLineContainer;
- private readonly Container barLineContainer;
+ private readonly Container barLineContainer;
public Container Judgements => judgements;
private readonly JudgementContainer judgements;
@@ -105,6 +104,7 @@ namespace osu.Game.Rulesets.Mania.UI
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Y,
+ Child = HitObjectContainer
}
},
judgements = new JudgementContainer
diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseHitCirclePlacementBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCirclePlacementBlueprint.cs
new file mode 100644
index 0000000000..313438a337
--- /dev/null
+++ b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCirclePlacementBlueprint.cs
@@ -0,0 +1,19 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Tests.Visual;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ public class TestCaseHitCirclePlacementBlueprint : PlacementBlueprintTestCase
+ {
+ protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableHitCircle((HitCircle)hitObject);
+ protected override PlacementBlueprint CreateBlueprint() => new HitCirclePlacementBlueprint();
+ }
+}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircleSelectionBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircleSelectionBlueprint.cs
new file mode 100644
index 0000000000..9662e0018f
--- /dev/null
+++ b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircleSelectionBlueprint.cs
@@ -0,0 +1,29 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Tests.Visual;
+using OpenTK;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ public class TestCaseHitCircleSelectionBlueprint : SelectionBlueprintTestCase
+ {
+ private readonly DrawableHitCircle drawableObject;
+
+ public TestCaseHitCircleSelectionBlueprint()
+ {
+ var hitCircle = new HitCircle { Position = new Vector2(256, 192) };
+ hitCircle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = 2 });
+
+ Add(drawableObject = new DrawableHitCircle(hitCircle));
+ }
+
+ protected override SelectionBlueprint CreateBlueprint() => new HitCircleSelectionBlueprint(drawableObject);
+ }
+}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs
index 3f9464a98f..0bd6bb5abc 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs
@@ -108,7 +108,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = Time.Current + 1000,
Position = new Vector2(239, 176),
- ControlPoints = new List
+ ControlPoints = new[]
{
Vector2.Zero,
new Vector2(154, 28),
@@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Osu.Tests
},
Distance = 700,
RepeatCount = repeats,
- RepeatSamples = createEmptySamples(repeats),
+ NodeSamples = createEmptySamples(repeats),
StackHeight = 10
};
@@ -141,14 +141,14 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = Time.Current + 1000,
Position = new Vector2(-(distance / 2), 0),
- ControlPoints = new List
+ ControlPoints = new[]
{
Vector2.Zero,
new Vector2(distance, 0),
},
Distance = distance,
RepeatCount = repeats,
- RepeatSamples = createEmptySamples(repeats),
+ NodeSamples = createEmptySamples(repeats),
StackHeight = stackHeight
};
@@ -161,7 +161,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = Time.Current + 1000,
Position = new Vector2(-200, 0),
- ControlPoints = new List
+ ControlPoints = new[]
{
Vector2.Zero,
new Vector2(200, 200),
@@ -169,7 +169,7 @@ namespace osu.Game.Rulesets.Osu.Tests
},
Distance = 600,
RepeatCount = repeats,
- RepeatSamples = createEmptySamples(repeats)
+ NodeSamples = createEmptySamples(repeats)
};
addSlider(slider, 2, 3);
@@ -181,10 +181,10 @@ namespace osu.Game.Rulesets.Osu.Tests
{
var slider = new Slider
{
- CurveType = CurveType.Linear,
+ PathType = PathType.Linear,
StartTime = Time.Current + 1000,
Position = new Vector2(-200, 0),
- ControlPoints = new List
+ ControlPoints = new[]
{
Vector2.Zero,
new Vector2(150, 75),
@@ -195,7 +195,7 @@ namespace osu.Game.Rulesets.Osu.Tests
},
Distance = 793.4417,
RepeatCount = repeats,
- RepeatSamples = createEmptySamples(repeats)
+ NodeSamples = createEmptySamples(repeats)
};
addSlider(slider, 2, 3);
@@ -207,10 +207,10 @@ namespace osu.Game.Rulesets.Osu.Tests
{
var slider = new Slider
{
- CurveType = CurveType.Bezier,
+ PathType = PathType.Bezier,
StartTime = Time.Current + 1000,
Position = new Vector2(-200, 0),
- ControlPoints = new List
+ ControlPoints = new[]
{
Vector2.Zero,
new Vector2(150, 75),
@@ -220,7 +220,7 @@ namespace osu.Game.Rulesets.Osu.Tests
},
Distance = 480,
RepeatCount = repeats,
- RepeatSamples = createEmptySamples(repeats)
+ NodeSamples = createEmptySamples(repeats)
};
addSlider(slider, 2, 3);
@@ -232,10 +232,10 @@ namespace osu.Game.Rulesets.Osu.Tests
{
var slider = new Slider
{
- CurveType = CurveType.Linear,
+ PathType = PathType.Linear,
StartTime = Time.Current + 1000,
Position = new Vector2(0, 0),
- ControlPoints = new List
+ ControlPoints = new[]
{
Vector2.Zero,
new Vector2(-200, 0),
@@ -246,7 +246,7 @@ namespace osu.Game.Rulesets.Osu.Tests
},
Distance = 1000,
RepeatCount = repeats,
- RepeatSamples = createEmptySamples(repeats)
+ NodeSamples = createEmptySamples(repeats)
};
addSlider(slider, 2, 3);
@@ -264,8 +264,8 @@ namespace osu.Game.Rulesets.Osu.Tests
{
StartTime = Time.Current + 1000,
Position = new Vector2(-100, 0),
- CurveType = CurveType.Catmull,
- ControlPoints = new List
+ PathType = PathType.Catmull,
+ ControlPoints = new[]
{
Vector2.Zero,
new Vector2(50, -50),
@@ -274,7 +274,7 @@ namespace osu.Game.Rulesets.Osu.Tests
},
Distance = 300,
RepeatCount = repeats,
- RepeatSamples = repeatSamples
+ NodeSamples = repeatSamples
};
addSlider(slider, 3, 1);
diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSliderPlacementBlueprint.cs
new file mode 100644
index 0000000000..1f693ad9f4
--- /dev/null
+++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSliderPlacementBlueprint.cs
@@ -0,0 +1,19 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Tests.Visual;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ public class TestCaseSliderPlacementBlueprint : PlacementBlueprintTestCase
+ {
+ protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableSlider((Slider)hitObject);
+ protected override PlacementBlueprint CreateBlueprint() => new SliderPlacementBlueprint();
+ }
+}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSliderSelectionBlueprint.cs
new file mode 100644
index 0000000000..78e3d76313
--- /dev/null
+++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSliderSelectionBlueprint.cs
@@ -0,0 +1,55 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections.Generic;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders;
+using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Tests.Visual;
+using OpenTK;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ public class TestCaseSliderSelectionBlueprint : SelectionBlueprintTestCase
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(SliderSelectionBlueprint),
+ typeof(SliderCircleSelectionBlueprint),
+ typeof(SliderBodyPiece),
+ typeof(SliderCircle),
+ typeof(PathControlPointVisualiser),
+ typeof(PathControlPointPiece)
+ };
+
+ private readonly DrawableSlider drawableObject;
+
+ public TestCaseSliderSelectionBlueprint()
+ {
+ var slider = new Slider
+ {
+ Position = new Vector2(256, 192),
+ ControlPoints = new[]
+ {
+ Vector2.Zero,
+ new Vector2(150, 150),
+ new Vector2(300, 0)
+ },
+ PathType = PathType.Bezier,
+ Distance = 350
+ };
+
+ slider.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = 2 });
+
+ Add(drawableObject = new DrawableSlider(slider));
+ }
+
+ protected override SelectionBlueprint CreateBlueprint() => new SliderSelectionBlueprint(drawableObject);
+ }
+}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSpinnerPlacementBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSpinnerPlacementBlueprint.cs
new file mode 100644
index 0000000000..9a90be2582
--- /dev/null
+++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSpinnerPlacementBlueprint.cs
@@ -0,0 +1,20 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Tests.Visual;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ public class TestCaseSpinnerPlacementBlueprint : PlacementBlueprintTestCase
+ {
+ protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableSpinner((Spinner)hitObject);
+
+ protected override PlacementBlueprint CreateBlueprint() => new SpinnerPlacementBlueprint();
+ }
+}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSpinnerSelectionBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSpinnerSelectionBlueprint.cs
new file mode 100644
index 0000000000..a0cfd4487e
--- /dev/null
+++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSpinnerSelectionBlueprint.cs
@@ -0,0 +1,50 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections.Generic;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Beatmaps;
+using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners;
+using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners.Components;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Tests.Visual;
+using OpenTK;
+
+namespace osu.Game.Rulesets.Osu.Tests
+{
+ public class TestCaseSpinnerSelectionBlueprint : SelectionBlueprintTestCase
+ {
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(SpinnerSelectionBlueprint),
+ typeof(SpinnerPiece)
+ };
+
+ private readonly DrawableSpinner drawableSpinner;
+
+ public TestCaseSpinnerSelectionBlueprint()
+ {
+ var spinner = new Spinner
+ {
+ Position = new Vector2(256, 256),
+ StartTime = -1000,
+ EndTime = 2000
+ };
+ spinner.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = 2 });
+
+ Add(new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Size = new Vector2(0.5f),
+ Child = drawableSpinner = new DrawableSpinner(spinner)
+ });
+ }
+
+ protected override SelectionBlueprint CreateBlueprint() => new SpinnerSelectionBlueprint(drawableSpinner) { Size = new Vector2(0.5f) };
+ }
+}
diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
index 247d5e18c1..6117812f45 100644
--- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
+++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj
@@ -2,9 +2,10 @@
-
-
-
+
+
+
+
WinExe
diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs
index 9e0e649eb2..87c81cdd3b 100644
--- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs
@@ -36,14 +36,17 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
StartTime = original.StartTime,
Samples = original.Samples,
ControlPoints = curveData.ControlPoints,
- CurveType = curveData.CurveType,
+ PathType = curveData.PathType,
Distance = curveData.Distance,
- RepeatSamples = curveData.RepeatSamples,
+ NodeSamples = curveData.NodeSamples,
RepeatCount = curveData.RepeatCount,
Position = positionData?.Position ?? Vector2.Zero,
NewCombo = comboData?.NewCombo ?? false,
ComboOffset = comboData?.ComboOffset ?? 0,
- LegacyLastTickOffset = legacyOffset?.LegacyLastTickOffset
+ LegacyLastTickOffset = legacyOffset?.LegacyLastTickOffset,
+ // prior to v8, speed multipliers don't adjust for how many ticks are generated over the same distance.
+ // this results in more (or less) ticks being generated in )Beatmap);
+
+ var osuBeatmap = (Beatmap)Beatmap;
+
+ // Reset stacking
+ foreach (var h in osuBeatmap.HitObjects)
+ h.StackHeight = 0;
+
+ if (Beatmap.BeatmapInfo.BeatmapVersion >= 6)
+ applyStacking(osuBeatmap);
+ else
+ applyStackingOld(osuBeatmap);
}
private void applyStacking(Beatmap beatmap)
{
- const int stack_distance = 3;
-
- // Reset stacking
- for (int i = 0; i <= beatmap.HitObjects.Count - 1; i++)
- beatmap.HitObjects[i].StackHeight = 0;
-
// Extend the end index to include objects they are stacked on
int extendedEndIndex = beatmap.HitObjects.Count - 1;
for (int i = beatmap.HitObjects.Count - 1; i >= 0; i--)
@@ -167,5 +173,40 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
}
}
}
+
+ private void applyStackingOld(Beatmap beatmap)
+ {
+ for (int i = 0; i < beatmap.HitObjects.Count; i++)
+ {
+ OsuHitObject currHitObject = beatmap.HitObjects[i];
+
+ if (currHitObject.StackHeight != 0 && !(currHitObject is Slider))
+ continue;
+
+ double startTime = (currHitObject as IHasEndTime)?.EndTime ?? currHitObject.StartTime;
+ int sliderStack = 0;
+
+ for (int j = i + 1; j < beatmap.HitObjects.Count; j++)
+ {
+ double stackThreshold = beatmap.HitObjects[i].TimePreempt * beatmap.BeatmapInfo.StackLeniency;
+
+ if (beatmap.HitObjects[j].StartTime - stackThreshold > startTime)
+ break;
+
+ if (Vector2Extensions.Distance(beatmap.HitObjects[j].Position, currHitObject.Position) < stack_distance)
+ {
+ currHitObject.StackHeight++;
+ startTime = (beatmap.HitObjects[j] as IHasEndTime)?.EndTime ?? beatmap.HitObjects[i].StartTime;
+ }
+ else if (Vector2Extensions.Distance(beatmap.HitObjects[j].Position, currHitObject.EndPosition) < stack_distance)
+ {
+ //Case for sliders - bump notes down and right, rather than up and left.
+ sliderStack++;
+ beatmap.HitObjects[j].StackHeight -= sliderStack;
+ startTime = (beatmap.HitObjects[j] as IHasEndTime)?.EndTime ?? beatmap.HitObjects[i].StartTime;
+ }
+ }
+ }
+ }
}
}
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
index 5e91ed7a97..8fc2b69267 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
@@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double sectionLength = section_length * timeRate;
// The first object doesn't generate a strain, so we begin with an incremented section end
- double currentSectionEnd = 2 * sectionLength;
+ double currentSectionEnd = Math.Ceiling(beatmap.HitObjects.First().StartTime / sectionLength) * sectionLength;
foreach (OsuDifficultyHitObject h in difficultyBeatmap)
{
@@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double hitWindowGreat = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate;
double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / timeRate;
- int maxCombo = beatmap.HitObjects.Count();
+ int maxCombo = beatmap.HitObjects.Count;
// Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above)
maxCombo += beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.Count - 1);
diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
index d4a60dd52f..b0887ac72b 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs
@@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
{
countHitCircles = Beatmap.HitObjects.Count(h => h is HitCircle);
- beatmapMaxCombo = Beatmap.HitObjects.Count();
+ beatmapMaxCombo = Beatmap.HitObjects.Count;
// Add the ticks + tail of the slider. 1 is subtracted because the "headcircle" would be counted twice (once for the slider itself in the line above)
beatmapMaxCombo += Beatmap.HitObjects.OfType().Sum(s => s.NestedHitObjects.Count - 1);
}
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyBeatmap.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyBeatmap.cs
index 4443a0e66b..24d4677981 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyBeatmap.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyBeatmap.cs
@@ -3,6 +3,7 @@
using System.Collections;
using System.Collections.Generic;
+using System.Linq;
using osu.Game.Rulesets.Osu.Objects;
namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
@@ -23,8 +24,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
{
// Sort OsuHitObjects by StartTime - they are not correctly ordered in some cases.
// This should probably happen before the objects reach the difficulty calculator.
- objects.Sort((a, b) => a.StartTime.CompareTo(b.StartTime));
- difficultyObjects = createDifficultyObjectEnumerator(objects, timeRate);
+ difficultyObjects = createDifficultyObjectEnumerator(objects.OrderBy(h => h.StartTime).ToList(), timeRate);
}
///
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
index 29de23406b..39e3c009da 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Preprocessing/OsuDifficultyHitObject.cs
@@ -21,15 +21,25 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
public OsuHitObject BaseObject { get; }
///
- /// Normalized distance from the of the previous .
+ /// Normalized distance from the end position of the previous to the start position of this .
///
- public double Distance { get; private set; }
+ public double JumpDistance { get; private set; }
+
+ ///
+ /// Normalized distance between the start and end position of the previous .
+ ///
+ public double TravelDistance { get; private set; }
///
/// Milliseconds elapsed since the StartTime of the previous .
///
public double DeltaTime { get; private set; }
+ ///
+ /// Milliseconds elapsed since the start time of the previous , with a minimum of 50ms.
+ ///
+ public double StrainTime { get; private set; }
+
private readonly OsuHitObject lastObject;
private readonly double timeRate;
@@ -51,31 +61,35 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
private void setDistances()
{
// We will scale distances by this factor, so we can assume a uniform CircleSize among beatmaps.
- double scalingFactor = normalized_radius / BaseObject.Radius;
+ float scalingFactor = normalized_radius / (float)BaseObject.Radius;
if (BaseObject.Radius < 30)
{
- double smallCircleBonus = Math.Min(30 - BaseObject.Radius, 5) / 50;
+ float smallCircleBonus = Math.Min(30 - (float)BaseObject.Radius, 5) / 50;
scalingFactor *= 1 + smallCircleBonus;
}
Vector2 lastCursorPosition = lastObject.StackedPosition;
- float lastTravelDistance = 0;
var lastSlider = lastObject as Slider;
if (lastSlider != null)
{
computeSliderCursorPosition(lastSlider);
lastCursorPosition = lastSlider.LazyEndPosition ?? lastCursorPosition;
- lastTravelDistance = lastSlider.LazyTravelDistance;
+
+ TravelDistance = lastSlider.LazyTravelDistance * scalingFactor;
}
- Distance = (lastTravelDistance + (BaseObject.StackedPosition - lastCursorPosition).Length) * scalingFactor;
+ // Don't need to jump to reach spinners
+ if (!(BaseObject is Spinner))
+ JumpDistance = (BaseObject.StackedPosition * scalingFactor - lastCursorPosition * scalingFactor).Length;
}
private void setTimingValues()
{
- // Every timing inverval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure.
- DeltaTime = Math.Max(50, (BaseObject.StartTime - lastObject.StartTime) / timeRate);
+ DeltaTime = (BaseObject.StartTime - lastObject.StartTime) / timeRate;
+
+ // Every strain interval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure
+ StrainTime = Math.Max(50, DeltaTime);
}
private void computeSliderCursorPosition(Slider slider)
@@ -87,8 +101,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
float approxFollowCircleRadius = (float)(slider.Radius * 3);
var computeVertex = new Action(t =>
{
+ double progress = ((int)t - (int)slider.StartTime) / (float)(int)slider.SpanDuration;
+ if (progress % 2 > 1)
+ progress = 1 - progress % 1;
+ else
+ progress = progress % 1;
+
// ReSharper disable once PossibleInvalidOperationException (bugged in current r# version)
- var diff = slider.StackedPositionAt(t) - slider.LazyEndPosition.Value;
+ var diff = slider.StackedPosition + slider.Path.PositionAt(progress) - slider.LazyEndPosition.Value;
float dist = diff.Length;
if (dist > approxFollowCircleRadius)
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs
index 0a45c62671..f11b6d66f6 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs
@@ -14,6 +14,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
protected override double SkillMultiplier => 26.25;
protected override double StrainDecayBase => 0.15;
- protected override double StrainValueOf(OsuDifficultyHitObject current) => Math.Pow(current.Distance, 0.99) / current.DeltaTime;
+ protected override double StrainValueOf(OsuDifficultyHitObject current)
+ => (Math.Pow(current.TravelDistance, 0.99) + Math.Pow(current.JumpDistance, 0.99)) / current.StrainTime;
}
}
diff --git a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
index b807f20037..1cde03624b 100644
--- a/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
+++ b/osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
@@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
protected override double StrainValueOf(OsuDifficultyHitObject current)
{
- double distance = current.Distance;
+ double distance = current.TravelDistance + current.JumpDistance;
double speedValue;
if (distance > single_spacing_threshold)
@@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
else
speedValue = 0.95;
- return speedValue / current.DeltaTime;
+ return speedValue / current.StrainTime;
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCirclePiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCirclePiece.cs
new file mode 100644
index 0000000000..9c33435285
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCirclePiece.cs
@@ -0,0 +1,44 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Graphics;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
+using OpenTK;
+
+namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components
+{
+ public class HitCirclePiece : CompositeDrawable
+ {
+ private readonly HitCircle hitCircle;
+
+ public HitCirclePiece(HitCircle hitCircle)
+ {
+ this.hitCircle = hitCircle;
+ Origin = Anchor.Centre;
+
+ Size = new Vector2((float)OsuHitObject.OBJECT_RADIUS * 2);
+ Scale = new Vector2(hitCircle.Scale);
+ CornerRadius = Size.X / 2;
+
+ InternalChild = new RingPiece();
+
+ hitCircle.PositionChanged += _ => UpdatePosition();
+ hitCircle.StackHeightChanged += _ => UpdatePosition();
+ hitCircle.ScaleChanged += _ => Scale = new Vector2(hitCircle.Scale);
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ Colour = colours.Yellow;
+
+ UpdatePosition();
+ }
+
+ protected virtual void UpdatePosition() => Position = hitCircle.StackedPosition;
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs
new file mode 100644
index 0000000000..eddd399a4a
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCirclePlacementBlueprint.cs
@@ -0,0 +1,42 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Input.Events;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
+using osu.Game.Rulesets.Osu.Objects;
+
+namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles
+{
+ public class HitCirclePlacementBlueprint : PlacementBlueprint
+ {
+ public new HitCircle HitObject => (HitCircle)base.HitObject;
+
+ public HitCirclePlacementBlueprint()
+ : base(new HitCircle())
+ {
+ InternalChild = new HitCirclePiece(HitObject);
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ // Fixes a 1-frame position discrpancy due to the first mouse move event happening in the next frame
+ HitObject.Position = GetContainingInputManager().CurrentState.Mouse.Position;
+ }
+
+ protected override bool OnClick(ClickEvent e)
+ {
+ HitObject.StartTime = EditorClock.CurrentTime;
+ EndPlacement();
+ return true;
+ }
+
+ protected override bool OnMouseMove(MouseMoveEvent e)
+ {
+ HitObject.Position = e.MousePosition;
+ return true;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCircleSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCircleSelectionBlueprint.cs
new file mode 100644
index 0000000000..a59dac1834
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/HitCircleSelectionBlueprint.cs
@@ -0,0 +1,18 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+
+namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles
+{
+ public class HitCircleSelectionBlueprint : OsuSelectionBlueprint
+ {
+ public HitCircleSelectionBlueprint(DrawableHitCircle hitCircle)
+ : base(hitCircle)
+ {
+ InternalChild = new HitCirclePiece((HitCircle)hitCircle.HitObject);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs
new file mode 100644
index 0000000000..8431d5d5d0
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/OsuSelectionBlueprint.cs
@@ -0,0 +1,22 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Input.Events;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Objects;
+
+namespace osu.Game.Rulesets.Osu.Edit.Blueprints
+{
+ public class OsuSelectionBlueprint : SelectionBlueprint
+ {
+ protected OsuHitObject OsuObject => (OsuHitObject)HitObject.HitObject;
+
+ public OsuSelectionBlueprint(DrawableHitObject hitObject)
+ : base(hitObject)
+ {
+ }
+
+ public override void AdjustPosition(DragEvent dragEvent) => OsuObject.Position += dragEvent.Delta;
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs
new file mode 100644
index 0000000000..175e9d79f4
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs
@@ -0,0 +1,113 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.Linq;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Lines;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Input.Events;
+using osu.Game.Graphics;
+using osu.Game.Rulesets.Osu.Objects;
+using OpenTK;
+
+namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
+{
+ public class PathControlPointPiece : CompositeDrawable
+ {
+ private readonly Slider slider;
+ private readonly int index;
+
+ private readonly Path path;
+ private readonly CircularContainer marker;
+
+ [Resolved]
+ private OsuColour colours { get; set; }
+
+ public PathControlPointPiece(Slider slider, int index)
+ {
+ this.slider = slider;
+ this.index = index;
+
+ Origin = Anchor.Centre;
+ AutoSizeAxes = Axes.Both;
+
+ InternalChildren = new Drawable[]
+ {
+ path = new SmoothPath
+ {
+ Anchor = Anchor.Centre,
+ PathWidth = 1
+ },
+ marker = new CircularContainer
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(10),
+ Masking = true,
+ Child = new Box { RelativeSizeAxes = Axes.Both }
+ }
+ };
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ Position = slider.StackedPosition + slider.ControlPoints[index];
+
+ marker.Colour = isSegmentSeparator ? colours.Red : colours.Yellow;
+
+ path.ClearVertices();
+
+ if (index != slider.ControlPoints.Length - 1)
+ {
+ path.AddVertex(Vector2.Zero);
+ path.AddVertex(slider.ControlPoints[index + 1] - slider.ControlPoints[index]);
+ }
+
+ path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero);
+ }
+
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => marker.ReceivePositionalInputAt(screenSpacePos);
+
+ protected override bool OnDragStart(DragStartEvent e) => true;
+
+ protected override bool OnDrag(DragEvent e)
+ {
+ var newControlPoints = slider.ControlPoints.ToArray();
+
+ if (index == 0)
+ {
+ // Special handling for the head - only the position of the slider changes
+ slider.Position += e.Delta;
+
+ // Since control points are relative to the position of the slider, they all need to be offset backwards by the delta
+ for (int i = 1; i < newControlPoints.Length; i++)
+ newControlPoints[i] -= e.Delta;
+ }
+ else
+ newControlPoints[index] += e.Delta;
+
+ if (isSegmentSeparatorWithNext)
+ newControlPoints[index + 1] = newControlPoints[index];
+
+ if (isSegmentSeparatorWithPrevious)
+ newControlPoints[index - 1] = newControlPoints[index];
+
+ slider.ControlPoints = newControlPoints;
+ slider.Path.Calculate(true);
+
+ return true;
+ }
+
+ protected override bool OnDragEnd(DragEndEvent e) => true;
+
+ private bool isSegmentSeparator => isSegmentSeparatorWithNext || isSegmentSeparatorWithPrevious;
+
+ private bool isSegmentSeparatorWithNext => index < slider.ControlPoints.Length - 1 && slider.ControlPoints[index + 1] == slider.ControlPoints[index];
+
+ private bool isSegmentSeparatorWithPrevious => index > 0 && slider.ControlPoints[index - 1] == slider.ControlPoints[index];
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
new file mode 100644
index 0000000000..db8e879126
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
@@ -0,0 +1,34 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Rulesets.Osu.Objects;
+
+namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
+{
+ public class PathControlPointVisualiser : CompositeDrawable
+ {
+ private readonly Slider slider;
+
+ private readonly Container pieces;
+
+ public PathControlPointVisualiser(Slider slider)
+ {
+ this.slider = slider;
+
+ InternalChild = pieces = new Container { RelativeSizeAxes = Axes.Both };
+
+ slider.ControlPointsChanged += _ => updatePathControlPoints();
+ updatePathControlPoints();
+ }
+
+ private void updatePathControlPoints()
+ {
+ while (slider.ControlPoints.Length > pieces.Count)
+ pieces.Add(new PathControlPointPiece(slider, pieces.Count));
+ while (slider.ControlPoints.Length < pieces.Count)
+ pieces.Remove(pieces[pieces.Count - 1]);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs
new file mode 100644
index 0000000000..6fc7d39e6c
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs
@@ -0,0 +1,59 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.Collections.Generic;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics.Containers;
+using osu.Game.Graphics;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
+using OpenTK;
+using OpenTK.Graphics;
+
+namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
+{
+ public class SliderBodyPiece : CompositeDrawable
+ {
+ private readonly Slider slider;
+ private readonly ManualSliderBody body;
+
+ public SliderBodyPiece(Slider slider)
+ {
+ this.slider = slider;
+
+ InternalChild = body = new ManualSliderBody
+ {
+ AccentColour = Color4.Transparent,
+ PathWidth = slider.Scale * 64
+ };
+
+ slider.PositionChanged += _ => updatePosition();
+ slider.ScaleChanged += _ => body.PathWidth = slider.Scale * 64;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ body.BorderColour = colours.Yellow;
+
+ updatePosition();
+ }
+
+ private void updatePosition() => Position = slider.StackedPosition;
+
+ protected override void Update()
+ {
+ base.Update();
+
+ slider.Path.Calculate();
+
+ var vertices = new List();
+ slider.Path.GetPathToProgress(vertices, 0, 1);
+
+ body.SetVertices(vertices);
+
+ Size = body.Size;
+ OriginPosition = body.PathOffset;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderCirclePiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderCirclePiece.cs
new file mode 100644
index 0000000000..a91739737f
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderCirclePiece.cs
@@ -0,0 +1,36 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
+using osu.Game.Rulesets.Osu.Objects;
+
+namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
+{
+ public class SliderCirclePiece : HitCirclePiece
+ {
+ private readonly Slider slider;
+ private readonly SliderPosition position;
+
+ public SliderCirclePiece(Slider slider, SliderPosition position)
+ : base(slider.HeadCircle)
+ {
+ this.slider = slider;
+ this.position = position;
+
+ slider.ControlPointsChanged += _ => UpdatePosition();
+ }
+
+ protected override void UpdatePosition()
+ {
+ switch (position)
+ {
+ case SliderPosition.Start:
+ Position = slider.StackedPosition + slider.Path.PositionAt(0);
+ break;
+ case SliderPosition.End:
+ Position = slider.StackedPosition + slider.Path.PositionAt(1);
+ break;
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderCircleSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderCircleSelectionBlueprint.cs
new file mode 100644
index 0000000000..4bac9d3556
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderCircleSelectionBlueprint.cs
@@ -0,0 +1,30 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Input.Events;
+using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+
+namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
+{
+ public class SliderCircleSelectionBlueprint : OsuSelectionBlueprint
+ {
+ private readonly Slider slider;
+
+ public SliderCircleSelectionBlueprint(DrawableOsuHitObject hitObject, Slider slider, SliderPosition position)
+ : base(hitObject)
+ {
+ this.slider = slider;
+
+ InternalChild = new SliderCirclePiece(slider, position);
+
+ Select();
+ }
+
+ // Todo: This is temporary, since the slider circle masks don't do anything special yet. In the future they will handle input.
+ public override bool HandlePositionalInput => false;
+
+ public override void AdjustPosition(DragEvent dragEvent) => slider.Position += dragEvent.Delta;
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs
new file mode 100644
index 0000000000..add9cb69f3
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs
@@ -0,0 +1,180 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using osu.Framework.Allocation;
+using osu.Framework.Extensions.IEnumerableExtensions;
+using osu.Framework.Graphics;
+using osu.Framework.Input.Events;
+using osu.Framework.MathUtils;
+using osu.Game.Graphics;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
+using OpenTK;
+using OpenTK.Input;
+
+namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
+{
+ public class SliderPlacementBlueprint : PlacementBlueprint
+ {
+ public new Objects.Slider HitObject => (Objects.Slider)base.HitObject;
+
+ private readonly List segments = new List();
+ private Vector2 cursor;
+
+ private PlacementState state;
+
+ public SliderPlacementBlueprint()
+ : base(new Objects.Slider())
+ {
+ RelativeSizeAxes = Axes.Both;
+ segments.Add(new Segment(Vector2.Zero));
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ InternalChildren = new Drawable[]
+ {
+ new SliderBodyPiece(HitObject),
+ new SliderCirclePiece(HitObject, SliderPosition.Start),
+ new SliderCirclePiece(HitObject, SliderPosition.End),
+ new PathControlPointVisualiser(HitObject),
+ };
+
+ setState(PlacementState.Initial);
+ }
+
+ protected override bool OnMouseMove(MouseMoveEvent e)
+ {
+ switch (state)
+ {
+ case PlacementState.Initial:
+ HitObject.Position = e.MousePosition;
+ return true;
+ case PlacementState.Body:
+ cursor = e.MousePosition - HitObject.Position;
+ return true;
+ }
+
+ return false;
+ }
+
+ protected override bool OnClick(ClickEvent e)
+ {
+ switch (state)
+ {
+ case PlacementState.Initial:
+ beginCurve();
+ break;
+ case PlacementState.Body:
+ switch (e.Button)
+ {
+ case MouseButton.Left:
+ segments.Last().ControlPoints.Add(cursor);
+ break;
+ }
+
+ break;
+ }
+
+ return true;
+ }
+
+ protected override bool OnMouseUp(MouseUpEvent e)
+ {
+ if (state == PlacementState.Body && e.Button == MouseButton.Right)
+ endCurve();
+ return base.OnMouseUp(e);
+ }
+
+ protected override bool OnDoubleClick(DoubleClickEvent e)
+ {
+ segments.Add(new Segment(segments[segments.Count - 1].ControlPoints.Last()));
+ return true;
+ }
+
+ private void beginCurve()
+ {
+ BeginPlacement();
+
+ HitObject.StartTime = EditorClock.CurrentTime;
+ setState(PlacementState.Body);
+ }
+
+ private void endCurve()
+ {
+ updateSlider();
+ EndPlacement();
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+ updateSlider();
+ }
+
+ private void updateSlider()
+ {
+ for (int i = 0; i < segments.Count; i++)
+ segments[i].Calculate(i == segments.Count - 1 ? (Vector2?)cursor : null);
+
+ HitObject.ControlPoints = segments.SelectMany(s => s.ControlPoints).Concat(cursor.Yield()).ToArray();
+ HitObject.PathType = HitObject.ControlPoints.Length > 2 ? PathType.Bezier : PathType.Linear;
+ HitObject.Distance = segments.Sum(s => s.Distance);
+ }
+
+ private void setState(PlacementState newState)
+ {
+ state = newState;
+ }
+
+ private enum PlacementState
+ {
+ Initial,
+ Body,
+ }
+
+ private class Segment
+ {
+ public float Distance { get; private set; }
+
+ public readonly List ControlPoints = new List();
+
+ public Segment(Vector2 offset)
+ {
+ ControlPoints.Add(offset);
+ }
+
+ public void Calculate(Vector2? cursor = null)
+ {
+ Span allControlPoints = stackalloc Vector2[ControlPoints.Count + (cursor.HasValue ? 1 : 0)];
+
+ for (int i = 0; i < ControlPoints.Count; i++)
+ allControlPoints[i] = ControlPoints[i];
+ if (cursor.HasValue)
+ allControlPoints[allControlPoints.Length - 1] = cursor.Value;
+
+ List result;
+
+ switch (allControlPoints.Length)
+ {
+ case 1:
+ case 2:
+ result = PathApproximator.ApproximateLinear(allControlPoints);
+ break;
+ default:
+ result = PathApproximator.ApproximateBezier(allControlPoints);
+ break;
+ }
+
+ Distance = 0;
+ for (int i = 0; i < result.Count - 1; i++)
+ Distance += Vector2.Distance(result[i], result[i + 1]);
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPosition.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPosition.cs
new file mode 100644
index 0000000000..a117a7056e
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPosition.cs
@@ -0,0 +1,11 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
+{
+ public enum SliderPosition
+ {
+ Start,
+ End
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs
new file mode 100644
index 0000000000..4810d76bf8
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs
@@ -0,0 +1,32 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics;
+using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using OpenTK;
+
+namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
+{
+ public class SliderSelectionBlueprint : OsuSelectionBlueprint
+ {
+ private readonly SliderCircleSelectionBlueprint headBlueprint;
+
+ public SliderSelectionBlueprint(DrawableSlider slider)
+ : base(slider)
+ {
+ var sliderObject = (Slider)slider.HitObject;
+
+ InternalChildren = new Drawable[]
+ {
+ new SliderBodyPiece(sliderObject),
+ headBlueprint = new SliderCircleSelectionBlueprint(slider.HeadCircle, sliderObject, SliderPosition.Start),
+ new SliderCircleSelectionBlueprint(slider.TailCircle, sliderObject, SliderPosition.End),
+ new PathControlPointVisualiser(sliderObject),
+ };
+ }
+
+ public override Vector2 SelectionPoint => headBlueprint.SelectionPoint;
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/Components/SpinnerPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/Components/SpinnerPiece.cs
new file mode 100644
index 0000000000..bd63a3e607
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/Components/SpinnerPiece.cs
@@ -0,0 +1,66 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Allocation;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
+using OpenTK;
+
+namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners.Components
+{
+ public class SpinnerPiece : CompositeDrawable
+ {
+ private readonly Spinner spinner;
+ private readonly CircularContainer circle;
+
+ public SpinnerPiece(Spinner spinner)
+ {
+ this.spinner = spinner;
+
+ Origin = Anchor.Centre;
+
+ RelativeSizeAxes = Axes.Both;
+ FillMode = FillMode.Fit;
+ Size = new Vector2(1.3f);
+
+ RingPiece ring;
+ InternalChildren = new Drawable[]
+ {
+ circle = new CircularContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ Alpha = 0.5f,
+ Child = new Box { RelativeSizeAxes = Axes.Both }
+ },
+ ring = new RingPiece
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre
+ }
+ };
+
+ ring.Scale = new Vector2(spinner.Scale);
+
+ spinner.PositionChanged += _ => updatePosition();
+ spinner.StackHeightChanged += _ => updatePosition();
+ spinner.ScaleChanged += _ => ring.Scale = new Vector2(spinner.Scale);
+
+ updatePosition();
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ Colour = colours.Yellow;
+ }
+
+ private void updatePosition() => Position = spinner.Position;
+
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => circle.ReceivePositionalInputAt(screenSpacePos);
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerPlacementBlueprint.cs
new file mode 100644
index 0000000000..c97adde427
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerPlacementBlueprint.cs
@@ -0,0 +1,45 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics;
+using osu.Framework.Input.Events;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners.Components;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.UI;
+
+namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners
+{
+ public class SpinnerPlacementBlueprint : PlacementBlueprint
+ {
+ public new Spinner HitObject => (Spinner)base.HitObject;
+
+ private readonly SpinnerPiece piece;
+
+ private bool isPlacingEnd;
+
+ public SpinnerPlacementBlueprint()
+ : base(new Spinner { Position = OsuPlayfield.BASE_SIZE / 2 })
+ {
+ InternalChild = piece = new SpinnerPiece(HitObject) { Alpha = 0.5f };
+ }
+
+ protected override bool OnClick(ClickEvent e)
+ {
+ if (isPlacingEnd)
+ {
+ HitObject.EndTime = EditorClock.CurrentTime;
+ EndPlacement();
+ }
+ else
+ {
+ HitObject.StartTime = EditorClock.CurrentTime;
+
+ isPlacingEnd = true;
+ piece.FadeTo(1f, 150, Easing.OutQuint);
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerSelectionBlueprint.cs
new file mode 100644
index 0000000000..9e9cc87c5e
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerSelectionBlueprint.cs
@@ -0,0 +1,29 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Input.Events;
+using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners.Components;
+using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using OpenTK;
+
+namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners
+{
+ public class SpinnerSelectionBlueprint : OsuSelectionBlueprint
+ {
+ private readonly SpinnerPiece piece;
+
+ public SpinnerSelectionBlueprint(DrawableSpinner spinner)
+ : base(spinner)
+ {
+ InternalChild = piece = new SpinnerPiece((Spinner)spinner.HitObject);
+ }
+
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => piece.ReceivePositionalInputAt(screenSpacePos);
+
+ public override void AdjustPosition(DragEvent dragEvent)
+ {
+ // Spinners don't support position adjustments
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/HitCircleCompositionTool.cs b/osu.Game.Rulesets.Osu/Edit/HitCircleCompositionTool.cs
new file mode 100644
index 0000000000..ddab70d53a
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/HitCircleCompositionTool.cs
@@ -0,0 +1,20 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Edit.Tools;
+using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
+using osu.Game.Rulesets.Osu.Objects;
+
+namespace osu.Game.Rulesets.Osu.Edit
+{
+ public class HitCircleCompositionTool : HitObjectCompositionTool
+ {
+ public HitCircleCompositionTool()
+ : base(nameof(HitCircle))
+ {
+ }
+
+ public override PlacementBlueprint CreatePlacementBlueprint() => new HitCirclePlacementBlueprint();
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleMask.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleMask.cs
deleted file mode 100644
index a2aa639004..0000000000
--- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/HitCircleMask.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using osu.Framework.Graphics;
-using osu.Framework.Allocation;
-using osu.Game.Graphics;
-using osu.Game.Rulesets.Edit;
-using osu.Game.Rulesets.Osu.Objects.Drawables;
-using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
-
-namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
-{
- public class HitCircleMask : HitObjectMask
- {
- public HitCircleMask(DrawableHitCircle hitCircle)
- : base(hitCircle)
- {
- Origin = Anchor.Centre;
-
- Position = hitCircle.Position;
- Size = hitCircle.Size;
- Scale = hitCircle.Scale;
-
- CornerRadius = Size.X / 2;
-
- AddInternal(new RingPiece());
-
- hitCircle.HitObject.PositionChanged += _ => Position = hitCircle.Position;
- }
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- Colour = colours.Yellow;
- }
- }
-}
diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs
deleted file mode 100644
index adb28289cf..0000000000
--- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderCircleMask.cs
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using osu.Framework.Allocation;
-using osu.Framework.Graphics;
-using osu.Game.Graphics;
-using osu.Game.Rulesets.Edit;
-using osu.Game.Rulesets.Osu.Objects;
-using osu.Game.Rulesets.Osu.Objects.Drawables;
-using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
-using OpenTK;
-
-namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
-{
- public class SliderCircleMask : HitObjectMask
- {
- public SliderCircleMask(DrawableHitCircle sliderHead, DrawableSlider slider)
- : this(sliderHead, Vector2.Zero, slider)
- {
- }
-
- public SliderCircleMask(DrawableSliderTail sliderTail, DrawableSlider slider)
- : this(sliderTail, ((Slider)slider.HitObject).Curve.PositionAt(1), slider)
- {
- }
-
- private readonly DrawableOsuHitObject hitObject;
-
- private SliderCircleMask(DrawableOsuHitObject hitObject, Vector2 position, DrawableSlider slider)
- : base(hitObject)
- {
- this.hitObject = hitObject;
-
- Origin = Anchor.Centre;
-
- Position = position;
- Size = slider.HeadCircle.Size;
- Scale = slider.HeadCircle.Scale;
-
- AddInternal(new RingPiece());
-
- Select();
- }
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- Colour = colours.Yellow;
- }
-
- protected override void Update()
- {
- base.Update();
-
- RelativeAnchorPosition = hitObject.RelativeAnchorPosition;
- }
-
- // Todo: This is temporary, since the slider circle masks don't do anything special yet. In the future they will handle input.
- public override bool HandleMouseInput => false;
- }
-}
diff --git a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderMask.cs b/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderMask.cs
deleted file mode 100644
index 0f6143a83d..0000000000
--- a/osu.Game.Rulesets.Osu/Edit/Layers/Selection/Overlays/SliderMask.cs
+++ /dev/null
@@ -1,67 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using osu.Framework.Allocation;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Primitives;
-using osu.Game.Graphics;
-using osu.Game.Rulesets.Edit;
-using osu.Game.Rulesets.Osu.Objects;
-using osu.Game.Rulesets.Osu.Objects.Drawables;
-using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
-using OpenTK;
-using OpenTK.Graphics;
-
-namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
-{
- public class SliderMask : HitObjectMask
- {
- private readonly SliderBody body;
- private readonly DrawableSlider slider;
-
- public SliderMask(DrawableSlider slider)
- : base(slider)
- {
- this.slider = slider;
-
- Position = slider.Position;
-
- var sliderObject = (Slider)slider.HitObject;
-
- InternalChildren = new Drawable[]
- {
- body = new SliderBody(sliderObject)
- {
- AccentColour = Color4.Transparent,
- PathWidth = sliderObject.Scale * 64
- },
- new SliderCircleMask(slider.HeadCircle, slider),
- new SliderCircleMask(slider.TailCircle, slider),
- };
-
- sliderObject.PositionChanged += _ => Position = slider.Position;
- }
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- body.BorderColour = colours.Yellow;
- }
-
- protected override void Update()
- {
- base.Update();
-
- Size = slider.Size;
- OriginPosition = slider.OriginPosition;
-
- // Need to cause one update
- body.UpdateProgress(0);
- }
-
- public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => body.ReceiveMouseInputAt(screenSpacePos);
-
- public override Vector2 SelectionPoint => ToScreenSpace(OriginPosition);
- public override Quad SelectionQuad => body.PathDrawQuad;
- }
-}
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs b/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs
index 6efa16bf56..8571de39f4 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs
@@ -4,6 +4,7 @@
using osu.Framework.Graphics.Cursor;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu.UI;
+using osu.Game.Rulesets.UI;
using OpenTK;
namespace osu.Game.Rulesets.Osu.Edit
@@ -15,8 +16,8 @@ namespace osu.Game.Rulesets.Osu.Edit
{
}
- protected override Vector2 PlayfieldArea => Vector2.One;
-
protected override CursorContainer CreateCursor() => null;
+
+ protected override Playfield CreatePlayfield() => new OsuPlayfield { Size = Vector2.One };
}
}
diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
index dce1fc2851..a706e1d4be 100644
--- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
+++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs
@@ -3,11 +3,14 @@
using System.Collections.Generic;
using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays;
+using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
+using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders;
+using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.UI;
@@ -15,35 +18,38 @@ using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Osu.Edit
{
- public class OsuHitObjectComposer : HitObjectComposer
+ public class OsuHitObjectComposer : HitObjectComposer
{
public OsuHitObjectComposer(Ruleset ruleset)
: base(ruleset)
{
}
- protected override RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => new OsuEditRulesetContainer(ruleset, beatmap);
+ protected override RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
+ => new OsuEditRulesetContainer(ruleset, beatmap);
- protected override IReadOnlyList CompositionTools => new ICompositionTool[]
+ protected override IReadOnlyList CompositionTools => new HitObjectCompositionTool[]
{
- new HitObjectCompositionTool(),
- new HitObjectCompositionTool(),
- new HitObjectCompositionTool()
+ new HitCircleCompositionTool(),
+ new SliderCompositionTool(),
+ new SpinnerCompositionTool()
};
- protected override ScalableContainer CreateLayerContainer() => new ScalableContainer(OsuPlayfield.BASE_SIZE.X) { RelativeSizeAxes = Axes.Both };
+ protected override Container CreateLayerContainer() => new PlayfieldAdjustmentContainer { RelativeSizeAxes = Axes.Both };
- public override HitObjectMask CreateMaskFor(DrawableHitObject hitObject)
+ public override SelectionBlueprint CreateBlueprintFor(DrawableHitObject hitObject)
{
switch (hitObject)
{
case DrawableHitCircle circle:
- return new HitCircleMask(circle);
+ return new HitCircleSelectionBlueprint(circle);
case DrawableSlider slider:
- return new SliderMask(slider);
+ return new SliderSelectionBlueprint(slider);
+ case DrawableSpinner spinner:
+ return new SpinnerSelectionBlueprint(spinner);
}
- return base.CreateMaskFor(hitObject);
+ return base.CreateBlueprintFor(hitObject);
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Edit/SliderCompositionTool.cs b/osu.Game.Rulesets.Osu/Edit/SliderCompositionTool.cs
new file mode 100644
index 0000000000..6d4f67c597
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/SliderCompositionTool.cs
@@ -0,0 +1,20 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Edit.Tools;
+using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders;
+using osu.Game.Rulesets.Osu.Objects;
+
+namespace osu.Game.Rulesets.Osu.Edit
+{
+ public class SliderCompositionTool : HitObjectCompositionTool
+ {
+ public SliderCompositionTool()
+ : base(nameof(Slider))
+ {
+ }
+
+ public override PlacementBlueprint CreatePlacementBlueprint() => new SliderPlacementBlueprint();
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Edit/SpinnerCompositionTool.cs b/osu.Game.Rulesets.Osu/Edit/SpinnerCompositionTool.cs
new file mode 100644
index 0000000000..5c19a1bac0
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Edit/SpinnerCompositionTool.cs
@@ -0,0 +1,20 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Edit.Tools;
+using osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners;
+using osu.Game.Rulesets.Osu.Objects;
+
+namespace osu.Game.Rulesets.Osu.Edit
+{
+ public class SpinnerCompositionTool : HitObjectCompositionTool
+ {
+ public SpinnerCompositionTool()
+ : base(nameof(Spinner))
+ {
+ }
+
+ public override PlacementBlueprint CreatePlacementBlueprint() => new SpinnerPlacementBlueprint();
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs
index 7a30e6b134..e01d71e1f8 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs
@@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using System.Collections.Generic;
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Rulesets.Mods;
@@ -33,11 +32,12 @@ namespace osu.Game.Rulesets.Osu.Mods
slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
slider.NestedHitObjects.OfType().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
- var newControlPoints = new List();
- slider.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, -c.Y)));
+ var newControlPoints = new Vector2[slider.ControlPoints.Length];
+ for (int i = 0; i < slider.ControlPoints.Length; i++)
+ newControlPoints[i] = new Vector2(slider.ControlPoints[i].X, -slider.ControlPoints[i].Y);
slider.ControlPoints = newControlPoints;
- slider.Curve?.Calculate(); // Recalculate the slider curve
+ slider.Path?.Calculate(); // Recalculate the slider curve
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs
index f3b7d60cf0..8d27502b3c 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs
@@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using osu.Framework.Input.States;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects;
@@ -77,7 +76,7 @@ namespace osu.Game.Rulesets.Osu.Mods
wasLeft = !wasLeft;
}
- osuInputManager.HandleCustomInput(new InputState(), state);
+ state.Apply(osuInputManager.CurrentState, osuInputManager);
}
public void ApplyToRulesetContainer(RulesetContainer rulesetContainer)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs
new file mode 100644
index 0000000000..440b314e5f
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs
@@ -0,0 +1,53 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections.Generic;
+using osu.Framework.Graphics;
+using osu.Game.Graphics;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Objects;
+using OpenTK;
+
+namespace osu.Game.Rulesets.Osu.Mods
+{
+ internal class OsuModTransform : Mod, IApplicableToDrawableHitObjects
+ {
+ public override string Name => "Transform";
+ public override string ShortenedName => "TR";
+ public override FontAwesome Icon => FontAwesome.fa_arrows;
+ public override ModType Type => ModType.Fun;
+ public override string Description => "Everything rotates. EVERYTHING.";
+ public override double ScoreMultiplier => 1;
+ public override Type[] IncompatibleMods => new[] { typeof(OsuModWiggle) };
+
+ private float theta;
+
+ public void ApplyToDrawableHitObjects(IEnumerable drawables)
+ {
+ foreach (var drawable in drawables)
+ {
+ var hitObject = (OsuHitObject) drawable.HitObject;
+
+ float appearDistance = (float)(hitObject.TimePreempt - hitObject.TimeFadeIn) / 2;
+
+ Vector2 originalPosition = drawable.Position;
+ Vector2 appearOffset = new Vector2((float)Math.Cos(theta), (float)Math.Sin(theta)) * appearDistance;
+
+ //the - 1 and + 1 prevents the hit objects to appear in the wrong position.
+ double appearTime = hitObject.StartTime - hitObject.TimePreempt - 1;
+ double moveDuration = hitObject.TimePreempt + 1;
+
+ using (drawable.BeginAbsoluteSequence(appearTime, true))
+ {
+ drawable
+ .MoveToOffset(appearOffset)
+ .MoveTo(originalPosition, moveDuration, Easing.InOutSine);
+ }
+
+ theta += (float) hitObject.TimeFadeIn / 1000;
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs
new file mode 100644
index 0000000000..e0a93453ce
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs
@@ -0,0 +1,72 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections.Generic;
+using osu.Framework.Graphics;
+using osu.Game.Graphics;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Osu.Objects;
+using OpenTK;
+
+namespace osu.Game.Rulesets.Osu.Mods
+{
+ internal class OsuModWiggle : Mod, IApplicableToDrawableHitObjects
+ {
+ public override string Name => "Wiggle";
+ public override string ShortenedName => "WG";
+ public override FontAwesome Icon => FontAwesome.fa_certificate;
+ public override ModType Type => ModType.Fun;
+ public override string Description => "They just won't stay still...";
+ public override double ScoreMultiplier => 1;
+ public override Type[] IncompatibleMods => new[] { typeof(OsuModTransform) };
+
+ private const int wiggle_duration = 90; // (ms) Higher = fewer wiggles
+ private const int wiggle_strength = 10; // Higher = stronger wiggles
+
+ public void ApplyToDrawableHitObjects(IEnumerable drawables)
+ {
+ foreach (var drawable in drawables)
+ drawable.ApplyCustomUpdateState += drawableOnApplyCustomUpdateState;
+ }
+
+ private void drawableOnApplyCustomUpdateState(DrawableHitObject drawable, ArmedState state)
+ {
+ var osuObject = (OsuHitObject)drawable.HitObject;
+ Vector2 origin = drawable.Position;
+
+ // Wiggle the repeat points with the slider instead of independently.
+ // Also fixes an issue with repeat points being positioned incorrectly.
+ if (osuObject is RepeatPoint)
+ return;
+
+ Random objRand = new Random((int)osuObject.StartTime);
+
+ // Wiggle all objects during TimePreempt
+ int amountWiggles = (int)osuObject.TimePreempt / wiggle_duration;
+
+ void wiggle()
+ {
+ float nextAngle = (float)(objRand.NextDouble() * 2 * Math.PI);
+ float nextDist = (float)(objRand.NextDouble() * wiggle_strength);
+ drawable.MoveTo(new Vector2((float)(nextDist * Math.Cos(nextAngle) + origin.X), (float)(nextDist * Math.Sin(nextAngle) + origin.Y)), wiggle_duration);
+ }
+
+ for (int i = 0; i < amountWiggles; i++)
+ using (drawable.BeginAbsoluteSequence(osuObject.StartTime - osuObject.TimePreempt + i * wiggle_duration, true))
+ wiggle();
+
+ // Keep wiggling sliders and spinners for their duration
+ if (!(osuObject is IHasEndTime endTime))
+ return;
+
+ amountWiggles = (int)(endTime.Duration / wiggle_duration);
+
+ for (int i = 0; i < amountWiggles; i++)
+ using (drawable.BeginAbsoluteSequence(osuObject.StartTime + i * wiggle_duration, true))
+ wiggle();
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
index 4bdddcef11..e663989eeb 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
@@ -61,6 +61,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Size = circle.DrawSize;
HitObject.PositionChanged += _ => Position = HitObject.StackedPosition;
+ HitObject.StackHeightChanged += _ => Position = HitObject.StackedPosition;
+ HitObject.ScaleChanged += s => Scale = new Vector2(s);
}
public override Color4 AccentColour
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index 66f491532d..514ae09064 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public readonly DrawableHitCircle HeadCircle;
public readonly DrawableSliderTail TailCircle;
- public readonly SliderBody Body;
+ public readonly SnakingSliderBody Body;
public readonly SliderBall Ball;
public DrawableSlider(Slider s)
@@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
InternalChildren = new Drawable[]
{
- Body = new SliderBody(s)
+ Body = new SnakingSliderBody(s)
{
PathWidth = s.Scale * 64,
},
@@ -85,6 +85,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
HitObject.PositionChanged += _ => Position = HitObject.StackedPosition;
+ HitObject.ScaleChanged += _ =>
+ {
+ Body.PathWidth = HitObject.Scale * 64;
+ Ball.Scale = new Vector2(HitObject.Scale);
+ };
+
+ slider.ControlPointsChanged += _ => Body.Refresh();
}
public override Color4 AccentColour
@@ -119,7 +126,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
double completionProgress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1);
foreach (var c in components.OfType()) c.UpdateProgress(completionProgress);
- foreach (var c in components.OfType()) c.UpdateSnakingPosition(slider.Curve.PositionAt(Body.SnakedStart ?? 0), slider.Curve.PositionAt(Body.SnakedEnd ?? 0));
+ foreach (var c in components.OfType()) c.UpdateSnakingPosition(slider.Path.PositionAt(Body.SnakedStart ?? 0), slider.Path.PositionAt(Body.SnakedEnd ?? 0));
foreach (var t in components.OfType()) t.Tracking = Ball.Tracking;
Size = Body.Size;
@@ -184,6 +191,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public Drawable ProxiedLayer => HeadCircle.ApproachCircle;
- public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Body.ReceiveMouseInputAt(screenSpacePos);
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Body.ReceivePositionalInputAt(screenSpacePos);
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs
index 6d6cba4936..6a836679a2 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs
@@ -16,7 +16,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
this.slider = slider;
- Position = HitObject.Position - slider.Position;
+ h.PositionChanged += _ => updatePosition();
+ slider.ControlPointsChanged += _ => updatePosition();
+
+ updatePosition();
}
protected override void Update()
@@ -33,5 +36,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public Action OnShake;
protected override void Shake(double maximumLength) => OnShake?.Invoke(maximumLength);
+
+ private void updatePosition() => Position = HitObject.Position - slider.Position;
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs
index 45c925b87a..cc88a6718b 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs
@@ -8,6 +8,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking
{
+ private readonly Slider slider;
+
///
/// The judgement text is provided by the .
///
@@ -18,6 +20,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public DrawableSliderTail(Slider slider, SliderTailCircle hitCircle)
: base(hitCircle)
{
+ this.slider = slider;
+
Origin = Anchor.Centre;
RelativeSizeAxes = Axes.Both;
@@ -25,7 +29,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
AlwaysPresent = true;
- Position = HitObject.Position - slider.Position;
+ hitCircle.PositionChanged += _ => updatePosition();
+ slider.ControlPointsChanged += _ => updatePosition();
+
+ updatePosition();
}
protected override void CheckForResult(bool userTriggered, double timeOffset)
@@ -33,5 +40,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (!userTriggered && timeOffset >= 0)
ApplyResult(r => r.Type = Tracking ? HitResult.Great : HitResult.Miss);
}
+
+ private void updatePosition() => Position = HitObject.Position - slider.Position;
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
index 51b1990a21..f3846bd52f 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
@@ -112,6 +112,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Alpha = 0
}
};
+
+ s.PositionChanged += _ => Position = s.Position;
}
public float Progress => MathHelper.Clamp(Disc.RotationAbsolute / 360 / Spinner.SpinsRequired, 0, 1);
@@ -167,7 +169,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void Update()
{
- Disc.Tracking = OsuActionInputManager.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton);
+ Disc.Tracking = OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false;
if (!spmCounter.IsPresent && Disc.Tracking)
spmCounter.FadeIn(HitObject.TimeFadeIn);
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs
index bd7a4ad3f6..6bb6991cc0 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs
@@ -12,6 +12,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
public class CirclePiece : Container, IKeyBindingHandler
{
+ // IsHovered is used
+ public override bool HandlePositionalInput => true;
+
public Func Hit;
public CirclePiece()
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ManualSliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ManualSliderBody.cs
new file mode 100644
index 0000000000..9d239c15f2
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/ManualSliderBody.cs
@@ -0,0 +1,20 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.Collections.Generic;
+using OpenTK;
+
+namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
+{
+ ///
+ /// A with the ability to set the drawn vertices manually.
+ ///
+ public class ManualSliderBody : SliderBody
+ {
+ public new void SetVertices(IReadOnlyList vertices)
+ {
+ base.SetVertices(vertices);
+ Size = Path.Size;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs
index 30140484de..acb3ee92ff 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/NumberPiece.cs
@@ -4,7 +4,6 @@
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics.Sprites;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
@@ -14,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
public class NumberPiece : Container
{
- private readonly SpriteText number;
+ private readonly SkinnableSpriteText number;
public string Text
{
@@ -41,15 +40,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
},
Child = new Box()
}, s => s.GetTexture("Play/osu/hitcircle") == null),
- number = new OsuSpriteText
+ number = new SkinnableSpriteText("Play/osu/number-text", _ => new OsuSpriteText
{
- Text = @"1",
Font = @"Venera",
UseFullGlyphHeight = false,
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
TextSize = 40,
- Alpha = 1
+ }, restrictSize: false)
+ {
+ Text = @"1"
}
};
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
index b79750a1b3..3081ae49fc 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
@@ -5,11 +5,11 @@ using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input.EventArgs;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
using osu.Game.Rulesets.Objects.Types;
using OpenTK.Graphics;
using osu.Game.Skinning;
+using OpenTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
@@ -102,24 +102,24 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
};
}
- private InputState lastState;
+ private Vector2? lastScreenSpaceMousePosition;
- protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
+ protected override bool OnMouseDown(MouseDownEvent e)
{
- lastState = state;
- return base.OnMouseDown(state, args);
+ lastScreenSpaceMousePosition = e.ScreenSpaceMousePosition;
+ return base.OnMouseDown(e);
}
- protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
+ protected override bool OnMouseUp(MouseUpEvent e)
{
- lastState = state;
- return base.OnMouseUp(state, args);
+ lastScreenSpaceMousePosition = e.ScreenSpaceMousePosition;
+ return base.OnMouseUp(e);
}
- protected override bool OnMouseMove(InputState state)
+ protected override bool OnMouseMove(MouseMoveEvent e)
{
- lastState = state;
- return base.OnMouseMove(state);
+ lastScreenSpaceMousePosition = e.ScreenSpaceMousePosition;
+ return base.OnMouseMove(e);
}
public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null)
@@ -153,10 +153,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
if (Time.Current < slider.EndTime)
{
- // Make sure to use the base version of ReceiveMouseInputAt so that we correctly check the position.
+ // Make sure to use the base version of ReceivePositionalInputAt so that we correctly check the position.
Tracking = canCurrentlyTrack
- && lastState != null
- && ReceiveMouseInputAt(lastState.Mouse.NativeState.Position)
+ && lastScreenSpaceMousePosition.HasValue
+ && ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value)
&& (drawableSlider?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false);
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
index 6f0197e711..ca2daa3adb 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs
@@ -1,230 +1,139 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using System;
using System.Collections.Generic;
-using osu.Framework.Allocation;
-using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Lines;
-using osu.Framework.Graphics.Textures;
-using OpenTK.Graphics.ES30;
-using OpenTK.Graphics;
using osu.Framework.Graphics.Primitives;
-using osu.Game.Rulesets.Objects.Types;
using OpenTK;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.PixelFormats;
+using OpenTK.Graphics;
+using OpenTK.Graphics.ES30;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
- public class SliderBody : Container, ISliderProgress
+ public abstract class SliderBody : CompositeDrawable
{
- private readonly Path path;
+ private readonly SliderPath path;
+ protected Path Path => path;
+
private readonly BufferedContainer container;
public float PathWidth
{
- get { return path.PathWidth; }
- set { path.PathWidth = value; }
+ get => path.PathWidth;
+ set => path.PathWidth = value;
}
///
/// Offset in absolute coordinates from the start of the curve.
///
- public Vector2 PathOffset { get; private set; }
-
- public readonly List CurrentCurve = new List();
-
- public readonly Bindable SnakingIn = new Bindable();
- public readonly Bindable SnakingOut = new Bindable();
-
- public double? SnakedStart { get; private set; }
- public double? SnakedEnd { get; private set; }
-
- private Color4 accentColour = Color4.White;
+ public virtual Vector2 PathOffset => path.PositionInBoundingBox(path.Vertices[0]);
///
/// Used to colour the path.
///
public Color4 AccentColour
{
- get { return accentColour; }
+ get => path.AccentColour;
set
{
- if (accentColour == value)
+ if (path.AccentColour == value)
return;
- accentColour = value;
+ path.AccentColour = value;
- if (LoadState >= LoadState.Ready)
- reloadTexture();
+ container.ForceRedraw();
}
}
- private Color4 borderColour = Color4.White;
-
///
/// Used to colour the path border.
///
public new Color4 BorderColour
{
- get { return borderColour; }
+ get => path.BorderColour;
set
{
- if (borderColour == value)
+ if (path.BorderColour == value)
return;
- borderColour = value;
-
- if (LoadState >= LoadState.Ready)
- reloadTexture();
- }
- }
-
- public Quad PathDrawQuad => container.ScreenSpaceDrawQuad;
-
- private int textureWidth => (int)PathWidth * 2;
-
- private Vector2 topLeftOffset;
-
- private readonly Slider slider;
-
- public SliderBody(Slider s)
- {
- slider = s;
-
- Children = new Drawable[]
- {
- container = new BufferedContainer
- {
- RelativeSizeAxes = Axes.Both,
- CacheDrawnFrameBuffer = true,
- Children = new Drawable[]
- {
- path = new Path
- {
- Blending = BlendingMode.None,
- },
- }
- },
- };
-
- container.Attach(RenderbufferInternalFormat.DepthComponent16);
- }
-
- public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => path.ReceiveMouseInputAt(screenSpacePos);
-
- public void SetRange(double p0, double p1)
- {
- if (p0 > p1)
- MathHelper.Swap(ref p0, ref p1);
-
- if (updateSnaking(p0, p1))
- {
- // The path is generated such that its size encloses it. This change of size causes the path
- // to move around while snaking, so we need to offset it to make sure it maintains the
- // same position as when it is fully snaked.
- var newTopLeftOffset = path.PositionInBoundingBox(Vector2.Zero);
- path.Position = topLeftOffset - newTopLeftOffset;
+ path.BorderColour = value;
container.ForceRedraw();
}
}
- [BackgroundDependencyLoader]
- private void load()
+ public Quad PathDrawQuad => container.ScreenSpaceDrawQuad;
+
+ protected SliderBody()
{
- reloadTexture();
- computeSize();
+ InternalChild = container = new BufferedContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ CacheDrawnFrameBuffer = true,
+ Child = path = new SliderPath { Blending = BlendingMode.None }
+ };
+
+ container.Attach(RenderbufferInternalFormat.DepthComponent16);
}
- private void reloadTexture()
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => path.ReceivePositionalInputAt(screenSpacePos);
+
+ ///
+ /// Sets the vertices of the path which should be drawn by this .
+ ///
+ /// The vertices
+ protected void SetVertices(IReadOnlyList vertices)
{
- var texture = new Texture(textureWidth, 1);
-
- //initialise background
- var raw = new Image(textureWidth, 1);
-
- const float aa_portion = 0.02f;
- const float border_portion = 0.128f;
- const float gradient_portion = 1 - border_portion;
-
- const float opacity_at_centre = 0.3f;
- const float opacity_at_edge = 0.8f;
-
- for (int i = 0; i < textureWidth; i++)
- {
- float progress = (float)i / (textureWidth - 1);
-
- if (progress <= border_portion)
- {
- raw[i, 0] = new Rgba32(BorderColour.R, BorderColour.G, BorderColour.B, Math.Min(progress / aa_portion, 1) * BorderColour.A);
- }
- else
- {
- progress -= border_portion;
- raw[i, 0] = new Rgba32(AccentColour.R, AccentColour.G, AccentColour.B,
- (opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * AccentColour.A);
- }
- }
-
- texture.SetData(new TextureUpload(raw));
- path.Texture = texture;
-
+ path.Vertices = vertices;
container.ForceRedraw();
}
- private void computeSize()
+ private class SliderPath : SmoothPath
{
- // Generate the entire curve
- slider.Curve.GetPathToProgress(CurrentCurve, 0, 1);
- foreach (Vector2 p in CurrentCurve)
- path.AddVertex(p);
+ private const float border_portion = 0.128f;
+ private const float gradient_portion = 1 - border_portion;
- Size = path.Size;
+ private const float opacity_at_centre = 0.3f;
+ private const float opacity_at_edge = 0.8f;
- topLeftOffset = path.PositionInBoundingBox(Vector2.Zero);
- PathOffset = path.PositionInBoundingBox(CurrentCurve[0]);
- }
+ private Color4 borderColour = Color4.White;
- private bool updateSnaking(double p0, double p1)
- {
- if (SnakedStart == p0 && SnakedEnd == p1) return false;
-
- SnakedStart = p0;
- SnakedEnd = p1;
-
- slider.Curve.GetPathToProgress(CurrentCurve, p0, p1);
-
- path.ClearVertices();
- foreach (Vector2 p in CurrentCurve)
- path.AddVertex(p);
-
- return true;
- }
-
- public void UpdateProgress(double completionProgress)
- {
- var span = slider.SpanAt(completionProgress);
- var spanProgress = slider.ProgressAt(completionProgress);
-
- double start = 0;
- double end = SnakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadeIn, 0, 1) : 1;
-
- if (span >= slider.SpanCount() - 1)
+ public Color4 BorderColour
{
- if (Math.Min(span, slider.SpanCount() - 1) % 2 == 1)
+ get => borderColour;
+ set
{
- start = 0;
- end = SnakingOut ? spanProgress : 1;
- }
- else
- {
- start = SnakingOut ? spanProgress : 0;
+ if (borderColour == value)
+ return;
+ borderColour = value;
+
+ InvalidateTexture();
}
}
- SetRange(start, end);
+ private Color4 accentColour = Color4.White;
+
+ public Color4 AccentColour
+ {
+ get => accentColour;
+ set
+ {
+ if (accentColour == value)
+ return;
+ accentColour = value;
+
+ InvalidateTexture();
+ }
+ }
+
+ protected override Color4 ColourAt(float position)
+ {
+ if (position <= border_portion)
+ return BorderColour;
+
+ position -= border_portion;
+ return new Color4(AccentColour.R, AccentColour.G, AccentColour.B, (opacity_at_edge - (opacity_at_edge - opacity_at_centre) * position / gradient_portion) * AccentColour.A);
+ }
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs
new file mode 100644
index 0000000000..0e6f3ad16c
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs
@@ -0,0 +1,118 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections.Generic;
+using osu.Framework.Allocation;
+using osu.Framework.Configuration;
+using osu.Game.Rulesets.Objects.Types;
+using OpenTK;
+
+namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
+{
+ ///
+ /// A which changes its curve depending on the snaking progress.
+ ///
+ public class SnakingSliderBody : SliderBody, ISliderProgress
+ {
+ public readonly List CurrentCurve = new List();
+
+ public readonly Bindable SnakingIn = new Bindable();
+ public readonly Bindable SnakingOut = new Bindable();
+
+ public double? SnakedStart { get; private set; }
+ public double? SnakedEnd { get; private set; }
+
+ public override Vector2 PathOffset => snakedPathOffset;
+
+ ///
+ /// The top-left position of the path when fully snaked.
+ ///
+ private Vector2 snakedPosition;
+
+ ///
+ /// The offset of the path from when fully snaked.
+ ///
+ private Vector2 snakedPathOffset;
+
+ private readonly Slider slider;
+
+ public SnakingSliderBody(Slider slider)
+ {
+ this.slider = slider;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Refresh();
+ }
+
+ public void UpdateProgress(double completionProgress)
+ {
+ var span = slider.SpanAt(completionProgress);
+ var spanProgress = slider.ProgressAt(completionProgress);
+
+ double start = 0;
+ double end = SnakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadeIn, 0, 1) : 1;
+
+ if (span >= slider.SpanCount() - 1)
+ {
+ if (Math.Min(span, slider.SpanCount() - 1) % 2 == 1)
+ {
+ start = 0;
+ end = SnakingOut ? spanProgress : 1;
+ }
+ else
+ {
+ start = SnakingOut ? spanProgress : 0;
+ }
+ }
+
+ setRange(start, end);
+ }
+
+ public void Refresh()
+ {
+ // Generate the entire curve
+ slider.Path.GetPathToProgress(CurrentCurve, 0, 1);
+ SetVertices(CurrentCurve);
+
+ // The body is sized to the full path size to avoid excessive autosize computations
+ Size = Path.Size;
+
+ snakedPosition = Path.PositionInBoundingBox(Vector2.Zero);
+ snakedPathOffset = Path.PositionInBoundingBox(Path.Vertices[0]);
+
+ var lastSnakedStart = SnakedStart ?? 0;
+ var lastSnakedEnd = SnakedEnd ?? 0;
+
+ SnakedStart = null;
+ SnakedEnd = null;
+
+ setRange(lastSnakedStart, lastSnakedEnd);
+ }
+
+ private void setRange(double p0, double p1)
+ {
+ if (p0 > p1)
+ MathHelper.Swap(ref p0, ref p1);
+
+ if (SnakedStart == p0 && SnakedEnd == p1) return;
+
+ SnakedStart = p0;
+ SnakedEnd = p1;
+
+ slider.Path.GetPathToProgress(CurrentCurve, p0, p1);
+
+ SetVertices(CurrentCurve);
+
+ // The bounding box of the path expands as it snakes, which in turn shifts the position of the path.
+ // Depending on the direction of expansion, it may appear as if the path is expanding towards the position of the slider
+ // rather than expanding out from the position of the slider.
+ // To remove this effect, the path's position is shifted towards its final snaked position
+
+ Path.Position = snakedPosition - Path.PositionInBoundingBox(Vector2.Zero);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs
index 1a7455838f..584fd93a70 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs
@@ -11,9 +11,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
public class SpinnerBackground : CircularContainer, IHasAccentColour
{
- public override bool HandleKeyboardInput => false;
- public override bool HandleMouseInput => false;
-
protected Box Disc;
public Color4 AccentColour
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs
index 5aba60ba03..4dd1c5f218 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs
@@ -4,7 +4,7 @@
using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
@@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
};
}
- public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
private bool tracking;
public bool Tracking
@@ -68,10 +68,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
}
}
- protected override bool OnMouseMove(InputState state)
+ protected override bool OnMouseMove(MouseMoveEvent e)
{
- mousePosition = Parent.ToLocalSpace(state.Mouse.NativeState.Position);
- return base.OnMouseMove(state);
+ mousePosition = Parent.ToLocalSpace(e.ScreenSpaceMousePosition);
+ return base.OnMouseMove(e);
}
private Vector2 mousePosition;
diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
index fdf5aaffa8..61d199a7dc 100644
--- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
@@ -7,22 +7,23 @@ using osu.Game.Rulesets.Objects;
using OpenTK;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Beatmaps.ControlPoints;
-using osu.Game.Rulesets.Edit.Types;
namespace osu.Game.Rulesets.Osu.Objects
{
- public abstract class OsuHitObject : HitObject, IHasComboInformation, IHasEditablePosition
+ public abstract class OsuHitObject : HitObject, IHasComboInformation, IHasPosition
{
public const double OBJECT_RADIUS = 64;
public event Action PositionChanged;
+ public event Action StackHeightChanged;
+ public event Action ScaleChanged;
public double TimePreempt = 600;
public double TimeFadeIn = 400;
private Vector2 position;
- public Vector2 Position
+ public virtual Vector2 Position
{
get => position;
set
@@ -44,13 +45,39 @@ namespace osu.Game.Rulesets.Osu.Objects
public Vector2 StackedEndPosition => EndPosition + StackOffset;
- public virtual int StackHeight { get; set; }
+ private int stackHeight;
+
+ public int StackHeight
+ {
+ get => stackHeight;
+ set
+ {
+ if (stackHeight == value)
+ return;
+ stackHeight = value;
+
+ StackHeightChanged?.Invoke(value);
+ }
+ }
public Vector2 StackOffset => new Vector2(StackHeight * Scale * -6.4f);
public double Radius => OBJECT_RADIUS * Scale;
- public float Scale { get; set; } = 1;
+ private float scale = 1;
+
+ public float Scale
+ {
+ get => scale;
+ set
+ {
+ if (scale == value)
+ return;
+ scale = value;
+
+ ScaleChanged?.Invoke(value);
+ }
+ }
public virtual bool NewCombo { get; set; }
@@ -72,8 +99,6 @@ namespace osu.Game.Rulesets.Osu.Objects
Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
}
- public virtual void OffsetPosition(Vector2 offset) => Position += offset;
-
protected override HitWindows CreateHitWindows() => new OsuHitWindows();
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs
index 7a0dcc77a6..cff742ca29 100644
--- a/osu.Game.Rulesets.Osu/Objects/Slider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs
@@ -22,7 +22,9 @@ namespace osu.Game.Rulesets.Osu.Objects
///
private const float base_scoring_distance = 100;
- public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity;
+ public event Action ControlPointsChanged;
+
+ public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity;
public double Duration => EndTime - StartTime;
public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t);
@@ -50,24 +52,49 @@ namespace osu.Game.Rulesets.Osu.Objects
}
}
- public SliderCurve Curve { get; } = new SliderCurve();
+ public SliderPath Path { get; } = new SliderPath();
- public List ControlPoints
+ public Vector2[] ControlPoints
{
- get { return Curve.ControlPoints; }
- set { Curve.ControlPoints = value; }
+ get => Path.ControlPoints;
+ set
+ {
+ if (Path.ControlPoints == value)
+ return;
+ Path.ControlPoints = value;
+
+ ControlPointsChanged?.Invoke(value);
+
+ if (TailCircle != null)
+ TailCircle.Position = EndPosition;
+ }
}
- public CurveType CurveType
+ public PathType PathType
{
- get { return Curve.CurveType; }
- set { Curve.CurveType = value; }
+ get { return Path.PathType; }
+ set { Path.PathType = value; }
}
public double Distance
{
- get { return Curve.Distance; }
- set { Curve.Distance = value; }
+ get { return Path.Distance; }
+ set { Path.Distance = value; }
+ }
+
+ public override Vector2 Position
+ {
+ get => base.Position;
+ set
+ {
+ base.Position = value;
+
+ if (HeadCircle != null)
+ HeadCircle.Position = value;
+
+ if (TailCircle != null)
+ TailCircle.Position = EndPosition;
+ }
}
public double? LegacyLastTickOffset { get; set; }
@@ -84,7 +111,8 @@ namespace osu.Game.Rulesets.Osu.Objects
///
internal float LazyTravelDistance;
- public List> RepeatSamples { get; set; } = new List>();
+ public List> NodeSamples { get; set; } = new List>();
+
public int RepeatCount { get; set; }
///
@@ -92,8 +120,21 @@ namespace osu.Game.Rulesets.Osu.Objects
///
public double SpanDuration => Duration / this.SpanCount();
- public double Velocity;
- public double TickDistance;
+ ///
+ /// Velocity of this .
+ ///
+ public double Velocity { get; private set; }
+
+ ///
+ /// Spacing between s of this .
+ ///
+ public double TickDistance { get; private set; }
+
+ ///
+ /// An extra multiplier that affects the number of s generated by this .
+ /// An increase in this value increases , which reduces the number of ticks generated.
+ ///
+ public double TickDistanceMultiplier = 1;
public HitCircle HeadCircle;
public SliderTailCircle TailCircle;
@@ -108,7 +149,7 @@ namespace osu.Game.Rulesets.Osu.Objects
double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier;
Velocity = scoringDistance / timingPoint.BeatLength;
- TickDistance = scoringDistance / difficulty.SliderTickRate;
+ TickDistance = scoringDistance / difficulty.SliderTickRate * TickDistanceMultiplier;
}
protected override void CreateNestedHitObjects()
@@ -125,17 +166,17 @@ namespace osu.Game.Rulesets.Osu.Objects
private void createSliderEnds()
{
- HeadCircle = new SliderCircle(this)
+ HeadCircle = new SliderCircle
{
StartTime = StartTime,
Position = Position,
- Samples = Samples,
+ Samples = getNodeSamples(0),
SampleControlPoint = SampleControlPoint,
IndexInCurrentCombo = IndexInCurrentCombo,
ComboIndex = ComboIndex,
};
- TailCircle = new SliderTailCircle(this)
+ TailCircle = new SliderTailCircle
{
StartTime = EndTime,
Position = EndPosition,
@@ -149,7 +190,7 @@ namespace osu.Game.Rulesets.Osu.Objects
private void createTicks()
{
- var length = Curve.Distance;
+ var length = Path.Distance;
var tickDistance = MathHelper.Clamp(TickDistance, 0, length);
if (tickDistance == 0) return;
@@ -188,7 +229,7 @@ namespace osu.Game.Rulesets.Osu.Objects
SpanIndex = span,
SpanStartTime = spanStartTime,
StartTime = spanStartTime + timeProgress * SpanDuration,
- Position = Position + Curve.PositionAt(distanceProgress),
+ Position = Position + Path.PositionAt(distanceProgress),
StackHeight = StackHeight,
Scale = Scale,
Samples = sampleList
@@ -206,14 +247,21 @@ namespace osu.Game.Rulesets.Osu.Objects
RepeatIndex = repeatIndex,
SpanDuration = SpanDuration,
StartTime = StartTime + repeat * SpanDuration,
- Position = Position + Curve.PositionAt(repeat % 2),
+ Position = Position + Path.PositionAt(repeat % 2),
StackHeight = StackHeight,
Scale = Scale,
- Samples = new List(RepeatSamples[repeatIndex])
+ Samples = getNodeSamples(1 + repeatIndex)
});
}
}
+ private List getNodeSamples(int nodeIndex)
+ {
+ if (nodeIndex < NodeSamples.Count)
+ return NodeSamples[nodeIndex];
+ return Samples;
+ }
+
public override Judgement CreateJudgement() => new OsuJudgement();
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/SliderCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderCircle.cs
index 1bdd16c9df..deda951378 100644
--- a/osu.Game.Rulesets.Osu/Objects/SliderCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/SliderCircle.cs
@@ -1,19 +1,9 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using OpenTK;
-
namespace osu.Game.Rulesets.Osu.Objects
{
public class SliderCircle : HitCircle
{
- private readonly Slider slider;
-
- public SliderCircle(Slider slider)
- {
- this.slider = slider;
- }
-
- public override void OffsetPosition(Vector2 offset) => slider.OffsetPosition(offset);
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs
index 23616ea005..b567bd8423 100644
--- a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs
@@ -8,11 +8,6 @@ namespace osu.Game.Rulesets.Osu.Objects
{
public class SliderTailCircle : SliderCircle
{
- public SliderTailCircle(Slider slider)
- : base(slider)
- {
- }
-
public override Judgement CreateJudgement() => new OsuSliderTailJudgement();
}
}
diff --git a/osu.Game.Rulesets.Osu/OsuInputManager.cs b/osu.Game.Rulesets.Osu/OsuInputManager.cs
index e7bbe755a0..e2f4c43a23 100644
--- a/osu.Game.Rulesets.Osu/OsuInputManager.cs
+++ b/osu.Game.Rulesets.Osu/OsuInputManager.cs
@@ -4,8 +4,7 @@
using System.Collections.Generic;
using System.ComponentModel;
using osu.Framework.Input.Bindings;
-using osu.Framework.Input.EventArgs;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Osu
@@ -36,22 +35,20 @@ namespace osu.Game.Rulesets.Osu
{
}
- protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) => AllowUserPresses && base.OnKeyDown(state, args);
- protected override bool OnKeyUp(InputState state, KeyUpEventArgs args) => AllowUserPresses && base.OnKeyUp(state, args);
- protected override bool OnJoystickPress(InputState state, JoystickEventArgs args) => AllowUserPresses && base.OnJoystickPress(state, args);
- protected override bool OnJoystickRelease(InputState state, JoystickEventArgs args) => AllowUserPresses && base.OnJoystickRelease(state, args);
- protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => AllowUserPresses && base.OnMouseDown(state, args);
- protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) => AllowUserPresses && base.OnMouseUp(state, args);
- protected override bool OnScroll(InputState state) => AllowUserPresses && base.OnScroll(state);
+ protected override bool Handle(UIEvent e)
+ {
+ if (!AllowUserPresses) return false;
+ return base.Handle(e);
+ }
}
}
public enum OsuAction
{
- [Description("Left Button")]
+ [Description("Left button")]
LeftButton,
- [Description("Right Button")]
+ [Description("Right button")]
RightButton
}
}
diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs
index fa6e9a018a..6736d10dab 100644
--- a/osu.Game.Rulesets.Osu/OsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs
@@ -117,6 +117,11 @@ namespace osu.Game.Rulesets.Osu
new OsuModRelax(),
new OsuModAutopilot(),
};
+ case ModType.Fun:
+ return new Mod[] {
+ new OsuModTransform(),
+ new OsuModWiggle(),
+ };
default:
return new Mod[] { };
}
diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
index 4a6b12d41a..4b5513ff9c 100644
--- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
+++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs
@@ -12,7 +12,7 @@ using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Shaders;
using osu.Framework.Graphics.Textures;
using osu.Framework.Input;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
using osu.Framework.Timing;
using OpenTK;
using OpenTK.Graphics;
@@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
}
}
- public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
[BackgroundDependencyLoader]
private void load(ShaderManager shaders, TextureStore textures)
@@ -117,15 +117,15 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
timeOffset = Time.Current;
}
- protected override bool OnMouseMove(InputState state)
+ protected override bool OnMouseMove(MouseMoveEvent e)
{
- Vector2 pos = state.Mouse.NativeState.Position;
+ Vector2 pos = e.ScreenSpaceMousePosition;
if (lastPosition == null)
{
lastPosition = pos;
resampler.AddPosition(lastPosition.Value);
- return base.OnMouseMove(state);
+ return base.OnMouseMove(e);
}
foreach (Vector2 pos2 in resampler.AddPosition(pos))
@@ -147,7 +147,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
}
}
- return base.OnMouseMove(state);
+ return base.OnMouseMove(e);
}
private void addPosition(Vector2 pos)
diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs
index 4d6722b61b..4a45d4fb31 100644
--- a/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs
+++ b/osu.Game.Rulesets.Osu/UI/Cursor/GameplayCursor.cs
@@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
return false;
}
- public override bool HandleMouseInput => true; // OverlayContainer will set this false when we go hidden, but we always want to receive input.
+ public override bool HandlePositionalInput => true; // OverlayContainer will set this false when we go hidden, but we always want to receive input.
protected override void PopIn()
{
diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
index 61937a535c..398680cb8d 100644
--- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
+++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
@@ -23,29 +23,35 @@ namespace osu.Game.Rulesets.Osu.UI
public static readonly Vector2 BASE_SIZE = new Vector2(512, 384);
public OsuPlayfield()
- : base(BASE_SIZE.X)
{
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
- AddRange(new Drawable[]
+ Size = new Vector2(0.75f);
+
+ InternalChild = new PlayfieldAdjustmentContainer
{
- connectionLayer = new FollowPointRenderer
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
{
- RelativeSizeAxes = Axes.Both,
- Depth = 2,
- },
- judgementLayer = new JudgementContainer
- {
- RelativeSizeAxes = Axes.Both,
- Depth = 1,
- },
- approachCircles = new Container
- {
- RelativeSizeAxes = Axes.Both,
- Depth = -1,
- },
- });
+ connectionLayer = new FollowPointRenderer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Depth = 2,
+ },
+ judgementLayer = new JudgementContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Depth = 1,
+ },
+ HitObjectContainer,
+ approachCircles = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Depth = -1,
+ },
+ }
+ };
}
public override void Add(DrawableHitObject h)
@@ -72,7 +78,8 @@ namespace osu.Game.Rulesets.Osu.UI
DrawableOsuJudgement explosion = new DrawableOsuJudgement(result, judgedObject)
{
Origin = Anchor.Centre,
- Position = ((OsuHitObject)judgedObject.HitObject).StackedEndPosition
+ Position = ((OsuHitObject)judgedObject.HitObject).StackedEndPosition,
+ Scale = new Vector2(((OsuHitObject)judgedObject.HitObject).Scale * 1.65f)
};
judgementLayer.Add(explosion);
diff --git a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs
index 4bc6992445..ea5718bed2 100644
--- a/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs
+++ b/osu.Game.Rulesets.Osu/UI/OsuRulesetContainer.cs
@@ -4,7 +4,6 @@
using System.Linq;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Input;
-using OpenTK;
using osu.Game.Beatmaps;
using osu.Game.Input.Handlers;
using osu.Game.Rulesets.Objects.Drawables;
@@ -32,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.UI
public override PassThroughInputManager CreateInputManager() => new OsuInputManager(Ruleset.RulesetInfo);
- protected override DrawableHitObject GetVisualRepresentation(OsuHitObject h)
+ public override DrawableHitObject GetVisualRepresentation(OsuHitObject h)
{
switch (h)
{
@@ -58,12 +57,6 @@ namespace osu.Game.Rulesets.Osu.UI
}
}
- protected override Vector2 GetAspectAdjustedSize()
- {
- var aspectSize = DrawSize.X * 0.75f < DrawSize.Y ? new Vector2(DrawSize.X, DrawSize.X * 0.75f) : new Vector2(DrawSize.Y * 4f / 3f, DrawSize.Y);
- return new Vector2(aspectSize.X / DrawSize.X, aspectSize.Y / DrawSize.Y);
- }
-
protected override CursorContainer CreateCursor() => new GameplayCursor();
}
}
diff --git a/osu.Game.Rulesets.Osu/UI/PlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Osu/UI/PlayfieldAdjustmentContainer.cs
new file mode 100644
index 0000000000..00d5692fda
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/UI/PlayfieldAdjustmentContainer.cs
@@ -0,0 +1,42 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using OpenTK;
+
+namespace osu.Game.Rulesets.Osu.UI
+{
+ public class PlayfieldAdjustmentContainer : Container
+ {
+ protected override Container Content => content;
+ private readonly Container content;
+
+ public PlayfieldAdjustmentContainer()
+ {
+ InternalChild = new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ FillMode = FillMode.Fit,
+ FillAspectRatio = 4f / 3,
+ Child = content = new ScalingContainer { RelativeSizeAxes = Axes.Both }
+ };
+ }
+
+ ///
+ /// A which scales its content relative to a target width.
+ ///
+ private class ScalingContainer : Container
+ {
+ protected override void Update()
+ {
+ base.Update();
+
+ Scale = new Vector2(Parent.ChildSize.X / OsuPlayfield.BASE_SIZE.X);
+ Size = Vector2.Divide(Vector2.One, Scale);
+ }
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
index 08a0579561..3ba64398f3 100644
--- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
+++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj
@@ -2,9 +2,10 @@
-
-
-
+
+
+
+
WinExe
diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
index c2cde332e8..c4a84f416e 100644
--- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
@@ -117,7 +117,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
{
- List> allSamples = curveData != null ? curveData.RepeatSamples : new List>(new[] { samples });
+ List> allSamples = curveData != null ? curveData.NodeSamples : new List>(new[] { samples });
int i = 0;
for (double j = obj.StartTime; j <= obj.StartTime + taikoDuration + tickSpacing / 8; j += tickSpacing)
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
index f59dc8c1ee..6f7264e23b 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
@@ -24,6 +24,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
private bool validActionPressed;
+ private bool pressHandledThisFrame;
+
protected DrawableHit(Hit hit)
: base(hit)
{
@@ -51,6 +53,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
public override bool OnPressed(TaikoAction action)
{
+ if (pressHandledThisFrame)
+ return true;
+
if (Judged)
return false;
@@ -62,6 +67,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
if (IsHit)
HitAction = action;
+ // Regardless of whether we've hit or not, any secondary key presses in the same frame should be discarded
+ // E.g. hitting a non-strong centre as a strong should not fall through and perform a hit on the next note
+ pressHandledThisFrame = true;
+
return result;
}
@@ -76,6 +85,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
base.Update();
+ // The input manager processes all input prior to us updating, so this is the perfect time
+ // for us to remove the extra press blocking, before input is handled in the next frame
+ pressHandledThisFrame = false;
+
Size = BaseSize * Parent.RelativeChildSize;
}
diff --git a/osu.Game.Rulesets.Taiko/TaikoInputManager.cs b/osu.Game.Rulesets.Taiko/TaikoInputManager.cs
index dc683ae2f5..3b430e7ad1 100644
--- a/osu.Game.Rulesets.Taiko/TaikoInputManager.cs
+++ b/osu.Game.Rulesets.Taiko/TaikoInputManager.cs
@@ -17,13 +17,13 @@ namespace osu.Game.Rulesets.Taiko
public enum TaikoAction
{
- [Description("Left (Rim)")]
+ [Description("Left (rim)")]
LeftRim,
- [Description("Left (Centre)")]
+ [Description("Left (centre)")]
LeftCentre,
- [Description("Right (Centre)")]
+ [Description("Right (centre)")]
RightCentre,
- [Description("Right (Rim)")]
+ [Description("Right (rim)")]
RightRim
}
}
diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs
index 4d660918b8..c7eba91564 100644
--- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs
+++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs
@@ -31,10 +31,10 @@ namespace osu.Game.Rulesets.Taiko.UI
switch (Result.Type)
{
case HitResult.Good:
- Colour = colours.GreenLight;
+ JudgementBody.Colour = colours.GreenLight;
break;
case HitResult.Great:
- Colour = colours.BlueLight;
+ JudgementBody.Colour = colours.BlueLight;
break;
}
}
diff --git a/osu.Game.Rulesets.Taiko/UI/PlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Taiko/UI/PlayfieldAdjustmentContainer.cs
new file mode 100644
index 0000000000..661a4e135c
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/UI/PlayfieldAdjustmentContainer.cs
@@ -0,0 +1,22 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Graphics.Containers;
+using OpenTK;
+
+namespace osu.Game.Rulesets.Taiko.UI
+{
+ public class PlayfieldAdjustmentContainer : Container
+ {
+ private const float default_relative_height = TaikoPlayfield.DEFAULT_HEIGHT / 768;
+ private const float default_aspect = 16f / 9f;
+
+ protected override void Update()
+ {
+ base.Update();
+
+ float aspectAdjust = MathHelper.Clamp(Parent.ChildSize.X / Parent.ChildSize.Y, 0.4f, 4) / default_aspect;
+ Size = new Vector2(1, default_relative_height * aspectAdjust);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
index 325beb38a5..40ed659bd6 100644
--- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
+++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
@@ -1,23 +1,24 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using osu.Framework.Allocation;
-using osu.Framework.Graphics;
-using osu.Framework.Graphics.Shapes;
-using osu.Game.Rulesets.Taiko.Objects;
-using OpenTK;
-using OpenTK.Graphics;
-using osu.Game.Rulesets.Taiko.Judgements;
-using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Graphics;
-using osu.Framework.Graphics.Containers;
-using osu.Framework.Extensions.Color4Extensions;
using System.Linq;
-using osu.Game.Rulesets.Judgements;
-using osu.Game.Rulesets.Taiko.Objects.Drawables;
+using osu.Framework.Allocation;
+using osu.Framework.Extensions.Color4Extensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Configuration;
+using osu.Game.Graphics;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
+using osu.Game.Rulesets.Taiko.Objects;
+using osu.Game.Rulesets.Taiko.Objects.Drawables;
+using osu.Game.Rulesets.Taiko.Judgements;
+using OpenTK;
+using OpenTK.Graphics;
namespace osu.Game.Rulesets.Taiko.UI
{
@@ -40,13 +41,12 @@ namespace osu.Game.Rulesets.Taiko.UI
protected override bool UserScrollSpeedAdjustment => false;
+ protected override SpeedChangeVisualisationMethod VisualisationMethod => SpeedChangeVisualisationMethod.Overlapping;
+
private readonly Container hitExplosionContainer;
private readonly Container kiaiExplosionContainer;
private readonly JudgementContainer judgementContainer;
- protected override Container Content => content;
- private readonly Container content;
-
private readonly Container topLevelHitContainer;
private readonly Container barlineContainer;
@@ -61,140 +61,147 @@ namespace osu.Game.Rulesets.Taiko.UI
{
Direction.Value = ScrollingDirection.Left;
- AddRangeInternal(new Drawable[]
+ InternalChild = new PlayfieldAdjustmentContainer
{
- backgroundContainer = new Container
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
{
- Name = "Transparent playfield background",
- RelativeSizeAxes = Axes.Both,
- Masking = true,
- EdgeEffect = new EdgeEffectParameters
+ backgroundContainer = new Container
{
- Type = EdgeEffectType.Shadow,
- Colour = Color4.Black.Opacity(0.2f),
- Radius = 5,
- },
- Children = new Drawable[]
- {
- background = new Box
+ Name = "Transparent playfield background",
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ EdgeEffect = new EdgeEffectParameters
{
- RelativeSizeAxes = Axes.Both,
- Alpha = 0.6f
+ Type = EdgeEffectType.Shadow,
+ Colour = Color4.Black.Opacity(0.2f),
+ Radius = 5,
},
- }
- },
- new Container
- {
- Name = "Right area",
- RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding { Left = left_area_size },
- Children = new Drawable[]
- {
- new Container
+ Children = new Drawable[]
{
- Name = "Masked elements before hit objects",
- RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding { Left = HIT_TARGET_OFFSET },
- Masking = true,
- Children = new Drawable[]
+ background = new Box
{
- hitExplosionContainer = new Container
- {
- RelativeSizeAxes = Axes.Both,
- FillMode = FillMode.Fit,
- Blending = BlendingMode.Additive,
- },
- new HitTarget
- {
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.Centre,
- RelativeSizeAxes = Axes.Both,
- FillMode = FillMode.Fit
- }
- }
- },
- barlineContainer = new Container
- {
- RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding { Left = HIT_TARGET_OFFSET }
- },
- content = new Container
- {
- Name = "Hit objects",
- RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding { Left = HIT_TARGET_OFFSET },
- Masking = true
- },
- kiaiExplosionContainer = new Container
- {
- Name = "Kiai hit explosions",
- RelativeSizeAxes = Axes.Both,
- FillMode = FillMode.Fit,
- Margin = new MarginPadding { Left = HIT_TARGET_OFFSET },
- Blending = BlendingMode.Additive
- },
- judgementContainer = new JudgementContainer
- {
- Name = "Judgements",
- RelativeSizeAxes = Axes.Y,
- Margin = new MarginPadding { Left = HIT_TARGET_OFFSET },
- Blending = BlendingMode.Additive
- },
- }
- },
- overlayBackgroundContainer = new Container
- {
- Name = "Left overlay",
- RelativeSizeAxes = Axes.Y,
- Size = new Vector2(left_area_size, 1),
- Children = new Drawable[]
- {
- overlayBackground = new Box
- {
- RelativeSizeAxes = Axes.Both,
- },
- new InputDrum(controlPoints)
- {
- Anchor = Anchor.CentreRight,
- Origin = Anchor.CentreRight,
- Scale = new Vector2(0.9f),
- Margin = new MarginPadding { Right = 20 }
- },
- new Box
- {
- Anchor = Anchor.TopRight,
- RelativeSizeAxes = Axes.Y,
- Width = 10,
- Colour = Framework.Graphics.Colour.ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.6f), Color4.Black.Opacity(0)),
- },
- }
- },
- new Container
- {
- Name = "Border",
- RelativeSizeAxes = Axes.Both,
- Masking = true,
- MaskingSmoothness = 0,
- BorderThickness = 2,
- AlwaysPresent = true,
- Children = new[]
- {
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Alpha = 0,
- AlwaysPresent = true
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0.6f
+ },
}
+ },
+ new Container
+ {
+ Name = "Right area",
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Left = left_area_size },
+ Children = new Drawable[]
+ {
+ new Container
+ {
+ Name = "Masked elements before hit objects",
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Left = HIT_TARGET_OFFSET },
+ Masking = true,
+ Children = new Drawable[]
+ {
+ hitExplosionContainer = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ FillMode = FillMode.Fit,
+ Blending = BlendingMode.Additive,
+ },
+ new HitTarget
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ FillMode = FillMode.Fit
+ }
+ }
+ },
+ barlineContainer = new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Left = HIT_TARGET_OFFSET }
+ },
+ new Container
+ {
+ Name = "Hit objects",
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Left = HIT_TARGET_OFFSET },
+ Masking = true,
+ Child = HitObjectContainer
+ },
+ kiaiExplosionContainer = new Container
+ {
+ Name = "Kiai hit explosions",
+ RelativeSizeAxes = Axes.Both,
+ FillMode = FillMode.Fit,
+ Margin = new MarginPadding { Left = HIT_TARGET_OFFSET },
+ Blending = BlendingMode.Additive
+ },
+ judgementContainer = new JudgementContainer
+ {
+ Name = "Judgements",
+ RelativeSizeAxes = Axes.Y,
+ Margin = new MarginPadding { Left = HIT_TARGET_OFFSET },
+ Blending = BlendingMode.Additive
+ },
+ }
+ },
+ overlayBackgroundContainer = new Container
+ {
+ Name = "Left overlay",
+ RelativeSizeAxes = Axes.Y,
+ Size = new Vector2(left_area_size, 1),
+ Children = new Drawable[]
+ {
+ overlayBackground = new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ },
+ new InputDrum(controlPoints)
+ {
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.CentreRight,
+ Scale = new Vector2(0.9f),
+ Margin = new MarginPadding { Right = 20 }
+ },
+ new Box
+ {
+ Anchor = Anchor.TopRight,
+ RelativeSizeAxes = Axes.Y,
+ Width = 10,
+ Colour = Framework.Graphics.Colour.ColourInfo.GradientHorizontal(Color4.Black.Opacity(0.6f), Color4.Black.Opacity(0)),
+ },
+ }
+ },
+ new Container
+ {
+ Name = "Border",
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ MaskingSmoothness = 0,
+ BorderThickness = 2,
+ AlwaysPresent = true,
+ Children = new[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0,
+ AlwaysPresent = true
+ }
+ }
+ },
+ topLevelHitContainer = new Container
+ {
+ Name = "Top level hit objects",
+ RelativeSizeAxes = Axes.Both,
}
- },
- topLevelHitContainer = new Container
- {
- Name = "Top level hit objects",
- RelativeSizeAxes = Axes.Both,
}
- });
+ };
- VisibleTimeRange.Value = 6000;
+ VisibleTimeRange.Value = 7000;
}
[BackgroundDependencyLoader]
diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs
index 229ab69ceb..c94ced3390 100644
--- a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs
+++ b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
-using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
@@ -13,7 +12,6 @@ using osu.Game.Rulesets.Taiko.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.Taiko.Replays;
-using OpenTK;
using System.Linq;
using osu.Framework.Input;
using osu.Game.Input.Handlers;
@@ -74,29 +72,13 @@ namespace osu.Game.Rulesets.Taiko.UI
}
}
- protected override Vector2 GetAspectAdjustedSize()
- {
- const float default_relative_height = TaikoPlayfield.DEFAULT_HEIGHT / 768;
- const float default_aspect = 16f / 9f;
-
- float aspectAdjust = MathHelper.Clamp(DrawWidth / DrawHeight, 0.4f, 4) / default_aspect;
-
- return new Vector2(1, default_relative_height * aspectAdjust);
- }
-
- protected override Vector2 PlayfieldArea => Vector2.One;
-
public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this);
public override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo);
- protected override Playfield CreatePlayfield() => new TaikoPlayfield(Beatmap.ControlPointInfo)
- {
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.CentreLeft
- };
+ protected override Playfield CreatePlayfield() => new TaikoPlayfield(Beatmap.ControlPointInfo);
- protected override DrawableHitObject GetVisualRepresentation(TaikoHitObject h)
+ public override DrawableHitObject GetVisualRepresentation(TaikoHitObject h)
{
switch (h)
{
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
index d3351f86f8..464cfbf5e9 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
@@ -8,11 +8,14 @@ using OpenTK.Graphics;
using osu.Game.Tests.Resources;
using System.Linq;
using osu.Game.Audio;
+using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Beatmaps.Formats;
using osu.Game.Beatmaps.Timing;
using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Legacy;
+using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Skinning;
@@ -21,6 +24,25 @@ namespace osu.Game.Tests.Beatmaps.Formats
[TestFixture]
public class LegacyBeatmapDecoderTest
{
+ [Test]
+ public void TestDecodeBeatmapVersion()
+ {
+ using (var resStream = Resource.OpenResource("beatmap-version.osu"))
+ using (var stream = new StreamReader(resStream))
+ {
+ var decoder = Decoder.GetDecoder(stream);
+
+ stream.BaseStream.Position = 0;
+ stream.DiscardBufferedData();
+
+ var working = new TestWorkingBeatmap(decoder.Decode(stream));
+
+ Assert.AreEqual(6, working.BeatmapInfo.BeatmapVersion);
+ Assert.AreEqual(6, working.Beatmap.BeatmapInfo.BeatmapVersion);
+ Assert.AreEqual(6, working.GetPlayableBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo.BeatmapVersion);
+ }
+ }
+
[Test]
public void TestDecodeBeatmapGeneral()
{
@@ -165,7 +187,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
}
[Test]
- public void TestDecodeBeatmapColors()
+ public void TestDecodeBeatmapColours()
{
var decoder = new LegacySkinDecoder();
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
@@ -181,6 +203,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
new Color4(128, 255, 128, 255),
new Color4(255, 187, 255, 255),
new Color4(255, 177, 140, 255),
+ new Color4(100, 100, 100, 100),
};
Assert.AreEqual(expectedColors.Length, comboColors.Count);
for (int i = 0; i < expectedColors.Length; i++)
@@ -311,5 +334,61 @@ namespace osu.Game.Tests.Beatmaps.Formats
SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
}
+
+ [Test]
+ public void TestDecodeSliderSamples()
+ {
+ var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
+ using (var resStream = Resource.OpenResource("slider-samples.osu"))
+ using (var stream = new StreamReader(resStream))
+ {
+ var hitObjects = decoder.Decode(stream).HitObjects;
+
+ var slider1 = (ConvertSlider)hitObjects[0];
+
+ Assert.AreEqual(1, slider1.NodeSamples[0].Count);
+ Assert.AreEqual(SampleInfo.HIT_NORMAL, slider1.NodeSamples[0][0].Name);
+ Assert.AreEqual(1, slider1.NodeSamples[1].Count);
+ Assert.AreEqual(SampleInfo.HIT_NORMAL, slider1.NodeSamples[1][0].Name);
+ Assert.AreEqual(1, slider1.NodeSamples[2].Count);
+ Assert.AreEqual(SampleInfo.HIT_NORMAL, slider1.NodeSamples[2][0].Name);
+
+ var slider2 = (ConvertSlider)hitObjects[1];
+
+ Assert.AreEqual(2, slider2.NodeSamples[0].Count);
+ Assert.AreEqual(SampleInfo.HIT_NORMAL, slider2.NodeSamples[0][0].Name);
+ Assert.AreEqual(SampleInfo.HIT_CLAP, slider2.NodeSamples[0][1].Name);
+ Assert.AreEqual(2, slider2.NodeSamples[1].Count);
+ Assert.AreEqual(SampleInfo.HIT_NORMAL, slider2.NodeSamples[1][0].Name);
+ Assert.AreEqual(SampleInfo.HIT_CLAP, slider2.NodeSamples[1][1].Name);
+ Assert.AreEqual(2, slider2.NodeSamples[2].Count);
+ Assert.AreEqual(SampleInfo.HIT_NORMAL, slider2.NodeSamples[2][0].Name);
+ Assert.AreEqual(SampleInfo.HIT_CLAP, slider2.NodeSamples[2][1].Name);
+
+ var slider3 = (ConvertSlider)hitObjects[2];
+
+ Assert.AreEqual(2, slider3.NodeSamples[0].Count);
+ Assert.AreEqual(SampleInfo.HIT_NORMAL, slider3.NodeSamples[0][0].Name);
+ Assert.AreEqual(SampleInfo.HIT_WHISTLE, slider3.NodeSamples[0][1].Name);
+ Assert.AreEqual(1, slider3.NodeSamples[1].Count);
+ Assert.AreEqual(SampleInfo.HIT_NORMAL, slider3.NodeSamples[1][0].Name);
+ Assert.AreEqual(2, slider3.NodeSamples[2].Count);
+ Assert.AreEqual(SampleInfo.HIT_NORMAL, slider3.NodeSamples[2][0].Name);
+ Assert.AreEqual(SampleInfo.HIT_CLAP, slider3.NodeSamples[2][1].Name);
+ }
+ }
+
+ [Test]
+ public void TestDecodeHitObjectNullAdditionBank()
+ {
+ var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
+ using (var resStream = Resource.OpenResource("hitobject-no-addition-bank.osu"))
+ using (var stream = new StreamReader(resStream))
+ {
+ var hitObjects = decoder.Decode(stream).HitObjects;
+
+ Assert.AreEqual(hitObjects[0].Samples[0].Bank, hitObjects[0].Samples[1].Bank);
+ }
+ }
}
}
diff --git a/osu.Game.Tests/Resources/Soleily - Renatus (Gamu) [Insane].osu b/osu.Game.Tests/Resources/Soleily - Renatus (Gamu) [Insane].osu
index 3e44dc0af8..67570ad21b 100644
--- a/osu.Game.Tests/Resources/Soleily - Renatus (Gamu) [Insane].osu
+++ b/osu.Game.Tests/Resources/Soleily - Renatus (Gamu) [Insane].osu
@@ -101,6 +101,7 @@ Combo3 : 128,255,255
Combo4 : 128,255,128
Combo5 : 255,187,255
Combo6 : 255,177,140
+Combo7 : 100,100,100,100
[HitObjects]
192,168,956,6,0,P|184:128|200:80,1,90,4|0,1:2|0:0,0:0:0:0:
diff --git a/osu.Game.Tests/Resources/beatmap-version.osu b/osu.Game.Tests/Resources/beatmap-version.osu
new file mode 100644
index 0000000000..5749054ac4
--- /dev/null
+++ b/osu.Game.Tests/Resources/beatmap-version.osu
@@ -0,0 +1 @@
+osu file format v6
\ No newline at end of file
diff --git a/osu.Game.Tests/Resources/hitobject-no-addition-bank.osu b/osu.Game.Tests/Resources/hitobject-no-addition-bank.osu
new file mode 100644
index 0000000000..43d0b8cc16
--- /dev/null
+++ b/osu.Game.Tests/Resources/hitobject-no-addition-bank.osu
@@ -0,0 +1,4 @@
+osu file format v14
+
+[HitObjects]
+444,320,1000,5,2,3:0:1:0:
\ No newline at end of file
diff --git a/osu.Game.Tests/Resources/slider-samples.osu b/osu.Game.Tests/Resources/slider-samples.osu
new file mode 100644
index 0000000000..7759a2e35f
--- /dev/null
+++ b/osu.Game.Tests/Resources/slider-samples.osu
@@ -0,0 +1,23 @@
+osu file format v14
+
+[General]
+SampleSet: Normal
+
+[Difficulty]
+
+SliderMultiplier:1.6
+SliderTickRate:2
+
+[TimingPoints]
+24735,389.61038961039,4,1,1,25,1,0
+
+[HitObjects]
+// Unified: Normal, Normal, Normal
+168,256,30579,6,0,B|248:320|320:248,2,160
+
+// Unified: Normal+Clap, Normal+Clap, Normal+Clap
+168,256,32137,6,8,B|248:320|320:248,2,160
+
+// Nodal: Normal+Whistle, Normal, Normal+Clap
+// Nodal sounds should override the unified clap sound
+168,256,33696,6,8,B|248:320|320:248,2,160,2|0|8
\ No newline at end of file
diff --git a/osu.Game.Tests/Visual/TestCaseBeatDivisorControl.cs b/osu.Game.Tests/Visual/TestCaseBeatDivisorControl.cs
index 1effa14e76..6c607acd11 100644
--- a/osu.Game.Tests/Visual/TestCaseBeatDivisorControl.cs
+++ b/osu.Game.Tests/Visual/TestCaseBeatDivisorControl.cs
@@ -5,7 +5,8 @@ using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
-using osu.Game.Screens.Edit.Screens.Compose;
+using osu.Game.Screens.Edit;
+using osu.Game.Screens.Edit.Compose.Components;
using OpenTK;
namespace osu.Game.Tests.Visual
diff --git a/osu.Game.Tests/Visual/TestCaseCursors.cs b/osu.Game.Tests/Visual/TestCaseCursors.cs
index 361e255894..1f409f043e 100644
--- a/osu.Game.Tests/Visual/TestCaseCursors.cs
+++ b/osu.Game.Tests/Visual/TestCaseCursors.cs
@@ -7,7 +7,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
using osu.Framework.MathUtils;
using osu.Game.Graphics.Cursor;
using osu.Game.Graphics.Sprites;
@@ -184,7 +184,7 @@ namespace osu.Game.Tests.Visual
///
/// The cursor to check.
private bool checkAtMouse(CursorContainer cursorContainer)
- => Precision.AlmostEquals(InputManager.CurrentState.Mouse.NativeState.Position, cursorContainer.ToScreenSpace(cursorContainer.ActiveCursor.DrawPosition));
+ => Precision.AlmostEquals(InputManager.CurrentState.Mouse.Position, cursorContainer.ToScreenSpace(cursorContainer.ActiveCursor.DrawPosition));
private class CustomCursorBox : Container, IProvideCursor
{
@@ -193,7 +193,7 @@ namespace osu.Game.Tests.Visual
public CursorContainer Cursor { get; }
public bool ProvidingUserCursor { get; }
- public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => base.ReceiveMouseInputAt(screenSpacePos) || SmoothTransition && !ProvidingUserCursor;
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => base.ReceivePositionalInputAt(screenSpacePos) || SmoothTransition && !ProvidingUserCursor;
private readonly Box background;
@@ -224,16 +224,16 @@ namespace osu.Game.Tests.Visual
};
}
- protected override bool OnHover(InputState state)
+ protected override bool OnHover(HoverEvent e)
{
background.FadeTo(0.4f, 250, Easing.OutQuint);
return false;
}
- protected override void OnHoverLost(InputState state)
+ protected override void OnHoverLost(HoverLostEvent e)
{
background.FadeTo(0.1f, 250);
- base.OnHoverLost(state);
+ base.OnHoverLost(e);
}
}
diff --git a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs
index e7bcfbf500..5c53fdfac4 100644
--- a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs
+++ b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs
@@ -6,7 +6,7 @@ using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Rulesets.Osu;
-using osu.Game.Screens.Edit.Screens.Compose;
+using osu.Game.Screens.Edit.Compose;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Visual
@@ -14,13 +14,13 @@ namespace osu.Game.Tests.Visual
[TestFixture]
public class TestCaseEditorCompose : EditorClockTestCase
{
- public override IReadOnlyList RequiredTypes => new[] { typeof(Compose) };
+ public override IReadOnlyList RequiredTypes => new[] { typeof(ComposeScreen) };
[BackgroundDependencyLoader]
private void load()
{
Beatmap.Value = new TestWorkingBeatmap(new OsuRuleset().RulesetInfo);
- Child = new Compose();
+ Child = new ComposeScreen();
}
}
}
diff --git a/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs b/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs
index 09f390ab74..9df36b0bc1 100644
--- a/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs
+++ b/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs
@@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Graphics;
-using osu.Game.Screens.Edit.Screens.Compose.RadioButtons;
+using osu.Game.Screens.Edit.Components.RadioButtons;
namespace osu.Game.Tests.Visual
{
diff --git a/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs b/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs
index 9ad8bf7b92..d2c1127f4c 100644
--- a/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs
+++ b/osu.Game.Tests/Visual/TestCaseEditorComposeTimeline.cs
@@ -13,7 +13,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
-using osu.Game.Screens.Edit.Screens.Compose.Timeline;
+using osu.Game.Screens.Edit.Compose.Components.Timeline;
using OpenTK.Graphics;
namespace osu.Game.Tests.Visual
diff --git a/osu.Game.Tests/Visual/TestCaseEditorMenuBar.cs b/osu.Game.Tests/Visual/TestCaseEditorMenuBar.cs
index cb4438b2ba..eab799011d 100644
--- a/osu.Game.Tests/Visual/TestCaseEditorMenuBar.cs
+++ b/osu.Game.Tests/Visual/TestCaseEditorMenuBar.cs
@@ -8,7 +8,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
-using osu.Game.Screens.Edit.Menus;
+using osu.Game.Screens.Edit.Components.Menus;
namespace osu.Game.Tests.Visual
{
diff --git a/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs b/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs
index 45b5ec2c11..417b0f94d7 100644
--- a/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs
+++ b/osu.Game.Tests/Visual/TestCaseGameplayMenuOverlay.cs
@@ -8,14 +8,14 @@ using System.Linq;
using OpenTK.Input;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Input.EventArgs;
using osu.Framework.Logging;
using osu.Game.Screens.Play;
+using OpenTK;
namespace osu.Game.Tests.Visual
{
[Description("player pause/fail screens")]
- public class TestCaseGameplayMenuOverlay : OsuTestCase
+ public class TestCaseGameplayMenuOverlay : ManualInputManagerTestCase
{
public override IReadOnlyList RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseContainer) };
@@ -73,12 +73,18 @@ namespace osu.Game.Tests.Visual
{
AddStep("Show overlay", () => failOverlay.Show());
- AddStep("Hover first button", () => failOverlay.Buttons.First().TriggerOnMouseMove(null));
+ AddStep("Hover first button", () => InputManager.MoveMouseTo(failOverlay.Buttons.First()));
AddStep("Hide overlay", () => failOverlay.Hide());
AddAssert("Overlay state is reset", () => !failOverlay.Buttons.Any(b => b.Selected));
}
+ private void press(Key key)
+ {
+ InputManager.PressKey(key);
+ InputManager.ReleaseKey(key);
+ }
+
///
/// Tests that pressing enter after an overlay shows doesn't trigger an event because a selection hasn't occurred.
///
@@ -86,7 +92,7 @@ namespace osu.Game.Tests.Visual
{
AddStep("Show overlay", () => pauseOverlay.Show());
- AddStep("Press enter", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Enter }));
+ AddStep("Press enter", () => press(Key.Enter));
AddAssert("Overlay still open", () => pauseOverlay.State == Visibility.Visible);
AddStep("Hide overlay", () => pauseOverlay.Hide());
@@ -99,7 +105,7 @@ namespace osu.Game.Tests.Visual
{
AddStep("Show overlay", () => pauseOverlay.Show());
- AddStep("Up arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up }));
+ AddStep("Up arrow", () => press(Key.Up));
AddAssert("Last button selected", () => pauseOverlay.Buttons.Last().Selected);
AddStep("Hide overlay", () => pauseOverlay.Hide());
@@ -112,7 +118,7 @@ namespace osu.Game.Tests.Visual
{
AddStep("Show overlay", () => pauseOverlay.Show());
- AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }));
+ AddStep("Down arrow", () => press(Key.Down));
AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected);
AddStep("Hide overlay", () => pauseOverlay.Hide());
@@ -125,11 +131,11 @@ namespace osu.Game.Tests.Visual
{
AddStep("Show overlay", () => failOverlay.Show());
- AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up }));
+ AddStep("Up arrow", () => press(Key.Up));
AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected);
- AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up }));
+ AddStep("Up arrow", () => press(Key.Up));
AddAssert("First button selected", () => failOverlay.Buttons.First().Selected);
- AddStep("Up arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up }));
+ AddStep("Up arrow", () => press(Key.Up));
AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected);
AddStep("Hide overlay", () => failOverlay.Hide());
@@ -142,11 +148,11 @@ namespace osu.Game.Tests.Visual
{
AddStep("Show overlay", () => failOverlay.Show());
- AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }));
+ AddStep("Down arrow", () => press(Key.Down));
AddAssert("First button selected", () => failOverlay.Buttons.First().Selected);
- AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }));
+ AddStep("Down arrow", () => press(Key.Down));
AddAssert("Last button selected", () => failOverlay.Buttons.Last().Selected);
- AddStep("Down arrow", () => failOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }));
+ AddStep("Down arrow", () => press(Key.Down));
AddAssert("First button selected", () => failOverlay.Buttons.First().Selected);
AddStep("Hide overlay", () => failOverlay.Hide());
@@ -161,8 +167,8 @@ namespace osu.Game.Tests.Visual
var secondButton = pauseOverlay.Buttons.Skip(1).First();
- AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }));
- AddStep("Hover second button", () => secondButton.TriggerOnMouseMove(null));
+ AddStep("Down arrow", () => press(Key.Down));
+ AddStep("Hover second button", () => InputManager.MoveMouseTo(secondButton));
AddAssert("First button not selected", () => !pauseOverlay.Buttons.First().Selected);
AddAssert("Second button selected", () => secondButton.Selected);
@@ -174,12 +180,16 @@ namespace osu.Game.Tests.Visual
///
private void testKeySelectionAfterMouseSelection()
{
- AddStep("Show overlay", () => pauseOverlay.Show());
+ AddStep("Show overlay", () =>
+ {
+ pauseOverlay.Show();
+ InputManager.MoveMouseTo(Vector2.Zero);
+ });
var secondButton = pauseOverlay.Buttons.Skip(1).First();
- AddStep("Hover second button", () => secondButton.TriggerOnMouseMove(null));
- AddStep("Up arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Up }));
+ AddStep("Hover second button", () => InputManager.MoveMouseTo(secondButton));
+ AddStep("Up arrow", () => press(Key.Up));
AddAssert("Second button not selected", () => !secondButton.Selected);
AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected);
@@ -195,9 +205,9 @@ namespace osu.Game.Tests.Visual
var secondButton = pauseOverlay.Buttons.Skip(1).First();
- AddStep("Hover second button", () => secondButton.TriggerOnMouseMove(null));
- AddStep("Unhover second button", () => secondButton.TriggerOnHoverLost(null));
- AddStep("Down arrow", () => pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down }));
+ AddStep("Hover second button", () => InputManager.MoveMouseTo(secondButton));
+ AddStep("Unhover second button", () => InputManager.MoveMouseTo(Vector2.Zero));
+ AddStep("Down arrow", () => press(Key.Down));
AddAssert("First button selected", () => pauseOverlay.Buttons.First().Selected); // Initial state condition
AddStep("Hide overlay", () => pauseOverlay.Hide());
@@ -218,7 +228,7 @@ namespace osu.Game.Tests.Visual
var lastAction = pauseOverlay.OnRetry;
pauseOverlay.OnRetry = () => triggered = true;
- retryButton.TriggerOnClick();
+ retryButton.Click();
pauseOverlay.OnRetry = lastAction;
});
@@ -235,23 +245,28 @@ namespace osu.Game.Tests.Visual
AddStep("Select second button", () =>
{
- pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down });
- pauseOverlay.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Down });
+ press(Key.Down);
+ press(Key.Down);
});
- var retryButton = pauseOverlay.Buttons.Skip(1).First();
-
bool triggered = false;
+ Action lastAction = null;
AddStep("Press enter", () =>
{
- var lastAction = pauseOverlay.OnRetry;
+ lastAction = pauseOverlay.OnRetry;
pauseOverlay.OnRetry = () => triggered = true;
-
- retryButton.TriggerOnKeyDown(null, new KeyDownEventArgs { Key = Key.Enter });
- pauseOverlay.OnRetry = lastAction;
+ press(Key.Enter);
});
- AddAssert("Action was triggered", () => triggered);
+ AddAssert("Action was triggered", () =>
+ {
+ if (lastAction != null)
+ {
+ pauseOverlay.OnRetry = lastAction;
+ lastAction = null;
+ }
+ return triggered;
+ });
AddAssert("Overlay is closed", () => pauseOverlay.State == Visibility.Hidden);
}
}
diff --git a/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs b/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs
index 5df371dd09..2629b29c6c 100644
--- a/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs
+++ b/osu.Game.Tests/Visual/TestCaseHitObjectComposer.cs
@@ -13,25 +13,34 @@ using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Edit;
+using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles;
+using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
using osu.Game.Rulesets.Osu.Objects;
-using osu.Game.Screens.Edit.Screens.Compose.Layers;
+using osu.Game.Screens.Edit.Compose;
+using osu.Game.Screens.Edit.Compose.Components;
using osu.Game.Tests.Beatmaps;
namespace osu.Game.Tests.Visual
{
[TestFixture]
- public class TestCaseHitObjectComposer : OsuTestCase
+ [Cached(Type = typeof(IPlacementHandler))]
+ public class TestCaseHitObjectComposer : OsuTestCase, IPlacementHandler
{
public override IReadOnlyList RequiredTypes => new[]
{
- typeof(MaskSelection),
- typeof(DragLayer),
+ typeof(SelectionBox),
+ typeof(DragBox),
typeof(HitObjectComposer),
typeof(OsuHitObjectComposer),
- typeof(HitObjectMaskLayer),
- typeof(NotNullAttribute)
+ typeof(BlueprintContainer),
+ typeof(NotNullAttribute),
+ typeof(HitCirclePiece),
+ typeof(HitCircleSelectionBlueprint),
+ typeof(HitCirclePlacementBlueprint),
};
+ private HitObjectComposer composer;
+
[BackgroundDependencyLoader]
private void load()
{
@@ -44,14 +53,12 @@ namespace osu.Game.Tests.Visual
new Slider
{
Position = new Vector2(128, 256),
- ControlPoints = new List
+ ControlPoints = new[]
{
Vector2.Zero,
new Vector2(216, 0),
},
- Distance = 400,
- Velocity = 1,
- TickDistance = 100,
+ Distance = 216,
Scale = 0.5f,
}
},
@@ -61,7 +68,15 @@ namespace osu.Game.Tests.Visual
Dependencies.CacheAs(clock);
Dependencies.CacheAs(clock);
- Child = new OsuHitObjectComposer(new OsuRuleset());
+ Child = composer = new OsuHitObjectComposer(new OsuRuleset());
}
+
+ public void BeginPlacement(HitObject hitObject)
+ {
+ }
+
+ public void EndPlacement(HitObject hitObject) => composer.Add(hitObject);
+
+ public void Delete(HitObject hitObject) => composer.Remove(hitObject);
}
}
diff --git a/osu.Game.Tests/Visual/TestCaseKeyCounter.cs b/osu.Game.Tests/Visual/TestCaseKeyCounter.cs
index b98875cd6a..178b47ed33 100644
--- a/osu.Game.Tests/Visual/TestCaseKeyCounter.cs
+++ b/osu.Game.Tests/Visual/TestCaseKeyCounter.cs
@@ -1,32 +1,45 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System;
+using System.Collections.Generic;
+using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Framework.MathUtils;
+using osu.Framework.Timing;
using osu.Game.Screens.Play;
using OpenTK.Input;
namespace osu.Game.Tests.Visual
{
[TestFixture]
- public class TestCaseKeyCounter : OsuTestCase
+ public class TestCaseKeyCounter : ManualInputManagerTestCase
{
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(KeyCounterKeyboard),
+ typeof(KeyCounterMouse),
+ typeof(KeyCounterCollection)
+ };
+
public TestCaseKeyCounter()
{
+ KeyCounterKeyboard rewindTestKeyCounterKeyboard;
KeyCounterCollection kc = new KeyCounterCollection
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Children = new KeyCounter[]
{
- new KeyCounterKeyboard(Key.Z),
+ rewindTestKeyCounterKeyboard = new KeyCounterKeyboard(Key.X),
new KeyCounterKeyboard(Key.X),
new KeyCounterMouse(MouseButton.Left),
new KeyCounterMouse(MouseButton.Right),
},
};
+
AddStep("Add random", () =>
{
Key key = (Key)((int)Key.A + RNG.Next(26));
@@ -34,7 +47,57 @@ namespace osu.Game.Tests.Visual
});
AddSliderStep("Fade time", 0, 200, 50, v => kc.FadeTime = v);
+ Key testKey = ((KeyCounterKeyboard)kc.Children.First()).Key;
+ double time1 = 0;
+
+ AddStep($"Press {testKey} key", () =>
+ {
+ InputManager.PressKey(testKey);
+ InputManager.ReleaseKey(testKey);
+ });
+
+ AddAssert($"Check {testKey} counter after keypress", () => rewindTestKeyCounterKeyboard.CountPresses == 1);
+
+ AddStep($"Press {testKey} key", () =>
+ {
+ InputManager.PressKey(testKey);
+ InputManager.ReleaseKey(testKey);
+ time1 = Clock.CurrentTime;
+ });
+
+ AddAssert($"Check {testKey} counter after keypress", () => rewindTestKeyCounterKeyboard.CountPresses == 2);
+
+ IFrameBasedClock oldClock = null;
+
+ AddStep($"Rewind {testKey} counter once", () =>
+ {
+ oldClock = rewindTestKeyCounterKeyboard.Clock;
+ rewindTestKeyCounterKeyboard.Clock = new FramedOffsetClock(new FixedClock(time1 - 10));
+ });
+
+ AddAssert($"Check {testKey} counter after rewind", () => rewindTestKeyCounterKeyboard.CountPresses == 1);
+
+ AddStep($"Rewind {testKey} counter to zero", () => rewindTestKeyCounterKeyboard.Clock = new FramedOffsetClock(new FixedClock(0)));
+
+ AddAssert($"Check {testKey} counter after rewind", () => rewindTestKeyCounterKeyboard.CountPresses == 0);
+
+ AddStep("Restore clock", () => rewindTestKeyCounterKeyboard.Clock = oldClock);
+
Add(kc);
}
+
+ private class FixedClock : IClock
+ {
+ private readonly double time;
+
+ public FixedClock(double time)
+ {
+ this.time = time;
+ }
+
+ public double CurrentTime => time;
+ public double Rate => 1;
+ public bool IsRunning => false;
+ }
}
}
diff --git a/osu.Game.Tests/Visual/TestCaseLabelledTextBox.cs b/osu.Game.Tests/Visual/TestCaseLabelledTextBox.cs
index d41739bfb5..e1470a860e 100644
--- a/osu.Game.Tests/Visual/TestCaseLabelledTextBox.cs
+++ b/osu.Game.Tests/Visual/TestCaseLabelledTextBox.cs
@@ -5,9 +5,9 @@ using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Game.Screens.Edit.Screens.Setup.Components.LabelledComponents;
using System;
using System.Collections.Generic;
+using osu.Game.Screens.Edit.Setup.Components.LabelledComponents;
namespace osu.Game.Tests.Visual
{
diff --git a/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs b/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs
index bbc9d2b860..b254325472 100644
--- a/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs
+++ b/osu.Game.Tests/Visual/TestCaseScrollingHitObjects.cs
@@ -121,14 +121,21 @@ namespace osu.Game.Tests.Visual
Direction = direction;
Padding = new MarginPadding(2);
- Content.Masking = true;
- AddInternal(new Box
+ InternalChildren = new Drawable[]
{
- RelativeSizeAxes = Axes.Both,
- Alpha = 0.5f,
- Depth = float.MaxValue
- });
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Alpha = 0.5f,
+ },
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ Child = HitObjectContainer
+ }
+ };
}
}
diff --git a/osu.Game.Tests/Visual/TestCaseZoomableScrollContainer.cs b/osu.Game.Tests/Visual/TestCaseZoomableScrollContainer.cs
index 8bd1b79a84..3bf809ebde 100644
--- a/osu.Game.Tests/Visual/TestCaseZoomableScrollContainer.cs
+++ b/osu.Game.Tests/Visual/TestCaseZoomableScrollContainer.cs
@@ -10,7 +10,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.MathUtils;
using osu.Game.Graphics;
using osu.Game.Graphics.Cursor;
-using osu.Game.Screens.Edit.Screens.Compose.Timeline;
+using osu.Game.Screens.Edit.Compose.Components.Timeline;
using OpenTK;
using OpenTK.Graphics;
diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj
index d638af0c38..c0f0695ff8 100644
--- a/osu.Game.Tests/osu.Game.Tests.csproj
+++ b/osu.Game.Tests/osu.Game.Tests.csproj
@@ -2,9 +2,11 @@
-
-
-
+
+
+
+
+
WinExe
diff --git a/osu.Game.props b/osu.Game.props
index ec859e64a5..4bcac479a0 100644
--- a/osu.Game.props
+++ b/osu.Game.props
@@ -1,7 +1,7 @@
- 7
+ 7.2
..\app.manifest
diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs
index 9aabb434a3..4ef7b28d49 100644
--- a/osu.Game/Beatmaps/Beatmap.cs
+++ b/osu.Game/Beatmaps/Beatmap.cs
@@ -48,7 +48,7 @@ namespace osu.Game.Beatmaps
[JsonConverter(typeof(TypedListConverter))]
public List HitObjects = new List();
- IEnumerable IBeatmap.HitObjects => HitObjects;
+ IReadOnlyList IBeatmap.HitObjects => HitObjects;
public virtual IEnumerable GetStatistics() => Enumerable.Empty();
diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs
index a1bb70135a..3cb7833a12 100644
--- a/osu.Game/Beatmaps/BeatmapConverter.cs
+++ b/osu.Game/Beatmaps/BeatmapConverter.cs
@@ -55,39 +55,40 @@ namespace osu.Game.Beatmaps
beatmap.BeatmapInfo = original.BeatmapInfo;
beatmap.ControlPointInfo = original.ControlPointInfo;
- beatmap.HitObjects = original.HitObjects.SelectMany(h => convert(h, original)).ToList();
+ beatmap.HitObjects = convertHitObjects(original.HitObjects, original);
beatmap.Breaks = original.Breaks;
return beatmap;
}
- ///
- /// Converts a hit object.
- ///
- /// The hit object to convert.
- /// The un-converted Beatmap.
- /// The converted hit object.
- private IEnumerable convert(HitObject original, IBeatmap beatmap)
+ private List convertHitObjects(IReadOnlyList hitObjects, IBeatmap beatmap)
{
- // Check if the hitobject is already the converted type
- T tObject = original as T;
- if (tObject != null)
- {
- yield return tObject;
- yield break;
- }
+ var result = new List(hitObjects.Count);
- var converted = ConvertHitObject(original, beatmap).ToList();
- ObjectConverted?.Invoke(original, converted);
-
- // Convert the hit object
- foreach (var obj in converted)
+ foreach (var obj in hitObjects)
{
- if (obj == null)
+ if (obj is T tObj)
+ {
+ result.Add(tObj);
continue;
+ }
- yield return obj;
+ var converted = ConvertHitObject(obj, beatmap);
+
+ if (ObjectConverted != null)
+ {
+ converted = converted.ToList();
+ ObjectConverted.Invoke(obj, converted);
+ }
+
+ foreach (var c in converted)
+ {
+ if (c != null)
+ result.Add(c);
+ }
}
+
+ return result;
}
///
diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
index 303a19aab3..3e1f3bdf54 100644
--- a/osu.Game/Beatmaps/BeatmapInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapInfo.cs
@@ -19,7 +19,6 @@ namespace osu.Game.Beatmaps
[JsonIgnore]
public int ID { get; set; }
- //TODO: should be in database
public int BeatmapVersion;
private int? onlineBeatmapID;
@@ -27,13 +26,15 @@ namespace osu.Game.Beatmaps
[JsonProperty("id")]
public int? OnlineBeatmapID
{
- get { return onlineBeatmapID; }
- set { onlineBeatmapID = value > 0 ? value : null; }
+ get => onlineBeatmapID;
+ set => onlineBeatmapID = value > 0 ? value : null;
}
[JsonIgnore]
public int BeatmapSetInfoID { get; set; }
+ public BeatmapSetOnlineStatus Status { get; set; } = BeatmapSetOnlineStatus.None;
+
[Required]
public BeatmapSetInfo BeatmapSet { get; set; }
@@ -82,7 +83,7 @@ namespace osu.Game.Beatmaps
[JsonIgnore]
public string StoredBookmarks
{
- get { return string.Join(",", Bookmarks); }
+ get => string.Join(",", Bookmarks);
set
{
if (string.IsNullOrEmpty(value))
@@ -93,8 +94,7 @@ namespace osu.Game.Beatmaps
Bookmarks = value.Split(',').Select(v =>
{
- int val;
- bool result = int.TryParse(v, out val);
+ bool result = int.TryParse(v, out int val);
return new { result, val };
}).Where(p => p.result).Select(p => p.val).ToArray();
}
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index 21df9a6c68..24c68d392b 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -1,4 +1,4 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
@@ -104,7 +104,7 @@ namespace osu.Game.Beatmaps
validateOnlineIds(beatmapSet.Beatmaps);
foreach (BeatmapInfo b in beatmapSet.Beatmaps)
- fetchAndPopulateOnlineIDs(b, beatmapSet.Beatmaps);
+ fetchAndPopulateOnlineValues(b, beatmapSet.Beatmaps);
// check if a set already exists with the same online id, delete if it does.
if (beatmapSet.OnlineBeatmapSetID != null)
@@ -148,11 +148,12 @@ namespace osu.Game.Beatmaps
///
/// The to be downloaded.
/// Whether the beatmap should be downloaded without video. Defaults to false.
- public void Download(BeatmapSetInfo beatmapSetInfo, bool noVideo = false)
+ /// Downloading can happen
+ public bool Download(BeatmapSetInfo beatmapSetInfo, bool noVideo = false)
{
var existing = GetExistingDownload(beatmapSetInfo);
- if (existing != null || api == null) return;
+ if (existing != null || api == null) return false;
if (!api.LocalUser.Value.IsSupporter)
{
@@ -161,7 +162,7 @@ namespace osu.Game.Beatmaps
Icon = FontAwesome.fa_superpowers,
Text = "You gotta be an osu!supporter to download for now 'yo"
});
- return;
+ return false;
}
var downloadNotification = new DownloadNotification
@@ -227,6 +228,7 @@ namespace osu.Game.Beatmaps
// don't run in the main api queue as this is a long-running task.
Task.Factory.StartNew(() => request.Perform(api), TaskCreationOptions.LongRunning);
BeatmapDownloadBegan?.Invoke(request);
+ return true;
}
protected override void PresentCompletedImport(IEnumerable imported)
@@ -348,7 +350,7 @@ namespace osu.Game.Beatmaps
OnlineBeatmapSetID = beatmap.BeatmapInfo.BeatmapSet?.OnlineBeatmapSetID,
Beatmaps = new List(),
Hash = computeBeatmapSetHash(reader),
- Metadata = beatmap.Metadata
+ Metadata = beatmap.Metadata,
};
}
@@ -388,21 +390,22 @@ namespace osu.Game.Beatmaps
}
///
- /// Query the API to populate mising OnlineBeatmapID / OnlineBeatmapSetID properties.
+ /// Query the API to populate missing values like OnlineBeatmapID / OnlineBeatmapSetID or (Rank-)Status.
///
/// The beatmap to populate.
/// The other beatmaps contained within this set.
/// Whether to re-query if the provided beatmap already has populated values.
/// True if population was successful.
- private bool fetchAndPopulateOnlineIDs(BeatmapInfo beatmap, IEnumerable otherBeatmaps, bool force = false)
+ private bool fetchAndPopulateOnlineValues(BeatmapInfo beatmap, IEnumerable otherBeatmaps, bool force = false)
{
if (api?.State != APIState.Online)
return false;
- if (!force && beatmap.OnlineBeatmapID != null && beatmap.BeatmapSet.OnlineBeatmapSetID != null)
+ if (!force && beatmap.OnlineBeatmapID != null && beatmap.BeatmapSet.OnlineBeatmapSetID != null
+ && beatmap.Status != BeatmapSetOnlineStatus.None && beatmap.BeatmapSet.Status != BeatmapSetOnlineStatus.None)
return true;
- Logger.Log("Attempting online lookup for IDs...", LoggingTarget.Database);
+ Logger.Log("Attempting online lookup for the missing values...", LoggingTarget.Database);
try
{
@@ -414,6 +417,9 @@ namespace osu.Game.Beatmaps
Logger.Log($"Successfully mapped to {res.OnlineBeatmapSetID} / {res.OnlineBeatmapID}.", LoggingTarget.Database);
+ beatmap.Status = res.Status;
+ beatmap.BeatmapSet.Status = res.BeatmapSet.Status;
+
if (otherBeatmaps.Any(b => b.OnlineBeatmapID == res.OnlineBeatmapID))
{
Logger.Log("Another beatmap in the same set already mapped to this ID. We'll skip adding it this time.", LoggingTarget.Database);
@@ -422,6 +428,7 @@ namespace osu.Game.Beatmaps
beatmap.BeatmapSet.OnlineBeatmapSetID = res.OnlineBeatmapSetID;
beatmap.OnlineBeatmapID = res.OnlineBeatmapID;
+
return true;
}
catch (Exception e)
diff --git a/osu.Game/Beatmaps/BeatmapProcessor.cs b/osu.Game/Beatmaps/BeatmapProcessor.cs
index 9d7cd673dc..9db2c5f08e 100644
--- a/osu.Game/Beatmaps/BeatmapProcessor.cs
+++ b/osu.Game/Beatmaps/BeatmapProcessor.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
+using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Beatmaps
@@ -44,6 +45,25 @@ namespace osu.Game.Beatmaps
public virtual void PostProcess()
{
+ void updateNestedCombo(HitObject obj, int comboIndex, int indexInCurrentCombo)
+ {
+ if (obj is IHasComboInformation objectComboInfo)
+ {
+ objectComboInfo.ComboIndex = comboIndex;
+ objectComboInfo.IndexInCurrentCombo = indexInCurrentCombo;
+ foreach (var nestedObject in obj.NestedHitObjects)
+ updateNestedCombo(nestedObject, comboIndex, indexInCurrentCombo);
+ }
+ }
+
+ foreach (var hitObject in Beatmap.HitObjects)
+ {
+ if (hitObject is IHasComboInformation objectComboInfo)
+ {
+ foreach (var nested in hitObject.NestedHitObjects)
+ updateNestedCombo(nested, objectComboInfo.ComboIndex, objectComboInfo.IndexInCurrentCombo);
+ }
+ }
}
}
}
diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs
index ebebe42097..7a7d010a31 100644
--- a/osu.Game/Beatmaps/BeatmapSetInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs
@@ -17,10 +17,12 @@ namespace osu.Game.Beatmaps
public int? OnlineBeatmapSetID
{
- get { return onlineBeatmapSetID; }
- set { onlineBeatmapSetID = value > 0 ? value : null; }
+ get => onlineBeatmapSetID;
+ set => onlineBeatmapSetID = value > 0 ? value : null;
}
+ public BeatmapSetOnlineStatus Status { get; set; } = BeatmapSetOnlineStatus.None;
+
public BeatmapMetadata Metadata { get; set; }
public List Beatmaps { get; set; }
diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs
index dad69321a5..f064d53358 100644
--- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs
+++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs
@@ -55,14 +55,14 @@ namespace osu.Game.Beatmaps.ControlPoints
///
/// The time to find the sound control point at.
/// The sound control point.
- public SampleControlPoint SamplePointAt(double time) => binarySearch(SamplePoints, time, SamplePoints.FirstOrDefault());
+ public SampleControlPoint SamplePointAt(double time) => binarySearch(SamplePoints, time, SamplePoints.Count > 0 ? SamplePoints[0] : null);
///
/// Finds the timing control point that is active at .
///
/// The time to find the timing control point at.
/// The timing control point.
- public TimingControlPoint TimingPointAt(double time) => binarySearch(TimingPoints, time, TimingPoints.FirstOrDefault());
+ public TimingControlPoint TimingPointAt(double time) => binarySearch(TimingPoints, time, TimingPoints.Count > 0 ? TimingPoints[0] : null);
///
/// Finds the maximum BPM represented by any timing control point.
@@ -104,17 +104,26 @@ namespace osu.Game.Beatmaps.ControlPoints
if (time < list[0].Time)
return prePoint ?? new T();
- int index = list.BinarySearch(new T { Time = time });
+ if (time >= list[list.Count - 1].Time)
+ return list[list.Count - 1];
- // Check if we've found an exact match (t == time)
- if (index >= 0)
- return list[index];
+ int l = 0;
+ int r = list.Count - 2;
- index = ~index;
+ while (l <= r)
+ {
+ int pivot = l + ((r - l) >> 1);
- // BinarySearch will return the index of the first element _greater_ than the search
- // This is the inactive point - the active point is the one before it (index - 1)
- return list[index - 1];
+ if (list[pivot].Time < time)
+ l = pivot + 1;
+ else if (list[pivot].Time > time)
+ r = pivot - 1;
+ else
+ return list[pivot];
+ }
+
+ // l will be the first control point with Time > time, but we want the one before it
+ return list[l - 1];
}
}
}
diff --git a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs
index eb60133fed..81eddaa43a 100644
--- a/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs
+++ b/osu.Game/Beatmaps/ControlPoints/TimingControlPoint.cs
@@ -16,7 +16,7 @@ namespace osu.Game.Beatmaps.ControlPoints
///
/// The beat length at this control point.
///
- public double BeatLength
+ public virtual double BeatLength
{
get => beatLength;
set => beatLength = MathHelper.Clamp(value, 6, 60000);
diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetDownloader.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetDownloader.cs
index 6f4d4c0d6f..5b5dbec9c8 100644
--- a/osu.Game/Beatmaps/Drawables/BeatmapSetDownloader.cs
+++ b/osu.Game/Beatmaps/Drawables/BeatmapSetDownloader.cs
@@ -71,9 +71,11 @@ namespace osu.Game.Beatmaps.Drawables
if (DownloadState.Value > DownloadStatus.NotDownloaded)
return;
- beatmaps.Download(set, noVideo);
-
- DownloadState.Value = DownloadStatus.Downloading;
+ if (beatmaps.Download(set, noVideo))
+ {
+ // Only change state if download can happen
+ DownloadState.Value = DownloadStatus.Downloading;
+ }
}
private void setAdded(BeatmapSetInfo s)
diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs
index c7e97cef55..da281b4db3 100644
--- a/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs
+++ b/osu.Game/Beatmaps/Drawables/BeatmapSetOnlineStatusPill.cs
@@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using System;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@@ -14,20 +13,35 @@ namespace osu.Game.Beatmaps.Drawables
{
private readonly OsuSpriteText statusText;
- private BeatmapSetOnlineStatus status = BeatmapSetOnlineStatus.None;
+ private BeatmapSetOnlineStatus status;
+
public BeatmapSetOnlineStatus Status
{
- get { return status; }
+ get => status;
set
{
- if (value == status) return;
+ if (status == value)
+ return;
status = value;
- statusText.Text = Enum.GetName(typeof(BeatmapSetOnlineStatus), Status)?.ToUpperInvariant();
+ Alpha = value == BeatmapSetOnlineStatus.None ? 0 : 1;
+ statusText.Text = value.ToString().ToUpperInvariant();
}
}
- public BeatmapSetOnlineStatusPill(float textSize, MarginPadding textPadding)
+ public float TextSize
+ {
+ get => statusText.TextSize;
+ set => statusText.TextSize = value;
+ }
+
+ public MarginPadding TextPadding
+ {
+ get => statusText.Padding;
+ set => statusText.Padding = value;
+ }
+
+ public BeatmapSetOnlineStatusPill()
{
AutoSizeAxes = Axes.Both;
Masking = true;
@@ -45,10 +59,10 @@ namespace osu.Game.Beatmaps.Drawables
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = @"Exo2.0-Bold",
- TextSize = textSize,
- Padding = textPadding,
},
};
+
+ Status = BeatmapSetOnlineStatus.None;
}
}
}
diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
index 25a76b52a7..5c129f76ec 100644
--- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs
@@ -26,15 +26,7 @@ namespace osu.Game.Beatmaps
Title = "no beatmaps available!"
},
BeatmapSet = new BeatmapSetInfo(),
- BaseDifficulty = new BeatmapDifficulty
- {
- DrainRate = 0,
- CircleSize = 0,
- OverallDifficulty = 0,
- ApproachRate = 0,
- SliderMultiplier = 0,
- SliderTickRate = 0,
- },
+ BaseDifficulty = new BeatmapDifficulty(),
Ruleset = new DummyRulesetInfo()
})
{
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index 181d17932d..71b7a65ccb 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -5,6 +5,7 @@ using System;
using System.Globalization;
using System.IO;
using System.Linq;
+using osu.Framework.IO.File;
using osu.Game.Beatmaps.Timing;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Beatmaps.ControlPoints;
@@ -58,7 +59,7 @@ namespace osu.Game.Beatmaps.Formats
hitObject.ApplyDefaults(this.beatmap.ControlPointInfo, this.beatmap.BeatmapInfo.BaseDifficulty);
}
- protected override bool ShouldSkipLine(string line) => base.ShouldSkipLine(line) || line.StartsWith(" ") || line.StartsWith("_");
+ protected override bool ShouldSkipLine(string line) => base.ShouldSkipLine(line) || line.StartsWith(" ", StringComparison.Ordinal) || line.StartsWith("_", StringComparison.Ordinal);
protected override void ParseLine(Beatmap beatmap, Section section, string line)
{
@@ -100,7 +101,7 @@ namespace osu.Game.Beatmaps.Formats
switch (pair.Key)
{
case @"AudioFilename":
- metadata.AudioFile = pair.Value;
+ metadata.AudioFile = FileSafety.PathStandardise(pair.Value);
break;
case @"AudioLeadIn":
beatmap.BeatmapInfo.AudioLeadIn = int.Parse(pair.Value);
@@ -256,7 +257,7 @@ namespace osu.Game.Beatmaps.Formats
{
case EventType.Background:
string filename = split[2].Trim('"');
- beatmap.BeatmapInfo.Metadata.BackgroundFile = filename;
+ beatmap.BeatmapInfo.Metadata.BackgroundFile = FileSafety.PathStandardise(filename);
break;
case EventType.Break:
var breakEvent = new BreakPeriod
@@ -318,12 +319,12 @@ namespace osu.Game.Beatmaps.Formats
if (timingChange)
{
- handleTimingControlPoint(new TimingControlPoint
- {
- Time = time,
- BeatLength = beatLength,
- TimeSignature = timeSignature
- });
+ var controlPoint = CreateTimingControlPoint();
+ controlPoint.Time = time;
+ controlPoint.BeatLength = beatLength;
+ controlPoint.TimeSignature = timeSignature;
+
+ handleTimingControlPoint(controlPoint);
}
handleDifficultyControlPoint(new DifficultyControlPoint
@@ -418,6 +419,8 @@ namespace osu.Game.Beatmaps.Formats
private double getOffsetTime(double time) => time + (ApplyOffsets ? offset : 0);
+ protected virtual TimingControlPoint CreateTimingControlPoint() => new TimingControlPoint();
+
[Flags]
internal enum EffectFlags
{
diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
index e9f37e583b..2ef7c5de30 100644
--- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs
@@ -69,7 +69,7 @@ namespace osu.Game.Beatmaps.Formats
protected string StripComments(string line)
{
- var index = line.IndexOf("//", StringComparison.Ordinal);
+ var index = line.AsSpan().IndexOf("//".AsSpan());
if (index > 0)
return line.Substring(0, index);
return line;
@@ -85,13 +85,19 @@ namespace osu.Game.Beatmaps.Formats
string[] split = pair.Value.Split(',');
- if (split.Length != 3)
- throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {pair.Value}");
+ if (split.Length != 3 && split.Length != 4)
+ throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B or R,G,B,A): {pair.Value}");
- if (!byte.TryParse(split[0], out var r) || !byte.TryParse(split[1], out var g) || !byte.TryParse(split[2], out var b))
+ Color4 colour;
+
+ try
+ {
+ colour = new Color4(byte.Parse(split[0]), byte.Parse(split[1]), byte.Parse(split[2]), split.Length == 4 ? byte.Parse(split[3]) : (byte)255);
+ }
+ catch (Exception e)
+ {
throw new InvalidOperationException(@"Color must be specified with 8-bit integer components");
-
- Color4 colour = new Color4(r, g, b, 255);
+ }
if (isCombo)
{
diff --git a/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs
new file mode 100644
index 0000000000..13a71aac3d
--- /dev/null
+++ b/osu.Game/Beatmaps/Formats/LegacyDifficultyCalculatorBeatmapDecoder.cs
@@ -0,0 +1,37 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System.Linq;
+using osu.Game.Beatmaps.ControlPoints;
+
+namespace osu.Game.Beatmaps.Formats
+{
+ ///
+ /// A built for difficulty calculation of legacy s
+ ///
+ /// To use this, the decoder must be registered by the application through .
+ /// Doing so will override any existing decoders.
+ ///
+ ///
+ public class LegacyDifficultyCalculatorBeatmapDecoder : LegacyBeatmapDecoder
+ {
+ public LegacyDifficultyCalculatorBeatmapDecoder(int version = LATEST_VERSION)
+ : base(version)
+ {
+ ApplyOffsets = false;
+ }
+
+ public new static void Register()
+ {
+ AddDecoder(@"osu file format v", m => new LegacyDifficultyCalculatorBeatmapDecoder(int.Parse(m.Split('v').Last())));
+ }
+
+ protected override TimingControlPoint CreateTimingControlPoint()
+ => new LegacyDifficultyCalculatorControlPoint();
+
+ private class LegacyDifficultyCalculatorControlPoint : TimingControlPoint
+ {
+ public override double BeatLength { get; set; } = 1000;
+ }
+ }
+}
diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
index a73a32325a..375c0b29c0 100644
--- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
@@ -298,6 +298,6 @@ namespace osu.Game.Beatmaps.Formats
}
}
- private string cleanFilename(string path) => FileSafety.PathStandardise(FileSafety.PathSanitise(path.Trim('\"')));
+ private string cleanFilename(string path) => FileSafety.PathStandardise(path.Trim('"'));
}
}
diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs
index fe20bce98a..3d8b94dc46 100644
--- a/osu.Game/Beatmaps/IBeatmap.cs
+++ b/osu.Game/Beatmaps/IBeatmap.cs
@@ -39,7 +39,7 @@ namespace osu.Game.Beatmaps
///
/// The hitobjects contained by this beatmap.
///
- IEnumerable HitObjects { get; }
+ IReadOnlyList HitObjects { get; }
///
/// Returns statistics for the contained in this beatmap.
diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs
index e0a22460ef..5b76122616 100644
--- a/osu.Game/Beatmaps/WorkingBeatmap.cs
+++ b/osu.Game/Beatmaps/WorkingBeatmap.cs
@@ -41,8 +41,13 @@ namespace osu.Game.Beatmaps
beatmap = new RecyclableLazy(() =>
{
var b = GetBeatmap() ?? new Beatmap();
- // use the database-backed info.
+
+ // The original beatmap version needs to be preserved as the database doesn't contain it
+ BeatmapInfo.BeatmapVersion = b.BeatmapInfo.BeatmapVersion;
+
+ // Use the database-backed info for more up-to-date values (beatmap id, ranked status, etc)
b.BeatmapInfo = BeatmapInfo;
+
return b;
});
diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs
index f7fe424aa9..9ac2cabe9f 100644
--- a/osu.Game/Configuration/OsuConfigManager.cs
+++ b/osu.Game/Configuration/OsuConfigManager.cs
@@ -83,8 +83,6 @@ namespace osu.Game.Configuration
Set(OsuSetting.ScoreDisplayMode, ScoringMode.Standardised);
- Set(OsuSetting.SpeedChangeVisualisation, SpeedChangeVisualisationMethod.Sequential);
-
Set(OsuSetting.IncreaseFirstObjectVisibility, true);
// Update
@@ -143,7 +141,6 @@ namespace osu.Game.Configuration
ChatDisplayHeight,
Version,
ShowConvertedBeatmaps,
- SpeedChangeVisualisation,
Skin,
ScreenshotFormat,
ScreenshotCaptureMenuCursor,
diff --git a/osu.Game/Configuration/SpeedChangeVisualisationMethod.cs b/osu.Game/Configuration/SpeedChangeVisualisationMethod.cs
index d38b1a89c5..39c6e5649c 100644
--- a/osu.Game/Configuration/SpeedChangeVisualisationMethod.cs
+++ b/osu.Game/Configuration/SpeedChangeVisualisationMethod.cs
@@ -10,6 +10,8 @@ namespace osu.Game.Configuration
[Description("Sequential")]
Sequential,
[Description("Overlapping")]
- Overlapping
+ Overlapping,
+ [Description("Constant")]
+ Constant
}
}
diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs
index f4f169f27c..3686b702c4 100644
--- a/osu.Game/Database/ArchiveModelManager.cs
+++ b/osu.Game/Database/ArchiveModelManager.cs
@@ -96,7 +96,8 @@ namespace osu.Game.Database
private void handleEvent(Action a)
{
if (delayingEvents)
- lock (queuedEvents) queuedEvents.Add(a);
+ lock (queuedEvents)
+ queuedEvents.Add(a);
else
a.Invoke();
}
@@ -281,17 +282,19 @@ namespace osu.Game.Database
/// Is a no-op for already deleted items.
///
/// The item to delete.
- public void Delete(TModel item)
+ /// false if no operation was performed
+ public bool Delete(TModel item)
{
using (ContextFactory.GetForWrite())
{
// re-fetch the model on the import context.
- var foundModel = queryModel().Include(s => s.Files).ThenInclude(f => f.FileInfo).First(s => s.ID == item.ID);
+ var foundModel = queryModel().Include(s => s.Files).ThenInclude(f => f.FileInfo).FirstOrDefault(s => s.ID == item.ID);
- if (foundModel.DeletePending) return;
+ if (foundModel == null || foundModel.DeletePending) return false;
if (ModelStore.Delete(foundModel))
Files.Dereference(foundModel.Files.Select(f => f.FileInfo).ToArray());
+ return true;
}
}
@@ -401,7 +404,7 @@ namespace osu.Game.Database
using (Stream s = reader.GetStream(file))
fileInfos.Add(new TFileModel
{
- Filename = FileSafety.PathSanitise(file),
+ Filename = FileSafety.PathStandardise(file),
FileInfo = files.Add(s)
});
@@ -438,6 +441,13 @@ namespace osu.Game.Database
return Task.CompletedTask;
}
+ if (!stable.ExistsDirectory(ImportFromStablePath))
+ {
+ // This handles situations like when the user does not have a Skins folder
+ Logger.Log($"No {ImportFromStablePath} folder available in osu!stable installation", LoggingTarget.Information, LogLevel.Error);
+ return Task.CompletedTask;
+ }
+
return Task.Factory.StartNew(() => Import(stable.GetDirectories(ImportFromStablePath).Select(f => stable.GetFullPath(f)).ToArray()), TaskCreationOptions.LongRunning);
}
diff --git a/osu.Game/Graphics/Backgrounds/Background.cs b/osu.Game/Graphics/Backgrounds/Background.cs
index 6fc8b0e070..d3d530a540 100644
--- a/osu.Game/Graphics/Backgrounds/Background.cs
+++ b/osu.Game/Graphics/Backgrounds/Background.cs
@@ -6,7 +6,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
-using OpenTK.Graphics;
namespace osu.Game.Graphics.Backgrounds
{
@@ -28,7 +27,6 @@ namespace osu.Game.Graphics.Backgrounds
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- Colour = Color4.DarkGray,
FillMode = FillMode.Fill,
});
}
diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs
index 0f382900ce..4a86d0e4f6 100644
--- a/osu.Game/Graphics/Backgrounds/Triangles.cs
+++ b/osu.Game/Graphics/Backgrounds/Triangles.cs
@@ -30,10 +30,6 @@ namespace osu.Game.Graphics.Backgrounds
///
private const float edge_smoothness = 1;
- public override bool HandleKeyboardInput => false;
- public override bool HandleMouseInput => false;
-
-
public Color4 ColourLight = Color4.White;
public Color4 ColourDark = Color4.Black;
diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs
index 38d82e26ec..5c8fddc342 100644
--- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs
+++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs
@@ -21,8 +21,6 @@ namespace osu.Game.Graphics.Containers
{
}
- public override bool HandleMouseInput => true;
-
private OsuGame game;
private ChannelManager channelManager;
private Action showNotImplementedError;
diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
index d2ab8441eb..2aa3fede2c 100644
--- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
+++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
@@ -8,7 +8,7 @@ using osu.Framework.Graphics.Containers;
using OpenTK;
using osu.Framework.Configuration;
using osu.Framework.Input.Bindings;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
using osu.Game.Audio;
using osu.Game.Input.Bindings;
using osu.Game.Overlays;
@@ -22,7 +22,7 @@ namespace osu.Game.Graphics.Containers
protected virtual bool PlaySamplesOnStateChange => true;
- protected override bool BlockPassThroughKeyboard => true;
+ protected override bool BlockNonPositionalInput => true;
private PreviewTrackManager previewTrackManager;
@@ -54,20 +54,20 @@ namespace osu.Game.Graphics.Containers
/// Whether mouse input should be blocked screen-wide while this overlay is visible.
/// Performing mouse actions outside of the valid extents will hide the overlay.
///
- public virtual bool BlockScreenWideMouse => BlockPassThroughMouse;
+ public virtual bool BlockScreenWideMouse => BlockPositionalInput;
// receive input outside our bounds so we can trigger a close event on ourselves.
- public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => BlockScreenWideMouse || base.ReceiveMouseInputAt(screenSpacePos);
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => BlockScreenWideMouse || base.ReceivePositionalInputAt(screenSpacePos);
- protected override bool OnClick(InputState state)
+ protected override bool OnClick(ClickEvent e)
{
- if (!base.ReceiveMouseInputAt(state.Mouse.NativeState.Position))
+ if (!base.ReceivePositionalInputAt(e.ScreenSpaceMousePosition))
{
State = Visibility.Hidden;
return true;
}
- return base.OnClick(state);
+ return base.OnClick(e);
}
public virtual bool OnPressed(GlobalAction action)
diff --git a/osu.Game/Graphics/Containers/OsuHoverContainer.cs b/osu.Game/Graphics/Containers/OsuHoverContainer.cs
index 12df19e7c0..577d889be3 100644
--- a/osu.Game/Graphics/Containers/OsuHoverContainer.cs
+++ b/osu.Game/Graphics/Containers/OsuHoverContainer.cs
@@ -6,7 +6,7 @@ using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
namespace osu.Game.Graphics.Containers
{
@@ -18,16 +18,16 @@ namespace osu.Game.Graphics.Containers
protected virtual IEnumerable EffectTargets => new[] { Content };
- protected override bool OnHover(InputState state)
+ protected override bool OnHover(HoverEvent e)
{
EffectTargets.ForEach(d => d.FadeColour(HoverColour, 500, Easing.OutQuint));
- return base.OnHover(state);
+ return base.OnHover(e);
}
- protected override void OnHoverLost(InputState state)
+ protected override void OnHoverLost(HoverLostEvent e)
{
EffectTargets.ForEach(d => d.FadeColour(IdleColour, 500, Easing.OutQuint));
- base.OnHoverLost(state);
+ base.OnHoverLost(e);
}
[BackgroundDependencyLoader]
diff --git a/osu.Game/Graphics/Containers/OsuScrollContainer.cs b/osu.Game/Graphics/Containers/OsuScrollContainer.cs
index 6d42be6fca..4f18eb9e7b 100644
--- a/osu.Game/Graphics/Containers/OsuScrollContainer.cs
+++ b/osu.Game/Graphics/Containers/OsuScrollContainer.cs
@@ -2,8 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics.Containers;
-using osu.Framework.Input.EventArgs;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
using OpenTK.Input;
namespace osu.Game.Graphics.Containers
@@ -21,7 +20,7 @@ namespace osu.Game.Graphics.Containers
///
public double DistanceDecayOnRightMouseScrollbar = 0.02;
- private bool shouldPerformRightMouseScroll(InputState state) => RightMouseScrollbar && state.Mouse.IsPressed(MouseButton.Right);
+ private bool shouldPerformRightMouseScroll(MouseButtonEvent e) => RightMouseScrollbar && e.Button == MouseButton.Right;
private void scrollToRelative(float value) => ScrollTo(Clamp((value - Scrollbar.DrawSize[ScrollDim] / 2) / Scrollbar.Size[ScrollDim]), true, DistanceDecayOnRightMouseScrollbar);
@@ -29,40 +28,40 @@ namespace osu.Game.Graphics.Containers
protected override bool IsDragging => base.IsDragging || mouseScrollBarDragging;
- protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
+ protected override bool OnMouseDown(MouseDownEvent e)
{
- if (shouldPerformRightMouseScroll(state))
+ if (shouldPerformRightMouseScroll(e))
{
- scrollToRelative(state.Mouse.Position[ScrollDim]);
+ scrollToRelative(e.MousePosition[ScrollDim]);
return true;
}
- return base.OnMouseDown(state, args);
+ return base.OnMouseDown(e);
}
- protected override bool OnDrag(InputState state)
+ protected override bool OnDrag(DragEvent e)
{
if (mouseScrollBarDragging)
{
- scrollToRelative(state.Mouse.Position[ScrollDim]);
+ scrollToRelative(e.MousePosition[ScrollDim]);
return true;
}
- return base.OnDrag(state);
+ return base.OnDrag(e);
}
- protected override bool OnDragStart(InputState state)
+ protected override bool OnDragStart(DragStartEvent e)
{
- if (shouldPerformRightMouseScroll(state))
+ if (shouldPerformRightMouseScroll(e))
{
mouseScrollBarDragging = true;
return true;
}
- return base.OnDragStart(state);
+ return base.OnDragStart(e);
}
- protected override bool OnDragEnd(InputState state)
+ protected override bool OnDragEnd(DragEndEvent e)
{
if (mouseScrollBarDragging)
{
@@ -70,7 +69,7 @@ namespace osu.Game.Graphics.Containers
return true;
}
- return base.OnDragEnd(state);
+ return base.OnDragEnd(e);
}
}
}
diff --git a/osu.Game/Graphics/Containers/ParallaxContainer.cs b/osu.Game/Graphics/Containers/ParallaxContainer.cs
index 8e1e5d54fa..a6b79a20dc 100644
--- a/osu.Game/Graphics/Containers/ParallaxContainer.cs
+++ b/osu.Game/Graphics/Containers/ParallaxContainer.cs
@@ -67,7 +67,7 @@ namespace osu.Game.Graphics.Containers
if (parallaxEnabled)
{
- Vector2 offset = (input.CurrentState.Mouse == null ? Vector2.Zero : ToLocalSpace(input.CurrentState.Mouse.NativeState.Position) - DrawSize / 2) * ParallaxAmount;
+ Vector2 offset = (input.CurrentState.Mouse == null ? Vector2.Zero : ToLocalSpace(input.CurrentState.Mouse.Position) - DrawSize / 2) * ParallaxAmount;
double elapsed = MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 1000);
diff --git a/osu.Game/Graphics/Cursor/MenuCursor.cs b/osu.Game/Graphics/Cursor/MenuCursor.cs
index b55e1aa5dd..ba858bf52d 100644
--- a/osu.Game/Graphics/Cursor/MenuCursor.cs
+++ b/osu.Game/Graphics/Cursor/MenuCursor.cs
@@ -12,8 +12,7 @@ using osu.Game.Configuration;
using System;
using JetBrains.Annotations;
using osu.Framework.Graphics.Textures;
-using osu.Framework.Input.EventArgs;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
using OpenTK.Input;
namespace osu.Game.Graphics.Cursor
@@ -40,11 +39,11 @@ namespace osu.Game.Graphics.Cursor
screenshotCursorVisibility.BindTo(screenshotManager.CursorVisibility);
}
- protected override bool OnMouseMove(InputState state)
+ protected override bool OnMouseMove(MouseMoveEvent e)
{
if (dragRotationState != DragRotationState.NotDragging)
{
- var position = state.Mouse.Position;
+ var position = e.MousePosition;
var distance = Vector2Extensions.Distance(position, positionMouseDown);
// don't start rotating until we're moved a minimum distance away from the mouse down location,
// else it can have an annoying effect.
@@ -53,7 +52,7 @@ namespace osu.Game.Graphics.Cursor
// don't rotate when distance is zero to avoid NaN
if (dragRotationState == DragRotationState.Rotating && distance > 0)
{
- Vector2 offset = state.Mouse.Position - positionMouseDown;
+ Vector2 offset = e.MousePosition - positionMouseDown;
float degrees = (float)MathHelper.RadiansToDegrees(Math.Atan2(-offset.X, offset.Y)) + 24.3f;
// Always rotate in the direction of least distance
@@ -66,13 +65,13 @@ namespace osu.Game.Graphics.Cursor
}
}
- return base.OnMouseMove(state);
+ return base.OnMouseMove(e);
}
- protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
+ protected override bool OnMouseDown(MouseDownEvent e)
{
// only trigger animation for main mouse buttons
- if (args.Button <= MouseButton.Right)
+ if (e.Button <= MouseButton.Right)
{
activeCursor.Scale = new Vector2(1);
activeCursor.ScaleTo(0.90f, 800, Easing.OutQuint);
@@ -81,29 +80,29 @@ namespace osu.Game.Graphics.Cursor
activeCursor.AdditiveLayer.FadeInFromZero(800, Easing.OutQuint);
}
- if (args.Button == MouseButton.Left && cursorRotate)
+ if (e.Button == MouseButton.Left && cursorRotate)
{
dragRotationState = DragRotationState.DragStarted;
- positionMouseDown = state.Mouse.Position;
+ positionMouseDown = e.MousePosition;
}
- return base.OnMouseDown(state, args);
+ return base.OnMouseDown(e);
}
- protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
+ protected override bool OnMouseUp(MouseUpEvent e)
{
- if (!state.Mouse.HasMainButtonPressed)
+ if (!e.IsPressed(MouseButton.Left) && !e.IsPressed(MouseButton.Right))
{
activeCursor.AdditiveLayer.FadeOutFromOne(500, Easing.OutQuint);
activeCursor.ScaleTo(1, 500, Easing.OutElastic);
}
- if (args.Button == MouseButton.Left)
+ if (e.Button == MouseButton.Left)
{
if (dragRotationState == DragRotationState.Rotating)
activeCursor.RotateTo(0, 600 * (1 + Math.Abs(activeCursor.Rotation / 720)), Easing.OutElasticHalf);
dragRotationState = DragRotationState.NotDragging;
}
- return base.OnMouseUp(state, args);
+ return base.OnMouseUp(e);
}
protected override void PopIn()
diff --git a/osu.Game/Graphics/DrawableDate.cs b/osu.Game/Graphics/DrawableDate.cs
index be794d93a6..28f8bdf82f 100644
--- a/osu.Game/Graphics/DrawableDate.cs
+++ b/osu.Game/Graphics/DrawableDate.cs
@@ -54,8 +54,6 @@ namespace osu.Game.Graphics
Scheduler.AddDelayed(updateTimeWithReschedule, timeUntilNextUpdate);
}
- public override bool HandleMouseInput => true;
-
protected virtual string Format() => Date.Humanize();
private void updateTime() => Text = Format();
diff --git a/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs b/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs
index f5017de639..ebb7b686e4 100644
--- a/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs
+++ b/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs
@@ -47,10 +47,10 @@ namespace osu.Game.Graphics.UserInterface
public readonly SpriteIcon Chevron;
//don't allow clicking between transitions and don't make the chevron clickable
- public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => Alpha == 1f && Text.ReceiveMouseInputAt(screenSpacePos);
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Alpha == 1f && Text.ReceivePositionalInputAt(screenSpacePos);
- public override bool HandleKeyboardInput => State == Visibility.Visible;
- public override bool HandleMouseInput => State == Visibility.Visible;
+ public override bool HandleNonPositionalInput => State == Visibility.Visible;
+ public override bool HandlePositionalInput => State == Visibility.Visible;
public override bool IsRemovable => true;
private Visibility state;
diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs
index ee2448ff02..b2220267ff 100644
--- a/osu.Game/Graphics/UserInterface/DialogButton.cs
+++ b/osu.Game/Graphics/UserInterface/DialogButton.cs
@@ -13,7 +13,7 @@ using osu.Game.Graphics.Sprites;
using osu.Framework.Extensions.Color4Extensions;
using osu.Game.Graphics.Containers;
using osu.Framework.Configuration;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
namespace osu.Game.Graphics.UserInterface
{
@@ -211,9 +211,9 @@ namespace osu.Game.Graphics.UserInterface
}
}
- public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => backgroundContainer.ReceiveMouseInputAt(screenSpacePos);
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => backgroundContainer.ReceivePositionalInputAt(screenSpacePos);
- protected override bool OnClick(InputState state)
+ protected override bool OnClick(ClickEvent e)
{
colourContainer.ResizeTo(new Vector2(1.5f, 1f), click_duration, Easing.In);
flash();
@@ -225,20 +225,20 @@ namespace osu.Game.Graphics.UserInterface
glowContainer.FadeOut();
});
- return base.OnClick(state);
+ return base.OnClick(e);
}
- protected override bool OnHover(InputState state)
+ protected override bool OnHover(HoverEvent e)
{
- base.OnHover(state);
+ base.OnHover(e);
Selected.Value = true;
return true;
}
- protected override void OnHoverLost(InputState state)
+ protected override void OnHoverLost(HoverLostEvent e)
{
- base.OnHoverLost(state);
+ base.OnHoverLost(e);
Selected.Value = false;
}
diff --git a/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs b/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs
index fe8995f310..d82448e8a2 100644
--- a/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs
+++ b/osu.Game/Graphics/UserInterface/ExternalLinkButton.cs
@@ -5,7 +5,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
using osu.Framework.Platform;
using OpenTK;
using OpenTK.Graphics;
@@ -37,19 +37,19 @@ namespace osu.Game.Graphics.UserInterface
this.host = host;
}
- protected override bool OnHover(InputState state)
+ protected override bool OnHover(HoverEvent e)
{
InternalChild.FadeColour(hoverColour, 500, Easing.OutQuint);
- return base.OnHover(state);
+ return base.OnHover(e);
}
- protected override void OnHoverLost(InputState state)
+ protected override void OnHoverLost(HoverLostEvent e)
{
InternalChild.FadeColour(Color4.White, 500, Easing.OutQuint);
- base.OnHoverLost(state);
+ base.OnHoverLost(e);
}
- protected override bool OnClick(InputState state)
+ protected override bool OnClick(ClickEvent e)
{
if(Link != null)
host.OpenUrlExternally(Link);
diff --git a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs
index e4fd71e17e..122ac7b627 100644
--- a/osu.Game/Graphics/UserInterface/FocusedTextBox.cs
+++ b/osu.Game/Graphics/UserInterface/FocusedTextBox.cs
@@ -3,8 +3,7 @@
using OpenTK.Graphics;
using System;
-using osu.Framework.Input.EventArgs;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
using osu.Game.Input.Bindings;
using OpenTK.Input;
@@ -34,22 +33,22 @@ namespace osu.Game.Graphics.UserInterface
}
// We may not be focused yet, but we need to handle keyboard input to be able to request focus
- public override bool HandleKeyboardInput => HoldFocus || base.HandleKeyboardInput;
+ public override bool HandleNonPositionalInput => HoldFocus || base.HandleNonPositionalInput;
- protected override void OnFocus(InputState state)
+ protected override void OnFocus(FocusEvent e)
{
- base.OnFocus(state);
+ base.OnFocus(e);
BorderThickness = 0;
}
- protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
+ protected override bool OnKeyDown(KeyDownEvent e)
{
if (!HasFocus) return false;
- if (args.Key == Key.Escape)
+ if (e.Key == Key.Escape)
return false; // disable the framework-level handling of escape key for confority (we use GlobalAction.Back).
- return base.OnKeyDown(state, args);
+ return base.OnKeyDown(e);
}
public override bool OnPressed(GlobalAction action)
diff --git a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs
index 27a06ba0b7..3641e251bc 100644
--- a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs
+++ b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs
@@ -5,7 +5,7 @@ using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Extensions;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
namespace osu.Game.Graphics.UserInterface
{
@@ -21,10 +21,10 @@ namespace osu.Game.Graphics.UserInterface
{
}
- protected override bool OnClick(InputState state)
+ protected override bool OnClick(ClickEvent e)
{
sampleClick?.Play();
- return base.OnClick(state);
+ return base.OnClick(e);
}
[BackgroundDependencyLoader]
diff --git a/osu.Game/Graphics/UserInterface/HoverSounds.cs b/osu.Game/Graphics/UserInterface/HoverSounds.cs
index 821305bc92..710948121e 100644
--- a/osu.Game/Graphics/UserInterface/HoverSounds.cs
+++ b/osu.Game/Graphics/UserInterface/HoverSounds.cs
@@ -8,7 +8,7 @@ using osu.Framework.Audio.Sample;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
namespace osu.Game.Graphics.UserInterface
{
@@ -28,10 +28,10 @@ namespace osu.Game.Graphics.UserInterface
RelativeSizeAxes = Axes.Both;
}
- protected override bool OnHover(InputState state)
+ protected override bool OnHover(HoverEvent e)
{
sampleHover?.Play();
- return base.OnHover(state);
+ return base.OnHover(e);
}
[BackgroundDependencyLoader]
diff --git a/osu.Game/Graphics/UserInterface/IconButton.cs b/osu.Game/Graphics/UserInterface/IconButton.cs
index be60812ba6..f10f03873d 100644
--- a/osu.Game/Graphics/UserInterface/IconButton.cs
+++ b/osu.Game/Graphics/UserInterface/IconButton.cs
@@ -4,7 +4,7 @@
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
namespace osu.Game.Graphics.UserInterface
{
@@ -84,16 +84,16 @@ namespace osu.Game.Graphics.UserInterface
});
}
- protected override bool OnHover(InputState state)
+ protected override bool OnHover(HoverEvent e)
{
icon.FadeColour(IconHoverColour, 500, Easing.OutQuint);
- return base.OnHover(state);
+ return base.OnHover(e);
}
- protected override void OnHoverLost(InputState state)
+ protected override void OnHoverLost(HoverLostEvent e)
{
icon.FadeColour(IconColour, 500, Easing.OutQuint);
- base.OnHoverLost(state);
+ base.OnHoverLost(e);
}
}
}
diff --git a/osu.Game/Graphics/UserInterface/LineGraph.cs b/osu.Game/Graphics/UserInterface/LineGraph.cs
index 3cb2446acc..ff2c4cf7cd 100644
--- a/osu.Game/Graphics/UserInterface/LineGraph.cs
+++ b/osu.Game/Graphics/UserInterface/LineGraph.cs
@@ -69,7 +69,7 @@ namespace osu.Game.Graphics.UserInterface
{
Masking = true,
RelativeSizeAxes = Axes.Both,
- Child = path = new Path { RelativeSizeAxes = Axes.Both, PathWidth = 1 }
+ Child = path = new SmoothPath { RelativeSizeAxes = Axes.Both, PathWidth = 1 }
});
}
diff --git a/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs b/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs
index 4428a058db..4516d7ce76 100644
--- a/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs
+++ b/osu.Game/Graphics/UserInterface/OsuAnimatedButton.cs
@@ -6,8 +6,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input.EventArgs;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
using osu.Game.Graphics.Containers;
using OpenTK.Graphics;
@@ -77,34 +76,34 @@ namespace osu.Game.Graphics.UserInterface
Enabled.BindValueChanged(enabled => this.FadeColour(enabled ? Color4.White : colours.Gray9, 200, Easing.OutQuint), true);
}
- protected override bool OnHover(InputState state)
+ protected override bool OnHover(HoverEvent e)
{
hover.FadeIn(500, Easing.OutQuint);
- return base.OnHover(state);
+ return base.OnHover(e);
}
- protected override void OnHoverLost(InputState state)
+ protected override void OnHoverLost(HoverLostEvent e)
{
hover.FadeOut(500, Easing.OutQuint);
- base.OnHoverLost(state);
+ base.OnHoverLost(e);
}
- protected override bool OnClick(InputState state)
+ protected override bool OnClick(ClickEvent e)
{
hover.FlashColour(FlashColour, 800, Easing.OutQuint);
- return base.OnClick(state);
+ return base.OnClick(e);
}
- protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
+ protected override bool OnMouseDown(MouseDownEvent e)
{
Content.ScaleTo(0.75f, 2000, Easing.OutQuint);
- return base.OnMouseDown(state, args);
+ return base.OnMouseDown(e);
}
- protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
+ protected override bool OnMouseUp(MouseUpEvent e)
{
Content.ScaleTo(1, 1000, Easing.OutElastic);
- return base.OnMouseUp(state, args);
+ return base.OnMouseUp(e);
}
}
}
diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs
index bb6a032a12..ab880cd473 100644
--- a/osu.Game/Graphics/UserInterface/OsuButton.cs
+++ b/osu.Game/Graphics/UserInterface/OsuButton.cs
@@ -7,8 +7,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Input.EventArgs;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
using osu.Game.Graphics.Sprites;
using OpenTK.Graphics;
@@ -56,28 +55,28 @@ namespace osu.Game.Graphics.UserInterface
this.FadeColour(enabled ? Color4.White : Color4.Gray, 200, Easing.OutQuint);
}
- protected override bool OnHover(InputState state)
+ protected override bool OnHover(HoverEvent e)
{
hover.FadeIn(200);
- return base.OnHover(state);
+ return base.OnHover(e);
}
- protected override void OnHoverLost(InputState state)
+ protected override void OnHoverLost(HoverLostEvent e)
{
hover.FadeOut(200);
- base.OnHoverLost(state);
+ base.OnHoverLost(e);
}
- protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
+ protected override bool OnMouseDown(MouseDownEvent e)
{
Content.ScaleTo(0.9f, 4000, Easing.OutQuint);
- return base.OnMouseDown(state, args);
+ return base.OnMouseDown(e);
}
- protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
+ protected override bool OnMouseUp(MouseUpEvent e)
{
Content.ScaleTo(1, 1000, Easing.OutElastic);
- return base.OnMouseUp(state, args);
+ return base.OnMouseUp(e);
}
protected override SpriteText CreateText() => new OsuSpriteText
diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs
index 68f59bd8cd..e267a7f848 100644
--- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs
+++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs
@@ -8,7 +8,7 @@ using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
using osu.Game.Graphics.Sprites;
using OpenTK.Graphics;
@@ -95,18 +95,18 @@ namespace osu.Game.Graphics.UserInterface
};
}
- protected override bool OnHover(InputState state)
+ protected override bool OnHover(HoverEvent e)
{
Nub.Glowing = true;
Nub.Expanded = true;
- return base.OnHover(state);
+ return base.OnHover(e);
}
- protected override void OnHoverLost(InputState state)
+ protected override void OnHoverLost(HoverLostEvent e)
{
Nub.Glowing = false;
Nub.Expanded = false;
- base.OnHoverLost(state);
+ base.OnHoverLost(e);
}
[BackgroundDependencyLoader]
diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs
index 830bde9dac..30803d1545 100644
--- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs
+++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs
@@ -30,7 +30,7 @@ namespace osu.Game.Graphics.UserInterface
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
- if (accentColour == default(Color4))
+ if (accentColour == default)
accentColour = colours.PinkDarker;
updateAccentColour();
}
@@ -51,6 +51,8 @@ namespace osu.Game.Graphics.UserInterface
#region OsuDropdownMenu
protected class OsuDropdownMenu : DropdownMenu, IHasAccentColour
{
+ public override bool HandleNonPositionalInput => State == MenuState.Open;
+
// todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring
public OsuDropdownMenu()
{
@@ -97,6 +99,9 @@ namespace osu.Game.Graphics.UserInterface
#region DrawableOsuDropdownMenuItem
public class DrawableOsuDropdownMenuItem : DrawableDropdownMenuItem, IHasAccentColour
{
+ // IsHovered is used
+ public override bool HandlePositionalInput => true;
+
private Color4? accentColour;
public Color4 AccentColour
{
diff --git a/osu.Game/Graphics/UserInterface/OsuMenu.cs b/osu.Game/Graphics/UserInterface/OsuMenu.cs
index abb077e94f..fe3e866a70 100644
--- a/osu.Game/Graphics/UserInterface/OsuMenu.cs
+++ b/osu.Game/Graphics/UserInterface/OsuMenu.cs
@@ -10,7 +10,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
using osu.Game.Graphics.Sprites;
using OpenTK;
@@ -97,25 +97,25 @@ namespace osu.Game.Graphics.UserInterface
}
}
- protected override bool OnHover(InputState state)
+ protected override bool OnHover(HoverEvent e)
{
sampleHover.Play();
text.BoldText.FadeIn(transition_length, Easing.OutQuint);
text.NormalText.FadeOut(transition_length, Easing.OutQuint);
- return base.OnHover(state);
+ return base.OnHover(e);
}
- protected override void OnHoverLost(InputState state)
+ protected override void OnHoverLost(HoverLostEvent e)
{
text.BoldText.FadeOut(transition_length, Easing.OutQuint);
text.NormalText.FadeIn(transition_length, Easing.OutQuint);
- base.OnHoverLost(state);
+ base.OnHoverLost(e);
}
- protected override bool OnClick(InputState state)
+ protected override bool OnClick(ClickEvent e)
{
sampleClick.Play();
- return base.OnClick(state);
+ return base.OnClick(e);
}
protected sealed override Drawable CreateContent() => text = CreateTextContainer();
diff --git a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs
index 75655ddb36..f4ec67db23 100644
--- a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs
+++ b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs
@@ -9,8 +9,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input.EventArgs;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
using osu.Framework.Platform;
namespace osu.Game.Graphics.UserInterface
@@ -43,23 +42,23 @@ namespace osu.Game.Graphics.UserInterface
this.host = host;
}
- protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
+ protected override bool OnKeyDown(KeyDownEvent e)
{
- if (args.Key == Key.CapsLock)
+ if (e.Key == Key.CapsLock)
updateCapsWarning(host.CapsLockEnabled);
- return base.OnKeyDown(state, args);
+ return base.OnKeyDown(e);
}
- protected override void OnFocus(InputState state)
+ protected override void OnFocus(FocusEvent e)
{
updateCapsWarning(host.CapsLockEnabled);
- base.OnFocus(state);
+ base.OnFocus(e);
}
- protected override void OnFocusLost(InputState state)
+ protected override void OnFocusLost(FocusLostEvent e)
{
updateCapsWarning(false);
- base.OnFocusLost(state);
+ base.OnFocusLost(e);
}
private void updateCapsWarning(bool visible) => warning.FadeTo(visible ? 1 : 0, 250, Easing.OutQuint);
diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs
index b7b5319e06..eecc10469e 100644
--- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs
+++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs
@@ -13,8 +13,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input.EventArgs;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
namespace osu.Game.Graphics.UserInterface
{
@@ -125,16 +124,16 @@ namespace osu.Game.Graphics.UserInterface
AccentColour = colours.Pink;
}
- protected override bool OnHover(InputState state)
+ protected override bool OnHover(HoverEvent e)
{
Nub.Glowing = true;
- return base.OnHover(state);
+ return base.OnHover(e);
}
- protected override void OnHoverLost(InputState state)
+ protected override void OnHoverLost(HoverLostEvent e)
{
Nub.Glowing = false;
- base.OnHoverLost(state);
+ base.OnHoverLost(e);
}
protected override void OnUserChange()
@@ -164,16 +163,16 @@ namespace osu.Game.Graphics.UserInterface
sample.Play();
}
- protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
+ protected override bool OnMouseDown(MouseDownEvent e)
{
Nub.Current.Value = true;
- return base.OnMouseDown(state, args);
+ return base.OnMouseDown(e);
}
- protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
+ protected override bool OnMouseUp(MouseUpEvent e)
{
Nub.Current.Value = false;
- return base.OnMouseUp(state, args);
+ return base.OnMouseUp(e);
}
protected override void UpdateAfterChildren()
diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs
index e2a0b88b2a..e7d6a87349 100644
--- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs
+++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs
@@ -14,7 +14,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
using osu.Framework.MathUtils;
using osu.Game.Graphics.Sprites;
@@ -53,7 +53,7 @@ namespace osu.Game.Graphics.UserInterface
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
- if (accentColour == default(Color4))
+ if (accentColour == default)
AccentColour = colours.Blue;
}
@@ -126,14 +126,14 @@ namespace osu.Game.Graphics.UserInterface
Text.FadeColour(AccentColour, transition_length, Easing.OutQuint);
}
- protected override bool OnHover(InputState state)
+ protected override bool OnHover(HoverEvent e)
{
if (!Active)
fadeActive();
return true;
}
- protected override void OnHoverLost(InputState state)
+ protected override void OnHoverLost(HoverLostEvent e)
{
if (!Active)
fadeInactive();
@@ -142,7 +142,7 @@ namespace osu.Game.Graphics.UserInterface
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
- if (accentColour == default(Color4))
+ if (accentColour == default)
AccentColour = colours.Blue;
}
@@ -265,16 +265,16 @@ namespace osu.Game.Graphics.UserInterface
Padding = new MarginPadding { Left = 5, Right = 5 };
}
- protected override bool OnHover(InputState state)
+ protected override bool OnHover(HoverEvent e)
{
Foreground.Colour = BackgroundColour;
- return base.OnHover(state);
+ return base.OnHover(e);
}
- protected override void OnHoverLost(InputState state)
+ protected override void OnHoverLost(HoverLostEvent e)
{
Foreground.Colour = BackgroundColourHover;
- base.OnHoverLost(state);
+ base.OnHoverLost(e);
}
}
}
diff --git a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs
index 04ba111153..1355ffdb8e 100644
--- a/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs
+++ b/osu.Game/Graphics/UserInterface/OsuTabControlCheckbox.cs
@@ -10,7 +10,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Sprites;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
namespace osu.Game.Graphics.UserInterface
{
@@ -59,18 +59,18 @@ namespace osu.Game.Graphics.UserInterface
text.FadeColour(AccentColour, transition_length, Easing.OutQuint);
}
- protected override bool OnHover(InputState state)
+ protected override bool OnHover(HoverEvent e)
{
fadeIn();
- return base.OnHover(state);
+ return base.OnHover(e);
}
- protected override void OnHoverLost(InputState state)
+ protected override void OnHoverLost(HoverLostEvent e)
{
if (!Current)
fadeOut();
- base.OnHoverLost(state);
+ base.OnHoverLost(e);
}
[BackgroundDependencyLoader]
diff --git a/osu.Game/Graphics/UserInterface/OsuTextBox.cs b/osu.Game/Graphics/UserInterface/OsuTextBox.cs
index 7a45ffdd4b..37464faa73 100644
--- a/osu.Game/Graphics/UserInterface/OsuTextBox.cs
+++ b/osu.Game/Graphics/UserInterface/OsuTextBox.cs
@@ -9,7 +9,7 @@ using osu.Game.Graphics.Sprites;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Input.Bindings;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
using osu.Game.Input.Bindings;
namespace osu.Game.Graphics.UserInterface
@@ -44,17 +44,17 @@ namespace osu.Game.Graphics.UserInterface
BorderColour = colour.Yellow;
}
- protected override void OnFocus(InputState state)
+ protected override void OnFocus(FocusEvent e)
{
BorderThickness = 3;
- base.OnFocus(state);
+ base.OnFocus(e);
}
- protected override void OnFocusLost(InputState state)
+ protected override void OnFocusLost(FocusLostEvent e)
{
BorderThickness = 0;
- base.OnFocusLost(state);
+ base.OnFocusLost(e);
}
protected override Drawable GetDrawableCharacter(char c) => new OsuSpriteText { Text = c.ToString(), TextSize = CalculatedTextSize };
diff --git a/osu.Game/Graphics/UserInterface/PageTabControl.cs b/osu.Game/Graphics/UserInterface/PageTabControl.cs
index d203532f9c..fb7b4c5676 100644
--- a/osu.Game/Graphics/UserInterface/PageTabControl.cs
+++ b/osu.Game/Graphics/UserInterface/PageTabControl.cs
@@ -10,7 +10,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Graphics.UserInterface
@@ -67,14 +67,14 @@ namespace osu.Game.Graphics.UserInterface
box.Colour = colours.Yellow;
}
- protected override bool OnHover(InputState state)
+ protected override bool OnHover(HoverEvent e)
{
if (!Active)
slideActive();
return true;
}
- protected override void OnHoverLost(InputState state)
+ protected override void OnHoverLost(HoverLostEvent e)
{
if (!Active)
slideInactive();
diff --git a/osu.Game/Graphics/UserInterface/RollingCounter.cs b/osu.Game/Graphics/UserInterface/RollingCounter.cs
index f84404a911..c2162b8a42 100644
--- a/osu.Game/Graphics/UserInterface/RollingCounter.cs
+++ b/osu.Game/Graphics/UserInterface/RollingCounter.cs
@@ -134,7 +134,7 @@ namespace osu.Game.Graphics.UserInterface
///
public virtual void ResetCount()
{
- SetCountWithoutRolling(default(T));
+ SetCountWithoutRolling(default);
}
///
diff --git a/osu.Game/Graphics/UserInterface/SearchTextBox.cs b/osu.Game/Graphics/UserInterface/SearchTextBox.cs
index 6067481979..08e93fad18 100644
--- a/osu.Game/Graphics/UserInterface/SearchTextBox.cs
+++ b/osu.Game/Graphics/UserInterface/SearchTextBox.cs
@@ -2,8 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
-using osu.Framework.Input.EventArgs;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
using OpenTK;
using OpenTK.Input;
@@ -33,11 +32,11 @@ namespace osu.Game.Graphics.UserInterface
PlaceholderText = "type to search";
}
- protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
+ protected override bool OnKeyDown(KeyDownEvent e)
{
- if (!state.Keyboard.ControlPressed && !state.Keyboard.ShiftPressed)
+ if (!e.ControlPressed && !e.ShiftPressed)
{
- switch (args.Key)
+ switch (e.Key)
{
case Key.Left:
case Key.Right:
@@ -49,7 +48,7 @@ namespace osu.Game.Graphics.UserInterface
if (!AllowCommit)
{
- switch (args.Key)
+ switch (e.Key)
{
case Key.KeypadEnter:
case Key.Enter:
@@ -57,16 +56,16 @@ namespace osu.Game.Graphics.UserInterface
}
}
- if (state.Keyboard.ShiftPressed)
+ if (e.ShiftPressed)
{
- switch (args.Key)
+ switch (e.Key)
{
case Key.Delete:
return false;
}
}
- return base.OnKeyDown(state, args);
+ return base.OnKeyDown(e);
}
}
}
diff --git a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs
index 4e6361d1ae..2000eb47e4 100644
--- a/osu.Game/Graphics/UserInterface/TwoLayerButton.cs
+++ b/osu.Game/Graphics/UserInterface/TwoLayerButton.cs
@@ -13,8 +13,7 @@ using osu.Game.Beatmaps.ControlPoints;
using osu.Framework.Audio.Track;
using System;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input.EventArgs;
-using osu.Framework.Input.States;
+using osu.Framework.Input.Events;
namespace osu.Game.Graphics.UserInterface
{
@@ -170,9 +169,9 @@ namespace osu.Game.Graphics.UserInterface
}
}
- public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => IconLayer.ReceiveMouseInputAt(screenSpacePos) || TextLayer.ReceiveMouseInputAt(screenSpacePos);
+ public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => IconLayer.ReceivePositionalInputAt(screenSpacePos) || TextLayer.ReceivePositionalInputAt(screenSpacePos);
- protected override bool OnHover(InputState state)
+ protected override bool OnHover(HoverEvent e)
{
this.ResizeTo(SIZE_EXTENDED, transform_time, Easing.OutElastic);
IconLayer.FadeColour(HoverColour, transform_time, Easing.OutElastic);
@@ -182,7 +181,7 @@ namespace osu.Game.Graphics.UserInterface
return true;
}
- protected override void OnHoverLost(InputState state)
+ protected override void OnHoverLost(HoverLostEvent e)
{
this.ResizeTo(SIZE_RETRACTED, transform_time, Easing.OutElastic);
IconLayer.FadeColour(TextLayer.Colour, transform_time, Easing.OutElastic);
@@ -190,12 +189,12 @@ namespace osu.Game.Graphics.UserInterface
bouncingIcon.ScaleTo(1, transform_time, Easing.OutElastic);
}
- protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
+ protected override bool OnMouseDown(MouseDownEvent e)
{
return true;
}
- protected override bool OnClick(InputState state)
+ protected override bool OnClick(ClickEvent e)
{
var flash = new Box
{
@@ -209,7 +208,7 @@ namespace osu.Game.Graphics.UserInterface
flash.FadeOut(500, Easing.OutQuint);
flash.Expire();
- return base.OnClick(state);
+ return base.OnClick(e);
}
private class BouncingIcon : BeatSyncedContainer
diff --git a/osu.Game/IPC/ArchiveImportIPCChannel.cs b/osu.Game/IPC/ArchiveImportIPCChannel.cs
index 6783b9712c..fa8168c1de 100644
--- a/osu.Game/IPC/ArchiveImportIPCChannel.cs
+++ b/osu.Game/IPC/ArchiveImportIPCChannel.cs
@@ -37,7 +37,7 @@ namespace osu.Game.IPC
return;
}
- if (importer.HandledExtensions.Contains(Path.GetExtension(path)))
+ if (importer.HandledExtensions.Contains(Path.GetExtension(path)?.ToLowerInvariant()))
importer.Import(path);
}
}
diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs
index b21deff509..2f5f1aea3f 100644
--- a/osu.Game/Input/Bindings/GlobalActionContainer.cs
+++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs
@@ -71,17 +71,17 @@ namespace osu.Game.Input.Bindings
ToggleSettings,
[Description("Toggle osu!direct")]
ToggleDirect,
- [Description("Increase Volume")]
+ [Description("Increase volume")]
IncreaseVolume,
- [Description("Decrease Volume")]
+ [Description("Decrease volume")]
DecreaseVolume,
[Description("Toggle mute")]
ToggleMute,
// In-Game Keybindings
- [Description("Skip Cutscene")]
+ [Description("Skip cutscene")]
SkipCutscene,
- [Description("Quick Retry (Hold)")]
+ [Description("Quick retry (hold)")]
QuickRetry,
[Description("Take screenshot")]
diff --git a/osu.Game/Input/Handlers/ReplayInputHandler.cs b/osu.Game/Input/Handlers/ReplayInputHandler.cs
index 5f53868b28..0a62c6ca72 100644
--- a/osu.Game/Input/Handlers/ReplayInputHandler.cs
+++ b/osu.Game/Input/Handlers/ReplayInputHandler.cs
@@ -3,10 +3,13 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using osu.Framework.Input.Handlers;
using osu.Framework.Input.StateChanges;
+using osu.Framework.Input.StateChanges.Events;
using osu.Framework.Input.States;
using osu.Framework.Platform;
+using osu.Game.Rulesets.UI;
using OpenTK;
namespace osu.Game.Input.Handlers
@@ -40,7 +43,29 @@ namespace osu.Game.Input.Handlers
public void Apply(InputState state, IInputStateChangeHandler handler)
{
- handler.HandleCustomInput(state, this);
+ if (!(state is RulesetInputManagerInputState inputState))
+ throw new InvalidOperationException($"{nameof(ReplayState)} should only be applied to a {nameof(RulesetInputManagerInputState)}");
+
+ var lastPressed = inputState.LastReplayState?.PressedActions ?? new List();
+ var released = lastPressed.Except(PressedActions).ToArray();
+ var pressed = PressedActions.Except(lastPressed).ToArray();
+
+ inputState.LastReplayState = this;
+
+ handler.HandleInputStateChange(new ReplayStateChangeEvent(state, this, released, pressed));
+ }
+ }
+
+ public class ReplayStateChangeEvent : InputStateChangeEvent
+ {
+ public readonly T[] ReleasedActions;
+ public readonly T[] PressedActions;
+
+ public ReplayStateChangeEvent(InputState state, IInput input, T[] releasedActions, T[] pressedActions)
+ : base(state, input)
+ {
+ ReleasedActions = releasedActions;
+ PressedActions = pressedActions;
}
}
}
diff --git a/osu.Game/Migrations/20180913080842_AddRankStatus.Designer.cs b/osu.Game/Migrations/20180913080842_AddRankStatus.Designer.cs
new file mode 100644
index 0000000000..5ab43da046
--- /dev/null
+++ b/osu.Game/Migrations/20180913080842_AddRankStatus.Designer.cs
@@ -0,0 +1,380 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using osu.Game.Database;
+
+namespace osu.Game.Migrations
+{
+ [DbContext(typeof(OsuDbContext))]
+ [Migration("20180913080842_AddRankStatus")]
+ partial class AddRankStatus
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "2.1.2-rtm-30932");
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("ApproachRate");
+
+ b.Property("CircleSize");
+
+ b.Property("DrainRate");
+
+ b.Property("OverallDifficulty");
+
+ b.Property("SliderMultiplier");
+
+ b.Property("SliderTickRate");
+
+ b.HasKey("ID");
+
+ b.ToTable("BeatmapDifficulty");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("AudioLeadIn");
+
+ b.Property("BaseDifficultyID");
+
+ b.Property("BeatDivisor");
+
+ b.Property("BeatmapSetInfoID");
+
+ b.Property("Countdown");
+
+ b.Property("DistanceSpacing");
+
+ b.Property("GridSize");
+
+ b.Property("Hash");
+
+ b.Property("Hidden");
+
+ b.Property("LetterboxInBreaks");
+
+ b.Property("MD5Hash");
+
+ b.Property("MetadataID");
+
+ b.Property("OnlineBeatmapID");
+
+ b.Property("Path");
+
+ b.Property("RulesetID");
+
+ b.Property("SpecialStyle");
+
+ b.Property("StackLeniency");
+
+ b.Property("StarDifficulty");
+
+ b.Property("Status");
+
+ b.Property("StoredBookmarks");
+
+ b.Property("TimelineZoom");
+
+ b.Property("Version");
+
+ b.Property("WidescreenStoryboard");
+
+ b.HasKey("ID");
+
+ b.HasIndex("BaseDifficultyID");
+
+ b.HasIndex("BeatmapSetInfoID");
+
+ b.HasIndex("Hash");
+
+ b.HasIndex("MD5Hash");
+
+ b.HasIndex("MetadataID");
+
+ b.HasIndex("OnlineBeatmapID")
+ .IsUnique();
+
+ b.HasIndex("RulesetID");
+
+ b.ToTable("BeatmapInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Artist");
+
+ b.Property("ArtistUnicode");
+
+ b.Property("AudioFile");
+
+ b.Property("AuthorString")
+ .HasColumnName("Author");
+
+ b.Property("BackgroundFile");
+
+ b.Property("PreviewTime");
+
+ b.Property("Source");
+
+ b.Property("Tags");
+
+ b.Property("Title");
+
+ b.Property("TitleUnicode");
+
+ b.HasKey("ID");
+
+ b.ToTable("BeatmapMetadata");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("BeatmapSetInfoID");
+
+ b.Property("FileInfoID");
+
+ b.Property("Filename")
+ .IsRequired();
+
+ b.HasKey("ID");
+
+ b.HasIndex("BeatmapSetInfoID");
+
+ b.HasIndex("FileInfoID");
+
+ b.ToTable("BeatmapSetFileInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("DeletePending");
+
+ b.Property("Hash");
+
+ b.Property("MetadataID");
+
+ b.Property("OnlineBeatmapSetID");
+
+ b.Property("Protected");
+
+ b.Property("Status");
+
+ b.HasKey("ID");
+
+ b.HasIndex("DeletePending");
+
+ b.HasIndex("Hash")
+ .IsUnique();
+
+ b.HasIndex("MetadataID");
+
+ b.HasIndex("OnlineBeatmapSetID")
+ .IsUnique();
+
+ b.ToTable("BeatmapSetInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("IntKey")
+ .HasColumnName("Key");
+
+ b.Property("RulesetID");
+
+ b.Property("StringValue")
+ .HasColumnName("Value");
+
+ b.Property("Variant");
+
+ b.HasKey("ID");
+
+ b.HasIndex("RulesetID", "Variant");
+
+ b.ToTable("Settings");
+ });
+
+ modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("IntAction")
+ .HasColumnName("Action");
+
+ b.Property("KeysString")
+ .HasColumnName("Keys");
+
+ b.Property("RulesetID");
+
+ b.Property("Variant");
+
+ b.HasKey("ID");
+
+ b.HasIndex("IntAction");
+
+ b.HasIndex("RulesetID", "Variant");
+
+ b.ToTable("KeyBinding");
+ });
+
+ modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Hash");
+
+ b.Property("ReferenceCount");
+
+ b.HasKey("ID");
+
+ b.HasIndex("Hash")
+ .IsUnique();
+
+ b.HasIndex("ReferenceCount");
+
+ b.ToTable("FileInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Available");
+
+ b.Property("InstantiationInfo");
+
+ b.Property("Name");
+
+ b.Property("ShortName");
+
+ b.HasKey("ID");
+
+ b.HasIndex("Available");
+
+ b.HasIndex("ShortName")
+ .IsUnique();
+
+ b.ToTable("RulesetInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("FileInfoID");
+
+ b.Property("Filename")
+ .IsRequired();
+
+ b.Property("SkinInfoID");
+
+ b.HasKey("ID");
+
+ b.HasIndex("FileInfoID");
+
+ b.HasIndex("SkinInfoID");
+
+ b.ToTable("SkinFileInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Creator");
+
+ b.Property("DeletePending");
+
+ b.Property("Name");
+
+ b.HasKey("ID");
+
+ b.ToTable("SkinInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
+ {
+ b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty")
+ .WithMany()
+ .HasForeignKey("BaseDifficultyID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet")
+ .WithMany("Beatmaps")
+ .HasForeignKey("BeatmapSetInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
+ .WithMany("Beatmaps")
+ .HasForeignKey("MetadataID");
+
+ b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
+ .WithMany()
+ .HasForeignKey("RulesetID")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
+ {
+ b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo")
+ .WithMany("Files")
+ .HasForeignKey("BeatmapSetInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
+ .WithMany()
+ .HasForeignKey("FileInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
+ {
+ b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
+ .WithMany("BeatmapSets")
+ .HasForeignKey("MetadataID");
+ });
+
+ modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
+ {
+ b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
+ .WithMany()
+ .HasForeignKey("FileInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.Skinning.SkinInfo")
+ .WithMany("Files")
+ .HasForeignKey("SkinInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/osu.Game/Migrations/20180913080842_AddRankStatus.cs b/osu.Game/Migrations/20180913080842_AddRankStatus.cs
new file mode 100644
index 0000000000..bba4944bb7
--- /dev/null
+++ b/osu.Game/Migrations/20180913080842_AddRankStatus.cs
@@ -0,0 +1,33 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+namespace osu.Game.Migrations
+{
+ public partial class AddRankStatus : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.AddColumn(
+ name: "Status",
+ table: "BeatmapSetInfo",
+ nullable: false,
+ defaultValue: -3); // NONE
+
+ migrationBuilder.AddColumn(
+ name: "Status",
+ table: "BeatmapInfo",
+ nullable: false,
+ defaultValue: -3); // NONE
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropColumn(
+ name: "Status",
+ table: "BeatmapSetInfo");
+
+ migrationBuilder.DropColumn(
+ name: "Status",
+ table: "BeatmapInfo");
+ }
+ }
+}
diff --git a/osu.Game/Migrations/20181007180454_StandardizePaths.Designer.cs b/osu.Game/Migrations/20181007180454_StandardizePaths.Designer.cs
new file mode 100644
index 0000000000..b387a45ecf
--- /dev/null
+++ b/osu.Game/Migrations/20181007180454_StandardizePaths.Designer.cs
@@ -0,0 +1,380 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using osu.Game.Database;
+
+namespace osu.Game.Migrations
+{
+ [DbContext(typeof(OsuDbContext))]
+ [Migration("20181007180454_StandardizePaths")]
+ partial class StandardizePaths
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasAnnotation("ProductVersion", "2.1.3-rtm-32065");
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("ApproachRate");
+
+ b.Property("CircleSize");
+
+ b.Property("DrainRate");
+
+ b.Property("OverallDifficulty");
+
+ b.Property("SliderMultiplier");
+
+ b.Property("SliderTickRate");
+
+ b.HasKey("ID");
+
+ b.ToTable("BeatmapDifficulty");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("AudioLeadIn");
+
+ b.Property("BaseDifficultyID");
+
+ b.Property("BeatDivisor");
+
+ b.Property("BeatmapSetInfoID");
+
+ b.Property("Countdown");
+
+ b.Property("DistanceSpacing");
+
+ b.Property("GridSize");
+
+ b.Property("Hash");
+
+ b.Property("Hidden");
+
+ b.Property("LetterboxInBreaks");
+
+ b.Property("MD5Hash");
+
+ b.Property("MetadataID");
+
+ b.Property("OnlineBeatmapID");
+
+ b.Property("Path");
+
+ b.Property("RulesetID");
+
+ b.Property("SpecialStyle");
+
+ b.Property("StackLeniency");
+
+ b.Property("StarDifficulty");
+
+ b.Property("Status");
+
+ b.Property("StoredBookmarks");
+
+ b.Property("TimelineZoom");
+
+ b.Property("Version");
+
+ b.Property("WidescreenStoryboard");
+
+ b.HasKey("ID");
+
+ b.HasIndex("BaseDifficultyID");
+
+ b.HasIndex("BeatmapSetInfoID");
+
+ b.HasIndex("Hash");
+
+ b.HasIndex("MD5Hash");
+
+ b.HasIndex("MetadataID");
+
+ b.HasIndex("OnlineBeatmapID")
+ .IsUnique();
+
+ b.HasIndex("RulesetID");
+
+ b.ToTable("BeatmapInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Artist");
+
+ b.Property("ArtistUnicode");
+
+ b.Property("AudioFile");
+
+ b.Property("AuthorString")
+ .HasColumnName("Author");
+
+ b.Property("BackgroundFile");
+
+ b.Property("PreviewTime");
+
+ b.Property("Source");
+
+ b.Property("Tags");
+
+ b.Property("Title");
+
+ b.Property("TitleUnicode");
+
+ b.HasKey("ID");
+
+ b.ToTable("BeatmapMetadata");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("BeatmapSetInfoID");
+
+ b.Property("FileInfoID");
+
+ b.Property("Filename")
+ .IsRequired();
+
+ b.HasKey("ID");
+
+ b.HasIndex("BeatmapSetInfoID");
+
+ b.HasIndex("FileInfoID");
+
+ b.ToTable("BeatmapSetFileInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("DeletePending");
+
+ b.Property("Hash");
+
+ b.Property("MetadataID");
+
+ b.Property("OnlineBeatmapSetID");
+
+ b.Property("Protected");
+
+ b.Property("Status");
+
+ b.HasKey("ID");
+
+ b.HasIndex("DeletePending");
+
+ b.HasIndex("Hash")
+ .IsUnique();
+
+ b.HasIndex("MetadataID");
+
+ b.HasIndex("OnlineBeatmapSetID")
+ .IsUnique();
+
+ b.ToTable("BeatmapSetInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("IntKey")
+ .HasColumnName("Key");
+
+ b.Property("RulesetID");
+
+ b.Property("StringValue")
+ .HasColumnName("Value");
+
+ b.Property("Variant");
+
+ b.HasKey("ID");
+
+ b.HasIndex("RulesetID", "Variant");
+
+ b.ToTable("Settings");
+ });
+
+ modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("IntAction")
+ .HasColumnName("Action");
+
+ b.Property("KeysString")
+ .HasColumnName("Keys");
+
+ b.Property("RulesetID");
+
+ b.Property("Variant");
+
+ b.HasKey("ID");
+
+ b.HasIndex("IntAction");
+
+ b.HasIndex("RulesetID", "Variant");
+
+ b.ToTable("KeyBinding");
+ });
+
+ modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Hash");
+
+ b.Property("ReferenceCount");
+
+ b.HasKey("ID");
+
+ b.HasIndex("Hash")
+ .IsUnique();
+
+ b.HasIndex("ReferenceCount");
+
+ b.ToTable("FileInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Available");
+
+ b.Property("InstantiationInfo");
+
+ b.Property("Name");
+
+ b.Property("ShortName");
+
+ b.HasKey("ID");
+
+ b.HasIndex("Available");
+
+ b.HasIndex("ShortName")
+ .IsUnique();
+
+ b.ToTable("RulesetInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("FileInfoID");
+
+ b.Property("Filename")
+ .IsRequired();
+
+ b.Property("SkinInfoID");
+
+ b.HasKey("ID");
+
+ b.HasIndex("FileInfoID");
+
+ b.HasIndex("SkinInfoID");
+
+ b.ToTable("SkinFileInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b =>
+ {
+ b.Property("ID")
+ .ValueGeneratedOnAdd();
+
+ b.Property("Creator");
+
+ b.Property("DeletePending");
+
+ b.Property("Name");
+
+ b.HasKey("ID");
+
+ b.ToTable("SkinInfo");
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
+ {
+ b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty")
+ .WithMany()
+ .HasForeignKey("BaseDifficultyID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet")
+ .WithMany("Beatmaps")
+ .HasForeignKey("BeatmapSetInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
+ .WithMany("Beatmaps")
+ .HasForeignKey("MetadataID");
+
+ b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset")
+ .WithMany()
+ .HasForeignKey("RulesetID")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
+ {
+ b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo")
+ .WithMany("Files")
+ .HasForeignKey("BeatmapSetInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
+ .WithMany()
+ .HasForeignKey("FileInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
+ {
+ b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata")
+ .WithMany("BeatmapSets")
+ .HasForeignKey("MetadataID");
+ });
+
+ modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
+ {
+ b.HasOne("osu.Game.IO.FileInfo", "FileInfo")
+ .WithMany()
+ .HasForeignKey("FileInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+
+ b.HasOne("osu.Game.Skinning.SkinInfo")
+ .WithMany("Files")
+ .HasForeignKey("SkinInfoID")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/osu.Game/Migrations/20181007180454_StandardizePaths.cs b/osu.Game/Migrations/20181007180454_StandardizePaths.cs
new file mode 100644
index 0000000000..274b8030a9
--- /dev/null
+++ b/osu.Game/Migrations/20181007180454_StandardizePaths.cs
@@ -0,0 +1,26 @@
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+using System.IO;
+
+namespace osu.Game.Migrations
+{
+ public partial class StandardizePaths : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ string windowsStyle = @"\";
+ string standardized = "/";
+
+ // Escaping \ does not seem to be needed.
+ migrationBuilder.Sql($"UPDATE `BeatmapInfo` SET `Path` = REPLACE(`Path`, '{windowsStyle}', '{standardized}')");
+ migrationBuilder.Sql($"UPDATE `BeatmapMetadata` SET `AudioFile` = REPLACE(`AudioFile`, '{windowsStyle}', '{standardized}')");
+ migrationBuilder.Sql($"UPDATE `BeatmapMetadata` SET `BackgroundFile` = REPLACE(`BackgroundFile`, '{windowsStyle}', '{standardized}')");
+ migrationBuilder.Sql($"UPDATE `BeatmapSetFileInfo` SET `Filename` = REPLACE(`Filename`, '{windowsStyle}', '{standardized}')");
+ migrationBuilder.Sql($"UPDATE `SkinFileInfo` SET `Filename` = REPLACE(`Filename`, '{windowsStyle}', '{standardized}')");
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ }
+ }
+}
diff --git a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs
index 6dbeaed62f..663676a6d3 100644
--- a/osu.Game/Migrations/OsuDbContextModelSnapshot.cs
+++ b/osu.Game/Migrations/OsuDbContextModelSnapshot.cs
@@ -1,7 +1,8 @@
//
-
+using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using osu.Game.Database;
namespace osu.Game.Migrations
@@ -13,7 +14,7 @@ namespace osu.Game.Migrations
{
#pragma warning disable 612, 618
modelBuilder
- .HasAnnotation("ProductVersion", "2.1.1-rtm-30846");
+ .HasAnnotation("ProductVersion", "2.1.3-rtm-32065");
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
{
@@ -78,6 +79,8 @@ namespace osu.Game.Migrations
b.Property("StarDifficulty");
+ b.Property("Status");
+
b.Property("StoredBookmarks");
b.Property("TimelineZoom");
@@ -173,6 +176,8 @@ namespace osu.Game.Migrations
b.Property("Protected");
+ b.Property