1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-21 17:27:24 +08:00

Merge branch 'master' into taiko-barline-skinning

This commit is contained in:
Dean Herbert 2020-04-25 18:58:44 +09:00 committed by GitHub
commit 4ca7ce7715
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 493 additions and 155 deletions

View File

@ -41,8 +41,6 @@ namespace osu.Game.Rulesets.Mania.Tests
AccentColour = Color4.OrangeRed, AccentColour = Color4.OrangeRed,
Clock = new FramedClock(new StopwatchClock()), // No scroll Clock = new FramedClock(new StopwatchClock()), // No scroll
}); });
AddStep("change direction", () => ((ScrollingTestContainer)HitObjectContainer).Flip());
} }
protected override Container CreateHitObjectContainer() => new ScrollingTestContainer(ScrollingDirection.Down) { RelativeSizeAxes = Axes.Both }; protected override Container CreateHitObjectContainer() => new ScrollingTestContainer(ScrollingDirection.Down) { RelativeSizeAxes = Axes.Both };

View File

@ -1,17 +1,59 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Linq;
using NUnit.Framework;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Testing;
using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Edit.Blueprints; using osu.Game.Rulesets.Mania.Edit.Blueprints;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.Objects.Drawables;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Tests.Visual;
using osuTK;
using osuTK.Input;
namespace osu.Game.Rulesets.Mania.Tests namespace osu.Game.Rulesets.Mania.Tests
{ {
public class TestSceneNotePlacementBlueprint : ManiaPlacementBlueprintTestScene public class TestSceneNotePlacementBlueprint : ManiaPlacementBlueprintTestScene
{ {
[SetUp]
public void Setup() => Schedule(() =>
{
this.ChildrenOfType<HitObjectContainer>().ForEach(c => c.Clear());
ResetPlacement();
((ScrollingTestContainer)HitObjectContainer).Direction = ScrollingDirection.Down;
});
[Test]
public void TestPlaceBeforeCurrentTimeDownwards()
{
AddStep("move mouse before current time", () => InputManager.MoveMouseTo(this.ChildrenOfType<Column>().Single().ScreenSpaceDrawQuad.BottomLeft - new Vector2(0, 10)));
AddStep("click", () => InputManager.Click(MouseButton.Left));
AddAssert("note start time < 0", () => getNote().StartTime < 0);
}
[Test]
public void TestPlaceAfterCurrentTimeDownwards()
{
AddStep("move mouse after current time", () => InputManager.MoveMouseTo(this.ChildrenOfType<Column>().Single()));
AddStep("click", () => InputManager.Click(MouseButton.Left));
AddAssert("note start time > 0", () => getNote().StartTime > 0);
}
private Note getNote() => this.ChildrenOfType<DrawableNote>().FirstOrDefault()?.HitObject;
protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableNote((Note)hitObject); protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableNote((Note)hitObject);
protected override PlacementBlueprint CreateBlueprint() => new NotePlacementBlueprint(); protected override PlacementBlueprint CreateBlueprint() => new NotePlacementBlueprint();
} }

View File

@ -13,7 +13,6 @@ using osu.Game.Rulesets.Mania.Beatmaps.Patterns;
using osu.Game.Rulesets.Mania.MathUtils; using osu.Game.Rulesets.Mania.MathUtils;
using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy; using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy;
using osuTK; using osuTK;
using osu.Game.Audio;
namespace osu.Game.Rulesets.Mania.Beatmaps namespace osu.Game.Rulesets.Mania.Beatmaps
{ {
@ -67,7 +66,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
} }
} }
public override bool CanConvert() => Beatmap.HitObjects.All(h => h is IHasXPosition || h is ManiaHitObject); public override bool CanConvert() => Beatmap.HitObjects.All(h => h is IHasXPosition);
protected override Beatmap<ManiaHitObject> ConvertBeatmap(IBeatmap original) protected override Beatmap<ManiaHitObject> ConvertBeatmap(IBeatmap original)
{ {
@ -239,8 +238,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
StartTime = HitObject.StartTime, StartTime = HitObject.StartTime,
Duration = endTimeData.Duration, Duration = endTimeData.Duration,
Column = column, Column = column,
Head = { Samples = sampleInfoListAt(HitObject.StartTime) }, Samples = HitObject.Samples,
Tail = { Samples = sampleInfoListAt(endTimeData.EndTime) }, NodeSamples = (HitObject as IHasRepeats)?.NodeSamples
}); });
} }
else if (HitObject is IHasXPosition) else if (HitObject is IHasXPosition)
@ -255,22 +254,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
return pattern; return pattern;
} }
/// <summary>
/// Retrieves the sample info list at a point in time.
/// </summary>
/// <param name="time">The time to retrieve the sample info list from.</param>
/// <returns></returns>
private IList<HitSampleInfo> sampleInfoListAt(double time)
{
if (!(HitObject is IHasCurve curveData))
return HitObject.Samples;
double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.SpanCount();
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
return curveData.NodeSamples[index];
}
} }
} }
} }

View File

@ -505,16 +505,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
} }
else else
{ {
var holdNote = new HoldNote newObject = new HoldNote
{ {
StartTime = startTime, StartTime = startTime,
Column = column,
Duration = endTime - startTime, Duration = endTime - startTime,
Head = { Samples = sampleInfoListAt(startTime) }, Column = column,
Tail = { Samples = sampleInfoListAt(endTime) } Samples = HitObject.Samples,
NodeSamples = (HitObject as IHasRepeats)?.NodeSamples
}; };
newObject = holdNote;
} }
pattern.Add(newObject); pattern.Add(newObject);

View File

@ -64,21 +64,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (holdNote) if (holdNote)
{ {
var hold = new HoldNote newObject = new HoldNote
{ {
StartTime = HitObject.StartTime, StartTime = HitObject.StartTime,
Duration = endTime - HitObject.StartTime,
Column = column, Column = column,
Duration = endTime - HitObject.StartTime Samples = HitObject.Samples,
NodeSamples = (HitObject as IHasRepeats)?.NodeSamples
}; };
if (hold.Head.Samples == null)
hold.Head.Samples = new List<HitSampleInfo>();
hold.Head.Samples.Add(new HitSampleInfo { Name = HitSampleInfo.HIT_NORMAL });
hold.Tail.Samples = HitObject.Samples;
newObject = hold;
} }
else else
{ {

View File

@ -3,6 +3,7 @@
using System; using System;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Mania.Edit.Blueprints.Components; using osu.Game.Rulesets.Mania.Edit.Blueprints.Components;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osuTK; using osuTK;
@ -46,6 +47,12 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
bodyPiece.Height = (bottomPosition - topPosition).Y; bodyPiece.Height = (bottomPosition - topPosition).Y;
} }
protected override void OnMouseUp(MouseUpEvent e)
{
base.OnMouseUp(e);
EndPlacement(true);
}
private double originalStartTime; private double originalStartTime;
public override void UpdatePosition(Vector2 screenSpacePosition) public override void UpdatePosition(Vector2 screenSpacePosition)

