mirror of
https://github.com/ppy/osu.git
synced 2025-01-28 09:43:10 +08:00
Merge branch 'master' into inheritable-allow-track-adjust
This commit is contained in:
commit
931e873a7e
@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
{
|
{
|
||||||
c.Add(CreateHitObject().With(h =>
|
c.Add(CreateHitObject().With(h =>
|
||||||
{
|
{
|
||||||
h.HitObject.StartTime = START_TIME;
|
h.HitObject.StartTime = Time.Current + 5000;
|
||||||
h.AccentColour.Value = Color4.Orange;
|
h.AccentColour.Value = Color4.Orange;
|
||||||
}));
|
}));
|
||||||
})
|
})
|
||||||
@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
{
|
{
|
||||||
c.Add(CreateHitObject().With(h =>
|
c.Add(CreateHitObject().With(h =>
|
||||||
{
|
{
|
||||||
h.HitObject.StartTime = START_TIME;
|
h.HitObject.StartTime = Time.Current + 5000;
|
||||||
h.AccentColour.Value = Color4.Orange;
|
h.AccentColour.Value = Color4.Orange;
|
||||||
}));
|
}));
|
||||||
})
|
})
|
||||||
|
@ -19,8 +19,6 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class ManiaSkinnableTestScene : SkinnableTestScene
|
public abstract class ManiaSkinnableTestScene : SkinnableTestScene
|
||||||
{
|
{
|
||||||
protected const double START_TIME = 1000000000;
|
|
||||||
|
|
||||||
[Cached(Type = typeof(IScrollingInfo))]
|
[Cached(Type = typeof(IScrollingInfo))]
|
||||||
private readonly TestScrollingInfo scrollingInfo = new TestScrollingInfo();
|
private readonly TestScrollingInfo scrollingInfo = new TestScrollingInfo();
|
||||||
|
|
||||||
@ -55,27 +53,8 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
public readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
|
public readonly Bindable<ScrollingDirection> Direction = new Bindable<ScrollingDirection>();
|
||||||
|
|
||||||
IBindable<ScrollingDirection> IScrollingInfo.Direction => Direction;
|
IBindable<ScrollingDirection> IScrollingInfo.Direction => Direction;
|
||||||
IBindable<double> IScrollingInfo.TimeRange { get; } = new Bindable<double>(1000);
|
IBindable<double> IScrollingInfo.TimeRange { get; } = new Bindable<double>(5000);
|
||||||
IScrollAlgorithm IScrollingInfo.Algorithm { get; } = new ZeroScrollAlgorithm();
|
IScrollAlgorithm IScrollingInfo.Algorithm { get; } = new ConstantScrollAlgorithm();
|
||||||
}
|
|
||||||
|
|
||||||
private class ZeroScrollAlgorithm : IScrollAlgorithm
|
|
||||||
{
|
|
||||||
public double GetDisplayStartTime(double originTime, float offset, double timeRange, float scrollLength)
|
|
||||||
=> double.MinValue;
|
|
||||||
|
|
||||||
public float GetLength(double startTime, double endTime, double timeRange, float scrollLength)
|
|
||||||
=> scrollLength;
|
|
||||||
|
|
||||||
public float PositionAt(double time, double currentTime, double timeRange, float scrollLength)
|
|
||||||
=> (float)((time - START_TIME) / timeRange) * scrollLength;
|
|
||||||
|
|
||||||
public double TimeAt(float position, double currentTime, double timeRange, float scrollLength)
|
|
||||||
=> 0;
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,13 +15,13 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
|
||||||
|
|
||||||
[TestCase(6.7568168283591499d, "diffcalc-test")]
|
[TestCase(6.6634445062299665d, "diffcalc-test")]
|
||||||
[TestCase(1.0348244046058293d, "zero-length-sliders")]
|
[TestCase(1.0414203870195022d, "zero-length-sliders")]
|
||||||
public void Test(double expected, string name)
|
public void Test(double expected, string name)
|
||||||
=> base.Test(expected, name);
|
=> base.Test(expected, name);
|
||||||
|
|
||||||
[TestCase(8.4783236764532557d, "diffcalc-test")]
|
[TestCase(8.3858089051603368d, "diffcalc-test")]
|
||||||
[TestCase(1.2708532136987165d, "zero-length-sliders")]
|
[TestCase(1.2723279173428435d, "zero-length-sliders")]
|
||||||
public void TestClockRateAdjusted(double expected, string name)
|
public void TestClockRateAdjusted(double expected, string name)
|
||||||
=> Test(expected, name, new OsuModDoubleTime());
|
=> Test(expected, name, new OsuModDoubleTime());
|
||||||
|
|
||||||
|
@ -34,7 +34,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty
|
|||||||
|
|
||||||
double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
|
double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
|
||||||
double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
|
double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
|
||||||
double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2;
|
|
||||||
|
double baseAimPerformance = Math.Pow(5 * Math.Max(1, aimRating / 0.0675) - 4, 3) / 100000;
|
||||||
|
double baseSpeedPerformance = Math.Pow(5 * Math.Max(1, speedRating / 0.0675) - 4, 3) / 100000;
|
||||||
|
double basePerformance = Math.Pow(Math.Pow(baseAimPerformance, 1.1) + Math.Pow(baseSpeedPerformance, 1.1), 1 / 1.1);
|
||||||
|
double starRating = basePerformance > 0.00001 ? Math.Cbrt(1.12) * 0.027 * (Math.Cbrt(100000 / Math.Pow(2, 1 / 1.1) * basePerformance) + 4) : 0;
|
||||||
|
|
||||||
HitWindows hitWindows = new OsuHitWindows();
|
HitWindows hitWindows = new OsuHitWindows();
|
||||||
hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);
|
hitWindows.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);
|
||||||
|
@ -55,6 +55,59 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddAssert("stack empty", () => Stack.CurrentScreen == null);
|
AddAssert("stack empty", () => Stack.CurrentScreen == null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestClockPositionPreservedBetweenSwitches()
|
||||||
|
{
|
||||||
|
BeatmapInfo targetDifficulty = null;
|
||||||
|
AddStep("seek editor to 00:05:00", () => EditorClock.Seek(5000));
|
||||||
|
|
||||||
|
AddStep("set target difficulty", () => targetDifficulty = importedBeatmapSet.Beatmaps.Last(beatmap => !beatmap.Equals(Beatmap.Value.BeatmapInfo)));
|
||||||
|
switchToDifficulty(() => targetDifficulty);
|
||||||
|
confirmEditingBeatmap(() => targetDifficulty);
|
||||||
|
AddAssert("editor clock at 00:05:00", () => EditorClock.CurrentTime == 5000);
|
||||||
|
|
||||||
|
AddStep("exit editor", () => Stack.Exit());
|
||||||
|
// ensure editor loader didn't resume.
|
||||||
|
AddAssert("stack empty", () => Stack.CurrentScreen == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestClipboardPreservedAfterSwitch([Values] bool sameRuleset)
|
||||||
|
{
|
||||||
|
BeatmapInfo targetDifficulty = null;
|
||||||
|
|
||||||
|
AddStep("select first object", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.First()));
|
||||||
|
AddStep("copy object", () => Editor.Copy());
|
||||||
|
|
||||||
|
AddStep("set target difficulty", () =>
|
||||||
|
{
|
||||||
|
targetDifficulty = sameRuleset
|
||||||
|
? importedBeatmapSet.Beatmaps.Last(beatmap => !beatmap.Equals(Beatmap.Value.BeatmapInfo) && beatmap.RulesetID == Beatmap.Value.BeatmapInfo.RulesetID)
|
||||||
|
: importedBeatmapSet.Beatmaps.Last(beatmap => !beatmap.Equals(Beatmap.Value.BeatmapInfo) && beatmap.RulesetID != Beatmap.Value.BeatmapInfo.RulesetID);
|
||||||
|
});
|
||||||
|
switchToDifficulty(() => targetDifficulty);
|
||||||
|
confirmEditingBeatmap(() => targetDifficulty);
|
||||||
|
|
||||||
|
AddAssert("no objects selected", () => !EditorBeatmap.SelectedHitObjects.Any());
|
||||||
|
AddStep("paste object", () => Editor.Paste());
|
||||||
|
|
||||||
|
if (sameRuleset)
|
||||||
|
AddAssert("object was pasted", () => EditorBeatmap.SelectedHitObjects.Any());
|
||||||
|
else
|
||||||
|
AddAssert("object was not pasted", () => !EditorBeatmap.SelectedHitObjects.Any());
|
||||||
|
|
||||||
|
AddStep("exit editor", () => Stack.Exit());
|
||||||
|
|
||||||
|
if (sameRuleset)
|
||||||
|
{
|
||||||
|
AddUntilStep("prompt for save dialog shown", () => DialogOverlay.CurrentDialog is PromptForSaveDialog);
|
||||||
|
AddStep("discard changes", () => ((PromptForSaveDialog)DialogOverlay.CurrentDialog).PerformOkAction());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure editor loader didn't resume.
|
||||||
|
AddAssert("stack empty", () => Stack.CurrentScreen == null);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestPreventSwitchDueToUnsavedChanges()
|
public void TestPreventSwitchDueToUnsavedChanges()
|
||||||
{
|
{
|
||||||
@ -118,7 +171,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
private void confirmEditingBeatmap(Func<BeatmapInfo> targetDifficulty)
|
private void confirmEditingBeatmap(Func<BeatmapInfo> targetDifficulty)
|
||||||
{
|
{
|
||||||
AddUntilStep("current beatmap is correct", () => Beatmap.Value.BeatmapInfo.Equals(targetDifficulty.Invoke()));
|
AddUntilStep("current beatmap is correct", () => Beatmap.Value.BeatmapInfo.Equals(targetDifficulty.Invoke()));
|
||||||
AddUntilStep("current screen is editor", () => Stack.CurrentScreen is Editor);
|
AddUntilStep("current screen is editor", () => Stack.CurrentScreen == Editor && Editor?.IsLoaded == true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
|
recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
|
||||||
{
|
{
|
||||||
Recorder = recorder = new TestReplayRecorder(new Score { Replay = replay })
|
Recorder = recorder = new TestReplayRecorder(new Score
|
||||||
|
{
|
||||||
|
Replay = replay,
|
||||||
|
ScoreInfo = { Beatmap = gameplayBeatmap.BeatmapInfo }
|
||||||
|
})
|
||||||
{
|
{
|
||||||
ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos),
|
ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos),
|
||||||
},
|
},
|
||||||
|
@ -45,7 +45,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
|
recordingManager = new TestRulesetInputManager(new TestSceneModSettings.TestRulesetInfo(), 0, SimultaneousBindingMode.Unique)
|
||||||
{
|
{
|
||||||
Recorder = new TestReplayRecorder(new Score { Replay = replay })
|
Recorder = new TestReplayRecorder(new Score
|
||||||
|
{
|
||||||
|
Replay = replay,
|
||||||
|
ScoreInfo = { Beatmap = gameplayBeatmap.BeatmapInfo }
|
||||||
|
})
|
||||||
{
|
{
|
||||||
ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos)
|
ScreenSpaceToGamefield = pos => recordingManager.ToLocalSpace(pos)
|
||||||
},
|
},
|
||||||
|
@ -354,7 +354,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
internal class TestReplayRecorder : ReplayRecorder<TestAction>
|
internal class TestReplayRecorder : ReplayRecorder<TestAction>
|
||||||
{
|
{
|
||||||
public TestReplayRecorder()
|
public TestReplayRecorder()
|
||||||
: base(new Score())
|
: base(new Score { ScoreInfo = { Beatmap = new BeatmapInfo() } })
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
@ -82,7 +83,23 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestJoinRoomWithPassword()
|
public void TestJoinRoomWithIncorrectPassword()
|
||||||
|
{
|
||||||
|
DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null;
|
||||||
|
|
||||||
|
AddStep("add room", () => RoomManager.AddRooms(1, withPassword: true));
|
||||||
|
AddStep("select room", () => InputManager.Key(Key.Down));
|
||||||
|
AddStep("attempt join room", () => InputManager.Key(Key.Enter));
|
||||||
|
AddUntilStep("password prompt appeared", () => (passwordEntryPopover = InputManager.ChildrenOfType<DrawableLoungeRoom.PasswordEntryPopover>().FirstOrDefault()) != null);
|
||||||
|
AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType<TextBox>().First().Text = "wrong");
|
||||||
|
AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType<OsuButton>().First().TriggerClick());
|
||||||
|
|
||||||
|
AddAssert("room not joined", () => loungeScreen.IsCurrentScreen());
|
||||||
|
AddUntilStep("password prompt still visible", () => passwordEntryPopover.State.Value == Visibility.Visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestJoinRoomWithCorrectPassword()
|
||||||
{
|
{
|
||||||
DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null;
|
DrawableLoungeRoom.PasswordEntryPopover passwordEntryPopover = null;
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Online.Rooms;
|
using osu.Game.Online.Rooms;
|
||||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
|
using osu.Game.Screens.OnlinePlay.Multiplayer.Match;
|
||||||
|
|
||||||
@ -15,11 +16,13 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
{
|
{
|
||||||
SelectedRoom.Value = new Room();
|
SelectedRoom.Value = new Room();
|
||||||
|
|
||||||
Child = new MultiplayerMatchFooter
|
Child = new Container
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Height = 50
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Height = 50,
|
||||||
|
Child = new MultiplayerMatchFooter()
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,33 @@ namespace osu.Game.Extensions
|
|||||||
return repeatDelegate;
|
return repeatDelegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shakes this drawable.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="target">The target to shake.</param>
|
||||||
|
/// <param name="shakeDuration">The length of a single shake.</param>
|
||||||
|
/// <param name="shakeMagnitude">Pixels of displacement per shake.</param>
|
||||||
|
/// <param name="maximumLength">The maximum length the shake should last.</param>
|
||||||
|
public static void Shake(this Drawable target, double shakeDuration = 80, float shakeMagnitude = 8, double? maximumLength = null)
|
||||||
|
{
|
||||||
|
// if we don't have enough time, don't bother shaking.
|
||||||
|
if (maximumLength < shakeDuration * 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var sequence = target.MoveToX(shakeMagnitude, shakeDuration / 2, Easing.OutSine).Then()
|
||||||
|
.MoveToX(-shakeMagnitude, shakeDuration, Easing.InOutSine).Then();
|
||||||
|
|
||||||
|
// if we don't have enough time for the second shake, skip it.
|
||||||
|
if (!maximumLength.HasValue || maximumLength >= shakeDuration * 4)
|
||||||
|
{
|
||||||
|
sequence = sequence
|
||||||
|
.MoveToX(shakeMagnitude, shakeDuration, Easing.InOutSine).Then()
|
||||||
|
.MoveToX(-shakeMagnitude, shakeDuration, Easing.InOutSine).Then();
|
||||||
|
}
|
||||||
|
|
||||||
|
sequence.MoveToX(0, shakeDuration / 2, Easing.InSine);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Accepts a delta vector in screen-space coordinates and converts it to one which can be applied to this drawable's position.
|
/// Accepts a delta vector in screen-space coordinates and converts it to one which can be applied to this drawable's position.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1,8 +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 osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Extensions;
|
||||||
|
|
||||||
namespace osu.Game.Graphics.Containers
|
namespace osu.Game.Graphics.Containers
|
||||||
{
|
{
|
||||||
@ -16,40 +16,10 @@ namespace osu.Game.Graphics.Containers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public float ShakeDuration = 80;
|
public float ShakeDuration = 80;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Total number of shakes. May be shortened if possible.
|
|
||||||
/// </summary>
|
|
||||||
public float TotalShakes = 4;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Pixels of displacement per shake.
|
|
||||||
/// </summary>
|
|
||||||
public float ShakeMagnitude = 8;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Shake the contents of this container.
|
/// Shake the contents of this container.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="maximumLength">The maximum length the shake should last.</param>
|
/// <param name="maximumLength">The maximum length the shake should last.</param>
|
||||||
public void Shake(double? maximumLength = null)
|
public void Shake(double? maximumLength = null) => this.Shake(ShakeDuration, maximumLength: maximumLength);
|
||||||
{
|
|
||||||
const float shake_amount = 8;
|
|
||||||
|
|
||||||
// if we don't have enough time, don't bother shaking.
|
|
||||||
if (maximumLength < ShakeDuration * 2)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var sequence = this.MoveToX(shake_amount, ShakeDuration / 2, Easing.OutSine).Then()
|
|
||||||
.MoveToX(-shake_amount, ShakeDuration, Easing.InOutSine).Then();
|
|
||||||
|
|
||||||
// if we don't have enough time for the second shake, skip it.
|
|
||||||
if (!maximumLength.HasValue || maximumLength >= ShakeDuration * 4)
|
|
||||||
{
|
|
||||||
sequence = sequence
|
|
||||||
.MoveToX(shake_amount, ShakeDuration, Easing.InOutSine).Then()
|
|
||||||
.MoveToX(-shake_amount, ShakeDuration, Easing.InOutSine).Then();
|
|
||||||
}
|
|
||||||
|
|
||||||
sequence.MoveToX(0, ShakeDuration / 2, Easing.InSine);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,11 +179,7 @@ namespace osu.Game.Online.Rooms
|
|||||||
if (EndDate.Value != null && DateTimeOffset.Now >= EndDate.Value)
|
if (EndDate.Value != null && DateTimeOffset.Now >= EndDate.Value)
|
||||||
Status.Value = new RoomStatusEnded();
|
Status.Value = new RoomStatusEnded();
|
||||||
|
|
||||||
// Todo: This is not the best way/place to do this, but the intention is to display all playlist items when the room has ended,
|
other.RemoveExpiredPlaylistItems();
|
||||||
// and display only the non-expired playlist items while the room is still active. In order to achieve this, all expired items are removed from the source Room.
|
|
||||||
// More refactoring is required before this can be done locally instead - DrawableRoomPlaylist is currently directly bound to the playlist to display items in the room.
|
|
||||||
if (!(Status.Value is RoomStatusEnded))
|
|
||||||
other.Playlist.RemoveAll(i => i.Expired);
|
|
||||||
|
|
||||||
if (!Playlist.SequenceEqual(other.Playlist))
|
if (!Playlist.SequenceEqual(other.Playlist))
|
||||||
{
|
{
|
||||||
@ -200,6 +196,15 @@ namespace osu.Game.Online.Rooms
|
|||||||
Position.Value = other.Position.Value;
|
Position.Value = other.Position.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RemoveExpiredPlaylistItems()
|
||||||
|
{
|
||||||
|
// Todo: This is not the best way/place to do this, but the intention is to display all playlist items when the room has ended,
|
||||||
|
// and display only the non-expired playlist items while the room is still active. In order to achieve this, all expired items are removed from the source Room.
|
||||||
|
// More refactoring is required before this can be done locally instead - DrawableRoomPlaylist is currently directly bound to the playlist to display items in the room.
|
||||||
|
if (!(Status.Value is RoomStatusEnded))
|
||||||
|
Playlist.RemoveAll(i => i.Expired);
|
||||||
|
}
|
||||||
|
|
||||||
public bool ShouldSerializeRoomID() => false;
|
public bool ShouldSerializeRoomID() => false;
|
||||||
public bool ShouldSerializeHost() => false;
|
public bool ShouldSerializeHost() => false;
|
||||||
public bool ShouldSerializeEndDate() => false;
|
public bool ShouldSerializeEndDate() => false;
|
||||||
|
@ -15,8 +15,6 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
using osu.Game.Replays.Legacy;
|
using osu.Game.Replays.Legacy;
|
||||||
using osu.Game.Rulesets;
|
|
||||||
using osu.Game.Rulesets.Mods;
|
|
||||||
using osu.Game.Rulesets.Replays;
|
using osu.Game.Rulesets.Replays;
|
||||||
using osu.Game.Rulesets.Replays.Types;
|
using osu.Game.Rulesets.Replays.Types;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
@ -46,15 +44,8 @@ namespace osu.Game.Online.Spectator
|
|||||||
private readonly BindableDictionary<int, SpectatorState> playingUserStates = new BindableDictionary<int, SpectatorState>();
|
private readonly BindableDictionary<int, SpectatorState> playingUserStates = new BindableDictionary<int, SpectatorState>();
|
||||||
|
|
||||||
private IBeatmap? currentBeatmap;
|
private IBeatmap? currentBeatmap;
|
||||||
|
|
||||||
private Score? currentScore;
|
private Score? currentScore;
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private IBindable<RulesetInfo> currentRuleset { get; set; } = null!;
|
|
||||||
|
|
||||||
[Resolved]
|
|
||||||
private IBindable<IReadOnlyList<Mod>> currentMods { get; set; } = null!;
|
|
||||||
|
|
||||||
private readonly SpectatorState currentState = new SpectatorState();
|
private readonly SpectatorState currentState = new SpectatorState();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -153,9 +144,9 @@ namespace osu.Game.Online.Spectator
|
|||||||
IsPlaying = true;
|
IsPlaying = true;
|
||||||
|
|
||||||
// transfer state at point of beginning play
|
// transfer state at point of beginning play
|
||||||
currentState.BeatmapID = beatmap.BeatmapInfo.OnlineBeatmapID;
|
currentState.BeatmapID = score.ScoreInfo.Beatmap.OnlineBeatmapID;
|
||||||
currentState.RulesetID = currentRuleset.Value.ID;
|
currentState.RulesetID = score.ScoreInfo.RulesetID;
|
||||||
currentState.Mods = currentMods.Value.Select(m => new APIMod(m));
|
currentState.Mods = score.ScoreInfo.Mods.Select(m => new APIMod(m)).ToArray();
|
||||||
|
|
||||||
currentBeatmap = beatmap.PlayableBeatmap;
|
currentBeatmap = beatmap.PlayableBeatmap;
|
||||||
currentScore = score;
|
currentScore = score;
|
||||||
|
@ -80,7 +80,7 @@ namespace osu.Game.Screens.Edit.Compose
|
|||||||
public bool OnPressed(PlatformAction action)
|
public bool OnPressed(PlatformAction action)
|
||||||
{
|
{
|
||||||
if (action == PlatformAction.Copy)
|
if (action == PlatformAction.Copy)
|
||||||
host.GetClipboard().SetText(formatSelectionAsString());
|
host.GetClipboard()?.SetText(formatSelectionAsString());
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -317,6 +317,16 @@ namespace osu.Game.Screens.Edit
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void UpdateClockSource() => clock.ChangeSource(Beatmap.Value.Track);
|
public void UpdateClockSource() => clock.ChangeSource(Beatmap.Value.Track);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Restore the editor to a provided state.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="state">The state to restore.</param>
|
||||||
|
public void RestoreState([NotNull] EditorState state) => Schedule(() =>
|
||||||
|
{
|
||||||
|
clock.Seek(state.Time);
|
||||||
|
clipboard.Value = state.ClipboardContent;
|
||||||
|
});
|
||||||
|
|
||||||
protected void Save()
|
protected void Save()
|
||||||
{
|
{
|
||||||
// no longer new after first user-triggered save.
|
// no longer new after first user-triggered save.
|
||||||
@ -740,7 +750,11 @@ namespace osu.Game.Screens.Edit
|
|||||||
return new DifficultyMenuItem(beatmapInfo, isCurrentDifficulty, SwitchToDifficulty);
|
return new DifficultyMenuItem(beatmapInfo, isCurrentDifficulty, SwitchToDifficulty);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void SwitchToDifficulty(BeatmapInfo beatmapInfo) => loader?.ScheduleDifficultySwitch(beatmapInfo);
|
protected void SwitchToDifficulty(BeatmapInfo nextBeatmap) => loader?.ScheduleDifficultySwitch(nextBeatmap, new EditorState
|
||||||
|
{
|
||||||
|
Time = clock.CurrentTimeAccurate,
|
||||||
|
ClipboardContent = editorBeatmap.BeatmapInfo.RulesetID == nextBeatmap.RulesetID ? clipboard.Value : string.Empty
|
||||||
|
});
|
||||||
|
|
||||||
private void cancelExit() => loader?.CancelPendingDifficultySwitch();
|
private void cancelExit() => loader?.CancelPendingDifficultySwitch();
|
||||||
|
|
||||||
|
@ -20,6 +20,13 @@ namespace osu.Game.Screens.Edit
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class EditorLoader : ScreenWithBeatmapBackground
|
public class EditorLoader : ScreenWithBeatmapBackground
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The stored state from the last editor opened.
|
||||||
|
/// This will be read by the next editor instance to be opened to restore any relevant previous state.
|
||||||
|
/// </summary>
|
||||||
|
[CanBeNull]
|
||||||
|
private EditorState state;
|
||||||
|
|
||||||
public override float BackgroundParallaxAmount => 0.1f;
|
public override float BackgroundParallaxAmount => 0.1f;
|
||||||
|
|
||||||
public override bool AllowBackButton => false;
|
public override bool AllowBackButton => false;
|
||||||
@ -61,7 +68,7 @@ namespace osu.Game.Screens.Edit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ScheduleDifficultySwitch(BeatmapInfo beatmapInfo)
|
public void ScheduleDifficultySwitch(BeatmapInfo nextBeatmap, EditorState editorState)
|
||||||
{
|
{
|
||||||
scheduledDifficultySwitch?.Cancel();
|
scheduledDifficultySwitch?.Cancel();
|
||||||
ValidForResume = true;
|
ValidForResume = true;
|
||||||
@ -70,7 +77,8 @@ namespace osu.Game.Screens.Edit
|
|||||||
|
|
||||||
scheduledDifficultySwitch = Schedule(() =>
|
scheduledDifficultySwitch = Schedule(() =>
|
||||||
{
|
{
|
||||||
Beatmap.Value = beatmapManager.GetWorkingBeatmap(beatmapInfo);
|
Beatmap.Value = beatmapManager.GetWorkingBeatmap(nextBeatmap);
|
||||||
|
state = editorState;
|
||||||
|
|
||||||
// This screen is a weird exception to the rule that nothing after song select changes the global beatmap.
|
// This screen is a weird exception to the rule that nothing after song select changes the global beatmap.
|
||||||
// Because of this, we need to update the background stack's beatmap to match.
|
// Because of this, we need to update the background stack's beatmap to match.
|
||||||
@ -83,7 +91,13 @@ namespace osu.Game.Screens.Edit
|
|||||||
|
|
||||||
private void pushEditor()
|
private void pushEditor()
|
||||||
{
|
{
|
||||||
this.Push(CreateEditor());
|
var editor = CreateEditor();
|
||||||
|
|
||||||
|
this.Push(editor);
|
||||||
|
|
||||||
|
if (state != null)
|
||||||
|
editor.RestoreState(state);
|
||||||
|
|
||||||
ValidForResume = false;
|
ValidForResume = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
23
osu.Game/Screens/Edit/EditorState.cs
Normal file
23
osu.Game/Screens/Edit/EditorState.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Edit
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Structure used to convey the general state of an <see cref="Editor"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
public class EditorState
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The current audio time.
|
||||||
|
/// </summary>
|
||||||
|
public double Time { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The editor clipboard content.
|
||||||
|
/// </summary>
|
||||||
|
public string ClipboardContent { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
@ -57,7 +57,10 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach (var incoming in result)
|
foreach (var incoming in result)
|
||||||
|
{
|
||||||
|
incoming.RemoveExpiredPlaylistItems();
|
||||||
RoomManager.AddOrUpdateRoom(incoming);
|
RoomManager.AddOrUpdateRoom(incoming);
|
||||||
|
}
|
||||||
|
|
||||||
initialRoomsReceived.Value = true;
|
initialRoomsReceived.Value = true;
|
||||||
tcs.SetResult(true);
|
tcs.SetResult(true);
|
||||||
|
@ -87,9 +87,10 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
|||||||
|
|
||||||
currentJoinRoomRequest.Failure += exception =>
|
currentJoinRoomRequest.Failure += exception =>
|
||||||
{
|
{
|
||||||
if (!(exception is OperationCanceledException))
|
if (exception is OperationCanceledException)
|
||||||
Logger.Log($"Failed to join room: {exception}", level: LogLevel.Important);
|
return;
|
||||||
onError?.Invoke(exception.ToString());
|
|
||||||
|
onError?.Invoke(exception.Message);
|
||||||
};
|
};
|
||||||
|
|
||||||
api.Queue(currentJoinRoomRequest);
|
api.Queue(currentJoinRoomRequest);
|
||||||
|
@ -39,6 +39,7 @@ namespace osu.Game.Screens.OnlinePlay.Components
|
|||||||
|
|
||||||
pollReq.Success += result =>
|
pollReq.Success += result =>
|
||||||
{
|
{
|
||||||
|
result.RemoveExpiredPlaylistItems();
|
||||||
RoomManager.AddOrUpdateRoom(result);
|
RoomManager.AddOrUpdateRoom(result);
|
||||||
tcs.SetResult(true);
|
tcs.SetResult(true);
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
// 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;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
@ -15,6 +14,9 @@ using osu.Framework.Graphics.Shapes;
|
|||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Extensions;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Graphics.UserInterfaceV2;
|
using osu.Game.Graphics.UserInterfaceV2;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
@ -120,7 +122,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Popover GetPopover() => new PasswordEntryPopover(Room) { JoinRequested = lounge.Join };
|
public Popover GetPopover() => new PasswordEntryPopover(Room);
|
||||||
|
|
||||||
public MenuItem[] ContextMenuItems => new MenuItem[]
|
public MenuItem[] ContextMenuItems => new MenuItem[]
|
||||||
{
|
{
|
||||||
@ -176,7 +178,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
|||||||
{
|
{
|
||||||
private readonly Room room;
|
private readonly Room room;
|
||||||
|
|
||||||
public Action<Room, string> JoinRequested;
|
[Resolved(canBeNull: true)]
|
||||||
|
private LoungeSubScreen lounge { get; set; }
|
||||||
|
|
||||||
public PasswordEntryPopover(Room room)
|
public PasswordEntryPopover(Room room)
|
||||||
{
|
{
|
||||||
@ -184,30 +187,61 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
|||||||
}
|
}
|
||||||
|
|
||||||
private OsuPasswordTextBox passwordTextbox;
|
private OsuPasswordTextBox passwordTextbox;
|
||||||
|
private TriangleButton joinButton;
|
||||||
|
private OsuSpriteText errorText;
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
Child = new FillFlowContainer
|
Child = new FillFlowContainer
|
||||||
{
|
{
|
||||||
Margin = new MarginPadding(10),
|
Margin = new MarginPadding(10),
|
||||||
Spacing = new Vector2(5),
|
Spacing = new Vector2(5),
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Vertical,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
passwordTextbox = new OsuPasswordTextBox
|
new FillFlowContainer
|
||||||
{
|
{
|
||||||
Width = 200,
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(5),
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
passwordTextbox = new OsuPasswordTextBox
|
||||||
|
{
|
||||||
|
Width = 200,
|
||||||
|
PlaceholderText = "password",
|
||||||
|
},
|
||||||
|
joinButton = new TriangleButton
|
||||||
|
{
|
||||||
|
Width = 80,
|
||||||
|
Text = "Join Room",
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
new TriangleButton
|
errorText = new OsuSpriteText
|
||||||
{
|
{
|
||||||
Width = 80,
|
Colour = colours.Red,
|
||||||
Text = "Join Room",
|
},
|
||||||
Action = () => JoinRequested?.Invoke(room, passwordTextbox.Text)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
joinButton.Action = () => lounge?.Join(room, passwordTextbox.Text, null, joinFailed);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void joinFailed(string error)
|
||||||
|
{
|
||||||
|
passwordTextbox.Text = string.Empty;
|
||||||
|
|
||||||
|
errorText.Text = error;
|
||||||
|
errorText
|
||||||
|
.FadeIn()
|
||||||
|
.FlashColour(Color4.White, 200)
|
||||||
|
.Delay(1000)
|
||||||
|
.FadeOutFromOne(1000, Easing.In);
|
||||||
|
|
||||||
|
Body.Shake();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -215,7 +249,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
|||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
Schedule(() => GetContainingInputManager().ChangeFocus(passwordTextbox));
|
Schedule(() => GetContainingInputManager().ChangeFocus(passwordTextbox));
|
||||||
passwordTextbox.OnCommit += (_, __) => JoinRequested?.Invoke(room, passwordTextbox.Text);
|
passwordTextbox.OnCommit += (_, __) => lounge?.Join(room, passwordTextbox.Text, null, joinFailed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -290,7 +290,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
|||||||
popoverContainer.HidePopover();
|
popoverContainer.HidePopover();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Join(Room room, string password) => Schedule(() =>
|
public void Join(Room room, string password, Action<Room> onSuccess = null, Action<string> onFailure = null) => Schedule(() =>
|
||||||
{
|
{
|
||||||
if (joiningRoomOperation != null)
|
if (joiningRoomOperation != null)
|
||||||
return;
|
return;
|
||||||
@ -302,10 +302,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge
|
|||||||
Open(room);
|
Open(room);
|
||||||
joiningRoomOperation?.Dispose();
|
joiningRoomOperation?.Dispose();
|
||||||
joiningRoomOperation = null;
|
joiningRoomOperation = null;
|
||||||
}, _ =>
|
onSuccess?.Invoke(room);
|
||||||
|
}, error =>
|
||||||
{
|
{
|
||||||
joiningRoomOperation?.Dispose();
|
joiningRoomOperation?.Dispose();
|
||||||
joiningRoomOperation = null;
|
joiningRoomOperation = null;
|
||||||
|
onFailure?.Invoke(error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -23,7 +23,10 @@ namespace osu.Game.Tests.Beatmaps
|
|||||||
protected abstract string ResourceAssembly { get; }
|
protected abstract string ResourceAssembly { get; }
|
||||||
|
|
||||||
protected void Test(double expected, string name, params Mod[] mods)
|
protected void Test(double expected, string name, params Mod[] mods)
|
||||||
=> Assert.AreEqual(expected, CreateDifficultyCalculator(getBeatmap(name)).Calculate(mods).StarRating);
|
{
|
||||||
|
// Platform-dependent math functions (Pow, Cbrt, Exp, etc) may result in minute differences.
|
||||||
|
Assert.That(CreateDifficultyCalculator(getBeatmap(name)).Calculate(mods).StarRating, Is.EqualTo(expected).Within(0.00001));
|
||||||
|
}
|
||||||
|
|
||||||
private WorkingBeatmap getBeatmap(string name)
|
private WorkingBeatmap getBeatmap(string name)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user