1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-14 00:53:19 +08:00

Merge branch 'master' into multiplayer-lounge

This commit is contained in:
Dean Herbert 2018-05-27 11:15:03 +09:00 committed by GitHub
commit 12b5ca1b0d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 585 additions and 128 deletions

@ -1 +1 @@
Subproject commit fac688633b8fcf34ae5d0514c26b03e217161eb4 Subproject commit a191c104b8e254e81a1a7bb1c200ccdf02628796

View File

@ -28,7 +28,20 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
var endTime = obj as IHasEndTime; var endTime = obj as IHasEndTime;
if (positionData == null) if (positionData == null)
{
if (endTime != null)
{
yield return new BananaShower
{
StartTime = obj.StartTime,
Samples = obj.Samples,
Duration = endTime.Duration,
NewCombo = comboData?.NewCombo ?? false
};
}
yield break; yield break;
}
if (curveData != null) if (curveData != null)
{ {
@ -48,19 +61,6 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
yield break; yield break;
} }
if (endTime != null)
{
yield return new BananaShower
{
StartTime = obj.StartTime,
Samples = obj.Samples,
Duration = endTime.Duration,
NewCombo = comboData?.NewCombo ?? false
};
yield break;
}
yield return new Fruit yield return new Fruit
{ {
StartTime = obj.StartTime, StartTime = obj.StartTime,

View File

@ -105,7 +105,8 @@ namespace osu.Game.Rulesets.Mania.Difficulty
private double computeAccuracyValue(double strainValue) private double computeAccuracyValue(double strainValue)
{ {
double hitWindowGreat = (Beatmap.HitObjects.First().HitWindows.Great / 2 - 0.5) / TimeRate; // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be remoevd in the future
double hitWindowGreat = (int)(Beatmap.HitObjects.First().HitWindows.Great / 2) / TimeRate;
if (hitWindowGreat <= 0) if (hitWindowGreat <= 0)
return 0; return 0;

View File

@ -61,20 +61,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty
if (mods.Any(m => !m.Ranked)) if (mods.Any(m => !m.Ranked))
return 0; return 0;
// Todo: In the future we should apply changes to PreEmpt/AR at an OsuHitObject/BaseDifficulty level, but this is done // Todo: These int casts are temporary to achieve 1:1 results with osu!stable, and should be remoevd in the future
// locally for now as doing so would modify animations and other things unexpectedly double hitWindowGreat = (int)(Beatmap.HitObjects.First().HitWindows.Great / 2) / TimeRate;
// DO NOT MODIFY THIS double preEmpt = (int)BeatmapDifficulty.DifficultyRange(Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate, 1800, 1200, 450) / TimeRate;
double ar = Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate;
if (mods.Any(m => m is OsuModHardRock))
ar = Math.Min(10, ar * 1.4);
if (mods.Any(m => m is OsuModEasy))
ar = Math.Max(0, ar / 2);
double preEmpt = BeatmapDifficulty.DifficultyRange(ar, 1800, 1200, 450) / TimeRate;
double hitWindowGreat = (Beatmap.HitObjects.First().HitWindows.Great / 2 - 0.5) / TimeRate;
realApproachRate = preEmpt > 1200 ? (1800 - preEmpt) / 120 : (1200 - preEmpt) / 150 + 5; realApproachRate = preEmpt > 1200 ? (1800 - preEmpt) / 120 : (1200 - preEmpt) / 150 + 5;
realOverallDifficulty = (80 - 0.5 - hitWindowGreat) / 6; realOverallDifficulty = (80 - hitWindowGreat) / 6;
// Custom multipliers for NoFail and SpunOut. // Custom multipliers for NoFail and SpunOut.
double multiplier = 1.12f; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things double multiplier = 1.12f; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things

View File

@ -94,7 +94,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
private double computeAccuracyValue() private double computeAccuracyValue()
{ {
double hitWindowGreat = (Beatmap.HitObjects.First().HitWindows.Great / 2 - 0.5) / TimeRate; // Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be remoevd in the future
double hitWindowGreat = (int)(Beatmap.HitObjects.First().HitWindows.Great / 2) / TimeRate;
if (hitWindowGreat <= 0) if (hitWindowGreat <= 0)
return 0; return 0;

View File

@ -19,12 +19,12 @@ namespace osu.Game.Tests.Visual
[TestFixture] [TestFixture]
public class TestCaseCursors : ManualInputManagerTestCase public class TestCaseCursors : ManualInputManagerTestCase
{ {
private readonly CursorOverrideContainer cursorOverrideContainer; private readonly MenuCursorContainer menuCursorContainer;
private readonly CustomCursorBox[] cursorBoxes = new CustomCursorBox[6]; private readonly CustomCursorBox[] cursorBoxes = new CustomCursorBox[6];
public TestCaseCursors() public TestCaseCursors()
{ {
Child = cursorOverrideContainer = new CursorOverrideContainer Child = menuCursorContainer = new MenuCursorContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Children = new[] Children = new[]
@ -99,7 +99,7 @@ namespace osu.Game.Tests.Visual
AddAssert("Check green cursor at mouse", () => checkAtMouse(cursorBoxes[0].Cursor)); AddAssert("Check green cursor at mouse", () => checkAtMouse(cursorBoxes[0].Cursor));
AddStep("Move out", moveOut); AddStep("Move out", moveOut);
AddAssert("Check green cursor invisible", () => !checkVisible(cursorBoxes[0].Cursor)); AddAssert("Check green cursor invisible", () => !checkVisible(cursorBoxes[0].Cursor));
AddAssert("Check global cursor visible", () => checkVisible(cursorOverrideContainer.Cursor)); AddAssert("Check global cursor visible", () => checkVisible(menuCursorContainer.Cursor));
} }
/// <summary> /// <summary>
@ -112,11 +112,11 @@ namespace osu.Game.Tests.Visual
AddStep("Move to purple area", () => InputManager.MoveMouseTo(cursorBoxes[3])); AddStep("Move to purple area", () => InputManager.MoveMouseTo(cursorBoxes[3]));
AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor));
AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].Cursor)); AddAssert("Check purple cursor at mouse", () => checkAtMouse(cursorBoxes[3].Cursor));
AddAssert("Check global cursor visible", () => checkVisible(cursorOverrideContainer.Cursor)); AddAssert("Check global cursor visible", () => checkVisible(menuCursorContainer.Cursor));
AddAssert("Check global cursor at mouse", () => checkAtMouse(cursorOverrideContainer.Cursor)); AddAssert("Check global cursor at mouse", () => checkAtMouse(menuCursorContainer.Cursor));
AddStep("Move out", moveOut); AddStep("Move out", moveOut);
AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor)); AddAssert("Check purple cursor visible", () => checkVisible(cursorBoxes[3].Cursor));
AddAssert("Check global cursor visible", () => checkVisible(cursorOverrideContainer.Cursor)); AddAssert("Check global cursor visible", () => checkVisible(menuCursorContainer.Cursor));
} }
/// <summary> /// <summary>

