Merge branch 'master' into roompanel

This commit is contained in:
Dean Herbert
2017-04-25 12:53:49 +09:00
committed by GitHub
534 changed files with 18003 additions and 6876 deletions

View File

@ -3,10 +3,8 @@
using System;
using System.Threading;
using osu.Framework.Allocation;
using osu.Framework.Screens;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Input;
using OpenTK;
@ -28,25 +26,17 @@ namespace osu.Game.Screens
return false;
}
private Framework.Game game;
[BackgroundDependencyLoader]
private void load(Framework.Game game)
{
this.game = game;
}
public override bool Push(Screen screen)
{
// When trying to push a non-loaded GameMode, load it asynchronously and re-invoke Push
// When trying to push a non-loaded screen, load it asynchronously and re-invoke Push
// once it's done.
if (screen.LoadState == LoadState.NotLoaded)
{
screen.LoadAsync(game, d => Push((BackgroundScreen)d));
LoadComponentAsync(screen, d => Push((BackgroundScreen)d));
return true;
}
// Make sure the in-progress loading is complete before pushing the GameMode.
// Make sure the in-progress loading is complete before pushing the screen.
while (screen.LoadState < LoadState.Loaded)
Thread.Sleep(1);

View File

@ -2,8 +2,8 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using OpenTK;
using osu.Framework.Graphics.Transforms;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Backgrounds;
@ -32,7 +32,7 @@ namespace osu.Game.Screens.Backgrounds
{
var newBackground = beatmap == null ? new Background(@"Backgrounds/bg1") : new BeatmapBackground(beatmap);
newBackground.LoadAsync(Game, delegate
LoadComponentAsync(newBackground, delegate
{
float newDepth = 0;
if (background != null)
@ -72,7 +72,7 @@ namespace osu.Game.Screens.Backgrounds
private class BeatmapBackground : Background
{
private WorkingBeatmap beatmap;
private readonly WorkingBeatmap beatmap;
public BeatmapBackground(WorkingBeatmap beatmap)
{

View File

@ -1,25 +1,38 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using OpenTK.Graphics;
using osu.Framework.Screens;
using osu.Game.Screens.Backgrounds;
using OpenTK.Graphics;
using osu.Game.Screens.Select;
namespace osu.Game.Screens.Edit
{
internal class Editor : ScreenWhiteBox
{
protected override IEnumerable<Type> PossibleChildren => new[] { typeof(EditSongSelect) };
protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4");
protected override void OnResuming(Screen last)
{
Beatmap?.Track?.Stop();
base.OnResuming(last);
}
protected override void OnEntering(Screen last)
{
base.OnEntering(last);
Background.Schedule(() => Background.FadeColour(Color4.DarkGray, 500));
Beatmap?.Track?.Stop();
}
protected override bool OnExiting(Screen next)
{
Background.Schedule(() => Background.FadeColour(Color4.White, 500));
Beatmap?.Track?.Start();
return base.OnExiting(next);
}
}

View File

@ -20,9 +20,9 @@ namespace osu.Game.Screens
private void load(OsuGame game)
{
if (game.IsDeployedBuild)
new Disclaimer().LoadAsync(game, d => Push((Screen)d));
LoadComponentAsync(new Disclaimer(), d => Push((Screen)d));
else
new Intro().LoadAsync(game, d => Push((Screen)d));
LoadComponentAsync(new Intro(), d => Push((Screen)d));
}
}
}

View File

@ -26,19 +26,16 @@ namespace osu.Game.Screens.Menu
/// </summary>
public class Button : Container, IStateful<ButtonState>
{
private Container iconText;
private Container box;
private Box boxHoverLayer;
private TextAwesome icon;
private string internalName;
private Action clickAction;
private Key triggerKey;
private readonly Container iconText;
private readonly Container box;
private readonly Box boxHoverLayer;
private readonly TextAwesome icon;
private readonly string internalName;
private readonly Action clickAction;
private readonly Key triggerKey;
private SampleChannel sampleClick;
public override bool Contains(Vector2 screenSpacePos)
{
return box.Contains(screenSpacePos);
}
protected override bool InternalContains(Vector2 screenSpacePos) => box.Contains(screenSpacePos);
public Button(string text, string internalName, FontAwesome symbol, Color4 colour, Action clickAction = null, float extraWidth = 0, Key triggerKey = Key.Unknown)
{
@ -135,7 +132,7 @@ namespace osu.Game.Screens.Menu
icon.ScaleTo(1, 500, EasingTypes.OutElasticHalf);
double offset = 0; //(1 - Game.Audio.SyncBeatProgress) * duration;
const double offset = 0; //(1 - Game.Audio.SyncBeatProgress) * duration;
double startTime = Time.Current + offset;
icon.RotateTo(10, offset, EasingTypes.InOutSine);

View File

@ -9,7 +9,6 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Overlays.Toolbar;
@ -32,7 +31,7 @@ namespace osu.Game.Screens.Menu
private Toolbar toolbar;
private FlowContainerWithOrigin buttonFlow;
private readonly FlowContainerWithOrigin buttonFlow;
//todo: make these non-internal somehow.
internal const float BUTTON_AREA_HEIGHT = 100;
@ -41,16 +40,16 @@ namespace osu.Game.Screens.Menu
public const int EXIT_DELAY = 3000;
private OsuLogo osuLogo;
private Drawable iconFacade;
private Container buttonArea;
private Box buttonAreaBackground;
private readonly OsuLogo osuLogo;
private readonly Drawable iconFacade;
private readonly Container buttonArea;
private readonly Box buttonAreaBackground;
private Button backButton;
private Button settingsButton;
private readonly Button backButton;
private readonly Button settingsButton;
private List<Button> buttonsTopLevel = new List<Button>();
private List<Button> buttonsPlay = new List<Button>();
private readonly List<Button> buttonsTopLevel = new List<Button>();
private readonly List<Button> buttonsPlay = new List<Button>();
public ButtonSystem()
{

View File

@ -16,11 +16,13 @@ namespace osu.Game.Screens.Menu
internal class Disclaimer : OsuScreen
{
private Intro intro;
private TextAwesome icon;
private readonly TextAwesome icon;
private Color4 iconColour;
internal override bool ShowOverlays => false;
internal override bool HasLocalCursorDisplayed => true;
public Disclaimer()
{
ValidForResume = false;
@ -86,9 +88,9 @@ namespace osu.Game.Screens.Menu
}
[BackgroundDependencyLoader]
private void load(OsuGame game, OsuColour colours)
private void load(OsuColour colours)
{
(intro = new Intro()).LoadAsync(game);
LoadComponentAsync(intro = new Intro());
iconColour = colours.Yellow;
}

View File

@ -7,7 +7,6 @@ using osu.Framework.Audio.Sample;
using osu.Framework.Audio.Track;
using osu.Framework.Screens;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Transforms;
using osu.Game.Graphics.Containers;
using osu.Game.Screens.Backgrounds;
using OpenTK.Graphics;
@ -16,7 +15,7 @@ namespace osu.Game.Screens.Menu
{
public class Intro : OsuScreen
{
private OsuLogo logo;
private readonly OsuLogo logo;
/// <summary>
/// Whether we have loaded the menu previously.
@ -28,6 +27,8 @@ namespace osu.Game.Screens.Menu
private SampleChannel seeya;
private Track bgm;
internal override bool HasLocalCursorDisplayed => true;
internal override bool ShowOverlays => false;
protected override BackgroundScreen CreateBackground() => new BackgroundScreenEmpty();
@ -75,7 +76,7 @@ namespace osu.Game.Screens.Menu
{
bgm.Start();
(mainMenu = new MainMenu()).LoadAsync(Game);
LoadComponentAsync(mainMenu = new MainMenu());
Scheduler.AddDelayed(delegate
{
@ -111,12 +112,12 @@ namespace osu.Game.Screens.Menu
//we also handle the exit transition.
seeya.Play();
double fadeOutTime = 2000;
const double fade_out_time = 2000;
Scheduler.AddDelayed(Exit, fadeOutTime);
Scheduler.AddDelayed(Exit, fade_out_time);
//don't want to fade out completely else we will stop running updates and shit will hit the fan.
Game.FadeTo(0.01f, fadeOutTime);
Game.FadeTo(0.01f, fade_out_time);
base.OnResuming(last);
}

View File

@ -2,14 +2,13 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Screens;
using osu.Framework.Screens.Testing;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Screens;
using osu.Game.Graphics.Containers;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Charts;
using osu.Game.Screens.Direct;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Multiplayer;
using OpenTK;
using osu.Game.Screens.Select;
@ -21,11 +20,12 @@ namespace osu.Game.Screens.Menu
{
public class MainMenu : OsuScreen
{
private ButtonSystem buttons;
private readonly ButtonSystem buttons;
internal override bool ShowOverlays => buttons.State != MenuState.Initial;
private BackgroundScreen background;
private readonly BackgroundScreen background;
private Screen songSelect;
protected override BackgroundScreen CreateBackground() => background;
@ -44,10 +44,9 @@ namespace osu.Game.Screens.Menu
{
OnChart = delegate { Push(new ChartListing()); },
OnDirect = delegate { Push(new OnlineListing()); },
OnEdit = delegate { Push(new EditSongSelect()); },
OnSolo = delegate { Push(new PlaySongSelect()); },
OnEdit = delegate { Push(new Editor()); },
OnSolo = delegate { Push(consumeSongSelect()); },
OnMulti = delegate { Push(new Lobby()); },
OnTest = delegate { Push(new TestBrowser()); },
OnExit = delegate { Exit(); },
}
}
@ -58,9 +57,24 @@ namespace osu.Game.Screens.Menu
[BackgroundDependencyLoader]
private void load(OsuGame game)
{
background.LoadAsync(game);
LoadComponentAsync(background);
buttons.OnSettings = game.ToggleOptions;
preloadSongSelect();
}
private void preloadSongSelect()
{
if (songSelect == null)
LoadComponentAsync(songSelect = new PlaySongSelect());
}
private Screen consumeSongSelect()
{
var s = songSelect;
songSelect = null;
return s;
}
protected override void OnEntering(Screen last)
@ -85,6 +99,9 @@ namespace osu.Game.Screens.Menu
{
base.OnResuming(last);
//we may have consumed our preloaded instance, so let's make another.
preloadSongSelect();
const float length = 300;
buttons.State = MenuState.TopLevel;

View File

@ -9,7 +9,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
@ -25,22 +24,22 @@ namespace osu.Game.Screens.Menu
{
public Color4 OsuPink = OsuColour.FromHex(@"e967a1");
private Sprite logo;
private CircularContainer logoContainer;
private Container logoBounceContainer;
private Container logoHoverContainer;
private readonly Sprite logo;
private readonly CircularContainer logoContainer;
private readonly Container logoBounceContainer;
private readonly Container logoHoverContainer;
private SampleChannel sampleClick;
private Container colourAndTriangles;
private readonly Container colourAndTriangles;
public Action Action;
public float SizeForFlow => logo == null ? 0 : logo.DrawSize.X * logo.Scale.X * logoBounceContainer.Scale.X * logoHoverContainer.Scale.X * 0.78f;
private Sprite ripple;
private readonly Sprite ripple;
private Container rippleContainer;
private readonly Container rippleContainer;
public bool Triangles
{
@ -50,10 +49,7 @@ namespace osu.Game.Screens.Menu
}
}
public override bool Contains(Vector2 screenSpacePos)
{
return logoContainer.Contains(screenSpacePos);
}
protected override bool InternalContains(Vector2 screenSpacePos) => logoContainer.Contains(screenSpacePos);
public bool Ripple
{
@ -65,7 +61,7 @@ namespace osu.Game.Screens.Menu
}
public bool Interactive = true;
private Box flashLayer;
private readonly Box flashLayer;
public OsuLogo()
{
@ -94,8 +90,10 @@ namespace osu.Game.Screens.Menu
logoContainer = new CircularContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Scale = new Vector2(0.8f),
Masking = true,
Children = new Drawable[]
{
colourAndTriangles = new Container
@ -196,8 +194,6 @@ namespace osu.Game.Screens.Menu
return true;
}
protected override bool OnDragStart(InputState state) => true;
protected override bool OnClick(InputState state)
{
if (!Interactive) return false;

View File

@ -1,11 +1,11 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Graphics.Containers;
namespace osu.Game.Screens
@ -15,7 +15,7 @@ namespace osu.Game.Screens
internal BackgroundScreen Background { get; private set; }
/// <summary>
/// Override to create a BackgroundMode for the current GameMode.
/// Override to create a BackgroundMode for the current screen.
/// Note that the instance created may not be the used instance if it matches the BackgroundMode equality clause.
/// </summary>
protected virtual BackgroundScreen CreateBackground() => null;
@ -24,8 +24,14 @@ namespace osu.Game.Screens
protected new OsuGameBase Game => base.Game as OsuGameBase;
internal virtual bool HasLocalCursorDisplayed => false;
internal virtual bool AllowRulesetChange => true;
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
private readonly Bindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
public WorkingBeatmap Beatmap
{
get
@ -38,13 +44,8 @@ namespace osu.Game.Screens
}
}
private void beatmap_ValueChanged(object sender, EventArgs e)
{
OnBeatmapChanged(beatmap.Value);
}
[BackgroundDependencyLoader(permitNulls: true)]
private void load(OsuGameBase game)
private void load(OsuGameBase game, OsuGame osuGame)
{
if (game != null)
{
@ -55,12 +56,24 @@ namespace osu.Game.Screens
beatmap.Value = localMap;
}
beatmap.ValueChanged += beatmap_ValueChanged;
beatmap.ValueChanged += OnBeatmapChanged;
if (osuGame != null)
ruleset.BindTo(osuGame.Ruleset);
}
/// <summary>
/// The global Beatmap was changed.
/// </summary>
protected virtual void OnBeatmapChanged(WorkingBeatmap beatmap)
{
}
protected override void Update()
{
if (!IsCurrentScreen) return;
ruleset.Disabled = !AllowRulesetChange;
}
protected override void OnEntering(Screen last)
@ -69,6 +82,8 @@ namespace osu.Game.Screens
BackgroundScreen bg = CreateBackground();
OnBeatmapChanged(Beatmap);
if (lastOsu?.Background != null)
{
if (bg == null || lastOsu.Background.Equals(bg))
@ -101,7 +116,7 @@ namespace osu.Game.Screens
if (Background != null && !Background.Equals(nextOsu?.Background))
{
if (nextOsu != null)
//We need to use MakeCurrent in case we are jumping up multiple game modes.
//We need to use MakeCurrent in case we are jumping up multiple game screens.
nextOsu.Background?.MakeCurrent();
else
Background.Exit();

View File

@ -1,42 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Screens;
using osu.Framework.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Screens.Backgrounds;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Screens.Play
{
internal class FailDialog : OsuScreen
{
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap);
private static readonly Vector2 background_blur = new Vector2(20);
public FailDialog()
{
Add(new OsuSpriteText
{
Text = "You failed!",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
TextSize = 50
});
}
protected override void OnEntering(Screen last)
{
base.OnEntering(last);
Background.Schedule(() => (Background as BackgroundScreenBeatmap)?.BlurTo(background_blur, 1000));
}
protected override bool OnExiting(Screen next)
{
Background.Schedule(() => Background.FadeColour(Color4.White, 500));
return base.OnExiting(next);
}
}
}

View File

@ -0,0 +1,36 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Input;
using OpenTK.Input;
using osu.Game.Graphics;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using System.Linq;
namespace osu.Game.Screens.Play
{
public class FailOverlay : MenuOverlay
{
public override string Header => "failed";
public override string Description => "you're dead, try again?";
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
AddButton("Retry", colours.YellowDark, OnRetry);
AddButton("Quit", new Color4(170, 27, 39, 255), OnQuit);
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (!args.Repeat && args.Key == Key.Escape)
{
Buttons.Children.Last().TriggerClick();
return true;
}
return base.OnKeyDown(state, args);
}
}
}

View File

@ -0,0 +1,82 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Input;
using OpenTK.Input;
using osu.Framework.Allocation;
using osu.Framework.Audio.Sample;
using osu.Framework.Audio;
using System;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using OpenTK.Graphics;
namespace osu.Game.Screens.Play
{
public class HotkeyRetryOverlay : Container
{
public Action Action;
private SampleChannel retrySample;
private Box overlay;
private const int activate_delay = 400;
private const int fadeout_delay = 200;
private bool fired;
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
retrySample = audio.Sample.Get(@"Menu/menuback");
RelativeSizeAxes = Axes.Both;
AlwaysPresent = true;
Children = new Drawable[]
{
overlay = new Box
{
Alpha = 0,
Colour = Color4.Black,
RelativeSizeAxes = Axes.Both,
}
};
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (args.Repeat) return false;
if (args.Key == Key.Tilde)
{
overlay.FadeIn(activate_delay, EasingTypes.Out);
return true;
}
return base.OnKeyDown(state, args);
}
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
{
if (args.Key == Key.Tilde && !fired)
{
overlay.FadeOut(fadeout_delay, EasingTypes.Out);
return true;
}
return base.OnKeyUp(state, args);
}
protected override void Update()
{
base.Update();
if (!fired && overlay.Alpha == 1)
{
fired = true;
retrySample.Play();
Action?.Invoke();
}
}
}
}

View File

@ -0,0 +1,16 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Input;
namespace osu.Game.Screens.Play
{
/// <summary>
/// An InputManager primarily used to map keys to new functions.
/// By default this does nothing; override TransformState to make alterations.
/// </summary>
public class KeyConversionInputManager : PassThroughInputManager
{
}
}

View File

@ -4,7 +4,6 @@
using System.Linq;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Input;
@ -14,6 +13,8 @@ namespace osu.Game.Screens.Play
{
public KeyCounterCollection()
{
AlwaysReceiveInput = true;
Direction = FillDirection.Horizontal;
AutoSizeAxes = Axes.Both;
}
@ -33,8 +34,6 @@ namespace osu.Game.Screens.Play
counter.ResetCount();
}
public override bool Contains(Vector2 screenSpacePos) => true;
//further: change default values here and in KeyCounter if needed, instead of passing them in every constructor
private bool isCounting;
public bool IsCounting
@ -107,16 +106,15 @@ namespace osu.Game.Screens.Play
public class Receptor : Drawable
{
private KeyCounterCollection target;
private readonly KeyCounterCollection target;
public Receptor(KeyCounterCollection target)
{
AlwaysReceiveInput = true;
RelativeSizeAxes = Axes.Both;
this.target = target;
}
public override bool Contains(Vector2 screenSpacePos) => true;
public override bool HandleInput => true;
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) => target.Children.Any(c => c.TriggerKeyDown(state, args));

View File

@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Input;
using OpenTK;
using OpenTK.Input;
namespace osu.Game.Screens.Play
@ -10,12 +9,25 @@ namespace osu.Game.Screens.Play
public class KeyCounterMouse : KeyCounter
{
public MouseButton Button { get; }
public KeyCounterMouse(MouseButton button) : base(button.ToString())
public KeyCounterMouse(MouseButton button) : base(getStringRepresentation(button))
{
AlwaysReceiveInput = true;
Button = button;
}
public override bool Contains(Vector2 screenSpacePos) => true;
private static string getStringRepresentation(MouseButton button)
{
switch (button)
{
default:
return button.ToString();
case MouseButton.Left:
return @"M1";
case MouseButton.Right:
return @"M2";
}
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{

View File

@ -0,0 +1,199 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
using osu.Game.Graphics.Sprites;
using osu.Game.Screens.Play.Pause;
using OpenTK;
using OpenTK.Graphics;
using osu.Game.Graphics;
using osu.Framework.Allocation;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Screens.Play
{
public abstract class MenuOverlay : OverlayContainer, IRequireHighFrequencyMousePosition
{
private const int transition_duration = 200;
private const int button_height = 70;
private const float background_alpha = 0.75f;
protected override bool HideOnEscape => false;
protected override bool BlockPassThroughKeyboard => true;
public Action OnRetry;
public Action OnQuit;
public abstract string Header { get; }
public abstract string Description { get; }
protected FillFlowContainer<DialogButton> Buttons;
public int Retries
{
set
{
if (retryCounterContainer != null)
{
// "You've retried 1,065 times in this session"
// "You've retried 1 time in this session"
retryCounterContainer.Children = new Drawable[]
{
new OsuSpriteText
{
Text = "You've retried ",
Shadow = true,
ShadowColour = new Color4(0, 0, 0, 0.25f),
TextSize = 18
},
new OsuSpriteText
{
Text = $"{value:n0}",
Font = @"Exo2.0-Bold",
Shadow = true,
ShadowColour = new Color4(0, 0, 0, 0.25f),
TextSize = 18
},
new OsuSpriteText
{
Text = $" time{(value == 1 ? "" : "s")} in this session",
Shadow = true,
ShadowColour = new Color4(0, 0, 0, 0.25f),
TextSize = 18
}
};
}
}
}
private FillFlowContainer retryCounterContainer;
public override bool HandleInput => State == Visibility.Visible;
protected override void PopIn() => FadeIn(transition_duration, EasingTypes.In);
protected override void PopOut() => FadeOut(transition_duration, EasingTypes.In);
// Don't let mouse down events through the overlay or people can click circles while paused.
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true;
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) => true;
protected override bool OnMouseMove(InputState state) => true;
protected void AddButton(string text, Color4 colour, Action action)
{
Buttons.Add(new PauseButton
{
Text = text,
ButtonColour = colour,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Height = button_height,
Action = delegate {
action?.Invoke();
Hide();
}
});
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = background_alpha,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 50),
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Children = new Drawable[]
{
new FillFlowContainer
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 20),
Children = new Drawable[]
{
new OsuSpriteText
{
Text = Header,
Font = @"Exo2.0-Medium",
Spacing = new Vector2(5, 0),
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
TextSize = 30,
Colour = colours.Yellow,
Shadow = true,
ShadowColour = new Color4(0, 0, 0, 0.25f)
},
new OsuSpriteText
{
Text = Description,
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Shadow = true,
ShadowColour = new Color4(0, 0, 0, 0.25f)
}
}
},
Buttons = new FillFlowContainer<DialogButton>
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Masking = true,
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.6f),
Radius = 50
},
},
retryCounterContainer = new FillFlowContainer
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
AutoSizeAxes = Axes.Both,
}
}
},
new PauseProgressBar
{
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
Width = 1f
}
};
Retries = 0;
}
protected MenuOverlay()
{
AlwaysReceiveInput = true;
RelativeSizeAxes = Axes.Both;
}
}
}

View File

@ -4,23 +4,16 @@
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Game.Graphics.UserInterface;
using OpenTK.Graphics;
namespace osu.Game.Screens.Play.Pause
{
public class QuitButton : DialogButton
public class PauseButton : DialogButton
{
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
ButtonColour = new Color4(170, 27, 39, 255); // The red from the design isn't in the palette so it's used directly
SampleHover = audio.Sample.Get(@"Menu/menuclick");
SampleClick = audio.Sample.Get(@"Menu/menuback");
}
public QuitButton()
{
Text = @"Quit to Main Menu";
}
}
}

View File

@ -13,10 +13,10 @@ namespace osu.Game.Screens.Play.Pause
{
public class PauseProgressBar : Container
{
private Color4 fillColour = new Color4(221, 255, 255, 255);
private Color4 glowColour = new Color4(221, 255, 255, 150);
private readonly Color4 fillColour = new Color4(221, 255, 255, 255);
private readonly Color4 glowColour = new Color4(221, 255, 255, 150);
private Container fill;
private readonly Container fill;
private WorkingBeatmap current;
[BackgroundDependencyLoader]

View File

@ -1,26 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Screens.Play.Pause
{
public class ResumeButton : DialogButton
{
[BackgroundDependencyLoader]
private void load(AudioManager audio, OsuColour colours)
{
ButtonColour = colours.Green;
SampleHover = audio.Sample.Get(@"Menu/menuclick");
SampleClick = audio.Sample.Get(@"Menu/menuback");
}
public ResumeButton()
{
Text = @"Continue";
}
}
}

View File

@ -1,26 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Screens.Play.Pause
{
public class RetryButton : DialogButton
{
[BackgroundDependencyLoader]
private void load(AudioManager audio, OsuColour colours)
{
ButtonColour = colours.YellowDark;
SampleHover = audio.Sample.Get(@"Menu/menuclick");
SampleClick = audio.Sample.Get(@"Menu/menu-play-click");
}
public RetryButton()
{
Text = @"Retry";
}
}
}

View File

@ -2,89 +2,27 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms;
using System.Linq;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Screens.Play.Pause;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
using OpenTK.Graphics;
using osu.Framework.Allocation;
namespace osu.Game.Screens.Play
{
public class PauseOverlay : OverlayContainer
public class PauseOverlay : MenuOverlay
{
private const int transition_duration = 200;
private const int button_height = 70;
private const float background_alpha = 0.75f;
protected override bool HideOnEscape => false;
public Action OnResume;
public Action OnRetry;
public Action OnQuit;
public int Retries
{
set
{
if (retryCounterContainer != null)
{
// "You've retried 1,065 times in this session"
// "You've retried 1 time in this session"
retryCounterContainer.Children = new Drawable[]
{
new OsuSpriteText
{
Text = "You've retried ",
Shadow = true,
ShadowColour = new Color4(0, 0, 0, 0.25f),
TextSize = 18
},
new OsuSpriteText
{
Text = $"{value:n0}",
Font = @"Exo2.0-Bold",
Shadow = true,
ShadowColour = new Color4(0, 0, 0, 0.25f),
TextSize = 18
},
new OsuSpriteText
{
Text = $" time{(value == 1 ? "" : "s")} in this session",
Shadow = true,
ShadowColour = new Color4(0, 0, 0, 0.25f),
TextSize = 18
}
};
}
}
}
private FillFlowContainer retryCounterContainer;
public override bool Contains(Vector2 screenSpacePos) => true;
public override bool HandleInput => State == Visibility.Visible;
protected override void PopIn() => FadeIn(transition_duration, EasingTypes.In);
protected override void PopOut() => FadeOut(transition_duration, EasingTypes.In);
// Don't let mouse down events through the overlay or people can click circles while paused.
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true;
public override string Header => "paused";
public override string Description => "you're not going to do what i think you're going to do, are ya?";
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (args.Key == Key.Escape)
if (!args.Repeat && args.Key == Key.Escape)
{
if (State == Visibility.Hidden) return false;
resume();
Buttons.Children.First().TriggerClick();
return true;
}
@ -94,130 +32,9 @@ namespace osu.Game.Screens.Play
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = background_alpha,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 50),
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Children = new Drawable[]
{
new FillFlowContainer
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 20),
Children = new Drawable[]
{
new OsuSpriteText
{
Text = @"paused",
Font = @"Exo2.0-Medium",
Spacing = new Vector2(5, 0),
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
TextSize = 30,
Colour = colours.Yellow,
Shadow = true,
ShadowColour = new Color4(0, 0, 0, 0.25f)
},
new OsuSpriteText
{
Text = @"you're not going to do what i think you're going to do, are ya?",
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Shadow = true,
ShadowColour = new Color4(0, 0, 0, 0.25f)
}
}
},
new FillFlowContainer
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Masking = true,
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Black.Opacity(0.6f),
Radius = 50
},
Children = new Drawable[]
{
new ResumeButton
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Height = button_height,
Action = resume
},
new RetryButton
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Height = button_height,
Action = delegate
{
OnRetry?.Invoke();
Hide();
}
},
new QuitButton
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
Height = button_height,
Action = delegate
{
OnQuit?.Invoke();
Hide();
}
}
}
},
retryCounterContainer = new FillFlowContainer
{
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
AutoSizeAxes = Axes.Both,
}
}
},
new PauseProgressBar
{
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
Width = 1f
}
};
Retries = 0;
}
private void resume()
{
OnResume?.Invoke();
Hide();
}
public PauseOverlay()
{
RelativeSizeAxes = Axes.Both;
AddButton("Continue", colours.Green, OnResume);
AddButton("Retry", colours.YellowDark, OnRetry);
AddButton("Quit", new Color4(170, 27, 39, 255), OnQuit);
}
}
}

