1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 07:33:20 +08:00

Merge branch 'master' into spinner-placement-2

This commit is contained in:
Dean Herbert 2018-11-01 18:05:44 +09:00 committed by GitHub
commit 1265d5ac0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 591 additions and 121 deletions

7
.gitignore vendored
View File

@ -10,6 +10,10 @@
# User-specific files (MonoDevelop/Xamarin Studio) # User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs *.userprefs
### Cake ###
tools/*
!tools/cakebuild.csproj
# Build results # Build results
bin/[Dd]ebug/ bin/[Dd]ebug/
[Dd]ebugPublic/ [Dd]ebugPublic/
@ -98,6 +102,7 @@ $tf/
_ReSharper*/ _ReSharper*/
*.[Rr]e[Ss]harper *.[Rr]e[Ss]harper
*.DotSettings.user *.DotSettings.user
inspectcode
# JustCode is a .NET coding add-in # JustCode is a .NET coding add-in
.JustCode .JustCode
@ -257,3 +262,5 @@ paket-files/
__pycache__/ __pycache__/
*.pyc *.pyc
Staging/ Staging/
inspectcodereport.xml

View File

@ -25,6 +25,7 @@ Build and run
- Using Visual Studio 2017, Rider or Visual Studio Code (configurations are included) - 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. - 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 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

View File

@ -1,22 +1,8 @@
clone_depth: 1 clone_depth: 1
version: '{branch}-{build}' version: '{branch}-{build}'
image: Visual Studio 2017 image: Visual Studio 2017
configuration: Debug test: off
cache:
- C:\ProgramData\chocolatey\bin -> appveyor.yml
- C:\ProgramData\chocolatey\lib -> appveyor.yml
install: install:
- cmd: git submodule update --init --recursive --depth=5 - cmd: git submodule update --init --recursive --depth=5
- cmd: choco install resharper-clt -y build_script:
- cmd: choco install nvika -y - cmd: PowerShell -Version 2.0 .\build.ps1
- 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

72
build.cake Normal file
View File

@ -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);

79
build.ps1 Normal file
View File

@ -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

37
build.sh Normal file
View File

@ -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[@]}"

5
cake.config Normal file
View File

@ -0,0 +1,5 @@
[Nuget]
Source=https://api.nuget.org/v3/index.json
UseInProcessClient=true
LoadDependencies=true

View File

@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Catch.Tests
Vector2.Zero, Vector2.Zero,
new Vector2(width * CatchPlayfield.BASE_WIDTH, 0) new Vector2(width * CatchPlayfield.BASE_WIDTH, 0)
}, },
CurveType = CurveType.Linear, PathType = PathType.Linear,
Distance = width * CatchPlayfield.BASE_WIDTH, Distance = width * CatchPlayfield.BASE_WIDTH,
StartTime = i * 2000, StartTime = i * 2000,
NewCombo = i % 8 == 0 NewCombo = i % 8 == 0

View File

@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
StartTime = obj.StartTime, StartTime = obj.StartTime,
Samples = obj.Samples, Samples = obj.Samples,
ControlPoints = curveData.ControlPoints, ControlPoints = curveData.ControlPoints,
CurveType = curveData.CurveType, PathType = curveData.PathType,
Distance = curveData.Distance, Distance = curveData.Distance,
RepeatSamples = curveData.RepeatSamples, RepeatSamples = curveData.RepeatSamples,
RepeatCount = curveData.RepeatCount, RepeatCount = curveData.RepeatCount,

View File