View File

@ -0,0 +1,23 @@
// Copyright (c) 2007-2018 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.Graphics.UserInterface;
using OpenTK;
namespace osu.Game.Tests.Visual
{
public class TestCaseExternalLinkButton : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(ExternalLinkButton) };
public TestCaseExternalLinkButton()
{
Child = new ExternalLinkButton("https://osu.ppy.sh/home")
{
Size = new Vector2(50)
};
}
}
}

View File

@ -0,0 +1,53 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Game.Screens.Play.HUD;
using OpenTK;
using OpenTK.Input;
namespace osu.Game.Tests.Visual
{
[Description("'Hold to Quit' UI element")]
public class TestCaseQuitButton : ManualInputManagerTestCase
{
private bool exitAction;
[BackgroundDependencyLoader]
private void load()
{
QuitButton quitButton;
Add(quitButton = new QuitButton
{
Origin = Anchor.BottomRight,
Anchor = Anchor.BottomRight,
Action = () => exitAction = true
});
var text = quitButton.Children.OfType<SpriteText>().First();
AddStep("Trigger text fade in", () => InputManager.MoveMouseTo(quitButton));
AddUntilStep(() => text.IsPresent && !exitAction, "Text visible");
AddStep("Trigger text fade out", () => InputManager.MoveMouseTo(Vector2.One));
AddUntilStep(() => !text.IsPresent && !exitAction, "Text is not visible");
AddStep("Trigger exit action", () =>
{
exitAction = false;
InputManager.MoveMouseTo(quitButton);
InputManager.PressButton(MouseButton.Left);
});
AddStep("Early release", () => InputManager.ReleaseButton(MouseButton.Left));
AddAssert("action not triggered", () => !exitAction);
AddStep("Trigger exit action", () => InputManager.PressButton(MouseButton.Left));
AddUntilStep(() => exitAction, $"{nameof(quitButton.Action)} was triggered");
}
}
}

View File

@ -0,0 +1,52 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
namespace osu.Game.Graphics.Containers
{
public abstract class HoldToConfirmContainer : Container
{
public Action Action;
private const int activate_delay = 400;
private const int fadeout_delay = 200;
private bool fired;
private bool confirming;
/// <summary>
/// Whether the overlay should be allowed to return from a fired state.
/// </summary>
protected virtual bool AllowMultipleFires => false;
public Bindable<double> Progress = new BindableDouble();
protected void BeginConfirm()
{
if (confirming || !AllowMultipleFires && fired) return;
confirming = true;
this.TransformBindableTo(Progress, 1, activate_delay * (1 - Progress.Value), Easing.Out).OnComplete(_ => Confirm());
}
protected virtual void Confirm()
{
Action?.Invoke();
fired = true;
}
protected void AbortConfirm()
{
if (!AllowMultipleFires && fired) return;
confirming = false;
this.TransformBindableTo(Progress, 0, fadeout_delay, Easing.Out);
}
}
}