View File

@ -2,28 +2,27 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Configuration;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Input;
using osu.Framework.Logging;
using osu.Framework.Screens;
using osu.Framework.Timing;
using osu.Game.Configuration;
using osu.Game.Database;
using osu.Game.Input.Handlers;
using osu.Game.Modes;
using osu.Game.Modes.UI;
using osu.Game.Rulesets;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Ranking;
using System;
using System.Linq;
using osu.Framework.Threading;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Ranking;
namespace osu.Game.Screens.Play
{
@ -33,47 +32,56 @@ namespace osu.Game.Screens.Play
internal override bool ShowOverlays => false;
internal override bool HasLocalCursorDisplayed => !IsPaused && !HasFailed && HitRenderer.ProvidingUserCursor;
public BeatmapInfo BeatmapInfo;
public bool IsPaused { get; private set; }
public Action RestartRequested;
public bool IsPaused => !interpolatedSourceClock.IsRunning;
internal override bool AllowRulesetChange => false;
public bool HasFailed { get; private set; }
public int RestartCount;
private double pauseCooldown = 1000;
private const double pause_cooldown = 1000;
private double lastPauseActionTime;
private bool canPause => Time.Current >= lastPauseActionTime + pauseCooldown;
private bool canPause => ValidForResume && !HasFailed && Time.Current >= lastPauseActionTime + pause_cooldown;
private IAdjustableClock sourceClock;
private OffsetClock offsetClock;
private IFrameBasedClock interpolatedSourceClock;
private Ruleset ruleset;
private RulesetInfo ruleset;
private ScoreProcessor scoreProcessor;
private HitRenderer hitRenderer;
protected HitRenderer HitRenderer;
#region User Settings
private Bindable<int> dimLevel;
private Bindable<bool> mouseWheelDisabled;
private Bindable<double> userAudioOffset;
#endregion
private SkipButton skipButton;
private HudOverlay hudOverlay;
private PauseOverlay pauseOverlay;
private FailOverlay failOverlay;
[BackgroundDependencyLoader]
private void load(AudioManager audio, BeatmapDatabase beatmaps, OsuConfigManager config)
[BackgroundDependencyLoader(permitNulls: true)]
private void load(AudioManager audio, BeatmapDatabase beatmaps, OsuConfigManager config, OsuGame osu)
{
var beatmap = Beatmap.Beatmap;
if (beatmap.BeatmapInfo?.Mode > PlayMode.Osu)
{
//we only support osu! mode for now because the hitobject parsing is crappy and needs a refactor.
Exit();
return;
}
Beatmap.Mods.Value.ForEach(m => m.PlayerLoading(this));
dimLevel = config.GetBindable<int>(OsuConfig.DimLevel);
mouseWheelDisabled = config.GetBindable<bool>(OsuConfig.MouseDisableWheel);
Ruleset rulesetInstance;
try
{
if (Beatmap == null)
@ -84,6 +92,22 @@ namespace osu.Game.Screens.Play
if (Beatmap == null)
throw new Exception("Beatmap was not loaded");
ruleset = osu?.Ruleset.Value ?? Beatmap.BeatmapInfo.Ruleset;
rulesetInstance = ruleset.CreateInstance();
try
{
HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap);
}
catch (BeatmapInvalidForRulesetException)
{
// we may fail to create a HitRenderer if the beatmap cannot be loaded with the user's preferred ruleset
// let's try again forcing the beatmap's ruleset.
ruleset = Beatmap.BeatmapInfo.Ruleset;
rulesetInstance = ruleset.CreateInstance();
HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap);
}
}
catch (Exception e)
{
@ -103,44 +127,38 @@ namespace osu.Game.Screens.Play
}
sourceClock = (IAdjustableClock)track ?? new StopwatchClock();
interpolatedSourceClock = new InterpolatingFramedClock(sourceClock);
offsetClock = new OffsetClock(sourceClock);
userAudioOffset = config.GetBindable<double>(OsuConfig.AudioOffset);
userAudioOffset.ValueChanged += v => offsetClock.Offset = v;
userAudioOffset.TriggerChange();
interpolatedSourceClock = new InterpolatingFramedClock(offsetClock);
Schedule(() =>
{
sourceClock.Reset();
foreach (var mod in Beatmap.Mods.Value.OfType<IApplicableToClock>())
mod.ApplyToClock(sourceClock);
});
ruleset = Ruleset.GetRuleset(Beatmap.PlayMode);
scoreProcessor = HitRenderer.CreateScoreProcessor();
hudOverlay = new StandardHudOverlay();
hudOverlay.KeyCounter.Add(ruleset.CreateGameplayKeys());
hudOverlay.BindProcessor(scoreProcessor = ruleset.CreateScoreProcessor(beatmap.HitObjects.Count));
pauseOverlay = new PauseOverlay
hudOverlay = new StandardHudOverlay()
{
Depth = -1,
OnResume = delegate
{
Delay(400);
Schedule(Resume);
},
OnRetry = Restart,
OnQuit = Exit
Anchor = Anchor.Centre,
Origin = Anchor.Centre
};
hitRenderer = ruleset.CreateHitRendererWith(beatmap);
hudOverlay.KeyCounter.Add(rulesetInstance.CreateGameplayKeys());
hudOverlay.BindProcessor(scoreProcessor);
hudOverlay.BindHitRenderer(HitRenderer);
if (ReplayInputHandler != null)
{
ReplayInputHandler.ToScreenSpace = hitRenderer.MapPlayfieldToScreenSpace;
hitRenderer.InputManager.ReplayInputHandler = ReplayInputHandler;
}
hudOverlay.BindHitRenderer(hitRenderer);
hudOverlay.Progress.Objects = HitRenderer.Objects;
hudOverlay.Progress.AudioClock = interpolatedSourceClock;
//bind HitRenderer to ScoreProcessor and ourselves (for a pass situation)
hitRenderer.OnJudgement += scoreProcessor.AddJudgement;
hitRenderer.OnAllJudged += onPass;
HitRenderer.OnAllJudged += onCompletion;
//bind ScoreProcessor to ourselves (for a fail situation)
scoreProcessor.Failed += onFail;
@ -153,7 +171,7 @@ namespace osu.Game.Screens.Play
Clock = interpolatedSourceClock,
Children = new Drawable[]
{
hitRenderer,
HitRenderer,
skipButton = new SkipButton
{
Alpha = 0
@ -161,7 +179,30 @@ namespace osu.Game.Screens.Play
}
},
hudOverlay,
pauseOverlay
pauseOverlay = new PauseOverlay
{
OnResume = delegate
{
Delay(400);
Schedule(Resume);
},
OnRetry = Restart,
OnQuit = Exit,
},
failOverlay = new FailOverlay
{
OnRetry = Restart,
OnQuit = Exit,
},
new HotkeyRetryOverlay
{
Action = () => {
//we want to hide the hitrenderer immediately (looks better).
//we may be able to remove this once the mouse cursor trail is improved.
HitRenderer?.Hide();
Restart();
},
}
};
}
@ -194,78 +235,80 @@ namespace osu.Game.Screens.Play
public void Pause(bool force = false)
{
if (canPause || force)
if (!canPause && !force) return;
// the actual pausing is potentially happening on a different thread.
// we want to wait for the source clock to stop so we can be sure all components are in a stable state.
if (!IsPaused)
{
sourceClock.Stop();
Schedule(() => Pause(force));
return;
}
// we need to do a final check after all of our children have processed up to the paused clock time.
// this is to cover cases where, for instance, the player fails in the last processed frame (which would change canPause).
// as the scheduler runs before children updates, let's schedule for the next frame.
Schedule(() =>
{
if (!canPause) return;
lastPauseActionTime = Time.Current;
hudOverlay.KeyCounter.IsCounting = false;
hudOverlay.Progress.Show();
pauseOverlay.Retries = RestartCount;
pauseOverlay.Show();
sourceClock.Stop();
IsPaused = true;
}
else
{
IsPaused = false;
}
});
}
public void Resume()
{
lastPauseActionTime = Time.Current;
hudOverlay.KeyCounter.IsCounting = true;
hudOverlay.Progress.Hide();
pauseOverlay.Hide();
sourceClock.Start();
IsPaused = false;
}
public void TogglePaused()
{
IsPaused = !IsPaused;
if (IsPaused) Pause(); else Resume();
}
public void Restart()
{
sourceClock.Stop(); // If the clock is running and Restart is called the game will lag until relaunch
var newPlayer = new Player();
newPlayer.LoadAsync(Game, delegate
{
newPlayer.RestartCount = RestartCount + 1;
ValidForResume = false;
if (!Push(newPlayer))
{
// Error(?)
}
});
ValidForResume = false;
RestartRequested?.Invoke();
Exit();
}
private void onPass()
private ScheduledDelegate onCompletionEvent;
private void onCompletion()
{
// Only show the completion screen if the player hasn't failed
if (scoreProcessor.HasFailed || onCompletionEvent != null)
return;
ValidForResume = false;
Delay(1000);
Schedule(delegate
onCompletionEvent = Schedule(delegate
{
ValidForResume = false;
Push(new Results
var score = new Score
{
Score = scoreProcessor.GetScore()
});
Beatmap = Beatmap.BeatmapInfo,
Ruleset = ruleset
};
scoreProcessor.PopulateScore(score);
score.User = HitRenderer.Replay?.User ?? (Game as OsuGame)?.API?.LocalUser?.Value;
Push(new Results(score));
});
}
private void onFail()
{
Content.FadeColour(Color4.Red, 500);
sourceClock.Stop();
Delay(500);
Schedule(delegate
{
ValidForResume = false;
Push(new FailDialog());
});
HasFailed = true;
failOverlay.Retries = RestartCount;
failOverlay.Show();
}
protected override void OnEntering(Screen last)
@ -276,7 +319,8 @@ namespace osu.Game.Screens.Play
Background?.FadeTo((100f - dimLevel) / 100, 1500, EasingTypes.OutQuint);
Content.Alpha = 0;
dimLevel.ValueChanged += dimChanged;
dimLevel.ValueChanged += newDim => Background?.FadeTo((100f - newDim) / 100, 800);
Content.ScaleTo(0.7f);
@ -291,49 +335,51 @@ namespace osu.Game.Screens.Play
sourceClock.Start();
initializeSkipButton();
});
//keep in mind this is using the interpolatedSourceClock so won't be run as early as we may expect.
HitRenderer.Alpha = 0;
HitRenderer.FadeIn(750, EasingTypes.OutQuint);
}
protected override void OnSuspending(Screen next)
{
Content.FadeOut(350);
Content.ScaleTo(0.7f, 750, EasingTypes.InQuint);
fadeOut();
base.OnSuspending(next);
}
protected override bool OnExiting(Screen next)
{
if (pauseOverlay == null) return false;
if (ReplayInputHandler != null) return false;
if (pauseOverlay.State != Visibility.Visible && !canPause) return true;
if (!IsPaused && sourceClock.IsRunning) // For if the user presses escape quickly when entering the map
if (!HasFailed && ValidForResume)
{
Pause();
return true;
}
else
{
FadeOut(250);
Content.ScaleTo(0.7f, 750, EasingTypes.InQuint);
if (pauseOverlay != null && !HitRenderer.HasReplayLoaded)
{
//pause screen override logic.
if (pauseOverlay?.State == Visibility.Hidden && !canPause) return true;
dimLevel.ValueChanged -= dimChanged;
Background?.FadeTo(1f, 200);
return base.OnExiting(next);
if (!IsPaused) // For if the user presses escape quickly when entering the map
{
Pause();
return true;
}
}
}
fadeOut();
return base.OnExiting(next);
}
private void dimChanged(object sender, EventArgs e)
private void fadeOut()
{
Background?.FadeTo((100f - dimLevel) / 100, 800);
const float fade_out_duration = 250;
HitRenderer?.FadeOut(fade_out_duration);
Content.FadeOut(fade_out_duration);
hudOverlay.ScaleTo(0.7f, fade_out_duration * 3, EasingTypes.In);
Background?.FadeTo(1f, fade_out_duration);
}
private Bindable<bool> mouseWheelDisabled;
public ReplayInputHandler ReplayInputHandler;
protected override bool OnWheel(InputState state) => mouseWheelDisabled.Value && !IsPaused;
}
}
}

View File

@ -1,26 +1,15 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Input;
using osu.Game.Configuration;
using System.Linq;
using osu.Framework.Timing;
using osu.Game.Input.Handlers;
using OpenTK.Input;
using KeyboardState = osu.Framework.Input.KeyboardState;
using MouseState = osu.Framework.Input.MouseState;
namespace osu.Game.Screens.Play
{
public class PlayerInputManager : PassThroughInputManager
{
private bool leftViaKeyboard;
private bool rightViaKeyboard;
private Bindable<bool> mouseDisabled;
private ManualClock clock = new ManualClock();
private readonly ManualClock clock = new ManualClock();
private IFrameBasedClock parentClock;
private ReplayInputHandler replayInputHandler;
@ -47,44 +36,8 @@ namespace osu.Game.Screens.Play
Clock = new FramedClock(clock);
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
mouseDisabled = config.GetBindable<bool>(OsuConfig.MouseDisableButtons);
}
protected override void TransformState(InputState state)
{
base.TransformState(state);
var mouse = state.Mouse as MouseState;
var keyboard = state.Keyboard as KeyboardState;
if (keyboard != null)
{
leftViaKeyboard = keyboard.Keys.Contains(Key.Z);
rightViaKeyboard = keyboard.Keys.Contains(Key.X);
}
if (mouse != null)
{
if (mouseDisabled.Value)
{
mouse.ButtonStates.Find(s => s.Button == MouseButton.Left).State = false;
mouse.ButtonStates.Find(s => s.Button == MouseButton.Right).State = false;
}
if (leftViaKeyboard)
mouse.ButtonStates.Find(s => s.Button == MouseButton.Left).State = true;
if (rightViaKeyboard)
mouse.ButtonStates.Find(s => s.Button == MouseButton.Right).State = true;
}
}
protected override void Update()
{
base.Update();
if (parentClock == null) return;
clock.Rate = parentClock.Rate;

View File

@ -6,7 +6,6 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Database;
@ -20,17 +19,27 @@ namespace osu.Game.Screens.Play
{
public class PlayerLoader : OsuScreen
{
private readonly Player player;
private OsuLogo logo;
private Player player;
private readonly OsuLogo logo;
private BeatmapMetadataDisplay info;
private bool showOverlays = true;
internal override bool ShowOverlays => showOverlays;
internal override bool AllowRulesetChange => false;
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap);
public PlayerLoader(Player player)
{
ValidForResume = false;
this.player = player;
player.RestartRequested = () => {
showOverlays = false;
ValidForResume = true;
};
Children = new Drawable[]
{
logo = new OsuLogo
@ -51,7 +60,38 @@ namespace osu.Game.Screens.Play
Origin = Anchor.Centre,
});
player.LoadAsync(Game);
LoadComponentAsync(player);
}
protected override void OnResuming(Screen last)
{
base.OnResuming(last);
contentIn();
//we will only be resumed if the player has requested a re-run (see ValidForResume setting above)
LoadComponentAsync(player = new Player
{
RestartCount = player.RestartCount + 1,
RestartRequested = player.RestartRequested,
Beatmap = player.Beatmap,
});
Delay(400);
Schedule(pushWhenLoaded);
}
private void contentIn()
{
Content.ScaleTo(1, 650, EasingTypes.OutQuint);
Content.FadeInFromZero(400);
}
private void contentOut()
{
Content.ScaleTo(0.7f, 300, EasingTypes.InQuint);
Content.FadeOut(250);
}
protected override void OnEntering(Screen last)
@ -61,20 +101,27 @@ namespace osu.Game.Screens.Play
Background.FadeTo(0.4f, 250);
Content.ScaleTo(0.7f);
Content.ScaleTo(1, 750, EasingTypes.OutQuint);
Content.FadeInFromZero(500);
Delay(1000, true);
contentIn();
Delay(500, true);
logo.MoveToOffset(new Vector2(0, -180), 500, EasingTypes.InOutExpo);
Delay(250, true);
info.FadeIn(500);
Delay(2000, true);
Delay(1400, true);
Content.ScaleTo(0.7f, 300, EasingTypes.InQuint);
Content.FadeOut(250);
Schedule(pushWhenLoaded);
}
private void pushWhenLoaded()
{
if (!player.IsLoaded)
Schedule(pushWhenLoaded);
contentOut();
Delay(250);
@ -84,6 +131,12 @@ namespace osu.Game.Screens.Play
if (!Push(player))
Exit();
else
{
//By default, we want to load the player and never be returned to.
//Note that this may change if the player we load requested a re-run.
ValidForResume = false;
}
});
}

View File