@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Catch.Objects
if (TickDistance == 0) if (TickDistance == 0)
return; return;
var length = Curve.Distance; var length = Path.Distance;
var tickDistance = Math.Min(TickDistance, length); var tickDistance = Math.Min(TickDistance, length);
var spanDuration = length / Velocity; var spanDuration = length / Velocity;
@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Catch.Objects
AddNested(new TinyDroplet AddNested(new TinyDroplet
{ {
StartTime = t, StartTime = t,
X = X + Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH, X = X + Path.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
{ {
Bank = s.Bank, Bank = s.Bank,
@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Catch.Objects
AddNested(new Droplet AddNested(new Droplet
{ {
StartTime = time, StartTime = time,
X = X + Curve.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH, X = X + Path.PositionAt(distanceProgress).X / CatchPlayfield.BASE_WIDTH,
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
{ {
Bank = s.Bank, Bank = s.Bank,
@ -127,12 +127,12 @@ namespace osu.Game.Rulesets.Catch.Objects
{ {
Samples = Samples, Samples = Samples,
StartTime = spanStartTime + spanDuration, 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; public float EndX => X + this.CurvePositionAt(1).X / CatchPlayfield.BASE_WIDTH;
@ -140,24 +140,24 @@ namespace osu.Game.Rulesets.Catch.Objects
public double Distance public double Distance
{ {
get { return Curve.Distance; } get { return Path.Distance; }
set { Curve.Distance = value; } set { Path.Distance = value; }
} }
public SliderCurve Curve { get; } = new SliderCurve(); public SliderPath Path { get; } = new SliderPath();
public Vector2[] ControlPoints public Vector2[] ControlPoints
{ {
get { return Curve.ControlPoints; } get { return Path.ControlPoints; }
set { Curve.ControlPoints = value; } set { Path.ControlPoints = value; }
} }
public List<List<SampleInfo>> RepeatSamples { get; set; } = new List<List<SampleInfo>>(); public List<List<SampleInfo>> RepeatSamples { get; set; } = new List<List<SampleInfo>>();
public CurveType CurveType public PathType PathType
{ {
get { return Curve.CurveType; } get { return Path.PathType; }
set { Curve.CurveType = value; } set { Path.PathType = value; }
} }
public double? LegacyLastTickOffset { get; set; } public double? LegacyLastTickOffset { get; set; }

View File

@ -5,13 +5,13 @@ using System;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Rulesets.Mania.Configuration; using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.Edit.Masks; using osu.Game.Rulesets.Mania.Edit.Masks;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;

View File

@ -5,13 +5,11 @@ using System.Collections.Generic;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.Mods namespace osu.Game.Rulesets.Mania.Mods
{ {
public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter, IApplicableToRulesetContainer<ManiaHitObject> public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter, IApplicableToBeatmap<ManiaHitObject>
{ {
public override string Name => "Dual Stages"; public override string Name => "Dual Stages";
public override string ShortenedName => "DS"; public override string ShortenedName => "DS";
@ -34,22 +32,21 @@ namespace osu.Game.Rulesets.Mania.Mods
mbc.TargetColumns *= 2; mbc.TargetColumns *= 2;
} }
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer) public void ApplyToBeatmap(Beatmap<ManiaHitObject> beatmap)
{ {
var mrc = (ManiaRulesetContainer)rulesetContainer;
// Although this can work, for now let's not allow keymods for mania-specific beatmaps
if (isForCurrentRuleset) if (isForCurrentRuleset)
return; return;
var maniaBeatmap = (ManiaBeatmap)beatmap;
var newDefinitions = new List<StageDefinition>(); var newDefinitions = new List<StageDefinition>();
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 });
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; public PlayfieldType PlayfieldType => PlayfieldType.Dual;

View File

@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{ {
var slider = new Slider var slider = new Slider
{ {
CurveType = CurveType.Linear, PathType = PathType.Linear,
StartTime = Time.Current + 1000, StartTime = Time.Current + 1000,
Position = new Vector2(-200, 0), Position = new Vector2(-200, 0),
ControlPoints = new[] ControlPoints = new[]
@ -207,7 +207,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{ {
var slider = new Slider var slider = new Slider
{ {
CurveType = CurveType.Bezier, PathType = PathType.Bezier,
StartTime = Time.Current + 1000, StartTime = Time.Current + 1000,
Position = new Vector2(-200, 0), Position = new Vector2(-200, 0),
ControlPoints = new[] ControlPoints = new[]
@ -232,7 +232,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{ {
var slider = new Slider var slider = new Slider
{ {
CurveType = CurveType.Linear, PathType = PathType.Linear,
StartTime = Time.Current + 1000, StartTime = Time.Current + 1000,
Position = new Vector2(0, 0), Position = new Vector2(0, 0),
ControlPoints = new[] ControlPoints = new[]
@ -264,7 +264,7 @@ namespace osu.Game.Rulesets.Osu.Tests
{ {
StartTime = Time.Current + 1000, StartTime = Time.Current + 1000,
Position = new Vector2(-100, 0), Position = new Vector2(-100, 0),
CurveType = CurveType.Catmull, PathType = PathType.Catmull,
ControlPoints = new[] ControlPoints = new[]
{ {
Vector2.Zero, Vector2.Zero,

View File

@ -1,11 +1,14 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // 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;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks; using osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks;
using osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks.Components;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
@ -15,6 +18,16 @@ namespace osu.Game.Rulesets.Osu.Tests
{ {
public class TestCaseSliderSelectionMask : HitObjectSelectionMaskTestCase public class TestCaseSliderSelectionMask : HitObjectSelectionMaskTestCase
{ {
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(SliderSelectionMask),
typeof(SliderCircleSelectionMask),
typeof(SliderBodyPiece),
typeof(SliderCircle),
typeof(PathControlPointVisualiser),
typeof(PathControlPointPiece)
};
private readonly DrawableSlider drawableObject; private readonly DrawableSlider drawableObject;
public TestCaseSliderSelectionMask() public TestCaseSliderSelectionMask()
@ -28,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Tests
new Vector2(150, 150), new Vector2(150, 150),
new Vector2(300, 0) new Vector2(300, 0)
}, },
CurveType = CurveType.Bezier, PathType = PathType.Bezier,
Distance = 350 Distance = 350
}; };

View File

@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
StartTime = original.StartTime, StartTime = original.StartTime,
Samples = original.Samples, Samples = original.Samples,
ControlPoints = curveData.ControlPoints, ControlPoints = curveData.ControlPoints,
CurveType = curveData.CurveType, PathType = curveData.PathType,
Distance = curveData.Distance, Distance = curveData.Distance,
RepeatSamples = curveData.RepeatSamples, RepeatSamples = curveData.RepeatSamples,
RepeatCount = curveData.RepeatCount, RepeatCount = curveData.RepeatCount,

View File

@ -108,7 +108,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
progress = progress % 1; progress = progress % 1;
// ReSharper disable once PossibleInvalidOperationException (bugged in current r# version) // ReSharper disable once PossibleInvalidOperationException (bugged in current r# version)
var diff = slider.StackedPosition + slider.Curve.PositionAt(progress) - slider.LazyEndPosition.Value; var diff = slider.StackedPosition + slider.Path.PositionAt(progress) - slider.LazyEndPosition.Value;
float dist = diff.Length; float dist = diff.Length;
if (dist > approxFollowCircleRadius) if (dist > approxFollowCircleRadius)

View File

@ -0,0 +1,113 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// 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.Masks.SliderMasks.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];
}
}

View File

@ -0,0 +1,34 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// 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.Masks.SliderMasks.Components
{
public class PathControlPointVisualiser : CompositeDrawable
{
private readonly Slider slider;
private readonly Container<PathControlPointPiece> pieces;
public PathControlPointVisualiser(Slider slider)
{
this.slider = slider;
InternalChild = pieces = new Container<PathControlPointPiece> { 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]);
}
}
}

View File

@ -46,6 +46,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks.Components
// Need to cause one update // Need to cause one update
body.UpdateProgress(0); body.UpdateProgress(0);
body.Refresh();
} }
} }
} }

View File

@ -16,6 +16,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks.Components
{ {
this.slider = slider; this.slider = slider;
this.position = position; this.position = position;
slider.ControlPointsChanged += _ => UpdatePosition();
} }
protected override void UpdatePosition() protected override void UpdatePosition()
@ -23,10 +25,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks.Components
switch (position) switch (position)
{ {
case SliderPosition.Start: case SliderPosition.Start:
Position = slider.StackedPosition + slider.Curve.PositionAt(0); Position = slider.StackedPosition + slider.Path.PositionAt(0);
break; break;
case SliderPosition.End: case SliderPosition.End:
Position = slider.StackedPosition + slider.Curve.PositionAt(1); Position = slider.StackedPosition + slider.Path.PositionAt(1);
break; break;
} }
} }

View File

@ -24,6 +24,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Masks.SliderMasks
new SliderBodyPiece(sliderObject), new SliderBodyPiece(sliderObject),
headMask = new SliderCircleSelectionMask(slider.HeadCircle, sliderObject, SliderPosition.Start), headMask = new SliderCircleSelectionMask(slider.HeadCircle, sliderObject, SliderPosition.Start),
new SliderCircleSelectionMask(slider.TailCircle, sliderObject, SliderPosition.End), new SliderCircleSelectionMask(slider.TailCircle, sliderObject, SliderPosition.End),
new PathControlPointVisualiser(sliderObject),
}; };
} }

View File

@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Mods
newControlPoints[i] = new Vector2(slider.ControlPoints[i].X, -slider.ControlPoints[i].Y); newControlPoints[i] = new Vector2(slider.ControlPoints[i].X, -slider.ControlPoints[i].Y);
slider.ControlPoints = newControlPoints; slider.ControlPoints = newControlPoints;
slider.Curve?.Calculate(); // Recalculate the slider curve slider.Path?.Calculate(); // Recalculate the slider curve
} }
} }
} }

View File

@ -85,6 +85,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
} }
HitObject.PositionChanged += _ => Position = HitObject.StackedPosition; HitObject.PositionChanged += _ => Position = HitObject.StackedPosition;
slider.ControlPointsChanged += _ => Body.Refresh();
} }
public override Color4 AccentColour public override Color4 AccentColour
@ -119,7 +121,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
double completionProgress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1); double completionProgress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1);
foreach (var c in components.OfType<ISliderProgress>()) c.UpdateProgress(completionProgress); foreach (var c in components.OfType<ISliderProgress>()) c.UpdateProgress(completionProgress);
foreach (var c in components.OfType<ITrackSnaking>()) c.UpdateSnakingPosition(slider.Curve.PositionAt(Body.SnakedStart ?? 0), slider.Curve.PositionAt(Body.SnakedEnd ?? 0)); foreach (var c in components.OfType<ITrackSnaking>()) c.UpdateSnakingPosition(slider.Path.PositionAt(Body.SnakedStart ?? 0), slider.Path.PositionAt(Body.SnakedEnd ?? 0));
foreach (var t in components.OfType<IRequireTracking>()) t.Tracking = Ball.Tracking; foreach (var t in components.OfType<IRequireTracking>()) t.Tracking = Ball.Tracking;
Size = Body.Size; Size = Body.Size;

View File

@ -16,7 +16,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
this.slider = slider; this.slider = slider;
Position = HitObject.Position - slider.Position; h.PositionChanged += _ => updatePosition();
slider.ControlPointsChanged += _ => updatePosition();
updatePosition();
} }
protected override void Update() protected override void Update()
@ -33,5 +36,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public Action<double> OnShake; public Action<double> OnShake;
protected override void Shake(double maximumLength) => OnShake?.Invoke(maximumLength); protected override void Shake(double maximumLength) => OnShake?.Invoke(maximumLength);
private void updatePosition() => Position = HitObject.Position - slider.Position;
} }
} }