View File

@ -12,7 +12,7 @@ namespace osu.Game.Graphics.Cursor
/// <summary> /// <summary>
/// A container which provides a <see cref="MenuCursor"/> which can be overridden by hovered <see cref="Drawable"/>s. /// A container which provides a <see cref="MenuCursor"/> which can be overridden by hovered <see cref="Drawable"/>s.
/// </summary> /// </summary>
public class CursorOverrideContainer : Container, IProvideCursor public class MenuCursorContainer : Container, IProvideCursor
{ {
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;
private readonly Container content; private readonly Container content;
@ -25,7 +25,7 @@ namespace osu.Game.Graphics.Cursor
public CursorContainer Cursor { get; } public CursorContainer Cursor { get; }
public bool ProvidingUserCursor => true; public bool ProvidingUserCursor => true;
public CursorOverrideContainer() public MenuCursorContainer()
{ {
AddRangeInternal(new Drawable[] AddRangeInternal(new Drawable[]
{ {

View File

@ -0,0 +1,63 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Diagnostics;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Input;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Graphics.UserInterface
{
public class ExternalLinkButton : CompositeDrawable, IHasTooltip
{
public string Link { get; set; }
private Color4 hoverColour;
public ExternalLinkButton(string link = null)
{
Link = link;
Size = new Vector2(12);
InternalChild = new SpriteIcon
{
Icon = FontAwesome.fa_external_link,
RelativeSizeAxes = Axes.Both
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
hoverColour = colours.Yellow;
}
protected override bool OnHover(InputState state)
{
InternalChild.FadeColour(hoverColour, 500, Easing.OutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
InternalChild.FadeColour(Color4.White, 500, Easing.OutQuint);
base.OnHoverLost(state);
}
protected override bool OnClick(InputState state)
{
if(Link != null)
Process.Start(new ProcessStartInfo
{
FileName = Link,
UseShellExecute = true //see https://github.com/dotnet/corefx/issues/10361
});
return true;
}
public string TooltipText => "View in browser";
}
}

View File

@ -212,7 +212,7 @@ namespace osu.Game
protected override void LoadComplete() protected override void LoadComplete()
{ {
// this needs to be cached before base.LoadComplete as it is used by CursorOverrideContainer. // this needs to be cached before base.LoadComplete as it is used by MenuCursorContainer.
dependencies.Cache(screenshotManager = new ScreenshotManager()); dependencies.Cache(screenshotManager = new ScreenshotManager());
base.LoadComplete(); base.LoadComplete();
@ -220,7 +220,7 @@ namespace osu.Game
// The next time this is updated is in UpdateAfterChildren, which occurs too late and results // The next time this is updated is in UpdateAfterChildren, which occurs too late and results
// in the cursor being shown for a few frames during the intro. // in the cursor being shown for a few frames during the intro.
// This prevents the cursor from showing until we have a screen with CursorVisible = true // This prevents the cursor from showing until we have a screen with CursorVisible = true
CursorOverrideContainer.CanShowCursor = currentScreen?.CursorVisible ?? false; MenuCursorContainer.CanShowCursor = currentScreen?.CursorVisible ?? false;
// hook up notifications to components. // hook up notifications to components.
SkinManager.PostNotification = n => notifications?.Post(n); SkinManager.PostNotification = n => notifications?.Post(n);
@ -548,7 +548,7 @@ namespace osu.Game
mainContent.Padding = new MarginPadding { Top = ToolbarOffset }; mainContent.Padding = new MarginPadding { Top = ToolbarOffset };
CursorOverrideContainer.CanShowCursor = currentScreen?.CursorVisible ?? false; MenuCursorContainer.CanShowCursor = currentScreen?.CursorVisible ?? false;
} }
private void screenAdded(Screen newScreen) private void screenAdded(Screen newScreen)

View File

@ -57,7 +57,7 @@ namespace osu.Game
protected SettingsStore SettingsStore; protected SettingsStore SettingsStore;
protected CursorOverrideContainer CursorOverrideContainer; protected MenuCursorContainer MenuCursorContainer;
protected override string MainResourceFile => @"osu.Game.Resources.dll"; protected override string MainResourceFile => @"osu.Game.Resources.dll";
@ -191,14 +191,14 @@ namespace osu.Game
GlobalActionContainer globalBinding; GlobalActionContainer globalBinding;
CursorOverrideContainer = new CursorOverrideContainer { RelativeSizeAxes = Axes.Both }; MenuCursorContainer = new MenuCursorContainer { RelativeSizeAxes = Axes.Both };
CursorOverrideContainer.Child = globalBinding = new GlobalActionContainer(this) MenuCursorContainer.Child = globalBinding = new GlobalActionContainer(this)
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Child = content = new OsuTooltipContainer(CursorOverrideContainer.Cursor) { RelativeSizeAxes = Axes.Both } Child = content = new OsuTooltipContainer(MenuCursorContainer.Cursor) { RelativeSizeAxes = Axes.Both }
}; };
base.Content.Add(new DrawSizePreservingFillContainer { Child = CursorOverrideContainer }); base.Content.Add(new DrawSizePreservingFillContainer { Child = MenuCursorContainer });
KeyBindingStore.Register(globalBinding); KeyBindingStore.Register(globalBinding);
dependencies.Cache(globalBinding); dependencies.Cache(globalBinding);

View File

@ -11,6 +11,7 @@ using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables; using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.BeatmapSet.Buttons; using osu.Game.Overlays.BeatmapSet.Buttons;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
@ -93,6 +94,7 @@ namespace osu.Game.Overlays.BeatmapSet
public Header() public Header()
{ {
ExternalLinkButton externalLink;
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
Height = 400; Height = 400;
Masking = true; Masking = true;
@ -160,11 +162,25 @@ namespace osu.Game.Overlays.BeatmapSet
Height = 113, Height = 113,
Child = Picker = new BeatmapPicker(), Child = Picker = new BeatmapPicker(),
}, },
new FillFlowContainer
{
Direction = FillDirection.Horizontal,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
title = new OsuSpriteText title = new OsuSpriteText
{ {
Font = @"Exo2.0-BoldItalic", Font = @"Exo2.0-BoldItalic",
TextSize = 37, TextSize = 37,
}, },
externalLink = new ExternalLinkButton
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Margin = new MarginPadding { Left = 3, Bottom = 4 }, //To better lineup with the font
},
}
},
artist = new OsuSpriteText artist = new OsuSpriteText
{ {
Font = @"Exo2.0-SemiBoldItalic", Font = @"Exo2.0-SemiBoldItalic",
@ -247,6 +263,7 @@ namespace osu.Game.Overlays.BeatmapSet
}; };
Picker.Beatmap.ValueChanged += b => Details.Beatmap = b; Picker.Beatmap.ValueChanged += b => Details.Beatmap = b;
Picker.Beatmap.ValueChanged += b => externalLink.Link = $@"https://osu.ppy.sh/beatmapsets/{BeatmapSet?.OnlineBeatmapSetID}#{b?.Ruleset.ShortName}/{b?.OnlineBeatmapID}";
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]

View File

@ -1,11 +1,10 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Containers;
using OpenTK.Graphics; using OpenTK.Graphics;
namespace osu.Game.Overlays namespace osu.Game.Overlays
@ -14,22 +13,10 @@ namespace osu.Game.Overlays
/// An overlay which will display a black screen that dims over a period before confirming an exit action. /// An overlay which will display a black screen that dims over a period before confirming an exit action.
/// Action is BYO (derived class will need to call <see cref="BeginConfirm"/> and <see cref="AbortConfirm"/> from a user event). /// Action is BYO (derived class will need to call <see cref="BeginConfirm"/> and <see cref="AbortConfirm"/> from a user event).
/// </summary> /// </summary>
public abstract class HoldToConfirmOverlay : Container public abstract class HoldToConfirmOverlay : HoldToConfirmContainer
{ {
public Action Action;
private Box overlay; private Box overlay;
private const int activate_delay = 400;
private const int fadeout_delay = 200;
private bool fired;
/// <summary>
/// Whether the overlay should be allowed to return from a fired state.
/// </summary>
protected virtual bool AllowMultipleFires => false;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
@ -45,22 +32,8 @@ namespace osu.Game.Overlays
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
} }
}; };
}
protected void BeginConfirm() Progress.ValueChanged += v => overlay.Alpha = (float)v;
{
if (!AllowMultipleFires && fired) return;
overlay.FadeIn(activate_delay * (1 - overlay.Alpha), Easing.Out).OnComplete(_ =>
{
Action?.Invoke();
fired = true;
});
}
protected void AbortConfirm()
{
if (!AllowMultipleFires && fired) return;
overlay.FadeOut(fadeout_delay, Easing.Out);
} }
} }
} }