@ -0,0 +1,23 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Replays;
namespace osu.Game.Screens.Play
{
public class ReplayPlayer : Player
{
public Replay Replay;
public ReplayPlayer(Replay replay)
{
Replay = replay;
}
protected override void LoadComplete()
{
base.LoadComplete();
HitRenderer.SetReplay(Replay);
}
}
}

View File

@ -0,0 +1,127 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using System;
using System.Collections.Generic;
using osu.Game.Graphics;
using osu.Framework.Allocation;
using System.Linq;
using osu.Framework.Timing;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Framework.Graphics.Primitives;
namespace osu.Game.Screens.Play
{
public class SongProgress : OverlayContainer
{
private const int bottom_bar_height = 5;
protected override bool HideOnEscape => false;
private static readonly Vector2 handle_size = new Vector2(14, 25);
private const float transition_duration = 200;
private readonly SongProgressBar bar;
private readonly SongProgressGraph graph;
public Action<double> OnSeek;
public IClock AudioClock;
private double lastHitTime => ((objects.Last() as IHasEndTime)?.EndTime ?? objects.Last().StartTime) + 1;
private IEnumerable<HitObject> objects;
public IEnumerable<HitObject> Objects
{
set
{
graph.Objects = objects = value;
}
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
graph.FillColour = bar.FillColour = colours.BlueLighter;
}
public SongProgress()
{
const float graph_height = SquareGraph.Column.WIDTH * 6;
Height = bottom_bar_height + graph_height + handle_size.Y;
Y = bottom_bar_height;
Children = new Drawable[]
{
graph = new SongProgressGraph
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
Height = graph_height,
Margin = new MarginPadding { Bottom = bottom_bar_height },
},
bar = new SongProgressBar(bottom_bar_height, graph_height, handle_size)
{
Alpha = 0,
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
SeekRequested = delegate (float position)
{
OnSeek?.Invoke(position);
},
},
};
}
protected override void LoadComplete()
{
State = Visibility.Visible;
}
private bool barVisible;
public void ToggleBar()
{
barVisible = !barVisible;
updateBarVisibility();
}
private void updateBarVisibility()
{
bar.FadeTo(barVisible ? 1 : 0, transition_duration, EasingTypes.In);
MoveTo(new Vector2(0, barVisible ? 0 : bottom_bar_height), transition_duration, EasingTypes.In);
}
protected override void PopIn()
{
updateBarVisibility();
FadeIn(500, EasingTypes.OutQuint);
}
protected override void PopOut()
{
FadeOut(100);
}
protected override void Update()
{
base.Update();
if (objects == null)
return;
double progress = (AudioClock?.CurrentTime ?? Time.Current) / lastHitTime;
bar.UpdatePosition((float)progress);
graph.Progress = (int)(graph.ColumnCount * progress);
}
}
}

View File

@ -0,0 +1,74 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Game.Overlays;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
namespace osu.Game.Screens.Play
{
public class SongProgressBar : DragBar
{
public Color4 FillColour
{
get { return Fill.Colour; }
set { Fill.Colour = value; }
}
public SongProgressBar(float barHeight, float handleBarHeight, Vector2 handleSize)
{
Height = barHeight + handleBarHeight + handleSize.Y;
Fill.RelativeSizeAxes = Axes.X;
Fill.Height = barHeight;
Add(new Box
{
Name = "Background",
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Height = barHeight,
Colour = Color4.Black,
Alpha = 0.5f,
Depth = 1
});
Fill.Add(new Container
{
Origin = Anchor.BottomRight,
Anchor = Anchor.BottomRight,
Width = 2,
Height = barHeight + handleBarHeight,
Colour = Color4.White,
Position = new Vector2(2, 0),
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
},
new Container
{
Origin = Anchor.BottomCentre,
Anchor = Anchor.TopCentre,
Size = handleSize,
CornerRadius = 5,
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White
}
}
}
}
});
}
}
}

View File

@ -0,0 +1,46 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using System.Collections.Generic;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Screens.Play
{
public class SongProgressGraph : SquareGraph
{
private IEnumerable<HitObject> objects;
public IEnumerable<HitObject> Objects
{
set
{
objects = value;
const int granularity = 200;
var lastHit = (objects.Last() as IHasEndTime)?.EndTime ?? 0;
if (lastHit == 0)
lastHit = objects.Last().StartTime;
var interval = (lastHit + 1) / granularity;
var values = new int[granularity];
foreach (var h in objects)
{
IHasEndTime end = h as IHasEndTime;
int startRange = (int)(h.StartTime / interval);
int endRange = (int)((end?.EndTime > 0 ? end.EndTime : h.StartTime) / interval);
for (int i = startRange; i <= endRange; i++)
values[i]++;
}
Values = values;
}
}
}
}

View File

@ -0,0 +1,238 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Framework;
using osu.Framework.Caching;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Screens.Play
{
public class SquareGraph : BufferedContainer
{
private Column[] columns = { };
public int ColumnCount => columns.Length;
public override bool HandleInput => false;
private int progress;
public int Progress
{
get { return progress; }
set
{
if (value == progress) return;
progress = value;
redrawProgress();
}
}
private float[] calculatedValues = { }; // values but adjusted to fit the amount of columns
private int[] values;
public int[] Values
{
get { return values; }
set
{
if (value == values) return;
values = value;
layout.Invalidate();
}
}
private Color4 fillColour;
public Color4 FillColour
{
get { return fillColour; }
set
{
if (value == fillColour) return;
fillColour = value;
redrawFilled();
}
}
public SquareGraph()
{
CacheDrawnFrameBuffer = true;
}
private Cached layout = new Cached();
public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true)
{
if ((invalidation & Invalidation.SizeInParentSpace) > 0)
layout.Invalidate();
return base.Invalidate(invalidation, source, shallPropagate);
}
protected override void Update()
{
base.Update();
layout.Refresh(recreateGraph);
}
/// <summary>
/// Redraws all the columns to match their lit/dimmed state.
/// </summary>
private void redrawProgress()
{
for (int i = 0; i < columns.Length; i++)
columns[i].State = i <= progress ? ColumnState.Lit : ColumnState.Dimmed;
ForceRedraw();
}
/// <summary>
/// Redraws the filled amount of all the columns.
/// </summary>
private void redrawFilled()
{
for (int i = 0; i < ColumnCount; i++)
columns[i].Filled = calculatedValues.ElementAtOrDefault(i);
ForceRedraw();
}
/// <summary>
/// Takes <see cref="Values"/> and adjusts it to fit the amount of columns.
/// </summary>
private void recalculateValues()
{
var newValues = new List<float>();
if (values == null)
{
for (float i = 0; i < ColumnCount; i++)
newValues.Add(0);
return;
}
var max = values.Max();
float step = values.Length / (float)ColumnCount;
for (float i = 0; i < values.Length; i += step)
{
newValues.Add((float)values[(int)i] / max);
}
calculatedValues = newValues.ToArray();
}
/// <summary>
/// Recreates the entire graph.
/// </summary>
private void recreateGraph()
{
var newColumns = new List<Column>();
for (float x = 0; x < DrawWidth; x += Column.WIDTH)
{
newColumns.Add(new Column
{
LitColour = fillColour,
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Height = DrawHeight,
Position = new Vector2(x, 0),
State = ColumnState.Dimmed,
});
}
columns = newColumns.ToArray();
Children = columns;
recalculateValues();
redrawFilled();
redrawProgress();
}
public class Column : Container, IStateful<ColumnState>
{
protected readonly Color4 EmptyColour = Color4.White.Opacity(20);
public Color4 LitColour = Color4.LightBlue;
protected readonly Color4 DimmedColour = Color4.White.Opacity(140);
private float cubeCount => DrawHeight / WIDTH;
private const float cube_size = 4;
private const float padding = 2;
public const float WIDTH = cube_size + padding;
private readonly List<Box> drawableRows = new List<Box>();
private float filled;
public float Filled
{
get { return filled; }
set
{
if (value == filled) return;
filled = value;
fillActive();
}
}
private ColumnState state;
public ColumnState State
{
get { return state; }
set
{
if (value == state) return;
state = value;
if (IsLoaded)
fillActive();
}
}
public Column()
{
Width = WIDTH;
}
protected override void LoadComplete()
{
for (int r = 0; r < cubeCount; r++)
{
drawableRows.Add(new Box
{
Size = new Vector2(cube_size),
Position = new Vector2(0, r * WIDTH + padding),
});
}
Children = drawableRows;
// Reverse drawableRows so when iterating through them they start at the bottom
drawableRows.Reverse();
fillActive();
}
private void fillActive()
{
Color4 colour = State == ColumnState.Lit ? LitColour : DimmedColour;
int countFilled = (int)MathHelper.Clamp(filled * drawableRows.Count, 0, drawableRows.Count);
for (int i = 0; i < drawableRows.Count; i++)
drawableRows[i].Colour = i < countFilled ? colour : EmptyColour;
}
}
public enum ColumnState
{
Lit,
Dimmed
}
}
}

View File

@ -0,0 +1,20 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
namespace osu.Game.Screens.Ranking
{
public class AspectContainer : Container
{
protected override void Update()
{
base.Update();
if (RelativeSizeAxes == Axes.X)
Height = DrawWidth;
else
Width = DrawHeight;
}
}
}

View File

@ -0,0 +1,12 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Screens.Ranking
{
public enum ResultMode
{
Summary,
Ranking,
Share
}
}

View File

@ -0,0 +1,108 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Screens.Ranking
{
public class ResultModeButton : TabItem<ResultMode>
{
private readonly FontAwesome icon;
private Color4 activeColour;
private Color4 inactiveColour;
private CircularContainer colouredPart;
public ResultModeButton(ResultMode mode) : base(mode)
{
switch (mode)
{
case ResultMode.Summary:
icon = FontAwesome.fa_asterisk;
break;
case ResultMode.Ranking:
icon = FontAwesome.fa_list;
break;
case ResultMode.Share:
icon = FontAwesome.fa_camera;
break;
}
}
public override bool Active
{
get
{
return base.Active;
}
set
{
base.Active = value;
colouredPart.FadeColour(Active ? activeColour : inactiveColour, 200, EasingTypes.OutQuint);
}
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Size = new Vector2(50);
Masking = true;
CornerRadius = 25;
activeColour = colours.PinkDarker;
inactiveColour = OsuColour.Gray(0.8f);
EdgeEffect = new EdgeEffect
{
Colour = Color4.Black.Opacity(0.4f),
Type = EdgeEffectType.Shadow,
Radius = 5,
};
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
},
colouredPart = new CircularContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.8f),
BorderThickness = 4,
BorderColour = Color4.White,
Colour = inactiveColour,
Children = new Drawable[]
{
new Box
{
AlwaysPresent = true, //for border rendering
RelativeSizeAxes = Axes.Both,
Colour = Color4.Transparent,
},
new TextAwesome
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Shadow = false,
Colour = OsuColour.Gray(0.95f),
Icon = icon,
TextSize = 20,
}
}
}
};
}
}
}

View File

@ -0,0 +1,31 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.UserInterface;
using OpenTK;
namespace osu.Game.Screens.Ranking
{
public class ResultModeTabControl : TabControl<ResultMode>
{
public ResultModeTabControl()
{
TabContainer.Anchor = Anchor.BottomCentre;
TabContainer.Origin = Anchor.BottomCentre;
TabContainer.Spacing = new Vector2(15);
TabContainer.Masking = false;
TabContainer.Padding = new MarginPadding(5);
}
protected override Dropdown<ResultMode> CreateDropdown() => null;
protected override TabItem<ResultMode> CreateTabItem(ResultMode value) => new ResultModeButton(value)
{
Anchor = TabContainer.Anchor,
Origin = TabContainer.Origin
};
}
}

View File

@ -1,94 +1,283 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Screens;
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Transforms;
using osu.Game.Graphics.Sprites;
using osu.Game.Modes;
using osu.Framework.Graphics.Primitives;
using osu.Game.Rulesets.Scoring;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Screens;
using osu.Game.Graphics.Containers;
using osu.Game.Screens.Backgrounds;
using OpenTK;
using OpenTK.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Screens.Ranking
{
internal class Results : OsuScreen
public class Results : OsuScreen
{
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap);
private readonly Score score;
private Container circleOuterBackground;
private Container circleOuter;
private Container circleInner;
private ParallaxContainer backgroundParallax;
private ResultModeTabControl modeChangeButtons;
internal override bool AllowRulesetChange => false;
private Container currentPage;
private static readonly Vector2 background_blur = new Vector2(20);
private ScoreDisplay scoreDisplay;
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap);
private const float overscan = 1.3f;
private const float circle_outer_scale = 0.96f;
public Results(Score score)
{
this.score = score;
}
private const float transition_time = 800;
private IEnumerable<Drawable> allCircles => new Drawable[] { circleOuterBackground, circleInner, circleOuter };
protected override void OnEntering(Screen last)
{
base.OnEntering(last);
Background.Schedule(() => (Background as BackgroundScreenBeatmap)?.BlurTo(background_blur, 1000));
(Background as BackgroundScreenBeatmap)?.BlurTo(background_blur, 2500, EasingTypes.OutQuint);
allCircles.ForEach(c =>
{
c.FadeOut();
c.ScaleTo(0);
});
backgroundParallax.FadeOut();
modeChangeButtons.FadeOut();
currentPage.FadeOut();
circleOuterBackground.ScaleTo(1, transition_time, EasingTypes.OutQuint);
circleOuterBackground.FadeTo(1, transition_time, EasingTypes.OutQuint);
Content.Delay(transition_time * 0.25f, true);
circleOuter.ScaleTo(1, transition_time, EasingTypes.OutQuint);
circleOuter.FadeTo(1, transition_time, EasingTypes.OutQuint);
Content.Delay(transition_time * 0.3f, true);
backgroundParallax.FadeIn(transition_time, EasingTypes.OutQuint);
circleInner.ScaleTo(1, transition_time, EasingTypes.OutQuint);
circleInner.FadeTo(1, transition_time, EasingTypes.OutQuint);
Content.Delay(transition_time * 0.4f, true);
modeChangeButtons.FadeIn(transition_time, EasingTypes.OutQuint);
currentPage.FadeIn(transition_time, EasingTypes.OutQuint);
Content.DelayReset();
}
protected override bool OnExiting(Screen next)
{
Background.Schedule(() => Background.FadeColour(Color4.White, 500));
allCircles.ForEach(c =>
{
c.ScaleTo(0, transition_time, EasingTypes.OutSine);
});
Content.FadeOut(transition_time / 4);
return base.OnExiting(next);
}
public Score Score
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
set
{
scoreDisplay?.FadeOut(500);
scoreDisplay?.Expire();
scoreDisplay = new ScoreDisplay(value)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
};
Add(scoreDisplay);
scoreDisplay.FadeIn(500);
scoreDisplay.ScaleTo(0.1f);
scoreDisplay.ScaleTo(1, 1000, EasingTypes.OutElastic);
scoreDisplay.RotateTo(360 * 5, 1000, EasingTypes.OutElastic);
}
}
}
internal class ScoreDisplay : Container
{
public ScoreDisplay(Score s)
{
AutoSizeAxes = Axes.Both;
Children = new Drawable[]
{
new FillFlowContainer
new AspectContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
RelativeSizeAxes = Axes.Y,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Height = overscan,
Children = new Drawable[]
{
new OsuSpriteText
circleOuterBackground = new CircularContainer
{
TextSize = 40,
Text = $@"Accuracy: {s.Accuracy:#0.00%}",
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Masking = true,
Children = new Drawable[]
{
new Box
{
Alpha = 0.2f,
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
}
}
},
new OsuSpriteText
circleOuter = new CircularContainer
{
TextSize = 40,
Text = $@"Score: {s.TotalScore}",
Size = new Vector2(circle_outer_scale),
EdgeEffect = new EdgeEffect
{
Colour = Color4.Black.Opacity(0.4f),
Type = EdgeEffectType.Shadow,
Radius = 15,
},
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
},
backgroundParallax = new ParallaxContainer
{
RelativeSizeAxes = Axes.Both,
ParallaxAmount = 0.01f,
Scale = new Vector2(1 / circle_outer_scale / overscan),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
new Sprite
{
Alpha = 0.2f,
Texture = Beatmap?.Background,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
FillMode = FillMode.Fill
}
}
},
modeChangeButtons = new ResultModeTabControl
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.X,
Height = 50,
Margin = new MarginPadding { Bottom = 110 },
},
new SpriteText
{
Text = $"{score.MaxCombo}x",
TextSize = 40,
RelativePositionAxes = Axes.X,
Font = @"Exo2.0-Bold",
X = 0.1f,
Colour = colours.BlueDarker,
Anchor = Anchor.CentreLeft,
Origin = Anchor.BottomCentre,
},
new SpriteText
{
Text = "max combo",
TextSize = 20,
RelativePositionAxes = Axes.X,
X = 0.1f,
Colour = colours.Gray6,
Anchor = Anchor.CentreLeft,
Origin = Anchor.TopCentre,
},
new SpriteText
{
Text = $"{score.Accuracy:P2}",
TextSize = 40,
RelativePositionAxes = Axes.X,
Font = @"Exo2.0-Bold",
X = 0.9f,
Colour = colours.BlueDarker,
Anchor = Anchor.CentreLeft,
Origin = Anchor.BottomCentre,
},
new SpriteText
{
Text = "accuracy",
TextSize = 20,
RelativePositionAxes = Axes.X,
X = 0.9f,
Colour = colours.Gray6,
Anchor = Anchor.CentreLeft,
Origin = Anchor.TopCentre,
},
}
},
new OsuSpriteText
circleInner = new CircularContainer
{
TextSize = 40,
Text = $@"MaxCombo: {s.MaxCombo}",
Size = new Vector2(0.6f),
EdgeEffect = new EdgeEffect
{
Colour = Color4.Black.Opacity(0.4f),
Type = EdgeEffectType.Shadow,
Radius = 15,
},
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
},
}
}
}
}
},
new BackButton
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Action = Exit
},
};
modeChangeButtons.AddItem(ResultMode.Summary);
modeChangeButtons.AddItem(ResultMode.Ranking);
//modeChangeButtons.AddItem(ResultMode.Share);
modeChangeButtons.Current.ValueChanged += mode =>
{
currentPage?.FadeOut();
currentPage?.Expire();
switch (mode)
{
case ResultMode.Summary:
currentPage = new ResultsPageScore(score, Beatmap);
break;
case ResultMode.Ranking:
currentPage = new ResultsPageRanking(score, Beatmap);
break;
}
if (currentPage != null)
circleInner.Add(currentPage);
};
modeChangeButtons.Current.TriggerChange();
}
}
}

View File

@ -0,0 +1,92 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Scoring;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Screens.Ranking
{
internal class ResultsPage : Container
{
protected readonly Score Score;
protected readonly WorkingBeatmap Beatmap;
private CircularContainer content;
private Box fill;
protected override Container<Drawable> Content => content;
public ResultsPage(Score score, WorkingBeatmap beatmap)
{
Score = score;
Beatmap = beatmap;
RelativeSizeAxes = Axes.Both;
}
protected override void LoadComplete()
{
base.LoadComplete();
fill.Delay(400);
fill.FadeInFromZero(600);
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
AddInternal(new Drawable[]
{
fill = new Box
{
Alpha = 0,
RelativeSizeAxes = Axes.Both,
Colour = colours.Gray6
},
new CircularContainer
{
EdgeEffect = new EdgeEffect
{
Colour = colours.GrayF.Opacity(0.8f),
Type = EdgeEffectType.Shadow,
Radius = 1,
},
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = 20,
BorderColour = Color4.White,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
new Box{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
},
}
},
content = new CircularContainer
{
EdgeEffect = new EdgeEffect
{
Colour = Color4.Black.Opacity(0.2f),
Type = EdgeEffectType.Shadow,
Radius = 15,
},
RelativeSizeAxes = Axes.Both,
Masking = true,
Size = new Vector2(0.88f),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}
});
}
}
}

View File

@ -0,0 +1,42 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Select.Leaderboards;
using OpenTK;
namespace osu.Game.Screens.Ranking
{
internal class ResultsPageRanking : ResultsPage
{
public ResultsPageRanking(Score score, WorkingBeatmap beatmap = null) : base(score, beatmap)
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Children = new Drawable[]
{
new Box
{
Colour = colours.GrayE,
RelativeSizeAxes = Axes.Both,
},
new Leaderboard
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Beatmap = Beatmap.BeatmapInfo ?? Score.Beatmap,
Scale = new Vector2(0.7f)
}
};
}
}
}

View File

