Merge branch 'master' into generic-interface

This commit is contained in:
Huo Yaoyuan 2017-05-18 19:19:40 +08:00
commit ff5854d6bf
13 changed files with 335 additions and 370 deletions

View File

@ -12,7 +12,7 @@ namespace osu.Desktop.VisualTests.Tests
{
public override string Description => @"Tests pause and fail overlays";
private PauseOverlay pauseOverlay;
private PauseContainer.PauseOverlay pauseOverlay;
private FailOverlay failOverlay;
private int retryCount;
@ -22,7 +22,7 @@ namespace osu.Desktop.VisualTests.Tests
retryCount = 0;
Add(pauseOverlay = new PauseOverlay
Add(pauseOverlay = new PauseContainer.PauseOverlay
{
OnResume = () => Logger.Log(@"Resume"),
OnRetry = () => Logger.Log(@"Retry"),

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 System;
namespace osu.Game.Rulesets.Mania.MathUtils
{
/// <summary>
/// A PRNG specified in http://heliosphan.org/fastrandom.html.
/// </summary>
internal class FastRandom
{
private const double uint_to_real = 1.0 / (int.MaxValue + 1.0);
private const uint int_mask = 0x7FFFFFFF;
private const uint y = 842502087;
private const uint z = 3579807591;
private const uint w = 273326509;
private uint _x, _y = y, _z = z, _w = w;
public FastRandom(int seed)
{
_x = (uint)seed;
}
public FastRandom()
: this(Environment.TickCount)
{
}
/// <summary>
/// Generates a random unsigned integer within the range [<see cref="uint.MinValue"/>, <see cref="uint.MaxValue"/>).
/// </summary>
/// <returns>The random value.</returns>
public uint NextUInt()
{
uint t = _x ^ _x << 11;
_x = _y;
_y = _z;
_z = _w;
return _w = _w ^ _w >> 19 ^ t ^ t >> 8;
}
/// <summary>
/// Generates a random integer value within the range [0, <see cref="int.MaxValue"/>).
/// </summary>
/// <returns>The random value.</returns>
public int Next() => (int)(int_mask & NextUInt());
/// <summary>
/// Generates a random integer value within the range [0, <paramref name="upperBound"/>).
/// </summary>
/// <param name="upperBound">The upper bound.</param>
/// <returns>The random value.</returns>
public int Next(int upperBound) => (int)(NextDouble() * upperBound);
/// <summary>
/// Generates a random integer value within the range [<paramref name="lowerBound"/>, <paramref name="upperBound"/>).
/// </summary>
/// <param name="lowerBound">The lower bound of the range.</param>
/// <param name="upperBound">The upper bound of the range.</param>
/// <returns>The random value.</returns>
public int Next(int lowerBound, int upperBound) => (int)(lowerBound + NextDouble() * (upperBound - lowerBound));
/// <summary>
/// Generates a random double value within the range [0, 1).
/// </summary>
/// <returns>The random value.</returns>
public double NextDouble() => uint_to_real * NextUInt();
private uint bitBuffer;
private int bitIndex = 32;
/// <summary>
/// Generates a reandom boolean value. Cached such that a random value is only generated once in every 32 calls.
/// </summary>
/// <returns>The random value.</returns>
public bool NextBool()
{
if (bitIndex == 32)
{
bitBuffer = NextUInt();
bitIndex = 1;
return (bitBuffer & 1) == 1;
}
bitIndex++;
return ((bitBuffer >>= 1) & 1) == 1;
}
}
}

View File

@ -48,6 +48,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Beatmaps\ManiaBeatmapConverter.cs" />
<Compile Include="MathUtils\FastRandom.cs" />
<Compile Include="Judgements\HitWindows.cs" />
<Compile Include="Judgements\ManiaJudgement.cs" />
<Compile Include="ManiaDifficultyCalculator.cs" />

View File

@ -41,12 +41,11 @@ namespace osu.Game.Screens.Play
get { return isCounting; }
set
{
if (value != isCounting)
{
isCounting = value;
foreach (var child in Children)
child.IsCounting = value;
}
if (value == isCounting) return;
isCounting = value;
foreach (var child in Children)
child.IsCounting = value;
}
}

View File

@ -8,11 +8,11 @@ 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.Framework.Audio;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Screens.Play
@ -89,7 +89,7 @@ namespace osu.Game.Screens.Play
protected void AddButton(string text, Color4 colour, Action action)
{
Buttons.Add(new PauseButton
Buttons.Add(new Button
{
Text = text,
ButtonColour = colour,
@ -179,12 +179,6 @@ namespace osu.Game.Screens.Play
}
}
},
new PauseProgressBar
{
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
Width = 1f
}
};
Retries = 0;
@ -195,5 +189,15 @@ namespace osu.Game.Screens.Play
AlwaysReceiveInput = true;
RelativeSizeAxes = Axes.Both;
}
public class Button : DialogButton
{
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
SampleHover = audio.Sample.Get(@"Menu/menuclick");
SampleClick = audio.Sample.Get(@"Menu/menuback");
}
}
}
}