View File

@ -8,6 +8,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
public class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking public class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking
{ {
private readonly Slider slider;
/// <summary> /// <summary>
/// The judgement text is provided by the <see cref="DrawableSlider"/>. /// The judgement text is provided by the <see cref="DrawableSlider"/>.
/// </summary> /// </summary>
@ -18,6 +20,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public DrawableSliderTail(Slider slider, SliderTailCircle hitCircle) public DrawableSliderTail(Slider slider, SliderTailCircle hitCircle)
: base(hitCircle) : base(hitCircle)
{ {
this.slider = slider;
Origin = Anchor.Centre; Origin = Anchor.Centre;
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
@ -25,7 +29,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
AlwaysPresent = true; AlwaysPresent = true;
Position = HitObject.Position - slider.Position; hitCircle.PositionChanged += _ => updatePosition();
slider.ControlPointsChanged += _ => updatePosition();
updatePosition();
} }
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
@ -33,5 +40,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (!userTriggered && timeOffset >= 0) if (!userTriggered && timeOffset >= 0)
ApplyResult(r => r.Type = Tracking ? HitResult.Great : HitResult.Miss); ApplyResult(r => r.Type = Tracking ? HitResult.Great : HitResult.Miss);
} }
private void updatePosition() => Position = HitObject.Position - slider.Position;
} }
} }

