mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 10:33:30 +08:00
Merge branch 'master' into pp-counter-fixed-width
This commit is contained in:
commit
759450502f
@ -0,0 +1,185 @@
|
||||
// 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.
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
{
|
||||
public class TestSceneSliderStreamConversion : TestSceneOsuEditor
|
||||
{
|
||||
private BindableBeatDivisor beatDivisor => (BindableBeatDivisor)Editor.Dependencies.Get(typeof(BindableBeatDivisor));
|
||||
|
||||
[Test]
|
||||
public void TestSimpleConversion()
|
||||
{
|
||||
Slider slider = null;
|
||||
|
||||
AddStep("select first slider", () =>
|
||||
{
|
||||
slider = (Slider)EditorBeatmap.HitObjects.First(h => h is Slider);
|
||||
EditorClock.Seek(slider.StartTime);
|
||||
EditorBeatmap.SelectedHitObjects.Add(slider);
|
||||
});
|
||||
|
||||
convertToStream();
|
||||
|
||||
AddAssert("stream created", () => streamCreatedFor(slider,
|
||||
(time: 0, pathPosition: 0),
|
||||
(time: 0.25, pathPosition: 0.25),
|
||||
(time: 0.5, pathPosition: 0.5),
|
||||
(time: 0.75, pathPosition: 0.75),
|
||||
(time: 1, pathPosition: 1)));
|
||||
|
||||
AddStep("undo", () => Editor.Undo());
|
||||
AddAssert("slider restored", () => sliderRestored(slider));
|
||||
|
||||
AddStep("select first slider", () =>
|
||||
{
|
||||
slider = (Slider)EditorBeatmap.HitObjects.First(h => h is Slider);
|
||||
EditorClock.Seek(slider.StartTime);
|
||||
EditorBeatmap.SelectedHitObjects.Add(slider);
|
||||
});
|
||||
AddStep("change beat divisor", () => beatDivisor.Value = 8);
|
||||
|
||||
convertToStream();
|
||||
AddAssert("stream created", () => streamCreatedFor(slider,
|
||||
(time: 0, pathPosition: 0),
|
||||
(time: 0.125, pathPosition: 0.125),
|
||||
(time: 0.25, pathPosition: 0.25),
|
||||
(time: 0.375, pathPosition: 0.375),
|
||||
(time: 0.5, pathPosition: 0.5),
|
||||
(time: 0.625, pathPosition: 0.625),
|
||||
(time: 0.75, pathPosition: 0.75),
|
||||
(time: 0.875, pathPosition: 0.875),
|
||||
(time: 1, pathPosition: 1)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestConversionWithNonMatchingDivisor()
|
||||
{
|
||||
Slider slider = null;
|
||||
|
||||
AddStep("select second slider", () =>
|
||||
{
|
||||
slider = (Slider)EditorBeatmap.HitObjects.Where(h => h is Slider).ElementAt(1);
|
||||
EditorClock.Seek(slider.StartTime);
|
||||
EditorBeatmap.SelectedHitObjects.Add(slider);
|
||||
});
|
||||
AddStep("change beat divisor", () => beatDivisor.Value = 3);
|
||||
|
||||
convertToStream();
|
||||
|
||||
AddAssert("stream created", () => streamCreatedFor(slider,
|
||||
(time: 0, pathPosition: 0),
|
||||
(time: 2 / 3d, pathPosition: 2 / 3d)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestConversionWithRepeats()
|
||||
{
|
||||
Slider slider = null;
|
||||
|
||||
AddStep("select first slider with repeats", () =>
|
||||
{
|
||||
slider = (Slider)EditorBeatmap.HitObjects.First(h => h is Slider s && s.RepeatCount > 0);
|
||||
EditorClock.Seek(slider.StartTime);
|
||||
EditorBeatmap.SelectedHitObjects.Add(slider);
|
||||
});
|
||||
AddStep("change beat divisor", () => beatDivisor.Value = 2);
|
||||
|
||||
convertToStream();
|
||||
|
||||
AddAssert("stream created", () => streamCreatedFor(slider,
|
||||
(time: 0, pathPosition: 0),
|
||||
(time: 0.25, pathPosition: 0.5),
|
||||
(time: 0.5, pathPosition: 1),
|
||||
(time: 0.75, pathPosition: 0.5),
|
||||
(time: 1, pathPosition: 0)));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestConversionPreservesSliderProperties()
|
||||
{
|
||||
Slider slider = null;
|
||||
|
||||
AddStep("select second new-combo-starting slider", () =>
|
||||
{
|
||||
slider = (Slider)EditorBeatmap.HitObjects.Where(h => h is Slider s && s.NewCombo).ElementAt(1);
|
||||
EditorClock.Seek(slider.StartTime);
|
||||
EditorBeatmap.SelectedHitObjects.Add(slider);
|
||||
});
|
||||
|
||||
convertToStream();
|
||||
|
||||
AddAssert("stream created", () => streamCreatedFor(slider,
|
||||
(time: 0, pathPosition: 0),
|
||||
(time: 0.25, pathPosition: 0.25),
|
||||
(time: 0.5, pathPosition: 0.5),
|
||||
(time: 0.75, pathPosition: 0.75),
|
||||
(time: 1, pathPosition: 1)));
|
||||
|
||||
AddStep("undo", () => Editor.Undo());
|
||||
AddAssert("slider restored", () => sliderRestored(slider));
|
||||
}
|
||||
|
||||
private void convertToStream()
|
||||
{
|
||||
AddStep("convert to stream", () =>
|
||||
{
|
||||
InputManager.PressKey(Key.LControl);
|
||||
InputManager.PressKey(Key.LShift);
|
||||
InputManager.Key(Key.F);
|
||||
InputManager.ReleaseKey(Key.LShift);
|
||||
InputManager.ReleaseKey(Key.LControl);
|
||||
});
|
||||
}
|
||||
|
||||
private bool streamCreatedFor(Slider slider, params (double time, double pathPosition)[] expectedCircles)
|
||||
{
|
||||
if (EditorBeatmap.HitObjects.Contains(slider))
|
||||
return false;
|
||||
|
||||
foreach ((double expectedTime, double expectedPathPosition) in expectedCircles)
|
||||
{
|
||||
double time = slider.StartTime + slider.Duration * expectedTime;
|
||||
Vector2 position = slider.Position + slider.Path.PositionAt(expectedPathPosition);
|
||||
|
||||
if (!EditorBeatmap.HitObjects.OfType<HitCircle>().Any(h => matches(h, time, position, slider.NewCombo && expectedTime == 0)))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
bool matches(HitCircle circle, double time, Vector2 position, bool startsNewCombo) =>
|
||||
Precision.AlmostEquals(circle.StartTime, time, 1)
|
||||
&& Precision.AlmostEquals(circle.Position, position, 0.01f)
|
||||
&& circle.NewCombo == startsNewCombo
|
||||
&& circle.Samples.SequenceEqual(slider.HeadCircle.Samples)
|
||||
&& circle.SampleControlPoint.IsRedundant(slider.SampleControlPoint);
|
||||
}
|
||||
|
||||
private bool sliderRestored(Slider slider)
|
||||
{
|
||||
var objects = EditorBeatmap.HitObjects.Where(h => h.StartTime >= slider.StartTime && h.GetEndTime() <= slider.EndTime).ToList();
|
||||
|
||||
if (objects.Count > 1)
|
||||
return false;
|
||||
|
||||
var hitObject = objects.Single();
|
||||
if (!(hitObject is Slider restoredSlider))
|
||||
return false;
|
||||
|
||||
return Precision.AlmostEquals(slider.StartTime, restoredSlider.StartTime)
|
||||
&& Precision.AlmostEquals(slider.GetEndTime(), restoredSlider.GetEndTime())
|
||||
&& Precision.AlmostEquals(slider.Position, restoredSlider.Position, 0.01f)
|
||||
&& Precision.AlmostEquals(slider.EndPosition, restoredSlider.EndPosition, 0.01f);
|
||||
}
|
||||
}
|
||||
}
|
@ -11,9 +11,12 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
@ -47,6 +50,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
[Resolved(CanBeNull = true)]
|
||||
private IEditorChangeHandler changeHandler { get; set; }
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private BindableBeatDivisor beatDivisor { get; set; }
|
||||
|
||||
public override Quad SelectionQuad => BodyPiece.ScreenSpaceDrawQuad;
|
||||
|
||||
private readonly BindableList<PathControlPoint> controlPoints = new BindableList<PathControlPoint>();
|
||||
@ -173,6 +179,20 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool OnKeyDown(KeyDownEvent e)
|
||||
{
|
||||
if (!IsSelected)
|
||||
return false;
|
||||
|
||||
if (e.Key == Key.F && e.ControlPressed && e.ShiftPressed)
|
||||
{
|
||||
convertToStream();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private int addControlPoint(Vector2 position)
|
||||
{
|
||||
position -= HitObject.Position;
|
||||
@ -234,9 +254,56 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
editorBeatmap?.Update(HitObject);
|
||||
}
|
||||
|
||||
private void convertToStream()
|
||||
{
|
||||
if (editorBeatmap == null || changeHandler == null || beatDivisor == null)
|
||||
return;
|
||||
|
||||
var timingPoint = editorBeatmap.ControlPointInfo.TimingPointAt(HitObject.StartTime);
|
||||
double streamSpacing = timingPoint.BeatLength / beatDivisor.Value;
|
||||
|
||||
changeHandler.BeginChange();
|
||||
|
||||
int i = 0;
|
||||
double time = HitObject.StartTime;
|
||||
|
||||
while (!Precision.DefinitelyBigger(time, HitObject.GetEndTime(), 1))
|
||||
{
|
||||
// positionWithRepeats is a fractional number in the range of [0, HitObject.SpanCount()]
|
||||
// and indicates how many fractional spans of a slider have passed up to time.
|
||||
double positionWithRepeats = (time - HitObject.StartTime) / HitObject.Duration * HitObject.SpanCount();
|
||||
double pathPosition = positionWithRepeats - (int)positionWithRepeats;
|
||||
// every second span is in the reverse direction - need to reverse the path position.
|
||||
if (Precision.AlmostBigger(positionWithRepeats % 2, 1))
|
||||
pathPosition = 1 - pathPosition;
|
||||
|
||||
Vector2 position = HitObject.Position + HitObject.Path.PositionAt(pathPosition);
|
||||
|
||||
var samplePoint = (SampleControlPoint)HitObject.SampleControlPoint.DeepClone();
|
||||
samplePoint.Time = time;
|
||||
|
||||
editorBeatmap.Add(new HitCircle
|
||||
{
|
||||
StartTime = time,
|
||||
Position = position,
|
||||
NewCombo = i == 0 && HitObject.NewCombo,
|
||||
SampleControlPoint = samplePoint,
|
||||
Samples = HitObject.HeadCircle.Samples.Select(s => s.With()).ToList()
|
||||
});
|
||||
|
||||
i += 1;
|
||||
time = HitObject.StartTime + i * streamSpacing;
|
||||
}
|
||||
|
||||
editorBeatmap.Remove(HitObject);
|
||||
|
||||
changeHandler.EndChange();
|
||||
}
|
||||
|
||||
public override MenuItem[] ContextMenuItems => new MenuItem[]
|
||||
{
|
||||
new OsuMenuItem("Add control point", MenuItemType.Standard, () => addControlPoint(rightClickPosition)),
|
||||
new OsuMenuItem("Convert to stream", MenuItemType.Destructive, convertToStream),
|
||||
};
|
||||
|
||||
// Always refer to the drawable object's slider body so subsequent movement deltas are calculated with updated positions.
|
||||
|
123
osu.Game.Tests/Visual/Editing/TestSceneEditorTestGameplay.cs
Normal file
123
osu.Game.Tests/Visual/Editing/TestSceneEditorTestGameplay.cs
Normal file
@ -0,0 +1,123 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Edit.Components.Timelines.Summary;
|
||||
using osu.Game.Tests.Beatmaps.IO;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Editing
|
||||
{
|
||||
public class TestSceneEditorTestGameplay : EditorTestScene
|
||||
{
|
||||
protected override bool IsolateSavingFromDatabase => false;
|
||||
|
||||
protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
|
||||
|
||||
[Resolved]
|
||||
private OsuGameBase game { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private BeatmapManager beatmaps { get; set; }
|
||||
|
||||
private BeatmapSetInfo importedBeatmapSet;
|
||||
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
AddStep("import test beatmap", () => importedBeatmapSet = ImportBeatmapTest.LoadOszIntoOsu(game).Result);
|
||||
base.SetUpSteps();
|
||||
}
|
||||
|
||||
protected override void LoadEditor()
|
||||
{
|
||||
Beatmap.Value = beatmaps.GetWorkingBeatmap(importedBeatmapSet.Beatmaps.First(b => b.RulesetID == 0));
|
||||
base.LoadEditor();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBasicGameplayTest()
|
||||
{
|
||||
AddStep("click test gameplay button", () =>
|
||||
{
|
||||
var button = Editor.ChildrenOfType<TestGameplayButton>().Single();
|
||||
|
||||
InputManager.MoveMouseTo(button);
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
|
||||
EditorPlayer editorPlayer = null;
|
||||
AddUntilStep("player pushed", () => (editorPlayer = Stack.CurrentScreen as EditorPlayer) != null);
|
||||
AddStep("exit player", () => editorPlayer.Exit());
|
||||
AddUntilStep("current screen is editor", () => Stack.CurrentScreen is Editor);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCancelGameplayTestWithUnsavedChanges()
|
||||
{
|
||||
AddStep("delete all but first object", () => EditorBeatmap.RemoveRange(EditorBeatmap.HitObjects.Skip(1).ToList()));
|
||||
|
||||
AddStep("click test gameplay button", () =>
|
||||
{
|
||||
var button = Editor.ChildrenOfType<TestGameplayButton>().Single();
|
||||
|
||||
InputManager.MoveMouseTo(button);
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
AddUntilStep("save prompt shown", () => DialogOverlay.CurrentDialog is SaveBeforeGameplayTestDialog);
|
||||
|
||||
AddStep("dismiss prompt", () =>
|
||||
{
|
||||
var button = DialogOverlay.CurrentDialog.Buttons.Last();
|
||||
InputManager.MoveMouseTo(button);
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
|
||||
AddWaitStep("wait some", 3);
|
||||
AddAssert("stayed in editor", () => Stack.CurrentScreen is Editor);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSaveChangesBeforeGameplayTest()
|
||||
{
|
||||
AddStep("delete all but first object", () => EditorBeatmap.RemoveRange(EditorBeatmap.HitObjects.Skip(1).ToList()));
|
||||
// bit of a hack to ensure this test can be ran multiple times without running into UNIQUE constraint failures
|
||||
AddStep("set unique difficulty name", () => EditorBeatmap.BeatmapInfo.DifficultyName = Guid.NewGuid().ToString());
|
||||
|
||||
AddStep("click test gameplay button", () =>
|
||||
{
|
||||
var button = Editor.ChildrenOfType<TestGameplayButton>().Single();
|
||||
|
||||
InputManager.MoveMouseTo(button);
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
AddUntilStep("save prompt shown", () => DialogOverlay.CurrentDialog is SaveBeforeGameplayTestDialog);
|
||||
|
||||
AddStep("save changes", () => DialogOverlay.CurrentDialog.PerformOkAction());
|
||||
|
||||
EditorPlayer editorPlayer = null;
|
||||
AddUntilStep("player pushed", () => (editorPlayer = Stack.CurrentScreen as EditorPlayer) != null);
|
||||
AddAssert("beatmap has 1 object", () => editorPlayer.Beatmap.Value.Beatmap.HitObjects.Count == 1);
|
||||
|
||||
AddUntilStep("wait for return to editor", () => Stack.CurrentScreen is Editor);
|
||||
AddAssert("track stopped", () => !Beatmap.Value.Track.IsRunning);
|
||||
}
|
||||
|
||||
public override void TearDownSteps()
|
||||
{
|
||||
base.TearDownSteps();
|
||||
AddStep("delete imported", () =>
|
||||
{
|
||||
beatmaps.Delete(importedBeatmapSet);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Catch;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
@ -23,6 +24,9 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
[Cached(typeof(IBeatSnapProvider))]
|
||||
private readonly EditorBeatmap editorBeatmap;
|
||||
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
|
||||
|
||||
public TestSceneSetupScreen()
|
||||
{
|
||||
editorBeatmap = new EditorBeatmap(new OsuBeatmap());
|
||||
|
@ -4,6 +4,7 @@
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.Edit;
|
||||
@ -18,6 +19,9 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
[Cached(typeof(IBeatSnapProvider))]
|
||||
private readonly EditorBeatmap editorBeatmap;
|
||||
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
|
||||
|
||||
protected override bool ScrollUsingMouseWheel => false;
|
||||
|
||||
public TestSceneTimingScreen()
|
||||
|
@ -1,9 +1,10 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Rulesets;
|
||||
@ -20,16 +21,17 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public class TestScenePerformancePointsCounter : OsuTestScene
|
||||
{
|
||||
[Cached]
|
||||
private GameplayState gameplayState;
|
||||
private DependencyProvidingContainer dependencyContainer;
|
||||
|
||||
[Cached]
|
||||
private GameplayState gameplayState;
|
||||
private ScoreProcessor scoreProcessor;
|
||||
|
||||
private int iteration;
|
||||
private Bindable<JudgementResult> lastJudgementResult = new Bindable<JudgementResult>();
|
||||
private PerformancePointsCounter counter;
|
||||
|
||||
public TestScenePerformancePointsCounter()
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps() => AddStep("create components", () =>
|
||||
{
|
||||
var ruleset = CreateRuleset();
|
||||
|
||||
@ -38,32 +40,43 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
var beatmap = CreateWorkingBeatmap(ruleset.RulesetInfo)
|
||||
.GetPlayableBeatmap(ruleset.RulesetInfo);
|
||||
|
||||
lastJudgementResult = new Bindable<JudgementResult>();
|
||||
|
||||
gameplayState = new GameplayState(beatmap, ruleset);
|
||||
gameplayState.LastJudgementResult.BindTo(lastJudgementResult);
|
||||
|
||||
scoreProcessor = new ScoreProcessor();
|
||||
}
|
||||
|
||||
Child = dependencyContainer = new DependencyProvidingContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
CachedDependencies = new (Type, object)[]
|
||||
{
|
||||
(typeof(GameplayState), gameplayState),
|
||||
(typeof(ScoreProcessor), scoreProcessor)
|
||||
}
|
||||
};
|
||||
|
||||
iteration = 0;
|
||||
});
|
||||
|
||||
protected override Ruleset CreateRuleset() => new OsuRuleset();
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
private void createCounter() => AddStep("Create counter", () =>
|
||||
{
|
||||
AddStep("Create counter", () =>
|
||||
dependencyContainer.Child = counter = new PerformancePointsCounter
|
||||
{
|
||||
iteration = 0;
|
||||
|
||||
Child = counter = new PerformancePointsCounter
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Scale = new Vector2(5),
|
||||
};
|
||||
});
|
||||
}
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Scale = new Vector2(5),
|
||||
};
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestBasicCounting()
|
||||
{
|
||||
int previousValue = 0;
|
||||
createCounter();
|
||||
|
||||
AddAssert("counter displaying zero", () => counter.Current.Value == 0);
|
||||
|
||||
@ -86,6 +99,17 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddUntilStep("counter non-zero", () => counter.Current.Value > 0);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCounterUpdatesWithJudgementsBeforeCreation()
|
||||
{
|
||||
AddRepeatStep("Add judgement", applyOneJudgement, 10);
|
||||
|
||||
createCounter();
|
||||
|
||||
AddUntilStep("counter non-zero", () => counter.Current.Value > 0);
|
||||
AddUntilStep("counter opaque", () => counter.Child.Alpha == 1);
|
||||
}
|
||||
|
||||
private void applyOneJudgement()
|
||||
{
|
||||
var scoreInfo = gameplayState.Score.ScoreInfo;
|
||||
@ -94,13 +118,14 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
scoreInfo.Accuracy = 1;
|
||||
scoreInfo.Statistics[HitResult.Great] = iteration * 1000;
|
||||
|
||||
scoreProcessor.ApplyResult(new OsuJudgementResult(new HitObject
|
||||
lastJudgementResult.Value = new OsuJudgementResult(new HitObject
|
||||
{
|
||||
StartTime = iteration * 10000,
|
||||
}, new OsuJudgement())
|
||||
{
|
||||
Type = HitResult.Perfect,
|
||||
});
|
||||
};
|
||||
scoreProcessor.ApplyResult(lastJudgementResult.Value);
|
||||
|
||||
iteration++;
|
||||
}
|
||||
|
@ -76,6 +76,7 @@ namespace osu.Game.Input.Bindings
|
||||
new KeyBinding(new[] { InputKey.J }, GlobalAction.EditorNudgeLeft),
|
||||
new KeyBinding(new[] { InputKey.K }, GlobalAction.EditorNudgeRight),
|
||||
new KeyBinding(new[] { InputKey.G }, GlobalAction.EditorCycleGridDisplayMode),
|
||||
new KeyBinding(new[] { InputKey.F5 }, GlobalAction.EditorTestGameplay),
|
||||
};
|
||||
|
||||
public IEnumerable<KeyBinding> InGameKeyBindings => new[]
|
||||
@ -288,6 +289,9 @@ namespace osu.Game.Input.Bindings
|
||||
ToggleChatFocus,
|
||||
|
||||
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorCycleGridDisplayMode))]
|
||||
EditorCycleGridDisplayMode
|
||||
EditorCycleGridDisplayMode,
|
||||
|
||||
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorTestGameplay))]
|
||||
EditorTestGameplay
|
||||
}
|
||||
}
|
||||
|
@ -169,6 +169,11 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString EditorCycleGridDisplayMode => new TranslatableString(getKey(@"editor_cycle_grid_display_mode"), @"Cycle grid display mode");
|
||||
|
||||
/// <summary>
|
||||
/// "Test gameplay"
|
||||
/// </summary>
|
||||
public static LocalisableString EditorTestGameplay => new TranslatableString(getKey(@"editor_test_gameplay"), @"Test gameplay");
|
||||
|
||||
/// <summary>
|
||||
/// "Hold for HUD"
|
||||
/// </summary>
|
||||
|
@ -0,0 +1,34 @@
|
||||
// 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.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Components.Timelines.Summary
|
||||
{
|
||||
public class TestGameplayButton : OsuButton
|
||||
{
|
||||
protected override SpriteText CreateText() => new OsuSpriteText
|
||||
{
|
||||
Depth = -1,
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Font = OsuFont.TorusAlternate.With(weight: FontWeight.Light, size: 24),
|
||||
Shadow = false
|
||||
};
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours, OverlayColourProvider colourProvider)
|
||||
{
|
||||
BackgroundColour = colours.Orange1;
|
||||
SpriteText.Colour = colourProvider.Background6;
|
||||
|
||||
Text = "Test!";
|
||||
}
|
||||
}
|
||||
}
|
@ -36,6 +36,7 @@ using osu.Game.Screens.Edit.Timing;
|
||||
using osu.Game.Screens.Edit.Verify;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Users;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osuTK.Input;
|
||||
|
||||
@ -90,6 +91,8 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
private DependencyContainer dependencies;
|
||||
|
||||
private TestGameplayButton testGameplayButton;
|
||||
|
||||
private bool isNewBeatmap;
|
||||
|
||||
protected override UserActivity InitialActivity => new UserActivity.Editing(Beatmap.Value.BeatmapInfo);
|
||||
@ -106,6 +109,9 @@ namespace osu.Game.Screens.Edit
|
||||
[Cached]
|
||||
public readonly EditorClipboard Clipboard = new EditorClipboard();
|
||||
|
||||
[Cached]
|
||||
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
|
||||
|
||||
public Editor(EditorLoader loader = null)
|
||||
{
|
||||
this.loader = loader;
|
||||
@ -262,7 +268,8 @@ namespace osu.Game.Screens.Edit
|
||||
{
|
||||
new Dimension(GridSizeMode.Absolute, 220),
|
||||
new Dimension(),
|
||||
new Dimension(GridSizeMode.Absolute, 220)
|
||||
new Dimension(GridSizeMode.Absolute, 220),
|
||||
new Dimension(GridSizeMode.Absolute, 120),
|
||||
},
|
||||
Content = new[]
|
||||
{
|
||||
@ -283,6 +290,13 @@ namespace osu.Game.Screens.Edit
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Left = 10 },
|
||||
Child = new PlaybackControl { RelativeSizeAxes = Axes.Both },
|
||||
},
|
||||
testGameplayButton = new TestGameplayButton
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding { Left = 10 },
|
||||
Size = new Vector2(1),
|
||||
Action = testGameplay
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -456,6 +470,10 @@ namespace osu.Game.Screens.Edit
|
||||
menuBar.Mode.Value = EditorScreenMode.Verify;
|
||||
return true;
|
||||
|
||||
case GlobalAction.EditorTestGameplay:
|
||||
testGameplayButton.TriggerClick();
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@ -510,7 +528,21 @@ namespace osu.Game.Screens.Edit
|
||||
ApplyToBackground(b => b.FadeColour(Color4.White, 500));
|
||||
resetTrack();
|
||||
|
||||
// To update the game-wide beatmap with any changes, perform a re-fetch on exit.
|
||||
refetchBeatmap();
|
||||
|
||||
return base.OnExiting(next);
|
||||
}
|
||||
|
||||
public override void OnSuspending(IScreen next)
|
||||
{
|
||||
refetchBeatmap();
|
||||
|
||||
base.OnSuspending(next);
|
||||
}
|
||||
|
||||
private void refetchBeatmap()
|
||||
{
|
||||
// To update the game-wide beatmap with any changes, perform a re-fetch on exit/suspend.
|
||||
// This is required as the editor makes its local changes via EditorBeatmap
|
||||
// (which are not propagated outwards to a potentially cached WorkingBeatmap).
|
||||
var refetchedBeatmap = beatmapManager.GetWorkingBeatmap(Beatmap.Value.BeatmapInfo);
|
||||
@ -520,8 +552,6 @@ namespace osu.Game.Screens.Edit
|
||||
Logger.Log("Editor providing re-fetched beatmap post edit session");
|
||||
Beatmap.Value = refetchedBeatmap;
|
||||
}
|
||||
|
||||
return base.OnExiting(next);
|
||||
}
|
||||
|
||||
private void confirmExitWithSave()
|
||||
@ -752,6 +782,24 @@ namespace osu.Game.Screens.Edit
|
||||
loader?.CancelPendingDifficultySwitch();
|
||||
}
|
||||
|
||||
private void testGameplay()
|
||||
{
|
||||
if (HasUnsavedChanges)
|
||||
{
|
||||
dialogOverlay.Push(new SaveBeforeGameplayTestDialog(() =>
|
||||
{
|
||||
Save();
|
||||
pushEditorPlayer();
|
||||
}));
|
||||
}
|
||||
else
|
||||
{
|
||||
pushEditorPlayer();
|
||||
}
|
||||
|
||||
void pushEditorPlayer() => this.Push(new PlayerLoader(() => new EditorPlayer()));
|
||||
}
|
||||
|
||||
public double SnapTime(double time, double? referenceTime) => editorBeatmap.SnapTime(time, referenceTime);
|
||||
|
||||
public double GetBeatLengthAtTime(double referenceTime) => editorBeatmap.GetBeatLengthAtTime(referenceTime);
|
||||
|
44
osu.Game/Screens/Edit/EditorPlayer.cs
Normal file
44
osu.Game/Screens/Edit/EditorPlayer.cs
Normal file
@ -0,0 +1,44 @@
|
||||
// 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.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Screens.Play;
|
||||
|
||||
namespace osu.Game.Screens.Edit
|
||||
{
|
||||
public class EditorPlayer : Player
|
||||
{
|
||||
public EditorPlayer()
|
||||
: base(new PlayerConfiguration { ShowResults = false })
|
||||
{
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private MusicController musicController { get; set; }
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
ScoreProcessor.HasCompleted.BindValueChanged(completed =>
|
||||
{
|
||||
if (completed.NewValue)
|
||||
Scheduler.AddDelayed(this.Exit, RESULTS_DISPLAY_DELAY);
|
||||
});
|
||||
}
|
||||
|
||||
protected override void PrepareReplay()
|
||||
{
|
||||
// don't record replays.
|
||||
}
|
||||
|
||||
protected override bool CheckModsAllowFailure() => false; // never fail.
|
||||
|
||||
public override bool OnExiting(IScreen next)
|
||||
{
|
||||
musicController.Stop();
|
||||
return base.OnExiting(next);
|
||||
}
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Screens.Edit
|
||||
{
|
||||
@ -26,7 +27,7 @@ namespace osu.Game.Screens.Edit
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
private void load(OverlayColourProvider colourProvider)
|
||||
{
|
||||
base.Content.Add(new Container
|
||||
{
|
||||
@ -41,7 +42,7 @@ namespace osu.Game.Screens.Edit
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = ColourProvider.Background3,
|
||||
Colour = colourProvider.Background3,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
roundedContent = new Container
|
||||
|
@ -6,7 +6,6 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Screens.Edit
|
||||
{
|
||||
@ -18,9 +17,6 @@ namespace osu.Game.Screens.Edit
|
||||
[Resolved]
|
||||
protected EditorBeatmap EditorBeatmap { get; private set; }
|
||||
|
||||
[Cached]
|
||||
protected readonly OverlayColourProvider ColourProvider;
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
private readonly Container content;
|
||||
|
||||
@ -34,8 +30,6 @@ namespace osu.Game.Screens.Edit
|
||||
Origin = Anchor.Centre;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
ColourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
|
||||
|
||||
InternalChild = content = new PopoverContainer { RelativeSizeAxes = Axes.Both };
|
||||
}
|
||||
|
||||
|
32
osu.Game/Screens/Edit/SaveBeforeGameplayTestDialog.cs
Normal file
32
osu.Game/Screens/Edit/SaveBeforeGameplayTestDialog.cs
Normal file
@ -0,0 +1,32 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Overlays.Dialog;
|
||||
|
||||
namespace osu.Game.Screens.Edit
|
||||
{
|
||||
public class SaveBeforeGameplayTestDialog : PopupDialog
|
||||
{
|
||||
public SaveBeforeGameplayTestDialog(Action saveAndPreview)
|
||||
{
|
||||
HeaderText = "The beatmap will be saved in order to test it.";
|
||||
|
||||
Icon = FontAwesome.Regular.Save;
|
||||
|
||||
Buttons = new PopupDialogButton[]
|
||||
{
|
||||
new PopupDialogOkButton
|
||||
{
|
||||
Text = "Sounds good, let's go!",
|
||||
Action = saveAndPreview
|
||||
},
|
||||
new PopupDialogCancelButton
|
||||
{
|
||||
Text = "Oops, continue editing",
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -92,6 +92,9 @@ namespace osu.Game.Screens.Play.HUD
|
||||
scoreProcessor.NewJudgement += onJudgementChanged;
|
||||
scoreProcessor.JudgementReverted += onJudgementChanged;
|
||||
}
|
||||
|
||||
if (gameplayState?.LastJudgementResult.Value != null)
|
||||
onJudgementChanged(gameplayState.LastJudgementResult.Value);
|
||||
}
|
||||
|
||||
private bool isValid;
|
||||
@ -155,7 +158,10 @@ namespace osu.Game.Screens.Play.HUD
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (scoreProcessor != null)
|
||||
{
|
||||
scoreProcessor.NewJudgement -= onJudgementChanged;
|
||||
scoreProcessor.JudgementReverted -= onJudgementChanged;
|
||||
}
|
||||
|
||||
loadCancellationSource?.Cancel();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user