View File

@ -1,19 +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.UserInterface;
namespace osu.Game.Screens.Play.Pause
{
public class PauseButton : DialogButton
{
[BackgroundDependencyLoader]
private void load(AudioManager audio)
{
SampleHover = audio.Sample.Get(@"Menu/menuclick");
SampleClick = audio.Sample.Get(@"Menu/menuback");
}
}
}

View File

@ -1,149 +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.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using OpenTK.Graphics;
namespace osu.Game.Screens.Play.Pause
{
public class PauseProgressBar : Container
{
private readonly Color4 fillColour = new Color4(221, 255, 255, 255);
private readonly Color4 glowColour = new Color4(221, 255, 255, 150);
private readonly Container fill;
private WorkingBeatmap current;
[BackgroundDependencyLoader]
private void load(OsuGameBase osuGame)
{
current = osuGame.Beatmap.Value;
}
protected override void Update()
{
base.Update();
if (current?.TrackLoaded ?? false)
{
fill.Width = (float)(current.Track.CurrentTime / current.Track.Length);
}
}
public PauseProgressBar()
{
RelativeSizeAxes = Axes.X;
Height = 60;
Children = new Drawable[]
{
new PauseProgressGraph
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
Height = 35,
Margin = new MarginPadding
{
Bottom = 5
}
},
new Container
{
Origin = Anchor.BottomRight,
Anchor = Anchor.BottomRight,
RelativeSizeAxes = Axes.X,
Height = 5,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
Alpha = 0.5f
}
}
},
fill = new Container
{
RelativeSizeAxes = Axes.X,
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
Width = 0,
Height = 60,
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
Masking = true,
Children = new Drawable[]
{
new Container
{
Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Height = 5,
Masking = true,
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Glow,
Colour = glowColour,
Radius = 5
},
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = fillColour
}
}
}
}
},
new Container
{
Origin = Anchor.BottomRight,
Anchor = Anchor.BottomRight,
Width = 2,
Height = 35,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White
},
new Container
{
Origin = Anchor.BottomCentre,
Anchor = Anchor.TopCentre,
Width = 14,
Height = 25,
CornerRadius = 5,
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White
}
}
}
}
}
}
}
};
}
}
}

View File

@ -1,12 +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.Graphics.Containers;
namespace osu.Game.Screens.Play.Pause
{
public class PauseProgressGraph : Container
{
// TODO: Implement the pause progress graph
}
}

View File

