diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index 8d8387378e..19881b5c33 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -15,13 +15,13 @@ namespace osu.Game.Rulesets.Osu.Tests { protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; - [TestCase(6.7568168283591499d, "diffcalc-test")] - [TestCase(1.0348244046058293d, "zero-length-sliders")] + [TestCase(6.6634445062299665d, "diffcalc-test")] + [TestCase(1.0414203870195022d, "zero-length-sliders")] public void Test(double expected, string name) => base.Test(expected, name); - [TestCase(8.4783236764532557d, "diffcalc-test")] - [TestCase(1.2708532136987165d, "zero-length-sliders")] + [TestCase(8.3858089051603368d, "diffcalc-test")] + [TestCase(1.2723279173428435d, "zero-length-sliders")] public void TestClockRateAdjusted(double expected, string name) => Test(expected, name, new OsuModDoubleTime()); diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs index 28b2174071..d10a807bd0 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs @@ -35,7 +35,11 @@ namespace osu.Game.Rulesets.Osu.Difficulty double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; double flashlightRating = Math.Sqrt(skills[2].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.SetDifficulty(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty); diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs index 61565c88f4..4e2a91af78 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerLoungeSubScreen.cs @@ -3,6 +3,7 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Framework.Screens; using osu.Framework.Testing; @@ -82,7 +83,23 @@ namespace osu.Game.Tests.Visual.Multiplayer } [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().FirstOrDefault()) != null); + AddStep("enter password in text box", () => passwordEntryPopover.ChildrenOfType().First().Text = "wrong"); + AddStep("press join room button", () => passwordEntryPopover.ChildrenOfType().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; diff --git a/osu.Game/Extensions/DrawableExtensions.cs b/osu.Game/Extensions/DrawableExtensions.cs index 52d8230fb6..03cc345947 100644 --- a/osu.Game/Extensions/DrawableExtensions.cs +++ b/osu.Game/Extensions/DrawableExtensions.cs @@ -38,6 +38,33 @@ namespace osu.Game.Extensions return repeatDelegate; } + /// + /// Shakes this drawable. + /// + /// The target to shake. + /// The length of a single shake. + /// Pixels of displacement per shake. + /// The maximum length the shake should last. + 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); + } + /// /// Accepts a delta vector in screen-space coordinates and converts it to one which can be applied to this drawable's position. /// diff --git a/osu.Game/Graphics/Containers/ShakeContainer.cs b/osu.Game/Graphics/Containers/ShakeContainer.cs index dca9df1e98..8a0ce287db 100644 --- a/osu.Game/Graphics/Containers/ShakeContainer.cs +++ b/osu.Game/Graphics/Containers/ShakeContainer.cs @@ -1,8 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Extensions; namespace osu.Game.Graphics.Containers { @@ -16,40 +16,10 @@ namespace osu.Game.Graphics.Containers /// public float ShakeDuration = 80; - /// - /// Total number of shakes. May be shortened if possible. - /// - public float TotalShakes = 4; - - /// - /// Pixels of displacement per shake. - /// - public float ShakeMagnitude = 8; - /// /// Shake the contents of this container. /// /// The maximum length the shake should last. - public void Shake(double? maximumLength = null) - { - 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); - } + public void Shake(double? maximumLength = null) => this.Shake(ShakeDuration, maximumLength: maximumLength); } } diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs index 3b6c1c8be0..a64d89b699 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs @@ -87,9 +87,10 @@ namespace osu.Game.Screens.OnlinePlay.Components currentJoinRoomRequest.Failure += exception => { - if (!(exception is OperationCanceledException)) - Logger.Log($"Failed to join room: {exception}", level: LogLevel.Important); - onError?.Invoke(exception.ToString()); + if (exception is OperationCanceledException) + return; + + onError?.Invoke(exception.Message); }; api.Queue(currentJoinRoomRequest); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs index a13d67a0c9..95fa34ab43 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/DrawableLoungeRoom.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -15,6 +14,9 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Bindings; 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.UserInterfaceV2; 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[] { @@ -176,7 +178,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { private readonly Room room; - public Action JoinRequested; + [Resolved(canBeNull: true)] + private LoungeSubScreen lounge { get; set; } public PasswordEntryPopover(Room room) { @@ -184,30 +187,61 @@ namespace osu.Game.Screens.OnlinePlay.Lounge } private OsuPasswordTextBox passwordTextbox; + private TriangleButton joinButton; + private OsuSpriteText errorText; [BackgroundDependencyLoader] - private void load() + private void load(OsuColour colours) { Child = new FillFlowContainer { Margin = new MarginPadding(10), Spacing = new Vector2(5), AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, + Direction = FillDirection.Vertical, 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, - Text = "Join Room", - Action = () => JoinRequested?.Invoke(room, passwordTextbox.Text) - } + Colour = colours.Red, + }, } }; + + 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() @@ -215,7 +249,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge base.LoadComplete(); Schedule(() => GetContainingInputManager().ChangeFocus(passwordTextbox)); - passwordTextbox.OnCommit += (_, __) => JoinRequested?.Invoke(room, passwordTextbox.Text); + passwordTextbox.OnCommit += (_, __) => lounge?.Join(room, passwordTextbox.Text, null, joinFailed); } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index cca1394b6d..08bdd0487a 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -290,7 +290,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge popoverContainer.HidePopover(); } - public void Join(Room room, string password) => Schedule(() => + public void Join(Room room, string password, Action onSuccess = null, Action onFailure = null) => Schedule(() => { if (joiningRoomOperation != null) return; @@ -302,10 +302,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge Open(room); joiningRoomOperation?.Dispose(); joiningRoomOperation = null; - }, _ => + onSuccess?.Invoke(room); + }, error => { joiningRoomOperation?.Dispose(); joiningRoomOperation = null; + onFailure?.Invoke(error); }); }); diff --git a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs index 76f229a799..fdb3e1d465 100644 --- a/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs +++ b/osu.Game/Tests/Beatmaps/DifficultyCalculatorTest.cs @@ -23,7 +23,10 @@ namespace osu.Game.Tests.Beatmaps protected abstract string ResourceAssembly { get; } 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) {