View File

@ -45,15 +45,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
// Generate the entire curve Refresh();
slider.Curve.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]);
} }
public void UpdateProgress(double completionProgress) public void UpdateProgress(double completionProgress)
@ -80,6 +72,27 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
setRange(start, end); 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) private void setRange(double p0, double p1)
{ {
if (p0 > p1) if (p0 > p1)
@ -90,7 +103,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
SnakedStart = p0; SnakedStart = p0;
SnakedEnd = p1; SnakedEnd = p1;
slider.Curve.GetPathToProgress(CurrentCurve, p0, p1); slider.Path.GetPathToProgress(CurrentCurve, p0, p1);
SetVertices(CurrentCurve); SetVertices(CurrentCurve);

View File

@ -22,7 +22,9 @@ namespace osu.Game.Rulesets.Osu.Objects
/// </summary> /// </summary>
private const float base_scoring_distance = 100; private const float base_scoring_distance = 100;
public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity; public event Action<Vector2[]> ControlPointsChanged;
public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity;
public double Duration => EndTime - StartTime; public double Duration => EndTime - StartTime;
public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t); public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t);
@ -50,24 +52,34 @@ namespace osu.Game.Rulesets.Osu.Objects
} }
} }
public SliderCurve Curve { get; } = new SliderCurve(); public SliderPath Path { get; } = new SliderPath();
public Vector2[] ControlPoints public Vector2[] ControlPoints
{ {
get { return Curve.ControlPoints; } get => Path.ControlPoints;
set { Curve.ControlPoints = value; } 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; } get { return Path.PathType; }
set { Curve.CurveType = value; } set { Path.PathType = value; }
} }
public double Distance public double Distance
{ {
get { return Curve.Distance; } get { return Path.Distance; }
set { Curve.Distance = value; } set { Path.Distance = value; }
} }
public override Vector2 Position public override Vector2 Position
@ -177,7 +189,7 @@ namespace osu.Game.Rulesets.Osu.Objects
private void createTicks() private void createTicks()
{ {
var length = Curve.Distance; var length = Path.Distance;
var tickDistance = MathHelper.Clamp(TickDistance, 0, length); var tickDistance = MathHelper.Clamp(TickDistance, 0, length);
if (tickDistance == 0) return; if (tickDistance == 0) return;
@ -216,7 +228,7 @@ namespace osu.Game.Rulesets.Osu.Objects
SpanIndex = span, SpanIndex = span,
SpanStartTime = spanStartTime, SpanStartTime = spanStartTime,
StartTime = spanStartTime + timeProgress * SpanDuration, StartTime = spanStartTime + timeProgress * SpanDuration,
Position = Position + Curve.PositionAt(distanceProgress), Position = Position + Path.PositionAt(distanceProgress),
StackHeight = StackHeight, StackHeight = StackHeight,
Scale = Scale, Scale = Scale,
Samples = sampleList Samples = sampleList
@ -234,7 +246,7 @@ namespace osu.Game.Rulesets.Osu.Objects
RepeatIndex = repeatIndex, RepeatIndex = repeatIndex,
SpanDuration = SpanDuration, SpanDuration = SpanDuration,
StartTime = StartTime + repeat * SpanDuration, StartTime = StartTime + repeat * SpanDuration,
Position = Position + Curve.PositionAt(repeat % 2), Position = Position + Path.PositionAt(repeat % 2),
StackHeight = StackHeight, StackHeight = StackHeight,
Scale = Scale, Scale = Scale,
Samples = new List<SampleInfo>(RepeatSamples[repeatIndex]) Samples = new List<SampleInfo>(RepeatSamples[repeatIndex])

View File

@ -15,6 +15,9 @@ using OpenTK;
namespace osu.Game.Rulesets.Edit namespace osu.Game.Rulesets.Edit
{ {
/// <summary>
/// A mask which governs the creation of a new <see cref="HitObject"/> to actualisation.
/// </summary>
public abstract class PlacementMask : CompositeDrawable, IRequireHighFrequencyMousePosition public abstract class PlacementMask : CompositeDrawable, IRequireHighFrequencyMousePosition
{ {
/// <summary> /// <summary>
@ -81,6 +84,8 @@ namespace osu.Game.Rulesets.Edit
switch (e) switch (e)
{ {
case ScrollEvent _:
return false;
case MouseEvent _: case MouseEvent _:
return true; return true;
default: default:

View File

@ -0,0 +1,22 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mods
{
/// <summary>
/// Interface for a <see cref="Mod"/> that applies changes to a <see cref="Beatmap"/>
/// after conversion and post-processing has completed.
/// </summary>
public interface IApplicableToBeatmap<TObject> : IApplicableMod
where TObject : HitObject
{
/// <summary>
/// Applies this <see cref="IApplicableToBeatmap{TObject}"/> to a <see cref="Beatmap{TObject}"/>.
/// </summary>
/// <param name="beatmap">The <see cref="Beatmap{TObject}"/> to apply to.</param>
void ApplyToBeatmap(Beatmap<TObject> beatmap);
}
}

View File

@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
}; };
} }
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> repeatSamples) protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List<List<SampleInfo>> repeatSamples)
{ {
newCombo |= forceNewCombo; newCombo |= forceNewCombo;
comboOffset += extraComboOffset; comboOffset += extraComboOffset;
@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
ComboOffset = comboOffset, ComboOffset = comboOffset,
ControlPoints = controlPoints, ControlPoints = controlPoints,
Distance = length, Distance = length,
CurveType = curveType, PathType = pathType,
RepeatSamples = repeatSamples, RepeatSamples = repeatSamples,
RepeatCount = repeatCount RepeatCount = repeatCount
}; };

View File

@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
} }
else if (type.HasFlag(ConvertHitObjectType.Slider)) else if (type.HasFlag(ConvertHitObjectType.Slider))
{ {
CurveType curveType = CurveType.Catmull; PathType pathType = PathType.Catmull;
double length = 0; double length = 0;
string[] pointSplit = split[5].Split('|'); string[] pointSplit = split[5].Split('|');
@ -90,16 +90,16 @@ namespace osu.Game.Rulesets.Objects.Legacy
switch (t) switch (t)
{ {
case @"C": case @"C":
curveType = CurveType.Catmull; pathType = PathType.Catmull;
break; break;
case @"B": case @"B":
curveType = CurveType.Bezier; pathType = PathType.Bezier;
break; break;
case @"L": case @"L":
curveType = CurveType.Linear; pathType = PathType.Linear;
break; break;
case @"P": case @"P":
curveType = CurveType.PerfectCurve; pathType = PathType.PerfectCurve;
break; break;
} }
@ -113,8 +113,8 @@ namespace osu.Game.Rulesets.Objects.Legacy
// osu-stable special-cased colinear perfect curves to a CurveType.Linear // osu-stable special-cased colinear perfect curves to a CurveType.Linear
bool isLinear(Vector2[] p) => Precision.AlmostEquals(0, (p[1].Y - p[0].Y) * (p[2].X - p[0].X) - (p[1].X - p[0].X) * (p[2].Y - p[0].Y)); bool isLinear(Vector2[] p) => Precision.AlmostEquals(0, (p[1].Y - p[0].Y) * (p[2].X - p[0].X) - (p[1].X - p[0].X) * (p[2].Y - p[0].Y));
if (points.Length == 3 && curveType == CurveType.PerfectCurve && isLinear(points)) if (points.Length == 3 && pathType == PathType.PerfectCurve && isLinear(points))
curveType = CurveType.Linear; pathType = PathType.Linear;
int repeatCount = Convert.ToInt32(split[6], CultureInfo.InvariantCulture); int repeatCount = Convert.ToInt32(split[6], CultureInfo.InvariantCulture);
@ -178,7 +178,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
for (int i = 0; i < nodes; i++) for (int i = 0; i < nodes; i++)
nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i])); nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i]));
result = CreateSlider(pos, combo, comboOffset, points, length, curveType, repeatCount, nodeSamples); result = CreateSlider(pos, combo, comboOffset, points, length, pathType, repeatCount, nodeSamples);
} }
else if (type.HasFlag(ConvertHitObjectType.Spinner)) else if (type.HasFlag(ConvertHitObjectType.Spinner))
{ {
@ -268,11 +268,11 @@ namespace osu.Game.Rulesets.Objects.Legacy
/// <param name="comboOffset">When starting a new combo, the offset of the new combo relative to the current one.</param> /// <param name="comboOffset">When starting a new combo, the offset of the new combo relative to the current one.</param>
/// <param name="controlPoints">The slider control points.</param> /// <param name="controlPoints">The slider control points.</param>
/// <param name="length">The slider length.</param> /// <param name="length">The slider length.</param>
/// <param name="curveType">The slider curve type.</param> /// <param name="pathType">The slider curve type.</param>
/// <param name="repeatCount">The slider repeat count.</param> /// <param name="repeatCount">The slider repeat count.</param>
/// <param name="repeatSamples">The samples to be played when the repeat nodes are hit. This includes the head and tail of the slider.</param> /// <param name="repeatSamples">The samples to be played when the repeat nodes are hit. This includes the head and tail of the slider.</param>
/// <returns>The hit object.</returns> /// <returns>The hit object.</returns>
protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> repeatSamples); protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List<List<SampleInfo>> repeatSamples);
/// <summary> /// <summary>
/// Creates a legacy Spinner-type hit object. /// Creates a legacy Spinner-type hit object.

