mirror of
https://github.com/ppy/osu.git
synced 2025-01-28 08:43:20 +08:00
Merge branch 'master' into slider-controlpoint-deletion
This commit is contained in:
commit
f849926049
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
|
contact_links:
|
||||||
|
- name: osu!stable issues
|
||||||
|
url: https://github.com/ppy/osu-stable-issues
|
||||||
|
about: For issues regarding osu!stable (not osu!lazer), open them here.
|
@ -1,7 +0,0 @@
|
|||||||
---
|
|
||||||
name: Missing for Live
|
|
||||||
about: Features which are available in osu!stable but not yet in osu!lazer.
|
|
||||||
---
|
|
||||||
**Describe the missing feature:**
|
|
||||||
|
|
||||||
**Proposal designs of the feature:**
|
|
@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
public Action<int> RequestSelection;
|
public Action<int> RequestSelection;
|
||||||
public Action<Vector2[]> ControlPointsChanged;
|
public Action<Vector2[]> ControlPointsChanged;
|
||||||
|
|
||||||
public readonly Bindable<bool> IsSelected = new Bindable<bool>();
|
public readonly BindableBool IsSelected = new BindableBool();
|
||||||
public readonly int Index;
|
public readonly int Index;
|
||||||
|
|
||||||
private readonly Slider slider;
|
private readonly Slider slider;
|
||||||
|
@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
private void selectPiece(int index)
|
private void selectPiece(int index)
|
||||||
{
|
{
|
||||||
if (inputManager.CurrentState.Keyboard.ControlPressed)
|
if (inputManager.CurrentState.Keyboard.ControlPressed)
|
||||||
Pieces[index].IsSelected.Value = true;
|
Pieces[index].IsSelected.Toggle();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
foreach (var piece in Pieces)
|
foreach (var piece in Pieces)
|
||||||
|
@ -38,9 +38,10 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Show()
|
protected override void PopIn()
|
||||||
{
|
{
|
||||||
base.Show();
|
base.PopIn();
|
||||||
|
|
||||||
GameplayCursor.ActiveCursor.Hide();
|
GameplayCursor.ActiveCursor.Hide();
|
||||||
cursorScaleContainer.MoveTo(GameplayCursor.ActiveCursor.Position);
|
cursorScaleContainer.MoveTo(GameplayCursor.ActiveCursor.Position);
|
||||||
clickToResumeCursor.Appear();
|
clickToResumeCursor.Appear();
|
||||||
@ -55,13 +56,13 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Hide()
|
protected override void PopOut()
|
||||||
{
|
{
|
||||||
|
base.PopOut();
|
||||||
|
|
||||||
localCursorContainer?.Expire();
|
localCursorContainer?.Expire();
|
||||||
localCursorContainer = null;
|
localCursorContainer = null;
|
||||||
GameplayCursor.ActiveCursor.Show();
|
GameplayCursor?.ActiveCursor?.Show();
|
||||||
|
|
||||||
base.Hide();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnHover(HoverEvent e) => true;
|
protected override bool OnHover(HoverEvent e) => true;
|
||||||
|
@ -69,6 +69,24 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
confirmClockRunning(true);
|
confirmClockRunning(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestPauseWithResumeOverlay()
|
||||||
|
{
|
||||||
|
AddStep("move cursor to center", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.Centre));
|
||||||
|
AddUntilStep("wait for hitobjects", () => Player.ScoreProcessor.Health.Value < 1);
|
||||||
|
|
||||||
|
pauseAndConfirm();
|
||||||
|
|
||||||
|
resume();
|
||||||
|
confirmClockRunning(false);
|
||||||
|
confirmPauseOverlayShown(false);
|
||||||
|
|
||||||
|
pauseAndConfirm();
|
||||||
|
|
||||||
|
AddUntilStep("resume overlay is not active", () => Player.DrawableRuleset.ResumeOverlay.State.Value == Visibility.Hidden);
|
||||||
|
confirmPaused();
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestResumeWithResumeOverlaySkipped()
|
public void TestResumeWithResumeOverlaySkipped()
|
||||||
{
|
{
|
||||||
|
@ -7,11 +7,10 @@ using osu.Game.Online;
|
|||||||
using osu.Game.Online.API.Requests.Responses;
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens.Play;
|
|
||||||
using osu.Game.Users;
|
using osu.Game.Users;
|
||||||
using osuTK;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using osu.Game.Screens.Ranking.Pages;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
{
|
{
|
||||||
@ -42,7 +41,6 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Size = new Vector2(80, 40),
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -22,10 +22,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
public override IReadOnlyList<Type> RequiredTypes => new[]
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
{
|
{
|
||||||
typeof(ScoreInfo),
|
|
||||||
typeof(Results),
|
typeof(Results),
|
||||||
typeof(ResultsPage),
|
typeof(ResultsPage),
|
||||||
typeof(ScoreResultsPage),
|
typeof(ScoreResultsPage),
|
||||||
|
typeof(RetryButton),
|
||||||
|
typeof(ReplayDownloadButton),
|
||||||
typeof(LocalLeaderboardPage)
|
typeof(LocalLeaderboardPage)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
102
osu.Game.Tests/Visual/Online/TestSceneBeatmapRulesetSelector.cs
Normal file
102
osu.Game.Tests/Visual/Online/TestSceneBeatmapRulesetSelector.cs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
// 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 NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Overlays.BeatmapSet;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Online
|
||||||
|
{
|
||||||
|
public class TestSceneBeatmapRulesetSelector : OsuTestScene
|
||||||
|
{
|
||||||
|
public override IReadOnlyList<Type> RequiredTypes => new[]
|
||||||
|
{
|
||||||
|
typeof(BeatmapRulesetSelector),
|
||||||
|
typeof(BeatmapRulesetTabItem),
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly TestRulesetSelector selector;
|
||||||
|
|
||||||
|
public TestSceneBeatmapRulesetSelector()
|
||||||
|
{
|
||||||
|
Add(selector = new TestRulesetSelector());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private RulesetStore rulesets { get; set; }
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMultipleRulesetsBeatmapSet()
|
||||||
|
{
|
||||||
|
var enabledRulesets = rulesets.AvailableRulesets.Skip(1).Take(2);
|
||||||
|
|
||||||
|
AddStep("load multiple rulesets beatmapset", () =>
|
||||||
|
{
|
||||||
|
selector.BeatmapSet = new BeatmapSetInfo
|
||||||
|
{
|
||||||
|
Beatmaps = enabledRulesets.Select(r => new BeatmapInfo { Ruleset = r }).ToList()
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
var tabItems = selector.TabContainer.TabItems;
|
||||||
|
AddAssert("other rulesets disabled", () => tabItems.Except(tabItems.Where(t => enabledRulesets.Any(r => r.Equals(t.Value)))).All(t => !t.Enabled.Value));
|
||||||
|
AddAssert("left-most ruleset selected", () => tabItems.First(t => t.Enabled.Value).Active.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSingleRulesetBeatmapSet()
|
||||||
|
{
|
||||||
|
var enabledRuleset = rulesets.AvailableRulesets.Last();
|
||||||
|
|
||||||
|
AddStep("load single ruleset beatmapset", () =>
|
||||||
|
{
|
||||||
|
selector.BeatmapSet = new BeatmapSetInfo
|
||||||
|
{
|
||||||
|
Beatmaps = new List<BeatmapInfo>
|
||||||
|
{
|
||||||
|
new BeatmapInfo
|
||||||
|
{
|
||||||
|
Ruleset = enabledRuleset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("single ruleset selected", () => selector.SelectedTab.Value.Equals(enabledRuleset));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestEmptyBeatmapSet()
|
||||||
|
{
|
||||||
|
AddStep("load empty beatmapset", () => selector.BeatmapSet = new BeatmapSetInfo
|
||||||
|
{
|
||||||
|
Beatmaps = new List<BeatmapInfo>()
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("no ruleset selected", () => selector.SelectedTab == null);
|
||||||
|
AddAssert("all rulesets disabled", () => selector.TabContainer.TabItems.All(t => !t.Enabled.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNullBeatmapSet()
|
||||||
|
{
|
||||||
|
AddStep("load null beatmapset", () => selector.BeatmapSet = null);
|
||||||
|
|
||||||
|
AddAssert("no ruleset selected", () => selector.SelectedTab == null);
|
||||||
|
AddAssert("all rulesets disabled", () => selector.TabContainer.TabItems.All(t => !t.Enabled.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestRulesetSelector : BeatmapRulesetSelector
|
||||||
|
{
|
||||||
|
public new TabItem<RulesetInfo> SelectedTab => base.SelectedTab;
|
||||||
|
|
||||||
|
public new TabFillFlowContainer TabContainer => base.TabContainer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -40,24 +40,19 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
typeof(PreviewButton),
|
typeof(PreviewButton),
|
||||||
typeof(SuccessRate),
|
typeof(SuccessRate),
|
||||||
typeof(BeatmapAvailability),
|
typeof(BeatmapAvailability),
|
||||||
|
typeof(BeatmapRulesetSelector),
|
||||||
|
typeof(BeatmapRulesetTabItem),
|
||||||
};
|
};
|
||||||
|
|
||||||
protected override bool UseOnlineAPI => true;
|
protected override bool UseOnlineAPI => true;
|
||||||
|
|
||||||
private RulesetInfo taikoRuleset;
|
|
||||||
private RulesetInfo maniaRuleset;
|
|
||||||
|
|
||||||
public TestSceneBeatmapSetOverlay()
|
public TestSceneBeatmapSetOverlay()
|
||||||
{
|
{
|
||||||
Add(overlay = new TestBeatmapSetOverlay());
|
Add(overlay = new TestBeatmapSetOverlay());
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[Resolved]
|
||||||
private void load(RulesetStore rulesets)
|
private RulesetStore rulesets { get; set; }
|
||||||
{
|
|
||||||
taikoRuleset = rulesets.GetRuleset(1);
|
|
||||||
maniaRuleset = rulesets.GetRuleset(3);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestLoading()
|
public void TestLoading()
|
||||||
@ -111,7 +106,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
StarDifficulty = 9.99,
|
StarDifficulty = 9.99,
|
||||||
Version = @"TEST",
|
Version = @"TEST",
|
||||||
Length = 456000,
|
Length = 456000,
|
||||||
Ruleset = maniaRuleset,
|
Ruleset = rulesets.GetRuleset(3),
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
BaseDifficulty = new BeatmapDifficulty
|
||||||
{
|
{
|
||||||
CircleSize = 1,
|
CircleSize = 1,
|
||||||
@ -189,7 +184,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
StarDifficulty = 5.67,
|
StarDifficulty = 5.67,
|
||||||
Version = @"ANOTHER TEST",
|
Version = @"ANOTHER TEST",
|
||||||
Length = 123000,
|
Length = 123000,
|
||||||
Ruleset = taikoRuleset,
|
Ruleset = rulesets.GetRuleset(1),
|
||||||
BaseDifficulty = new BeatmapDifficulty
|
BaseDifficulty = new BeatmapDifficulty
|
||||||
{
|
{
|
||||||
CircleSize = 9,
|
CircleSize = 9,
|
||||||
@ -217,6 +212,54 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
downloadAssert(false);
|
downloadAssert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMultipleRulesets()
|
||||||
|
{
|
||||||
|
AddStep("show multiple rulesets beatmap", () =>
|
||||||
|
{
|
||||||
|
var beatmaps = new List<BeatmapInfo>();
|
||||||
|
|
||||||
|
foreach (var ruleset in rulesets.AvailableRulesets.Skip(1))
|
||||||
|
{
|
||||||
|
beatmaps.Add(new BeatmapInfo
|
||||||
|
{
|
||||||
|
Version = ruleset.Name,
|
||||||
|
Ruleset = ruleset,
|
||||||
|
BaseDifficulty = new BeatmapDifficulty(),
|
||||||
|
OnlineInfo = new BeatmapOnlineInfo(),
|
||||||
|
Metrics = new BeatmapMetrics
|
||||||
|
{
|
||||||
|
Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
|
Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
overlay.ShowBeatmapSet(new BeatmapSetInfo
|
||||||
|
{
|
||||||
|
Metadata = new BeatmapMetadata
|
||||||
|
{
|
||||||
|
Title = @"multiple rulesets beatmap",
|
||||||
|
Artist = @"none",
|
||||||
|
Author = new User
|
||||||
|
{
|
||||||
|
Username = "BanchoBot",
|
||||||
|
Id = 3,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
OnlineInfo = new BeatmapSetOnlineInfo
|
||||||
|
{
|
||||||
|
Covers = new BeatmapSetOnlineCovers(),
|
||||||
|
},
|
||||||
|
Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).ToArray() },
|
||||||
|
Beatmaps = beatmaps
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("shown beatmaps of current ruleset", () => overlay.Header.Picker.Difficulties.All(b => b.Beatmap.Ruleset.Equals(overlay.Header.RulesetSelector.Current.Value)));
|
||||||
|
AddAssert("left-most beatmap selected", () => overlay.Header.Picker.Difficulties.First().State == BeatmapPicker.DifficultySelectorState.Selected);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestHide()
|
public void TestHide()
|
||||||
{
|
{
|
||||||
@ -281,12 +324,12 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
|
|
||||||
private void downloadAssert(bool shown)
|
private void downloadAssert(bool shown)
|
||||||
{
|
{
|
||||||
AddAssert($"is download button {(shown ? "shown" : "hidden")}", () => overlay.DownloadButtonsVisible == shown);
|
AddAssert($"is download button {(shown ? "shown" : "hidden")}", () => overlay.Header.DownloadButtonsVisible == shown);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestBeatmapSetOverlay : BeatmapSetOverlay
|
private class TestBeatmapSetOverlay : BeatmapSetOverlay
|
||||||
{
|
{
|
||||||
public bool DownloadButtonsVisible => Header.DownloadButtonsVisible;
|
public new Header Header => base.Header;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -392,8 +392,15 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
req.Failure += e => { LogForModel(set, $"Online retrieval failed for {beatmap} ({e.Message})"); };
|
req.Failure += e => { LogForModel(set, $"Online retrieval failed for {beatmap} ({e.Message})"); };
|
||||||
|
|
||||||
// intentionally blocking to limit web request concurrency
|
try
|
||||||
req.Perform(api);
|
{
|
||||||
|
// intentionally blocking to limit web request concurrency
|
||||||
|
req.Perform(api);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
LogForModel(set, $"Online retrieval failed for {beatmap} ({e.Message})");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,16 +86,7 @@ namespace osu.Game.Database
|
|||||||
}, TaskCreationOptions.LongRunning);
|
}, TaskCreationOptions.LongRunning);
|
||||||
};
|
};
|
||||||
|
|
||||||
request.Failure += error =>
|
request.Failure += triggerFailure;
|
||||||
{
|
|
||||||
DownloadFailed?.Invoke(request);
|
|
||||||
|
|
||||||
if (error is OperationCanceledException) return;
|
|
||||||
|
|
||||||
notification.State = ProgressNotificationState.Cancelled;
|
|
||||||
Logger.Error(error, $"{HumanisedModelName.Titleize()} download failed!");
|
|
||||||
currentDownloads.Remove(request);
|
|
||||||
};
|
|
||||||
|
|
||||||
notification.CancelRequested += () =>
|
notification.CancelRequested += () =>
|
||||||
{
|
{
|
||||||
@ -108,11 +99,31 @@ namespace osu.Game.Database
|
|||||||
currentDownloads.Add(request);
|
currentDownloads.Add(request);
|
||||||
PostNotification?.Invoke(notification);
|
PostNotification?.Invoke(notification);
|
||||||
|
|
||||||
Task.Factory.StartNew(() => request.Perform(api), TaskCreationOptions.LongRunning);
|
Task.Factory.StartNew(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
request.Perform(api);
|
||||||
|
}
|
||||||
|
catch (Exception error)
|
||||||
|
{
|
||||||
|
triggerFailure(error);
|
||||||
|
}
|
||||||
|
}, TaskCreationOptions.LongRunning);
|
||||||
|
|
||||||
DownloadBegan?.Invoke(request);
|
DownloadBegan?.Invoke(request);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
void triggerFailure(Exception error)
|
||||||
|
{
|
||||||
|
DownloadFailed?.Invoke(request);
|
||||||
|
|
||||||
|
if (error is OperationCanceledException) return;
|
||||||
|
|
||||||
|
notification.State = ProgressNotificationState.Cancelled;
|
||||||
|
Logger.Error(error, $"{HumanisedModelName.Titleize()} download failed!");
|
||||||
|
currentDownloads.Remove(request);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsAvailableLocally(TModel model) => CheckLocalAvailability(model, modelStore.ConsumableItems.Where(m => !m.DeletePending));
|
public bool IsAvailableLocally(TModel model) => CheckLocalAvailability(model, modelStore.ConsumableItems.Where(m => !m.DeletePending));
|
||||||
|
@ -17,6 +17,7 @@ using osu.Game.Beatmaps.Drawables;
|
|||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -27,10 +28,11 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
private const float tile_icon_padding = 7;
|
private const float tile_icon_padding = 7;
|
||||||
private const float tile_spacing = 2;
|
private const float tile_spacing = 2;
|
||||||
|
|
||||||
private readonly DifficultiesContainer difficulties;
|
|
||||||
private readonly OsuSpriteText version, starRating;
|
private readonly OsuSpriteText version, starRating;
|
||||||
private readonly Statistic plays, favourites;
|
private readonly Statistic plays, favourites;
|
||||||
|
|
||||||
|
public readonly DifficultiesContainer Difficulties;
|
||||||
|
|
||||||
public readonly Bindable<BeatmapInfo> Beatmap = new Bindable<BeatmapInfo>();
|
public readonly Bindable<BeatmapInfo> Beatmap = new Bindable<BeatmapInfo>();
|
||||||
|
|
||||||
private BeatmapSetInfo beatmapSet;
|
private BeatmapSetInfo beatmapSet;
|
||||||
@ -43,38 +45,10 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
if (value == beatmapSet) return;
|
if (value == beatmapSet) return;
|
||||||
|
|
||||||
beatmapSet = value;
|
beatmapSet = value;
|
||||||
|
|
||||||
updateDisplay();
|
updateDisplay();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateDisplay()
|
|
||||||
{
|
|
||||||
difficulties.Clear();
|
|
||||||
|
|
||||||
if (BeatmapSet != null)
|
|
||||||
{
|
|
||||||
difficulties.ChildrenEnumerable = BeatmapSet.Beatmaps.OrderBy(beatmap => beatmap.StarDifficulty).Select(b => new DifficultySelectorButton(b)
|
|
||||||
{
|
|
||||||
State = DifficultySelectorState.NotSelected,
|
|
||||||
OnHovered = beatmap =>
|
|
||||||
{
|
|
||||||
showBeatmap(beatmap);
|
|
||||||
starRating.Text = beatmap.StarDifficulty.ToString("Star Difficulty 0.##");
|
|
||||||
starRating.FadeIn(100);
|
|
||||||
},
|
|
||||||
OnClicked = beatmap => { Beatmap.Value = beatmap; },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
starRating.FadeOut(100);
|
|
||||||
Beatmap.Value = BeatmapSet?.Beatmaps.FirstOrDefault();
|
|
||||||
plays.Value = BeatmapSet?.OnlineInfo.PlayCount ?? 0;
|
|
||||||
favourites.Value = BeatmapSet?.OnlineInfo.FavouriteCount ?? 0;
|
|
||||||
|
|
||||||
updateDifficultyButtons();
|
|
||||||
}
|
|
||||||
|
|
||||||
public BeatmapPicker()
|
public BeatmapPicker()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
@ -89,7 +63,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
Direction = FillDirection.Vertical,
|
Direction = FillDirection.Vertical,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
difficulties = new DifficultiesContainer
|
Difficulties = new DifficultiesContainer
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
@ -147,6 +121,9 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IBindable<RulesetInfo> ruleset { get; set; }
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
@ -158,10 +135,39 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
|
ruleset.ValueChanged += r => updateDisplay();
|
||||||
|
|
||||||
// done here so everything can bind in intialization and get the first trigger
|
// done here so everything can bind in intialization and get the first trigger
|
||||||
Beatmap.TriggerChange();
|
Beatmap.TriggerChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateDisplay()
|
||||||
|
{
|
||||||
|
Difficulties.Clear();
|
||||||
|
|
||||||
|
if (BeatmapSet != null)
|
||||||
|
{
|
||||||
|
Difficulties.ChildrenEnumerable = BeatmapSet.Beatmaps.Where(b => b.Ruleset.Equals(ruleset.Value)).OrderBy(b => b.StarDifficulty).Select(b => new DifficultySelectorButton(b)
|
||||||
|
{
|
||||||
|
State = DifficultySelectorState.NotSelected,
|
||||||
|
OnHovered = beatmap =>
|
||||||
|
{
|
||||||
|
showBeatmap(beatmap);
|
||||||
|
starRating.Text = beatmap.StarDifficulty.ToString("Star Difficulty 0.##");
|
||||||
|
starRating.FadeIn(100);
|
||||||
|
},
|
||||||
|
OnClicked = beatmap => { Beatmap.Value = beatmap; },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
starRating.FadeOut(100);
|
||||||
|
Beatmap.Value = Difficulties.FirstOrDefault()?.Beatmap;
|
||||||
|
plays.Value = BeatmapSet?.OnlineInfo.PlayCount ?? 0;
|
||||||
|
favourites.Value = BeatmapSet?.OnlineInfo.FavouriteCount ?? 0;
|
||||||
|
|
||||||
|
updateDifficultyButtons();
|
||||||
|
}
|
||||||
|
|
||||||
private void showBeatmap(BeatmapInfo beatmap)
|
private void showBeatmap(BeatmapInfo beatmap)
|
||||||
{
|
{
|
||||||
version.Text = beatmap?.Version;
|
version.Text = beatmap?.Version;
|
||||||
@ -169,10 +175,10 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
|
|
||||||
private void updateDifficultyButtons()
|
private void updateDifficultyButtons()
|
||||||
{
|
{
|
||||||
difficulties.Children.ToList().ForEach(diff => diff.State = diff.Beatmap == Beatmap.Value ? DifficultySelectorState.Selected : DifficultySelectorState.NotSelected);
|
Difficulties.Children.ToList().ForEach(diff => diff.State = diff.Beatmap == Beatmap.Value ? DifficultySelectorState.Selected : DifficultySelectorState.NotSelected);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DifficultiesContainer : FillFlowContainer<DifficultySelectorButton>
|
public class DifficultiesContainer : FillFlowContainer<DifficultySelectorButton>
|
||||||
{
|
{
|
||||||
public Action OnLostHover;
|
public Action OnLostHover;
|
||||||
|
|
||||||
@ -183,7 +189,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DifficultySelectorButton : OsuClickableContainer, IStateful<DifficultySelectorState>
|
public class DifficultySelectorButton : OsuClickableContainer, IStateful<DifficultySelectorState>
|
||||||
{
|
{
|
||||||
private const float transition_duration = 100;
|
private const float transition_duration = 100;
|
||||||
private const float size = 52;
|
private const float size = 52;
|
||||||
@ -320,7 +326,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum DifficultySelectorState
|
public enum DifficultySelectorState
|
||||||
{
|
{
|
||||||
Selected,
|
Selected,
|
||||||
NotSelected,
|
NotSelected,
|
||||||
|
48
osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs
Normal file
48
osu.Game/Overlays/BeatmapSet/BeatmapRulesetSelector.cs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// 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.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osuTK;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.BeatmapSet
|
||||||
|
{
|
||||||
|
public class BeatmapRulesetSelector : RulesetSelector
|
||||||
|
{
|
||||||
|
private readonly Bindable<BeatmapSetInfo> beatmapSet = new Bindable<BeatmapSetInfo>();
|
||||||
|
|
||||||
|
public BeatmapSetInfo BeatmapSet
|
||||||
|
{
|
||||||
|
get => beatmapSet.Value;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
// propagate value to tab items first to enable only available rulesets.
|
||||||
|
beatmapSet.Value = value;
|
||||||
|
|
||||||
|
SelectTab(TabContainer.TabItems.FirstOrDefault(t => t.Enabled.Value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public BeatmapRulesetSelector()
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override TabItem<RulesetInfo> CreateTabItem(RulesetInfo value) => new BeatmapRulesetTabItem(value)
|
||||||
|
{
|
||||||
|
BeatmapSet = { BindTarget = beatmapSet }
|
||||||
|
};
|
||||||
|
|
||||||
|
protected override TabFillFlowContainer CreateTabFlow() => new TabFillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(10, 0),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
145
osu.Game/Overlays/BeatmapSet/BeatmapRulesetTabItem.cs
Normal file
145
osu.Game/Overlays/BeatmapSet/BeatmapRulesetTabItem.cs
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
// 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.Bindables;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Graphics.UserInterface;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace osu.Game.Overlays.BeatmapSet
|
||||||
|
{
|
||||||
|
public class BeatmapRulesetTabItem : TabItem<RulesetInfo>
|
||||||
|
{
|
||||||
|
private readonly OsuSpriteText name, count;
|
||||||
|
private readonly Box bar;
|
||||||
|
|
||||||
|
public readonly Bindable<BeatmapSetInfo> BeatmapSet = new Bindable<BeatmapSetInfo>();
|
||||||
|
|
||||||
|
public override bool PropagatePositionalInputSubTree => Enabled.Value && !Active.Value && base.PropagatePositionalInputSubTree;
|
||||||
|
|
||||||
|
public BeatmapRulesetTabItem(RulesetInfo value)
|
||||||
|
: base(value)
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
FillFlowContainer nameContainer;
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
nameContainer = new FillFlowContainer
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Margin = new MarginPadding { Bottom = 7.5f },
|
||||||
|
Spacing = new Vector2(2.5f),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
name = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Text = value.Name,
|
||||||
|
Font = OsuFont.Default.With(size: 18),
|
||||||
|
},
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
|
CornerRadius = 4f,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Color4.Black.Opacity(0.5f),
|
||||||
|
},
|
||||||
|
count = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Alpha = 0,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Margin = new MarginPadding { Horizontal = 5f },
|
||||||
|
Font = OsuFont.Default.With(weight: FontWeight.SemiBold),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
bar = new Box
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomCentre,
|
||||||
|
Origin = Anchor.BottomCentre,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
},
|
||||||
|
new HoverClickSounds(),
|
||||||
|
};
|
||||||
|
|
||||||
|
BeatmapSet.BindValueChanged(setInfo =>
|
||||||
|
{
|
||||||
|
var beatmapsCount = setInfo.NewValue?.Beatmaps.Count(b => b.Ruleset.Equals(Value)) ?? 0;
|
||||||
|
|
||||||
|
count.Text = beatmapsCount.ToString();
|
||||||
|
count.Alpha = beatmapsCount > 0 ? 1f : 0f;
|
||||||
|
|
||||||
|
Enabled.Value = beatmapsCount > 0;
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
Enabled.BindValueChanged(v => nameContainer.Alpha = v.NewValue ? 1f : 0.5f, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuColour colour { get; set; }
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
count.Colour = colour.Gray9;
|
||||||
|
bar.Colour = colour.Blue;
|
||||||
|
|
||||||
|
updateState();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateState()
|
||||||
|
{
|
||||||
|
var isHoveredOrActive = IsHovered || Active.Value;
|
||||||
|
|
||||||
|
bar.ResizeHeightTo(isHoveredOrActive ? 4 : 0, 200, Easing.OutQuint);
|
||||||
|
|
||||||
|
name.Colour = isHoveredOrActive ? colour.GrayE : colour.GrayC;
|
||||||
|
name.Font = name.Font.With(weight: Active.Value ? FontWeight.Bold : FontWeight.Regular);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Hovering and activation logic
|
||||||
|
|
||||||
|
protected override void OnActivated() => updateState();
|
||||||
|
|
||||||
|
protected override void OnDeactivated() => updateState();
|
||||||
|
|
||||||
|
protected override bool OnHover(HoverEvent e)
|
||||||
|
{
|
||||||
|
updateState();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnHoverLost(HoverLostEvent e) => updateState();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
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.Colour;
|
using osu.Framework.Graphics.Colour;
|
||||||
@ -16,6 +17,7 @@ using osu.Game.Graphics.UserInterface;
|
|||||||
using osu.Game.Online;
|
using osu.Game.Online;
|
||||||
using osu.Game.Overlays.BeatmapSet.Buttons;
|
using osu.Game.Overlays.BeatmapSet.Buttons;
|
||||||
using osu.Game.Overlays.Direct;
|
using osu.Game.Overlays.Direct;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
|
|
||||||
@ -39,6 +41,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
|
|
||||||
public bool DownloadButtonsVisible => downloadButtonsContainer.Any();
|
public bool DownloadButtonsVisible => downloadButtonsContainer.Any();
|
||||||
|
|
||||||
|
public readonly BeatmapRulesetSelector RulesetSelector;
|
||||||
public readonly BeatmapPicker Picker;
|
public readonly BeatmapPicker Picker;
|
||||||
|
|
||||||
private readonly FavouriteButton favouriteButton;
|
private readonly FavouriteButton favouriteButton;
|
||||||
@ -47,6 +50,9 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
|
|
||||||
private readonly LoadingAnimation loading;
|
private readonly LoadingAnimation loading;
|
||||||
|
|
||||||
|
[Cached(typeof(IBindable<RulesetInfo>))]
|
||||||
|
private readonly Bindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
|
||||||
|
|
||||||
public Header()
|
public Header()
|
||||||
{
|
{
|
||||||
ExternalLinkButton externalLink;
|
ExternalLinkButton externalLink;
|
||||||
@ -69,12 +75,18 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Height = tabs_height,
|
Height = tabs_height,
|
||||||
Children = new[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
tabsBg = new Box
|
tabsBg = new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
},
|
},
|
||||||
|
RulesetSelector = new BeatmapRulesetSelector
|
||||||
|
{
|
||||||
|
Current = ruleset,
|
||||||
|
Anchor = Anchor.BottomCentre,
|
||||||
|
Origin = Anchor.BottomCentre,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new Container
|
new Container
|
||||||
@ -223,7 +235,7 @@ namespace osu.Game.Overlays.BeatmapSet
|
|||||||
|
|
||||||
BeatmapSet.BindValueChanged(setInfo =>
|
BeatmapSet.BindValueChanged(setInfo =>
|
||||||
{
|
{
|
||||||
Picker.BeatmapSet = author.BeatmapSet = beatmapAvailability.BeatmapSet = Details.BeatmapSet = setInfo.NewValue;
|
Picker.BeatmapSet = RulesetSelector.BeatmapSet = author.BeatmapSet = beatmapAvailability.BeatmapSet = Details.BeatmapSet = setInfo.NewValue;
|
||||||
cover.BeatmapSet = setInfo.NewValue;
|
cover.BeatmapSet = setInfo.NewValue;
|
||||||
|
|
||||||
if (setInfo.NewValue == null)
|
if (setInfo.NewValue == null)
|
||||||
|
@ -44,7 +44,17 @@ namespace osu.Game.Overlays.Changelog
|
|||||||
req.Failure += _ => complete = true;
|
req.Failure += _ => complete = true;
|
||||||
|
|
||||||
// This is done on a separate thread to support cancellation below
|
// This is done on a separate thread to support cancellation below
|
||||||
Task.Run(() => req.Perform(api));
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
req.Perform(api);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
complete = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
while (!complete)
|
while (!complete)
|
||||||
{
|
{
|
||||||
|
@ -170,6 +170,7 @@ namespace osu.Game.Overlays
|
|||||||
var tcs = new TaskCompletionSource<bool>();
|
var tcs = new TaskCompletionSource<bool>();
|
||||||
|
|
||||||
var req = new GetChangelogRequest();
|
var req = new GetChangelogRequest();
|
||||||
|
|
||||||
req.Success += res => Schedule(() =>
|
req.Success += res => Schedule(() =>
|
||||||
{
|
{
|
||||||
// remap streams to builds to ensure model equality
|
// remap streams to builds to ensure model equality
|
||||||
@ -183,8 +184,22 @@ namespace osu.Game.Overlays
|
|||||||
|
|
||||||
tcs.SetResult(true);
|
tcs.SetResult(true);
|
||||||
});
|
});
|
||||||
req.Failure += _ => initialFetchTask = null;
|
|
||||||
req.Perform(API);
|
req.Failure += _ =>
|
||||||
|
{
|
||||||
|
initialFetchTask = null;
|
||||||
|
tcs.SetResult(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
req.Perform(API);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
initialFetchTask = null;
|
||||||
|
tcs.SetResult(false);
|
||||||
|
}
|
||||||
|
|
||||||
await tcs.Task;
|
await tcs.Task;
|
||||||
});
|
});
|
||||||
|
@ -239,6 +239,12 @@ namespace osu.Game.Rulesets.UI
|
|||||||
continueResume();
|
continueResume();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void CancelResume()
|
||||||
|
{
|
||||||
|
// called if the user pauses while the resume overlay is open
|
||||||
|
ResumeOverlay?.Hide();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates and adds the visual representation of a <see cref="TObject"/> to this <see cref="DrawableRuleset{TObject}"/>.
|
/// Creates and adds the visual representation of a <see cref="TObject"/> to this <see cref="DrawableRuleset{TObject}"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -453,6 +459,11 @@ namespace osu.Game.Rulesets.UI
|
|||||||
/// <param name="continueResume">The action to run when resuming is to be completed.</param>
|
/// <param name="continueResume">The action to run when resuming is to be completed.</param>
|
||||||
public abstract void RequestResume(Action continueResume);
|
public abstract void RequestResume(Action continueResume);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked when the user requests to pause while the resume overlay is active.
|
||||||
|
/// </summary>
|
||||||
|
public abstract void CancelResume();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a <see cref="ScoreProcessor"/> for the associated ruleset and link with this
|
/// Create a <see cref="ScoreProcessor"/> for the associated ruleset and link with this
|
||||||
/// <see cref="DrawableRuleset"/>.
|
/// <see cref="DrawableRuleset"/>.
|
||||||
|
@ -13,12 +13,6 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
{
|
{
|
||||||
public class ScrollingHitObjectContainer : HitObjectContainer
|
public class ScrollingHitObjectContainer : HitObjectContainer
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// A multiplier applied to the length of the scrolling area to determine a safe default lifetime end for hitobjects.
|
|
||||||
/// This is only used to limit the lifetime end within reason, as proper lifetime management should be implemented on hitobjects themselves.
|
|
||||||
/// </summary>
|
|
||||||
private const float safe_lifetime_end_multiplier = 2;
|
|
||||||
|
|
||||||
private readonly IBindable<double> timeRange = new BindableDouble();
|
private readonly IBindable<double> timeRange = new BindableDouble();
|
||||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||||
|
|
||||||
@ -123,28 +117,22 @@ namespace osu.Game.Rulesets.UI.Scrolling
|
|||||||
if (cached.IsValid)
|
if (cached.IsValid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
double endTime = hitObject.HitObject.StartTime;
|
|
||||||
|
|
||||||
if (hitObject.HitObject is IHasEndTime e)
|
if (hitObject.HitObject is IHasEndTime e)
|
||||||
{
|
{
|
||||||
endTime = e.EndTime;
|
|
||||||
|
|
||||||
switch (direction.Value)
|
switch (direction.Value)
|
||||||
{
|
{
|
||||||
case ScrollingDirection.Up:
|
case ScrollingDirection.Up:
|
||||||
case ScrollingDirection.Down:
|
case ScrollingDirection.Down:
|
||||||
hitObject.Height = scrollingInfo.Algorithm.GetLength(hitObject.HitObject.StartTime, endTime, timeRange.Value, scrollLength);
|
hitObject.Height = scrollingInfo.Algorithm.GetLength(hitObject.HitObject.StartTime, e.EndTime, timeRange.Value, scrollLength);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ScrollingDirection.Left:
|
case ScrollingDirection.Left:
|
||||||
case ScrollingDirection.Right:
|
case ScrollingDirection.Right:
|
||||||
hitObject.Width = scrollingInfo.Algorithm.GetLength(hitObject.HitObject.StartTime, endTime, timeRange.Value, scrollLength);
|
hitObject.Width = scrollingInfo.Algorithm.GetLength(hitObject.HitObject.StartTime, e.EndTime, timeRange.Value, scrollLength);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hitObject.LifetimeEnd = scrollingInfo.Algorithm.TimeAt(scrollLength * safe_lifetime_end_multiplier, endTime, timeRange.Value, scrollLength);
|
|
||||||
|
|
||||||
foreach (var obj in hitObject.NestedHitObjects)
|
foreach (var obj in hitObject.NestedHitObjects)
|
||||||
{
|
{
|
||||||
computeInitialStateRecursive(obj);
|
computeInitialStateRecursive(obj);
|
||||||
|
@ -167,14 +167,17 @@ namespace osu.Game.Screens.Multi
|
|||||||
public void APIStateChanged(IAPIProvider api, APIState state)
|
public void APIStateChanged(IAPIProvider api, APIState state)
|
||||||
{
|
{
|
||||||
if (state != APIState.Online)
|
if (state != APIState.Online)
|
||||||
forcefullyExit();
|
Schedule(forcefullyExit);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void forcefullyExit()
|
private void forcefullyExit()
|
||||||
{
|
{
|
||||||
// This is temporary since we don't currently have a way to force screens to be exited
|
// This is temporary since we don't currently have a way to force screens to be exited
|
||||||
if (this.IsCurrentScreen())
|
if (this.IsCurrentScreen())
|
||||||
this.Exit();
|
{
|
||||||
|
while (this.IsCurrentScreen())
|
||||||
|
this.Exit();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this.MakeCurrent();
|
this.MakeCurrent();
|
||||||
@ -212,6 +215,8 @@ namespace osu.Game.Screens.Multi
|
|||||||
|
|
||||||
public override bool OnExiting(IScreen next)
|
public override bool OnExiting(IScreen next)
|
||||||
{
|
{
|
||||||
|
roomManager.PartRoom();
|
||||||
|
|
||||||
if (screenStack.CurrentScreen != null && !(screenStack.CurrentScreen is LoungeSubScreen))
|
if (screenStack.CurrentScreen != null && !(screenStack.CurrentScreen is LoungeSubScreen))
|
||||||
{
|
{
|
||||||
screenStack.Exit();
|
screenStack.Exit();
|
||||||
|
@ -87,9 +87,8 @@ namespace osu.Game.Screens.Multi
|
|||||||
public void JoinRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null)
|
public void JoinRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null)
|
||||||
{
|
{
|
||||||
currentJoinRoomRequest?.Cancel();
|
currentJoinRoomRequest?.Cancel();
|
||||||
currentJoinRoomRequest = null;
|
|
||||||
|
|
||||||
currentJoinRoomRequest = new JoinRoomRequest(room, api.LocalUser.Value);
|
currentJoinRoomRequest = new JoinRoomRequest(room, api.LocalUser.Value);
|
||||||
|
|
||||||
currentJoinRoomRequest.Success += () =>
|
currentJoinRoomRequest.Success += () =>
|
||||||
{
|
{
|
||||||
joinedRoom = room;
|
joinedRoom = room;
|
||||||
@ -98,7 +97,8 @@ namespace osu.Game.Screens.Multi
|
|||||||
|
|
||||||
currentJoinRoomRequest.Failure += exception =>
|
currentJoinRoomRequest.Failure += exception =>
|
||||||
{
|
{
|
||||||
Logger.Log($"Failed to join room: {exception}", level: LogLevel.Important);
|
if (!(exception is OperationCanceledException))
|
||||||
|
Logger.Log($"Failed to join room: {exception}", level: LogLevel.Important);
|
||||||
onError?.Invoke(exception.ToString());
|
onError?.Invoke(exception.ToString());
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -107,6 +107,8 @@ namespace osu.Game.Screens.Multi
|
|||||||
|
|
||||||
public void PartRoom()
|
public void PartRoom()
|
||||||
{
|
{
|
||||||
|
currentJoinRoomRequest?.Cancel();
|
||||||
|
|
||||||
if (joinedRoom == null)
|
if (joinedRoom == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ using System.Threading.Tasks;
|
|||||||
using osu.Framework;
|
using osu.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
@ -29,7 +30,7 @@ namespace osu.Game.Screens.Play
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The original source (usually a <see cref="WorkingBeatmap"/>'s track).
|
/// The original source (usually a <see cref="WorkingBeatmap"/>'s track).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly IAdjustableClock sourceClock;
|
private IAdjustableClock sourceClock;
|
||||||
|
|
||||||
public readonly BindableBool IsPaused = new BindableBool();
|
public readonly BindableBool IsPaused = new BindableBool();
|
||||||
|
|
||||||
@ -153,6 +154,18 @@ namespace osu.Game.Screens.Play
|
|||||||
IsPaused.Value = true;
|
IsPaused.Value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes the backing clock to avoid using the originally provided beatmap's track.
|
||||||
|
/// </summary>
|
||||||
|
public void StopUsingBeatmapClock()
|
||||||
|
{
|
||||||
|
if (sourceClock != beatmap.Track)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sourceClock = new TrackVirtual(beatmap.Track.Length);
|
||||||
|
adjustableClock.ChangeSource(sourceClock);
|
||||||
|
}
|
||||||
|
|
||||||
public void ResetLocalAdjustments()
|
public void ResetLocalAdjustments()
|
||||||
{
|
{
|
||||||
// In the case of replays, we may have changed the playback rate.
|
// In the case of replays, we may have changed the playback rate.
|
||||||
|
@ -30,6 +30,7 @@ using osu.Game.Users;
|
|||||||
|
|
||||||
namespace osu.Game.Screens.Play
|
namespace osu.Game.Screens.Play
|
||||||
{
|
{
|
||||||
|
[Cached]
|
||||||
public class Player : ScreenWithBeatmapBackground
|
public class Player : ScreenWithBeatmapBackground
|
||||||
{
|
{
|
||||||
public override bool AllowBackButton => false; // handled by HoldForMenuButton
|
public override bool AllowBackButton => false; // handled by HoldForMenuButton
|
||||||
@ -311,14 +312,19 @@ namespace osu.Game.Screens.Play
|
|||||||
this.Exit();
|
this.Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Restart gameplay via a parent <see cref="PlayerLoader"/>.
|
||||||
|
/// <remarks>This can be called from a child screen in order to trigger the restart process.</remarks>
|
||||||
|
/// </summary>
|
||||||
public void Restart()
|
public void Restart()
|
||||||
{
|
{
|
||||||
if (!this.IsCurrentScreen()) return;
|
|
||||||
|
|
||||||
sampleRestart?.Play();
|
sampleRestart?.Play();
|
||||||
|
|
||||||
RestartRequested?.Invoke();
|
RestartRequested?.Invoke();
|
||||||
performImmediateExit();
|
|
||||||
|
if (this.IsCurrentScreen())
|
||||||
|
performImmediateExit();
|
||||||
|
else
|
||||||
|
this.MakeCurrent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ScheduledDelegate completionProgressDelegate;
|
private ScheduledDelegate completionProgressDelegate;
|
||||||
@ -443,7 +449,12 @@ namespace osu.Game.Screens.Play
|
|||||||
{
|
{
|
||||||
if (!canPause) return;
|
if (!canPause) return;
|
||||||
|
|
||||||
IsResuming = false;
|
if (IsResuming)
|
||||||
|
{
|
||||||
|
DrawableRuleset.CancelResume();
|
||||||
|
IsResuming = false;
|
||||||
|
}
|
||||||
|
|
||||||
GameplayClockContainer.Stop();
|
GameplayClockContainer.Stop();
|
||||||
PauseOverlay.Show();
|
PauseOverlay.Show();
|
||||||
lastPauseActionTime = GameplayClockContainer.GameplayClock.CurrentTime;
|
lastPauseActionTime = GameplayClockContainer.GameplayClock.CurrentTime;
|
||||||
@ -527,6 +538,10 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
GameplayClockContainer.ResetLocalAdjustments();
|
GameplayClockContainer.ResetLocalAdjustments();
|
||||||
|
|
||||||
|
// GameplayClockContainer performs seeks / start / stop operations on the beatmap's track.
|
||||||
|
// as we are no longer the current screen, we cannot guarantee the track is still usable.
|
||||||
|
GameplayClockContainer.StopUsingBeatmapClock();
|
||||||
|
|
||||||
fadeOut();
|
fadeOut();
|
||||||
return base.OnExiting(next);
|
return base.OnExiting(next);
|
||||||
}
|
}
|
||||||
|
@ -4,12 +4,13 @@
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Online;
|
|
||||||
using osu.Game.Scoring;
|
|
||||||
using osu.Game.Online.API.Requests.Responses;
|
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Online;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Play
|
namespace osu.Game.Screens.Ranking.Pages
|
||||||
{
|
{
|
||||||
public class ReplayDownloadButton : DownloadTrackingComposite<ScoreInfo, ScoreManager>
|
public class ReplayDownloadButton : DownloadTrackingComposite<ScoreInfo, ScoreManager>
|
||||||
{
|
{
|
||||||
@ -33,6 +34,7 @@ namespace osu.Game.Screens.Play
|
|||||||
public ReplayDownloadButton(ScoreInfo score)
|
public ReplayDownloadButton(ScoreInfo score)
|
||||||
: base(score)
|
: base(score)
|
||||||
{
|
{
|
||||||
|
Size = new Vector2(50, 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
54
osu.Game/Screens/Ranking/Pages/RetryButton.cs
Normal file
54
osu.Game/Screens/Ranking/Pages/RetryButton.cs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// 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.Shapes;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Ranking.Pages
|
||||||
|
{
|
||||||
|
public class RetryButton : OsuAnimatedButton
|
||||||
|
{
|
||||||
|
private readonly Box background;
|
||||||
|
|
||||||
|
[Resolved(canBeNull: true)]
|
||||||
|
private Player player { get; set; }
|
||||||
|
|
||||||
|
public RetryButton()
|
||||||
|
{
|
||||||
|
Size = new Vector2(50, 30);
|
||||||
|
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
background = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Depth = float.MaxValue
|
||||||
|
},
|
||||||
|
new SpriteIcon
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Size = new Vector2(13),
|
||||||
|
Icon = FontAwesome.Solid.ArrowCircleLeft,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
TooltipText = "Retry";
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours)
|
||||||
|
{
|
||||||
|
background.Colour = colours.Green;
|
||||||
|
|
||||||
|
if (player != null)
|
||||||
|
Action = () => player.Restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -169,12 +169,19 @@ namespace osu.Game.Screens.Ranking.Pages
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
new ReplayDownloadButton(score)
|
new FillFlowContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.BottomCentre,
|
Anchor = Anchor.BottomCentre,
|
||||||
Origin = Anchor.BottomCentre,
|
Origin = Anchor.BottomCentre,
|
||||||
Margin = new MarginPadding { Bottom = 10 },
|
Margin = new MarginPadding { Bottom = 10 },
|
||||||
Size = new Vector2(50, 30),
|
Spacing = new Vector2(5),
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new ReplayDownloadButton(score),
|
||||||
|
new RetryButton()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ using osu.Game.Graphics;
|
|||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
|
||||||
namespace osu.Game.Screens.Ranking
|
namespace osu.Game.Screens.Ranking
|
||||||
{
|
{
|
||||||
@ -34,6 +35,9 @@ namespace osu.Game.Screens.Ranking
|
|||||||
|
|
||||||
private ResultModeTabControl modeChangeButtons;
|
private ResultModeTabControl modeChangeButtons;
|
||||||
|
|
||||||
|
[Resolved(canBeNull: true)]
|
||||||
|
private Player player { get; set; }
|
||||||
|
|
||||||
public override bool DisallowExternalBeatmapRulesetChanges => true;
|
public override bool DisallowExternalBeatmapRulesetChanges => true;
|
||||||
|
|
||||||
protected readonly ScoreInfo Score;
|
protected readonly ScoreInfo Score;
|
||||||
@ -100,10 +104,7 @@ namespace osu.Game.Screens.Ranking
|
|||||||
|
|
||||||
public override bool OnExiting(IScreen next)
|
public override bool OnExiting(IScreen next)
|
||||||
{
|
{
|
||||||
allCircles.ForEach(c =>
|
allCircles.ForEach(c => c.ScaleTo(0, transition_time, Easing.OutSine));
|
||||||
{
|
|
||||||
c.ScaleTo(0, transition_time, Easing.OutSine);
|
|
||||||
});
|
|
||||||
|
|
||||||
Background.ScaleTo(1f, transition_time / 4, Easing.OutQuint);
|
Background.ScaleTo(1f, transition_time / 4, Easing.OutQuint);
|
||||||
|
|
||||||
@ -253,7 +254,16 @@ namespace osu.Game.Screens.Ranking
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
new HotkeyRetryOverlay
|
||||||
|
{
|
||||||
|
Action = () =>
|
||||||
|
{
|
||||||
|
if (!this.IsCurrentScreen()) return;
|
||||||
|
|
||||||
|
player?.Restart();
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
var pages = CreateResultPages();
|
var pages = CreateResultPages();
|
||||||
|
Loading…
Reference in New Issue
Block a user