View File

@ -101,7 +101,7 @@ namespace osu.Game.Overlays.Music
private void updateSelectedSet() private void updateSelectedSet()
{ {
foreach (PlaylistItem s in items.Children) foreach (PlaylistItem s in items.Children)
s.Selected = s.BeatmapSetInfo.ID == beatmapBacking.Value.BeatmapSetInfo.ID; s.Selected = s.BeatmapSetInfo.ID == beatmapBacking.Value.BeatmapSetInfo?.ID;
} }
public string SearchTerm public string SearchTerm

View File

@ -79,7 +79,10 @@ namespace osu.Game.Overlays.Music
{ {
BeatmapInfo beatmap = list.FirstVisibleSet?.Beatmaps?.FirstOrDefault(); BeatmapInfo beatmap = list.FirstVisibleSet?.Beatmaps?.FirstOrDefault();
if (beatmap != null) if (beatmap != null)
{
beatmapBacking.Value = beatmaps.GetWorkingBeatmap(beatmap); beatmapBacking.Value = beatmaps.GetWorkingBeatmap(beatmap);
beatmapBacking.Value.Track.Restart();
}
}; };
} }
@ -109,6 +112,7 @@ namespace osu.Game.Overlays.Music
} }
beatmapBacking.Value = beatmaps.GetWorkingBeatmap(set.Beatmaps.First()); beatmapBacking.Value = beatmaps.GetWorkingBeatmap(set.Beatmaps.First());
beatmapBacking.Value.Track.Restart();
} }
} }

