mirror of
https://github.com/osukey/osukey.git
synced 2025-08-05 15:44:04 +09:00
Merge remote-tracking branch 'upstream/master' into difficulty-icon-tooltip
This commit is contained in:
@ -11,8 +11,11 @@ namespace osu.Game.Screens
|
||||
{
|
||||
public abstract class BackgroundScreen : Screen, IEquatable<BackgroundScreen>
|
||||
{
|
||||
protected BackgroundScreen()
|
||||
private readonly bool animateOnEnter;
|
||||
|
||||
protected BackgroundScreen(bool animateOnEnter = true)
|
||||
{
|
||||
this.animateOnEnter = animateOnEnter;
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
}
|
||||
@ -39,11 +42,14 @@ namespace osu.Game.Screens
|
||||
|
||||
public override void OnEntering(IScreen last)
|
||||
{
|
||||
this.FadeOut();
|
||||
this.MoveToX(x_movement_amount);
|
||||
if (animateOnEnter)
|
||||
{
|
||||
this.FadeOut();
|
||||
this.MoveToX(x_movement_amount);
|
||||
|
||||
this.FadeIn(transition_length, Easing.InOutQuart);
|
||||
this.MoveToX(0, transition_length, Easing.InOutQuart);
|
||||
this.FadeIn(transition_length, Easing.InOutQuart);
|
||||
this.MoveToX(0, transition_length, Easing.InOutQuart);
|
||||
}
|
||||
|
||||
base.OnEntering(last);
|
||||
}
|
||||
|
@ -1,19 +1,28 @@
|
||||
// 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.
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.Backgrounds;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Screens.Play;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.Backgrounds
|
||||
{
|
||||
public class BackgroundScreenBeatmap : BackgroundScreen
|
||||
{
|
||||
/// <summary>
|
||||
/// The amount of blur to apply when full user blur is requested.
|
||||
/// </summary>
|
||||
public const float USER_BLUR_FACTOR = 25;
|
||||
|
||||
protected Background Background;
|
||||
|
||||
private WorkingBeatmap beatmap;
|
||||
@ -30,16 +39,17 @@ namespace osu.Game.Screens.Backgrounds
|
||||
/// </summary>
|
||||
public readonly Bindable<float> BlurAmount = new Bindable<float>();
|
||||
|
||||
private readonly UserDimContainer fadeContainer;
|
||||
private readonly DimmableBackground dimmable;
|
||||
|
||||
protected virtual UserDimContainer CreateFadeContainer() => new UserDimContainer { RelativeSizeAxes = Axes.Both };
|
||||
protected virtual DimmableBackground CreateFadeContainer() => new DimmableBackground { RelativeSizeAxes = Axes.Both };
|
||||
|
||||
public BackgroundScreenBeatmap(WorkingBeatmap beatmap = null)
|
||||
{
|
||||
Beatmap = beatmap;
|
||||
InternalChild = fadeContainer = CreateFadeContainer();
|
||||
fadeContainer.EnableUserDim.BindTo(EnableUserDim);
|
||||
fadeContainer.BlurAmount.BindTo(BlurAmount);
|
||||
|
||||
InternalChild = dimmable = CreateFadeContainer();
|
||||
dimmable.EnableUserDim.BindTo(EnableUserDim);
|
||||
dimmable.BlurAmount.BindTo(BlurAmount);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -86,8 +96,8 @@ namespace osu.Game.Screens.Backgrounds
|
||||
}
|
||||
|
||||
b.Depth = newDepth;
|
||||
fadeContainer.Background = Background = b;
|
||||
StoryboardReplacesBackground.BindTo(fadeContainer.StoryboardReplacesBackground);
|
||||
dimmable.Background = Background = b;
|
||||
StoryboardReplacesBackground.BindTo(dimmable.StoryboardReplacesBackground);
|
||||
}
|
||||
|
||||
public override bool Equals(BackgroundScreen other)
|
||||
@ -112,5 +122,70 @@ namespace osu.Game.Screens.Backgrounds
|
||||
Sprite.Texture = Beatmap?.Background ?? textures.Get(@"Backgrounds/bg1");
|
||||
}
|
||||
}
|
||||
|
||||
public class DimmableBackground : UserDimContainer
|
||||
{
|
||||
/// <summary>
|
||||
/// The amount of blur to be applied to the background in addition to user-specified blur.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Used in contexts where there can potentially be both user and screen-specified blurring occuring at the same time, such as in <see cref="PlayerLoader"/>
|
||||
/// </remarks>
|
||||
public readonly Bindable<float> BlurAmount = new Bindable<float>();
|
||||
|
||||
public Background Background
|
||||
{
|
||||
get => background;
|
||||
set
|
||||
{
|
||||
background?.Expire();
|
||||
|
||||
base.Add(background = value);
|
||||
background.BlurTo(blurTarget, 0, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
|
||||
private Bindable<double> userBlurLevel { get; set; }
|
||||
|
||||
private Background background;
|
||||
|
||||
public override void Add(Drawable drawable)
|
||||
{
|
||||
if (drawable is Background)
|
||||
throw new InvalidOperationException($"Use {nameof(Background)} to set a background.");
|
||||
|
||||
base.Add(drawable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// As an optimisation, we add the two blur portions to be applied rather than actually applying two separate blurs.
|
||||
/// </summary>
|
||||
private Vector2 blurTarget => EnableUserDim.Value
|
||||
? new Vector2(BlurAmount.Value + (float)userBlurLevel.Value * USER_BLUR_FACTOR)
|
||||
: new Vector2(BlurAmount.Value);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
userBlurLevel = config.GetBindable<double>(OsuSetting.BlurLevel);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
userBlurLevel.ValueChanged += _ => UpdateVisuals();
|
||||
BlurAmount.ValueChanged += _ => UpdateVisuals();
|
||||
}
|
||||
|
||||
protected override bool ShowDimContent => !ShowStoryboard.Value || !StoryboardReplacesBackground.Value; // The background needs to be hidden in the case of it being replaced by the storyboard
|
||||
|
||||
protected override void UpdateVisuals()
|
||||
{
|
||||
base.UpdateVisuals();
|
||||
|
||||
Background?.BlurTo(blurTarget, BACKGROUND_FADE_DURATION, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,13 +18,18 @@ namespace osu.Game.Screens.Backgrounds
|
||||
private Background background;
|
||||
|
||||
private int currentDisplay;
|
||||
private const int background_count = 5;
|
||||
private const int background_count = 7;
|
||||
|
||||
private string backgroundName => $@"Menu/menu-background-{currentDisplay % background_count + 1}";
|
||||
|
||||
private Bindable<User> user;
|
||||
private Bindable<Skin> skin;
|
||||
|
||||
public BackgroundScreenDefault(bool animateOnEnter = true)
|
||||
: base(animateOnEnter)
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IAPIProvider api, SkinManager skinManager)
|
||||
{
|
||||
|
@ -1,9 +0,0 @@
|
||||
// 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.
|
||||
|
||||
namespace osu.Game.Screens.Direct
|
||||
{
|
||||
public class OnlineListing : ScreenWhiteBox
|
||||
{
|
||||
}
|
||||
}
|
@ -24,14 +24,18 @@ using osu.Game.Screens.Edit.Design;
|
||||
using osuTK.Input;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Screens.Edit
|
||||
{
|
||||
public class Editor : OsuScreen
|
||||
public class Editor : OsuScreen, IKeyBindingHandler<GlobalAction>
|
||||
{
|
||||
protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4");
|
||||
|
||||
public override bool AllowBackButton => false;
|
||||
|
||||
public override bool HideOverlaysOnEnter => true;
|
||||
|
||||
public override bool DisallowExternalBeatmapRulesetChanges => true;
|
||||
@ -204,32 +208,48 @@ namespace osu.Game.Screens.Edit
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool OnPressed(GlobalAction action)
|
||||
{
|
||||
if (action == GlobalAction.Back)
|
||||
{
|
||||
// as we don't want to display the back button, manual handling of exit action is required.
|
||||
this.Exit();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnReleased(GlobalAction action) => action == GlobalAction.Back;
|
||||
|
||||
public override void OnResuming(IScreen last)
|
||||
{
|
||||
Beatmap.Value.Track?.Stop();
|
||||
base.OnResuming(last);
|
||||
Beatmap.Value.Track?.Stop();
|
||||
}
|
||||
|
||||
public override void OnEntering(IScreen last)
|
||||
{
|
||||
base.OnEntering(last);
|
||||
|
||||
Background.FadeColour(Color4.DarkGray, 500);
|
||||
Beatmap.Value.Track?.Stop();
|
||||
resetTrack();
|
||||
}
|
||||
|
||||
public override bool OnExiting(IScreen next)
|
||||
{
|
||||
Background.FadeColour(Color4.White, 500);
|
||||
|
||||
if (Beatmap.Value.Track != null)
|
||||
{
|
||||
Beatmap.Value.Track.Tempo.Value = 1;
|
||||
Beatmap.Value.Track.Start();
|
||||
}
|
||||
resetTrack();
|
||||
|
||||
return base.OnExiting(next);
|
||||
}
|
||||
|
||||
private void resetTrack()
|
||||
{
|
||||
Beatmap.Value.Track?.ResetSpeedAdjustments();
|
||||
Beatmap.Value.Track?.Stop();
|
||||
}
|
||||
|
||||
private void exportBeatmap() => host.OpenFileExternally(Beatmap.Value.Save());
|
||||
|
||||
private void onModeChanged(ValueChangedEvent<EditorScreenMode> e)
|
||||
|
@ -17,6 +17,11 @@ namespace osu.Game.Screens
|
||||
/// </summary>
|
||||
bool DisallowExternalBeatmapRulesetChanges { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether the user can exit this this <see cref="IOsuScreen"/> by pressing the back button.
|
||||
/// </summary>
|
||||
bool AllowBackButton { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether a top-level component should be allowed to exit the current screen to, for example,
|
||||
/// complete an import. Note that this can be overridden by a user if they specifically request.
|
||||
|
@ -9,22 +9,15 @@ using osu.Framework.Graphics.Shaders;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osuTK;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Configuration;
|
||||
using IntroSequence = osu.Game.Configuration.IntroSequence;
|
||||
|
||||
namespace osu.Game.Screens
|
||||
{
|
||||
public class Loader : OsuScreen
|
||||
public class Loader : StartupScreen
|
||||
{
|
||||
private bool showDisclaimer;
|
||||
|
||||
public override bool HideOverlaysOnEnter => true;
|
||||
|
||||
public override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled;
|
||||
|
||||
public override bool CursorVisible => false;
|
||||
|
||||
protected override bool AllowBackButton => false;
|
||||
|
||||
public Loader()
|
||||
{
|
||||
ValidForResume = false;
|
||||
@ -54,7 +47,27 @@ namespace osu.Game.Screens
|
||||
private OsuScreen loadableScreen;
|
||||
private ShaderPrecompiler precompiler;
|
||||
|
||||
protected virtual OsuScreen CreateLoadableScreen() => showDisclaimer ? (OsuScreen)new Disclaimer() : new Intro();
|
||||
private IntroSequence introSequence;
|
||||
|
||||
protected virtual OsuScreen CreateLoadableScreen()
|
||||
{
|
||||
if (showDisclaimer)
|
||||
return new Disclaimer(getIntroSequence());
|
||||
|
||||
return getIntroSequence();
|
||||
}
|
||||
|
||||
private IntroScreen getIntroSequence()
|
||||
{
|
||||
switch (introSequence)
|
||||
{
|
||||
case IntroSequence.Circles:
|
||||
return new IntroCircles();
|
||||
|
||||
default:
|
||||
return new IntroTriangles();
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual ShaderPrecompiler CreateShaderPrecompiler() => new ShaderPrecompiler();
|
||||
|
||||
@ -80,9 +93,10 @@ namespace osu.Game.Screens
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuGameBase game)
|
||||
private void load(OsuGameBase game, OsuConfigManager config)
|
||||
{
|
||||
showDisclaimer = game.IsDeployedBuild;
|
||||
introSequence = config.Get<IntroSequence>(OsuSetting.IntroSequence);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -332,7 +332,7 @@ namespace osu.Game.Screens.Menu
|
||||
break;
|
||||
|
||||
case ButtonSystemState.EnteringMode:
|
||||
logoTrackingContainer.StartTracking(logo, 0, Easing.In);
|
||||
logoTrackingContainer.StartTracking(logo, lastState == ButtonSystemState.Initial ? MainMenu.FADE_OUT_DURATION : 0, Easing.InSine);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -15,33 +15,29 @@ using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Online.API;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Screens.Menu
|
||||
{
|
||||
public class Disclaimer : OsuScreen
|
||||
public class Disclaimer : StartupScreen
|
||||
{
|
||||
private Intro intro;
|
||||
private SpriteIcon icon;
|
||||
private Color4 iconColour;
|
||||
private LinkFlowContainer textFlow;
|
||||
private LinkFlowContainer supportFlow;
|
||||
|
||||
public override bool HideOverlaysOnEnter => true;
|
||||
public override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled;
|
||||
|
||||
public override bool CursorVisible => false;
|
||||
|
||||
private Drawable heart;
|
||||
|
||||
private const float icon_y = -85;
|
||||
private const float icon_size = 30;
|
||||
|
||||
private readonly OsuScreen nextScreen;
|
||||
|
||||
private readonly Bindable<User> currentUser = new Bindable<User>();
|
||||
|
||||
public Disclaimer()
|
||||
public Disclaimer(OsuScreen nextScreen = null)
|
||||
{
|
||||
this.nextScreen = nextScreen;
|
||||
ValidForResume = false;
|
||||
}
|
||||
|
||||
@ -152,7 +148,8 @@ namespace osu.Game.Screens.Menu
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
LoadComponentAsync(intro = new Intro());
|
||||
if (nextScreen != null)
|
||||
LoadComponentAsync(nextScreen);
|
||||
}
|
||||
|
||||
public override void OnEntering(IScreen last)
|
||||
@ -176,7 +173,7 @@ namespace osu.Game.Screens.Menu
|
||||
.Then(5500)
|
||||
.FadeOut(250)
|
||||
.ScaleTo(0.9f, 250, Easing.InQuint)
|
||||
.Finally(d => this.Push(intro));
|
||||
.Finally(d => this.Push(nextScreen));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,174 +0,0 @@
|
||||
// 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.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.IO.Archives;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Screens.Menu
|
||||
{
|
||||
public class Intro : OsuScreen
|
||||
{
|
||||
private const string menu_music_beatmap_hash = "3c8b1fcc9434dbb29e2fb613d3b9eada9d7bb6c125ceb32396c3b53437280c83";
|
||||
|
||||
/// <summary>
|
||||
/// Whether we have loaded the menu previously.
|
||||
/// </summary>
|
||||
public bool DidLoadMenu;
|
||||
|
||||
private MainMenu mainMenu;
|
||||
private SampleChannel welcome;
|
||||
private SampleChannel seeya;
|
||||
|
||||
public override bool HideOverlaysOnEnter => true;
|
||||
public override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled;
|
||||
|
||||
public override bool CursorVisible => false;
|
||||
|
||||
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBlack();
|
||||
|
||||
private Bindable<bool> menuVoice;
|
||||
private Bindable<bool> menuMusic;
|
||||
private Track track;
|
||||
private WorkingBeatmap introBeatmap;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio, OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game)
|
||||
{
|
||||
menuVoice = config.GetBindable<bool>(OsuSetting.MenuVoice);
|
||||
menuMusic = config.GetBindable<bool>(OsuSetting.MenuMusic);
|
||||
|
||||
BeatmapSetInfo setInfo = null;
|
||||
|
||||
if (!menuMusic.Value)
|
||||
{
|
||||
var sets = beatmaps.GetAllUsableBeatmapSets();
|
||||
if (sets.Count > 0)
|
||||
setInfo = beatmaps.QueryBeatmapSet(s => s.ID == sets[RNG.Next(0, sets.Count - 1)].ID);
|
||||
}
|
||||
|
||||
if (setInfo == null)
|
||||
{
|
||||
setInfo = beatmaps.QueryBeatmapSet(b => b.Hash == menu_music_beatmap_hash);
|
||||
|
||||
if (setInfo == null)
|
||||
{
|
||||
// we need to import the default menu background beatmap
|
||||
setInfo = beatmaps.Import(new ZipArchiveReader(game.Resources.GetStream(@"Tracks/circles.osz"), "circles.osz")).Result;
|
||||
|
||||
setInfo.Protected = true;
|
||||
beatmaps.Update(setInfo);
|
||||
}
|
||||
}
|
||||
|
||||
introBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]);
|
||||
track = introBeatmap.Track;
|
||||
|
||||
welcome = audio.Samples.Get(@"welcome");
|
||||
seeya = audio.Samples.Get(@"seeya");
|
||||
}
|
||||
|
||||
private const double delay_step_one = 2300;
|
||||
private const double delay_step_two = 600;
|
||||
|
||||
public const int EXIT_DELAY = 3000;
|
||||
|
||||
protected override void LogoArriving(OsuLogo logo, bool resuming)
|
||||
{
|
||||
base.LogoArriving(logo, resuming);
|
||||
|
||||
if (!resuming)
|
||||
{
|
||||
Beatmap.Value = introBeatmap;
|
||||
|
||||
if (menuVoice.Value)
|
||||
welcome.Play();
|
||||
|
||||
Scheduler.AddDelayed(delegate
|
||||
{
|
||||
// Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Manu.
|
||||
if (menuMusic.Value)
|
||||
track.Start();
|
||||
|
||||
LoadComponentAsync(mainMenu = new MainMenu());
|
||||
|
||||
Scheduler.AddDelayed(delegate
|
||||
{
|
||||
DidLoadMenu = true;
|
||||
this.Push(mainMenu);
|
||||
}, delay_step_one);
|
||||
}, delay_step_two);
|
||||
}
|
||||
|
||||
logo.Colour = Color4.White;
|
||||
logo.Ripple = false;
|
||||
|
||||
const int quick_appear = 350;
|
||||
|
||||
int initialMovementTime = logo.Alpha > 0.2f ? quick_appear : 0;
|
||||
|
||||
logo.MoveTo(new Vector2(0.5f), initialMovementTime, Easing.OutQuint);
|
||||
|
||||
if (!resuming)
|
||||
{
|
||||
logo.ScaleTo(1);
|
||||
logo.FadeIn();
|
||||
logo.PlayIntro();
|
||||
}
|
||||
else
|
||||
{
|
||||
logo.Triangles = false;
|
||||
|
||||
logo
|
||||
.ScaleTo(1, initialMovementTime, Easing.OutQuint)
|
||||
.FadeIn(quick_appear, Easing.OutQuint)
|
||||
.Then()
|
||||
.RotateTo(20, EXIT_DELAY * 1.5f)
|
||||
.FadeOut(EXIT_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnSuspending(IScreen next)
|
||||
{
|
||||
this.FadeOut(300);
|
||||
base.OnSuspending(next);
|
||||
}
|
||||
|
||||
public override bool OnExiting(IScreen next)
|
||||
{
|
||||
//cancel exiting if we haven't loaded the menu yet.
|
||||
return !DidLoadMenu;
|
||||
}
|
||||
|
||||
public override void OnResuming(IScreen last)
|
||||
{
|
||||
this.FadeIn(300);
|
||||
|
||||
double fadeOutTime = EXIT_DELAY;
|
||||
//we also handle the exit transition.
|
||||
if (menuVoice.Value)
|
||||
seeya.Play();
|
||||
else
|
||||
fadeOutTime = 500;
|
||||
|
||||
Scheduler.AddDelayed(this.Exit, fadeOutTime);
|
||||
|
||||
//don't want to fade out completely else we will stop running updates.
|
||||
Game.FadeTo(0.01f, fadeOutTime);
|
||||
|
||||
base.OnResuming(last);
|
||||
}
|
||||
}
|
||||
}
|
106
osu.Game/Screens/Menu/IntroCircles.cs
Normal file
106
osu.Game/Screens/Menu/IntroCircles.cs
Normal file
@ -0,0 +1,106 @@
|
||||
// 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.Audio.Sample;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.IO.Archives;
|
||||
|
||||
namespace osu.Game.Screens.Menu
|
||||
{
|
||||
public class IntroCircles : IntroScreen
|
||||
{
|
||||
private const string menu_music_beatmap_hash = "3c8b1fcc9434dbb29e2fb613d3b9eada9d7bb6c125ceb32396c3b53437280c83";
|
||||
|
||||
private SampleChannel welcome;
|
||||
|
||||
private Bindable<bool> menuMusic;
|
||||
|
||||
private Track track;
|
||||
|
||||
private WorkingBeatmap introBeatmap;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game, ISampleStore samples)
|
||||
{
|
||||
menuMusic = config.GetBindable<bool>(OsuSetting.MenuMusic);
|
||||
|
||||
BeatmapSetInfo setInfo = null;
|
||||
|
||||
if (!menuMusic.Value)
|
||||
{
|
||||
var sets = beatmaps.GetAllUsableBeatmapSets();
|
||||
if (sets.Count > 0)
|
||||
setInfo = beatmaps.QueryBeatmapSet(s => s.ID == sets[RNG.Next(0, sets.Count - 1)].ID);
|
||||
}
|
||||
|
||||
if (setInfo == null)
|
||||
{
|
||||
setInfo = beatmaps.QueryBeatmapSet(b => b.Hash == menu_music_beatmap_hash);
|
||||
|
||||
if (setInfo == null)
|
||||
{
|
||||
// we need to import the default menu background beatmap
|
||||
setInfo = beatmaps.Import(new ZipArchiveReader(game.Resources.GetStream(@"Tracks/circles.osz"), "circles.osz")).Result;
|
||||
|
||||
setInfo.Protected = true;
|
||||
beatmaps.Update(setInfo);
|
||||
}
|
||||
}
|
||||
|
||||
introBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]);
|
||||
track = introBeatmap.Track;
|
||||
|
||||
if (config.Get<bool>(OsuSetting.MenuVoice))
|
||||
welcome = samples.Get(@"welcome");
|
||||
}
|
||||
|
||||
private const double delay_step_one = 2300;
|
||||
private const double delay_step_two = 600;
|
||||
|
||||
protected override void LogoArriving(OsuLogo logo, bool resuming)
|
||||
{
|
||||
base.LogoArriving(logo, resuming);
|
||||
|
||||
if (!resuming)
|
||||
{
|
||||
Beatmap.Value = introBeatmap;
|
||||
introBeatmap = null;
|
||||
|
||||
welcome?.Play();
|
||||
|
||||
Scheduler.AddDelayed(delegate
|
||||
{
|
||||
// Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Manu.
|
||||
if (menuMusic.Value)
|
||||
{
|
||||
track.Restart();
|
||||
track = null;
|
||||
}
|
||||
|
||||
PrepareMenuLoad();
|
||||
|
||||
Scheduler.AddDelayed(LoadMenu, delay_step_one);
|
||||
}, delay_step_two);
|
||||
|
||||
logo.ScaleTo(1);
|
||||
logo.FadeIn();
|
||||
logo.PlayIntro();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnSuspending(IScreen next)
|
||||
{
|
||||
track = null;
|
||||
|
||||
this.FadeOut(300);
|
||||
base.OnSuspending(next);
|
||||
}
|
||||
}
|
||||
}
|
114
osu.Game/Screens/Menu/IntroScreen.cs
Normal file
114
osu.Game/Screens/Menu/IntroScreen.cs
Normal file
@ -0,0 +1,114 @@
|
||||
// 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.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Menu
|
||||
{
|
||||
public abstract class IntroScreen : StartupScreen
|
||||
{
|
||||
private readonly BindableDouble exitingVolumeFade = new BindableDouble(1);
|
||||
|
||||
public const int EXIT_DELAY = 3000;
|
||||
|
||||
[Resolved]
|
||||
private AudioManager audio { get; set; }
|
||||
|
||||
private SampleChannel seeya;
|
||||
|
||||
private Bindable<bool> menuVoice;
|
||||
|
||||
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBlack();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game)
|
||||
{
|
||||
menuVoice = config.GetBindable<bool>(OsuSetting.MenuVoice);
|
||||
seeya = audio.Samples.Get(@"seeya");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether we have loaded the menu previously.
|
||||
/// </summary>
|
||||
public bool DidLoadMenu { get; private set; }
|
||||
|
||||
public override bool OnExiting(IScreen next)
|
||||
{
|
||||
//cancel exiting if we haven't loaded the menu yet.
|
||||
return !DidLoadMenu;
|
||||
}
|
||||
|
||||
public override void OnResuming(IScreen last)
|
||||
{
|
||||
this.FadeIn(300);
|
||||
|
||||
double fadeOutTime = EXIT_DELAY;
|
||||
//we also handle the exit transition.
|
||||
if (menuVoice.Value)
|
||||
seeya.Play();
|
||||
else
|
||||
fadeOutTime = 500;
|
||||
|
||||
audio.AddAdjustment(AdjustableProperty.Volume, exitingVolumeFade);
|
||||
this.TransformBindableTo(exitingVolumeFade, 0, fadeOutTime).OnComplete(_ => this.Exit());
|
||||
|
||||
//don't want to fade out completely else we will stop running updates.
|
||||
Game.FadeTo(0.01f, fadeOutTime);
|
||||
|
||||
base.OnResuming(last);
|
||||
}
|
||||
|
||||
protected override void LogoArriving(OsuLogo logo, bool resuming)
|
||||
{
|
||||
base.LogoArriving(logo, resuming);
|
||||
|
||||
logo.Colour = Color4.White;
|
||||
logo.Triangles = false;
|
||||
logo.Ripple = false;
|
||||
|
||||
if (!resuming)
|
||||
{
|
||||
logo.MoveTo(new Vector2(0.5f));
|
||||
logo.ScaleTo(Vector2.One);
|
||||
logo.Hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
const int quick_appear = 350;
|
||||
int initialMovementTime = logo.Alpha > 0.2f ? quick_appear : 0;
|
||||
|
||||
logo.MoveTo(new Vector2(0.5f), initialMovementTime, Easing.OutQuint);
|
||||
|
||||
logo
|
||||
.ScaleTo(1, initialMovementTime, Easing.OutQuint)
|
||||
.FadeIn(quick_appear, Easing.OutQuint)
|
||||
.Then()
|
||||
.RotateTo(20, EXIT_DELAY * 1.5f)
|
||||
.FadeOut(EXIT_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
private MainMenu mainMenu;
|
||||
|
||||
protected void PrepareMenuLoad()
|
||||
{
|
||||
LoadComponentAsync(mainMenu = new MainMenu());
|
||||
}
|
||||
|
||||
protected void LoadMenu()
|
||||
{
|
||||
DidLoadMenu = true;
|
||||
this.Push(mainMenu);
|
||||
}
|
||||
}
|
||||
}
|
413
osu.Game/Screens/Menu/IntroTriangles.cs
Normal file
413
osu.Game/Screens/Menu/IntroTriangles.cs
Normal file
@ -0,0 +1,413 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Graphics.Video;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.IO.Archives;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Menu
|
||||
{
|
||||
public class IntroTriangles : IntroScreen
|
||||
{
|
||||
private const string menu_music_beatmap_hash = "a1556d0801b3a6b175dda32ef546f0ec812b400499f575c44fccbe9c67f9b1e5";
|
||||
|
||||
private SampleChannel welcome;
|
||||
|
||||
protected override BackgroundScreen CreateBackground() => background = new BackgroundScreenDefault(false)
|
||||
{
|
||||
Alpha = 0,
|
||||
};
|
||||
|
||||
[Resolved]
|
||||
private AudioManager audio { get; set; }
|
||||
|
||||
private Bindable<bool> menuMusic;
|
||||
private Track track;
|
||||
private WorkingBeatmap introBeatmap;
|
||||
|
||||
private BackgroundScreenDefault background;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuConfigManager config, BeatmapManager beatmaps, Framework.Game game)
|
||||
{
|
||||
menuMusic = config.GetBindable<bool>(OsuSetting.MenuMusic);
|
||||
|
||||
BeatmapSetInfo setInfo = null;
|
||||
|
||||
if (!menuMusic.Value)
|
||||
{
|
||||
var sets = beatmaps.GetAllUsableBeatmapSets();
|
||||
if (sets.Count > 0)
|
||||
setInfo = beatmaps.QueryBeatmapSet(s => s.ID == sets[RNG.Next(0, sets.Count - 1)].ID);
|
||||
}
|
||||
|
||||
if (setInfo == null)
|
||||
{
|
||||
setInfo = beatmaps.QueryBeatmapSet(b => b.Hash == menu_music_beatmap_hash);
|
||||
|
||||
if (setInfo == null)
|
||||
{
|
||||
// we need to import the default menu background beatmap
|
||||
setInfo = beatmaps.Import(new ZipArchiveReader(game.Resources.GetStream(@"Tracks/triangles.osz"), "triangles.osz")).Result;
|
||||
|
||||
setInfo.Protected = true;
|
||||
beatmaps.Update(setInfo);
|
||||
}
|
||||
}
|
||||
|
||||
introBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]);
|
||||
|
||||
track = introBeatmap.Track;
|
||||
track.Reset();
|
||||
|
||||
if (config.Get<bool>(OsuSetting.MenuVoice) && !menuMusic.Value)
|
||||
// triangles has welcome sound included in the track. only play this if the user doesn't want menu music.
|
||||
welcome = audio.Samples.Get(@"welcome");
|
||||
}
|
||||
|
||||
protected override void LogoArriving(OsuLogo logo, bool resuming)
|
||||
{
|
||||
base.LogoArriving(logo, resuming);
|
||||
|
||||
logo.Triangles = true;
|
||||
|
||||
if (!resuming)
|
||||
{
|
||||
Beatmap.Value = introBeatmap;
|
||||
introBeatmap = null;
|
||||
|
||||
PrepareMenuLoad();
|
||||
|
||||
LoadComponentAsync(new TrianglesIntroSequence(logo, background)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Clock = new FramedClock(menuMusic.Value ? track : null),
|
||||
LoadMenu = LoadMenu
|
||||
}, t =>
|
||||
{
|
||||
AddInternal(t);
|
||||
welcome?.Play();
|
||||
|
||||
// Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Menu.
|
||||
if (menuMusic.Value)
|
||||
track.Start();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnResuming(IScreen last)
|
||||
{
|
||||
base.OnResuming(last);
|
||||
background.FadeOut(100);
|
||||
}
|
||||
|
||||
public override void OnSuspending(IScreen next)
|
||||
{
|
||||
track = null;
|
||||
base.OnSuspending(next);
|
||||
}
|
||||
|
||||
private class TrianglesIntroSequence : CompositeDrawable
|
||||
{
|
||||
private readonly OsuLogo logo;
|
||||
private readonly BackgroundScreenDefault background;
|
||||
private OsuSpriteText welcomeText;
|
||||
|
||||
private RulesetFlow rulesets;
|
||||
private Container rulesetsScale;
|
||||
private Drawable logoContainerSecondary;
|
||||
private Drawable logoContainer;
|
||||
|
||||
private GlitchingTriangles triangles;
|
||||
|
||||
public Action LoadMenu;
|
||||
|
||||
public TrianglesIntroSequence(OsuLogo logo, BackgroundScreenDefault background)
|
||||
{
|
||||
this.logo = logo;
|
||||
this.background = background;
|
||||
}
|
||||
|
||||
private OsuGameBase game;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures, OsuGameBase game)
|
||||
{
|
||||
this.game = game;
|
||||
|
||||
InternalChildren = new[]
|
||||
{
|
||||
triangles = new GlitchingTriangles
|
||||
{
|
||||
Alpha = 0,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(0.4f, 0.16f)
|
||||
},
|
||||
welcomeText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Padding = new MarginPadding { Bottom = 10 },
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Light, size: 42),
|
||||
Alpha = 1,
|
||||
Spacing = new Vector2(5),
|
||||
},
|
||||
rulesetsScale = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
rulesets = new RulesetFlow()
|
||||
}
|
||||
},
|
||||
logoContainerSecondary = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Child = logoContainer = new LazerLogo(textures.GetStream("Menu/logo-triangles.mp4"))
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private const double text_1 = 200;
|
||||
private const double text_2 = 400;
|
||||
private const double text_3 = 700;
|
||||
private const double text_4 = 900;
|
||||
private const double text_glitch = 1060;
|
||||
|
||||
private const double rulesets_1 = 1450;
|
||||
private const double rulesets_2 = 1650;
|
||||
private const double rulesets_3 = 1850;
|
||||
|
||||
private const double logo_scale_duration = 920;
|
||||
private const double logo_1 = 2080;
|
||||
private const double logo_2 = logo_1 + logo_scale_duration;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
const float scale_start = 1.2f;
|
||||
const float scale_adjust = 0.8f;
|
||||
|
||||
rulesets.Hide();
|
||||
logoContainer.Hide();
|
||||
background.Hide();
|
||||
|
||||
using (BeginAbsoluteSequence(0, true))
|
||||
{
|
||||
using (BeginDelayedSequence(text_1, true))
|
||||
welcomeText.FadeIn().OnComplete(t => t.Text = "wel");
|
||||
|
||||
using (BeginDelayedSequence(text_2, true))
|
||||
welcomeText.FadeIn().OnComplete(t => t.Text = "welcome");
|
||||
|
||||
using (BeginDelayedSequence(text_3, true))
|
||||
welcomeText.FadeIn().OnComplete(t => t.Text = "welcome to");
|
||||
|
||||
using (BeginDelayedSequence(text_4, true))
|
||||
{
|
||||
welcomeText.FadeIn().OnComplete(t => t.Text = "welcome to osu!");
|
||||
welcomeText.TransformTo(nameof(welcomeText.Spacing), new Vector2(50, 0), 5000);
|
||||
}
|
||||
|
||||
using (BeginDelayedSequence(text_glitch, true))
|
||||
triangles.FadeIn();
|
||||
|
||||
using (BeginDelayedSequence(rulesets_1, true))
|
||||
{
|
||||
rulesetsScale.ScaleTo(0.8f, 1000);
|
||||
rulesets.FadeIn().ScaleTo(1).TransformSpacingTo(new Vector2(200, 0));
|
||||
welcomeText.FadeOut();
|
||||
triangles.FadeOut();
|
||||
}
|
||||
|
||||
using (BeginDelayedSequence(rulesets_2, true))
|
||||
{
|
||||
rulesets.ScaleTo(2).TransformSpacingTo(new Vector2(30, 0));
|
||||
}
|
||||
|
||||
using (BeginDelayedSequence(rulesets_3, true))
|
||||
{
|
||||
rulesets.ScaleTo(4).TransformSpacingTo(new Vector2(10, 0));
|
||||
rulesetsScale.ScaleTo(1.3f, 1000);
|
||||
}
|
||||
|
||||
using (BeginDelayedSequence(logo_1, true))
|
||||
{
|
||||
rulesets.FadeOut();
|
||||
|
||||
// matching flyte curve y = 0.25x^2 + (max(0, x - 0.7) / 0.3) ^ 5
|
||||
logoContainer.FadeIn().ScaleTo(scale_start).Then().Delay(logo_scale_duration * 0.7f).ScaleTo(scale_start - scale_adjust, logo_scale_duration * 0.3f, Easing.InQuint);
|
||||
logoContainerSecondary.ScaleTo(scale_start).Then().ScaleTo(scale_start - scale_adjust * 0.25f, logo_scale_duration, Easing.InQuad);
|
||||
}
|
||||
|
||||
using (BeginDelayedSequence(logo_2, true))
|
||||
{
|
||||
logoContainer.FadeOut().OnComplete(_ =>
|
||||
{
|
||||
logo.FadeIn();
|
||||
background.FadeIn();
|
||||
|
||||
game.Add(new GameWideFlash());
|
||||
|
||||
LoadMenu();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class GameWideFlash : Box
|
||||
{
|
||||
private const double flash_length = 1000;
|
||||
|
||||
public GameWideFlash()
|
||||
{
|
||||
Colour = Color4.White;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Blending = BlendingMode.Additive;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
this.FadeOutFromOne(flash_length, Easing.Out);
|
||||
}
|
||||
}
|
||||
|
||||
private class LazerLogo : CompositeDrawable
|
||||
{
|
||||
public LazerLogo(Stream videoStream)
|
||||
{
|
||||
Size = new Vector2(960);
|
||||
|
||||
InternalChild = new VideoSprite(videoStream)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Clock = new FramedOffsetClock(Clock) { Offset = -logo_1 }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private class RulesetFlow : FillFlowContainer
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(RulesetStore rulesets)
|
||||
{
|
||||
var modes = new List<Drawable>();
|
||||
|
||||
foreach (var ruleset in rulesets.AvailableRulesets)
|
||||
{
|
||||
var icon = new ConstrainedIconContainer
|
||||
{
|
||||
Icon = ruleset.CreateInstance().CreateIcon(),
|
||||
Size = new Vector2(30),
|
||||
};
|
||||
|
||||
modes.Add(icon);
|
||||
}
|
||||
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Children = modes;
|
||||
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
}
|
||||
}
|
||||
|
||||
private class GlitchingTriangles : CompositeDrawable
|
||||
{
|
||||
public GlitchingTriangles()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
private double? lastGenTime;
|
||||
|
||||
private const double time_between_triangles = 22;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (lastGenTime == null || Time.Current - lastGenTime > time_between_triangles)
|
||||
{
|
||||
lastGenTime = (lastGenTime ?? Time.Current) + time_between_triangles;
|
||||
|
||||
Drawable triangle = new OutlineTriangle(RNG.NextBool(), (RNG.NextSingle() + 0.2f) * 80)
|
||||
{
|
||||
RelativePositionAxes = Axes.Both,
|
||||
Position = new Vector2(RNG.NextSingle(), RNG.NextSingle()),
|
||||
};
|
||||
|
||||
AddInternal(triangle);
|
||||
|
||||
triangle.FadeOutFromOne(120);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a sprite that is drawn in a triangle shape, instead of a rectangle shape.
|
||||
/// </summary>
|
||||
public class OutlineTriangle : BufferedContainer
|
||||
{
|
||||
public OutlineTriangle(bool outlineOnly, float size)
|
||||
{
|
||||
Size = new Vector2(size);
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Triangle { RelativeSizeAxes = Axes.Both },
|
||||
};
|
||||
|
||||
if (outlineOnly)
|
||||
{
|
||||
AddInternal(new Triangle
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Colour = Color4.Black,
|
||||
Size = new Vector2(size - 5),
|
||||
Blending = BlendingMode.None,
|
||||
});
|
||||
}
|
||||
|
||||
Blending = BlendingMode.Additive;
|
||||
CacheDrawnFrameBuffer = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -96,13 +96,13 @@ namespace osu.Game.Screens.Menu
|
||||
var track = beatmap.Value.TrackLoaded ? beatmap.Value.Track : null;
|
||||
var effect = beatmap.Value.BeatmapLoaded ? beatmap.Value.Beatmap.ControlPointInfo.EffectPointAt(track?.CurrentTime ?? Time.Current) : null;
|
||||
|
||||
float[] temporalAmplitudes = track?.CurrentAmplitudes.FrequencyAmplitudes ?? new float[256];
|
||||
float[] temporalAmplitudes = track?.CurrentAmplitudes.FrequencyAmplitudes;
|
||||
|
||||
for (int i = 0; i < bars_per_visualiser; i++)
|
||||
{
|
||||
if (track?.IsRunning ?? false)
|
||||
{
|
||||
float targetAmplitude = temporalAmplitudes[(i + indexOffset) % bars_per_visualiser] * (effect?.KiaiMode == true ? 1 : 0.5f);
|
||||
float targetAmplitude = (temporalAmplitudes?[(i + indexOffset) % bars_per_visualiser] ?? 0) * (effect?.KiaiMode == true ? 1 : 0.5f);
|
||||
if (targetAmplitude > frequencyAmplitudes[i])
|
||||
frequencyAmplitudes[i] = targetAmplitude;
|
||||
}
|
||||
@ -115,7 +115,6 @@ namespace osu.Game.Screens.Menu
|
||||
}
|
||||
|
||||
indexOffset = (indexOffset + index_change) % bars_per_visualiser;
|
||||
Scheduler.AddDelayed(updateAmplitudes, time_between_updates);
|
||||
}
|
||||
|
||||
private void updateColour()
|
||||
@ -131,7 +130,9 @@ namespace osu.Game.Screens.Menu
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
updateAmplitudes();
|
||||
|
||||
var delayed = Scheduler.AddDelayed(updateAmplitudes, time_between_updates, true);
|
||||
delayed.PerformRepeatCatchUpExecutions = false;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
|
@ -14,7 +14,6 @@ using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
using osu.Game.Screens.Charts;
|
||||
using osu.Game.Screens.Direct;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Multi;
|
||||
using osu.Game.Screens.Select;
|
||||
@ -23,11 +22,13 @@ namespace osu.Game.Screens.Menu
|
||||
{
|
||||
public class MainMenu : OsuScreen
|
||||
{
|
||||
private ButtonSystem buttons;
|
||||
public const float FADE_IN_DURATION = 300;
|
||||
|
||||
public const float FADE_OUT_DURATION = 400;
|
||||
|
||||
public override bool HideOverlaysOnEnter => buttons == null || buttons.State == ButtonSystemState.Initial;
|
||||
|
||||
protected override bool AllowBackButton => buttons.State != ButtonSystemState.Initial && host.CanExit;
|
||||
public override bool AllowBackButton => false;
|
||||
|
||||
public override bool AllowExternalScreenChange => true;
|
||||
|
||||
@ -35,9 +36,14 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
private MenuSideFlashes sideFlashes;
|
||||
|
||||
private ButtonSystem buttons;
|
||||
|
||||
[Resolved]
|
||||
private GameHost host { get; set; }
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private MusicController music { get; set; }
|
||||
|
||||
private BackgroundScreenDefault background;
|
||||
|
||||
protected override BackgroundScreen CreateBackground() => background;
|
||||
@ -58,7 +64,6 @@ namespace osu.Game.Screens.Menu
|
||||
buttons = new ButtonSystem
|
||||
{
|
||||
OnChart = delegate { this.Push(new ChartListing()); },
|
||||
OnDirect = delegate { this.Push(new OnlineListing()); },
|
||||
OnEdit = delegate { this.Push(new Editor()); },
|
||||
OnSolo = onSolo,
|
||||
OnMulti = delegate { this.Push(new Multiplayer()); },
|
||||
@ -116,7 +121,7 @@ namespace osu.Game.Screens.Menu
|
||||
var track = Beatmap.Value.Track;
|
||||
var metadata = Beatmap.Value.Metadata;
|
||||
|
||||
if (last is Intro && track != null)
|
||||
if (last is IntroScreen && track != null)
|
||||
{
|
||||
if (!track.IsRunning)
|
||||
{
|
||||
@ -141,12 +146,10 @@ namespace osu.Game.Screens.Menu
|
||||
{
|
||||
buttons.State = ButtonSystemState.TopLevel;
|
||||
|
||||
const float length = 300;
|
||||
this.FadeIn(FADE_IN_DURATION, Easing.OutQuint);
|
||||
this.MoveTo(new Vector2(0, 0), FADE_IN_DURATION, Easing.OutQuint);
|
||||
|
||||
this.FadeIn(length, Easing.OutQuint);
|
||||
this.MoveTo(new Vector2(0, 0), length, Easing.OutQuint);
|
||||
|
||||
sideFlashes.Delay(length).FadeIn(64, Easing.InQuint);
|
||||
sideFlashes.Delay(FADE_IN_DURATION).FadeIn(64, Easing.InQuint);
|
||||
}
|
||||
}
|
||||
|
||||
@ -171,12 +174,10 @@ namespace osu.Game.Screens.Menu
|
||||
{
|
||||
base.OnSuspending(next);
|
||||
|
||||
const float length = 400;
|
||||
|
||||
buttons.State = ButtonSystemState.EnteringMode;
|
||||
|
||||
this.FadeOut(length, Easing.InSine);
|
||||
this.MoveTo(new Vector2(-800, 0), length, Easing.InSine);
|
||||
this.FadeOut(FADE_OUT_DURATION, Easing.InSine);
|
||||
this.MoveTo(new Vector2(-800, 0), FADE_OUT_DURATION, Easing.InSine);
|
||||
|
||||
sideFlashes.FadeOut(64, Easing.OutQuint);
|
||||
}
|
||||
@ -189,6 +190,9 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
//we may have consumed our preloaded instance, so let's make another.
|
||||
preloadSongSelect();
|
||||
|
||||
if (Beatmap.Value.Track != null && music?.IsUserPaused != true)
|
||||
Beatmap.Value.Track.Start();
|
||||
}
|
||||
|
||||
public override bool OnExiting(IScreen next)
|
||||
|
@ -14,6 +14,7 @@ namespace osu.Game.Screens.Multi.Lounge.Components
|
||||
{
|
||||
protected override Color4 BackgroundColour => OsuColour.FromHex(@"362e42");
|
||||
protected override PrimaryFilter DefaultTab => PrimaryFilter.Open;
|
||||
protected override SecondaryFilter DefaultCategory => SecondaryFilter.Public;
|
||||
|
||||
protected override float ContentHorizontalPadding => base.ContentHorizontalPadding + OsuScreen.HORIZONTAL_OVERFLOW_PADDING;
|
||||
|
||||
|
@ -25,10 +25,14 @@ namespace osu.Game.Screens.Multi.Match.Components
|
||||
{
|
||||
public const float HEIGHT = 200;
|
||||
|
||||
public MatchTabControl Tabs;
|
||||
public readonly BindableBool ShowBeatmapPanel = new BindableBool();
|
||||
|
||||
public MatchTabControl Tabs { get; private set; }
|
||||
|
||||
public Action RequestBeatmapSelection;
|
||||
|
||||
private MatchBeatmapPanel beatmapPanel;
|
||||
|
||||
public Header()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
@ -53,8 +57,14 @@ namespace osu.Game.Screens.Multi.Match.Components
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.4f), Color4.Black.Opacity(0.6f)),
|
||||
Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.7f), Color4.Black.Opacity(0.8f)),
|
||||
},
|
||||
beatmapPanel = new MatchBeatmapPanel
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Margin = new MarginPadding { Right = 100 },
|
||||
}
|
||||
}
|
||||
},
|
||||
new Box
|
||||
@ -114,6 +124,12 @@ namespace osu.Game.Screens.Multi.Match.Components
|
||||
beatmapButton.Action = () => RequestBeatmapSelection?.Invoke();
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
ShowBeatmapPanel.BindValueChanged(value => beatmapPanel.FadeTo(value.NewValue ? 1 : 0, 200, Easing.OutQuint), true);
|
||||
}
|
||||
|
||||
private class BeatmapSelectButton : HeaderButton
|
||||
{
|
||||
[Resolved(typeof(Room), nameof(Room.RoomID))]
|
||||
|
@ -28,7 +28,6 @@ namespace osu.Game.Screens.Multi.Match.Components
|
||||
private void load()
|
||||
{
|
||||
ReadyButton readyButton;
|
||||
ViewBeatmapButton viewBeatmapButton;
|
||||
HostInfo hostInfo;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
@ -80,7 +79,6 @@ namespace osu.Game.Screens.Multi.Match.Components
|
||||
Direction = FillDirection.Horizontal,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
viewBeatmapButton = new ViewBeatmapButton(),
|
||||
readyButton = new ReadyButton
|
||||
{
|
||||
Action = () => OnStart?.Invoke()
|
||||
@ -91,11 +89,7 @@ namespace osu.Game.Screens.Multi.Match.Components
|
||||
},
|
||||
};
|
||||
|
||||
CurrentItem.BindValueChanged(item =>
|
||||
{
|
||||
viewBeatmapButton.Beatmap.Value = item.NewValue?.Beatmap;
|
||||
readyButton.Beatmap.Value = item.NewValue?.Beatmap;
|
||||
}, true);
|
||||
CurrentItem.BindValueChanged(item => readyButton.Beatmap.Value = item.NewValue?.Beatmap, true);
|
||||
|
||||
hostInfo.Host.BindTo(Host);
|
||||
}
|
||||
|
62
osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs
Normal file
62
osu.Game/Screens/Multi/Match/Components/MatchBeatmapPanel.cs
Normal file
@ -0,0 +1,62 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Threading;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Overlays.Direct;
|
||||
using osu.Game.Rulesets;
|
||||
|
||||
namespace osu.Game.Screens.Multi.Match.Components
|
||||
{
|
||||
public class MatchBeatmapPanel : MultiplayerComposite
|
||||
{
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private RulesetStore rulesets { get; set; }
|
||||
|
||||
private CancellationTokenSource loadCancellation;
|
||||
private GetBeatmapSetRequest request;
|
||||
private DirectGridPanel panel;
|
||||
|
||||
public MatchBeatmapPanel()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
CurrentItem.BindValueChanged(item => loadNewPanel(item.NewValue?.Beatmap), true);
|
||||
}
|
||||
|
||||
private void loadNewPanel(BeatmapInfo beatmap)
|
||||
{
|
||||
loadCancellation?.Cancel();
|
||||
request?.Cancel();
|
||||
|
||||
panel?.FadeOut(200);
|
||||
panel?.Expire();
|
||||
panel = null;
|
||||
|
||||
if (beatmap?.OnlineBeatmapID == null)
|
||||
return;
|
||||
|
||||
loadCancellation = new CancellationTokenSource();
|
||||
|
||||
request = new GetBeatmapSetRequest(beatmap.OnlineBeatmapID.Value, BeatmapSetLookupType.BeatmapId);
|
||||
request.Success += res => Schedule(() =>
|
||||
{
|
||||
panel = new DirectGridPanel(res.ToBeatmapSet(rulesets));
|
||||
LoadComponentAsync(panel, AddInternal, loadCancellation.Token);
|
||||
});
|
||||
|
||||
api.Queue(request);
|
||||
}
|
||||
}
|
||||
}
|
@ -33,6 +33,8 @@ namespace osu.Game.Screens.Multi.Match.Components
|
||||
}, true);
|
||||
}
|
||||
|
||||
protected override bool IsOnlineScope => true;
|
||||
|
||||
protected override APIRequest FetchScores(Action<IEnumerable<APIRoomScoreInfo>> scoresCallback)
|
||||
{
|
||||
if (roomId.Value == null)
|
||||
|
@ -54,11 +54,8 @@ namespace osu.Game.Screens.Multi.Match.Components
|
||||
hasBeatmap = beatmaps.QueryBeatmap(b => b.OnlineBeatmapID == beatmap.OnlineBeatmapID) != null;
|
||||
}
|
||||
|
||||
private void beatmapAdded(BeatmapSetInfo model, bool existing)
|
||||
private void beatmapAdded(BeatmapSetInfo model)
|
||||
{
|
||||
if (Beatmap.Value == null)
|
||||
return;
|
||||
|
||||
if (model.Beatmaps.Any(b => b.OnlineBeatmapID == Beatmap.Value.OnlineBeatmapID))
|
||||
Schedule(() => hasBeatmap = true);
|
||||
}
|
||||
|
@ -1,46 +0,0 @@
|
||||
// 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.Graphics;
|
||||
using osu.Game.Beatmaps;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.Multi.Match.Components
|
||||
{
|
||||
public class ViewBeatmapButton : HeaderButton
|
||||
{
|
||||
public readonly Bindable<BeatmapInfo> Beatmap = new Bindable<BeatmapInfo>();
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private OsuGame osuGame { get; set; }
|
||||
|
||||
public ViewBeatmapButton()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y;
|
||||
Size = new Vector2(200, 1);
|
||||
|
||||
Text = "View beatmap";
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
if (osuGame != null)
|
||||
Beatmap.BindValueChanged(beatmap => updateAction(beatmap.NewValue), true);
|
||||
}
|
||||
|
||||
private void updateAction(BeatmapInfo beatmap)
|
||||
{
|
||||
if (beatmap == null)
|
||||
{
|
||||
Enabled.Value = false;
|
||||
return;
|
||||
}
|
||||
|
||||
Action = () => osuGame.ShowBeatmap(beatmap.OnlineBeatmapID ?? 0);
|
||||
Enabled.Value = true;
|
||||
}
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Multiplayer.GameTypes;
|
||||
@ -18,7 +19,8 @@ using PlaylistItem = osu.Game.Online.Multiplayer.PlaylistItem;
|
||||
|
||||
namespace osu.Game.Screens.Multi.Match
|
||||
{
|
||||
public class MatchSubScreen : MultiplayerSubScreen
|
||||
[Cached(typeof(IPreviewTrackOwner))]
|
||||
public class MatchSubScreen : MultiplayerSubScreen, IPreviewTrackOwner
|
||||
{
|
||||
public override bool DisallowExternalBeatmapRulesetChanges => true;
|
||||
|
||||
@ -44,6 +46,9 @@ namespace osu.Game.Screens.Multi.Match
|
||||
[Resolved]
|
||||
private BeatmapManager beatmapManager { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private PreviewTrackManager previewTrackManager { get; set; }
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private OsuGame game { get; set; }
|
||||
|
||||
@ -146,18 +151,12 @@ namespace osu.Game.Screens.Multi.Match
|
||||
{
|
||||
const float fade_duration = 500;
|
||||
|
||||
if (tab.NewValue is SettingsMatchPage)
|
||||
{
|
||||
settings.Show();
|
||||
info.FadeOut(fade_duration, Easing.OutQuint);
|
||||
bottomRow.FadeOut(fade_duration, Easing.OutQuint);
|
||||
}
|
||||
else
|
||||
{
|
||||
settings.Hide();
|
||||
info.FadeIn(fade_duration, Easing.OutQuint);
|
||||
bottomRow.FadeIn(fade_duration, Easing.OutQuint);
|
||||
}
|
||||
var settingsDisplayed = tab.NewValue is SettingsMatchPage;
|
||||
|
||||
header.ShowBeatmapPanel.Value = !settingsDisplayed;
|
||||
settings.State.Value = settingsDisplayed ? Visibility.Visible : Visibility.Hidden;
|
||||
info.FadeTo(settingsDisplayed ? 0 : 1, fade_duration, Easing.OutQuint);
|
||||
bottomRow.FadeTo(settingsDisplayed ? 0 : 1, fade_duration, Easing.OutQuint);
|
||||
}, true);
|
||||
|
||||
chat.Exit += () =>
|
||||
@ -179,8 +178,8 @@ namespace osu.Game.Screens.Multi.Match
|
||||
public override bool OnExiting(IScreen next)
|
||||
{
|
||||
RoomManager?.PartRoom();
|
||||
|
||||
Mods.Value = Array.Empty<Mod>();
|
||||
previewTrackManager.StopAnyPlaying(this);
|
||||
|
||||
return base.OnExiting(next);
|
||||
}
|
||||
@ -195,14 +194,17 @@ namespace osu.Game.Screens.Multi.Match
|
||||
|
||||
Beatmap.Value = beatmapManager.GetWorkingBeatmap(localBeatmap);
|
||||
Mods.Value = e.NewValue?.RequiredMods?.ToArray() ?? Array.Empty<Mod>();
|
||||
|
||||
if (e.NewValue?.Ruleset != null)
|
||||
Ruleset.Value = e.NewValue.Ruleset;
|
||||
|
||||
previewTrackManager.StopAnyPlaying(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle the case where a beatmap is imported (and can be used by this match).
|
||||
/// </summary>
|
||||
private void beatmapAdded(BeatmapSetInfo model, bool existing) => Schedule(() =>
|
||||
private void beatmapAdded(BeatmapSetInfo model) => Schedule(() =>
|
||||
{
|
||||
if (Beatmap.Value != beatmapManager.DefaultBeatmap)
|
||||
return;
|
||||
@ -222,6 +224,8 @@ namespace osu.Game.Screens.Multi.Match
|
||||
|
||||
private void onStart()
|
||||
{
|
||||
previewTrackManager.StopAnyPlaying(this);
|
||||
|
||||
switch (type.Value)
|
||||
{
|
||||
default:
|
||||
|
@ -212,6 +212,12 @@ namespace osu.Game.Screens.Multi
|
||||
|
||||
public override bool OnExiting(IScreen next)
|
||||
{
|
||||
if (screenStack.CurrentScreen != null && !(screenStack.CurrentScreen is LoungeSubScreen))
|
||||
{
|
||||
screenStack.Exit();
|
||||
return true;
|
||||
}
|
||||
|
||||
waves.Hide();
|
||||
|
||||
this.Delay(WaveContainer.DISAPPEAR_DURATION).FadeOut();
|
||||
@ -257,7 +263,7 @@ namespace osu.Game.Screens.Multi
|
||||
private void subScreenChanged(IScreen newScreen)
|
||||
{
|
||||
updatePollingRate(isIdle.Value);
|
||||
createButton.FadeTo(newScreen is MatchSubScreen ? 0 : 1, 200);
|
||||
createButton.FadeTo(newScreen is LoungeSubScreen ? 1 : 0, 200);
|
||||
|
||||
updateTrack();
|
||||
}
|
||||
|
@ -8,10 +8,8 @@ using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osu.Game.Overlays;
|
||||
@ -21,7 +19,7 @@ using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Screens
|
||||
{
|
||||
public abstract class OsuScreen : Screen, IOsuScreen, IKeyBindingHandler<GlobalAction>, IHasDescription
|
||||
public abstract class OsuScreen : Screen, IOsuScreen, IHasDescription
|
||||
{
|
||||
/// <summary>
|
||||
/// The amount of negative padding that should be applied to game background content which touches both the left and right sides of the screen.
|
||||
@ -36,7 +34,7 @@ namespace osu.Game.Screens
|
||||
|
||||
public string Description => Title;
|
||||
|
||||
protected virtual bool AllowBackButton => true;
|
||||
public virtual bool AllowBackButton => true;
|
||||
|
||||
public virtual bool AllowExternalScreenChange => false;
|
||||
|
||||
@ -131,21 +129,6 @@ namespace osu.Game.Screens
|
||||
sampleExit = audio.Samples.Get(@"UI/screen-back");
|
||||
}
|
||||
|
||||
public virtual bool OnPressed(GlobalAction action)
|
||||
{
|
||||
if (!this.IsCurrentScreen()) return false;
|
||||
|
||||
if (action == GlobalAction.Back && AllowBackButton)
|
||||
{
|
||||
this.Exit();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnReleased(GlobalAction action) => action == GlobalAction.Back && AllowBackButton;
|
||||
|
||||
public override void OnResuming(IScreen last)
|
||||
{
|
||||
if (PlayResumeSound)
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
@ -15,26 +16,45 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
public class BreakOverlay : Container
|
||||
{
|
||||
private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2;
|
||||
/// <summary>
|
||||
/// The duration of the break overlay fading.
|
||||
/// </summary>
|
||||
public const double BREAK_FADE_DURATION = BreakPeriod.MIN_BREAK_DURATION / 2;
|
||||
|
||||
private const float remaining_time_container_max_size = 0.3f;
|
||||
private const int vertical_margin = 25;
|
||||
|
||||
private List<BreakPeriod> breaks;
|
||||
|
||||
private readonly Container fadeContainer;
|
||||
|
||||
public List<BreakPeriod> Breaks
|
||||
private IReadOnlyList<BreakPeriod> breaks;
|
||||
|
||||
public IReadOnlyList<BreakPeriod> Breaks
|
||||
{
|
||||
get => breaks;
|
||||
set
|
||||
{
|
||||
breaks = value;
|
||||
initializeBreaks();
|
||||
|
||||
// reset index in case the new breaks list is smaller than last one
|
||||
isBreakTime.Value = false;
|
||||
CurrentBreakIndex = 0;
|
||||
|
||||
if (IsLoaded)
|
||||
initializeBreaks();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool RemoveCompletedTransforms => false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the gameplay is currently in a break.
|
||||
/// </summary>
|
||||
public IBindable<bool> IsBreakTime => isBreakTime;
|
||||
|
||||
protected int CurrentBreakIndex;
|
||||
|
||||
private readonly BindableBool isBreakTime = new BindableBool();
|
||||
|
||||
private readonly Container remainingTimeAdjustmentBox;
|
||||
private readonly Container remainingTimeBox;
|
||||
private readonly RemainingTimeCounter remainingTimeCounter;
|
||||
@ -109,10 +129,36 @@ namespace osu.Game.Screens.Play
|
||||
initializeBreaks();
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
updateBreakTimeBindable();
|
||||
}
|
||||
|
||||
private void updateBreakTimeBindable()
|
||||
{
|
||||
if (breaks == null || breaks.Count == 0)
|
||||
return;
|
||||
|
||||
var time = Clock.CurrentTime;
|
||||
|
||||
if (time > breaks[CurrentBreakIndex].EndTime)
|
||||
{
|
||||
while (time > breaks[CurrentBreakIndex].EndTime && CurrentBreakIndex < breaks.Count - 1)
|
||||
CurrentBreakIndex++;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (time < breaks[CurrentBreakIndex].StartTime && CurrentBreakIndex > 0)
|
||||
CurrentBreakIndex--;
|
||||
}
|
||||
|
||||
var currentBreak = breaks[CurrentBreakIndex];
|
||||
isBreakTime.Value = currentBreak.HasEffect && currentBreak.Contains(time);
|
||||
}
|
||||
|
||||
private void initializeBreaks()
|
||||
{
|
||||
if (!IsLoaded) return; // we need a clock.
|
||||
|
||||
FinishTransforms(true);
|
||||
Scheduler.CancelDelayedTasks();
|
||||
|
||||
@ -125,25 +171,25 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
using (BeginAbsoluteSequence(b.StartTime, true))
|
||||
{
|
||||
fadeContainer.FadeIn(fade_duration);
|
||||
breakArrows.Show(fade_duration);
|
||||
fadeContainer.FadeIn(BREAK_FADE_DURATION);
|
||||
breakArrows.Show(BREAK_FADE_DURATION);
|
||||
|
||||
remainingTimeAdjustmentBox
|
||||
.ResizeWidthTo(remaining_time_container_max_size, fade_duration, Easing.OutQuint)
|
||||
.Delay(b.Duration - fade_duration)
|
||||
.ResizeWidthTo(remaining_time_container_max_size, BREAK_FADE_DURATION, Easing.OutQuint)
|
||||
.Delay(b.Duration - BREAK_FADE_DURATION)
|
||||
.ResizeWidthTo(0);
|
||||
|
||||
remainingTimeBox
|
||||
.ResizeWidthTo(0, b.Duration - fade_duration)
|
||||
.ResizeWidthTo(0, b.Duration - BREAK_FADE_DURATION)
|
||||
.Then()
|
||||
.ResizeWidthTo(1);
|
||||
|
||||
remainingTimeCounter.CountTo(b.Duration).CountTo(0, b.Duration);
|
||||
|
||||
using (BeginDelayedSequence(b.Duration - fade_duration, true))
|
||||
using (BeginDelayedSequence(b.Duration - BREAK_FADE_DURATION, true))
|
||||
{
|
||||
fadeContainer.FadeOut(fade_duration);
|
||||
breakArrows.Hide(fade_duration);
|
||||
fadeContainer.FadeOut(BREAK_FADE_DURATION);
|
||||
breakArrows.Hide(BREAK_FADE_DURATION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
42
osu.Game/Screens/Play/ComboEffects.cs
Normal file
42
osu.Game/Screens/Play/ComboEffects.cs
Normal file
@ -0,0 +1,42 @@
|
||||
// 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.Graphics.Containers;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
public class ComboEffects : CompositeDrawable
|
||||
{
|
||||
private readonly ScoreProcessor processor;
|
||||
|
||||
private SkinnableSound comboBreakSample;
|
||||
|
||||
public ComboEffects(ScoreProcessor processor)
|
||||
{
|
||||
this.processor = processor;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
InternalChild = comboBreakSample = new SkinnableSound(new SampleInfo("combobreak"));
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
processor.Combo.BindValueChanged(onComboChange, true);
|
||||
}
|
||||
|
||||
private void onComboChange(ValueChangedEvent<int> combo)
|
||||
{
|
||||
if (combo.NewValue == 0 && combo.OldValue > 20)
|
||||
comboBreakSample?.Play();
|
||||
}
|
||||
}
|
||||
}
|
55
osu.Game/Screens/Play/DimmableStoryboard.cs
Normal file
55
osu.Game/Screens/Play/DimmableStoryboard.cs
Normal file
@ -0,0 +1,55 @@
|
||||
// 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.Game.Graphics.Containers;
|
||||
using osu.Game.Storyboards;
|
||||
using osu.Game.Storyboards.Drawables;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
/// <summary>
|
||||
/// A container that handles <see cref="Storyboard"/> loading, as well as applies user-specified visual settings to it.
|
||||
/// </summary>
|
||||
public class DimmableStoryboard : UserDimContainer
|
||||
{
|
||||
private readonly Storyboard storyboard;
|
||||
private DrawableStoryboard drawableStoryboard;
|
||||
|
||||
public DimmableStoryboard(Storyboard storyboard)
|
||||
{
|
||||
this.storyboard = storyboard;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
initializeStoryboard(false);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
ShowStoryboard.BindValueChanged(_ => initializeStoryboard(true), true);
|
||||
base.LoadComplete();
|
||||
}
|
||||
|
||||
protected override bool ShowDimContent => ShowStoryboard.Value && DimLevel < 1;
|
||||
|
||||
private void initializeStoryboard(bool async)
|
||||
{
|
||||
if (drawableStoryboard != null)
|
||||
return;
|
||||
|
||||
if (!ShowStoryboard.Value)
|
||||
return;
|
||||
|
||||
drawableStoryboard = storyboard.CreateDrawable();
|
||||
drawableStoryboard.Masking = true;
|
||||
|
||||
if (async)
|
||||
LoadComponentAsync(drawableStoryboard, Add);
|
||||
else
|
||||
Add(drawableStoryboard);
|
||||
}
|
||||
}
|
||||
}
|
@ -23,7 +23,8 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
public class HUDOverlay : Container
|
||||
{
|
||||
private const int duration = 100;
|
||||
private const int duration = 250;
|
||||
private const Easing easing = Easing.OutQuint;
|
||||
|
||||
public readonly KeyCounterDisplay KeyCounter;
|
||||
public readonly RollingCounter<int> ComboCounter;
|
||||
@ -35,6 +36,8 @@ namespace osu.Game.Screens.Play
|
||||
public readonly HoldForMenuButton HoldToQuit;
|
||||
public readonly PlayerSettingsOverlay PlayerSettingsOverlay;
|
||||
|
||||
public Bindable<bool> ShowHealthbar = new Bindable<bool>(true);
|
||||
|
||||
private readonly ScoreProcessor scoreProcessor;
|
||||
private readonly DrawableRuleset drawableRuleset;
|
||||
private readonly IReadOnlyList<Mod> mods;
|
||||
@ -47,6 +50,8 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
public Action<double> RequestSeek;
|
||||
|
||||
private readonly Container topScoreContainer;
|
||||
|
||||
public HUDOverlay(ScoreProcessor scoreProcessor, DrawableRuleset drawableRuleset, IReadOnlyList<Mod> mods)
|
||||
{
|
||||
this.scoreProcessor = scoreProcessor;
|
||||
@ -62,11 +67,10 @@ namespace osu.Game.Screens.Play
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Container
|
||||
topScoreContainer = new Container
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
Y = 30,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
AutoSizeDuration = 200,
|
||||
AutoSizeEasing = Easing.Out,
|
||||
@ -113,8 +117,21 @@ namespace osu.Game.Screens.Play
|
||||
ModDisplay.Current.Value = mods;
|
||||
|
||||
showHud = config.GetBindable<bool>(OsuSetting.ShowInterface);
|
||||
showHud.ValueChanged += visible => visibilityContainer.FadeTo(visible.NewValue ? 1 : 0, duration);
|
||||
showHud.TriggerChange();
|
||||
showHud.BindValueChanged(visible => visibilityContainer.FadeTo(visible.NewValue ? 1 : 0, duration, easing), true);
|
||||
|
||||
ShowHealthbar.BindValueChanged(healthBar =>
|
||||
{
|
||||
if (healthBar.NewValue)
|
||||
{
|
||||
HealthDisplay.FadeIn(duration, easing);
|
||||
topScoreContainer.MoveToY(30, duration, easing);
|
||||
}
|
||||
else
|
||||
{
|
||||
HealthDisplay.FadeOut(duration, easing);
|
||||
topScoreContainer.MoveToY(0, duration, easing);
|
||||
}
|
||||
}, true);
|
||||
|
||||
if (!showHud.Value && !hasShownNotificationOnce)
|
||||
{
|
||||
|
28
osu.Game/Screens/Play/HotkeyExitOverlay.cs
Normal file
28
osu.Game/Screens/Play/HotkeyExitOverlay.cs
Normal file
@ -0,0 +1,28 @@
|
||||
// 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.Input.Bindings;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
public class HotkeyExitOverlay : HoldToConfirmOverlay, IKeyBindingHandler<GlobalAction>
|
||||
{
|
||||
public bool OnPressed(GlobalAction action)
|
||||
{
|
||||
if (action != GlobalAction.QuickExit) return false;
|
||||
|
||||
BeginConfirm();
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool OnReleased(GlobalAction action)
|
||||
{
|
||||
if (action != GlobalAction.QuickExit) return false;
|
||||
|
||||
AbortConfirm();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -26,14 +26,13 @@ using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Storyboards.Drawables;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
public class Player : ScreenWithBeatmapBackground
|
||||
{
|
||||
protected override bool AllowBackButton => false; // handled by HoldForMenuButton
|
||||
public override bool AllowBackButton => false; // handled by HoldForMenuButton
|
||||
|
||||
protected override UserActivity InitialActivity => new UserActivity.SoloGame(Beatmap.Value.BeatmapInfo, Ruleset.Value);
|
||||
|
||||
@ -67,6 +66,8 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private SampleChannel sampleRestart;
|
||||
|
||||
private BreakOverlay breakOverlay;
|
||||
|
||||
protected ScoreProcessor ScoreProcessor { get; private set; }
|
||||
protected DrawableRuleset DrawableRuleset { get; private set; }
|
||||
|
||||
@ -76,6 +77,8 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
protected GameplayClockContainer GameplayClockContainer { get; private set; }
|
||||
|
||||
protected DimmableStoryboard DimmableStoryboard { get; private set; }
|
||||
|
||||
[Cached]
|
||||
[Cached(Type = typeof(IBindable<IReadOnlyList<Mod>>))]
|
||||
protected new readonly Bindable<IReadOnlyList<Mod>> Mods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
||||
@ -109,7 +112,6 @@ namespace osu.Game.Screens.Play
|
||||
sampleRestart = audio.Samples.Get(@"Gameplay/restart");
|
||||
|
||||
mouseWheelDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableWheel);
|
||||
showStoryboard = config.GetBindable<bool>(OsuSetting.ShowStoryboard);
|
||||
|
||||
ScoreProcessor = DrawableRuleset.CreateScoreProcessor();
|
||||
ScoreProcessor.Mods.BindTo(Mods);
|
||||
@ -121,16 +123,20 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
GameplayClockContainer.Children = new[]
|
||||
{
|
||||
StoryboardContainer = CreateStoryboardContainer(),
|
||||
DimmableStoryboard = new DimmableStoryboard(Beatmap.Value.Storyboard) { RelativeSizeAxes = Axes.Both },
|
||||
new ScalingContainer(ScalingMode.Gameplay)
|
||||
{
|
||||
Child = new LocalSkinOverrideContainer(working.Skin)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = DrawableRuleset
|
||||
Children = new Drawable[]
|
||||
{
|
||||
DrawableRuleset,
|
||||
new ComboEffects(ScoreProcessor)
|
||||
}
|
||||
}
|
||||
},
|
||||
new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor)
|
||||
breakOverlay = new BreakOverlay(working.Beatmap.BeatmapInfo.LetterboxInBreaks, ScoreProcessor)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@ -177,6 +183,16 @@ namespace osu.Game.Screens.Play
|
||||
Restart();
|
||||
},
|
||||
},
|
||||
new HotkeyExitOverlay
|
||||
{
|
||||
Action = () =>
|
||||
{
|
||||
if (!this.IsCurrentScreen()) return;
|
||||
|
||||
fadeOut(true);
|
||||
performImmediateExit();
|
||||
},
|
||||
},
|
||||
failAnimation = new FailAnimation(DrawableRuleset) { OnComplete = onFailComplete, }
|
||||
};
|
||||
|
||||
@ -185,9 +201,6 @@ namespace osu.Game.Screens.Play
|
||||
// bind clock into components that require it
|
||||
DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused);
|
||||
|
||||
// load storyboard as part of player's load if we can
|
||||
initializeStoryboard(false);
|
||||
|
||||
// Bind ScoreProcessor to ourselves
|
||||
ScoreProcessor.AllJudged += onCompletion;
|
||||
ScoreProcessor.Failed += onFail;
|
||||
@ -241,6 +254,16 @@ namespace osu.Game.Screens.Play
|
||||
return working;
|
||||
}
|
||||
|
||||
private void performImmediateExit()
|
||||
{
|
||||
// if a restart has been requested, cancel any pending completion (user has shown intent to restart).
|
||||
completionProgressDelegate?.Cancel();
|
||||
|
||||
ValidForResume = false;
|
||||
|
||||
performUserRequestedExit();
|
||||
}
|
||||
|
||||
private void performUserRequestedExit()
|
||||
{
|
||||
if (!this.IsCurrentScreen()) return;
|
||||
@ -254,20 +277,16 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
sampleRestart?.Play();
|
||||
|
||||
// if a restart has been requested, cancel any pending completion (user has shown intent to restart).
|
||||
onCompletionEvent = null;
|
||||
|
||||
ValidForResume = false;
|
||||
RestartRequested?.Invoke();
|
||||
this.Exit();
|
||||
performImmediateExit();
|
||||
}
|
||||
|
||||
private ScheduledDelegate onCompletionEvent;
|
||||
private ScheduledDelegate completionProgressDelegate;
|
||||
|
||||
private void onCompletion()
|
||||
{
|
||||
// Only show the completion screen if the player hasn't failed
|
||||
if (ScoreProcessor.HasFailed || onCompletionEvent != null)
|
||||
if (ScoreProcessor.HasFailed || completionProgressDelegate != null)
|
||||
return;
|
||||
|
||||
ValidForResume = false;
|
||||
@ -276,7 +295,7 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
using (BeginDelayedSequence(1000))
|
||||
{
|
||||
onCompletionEvent = Schedule(delegate
|
||||
completionProgressDelegate = Schedule(delegate
|
||||
{
|
||||
if (!this.IsCurrentScreen()) return;
|
||||
|
||||
@ -285,8 +304,6 @@ namespace osu.Game.Screens.Play
|
||||
scoreManager.Import(score).Wait();
|
||||
|
||||
this.Push(CreateResults(score));
|
||||
|
||||
onCompletionEvent = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -310,41 +327,6 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
protected virtual Results CreateResults(ScoreInfo score) => new SoloResults(score);
|
||||
|
||||
#region Storyboard
|
||||
|
||||
private DrawableStoryboard storyboard;
|
||||
protected UserDimContainer StoryboardContainer { get; private set; }
|
||||
|
||||
protected virtual UserDimContainer CreateStoryboardContainer() => new UserDimContainer(true)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 1,
|
||||
EnableUserDim = { Value = true }
|
||||
};
|
||||
|
||||
private Bindable<bool> showStoryboard;
|
||||
|
||||
private void initializeStoryboard(bool asyncLoad)
|
||||
{
|
||||
if (StoryboardContainer == null || storyboard != null)
|
||||
return;
|
||||
|
||||
if (!showStoryboard.Value)
|
||||
return;
|
||||
|
||||
var beatmap = Beatmap.Value;
|
||||
|
||||
storyboard = beatmap.Storyboard.CreateDrawable();
|
||||
storyboard.Masking = true;
|
||||
|
||||
if (asyncLoad)
|
||||
LoadComponentAsync(storyboard, StoryboardContainer.Add);
|
||||
else
|
||||
StoryboardContainer.Add(storyboard);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Fail Logic
|
||||
|
||||
protected FailOverlay FailOverlay { get; private set; }
|
||||
@ -431,8 +413,7 @@ namespace osu.Game.Screens.Play
|
||||
PauseOverlay.Hide();
|
||||
|
||||
// breaks and time-based conditions may allow instant resume.
|
||||
double time = GameplayClockContainer.GameplayClock.CurrentTime;
|
||||
if (Beatmap.Value.Beatmap.Breaks.Any(b => b.Contains(time)) || time < Beatmap.Value.Beatmap.HitObjects.First().StartTime)
|
||||
if (breakOverlay.IsBreakTime.Value || GameplayClockContainer.GameplayClock.CurrentTime < Beatmap.Value.Beatmap.HitObjects.First().StartTime)
|
||||
completeResume();
|
||||
else
|
||||
DrawableRuleset.RequestResume(completeResume);
|
||||
@ -462,18 +443,19 @@ namespace osu.Game.Screens.Play
|
||||
.Delay(250)
|
||||
.FadeIn(250);
|
||||
|
||||
showStoryboard.ValueChanged += _ => initializeStoryboard(true);
|
||||
|
||||
Background.EnableUserDim.Value = true;
|
||||
Background.BlurAmount.Value = 0;
|
||||
|
||||
Background.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground);
|
||||
StoryboardContainer.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground);
|
||||
DimmableStoryboard.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground);
|
||||
|
||||
storyboardReplacesBackground.Value = Beatmap.Value.Storyboard.ReplacesBackground && Beatmap.Value.Storyboard.HasDrawable;
|
||||
|
||||
GameplayClockContainer.Restart();
|
||||
GameplayClockContainer.FadeInFromZero(750, Easing.OutQuint);
|
||||
|
||||
foreach (var mod in Mods.Value.OfType<IApplicableToHUD>())
|
||||
mod.ApplyToHUD(HUDOverlay);
|
||||
}
|
||||
|
||||
public override void OnSuspending(IScreen next)
|
||||
@ -484,10 +466,10 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
public override bool OnExiting(IScreen next)
|
||||
{
|
||||
if (onCompletionEvent != null)
|
||||
if (completionProgressDelegate != null && !completionProgressDelegate.Cancelled && !completionProgressDelegate.Completed)
|
||||
{
|
||||
// Proceed to result screen if beatmap already finished playing
|
||||
onCompletionEvent.RunTask();
|
||||
// proceed to result screen if beatmap already finished playing
|
||||
completionProgressDelegate.RunTask();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Input;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
@ -53,6 +54,8 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private InputManager inputManager;
|
||||
|
||||
private IdleTracker idleTracker;
|
||||
|
||||
public PlayerLoader(Func<Player> createPlayer)
|
||||
{
|
||||
this.createPlayer = createPlayer;
|
||||
@ -93,7 +96,8 @@ namespace osu.Game.Screens.Play
|
||||
VisualSettings = new VisualSettings(),
|
||||
new InputSettings()
|
||||
}
|
||||
}
|
||||
},
|
||||
idleTracker = new IdleTracker(750)
|
||||
});
|
||||
|
||||
loadNewPlayer();
|
||||
@ -193,7 +197,7 @@ namespace osu.Game.Screens.Play
|
||||
// Here because IsHovered will not update unless we do so.
|
||||
public override bool HandlePositionalInput => true;
|
||||
|
||||
private bool readyForPush => player.LoadState == LoadState.Ready && IsHovered && GetContainingInputManager()?.DraggedDrawable == null;
|
||||
private bool readyForPush => player.LoadState == LoadState.Ready && (IsHovered || idleTracker.IsIdle.Value) && inputManager?.DraggedDrawable == null;
|
||||
|
||||
private void pushWhenLoaded()
|
||||
{
|
||||
@ -247,6 +251,7 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
public override void OnSuspending(IScreen next)
|
||||
{
|
||||
BackgroundBrightnessReduction = false;
|
||||
base.OnSuspending(next);
|
||||
cancelLoad();
|
||||
}
|
||||
@ -258,6 +263,7 @@ namespace osu.Game.Screens.Play
|
||||
cancelLoad();
|
||||
|
||||
Background.EnableUserDim.Value = false;
|
||||
BackgroundBrightnessReduction = false;
|
||||
|
||||
return base.OnExiting(next);
|
||||
}
|
||||
@ -273,6 +279,22 @@ namespace osu.Game.Screens.Play
|
||||
}
|
||||
}
|
||||
|
||||
private bool backgroundBrightnessReduction;
|
||||
|
||||
protected bool BackgroundBrightnessReduction
|
||||
{
|
||||
get => backgroundBrightnessReduction;
|
||||
set
|
||||
{
|
||||
if (value == backgroundBrightnessReduction)
|
||||
return;
|
||||
|
||||
backgroundBrightnessReduction = value;
|
||||
|
||||
Background.FadeColour(OsuColour.Gray(backgroundBrightnessReduction ? 0.8f : 1), 200);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
@ -287,12 +309,16 @@ namespace osu.Game.Screens.Play
|
||||
// Preview user-defined background dim and blur when hovered on the visual settings panel.
|
||||
Background.EnableUserDim.Value = true;
|
||||
Background.BlurAmount.Value = 0;
|
||||
|
||||
BackgroundBrightnessReduction = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Returns background dim and blur to the values specified by PlayerLoader.
|
||||
Background.EnableUserDim.Value = false;
|
||||
Background.BlurAmount.Value = BACKGROUND_BLUR;
|
||||
|
||||
BackgroundBrightnessReduction = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
99
osu.Game/Screens/Play/ReplayDownloadButton.cs
Normal file
99
osu.Game/Screens/Play/ReplayDownloadButton.cs
Normal file
@ -0,0 +1,99 @@
|
||||
// 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.Game.Graphics.Containers;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
public class ReplayDownloadButton : DownloadTrackingComposite<ScoreInfo, ScoreManager>
|
||||
{
|
||||
private DownloadButton button;
|
||||
private ShakeContainer shakeContainer;
|
||||
|
||||
private ReplayAvailability replayAvailability
|
||||
{
|
||||
get
|
||||
{
|
||||
if (State.Value == DownloadState.LocallyAvailable)
|
||||
return ReplayAvailability.Local;
|
||||
|
||||
if (Model.Value is APILegacyScoreInfo apiScore && apiScore.Replay)
|
||||
return ReplayAvailability.Online;
|
||||
|
||||
return ReplayAvailability.NotAvailable;
|
||||
}
|
||||
}
|
||||
|
||||
public ReplayDownloadButton(ScoreInfo score)
|
||||
: base(score)
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(OsuGame game, ScoreManager scores)
|
||||
{
|
||||
InternalChild = shakeContainer = new ShakeContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = button = new DownloadButton
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
}
|
||||
};
|
||||
|
||||
button.Action = () =>
|
||||
{
|
||||
switch (State.Value)
|
||||
{
|
||||
case DownloadState.LocallyAvailable:
|
||||
game?.PresentScore(Model.Value);
|
||||
break;
|
||||
|
||||
case DownloadState.NotDownloaded:
|
||||
scores.Download(Model.Value);
|
||||
break;
|
||||
|
||||
case DownloadState.Downloaded:
|
||||
case DownloadState.Downloading:
|
||||
shakeContainer.Shake();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
State.BindValueChanged(state =>
|
||||
{
|
||||
button.State.Value = state.NewValue;
|
||||
|
||||
switch (replayAvailability)
|
||||
{
|
||||
case ReplayAvailability.Local:
|
||||
button.TooltipText = @"Watch replay";
|
||||
break;
|
||||
|
||||
case ReplayAvailability.Online:
|
||||
button.TooltipText = @"Download replay";
|
||||
break;
|
||||
|
||||
default:
|
||||
button.TooltipText = @"Replay unavailable";
|
||||
break;
|
||||
}
|
||||
}, true);
|
||||
|
||||
button.Enabled.Value = replayAvailability != ReplayAvailability.NotAvailable;
|
||||
}
|
||||
|
||||
private enum ReplayAvailability
|
||||
{
|
||||
Local,
|
||||
Online,
|
||||
NotAvailable,
|
||||
}
|
||||
}
|
||||
}
|
34
osu.Game/Screens/Play/ReplayPlayerLoader.cs
Normal file
34
osu.Game/Screens/Play/ReplayPlayerLoader.cs
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Scoring;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
{
|
||||
public class ReplayPlayerLoader : PlayerLoader
|
||||
{
|
||||
private readonly ScoreInfo scoreInfo;
|
||||
|
||||
public ReplayPlayerLoader(Score score)
|
||||
: base(() => new ReplayPlayer(score))
|
||||
{
|
||||
if (score.Replay == null)
|
||||
throw new ArgumentNullException(nameof(score.Replay), $"{nameof(score)} must have a non-null {nameof(score.Replay)}.");
|
||||
|
||||
scoreInfo = score.ScoreInfo;
|
||||
}
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
var dependencies = base.CreateChildDependencies(parent);
|
||||
|
||||
// these will be reverted thanks to PlayerLoader's lease.
|
||||
Mods.Value = scoreInfo.Mods;
|
||||
Ruleset.Value = scoreInfo.Ruleset;
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
}
|
||||
}
|
@ -161,6 +161,8 @@ namespace osu.Game.Screens.Play
|
||||
private Visibility state;
|
||||
private ScheduledDelegate scheduledHide;
|
||||
|
||||
public override bool IsPresent => true;
|
||||
|
||||
public Visibility State
|
||||
{
|
||||
get => state;
|
||||
@ -201,14 +203,15 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
{
|
||||
Show();
|
||||
scheduledHide?.Cancel();
|
||||
return base.OnMouseDown(e);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool OnMouseUp(MouseUpEvent e)
|
||||
{
|
||||
Show();
|
||||
return base.OnMouseUp(e);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Hide() => State = Visibility.Hidden;
|
||||
|
@ -75,7 +75,7 @@ namespace osu.Game.Screens.Play
|
||||
return base.Invalidate(invalidation, source, shallPropagate);
|
||||
}
|
||||
|
||||
private Cached layout = new Cached();
|
||||
private readonly Cached layout = new Cached();
|
||||
private ScheduledDelegate scheduledCreate;
|
||||
|
||||
protected override void Update()
|
||||
|
@ -33,9 +33,12 @@ namespace osu.Game.Screens.Ranking.Pages
|
||||
private Container scoreContainer;
|
||||
private ScoreCounter scoreCounter;
|
||||
|
||||
private readonly ScoreInfo score;
|
||||
|
||||
public ScoreResultsPage(ScoreInfo score, WorkingBeatmap beatmap)
|
||||
: base(score, beatmap)
|
||||
{
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
private FillFlowContainer<DrawableScoreStatistic> statisticsContainer;
|
||||
@ -163,9 +166,16 @@ namespace osu.Game.Screens.Ranking.Pages
|
||||
Direction = FillDirection.Horizontal,
|
||||
LayoutDuration = 200,
|
||||
LayoutEasing = Easing.OutQuint
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
new ReplayDownloadButton(score)
|
||||
{
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Margin = new MarginPadding { Bottom = 10 },
|
||||
Size = new Vector2(50, 30),
|
||||
},
|
||||
};
|
||||
|
||||
statisticsContainer.ChildrenEnumerable = Score.Statistics.OrderByDescending(p => p.Key).Select(s => new DrawableScoreStatistic(s));
|
||||
|
@ -16,7 +16,6 @@ using osu.Game.Screens.Backgrounds;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Scoring;
|
||||
@ -254,18 +253,15 @@ namespace osu.Game.Screens.Ranking
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
new BackButton
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Action = this.Exit
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
foreach (var t in CreateResultPages())
|
||||
modeChangeButtons.AddItem(t);
|
||||
modeChangeButtons.Current.Value = modeChangeButtons.Items.FirstOrDefault();
|
||||
var pages = CreateResultPages();
|
||||
|
||||
foreach (var p in pages)
|
||||
modeChangeButtons.AddItem(p);
|
||||
|
||||
modeChangeButtons.Current.Value = pages.FirstOrDefault();
|
||||
|
||||
modeChangeButtons.Current.BindValueChanged(page =>
|
||||
{
|
||||
|
@ -20,8 +20,6 @@ namespace osu.Game.Screens
|
||||
{
|
||||
public class ScreenWhiteBox : OsuScreen
|
||||
{
|
||||
private readonly BackButton popButton;
|
||||
|
||||
private const double transition_time = 1000;
|
||||
|
||||
protected virtual IEnumerable<Type> PossibleChildren => null;
|
||||
@ -35,10 +33,6 @@ namespace osu.Game.Screens
|
||||
{
|
||||
base.OnEntering(last);
|
||||
|
||||
//only show the pop button if we are entered form another screen.
|
||||
if (last != null)
|
||||
popButton.Alpha = 1;
|
||||
|
||||
Alpha = 0;
|
||||
textContainer.Position = new Vector2(DrawSize.X / 16, 0);
|
||||
|
||||
@ -144,13 +138,6 @@ namespace osu.Game.Screens
|
||||
},
|
||||
}
|
||||
},
|
||||
popButton = new BackButton
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Alpha = 0,
|
||||
Action = this.Exit
|
||||
},
|
||||
childModeButtons = new FillFlowContainer
|
||||
{
|
||||
Direction = FillDirection.Vertical,
|
||||
|
@ -11,7 +11,6 @@ using osu.Game.Configuration;
|
||||
using osuTK.Input;
|
||||
using osu.Framework.MathUtils;
|
||||
using System.Diagnostics;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Caching;
|
||||
@ -27,6 +26,9 @@ namespace osu.Game.Screens.Select
|
||||
{
|
||||
public class BeatmapCarousel : OsuScrollContainer
|
||||
{
|
||||
private const float bleed_top = FilterControl.HEIGHT;
|
||||
private const float bleed_bottom = Footer.HEIGHT;
|
||||
|
||||
/// <summary>
|
||||
/// Triggered when the <see cref="BeatmapSets"/> loaded change and are completely loaded.
|
||||
/// </summary>
|
||||
@ -64,40 +66,35 @@ namespace osu.Game.Screens.Select
|
||||
public IEnumerable<BeatmapSetInfo> BeatmapSets
|
||||
{
|
||||
get => beatmapSets.Select(g => g.BeatmapSet);
|
||||
set => loadBeatmapSets(() => value);
|
||||
set => loadBeatmapSets(value);
|
||||
}
|
||||
|
||||
public void LoadBeatmapSetsFromManager(BeatmapManager manager) => loadBeatmapSets(manager.GetAllUsableBeatmapSetsEnumerable);
|
||||
|
||||
private void loadBeatmapSets(Func<IEnumerable<BeatmapSetInfo>> beatmapSets)
|
||||
private void loadBeatmapSets(IEnumerable<BeatmapSetInfo> beatmapSets)
|
||||
{
|
||||
CarouselRoot newRoot = new CarouselRoot(this);
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
beatmapSets().Select(createCarouselSet).Where(g => g != null).ForEach(newRoot.AddChild);
|
||||
newRoot.Filter(activeCriteria);
|
||||
beatmapSets.Select(createCarouselSet).Where(g => g != null).ForEach(newRoot.AddChild);
|
||||
newRoot.Filter(activeCriteria);
|
||||
|
||||
// preload drawables as the ctor overhead is quite high currently.
|
||||
var _ = newRoot.Drawables;
|
||||
}).ContinueWith(_ => Schedule(() =>
|
||||
{
|
||||
root = newRoot;
|
||||
scrollableContent.Clear(false);
|
||||
itemsCache.Invalidate();
|
||||
scrollPositionCache.Invalidate();
|
||||
// preload drawables as the ctor overhead is quite high currently.
|
||||
var _ = newRoot.Drawables;
|
||||
|
||||
Schedule(() =>
|
||||
{
|
||||
BeatmapSetsChanged?.Invoke();
|
||||
BeatmapSetsLoaded = true;
|
||||
});
|
||||
}));
|
||||
root = newRoot;
|
||||
scrollableContent.Clear(false);
|
||||
itemsCache.Invalidate();
|
||||
scrollPositionCache.Invalidate();
|
||||
|
||||
// Run on late scheduler want to ensure this runs after all pending UpdateBeatmapSet / RemoveBeatmapSet operations are run.
|
||||
SchedulerAfterChildren.Add(() =>
|
||||
{
|
||||
BeatmapSetsChanged?.Invoke();
|
||||
BeatmapSetsLoaded = true;
|
||||
});
|
||||
}
|
||||
|
||||
private readonly List<float> yPositions = new List<float>();
|
||||
private Cached itemsCache = new Cached();
|
||||
private Cached scrollPositionCache = new Cached();
|
||||
private readonly Cached itemsCache = new Cached();
|
||||
private readonly Cached scrollPositionCache = new Cached();
|
||||
|
||||
private readonly Container<DrawableCarouselItem> scrollableContent;
|
||||
|
||||
@ -125,63 +122,59 @@ namespace osu.Game.Screens.Select
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(permitNulls: true)]
|
||||
private void load(OsuConfigManager config)
|
||||
private void load(OsuConfigManager config, BeatmapManager beatmaps)
|
||||
{
|
||||
config.BindWith(OsuSetting.RandomSelectAlgorithm, RandomAlgorithm);
|
||||
config.BindWith(OsuSetting.SongSelectRightMouseScroll, RightClickScrollingEnabled);
|
||||
|
||||
RightClickScrollingEnabled.ValueChanged += enabled => RightMouseScrollbar = enabled.NewValue;
|
||||
RightClickScrollingEnabled.TriggerChange();
|
||||
|
||||
loadBeatmapSets(beatmaps.GetAllUsableBeatmapSetsEnumerable());
|
||||
}
|
||||
|
||||
public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet)
|
||||
public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() =>
|
||||
{
|
||||
Schedule(() =>
|
||||
{
|
||||
var existingSet = beatmapSets.FirstOrDefault(b => b.BeatmapSet.ID == beatmapSet.ID);
|
||||
var existingSet = beatmapSets.FirstOrDefault(b => b.BeatmapSet.ID == beatmapSet.ID);
|
||||
|
||||
if (existingSet == null)
|
||||
return;
|
||||
if (existingSet == null)
|
||||
return;
|
||||
|
||||
root.RemoveChild(existingSet);
|
||||
itemsCache.Invalidate();
|
||||
});
|
||||
|
||||
public void UpdateBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() =>
|
||||
{
|
||||
int? previouslySelectedID = null;
|
||||
CarouselBeatmapSet existingSet = beatmapSets.FirstOrDefault(b => b.BeatmapSet.ID == beatmapSet.ID);
|
||||
|
||||
// If the selected beatmap is about to be removed, store its ID so it can be re-selected if required
|
||||
if (existingSet?.State?.Value == CarouselItemState.Selected)
|
||||
previouslySelectedID = selectedBeatmap?.Beatmap.ID;
|
||||
|
||||
var newSet = createCarouselSet(beatmapSet);
|
||||
|
||||
if (existingSet != null)
|
||||
root.RemoveChild(existingSet);
|
||||
itemsCache.Invalidate();
|
||||
});
|
||||
}
|
||||
|
||||
public void UpdateBeatmapSet(BeatmapSetInfo beatmapSet)
|
||||
{
|
||||
Schedule(() =>
|
||||
if (newSet == null)
|
||||
{
|
||||
int? previouslySelectedID = null;
|
||||
CarouselBeatmapSet existingSet = beatmapSets.FirstOrDefault(b => b.BeatmapSet.ID == beatmapSet.ID);
|
||||
|
||||
// If the selected beatmap is about to be removed, store its ID so it can be re-selected if required
|
||||
if (existingSet?.State?.Value == CarouselItemState.Selected)
|
||||
previouslySelectedID = selectedBeatmap?.Beatmap.ID;
|
||||
|
||||
var newSet = createCarouselSet(beatmapSet);
|
||||
|
||||
if (existingSet != null)
|
||||
root.RemoveChild(existingSet);
|
||||
|
||||
if (newSet == null)
|
||||
{
|
||||
itemsCache.Invalidate();
|
||||
return;
|
||||
}
|
||||
|
||||
root.AddChild(newSet);
|
||||
|
||||
applyActiveCriteria(false, false);
|
||||
|
||||
//check if we can/need to maintain our current selection.
|
||||
if (previouslySelectedID != null)
|
||||
select((CarouselItem)newSet.Beatmaps.FirstOrDefault(b => b.Beatmap.ID == previouslySelectedID) ?? newSet);
|
||||
|
||||
itemsCache.Invalidate();
|
||||
Schedule(() => BeatmapSetsChanged?.Invoke());
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
root.AddChild(newSet);
|
||||
|
||||
applyActiveCriteria(false, false);
|
||||
|
||||
//check if we can/need to maintain our current selection.
|
||||
if (previouslySelectedID != null)
|
||||
select((CarouselItem)newSet.Beatmaps.FirstOrDefault(b => b.Beatmap.ID == previouslySelectedID) ?? newSet);
|
||||
|
||||
itemsCache.Invalidate();
|
||||
Schedule(() => BeatmapSetsChanged?.Invoke());
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Selects a given beatmap on the carousel.
|
||||
@ -346,6 +339,25 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
public bool AllowSelection = true;
|
||||
|
||||
/// <summary>
|
||||
/// Half the height of the visible content.
|
||||
/// <remarks>
|
||||
/// This is different from the height of <see cref="ScrollContainer{T}.displayableContent"/>, since
|
||||
/// the beatmap carousel bleeds into the <see cref="FilterControl"/> and the <see cref="Footer"/>
|
||||
/// </remarks>
|
||||
/// </summary>
|
||||
private float visibleHalfHeight => (DrawHeight + bleed_bottom + bleed_top) / 2;
|
||||
|
||||
/// <summary>
|
||||
/// The position of the lower visible bound with respect to the current scroll position.
|
||||
/// </summary>
|
||||
private float visibleBottomBound => Current + DrawHeight + bleed_bottom;
|
||||
|
||||
/// <summary>
|
||||
/// The position of the upper visible bound with respect to the current scroll position.
|
||||
/// </summary>
|
||||
private float visibleUpperBound => Current - bleed_top;
|
||||
|
||||
public void FlushPendingFilterOperations()
|
||||
{
|
||||
if (PendingFilter?.Completed == false)
|
||||
@ -422,6 +434,8 @@ namespace osu.Game.Screens.Select
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool ReceivePositionalInputAtSubTree(Vector2 screenSpacePos) => ReceivePositionalInputAt(screenSpacePos);
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
@ -432,17 +446,15 @@ namespace osu.Game.Screens.Select
|
||||
if (!scrollPositionCache.IsValid)
|
||||
updateScrollPosition();
|
||||
|
||||
float drawHeight = DrawHeight;
|
||||
|
||||
// Remove all items that should no longer be on-screen
|
||||
scrollableContent.RemoveAll(p => p.Y < Current - p.DrawHeight || p.Y > Current + drawHeight || !p.IsPresent);
|
||||
scrollableContent.RemoveAll(p => p.Y < visibleUpperBound - p.DrawHeight || p.Y > visibleBottomBound || !p.IsPresent);
|
||||
|
||||
// Find index range of all items that should be on-screen
|
||||
Trace.Assert(Items.Count == yPositions.Count);
|
||||
|
||||
int firstIndex = yPositions.BinarySearch(Current - DrawableCarouselItem.MAX_HEIGHT);
|
||||
int firstIndex = yPositions.BinarySearch(visibleUpperBound - DrawableCarouselItem.MAX_HEIGHT);
|
||||
if (firstIndex < 0) firstIndex = ~firstIndex;
|
||||
int lastIndex = yPositions.BinarySearch(Current + drawHeight);
|
||||
int lastIndex = yPositions.BinarySearch(visibleBottomBound);
|
||||
if (lastIndex < 0) lastIndex = ~lastIndex;
|
||||
|
||||
int notVisibleCount = 0;
|
||||
@ -494,9 +506,8 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
// Update externally controlled state of currently visible items
|
||||
// (e.g. x-offset and opacity).
|
||||
float halfHeight = drawHeight / 2;
|
||||
foreach (DrawableCarouselItem p in scrollableContent.Children)
|
||||
updateItem(p, halfHeight);
|
||||
updateItem(p);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
@ -550,7 +561,7 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
yPositions.Clear();
|
||||
|
||||
float currentY = DrawHeight / 2;
|
||||
float currentY = visibleHalfHeight;
|
||||
DrawableCarouselBeatmapSet lastSet = null;
|
||||
|
||||
scrollTarget = null;
|
||||
@ -583,7 +594,6 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
float? setY = null;
|
||||
if (!d.IsLoaded || beatmap.Alpha == 0) // can't use IsPresent due to DrawableCarouselItem override.
|
||||
// ReSharper disable once PossibleNullReferenceException (resharper broken?)
|
||||
setY = lastSet.Y + lastSet.DrawHeight + 5;
|
||||
|
||||
if (d.IsLoaded)
|
||||
@ -604,7 +614,7 @@ namespace osu.Game.Screens.Select
|
||||
currentY += d.DrawHeight + 5;
|
||||
}
|
||||
|
||||
currentY += DrawHeight / 2;
|
||||
currentY += visibleHalfHeight;
|
||||
scrollableContent.Height = currentY;
|
||||
|
||||
if (BeatmapSetsLoaded && (selectedBeatmapSet == null || selectedBeatmap == null || selectedBeatmapSet.State.Value != CarouselItemState.Selected))
|
||||
@ -645,18 +655,15 @@ namespace osu.Game.Screens.Select
|
||||
/// the current scroll position.
|
||||
/// </summary>
|
||||
/// <param name="p">The item to be updated.</param>
|
||||
/// <param name="halfHeight">Half the draw height of the carousel container.</param>
|
||||
private void updateItem(DrawableCarouselItem p, float halfHeight)
|
||||
private void updateItem(DrawableCarouselItem p)
|
||||
{
|
||||
var height = p.IsPresent ? p.DrawHeight : 0;
|
||||
|
||||
float itemDrawY = p.Position.Y - Current + height / 2;
|
||||
float dist = Math.Abs(1f - itemDrawY / halfHeight);
|
||||
float itemDrawY = p.Position.Y - visibleUpperBound + p.DrawHeight / 2;
|
||||
float dist = Math.Abs(1f - itemDrawY / visibleHalfHeight);
|
||||
|
||||
// Setting the origin position serves as an additive position on top of potential
|
||||
// local transformation we may want to apply (e.g. when a item gets selected, we
|
||||
// may want to smoothly transform it leftwards.)
|
||||
p.OriginPosition = new Vector2(-offsetX(dist, halfHeight), 0);
|
||||
p.OriginPosition = new Vector2(-offsetX(dist, visibleHalfHeight), 0);
|
||||
|
||||
// We are applying a multiplicative alpha (which is internally done by nesting an
|
||||
// additional container and setting that container's alpha) such that we can
|
||||
|
@ -41,6 +41,8 @@ namespace osu.Game.Screens.Select
|
||||
RelativeSizeAxes = Axes.X,
|
||||
OnFilter = (tab, mods) =>
|
||||
{
|
||||
Leaderboard.FilterMods = mods;
|
||||
|
||||
switch (tab)
|
||||
{
|
||||
case BeatmapDetailTab.Details:
|
||||
|
@ -25,22 +25,6 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private Bindable<BeatmapDetailTab> selectedTab;
|
||||
|
||||
private void invokeOnFilter()
|
||||
{
|
||||
OnFilter?.Invoke(tabs.Current.Value, modsCheckbox.Current.Value);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colour, OsuConfigManager config)
|
||||
{
|
||||
modsCheckbox.AccentColour = tabs.AccentColour = colour.YellowLight;
|
||||
|
||||
selectedTab = config.GetBindable<BeatmapDetailTab>(OsuSetting.BeatmapDetailTab);
|
||||
|
||||
tabs.Current.BindTo(selectedTab);
|
||||
tabs.Current.TriggerChange();
|
||||
}
|
||||
|
||||
public BeatmapDetailAreaTabControl()
|
||||
{
|
||||
Height = HEIGHT;
|
||||
@ -66,12 +50,31 @@ namespace osu.Game.Screens.Select
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomRight,
|
||||
Text = @"Mods",
|
||||
Alpha = 0,
|
||||
},
|
||||
};
|
||||
|
||||
tabs.Current.ValueChanged += _ => invokeOnFilter();
|
||||
modsCheckbox.Current.ValueChanged += _ => invokeOnFilter();
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colour, OsuConfigManager config)
|
||||
{
|
||||
modsCheckbox.AccentColour = tabs.AccentColour = colour.YellowLight;
|
||||
|
||||
selectedTab = config.GetBindable<BeatmapDetailTab>(OsuSetting.BeatmapDetailTab);
|
||||
|
||||
tabs.Current.BindTo(selectedTab);
|
||||
tabs.Current.TriggerChange();
|
||||
}
|
||||
|
||||
private void invokeOnFilter()
|
||||
{
|
||||
OnFilter?.Invoke(tabs.Current.Value, modsCheckbox.Current.Value);
|
||||
|
||||
modsCheckbox.FadeTo(tabs.Current.Value == BeatmapDetailTab.Details ? 0 : 1, 200, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
|
||||
public enum BeatmapDetailTab
|
||||
|
@ -35,7 +35,7 @@ namespace osu.Game.Screens.Select
|
||||
private readonly MetadataSection description, source, tags;
|
||||
private readonly Container failRetryContainer;
|
||||
private readonly FailRetryGraph failRetryGraph;
|
||||
private readonly DimmedLoadingAnimation loading;
|
||||
private readonly DimmedLoadingLayer loading;
|
||||
|
||||
private IAPIProvider api;
|
||||
|
||||
@ -156,10 +156,7 @@ namespace osu.Game.Screens.Select
|
||||
},
|
||||
},
|
||||
},
|
||||
loading = new DimmedLoadingAnimation
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
loading = new DimmedLoadingLayer(),
|
||||
};
|
||||
}
|
||||
|
||||
@ -365,35 +362,5 @@ namespace osu.Game.Screens.Select
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private class DimmedLoadingAnimation : VisibilityContainer
|
||||
{
|
||||
private readonly LoadingAnimation loading;
|
||||
|
||||
public DimmedLoadingAnimation()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black.Opacity(0.5f),
|
||||
},
|
||||
loading = new LoadingAnimation(),
|
||||
};
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
this.FadeIn(transition_duration, Easing.OutQuint);
|
||||
loading.Show();
|
||||
}
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
this.FadeOut(transition_duration, Easing.OutQuint);
|
||||
loading.Hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,8 +18,6 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
@ -143,6 +141,7 @@ namespace osu.Game.Screens.Select
|
||||
private readonly RulesetInfo ruleset;
|
||||
|
||||
public BufferedWedgeInfo(WorkingBeatmap beatmap, RulesetInfo userRuleset)
|
||||
: base(pixelSnapping: true)
|
||||
{
|
||||
this.beatmap = beatmap;
|
||||
ruleset = userRuleset ?? beatmap.BeatmapInfo.Ruleset;
|
||||
@ -154,7 +153,6 @@ namespace osu.Game.Screens.Select
|
||||
var beatmapInfo = beatmap.BeatmapInfo;
|
||||
var metadata = beatmapInfo.Metadata ?? beatmap.BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
|
||||
|
||||
PixelSnapping = true;
|
||||
CacheDrawnFrameBuffer = true;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
@ -289,14 +287,11 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
if (b?.HitObjects?.Any() == true)
|
||||
{
|
||||
HitObject lastObject = b.HitObjects.LastOrDefault();
|
||||
double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject?.StartTime ?? 0;
|
||||
|
||||
labels.Add(new InfoLabel(new BeatmapStatistic
|
||||
{
|
||||
Name = "Length",
|
||||
Icon = FontAwesome.Regular.Clock,
|
||||
Content = TimeSpan.FromMilliseconds(endTime - b.HitObjects.First().StartTime).ToString(@"m\:ss"),
|
||||
Content = TimeSpan.FromMilliseconds(b.BeatmapInfo.Length).ToString(@"m\:ss"),
|
||||
}));
|
||||
|
||||
labels.Add(new InfoLabel(new BeatmapStatistic
|
||||
|
@ -48,6 +48,12 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
case SortMode.DateAdded:
|
||||
return otherSet.BeatmapSet.DateAdded.CompareTo(BeatmapSet.DateAdded);
|
||||
|
||||
case SortMode.BPM:
|
||||
return BeatmapSet.MaxBPM.CompareTo(otherSet.BeatmapSet.MaxBPM);
|
||||
|
||||
case SortMode.Length:
|
||||
return BeatmapSet.MaxLength.CompareTo(otherSet.BeatmapSet.MaxLength);
|
||||
|
||||
case SortMode.Difficulty:
|
||||
return BeatmapSet.MaxStarDifficulty.CompareTo(otherSet.BeatmapSet.MaxStarDifficulty);
|
||||
}
|
||||
|
@ -1,18 +0,0 @@
|
||||
// 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.Screens;
|
||||
|
||||
namespace osu.Game.Screens.Select
|
||||
{
|
||||
public class EditSongSelect : SongSelect
|
||||
{
|
||||
protected override bool ShowFooter => false;
|
||||
|
||||
protected override bool OnStart()
|
||||
{
|
||||
this.Exit();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -14,7 +14,6 @@ using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Screens.Select.Filter;
|
||||
using Container = osu.Framework.Graphics.Containers.Container;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets;
|
||||
|
||||
@ -22,6 +21,8 @@ namespace osu.Game.Screens.Select
|
||||
{
|
||||
public class FilterControl : Container
|
||||
{
|
||||
public const float HEIGHT = 100;
|
||||
|
||||
public Action<FilterCriteria> FilterChanged;
|
||||
|
||||
private readonly OsuTabControl<SortMode> sortTabs;
|
||||
@ -120,7 +121,8 @@ namespace osu.Game.Screens.Select
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 24,
|
||||
Width = 0.5f,
|
||||
AutoSort = true
|
||||
AutoSort = true,
|
||||
Current = { Value = GroupMode.Title }
|
||||
},
|
||||
//spriteText = new OsuSpriteText
|
||||
//{
|
||||
@ -139,6 +141,7 @@ namespace osu.Game.Screens.Select
|
||||
Width = 0.5f,
|
||||
Height = 24,
|
||||
AutoSort = true,
|
||||
Current = { Value = SortMode.Title }
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -185,11 +188,5 @@ namespace osu.Game.Screens.Select
|
||||
}
|
||||
|
||||
private void updateCriteria() => FilterChanged?.Invoke(CreateCriteria());
|
||||
|
||||
protected override bool OnMouseDown(MouseDownEvent e) => true;
|
||||
|
||||
protected override bool OnMouseMove(MouseMoveEvent e) => true;
|
||||
|
||||
protected override bool OnClick(ClickEvent e) => true;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osuTK;
|
||||
@ -25,8 +24,6 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private const float padding = 80;
|
||||
|
||||
public Action OnBack;
|
||||
|
||||
private readonly FillFlowContainer<FooterButton> buttons;
|
||||
|
||||
private readonly List<OverlayContainer> overlays = new List<OverlayContainer>();
|
||||
@ -83,12 +80,6 @@ namespace osu.Game.Screens.Select
|
||||
Height = 3,
|
||||
Position = new Vector2(0, -3),
|
||||
},
|
||||
new BackButton
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Action = () => OnBack?.Invoke()
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
@ -103,7 +94,7 @@ namespace osu.Game.Screens.Select
|
||||
buttons = new FillFlowContainer<FooterButton>
|
||||
{
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(0.2f, 0),
|
||||
Spacing = new Vector2(-FooterButton.SHEAR_WIDTH, 0),
|
||||
AutoSizeAxes = Axes.Both,
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,9 @@ namespace osu.Game.Screens.Select
|
||||
{
|
||||
public class FooterButton : OsuClickableContainer
|
||||
{
|
||||
private static readonly Vector2 shearing = new Vector2(0.15f, 0);
|
||||
public static readonly float SHEAR_WIDTH = 7.5f;
|
||||
|
||||
protected static readonly Vector2 SHEAR = new Vector2(SHEAR_WIDTH / Footer.HEIGHT, 0);
|
||||
|
||||
public string Text
|
||||
{
|
||||
@ -59,37 +61,35 @@ namespace osu.Game.Screens.Select
|
||||
private readonly Box box;
|
||||
private readonly Box light;
|
||||
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => box.ReceivePositionalInputAt(screenSpacePos);
|
||||
|
||||
public FooterButton()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Shear = SHEAR;
|
||||
Children = new Drawable[]
|
||||
{
|
||||
TextContainer = new Container
|
||||
{
|
||||
Size = new Vector2(100, 50),
|
||||
Child = SpriteText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
}
|
||||
},
|
||||
box = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Shear = shearing,
|
||||
EdgeSmoothness = new Vector2(2, 0),
|
||||
Colour = Color4.White,
|
||||
Alpha = 0,
|
||||
},
|
||||
light = new Box
|
||||
{
|
||||
Shear = shearing,
|
||||
Height = 4,
|
||||
EdgeSmoothness = new Vector2(2, 0),
|
||||
RelativeSizeAxes = Axes.X,
|
||||
},
|
||||
TextContainer = new Container
|
||||
{
|
||||
Size = new Vector2(100 - SHEAR_WIDTH, 50),
|
||||
Shear = -SHEAR,
|
||||
Child = SpriteText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -9,22 +9,30 @@ using osu.Game.Rulesets.Mods;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Game.Graphics;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Screens.Select
|
||||
{
|
||||
public class FooterButtonMods : FooterButton
|
||||
public class FooterButtonMods : FooterButton, IHasCurrentValue<IReadOnlyList<Mod>>
|
||||
{
|
||||
public FooterButtonMods(Bindable<IReadOnlyList<Mod>> mods)
|
||||
public Bindable<IReadOnlyList<Mod>> Current
|
||||
{
|
||||
FooterModDisplay modDisplay;
|
||||
get => modDisplay.Current;
|
||||
set => modDisplay.Current = value;
|
||||
}
|
||||
|
||||
private readonly FooterModDisplay modDisplay;
|
||||
|
||||
public FooterButtonMods()
|
||||
{
|
||||
Add(new Container
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Shear = -SHEAR,
|
||||
Child = modDisplay = new FooterModDisplay
|
||||
{
|
||||
DisplayUnrankedText = false,
|
||||
@ -33,9 +41,6 @@ namespace osu.Game.Screens.Select
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Margin = new MarginPadding { Left = 70 }
|
||||
});
|
||||
|
||||
if (mods != null)
|
||||
modDisplay.Current = mods;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
@ -12,7 +12,7 @@ namespace osu.Game.Screens.Select
|
||||
public ImportFromStablePopup(Action importFromStable)
|
||||
{
|
||||
HeaderText = @"You have no beatmaps!";
|
||||
BodyText = "An existing copy of osu! was found, though.\nWould you like to import your beatmaps (and skins)?";
|
||||
BodyText = "An existing copy of osu! was found, though.\nWould you like to import your beatmaps, skins and scores?";
|
||||
|
||||
Icon = FontAwesome.Solid.Plane;
|
||||
|
||||
|
@ -11,6 +11,7 @@ using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests;
|
||||
using osu.Game.Online.Leaderboards;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Scoring;
|
||||
|
||||
namespace osu.Game.Screens.Select.Leaderboards
|
||||
@ -36,12 +37,34 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
}
|
||||
}
|
||||
|
||||
private bool filterMods;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to apply the game's currently selected mods as a filter when retrieving scores.
|
||||
/// </summary>
|
||||
public bool FilterMods
|
||||
{
|
||||
get => filterMods;
|
||||
set
|
||||
{
|
||||
if (value == filterMods)
|
||||
return;
|
||||
|
||||
filterMods = value;
|
||||
|
||||
UpdateScores();
|
||||
}
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private ScoreManager scoreManager { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IBindable<RulesetInfo> ruleset { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IBindable<IReadOnlyList<Mod>> mods { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
|
||||
@ -49,30 +72,68 @@ namespace osu.Game.Screens.Select.Leaderboards
|
||||
private void load()
|
||||
{
|
||||
ruleset.ValueChanged += _ => UpdateScores();
|
||||
mods.ValueChanged += _ =>
|
||||
{
|
||||
if (filterMods)
|
||||
UpdateScores();
|
||||
};
|
||||
}
|
||||
|
||||
protected override bool IsOnlineScope => Scope != BeatmapLeaderboardScope.Local;
|
||||
|
||||
protected override APIRequest FetchScores(Action<IEnumerable<ScoreInfo>> scoresCallback)
|
||||
{
|
||||
if (Scope == BeatmapLeaderboardScope.Local)
|
||||
{
|
||||
Scores = scoreManager.QueryScores(s => !s.DeletePending && s.Beatmap.ID == Beatmap.ID).OrderByDescending(s => s.TotalScore).ToArray();
|
||||
var scores = scoreManager
|
||||
.QueryScores(s => !s.DeletePending && s.Beatmap.ID == Beatmap.ID && s.Ruleset.ID == ruleset.Value.ID);
|
||||
|
||||
if (filterMods && !mods.Value.Any())
|
||||
{
|
||||
// we need to filter out all scores that have any mods to get all local nomod scores
|
||||
scores = scores.Where(s => !s.Mods.Any());
|
||||
}
|
||||
else if (filterMods)
|
||||
{
|
||||
// otherwise find all the scores that have *any* of the currently selected mods (similar to how web applies mod filters)
|
||||
// we're creating and using a string list representation of selected mods so that it can be translated into the DB query itself
|
||||
var selectedMods = mods.Value.Select(m => m.Acronym);
|
||||
scores = scores.Where(s => s.Mods.Any(m => selectedMods.Contains(m.Acronym)));
|
||||
}
|
||||
|
||||
Scores = scores.OrderByDescending(s => s.TotalScore).ToArray();
|
||||
PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Beatmap?.OnlineBeatmapID == null)
|
||||
if (api?.IsLoggedIn != true)
|
||||
{
|
||||
PlaceholderState = PlaceholderState.NotLoggedIn;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Beatmap?.OnlineBeatmapID == null || Beatmap?.Status <= BeatmapSetOnlineStatus.Pending)
|
||||
{
|
||||
PlaceholderState = PlaceholderState.Unavailable;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Scope != BeatmapLeaderboardScope.Global && !api.LocalUser.Value.IsSupporter)
|
||||
if (!api.LocalUser.Value.IsSupporter && (Scope != BeatmapLeaderboardScope.Global || filterMods))
|
||||
{
|
||||
PlaceholderState = PlaceholderState.NotSupporter;
|
||||
return null;
|
||||
}
|
||||
|
||||
var req = new GetScoresRequest(Beatmap, ruleset.Value ?? Beatmap.Ruleset, Scope);
|
||||
IReadOnlyList<Mod> requestMods = null;
|
||||
|
||||
if (filterMods && !mods.Value.Any())
|
||||
// add nomod for the request
|
||||
requestMods = new Mod[] { new ModNoMod() };
|
||||
else if (filterMods)
|
||||
requestMods = mods.Value;
|
||||
|
||||
var req = new GetScoresRequest(Beatmap, ruleset.Value ?? Beatmap.Ruleset, Scope, requestMods);
|
||||
|
||||
req.Success += r => scoresCallback?.Invoke(r.Scores);
|
||||
|
||||
|
@ -1,13 +1,22 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace osu.Game.Screens.Select.Leaderboards
|
||||
{
|
||||
public enum BeatmapLeaderboardScope
|
||||
{
|
||||
[Description("Local Ranking")]
|
||||
Local,
|
||||
|
||||
[Description("Country Ranking")]
|
||||
Country,
|
||||
|
||||
[Description("Global Ranking")]
|
||||
Global,
|
||||
|
||||
[Description("Friend Ranking")]
|
||||
Friend,
|
||||
}
|
||||
}
|
||||
|
@ -110,8 +110,7 @@ namespace osu.Game.Screens.Select.Options
|
||||
HotKey = hotkey
|
||||
};
|
||||
|
||||
buttonsContainer.Add(button);
|
||||
buttonsContainer.SetLayoutPosition(button, depth);
|
||||
buttonsContainer.Insert((int)depth, button);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,10 +34,12 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Game.Scoring;
|
||||
|
||||
namespace osu.Game.Screens.Select
|
||||
{
|
||||
public abstract class SongSelect : OsuScreen
|
||||
public abstract class SongSelect : OsuScreen, IKeyBindingHandler<GlobalAction>
|
||||
{
|
||||
private static readonly Vector2 wedged_container_size = new Vector2(0.5f, 245);
|
||||
|
||||
@ -85,6 +87,9 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
private readonly Bindable<RulesetInfo> decoupledRuleset = new Bindable<RulesetInfo>();
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private MusicController music { get; set; }
|
||||
|
||||
[Cached]
|
||||
[Cached(Type = typeof(IBindable<IReadOnlyList<Mod>>))]
|
||||
private readonly Bindable<IReadOnlyList<Mod>> mods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>()); // Bound to the game's mods, but is not reset on exiting
|
||||
@ -116,7 +121,7 @@ namespace osu.Game.Screens.Select
|
||||
Size = new Vector2(wedged_container_size.X, 1),
|
||||
Padding = new MarginPadding
|
||||
{
|
||||
Bottom = 50,
|
||||
Bottom = Footer.HEIGHT,
|
||||
Top = wedged_container_size.Y + left_area_padding,
|
||||
Left = left_area_padding,
|
||||
Right = left_area_padding * 2,
|
||||
@ -142,20 +147,29 @@ namespace osu.Game.Screens.Select
|
||||
Width = 0.5f,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
Carousel = new BeatmapCarousel
|
||||
new Container
|
||||
{
|
||||
Masking = false,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Size = new Vector2(1 - wedged_container_size.X, 1),
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
SelectionChanged = updateSelectedBeatmap,
|
||||
BeatmapSetsChanged = carouselBeatmapsLoaded,
|
||||
Padding = new MarginPadding
|
||||
{
|
||||
Top = FilterControl.HEIGHT,
|
||||
Bottom = Footer.HEIGHT
|
||||
},
|
||||
Child = Carousel = new BeatmapCarousel
|
||||
{
|
||||
Masking = false,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Size = new Vector2(1 - wedged_container_size.X, 1),
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
SelectionChanged = updateSelectedBeatmap,
|
||||
BeatmapSetsChanged = carouselBeatmapsLoaded,
|
||||
},
|
||||
},
|
||||
FilterControl = new FilterControl
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 100,
|
||||
Height = FilterControl.HEIGHT,
|
||||
FilterChanged = c => Carousel.Filter(c),
|
||||
Background = { Width = 2 },
|
||||
Exit = () =>
|
||||
@ -186,31 +200,27 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
if (ShowFooter)
|
||||
{
|
||||
AddInternal(FooterPanels = new Container
|
||||
AddRangeInternal(new[]
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Margin = new MarginPadding
|
||||
{
|
||||
Bottom = Footer.HEIGHT,
|
||||
},
|
||||
});
|
||||
AddInternal(Footer = new Footer
|
||||
{
|
||||
OnBack = ExitFromBack,
|
||||
});
|
||||
|
||||
FooterPanels.AddRange(new Drawable[]
|
||||
{
|
||||
BeatmapOptions = new BeatmapOptionsOverlay(),
|
||||
ModSelect = new ModSelectOverlay
|
||||
FooterPanels = new Container
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
}
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Margin = new MarginPadding { Bottom = Footer.HEIGHT },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
BeatmapOptions = new BeatmapOptionsOverlay(),
|
||||
ModSelect = new ModSelectOverlay
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Anchor = Anchor.BottomCentre,
|
||||
}
|
||||
}
|
||||
},
|
||||
Footer = new Footer()
|
||||
});
|
||||
}
|
||||
|
||||
@ -218,13 +228,11 @@ namespace osu.Game.Screens.Select
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuColour colours, SkinManager skins)
|
||||
private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuColour colours, SkinManager skins, ScoreManager scores)
|
||||
{
|
||||
mods.BindTo(Mods);
|
||||
|
||||
if (Footer != null)
|
||||
{
|
||||
Footer.AddButton(new FooterButtonMods(mods), ModSelect);
|
||||
Footer.AddButton(new FooterButtonMods { Current = mods }, ModSelect);
|
||||
Footer.AddButton(new FooterButtonRandom { Action = triggerRandom });
|
||||
Footer.AddButton(new FooterButtonOptions(), BeatmapOptions);
|
||||
|
||||
@ -247,23 +255,28 @@ namespace osu.Game.Screens.Select
|
||||
sampleChangeBeatmap = audio.Samples.Get(@"SongSelect/select-expand");
|
||||
SampleConfirm = audio.Samples.Get(@"SongSelect/confirm-selection");
|
||||
|
||||
Carousel.LoadBeatmapSetsFromManager(this.beatmaps);
|
||||
|
||||
if (dialogOverlay != null)
|
||||
{
|
||||
Schedule(() =>
|
||||
{
|
||||
// if we have no beatmaps but osu-stable is found, let's prompt the user to import.
|
||||
if (!beatmaps.GetAllUsableBeatmapSets().Any() && beatmaps.StableInstallationAvailable)
|
||||
if (!beatmaps.GetAllUsableBeatmapSetsEnumerable().Any() && beatmaps.StableInstallationAvailable)
|
||||
dialogOverlay.Push(new ImportFromStablePopup(() =>
|
||||
{
|
||||
Task.Run(beatmaps.ImportFromStableAsync);
|
||||
Task.Run(beatmaps.ImportFromStableAsync).ContinueWith(_ => scores.ImportFromStableAsync(), TaskContinuationOptions.OnlyOnRanToCompletion);
|
||||
Task.Run(skins.ImportFromStableAsync);
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
mods.BindTo(Mods);
|
||||
}
|
||||
|
||||
private DependencyContainer dependencies;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
@ -277,17 +290,6 @@ namespace osu.Game.Screens.Select
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
protected virtual void ExitFromBack()
|
||||
{
|
||||
if (ModSelect.State.Value == Visibility.Visible)
|
||||
{
|
||||
ModSelect.Hide();
|
||||
return;
|
||||
}
|
||||
|
||||
this.Exit();
|
||||
}
|
||||
|
||||
public void Edit(BeatmapInfo beatmap = null)
|
||||
{
|
||||
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap ?? beatmapNoDebounce);
|
||||
@ -341,7 +343,7 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
if (this.IsCurrentScreen() && !Carousel.SelectBeatmap(e.NewValue?.BeatmapInfo, false))
|
||||
// If selecting new beatmap without bypassing filters failed, there's possibly a ruleset mismatch
|
||||
if (e.NewValue?.BeatmapInfo?.Ruleset != null && e.NewValue.BeatmapInfo.Ruleset != decoupledRuleset.Value)
|
||||
if (e.NewValue?.BeatmapInfo?.Ruleset != null && !e.NewValue.BeatmapInfo.Ruleset.Equals(decoupledRuleset.Value))
|
||||
{
|
||||
Ruleset.Value = e.NewValue.BeatmapInfo.Ruleset;
|
||||
Carousel.SelectBeatmap(e.NewValue.BeatmapInfo);
|
||||
@ -518,6 +520,12 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
public override bool OnExiting(IScreen next)
|
||||
{
|
||||
if (ModSelect.State.Value == Visibility.Visible)
|
||||
{
|
||||
ModSelect.Hide();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (base.OnExiting(next))
|
||||
return true;
|
||||
|
||||
@ -579,14 +587,14 @@ namespace osu.Game.Screens.Select
|
||||
{
|
||||
Track track = Beatmap.Value.Track;
|
||||
|
||||
if (!track.IsRunning || restart)
|
||||
if ((!track.IsRunning || restart) && music?.IsUserPaused != true)
|
||||
{
|
||||
track.RestartPoint = Beatmap.Value.Metadata.PreviewTime;
|
||||
track.Restart();
|
||||
}
|
||||
}
|
||||
|
||||
private void onBeatmapSetAdded(BeatmapSetInfo s, bool existing) => Carousel.UpdateBeatmapSet(s);
|
||||
private void onBeatmapSetAdded(BeatmapSetInfo s) => Carousel.UpdateBeatmapSet(s);
|
||||
private void onBeatmapSetRemoved(BeatmapSetInfo s) => Carousel.RemoveBeatmapSet(s);
|
||||
private void onBeatmapRestored(BeatmapInfo b) => Carousel.UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID));
|
||||
private void onBeatmapHidden(BeatmapInfo b) => Carousel.UpdateBeatmapSet(beatmaps.QueryBeatmapSet(s => s.ID == b.BeatmapSetInfoID));
|
||||
@ -649,7 +657,7 @@ namespace osu.Game.Screens.Select
|
||||
Schedule(() => BeatmapDetails.Leaderboard.RefreshScores())));
|
||||
}
|
||||
|
||||
public override bool OnPressed(GlobalAction action)
|
||||
public virtual bool OnPressed(GlobalAction action)
|
||||
{
|
||||
if (!this.IsCurrentScreen()) return false;
|
||||
|
||||
@ -660,9 +668,11 @@ namespace osu.Game.Screens.Select
|
||||
return true;
|
||||
}
|
||||
|
||||
return base.OnPressed(action);
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool OnReleased(GlobalAction action) => action == GlobalAction.Select;
|
||||
|
||||
protected override bool OnKeyDown(KeyDownEvent e)
|
||||
{
|
||||
if (e.Repeat) return false;
|
||||
|
21
osu.Game/Screens/StartupScreen.cs
Normal file
21
osu.Game/Screens/StartupScreen.cs
Normal file
@ -0,0 +1,21 @@
|
||||
// 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.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Screens
|
||||
{
|
||||
/// <summary>
|
||||
/// A screen which is shown once as part of the startup procedure.
|
||||
/// </summary>
|
||||
public abstract class StartupScreen : OsuScreen
|
||||
{
|
||||
public override bool AllowBackButton => false;
|
||||
|
||||
public override bool HideOverlaysOnEnter => true;
|
||||
|
||||
public override bool CursorVisible => false;
|
||||
|
||||
public override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user