@ -0,0 +1,155 @@
// 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 osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Framework.Timing;
using osu.Game.Graphics;
using OpenTK.Graphics;
using OpenTK.Input;
namespace osu.Game.Screens.Play
{
/// <summary>
/// A container which handles pausing children, displaying a pause overlay with choices etc.
/// This alleviates a lot of the intricate pause logic from being in <see cref="Player"/>
/// </summary>
public class PauseContainer : Container
{
public bool IsPaused { get; private set; }
public bool AllowExit => IsPaused && pauseOverlay.Alpha == 1;
public Func<bool> CheckCanPause;
private const double pause_cooldown = 1000;
private double lastPauseActionTime;
private readonly PauseOverlay pauseOverlay;
private readonly Container content;
protected override Container<Drawable> Content => content;
public int Retries { set { pauseOverlay.Retries = value; } }
public bool CanPause => (CheckCanPause?.Invoke() ?? true) && Time.Current >= lastPauseActionTime + pause_cooldown;
public Action OnRetry;
public Action OnQuit;
public Action OnResume;
public Action OnPause;
public IAdjustableClock AudioClock;
public FramedClock FramedClock;
public PauseContainer()
{
RelativeSizeAxes = Axes.Both;
AddInternal(content = new Container { RelativeSizeAxes = Axes.Both });
AddInternal(pauseOverlay = new PauseOverlay
{
OnResume = delegate
{
Delay(400);
Schedule(Resume);
},
OnRetry = () => OnRetry(),
OnQuit = () => OnQuit(),
});
}
public void Pause(bool force = false)
{
if (!CanPause && !force) return;
if (IsPaused) return;
// stop the decoupled clock (stops the audio eventually)
AudioClock.Stop();
// stop processing updatess on the offset clock (instantly freezes time for all our components)
FramedClock.ProcessSourceClockFrames = false;
IsPaused = true;
// 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 current processing frame.
Schedule(() =>
{
if (!CanPause) return;
lastPauseActionTime = Time.Current;
OnPause?.Invoke();
pauseOverlay.Show();
});
}
public void Resume()
{
if (!IsPaused) return;
IsPaused = false;
FramedClock.ProcessSourceClockFrames = true;
lastPauseActionTime = Time.Current;
OnResume?.Invoke();
pauseOverlay.Hide();
AudioClock.Start();
}
private OsuGameBase game;
[BackgroundDependencyLoader]
private void load(OsuGameBase game)
{
this.game = game;
}
protected override void Update()
{
// eagerly pause when we lose window focus (if we are locally playing).
if (!game.IsActive && CanPause)
Pause();
base.Update();
}
public class PauseOverlay : MenuOverlay
{
public Action OnResume;
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.Repeat && args.Key == Key.Escape)
{
Buttons.Children.First().TriggerClick();
return true;
}
return base.OnKeyDown(state, args);
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
AddButton("Continue", colours.Green, OnResume);
AddButton("Retry", colours.YellowDark, OnRetry);
AddButton("Quit", new Color4(170, 27, 39, 255), OnQuit);
}
}
}
}

View File

@ -1,40 +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 System;
using System.Linq;
using osu.Framework.Input;
using osu.Game.Graphics;
using OpenTK.Input;
using OpenTK.Graphics;
using osu.Framework.Allocation;
namespace osu.Game.Screens.Play
{
public class PauseOverlay : MenuOverlay
{
public Action OnResume;
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.Repeat && args.Key == Key.Escape)
{
Buttons.Children.First().TriggerClick();
return true;
}
return base.OnKeyDown(state, args);
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
AddButton("Continue", colours.Green, OnResume);
AddButton("Retry", colours.YellowDark, OnRetry);
AddButton("Quit", new Color4(170, 27, 39, 255), OnQuit);
}
}
}

View File