View File

@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System; using System;
using System.Diagnostics;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
@ -10,13 +9,13 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Profile.Header; using osu.Game.Overlays.Profile.Header;
using osu.Game.Users; using osu.Game.Users;
@ -105,11 +104,28 @@ namespace osu.Game.Overlays.Profile
Y = -75, Y = -75,
Size = new Vector2(25, 25) Size = new Vector2(25, 25)
}, },
new ProfileLink(user) new FillFlowContainer
{ {
Direction = FillDirection.Horizontal,
AutoSizeAxes = Axes.Both,
Anchor = Anchor.BottomLeft, Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft, Origin = Anchor.BottomLeft,
Y = -48, Y = -48,
Children = new Drawable[]
{
new OsuSpriteText
{
Text = user.Username,
Font = @"Exo2.0-RegularItalic",
TextSize = 30,
},
new ExternalLinkButton($@"https://osu.ppy.sh/users/{user.Id}")
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
Margin = new MarginPadding { Left = 3, Bottom = 3 }, //To better lineup with the font
},
}
}, },
countryFlag = new DrawableFlag(user.Country) countryFlag = new DrawableFlag(user.Country)
{ {
@ -455,28 +471,6 @@ namespace osu.Game.Overlays.Profile
infoTextRight.NewLine(); infoTextRight.NewLine();
} }
private class ProfileLink : OsuHoverContainer, IHasTooltip
{
public string TooltipText => "View Profile in Browser";
public override bool HandleMouseInput => true;
public ProfileLink(User user)
{
Action = () => Process.Start($@"https://osu.ppy.sh/users/{user.Id}");
AutoSizeAxes = Axes.Both;
Child = new OsuSpriteText
{
Text = user.Username,
Font = @"Exo2.0-RegularItalic",
TextSize = 30,
};
}
}
private class GradeBadge : Container private class GradeBadge : Container
{ {
private const float width = 50; private const float width = 50;

View File

@ -48,7 +48,7 @@ namespace osu.Game.Screens.Edit
{ {
// TODO: should probably be done at a RulesetContainer level to share logic with Player. // TODO: should probably be done at a RulesetContainer level to share logic with Player.
var sourceClock = (IAdjustableClock)Beatmap.Value.Track ?? new StopwatchClock(); var sourceClock = (IAdjustableClock)Beatmap.Value.Track ?? new StopwatchClock();
clock = new EditorClock(Beatmap.Value.Beatmap.ControlPointInfo, beatDivisor) { IsCoupled = false }; clock = new EditorClock(Beatmap, beatDivisor) { IsCoupled = false };
clock.ChangeSource(sourceClock); clock.ChangeSource(sourceClock);
dependencies.CacheAs<IFrameBasedClock>(clock); dependencies.CacheAs<IFrameBasedClock>(clock);

View File

@ -3,10 +3,13 @@
using System; using System;
using System.Linq; using System.Linq;
using osu.Framework.Configuration;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
using osu.Framework.Timing; using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Screens.Edit.Screens.Compose; using osu.Game.Screens.Edit.Screens.Compose;
using OpenTK;
namespace osu.Game.Screens.Edit namespace osu.Game.Screens.Edit
{ {
@ -15,15 +18,26 @@ namespace osu.Game.Screens.Edit
/// </summary> /// </summary>
public class EditorClock : DecoupleableInterpolatingFramedClock public class EditorClock : DecoupleableInterpolatingFramedClock
{ {
public readonly double TrackLength;
public ControlPointInfo ControlPointInfo; public ControlPointInfo ControlPointInfo;
private readonly BindableBeatDivisor beatDivisor; private readonly BindableBeatDivisor beatDivisor;
public EditorClock(ControlPointInfo controlPointInfo, BindableBeatDivisor beatDivisor) public EditorClock(Bindable<WorkingBeatmap> beatmap, BindableBeatDivisor beatDivisor)
{
this.beatDivisor = beatDivisor;
ControlPointInfo = beatmap.Value.Beatmap.ControlPointInfo;
TrackLength = beatmap.Value.Track.Length;
}
public EditorClock(ControlPointInfo controlPointInfo, double trackLength, BindableBeatDivisor beatDivisor)
{ {
this.beatDivisor = beatDivisor; this.beatDivisor = beatDivisor;
ControlPointInfo = controlPointInfo; ControlPointInfo = controlPointInfo;
TrackLength = trackLength;
} }
/// <summary> /// <summary>
@ -111,6 +125,8 @@ namespace osu.Game.Screens.Edit
if (seekTime > nextTimingPoint?.Time) if (seekTime > nextTimingPoint?.Time)
seekTime = nextTimingPoint.Time; seekTime = nextTimingPoint.Time;
// Ensure the sought point is within the boundaries
seekTime = MathHelper.Clamp(seekTime, 0, TrackLength);
Seek(seekTime); Seek(seekTime);
} }
} }

View File

@ -154,6 +154,8 @@ namespace osu.Game.Screens.Menu
case Key.Space: case Key.Space:
logo?.TriggerOnClick(state); logo?.TriggerOnClick(state);
return true; return true;
case Key.Escape:
return goBack();
} }
return false; return false;
@ -164,6 +166,14 @@ namespace osu.Game.Screens.Menu
switch (action) switch (action)
{ {
case GlobalAction.Back: case GlobalAction.Back:
return goBack();
default:
return false;
}
}
private bool goBack()
{
switch (State) switch (State)
{ {
case MenuState.TopLevel: case MenuState.TopLevel:
@ -175,9 +185,6 @@ namespace osu.Game.Screens.Menu
default: default:
return false; return false;
} }
default:
return false;
}
} }
public bool OnReleased(GlobalAction action) public bool OnReleased(GlobalAction action)
@ -328,6 +335,9 @@ namespace osu.Game.Screens.Menu
logoDelayedAction = Scheduler.AddDelayed(() => logoDelayedAction = Scheduler.AddDelayed(() =>
{ {
hideOverlaysOnEnter.Value = true;
allowOpeningOverlays.Value = false;
logo.ClearTransforms(targetMember: nameof(Position)); logo.ClearTransforms(targetMember: nameof(Position));
logo.RelativePositionAxes = Axes.Both; logo.RelativePositionAxes = Axes.Both;
@ -355,6 +365,7 @@ namespace osu.Game.Screens.Menu
logoTracking = true; logoTracking = true;
logo.Impact(); logo.Impact();
hideOverlaysOnEnter.Value = false; hideOverlaysOnEnter.Value = false;
allowOpeningOverlays.Value = true; allowOpeningOverlays.Value = true;
}, 200); }, 200);