View File

@ -50,16 +50,10 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
return base.OnMouseDown(e); return base.OnMouseDown(e);
HitObject.Column = Column.Index; HitObject.Column = Column.Index;
BeginPlacement(TimeAt(e.ScreenSpaceMousePosition)); BeginPlacement(TimeAt(e.ScreenSpaceMousePosition), true);
return true; return true;
} }
protected override void OnMouseUp(MouseUpEvent e)
{
EndPlacement(true);
base.OnMouseUp(e);
}
public override void UpdatePosition(Vector2 screenSpacePosition) public override void UpdatePosition(Vector2 screenSpacePosition)
{ {
if (!PlacementActive) if (!PlacementActive)

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input.Events;
using osu.Game.Rulesets.Mania.Edit.Blueprints.Components; using osu.Game.Rulesets.Mania.Edit.Blueprints.Components;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
@ -26,5 +27,15 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints
Width = SnappedWidth; Width = SnappedWidth;
Position = SnappedMousePosition; Position = SnappedMousePosition;
} }
protected override bool OnMouseDown(MouseDownEvent e)
{
base.OnMouseDown(e);
// Place the note immediately.
EndPlacement(true);
return true;
}
} }
} }

View File

@ -51,7 +51,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
AddRangeInternal(new[] AddRangeInternal(new[]
{ {
bodyPiece = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HoldNoteBody, hitObject.Column), _ => new DefaultBodyPiece()) bodyPiece = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HoldNoteBody, hitObject.Column), _ => new DefaultBodyPiece
{
RelativeSizeAxes = Axes.Both
})
{ {
RelativeSizeAxes = Axes.X RelativeSizeAxes = Axes.X
}, },
@ -127,6 +130,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
bodyPiece.Anchor = bodyPiece.Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft; bodyPiece.Anchor = bodyPiece.Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
} }
public override void PlaySamples()
{
// Samples are played by the head/tail notes.
}
protected override void Update() protected override void Update()
{ {
base.Update(); base.Update();

View File

@ -34,7 +34,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
public DefaultBodyPiece() public DefaultBodyPiece()
{ {
RelativeSizeAxes = Axes.Both;
Blending = BlendingParameters.Additive; Blending = BlendingParameters.Additive;
AddLayout(subtractionCache); AddLayout(subtractionCache);

View File

@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Game.Audio;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
@ -28,6 +30,8 @@ namespace osu.Game.Rulesets.Mania.Objects
set set
{ {
duration = value; duration = value;
if (Tail != null)
Tail.StartTime = EndTime; Tail.StartTime = EndTime;
} }
} }
@ -38,7 +42,11 @@ namespace osu.Game.Rulesets.Mania.Objects
set set
{ {
base.StartTime = value; base.StartTime = value;
if (Head != null)
Head.StartTime = value; Head.StartTime = value;
if (Tail != null)
Tail.StartTime = EndTime; Tail.StartTime = EndTime;
} }
} }
@ -49,20 +57,26 @@ namespace osu.Game.Rulesets.Mania.Objects
set set
{ {
base.Column = value; base.Column = value;
if (Head != null)
Head.Column = value; Head.Column = value;
if (Tail != null)
Tail.Column = value; Tail.Column = value;
} }
} }
public List<IList<HitSampleInfo>> NodeSamples { get; set; }
/// <summary> /// <summary>
/// The head note of the hold. /// The head note of the hold.
/// </summary> /// </summary>
public readonly Note Head = new Note(); public Note Head { get; private set; }
/// <summary> /// <summary>
/// The tail note of the hold. /// The tail note of the hold.
/// </summary> /// </summary>
public readonly TailNote Tail = new TailNote(); public TailNote Tail { get; private set; }
/// <summary> /// <summary>
/// The time between ticks of this hold. /// The time between ticks of this hold.
@ -83,8 +97,19 @@ namespace osu.Game.Rulesets.Mania.Objects
createTicks(); createTicks();
AddNested(Head); AddNested(Head = new Note
AddNested(Tail); {
StartTime = StartTime,
Column = Column,
Samples = getNodeSamples(0),
});
AddNested(Tail = new TailNote
{
StartTime = EndTime,
Column = Column,
Samples = getNodeSamples((NodeSamples?.Count - 1) ?? 1),
});
} }
private void createTicks() private void createTicks()
@ -105,5 +130,8 @@ namespace osu.Game.Rulesets.Mania.Objects
public override Judgement CreateJudgement() => new IgnoreJudgement(); public override Judgement CreateJudgement() => new IgnoreJudgement();
protected override HitWindows CreateHitWindows() => HitWindows.Empty; protected override HitWindows CreateHitWindows() => HitWindows.Empty;
private IList<HitSampleInfo> getNodeSamples(int nodeIndex) =>
nodeIndex < NodeSamples?.Count ? NodeSamples[nodeIndex] : Samples;
} }
} }

View File

@ -5,11 +5,12 @@ using osu.Framework.Bindables;
using osu.Game.Rulesets.Mania.Objects.Types; using osu.Game.Rulesets.Mania.Objects.Types;
using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania.Objects namespace osu.Game.Rulesets.Mania.Objects
{ {
public abstract class ManiaHitObject : HitObject, IHasColumn public abstract class ManiaHitObject : HitObject, IHasColumn, IHasXPosition
{ {
public readonly Bindable<int> ColumnBindable = new Bindable<int>(); public readonly Bindable<int> ColumnBindable = new Bindable<int>();
@ -20,5 +21,11 @@ namespace osu.Game.Rulesets.Mania.Objects
} }
protected override HitWindows CreateHitWindows() => new ManiaHitWindows(); protected override HitWindows CreateHitWindows() => new ManiaHitWindows();
#region LegacyBeatmapEncoder
float IHasXPosition.X => Column;
#endregion
} }
} }