@ -0,0 +1,393 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Configuration;
using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Select.Leaderboards;
using osu.Game.Users;
using OpenTK;
using OpenTK.Graphics;
using System;
using System.Collections.Generic;
using osu.Framework.Extensions.Color4Extensions;
using osu.Game.Beatmaps;
using osu.Game.Screens.Play;
using osu.Game.Rulesets.Scoring;
using osu.Framework.Graphics.Colour;
using System.Linq;
namespace osu.Game.Screens.Ranking
{
internal class ResultsPageScore : ResultsPage
{
private ScoreCounter scoreCounter;
public ResultsPageScore(Score score, WorkingBeatmap beatmap) : base(score, beatmap) { }
private FillFlowContainer<DrawableScoreStatistic> statisticsContainer;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
const float user_header_height = 120;
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = user_header_height },
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
},
}
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new UserHeader(Score.User)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
Height = user_header_height,
},
new DrawableRank(Score.Rank)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Size = new Vector2(150, 60),
Margin = new MarginPadding(20),
},
new Container
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
Height = 60,
Children = new Drawable[]
{
new SongProgressGraph
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.5f,
Objects = Beatmap.Beatmap.HitObjects,
},
scoreCounter = new SlowScoreCounter(6)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Colour = colours.PinkDarker,
Y = 10,
TextSize = 56,
},
}
},
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Colour = colours.PinkDarker,
Shadow = false,
Font = @"Exo2.0-Bold",
TextSize = 16,
Text = "total score",
Margin = new MarginPadding { Bottom = 15 },
},
new BeatmapDetails(Beatmap.BeatmapInfo)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Margin = new MarginPadding { Bottom = 10 },
},
new DateDisplay(Score.Date)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
new Container
{
RelativeSizeAxes = Axes.X,
Size = new Vector2(0.75f, 1),
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Margin = new MarginPadding { Top = 10, Bottom = 10 },
Children = new Drawable[]
{
new Box
{
ColourInfo = ColourInfo.GradientHorizontal(
colours.GrayC.Opacity(0),
colours.GrayC.Opacity(0.9f)),
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.5f, 1),
},
new Box
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
ColourInfo = ColourInfo.GradientHorizontal(
colours.GrayC.Opacity(0.9f),
colours.GrayC.Opacity(0)),
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.5f, 1),
},
}
},
statisticsContainer = new FillFlowContainer<DrawableScoreStatistic>
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Direction = FillDirection.Horizontal,
LayoutDuration = 200,
LayoutEasing = EasingTypes.OutQuint
}
}
}
};
statisticsContainer.Children = Score.Statistics.Select(s => new DrawableScoreStatistic(s));
}
protected override void LoadComplete()
{
base.LoadComplete();
Schedule(() =>
{
scoreCounter.Increment(Score.TotalScore);
int delay = 0;
foreach (var s in statisticsContainer.Children)
{
s.FadeOut();
s.Delay(delay += 200);
s.FadeIn(300 + delay, EasingTypes.Out);
}
});
}
private class DrawableScoreStatistic : Container
{
private readonly KeyValuePair<string, dynamic> statistic;
public DrawableScoreStatistic(KeyValuePair<string, dynamic> statistic)
{
this.statistic = statistic;
AutoSizeAxes = Axes.Both;
Margin = new MarginPadding { Left = 5, Right = 5 };
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Children = new Drawable[]
{
new SpriteText {
Text = statistic.Value.ToString().PadLeft(4, '0'),
Colour = colours.Gray7,
TextSize = 30,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
new SpriteText {
Text = statistic.Key,
Colour = colours.Gray7,
Font = @"Exo2.0-Bold",
Y = 26,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
};
}
}
private class DateDisplay : Container
{
private DateTime date;
public DateDisplay(DateTime date)
{
this.date = date;
AutoSizeAxes = Axes.Y;
Width = 140;
Masking = true;
CornerRadius = 5;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colours.Gray6,
},
new OsuSpriteText
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
Text = date.ToString("HH:mm"),
Padding = new MarginPadding { Left = 10, Right = 10, Top = 5, Bottom = 5 },
Colour = Color4.White,
},
new OsuSpriteText
{
Origin = Anchor.CentreRight,
Anchor = Anchor.CentreRight,
Text = date.ToString("yyyy/MM/dd"),
Padding = new MarginPadding { Left = 10, Right = 10, Top = 5, Bottom = 5 },
Colour = Color4.White,
}
};
}
}
private class BeatmapDetails : Container
{
private readonly BeatmapInfo beatmap;
private Bindable<bool> preferUnicode;
private readonly OsuSpriteText title;
private readonly OsuSpriteText artist;
private readonly OsuSpriteText versionMapper;
public BeatmapDetails(BeatmapInfo beatmap)
{
this.beatmap = beatmap;
AutoSizeAxes = Axes.Both;
Children = new Drawable[]
{
new FillFlowContainer
{
Direction = FillDirection.Vertical,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
title = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Shadow = false,
TextSize = 24,
Font = @"Exo2.0-BoldItalic",
},
artist = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Shadow = false,
TextSize = 20,
Font = @"Exo2.0-BoldItalic",
},
versionMapper = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Shadow = false,
TextSize = 16,
Font = @"Exo2.0-Bold",
},
}
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, OsuConfigManager config)
{
title.Colour = artist.Colour = colours.BlueDarker;
versionMapper.Colour = colours.Gray8;
versionMapper.Text = $"{beatmap.Version} - mapped by {beatmap.Metadata.Author}";
preferUnicode = config.GetBindable<bool>(OsuConfig.ShowUnicode);
preferUnicode.ValueChanged += unicode =>
{
title.Text = unicode ? beatmap.Metadata.TitleUnicode : beatmap.Metadata.Title;
artist.Text = unicode ? beatmap.Metadata.ArtistUnicode : beatmap.Metadata.Artist;
};
preferUnicode.TriggerChange();
}
}
private class UserHeader : Container
{
private readonly User user;
private readonly Sprite cover;
public UserHeader(User user)
{
this.user = user;
Children = new Drawable[]
{
cover = new Sprite
{
FillMode = FillMode.Fill,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
new OsuSpriteText
{
Font = @"Exo2.0-RegularItalic",
Text = user.Username,
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
TextSize = 30,
Padding = new MarginPadding { Bottom = 10 },
}
};
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
if (!string.IsNullOrEmpty(user.CoverUrl))
cover.Texture = textures.Get(user.CoverUrl);
}
}
private class SlowScoreCounter : ScoreCounter
{
protected override double RollingDuration => 3000;
protected override EasingTypes RollingEasing => EasingTypes.OutPow10;
public SlowScoreCounter(uint leading = 0) : base(leading)
{
DisplayedCountSpriteText.Shadow = false;
DisplayedCountSpriteText.Font = @"Venera-Light";
UseCommaSeparator = true;
}
}
}
}

View File

@ -7,26 +7,28 @@ using osu.Framework.Screens;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.Sprites;
using osu.Game.Screens.Backgrounds;
using osu.Game.Graphics.UserInterface;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Game.Graphics;
using osu.Framework.Extensions.Color4Extensions;
namespace osu.Game.Screens
{
public class ScreenWhiteBox : OsuScreen
{
private BackButton popButton;
private readonly BackButton popButton;
private const double transition_time = 1000;
protected virtual IEnumerable<Type> PossibleChildren => null;
private Container textContainer;
private Box box;
private readonly FillFlowContainer textContainer;
private readonly Container boxContainer;
protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg2");
@ -34,20 +36,20 @@ namespace osu.Game.Screens
{
base.OnEntering(last);
//only show the pop button if we are entered form another gamemode.
//only show the pop button if we are entered form another screen.
if (last != null)
popButton.Alpha = 1;
Content.Alpha = 0;
textContainer.Position = new Vector2(DrawSize.X / 16, 0);
box.ScaleTo(0.2f);
box.RotateTo(-20);
boxContainer.ScaleTo(0.2f);
boxContainer.RotateTo(-20);
Content.Delay(300, true);
box.ScaleTo(1, transition_time, EasingTypes.OutElastic);
box.RotateTo(0, transition_time / 2, EasingTypes.OutQuint);
boxContainer.ScaleTo(1, transition_time, EasingTypes.OutElastic);
boxContainer.RotateTo(0, transition_time / 2, EasingTypes.OutQuint);
textContainer.MoveTo(Vector2.Zero, transition_time, EasingTypes.OutExpo);
Content.FadeIn(transition_time, EasingTypes.OutExpo);
@ -83,36 +85,62 @@ namespace osu.Game.Screens
Children = new Drawable[]
{
box = new Box
boxContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.3f),
RelativeSizeAxes = Axes.Both,
CornerRadius = 20,
Masking = true,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Colour = getColourFor(GetType()),
Alpha = 1,
BlendingMode = BlendingMode.Additive,
},
textContainer = new Container
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new[]
Children = new Drawable[]
{
new OsuSpriteText
new Box
{
Text = GetType().Name,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
TextSize = 50,
RelativeSizeAxes = Axes.Both,
Colour = getColourFor(GetType()),
Alpha = 0.2f,
BlendingMode = BlendingMode.Additive,
},
new OsuSpriteText
textContainer = new FillFlowContainer
{
Text = GetType().Namespace,
AutoSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Position = new Vector2(0, 30)
Direction = FillDirection.Vertical,
Children = new[]
{
new TextAwesome
{
Icon = FontAwesome.fa_universal_access,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
TextSize = 50,
},
new OsuSpriteText
{
Text = GetType().Name,
Colour = getColourFor(GetType()).Lighten(0.8f),
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
TextSize = 50,
},
new OsuSpriteText
{
Text = "is not yet ready for use!",
TextSize = 20,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
new OsuSpriteText
{
Text = "please check back a bit later.",
TextSize = 14,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
}
},
}
},
@ -121,17 +149,15 @@ namespace osu.Game.Screens
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Alpha = 0,
Action = delegate {
Exit();
}
Action = Exit
},
childModeButtons = new FillFlowContainer
{
Direction = FillDirection.Vertical,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.1f, 1)
RelativeSizeAxes = Axes.Y,
Size = new Vector2(TwoLayerButton.SIZE_RETRACTED.X, 1)
}
};
@ -139,14 +165,11 @@ namespace osu.Game.Screens
{
foreach (Type t in PossibleChildren)
{
childModeButtons.Add(new Button
childModeButtons.Add(new ChildModeButton
{
Text = $@"{t.Name}",
RelativeSizeAxes = Axes.X,
Size = new Vector2(1, 40),
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
BackgroundColour = getColourFor(t),
HoverColour = getColourFor(t).Lighten(0.2f),
Action = delegate
{
Push(Activator.CreateInstance(t) as Screen);
@ -164,5 +187,21 @@ namespace osu.Game.Screens
byte b = (byte)MathHelper.Clamp((hash & 0x0000FF) * 0.8f, 20, 255);
return new Color4(r, g, b, 255);
}
public class ChildModeButton : TwoLayerButton
{
public ChildModeButton()
{
Icon = FontAwesome.fa_osu_right_o;
Anchor = Anchor.BottomRight;
Origin = Anchor.BottomRight;
}
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
ActivationSound = audio.Sample.Get(@"Menu/menuhit");
}
}
}
}

View File

@ -4,7 +4,6 @@
using OpenTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Transforms;
using osu.Game.Database;
using System;
using System.Collections.Generic;
@ -15,66 +14,257 @@ using OpenTK.Input;
using System.Collections;
using osu.Framework.MathUtils;
using System.Diagnostics;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Threading;
namespace osu.Game.Screens.Select
{
internal class CarouselContainer : ScrollContainer, IEnumerable<BeatmapGroup>
internal class BeatmapCarousel : ScrollContainer, IEnumerable<BeatmapGroup>
{
private Container<Panel> scrollableContent;
private List<BeatmapGroup> groups = new List<BeatmapGroup>();
private List<Panel> panels = new List<Panel>();
public BeatmapInfo SelectedBeatmap => selectedPanel?.Beatmap;
public BeatmapGroup SelectedGroup { get; private set; }
public BeatmapPanel SelectedPanel { get; private set; }
public Action BeatmapsChanged;
private List<float> yPositions = new List<float>();
public CarouselContainer()
public IEnumerable<BeatmapSetInfo> Beatmaps
{
DistanceDecayJump = 0.01;
get
{
return groups.Select(g => g.BeatmapSet);
}
set
{
scrollableContent.Clear(false);
panels.Clear();
groups.Clear();
List<BeatmapGroup> newGroups = null;
Task.Run(() =>
{
newGroups = value.Select(createGroup).ToList();
criteria.Filter(newGroups);
}).ContinueWith(t =>
{
Schedule(() =>
{
foreach (var g in newGroups)
addGroup(g);
computeYPositions();
BeatmapsChanged?.Invoke();
});
});
}
}
private readonly List<float> yPositions = new List<float>();
/// <summary>
/// Required for now unfortunately.
/// </summary>
private BeatmapDatabase database;
private readonly Container<Panel> scrollableContent;
private readonly List<BeatmapGroup> groups = new List<BeatmapGroup>();
private readonly List<Panel> panels = new List<Panel>();
private BeatmapGroup selectedGroup;
private BeatmapPanel selectedPanel;
public BeatmapCarousel()
{
Add(scrollableContent = new Container<Panel>
{
RelativeSizeAxes = Axes.X,
});
}
public void AddGroup(BeatmapGroup group)
public void AddBeatmap(BeatmapSetInfo beatmapSet)
{
groups.Add(group);
var group = createGroup(beatmapSet);
panels.Add(group.Header);
group.Header.UpdateClock(Clock);
foreach (BeatmapPanel panel in group.BeatmapPanels)
//for the time being, let's completely load the difficulty panels in the background.
//this likely won't scale so well, but allows us to completely async the loading flow.
Schedule(delegate
{
panels.Add(panel);
panel.UpdateClock(Clock);
}
computeYPositions();
addGroup(group);
computeYPositions();
if (selectedGroup == null)
selectGroup(group);
});
}
public void RemoveGroup(BeatmapGroup group)
public void SelectBeatmap(BeatmapInfo beatmap, bool animated = true)
{
if (beatmap == null)
{
SelectNext();
return;
}
foreach (BeatmapGroup group in groups)
{
var panel = group.BeatmapPanels.FirstOrDefault(p => p.Beatmap.Equals(beatmap));
if (panel != null)
{
selectGroup(group, panel, animated);
return;
}
}
}
public void RemoveBeatmap(BeatmapSetInfo info) => removeGroup(groups.Find(b => b.BeatmapSet.ID == info.ID));
public Action<BeatmapGroup, BeatmapInfo> SelectionChanged;
public Action StartRequested;
public void SelectNext(int direction = 1, bool skipDifficulties = true)
{
if (groups.Count == 0)
{
selectedGroup = null;
selectedPanel = null;
return;
}
if (!skipDifficulties && selectedGroup != null)
{
int i = selectedGroup.BeatmapPanels.IndexOf(selectedPanel) + direction;
if (i >= 0 && i < selectedGroup.BeatmapPanels.Count)
{
//changing difficulty panel, not set.
selectGroup(selectedGroup, selectedGroup.BeatmapPanels[i]);
return;
}
}
int startIndex = Math.Max(0, groups.IndexOf(selectedGroup));
int index = startIndex;
do
{
index = (index + direction + groups.Count) % groups.Count;
if (groups[index].State != BeatmapGroupState.Hidden)
{
SelectBeatmap(groups[index].BeatmapPanels.First().Beatmap);
return;
}
} while (index != startIndex);
}
public void SelectRandom()
{
List<BeatmapGroup> visibleGroups = groups.Where(selectGroup => selectGroup.State != BeatmapGroupState.Hidden).ToList();
if (visibleGroups.Count < 1)
return;
BeatmapGroup group = visibleGroups[RNG.Next(visibleGroups.Count)];
BeatmapPanel panel = group?.BeatmapPanels.First();
if (panel == null)
return;
selectGroup(group, panel);
}
private FilterCriteria criteria = new FilterCriteria();
private ScheduledDelegate filterTask;
public void Filter(FilterCriteria newCriteria = null, bool debounce = true)
{
if (newCriteria != null)
criteria = newCriteria;
if (!IsLoaded) return;
Action perform = delegate
{
filterTask = null;
criteria.Filter(groups);
var filtered = new List<BeatmapGroup>(groups);
scrollableContent.Clear(false);
panels.Clear();
groups.Clear();
foreach (var g in filtered)
addGroup(g);
computeYPositions();
if (selectedGroup == null || selectedGroup.State == BeatmapGroupState.Hidden)
SelectNext();
else
selectGroup(selectedGroup);
};
filterTask?.Cancel();
if (debounce)
filterTask = Scheduler.AddDelayed(perform, 250);
else
perform();
}
public IEnumerator<BeatmapGroup> GetEnumerator() => groups.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
private BeatmapGroup createGroup(BeatmapSetInfo beatmapSet)
{
database.GetChildren(beatmapSet);
beatmapSet.Beatmaps.ForEach(b =>
{
database.GetChildren(b);
if (b.Metadata == null)
b.Metadata = beatmapSet.Metadata;
});
return new BeatmapGroup(beatmapSet, database)
{
SelectionChanged = SelectionChanged,
StartRequested = b => StartRequested?.Invoke(),
State = BeatmapGroupState.Collapsed
};
}
[BackgroundDependencyLoader(permitNulls: true)]
private void load(BeatmapDatabase database)
{
this.database = database;
}
private void addGroup(BeatmapGroup group)
{
groups.Add(group);
panels.Add(group.Header);
panels.AddRange(group.BeatmapPanels);
}
private void removeGroup(BeatmapGroup group)
{
groups.Remove(group);
panels.Remove(group.Header);
foreach (var p in group.BeatmapPanels)
panels.Remove(p);
scrollableContent.Remove(group.Header);
scrollableContent.Remove(group.BeatmapPanels);
if (selectedGroup == group)
SelectNext();
computeYPositions();
}
private void movePanel(Panel panel, bool advance, bool animated, ref float currentY)
{
yPositions.Add(currentY);
panel.MoveToY(currentY, animated ? 750 : 0, EasingTypes.OutExpo);
if (advance)
currentY += panel.DrawHeight + 5;
}
/// <summary>
/// Computes the target Y positions for every panel in the carousel.
/// </summary>
@ -97,7 +287,7 @@ namespace osu.Game.Screens.Select
foreach (BeatmapPanel panel in group.BeatmapPanels)
{
if (panel == SelectedPanel)
if (panel == selectedPanel)
selectedY = currentY + panel.DrawHeight / 2 - DrawHeight / 2;
panel.MoveToX(-50, 500, EasingTypes.OutExpo);
@ -127,125 +317,62 @@ namespace osu.Game.Screens.Select
return selectedY;
}
public void SelectBeatmap(BeatmapInfo beatmap, bool animated = true)
private void movePanel(Panel panel, bool advance, bool animated, ref float currentY)
{
foreach (BeatmapGroup group in groups)
{
var panel = group.BeatmapPanels.FirstOrDefault(p => p.Beatmap.Equals(beatmap));
if (panel != null)
{
selectGroup(group, panel, animated);
return;
}
}
yPositions.Add(currentY);
panel.MoveToY(currentY, animated ? 750 : 0, EasingTypes.OutExpo);
if (advance)
currentY += panel.DrawHeight + 5;
}
private void selectGroup(BeatmapGroup group, BeatmapPanel panel, bool animated = true)
private void selectGroup(BeatmapGroup group, BeatmapPanel panel = null, bool animated = true)
{
if (panel == null)
panel = group.BeatmapPanels.First();
Trace.Assert(group.BeatmapPanels.Contains(panel), @"Selected panel must be in provided group");
if (SelectedGroup != null && SelectedGroup != group && SelectedGroup.State != BeatmapGroupState.Hidden)
SelectedGroup.State = BeatmapGroupState.Collapsed;
if (selectedGroup != null && selectedGroup != group && selectedGroup.State != BeatmapGroupState.Hidden)
selectedGroup.State = BeatmapGroupState.Collapsed;
group.State = BeatmapGroupState.Expanded;
SelectedGroup = group;
selectedGroup = group;
panel.State = PanelSelectedState.Selected;
SelectedPanel = panel;
selectedPanel = panel;
float selectedY = computeYPositions(animated);
ScrollTo(selectedY, animated);
}
public void Sort(FilterControl.SortMode mode)
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
List<BeatmapGroup> sortedGroups = new List<BeatmapGroup>(groups);
switch (mode)
int direction = 0;
bool skipDifficulties = false;
switch (args.Key)
{
case FilterControl.SortMode.Artist:
sortedGroups.Sort((x, y) => string.Compare(x.BeatmapSet.Metadata.Artist, y.BeatmapSet.Metadata.Artist, StringComparison.InvariantCultureIgnoreCase));
case Key.Up:
direction = -1;
break;
case FilterControl.SortMode.Title:
sortedGroups.Sort((x, y) => string.Compare(x.BeatmapSet.Metadata.Title, y.BeatmapSet.Metadata.Title, StringComparison.InvariantCultureIgnoreCase));
case Key.Down:
direction = 1;
break;
case FilterControl.SortMode.Author:
sortedGroups.Sort((x, y) => string.Compare(x.BeatmapSet.Metadata.Author, y.BeatmapSet.Metadata.Author, StringComparison.InvariantCultureIgnoreCase));
case Key.Left:
direction = -1;
skipDifficulties = true;
break;
case FilterControl.SortMode.Difficulty:
sortedGroups.Sort((x, y) =>
{
float xAverage = 0, yAverage = 0;
int counter = 0;
foreach (BeatmapInfo set in x.BeatmapSet.Beatmaps)
{
xAverage += set.StarDifficulty;
counter++;
}
xAverage /= counter;
counter = 0;
foreach (BeatmapInfo set in y.BeatmapSet.Beatmaps)
{
yAverage += set.StarDifficulty;
counter++;
}
yAverage /= counter;
if (xAverage > yAverage)
return 1;
else
return -1;
});
case Key.Right:
direction = 1;
skipDifficulties = true;
break;
default:
throw new NotImplementedException();
}
scrollableContent.Clear(false);
panels.Clear();
groups.Clear();
if (direction == 0)
return base.OnKeyDown(state, args);
foreach (BeatmapGroup group in sortedGroups)
AddGroup(group);
}
/// <summary>
/// Computes the x-offset of currently visible panels. Makes the carousel appear round.
/// </summary>
/// <param name="dist">
/// Vertical distance from the center of the carousel container
/// ranging from -1 to 1.
/// </param>
/// <param name="halfHeight">Half the height of the carousel container.</param>
private static float offsetX(float dist, float halfHeight)
{
// The radius of the circle the carousel moves on.
const float circle_radius = 3;
double discriminant = Math.Max(0, circle_radius * circle_radius - dist * dist);
float x = (circle_radius - (float)Math.Sqrt(discriminant)) * halfHeight;
return 125 + x;
}
/// <summary>
/// Update a panel's x position and multiplicative alpha based on its y position and
/// the current scroll position.
/// </summary>
/// <param name="p">The panel to be updated.</param>
/// <param name="halfHeight">Half the draw height of the carousel container.</param>
private void updatePanel(Panel p, float halfHeight)
{
var height = p.IsPresent ? p.DrawHeight : 0;
float panelDrawY = p.Position.Y - Current + height / 2;
float dist = Math.Abs(1f - panelDrawY / halfHeight);
// Setting the origin position serves as an additive position on top of potential
// local transformation we may want to apply (e.g. when a panel gets selected, we
// may want to smoothly transform it leftwards.)
p.OriginPosition = new Vector2(-offsetX(dist, halfHeight), 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
// layer transformations on top, with a similar reasoning to the previous comment.
p.SetMultiplicativeAlpha(MathHelper.Clamp(1.75f - 1.5f * dist, 0, 1));
SelectNext(direction, skipDifficulties);
return true;
}
protected override void Update()
@ -294,80 +421,46 @@ namespace osu.Game.Screens.Select
updatePanel(p, halfHeight);
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
/// <summary>
/// Computes the x-offset of currently visible panels. Makes the carousel appear round.
/// </summary>
/// <param name="dist">
/// Vertical distance from the center of the carousel container
/// ranging from -1 to 1.
/// </param>
/// <param name="halfHeight">Half the height of the carousel container.</param>
private static float offsetX(float dist, float halfHeight)
{
int direction = 0;
bool skipDifficulties = false;
// The radius of the circle the carousel moves on.
const float circle_radius = 3;
double discriminant = Math.Max(0, circle_radius * circle_radius - dist * dist);
float x = (circle_radius - (float)Math.Sqrt(discriminant)) * halfHeight;
switch (args.Key)
{
case Key.Up:
direction = -1;
break;
case Key.Down:
direction = 1;
break;
case Key.Left:
direction = -1;
skipDifficulties = true;
break;
case Key.Right:
direction = 1;
skipDifficulties = true;
break;
}
if (direction == 0)
return base.OnKeyDown(state, args);
SelectNext(direction, skipDifficulties);
return true;
return 125 + x;
}
public void SelectNext(int direction = 1, bool skipDifficulties = true)
/// <summary>
/// Update a panel's x position and multiplicative alpha based on its y position and
/// the current scroll position.
/// </summary>
/// <param name="p">The panel to be updated.</param>
/// <param name="halfHeight">Half the draw height of the carousel container.</param>
private void updatePanel(Panel p, float halfHeight)
{
if (!skipDifficulties && SelectedGroup != null)
{
int i = SelectedGroup.BeatmapPanels.IndexOf(SelectedPanel) + direction;
var height = p.IsPresent ? p.DrawHeight : 0;
if (i >= 0 && i < SelectedGroup.BeatmapPanels.Count)
{
//changing difficulty panel, not set.
selectGroup(SelectedGroup, SelectedGroup.BeatmapPanels[i]);
return;
}
}
float panelDrawY = p.Position.Y - Current + height / 2;
float dist = Math.Abs(1f - panelDrawY / halfHeight);
int startIndex = groups.IndexOf(SelectedGroup);
int index = startIndex;
// Setting the origin position serves as an additive position on top of potential
// local transformation we may want to apply (e.g. when a panel gets selected, we
// may want to smoothly transform it leftwards.)
p.OriginPosition = new Vector2(-offsetX(dist, halfHeight), 0);
do
{
index = (index + direction + groups.Count) % groups.Count;
if (groups[index].State != BeatmapGroupState.Hidden)
{
SelectBeatmap(groups[index].BeatmapPanels.First().Beatmap);
return;
}
} while (index != startIndex);
// 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
// layer transformations on top, with a similar reasoning to the previous comment.
p.SetMultiplicativeAlpha(MathHelper.Clamp(1.75f - 1.5f * dist, 0, 1));
}
public void SelectRandom()
{
List<BeatmapGroup> visibleGroups = groups.Where(selectGroup => selectGroup.State != BeatmapGroupState.Hidden).ToList();
if (visibleGroups.Count < 1)
return;
BeatmapGroup group = visibleGroups[RNG.Next(visibleGroups.Count)];
BeatmapPanel panel = group?.BeatmapPanels.First();
if (panel == null)
return;
selectGroup(group, panel);
}
public IEnumerator<BeatmapGroup> GetEnumerator() => groups.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}

View File

@ -0,0 +1,81 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Game.Beatmaps;
using osu.Game.Screens.Select.Leaderboards;
namespace osu.Game.Screens.Select
{
public class BeatmapDetailArea : Container
{
private readonly Container content;
protected override Container<Drawable> Content => content;
public readonly BeatmapDetails Details;
public readonly Leaderboard Leaderboard;
private WorkingBeatmap beatmap;
public WorkingBeatmap Beatmap
{
get
{
return beatmap;
}
set
{
beatmap = value;
Leaderboard.Beatmap = beatmap?.BeatmapInfo;
Details.Beatmap = beatmap?.BeatmapInfo;
}
}
public BeatmapDetailArea()
{
AddInternal(new Drawable[]
{
new BeatmapDetailAreaTabControl
{
RelativeSizeAxes = Axes.X,
OnFilter = (tab, mods) =>
{
switch (tab)
{
case BeatmapDetailTab.Details:
Details.Show();
Leaderboard.Hide();
break;
default:
Details.Hide();
Leaderboard.Show();
break;
}
},
},
content = new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = BeatmapDetailAreaTabControl.HEIGHT },
},
});
Add(new Drawable[]
{
Details = new BeatmapDetails
{
RelativeSizeAxes = Axes.X,
Masking = true,
Height = 352,
Alpha = 0,
},
Leaderboard = new Leaderboard
{
RelativeSizeAxes = Axes.Both,
}
});
}
}
}