@ -32,29 +32,24 @@ namespace osu.Game.Screens.Play
internal override bool ShowOverlays => false;
internal override bool HasLocalCursorDisplayed => !IsPaused && !HasFailed && HitRenderer.ProvidingUserCursor;
internal override bool HasLocalCursorDisplayed => !pauseContainer.IsPaused && !HasFailed && HitRenderer.ProvidingUserCursor;
public BeatmapInfo BeatmapInfo;
public Action RestartRequested;
public bool IsPaused { get; private set; }
internal override bool AllowRulesetChange => false;
public bool HasFailed { get; private set; }
public int RestartCount;
private const double pause_cooldown = 1000;
private double lastPauseActionTime;
private bool canPause => ValidForResume && !HasFailed && Time.Current >= lastPauseActionTime + pause_cooldown;
private IAdjustableClock adjustableSourceClock;
private FramedOffsetClock offsetClock;
private DecoupleableInterpolatingFramedClock decoupledClock;
private PauseContainer pauseContainer;
private RulesetInfo ruleset;
private ScoreProcessor scoreProcessor;
@ -70,10 +65,7 @@ namespace osu.Game.Screens.Play
private SkipButton skipButton;
private Container hitRendererContainer;
private HUDOverlay hudOverlay;
private PauseOverlay pauseOverlay;
private FailOverlay failOverlay;
[BackgroundDependencyLoader(permitNulls: true)]
@ -152,14 +144,63 @@ namespace osu.Game.Screens.Play
decoupledClock.ChangeSource(adjustableSourceClock);
});
scoreProcessor = HitRenderer.CreateScoreProcessor();
hudOverlay = new StandardHUDOverlay()
Children = new Drawable[]
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre
pauseContainer = new PauseContainer
{
AudioClock = decoupledClock,
FramedClock = offsetClock,
OnRetry = Restart,
OnQuit = Exit,
CheckCanPause = () => ValidForResume && !HasFailed,
Retries = RestartCount,
OnPause = () => {
hudOverlay.KeyCounter.IsCounting = pauseContainer.IsPaused;
},
OnResume = () => {
hudOverlay.KeyCounter.IsCounting = true;
},
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Clock = offsetClock,
Children = new Drawable[]
{
HitRenderer,
skipButton = new SkipButton
{
Alpha = 0,
Margin = new MarginPadding { Bottom = 140 } // this is temporary
},
}
},
hudOverlay = new StandardHUDOverlay
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre
},
}
},
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();
},
}
};
scoreProcessor = HitRenderer.CreateScoreProcessor();
hudOverlay.KeyCounter.Add(rulesetInstance.CreateGameplayKeys());
hudOverlay.BindProcessor(scoreProcessor);
hudOverlay.BindHitRenderer(HitRenderer);
@ -176,61 +217,6 @@ namespace osu.Game.Screens.Play
//bind ScoreProcessor to ourselves (for a fail situation)
scoreProcessor.Failed += onFail;
Children = new Drawable[]
{
hitRendererContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Clock = offsetClock,
Children = new Drawable[]
{
HitRenderer,
skipButton = new SkipButton { Alpha = 0 },
}
},
}
},
hudOverlay,
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();
},
}
};
}
protected override void Update()
{
// eagerly pause when we lose window focus (if we are locally playing).
if (!Game.IsActive && !HitRenderer.HasReplayLoaded)
Pause();
base.Update();
}
private void initializeSkipButton()
@ -260,48 +246,6 @@ namespace osu.Game.Screens.Play
skipButton.Expire();
}
public void Pause(bool force = false)
{
if (!canPause && !force) return;
if (IsPaused) return;
// stop the decoupled clock (stops the audio eventually)
decoupledClock.Stop();
// stop processing updatess on the offset clock (instantly freezes time for all our components)
offsetClock.ProcessSourceClockFrames = false;
IsPaused = true;
// 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 current processing frame.
Schedule(() =>
{
if (!canPause) return;
lastPauseActionTime = Time.Current;
hudOverlay.KeyCounter.IsCounting = false;
hudOverlay.Progress.Show();
pauseOverlay.Retries = RestartCount;
pauseOverlay.Show();
});
}
public void Resume()
{
if (!IsPaused) return;
IsPaused = false;
offsetClock.ProcessSourceClockFrames = true;
lastPauseActionTime = Time.Current;
hudOverlay.KeyCounter.IsCounting = true;
hudOverlay.Progress.Hide();
pauseOverlay.Hide();
decoupledClock.Start();
}
public void Restart()
{
ValidForResume = false;
@ -367,8 +311,8 @@ namespace osu.Game.Screens.Play
initializeSkipButton();
});
hitRendererContainer.Alpha = 0;
hitRendererContainer.FadeIn(750, EasingTypes.OutQuint);
pauseContainer.Alpha = 0;
pauseContainer.FadeIn(750, EasingTypes.OutQuint);
}
protected override void OnSuspending(Screen next)
@ -379,23 +323,14 @@ namespace osu.Game.Screens.Play
protected override bool OnExiting(Screen next)
{
if (!HasFailed && ValidForResume)
if (HasFailed || !ValidForResume || pauseContainer.AllowExit || HitRenderer.HasReplayLoaded)
{
if (pauseOverlay != null && !HitRenderer.HasReplayLoaded)
{
//pause screen override logic.
if (pauseOverlay?.State == Visibility.Hidden && !canPause) return true;
if (!IsPaused) // For if the user presses escape quickly when entering the map
{
Pause();
return true;
}
}
fadeOut();
return base.OnExiting(next);
}
fadeOut();
return base.OnExiting(next);
pauseContainer.Pause();
return true;
}
private void fadeOut()
@ -410,6 +345,6 @@ namespace osu.Game.Screens.Play
Background?.FadeTo(1f, fade_out_duration);
}
protected override bool OnWheel(InputState state) => mouseWheelDisabled.Value && !IsPaused;
protected override bool OnWheel(InputState state) => mouseWheelDisabled.Value && !pauseContainer.IsPaused;
}
}