View File

@ -259,6 +259,23 @@ namespace osu.Game.Rulesets.Osu.Tests
assertControlPointType(2, PathType.PerfectCurve); assertControlPointType(2, PathType.PerfectCurve);
} }
[Test]
public void TestBeginPlacementWithoutReleasingMouse()
{
addMovementStep(new Vector2(200));
AddStep("press left button", () => InputManager.PressButton(MouseButton.Left));
addMovementStep(new Vector2(400, 200));
AddStep("release left button", () => InputManager.ReleaseButton(MouseButton.Left));
addClickStep(MouseButton.Right);
assertPlaced(true);
assertLength(200);
assertControlPointCount(2);
assertControlPointType(0, PathType.Linear);
}
private void addMovementStep(Vector2 position) => AddStep($"move mouse to {position}", () => InputManager.MoveMouseTo(InputManager.ToScreenSpace(position))); private void addMovementStep(Vector2 position) => AddStep($"move mouse to {position}", () => InputManager.MoveMouseTo(InputManager.ToScreenSpace(position)));
private void addClickStep(MouseButton button) private void addClickStep(MouseButton button)

View File

@ -6,6 +6,7 @@ using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components; using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osuTK; using osuTK;
using osuTK.Input;
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles
{ {
@ -28,16 +29,17 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles
circlePiece.UpdateFrom(HitObject); circlePiece.UpdateFrom(HitObject);
} }
protected override bool OnClick(ClickEvent e) protected override bool OnMouseDown(MouseDownEvent e)
{
if (e.Button == MouseButton.Left)
{ {
EndPlacement(true); EndPlacement(true);
return true; return true;
} }
public override void UpdatePosition(Vector2 screenSpacePosition) return base.OnMouseDown(e);
{ }
BeginPlacement();
HitObject.Position = ToLocalSpace(screenSpacePosition); public override void UpdatePosition(Vector2 screenSpacePosition) => HitObject.Position = ToLocalSpace(screenSpacePosition);
}
} }
} }

View File

@ -28,7 +28,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
public Action<PathControlPointPiece, MouseButtonEvent> RequestSelection; public Action<PathControlPointPiece, MouseButtonEvent> RequestSelection;
public readonly BindableBool IsSelected = new BindableBool(); public readonly BindableBool IsSelected = new BindableBool();
public readonly PathControlPoint ControlPoint; public readonly PathControlPoint ControlPoint;
private readonly Slider slider; private readonly Slider slider;
@ -146,6 +145,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
protected override bool OnDragStart(DragStartEvent e) protected override bool OnDragStart(DragStartEvent e)
{ {
if (RequestSelection == null)
return false;
if (e.Button == MouseButton.Left) if (e.Button == MouseButton.Left)
{ {
changeHandler?.BeginChange(); changeHandler?.BeginChange();

View File

@ -82,8 +82,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
} }
} }
protected override bool OnClick(ClickEvent e) protected override bool OnMouseDown(MouseDownEvent e)
{ {
if (e.Button != MouseButton.Left)
return base.OnMouseDown(e);
switch (state) switch (state)
{ {
case PlacementState.Initial: case PlacementState.Initial:
@ -91,9 +94,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
break; break;
case PlacementState.Body: case PlacementState.Body:
if (e.Button != MouseButton.Left)
break;
if (canPlaceNewControlPoint(out var lastPoint)) if (canPlaceNewControlPoint(out var lastPoint))
{ {
// Place a new point by detatching the current cursor. // Place a new point by detatching the current cursor.
@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
currentSegmentLength = 1; currentSegmentLength = 1;
} }
return true; break;
} }
return true; return true;

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
/// osu! is generally slower than taiko, so a factor is added to increase /// osu! is generally slower than taiko, so a factor is added to increase
/// speed. This must be used everywhere slider length or beat length is used. /// speed. This must be used everywhere slider length or beat length is used.
/// </summary> /// </summary>
private const float legacy_velocity_multiplier = 1.4f; public const float LEGACY_VELOCITY_MULTIPLIER = 1.4f;
/// <summary> /// <summary>
/// Because swells are easier in taiko than spinners are in osu!, /// Because swells are easier in taiko than spinners are in osu!,
@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
// Rewrite the beatmap info to add the slider velocity multiplier // Rewrite the beatmap info to add the slider velocity multiplier
original.BeatmapInfo = original.BeatmapInfo.Clone(); original.BeatmapInfo = original.BeatmapInfo.Clone();
original.BeatmapInfo.BaseDifficulty = original.BeatmapInfo.BaseDifficulty.Clone(); original.BeatmapInfo.BaseDifficulty = original.BeatmapInfo.BaseDifficulty.Clone();
original.BeatmapInfo.BaseDifficulty.SliderMultiplier *= legacy_velocity_multiplier; original.BeatmapInfo.BaseDifficulty.SliderMultiplier *= LEGACY_VELOCITY_MULTIPLIER;
Beatmap<TaikoHitObject> converted = base.ConvertBeatmap(original); Beatmap<TaikoHitObject> converted = base.ConvertBeatmap(original);
@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
double speedAdjustedBeatLength = timingPoint.BeatLength / speedAdjustment; double speedAdjustedBeatLength = timingPoint.BeatLength / speedAdjustment;
// The true distance, accounting for any repeats. This ends up being the drum roll distance later // The true distance, accounting for any repeats. This ends up being the drum roll distance later
double distance = distanceData.Distance * spans * legacy_velocity_multiplier; double distance = distanceData.Distance * spans * LEGACY_VELOCITY_MULTIPLIER;
// The velocity of the taiko hit object - calculated as the velocity of a drum roll // The velocity of the taiko hit object - calculated as the velocity of a drum roll
double taikoVelocity = taiko_base_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier / speedAdjustedBeatLength; double taikoVelocity = taiko_base_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier / speedAdjustedBeatLength;

View File

@ -3,15 +3,20 @@
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using System; using System;
using System.Collections.Generic;
using osu.Game.Audio;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Beatmaps;
using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Judgements;
using osuTK;
namespace osu.Game.Rulesets.Taiko.Objects namespace osu.Game.Rulesets.Taiko.Objects
{ {
public class DrumRoll : TaikoHitObject, IHasEndTime public class DrumRoll : TaikoHitObject, IHasCurve
{ {
/// <summary> /// <summary>
/// Drum roll distance that results in a duration of 1 speed-adjusted beat length. /// Drum roll distance that results in a duration of 1 speed-adjusted beat length.
@ -26,6 +31,11 @@ namespace osu.Game.Rulesets.Taiko.Objects
public double Duration { get; set; } public double Duration { get; set; }
/// <summary>
/// Velocity of this <see cref="DrumRoll"/>.
/// </summary>
public double Velocity { get; private set; }
/// <summary> /// <summary>
/// Numer of ticks per beat length. /// Numer of ticks per beat length.
/// </summary> /// </summary>
@ -54,6 +64,10 @@ namespace osu.Game.Rulesets.Taiko.Objects
base.ApplyDefaultsToSelf(controlPointInfo, difficulty); base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
DifficultyControlPoint difficultyPoint = controlPointInfo.DifficultyPointAt(StartTime);
double scoringDistance = base_distance * difficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier;
Velocity = scoringDistance / timingPoint.BeatLength;
tickSpacing = timingPoint.BeatLength / TickRate; tickSpacing = timingPoint.BeatLength / TickRate;
overallDifficulty = difficulty.OverallDifficulty; overallDifficulty = difficulty.OverallDifficulty;
@ -93,5 +107,18 @@ namespace osu.Game.Rulesets.Taiko.Objects
public override Judgement CreateJudgement() => new TaikoDrumRollJudgement(); public override Judgement CreateJudgement() => new TaikoDrumRollJudgement();
protected override HitWindows CreateHitWindows() => HitWindows.Empty; protected override HitWindows CreateHitWindows() => HitWindows.Empty;
#region LegacyBeatmapEncoder
double IHasDistance.Distance => Duration * Velocity;
int IHasRepeats.RepeatCount { get => 0; set { } }
List<IList<HitSampleInfo>> IHasRepeats.NodeSamples => new List<IList<HitSampleInfo>>();
SliderPath IHasCurve.Path
=> new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(1) }, ((IHasDistance)this).Distance / TaikoBeatmapConverter.LEGACY_VELOCITY_MULTIPLIER);
#endregion
} }
} }