View File

@ -0,0 +1,85 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Screens.Select
{
public class BeatmapDetailAreaTabControl : Container
{
public static readonly float HEIGHT = 24;
private readonly OsuTabControlCheckbox modsCheckbox;
private readonly OsuTabControl<BeatmapDetailTab> tabs;
public Action<BeatmapDetailTab, bool> OnFilter; //passed the selected tab and if mods is checked
private Bindable<BeatmapDetailTab> selectedTab;
private void invokeOnFilter()
{
OnFilter?.Invoke(tabs.Current, modsCheckbox.Current);
}
[BackgroundDependencyLoader]
private void load(OsuColour colour, OsuConfigManager config)
{
modsCheckbox.AccentColour = tabs.AccentColour = colour.YellowLight;
selectedTab = config.GetBindable<BeatmapDetailTab>(OsuConfig.BeatmapDetailTab);
tabs.Current.BindTo(selectedTab);
tabs.Current.TriggerChange();
}
public BeatmapDetailAreaTabControl()
{
Height = HEIGHT;
Children = new Drawable[]
{
new Box
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Height = 1,
Colour = Color4.White.Opacity(0.2f),
},
tabs = new OsuTabControl<BeatmapDetailTab>
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.Both,
},
modsCheckbox = new OsuTabControlCheckbox
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
Text = @"Mods",
},
};
tabs.Current.ValueChanged += item => invokeOnFilter();
modsCheckbox.Current.ValueChanged += item => invokeOnFilter();
}
}
public enum BeatmapDetailTab
{
Details,
Local,
Country,
Global,
Friends
}
}

View File

@ -0,0 +1,488 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using System.Globalization;
using System.Linq;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Framework.Threading;
namespace osu.Game.Screens.Select
{
public class BeatmapDetails : Container
{
private readonly MetadataSegment description;
private readonly MetadataSegment source;
private readonly MetadataSegment tags;
private readonly DifficultyRow circleSize;
private readonly DifficultyRow drainRate;
private readonly DifficultyRow overallDifficulty;
private readonly DifficultyRow approachRate;
private readonly DifficultyRow stars;
private readonly Container ratingsContainer;
private readonly Bar ratingsBar;
private readonly OsuSpriteText negativeRatings;
private readonly OsuSpriteText positiveRatings;
private readonly BarGraph ratingsGraph;
private readonly FillFlowContainer retryFailContainer;
private readonly BarGraph retryGraph;
private readonly BarGraph failGraph;
private ScheduledDelegate pendingBeatmapSwitch;
private BeatmapInfo beatmap;
public BeatmapInfo Beatmap
{
get { return beatmap; }
set
{
beatmap = value;
pendingBeatmapSwitch?.Cancel();
pendingBeatmapSwitch = Schedule(updateStats);
}
}
private void updateStats()
{
if (beatmap == null) return;
description.Text = beatmap.Version;
source.Text = beatmap.Metadata.Source;
tags.Text = beatmap.Metadata.Tags;
circleSize.Value = beatmap.Difficulty.CircleSize;
drainRate.Value = beatmap.Difficulty.DrainRate;
overallDifficulty.Value = beatmap.Difficulty.OverallDifficulty;
approachRate.Value = beatmap.Difficulty.ApproachRate;
stars.Value = (float)beatmap.StarDifficulty;
var requestedBeatmap = beatmap;
if (requestedBeatmap.Metrics == null)
{
var lookup = new GetBeatmapDetailsRequest(requestedBeatmap);
lookup.Success += res =>
{
if (beatmap != requestedBeatmap)
//the beatmap has been changed since we started the lookup.
return;
requestedBeatmap.Metrics = res;
Schedule(() => updateMetrics(res));
};
lookup.Failure += e => updateMetrics(null);
api.Queue(lookup);
}
updateMetrics(requestedBeatmap.Metrics, false);
}
/// <summary>
/// Update displayed metrics.
/// </summary>
/// <param name="metrics">New metrics to overwrite the existing display. Can be null.</param>
/// <param name="failOnMissing">Whether to hide the display on null or empty metrics. If false, we will dim as if waiting for further updates.</param>
private void updateMetrics(BeatmapMetrics metrics, bool failOnMissing = true)
{
var hasRatings = metrics?.Ratings.Any() ?? false;
var hasRetriesFails = (metrics?.Retries.Any() ?? false) && metrics.Fails.Any();
if (hasRatings)
{
var ratings = metrics.Ratings.ToList();
ratingsContainer.Show();
negativeRatings.Text = ratings.GetRange(0, ratings.Count / 2).Sum().ToString();
positiveRatings.Text = ratings.GetRange(ratings.Count / 2, ratings.Count / 2).Sum().ToString();
ratingsBar.Length = (float)ratings.GetRange(0, ratings.Count / 2).Sum() / ratings.Sum();
ratingsGraph.Values = ratings.Select(rating => (float)rating);
ratingsContainer.FadeColour(Color4.White, 500, EasingTypes.Out);
}
else if (failOnMissing)
ratingsGraph.Values = new float[10];
else
ratingsContainer.FadeColour(Color4.Gray, 500, EasingTypes.Out);
if (hasRetriesFails)
{
var retries = metrics.Retries;
var fails = metrics.Fails;
retryFailContainer.Show();
float maxValue = fails.Zip(retries, (fail, retry) => fail + retry).Max();
failGraph.MaxValue = maxValue;
retryGraph.MaxValue = maxValue;
failGraph.Values = fails.Select(fail => (float)fail);
retryGraph.Values = retries.Zip(fails, (retry, fail) => retry + MathHelper.Clamp(fail, 0, maxValue));
retryFailContainer.FadeColour(Color4.White, 500, EasingTypes.Out);
}
else if (failOnMissing)
{
failGraph.Values = new float[100];
retryGraph.Values = new float[100];
}
else
retryFailContainer.FadeColour(Color4.Gray, 500, EasingTypes.Out);
}
public BeatmapDetails()
{
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.5f,
},
new FillFlowContainer<MetadataSegment>()
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Width = 0.4f,
Direction = FillDirection.Vertical,
LayoutDuration = 200,
LayoutEasing = EasingTypes.OutQuint,
Children = new []
{
description = new MetadataSegment("Description"),
source = new MetadataSegment("Source"),
tags = new MetadataSegment("Tags")
},
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Width = 0.6f,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 15),
Padding = new MarginPadding(10) { Top = 0 },
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.5f,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0,5),
Padding = new MarginPadding(10),
Children = new []
{
circleSize = new DifficultyRow("Circle Size", 7),
drainRate = new DifficultyRow("HP Drain"),
overallDifficulty = new DifficultyRow("Accuracy"),
approachRate = new DifficultyRow("Approach Rate"),
stars = new DifficultyRow("Star Diffculty"),
},
},
},
},
ratingsContainer = new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Alpha = 0,
AlwaysPresent = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.5f,
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Padding = new MarginPadding
{
Top = 25,
Left = 15,
Right = 15,
},
Children = new Drawable[]
{
new OsuSpriteText
{
Text = "User Rating",
Font = @"Exo2.0-Medium",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
ratingsBar = new Bar
{
RelativeSizeAxes = Axes.X,
Height = 5,
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new[]
{
negativeRatings = new OsuSpriteText
{
Font = @"Exo2.0-Regular",
Text = "0",
},
positiveRatings = new OsuSpriteText
{
Font = @"Exo2.0-Regular",
Text = "0",
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
},
},
},
new OsuSpriteText
{
Text = "Rating Spread",
TextSize = 14,
Font = @"Exo2.0-Regular",
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
ratingsGraph = new BarGraph
{
RelativeSizeAxes = Axes.X,
Height = 50,
},
},
},
},
},
retryFailContainer = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Alpha = 0,
Children = new Drawable[]
{
new OsuSpriteText
{
Text = "Points of Failure",
Font = @"Exo2.0-Regular",
},
new Container<BarGraph>
{
RelativeSizeAxes = Axes.X,
Size = new Vector2(1 / 0.6f, 50),
Children = new[]
{
retryGraph = new BarGraph
{
RelativeSizeAxes = Axes.Both,
},
failGraph = new BarGraph
{
RelativeSizeAxes = Axes.Both,
},
},
},
}
},
},
}
};
}
private APIAccess api;
[BackgroundDependencyLoader]
private void load(OsuColour colour, APIAccess api)
{
this.api = api;
description.AccentColour = colour.GrayB;
source.AccentColour = colour.GrayB;
tags.AccentColour = colour.YellowLight;
stars.AccentColour = colour.Yellow;
ratingsBar.BackgroundColour = colour.Green;
ratingsBar.AccentColour = colour.YellowDark;
ratingsGraph.Colour = colour.BlueDark;
failGraph.Colour = colour.YellowDarker;
retryGraph.Colour = colour.Yellow;
}
private class DifficultyRow : Container, IHasAccentColour
{
private readonly OsuSpriteText name;
private readonly Bar bar;
private readonly OsuSpriteText valueText;
private readonly float maxValue;
private float difficultyValue;
public float Value
{
get
{
return difficultyValue;
}
set
{
difficultyValue = value;
bar.Length = value / maxValue;
valueText.Text = value.ToString("N1", CultureInfo.CurrentCulture);
}
}
public Color4 AccentColour
{
get
{
return bar.AccentColour;
}
set
{
bar.AccentColour = value;
}
}
public DifficultyRow(string difficultyName, float maxValue = 10)
{
this.maxValue = maxValue;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
name = new OsuSpriteText
{
Font = @"Exo2.0-Regular",
Text = difficultyName,
},
bar = new Bar
{
Origin = Anchor.CentreLeft,
Anchor = Anchor.CentreLeft,
RelativeSizeAxes = Axes.Both,
Size = new Vector2(1, 0.35f),
Padding = new MarginPadding { Left = 100, Right = 25 },
},
valueText = new OsuSpriteText
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Font = @"Exo2.0-Regular",
},
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colour)
{
name.Colour = colour.GrayB;
bar.BackgroundColour = colour.Gray7;
valueText.Colour = colour.GrayB;
}
}
private class MetadataSegment : Container, IHasAccentColour
{
private readonly OsuSpriteText header;
private readonly FillFlowContainer<OsuSpriteText> content;
public string Text
{
set
{
if (string.IsNullOrEmpty(value))
Hide();
else
{
Show();
if (header.Text == "Tags")
content.Children = value.Split(' ').Select(text => new OsuSpriteText
{
Text = text,
Font = "Exo2.0-Regular",
});
else
content.Children = new[]
{
new OsuSpriteText
{
Text = value,
Font = "Exo2.0-Regular",
}
};
}
}
}
public Color4 AccentColour
{
get
{
return content.Colour;
}
set
{
content.Colour = value;
}
}
public MetadataSegment(string headerText)
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Margin = new MarginPadding { Top = 10 };
Children = new Drawable[]
{
header = new OsuSpriteText
{
Font = @"Exo2.0-Bold",
Text = headerText,
},
content = new FillFlowContainer<OsuSpriteText>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Full,
Spacing = new Vector2(5,0),
Margin = new MarginPadding { Top = header.TextSize }
}
};
}
}
}
}

View File

@ -3,34 +3,33 @@
using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
using System.Linq;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Framework.Graphics.Colour;
using osu.Game.Beatmaps.Drawables;
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Modes;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Screens.Select
{
internal class BeatmapInfoWedge : Container
internal class BeatmapInfoWedge : OverlayContainer
{
private static readonly Vector2 wedged_container_shear = new Vector2(0.15f, 0);
private BufferedContainer beatmapInfoContainer;
private OsuGameBase game;
private Drawable beatmapInfoContainer;
public BeatmapInfoWedge()
{
@ -47,17 +46,34 @@ namespace osu.Game.Screens.Select
};
}
[BackgroundDependencyLoader]
private void load(OsuGameBase game)
protected override bool HideOnEscape => false;
protected override bool BlockPassThroughMouse => false;
protected override void PopIn()
{
this.game = game;
MoveToX(0, 800, EasingTypes.OutQuint);
RotateTo(0, 800, EasingTypes.OutQuint);
}
protected override void PopOut()
{
MoveToX(-100, 800, EasingTypes.InQuint);
RotateTo(10, 800, EasingTypes.InQuint);
}
public void UpdateBeatmap(WorkingBeatmap beatmap)
{
if (beatmap?.BeatmapInfo == null)
{
State = Visibility.Hidden;
beatmapInfoContainer?.FadeOut(250);
beatmapInfoContainer?.Expire();
beatmapInfoContainer = null;
return;
}
State = Visibility.Visible;
var lastContainer = beatmapInfoContainer;
float newDepth = lastContainer?.Depth + 1 ?? 0;
@ -69,11 +85,14 @@ namespace osu.Game.Screens.Select
if (beatmap.Beatmap != null)
{
HitObject lastObject = beatmap.Beatmap.HitObjects.LastOrDefault();
double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject?.StartTime ?? 0;
labels.Add(new InfoLabel(new BeatmapStatistic
{
Name = "Length",
Icon = FontAwesome.fa_clock_o,
Content = beatmap.Beatmap.HitObjects.Count == 0 ? "-" : TimeSpan.FromMilliseconds(beatmap.Beatmap.HitObjects.Last().EndTime - beatmap.Beatmap.HitObjects.First().StartTime).ToString(@"m\:ss"),
Content = beatmap.Beatmap.HitObjects.Count == 0 ? "-" : TimeSpan.FromMilliseconds(endTime - beatmap.Beatmap.HitObjects.First().StartTime).ToString(@"m\:ss"),
}));
labels.Add(new InfoLabel(new BeatmapStatistic
@ -84,119 +103,142 @@ namespace osu.Game.Screens.Select
}));
//get statistics fromt he current ruleset.
Ruleset.GetRuleset(beatmap.BeatmapInfo.Mode).GetBeatmapStatistics(beatmap).ForEach(s => labels.Add(new InfoLabel(s)));
labels.AddRange(beatmap.BeatmapInfo.Ruleset.CreateInstance().GetBeatmapStatistics(beatmap).Select(s => new InfoLabel(s)));
}
(beatmapInfoContainer = new BufferedContainer
{
Depth = newDepth,
PixelSnapping = true,
CacheDrawnFrameBuffer = true,
Shear = -Shear,
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
AlwaysPresent = true;
Add(beatmapInfoContainer = new AsyncLoadWrapper(
new BufferedContainer
{
// We will create the white-to-black gradient by modulating transparency and having
// a black backdrop. This results in an sRGB-space gradient and not linear space,
// transitioning from white to black more perceptually uniformly.
new Box
OnLoadComplete = d =>
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
FadeIn(250);
lastContainer?.FadeOut(250);
lastContainer?.Expire();
},
// We use a container, such that we can set the colour gradient to go across the
// vertices of the masked container instead of the vertices of the (larger) sprite.
new Container
PixelSnapping = true,
CacheDrawnFrameBuffer = true,
Shear = -Shear,
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
RelativeSizeAxes = Axes.Both,
ColourInfo = ColourInfo.GradientVertical(Color4.White, Color4.White.Opacity(0.3f)),
Children = new []
// We will create the white-to-black gradient by modulating transparency and having
// a black backdrop. This results in an sRGB-space gradient and not linear space,
// transitioning from white to black more perceptually uniformly.
new Box
{
// Zoomed-in and cropped beatmap background
new BeatmapBackgroundSprite(beatmap)
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
},
// We use a container, such that we can set the colour gradient to go across the
// vertices of the masked container instead of the vertices of the (larger) sprite.
new Container
{
RelativeSizeAxes = Axes.Both,
ColourInfo = ColourInfo.GradientVertical(Color4.White, Color4.White.Opacity(0.3f)),
Children = new[]
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
FillMode = FillMode.Fill,
// Zoomed-in and cropped beatmap background
new BeatmapBackgroundSprite(beatmap)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
FillMode = FillMode.Fill,
},
},
},
},
// Text for beatmap info
new FillFlowContainer
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Direction = FillDirection.Vertical,
Margin = new MarginPadding { Top = 10, Left = 25, Right = 10, Bottom = 20 },
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
new DifficultyColourBar(beatmap.BeatmapInfo)
{
new OsuSpriteText
RelativeSizeAxes = Axes.Y,
Width = 20,
},
new FillFlowContainer
{
Name = "Top-aligned metadata",
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Direction = FillDirection.Vertical,
Margin = new MarginPadding { Top = 10, Left = 25, Right = 10, Bottom = 20 },
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
Font = @"Exo2.0-MediumItalic",
Text = metadata.Artist + " -- " + metadata.Title,
TextSize = 28,
Shadow = true,
},
new OsuSpriteText
{
Font = @"Exo2.0-MediumItalic",
Text = beatmapInfo.Version,
TextSize = 17,
Shadow = true,
},
new FillFlowContainer
{
Margin = new MarginPadding { Top = 10 },
Direction = FillDirection.Horizontal,
AutoSizeAxes = Axes.Both,
Children = new []
new OsuSpriteText
{
new OsuSpriteText
{
Font = @"Exo2.0-Medium",
Text = "mapped by ",
TextSize = 15,
Shadow = true,
},
new OsuSpriteText
{
Font = @"Exo2.0-Bold",
Text = metadata.Author,
TextSize = 15,
Shadow = true,
},
}
},
new FillFlowContainer
Font = @"Exo2.0-MediumItalic",
Text = beatmapInfo.Version,
TextSize = 24,
},
}
},
new FillFlowContainer
{
Name = "Bottom-aligned metadata",
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Direction = FillDirection.Vertical,
Margin = new MarginPadding { Top = 15, Left = 25, Right = 10, Bottom = 20 },
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
Margin = new MarginPadding { Top = 20 },
Spacing = new Vector2(40, 0),
AutoSizeAxes = Axes.Both,
Children = labels
},
}
},
}
}).LoadAsync(game, delegate (Drawable d)
new OsuSpriteText
{
Font = @"Exo2.0-MediumItalic",
Text = !string.IsNullOrEmpty(metadata.Source) ? metadata.Source + " — " + metadata.Title : metadata.Title,
TextSize = 28,
},
new OsuSpriteText
{
Font = @"Exo2.0-MediumItalic",
Text = metadata.Artist,
TextSize = 17,
},
new FillFlowContainer
{
Margin = new MarginPadding { Top = 10 },
Direction = FillDirection.Horizontal,
AutoSizeAxes = Axes.Both,
Children = new[]
{
new OsuSpriteText
{
Font = @"Exo2.0-Medium",
Text = "mapped by ",
TextSize = 15,
},
new OsuSpriteText
{
Font = @"Exo2.0-Bold",
Text = metadata.Author,
TextSize = 15,
},
}
},
new FillFlowContainer
{
Margin = new MarginPadding { Top = 20, Left = 10 },
Spacing = new Vector2(40, 0),
AutoSizeAxes = Axes.Both,
Children = labels
},
}
},
}
})
{
FadeIn(250);
lastContainer?.FadeOut(250);
lastContainer?.Expire();
Add(d);
Depth = newDepth,
});
}
private string getBPMRange(Beatmap beatmap)
{
double bpmMax = beatmap.BPMMaximum;
double bpmMin = beatmap.BPMMinimum;
double bpmMax = beatmap.TimingInfo.BPMMaximum;
double bpmMin = beatmap.TimingInfo.BPMMinimum;
if (Precision.AlmostEquals(bpmMin, bpmMax)) return Math.Round(bpmMin) + "bpm";
return Math.Round(bpmMin) + "-" + Math.Round(bpmMax) + "bpm (mostly " + Math.Round(beatmap.BPMMode) + "bpm)";
return Math.Round(bpmMin) + "-" + Math.Round(bpmMax) + "bpm (mostly " + Math.Round(beatmap.TimingInfo.BPMMode) + "bpm)";
}
public class InfoLabel : Container
@ -211,14 +253,16 @@ namespace osu.Game.Screens.Select
Icon = FontAwesome.fa_square,
Origin = Anchor.Centre,
Colour = new Color4(68, 17, 136, 255),
Rotation = 45
Rotation = 45,
TextSize = 20
},
new TextAwesome
{
Icon = statistic.Icon,
Origin = Anchor.Centre,
Colour = new Color4(255, 221, 85, 255),
Scale = new Vector2(0.8f)
Scale = new Vector2(0.8f),
TextSize = 20
},
new OsuSpriteText
{
@ -232,5 +276,37 @@ namespace osu.Game.Screens.Select
};
}
}
private class DifficultyColourBar : DifficultyColouredContainer
{
public DifficultyColourBar(BeatmapInfo beatmap) : base(beatmap)
{
}
[BackgroundDependencyLoader]
private void load()
{
const float full_opacity_ratio = 0.7f;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = AccentColour,
Width = full_opacity_ratio,
},
new Box
{
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Both,
Colour = AccentColour,
Alpha = 0.5f,
X = full_opacity_ratio,
Width = 1 - full_opacity_ratio,
}
};
}
}
}
}