View File

@ -19,6 +19,7 @@ namespace osu.Game.Screens.Menu
private Color4 iconColour; private Color4 iconColour;
protected override bool HideOverlaysOnEnter => true; protected override bool HideOverlaysOnEnter => true;
protected override bool AllowOpeningOverlays => false;
public override bool CursorVisible => false; public override bool CursorVisible => false;

View File

@ -37,14 +37,14 @@ namespace osu.Game.Screens
/// <summary> /// <summary>
/// Whether overlays should be hidden when this screen is entered or resumed. /// Whether overlays should be hidden when this screen is entered or resumed.
/// </summary> /// </summary>
protected virtual bool HideOverlaysOnEnter => hideOverlaysOnEnter; protected virtual bool HideOverlaysOnEnter => false;
private readonly BindableBool allowOpeningOverlays = new BindableBool(); private readonly BindableBool allowOpeningOverlays = new BindableBool();
/// <summary> /// <summary>
/// Whether overlays should be able to be opened while this screen is active. /// Whether overlays should be able to be opened while this screen is active.
/// </summary> /// </summary>
protected virtual bool AllowOpeningOverlays => allowOpeningOverlays; protected virtual bool AllowOpeningOverlays => true;
/// <summary> /// <summary>
/// Whether this <see cref="OsuScreen"/> allows the cursor to be displayed. /// Whether this <see cref="OsuScreen"/> allows the cursor to be displayed.

View File