View File

@ -55,7 +55,6 @@ namespace osu.Game.Rulesets.Taiko.UI
Name = "Right area", Name = "Right area",
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Both, RelativePositionAxes = Axes.Both,
Masking = true,
Children = new Drawable[] Children = new Drawable[]
{ {
new Container new Container

View File

@ -5,7 +5,7 @@ using NUnit.Framework;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Screens.Edit; using osu.Game.Screens.Edit;
namespace osu.Game.Tests.Editor namespace osu.Game.Tests.Editing
{ {
[TestFixture] [TestFixture]
public class EditorChangeHandlerTest public class EditorChangeHandlerTest

View File

@ -17,7 +17,7 @@ using osu.Game.Screens.Edit;
using osuTK; using osuTK;
using Decoder = osu.Game.Beatmaps.Formats.Decoder; using Decoder = osu.Game.Beatmaps.Formats.Decoder;
namespace osu.Game.Tests.Editor namespace osu.Game.Tests.Editing
{ {
[TestFixture] [TestFixture]
public class LegacyEditorBeatmapPatcherTest public class LegacyEditorBeatmapPatcherTest

View File

@ -14,7 +14,7 @@ using osu.Game.Rulesets.Osu.Edit;
using osu.Game.Screens.Edit; using osu.Game.Screens.Edit;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
namespace osu.Game.Tests.Editor namespace osu.Game.Tests.Editing
{ {
[HeadlessTest] [HeadlessTest]
public class TestSceneHitObjectComposerDistanceSnapping : EditorClockTestScene public class TestSceneHitObjectComposerDistanceSnapping : EditorClockTestScene

View File

@ -0,0 +1,30 @@
osu file format v14
[General]
SampleSet: Normal
StackLeniency: 0.7
Mode: 2
[Difficulty]
HPDrainRate:3
CircleSize:5
OverallDifficulty:8
ApproachRate:8
SliderMultiplier:3.59999990463257
SliderTickRate:2
[TimingPoints]
24,352.941176470588,4,1,1,100,1,0
6376,-50,4,1,1,100,0,0
[HitObjects]
32,183,24,5,0,0:0:0:0:
106,123,200,1,10,0:0:0:0:
199,108,376,1,2,0:0:0:0:
305,105,553,5,4,0:0:0:0:
386,112,729,1,14,0:0:0:0:
486,197,906,5,12,0:0:0:0:
14,199,1082,2,0,L|473:198,1,449.999988079071
14,199,1700,6,6,P|248:33|490:222,1,629.9999833107,0|8,0:0|0:0,0:0:0:0:
10,190,2494,2,8,B|252:29|254:335|468:167,1,449.999988079071,10|12,0:0|0:0,0:0:0:0:
256,192,3112,12,0,3906,0:0:0:0:

View File

@ -0,0 +1,39 @@
osu file format v14
[General]
SampleSet: Normal
StackLeniency: 0.7
Mode: 3
[Difficulty]
HPDrainRate:3
CircleSize:5
OverallDifficulty:8
ApproachRate:8
SliderMultiplier:3.59999990463257
SliderTickRate:2
[TimingPoints]
24,352.941176470588,4,1,1,100,1,0
6376,-50,4,1,1,100,0,0
[HitObjects]
51,192,24,1,0,0:0:0:0:
153,192,200,1,0,0:0:0:0:
358,192,376,1,0,0:0:0:0:
460,192,553,1,0,0:0:0:0:
460,192,729,128,0,1435:0:0:0:0:
358,192,906,128,0,1612:0:0:0:0:
256,192,1082,128,0,1788:0:0:0:0:
153,192,1259,128,0,1965:0:0:0:0:
51,192,1435,128,0,2141:0:0:0:0:
51,192,2318,1,12,0:0:0:0:
153,192,2318,1,4,0:0:0:0:
256,192,2318,1,6,0:0:0:0:
358,192,2318,1,14,0:0:0:0:
460,192,2318,1,0,0:0:0:0:
51,192,2494,128,0,2582:0:0:0:0:
153,192,2494,128,14,2582:0:0:0:0:
256,192,2494,128,6,2582:0:0:0:0:
358,192,2494,128,4,2582:0:0:0:0:
460,192,2494,128,12,2582:0:0:0:0:

View File

@ -0,0 +1,42 @@
osu file format v14
[General]
SampleSet: Normal
StackLeniency: 0.7
Mode: 1
[Difficulty]
HPDrainRate:3
CircleSize:5
OverallDifficulty:8
ApproachRate:8
SliderMultiplier:3.59999990463257
SliderTickRate:2
[TimingPoints]
24,352.941176470588,4,1,1,100,1,0
6376,-50,4,1,1,100,0,0
[HitObjects]
231,129,24,1,0,0:0:0:0:
231,129,200,1,0,0:0:0:0:
231,129,376,1,0,0:0:0:0:
231,129,553,1,0,0:0:0:0:
231,129,729,1,0,0:0:0:0:
373,132,906,1,4,0:0:0:0:
373,132,1082,1,4,0:0:0:0:
373,132,1259,1,4,0:0:0:0:
373,132,1435,1,4,0:0:0:0:
231,129,1788,1,8,0:0:0:0:
231,129,1964,1,8,0:0:0:0:
231,129,2140,1,8,0:0:0:0:
231,129,2317,1,8,0:0:0:0:
231,129,2493,1,8,0:0:0:0:
373,132,2670,1,12,0:0:0:0:
373,132,2846,1,12,0:0:0:0:
373,132,3023,1,12,0:0:0:0:
373,132,3199,1,12,0:0:0:0:
51,189,3553,2,0,L|150:188,1,89.9999976158143
52,191,3906,2,0,L|512:189,1,449.999988079071
26,196,4612,2,4,L|501:195,1,449.999988079071
17,242,5318,2,10,P|250:69|495:243,1,629.9999833107,0|8,0:0|0:0,0:0:0:0:

View File

@ -14,7 +14,7 @@ using osu.Game.Screens.Edit.Compose.Components;
using osuTK; using osuTK;
using osuTK.Input; using osuTK.Input;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editing
{ {
public class TestSceneBeatDivisorControl : OsuManualInputManagerTestScene public class TestSceneBeatDivisorControl : OsuManualInputManagerTestScene
{ {

View File

@ -9,7 +9,7 @@ using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Screens.Edit; using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Compose; using osu.Game.Screens.Edit.Compose;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editing
{ {
[TestFixture] [TestFixture]
public class TestSceneComposeScreen : EditorClockTestScene public class TestSceneComposeScreen : EditorClockTestScene

View File

@ -13,7 +13,7 @@ using osu.Game.Screens.Edit.Compose.Components;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editing
{ {
public class TestSceneDistanceSnapGrid : EditorClockTestScene public class TestSceneDistanceSnapGrid : EditorClockTestScene
{ {

View File

@ -4,33 +4,27 @@
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Screens.Edit; using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Compose.Components.Timeline;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editing
{ {
public class TestSceneEditorChangeStates : ScreenTestScene public class TestSceneEditorChangeStates : EditorTestScene
{ {
public TestSceneEditorChangeStates()
: base(new OsuRuleset())
{
}
private EditorBeatmap editorBeatmap; private EditorBeatmap editorBeatmap;
private TestEditor editor;
public override void SetUpSteps() public override void SetUpSteps()
{ {
base.SetUpSteps(); base.SetUpSteps();
AddStep("load editor", () => AddStep("get beatmap", () => editorBeatmap = Editor.ChildrenOfType<EditorBeatmap>().Single());
{
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
LoadScreen(editor = new TestEditor());
});
AddUntilStep("wait for editor to load", () => editor.ChildrenOfType<HitObjectComposer>().FirstOrDefault()?.IsLoaded == true
&& editor.ChildrenOfType<TimelineArea>().FirstOrDefault()?.IsLoaded == true);
AddStep("get beatmap", () => editorBeatmap = editor.ChildrenOfType<EditorBeatmap>().Single());
} }
[Test] [Test]
@ -158,11 +152,13 @@ namespace osu.Game.Tests.Visual.Editor
AddAssert("no hitobject added", () => addedObject == null); AddAssert("no hitobject added", () => addedObject == null);
} }
private void addUndoSteps() => AddStep("undo", () => editor.Undo()); private void addUndoSteps() => AddStep("undo", () => ((TestEditor)Editor).Undo());
private void addRedoSteps() => AddStep("redo", () => editor.Redo()); private void addRedoSteps() => AddStep("redo", () => ((TestEditor)Editor).Redo());
private class TestEditor : Screens.Edit.Editor protected override Editor CreateEditor() => new TestEditor();
private class TestEditor : Editor
{ {
public new void Undo() => base.Undo(); public new void Undo() => base.Undo();

View File

@ -7,7 +7,7 @@ using NUnit.Framework;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Screens.Edit.Components.RadioButtons; using osu.Game.Screens.Edit.Components.RadioButtons;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editing
{ {
[TestFixture] [TestFixture]
public class TestSceneEditorComposeRadioButtons : OsuTestScene public class TestSceneEditorComposeRadioButtons : OsuTestScene

View File

@ -10,7 +10,7 @@ using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Edit.Components.Menus; using osu.Game.Screens.Edit.Components.Menus;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editing
{ {
[TestFixture] [TestFixture]
public class TestSceneEditorMenuBar : OsuTestScene public class TestSceneEditorMenuBar : OsuTestScene

View File

@ -13,7 +13,7 @@ using osu.Game.Rulesets.Osu.Objects;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editing
{ {
[TestFixture] [TestFixture]
public class TestSceneEditorSeekSnapping : EditorClockTestScene public class TestSceneEditorSeekSnapping : EditorClockTestScene

View File

@ -10,7 +10,7 @@ using osu.Game.Rulesets.Osu;
using osu.Game.Screens.Edit.Components.Timelines.Summary; using osu.Game.Screens.Edit.Components.Timelines.Summary;
using osuTK; using osuTK;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editing
{ {
[TestFixture] [TestFixture]
public class TestSceneEditorSummaryTimeline : EditorClockTestScene public class TestSceneEditorSummaryTimeline : EditorClockTestScene

View File

@ -20,7 +20,7 @@ using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Compose.Components; using osu.Game.Screens.Edit.Compose.Components;
using osuTK; using osuTK;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editing
{ {
[TestFixture] [TestFixture]
public class TestSceneHitObjectComposer : EditorClockTestScene public class TestSceneHitObjectComposer : EditorClockTestScene

View File

@ -9,7 +9,7 @@ using osu.Game.Beatmaps;
using osu.Game.Screens.Edit.Components; using osu.Game.Screens.Edit.Components;
using osuTK; using osuTK;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editing
{ {
[TestFixture] [TestFixture]
public class TestScenePlaybackControl : OsuTestScene public class TestScenePlaybackControl : OsuTestScene

View File

@ -7,7 +7,7 @@ using NUnit.Framework;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Screens.Edit.Compose.Components.Timeline; using osu.Game.Screens.Edit.Compose.Components.Timeline;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editing
{ {
[TestFixture] [TestFixture]
public class TestSceneTimelineBlueprintContainer : TimelineTestScene public class TestSceneTimelineBlueprintContainer : TimelineTestScene

View File

@ -8,7 +8,7 @@ using osu.Game.Screens.Edit.Compose.Components;
using osu.Game.Screens.Edit.Compose.Components.Timeline; using osu.Game.Screens.Edit.Compose.Components.Timeline;
using osuTK; using osuTK;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editing
{ {
[TestFixture] [TestFixture]
public class TestSceneTimelineTickDisplay : TimelineTestScene public class TestSceneTimelineTickDisplay : TimelineTestScene

View File

@ -9,7 +9,7 @@ using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Screens.Edit; using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Timing; using osu.Game.Screens.Edit.Timing;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editing
{ {
[TestFixture] [TestFixture]
public class TestSceneTimingScreen : EditorClockTestScene public class TestSceneTimingScreen : EditorClockTestScene

View File

@ -14,7 +14,7 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editing
{ {
[TestFixture] [TestFixture]
public class TestSceneWaveform : OsuTestScene public class TestSceneWaveform : OsuTestScene

View File

@ -15,7 +15,7 @@ using osu.Game.Screens.Edit.Compose.Components.Timeline;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editing
{ {
public class TestSceneZoomableScrollContainer : OsuManualInputManagerTestScene public class TestSceneZoomableScrollContainer : OsuManualInputManagerTestScene
{ {

View File

@ -18,7 +18,7 @@ using osu.Game.Screens.Edit.Compose.Components.Timeline;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editing
{ {
public abstract class TimelineTestScene : EditorClockTestScene public abstract class TimelineTestScene : EditorClockTestScene
{ {

View File

@ -5,13 +5,17 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Overlays.Toolbar; using osu.Game.Overlays.Toolbar;
using osu.Game.Rulesets;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Menus namespace osu.Game.Tests.Visual.Menus
{ {
[TestFixture] [TestFixture]
public class TestSceneToolbar : OsuTestScene public class TestSceneToolbar : OsuManualInputManagerTestScene
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {
@ -21,24 +25,62 @@ namespace osu.Game.Tests.Visual.Menus
typeof(ToolbarNotificationButton), typeof(ToolbarNotificationButton),
}; };
public TestSceneToolbar() private Toolbar toolbar;
{
var toolbar = new Toolbar { State = { Value = Visibility.Visible } };
ToolbarNotificationButton notificationButton = null;
AddStep("create toolbar", () => [Resolved]
private RulesetStore rulesets { get; set; }
[SetUp]
public void SetUp() => Schedule(() =>
{ {
Add(toolbar); Child = toolbar = new Toolbar { State = { Value = Visibility.Visible } };
notificationButton = toolbar.Children.OfType<FillFlowContainer>().Last().Children.OfType<ToolbarNotificationButton>().First();
}); });
void setNotifications(int count) => AddStep($"set notification count to {count}", () => notificationButton.NotificationCount.Value = count); [Test]
public void TestNotificationCounter()
{
ToolbarNotificationButton notificationButton = null;
AddStep("retrieve notification button", () => notificationButton = toolbar.ChildrenOfType<ToolbarNotificationButton>().Single());
setNotifications(1); setNotifications(1);
setNotifications(2); setNotifications(2);
setNotifications(3); setNotifications(3);
setNotifications(0); setNotifications(0);
setNotifications(144); setNotifications(144);
void setNotifications(int count)
=> AddStep($"set notification count to {count}",
() => notificationButton.NotificationCount.Value = count);
}
[TestCase(false)]
[TestCase(true)]
public void TestRulesetSwitchingShortcut(bool toolbarHidden)
{
ToolbarRulesetSelector rulesetSelector = null;
if (toolbarHidden)
AddStep("hide toolbar", () => toolbar.Hide());
AddStep("retrieve ruleset selector", () => rulesetSelector = toolbar.ChildrenOfType<ToolbarRulesetSelector>().Single());
for (int i = 0; i < 4; i++)
{
var expected = rulesets.AvailableRulesets.ElementAt(i);
var numberKey = Key.Number1 + i;
AddStep($"switch to ruleset {i} via shortcut", () =>
{
InputManager.PressKey(Key.ControlLeft);
InputManager.PressKey(numberKey);
InputManager.ReleaseKey(Key.ControlLeft);
InputManager.ReleaseKey(numberKey);
});
AddUntilStep("ruleset switched", () => rulesetSelector.Current.Value.Equals(expected));
}
} }
} }
} }

View File

@ -13,6 +13,7 @@ using osu.Game.Beatmaps.Legacy;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osuTK;
namespace osu.Game.Beatmaps.Formats namespace osu.Game.Beatmaps.Formats
{ {
@ -124,7 +125,12 @@ namespace osu.Game.Beatmaps.Formats
writer.WriteLine(FormattableString.Invariant($"CircleSize: {beatmap.BeatmapInfo.BaseDifficulty.CircleSize}")); writer.WriteLine(FormattableString.Invariant($"CircleSize: {beatmap.BeatmapInfo.BaseDifficulty.CircleSize}"));
writer.WriteLine(FormattableString.Invariant($"OverallDifficulty: {beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty}")); writer.WriteLine(FormattableString.Invariant($"OverallDifficulty: {beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty}"));
writer.WriteLine(FormattableString.Invariant($"ApproachRate: {beatmap.BeatmapInfo.BaseDifficulty.ApproachRate}")); writer.WriteLine(FormattableString.Invariant($"ApproachRate: {beatmap.BeatmapInfo.BaseDifficulty.ApproachRate}"));
writer.WriteLine(FormattableString.Invariant($"SliderMultiplier: {beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier}"));
// Taiko adjusts the slider multiplier (see: TaikoBeatmapConverter.LEGACY_VELOCITY_MULTIPLIER)
writer.WriteLine(beatmap.BeatmapInfo.RulesetID == 1
? FormattableString.Invariant($"SliderMultiplier: {beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier / 1.4f}")
: FormattableString.Invariant($"SliderMultiplier: {beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier}"));
writer.WriteLine(FormattableString.Invariant($"SliderTickRate: {beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate}")); writer.WriteLine(FormattableString.Invariant($"SliderTickRate: {beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate}"));
} }
@ -197,51 +203,63 @@ namespace osu.Game.Beatmaps.Formats
writer.WriteLine("[HitObjects]"); writer.WriteLine("[HitObjects]");
// TODO: implement other legacy rulesets foreach (var h in beatmap.HitObjects)
handleHitObject(writer, h);
}
private void handleHitObject(TextWriter writer, HitObject hitObject)
{
Vector2 position = new Vector2(256, 192);
switch (beatmap.BeatmapInfo.RulesetID) switch (beatmap.BeatmapInfo.RulesetID)
{ {
case 0: case 0:
foreach (var h in beatmap.HitObjects) position = ((IHasPosition)hitObject).Position;
handleOsuHitObject(writer, h); break;
case 2:
position.X = ((IHasXPosition)hitObject).X * 512;
break;
case 3:
int totalColumns = (int)Math.Max(1, beatmap.BeatmapInfo.BaseDifficulty.CircleSize);
position.X = (int)Math.Ceiling(((IHasXPosition)hitObject).X * (512f / totalColumns));
break; break;
} }
}
private void handleOsuHitObject(TextWriter writer, HitObject hitObject) writer.Write(FormattableString.Invariant($"{position.X},"));
{ writer.Write(FormattableString.Invariant($"{position.Y},"));
var positionData = (IHasPosition)hitObject;
writer.Write(FormattableString.Invariant($"{positionData.X},"));
writer.Write(FormattableString.Invariant($"{positionData.Y},"));
writer.Write(FormattableString.Invariant($"{hitObject.StartTime},")); writer.Write(FormattableString.Invariant($"{hitObject.StartTime},"));
writer.Write(FormattableString.Invariant($"{(int)getObjectType(hitObject)},")); writer.Write(FormattableString.Invariant($"{(int)getObjectType(hitObject)},"));
writer.Write(FormattableString.Invariant($"{(int)toLegacyHitSoundType(hitObject.Samples)},"));
writer.Write(hitObject is IHasCurve
? FormattableString.Invariant($"0,")
: FormattableString.Invariant($"{(int)toLegacyHitSoundType(hitObject.Samples)},"));
if (hitObject is IHasCurve curveData) if (hitObject is IHasCurve curveData)
{ {
addCurveData(writer, curveData, positionData); addCurveData(writer, curveData, position);
writer.Write(getSampleBank(hitObject.Samples, zeroBanks: true)); writer.Write(getSampleBank(hitObject.Samples, zeroBanks: true));
} }
else else
{ {
if (hitObject is IHasEndTime endTimeData) if (hitObject is IHasEndTime _)
writer.Write(FormattableString.Invariant($"{endTimeData.EndTime},")); addEndTimeData(writer, hitObject);
writer.Write(getSampleBank(hitObject.Samples)); writer.Write(getSampleBank(hitObject.Samples));
} }
writer.WriteLine(); writer.WriteLine();
} }
private static LegacyHitObjectType getObjectType(HitObject hitObject) private LegacyHitObjectType getObjectType(HitObject hitObject)
{ {
var comboData = (IHasCombo)hitObject; LegacyHitObjectType type = 0;
var type = (LegacyHitObjectType)(comboData.ComboOffset << 4); if (hitObject is IHasCombo combo)
{
type = (LegacyHitObjectType)(combo.ComboOffset << 4);
if (comboData.NewCombo) type |= LegacyHitObjectType.NewCombo; if (combo.NewCombo)
type |= LegacyHitObjectType.NewCombo;
}
switch (hitObject) switch (hitObject)
{ {
@ -250,6 +268,9 @@ namespace osu.Game.Beatmaps.Formats
break; break;
case IHasEndTime _: case IHasEndTime _:
if (beatmap.BeatmapInfo.RulesetID == 3)
type |= LegacyHitObjectType.Hold;
else
type |= LegacyHitObjectType.Spinner; type |= LegacyHitObjectType.Spinner;
break; break;
@ -261,7 +282,7 @@ namespace osu.Game.Beatmaps.Formats
return type; return type;
} }
private void addCurveData(TextWriter writer, IHasCurve curveData, IHasPosition positionData) private void addCurveData(TextWriter writer, IHasCurve curveData, Vector2 position)
{ {
PathType? lastType = null; PathType? lastType = null;
@ -297,13 +318,13 @@ namespace osu.Game.Beatmaps.Formats
else else
{ {
// New segment with the same type - duplicate the control point // New segment with the same type - duplicate the control point
writer.Write(FormattableString.Invariant($"{positionData.X + point.Position.Value.X}:{positionData.Y + point.Position.Value.Y}|")); writer.Write(FormattableString.Invariant($"{position.X + point.Position.Value.X}:{position.Y + point.Position.Value.Y}|"));
} }
} }
if (i != 0) if (i != 0)
{ {
writer.Write(FormattableString.Invariant($"{positionData.X + point.Position.Value.X}:{positionData.Y + point.Position.Value.Y}")); writer.Write(FormattableString.Invariant($"{position.X + point.Position.Value.X}:{position.Y + point.Position.Value.Y}"));
writer.Write(i != curveData.Path.ControlPoints.Count - 1 ? "|" : ","); writer.Write(i != curveData.Path.ControlPoints.Count - 1 ? "|" : ",");
} }
} }
@ -324,6 +345,20 @@ namespace osu.Game.Beatmaps.Formats
} }
} }
private void addEndTimeData(TextWriter writer, HitObject hitObject)
{
var endTimeData = (IHasEndTime)hitObject;
var type = getObjectType(hitObject);
char suffix = ',';
// Holds write the end time as if it's part of sample data.
if (type == LegacyHitObjectType.Hold)
suffix = ':';
writer.Write(FormattableString.Invariant($"{endTimeData.EndTime}{suffix}"));
}
private string getSampleBank(IList<HitSampleInfo> samples, bool banksOnly = false, bool zeroBanks = false) private string getSampleBank(IList<HitSampleInfo> samples, bool banksOnly = false, bool zeroBanks = false)
{ {
LegacySampleBank normalBank = toLegacySampleBank(samples.SingleOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL)?.Bank); LegacySampleBank normalBank = toLegacySampleBank(samples.SingleOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL)?.Bank);

View File

@ -153,7 +153,7 @@ namespace osu.Game.Overlays.Comments
request?.Cancel(); request?.Cancel();
loadCancellation?.Cancel(); loadCancellation?.Cancel();
request = new GetCommentsRequest(id.Value, type.Value, Sort.Value, currentPage++, 0); request = new GetCommentsRequest(id.Value, type.Value, Sort.Value, currentPage++, 0);
request.Success += onSuccess; request.Success += res => Schedule(() => onSuccess(res));
api.PerformAsync(request); api.PerformAsync(request);
} }

View File

@ -33,6 +33,9 @@ namespace osu.Game.Overlays.Toolbar
private readonly Bindable<OverlayActivation> overlayActivationMode = new Bindable<OverlayActivation>(OverlayActivation.All); private readonly Bindable<OverlayActivation> overlayActivationMode = new Bindable<OverlayActivation>(OverlayActivation.All);
// Toolbar components like RulesetSelector should receive keyboard input events even when the toolbar is hidden.
public override bool PropagateNonPositionalInputSubTree => true;
public Toolbar() public Toolbar()
{ {
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
@ -148,7 +151,7 @@ namespace osu.Game.Overlays.Toolbar
protected override void PopOut() protected override void PopOut()
{ {
userButton?.StateContainer.Hide(); userButton.StateContainer?.Hide();
this.MoveToY(-DrawSize.Y, transition_time, Easing.OutQuint); this.MoveToY(-DrawSize.Y, transition_time, Easing.OutQuint);
this.FadeOut(transition_time); this.FadeOut(transition_time);

View File

@ -43,6 +43,15 @@ namespace osu.Game.Rulesets.UI
return true; return true;
} }
public virtual void Clear(bool disposeChildren = true)
{
ClearInternal(disposeChildren);
foreach (var kvp in startTimeMap)
kvp.Value.bindable.UnbindAll();
startTimeMap.Clear();
}
public int IndexOf(DrawableHitObject hitObject) => IndexOfInternal(hitObject); public int IndexOf(DrawableHitObject hitObject) => IndexOfInternal(hitObject);
private void onStartTimeChanged(DrawableHitObject hitObject) private void onStartTimeChanged(DrawableHitObject hitObject)

View File

@ -58,6 +58,14 @@ namespace osu.Game.Rulesets.UI.Scrolling
return result; return result;
} }
public override void Clear(bool disposeChildren = true)
{
base.Clear(disposeChildren);
initialStateCache.Invalidate();
hitObjectInitialStateCache.Clear();
}
private float scrollLength; private float scrollLength;
protected override void Update() protected override void Update()

View File

@ -60,9 +60,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
waveform.Waveform = b.NewValue.Waveform; waveform.Waveform = b.NewValue.Waveform;
track = b.NewValue.Track; track = b.NewValue.Track;
if (track.Length > 0)
{
MaxZoom = getZoomLevelForVisibleMilliseconds(500); MaxZoom = getZoomLevelForVisibleMilliseconds(500);
MinZoom = getZoomLevelForVisibleMilliseconds(10000); MinZoom = getZoomLevelForVisibleMilliseconds(10000);
Zoom = getZoomLevelForVisibleMilliseconds(2000); Zoom = getZoomLevelForVisibleMilliseconds(2000);
}
}, true); }, true);
} }
@ -135,7 +138,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
private void scrollToTrackTime() private void scrollToTrackTime()
{ {
if (!track.IsLoaded) if (!track.IsLoaded || track.Length == 0)
return; return;
ScrollTo((float)(adjustableClock.CurrentTime / track.Length) * Content.DrawWidth, false); ScrollTo((float)(adjustableClock.CurrentTime / track.Length) * Content.DrawWidth, false);

View File

@ -23,6 +23,7 @@ namespace osu.Game.Tests.Beatmaps
HitObjects = baseBeatmap.HitObjects; HitObjects = baseBeatmap.HitObjects;
BeatmapInfo.Ruleset = ruleset; BeatmapInfo.Ruleset = ruleset;
BeatmapInfo.RulesetID = ruleset.ID ?? 0;
BeatmapInfo.BeatmapSet.Metadata = BeatmapInfo.Metadata; BeatmapInfo.BeatmapSet.Metadata = BeatmapInfo.Metadata;
BeatmapInfo.BeatmapSet.Beatmaps = new List<BeatmapInfo> { BeatmapInfo }; BeatmapInfo.BeatmapSet.Beatmaps = new List<BeatmapInfo> { BeatmapInfo };
BeatmapInfo.BeatmapSet.OnlineInfo = new BeatmapSetOnlineInfo BeatmapInfo.BeatmapSet.OnlineInfo = new BeatmapSetOnlineInfo

View File

@ -3,9 +3,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Testing;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Edit;
using osu.Game.Screens.Edit; using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Compose.Components.Timeline;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
@ -13,6 +17,8 @@ namespace osu.Game.Tests.Visual
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(Editor), typeof(EditorScreen) }; public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(Editor), typeof(EditorScreen) };
protected Editor Editor { get; private set; }
private readonly Ruleset ruleset; private readonly Ruleset ruleset;
protected EditorTestScene(Ruleset ruleset) protected EditorTestScene(Ruleset ruleset)
@ -30,7 +36,11 @@ namespace osu.Game.Tests.Visual
{ {
base.SetUpSteps(); base.SetUpSteps();
AddStep("Load editor", () => LoadScreen(new Editor())); AddStep("load editor", () => LoadScreen(Editor = CreateEditor()));
} AddUntilStep("wait for editor to load", () => Editor.ChildrenOfType<HitObjectComposer>().FirstOrDefault()?.IsLoaded == true
&& Editor.ChildrenOfType<TimelineArea>().FirstOrDefault()?.IsLoaded == true);
}
protected virtual Editor CreateEditor() => new Editor();
} }
} }

View File

@ -30,6 +30,11 @@ namespace osu.Game.Tests.Visual
set => scrollingInfo.TimeRange.Value = value; set => scrollingInfo.TimeRange.Value = value;
} }
public ScrollingDirection Direction
{
set => scrollingInfo.Direction.Value = value;
}
public IScrollingInfo ScrollingInfo => scrollingInfo; public IScrollingInfo ScrollingInfo => scrollingInfo;
[Cached(Type = typeof(IScrollingInfo))] [Cached(Type = typeof(IScrollingInfo))]