View File

@ -1,19 +1,12 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Edit;
namespace osu.Game.Screens.Select
{
internal class EditSongSelect : ScreenWhiteBox
public class EditSongSelect : SongSelect
{
protected override IEnumerable<Type> PossibleChildren => new[] {
typeof(Editor)
};
protected override bool ShowFooter => false;
protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4");
protected override void OnSelected() => Exit();
}
}

View File

@ -0,0 +1,41 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.ComponentModel;
namespace osu.Game.Screens.Select.Filter
{
public enum GroupMode
{
[Description("All")]
All,
[Description("Artist")]
Artist,
[Description("Author")]
Author,
[Description("BPM")]
BPM,
[Description("Collections")]
Collections,
[Description("Date Added")]
DateAdded,
[Description("Difficulty")]
Difficulty,
[Description("Favorites")]
Favorites,
[Description("Length")]
Length,
[Description("My Maps")]
MyMaps,
[Description("No Grouping")]
NoGrouping,
[Description("Rank Achieved")]
RankAchieved,
[Description("Ranked Status")]
RankedStatus,
[Description("Recently Played")]
RecentlyPlayed,
[Description("Title")]
Title
}
}

View File

@ -0,0 +1,27 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.ComponentModel;
namespace osu.Game.Screens.Select.Filter
{
public enum SortMode
{
[Description("Artist")]
Artist,
[Description("Author")]
Author,
[Description("BPM")]
BPM,
[Description("Date Added")]
DateAdded,
[Description("Difficulty")]
Difficulty,
[Description("Length")]
Length,
[Description("Rank Achieved")]
RankAchieved,
[Description("Title")]
Title
}
}

View File

@ -5,36 +5,70 @@ using System;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Select.Filter;
using Container = osu.Framework.Graphics.Containers.Container;
using osu.Framework.Input;
using osu.Game.Database;
namespace osu.Game.Screens.Select
{
public class FilterControl : Container
{
public Action FilterChanged;
public Action<FilterCriteria> FilterChanged;
private readonly OsuTabControl<SortMode> sortTabs;
private readonly TabControl<GroupMode> groupTabs;
public string Search => searchTextBox.Text;
private SortMode sort = SortMode.Title;
public SortMode Sort {
get { return sort; }
set {
public SortMode Sort
{
get { return sort; }
set
{
if (sort != value)
{
sort = value;
FilterChanged?.Invoke();
FilterChanged?.Invoke(CreateCriteria());
}
}
}
}
private GroupMode group = GroupMode.All;
public GroupMode Group
{
get { return group; }
set
{
if (group != value)
{
group = value;
FilterChanged?.Invoke(CreateCriteria());
}
}
}
public FilterCriteria CreateCriteria() => new FilterCriteria
{
Group = group,
Sort = sort,
SearchText = searchTextBox.Text,
Ruleset = ruleset
};
public Action Exit;
private SearchTextBox searchTextBox;
private readonly SearchTextBox searchTextBox;
protected override bool InternalContains(Vector2 screenSpacePos) => base.InternalContains(screenSpacePos) || groupTabs.Contains(screenSpacePos) || sortTabs.Contains(screenSpacePos);
public FilterControl()
{
@ -46,30 +80,76 @@ namespace osu.Game.Screens.Select
Alpha = 0.8f,
RelativeSizeAxes = Axes.Both,
},
new FillFlowContainer
new Container
{
Padding = new MarginPadding(20),
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
AlwaysReceiveInput = true,
RelativeSizeAxes = Axes.Both,
Width = 0.5f,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Width = 0.4f, // TODO: InnerWidth property or something
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
searchTextBox = new SearchTextBox {
searchTextBox = new SearchTextBox
{
RelativeSizeAxes = Axes.X,
OnChange = (sender, newText) =>
{
if (newText)
FilterChanged?.Invoke();
},
Exit = () => Exit?.Invoke(),
},
new GroupSortTabs()
new Box
{
RelativeSizeAxes = Axes.X,
Height = 1,
Colour = OsuColour.Gray(80),
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
},
new FillFlowContainer
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
Direction = FillDirection.Horizontal,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
AlwaysReceiveInput = true,
Children = new Drawable[]
{
groupTabs = new OsuTabControl<GroupMode>
{
RelativeSizeAxes = Axes.X,
Height = 24,
Width = 0.5f,
AutoSort = true
},
//spriteText = new OsuSpriteText
//{
// Font = @"Exo2.0-Bold",
// Text = "Sort results by",
// TextSize = 14,
// Margin = new MarginPadding
// {
// Top = 5,
// Bottom = 5
// },
//},
sortTabs = new OsuTabControl<SortMode>()
{
RelativeSizeAxes = Axes.X,
Width = 0.5f,
Height = 24,
AutoSort = true,
}
}
},
}
}
};
searchTextBox.Current.ValueChanged += t => FilterChanged?.Invoke(CreateCriteria());
groupTabs.PinItem(GroupMode.All);
groupTabs.PinItem(GroupMode.RecentlyPlayed);
groupTabs.Current.ValueChanged += val => Group = val;
sortTabs.Current.ValueChanged += val => Sort = val;
}
public void Deactivate()
@ -77,214 +157,31 @@ namespace osu.Game.Screens.Select
searchTextBox.HoldFocus = false;
searchTextBox.TriggerFocusLost();
}
public void Activate()
{
searchTextBox.HoldFocus = true;
}
private class TabItem : ClickableContainer
private readonly Bindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
[BackgroundDependencyLoader(permitNulls:true)]
private void load(OsuColour colours, OsuGame osu)
{
public string Text
{
get { return text.Text; }
set { text.Text = value; }
}
sortTabs.AccentColour = colours.GreenLight;
private void fadeActive()
{
box.FadeIn(300);
text.FadeColour(Color4.White, 300);
}
private void fadeInactive()
{
box.FadeOut(300);
text.FadeColour(fadeColour, 300);
}
private bool active;
public bool Active
{
get { return active; }
set
{
active = value;
if (active)
fadeActive();
else
fadeInactive();
}
}
private SpriteText text;
private Box box;
private Color4 fadeColour;
protected override bool OnHover(InputState state)
{
if (!active)
fadeActive();
return true;
}
protected override void OnHoverLost(InputState state)
{
if (!active)
fadeInactive();
}
public TabItem()
{
AutoSizeAxes = Axes.Both;
Children = new Drawable[]
{
text = new OsuSpriteText
{
Margin = new MarginPadding(5),
TextSize = 14,
Font = @"Exo2.0-Bold",
},
box = new Box
{
RelativeSizeAxes = Axes.X,
Height = 1,
Alpha = 0,
Colour = Color4.White,
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
text.Colour = colours.Blue;
fadeColour = colours.Blue;
}
if (osu != null)
ruleset.BindTo(osu.Ruleset);
ruleset.ValueChanged += val => FilterChanged?.Invoke(CreateCriteria());
ruleset.TriggerChange();
}
private class GroupSortTabs : Container
{
private TextAwesome groupsEllipsis, sortEllipsis;
private SpriteText sortLabel;
public GroupSortTabs()
{
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.X,
Height = 1,
Colour = OsuColour.Gray(80),
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Children = new Drawable[]
{
new TabItem
{
Text = "All",
Active = true,
},
new TabItem
{
Text = "Recently Played",
Active = false,
},
new TabItem
{
Text = "Collections",
Active = false,
},
groupsEllipsis = new TextAwesome
{
Icon = FontAwesome.fa_ellipsis_h,
Origin = Anchor.TopLeft,
TextSize = 14,
Margin = new MarginPadding { Top = 5, Bottom = 5 },
}
}
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Origin = Anchor.TopRight,
Anchor = Anchor.TopRight,
Children = new Drawable[]
{
sortLabel = new OsuSpriteText
{
Font = @"Exo2.0-Bold",
Text = "Sort results by",
TextSize = 14,
Margin = new MarginPadding { Top = 5, Bottom = 5 },
},
new TabItem
{
Text = "Artist",
Active = true,
},
sortEllipsis = new TextAwesome
{
Icon = FontAwesome.fa_ellipsis_h,
Origin = Anchor.TopLeft,
TextSize = 14,
Margin = new MarginPadding { Top = 5, Bottom = 5 },
}
}
},
};
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
groupsEllipsis.Colour = colours.Blue;
sortLabel.Colour = colours.GreenLight;
sortEllipsis.Colour = colours.GreenLight;
}
}
public enum SortMode
{
Artist,
BPM,
Author,
DateAdded,
Difficulty,
Length,
RankAchieved,
Title
}
protected override bool OnMouseMove(InputState state) => true;
public enum GroupMode
{
NoGrouping,
Artist,
BPM,
Author,
DateAdded,
Difficulty,
Length,
RankAchieved,
Title,
Collections,
Favorites,
MyMaps,
RankedStatus,
RecentlyPlayed
}
protected override bool OnClick(InputState state) => true;
protected override bool OnDragStart(InputState state) => true;
}
}

View File

@ -0,0 +1,67 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Database;
using osu.Game.Screens.Select.Filter;
namespace osu.Game.Screens.Select
{
public class FilterCriteria
{
public GroupMode Group;
public SortMode Sort;
public string SearchText;
public RulesetInfo Ruleset;
public void Filter(List<BeatmapGroup> groups)
{
foreach (var g in groups)
{
var set = g.BeatmapSet;
bool hasCurrentMode = set.Beatmaps.Any(bm => bm.RulesetID == (Ruleset?.ID ?? 0));
bool match = hasCurrentMode;
match &= string.IsNullOrEmpty(SearchText)
|| (set.Metadata.Artist ?? string.Empty).IndexOf(SearchText, StringComparison.InvariantCultureIgnoreCase) != -1
|| (set.Metadata.ArtistUnicode ?? string.Empty).IndexOf(SearchText, StringComparison.InvariantCultureIgnoreCase) != -1
|| (set.Metadata.Title ?? string.Empty).IndexOf(SearchText, StringComparison.InvariantCultureIgnoreCase) != -1
|| (set.Metadata.TitleUnicode ?? string.Empty).IndexOf(SearchText, StringComparison.InvariantCultureIgnoreCase) != -1
|| (set.Metadata.Tags ?? string.Empty).IndexOf(SearchText, StringComparison.InvariantCultureIgnoreCase) != -1
|| (set.Metadata.Source ?? string.Empty).IndexOf(SearchText, StringComparison.InvariantCultureIgnoreCase) != -1;
switch (g.State)
{
case BeatmapGroupState.Hidden:
if (match) g.State = BeatmapGroupState.Collapsed;
break;
default:
if (!match) g.State = BeatmapGroupState.Hidden;
break;
}
}
switch (Sort)
{
default:
case SortMode.Artist:
groups.Sort((x, y) => string.Compare(x.BeatmapSet.Metadata.Artist, y.BeatmapSet.Metadata.Artist, StringComparison.InvariantCultureIgnoreCase));
break;
case SortMode.Title:
groups.Sort((x, y) => string.Compare(x.BeatmapSet.Metadata.Title, y.BeatmapSet.Metadata.Title, StringComparison.InvariantCultureIgnoreCase));
break;
case SortMode.Author:
groups.Sort((x, y) => string.Compare(x.BeatmapSet.Metadata.Author, y.BeatmapSet.Metadata.Author, StringComparison.InvariantCultureIgnoreCase));
break;
case SortMode.Difficulty:
groups.Sort((x, y) => x.BeatmapSet.MaxStarDifficulty.CompareTo(y.BeatmapSet.MaxStarDifficulty));
break;
}
}
}
}

View File

@ -4,11 +4,11 @@
using System;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Menu;
@ -16,33 +16,43 @@ namespace osu.Game.Screens.Select
{
public class Footer : Container
{
private Box modeLight;
private readonly Box modeLight;
private const float play_song_select_button_width = 100;
private const float play_song_select_button_height = 50;
public const float HEIGHT = 50;
public const int TRANSITION_LENGTH = 300;
private const float padding = 80;
public override bool Contains(Vector2 screenSpacePos) => true;
public Action OnBack;
public Action OnStart;
private FillFlowContainer buttons;
private readonly FillFlowContainer buttons;
public OsuLogo StartButton;
public void AddButton(string text, Color4 colour, Action action)
/// <param name="text">Text on the button.</param>
/// <param name="colour">Colour of the button.</param>
/// <param name="hotkey">Hotkey of the button.</param>
/// <param name="action">Action the button does.</param>
/// <param name="depth">
/// <para>Higher depth to be put on the left, and lower to be put on the right.</para>
/// <para>Notice this is different to <see cref="Options.BeatmapOptionsOverlay"/>!</para>
/// </param>
public void AddButton(string text, Color4 colour, Action action, Key? hotkey = null, float depth = 0)
{
var button = new FooterButton
{
Text = text,
Height = play_song_select_button_height,
Width = play_song_select_button_width,
Depth = depth,
SelectedColour = colour,
DeselectedColour = colour.Opacity(0.5f),
Hotkey = hotkey,
};
button.Hovered = () => updateModeLight(button);
@ -58,10 +68,10 @@ namespace osu.Game.Screens.Select
public Footer()
{
const float bottom_tool_height = 50;
AlwaysReceiveInput = true;
RelativeSizeAxes = Axes.X;
Height = bottom_tool_height;
Height = HEIGHT;
Anchor = Anchor.BottomCentre;
Origin = Anchor.BottomCentre;
Children = new Drawable[]
@ -89,7 +99,7 @@ namespace osu.Game.Screens.Select
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Action = () => OnBack?.Invoke(),
Action = () => OnBack?.Invoke()
},
new FillFlowContainer
{

View File

@ -1,14 +1,14 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input;
using System;
using osu.Framework.Graphics.Transforms;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Screens.Select
@ -34,7 +34,7 @@ namespace osu.Game.Screens.Select
set
{
deselectedColour = value;
if(light.Colour != SelectedColour)
if (light.Colour != SelectedColour)
light.Colour = value;
}
}
@ -50,9 +50,9 @@ namespace osu.Game.Screens.Select
}
}
private SpriteText spriteText;
private Box box;
private Box light;
private readonly SpriteText spriteText;
private readonly Box box;
private readonly Box light;
public FooterButton()
{
@ -83,6 +83,7 @@ namespace osu.Game.Screens.Select
public Action Hovered;
public Action HoverLost;
public Key? Hotkey;
protected override bool OnHover(InputState state)
{
@ -119,5 +120,15 @@ namespace osu.Game.Screens.Select
return base.OnClick(state);
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (!args.Repeat && args.Key == Hotkey)
{
OnClick(state);
return true;
}
return base.OnKeyDown(state, args);
}
}
}

View File

@ -0,0 +1,41 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Extensions;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Screens.Select.Leaderboards
{
public class DrawableRank : Container
{
private readonly Sprite rankSprite;
public ScoreRank Rank { get; private set; }
[BackgroundDependencyLoader]
private void load(TextureStore textures)
{
rankSprite.Texture = textures.Get($@"Grades/{Rank.GetDescription()}");
}
public DrawableRank(ScoreRank rank)
{
Rank = rank;
Children = new Drawable[]
{
rankSprite = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
FillMode = FillMode.Fit
},
};
}
}
}

View File

@ -0,0 +1,160 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using System;
using osu.Framework.Allocation;
using osu.Framework.Threading;
using osu.Game.Database;
using osu.Game.Rulesets.Scoring;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
namespace osu.Game.Screens.Select.Leaderboards
{
public class Leaderboard : Container
{
private readonly ScrollContainer scrollContainer;
private readonly FillFlowContainer<LeaderboardScore> scrollFlow;
public Action<Score> ScoreSelected;
private IEnumerable<Score> scores;
public IEnumerable<Score> Scores
{
get { return scores; }
set
{
scores = value;
getScoresRequest?.Cancel();
int i = 150;
if (scores == null)
{
foreach (var c in scrollFlow.Children)
c.FadeOut(i += 10);
foreach (var c in scrollFlow.Children)
c.LifetimeEnd = Time.Current + i;
return;
}
scrollFlow.Clear();
i = 0;
foreach (var s in scores)
{
var ls = new LeaderboardScore(s, 1 + i)
{
AlwaysPresent = true,
Action = () => ScoreSelected?.Invoke(s),
State = Visibility.Hidden,
};
scrollFlow.Add(ls);
ls.Delay(i++ * 50, true);
ls.Show();
}
scrollContainer.ScrollTo(0f, false);
}
}
public Leaderboard()
{
Children = new Drawable[]
{
scrollContainer = new ScrollContainer
{
RelativeSizeAxes = Axes.Both,
ScrollDraggerVisible = false,
Children = new Drawable[]
{
scrollFlow = new FillFlowContainer<LeaderboardScore>
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(0f, 5f),
Padding = new MarginPadding { Top = 10, Bottom = 5 },
},
},
},
};
}
private APIAccess api;
private BeatmapInfo beatmap;
private ScheduledDelegate pendingBeatmapSwitch;
public BeatmapInfo Beatmap
{
get { return beatmap; }
set
{
beatmap = value;
Scores = null;
pendingBeatmapSwitch?.Cancel();
pendingBeatmapSwitch = Schedule(updateScores);
}
}
[BackgroundDependencyLoader(permitNulls: true)]
private void load(APIAccess api)
{
this.api = api;
}
private GetScoresRequest getScoresRequest;
private void updateScores()
{
if (!IsLoaded) return;
Scores = null;
getScoresRequest?.Cancel();
if (api == null || Beatmap == null) return;
getScoresRequest = new GetScoresRequest(Beatmap);
getScoresRequest.Success += r => Scores = r.Scores;
api.Queue(getScoresRequest);
}
protected override void Update()
{
base.Update();
var fadeStart = scrollContainer.Current + scrollContainer.DrawHeight;
if (!scrollContainer.IsScrolledToEnd())
fadeStart -= LeaderboardScore.HEIGHT;
foreach (var c in scrollFlow.Children)
{
var topY = c.ToSpaceOfOtherDrawable(Vector2.Zero, scrollFlow).Y;
var bottomY = topY + LeaderboardScore.HEIGHT;
if (bottomY < fadeStart)
c.Colour = Color4.White;
else if (topY > fadeStart + LeaderboardScore.HEIGHT)
c.Colour = Color4.Transparent;
else
{
c.ColourInfo = ColourInfo.GradientVertical(
Color4.White.Opacity(Math.Min(1 - (topY - fadeStart) / LeaderboardScore.HEIGHT, 1)),
Color4.White.Opacity(Math.Min(1 - (bottomY - fadeStart) / LeaderboardScore.HEIGHT, 1)));
}
}
}
}
}

View File

