mirror of
https://github.com/ppy/osu.git
synced 2025-02-22 04:32:55 +08:00
Merge branch 'master' into taiko-explosion-rework
This commit is contained in:
commit
40dea0e2db
@ -31,6 +31,9 @@ namespace osu.Game.Rulesets.Catch.Replays
|
|||||||
|
|
||||||
public override Replay Generate()
|
public override Replay Generate()
|
||||||
{
|
{
|
||||||
|
if (Beatmap.HitObjects.Count == 0)
|
||||||
|
return Replay;
|
||||||
|
|
||||||
// todo: add support for HT DT
|
// todo: add support for HT DT
|
||||||
const double dash_speed = Catcher.BASE_SPEED;
|
const double dash_speed = Catcher.BASE_SPEED;
|
||||||
const double movement_speed = dash_speed / 2;
|
const double movement_speed = dash_speed / 2;
|
||||||
|
@ -46,6 +46,9 @@ namespace osu.Game.Rulesets.Mania.Replays
|
|||||||
|
|
||||||
public override Replay Generate()
|
public override Replay Generate()
|
||||||
{
|
{
|
||||||
|
if (Beatmap.HitObjects.Count == 0)
|
||||||
|
return Replay;
|
||||||
|
|
||||||
var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time);
|
var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time);
|
||||||
|
|
||||||
var actions = new List<ManiaAction>();
|
var actions = new List<ManiaAction>();
|
||||||
|
@ -15,6 +15,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
{
|
{
|
||||||
private readonly ManualSliderBody body;
|
private readonly ManualSliderBody body;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Offset in absolute (local) coordinates from the start of the curve.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 PathStartLocation => body.PathOffset;
|
||||||
|
|
||||||
public SliderBodyPiece()
|
public SliderBodyPiece()
|
||||||
{
|
{
|
||||||
InternalChild = body = new ManualSliderBody
|
InternalChild = body = new ManualSliderBody
|
||||||
|
@ -190,7 +190,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
new OsuMenuItem("Add control point", MenuItemType.Standard, () => addControlPoint(rightClickPosition)),
|
new OsuMenuItem("Add control point", MenuItemType.Standard, () => addControlPoint(rightClickPosition)),
|
||||||
};
|
};
|
||||||
|
|
||||||
public override Vector2 ScreenSpaceSelectionPoint => ((DrawableSlider)DrawableObject).HeadCircle.ScreenSpaceDrawQuad.Centre;
|
public override Vector2 ScreenSpaceSelectionPoint => BodyPiece.ToScreenSpace(BodyPiece.PathStartLocation);
|
||||||
|
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => BodyPiece.ReceivePositionalInputAt(screenSpacePos);
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => BodyPiece.ReceivePositionalInputAt(screenSpacePos);
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.UI;
|
using osu.Game.Rulesets.Osu.UI;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -20,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
/// Hit objects are intentionally made to fade out at a constant slower rate than in gameplay.
|
/// Hit objects are intentionally made to fade out at a constant slower rate than in gameplay.
|
||||||
/// This allows a mapper to gain better historical context and use recent hitobjects as reference / snap points.
|
/// This allows a mapper to gain better historical context and use recent hitobjects as reference / snap points.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const double editor_hit_object_fade_out_extension = 500;
|
private const double editor_hit_object_fade_out_extension = 700;
|
||||||
|
|
||||||
public DrawableOsuEditRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods)
|
public DrawableOsuEditRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList<Mod> mods)
|
||||||
: base(ruleset, beatmap, mods)
|
: base(ruleset, beatmap, mods)
|
||||||
@ -32,11 +33,30 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
|
|
||||||
private void updateState(DrawableHitObject hitObject, ArmedState state)
|
private void updateState(DrawableHitObject hitObject, ArmedState state)
|
||||||
{
|
{
|
||||||
switch (state)
|
if (state == ArmedState.Idle)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// adjust the visuals of certain object types to make them stay on screen for longer than usual.
|
||||||
|
switch (hitObject)
|
||||||
{
|
{
|
||||||
case ArmedState.Miss:
|
default:
|
||||||
|
// there are quite a few drawable hit types we don't want to extent (spinners, ticks etc.)
|
||||||
|
return;
|
||||||
|
|
||||||
|
case DrawableSlider _:
|
||||||
|
// no specifics to sliders but let them fade slower below.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DrawableHitCircle circle: // also handles slider heads
|
||||||
|
circle.ApproachCircle
|
||||||
|
.FadeOutFromOne(editor_hit_object_fade_out_extension)
|
||||||
|
.Expire();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Get the existing fade out transform
|
// Get the existing fade out transform
|
||||||
var existing = hitObject.Transforms.LastOrDefault(t => t.TargetMember == nameof(Alpha));
|
var existing = hitObject.Transforms.LastOrDefault(t => t.TargetMember == nameof(Alpha));
|
||||||
|
|
||||||
if (existing == null)
|
if (existing == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -44,8 +64,6 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
|
|
||||||
using (hitObject.BeginAbsoluteSequence(existing.StartTime))
|
using (hitObject.BeginAbsoluteSequence(existing.StartTime))
|
||||||
hitObject.FadeOut(editor_hit_object_fade_out_extension).Expire();
|
hitObject.FadeOut(editor_hit_object_fade_out_extension).Expire();
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Playfield CreatePlayfield() => new OsuPlayfieldNoCursor();
|
protected override Playfield CreatePlayfield() => new OsuPlayfieldNoCursor();
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -93,7 +92,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
drawableSpinner.RotationTracker.Complete.BindValueChanged(complete => updateComplete(complete.NewValue, 200));
|
drawableSpinner.RotationTracker.Complete.BindValueChanged(complete => updateComplete(complete.NewValue, 200));
|
||||||
drawableSpinner.State.BindValueChanged(updateStateTransforms, true);
|
drawableSpinner.ApplyCustomUpdateState += updateStateTransforms;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
@ -123,7 +122,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
mainContainer.Rotation = drawableSpinner.RotationTracker.Rotation;
|
mainContainer.Rotation = drawableSpinner.RotationTracker.Rotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateStateTransforms(ValueChangedEvent<ArmedState> state)
|
private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
|
||||||
{
|
{
|
||||||
centre.ScaleTo(0);
|
centre.ScaleTo(0);
|
||||||
mainContainer.ScaleTo(0);
|
mainContainer.ScaleTo(0);
|
||||||
@ -144,11 +143,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
}
|
}
|
||||||
|
|
||||||
// transforms we have from completing the spinner will be rolled back, so reapply immediately.
|
// transforms we have from completing the spinner will be rolled back, so reapply immediately.
|
||||||
updateComplete(state.NewValue == ArmedState.Hit, 0);
|
updateComplete(state == ArmedState.Hit, 0);
|
||||||
|
|
||||||
using (BeginDelayedSequence(spinner.Duration, true))
|
using (BeginDelayedSequence(spinner.Duration, true))
|
||||||
{
|
{
|
||||||
switch (state.NewValue)
|
switch (state)
|
||||||
{
|
{
|
||||||
case ArmedState.Hit:
|
case ArmedState.Hit:
|
||||||
this.ScaleTo(Scale * 1.2f, 320, Easing.Out);
|
this.ScaleTo(Scale * 1.2f, 320, Easing.Out);
|
||||||
@ -185,5 +184,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
|
if (drawableSpinner != null)
|
||||||
|
drawableSpinner.ApplyCustomUpdateState -= updateStateTransforms;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,6 +72,9 @@ namespace osu.Game.Rulesets.Osu.Replays
|
|||||||
|
|
||||||
public override Replay Generate()
|
public override Replay Generate()
|
||||||
{
|
{
|
||||||
|
if (Beatmap.HitObjects.Count == 0)
|
||||||
|
return Replay;
|
||||||
|
|
||||||
buttonIndex = 0;
|
buttonIndex = 0;
|
||||||
|
|
||||||
AddFrameToReplay(new OsuReplayFrame(-100000, new Vector2(256, 500)));
|
AddFrameToReplay(new OsuReplayFrame(-100000, new Vector2(256, 500)));
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// 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.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
@ -72,10 +71,10 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
this.FadeOut();
|
this.FadeOut();
|
||||||
drawableSpinner.State.BindValueChanged(updateStateTransforms, true);
|
drawableSpinner.ApplyCustomUpdateState += updateStateTransforms;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateStateTransforms(ValueChangedEvent<ArmedState> state)
|
private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
|
||||||
{
|
{
|
||||||
var spinner = (Spinner)drawableSpinner.HitObject;
|
var spinner = (Spinner)drawableSpinner.HitObject;
|
||||||
|
|
||||||
@ -95,5 +94,13 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
|
|
||||||
Scale = new Vector2(final_scale * (0.8f + (float)Interpolation.ApplyEasing(Easing.Out, drawableSpinner.Progress) * 0.2f));
|
Scale = new Vector2(final_scale * (0.8f + (float)Interpolation.ApplyEasing(Easing.Out, drawableSpinner.Progress) * 0.2f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
|
if (drawableSpinner != null)
|
||||||
|
drawableSpinner.ApplyCustomUpdateState -= updateStateTransforms;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
@ -86,10 +85,10 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
this.FadeOut();
|
this.FadeOut();
|
||||||
drawableSpinner.State.BindValueChanged(updateStateTransforms, true);
|
drawableSpinner.ApplyCustomUpdateState += updateStateTransforms;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateStateTransforms(ValueChangedEvent<ArmedState> state)
|
private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
|
||||||
{
|
{
|
||||||
var spinner = drawableSpinner.HitObject;
|
var spinner = drawableSpinner.HitObject;
|
||||||
|
|
||||||
@ -127,5 +126,13 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
|||||||
|
|
||||||
return (float)barCount / total_bars * final_metre_height;
|
return (float)barCount / total_bars * final_metre_height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
|
if (drawableSpinner != null)
|
||||||
|
drawableSpinner.ApplyCustomUpdateState -= updateStateTransforms;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,13 @@ namespace osu.Game.Rulesets.Taiko.Edit
|
|||||||
ChangeHandler.BeginChange();
|
ChangeHandler.BeginChange();
|
||||||
|
|
||||||
foreach (var h in hits)
|
foreach (var h in hits)
|
||||||
|
{
|
||||||
|
if (h.IsStrong != state)
|
||||||
|
{
|
||||||
h.IsStrong = state;
|
h.IsStrong = state;
|
||||||
|
EditorBeatmap.UpdateHitObject(h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ChangeHandler.EndChange();
|
ChangeHandler.EndChange();
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,9 @@ namespace osu.Game.Rulesets.Taiko.Replays
|
|||||||
|
|
||||||
public override Replay Generate()
|
public override Replay Generate()
|
||||||
{
|
{
|
||||||
|
if (Beatmap.HitObjects.Count == 0)
|
||||||
|
return Replay;
|
||||||
|
|
||||||
bool hitButton = true;
|
bool hitButton = true;
|
||||||
|
|
||||||
Frames.Add(new TaikoReplayFrame(-100000));
|
Frames.Add(new TaikoReplayFrame(-100000));
|
||||||
|
@ -2,7 +2,9 @@
|
|||||||
// 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 NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Editing
|
namespace osu.Game.Tests.Editing
|
||||||
@ -13,11 +15,12 @@ namespace osu.Game.Tests.Editing
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestSaveRestoreState()
|
public void TestSaveRestoreState()
|
||||||
{
|
{
|
||||||
var handler = new EditorChangeHandler(new EditorBeatmap(new Beatmap()));
|
var (handler, beatmap) = createChangeHandler();
|
||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.False);
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
Assert.That(handler.CanRedo.Value, Is.False);
|
Assert.That(handler.CanRedo.Value, Is.False);
|
||||||
|
|
||||||
|
addArbitraryChange(beatmap);
|
||||||
handler.SaveState();
|
handler.SaveState();
|
||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.True);
|
Assert.That(handler.CanUndo.Value, Is.True);
|
||||||
@ -29,15 +32,48 @@ namespace osu.Game.Tests.Editing
|
|||||||
Assert.That(handler.CanRedo.Value, Is.True);
|
Assert.That(handler.CanRedo.Value, Is.True);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSaveSameStateDoesNotSave()
|
||||||
|
{
|
||||||
|
var (handler, beatmap) = createChangeHandler();
|
||||||
|
|
||||||
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
|
Assert.That(handler.CanRedo.Value, Is.False);
|
||||||
|
|
||||||
|
addArbitraryChange(beatmap);
|
||||||
|
handler.SaveState();
|
||||||
|
|
||||||
|
Assert.That(handler.CanUndo.Value, Is.True);
|
||||||
|
Assert.That(handler.CanRedo.Value, Is.False);
|
||||||
|
|
||||||
|
string hash = handler.CurrentStateHash;
|
||||||
|
|
||||||
|
// save a save without making any changes
|
||||||
|
handler.SaveState();
|
||||||
|
|
||||||
|
Assert.That(hash, Is.EqualTo(handler.CurrentStateHash));
|
||||||
|
|
||||||
|
handler.RestoreState(-1);
|
||||||
|
|
||||||
|
Assert.That(hash, Is.Not.EqualTo(handler.CurrentStateHash));
|
||||||
|
|
||||||
|
// we should only be able to restore once even though we saved twice.
|
||||||
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
|
Assert.That(handler.CanRedo.Value, Is.True);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestMaxStatesSaved()
|
public void TestMaxStatesSaved()
|
||||||
{
|
{
|
||||||
var handler = new EditorChangeHandler(new EditorBeatmap(new Beatmap()));
|
var (handler, beatmap) = createChangeHandler();
|
||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.False);
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
|
|
||||||
for (int i = 0; i < EditorChangeHandler.MAX_SAVED_STATES; i++)
|
for (int i = 0; i < EditorChangeHandler.MAX_SAVED_STATES; i++)
|
||||||
|
{
|
||||||
|
addArbitraryChange(beatmap);
|
||||||
handler.SaveState();
|
handler.SaveState();
|
||||||
|
}
|
||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.True);
|
Assert.That(handler.CanUndo.Value, Is.True);
|
||||||
|
|
||||||
@ -53,12 +89,15 @@ namespace osu.Game.Tests.Editing
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestMaxStatesExceeded()
|
public void TestMaxStatesExceeded()
|
||||||
{
|
{
|
||||||
var handler = new EditorChangeHandler(new EditorBeatmap(new Beatmap()));
|
var (handler, beatmap) = createChangeHandler();
|
||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.False);
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
|
|
||||||
for (int i = 0; i < EditorChangeHandler.MAX_SAVED_STATES * 2; i++)
|
for (int i = 0; i < EditorChangeHandler.MAX_SAVED_STATES * 2; i++)
|
||||||
|
{
|
||||||
|
addArbitraryChange(beatmap);
|
||||||
handler.SaveState();
|
handler.SaveState();
|
||||||
|
}
|
||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.True);
|
Assert.That(handler.CanUndo.Value, Is.True);
|
||||||
|
|
||||||
@ -70,5 +109,17 @@ namespace osu.Game.Tests.Editing
|
|||||||
|
|
||||||
Assert.That(handler.CanUndo.Value, Is.False);
|
Assert.That(handler.CanUndo.Value, Is.False);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private (EditorChangeHandler, EditorBeatmap) createChangeHandler()
|
||||||
|
{
|
||||||
|
var beatmap = new EditorBeatmap(new Beatmap());
|
||||||
|
|
||||||
|
return (new EditorChangeHandler(beatmap), beatmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addArbitraryChange(EditorBeatmap beatmap)
|
||||||
|
{
|
||||||
|
beatmap.Add(new HitCircle { StartTime = RNG.Next(0, 100000) });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,14 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
protected new OverlayTestPlayer Player => base.Player as OverlayTestPlayer;
|
protected new OverlayTestPlayer Player => base.Player as OverlayTestPlayer;
|
||||||
|
|
||||||
|
public override void SetUpSteps()
|
||||||
|
{
|
||||||
|
base.SetUpSteps();
|
||||||
|
|
||||||
|
AddUntilStep("gameplay has started",
|
||||||
|
() => Player.GameplayClockContainer.GameplayClock.CurrentTime > Player.DrawableRuleset.GameplayStartTime);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestGameplayOverlayActivation()
|
public void TestGameplayOverlayActivation()
|
||||||
{
|
{
|
||||||
@ -21,7 +29,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestGameplayOverlayActivationPaused()
|
public void TestGameplayOverlayActivationPaused()
|
||||||
{
|
{
|
||||||
AddUntilStep("activation mode is disabled", () => Player.OverlayActivationMode == OverlayActivation.Disabled);
|
AddAssert("activation mode is disabled", () => Player.OverlayActivationMode == OverlayActivation.Disabled);
|
||||||
AddStep("pause gameplay", () => Player.Pause());
|
AddStep("pause gameplay", () => Player.Pause());
|
||||||
AddUntilStep("activation mode is user triggered", () => Player.OverlayActivationMode == OverlayActivation.UserTriggered);
|
AddUntilStep("activation mode is user triggered", () => Player.OverlayActivationMode == OverlayActivation.UserTriggered);
|
||||||
}
|
}
|
||||||
|
@ -112,6 +112,9 @@ namespace osu.Game.Graphics.Containers
|
|||||||
|
|
||||||
CornerRadius = 5;
|
CornerRadius = 5;
|
||||||
|
|
||||||
|
// needs to be set initially for the ResizeTo to respect minimum size
|
||||||
|
Size = new Vector2(SCROLL_BAR_HEIGHT);
|
||||||
|
|
||||||
const float margin = 3;
|
const float margin = 3;
|
||||||
|
|
||||||
Margin = new MarginPadding
|
Margin = new MarginPadding
|
||||||
|
@ -45,15 +45,21 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
beatmap.HitObjectAdded += addHitObject;
|
beatmap.HitObjectAdded += addHitObject;
|
||||||
|
beatmap.HitObjectUpdated += updateReplay;
|
||||||
beatmap.HitObjectRemoved += removeHitObject;
|
beatmap.HitObjectRemoved += removeHitObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateReplay(HitObject obj = null) =>
|
||||||
|
drawableRuleset.RegenerateAutoplay();
|
||||||
|
|
||||||
private void addHitObject(HitObject hitObject)
|
private void addHitObject(HitObject hitObject)
|
||||||
{
|
{
|
||||||
var drawableObject = drawableRuleset.CreateDrawableRepresentation((TObject)hitObject);
|
var drawableObject = drawableRuleset.CreateDrawableRepresentation((TObject)hitObject);
|
||||||
|
|
||||||
drawableRuleset.Playfield.Add(drawableObject);
|
drawableRuleset.Playfield.Add(drawableObject);
|
||||||
drawableRuleset.Playfield.PostProcess();
|
drawableRuleset.Playfield.PostProcess();
|
||||||
|
|
||||||
|
updateReplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeHitObject(HitObject hitObject)
|
private void removeHitObject(HitObject hitObject)
|
||||||
@ -62,6 +68,8 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
|
|
||||||
drawableRuleset.Playfield.Remove(drawableObject);
|
drawableRuleset.Playfield.Remove(drawableObject);
|
||||||
drawableRuleset.Playfield.PostProcess();
|
drawableRuleset.Playfield.PostProcess();
|
||||||
|
|
||||||
|
drawableRuleset.RegenerateAutoplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool PropagatePositionalInputSubTree => false;
|
public override bool PropagatePositionalInputSubTree => false;
|
||||||
|
@ -76,7 +76,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
drawableRulesetWrapper = new DrawableEditRulesetWrapper<TObject>(CreateDrawableRuleset(Ruleset, EditorBeatmap.PlayableBeatmap))
|
drawableRulesetWrapper = new DrawableEditRulesetWrapper<TObject>(CreateDrawableRuleset(Ruleset, EditorBeatmap.PlayableBeatmap, new[] { Ruleset.GetAutoplayMod() }))
|
||||||
{
|
{
|
||||||
Clock = EditorClock,
|
Clock = EditorClock,
|
||||||
ProcessCustomClock = false
|
ProcessCustomClock = false
|
||||||
|
@ -151,8 +151,11 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
public virtual PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new PlayfieldAdjustmentContainer();
|
public virtual PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new PlayfieldAdjustmentContainer();
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuConfigManager config { get; set; }
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuConfigManager config, CancellationToken? cancellationToken)
|
private void load(CancellationToken? cancellationToken)
|
||||||
{
|
{
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
@ -178,11 +181,18 @@ namespace osu.Game.Rulesets.UI
|
|||||||
.WithChild(ResumeOverlay)));
|
.WithChild(ResumeOverlay)));
|
||||||
}
|
}
|
||||||
|
|
||||||
applyRulesetMods(Mods, config);
|
RegenerateAutoplay();
|
||||||
|
|
||||||
loadObjects(cancellationToken);
|
loadObjects(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RegenerateAutoplay()
|
||||||
|
{
|
||||||
|
// for now this is applying mods which aren't just autoplay.
|
||||||
|
// we'll need to reconsider this flow in the future.
|
||||||
|
applyRulesetMods(Mods, config);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates and adds drawable representations of hit objects to the play field.
|
/// Creates and adds drawable representations of hit objects to the play field.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -123,9 +123,8 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!FrameStablePlayback)
|
if (FrameStablePlayback)
|
||||||
return;
|
{
|
||||||
|
|
||||||
if (firstConsumption)
|
if (firstConsumption)
|
||||||
{
|
{
|
||||||
// On the first update, frame-stability seeking would result in unexpected/unwanted behaviour.
|
// On the first update, frame-stability seeking would result in unexpected/unwanted behaviour.
|
||||||
@ -145,18 +144,43 @@ namespace osu.Game.Rulesets.UI
|
|||||||
? Math.Min(newProposedTime, manualClock.CurrentTime + sixty_frame_time)
|
? Math.Min(newProposedTime, manualClock.CurrentTime + sixty_frame_time)
|
||||||
: Math.Max(newProposedTime, manualClock.CurrentTime - sixty_frame_time);
|
: Math.Max(newProposedTime, manualClock.CurrentTime - sixty_frame_time);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (isAttached)
|
if (isAttached)
|
||||||
{
|
{
|
||||||
double? newTime = ReplayInputHandler.SetFrameFromTime(newProposedTime);
|
double? newTime;
|
||||||
|
|
||||||
|
if (FrameStablePlayback)
|
||||||
|
{
|
||||||
|
// when stability is turned on, we shouldn't execute for time values the replay is unable to satisfy.
|
||||||
|
if ((newTime = ReplayInputHandler.SetFrameFromTime(newProposedTime)) == null)
|
||||||
|
{
|
||||||
|
// setting invalid state here ensures that gameplay will not continue (ie. our child
|
||||||
|
// hierarchy won't be updated).
|
||||||
|
validState = false;
|
||||||
|
|
||||||
|
// potentially loop to catch-up playback.
|
||||||
|
requireMoreUpdateLoops = true;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// when stability is disabled, we don't really care about accuracy.
|
||||||
|
// looping over the replay will allow it to catch up and feed out the required values
|
||||||
|
// for the current time.
|
||||||
|
while ((newTime = ReplayInputHandler.SetFrameFromTime(newProposedTime)) != newProposedTime)
|
||||||
|
{
|
||||||
if (newTime == null)
|
if (newTime == null)
|
||||||
{
|
{
|
||||||
// we shouldn't execute for this time value. probably waiting on more replay data.
|
// special case for when the replay actually can't arrive at the required time.
|
||||||
|
// protects from potential endless loop.
|
||||||
validState = false;
|
validState = false;
|
||||||
requireMoreUpdateLoops = true;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
newProposedTime = newTime.Value;
|
newProposedTime = newTime.Value;
|
||||||
}
|
}
|
||||||
|
@ -201,6 +201,10 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
if (isDraggingBlueprint)
|
if (isDraggingBlueprint)
|
||||||
{
|
{
|
||||||
|
// handle positional change etc.
|
||||||
|
foreach (var obj in selectedHitObjects)
|
||||||
|
Beatmap.UpdateHitObject(obj);
|
||||||
|
|
||||||
changeHandler?.EndChange();
|
changeHandler?.EndChange();
|
||||||
isDraggingBlueprint = false;
|
isDraggingBlueprint = false;
|
||||||
}
|
}
|
||||||
|
@ -212,6 +212,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
|
|
||||||
if (blueprint != null)
|
if (blueprint != null)
|
||||||
{
|
{
|
||||||
|
// doing this post-creations as adding the default hit sample should be the case regardless of the ruleset.
|
||||||
|
blueprint.HitObject.Samples.Add(new HitSampleInfo { Name = HitSampleInfo.HIT_NORMAL });
|
||||||
|
|
||||||
placementBlueprintContainer.Child = currentPlacement = blueprint;
|
placementBlueprintContainer.Child = currentPlacement = blueprint;
|
||||||
|
|
||||||
// Fixes a 1-frame position discrepancy due to the first mouse move event happening in the next frame
|
// Fixes a 1-frame position discrepancy due to the first mouse move event happening in the next frame
|
||||||
|
@ -288,8 +288,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
|||||||
{
|
{
|
||||||
var comboInfo = h as IHasComboInformation;
|
var comboInfo = h as IHasComboInformation;
|
||||||
|
|
||||||
if (comboInfo == null)
|
if (comboInfo == null || comboInfo.NewCombo == state) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
comboInfo.NewCombo = state;
|
comboInfo.NewCombo = state;
|
||||||
EditorBeatmap?.UpdateHitObject(h);
|
EditorBeatmap?.UpdateHitObject(h);
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
@ -89,24 +90,28 @@ namespace osu.Game.Screens.Edit
|
|||||||
if (isRestoring)
|
if (isRestoring)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
using (var stream = new MemoryStream())
|
||||||
|
{
|
||||||
|
using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true))
|
||||||
|
new LegacyBeatmapEncoder(editorBeatmap, editorBeatmap.BeatmapSkin).Encode(sw);
|
||||||
|
|
||||||
|
var newState = stream.ToArray();
|
||||||
|
|
||||||
|
// if the previous state is binary equal we don't need to push a new one, unless this is the initial state.
|
||||||
|
if (savedStates.Count > 0 && newState.SequenceEqual(savedStates.Last())) return;
|
||||||
|
|
||||||
if (currentState < savedStates.Count - 1)
|
if (currentState < savedStates.Count - 1)
|
||||||
savedStates.RemoveRange(currentState + 1, savedStates.Count - currentState - 1);
|
savedStates.RemoveRange(currentState + 1, savedStates.Count - currentState - 1);
|
||||||
|
|
||||||
if (savedStates.Count > MAX_SAVED_STATES)
|
if (savedStates.Count > MAX_SAVED_STATES)
|
||||||
savedStates.RemoveAt(0);
|
savedStates.RemoveAt(0);
|
||||||
|
|
||||||
using (var stream = new MemoryStream())
|
savedStates.Add(newState);
|
||||||
{
|
|
||||||
using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true))
|
|
||||||
new LegacyBeatmapEncoder(editorBeatmap, editorBeatmap.BeatmapSkin).Encode(sw);
|
|
||||||
|
|
||||||
savedStates.Add(stream.ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
currentState = savedStates.Count - 1;
|
currentState = savedStates.Count - 1;
|
||||||
|
|
||||||
updateBindables();
|
updateBindables();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Restores an older or newer state.
|
/// Restores an older or newer state.
|
||||||
|
Loading…
Reference in New Issue
Block a user