mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 12:33:01 +08:00
Merge branch 'master' into Cakefy_osu!
This commit is contained in:
commit
701a1ecb00
@ -5,13 +5,11 @@ using System.Collections.Generic;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Mania.UI;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter, IApplicableToRulesetContainer<ManiaHitObject>
|
||||
public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter, IApplicableToBeatmap<ManiaHitObject>
|
||||
{
|
||||
public override string Name => "Dual Stages";
|
||||
public override string ShortenedName => "DS";
|
||||
@ -34,22 +32,21 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
mbc.TargetColumns *= 2;
|
||||
}
|
||||
|
||||
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
|
||||
public void ApplyToBeatmap(Beatmap<ManiaHitObject> beatmap)
|
||||
{
|
||||
var mrc = (ManiaRulesetContainer)rulesetContainer;
|
||||
|
||||
// Although this can work, for now let's not allow keymods for mania-specific beatmaps
|
||||
if (isForCurrentRuleset)
|
||||
return;
|
||||
|
||||
var maniaBeatmap = (ManiaBeatmap)beatmap;
|
||||
|
||||
var newDefinitions = new List<StageDefinition>();
|
||||
foreach (var existing in mrc.Beatmap.Stages)
|
||||
foreach (var existing in maniaBeatmap.Stages)
|
||||
{
|
||||
newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 });
|
||||
newDefinitions.Add(new StageDefinition { Columns = existing.Columns / 2 });
|
||||
}
|
||||
|
||||
mrc.Beatmap.Stages = newDefinitions;
|
||||
maniaBeatmap.Stages = newDefinitions;
|
||||
}
|
||||
|
||||
public PlayfieldType PlayfieldType => PlayfieldType.Dual;
|
||||
|
@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
|
||||
{
|
||||
public class SliderMask : HitObjectMask
|
||||
{
|
||||
private readonly SliderBody body;
|
||||
private readonly SnakingSliderBody body;
|
||||
private readonly DrawableSlider slider;
|
||||
|
||||
public SliderMask(DrawableSlider slider)
|
||||
@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Layers.Selection.Overlays
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
body = new SliderBody(sliderObject)
|
||||
body = new SnakingSliderBody(sliderObject)
|
||||
{
|
||||
AccentColour = Color4.Transparent,
|
||||
PathWidth = sliderObject.Scale * 64
|
||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
public readonly DrawableHitCircle HeadCircle;
|
||||
public readonly DrawableSliderTail TailCircle;
|
||||
|
||||
public readonly SliderBody Body;
|
||||
public readonly SnakingSliderBody Body;
|
||||
public readonly SliderBall Ball;
|
||||
|
||||
public DrawableSlider(Slider s)
|
||||
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
Body = new SliderBody(s)
|
||||
Body = new SnakingSliderBody(s)
|
||||
{
|
||||
PathWidth = s.Scale * 64,
|
||||
},
|
||||
|
@ -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 OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="SliderBody"/> with the ability to set the drawn vertices manually.
|
||||
/// </summary>
|
||||
public class ManualSliderBody : SliderBody
|
||||
{
|
||||
public new void SetVertices(IReadOnlyList<Vector2> vertices)
|
||||
{
|
||||
base.SetVertices(vertices);
|
||||
Size = Path.Size;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,24 +1,22 @@
|
||||
// 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
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Lines;
|
||||
using OpenTK.Graphics.ES30;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using OpenTK.Graphics.ES30;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
public class SliderBody : Container, ISliderProgress
|
||||
public abstract class SliderBody : CompositeDrawable
|
||||
{
|
||||
private readonly SliderPath path;
|
||||
protected Path Path => path;
|
||||
|
||||
private readonly BufferedContainer container;
|
||||
|
||||
public float PathWidth
|
||||
@ -30,15 +28,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
/// <summary>
|
||||
/// Offset in absolute coordinates from the start of the curve.
|
||||
/// </summary>
|
||||
public Vector2 PathOffset { get; private set; }
|
||||
|
||||
public readonly List<Vector2> CurrentCurve = new List<Vector2>();
|
||||
|
||||
public readonly Bindable<bool> SnakingIn = new Bindable<bool>();
|
||||
public readonly Bindable<bool> SnakingOut = new Bindable<bool>();
|
||||
|
||||
public double? SnakedStart { get; private set; }
|
||||
public double? SnakedEnd { get; private set; }
|
||||
public virtual Vector2 PathOffset => path.PositionInBoundingBox(path.Vertices[0]);
|
||||
|
||||
/// <summary>
|
||||
/// Used to colour the path.
|
||||
@ -74,28 +64,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
|
||||
public Quad PathDrawQuad => container.ScreenSpaceDrawQuad;
|
||||
|
||||
private Vector2 topLeftOffset;
|
||||
|
||||
private readonly Slider slider;
|
||||
|
||||
public SliderBody(Slider s)
|
||||
protected SliderBody()
|
||||
{
|
||||
slider = s;
|
||||
|
||||
Children = new Drawable[]
|
||||
InternalChild = container = new BufferedContainer
|
||||
{
|
||||
container = new BufferedContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
CacheDrawnFrameBuffer = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
path = new SliderPath
|
||||
{
|
||||
Blending = BlendingMode.None,
|
||||
},
|
||||
}
|
||||
},
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
CacheDrawnFrameBuffer = true,
|
||||
Child = path = new SliderPath { Blending = BlendingMode.None }
|
||||
};
|
||||
|
||||
container.Attach(RenderbufferInternalFormat.DepthComponent16);
|
||||
@ -103,80 +78,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => path.ReceivePositionalInputAt(screenSpacePos);
|
||||
|
||||
public void SetRange(double p0, double p1)
|
||||
/// <summary>
|
||||
/// Sets the vertices of the path which should be drawn by this <see cref="SliderBody"/>.
|
||||
/// </summary>
|
||||
/// <param name="vertices">The vertices</param>
|
||||
protected void SetVertices(IReadOnlyList<Vector2> vertices)
|
||||
{
|
||||
if (p0 > p1)
|
||||
MathHelper.Swap(ref p0, ref p1);
|
||||
|
||||
if (updateSnaking(p0, p1))
|
||||
{
|
||||
// The path is generated such that its size encloses it. This change of size causes the path
|
||||
// to move around while snaking, so we need to offset it to make sure it maintains the
|
||||
// same position as when it is fully snaked.
|
||||
var newTopLeftOffset = path.PositionInBoundingBox(Vector2.Zero);
|
||||
path.Position = topLeftOffset - newTopLeftOffset;
|
||||
|
||||
container.ForceRedraw();
|
||||
}
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
computeSize();
|
||||
}
|
||||
|
||||
private void computeSize()
|
||||
{
|
||||
// Generate the entire curve
|
||||
slider.Curve.GetPathToProgress(CurrentCurve, 0, 1);
|
||||
foreach (Vector2 p in CurrentCurve)
|
||||
path.AddVertex(p);
|
||||
|
||||
Size = path.Size;
|
||||
|
||||
topLeftOffset = path.PositionInBoundingBox(Vector2.Zero);
|
||||
PathOffset = path.PositionInBoundingBox(CurrentCurve[0]);
|
||||
}
|
||||
|
||||
private bool updateSnaking(double p0, double p1)
|
||||
{
|
||||
if (SnakedStart == p0 && SnakedEnd == p1) return false;
|
||||
|
||||
SnakedStart = p0;
|
||||
SnakedEnd = p1;
|
||||
|
||||
slider.Curve.GetPathToProgress(CurrentCurve, p0, p1);
|
||||
|
||||
path.ClearVertices();
|
||||
foreach (Vector2 p in CurrentCurve)
|
||||
path.AddVertex(p);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void UpdateProgress(double completionProgress)
|
||||
{
|
||||
var span = slider.SpanAt(completionProgress);
|
||||
var spanProgress = slider.ProgressAt(completionProgress);
|
||||
|
||||
double start = 0;
|
||||
double end = SnakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadeIn, 0, 1) : 1;
|
||||
|
||||
if (span >= slider.SpanCount() - 1)
|
||||
{
|
||||
if (Math.Min(span, slider.SpanCount() - 1) % 2 == 1)
|
||||
{
|
||||
start = 0;
|
||||
end = SnakingOut ? spanProgress : 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
start = SnakingOut ? spanProgress : 0;
|
||||
}
|
||||
}
|
||||
|
||||
SetRange(start, end);
|
||||
path.Vertices = vertices;
|
||||
container.ForceRedraw();
|
||||
}
|
||||
|
||||
private class SliderPath : SmoothPath
|
||||
|
@ -0,0 +1,105 @@
|
||||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Configuration;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
{
|
||||
/// <summary>
|
||||
/// A <see cref="SliderBody"/> which changes its curve depending on the snaking progress.
|
||||
/// </summary>
|
||||
public class SnakingSliderBody : SliderBody, ISliderProgress
|
||||
{
|
||||
public readonly List<Vector2> CurrentCurve = new List<Vector2>();
|
||||
|
||||
public readonly Bindable<bool> SnakingIn = new Bindable<bool>();
|
||||
public readonly Bindable<bool> SnakingOut = new Bindable<bool>();
|
||||
|
||||
public double? SnakedStart { get; private set; }
|
||||
public double? SnakedEnd { get; private set; }
|
||||
|
||||
public override Vector2 PathOffset => snakedPathOffset;
|
||||
|
||||
/// <summary>
|
||||
/// The top-left position of the path when fully snaked.
|
||||
/// </summary>
|
||||
private Vector2 snakedPosition;
|
||||
|
||||
/// <summary>
|
||||
/// The offset of the path from <see cref="snakedPosition"/> when fully snaked.
|
||||
/// </summary>
|
||||
private Vector2 snakedPathOffset;
|
||||
|
||||
private readonly Slider slider;
|
||||
|
||||
public SnakingSliderBody(Slider slider)
|
||||
{
|
||||
this.slider = slider;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
// Generate the entire curve
|
||||
slider.Curve.GetPathToProgress(CurrentCurve, 0, 1);
|
||||
SetVertices(CurrentCurve);
|
||||
|
||||
// The body is sized to the full path size to avoid excessive autosize computations
|
||||
Size = Path.Size;
|
||||
|
||||
snakedPosition = Path.PositionInBoundingBox(Vector2.Zero);
|
||||
snakedPathOffset = Path.PositionInBoundingBox(Path.Vertices[0]);
|
||||
}
|
||||
|
||||
public void UpdateProgress(double completionProgress)
|
||||
{
|
||||
var span = slider.SpanAt(completionProgress);
|
||||
var spanProgress = slider.ProgressAt(completionProgress);
|
||||
|
||||
double start = 0;
|
||||
double end = SnakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadeIn, 0, 1) : 1;
|
||||
|
||||
if (span >= slider.SpanCount() - 1)
|
||||
{
|
||||
if (Math.Min(span, slider.SpanCount() - 1) % 2 == 1)
|
||||
{
|
||||
start = 0;
|
||||
end = SnakingOut ? spanProgress : 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
start = SnakingOut ? spanProgress : 0;
|
||||
}
|
||||
}
|
||||
|
||||
setRange(start, end);
|
||||
}
|
||||
|
||||
private void setRange(double p0, double p1)
|
||||
{
|
||||
if (p0 > p1)
|
||||
MathHelper.Swap(ref p0, ref p1);
|
||||
|
||||
if (SnakedStart == p0 && SnakedEnd == p1) return;
|
||||
|
||||
SnakedStart = p0;
|
||||
SnakedEnd = p1;
|
||||
|
||||
slider.Curve.GetPathToProgress(CurrentCurve, p0, p1);
|
||||
|
||||
SetVertices(CurrentCurve);
|
||||
|
||||
// The bounding box of the path expands as it snakes, which in turn shifts the position of the path.
|
||||
// Depending on the direction of expansion, it may appear as if the path is expanding towards the position of the slider
|
||||
// rather than expanding out from the position of the slider.
|
||||
// To remove this effect, the path's position is shifted towards its final snaked position
|
||||
|
||||
Path.Position = snakedPosition - Path.PositionInBoundingBox(Vector2.Zero);
|
||||
}
|
||||
}
|
||||
}
|
22
osu.Game/Rulesets/Mods/IApplicableToBeatmap.cs
Normal file
22
osu.Game/Rulesets/Mods/IApplicableToBeatmap.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.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for a <see cref="Mod"/> that applies changes to a <see cref="Beatmap"/>
|
||||
/// after conversion and post-processing has completed.
|
||||
/// </summary>
|
||||
public interface IApplicableToBeatmap<TObject> : IApplicableMod
|
||||
where TObject : HitObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Applies this <see cref="IApplicableToBeatmap{TObject}"/> to a <see cref="Beatmap{TObject}"/>.
|
||||
/// </summary>
|
||||
/// <param name="beatmap">The <see cref="Beatmap{TObject}"/> to apply to.</param>
|
||||
void ApplyToBeatmap(Beatmap<TObject> beatmap);
|
||||
}
|
||||
}
|
@ -238,6 +238,8 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
KeyBindingInputManager = CreateInputManager();
|
||||
KeyBindingInputManager.RelativeSizeAxes = Axes.Both;
|
||||
|
||||
applyBeatmapMods(Mods);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -255,16 +257,29 @@ namespace osu.Game.Rulesets.UI
|
||||
KeyBindingInputManager.Add(Cursor);
|
||||
|
||||
// Apply mods
|
||||
applyMods(Mods, config);
|
||||
applyRulesetMods(Mods, config);
|
||||
|
||||
loadObjects();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the active mods to the Beatmap.
|
||||
/// </summary>
|
||||
/// <param name="mods"></param>
|
||||
private void applyBeatmapMods(IEnumerable<Mod> mods)
|
||||
{
|
||||
if (mods == null)
|
||||
return;
|
||||
|
||||
foreach (var mod in mods.OfType<IApplicableToBeatmap<TObject>>())
|
||||
mod.ApplyToBeatmap(Beatmap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the active mods to this RulesetContainer.
|
||||
/// </summary>
|
||||
/// <param name="mods"></param>
|
||||
private void applyMods(IEnumerable<Mod> mods, OsuConfigManager config)
|
||||
private void applyRulesetMods(IEnumerable<Mod> mods, OsuConfigManager config)
|
||||
{
|
||||
if (mods == null)
|
||||
return;
|
||||
|
@ -18,7 +18,7 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.1.4" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2018.1018.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2018.1030.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.22.0" />
|
||||
<PackageReference Include="NUnit" Version="3.11.0" />
|
||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||
|
Loading…
Reference in New Issue
Block a user