@ -0,0 +1,393 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Framework.Extensions.Color4Extensions;
using osu.Game.Rulesets.Mods;
using osu.Game.Users;
using osu.Framework;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Screens.Select.Leaderboards
{
public class LeaderboardScore : ClickableContainer, IStateful<Visibility>
{
public static readonly float HEIGHT = 60;
public readonly int RankPosition;
public readonly Score Score;
private const float corner_radius = 5;
private const float edge_margin = 5;
private const float background_alpha = 0.25f;
private const float rank_width = 30;
private readonly Box background;
private readonly Container content;
private readonly Container avatar;
private readonly DrawableRank scoreRank;
private readonly OsuSpriteText nameLabel;
private readonly GlowingSpriteText scoreLabel;
private readonly ScoreComponentLabel maxCombo;
private readonly ScoreComponentLabel accuracy;
private readonly Container flagBadgeContainer;
private readonly FillFlowContainer<ScoreModIcon> modsContainer;
private Visibility state;
public Visibility State
{
get { return state; }
set
{
state = value;
switch (state)
{
case Visibility.Hidden:
foreach (var d in new Drawable[] { avatar, nameLabel, scoreLabel, scoreRank, flagBadgeContainer, maxCombo, accuracy, modsContainer })
d.FadeOut();
Alpha = 0;
content.MoveToY(75);
avatar.MoveToX(75);
nameLabel.MoveToX(150);
break;
case Visibility.Visible:
FadeIn(200);
content.MoveToY(0, 800, EasingTypes.OutQuint);
Delay(100, true);
avatar.FadeIn(300, EasingTypes.OutQuint);
nameLabel.FadeIn(350, EasingTypes.OutQuint);
avatar.MoveToX(0, 300, EasingTypes.OutQuint);
nameLabel.MoveToX(0, 350, EasingTypes.OutQuint);
Delay(250, true);
scoreLabel.FadeIn(200);
scoreRank.FadeIn(200);
Delay(50, true);
var drawables = new Drawable[] { flagBadgeContainer, maxCombo, accuracy, modsContainer, };
for (int i = 0; i < drawables.Length; i++)
{
drawables[i].FadeIn(100 + i * 50);
}
break;
}
}
}
public LeaderboardScore(Score score, int rank)
{
Score = score;
RankPosition = rank;
RelativeSizeAxes = Axes.X;
Height = HEIGHT;
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Y,
Width = rank_width,
Children = new[]
{
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = @"Exo2.0-MediumItalic",
TextSize = 22,
Text = RankPosition.ToString(),
},
},
},
content = new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = rank_width, },
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
CornerRadius = corner_radius,
Masking = true,
Children = new[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = background_alpha,
},
},
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(edge_margin),
Children = new Drawable[]
{
avatar = new DelayedLoadWrapper(
new Avatar(Score.User)
{
RelativeSizeAxes = Axes.Both,
CornerRadius = corner_radius,
Masking = true,
OnLoadComplete = d => d.FadeInFromZero(200),
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Shadow,
Radius = 1,
Colour = Color4.Black.Opacity(0.2f),
},
})
{
TimeBeforeLoad = 500,
RelativeSizeAxes = Axes.None,
Size = new Vector2(HEIGHT - edge_margin * 2, HEIGHT - edge_margin * 2),
},
new Container
{
RelativeSizeAxes = Axes.Y,
AutoSizeAxes = Axes.X,
Position = new Vector2(HEIGHT - edge_margin, 0f),
Children = new Drawable[]
{
nameLabel = new OsuSpriteText
{
Text = Score.User.Username,
Font = @"Exo2.0-BoldItalic",
TextSize = 23,
},
new FillFlowContainer
{
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10f, 0f),
Children = new Drawable[]
{
flagBadgeContainer = new Container
{
Size = new Vector2(87f, 20f),
Masking = true,
Children = new Drawable[]
{
new DrawableFlag(Score.User?.Country?.FlagName ?? "__")
{
Width = 30,
RelativeSizeAxes = Axes.Y,
},
},
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10f, 0f),
Margin = new MarginPadding { Left = edge_margin, },
Children = new Drawable[]
{
maxCombo = new ScoreComponentLabel(FontAwesome.fa_link, Score.MaxCombo.ToString()),
accuracy = new ScoreComponentLabel(FontAwesome.fa_crosshairs, string.Format(Score.Accuracy % 1 == 0 ? @"{0:P0}" : @"{0:P2}", Score.Accuracy)),
},
},
},
},
},
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(5f, 0f),
Children = new Drawable[]
{
scoreLabel = new GlowingSpriteText(Score.TotalScore.ToString(@"N0"), @"Venera", 23, Color4.White, OsuColour.FromHex(@"83ccfa")),
new Container
{
Size = new Vector2(40f, 20f),
Children = new[]
{
scoreRank = new DrawableRank(Score.Rank)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(40f),
},
},
},
},
},
modsContainer = new FillFlowContainer<ScoreModIcon>
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
},
},
},
},
},
};
if (Score.Mods != null)
{
foreach (Mod mod in Score.Mods)
{
// TODO: Get actual mod colours
modsContainer.Add(new ScoreModIcon(mod.Icon, OsuColour.FromHex(@"ffcc22")));
}
}
}
public void ToggleVisibility() => State = State == Visibility.Visible ? Visibility.Hidden : Visibility.Visible;
public override void Hide() => State = Visibility.Hidden;
public override void Show() => State = Visibility.Visible;
protected override bool OnHover(Framework.Input.InputState state)
{
background.FadeTo(0.5f, 300, EasingTypes.OutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(Framework.Input.InputState state)
{
background.FadeTo(background_alpha, 200, EasingTypes.OutQuint);
base.OnHoverLost(state);
}
private class GlowingSpriteText : Container
{
public GlowingSpriteText(string text, string font, int textSize, Color4 textColour, Color4 glowColour)
{
AutoSizeAxes = Axes.Both;
Children = new Drawable[]
{
new BufferedContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
BlurSigma = new Vector2(4),
CacheDrawnFrameBuffer = true,
RelativeSizeAxes = Axes.Both,
BlendingMode = BlendingMode.Additive,
Size = new Vector2(3f),
Children = new[]
{
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = font,
TextSize = textSize,
FixedWidth = true,
Text = text,
Colour = glowColour,
Shadow = false,
},
},
},
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = font,
FixedWidth = true,
TextSize = textSize,
Text = text,
Colour = textColour,
Shadow = false,
},
};
}
}
private class ScoreModIcon : Container
{
public ScoreModIcon(FontAwesome icon, Color4 colour)
{
AutoSizeAxes = Axes.Both;
Children = new[]
{
new TextAwesome
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Icon = FontAwesome.fa_osu_mod_bg,
Colour = colour,
Shadow = true,
TextSize = 30,
UseFullGlyphHeight = false,
},
new TextAwesome
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Icon = icon,
Colour = OsuColour.Gray(84),
TextSize = 18,
Position = new Vector2(0f, 2f),
UseFullGlyphHeight = false,
},
};
}
}
private class ScoreComponentLabel : Container
{
public ScoreComponentLabel(FontAwesome icon, string value)
{
Anchor = Anchor.CentreLeft;
Origin = Anchor.CentreLeft;
Size = new Vector2(60f, 20f);
Padding = new MarginPadding { Top = 10f, };
Children = new Drawable[]
{
new TextAwesome
{
Origin = Anchor.Centre,
Icon = FontAwesome.fa_square,
Colour = OsuColour.FromHex(@"3087ac"),
Rotation = 45,
Shadow = true,
},
new TextAwesome
{
Origin = Anchor.Centre,
Icon = icon,
Colour = OsuColour.FromHex(@"a4edff"),
Scale = new Vector2(0.8f),
},
new GlowingSpriteText(value, @"Exo2.0-Bold", 17, Color4.White, OsuColour.FromHex(@"83ccfa"))
{
Origin = Anchor.CentreLeft,
Margin = new MarginPadding { Left = 15, },
},
};
}
}
}
}

View File

@ -1,12 +1,10 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Screens.Backgrounds;
namespace osu.Game.Screens.Select
{
internal class MatchSongSelect : ScreenWhiteBox
public class MatchSongSelect : SongSelect
{
protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4");
protected override void OnSelected() => Exit();
}
}

View File

@ -3,12 +3,12 @@
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Input;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
@ -17,12 +17,14 @@ namespace osu.Game.Screens.Select.Options
{
public class BeatmapOptionsButton : ClickableContainer
{
private static readonly float width = 130;
private const float width = 130;
private Box background, flash;
private TextAwesome iconText;
private OsuSpriteText firstLine, secondLine;
private Container box;
private readonly Box background;
private readonly Box flash;
private readonly TextAwesome iconText;
private readonly OsuSpriteText firstLine;
private readonly OsuSpriteText secondLine;
private readonly Container box;
public Color4 ButtonColour
{
@ -48,6 +50,8 @@ namespace osu.Game.Screens.Select.Options
set { secondLine.Text = value; }
}
public Key? HotKey;
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
flash.FadeTo(0.1f, 1000, EasingTypes.OutQuint);
@ -69,7 +73,18 @@ namespace osu.Game.Screens.Select.Options
return base.OnClick(state);
}
public override bool Contains(Vector2 screenSpacePos) => box.Contains(screenSpacePos);
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (!args.Repeat && args.Key == HotKey)
{
OnClick(state);
return true;
}
return false;
}
protected override bool InternalContains(Vector2 screenSpacePos) => box.Contains(screenSpacePos);
public BeatmapOptionsButton()
{

View File

@ -1,24 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Game.Graphics;
namespace osu.Game.Screens.Select.Options
{
public class BeatmapOptionsClearLocalScoresButton : BeatmapOptionsButton
{
[BackgroundDependencyLoader]
private void load(OsuColour colour)
{
ButtonColour = colour.Purple;
}
public BeatmapOptionsClearLocalScoresButton()
{
Icon = FontAwesome.fa_eraser;
FirstLineText = @"Clear";
SecondLineText = @"local scores";
}
}
}

View File

@ -1,24 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Game.Graphics;
namespace osu.Game.Screens.Select.Options
{
public class BeatmapOptionsDeleteButton : BeatmapOptionsButton
{
[BackgroundDependencyLoader]
private void load(OsuColour colour)
{
ButtonColour = colour.Pink;
}
public BeatmapOptionsDeleteButton()
{
Icon = FontAwesome.fa_trash;
FirstLineText = @"Delete";
SecondLineText = @"Beatmap";
}
}
}

View File

@ -1,24 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Game.Graphics;
namespace osu.Game.Screens.Select.Options
{
public class BeatmapOptionsEditButton : BeatmapOptionsButton
{
[BackgroundDependencyLoader]
private void load(OsuColour colour)
{
ButtonColour = colour.Yellow;
}
public BeatmapOptionsEditButton()
{
Icon = FontAwesome.fa_pencil;
FirstLineText = @"Edit";
SecondLineText = @"Beatmap";
}
}
}

View File

@ -6,37 +6,34 @@ using System.Collections.Generic;
using System.Linq;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms;
using osu.Game.Graphics;
namespace osu.Game.Screens.Select.Options
{
public class BeatmapOptionsOverlay : FocusedOverlayContainer
{
private const float transition_duration = 500;
private const float x_position = 290;
private const float x_position = 0.2f;
private const float x_movement = 0.8f;
private const float height = 100;
private Box holder;
private FillFlowContainer<BeatmapOptionsButton> buttonsContainer;
public Action OnRemoveFromUnplayed;
public Action OnClearLocalScores;
public Action OnEdit;
public Action OnDelete;
private readonly Box holder;
private readonly FillFlowContainer<BeatmapOptionsButton> buttonsContainer;
protected override void PopIn()
{
base.PopIn();
if (buttonsContainer.Position.X >= DrawWidth || buttonsContainer.Alpha <= 0)
buttonsContainer.MoveToX(-buttonsContainer.DrawWidth);
FadeIn(transition_duration, EasingTypes.OutQuint);
buttonsContainer.Alpha = 1;
if (buttonsContainer.Position.X == 1 || Alpha == 0)
buttonsContainer.MoveToX(x_position - x_movement);
holder.ScaleTo(new Vector2(1, 1), transition_duration / 2, EasingTypes.OutQuint);
@ -50,15 +47,10 @@ namespace osu.Game.Screens.Select.Options
holder.ScaleTo(new Vector2(1, 0), transition_duration / 2, EasingTypes.InSine);
buttonsContainer.MoveToX(DrawWidth, transition_duration, EasingTypes.InSine);
buttonsContainer.MoveToX(x_position + x_movement, transition_duration, EasingTypes.InSine);
buttonsContainer.TransformSpacingTo(new Vector2(200f, 0f), transition_duration, EasingTypes.InSine);
Delay(transition_duration);
Schedule(() =>
{
if (State == Visibility.Hidden)
buttonsContainer.Alpha = 0;
});
FadeOut(transition_duration, EasingTypes.InQuint);
}
public BeatmapOptionsOverlay()
@ -82,48 +74,42 @@ namespace osu.Game.Screens.Select.Options
buttonsContainer = new ButtonFlow
{
Height = height,
RelativePositionAxes = Axes.X,
AutoSizeAxes = Axes.X,
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
Children = new BeatmapOptionsButton[]
{
new BeatmapOptionsRemoveFromUnplayedButton
{
Action = () =>
{
Hide();
OnRemoveFromUnplayed?.Invoke();
},
},
new BeatmapOptionsClearLocalScoresButton
{
Action = () =>
{
Hide();
OnClearLocalScores?.Invoke();
},
},
new BeatmapOptionsEditButton
{
Action = () =>
{
Hide();
OnEdit?.Invoke();
},
},
new BeatmapOptionsDeleteButton
{
Action = () =>
{
Hide();
OnDelete?.Invoke();
},
},
},
},
};
}
/// <param name="firstLine">Text in the first line.</param>
/// <param name="secondLine">Text in the second line.</param>
/// <param name="colour">Colour of the button.</param>
/// <param name="icon">Icon of the button.</param>
/// <param name="hotkey">Hotkey of the button.</param>
/// <param name="action">Action the button does.</param>
/// <param name="depth">
/// <para>Lower depth to be put on the left, and higher to be put on the right.</para>
/// <para>Notice this is different to <see cref="Footer"/>!</para>
/// </param>
public void AddButton(string firstLine, string secondLine, FontAwesome icon, Color4 colour, Action action, Key? hotkey = null, float depth = 0)
{
buttonsContainer.Add(new BeatmapOptionsButton
{
FirstLineText = firstLine,
SecondLineText = secondLine,
Icon = icon,
ButtonColour = colour,
Depth = depth,
Action = () =>
{
Hide();
action?.Invoke();
},
HotKey = hotkey
});
}
private class ButtonFlow : FillFlowContainer<BeatmapOptionsButton>
{
protected override IComparer<Drawable> DepthComparer => new ReverseCreationOrderDepthComparer();

View File

@ -1,24 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Game.Graphics;
namespace osu.Game.Screens.Select.Options
{
public class BeatmapOptionsRemoveFromUnplayedButton : BeatmapOptionsButton
{
[BackgroundDependencyLoader]
private void load(OsuColour colour)
{
ButtonColour = colour.Purple;
}
public BeatmapOptionsRemoveFromUnplayedButton()
{
Icon = FontAwesome.fa_times_circle_o;
FirstLineText = @"Remove";
SecondLineText = @"from Unplayed";
}
}
}

View File

@ -1,474 +1,93 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Configuration;
using osu.Framework.Screens;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Modes;
using osu.Game.Screens.Backgrounds;
using OpenTK;
using osu.Game.Screens.Play;
using osu.Framework.Audio.Sample;
using osu.Framework.Graphics.Transforms;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics;
using osu.Framework.Input;
using OpenTK.Input;
using System.Collections.Generic;
using osu.Framework.Threading;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Overlays.Mods;
using osu.Game.Overlays;
using osu.Game.Screens.Select.Options;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking;
namespace osu.Game.Screens.Select
{
public class PlaySongSelect : OsuScreen
public class PlaySongSelect : SongSelect
{
private Bindable<PlayMode> playMode = new Bindable<PlayMode>();
private BeatmapDatabase database;
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap);
private CarouselContainer carousel;
private TrackManager trackManager;
private DialogOverlay dialogOverlay;
private static readonly Vector2 wedged_container_size = new Vector2(0.5f, 225);
private BeatmapInfoWedge beatmapInfoWedge;
private ModSelectOverlay modSelect;
private static readonly Vector2 background_blur = new Vector2(20);
private CancellationTokenSource initialAddSetsTask;
private SampleChannel sampleChangeDifficulty;
private SampleChannel sampleChangeBeatmap;
private List<BeatmapGroup> beatmapGroups;
private BeatmapOptionsOverlay beatmapOptions;
private Footer footer;
private OsuScreen player;
private readonly ModSelectOverlay modSelect;
private readonly BeatmapDetailArea beatmapDetails;
private FilterControl filter;
public FilterControl Filter
public PlaySongSelect()
{
get
FooterPanels.Add(modSelect = new ModSelectOverlay
{
return filter;
}
private set
RelativeSizeAxes = Axes.X,
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
});
LeftContent.Add(beatmapDetails = new BeatmapDetailArea
{
if (filter != value)
{
filter = value;
filterChanged();
}
}
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = 10, Right = 5 },
});
beatmapDetails.Leaderboard.ScoreSelected += s => Push(new Results(s));
}
[BackgroundDependencyLoader(permitNulls: true)]
private void load(BeatmapDatabase beatmaps, AudioManager audio, DialogOverlay dialog, Framework.Game game,
OsuGame osu, OsuColour colours)
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
const float carousel_width = 640;
const float filter_height = 100;
Footer.AddButton(@"mods", colours.Yellow, modSelect.ToggleVisibility, Key.F1, float.MaxValue);
beatmapGroups = new List<BeatmapGroup>();
Children = new Drawable[]
BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.fa_times_circle_o, colours.Purple, null, Key.Number1);
BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.fa_eraser, colours.Purple, null, Key.Number2);
BeatmapOptions.AddButton(@"Edit", @"Beatmap", FontAwesome.fa_pencil, colours.Yellow, () =>
{
new ParallaxContainer
{
Padding = new MarginPadding { Top = filter_height },
ParallaxAmount = 0.005f,
RelativeSizeAxes = Axes.Both,
Children = new[]
{
new WedgeBackground
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding
{
Right = carousel_width * 0.76f
},
},
}
},
carousel = new CarouselContainer
{
RelativeSizeAxes = Axes.Y,
Size = new Vector2(carousel_width, 1),
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
},
filter = new FilterControl
{
RelativeSizeAxes = Axes.X,
Height = filter_height,
FilterChanged = () => filterChanged(),
Exit = Exit,
},
beatmapInfoWedge = new BeatmapInfoWedge
{
Alpha = 0,
Size = wedged_container_size,
RelativeSizeAxes = Axes.X,
Margin = new MarginPadding
{
Top = 20,
Right = 20,
},
},
beatmapOptions = new BeatmapOptionsOverlay
{
OnRemoveFromUnplayed = null,
OnClearLocalScores = null,
OnEdit = null,
OnDelete = promptDelete,
Margin = new MarginPadding
{
Bottom = 50,
},
},
modSelect = new ModSelectOverlay
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
Margin = new MarginPadding
{
Bottom = 50,
},
},
footer = new Footer
{
OnBack = Exit,
OnStart = () =>
{
if (player != null || Beatmap == null)
return;
Beatmap.PreferredPlayMode = playMode.Value;
(player = new PlayerLoader(new Player
{
Beatmap = Beatmap, //eagerly set this so it's present before push.
})).LoadAsync(Game, l => Push(player));
}
},
};
footer.AddButton(@"mods", colours.Yellow, modSelect.ToggleVisibility);
footer.AddButton(@"random", colours.Green, carousel.SelectRandom);
footer.AddButton(@"options", colours.Blue, beatmapOptions.ToggleVisibility);
if (osu != null)
playMode.BindTo(osu.PlayMode);
playMode.ValueChanged += playMode_ValueChanged;
if (database == null)
database = beatmaps;
database.BeatmapSetAdded += onBeatmapSetAdded;
database.BeatmapSetRemoved += onBeatmapSetRemoved;
trackManager = audio.Track;
dialogOverlay = dialog;
sampleChangeDifficulty = audio.Sample.Get(@"SongSelect/select-difficulty");
sampleChangeBeatmap = audio.Sample.Get(@"SongSelect/select-expand");
initialAddSetsTask = new CancellationTokenSource();
Task.Factory.StartNew(() => addBeatmapSets(game, initialAddSetsTask.Token), initialAddSetsTask.Token);
ValidForResume = false;
Push(new Editor());
}, Key.Number3);
}
private ScheduledDelegate filterTask;
private void filterChanged(bool debounce = true, bool eagerSelection = true)
protected override void OnBeatmapChanged(WorkingBeatmap beatmap)
{
filterTask?.Cancel();
filterTask = Scheduler.AddDelayed(() =>
{
filterTask = null;
var search = filter.Search;
BeatmapGroup newSelection = null;
carousel.Sort(filter.Sort);
foreach (var beatmapGroup in carousel)
{
var set = beatmapGroup.BeatmapSet;
beatmap?.Mods.BindTo(modSelect.SelectedMods);
bool hasCurrentMode = set.Beatmaps.Any(bm => bm.Mode == playMode);
beatmapDetails.Beatmap = beatmap;
bool match = hasCurrentMode;
match &= string.IsNullOrEmpty(search)
|| (set.Metadata.Artist ?? "").IndexOf(search, StringComparison.InvariantCultureIgnoreCase) != -1
|| (set.Metadata.ArtistUnicode ?? "").IndexOf(search, StringComparison.InvariantCultureIgnoreCase) != -1
|| (set.Metadata.Title ?? "").IndexOf(search, StringComparison.InvariantCultureIgnoreCase) != -1
|| (set.Metadata.TitleUnicode ?? "").IndexOf(search, StringComparison.InvariantCultureIgnoreCase) != -1;
if (match)
{
if (newSelection == null || beatmapGroup.BeatmapSet.OnlineBeatmapSetID == Beatmap.BeatmapSetInfo.OnlineBeatmapSetID)
{
if (newSelection != null)
newSelection.State = BeatmapGroupState.Collapsed;
newSelection = beatmapGroup;
}
else
beatmapGroup.State = BeatmapGroupState.Collapsed;
}
else
{
beatmapGroup.State = BeatmapGroupState.Hidden;
}
}
if (newSelection != null)
{
if (newSelection.BeatmapPanels.Any(b => b.Beatmap.ID == Beatmap.BeatmapInfo.ID))
carousel.SelectBeatmap(Beatmap.BeatmapInfo, false);
else if (eagerSelection)
carousel.SelectBeatmap(newSelection.BeatmapSet.Beatmaps[0], false);
}
}, debounce ? 250 : 0);
}
private void onBeatmapSetAdded(BeatmapSetInfo s) => Schedule(() => addBeatmapSet(s, Game, true));
private void onBeatmapSetRemoved(BeatmapSetInfo s) => Schedule(() => removeBeatmapSet(s));
protected override void OnEntering(Screen last)
{
base.OnEntering(last);
ensurePlayingSelected();
changeBackground(Beatmap);
Content.FadeInFromZero(250);
beatmapInfoWedge.MoveToX(-50);
beatmapInfoWedge.MoveToX(0, 800, EasingTypes.OutQuint);
filter.Activate();
base.OnBeatmapChanged(beatmap);
}
protected override void OnResuming(Screen last)
{
player = null;
changeBackground(Beatmap);
ensurePlayingSelected();
base.OnResuming(last);
Content.FadeIn(250);
Content.ScaleTo(1, 250, EasingTypes.OutSine);
filter.Activate();
}
protected override void OnSuspending(Screen next)
{
Content.ScaleTo(1.1f, 250, EasingTypes.InSine);
Content.FadeOut(250);
filter.Deactivate();
base.OnSuspending(next);
}
protected override bool OnExiting(Screen next)
{
beatmapInfoWedge.MoveToX(-100, 800, EasingTypes.InQuint);
beatmapInfoWedge.RotateTo(10, 800, EasingTypes.InQuint);
if (modSelect.State == Visibility.Visible)
{
modSelect.Hide();
return true;
}
Content.FadeOut(100);
filter.Deactivate();
return base.OnExiting(next);
}
protected override void Dispose(bool isDisposing)
protected override void OnSelected()
{
base.Dispose(isDisposing);
if (player != null) return;
database.BeatmapSetAdded -= onBeatmapSetAdded;
database.BeatmapSetRemoved -= onBeatmapSetRemoved;
initialAddSetsTask.Cancel();
}
private void playMode_ValueChanged(object sender, EventArgs e)
{
filterChanged(false);
}
private void changeBackground(WorkingBeatmap beatmap)
{
var backgroundModeBeatmap = Background as BackgroundScreenBeatmap;
if (backgroundModeBeatmap != null)
LoadComponentAsync(player = new PlayerLoader(new Player
{
backgroundModeBeatmap.Beatmap = beatmap;
backgroundModeBeatmap.BlurTo(background_blur, 1000);
backgroundModeBeatmap.FadeTo(1, 250);
}
if (beatmap != null)
beatmapInfoWedge.UpdateBeatmap(beatmap);
}
/// <summary>
/// The global Beatmap was changed.
/// </summary>
protected override void OnBeatmapChanged(WorkingBeatmap beatmap)
{
base.OnBeatmapChanged(beatmap);
beatmap.Mods.BindTo(modSelect.SelectedMods);
//todo: change background in selectionChanged instead; support per-difficulty backgrounds.
changeBackground(beatmap);
carousel.SelectBeatmap(beatmap.BeatmapInfo);
}
/// <summary>
/// selection has been changed as the result of interaction with the carousel.
/// </summary>
private void selectionChanged(BeatmapGroup group, BeatmapInfo beatmap)
{
bool beatmapSetChange = false;
if (!beatmap.Equals(Beatmap?.BeatmapInfo))
{
if (beatmap.BeatmapSetInfoID == Beatmap?.BeatmapInfo.BeatmapSetInfoID)
sampleChangeDifficulty.Play();
else
{
sampleChangeBeatmap.Play();
beatmapSetChange = true;
}
Beatmap = database.GetWorkingBeatmap(beatmap, Beatmap);
}
ensurePlayingSelected(beatmapSetChange);
}
private void ensurePlayingSelected(bool preview = false)
{
Track track = Beatmap?.Track;
if (track != null)
{
trackManager.SetExclusive(track);
if (preview)
track.Seek(Beatmap.Beatmap.Metadata.PreviewTime);
track.Start();
}
}
private void addBeatmapSet(BeatmapSetInfo beatmapSet, Framework.Game game, bool select = false)
{
beatmapSet = database.GetWithChildren<BeatmapSetInfo>(beatmapSet.ID);
beatmapSet.Beatmaps.ForEach(b =>
{
database.GetChildren(b);
if (b.Metadata == null) b.Metadata = beatmapSet.Metadata;
});
var group = new BeatmapGroup(beatmapSet, database)
{
SelectionChanged = selectionChanged,
StartRequested = b => footer.StartButton.TriggerClick()
};
//for the time being, let's completely load the difficulty panels in the background.
//this likely won't scale so well, but allows us to completely async the loading flow.
Task.WhenAll(group.BeatmapPanels.Select(panel => panel.LoadAsync(game))).ContinueWith(task => Schedule(delegate
{
beatmapGroups.Add(group);
group.State = BeatmapGroupState.Collapsed;
carousel.AddGroup(group);
filterChanged(false, false);
if (Beatmap == null || select)
carousel.SelectBeatmap(beatmapSet.Beatmaps.First());
else
carousel.SelectBeatmap(Beatmap.BeatmapInfo);
}));
}
private void removeBeatmapSet(BeatmapSetInfo beatmapSet)
{
var group = beatmapGroups.Find(b => b.BeatmapSet.ID == beatmapSet.ID);
if (group == null) return;
if (carousel.SelectedGroup == group)
carousel.SelectNext();
beatmapGroups.Remove(group);
carousel.RemoveGroup(group);
if (beatmapGroups.Count == 0)
Beatmap = null;
}
private void addBeatmapSets(Framework.Game game, CancellationToken token)
{
foreach (var beatmapSet in database.Query<BeatmapSetInfo>().Where(b => !b.DeletePending))
{
if (token.IsCancellationRequested) return;
addBeatmapSet(beatmapSet, game);
}
}
private void promptDelete()
{
if (Beatmap != null)
dialogOverlay?.Push(new BeatmapDeleteDialog(Beatmap));
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (args.Repeat) return false;
switch (args.Key)
{
case Key.F1:
modSelect.ToggleVisibility();
return true;
case Key.F2:
carousel.SelectRandom();
return true;
case Key.F3:
beatmapOptions.ToggleVisibility();
return true;
case Key.Enter:
footer.StartButton.TriggerClick();
return true;
case Key.Delete:
if (state.Keyboard.ShiftPressed)
{
promptDelete();
return true;
}
break;
}
return base.OnKeyDown(state, args);
Beatmap = Beatmap, //eagerly set this so it's present before push.
}), l => Push(player));
}
}
}