@ -0,0 +1,198 @@
// Copyright (c) 2007-2018 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.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input;
using osu.Framework.MathUtils;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using OpenTK;
namespace osu.Game.Screens.Play.HUD
{
public class QuitButton : FillFlowContainer
{
public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => true;
private readonly Button button;
public Action Action
{
set => button.Action = value;
}
private readonly OsuSpriteText text;
public QuitButton()
{
Direction = FillDirection.Horizontal;
Spacing = new Vector2(20, 0);
Margin = new MarginPadding(10);
Children = new Drawable[]
{
text = new OsuSpriteText
{
Text = "hold for menu",
Font = @"Exo2.0-Bold",
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft
},
button = new Button
{
HoverGained = () => text.FadeIn(500, Easing.OutQuint),
HoverLost = () => text.FadeOut(500, Easing.OutQuint)
}
};
AutoSizeAxes = Axes.Both;
}
protected override void LoadComplete()
{
text.FadeInFromZero(500, Easing.OutQuint).Delay(1500).FadeOut(500, Easing.OutQuint);
base.LoadComplete();
}
private float positionalAdjust;
protected override bool OnMouseMove(InputState state)
{
positionalAdjust = Vector2.Distance(state.Mouse.NativeState.Position, button.ScreenSpaceDrawQuad.Centre) / 200;
return base.OnMouseMove(state);
}
protected override void Update()
{
base.Update();
if (text.Alpha > 0 || button.Progress.Value > 0 || button.IsHovered)
Alpha = 1;
else
Alpha = Interpolation.ValueAt(
MathHelper.Clamp(Clock.ElapsedFrameTime, 0, 1000),
Alpha, MathHelper.Clamp(1 - positionalAdjust, 0.04f, 1), 0, 200, Easing.OutQuint);
}
private class Button : HoldToConfirmContainer
{
private SpriteIcon icon;
private CircularProgress circularProgress;
private Circle overlayCircle;
protected override bool AllowMultipleFires => true;
public Action HoverGained;
public Action HoverLost;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Size = new Vector2(60);
Child = new CircularContainer
{
Masking = true,
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colours.Gray1,
Alpha = 0.5f,
},
circularProgress = new CircularProgress
{
RelativeSizeAxes = Axes.Both,
InnerRadius = 1
},
overlayCircle = new Circle
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Colour = colours.Gray1,
Size = new Vector2(0.9f),
},
icon = new SpriteIcon
{
Shadow = false,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(15),
Icon = FontAwesome.fa_close
},
}
};
bind();
}
private void bind()
{
circularProgress.Current.BindTo(Progress);
Progress.ValueChanged += v => icon.Scale = new Vector2(1 + (float)v * 0.2f);
}
private bool pendingAnimation;
protected override void Confirm()
{
base.Confirm();
// temporarily unbind as to not look weird if releasing during confirm animation (can see the unwind of progress).
Progress.UnbindAll();
// avoid starting a new confirm call until we finish animating.
pendingAnimation = true;
Progress.Value = 0;
overlayCircle.ScaleTo(0, 100)
.Then().FadeOut().ScaleTo(1).FadeIn(500)
.OnComplete(a =>
{
icon.ScaleTo(1, 100);
circularProgress.FadeOut(100).OnComplete(_ =>
{
bind();
circularProgress.FadeIn();
pendingAnimation = false;
});
});
}
protected override bool OnHover(InputState state)
{
HoverGained?.Invoke();
return true;
}
protected override void OnHoverLost(InputState state)
{
HoverLost?.Invoke();
base.OnHoverLost(state);
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
if (!pendingAnimation && state.Mouse.Buttons.Count == 1)
BeginConfirm();
return true;
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
if (state.Mouse.Buttons.Count == 0)
AbortConfirm();
return true;
}
}
}
}

View File