View File

@ -20,9 +20,9 @@ namespace osu.Game.Rulesets.Objects.Legacy
/// <summary> /// <summary>
/// <see cref="ConvertSlider"/>s don't need a curve since they're converted to ruleset-specific hitobjects. /// <see cref="ConvertSlider"/>s don't need a curve since they're converted to ruleset-specific hitobjects.
/// </summary> /// </summary>
public SliderCurve Curve { get; } = null; public SliderPath Path { get; } = null;
public Vector2[] ControlPoints { get; set; } public Vector2[] ControlPoints { get; set; }
public CurveType CurveType { get; set; } public PathType PathType { get; set; }
public double Distance { get; set; } public double Distance { get; set; }

View File

@ -26,14 +26,14 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
}; };
} }
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> repeatSamples) protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List<List<SampleInfo>> repeatSamples)
{ {
return new ConvertSlider return new ConvertSlider
{ {
X = position.X, X = position.X,
ControlPoints = controlPoints, ControlPoints = controlPoints,
Distance = length, Distance = length,
CurveType = curveType, PathType = pathType,
RepeatSamples = repeatSamples, RepeatSamples = repeatSamples,
RepeatCount = repeatCount RepeatCount = repeatCount
}; };

View File

@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
}; };
} }
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> repeatSamples) protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List<List<SampleInfo>> repeatSamples)
{ {
newCombo |= forceNewCombo; newCombo |= forceNewCombo;
comboOffset += extraComboOffset; comboOffset += extraComboOffset;
@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
ComboOffset = comboOffset, ComboOffset = comboOffset,
ControlPoints = controlPoints, ControlPoints = controlPoints,
Distance = Math.Max(0, length), Distance = Math.Max(0, length),
CurveType = curveType, PathType = pathType,
RepeatSamples = repeatSamples, RepeatSamples = repeatSamples,
RepeatCount = repeatCount RepeatCount = repeatCount
}; };