View File

@ -26,6 +26,7 @@ namespace osu.Game.Screens.Select
Origin = Anchor.CentreRight,
Anchor = Anchor.CentreRight,
Margin = new MarginPadding { Right = 10 },
TextSize = 20
}
});

View File

@ -0,0 +1,373 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using System.Threading;
using OpenTK;
using OpenTK.Input;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Audio.Track;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives;
using osu.Framework.Input;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Overlays;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Select.Options;
namespace osu.Game.Screens.Select
{
public abstract class SongSelect : OsuScreen
{
private readonly Bindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
private BeatmapDatabase database;
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap);
private readonly BeatmapCarousel carousel;
private TrackManager trackManager;
private DialogOverlay dialogOverlay;
private static readonly Vector2 wedged_container_size = new Vector2(0.5f, 245);
private const float left_area_padding = 20;
private readonly BeatmapInfoWedge beatmapInfoWedge;
protected Container LeftContent;
private static readonly Vector2 background_blur = new Vector2(20);
private CancellationTokenSource initialAddSetsTask;
private SampleChannel sampleChangeDifficulty;
private SampleChannel sampleChangeBeatmap;
protected virtual bool ShowFooter => true;
/// <summary>
/// Can be null if <see cref="ShowFooter"/> is false.
/// </summary>
protected readonly BeatmapOptionsOverlay BeatmapOptions;
/// <summary>
/// Can be null if <see cref="ShowFooter"/> is false.
/// </summary>
protected readonly Footer Footer;
/// <summary>
/// Contains any panel which is triggered by a footer button.
/// Helps keep them located beneath the footer itself.
/// </summary>
protected readonly Container FooterPanels;
public readonly FilterControl FilterControl;
protected SongSelect()
{
const float carousel_width = 640;
const float filter_height = 100;
Add(new ParallaxContainer
{
Padding = new MarginPadding { Top = filter_height },
ParallaxAmount = 0.005f,
RelativeSizeAxes = Axes.Both,
Children = new[]
{
new WedgeBackground
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Right = carousel_width * 0.76f },
}
}
});
Add(LeftContent = new Container
{
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
RelativeSizeAxes = Axes.Both,
Size = new Vector2(wedged_container_size.X, 1),
Padding = new MarginPadding
{
Bottom = 50,
Top = wedged_container_size.Y + left_area_padding,
Left = left_area_padding,
Right = left_area_padding * 2,
}
});
Add(carousel = new BeatmapCarousel
{
RelativeSizeAxes = Axes.Y,
Size = new Vector2(carousel_width, 1),
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
SelectionChanged = selectionChanged,
StartRequested = raiseSelect
});
Add(FilterControl = new FilterControl
{
RelativeSizeAxes = Axes.X,
Height = filter_height,
FilterChanged = criteria => filterChanged(criteria),
Exit = Exit,
});
Add(beatmapInfoWedge = new BeatmapInfoWedge
{
Alpha = 0,
Size = wedged_container_size,
RelativeSizeAxes = Axes.X,
Margin = new MarginPadding
{
Top = left_area_padding,
Right = left_area_padding,
},
X = -50,
});
if (ShowFooter)
{
Add(FooterPanels = new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Margin = new MarginPadding
{
Bottom = Footer.HEIGHT,
},
});
Add(Footer = new Footer
{
OnBack = Exit,
OnStart = raiseSelect,
});
FooterPanels.Add(BeatmapOptions = new BeatmapOptionsOverlay());
}
}
[BackgroundDependencyLoader(permitNulls: true)]
private void load(BeatmapDatabase beatmaps, AudioManager audio, DialogOverlay dialog, OsuGame osu, OsuColour colours)
{
if (Footer != null)
{
Footer.AddButton(@"random", colours.Green, SelectRandom, Key.F2);
Footer.AddButton(@"options", colours.Blue, BeatmapOptions.ToggleVisibility, Key.F3);
BeatmapOptions.AddButton(@"Delete", @"Beatmap", FontAwesome.fa_trash, colours.Pink, promptDelete, Key.Number4, float.MaxValue);
}
if (database == null)
database = beatmaps;
if (osu != null)
ruleset.BindTo(osu.Ruleset);
database.BeatmapSetAdded += onBeatmapSetAdded;
database.BeatmapSetRemoved += onBeatmapSetRemoved;
trackManager = audio.Track;
dialogOverlay = dialog;
sampleChangeDifficulty = audio.Sample.Get(@"SongSelect/select-difficulty");
sampleChangeBeatmap = audio.Sample.Get(@"SongSelect/select-expand");
initialAddSetsTask = new CancellationTokenSource();
carousel.BeatmapsChanged = beatmapsLoaded;
carousel.Beatmaps = database.Query<BeatmapSetInfo>().Where(b => !b.DeletePending);
}
private void beatmapsLoaded()
{
if (Beatmap != null)
carousel.SelectBeatmap(Beatmap.BeatmapInfo, false);
else
carousel.SelectNext();
}
private void raiseSelect()
{
if (Beatmap == null) return;
OnSelected();
}
public void SelectRandom() => carousel.SelectRandom();
protected abstract void OnSelected();
private void filterChanged(FilterCriteria criteria, bool debounce = true)
{
carousel.Filter(criteria, debounce);
}
private void onBeatmapSetAdded(BeatmapSetInfo s) => carousel.AddBeatmap(s);
private void onBeatmapSetRemoved(BeatmapSetInfo s) => Schedule(() => removeBeatmapSet(s));
protected override void OnEntering(Screen last)
{
base.OnEntering(last);
ensurePlayingSelected();
changeBackground(Beatmap);
Content.FadeInFromZero(250);
beatmapInfoWedge.State = Visibility.Visible;
FilterControl.Activate();
}
protected override void OnResuming(Screen last)
{
changeBackground(Beatmap);
ensurePlayingSelected();
base.OnResuming(last);
Content.FadeIn(250);
Content.ScaleTo(1, 250, EasingTypes.OutSine);
FilterControl.Activate();
}
protected override void OnSuspending(Screen next)
{
Content.ScaleTo(1.1f, 250, EasingTypes.InSine);
Content.FadeOut(250);
FilterControl.Deactivate();
base.OnSuspending(next);
}
protected override bool OnExiting(Screen next)
{
beatmapInfoWedge.State = Visibility.Hidden;
Content.FadeOut(100);
FilterControl.Deactivate();
return base.OnExiting(next);
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
database.BeatmapSetAdded -= onBeatmapSetAdded;
database.BeatmapSetRemoved -= onBeatmapSetRemoved;
initialAddSetsTask.Cancel();
}
private void changeBackground(WorkingBeatmap beatmap)
{
var backgroundModeBeatmap = Background as BackgroundScreenBeatmap;
if (backgroundModeBeatmap != null)
{
backgroundModeBeatmap.Beatmap = beatmap;
backgroundModeBeatmap.BlurTo(background_blur, 1000);
backgroundModeBeatmap.FadeTo(1, 250);
}
beatmapInfoWedge.UpdateBeatmap(beatmap);
}
/// <summary>
/// The global Beatmap was changed.
/// </summary>
protected override void OnBeatmapChanged(WorkingBeatmap beatmap)
{
base.OnBeatmapChanged(beatmap);
//todo: change background in selectionChanged instead; support per-difficulty backgrounds.
changeBackground(beatmap);
carousel.SelectBeatmap(beatmap?.BeatmapInfo);
}
/// <summary>
/// selection has been changed as the result of interaction with the carousel.
/// </summary>
private void selectionChanged(BeatmapGroup group, BeatmapInfo beatmap)
{
bool beatmapSetChange = false;
if (!beatmap.Equals(Beatmap?.BeatmapInfo))
{
if (beatmap.BeatmapSetInfoID == Beatmap?.BeatmapInfo.BeatmapSetInfoID)
sampleChangeDifficulty.Play();
else
{
sampleChangeBeatmap.Play();
beatmapSetChange = true;
}
Beatmap = database.GetWorkingBeatmap(beatmap, Beatmap);
}
ensurePlayingSelected(beatmapSetChange);
}
private void ensurePlayingSelected(bool preview = false)
{
Track track = Beatmap?.Track;
if (track != null)
{
trackManager.SetExclusive(track);
if (preview)
track.Seek(Beatmap.Beatmap.Metadata.PreviewTime);
track.Start();
}
}
private void selectBeatmap(BeatmapSetInfo beatmapSet = null)
{
carousel.SelectBeatmap(beatmapSet != null ? beatmapSet.Beatmaps.First() : Beatmap?.BeatmapInfo);
}
private void removeBeatmapSet(BeatmapSetInfo beatmapSet)
{
carousel.RemoveBeatmap(beatmapSet);
if (carousel.SelectedBeatmap == null)
Beatmap = null;
}
private void promptDelete()
{
if (Beatmap != null)
dialogOverlay?.Push(new BeatmapDeleteDialog(Beatmap));
}
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (args.Repeat) return false;
switch (args.Key)
{
case Key.Enter:
raiseSelect();
return true;
case Key.Delete:
if (state.Keyboard.ShiftPressed)
{
promptDelete();
return true;
}
break;
}
return base.OnKeyDown(state, args);
}
}
}

View File

@ -30,7 +30,7 @@ namespace osu.Game.Screens.Tournament.Components
}
}
private List<VisualiserLine> allLines = new List<VisualiserLine>();
private readonly List<VisualiserLine> allLines = new List<VisualiserLine>();
private float offset;

View File

@ -21,6 +21,7 @@ using osu.Game.Screens.Tournament.Components;
using osu.Game.Screens.Tournament.Teams;
using OpenTK;
using OpenTK.Graphics;
using osu.Game.Users;
namespace osu.Game.Screens.Tournament
{
@ -36,7 +37,7 @@ namespace osu.Game.Screens.Tournament
private GroupContainer groupsContainer;
private OsuSpriteText fullTeamNameText;
private List<Team> allTeams = new List<Team>();
private readonly List<Country> allTeams = new List<Country>();
private DrawingsConfigManager drawingsConfig;
@ -160,7 +161,7 @@ namespace osu.Game.Screens.Tournament
Text = "Control Panel",
TextSize = 22f,
Font = "Exo2.0-Boldd"
Font = "Exo2.0-Bold"
},
new FillFlowContainer
{
@ -238,7 +239,7 @@ namespace osu.Game.Screens.Tournament
reset(true);
}
private void onTeamSelected(Team team)
private void onTeamSelected(Country team)
{
groupsContainer.AddTeam(team);
@ -275,7 +276,7 @@ namespace osu.Game.Screens.Tournament
teamsContainer.ClearTeams();
allTeams.Clear();
foreach (Team t in TeamList.Teams)
foreach (Country t in TeamList.Teams)
{
if (groupsContainer.ContainsTeam(t.FullName))
continue;
@ -311,7 +312,7 @@ namespace osu.Game.Screens.Tournament
if (line.ToUpper().StartsWith("GROUP"))
continue;
Team teamToAdd = allTeams.FirstOrDefault(t => t.FullName == line);
Country teamToAdd = allTeams.FirstOrDefault(t => t.FullName == line);
if (teamToAdd == null)
continue;

View File

@ -11,9 +11,9 @@ using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Game.Graphics.Sprites;
using osu.Game.Screens.Tournament.Teams;
using OpenTK;
using OpenTK.Graphics;
using osu.Game.Users;
namespace osu.Game.Screens.Tournament
{
@ -23,9 +23,9 @@ namespace osu.Game.Screens.Tournament
public int TeamsCount { get; private set; }
private FlowContainer<GroupTeam> teams;
private readonly FlowContainer<GroupTeam> teams;
private List<GroupTeam> allTeams = new List<GroupTeam>();
private readonly List<GroupTeam> allTeams = new List<GroupTeam>();
public Group(string name)
{
@ -73,7 +73,7 @@ namespace osu.Game.Screens.Tournament
};
}
public void AddTeam(Team team)
public void AddTeam(Country team)
{
GroupTeam gt = new GroupTeam(team);
@ -91,7 +91,7 @@ namespace osu.Game.Screens.Tournament
return allTeams.Any(t => t.Team.FullName == fullName);
}
public bool RemoveTeam(Team team)
public bool RemoveTeam(Country team)
{
allTeams.RemoveAll(gt => gt.Team == team);
@ -122,12 +122,12 @@ namespace osu.Game.Screens.Tournament
private class GroupTeam : Container
{
public Team Team;
public readonly Country Team;
private FillFlowContainer innerContainer;
private Sprite flagSprite;
private readonly FillFlowContainer innerContainer;
private readonly Sprite flagSprite;
public GroupTeam(Team team)
public GroupTeam(Country team)
{
Team = team;

View File

@ -7,16 +7,16 @@ using System.Linq;
using System.Text;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Screens.Tournament.Teams;
using OpenTK;
using osu.Game.Users;
namespace osu.Game.Screens.Tournament
{
public class GroupContainer : Container
{
private List<Group> groups = new List<Group>();
private readonly List<Group> groups = new List<Group>();
private int maxTeams;
private readonly int maxTeams;
private int currentGroup;
public GroupContainer(int numGroups, int teamsPerGroup)
@ -64,7 +64,7 @@ namespace osu.Game.Screens.Tournament
}
}
public void AddTeam(Team team)
public void AddTeam(Country team)
{
if (groups[currentGroup].TeamsCount == maxTeams)
return;

View File

@ -13,20 +13,20 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Threading;
using osu.Game.Screens.Tournament.Teams;
using OpenTK;
using OpenTK.Graphics;
using osu.Game.Users;
namespace osu.Game.Screens.Tournament
{
public class ScrollingTeamContainer : Container
{
public event Action OnScrollStarted;
public event Action<Team> OnSelected;
public event Action<Country> OnSelected;
private readonly List<Team> availableTeams = new List<Team>();
private readonly List<Country> availableTeams = new List<Country>();
private Container tracker;
private readonly Container tracker;
private float speed;
private int expiredCount;
@ -158,7 +158,7 @@ namespace osu.Game.Screens.Tournament
}
}
public void AddTeam(Team team)
public void AddTeam(Country team)
{
if (availableTeams.Contains(team))
return;
@ -169,12 +169,12 @@ namespace osu.Game.Screens.Tournament
scrollState = ScrollState.Idle;
}
public void AddTeams(IEnumerable<Team> teams)
public void AddTeams(IEnumerable<Country> teams)
{
if (teams == null)
return;
foreach (Team t in teams)
foreach (Country t in teams)
AddTeam(t);
}
@ -185,7 +185,7 @@ namespace osu.Game.Screens.Tournament
scrollState = ScrollState.Idle;
}
public void RemoveTeam(Team team)
public void RemoveTeam(Country team)
{
availableTeams.Remove(team);
@ -270,7 +270,7 @@ namespace osu.Game.Screens.Tournament
private void addFlags()
{
foreach (Team t in availableTeams)
foreach (Country t in availableTeams)
{
Add(new ScrollingTeam(t)
{
@ -298,9 +298,7 @@ namespace osu.Game.Screens.Tournament
private void speedTo(float value, double duration = 0, EasingTypes easing = EasingTypes.None)
{
DelayReset();
UpdateTransformsOfType(typeof(TransformScrollSpeed));
TransformFloatTo(speed, value, duration, easing, new TransformScrollSpeed());
TransformTo(() => speed, value, duration, easing, new TransformScrollSpeed());
}
private enum ScrollState
@ -326,10 +324,10 @@ namespace osu.Game.Screens.Tournament
public const float WIDTH = 58;
public const float HEIGHT = 41;
public Team Team;
public Country Team;
private Sprite flagSprite;
private Box outline;
private readonly Sprite flagSprite;
private readonly Box outline;
private bool selected;
public bool Selected
@ -346,7 +344,7 @@ namespace osu.Game.Screens.Tournament
}
}
public ScrollingTeam(Team team)
public ScrollingTeam(Country team)
{
Team = team;

View File

@ -2,11 +2,12 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Game.Users;
namespace osu.Game.Screens.Tournament.Teams
{
public interface ITeamList
{
IEnumerable<Team> Teams { get; }
IEnumerable<Country> Teams { get; }
}
}

View File

@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.IO;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.Users;
namespace osu.Game.Screens.Tournament.Teams
{
@ -13,18 +14,18 @@ namespace osu.Game.Screens.Tournament.Teams
{
private const string teams_filename = "drawings.txt";
private Storage storage;
private readonly Storage storage;
public StorageBackedTeamList(Storage storage)
{
this.storage = storage;
}
public IEnumerable<Team> Teams
public IEnumerable<Country> Teams
{
get
{
var teams = new List<Team>();
var teams = new List<Country>();
try
{
@ -52,7 +53,7 @@ namespace osu.Game.Screens.Tournament.Teams
string acronym = split.Length >= 3 ? split[2].Trim() : teamName;
acronym = acronym.Substring(0, Math.Min(3, acronym.Length));
teams.Add(new Team
teams.Add(new Country
{
FlagName = flagName,
FullName = teamName,

View File

@ -1,23 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Screens.Tournament.Teams
{
public class Team
{
/// <summary>
/// The name of this team.
/// </summary>
public string FullName;
/// <summary>
/// Short acronym which appears in the group boxes post-selection.
/// </summary>
public string Acronym;
/// <summary>
/// Two-letter flag acronym (ISO 3166 standard)
/// </summary>
public string FlagName;
}
}