@ -34,6 +34,7 @@ namespace osu.Game.Screens.Play
public readonly HealthDisplay HealthDisplay; public readonly HealthDisplay HealthDisplay;
public readonly SongProgress Progress; public readonly SongProgress Progress;
public readonly ModDisplay ModDisplay; public readonly ModDisplay ModDisplay;
public readonly QuitButton HoldToQuit;
public readonly PlayerSettingsOverlay PlayerSettingsOverlay; public readonly PlayerSettingsOverlay PlayerSettingsOverlay;
private Bindable<bool> showHud; private Bindable<bool> showHud;
@ -51,14 +52,26 @@ namespace osu.Game.Screens.Play
Children = new Drawable[] Children = new Drawable[]
{ {
KeyCounter = CreateKeyCounter(),
ComboCounter = CreateComboCounter(), ComboCounter = CreateComboCounter(),
ScoreCounter = CreateScoreCounter(), ScoreCounter = CreateScoreCounter(),
AccuracyCounter = CreateAccuracyCounter(), AccuracyCounter = CreateAccuracyCounter(),
HealthDisplay = CreateHealthDisplay(), HealthDisplay = CreateHealthDisplay(),
Progress = CreateProgress(), Progress = CreateProgress(),
ModDisplay = CreateModsContainer(), ModDisplay = CreateModsContainer(),
PlayerSettingsOverlay = CreatePlayerSettingsOverlay() PlayerSettingsOverlay = CreatePlayerSettingsOverlay(),
new FillFlowContainer
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
Position = -new Vector2(5, TwoLayerButton.SIZE_RETRACTED.Y),
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
KeyCounter = CreateKeyCounter(),
HoldToQuit = CreateQuitButton(),
}
}
} }
}); });
@ -187,7 +200,6 @@ namespace osu.Game.Screens.Play
Anchor = Anchor.BottomRight, Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight, Origin = Anchor.BottomRight,
Margin = new MarginPadding(10), Margin = new MarginPadding(10),
Y = -TwoLayerButton.SIZE_RETRACTED.Y,
}; };
protected virtual ScoreCounter CreateScoreCounter() => new ScoreCounter(6) protected virtual ScoreCounter CreateScoreCounter() => new ScoreCounter(6)
@ -205,6 +217,12 @@ namespace osu.Game.Screens.Play
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
}; };
protected virtual QuitButton CreateQuitButton() => new QuitButton
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
};
protected virtual ModDisplay CreateModsContainer() => new ModDisplay protected virtual ModDisplay CreateModsContainer() => new ModDisplay
{ {
Anchor = Anchor.TopRight, Anchor = Anchor.TopRight,

View File

@ -183,6 +183,7 @@ namespace osu.Game.Screens.Play
ProcessCustomClock = false, ProcessCustomClock = false,
Breaks = beatmap.Breaks Breaks = beatmap.Breaks
}, },
RulesetContainer.Cursor?.CreateProxy() ?? new Container(),
hudOverlay = new HUDOverlay(scoreProcessor, RulesetContainer, working, offsetClock, adjustableClock) hudOverlay = new HUDOverlay(scoreProcessor, RulesetContainer, working, offsetClock, adjustableClock)
{ {
Clock = Clock, // hud overlay doesn't want to use the audio clock directly Clock = Clock, // hud overlay doesn't want to use the audio clock directly
@ -190,7 +191,6 @@ namespace osu.Game.Screens.Play
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre Origin = Anchor.Centre
}, },
RulesetContainer.Cursor?.CreateProxy() ?? new Container(),
new SkipOverlay(firstObjectTime) new SkipOverlay(firstObjectTime)
{ {
Clock = Clock, // skip button doesn't want to use the audio clock directly Clock = Clock, // skip button doesn't want to use the audio clock directly
@ -219,6 +219,8 @@ namespace osu.Game.Screens.Play
} }
}; };
hudOverlay.HoldToQuit.Action = Exit;
if (ShowStoryboard) if (ShowStoryboard)
initializeStoryboard(false); initializeStoryboard(false);

View File

@ -7,15 +7,15 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.Localisation;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Framework.Threading;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using OpenTK;
using osu.Framework.Localisation;
using osu.Framework.Threading;
using osu.Game.Screens.Menu; using osu.Game.Screens.Menu;
using osu.Game.Screens.Play.PlayerSettings; using osu.Game.Screens.Play.PlayerSettings;
using OpenTK;
namespace osu.Game.Screens.Play namespace osu.Game.Screens.Play
{ {
@ -51,11 +51,19 @@ namespace osu.Game.Screens.Play
Origin = Anchor.Centre, Origin = Anchor.Centre,
}); });
Add(new VisualSettings Add(new FillFlowContainer<PlayerSettingsGroup>
{ {
Anchor = Anchor.TopRight, Anchor = Anchor.TopRight,
Origin = Anchor.TopRight, Origin = Anchor.TopRight,
Margin = new MarginPadding(25) AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 20),
Margin = new MarginPadding(25),
Children = new PlayerSettingsGroup[]
{
new VisualSettings(),
new InputSettings()
}
}); });
loadTask = LoadComponentAsync(player); loadTask = LoadComponentAsync(player);

View File

@ -0,0 +1,30 @@
// Copyright (c) 2007-2018 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.Game.Configuration;
namespace osu.Game.Screens.Play.PlayerSettings
{
public class InputSettings : PlayerSettingsGroup
{
protected override string Title => "Input settings";
private readonly PlayerCheckbox mouseButtonsCheckbox;
public InputSettings()
{
Children = new Drawable[]
{
mouseButtonsCheckbox = new PlayerCheckbox
{
LabelText = "Disable mouse buttons"
}
};
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config) => mouseButtonsCheckbox.Bindable = config.GetBindable<bool>(OsuSetting.MouseDisableButtons);
}
}

View File

@ -29,7 +29,7 @@ namespace osu.Game.Tests.Visual
protected EditorClockTestCase() protected EditorClockTestCase()
{ {
Clock = new EditorClock(new ControlPointInfo(), BeatDivisor) { IsCoupled = false }; Clock = new EditorClock(new ControlPointInfo(), 5000, BeatDivisor) { IsCoupled = false };
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]