View File

@ -31,6 +31,8 @@ namespace osu.Game.Screens.Play
public Action<double> OnSeek;
public override bool HandleInput => AllowSeeking;
private IClock audioClock;
public IClock AudioClock { set { audioClock = info.AudioClock = value; } }

View File

@ -231,6 +231,7 @@
<Compile Include="Screens\Charts\ChartInfo.cs" />
<Compile Include="Screens\Edit\Editor.cs" />
<Compile Include="Screens\Play\HotkeyRetryOverlay.cs" />
<Compile Include="Screens\Play\PauseContainer.cs" />
<Compile Include="Screens\Play\SongProgressInfo.cs" />
<Compile Include="Screens\Play\HUD\ModDisplay.cs" />
<Compile Include="Screens\Play\SquareGraph.cs" />
@ -254,8 +255,6 @@
<Compile Include="Screens\Play\FailOverlay.cs" />
<Compile Include="Screens\Play\MenuOverlay.cs" />
<Compile Include="Screens\Play\KeyConversionInputManager.cs" />
<Compile Include="Screens\Play\PauseOverlay.cs" />
<Compile Include="Screens\Play\Pause\PauseButton.cs" />
<Compile Include="Screens\Play\PlayerInputManager.cs" />
<Compile Include="Screens\Play\PlayerLoader.cs" />
<Compile Include="Screens\Play\ReplayPlayer.cs" />
@ -395,8 +394,6 @@
<Compile Include="Screens\Play\SongProgress.cs" />
<Compile Include="Screens\Play\SongProgressGraph.cs" />
<Compile Include="Screens\Play\SongProgressBar.cs" />
<Compile Include="Screens\Play\Pause\PauseProgressBar.cs" />
<Compile Include="Screens\Play\Pause\PauseProgressGraph.cs" />
<Compile Include="Overlays\Mods\ModSelectOverlay.cs" />
<Compile Include="Rulesets\Mods\Mod.cs" />
<Compile Include="Overlays\Mods\ModButtonEmpty.cs" />