View File

@ -23,13 +23,13 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
return new ConvertHit(); return new ConvertHit();
} }
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> repeatSamples) protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, Vector2[] controlPoints, double length, PathType pathType, int repeatCount, List<List<SampleInfo>> repeatSamples)
{ {
return new ConvertSlider return new ConvertSlider
{ {
ControlPoints = controlPoints, ControlPoints = controlPoints,
Distance = length, Distance = length,
CurveType = curveType, PathType = pathType,
RepeatSamples = repeatSamples, RepeatSamples = repeatSamples,
RepeatCount = repeatCount RepeatCount = repeatCount
}; };

View File

@ -10,13 +10,13 @@ using OpenTK;
namespace osu.Game.Rulesets.Objects namespace osu.Game.Rulesets.Objects
{ {
public class SliderCurve public class SliderPath
{ {
public double Distance; public double Distance;
public Vector2[] ControlPoints; public Vector2[] ControlPoints;
public CurveType CurveType = CurveType.PerfectCurve; public PathType PathType = PathType.PerfectCurve;
public Vector2 Offset; public Vector2 Offset;
@ -25,15 +25,15 @@ namespace osu.Game.Rulesets.Objects
private List<Vector2> calculateSubpath(ReadOnlySpan<Vector2> subControlPoints) private List<Vector2> calculateSubpath(ReadOnlySpan<Vector2> subControlPoints)
{ {
switch (CurveType) switch (PathType)
{ {
case CurveType.Linear: case PathType.Linear:
var result = new List<Vector2>(subControlPoints.Length); var result = new List<Vector2>(subControlPoints.Length);
foreach (var c in subControlPoints) foreach (var c in subControlPoints)
result.Add(c); result.Add(c);
return result; return result;
case CurveType.PerfectCurve: case PathType.PerfectCurve:
//we can only use CircularArc iff we have exactly three control points and no dissection. //we can only use CircularArc iff we have exactly three control points and no dissection.
if (ControlPoints.Length != 3 || subControlPoints.Length != 3) if (ControlPoints.Length != 3 || subControlPoints.Length != 3)
break; break;
@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Objects
break; break;
return subpath; return subpath;
case CurveType.Catmull: case PathType.Catmull:
return new CatmullApproximator(subControlPoints).CreateCatmull(); return new CatmullApproximator(subControlPoints).CreateCatmull();
} }
@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Objects
Vector2 diff = calculatedPath[i + 1] - calculatedPath[i]; Vector2 diff = calculatedPath[i + 1] - calculatedPath[i];
double d = diff.Length; double d = diff.Length;
// Shorten slider curves that are too long compared to what's // Shorten slider paths that are too long compared to what's
// in the .osu file. // in the .osu file.
if (Distance - l < d) if (Distance - l < d)
{ {
@ -109,7 +109,7 @@ namespace osu.Game.Rulesets.Objects
cumulativeLength.Add(l); cumulativeLength.Add(l);
} }
// Lengthen slider curves that are too short compared to what's // Lengthen slider paths that are too short compared to what's
// in the .osu file. // in the .osu file.
if (l < Distance && calculatedPath.Count > 1) if (l < Distance && calculatedPath.Count > 1)
{ {
@ -124,10 +124,33 @@ namespace osu.Game.Rulesets.Objects
} }
} }
public void Calculate() private void calculateCumulativeLength()
{
double l = 0;
cumulativeLength.Clear();
cumulativeLength.Add(l);
for (int i = 0; i < calculatedPath.Count - 1; ++i)
{
Vector2 diff = calculatedPath[i + 1] - calculatedPath[i];
double d = diff.Length;
l += d;
cumulativeLength.Add(l);
}
Distance = l;
}
public void Calculate(bool updateDistance = false)
{ {
calculatePath(); calculatePath();
calculateCumulativeLengthAndTrimPath();
if (!updateDistance)
calculateCumulativeLengthAndTrimPath();
else
calculateCumulativeLength();
} }
private int indexOfDistance(double d) private int indexOfDistance(double d)
@ -168,10 +191,10 @@ namespace osu.Game.Rulesets.Objects
} }
/// <summary> /// <summary>
/// Computes the slider curve until a given progress that ranges from 0 (beginning of the slider) /// Computes the slider path until a given progress that ranges from 0 (beginning of the slider)
/// to 1 (end of the slider) and stores the generated path in the given list. /// to 1 (end of the slider) and stores the generated path in the given list.
/// </summary> /// </summary>
/// <param name="path">The list to be filled with the computed curve.</param> /// <param name="path">The list to be filled with the computed path.</param>
/// <param name="p0">Start progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider).</param> /// <param name="p0">Start progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider).</param>
/// <param name="p1">End progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider).</param> /// <param name="p1">End progress. Ranges from 0 (beginning of the slider) to 1 (end of the slider).</param>
public void GetPathToProgress(List<Vector2> path, double p0, double p1) public void GetPathToProgress(List<Vector2> path, double p0, double p1)
@ -196,10 +219,10 @@ namespace osu.Game.Rulesets.Objects
} }
/// <summary> /// <summary>
/// Computes the position on the slider at a given progress that ranges from 0 (beginning of the curve) /// Computes the position on the slider at a given progress that ranges from 0 (beginning of the path)
/// to 1 (end of the curve). /// to 1 (end of the path).
/// </summary> /// </summary>
/// <param name="progress">Ranges from 0 (beginning of the curve) to 1 (end of the curve).</param> /// <param name="progress">Ranges from 0 (beginning of the path) to 1 (end of the path).</param>
/// <returns></returns> /// <returns></returns>
public Vector2 PositionAt(double progress) public Vector2 PositionAt(double progress)
{ {

View File

@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Objects.Types
/// <summary> /// <summary>
/// The curve. /// The curve.
/// </summary> /// </summary>
SliderCurve Curve { get; } SliderPath Path { get; }
/// <summary> /// <summary>
/// The control points that shape the curve. /// The control points that shape the curve.
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Objects.Types
/// <summary> /// <summary>
/// The type of curve. /// The type of curve.
/// </summary> /// </summary>
CurveType CurveType { get; } PathType PathType { get; }
} }
public static class HasCurveExtensions public static class HasCurveExtensions
@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Objects.Types
/// <param name="progress">[0, 1] where 0 is the start time of the <see cref="HitObject"/> and 1 is the end time of the <see cref="HitObject"/>.</param> /// <param name="progress">[0, 1] where 0 is the start time of the <see cref="HitObject"/> and 1 is the end time of the <see cref="HitObject"/>.</param>
/// <returns>The position on the curve.</returns> /// <returns>The position on the curve.</returns>
public static Vector2 CurvePositionAt(this IHasCurve obj, double progress) public static Vector2 CurvePositionAt(this IHasCurve obj, double progress)
=> obj.Curve.PositionAt(obj.ProgressAt(progress)); => obj.Path.PositionAt(obj.ProgressAt(progress));
/// <summary> /// <summary>
/// Computes the progress along the curve relative to how much of the <see cref="HitObject"/> has been completed. /// Computes the progress along the curve relative to how much of the <see cref="HitObject"/> has been completed.

View File

@ -3,7 +3,7 @@
namespace osu.Game.Rulesets.Objects.Types namespace osu.Game.Rulesets.Objects.Types
{ {
public enum CurveType public enum PathType
{ {
Catmull, Catmull,
Bezier, Bezier,

View File

@ -238,6 +238,8 @@ namespace osu.Game.Rulesets.UI
KeyBindingInputManager = CreateInputManager(); KeyBindingInputManager = CreateInputManager();
KeyBindingInputManager.RelativeSizeAxes = Axes.Both; KeyBindingInputManager.RelativeSizeAxes = Axes.Both;
applyBeatmapMods(Mods);
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -255,16 +257,29 @@ namespace osu.Game.Rulesets.UI
KeyBindingInputManager.Add(Cursor); KeyBindingInputManager.Add(Cursor);
// Apply mods // Apply mods
applyMods(Mods, config); applyRulesetMods(Mods, config);
loadObjects(); loadObjects();
} }
/// <summary>
/// Applies the active mods to the Beatmap.
/// </summary>
/// <param name="mods"></param>
private void applyBeatmapMods(IEnumerable<Mod> mods)
{
if (mods == null)
return;
foreach (var mod in mods.OfType<IApplicableToBeatmap<TObject>>())
mod.ApplyToBeatmap(Beatmap);
}
/// <summary> /// <summary>
/// Applies the active mods to this RulesetContainer. /// Applies the active mods to this RulesetContainer.
/// </summary> /// </summary>
/// <param name="mods"></param> /// <param name="mods"></param>
private void applyMods(IEnumerable<Mod> mods, OsuConfigManager config) private void applyRulesetMods(IEnumerable<Mod> mods, OsuConfigManager config)
{ {
if (mods == null) if (mods == null)
return; return;

View File

@ -87,6 +87,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers
placementHandler.Delete(h.HitObject.HitObject); placementHandler.Delete(h.HitObject.HitObject);
return true; return true;
} }
return base.OnKeyDown(e); return base.OnKeyDown(e);
} }

View File

@ -134,11 +134,13 @@ namespace osu.Game.Screens.Play
{ {
PlayerSettingsOverlay.Show(); PlayerSettingsOverlay.Show();
ModDisplay.FadeIn(200); ModDisplay.FadeIn(200);
KeyCounter.Margin = new MarginPadding(10) { Bottom = 30 };
} }
else else
{ {
PlayerSettingsOverlay.Hide(); PlayerSettingsOverlay.Hide();
ModDisplay.Delay(2000).FadeOut(200); ModDisplay.Delay(2000).FadeOut(200);
KeyCounter.Margin = new MarginPadding(10);
} }
} }

