mirror of
https://github.com/ppy/osu.git
synced 2025-01-06 21:12:55 +08:00
Merge branch 'Cakefy_osu!' of https://github.com/miterosan/osu into Cakefy_osu!
This commit is contained in:
commit
cf41cc83dc
@ -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/BCrS8/7faccf7876.enc # signing certificate
|
|
||||||
- cmd: appveyor-tools\secure-file -decrypt 7faccf7876.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
|
|
@ -27,9 +27,9 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="System.IO.Packaging" Version="4.5.0" />
|
<PackageReference Include="System.IO.Packaging" Version="4.5.0" />
|
||||||
<PackageReference Include="ppy.squirrel.windows" Version="1.8.0.6" />
|
<PackageReference Include="ppy.squirrel.windows" Version="1.9.0.2" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.3" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.3" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.1.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Resources">
|
<ItemGroup Label="Resources">
|
||||||
<EmbeddedResource Include="lazer.ico" />
|
<EmbeddedResource Include="lazer.ico" />
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// 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.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
@ -38,7 +37,7 @@ namespace osu.Game.Rulesets.Catch.Tests
|
|||||||
beatmap.HitObjects.Add(new JuiceStream
|
beatmap.HitObjects.Add(new JuiceStream
|
||||||
{
|
{
|
||||||
X = 0.5f - width / 2,
|
X = 0.5f - width / 2,
|
||||||
ControlPoints = new List<Vector2>
|
ControlPoints = new[]
|
||||||
{
|
{
|
||||||
Vector2.Zero,
|
Vector2.Zero,
|
||||||
new Vector2(width * CatchPlayfield.BASE_WIDTH, 0)
|
new Vector2(width * CatchPlayfield.BASE_WIDTH, 0)
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.10.1" />
|
<PackageReference Include="NUnit" Version="3.11.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
||||||
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
|
@ -146,7 +146,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
|
|
||||||
public SliderCurve Curve { get; } = new SliderCurve();
|
public SliderCurve Curve { get; } = new SliderCurve();
|
||||||
|
|
||||||
public List<Vector2> ControlPoints
|
public Vector2[] ControlPoints
|
||||||
{
|
{
|
||||||
get { return Curve.ControlPoints; }
|
get { return Curve.ControlPoints; }
|
||||||
set { Curve.ControlPoints = value; }
|
set { Curve.ControlPoints = value; }
|
||||||
|
@ -11,6 +11,7 @@ using osu.Game.Rulesets.Catch.Objects.Drawable;
|
|||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
|
using OpenTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.UI
|
namespace osu.Game.Rulesets.Catch.UI
|
||||||
{
|
{
|
||||||
@ -18,9 +19,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
{
|
{
|
||||||
public const float BASE_WIDTH = 512;
|
public const float BASE_WIDTH = 512;
|
||||||
|
|
||||||
protected override Container<Drawable> Content => content;
|
|
||||||
private readonly Container<Drawable> content;
|
|
||||||
|
|
||||||
private readonly CatcherArea catcherArea;
|
private readonly CatcherArea catcherArea;
|
||||||
|
|
||||||
protected override bool UserScrollSpeedAdjustment => false;
|
protected override bool UserScrollSpeedAdjustment => false;
|
||||||
@ -28,7 +26,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
protected override SpeedChangeVisualisationMethod VisualisationMethod => SpeedChangeVisualisationMethod.Constant;
|
protected override SpeedChangeVisualisationMethod VisualisationMethod => SpeedChangeVisualisationMethod.Constant;
|
||||||
|
|
||||||
public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation)
|
public CatchPlayfield(BeatmapDifficulty difficulty, Func<CatchHitObject, DrawableHitObject<CatchHitObject>> getVisualRepresentation)
|
||||||
: base(BASE_WIDTH)
|
|
||||||
{
|
{
|
||||||
Direction.Value = ScrollingDirection.Down;
|
Direction.Value = ScrollingDirection.Down;
|
||||||
|
|
||||||
@ -37,27 +34,27 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
Anchor = Anchor.TopCentre;
|
Anchor = Anchor.TopCentre;
|
||||||
Origin = Anchor.TopCentre;
|
Origin = Anchor.TopCentre;
|
||||||
|
|
||||||
base.Content.Anchor = Anchor.BottomLeft;
|
Size = new Vector2(0.86f); // matches stable's vertical offset for catcher plate
|
||||||
base.Content.Origin = Anchor.BottomLeft;
|
|
||||||
|
|
||||||
base.Content.AddRange(new Drawable[]
|
InternalChild = new PlayfieldAdjustmentContainer
|
||||||
{
|
{
|
||||||
explodingFruitContainer = new Container
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
explodingFruitContainer = new Container
|
||||||
},
|
{
|
||||||
catcherArea = new CatcherArea(difficulty)
|
RelativeSizeAxes = Axes.Both,
|
||||||
{
|
},
|
||||||
GetVisualRepresentation = getVisualRepresentation,
|
catcherArea = new CatcherArea(difficulty)
|
||||||
ExplodingFruitTarget = explodingFruitContainer,
|
{
|
||||||
Anchor = Anchor.BottomLeft,
|
GetVisualRepresentation = getVisualRepresentation,
|
||||||
Origin = Anchor.TopLeft,
|
ExplodingFruitTarget = explodingFruitContainer,
|
||||||
},
|
Anchor = Anchor.BottomLeft,
|
||||||
content = new Container<Drawable>
|
Origin = Anchor.TopLeft,
|
||||||
{
|
},
|
||||||
RelativeSizeAxes = Axes.Both,
|
HitObjectContainer
|
||||||
},
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
VisibleTimeRange.Value = BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
|
VisibleTimeRange.Value = BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@ using osu.Game.Rulesets.Replays;
|
|||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using OpenTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.UI
|
namespace osu.Game.Rulesets.Catch.UI
|
||||||
{
|
{
|
||||||
@ -32,8 +31,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo);
|
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<CatchHitObject> GetVisualRepresentation(CatchHitObject h)
|
protected override DrawableHitObject<CatchHitObject> GetVisualRepresentation(CatchHitObject h)
|
||||||
{
|
{
|
||||||
switch (h)
|
switch (h)
|
||||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
{
|
{
|
||||||
public class CatcherArea : Container
|
public class CatcherArea : Container
|
||||||
{
|
{
|
||||||
public const float CATCHER_SIZE = 84;
|
public const float CATCHER_SIZE = 100;
|
||||||
|
|
||||||
protected readonly Catcher MovableCatcher;
|
protected readonly Catcher MovableCatcher;
|
||||||
|
|
||||||
|
42
osu.Game.Rulesets.Catch/UI/PlayfieldAdjustmentContainer.cs
Normal file
42
osu.Game.Rulesets.Catch/UI/PlayfieldAdjustmentContainer.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// 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 OpenTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.UI
|
||||||
|
{
|
||||||
|
public class PlayfieldAdjustmentContainer : Container
|
||||||
|
{
|
||||||
|
protected override Container<Drawable> 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 }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="Container"/> which scales its content relative to a target width.
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,9 +2,10 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.10.1" />
|
<PackageReference Include="NUnit" Version="3.11.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
||||||
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
|
@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
TargetColumns = (int)Math.Max(1, roundedCircleSize);
|
TargetColumns = (int)Math.Max(1, roundedCircleSize);
|
||||||
else
|
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)
|
if (percentSliderOrSpinner < 0.2)
|
||||||
TargetColumns = 7;
|
TargetColumns = 7;
|
||||||
else if (percentSliderOrSpinner < 0.3 || roundedCircleSize >= 5)
|
else if (percentSliderOrSpinner < 0.3 || roundedCircleSize >= 5)
|
||||||
|
@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
drainTime = 10000;
|
drainTime = 10000;
|
||||||
|
|
||||||
BeatmapDifficulty difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty;
|
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);
|
conversionDifficulty = Math.Min(conversionDifficulty.Value, 12);
|
||||||
|
|
||||||
return conversionDifficulty.Value;
|
return conversionDifficulty.Value;
|
||||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Configuration
|
|||||||
{
|
{
|
||||||
base.InitialiseDefaults();
|
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);
|
Set(ManiaSetting.ScrollDirection, ManiaScrollingDirection.Down);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,8 +20,7 @@ namespace osu.Game.Rulesets.Mania.Edit
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
Size = Vector2.One
|
||||||
};
|
};
|
||||||
|
|
||||||
protected override Vector2 PlayfieldArea => Vector2.One;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,8 +30,6 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
internal readonly Container TopLevelContainer;
|
internal readonly Container TopLevelContainer;
|
||||||
private readonly Container explosionContainer;
|
private readonly Container explosionContainer;
|
||||||
|
|
||||||
protected override Container<Drawable> Content => hitObjectArea;
|
|
||||||
|
|
||||||
public Column()
|
public Column()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Y;
|
RelativeSizeAxes = Axes.Y;
|
||||||
@ -54,7 +52,10 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
hitObjectArea = new ColumnHitObjectArea { RelativeSizeAxes = Axes.Both },
|
hitObjectArea = new ColumnHitObjectArea(HitObjectContainer)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
explosionContainer = new Container
|
explosionContainer = new Container
|
||||||
{
|
{
|
||||||
Name = "Hit explosions",
|
Name = "Hit explosions",
|
||||||
|
@ -8,28 +8,24 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.UI.Components
|
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_height = 10;
|
||||||
private const float hit_target_bar_height = 2;
|
private const float hit_target_bar_height = 2;
|
||||||
|
|
||||||
private Container<Drawable> content;
|
|
||||||
protected override Container<Drawable> Content => content;
|
|
||||||
|
|
||||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||||
|
|
||||||
private Container hitTargetLine;
|
private readonly Container hitTargetLine;
|
||||||
|
private readonly Drawable hitTargetBar;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
public ColumnHitObjectArea(HitObjectContainer hitObjectContainer)
|
||||||
private void load(IScrollingInfo scrollingInfo)
|
|
||||||
{
|
{
|
||||||
Drawable hitTargetBar;
|
|
||||||
|
|
||||||
InternalChildren = new[]
|
InternalChildren = new[]
|
||||||
{
|
{
|
||||||
hitTargetBar = new Box
|
hitTargetBar = new Box
|
||||||
@ -45,13 +41,13 @@ namespace osu.Game.Rulesets.Mania.UI.Components
|
|||||||
Masking = true,
|
Masking = true,
|
||||||
Child = new Box { RelativeSizeAxes = Axes.Both }
|
Child = new Box { RelativeSizeAxes = Axes.Both }
|
||||||
},
|
},
|
||||||
content = new Container
|
hitObjectContainer
|
||||||
{
|
|
||||||
Name = "Hit objects",
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(IScrollingInfo scrollingInfo)
|
||||||
|
{
|
||||||
direction.BindTo(scrollingInfo.Direction);
|
direction.BindTo(scrollingInfo.Direction);
|
||||||
direction.BindValueChanged(direction =>
|
direction.BindValueChanged(direction =>
|
||||||
{
|
{
|
||||||
|
@ -10,6 +10,7 @@ using osu.Game.Rulesets.Mania.Beatmaps;
|
|||||||
using osu.Game.Rulesets.Mania.Configuration;
|
using osu.Game.Rulesets.Mania.Configuration;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using OpenTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.UI
|
namespace osu.Game.Rulesets.Mania.UI
|
||||||
{
|
{
|
||||||
@ -25,6 +26,8 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
if (stageDefinitions.Count <= 0)
|
if (stageDefinitions.Count <= 0)
|
||||||
throw new ArgumentException("Can't have zero or fewer stages.");
|
throw new ArgumentException("Can't have zero or fewer stages.");
|
||||||
|
|
||||||
|
Size = new Vector2(1, 0.8f);
|
||||||
|
|
||||||
GridContainer playfieldGrid;
|
GridContainer playfieldGrid;
|
||||||
AddInternal(playfieldGrid = new GridContainer
|
AddInternal(playfieldGrid = new GridContainer
|
||||||
{
|
{
|
||||||
|
@ -24,7 +24,6 @@ using osu.Game.Rulesets.Replays;
|
|||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using OpenTK;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.UI
|
namespace osu.Game.Rulesets.Mania.UI
|
||||||
{
|
{
|
||||||
@ -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);
|
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ using osu.Game.Rulesets.UI.Scrolling;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.UI
|
namespace osu.Game.Rulesets.Mania.UI
|
||||||
{
|
{
|
||||||
public class ManiaScrollingPlayfield : ScrollingPlayfield
|
public abstract class ManiaScrollingPlayfield : ScrollingPlayfield
|
||||||
{
|
{
|
||||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||||
|
|
||||||
|
@ -30,8 +30,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
public IReadOnlyList<Column> Columns => columnFlow.Children;
|
public IReadOnlyList<Column> Columns => columnFlow.Children;
|
||||||
private readonly FillFlowContainer<Column> columnFlow;
|
private readonly FillFlowContainer<Column> columnFlow;
|
||||||
|
|
||||||
protected override Container<Drawable> Content => barLineContainer;
|
private readonly Container barLineContainer;
|
||||||
private readonly Container<Drawable> barLineContainer;
|
|
||||||
|
|
||||||
public Container<DrawableManiaJudgement> Judgements => judgements;
|
public Container<DrawableManiaJudgement> Judgements => judgements;
|
||||||
private readonly JudgementContainer<DrawableManiaJudgement> judgements;
|
private readonly JudgementContainer<DrawableManiaJudgement> judgements;
|
||||||
@ -105,6 +104,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
Anchor = Anchor.TopCentre,
|
Anchor = Anchor.TopCentre,
|
||||||
Origin = Anchor.TopCentre,
|
Origin = Anchor.TopCentre,
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Child = HitObjectContainer
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
judgements = new JudgementContainer<DrawableManiaJudgement>
|
judgements = new JudgementContainer<DrawableManiaJudgement>
|
||||||
|
@ -108,7 +108,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
StartTime = Time.Current + 1000,
|
StartTime = Time.Current + 1000,
|
||||||
Position = new Vector2(239, 176),
|
Position = new Vector2(239, 176),
|
||||||
ControlPoints = new List<Vector2>
|
ControlPoints = new[]
|
||||||
{
|
{
|
||||||
Vector2.Zero,
|
Vector2.Zero,
|
||||||
new Vector2(154, 28),
|
new Vector2(154, 28),
|
||||||
@ -141,7 +141,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
StartTime = Time.Current + 1000,
|
StartTime = Time.Current + 1000,
|
||||||
Position = new Vector2(-(distance / 2), 0),
|
Position = new Vector2(-(distance / 2), 0),
|
||||||
ControlPoints = new List<Vector2>
|
ControlPoints = new[]
|
||||||
{
|
{
|
||||||
Vector2.Zero,
|
Vector2.Zero,
|
||||||
new Vector2(distance, 0),
|
new Vector2(distance, 0),
|
||||||
@ -161,7 +161,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
StartTime = Time.Current + 1000,
|
StartTime = Time.Current + 1000,
|
||||||
Position = new Vector2(-200, 0),
|
Position = new Vector2(-200, 0),
|
||||||
ControlPoints = new List<Vector2>
|
ControlPoints = new[]
|
||||||
{
|
{
|
||||||
Vector2.Zero,
|
Vector2.Zero,
|
||||||
new Vector2(200, 200),
|
new Vector2(200, 200),
|
||||||
@ -184,7 +184,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
CurveType = CurveType.Linear,
|
CurveType = CurveType.Linear,
|
||||||
StartTime = Time.Current + 1000,
|
StartTime = Time.Current + 1000,
|
||||||
Position = new Vector2(-200, 0),
|
Position = new Vector2(-200, 0),
|
||||||
ControlPoints = new List<Vector2>
|
ControlPoints = new[]
|
||||||
{
|
{
|
||||||
Vector2.Zero,
|
Vector2.Zero,
|
||||||
new Vector2(150, 75),
|
new Vector2(150, 75),
|
||||||
@ -210,7 +210,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
CurveType = CurveType.Bezier,
|
CurveType = CurveType.Bezier,
|
||||||
StartTime = Time.Current + 1000,
|
StartTime = Time.Current + 1000,
|
||||||
Position = new Vector2(-200, 0),
|
Position = new Vector2(-200, 0),
|
||||||
ControlPoints = new List<Vector2>
|
ControlPoints = new[]
|
||||||
{
|
{
|
||||||
Vector2.Zero,
|
Vector2.Zero,
|
||||||
new Vector2(150, 75),
|
new Vector2(150, 75),
|
||||||
@ -235,7 +235,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
CurveType = CurveType.Linear,
|
CurveType = CurveType.Linear,
|
||||||
StartTime = Time.Current + 1000,
|
StartTime = Time.Current + 1000,
|
||||||
Position = new Vector2(0, 0),
|
Position = new Vector2(0, 0),
|
||||||
ControlPoints = new List<Vector2>
|
ControlPoints = new[]
|
||||||
{
|
{
|
||||||
Vector2.Zero,
|
Vector2.Zero,
|
||||||
new Vector2(-200, 0),
|
new Vector2(-200, 0),
|
||||||
@ -265,7 +265,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,
|
CurveType = CurveType.Catmull,
|
||||||
ControlPoints = new List<Vector2>
|
ControlPoints = new[]
|
||||||
{
|
{
|
||||||
Vector2.Zero,
|
Vector2.Zero,
|
||||||
new Vector2(50, -50),
|
new Vector2(50, -50),
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.10.1" />
|
<PackageReference Include="NUnit" Version="3.11.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
||||||
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
|
@ -43,7 +43,10 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
Position = positionData?.Position ?? Vector2.Zero,
|
Position = positionData?.Position ?? Vector2.Zero,
|
||||||
NewCombo = comboData?.NewCombo ?? false,
|
NewCombo = comboData?.NewCombo ?? false,
|
||||||
ComboOffset = comboData?.ComboOffset ?? 0,
|
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 <v8 maps for the same time duration.
|
||||||
|
TickDistanceMultiplier = beatmap.BeatmapInfo.BeatmapVersion < 8 ? 1f / beatmap.ControlPointInfo.DifficultyPointAt(original.StartTime).SpeedMultiplier : 1
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else if (endTimeData != null)
|
else if (endTimeData != null)
|
||||||
|
@ -10,6 +10,8 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
{
|
{
|
||||||
public class OsuBeatmapProcessor : BeatmapProcessor
|
public class OsuBeatmapProcessor : BeatmapProcessor
|
||||||
{
|
{
|
||||||
|
private const int stack_distance = 3;
|
||||||
|
|
||||||
public OsuBeatmapProcessor(IBeatmap beatmap)
|
public OsuBeatmapProcessor(IBeatmap beatmap)
|
||||||
: base(beatmap)
|
: base(beatmap)
|
||||||
{
|
{
|
||||||
@ -18,17 +20,21 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
public override void PostProcess()
|
public override void PostProcess()
|
||||||
{
|
{
|
||||||
base.PostProcess();
|
base.PostProcess();
|
||||||
applyStacking((Beatmap<OsuHitObject>)Beatmap);
|
|
||||||
|
var osuBeatmap = (Beatmap<OsuHitObject>)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<OsuHitObject> beatmap)
|
private void applyStacking(Beatmap<OsuHitObject> 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
|
// Extend the end index to include objects they are stacked on
|
||||||
int extendedEndIndex = beatmap.HitObjects.Count - 1;
|
int extendedEndIndex = beatmap.HitObjects.Count - 1;
|
||||||
for (int i = beatmap.HitObjects.Count - 1; i >= 0; i--)
|
for (int i = beatmap.HitObjects.Count - 1; i >= 0; i--)
|
||||||
@ -167,5 +173,40 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void applyStackingOld(Beatmap<OsuHitObject> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
double sectionLength = section_length * timeRate;
|
double sectionLength = section_length * timeRate;
|
||||||
|
|
||||||
// The first object doesn't generate a strain, so we begin with an incremented section end
|
// 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)
|
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 hitWindowGreat = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate;
|
||||||
double preempt = (int)BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / 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)
|
// 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<Slider>().Sum(s => s.NestedHitObjects.Count - 1);
|
maxCombo += beatmap.HitObjects.OfType<Slider>().Sum(s => s.NestedHitObjects.Count - 1);
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
{
|
{
|
||||||
countHitCircles = Beatmap.HitObjects.Count(h => h is HitCircle);
|
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)
|
// 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<Slider>().Sum(s => s.NestedHitObjects.Count - 1);
|
beatmapMaxCombo += Beatmap.HitObjects.OfType<Slider>().Sum(s => s.NestedHitObjects.Count - 1);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
|
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.
|
// Sort OsuHitObjects by StartTime - they are not correctly ordered in some cases.
|
||||||
// This should probably happen before the objects reach the difficulty calculator.
|
// This should probably happen before the objects reach the difficulty calculator.
|
||||||
objects.Sort((a, b) => a.StartTime.CompareTo(b.StartTime));
|
difficultyObjects = createDifficultyObjectEnumerator(objects.OrderBy(h => h.StartTime).ToList(), timeRate);
|
||||||
difficultyObjects = createDifficultyObjectEnumerator(objects, timeRate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -21,15 +21,25 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
|
|||||||
public OsuHitObject BaseObject { get; }
|
public OsuHitObject BaseObject { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Normalized distance from the <see cref="OsuHitObject.StackedPosition"/> of the previous <see cref="OsuDifficultyHitObject"/>.
|
/// Normalized distance from the end position of the previous <see cref="OsuDifficultyHitObject"/> to the start position of this <see cref="OsuDifficultyHitObject"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double Distance { get; private set; }
|
public double JumpDistance { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Normalized distance between the start and end position of the previous <see cref="OsuDifficultyHitObject"/>.
|
||||||
|
/// </summary>
|
||||||
|
public double TravelDistance { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Milliseconds elapsed since the StartTime of the previous <see cref="OsuDifficultyHitObject"/>.
|
/// Milliseconds elapsed since the StartTime of the previous <see cref="OsuDifficultyHitObject"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double DeltaTime { get; private set; }
|
public double DeltaTime { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Milliseconds elapsed since the start time of the previous <see cref="OsuDifficultyHitObject"/>, with a minimum of 50ms.
|
||||||
|
/// </summary>
|
||||||
|
public double StrainTime { get; private set; }
|
||||||
|
|
||||||
private readonly OsuHitObject lastObject;
|
private readonly OsuHitObject lastObject;
|
||||||
private readonly double timeRate;
|
private readonly double timeRate;
|
||||||
|
|
||||||
@ -51,31 +61,35 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
|
|||||||
private void setDistances()
|
private void setDistances()
|
||||||
{
|
{
|
||||||
// We will scale distances by this factor, so we can assume a uniform CircleSize among beatmaps.
|
// 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)
|
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;
|
scalingFactor *= 1 + smallCircleBonus;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2 lastCursorPosition = lastObject.StackedPosition;
|
Vector2 lastCursorPosition = lastObject.StackedPosition;
|
||||||
float lastTravelDistance = 0;
|
|
||||||
|
|
||||||
var lastSlider = lastObject as Slider;
|
var lastSlider = lastObject as Slider;
|
||||||
if (lastSlider != null)
|
if (lastSlider != null)
|
||||||
{
|
{
|
||||||
computeSliderCursorPosition(lastSlider);
|
computeSliderCursorPosition(lastSlider);
|
||||||
lastCursorPosition = lastSlider.LazyEndPosition ?? lastCursorPosition;
|
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()
|
private void setTimingValues()
|
||||||
{
|
{
|
||||||
// Every timing inverval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure.
|
DeltaTime = (BaseObject.StartTime - lastObject.StartTime) / timeRate;
|
||||||
DeltaTime = Math.Max(50, (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)
|
private void computeSliderCursorPosition(Slider slider)
|
||||||
@ -87,8 +101,14 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
|
|||||||
float approxFollowCircleRadius = (float)(slider.Radius * 3);
|
float approxFollowCircleRadius = (float)(slider.Radius * 3);
|
||||||
var computeVertex = new Action<double>(t =>
|
var computeVertex = new Action<double>(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)
|
// ReSharper disable once PossibleInvalidOperationException (bugged in current r# version)
|
||||||
var diff = slider.StackedPositionAt(t) - slider.LazyEndPosition.Value;
|
var diff = slider.StackedPosition + slider.Curve.PositionAt(progress) - slider.LazyEndPosition.Value;
|
||||||
float dist = diff.Length;
|
float dist = diff.Length;
|
||||||
|
|
||||||
if (dist > approxFollowCircleRadius)
|
if (dist > approxFollowCircleRadius)
|
||||||
|
@ -14,6 +14,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
|||||||
protected override double SkillMultiplier => 26.25;
|
protected override double SkillMultiplier => 26.25;
|
||||||
protected override double StrainDecayBase => 0.15;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
|||||||
|
|
||||||
protected override double StrainValueOf(OsuDifficultyHitObject current)
|
protected override double StrainValueOf(OsuDifficultyHitObject current)
|
||||||
{
|
{
|
||||||
double distance = current.Distance;
|
double distance = current.TravelDistance + current.JumpDistance;
|
||||||
|
|
||||||
double speedValue;
|
double speedValue;
|
||||||
if (distance > single_spacing_threshold)
|
if (distance > single_spacing_threshold)
|
||||||
@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Skills
|
|||||||
else
|
else
|
||||||
speedValue = 0.95;
|
speedValue = 0.95;
|
||||||
|
|
||||||
return speedValue / current.DeltaTime;
|
return speedValue / current.StrainTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Osu.UI;
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Edit
|
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 CursorContainer CreateCursor() => null;
|
||||||
|
|
||||||
|
protected override Playfield CreatePlayfield() => new OsuPlayfield { Size = Vector2.One };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
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;
|
||||||
@ -31,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
new HitObjectCompositionTool<Spinner>()
|
new HitObjectCompositionTool<Spinner>()
|
||||||
};
|
};
|
||||||
|
|
||||||
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 HitObjectMask CreateMaskFor(DrawableHitObject hitObject)
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// 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.Collections.Generic;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
@ -33,8 +32,9 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
||||||
slider.NestedHitObjects.OfType<RepeatPoint>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
slider.NestedHitObjects.OfType<RepeatPoint>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
|
||||||
|
|
||||||
var newControlPoints = new List<Vector2>();
|
var newControlPoints = new Vector2[slider.ControlPoints.Length];
|
||||||
slider.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, -c.Y)));
|
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.ControlPoints = newControlPoints;
|
||||||
slider.Curve?.Calculate(); // Recalculate the slider curve
|
slider.Curve?.Calculate(); // Recalculate the slider curve
|
||||||
|
@ -37,6 +37,11 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
var osuObject = (OsuHitObject)drawable.HitObject;
|
var osuObject = (OsuHitObject)drawable.HitObject;
|
||||||
Vector2 origin = drawable.Position;
|
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);
|
Random objRand = new Random((int)osuObject.StartTime);
|
||||||
|
|
||||||
// Wiggle all objects during TimePreempt
|
// Wiggle all objects during TimePreempt
|
||||||
|
@ -8,26 +8,23 @@ using osu.Framework.Configuration;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Lines;
|
using osu.Framework.Graphics.Lines;
|
||||||
using osu.Framework.Graphics.Textures;
|
|
||||||
using OpenTK.Graphics.ES30;
|
using OpenTK.Graphics.ES30;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using osu.Framework.Graphics.Primitives;
|
using osu.Framework.Graphics.Primitives;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using SixLabors.ImageSharp;
|
|
||||||
using SixLabors.ImageSharp.PixelFormats;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||||
{
|
{
|
||||||
public class SliderBody : Container, ISliderProgress
|
public class SliderBody : Container, ISliderProgress
|
||||||
{
|
{
|
||||||
private readonly Path path;
|
private readonly SliderPath path;
|
||||||
private readonly BufferedContainer container;
|
private readonly BufferedContainer container;
|
||||||
|
|
||||||
public float PathWidth
|
public float PathWidth
|
||||||
{
|
{
|
||||||
get { return path.PathWidth; }
|
get => path.PathWidth;
|
||||||
set { path.PathWidth = value; }
|
set => path.PathWidth = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -43,48 +40,40 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
public double? SnakedStart { get; private set; }
|
public double? SnakedStart { get; private set; }
|
||||||
public double? SnakedEnd { get; private set; }
|
public double? SnakedEnd { get; private set; }
|
||||||
|
|
||||||
private Color4 accentColour = Color4.White;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used to colour the path.
|
/// Used to colour the path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Color4 AccentColour
|
public Color4 AccentColour
|
||||||
{
|
{
|
||||||
get { return accentColour; }
|
get => path.AccentColour;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (accentColour == value)
|
if (path.AccentColour == value)
|
||||||
return;
|
return;
|
||||||
accentColour = value;
|
path.AccentColour = value;
|
||||||
|
|
||||||
if (LoadState >= LoadState.Ready)
|
container.ForceRedraw();
|
||||||
reloadTexture();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color4 borderColour = Color4.White;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Used to colour the path border.
|
/// Used to colour the path border.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public new Color4 BorderColour
|
public new Color4 BorderColour
|
||||||
{
|
{
|
||||||
get { return borderColour; }
|
get => path.BorderColour;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (borderColour == value)
|
if (path.BorderColour == value)
|
||||||
return;
|
return;
|
||||||
borderColour = value;
|
path.BorderColour = value;
|
||||||
|
|
||||||
if (LoadState >= LoadState.Ready)
|
container.ForceRedraw();
|
||||||
reloadTexture();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Quad PathDrawQuad => container.ScreenSpaceDrawQuad;
|
public Quad PathDrawQuad => container.ScreenSpaceDrawQuad;
|
||||||
|
|
||||||
private int textureWidth => (int)PathWidth * 2;
|
|
||||||
|
|
||||||
private Vector2 topLeftOffset;
|
private Vector2 topLeftOffset;
|
||||||
|
|
||||||
private readonly Slider slider;
|
private readonly Slider slider;
|
||||||
@ -101,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
CacheDrawnFrameBuffer = true,
|
CacheDrawnFrameBuffer = true,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
path = new Path
|
path = new SliderPath
|
||||||
{
|
{
|
||||||
Blending = BlendingMode.None,
|
Blending = BlendingMode.None,
|
||||||
},
|
},
|
||||||
@ -134,46 +123,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
reloadTexture();
|
|
||||||
computeSize();
|
computeSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reloadTexture()
|
|
||||||
{
|
|
||||||
var texture = new Texture(textureWidth, 1);
|
|
||||||
|
|
||||||
//initialise background
|
|
||||||
var raw = new Image<Rgba32>(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;
|
|
||||||
|
|
||||||
container.ForceRedraw();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void computeSize()
|
private void computeSize()
|
||||||
{
|
{
|
||||||
// Generate the entire curve
|
// Generate the entire curve
|
||||||
@ -226,5 +178,53 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
|
|
||||||
SetRange(start, end);
|
SetRange(start, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class SliderPath : SmoothPath
|
||||||
|
{
|
||||||
|
private const float border_portion = 0.128f;
|
||||||
|
private const float gradient_portion = 1 - border_portion;
|
||||||
|
|
||||||
|
private const float opacity_at_centre = 0.3f;
|
||||||
|
private const float opacity_at_edge = 0.8f;
|
||||||
|
|
||||||
|
private Color4 borderColour = Color4.White;
|
||||||
|
|
||||||
|
public Color4 BorderColour
|
||||||
|
{
|
||||||
|
get => borderColour;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (borderColour == value)
|
||||||
|
return;
|
||||||
|
borderColour = value;
|
||||||
|
|
||||||
|
InvalidateTexture();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
|
|
||||||
public SliderCurve Curve { get; } = new SliderCurve();
|
public SliderCurve Curve { get; } = new SliderCurve();
|
||||||
|
|
||||||
public List<Vector2> ControlPoints
|
public Vector2[] ControlPoints
|
||||||
{
|
{
|
||||||
get { return Curve.ControlPoints; }
|
get { return Curve.ControlPoints; }
|
||||||
set { Curve.ControlPoints = value; }
|
set { Curve.ControlPoints = value; }
|
||||||
@ -92,8 +92,21 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public double SpanDuration => Duration / this.SpanCount();
|
public double SpanDuration => Duration / this.SpanCount();
|
||||||
|
|
||||||
public double Velocity;
|
/// <summary>
|
||||||
public double TickDistance;
|
/// Velocity of this <see cref="Slider"/>.
|
||||||
|
/// </summary>
|
||||||
|
public double Velocity { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Spacing between <see cref="SliderTick"/>s of this <see cref="Slider"/>.
|
||||||
|
/// </summary>
|
||||||
|
public double TickDistance { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An extra multiplier that affects the number of <see cref="SliderTick"/>s generated by this <see cref="Slider"/>.
|
||||||
|
/// An increase in this value increases <see cref="TickDistance"/>, which reduces the number of ticks generated.
|
||||||
|
/// </summary>
|
||||||
|
public double TickDistanceMultiplier = 1;
|
||||||
|
|
||||||
public HitCircle HeadCircle;
|
public HitCircle HeadCircle;
|
||||||
public SliderTailCircle TailCircle;
|
public SliderTailCircle TailCircle;
|
||||||
@ -108,7 +121,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier;
|
double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier;
|
||||||
|
|
||||||
Velocity = scoringDistance / timingPoint.BeatLength;
|
Velocity = scoringDistance / timingPoint.BeatLength;
|
||||||
TickDistance = scoringDistance / difficulty.SliderTickRate;
|
TickDistance = scoringDistance / difficulty.SliderTickRate * TickDistanceMultiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void CreateNestedHitObjects()
|
protected override void CreateNestedHitObjects()
|
||||||
|
@ -45,10 +45,10 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
|
|
||||||
public enum OsuAction
|
public enum OsuAction
|
||||||
{
|
{
|
||||||
[Description("Left Button")]
|
[Description("Left button")]
|
||||||
LeftButton,
|
LeftButton,
|
||||||
|
|
||||||
[Description("Right Button")]
|
[Description("Right button")]
|
||||||
RightButton
|
RightButton
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,29 +23,35 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
public static readonly Vector2 BASE_SIZE = new Vector2(512, 384);
|
public static readonly Vector2 BASE_SIZE = new Vector2(512, 384);
|
||||||
|
|
||||||
public OsuPlayfield()
|
public OsuPlayfield()
|
||||||
: base(BASE_SIZE.X)
|
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre;
|
Anchor = Anchor.Centre;
|
||||||
Origin = 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,
|
connectionLayer = new FollowPointRenderer
|
||||||
Depth = 2,
|
{
|
||||||
},
|
RelativeSizeAxes = Axes.Both,
|
||||||
judgementLayer = new JudgementContainer<DrawableOsuJudgement>
|
Depth = 2,
|
||||||
{
|
},
|
||||||
RelativeSizeAxes = Axes.Both,
|
judgementLayer = new JudgementContainer<DrawableOsuJudgement>
|
||||||
Depth = 1,
|
{
|
||||||
},
|
RelativeSizeAxes = Axes.Both,
|
||||||
approachCircles = new Container
|
Depth = 1,
|
||||||
{
|
},
|
||||||
RelativeSizeAxes = Axes.Both,
|
HitObjectContainer,
|
||||||
Depth = -1,
|
approachCircles = new Container
|
||||||
},
|
{
|
||||||
});
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Depth = -1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Add(DrawableHitObject h)
|
public override void Add(DrawableHitObject h)
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using OpenTK;
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Input.Handlers;
|
using osu.Game.Input.Handlers;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
@ -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();
|
protected override CursorContainer CreateCursor() => new GameplayCursor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
42
osu.Game.Rulesets.Osu/UI/PlayfieldAdjustmentContainer.cs
Normal file
42
osu.Game.Rulesets.Osu/UI/PlayfieldAdjustmentContainer.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// 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 OpenTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.UI
|
||||||
|
{
|
||||||
|
public class PlayfieldAdjustmentContainer : Container
|
||||||
|
{
|
||||||
|
protected override Container<Drawable> 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 }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="Container"/> which scales its content relative to a target width.
|
||||||
|
/// </summary>
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,9 +2,10 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.10.1" />
|
<PackageReference Include="NUnit" Version="3.11.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
||||||
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
|
@ -17,13 +17,13 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
|
|
||||||
public enum TaikoAction
|
public enum TaikoAction
|
||||||
{
|
{
|
||||||
[Description("Left (Rim)")]
|
[Description("Left (rim)")]
|
||||||
LeftRim,
|
LeftRim,
|
||||||
[Description("Left (Centre)")]
|
[Description("Left (centre)")]
|
||||||
LeftCentre,
|
LeftCentre,
|
||||||
[Description("Right (Centre)")]
|
[Description("Right (centre)")]
|
||||||
RightCentre,
|
RightCentre,
|
||||||
[Description("Right (Rim)")]
|
[Description("Right (rim)")]
|
||||||
RightRim
|
RightRim
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
22
osu.Game.Rulesets.Taiko/UI/PlayfieldAdjustmentContainer.cs
Normal file
22
osu.Game.Rulesets.Taiko/UI/PlayfieldAdjustmentContainer.cs
Normal 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.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -47,9 +47,6 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
private readonly Container<KiaiHitExplosion> kiaiExplosionContainer;
|
private readonly Container<KiaiHitExplosion> kiaiExplosionContainer;
|
||||||
private readonly JudgementContainer<DrawableTaikoJudgement> judgementContainer;
|
private readonly JudgementContainer<DrawableTaikoJudgement> judgementContainer;
|
||||||
|
|
||||||
protected override Container<Drawable> Content => content;
|
|
||||||
private readonly Container content;
|
|
||||||
|
|
||||||
private readonly Container topLevelHitContainer;
|
private readonly Container topLevelHitContainer;
|
||||||
|
|
||||||
private readonly Container barlineContainer;
|
private readonly Container barlineContainer;
|
||||||
@ -64,140 +61,147 @@ namespace osu.Game.Rulesets.Taiko.UI
|
|||||||
{
|
{
|
||||||
Direction.Value = ScrollingDirection.Left;
|
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",
|
backgroundContainer = new Container
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Masking = true,
|
|
||||||
EdgeEffect = new EdgeEffectParameters
|
|
||||||
{
|
{
|
||||||
Type = EdgeEffectType.Shadow,
|
Name = "Transparent playfield background",
|
||||||
Colour = Color4.Black.Opacity(0.2f),
|
RelativeSizeAxes = Axes.Both,
|
||||||
Radius = 5,
|
Masking = true,
|
||||||
},
|
EdgeEffect = new EdgeEffectParameters
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
|
||||||
background = new Box
|
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
Type = EdgeEffectType.Shadow,
|
||||||
Alpha = 0.6f
|
Colour = Color4.Black.Opacity(0.2f),
|
||||||
|
Radius = 5,
|
||||||
},
|
},
|
||||||
}
|
Children = new Drawable[]
|
||||||
},
|
|
||||||
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",
|
background = new Box
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Padding = new MarginPadding { Left = HIT_TARGET_OFFSET },
|
|
||||||
Masking = true,
|
|
||||||
Children = new Drawable[]
|
|
||||||
{
|
{
|
||||||
hitExplosionContainer = new Container<HitExplosion>
|
RelativeSizeAxes = Axes.Both,
|
||||||
{
|
Alpha = 0.6f
|
||||||
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<KiaiHitExplosion>
|
|
||||||
{
|
|
||||||
Name = "Kiai hit explosions",
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
FillMode = FillMode.Fit,
|
|
||||||
Margin = new MarginPadding { Left = HIT_TARGET_OFFSET },
|
|
||||||
Blending = BlendingMode.Additive
|
|
||||||
},
|
|
||||||
judgementContainer = new JudgementContainer<DrawableTaikoJudgement>
|
|
||||||
{
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
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<HitExplosion>
|
||||||
|
{
|
||||||
|
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<KiaiHitExplosion>
|
||||||
|
{
|
||||||
|
Name = "Kiai hit explosions",
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
FillMode = FillMode.Fit,
|
||||||
|
Margin = new MarginPadding { Left = HIT_TARGET_OFFSET },
|
||||||
|
Blending = BlendingMode.Additive
|
||||||
|
},
|
||||||
|
judgementContainer = new JudgementContainer<DrawableTaikoJudgement>
|
||||||
|
{
|
||||||
|
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]
|
[BackgroundDependencyLoader]
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// 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 osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
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.Taiko.Scoring;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osu.Game.Rulesets.Taiko.Replays;
|
using osu.Game.Rulesets.Taiko.Replays;
|
||||||
using OpenTK;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Game.Input.Handlers;
|
using osu.Game.Input.Handlers;
|
||||||
@ -74,27 +72,11 @@ 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 ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this);
|
||||||
|
|
||||||
public override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo);
|
public override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo);
|
||||||
|
|
||||||
protected override Playfield CreatePlayfield() => new TaikoPlayfield(Beatmap.ControlPointInfo)
|
protected override Playfield CreatePlayfield() => new TaikoPlayfield(Beatmap.ControlPointInfo);
|
||||||
{
|
|
||||||
Anchor = Anchor.CentreLeft,
|
|
||||||
Origin = Anchor.CentreLeft
|
|
||||||
};
|
|
||||||
|
|
||||||
protected override DrawableHitObject<TaikoHitObject> GetVisualRepresentation(TaikoHitObject h)
|
protected override DrawableHitObject<TaikoHitObject> GetVisualRepresentation(TaikoHitObject h)
|
||||||
{
|
{
|
||||||
|
@ -8,11 +8,13 @@ using OpenTK.Graphics;
|
|||||||
using osu.Game.Tests.Resources;
|
using osu.Game.Tests.Resources;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Beatmaps.Formats;
|
using osu.Game.Beatmaps.Formats;
|
||||||
using osu.Game.Beatmaps.Timing;
|
using osu.Game.Beatmaps.Timing;
|
||||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
using osu.Game.Rulesets.Catch.Beatmaps;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
@ -21,6 +23,25 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class LegacyBeatmapDecoderTest
|
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<Beatmap>(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]
|
[Test]
|
||||||
public void TestDecodeBeatmapGeneral()
|
public void TestDecodeBeatmapGeneral()
|
||||||
{
|
{
|
||||||
@ -165,7 +186,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestDecodeBeatmapColors()
|
public void TestDecodeBeatmapColours()
|
||||||
{
|
{
|
||||||
var decoder = new LegacySkinDecoder();
|
var decoder = new LegacySkinDecoder();
|
||||||
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||||
@ -181,6 +202,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
|||||||
new Color4(128, 255, 128, 255),
|
new Color4(128, 255, 128, 255),
|
||||||
new Color4(255, 187, 255, 255),
|
new Color4(255, 187, 255, 255),
|
||||||
new Color4(255, 177, 140, 255),
|
new Color4(255, 177, 140, 255),
|
||||||
|
new Color4(100, 100, 100, 100),
|
||||||
};
|
};
|
||||||
Assert.AreEqual(expectedColors.Length, comboColors.Count);
|
Assert.AreEqual(expectedColors.Length, comboColors.Count);
|
||||||
for (int i = 0; i < expectedColors.Length; i++)
|
for (int i = 0; i < expectedColors.Length; i++)
|
||||||
|
@ -101,6 +101,7 @@ Combo3 : 128,255,255
|
|||||||
Combo4 : 128,255,128
|
Combo4 : 128,255,128
|
||||||
Combo5 : 255,187,255
|
Combo5 : 255,187,255
|
||||||
Combo6 : 255,177,140
|
Combo6 : 255,177,140
|
||||||
|
Combo7 : 100,100,100,100
|
||||||
|
|
||||||
[HitObjects]
|
[HitObjects]
|
||||||
192,168,956,6,0,P|184:128|200:80,1,90,4|0,1:2|0:0,0:0:0:0:
|
192,168,956,6,0,P|184:128|200:80,1,90,4|0,1:2|0:0,0:0:0:0:
|
||||||
|
1
osu.Game.Tests/Resources/beatmap-version.osu
Normal file
1
osu.Game.Tests/Resources/beatmap-version.osu
Normal file
@ -0,0 +1 @@
|
|||||||
|
osu file format v6
|
@ -44,14 +44,12 @@ namespace osu.Game.Tests.Visual
|
|||||||
new Slider
|
new Slider
|
||||||
{
|
{
|
||||||
Position = new Vector2(128, 256),
|
Position = new Vector2(128, 256),
|
||||||
ControlPoints = new List<Vector2>
|
ControlPoints = new[]
|
||||||
{
|
{
|
||||||
Vector2.Zero,
|
Vector2.Zero,
|
||||||
new Vector2(216, 0),
|
new Vector2(216, 0),
|
||||||
},
|
},
|
||||||
Distance = 400,
|
Distance = 216,
|
||||||
Velocity = 1,
|
|
||||||
TickDistance = 100,
|
|
||||||
Scale = 0.5f,
|
Scale = 0.5f,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -121,14 +121,21 @@ namespace osu.Game.Tests.Visual
|
|||||||
Direction = direction;
|
Direction = direction;
|
||||||
|
|
||||||
Padding = new MarginPadding(2);
|
Padding = new MarginPadding(2);
|
||||||
Content.Masking = true;
|
|
||||||
|
|
||||||
AddInternal(new Box
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
new Box
|
||||||
Alpha = 0.5f,
|
{
|
||||||
Depth = float.MaxValue
|
RelativeSizeAxes = Axes.Both,
|
||||||
});
|
Alpha = 0.5f,
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
|
Child = HitObjectContainer
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,9 +2,11 @@
|
|||||||
<Import Project="..\osu.TestProject.props" />
|
<Import Project="..\osu.TestProject.props" />
|
||||||
<ItemGroup Label="Package References">
|
<ItemGroup Label="Package References">
|
||||||
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
|
<PackageReference Include="DeepEqual" Version="1.6.0" />
|
||||||
<PackageReference Include="NUnit" Version="3.10.1" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
|
||||||
|
<PackageReference Include="NUnit" Version="3.11.0" />
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
|
||||||
|
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Project">
|
<PropertyGroup Label="Project">
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<!-- Contains required properties for osu!framework projects. -->
|
<!-- Contains required properties for osu!framework projects. -->
|
||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup Label="C#">
|
<PropertyGroup Label="C#">
|
||||||
<LangVersion>7</LangVersion>
|
<LangVersion>7.2</LangVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ApplicationManifest>..\app.manifest</ApplicationManifest>
|
<ApplicationManifest>..\app.manifest</ApplicationManifest>
|
||||||
|
@ -48,7 +48,7 @@ namespace osu.Game.Beatmaps
|
|||||||
[JsonConverter(typeof(TypedListConverter<HitObject>))]
|
[JsonConverter(typeof(TypedListConverter<HitObject>))]
|
||||||
public List<T> HitObjects = new List<T>();
|
public List<T> HitObjects = new List<T>();
|
||||||
|
|
||||||
IEnumerable<HitObject> IBeatmap.HitObjects => HitObjects;
|
IReadOnlyList<HitObject> IBeatmap.HitObjects => HitObjects;
|
||||||
|
|
||||||
public virtual IEnumerable<BeatmapStatistic> GetStatistics() => Enumerable.Empty<BeatmapStatistic>();
|
public virtual IEnumerable<BeatmapStatistic> GetStatistics() => Enumerable.Empty<BeatmapStatistic>();
|
||||||
|
|
||||||
|
@ -55,39 +55,40 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
beatmap.BeatmapInfo = original.BeatmapInfo;
|
beatmap.BeatmapInfo = original.BeatmapInfo;
|
||||||
beatmap.ControlPointInfo = original.ControlPointInfo;
|
beatmap.ControlPointInfo = original.ControlPointInfo;
|
||||||
beatmap.HitObjects = original.HitObjects.SelectMany(h => convert(h, original)).ToList();
|
beatmap.HitObjects = convertHitObjects(original.HitObjects, original);
|
||||||
beatmap.Breaks = original.Breaks;
|
beatmap.Breaks = original.Breaks;
|
||||||
|
|
||||||
return beatmap;
|
return beatmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private List<T> convertHitObjects(IReadOnlyList<HitObject> hitObjects, IBeatmap beatmap)
|
||||||
/// Converts a hit object.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="original">The hit object to convert.</param>
|
|
||||||
/// <param name="beatmap">The un-converted Beatmap.</param>
|
|
||||||
/// <returns>The converted hit object.</returns>
|
|
||||||
private IEnumerable<T> convert(HitObject original, IBeatmap beatmap)
|
|
||||||
{
|
{
|
||||||
// Check if the hitobject is already the converted type
|
var result = new List<T>(hitObjects.Count);
|
||||||
T tObject = original as T;
|
|
||||||
if (tObject != null)
|
|
||||||
{
|
|
||||||
yield return tObject;
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var converted = ConvertHitObject(original, beatmap).ToList();
|
foreach (var obj in hitObjects)
|
||||||
ObjectConverted?.Invoke(original, converted);
|
|
||||||
|
|
||||||
// Convert the hit object
|
|
||||||
foreach (var obj in converted)
|
|
||||||
{
|
{
|
||||||
if (obj == null)
|
if (obj is T tObj)
|
||||||
|
{
|
||||||
|
result.Add(tObj);
|
||||||
continue;
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -19,7 +19,6 @@ namespace osu.Game.Beatmaps
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public int ID { get; set; }
|
public int ID { get; set; }
|
||||||
|
|
||||||
//TODO: should be in database
|
|
||||||
public int BeatmapVersion;
|
public int BeatmapVersion;
|
||||||
|
|
||||||
private int? onlineBeatmapID;
|
private int? onlineBeatmapID;
|
||||||
|
@ -148,11 +148,12 @@ namespace osu.Game.Beatmaps
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="beatmapSetInfo">The <see cref="BeatmapSetInfo"/> to be downloaded.</param>
|
/// <param name="beatmapSetInfo">The <see cref="BeatmapSetInfo"/> to be downloaded.</param>
|
||||||
/// <param name="noVideo">Whether the beatmap should be downloaded without video. Defaults to false.</param>
|
/// <param name="noVideo">Whether the beatmap should be downloaded without video. Defaults to false.</param>
|
||||||
public void Download(BeatmapSetInfo beatmapSetInfo, bool noVideo = false)
|
/// <returns>Downloading can happen</returns>
|
||||||
|
public bool Download(BeatmapSetInfo beatmapSetInfo, bool noVideo = false)
|
||||||
{
|
{
|
||||||
var existing = GetExistingDownload(beatmapSetInfo);
|
var existing = GetExistingDownload(beatmapSetInfo);
|
||||||
|
|
||||||
if (existing != null || api == null) return;
|
if (existing != null || api == null) return false;
|
||||||
|
|
||||||
if (!api.LocalUser.Value.IsSupporter)
|
if (!api.LocalUser.Value.IsSupporter)
|
||||||
{
|
{
|
||||||
@ -161,7 +162,7 @@ namespace osu.Game.Beatmaps
|
|||||||
Icon = FontAwesome.fa_superpowers,
|
Icon = FontAwesome.fa_superpowers,
|
||||||
Text = "You gotta be an osu!supporter to download for now 'yo"
|
Text = "You gotta be an osu!supporter to download for now 'yo"
|
||||||
});
|
});
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var downloadNotification = new DownloadNotification
|
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.
|
// don't run in the main api queue as this is a long-running task.
|
||||||
Task.Factory.StartNew(() => request.Perform(api), TaskCreationOptions.LongRunning);
|
Task.Factory.StartNew(() => request.Perform(api), TaskCreationOptions.LongRunning);
|
||||||
BeatmapDownloadBegan?.Invoke(request);
|
BeatmapDownloadBegan?.Invoke(request);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PresentCompletedImport(IEnumerable<BeatmapSetInfo> imported)
|
protected override void PresentCompletedImport(IEnumerable<BeatmapSetInfo> imported)
|
||||||
|
@ -55,14 +55,14 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="time">The time to find the sound control point at.</param>
|
/// <param name="time">The time to find the sound control point at.</param>
|
||||||
/// <returns>The sound control point.</returns>
|
/// <returns>The sound control point.</returns>
|
||||||
public SampleControlPoint SamplePointAt(double time) => binarySearch(SamplePoints, time, SamplePoints.FirstOrDefault());
|
public SampleControlPoint SamplePointAt(double time) => binarySearch(SamplePoints, time, SamplePoints.Count > 0 ? SamplePoints[0] : null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finds the timing control point that is active at <paramref name="time"/>.
|
/// Finds the timing control point that is active at <paramref name="time"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="time">The time to find the timing control point at.</param>
|
/// <param name="time">The time to find the timing control point at.</param>
|
||||||
/// <returns>The timing control point.</returns>
|
/// <returns>The timing control point.</returns>
|
||||||
public TimingControlPoint TimingPointAt(double time) => binarySearch(TimingPoints, time, TimingPoints.FirstOrDefault());
|
public TimingControlPoint TimingPointAt(double time) => binarySearch(TimingPoints, time, TimingPoints.Count > 0 ? TimingPoints[0] : null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finds the maximum BPM represented by any timing control point.
|
/// Finds the maximum BPM represented by any timing control point.
|
||||||
@ -104,17 +104,26 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
if (time < list[0].Time)
|
if (time < list[0].Time)
|
||||||
return prePoint ?? new T();
|
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)
|
int l = 0;
|
||||||
if (index >= 0)
|
int r = list.Count - 2;
|
||||||
return list[index];
|
|
||||||
|
|
||||||
index = ~index;
|
while (l <= r)
|
||||||
|
{
|
||||||
|
int pivot = l + ((r - l) >> 1);
|
||||||
|
|
||||||
// BinarySearch will return the index of the first element _greater_ than the search
|
if (list[pivot].Time < time)
|
||||||
// This is the inactive point - the active point is the one before it (index - 1)
|
l = pivot + 1;
|
||||||
return list[index - 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];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Beatmaps.ControlPoints
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The beat length at this control point.
|
/// The beat length at this control point.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double BeatLength
|
public virtual double BeatLength
|
||||||
{
|
{
|
||||||
get => beatLength;
|
get => beatLength;
|
||||||
set => beatLength = MathHelper.Clamp(value, 6, 60000);
|
set => beatLength = MathHelper.Clamp(value, 6, 60000);
|
||||||
|
@ -71,9 +71,11 @@ namespace osu.Game.Beatmaps.Drawables
|
|||||||
if (DownloadState.Value > DownloadStatus.NotDownloaded)
|
if (DownloadState.Value > DownloadStatus.NotDownloaded)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
beatmaps.Download(set, noVideo);
|
if (beatmaps.Download(set, noVideo))
|
||||||
|
{
|
||||||
DownloadState.Value = DownloadStatus.Downloading;
|
// Only change state if download can happen
|
||||||
|
DownloadState.Value = DownloadStatus.Downloading;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setAdded(BeatmapSetInfo s)
|
private void setAdded(BeatmapSetInfo s)
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.IO.File;
|
||||||
using osu.Game.Beatmaps.Timing;
|
using osu.Game.Beatmaps.Timing;
|
||||||
using osu.Game.Rulesets.Objects.Legacy;
|
using osu.Game.Rulesets.Objects.Legacy;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
@ -58,7 +59,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
hitObject.ApplyDefaults(this.beatmap.ControlPointInfo, this.beatmap.BeatmapInfo.BaseDifficulty);
|
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)
|
protected override void ParseLine(Beatmap beatmap, Section section, string line)
|
||||||
{
|
{
|
||||||
@ -100,7 +101,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
switch (pair.Key)
|
switch (pair.Key)
|
||||||
{
|
{
|
||||||
case @"AudioFilename":
|
case @"AudioFilename":
|
||||||
metadata.AudioFile = pair.Value;
|
metadata.AudioFile = FileSafety.PathStandardise(pair.Value);
|
||||||
break;
|
break;
|
||||||
case @"AudioLeadIn":
|
case @"AudioLeadIn":
|
||||||
beatmap.BeatmapInfo.AudioLeadIn = int.Parse(pair.Value);
|
beatmap.BeatmapInfo.AudioLeadIn = int.Parse(pair.Value);
|
||||||
@ -256,7 +257,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
{
|
{
|
||||||
case EventType.Background:
|
case EventType.Background:
|
||||||
string filename = split[2].Trim('"');
|
string filename = split[2].Trim('"');
|
||||||
beatmap.BeatmapInfo.Metadata.BackgroundFile = filename;
|
beatmap.BeatmapInfo.Metadata.BackgroundFile = FileSafety.PathStandardise(filename);
|
||||||
break;
|
break;
|
||||||
case EventType.Break:
|
case EventType.Break:
|
||||||
var breakEvent = new BreakPeriod
|
var breakEvent = new BreakPeriod
|
||||||
@ -318,12 +319,12 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
|
|
||||||
if (timingChange)
|
if (timingChange)
|
||||||
{
|
{
|
||||||
handleTimingControlPoint(new TimingControlPoint
|
var controlPoint = CreateTimingControlPoint();
|
||||||
{
|
controlPoint.Time = time;
|
||||||
Time = time,
|
controlPoint.BeatLength = beatLength;
|
||||||
BeatLength = beatLength,
|
controlPoint.TimeSignature = timeSignature;
|
||||||
TimeSignature = timeSignature
|
|
||||||
});
|
handleTimingControlPoint(controlPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleDifficultyControlPoint(new DifficultyControlPoint
|
handleDifficultyControlPoint(new DifficultyControlPoint
|
||||||
@ -418,6 +419,8 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
|
|
||||||
private double getOffsetTime(double time) => time + (ApplyOffsets ? offset : 0);
|
private double getOffsetTime(double time) => time + (ApplyOffsets ? offset : 0);
|
||||||
|
|
||||||
|
protected virtual TimingControlPoint CreateTimingControlPoint() => new TimingControlPoint();
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
internal enum EffectFlags
|
internal enum EffectFlags
|
||||||
{
|
{
|
||||||
|
@ -69,7 +69,7 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
|
|
||||||
protected string StripComments(string line)
|
protected string StripComments(string line)
|
||||||
{
|
{
|
||||||
var index = line.IndexOf("//", StringComparison.Ordinal);
|
var index = line.AsSpan().IndexOf("//".AsSpan());
|
||||||
if (index > 0)
|
if (index > 0)
|
||||||
return line.Substring(0, index);
|
return line.Substring(0, index);
|
||||||
return line;
|
return line;
|
||||||
@ -85,13 +85,19 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
|
|
||||||
string[] split = pair.Value.Split(',');
|
string[] split = pair.Value.Split(',');
|
||||||
|
|
||||||
if (split.Length != 3)
|
if (split.Length != 3 && split.Length != 4)
|
||||||
throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {pair.Value}");
|
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");
|
throw new InvalidOperationException(@"Color must be specified with 8-bit integer components");
|
||||||
|
}
|
||||||
Color4 colour = new Color4(r, g, b, 255);
|
|
||||||
|
|
||||||
if (isCombo)
|
if (isCombo)
|
||||||
{
|
{
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
// 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.Game.Beatmaps.ControlPoints;
|
||||||
|
|
||||||
|
namespace osu.Game.Beatmaps.Formats
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="LegacyBeatmapDecoder"/> built for difficulty calculation of legacy <see cref="Beatmap"/>s
|
||||||
|
/// <remarks>
|
||||||
|
/// To use this, the decoder must be registered by the application through <see cref="LegacyDifficultyCalculatorBeatmapDecoder.Register"/>.
|
||||||
|
/// Doing so will override any existing <see cref="Beatmap"/> decoders.
|
||||||
|
/// </remarks>
|
||||||
|
/// </summary>
|
||||||
|
public class LegacyDifficultyCalculatorBeatmapDecoder : LegacyBeatmapDecoder
|
||||||
|
{
|
||||||
|
public LegacyDifficultyCalculatorBeatmapDecoder(int version = LATEST_VERSION)
|
||||||
|
: base(version)
|
||||||
|
{
|
||||||
|
ApplyOffsets = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public new static void Register()
|
||||||
|
{
|
||||||
|
AddDecoder<Beatmap>(@"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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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('"'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ namespace osu.Game.Beatmaps
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The hitobjects contained by this beatmap.
|
/// The hitobjects contained by this beatmap.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IEnumerable<HitObject> HitObjects { get; }
|
IReadOnlyList<HitObject> HitObjects { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns statistics for the <see cref="HitObjects"/> contained in this beatmap.
|
/// Returns statistics for the <see cref="HitObjects"/> contained in this beatmap.
|
||||||
|
@ -41,8 +41,13 @@ namespace osu.Game.Beatmaps
|
|||||||
beatmap = new RecyclableLazy<IBeatmap>(() =>
|
beatmap = new RecyclableLazy<IBeatmap>(() =>
|
||||||
{
|
{
|
||||||
var b = GetBeatmap() ?? new Beatmap();
|
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;
|
b.BeatmapInfo = BeatmapInfo;
|
||||||
|
|
||||||
return b;
|
return b;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -404,7 +404,7 @@ namespace osu.Game.Database
|
|||||||
using (Stream s = reader.GetStream(file))
|
using (Stream s = reader.GetStream(file))
|
||||||
fileInfos.Add(new TFileModel
|
fileInfos.Add(new TFileModel
|
||||||
{
|
{
|
||||||
Filename = FileSafety.PathSanitise(file),
|
Filename = FileSafety.PathStandardise(file),
|
||||||
FileInfo = files.Add(s)
|
FileInfo = files.Add(s)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
{
|
{
|
||||||
Masking = true,
|
Masking = true,
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Child = path = new Path { RelativeSizeAxes = Axes.Both, PathWidth = 1 }
|
Child = path = new SmoothPath { RelativeSizeAxes = Axes.Both, PathWidth = 1 }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
if (accentColour == default(Color4))
|
if (accentColour == default)
|
||||||
accentColour = colours.PinkDarker;
|
accentColour = colours.PinkDarker;
|
||||||
updateAccentColour();
|
updateAccentColour();
|
||||||
}
|
}
|
||||||
@ -51,6 +51,8 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
#region OsuDropdownMenu
|
#region OsuDropdownMenu
|
||||||
protected class OsuDropdownMenu : DropdownMenu, IHasAccentColour
|
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
|
// todo: this uses the same styling as OsuMenu. hopefully we can just use OsuMenu in the future with some refactoring
|
||||||
public OsuDropdownMenu()
|
public OsuDropdownMenu()
|
||||||
{
|
{
|
||||||
|
@ -53,7 +53,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
if (accentColour == default(Color4))
|
if (accentColour == default)
|
||||||
AccentColour = colours.Blue;
|
AccentColour = colours.Blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +142,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
if (accentColour == default(Color4))
|
if (accentColour == default)
|
||||||
AccentColour = colours.Blue;
|
AccentColour = colours.Blue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void ResetCount()
|
public virtual void ResetCount()
|
||||||
{
|
{
|
||||||
SetCountWithoutRolling(default(T));
|
SetCountWithoutRolling(default);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -71,17 +71,17 @@ namespace osu.Game.Input.Bindings
|
|||||||
ToggleSettings,
|
ToggleSettings,
|
||||||
[Description("Toggle osu!direct")]
|
[Description("Toggle osu!direct")]
|
||||||
ToggleDirect,
|
ToggleDirect,
|
||||||
[Description("Increase Volume")]
|
[Description("Increase volume")]
|
||||||
IncreaseVolume,
|
IncreaseVolume,
|
||||||
[Description("Decrease Volume")]
|
[Description("Decrease volume")]
|
||||||
DecreaseVolume,
|
DecreaseVolume,
|
||||||
[Description("Toggle mute")]
|
[Description("Toggle mute")]
|
||||||
ToggleMute,
|
ToggleMute,
|
||||||
|
|
||||||
// In-Game Keybindings
|
// In-Game Keybindings
|
||||||
[Description("Skip Cutscene")]
|
[Description("Skip cutscene")]
|
||||||
SkipCutscene,
|
SkipCutscene,
|
||||||
[Description("Quick Retry (Hold)")]
|
[Description("Quick retry (hold)")]
|
||||||
QuickRetry,
|
QuickRetry,
|
||||||
|
|
||||||
[Description("Take screenshot")]
|
[Description("Take screenshot")]
|
||||||
|
380
osu.Game/Migrations/20181007180454_StandardizePaths.Designer.cs
generated
Normal file
380
osu.Game/Migrations/20181007180454_StandardizePaths.Designer.cs
generated
Normal file
@ -0,0 +1,380 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
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<int>("ID")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<float>("ApproachRate");
|
||||||
|
|
||||||
|
b.Property<float>("CircleSize");
|
||||||
|
|
||||||
|
b.Property<float>("DrainRate");
|
||||||
|
|
||||||
|
b.Property<float>("OverallDifficulty");
|
||||||
|
|
||||||
|
b.Property<double>("SliderMultiplier");
|
||||||
|
|
||||||
|
b.Property<double>("SliderTickRate");
|
||||||
|
|
||||||
|
b.HasKey("ID");
|
||||||
|
|
||||||
|
b.ToTable("BeatmapDifficulty");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("ID")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("AudioLeadIn");
|
||||||
|
|
||||||
|
b.Property<int>("BaseDifficultyID");
|
||||||
|
|
||||||
|
b.Property<int>("BeatDivisor");
|
||||||
|
|
||||||
|
b.Property<int>("BeatmapSetInfoID");
|
||||||
|
|
||||||
|
b.Property<bool>("Countdown");
|
||||||
|
|
||||||
|
b.Property<double>("DistanceSpacing");
|
||||||
|
|
||||||
|
b.Property<int>("GridSize");
|
||||||
|
|
||||||
|
b.Property<string>("Hash");
|
||||||
|
|
||||||
|
b.Property<bool>("Hidden");
|
||||||
|
|
||||||
|
b.Property<bool>("LetterboxInBreaks");
|
||||||
|
|
||||||
|
b.Property<string>("MD5Hash");
|
||||||
|
|
||||||
|
b.Property<int?>("MetadataID");
|
||||||
|
|
||||||
|
b.Property<int?>("OnlineBeatmapID");
|
||||||
|
|
||||||
|
b.Property<string>("Path");
|
||||||
|
|
||||||
|
b.Property<int>("RulesetID");
|
||||||
|
|
||||||
|
b.Property<bool>("SpecialStyle");
|
||||||
|
|
||||||
|
b.Property<float>("StackLeniency");
|
||||||
|
|
||||||
|
b.Property<double>("StarDifficulty");
|
||||||
|
|
||||||
|
b.Property<int>("Status");
|
||||||
|
|
||||||
|
b.Property<string>("StoredBookmarks");
|
||||||
|
|
||||||
|
b.Property<double>("TimelineZoom");
|
||||||
|
|
||||||
|
b.Property<string>("Version");
|
||||||
|
|
||||||
|
b.Property<bool>("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<int>("ID")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("Artist");
|
||||||
|
|
||||||
|
b.Property<string>("ArtistUnicode");
|
||||||
|
|
||||||
|
b.Property<string>("AudioFile");
|
||||||
|
|
||||||
|
b.Property<string>("AuthorString")
|
||||||
|
.HasColumnName("Author");
|
||||||
|
|
||||||
|
b.Property<string>("BackgroundFile");
|
||||||
|
|
||||||
|
b.Property<int>("PreviewTime");
|
||||||
|
|
||||||
|
b.Property<string>("Source");
|
||||||
|
|
||||||
|
b.Property<string>("Tags");
|
||||||
|
|
||||||
|
b.Property<string>("Title");
|
||||||
|
|
||||||
|
b.Property<string>("TitleUnicode");
|
||||||
|
|
||||||
|
b.HasKey("ID");
|
||||||
|
|
||||||
|
b.ToTable("BeatmapMetadata");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("ID")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("BeatmapSetInfoID");
|
||||||
|
|
||||||
|
b.Property<int>("FileInfoID");
|
||||||
|
|
||||||
|
b.Property<string>("Filename")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasKey("ID");
|
||||||
|
|
||||||
|
b.HasIndex("BeatmapSetInfoID");
|
||||||
|
|
||||||
|
b.HasIndex("FileInfoID");
|
||||||
|
|
||||||
|
b.ToTable("BeatmapSetFileInfo");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("ID")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<bool>("DeletePending");
|
||||||
|
|
||||||
|
b.Property<string>("Hash");
|
||||||
|
|
||||||
|
b.Property<int?>("MetadataID");
|
||||||
|
|
||||||
|
b.Property<int?>("OnlineBeatmapSetID");
|
||||||
|
|
||||||
|
b.Property<bool>("Protected");
|
||||||
|
|
||||||
|
b.Property<int>("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<int>("ID")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("IntKey")
|
||||||
|
.HasColumnName("Key");
|
||||||
|
|
||||||
|
b.Property<int?>("RulesetID");
|
||||||
|
|
||||||
|
b.Property<string>("StringValue")
|
||||||
|
.HasColumnName("Value");
|
||||||
|
|
||||||
|
b.Property<int?>("Variant");
|
||||||
|
|
||||||
|
b.HasKey("ID");
|
||||||
|
|
||||||
|
b.HasIndex("RulesetID", "Variant");
|
||||||
|
|
||||||
|
b.ToTable("Settings");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("ID")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("IntAction")
|
||||||
|
.HasColumnName("Action");
|
||||||
|
|
||||||
|
b.Property<string>("KeysString")
|
||||||
|
.HasColumnName("Keys");
|
||||||
|
|
||||||
|
b.Property<int?>("RulesetID");
|
||||||
|
|
||||||
|
b.Property<int?>("Variant");
|
||||||
|
|
||||||
|
b.HasKey("ID");
|
||||||
|
|
||||||
|
b.HasIndex("IntAction");
|
||||||
|
|
||||||
|
b.HasIndex("RulesetID", "Variant");
|
||||||
|
|
||||||
|
b.ToTable("KeyBinding");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("ID")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("Hash");
|
||||||
|
|
||||||
|
b.Property<int>("ReferenceCount");
|
||||||
|
|
||||||
|
b.HasKey("ID");
|
||||||
|
|
||||||
|
b.HasIndex("Hash")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.HasIndex("ReferenceCount");
|
||||||
|
|
||||||
|
b.ToTable("FileInfo");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
|
||||||
|
{
|
||||||
|
b.Property<int?>("ID")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<bool>("Available");
|
||||||
|
|
||||||
|
b.Property<string>("InstantiationInfo");
|
||||||
|
|
||||||
|
b.Property<string>("Name");
|
||||||
|
|
||||||
|
b.Property<string>("ShortName");
|
||||||
|
|
||||||
|
b.HasKey("ID");
|
||||||
|
|
||||||
|
b.HasIndex("Available");
|
||||||
|
|
||||||
|
b.HasIndex("ShortName")
|
||||||
|
.IsUnique();
|
||||||
|
|
||||||
|
b.ToTable("RulesetInfo");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("ID")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<int>("FileInfoID");
|
||||||
|
|
||||||
|
b.Property<string>("Filename")
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Property<int>("SkinInfoID");
|
||||||
|
|
||||||
|
b.HasKey("ID");
|
||||||
|
|
||||||
|
b.HasIndex("FileInfoID");
|
||||||
|
|
||||||
|
b.HasIndex("SkinInfoID");
|
||||||
|
|
||||||
|
b.ToTable("SkinFileInfo");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("ID")
|
||||||
|
.ValueGeneratedOnAdd();
|
||||||
|
|
||||||
|
b.Property<string>("Creator");
|
||||||
|
|
||||||
|
b.Property<bool>("DeletePending");
|
||||||
|
|
||||||
|
b.Property<string>("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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
osu.Game/Migrations/20181007180454_StandardizePaths.cs
Normal file
26
osu.Game/Migrations/20181007180454_StandardizePaths.cs
Normal file
@ -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)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -14,7 +14,7 @@ namespace osu.Game.Migrations
|
|||||||
{
|
{
|
||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder
|
modelBuilder
|
||||||
.HasAnnotation("ProductVersion", "2.1.2-rtm-30932");
|
.HasAnnotation("ProductVersion", "2.1.3-rtm-32065");
|
||||||
|
|
||||||
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
|
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
|
||||||
{
|
{
|
||||||
|
@ -1,16 +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.Collections.Generic;
|
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Direct;
|
using osu.Game.Overlays.Direct;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
|
|
||||||
namespace osu.Game.Online.API.Requests
|
namespace osu.Game.Online.API.Requests
|
||||||
{
|
{
|
||||||
public class SearchBeatmapSetsRequest : APIRequest<IEnumerable<APIBeatmapSet>>
|
public class SearchBeatmapSetsRequest : APIRequest<SearchBeatmapSetsResponse>
|
||||||
{
|
{
|
||||||
private readonly string query;
|
private readonly string query;
|
||||||
private readonly RulesetInfo ruleset;
|
private readonly RulesetInfo ruleset;
|
||||||
@ -35,6 +33,7 @@ namespace osu.Game.Online.API.Requests
|
|||||||
public enum BeatmapSearchCategory
|
public enum BeatmapSearchCategory
|
||||||
{
|
{
|
||||||
Any = 7,
|
Any = 7,
|
||||||
|
|
||||||
[Description("Ranked & Approved")]
|
[Description("Ranked & Approved")]
|
||||||
RankedApproved = 0,
|
RankedApproved = 0,
|
||||||
Approved = 1,
|
Approved = 1,
|
||||||
@ -43,6 +42,7 @@ namespace osu.Game.Online.API.Requests
|
|||||||
Qualified = 3,
|
Qualified = 3,
|
||||||
Pending = 4,
|
Pending = 4,
|
||||||
Graveyard = 5,
|
Graveyard = 5,
|
||||||
|
|
||||||
[Description("My Maps")]
|
[Description("My Maps")]
|
||||||
MyMaps = 6,
|
MyMaps = 6,
|
||||||
}
|
}
|
||||||
|
20
osu.Game/Online/API/Requests/SearchBeatmapSetsResponse.cs
Normal file
20
osu.Game/Online/API/Requests/SearchBeatmapSetsResponse.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
|
||||||
|
namespace osu.Game.Online.API.Requests
|
||||||
|
{
|
||||||
|
public class SearchBeatmapSetsResponse
|
||||||
|
{
|
||||||
|
public IEnumerable<APIBeatmapSet> BeatmapSets;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A collection of parameters which should be passed to the search endpoint to fetch the next page.
|
||||||
|
/// </summary>
|
||||||
|
[JsonProperty("cursor")]
|
||||||
|
public dynamic CursorJson;
|
||||||
|
}
|
||||||
|
}
|
@ -291,7 +291,7 @@ namespace osu.Game.Overlays
|
|||||||
messageRequest?.Cancel();
|
messageRequest?.Cancel();
|
||||||
|
|
||||||
ListChannelsRequest req = new ListChannelsRequest();
|
ListChannelsRequest req = new ListChannelsRequest();
|
||||||
req.Success += delegate (List<Channel> channels)
|
req.Success += delegate(List<Channel> channels)
|
||||||
{
|
{
|
||||||
AvailableChannels = channels;
|
AvailableChannels = channels;
|
||||||
|
|
||||||
@ -323,10 +323,7 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
protected Channel CurrentChannel
|
protected Channel CurrentChannel
|
||||||
{
|
{
|
||||||
get
|
get { return currentChannel; }
|
||||||
{
|
|
||||||
return currentChannel;
|
|
||||||
}
|
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
@ -445,13 +442,7 @@ namespace osu.Game.Overlays
|
|||||||
if (updates?.Presence != null)
|
if (updates?.Presence != null)
|
||||||
{
|
{
|
||||||
foreach (var channel in updates.Presence)
|
foreach (var channel in updates.Presence)
|
||||||
{
|
addChannel(AvailableChannels.Find(c => c.Id == channel.Id));
|
||||||
if (careChannels.Find(c => c.Id == channel.Id) == null)
|
|
||||||
{
|
|
||||||
channel.Joined.Value = true;
|
|
||||||
addChannel(channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var group in updates.Messages.GroupBy(m => m.ChannelId))
|
foreach (var group in updates.Messages.GroupBy(m => m.ChannelId))
|
||||||
careChannels.Find(c => c.Id == group.Key)?.AddNewMessages(group.ToArray());
|
careChannels.Find(c => c.Id == group.Key)?.AddNewMessages(group.ToArray());
|
||||||
@ -462,10 +453,7 @@ namespace osu.Game.Overlays
|
|||||||
fetchReq = null;
|
fetchReq = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchReq.Failure += delegate
|
fetchReq.Failure += delegate { fetchReq = null; };
|
||||||
{
|
|
||||||
fetchReq = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
api.Queue(fetchReq);
|
api.Queue(fetchReq);
|
||||||
}
|
}
|
||||||
|
@ -288,7 +288,7 @@ namespace osu.Game.Overlays
|
|||||||
{
|
{
|
||||||
Task.Run(() =>
|
Task.Run(() =>
|
||||||
{
|
{
|
||||||
var sets = response.Select(r => r.ToBeatmapSet(rulesets)).ToList();
|
var sets = response.BeatmapSets.Select(r => r.ToBeatmapSet(rulesets)).ToList();
|
||||||
|
|
||||||
// may not need scheduling; loads async internally.
|
// may not need scheduling; loads async internally.
|
||||||
Schedule(() =>
|
Schedule(() =>
|
||||||
|
@ -196,10 +196,16 @@ namespace osu.Game.Overlays
|
|||||||
playlist.StateChanged += s => playlistButton.FadeColour(s == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint);
|
playlist.StateChanged += s => playlistButton.FadeColour(s == Visibility.Visible ? colours.Yellow : Color4.White, 200, Easing.OutQuint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ScheduledDelegate seekDelegate;
|
||||||
|
|
||||||
private void attemptSeek(double progress)
|
private void attemptSeek(double progress)
|
||||||
{
|
{
|
||||||
if (!beatmap.Disabled)
|
seekDelegate?.Cancel();
|
||||||
current?.Track.Seek(progress);
|
seekDelegate = Schedule(() =>
|
||||||
|
{
|
||||||
|
if (!beatmap.Disabled)
|
||||||
|
current?.Track.Seek(progress);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void playlistOrderChanged(BeatmapSetInfo beatmapSetInfo, int index)
|
private void playlistOrderChanged(BeatmapSetInfo beatmapSetInfo, int index)
|
||||||
|
@ -74,7 +74,7 @@ namespace osu.Game.Overlays.Profile.Header
|
|||||||
Anchor = Anchor.BottomCentre,
|
Anchor = Anchor.BottomCentre,
|
||||||
Origin = Anchor.BottomCentre,
|
Origin = Anchor.BottomCentre,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = 75,
|
Height = 60,
|
||||||
Y = -secondary_textsize,
|
Y = -secondary_textsize,
|
||||||
Alpha = 0,
|
Alpha = 0,
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
|
|||||||
{
|
{
|
||||||
new SettingsSlider<double, OffsetSlider>
|
new SettingsSlider<double, OffsetSlider>
|
||||||
{
|
{
|
||||||
LabelText = "Audio Offset",
|
LabelText = "Audio offset",
|
||||||
Bindable = config.GetBindable<double>(OsuSetting.AudioOffset),
|
Bindable = config.GetBindable<double>(OsuSetting.AudioOffset),
|
||||||
KeyboardStep = 1f
|
KeyboardStep = 1f
|
||||||
},
|
},
|
||||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
|
|||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new SettingsSlider<double> { LabelText = "Master", Bindable = audio.Volume, KeyboardStep = 0.01f },
|
new SettingsSlider<double> { LabelText = "Master", Bindable = audio.Volume, KeyboardStep = 0.01f },
|
||||||
new SettingsSlider<double> { LabelText = "Master (Window Inactive)", Bindable = config.GetBindable<double>(OsuSetting.VolumeInactive), KeyboardStep = 0.01f },
|
new SettingsSlider<double> { LabelText = "Master (window inactive)", Bindable = config.GetBindable<double>(OsuSetting.VolumeInactive), KeyboardStep = 0.01f },
|
||||||
new SettingsSlider<double> { LabelText = "Effect", Bindable = audio.VolumeSample, KeyboardStep = 0.01f },
|
new SettingsSlider<double> { LabelText = "Effect", Bindable = audio.VolumeSample, KeyboardStep = 0.01f },
|
||||||
new SettingsSlider<double> { LabelText = "Music", Bindable = audio.VolumeTrack, KeyboardStep = 0.01f },
|
new SettingsSlider<double> { LabelText = "Music", Bindable = audio.VolumeTrack, KeyboardStep = 0.01f },
|
||||||
};
|
};
|
||||||
|
@ -15,7 +15,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input
|
|||||||
{
|
{
|
||||||
new SettingsButton
|
new SettingsButton
|
||||||
{
|
{
|
||||||
Text = "Key Configuration",
|
Text = "Key configuration",
|
||||||
Action = keyConfig.ToggleVisibility
|
Action = keyConfig.ToggleVisibility
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -26,12 +26,12 @@ namespace osu.Game.Overlays.Settings.Sections.Input
|
|||||||
{
|
{
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
{
|
{
|
||||||
LabelText = "Raw Input",
|
LabelText = "Raw input",
|
||||||
Bindable = rawInputToggle
|
Bindable = rawInputToggle
|
||||||
},
|
},
|
||||||
sensitivity = new SensitivitySetting
|
sensitivity = new SensitivitySetting
|
||||||
{
|
{
|
||||||
LabelText = "Cursor Sensitivity",
|
LabelText = "Cursor sensitivity",
|
||||||
Bindable = config.GetBindable<double>(FrameworkSetting.CursorSensitivity)
|
Bindable = config.GetBindable<double>(FrameworkSetting.CursorSensitivity)
|
||||||
},
|
},
|
||||||
new SettingsCheckbox
|
new SettingsCheckbox
|
||||||
|
@ -105,6 +105,8 @@ namespace osu.Game.Overlays.Toolbar
|
|||||||
public override bool HandleNonPositionalInput => !ruleset.Disabled && base.HandleNonPositionalInput;
|
public override bool HandleNonPositionalInput => !ruleset.Disabled && base.HandleNonPositionalInput;
|
||||||
public override bool HandlePositionalInput => !ruleset.Disabled && base.HandlePositionalInput;
|
public override bool HandlePositionalInput => !ruleset.Disabled && base.HandlePositionalInput;
|
||||||
|
|
||||||
|
public override bool PropagatePositionalInputSubTree => !ruleset.Disabled && base.PropagatePositionalInputSubTree;
|
||||||
|
|
||||||
private void disabledChanged(bool isDisabled) => this.FadeColour(isDisabled ? Color4.Gray : Color4.White, 300);
|
private void disabledChanged(bool isDisabled) => this.FadeColour(isDisabled ? Color4.Gray : Color4.White, 300);
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
|
@ -165,6 +165,6 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a <see cref="ScalableContainer"/> which provides a layer above or below the <see cref="Playfield"/>.
|
/// Creates a <see cref="ScalableContainer"/> which provides a layer above or below the <see cref="Playfield"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual ScalableContainer CreateLayerContainer() => new ScalableContainer { RelativeSizeAxes = Axes.Both };
|
protected virtual Container CreateLayerContainer() => new Container { RelativeSizeAxes = Axes.Both };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Applies this <see cref="IApplicableToDrawableHitObjects"/> to a list of <see cref="DrawableHitObject"/>s.
|
/// Applies this <see cref="IApplicableToDrawableHitObjects"/> to a list of <see cref="DrawableHitObject"/>s.
|
||||||
|
/// This will only be invoked with top-level <see cref="DrawableHitObject"/>s. Access <see cref="DrawableHitObject.NestedHitObjects"/> if adjusting nested objects is necessary.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="drawables">The list of <see cref="DrawableHitObject"/>s to apply to.</param>
|
/// <param name="drawables">The list of <see cref="DrawableHitObject"/>s to apply to.</param>
|
||||||
void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables);
|
void ApplyToDrawableHitObjects(IEnumerable<DrawableHitObject> drawables);
|
||||||
|
@ -1,25 +1,26 @@
|
|||||||
// 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 System.Collections.Generic;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Objects
|
namespace osu.Game.Rulesets.Objects
|
||||||
{
|
{
|
||||||
public class BezierApproximator
|
public readonly ref struct BezierApproximator
|
||||||
{
|
{
|
||||||
private readonly int count;
|
private readonly int count;
|
||||||
private readonly List<Vector2> controlPoints;
|
private readonly ReadOnlySpan<Vector2> controlPoints;
|
||||||
private readonly Vector2[] subdivisionBuffer1;
|
private readonly Vector2[] subdivisionBuffer1;
|
||||||
private readonly Vector2[] subdivisionBuffer2;
|
private readonly Vector2[] subdivisionBuffer2;
|
||||||
|
|
||||||
private const float tolerance = 0.25f;
|
private const float tolerance = 0.25f;
|
||||||
private const float tolerance_sq = tolerance * tolerance;
|
private const float tolerance_sq = tolerance * tolerance;
|
||||||
|
|
||||||
public BezierApproximator(List<Vector2> controlPoints)
|
public BezierApproximator(ReadOnlySpan<Vector2> controlPoints)
|
||||||
{
|
{
|
||||||
this.controlPoints = controlPoints;
|
this.controlPoints = controlPoints;
|
||||||
count = controlPoints.Count;
|
count = controlPoints.Length;
|
||||||
|
|
||||||
subdivisionBuffer1 = new Vector2[count];
|
subdivisionBuffer1 = new Vector2[count];
|
||||||
subdivisionBuffer2 = new Vector2[count * 2 - 1];
|
subdivisionBuffer2 = new Vector2[count * 2 - 1];
|
||||||
|
@ -1,40 +1,40 @@
|
|||||||
// 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 System.Collections.Generic;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Objects
|
namespace osu.Game.Rulesets.Objects
|
||||||
{
|
{
|
||||||
public class CatmullApproximator
|
public readonly ref struct CatmullApproximator
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The amount of pieces to calculate for each controlpoint quadruplet.
|
/// The amount of pieces to calculate for each controlpoint quadruplet.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const int detail = 50;
|
private const int detail = 50;
|
||||||
|
|
||||||
private readonly List<Vector2> controlPoints;
|
private readonly ReadOnlySpan<Vector2> controlPoints;
|
||||||
|
|
||||||
public CatmullApproximator(List<Vector2> controlPoints)
|
public CatmullApproximator(ReadOnlySpan<Vector2> controlPoints)
|
||||||
{
|
{
|
||||||
this.controlPoints = controlPoints;
|
this.controlPoints = controlPoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a piecewise-linear approximation of a Catmull-Rom spline.
|
/// Creates a piecewise-linear approximation of a Catmull-Rom spline.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A list of vectors representing the piecewise-linear approximation.</returns>
|
/// <returns>A list of vectors representing the piecewise-linear approximation.</returns>
|
||||||
public List<Vector2> CreateCatmull()
|
public List<Vector2> CreateCatmull()
|
||||||
{
|
{
|
||||||
var result = new List<Vector2>();
|
var result = new List<Vector2>((controlPoints.Length - 1) * detail * 2);
|
||||||
|
|
||||||
for (int i = 0; i < controlPoints.Count - 1; i++)
|
for (int i = 0; i < controlPoints.Length - 1; i++)
|
||||||
{
|
{
|
||||||
var v1 = i > 0 ? controlPoints[i - 1] : controlPoints[i];
|
var v1 = i > 0 ? controlPoints[i - 1] : controlPoints[i];
|
||||||
var v2 = controlPoints[i];
|
var v2 = controlPoints[i];
|
||||||
var v3 = i < controlPoints.Count - 1 ? controlPoints[i + 1] : v2 + v2 - v1;
|
var v3 = i < controlPoints.Length - 1 ? controlPoints[i + 1] : v2 + v2 - v1;
|
||||||
var v4 = i < controlPoints.Count - 2 ? controlPoints[i + 2] : v3 + v3 - v2;
|
var v4 = i < controlPoints.Length - 2 ? controlPoints[i + 2] : v3 + v3 - v2;
|
||||||
|
|
||||||
for (int c = 0; c < detail; c++)
|
for (int c = 0; c < detail; c++)
|
||||||
{
|
{
|
||||||
|
@ -8,21 +8,15 @@ using OpenTK;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Objects
|
namespace osu.Game.Rulesets.Objects
|
||||||
{
|
{
|
||||||
public class CircularArcApproximator
|
public readonly ref struct CircularArcApproximator
|
||||||
{
|
{
|
||||||
private readonly Vector2 a;
|
|
||||||
private readonly Vector2 b;
|
|
||||||
private readonly Vector2 c;
|
|
||||||
|
|
||||||
private int amountPoints;
|
|
||||||
|
|
||||||
private const float tolerance = 0.1f;
|
private const float tolerance = 0.1f;
|
||||||
|
|
||||||
public CircularArcApproximator(Vector2 a, Vector2 b, Vector2 c)
|
private readonly ReadOnlySpan<Vector2> controlPoints;
|
||||||
|
|
||||||
|
public CircularArcApproximator(ReadOnlySpan<Vector2> controlPoints)
|
||||||
{
|
{
|
||||||
this.a = a;
|
this.controlPoints = controlPoints;
|
||||||
this.b = b;
|
|
||||||
this.c = c;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -31,6 +25,10 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
/// <returns>A list of vectors representing the piecewise-linear approximation.</returns>
|
/// <returns>A list of vectors representing the piecewise-linear approximation.</returns>
|
||||||
public List<Vector2> CreateArc()
|
public List<Vector2> CreateArc()
|
||||||
{
|
{
|
||||||
|
Vector2 a = controlPoints[0];
|
||||||
|
Vector2 b = controlPoints[1];
|
||||||
|
Vector2 c = controlPoints[2];
|
||||||
|
|
||||||
float aSq = (b - c).LengthSquared;
|
float aSq = (b - c).LengthSquared;
|
||||||
float bSq = (a - c).LengthSquared;
|
float bSq = (a - c).LengthSquared;
|
||||||
float cSq = (a - b).LengthSquared;
|
float cSq = (a - b).LengthSquared;
|
||||||
@ -81,7 +79,7 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
// is: 2 * Math.Acos(1 - TOLERANCE / r)
|
// is: 2 * Math.Acos(1 - TOLERANCE / r)
|
||||||
// The special case is required for extremely short sliders where the radius is smaller than
|
// The special case is required for extremely short sliders where the radius is smaller than
|
||||||
// the tolerance. This is a pathological rather than a realistic case.
|
// the tolerance. This is a pathological rather than a realistic case.
|
||||||
amountPoints = 2 * r <= tolerance ? 2 : Math.Max(2, (int)Math.Ceiling(thetaRange / (2 * Math.Acos(1 - tolerance / r))));
|
int amountPoints = 2 * r <= tolerance ? 2 : Math.Max(2, (int)Math.Ceiling(thetaRange / (2 * Math.Acos(1 - tolerance / r))));
|
||||||
|
|
||||||
List<Vector2> output = new List<Vector2>(amountPoints);
|
List<Vector2> output = new List<Vector2>(amountPoints);
|
||||||
|
|
||||||
|
@ -145,7 +145,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
|||||||
public event Action<DrawableHitObject, ArmedState> ApplyCustomUpdateState;
|
public event Action<DrawableHitObject, ArmedState> ApplyCustomUpdateState;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Plays all the hitsounds for this <see cref="DrawableHitObject"/>.
|
/// Plays all the hit sounds for this <see cref="DrawableHitObject"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void PlaySamples() => Samples?.Play();
|
public void PlaySamples() => Samples?.Play();
|
||||||
|
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
// 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 System.Collections.Generic;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
|
||||||
using osu.Framework.Lists;
|
using osu.Framework.Lists;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -58,10 +56,10 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public HitWindows HitWindows { get; set; }
|
public HitWindows HitWindows { get; set; }
|
||||||
|
|
||||||
private readonly Lazy<SortedList<HitObject>> nestedHitObjects = new Lazy<SortedList<HitObject>>(() => new SortedList<HitObject>((h1, h2) => h1.StartTime.CompareTo(h2.StartTime)));
|
private readonly SortedList<HitObject> nestedHitObjects = new SortedList<HitObject>(compareObjects);
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public IReadOnlyList<HitObject> NestedHitObjects => nestedHitObjects.Value;
|
public IReadOnlyList<HitObject> NestedHitObjects => nestedHitObjects;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Applies default values to this HitObject.
|
/// Applies default values to this HitObject.
|
||||||
@ -72,18 +70,14 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
{
|
{
|
||||||
ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||||
|
|
||||||
if (nestedHitObjects.IsValueCreated)
|
nestedHitObjects.Clear();
|
||||||
nestedHitObjects.Value.Clear();
|
|
||||||
|
|
||||||
CreateNestedHitObjects();
|
CreateNestedHitObjects();
|
||||||
|
|
||||||
if (nestedHitObjects.IsValueCreated)
|
foreach (var h in nestedHitObjects)
|
||||||
{
|
{
|
||||||
nestedHitObjects.Value.ForEach(h =>
|
h.HitWindows = HitWindows;
|
||||||
{
|
h.ApplyDefaults(controlPointInfo, difficulty);
|
||||||
h.HitWindows = HitWindows;
|
|
||||||
h.ApplyDefaults(controlPointInfo, difficulty);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +98,7 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void AddNested(HitObject hitObject) => nestedHitObjects.Value.Add(hitObject);
|
protected void AddNested(HitObject hitObject) => nestedHitObjects.Add(hitObject);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates the <see cref="Judgement"/> that represents the scoring information for this <see cref="HitObject"/>.
|
/// Creates the <see cref="Judgement"/> that represents the scoring information for this <see cref="HitObject"/>.
|
||||||
@ -120,5 +114,7 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
/// </para>
|
/// </para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual HitWindows CreateHitWindows() => new HitWindows();
|
protected virtual HitWindows CreateHitWindows() => new HitWindows();
|
||||||
|
|
||||||
|
private static int compareObjects(HitObject first, HitObject second) => first.StartTime.CompareTo(second.StartTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List<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, CurveType curveType, int repeatCount, List<List<SampleInfo>> repeatSamples)
|
||||||
{
|
{
|
||||||
newCombo |= forceNewCombo;
|
newCombo |= forceNewCombo;
|
||||||
comboOffset += extraComboOffset;
|
comboOffset += extraComboOffset;
|
||||||
|
@ -72,10 +72,18 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
{
|
{
|
||||||
CurveType curveType = CurveType.Catmull;
|
CurveType curveType = CurveType.Catmull;
|
||||||
double length = 0;
|
double length = 0;
|
||||||
var points = new List<Vector2> { Vector2.Zero };
|
|
||||||
|
|
||||||
string[] pointsplit = split[5].Split('|');
|
string[] pointSplit = split[5].Split('|');
|
||||||
foreach (string t in pointsplit)
|
|
||||||
|
int pointCount = 1;
|
||||||
|
foreach (var t in pointSplit)
|
||||||
|
if (t.Length > 1)
|
||||||
|
pointCount++;
|
||||||
|
|
||||||
|
var points = new Vector2[pointCount];
|
||||||
|
|
||||||
|
int pointIndex = 1;
|
||||||
|
foreach (string t in pointSplit)
|
||||||
{
|
{
|
||||||
if (t.Length == 1)
|
if (t.Length == 1)
|
||||||
{
|
{
|
||||||
@ -94,16 +102,18 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
curveType = CurveType.PerfectCurve;
|
curveType = CurveType.PerfectCurve;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
string[] temp = t.Split(':');
|
string[] temp = t.Split(':');
|
||||||
points.Add(new Vector2((int)Convert.ToDouble(temp[0], CultureInfo.InvariantCulture), (int)Convert.ToDouble(temp[1], CultureInfo.InvariantCulture)) - pos);
|
points[pointIndex++] = new Vector2((int)Convert.ToDouble(temp[0], CultureInfo.InvariantCulture), (int)Convert.ToDouble(temp[1], CultureInfo.InvariantCulture)) - pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
// osu-stable special-cased colinear perfect curves to a CurveType.Linear
|
// osu-stable special-cased colinear perfect curves to a CurveType.Linear
|
||||||
bool isLinear(List<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.Count == 3 && curveType == CurveType.PerfectCurve && isLinear(points))
|
|
||||||
|
if (points.Length == 3 && curveType == CurveType.PerfectCurve && isLinear(points))
|
||||||
curveType = CurveType.Linear;
|
curveType = CurveType.Linear;
|
||||||
|
|
||||||
int repeatCount = Convert.ToInt32(split[6], CultureInfo.InvariantCulture);
|
int repeatCount = Convert.ToInt32(split[6], CultureInfo.InvariantCulture);
|
||||||
@ -262,7 +272,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
/// <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, List<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, CurveType curveType, int repeatCount, List<List<SampleInfo>> repeatSamples);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a legacy Spinner-type hit object.
|
/// Creates a legacy Spinner-type hit object.
|
||||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
/// <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 SliderCurve Curve { get; } = null;
|
||||||
public List<Vector2> ControlPoints { get; set; }
|
public Vector2[] ControlPoints { get; set; }
|
||||||
public CurveType CurveType { get; set; }
|
public CurveType CurveType { get; set; }
|
||||||
|
|
||||||
public double Distance { get; set; }
|
public double Distance { get; set; }
|
||||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List<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, CurveType curveType, int repeatCount, List<List<SampleInfo>> repeatSamples)
|
||||||
{
|
{
|
||||||
return new ConvertSlider
|
return new ConvertSlider
|
||||||
{
|
{
|
||||||
|
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List<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, CurveType curveType, int repeatCount, List<List<SampleInfo>> repeatSamples)
|
||||||
{
|
{
|
||||||
newCombo |= forceNewCombo;
|
newCombo |= forceNewCombo;
|
||||||
comboOffset += extraComboOffset;
|
comboOffset += extraComboOffset;
|
||||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
|
|||||||
return new ConvertHit();
|
return new ConvertHit();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List<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, CurveType curveType, int repeatCount, List<List<SampleInfo>> repeatSamples)
|
||||||
{
|
{
|
||||||
return new ConvertSlider
|
return new ConvertSlider
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// 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 System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.MathUtils;
|
using osu.Framework.MathUtils;
|
||||||
@ -13,7 +14,7 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
{
|
{
|
||||||
public double Distance;
|
public double Distance;
|
||||||
|
|
||||||
public List<Vector2> ControlPoints;
|
public Vector2[] ControlPoints;
|
||||||
|
|
||||||
public CurveType CurveType = CurveType.PerfectCurve;
|
public CurveType CurveType = CurveType.PerfectCurve;
|
||||||
|
|
||||||
@ -22,19 +23,23 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
private readonly List<Vector2> calculatedPath = new List<Vector2>();
|
private readonly List<Vector2> calculatedPath = new List<Vector2>();
|
||||||
private readonly List<double> cumulativeLength = new List<double>();
|
private readonly List<double> cumulativeLength = new List<double>();
|
||||||
|
|
||||||
private List<Vector2> calculateSubpath(List<Vector2> subControlPoints)
|
private List<Vector2> calculateSubpath(ReadOnlySpan<Vector2> subControlPoints)
|
||||||
{
|
{
|
||||||
switch (CurveType)
|
switch (CurveType)
|
||||||
{
|
{
|
||||||
case CurveType.Linear:
|
case CurveType.Linear:
|
||||||
return subControlPoints;
|
var result = new List<Vector2>(subControlPoints.Length);
|
||||||
|
foreach (var c in subControlPoints)
|
||||||
|
result.Add(c);
|
||||||
|
|
||||||
|
return result;
|
||||||
case CurveType.PerfectCurve:
|
case CurveType.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.Count != 3 || subControlPoints.Count != 3)
|
if (ControlPoints.Length != 3 || subControlPoints.Length != 3)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Here we have exactly 3 control points. Attempt to fit a circular arc.
|
// Here we have exactly 3 control points. Attempt to fit a circular arc.
|
||||||
List<Vector2> subpath = new CircularArcApproximator(subControlPoints[0], subControlPoints[1], subControlPoints[2]).CreateArc();
|
List<Vector2> subpath = new CircularArcApproximator(subControlPoints).CreateArc();
|
||||||
|
|
||||||
// If for some reason a circular arc could not be fit to the 3 given points, fall back to a numerically stable bezier approximation.
|
// If for some reason a circular arc could not be fit to the 3 given points, fall back to a numerically stable bezier approximation.
|
||||||
if (subpath.Count == 0)
|
if (subpath.Count == 0)
|
||||||
@ -55,18 +60,23 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
// Sliders may consist of various subpaths separated by two consecutive vertices
|
// Sliders may consist of various subpaths separated by two consecutive vertices
|
||||||
// with the same position. The following loop parses these subpaths and computes
|
// with the same position. The following loop parses these subpaths and computes
|
||||||
// their shape independently, consecutively appending them to calculatedPath.
|
// their shape independently, consecutively appending them to calculatedPath.
|
||||||
List<Vector2> subControlPoints = new List<Vector2>();
|
|
||||||
for (int i = 0; i < ControlPoints.Count; ++i)
|
int start = 0;
|
||||||
|
int end = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < ControlPoints.Length; ++i)
|
||||||
{
|
{
|
||||||
subControlPoints.Add(ControlPoints[i]);
|
end++;
|
||||||
if (i == ControlPoints.Count - 1 || ControlPoints[i] == ControlPoints[i + 1])
|
|
||||||
|
if (i == ControlPoints.Length - 1 || ControlPoints[i] == ControlPoints[i + 1])
|
||||||
{
|
{
|
||||||
List<Vector2> subpath = calculateSubpath(subControlPoints);
|
ReadOnlySpan<Vector2> cpSpan = ControlPoints.AsSpan().Slice(start, end - start);
|
||||||
foreach (Vector2 t in subpath)
|
|
||||||
|
foreach (Vector2 t in calculateSubpath(cpSpan))
|
||||||
if (calculatedPath.Count == 0 || calculatedPath.Last() != t)
|
if (calculatedPath.Count == 0 || calculatedPath.Last() != t)
|
||||||
calculatedPath.Add(t);
|
calculatedPath.Add(t);
|
||||||
|
|
||||||
subControlPoints.Clear();
|
start = end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -166,7 +176,7 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
/// <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)
|
||||||
{
|
{
|
||||||
if (calculatedPath.Count == 0 && ControlPoints.Count > 0)
|
if (calculatedPath.Count == 0 && ControlPoints.Length > 0)
|
||||||
Calculate();
|
Calculate();
|
||||||
|
|
||||||
double d0 = progressToDistance(p0);
|
double d0 = progressToDistance(p0);
|
||||||
@ -193,7 +203,7 @@ namespace osu.Game.Rulesets.Objects
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public Vector2 PositionAt(double progress)
|
public Vector2 PositionAt(double progress)
|
||||||
{
|
{
|
||||||
if (calculatedPath.Count == 0 && ControlPoints.Count > 0)
|
if (calculatedPath.Count == 0 && ControlPoints.Length > 0)
|
||||||
Calculate();
|
Calculate();
|
||||||
|
|
||||||
double d = progressToDistance(progress);
|
double d = progressToDistance(progress);
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// 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.Collections.Generic;
|
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Objects.Types
|
namespace osu.Game.Rulesets.Objects.Types
|
||||||
@ -19,7 +18,7 @@ namespace osu.Game.Rulesets.Objects.Types
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The control points that shape the curve.
|
/// The control points that shape the curve.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
List<Vector2> ControlPoints { get; }
|
Vector2[] ControlPoints { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The type of curve.
|
/// The type of curve.
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user