View File

@ -21,7 +21,7 @@ namespace osu.Game.Screens.Play
{ {
private const int bottom_bar_height = 5; private const int bottom_bar_height = 5;
private static readonly Vector2 handle_size = new Vector2(14, 25); private static readonly Vector2 handle_size = new Vector2(10, 18);
private const float transition_duration = 200; private const float transition_duration = 200;
@ -135,6 +135,8 @@ namespace osu.Game.Screens.Play
{ {
bar.FadeTo(allowSeeking ? 1 : 0, transition_duration, Easing.In); bar.FadeTo(allowSeeking ? 1 : 0, transition_duration, Easing.In);
this.MoveTo(new Vector2(0, allowSeeking ? 0 : bottom_bar_height), transition_duration, Easing.In); this.MoveTo(new Vector2(0, allowSeeking ? 0 : bottom_bar_height), transition_duration, Easing.In);
info.Margin = new MarginPadding { Bottom = Height - (allowSeeking ? 0 : handle_size.Y) };
} }
protected override void PopIn() protected override void PopIn()

View File

@ -350,7 +350,7 @@ namespace osu.Game.Screens.Select
} }
} }
ensurePlayingSelected(preview); if (IsCurrentScreen) ensurePlayingSelected(preview);
UpdateBeatmap(Beatmap.Value); UpdateBeatmap(Beatmap.Value);
} }

View File

@ -18,7 +18,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.1.4" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.1.4" />
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
<PackageReference Include="ppy.osu.Framework" Version="0.0.7442" /> <PackageReference Include="ppy.osu.Framework" Version="2018.1030.0" />
<PackageReference Include="SharpCompress" Version="0.22.0" /> <PackageReference Include="SharpCompress" Version="0.22.0" />
<PackageReference Include="NUnit" Version="3.11.0" /> <PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" /> <PackageReference Include="SharpRaven" Version="2.4.0" />

11
tools/cakebuild.csproj Normal file
View File

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