1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-11 16:27:26 +08:00

Merge branch 'master' into mapping-del

This commit is contained in:
Dean Herbert 2018-05-14 20:56:12 +09:00 committed by GitHub
commit 7507d18a93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 252 additions and 93 deletions

View File

@ -14,7 +14,7 @@ using osu.Game.Tests.Beatmaps;
namespace osu.Game.Rulesets.Catch.Tests namespace osu.Game.Rulesets.Catch.Tests
{ {
public class CatchBeatmapConversionTest : BeatmapConversionTest<TestCatchRuleset, ConvertValue> internal class CatchBeatmapConversionTest : BeatmapConversionTest<TestCatchRuleset, ConvertValue>
{ {
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch"; protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Catch.Tests
protected override IBeatmapConverter CreateConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap); protected override IBeatmapConverter CreateConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap);
} }
public struct ConvertValue : IEquatable<ConvertValue> internal struct ConvertValue : IEquatable<ConvertValue>
{ {
/// <summary> /// <summary>
/// A sane value to account for osu!stable using ints everwhere. /// A sane value to account for osu!stable using ints everwhere.
@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Catch.Tests
&& Precision.AlmostEquals(Position, other.Position, conversion_lenience); && Precision.AlmostEquals(Position, other.Position, conversion_lenience);
} }
public class TestCatchRuleset : CatchRuleset internal class TestCatchRuleset : CatchRuleset
{ {
} }
} }

View File

@ -14,7 +14,7 @@ using osu.Game.Tests.Beatmaps;
namespace osu.Game.Rulesets.Mania.Tests namespace osu.Game.Rulesets.Mania.Tests
{ {
public class ManiaBeatmapConversionTest : BeatmapConversionTest<TestManiaRuleset, ConvertValue> internal class ManiaBeatmapConversionTest : BeatmapConversionTest<TestManiaRuleset, ConvertValue>
{ {
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania"; protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Mania.Tests
protected override IBeatmapConverter CreateConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap); protected override IBeatmapConverter CreateConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap);
} }
public struct ConvertValue : IEquatable<ConvertValue> internal struct ConvertValue : IEquatable<ConvertValue>
{ {
/// <summary> /// <summary>
/// A sane value to account for osu!stable using ints everwhere. /// A sane value to account for osu!stable using ints everwhere.
@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Mania.Tests
&& Column == other.Column; && Column == other.Column;
} }
public class TestManiaRuleset : ManiaRuleset internal class TestManiaRuleset : ManiaRuleset
{ {
} }
} }

View File

@ -15,7 +15,7 @@ using OpenTK;
namespace osu.Game.Rulesets.Osu.Tests namespace osu.Game.Rulesets.Osu.Tests
{ {
public class OsuBeatmapConversionTest : BeatmapConversionTest<TestOsuRuleset, ConvertValue> internal class OsuBeatmapConversionTest : BeatmapConversionTest<TestOsuRuleset, ConvertValue>
{ {
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.Tests
protected override IBeatmapConverter CreateConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap); protected override IBeatmapConverter CreateConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap);
} }
public struct ConvertValue : IEquatable<ConvertValue> internal struct ConvertValue : IEquatable<ConvertValue>
{ {
/// <summary> /// <summary>
/// A sane value to account for osu!stable using ints everwhere. /// A sane value to account for osu!stable using ints everwhere.
@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.Tests
&& Precision.AlmostEquals(EndY, other.EndY, conversion_lenience); && Precision.AlmostEquals(EndY, other.EndY, conversion_lenience);
} }
public class TestOsuRuleset : OsuRuleset internal class TestOsuRuleset : OsuRuleset
{ {
} }
} }

View File

@ -18,7 +18,17 @@ namespace osu.Game.Rulesets.Osu.Scoring
private readonly int beatmapMaxCombo; private readonly int beatmapMaxCombo;
private Mod[] mods; private Mod[] mods;
/// <summary>
/// Approach rate adjusted by mods.
/// </summary>
private double realApproachRate; private double realApproachRate;
/// <summary>
/// Overall difficulty adjusted by mods.
/// </summary>
private double realOverallDifficulty;
private double accuracy; private double accuracy;
private int scoreMaxCombo; private int scoreMaxCombo;
private int count300; private int count300;
@ -58,8 +68,12 @@ namespace osu.Game.Rulesets.Osu.Scoring
ar = Math.Min(10, ar * 1.4); ar = Math.Min(10, ar * 1.4);
if (mods.Any(m => m is OsuModEasy)) if (mods.Any(m => m is OsuModEasy))
ar = Math.Max(0, ar / 2); ar = Math.Max(0, ar / 2);
double preEmpt = BeatmapDifficulty.DifficultyRange(ar, 1800, 1200, 450);
double preEmpt = BeatmapDifficulty.DifficultyRange(ar, 1800, 1200, 450) / TimeRate;
double hitWindow300 = (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 - hitWindow300) / 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
@ -85,6 +99,9 @@ namespace osu.Game.Rulesets.Osu.Scoring
categoryRatings.Add("Aim", aimValue); categoryRatings.Add("Aim", aimValue);
categoryRatings.Add("Speed", speedValue); categoryRatings.Add("Speed", speedValue);
categoryRatings.Add("Accuracy", accuracyValue); categoryRatings.Add("Accuracy", accuracyValue);
categoryRatings.Add("OD", realOverallDifficulty);
categoryRatings.Add("AR", realApproachRate);
categoryRatings.Add("Max Combo", beatmapMaxCombo);
} }
return totalValue; return totalValue;
@ -133,7 +150,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
// Scale the aim value with accuracy _slightly_ // Scale the aim value with accuracy _slightly_
aimValue *= 0.5f + accuracy / 2.0f; aimValue *= 0.5f + accuracy / 2.0f;
// It is important to also consider accuracy difficulty when doing that // It is important to also consider accuracy difficulty when doing that
aimValue *= 0.98f + Math.Pow(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 2) / 2500; aimValue *= 0.98f + Math.Pow(realOverallDifficulty, 2) / 2500;
return aimValue; return aimValue;
} }
@ -159,7 +176,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
// Scale the speed value with accuracy _slightly_ // Scale the speed value with accuracy _slightly_
speedValue *= 0.5f + accuracy / 2.0f; speedValue *= 0.5f + accuracy / 2.0f;
// It is important to also consider accuracy difficulty when doing that // It is important to also consider accuracy difficulty when doing that
speedValue *= 0.98f + Math.Pow(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 2) / 2500; speedValue *= 0.98f + Math.Pow(realOverallDifficulty, 2) / 2500;
return speedValue; return speedValue;
} }
@ -181,7 +198,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
// Lots of arbitrary values from testing. // Lots of arbitrary values from testing.
// Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution // Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution
double accuracyValue = Math.Pow(1.52163f, Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty) * Math.Pow(betterAccuracyPercentage, 24) * 2.83f; double accuracyValue = Math.Pow(1.52163f, realOverallDifficulty) * Math.Pow(betterAccuracyPercentage, 24) * 2.83f;
// Bonus for many hitcircles - it's harder to keep good accuracy up for longer // Bonus for many hitcircles - it's harder to keep good accuracy up for longer
accuracyValue *= Math.Min(1.15f, Math.Pow(amountHitObjectsWithAccuracy / 1000.0f, 0.3f)); accuracyValue *= Math.Min(1.15f, Math.Pow(amountHitObjectsWithAccuracy / 1000.0f, 0.3f));

View File

@ -14,7 +14,7 @@ using osu.Game.Tests.Beatmaps;
namespace osu.Game.Rulesets.Taiko.Tests namespace osu.Game.Rulesets.Taiko.Tests
{ {
public class TaikoBeatmapConversionTest : BeatmapConversionTest<TestTaikoRuleset, ConvertValue> internal class TaikoBeatmapConversionTest : BeatmapConversionTest<TestTaikoRuleset, ConvertValue>
{ {
protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko"; protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko";
@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
protected override IBeatmapConverter CreateConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap); protected override IBeatmapConverter CreateConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap);
} }
public struct ConvertValue : IEquatable<ConvertValue> internal struct ConvertValue : IEquatable<ConvertValue>
{ {
/// <summary> /// <summary>
/// A sane value to account for osu!stable using ints everwhere. /// A sane value to account for osu!stable using ints everwhere.
@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
&& IsStrong == other.IsStrong; && IsStrong == other.IsStrong;
} }
public class TestTaikoRuleset : TaikoRuleset internal class TestTaikoRuleset : TaikoRuleset
{ {
} }
} }

View File

@ -0,0 +1,62 @@
// 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.Framework.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Screens.Menu;
namespace osu.Game.Tests.Visual
{
public class TestCaseHoldToConfirmOverlay : OsuTestCase
{
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(ExitConfirmOverlay) };
public TestCaseHoldToConfirmOverlay()
{
bool fired = false;
var firedText = new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Text = "Fired!",
TextSize = 50,
Alpha = 0,
};
var overlay = new TestHoldToConfirmOverlay
{
Action = () =>
{
fired = true;
firedText.FadeTo(1).Then().FadeOut(1000);
}
};
Children = new Drawable[]
{
overlay,
firedText
};
AddStep("start confirming", () => overlay.Begin());
AddStep("abort confirming", () => overlay.Abort());
AddAssert("ensure aborted", () => !fired);
AddStep("start confirming", () => overlay.Begin());
AddUntilStep(() => fired, "wait until confirmed");
}
private class TestHoldToConfirmOverlay : ExitConfirmOverlay
{
protected override bool AllowMultipleFires => true;
public void Begin() => BeginConfirm();
public void Abort() => AbortConfirm();
}
}
}

View File

@ -14,7 +14,7 @@ namespace osu.Game.Beatmaps
protected readonly IBeatmap Beatmap; protected readonly IBeatmap Beatmap;
protected readonly Mod[] Mods; protected readonly Mod[] Mods;
protected double TimeRate = 1; protected double TimeRate { get; private set; } = 1;
protected DifficultyCalculator(IBeatmap beatmap, Mod[] mods = null) protected DifficultyCalculator(IBeatmap beatmap, Mod[] mods = null)
{ {

View File

@ -44,19 +44,6 @@ namespace osu.Game.Graphics.Containers
return base.OnClick(state); return base.OnClick(state);
} }
protected override bool OnDragStart(InputState state)
{
if (!base.ReceiveMouseInputAt(state.Mouse.NativeState.Position))
{
State = Visibility.Hidden;
return true;
}
return base.OnDragStart(state);
}
protected override bool OnDrag(InputState state) => State == Visibility.Hidden;
private void onStateChanged(Visibility visibility) private void onStateChanged(Visibility visibility)
{ {
switch (visibility) switch (visibility)

View File

@ -0,0 +1,66 @@
// 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 OpenTK.Graphics;
namespace osu.Game.Overlays
{
/// <summary>
/// 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).
/// </summary>
public abstract class HoldToConfirmOverlay : Container
{
public Action Action;
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]
private void load()
{
RelativeSizeAxes = Axes.Both;
AlwaysPresent = true;
Children = new Drawable[]
{
overlay = new Box
{
Alpha = 0,
Colour = Color4.Black,
RelativeSizeAxes = Axes.Both,
}
};
}
protected void BeginConfirm()
{
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

@ -100,6 +100,8 @@ namespace osu.Game.Overlays
public bool Adjust(GlobalAction action) public bool Adjust(GlobalAction action)
{ {
if (!IsLoaded) return false;
switch (action) switch (action)
{ {
case GlobalAction.DecreaseVolume: case GlobalAction.DecreaseVolume:

View File

@ -112,7 +112,7 @@ namespace osu.Game.Rulesets
try try
{ {
var assembly = Assembly.LoadFrom(file); var assembly = Assembly.LoadFrom(file);
loaded_assemblies[assembly] = assembly.GetTypes().First(t => t.IsSubclassOf(typeof(Ruleset))); loaded_assemblies[assembly] = assembly.GetTypes().First(t => t.IsPublic && t.IsSubclassOf(typeof(Ruleset)));
} }
catch (Exception) catch (Exception)
{ {

View File

@ -2,7 +2,11 @@
// 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.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Timing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Scoring namespace osu.Game.Rulesets.Scoring
{ {
@ -14,6 +18,8 @@ namespace osu.Game.Rulesets.Scoring
protected readonly IBeatmap Beatmap; protected readonly IBeatmap Beatmap;
protected readonly Score Score; protected readonly Score Score;
protected double TimeRate { get; private set; } = 1;
protected PerformanceCalculator(Ruleset ruleset, IBeatmap beatmap, Score score) protected PerformanceCalculator(Ruleset ruleset, IBeatmap beatmap, Score score)
{ {
Score = score; Score = score;
@ -22,6 +28,15 @@ namespace osu.Game.Rulesets.Scoring
var diffCalc = ruleset.CreateDifficultyCalculator(beatmap, score.Mods); var diffCalc = ruleset.CreateDifficultyCalculator(beatmap, score.Mods);
diffCalc.Calculate(attributes); diffCalc.Calculate(attributes);
ApplyMods(score.Mods);
}
protected virtual void ApplyMods(Mod[] mods)
{
var clock = new StopwatchClock();
mods.OfType<IApplicableToClock>().ForEach(m => m.ApplyToClock(clock));
TimeRate = clock.Rate;
} }
public abstract double Calculate(Dictionary<string, double> categoryDifficulty = null); public abstract double Calculate(Dictionary<string, double> categoryDifficulty = null);

View File

@ -0,0 +1,34 @@
// 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.Input;
using osu.Game.Overlays;
using OpenTK.Input;
namespace osu.Game.Screens.Menu
{
public class ExitConfirmOverlay : HoldToConfirmOverlay
{
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
{
if (args.Key == Key.Escape && !args.Repeat)
{
BeginConfirm();
return true;
}
return base.OnKeyDown(state, args);
}
protected override bool OnKeyUp(InputState state, KeyUpEventArgs args)
{
if (args.Key == Key.Escape)
{
AbortConfirm();
return true;
}
return base.OnKeyUp(state, args);
}
}
}

View File

@ -39,6 +39,10 @@ namespace osu.Game.Screens.Menu
Children = new Drawable[] Children = new Drawable[]
{ {
new ExitConfirmOverlay
{
Action = Exit,
},
new ParallaxContainer new ParallaxContainer
{ {
ParallaxAmount = 0.01f, ParallaxAmount = 0.01f,

View File

@ -1,50 +1,19 @@
// 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 osu.Framework.Allocation;
using System;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Game.Input.Bindings; using osu.Game.Input.Bindings;
using OpenTK.Graphics; using osu.Game.Overlays;
namespace osu.Game.Screens.Play namespace osu.Game.Screens.Play
{ {
public class HotkeyRetryOverlay : Container, IKeyBindingHandler<GlobalAction> public class HotkeyRetryOverlay : HoldToConfirmOverlay, IKeyBindingHandler<GlobalAction>
{ {
public Action Action;
private Box overlay;
private const int activate_delay = 400;
private const int fadeout_delay = 200;
private bool fired;
[BackgroundDependencyLoader]
private void load()
{
RelativeSizeAxes = Axes.Both;
AlwaysPresent = true;
Children = new Drawable[]
{
overlay = new Box
{
Alpha = 0,
Colour = Color4.Black,
RelativeSizeAxes = Axes.Both,
}
};
}
public bool OnPressed(GlobalAction action) public bool OnPressed(GlobalAction action)
{ {
if (action != GlobalAction.QuickRetry) return false; if (action != GlobalAction.QuickRetry) return false;
overlay.FadeIn(activate_delay, Easing.Out); BeginConfirm();
return true; return true;
} }
@ -52,18 +21,8 @@ namespace osu.Game.Screens.Play
{ {
if (action != GlobalAction.QuickRetry) return false; if (action != GlobalAction.QuickRetry) return false;
overlay.FadeOut(fadeout_delay, Easing.Out); AbortConfirm();
return true; return true;
} }
protected override void Update()
{
base.Update();
if (!fired && overlay.Alpha == 1)
{
fired = true;
Action?.Invoke();
}
}
} }
} }

View File

@ -162,7 +162,7 @@ namespace osu.Game.Screens.Play
hudOverlay.KeyCounter.IsCounting = pauseContainer.IsPaused; hudOverlay.KeyCounter.IsCounting = pauseContainer.IsPaused;
}, },
OnResume = () => hudOverlay.KeyCounter.IsCounting = true, OnResume = () => hudOverlay.KeyCounter.IsCounting = true,
Children = new Drawable[] Children = new[]
{ {
storyboardContainer = new Container storyboardContainer = new Container
{ {
@ -174,12 +174,12 @@ namespace osu.Game.Screens.Play
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Child = RulesetContainer Child = RulesetContainer
}, },
new SkipOverlay(firstObjectTime) new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks, scoreProcessor)
{ {
Clock = Clock, // skip button doesn't want to use the audio clock directly Anchor = Anchor.Centre,
Origin = Anchor.Centre,
ProcessCustomClock = false, ProcessCustomClock = false,
AdjustableClock = adjustableClock, Breaks = beatmap.Breaks
FramedClock = offsetClock,
}, },
hudOverlay = new HUDOverlay(scoreProcessor, RulesetContainer, working, offsetClock, adjustableClock) hudOverlay = new HUDOverlay(scoreProcessor, RulesetContainer, working, offsetClock, adjustableClock)
{ {
@ -188,13 +188,14 @@ namespace osu.Game.Screens.Play
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre Origin = Anchor.Centre
}, },
new BreakOverlay(beatmap.BeatmapInfo.LetterboxInBreaks, scoreProcessor) RulesetContainer.Cursor?.CreateProxy() ?? new Container(),
new SkipOverlay(firstObjectTime)
{ {
Anchor = Anchor.Centre, Clock = Clock, // skip button doesn't want to use the audio clock directly
Origin = Anchor.Centre,
ProcessCustomClock = false, ProcessCustomClock = false,
Breaks = beatmap.Breaks AdjustableClock = adjustableClock,
} FramedClock = offsetClock,
},
} }
}, },
failOverlay = new FailOverlay failOverlay = new FailOverlay

View File

@ -85,11 +85,13 @@ namespace osu.Game.Screens.Play
if (currentSecond != previousSecond && songCurrentTime < songLength) if (currentSecond != previousSecond && songCurrentTime < songLength)
{ {
timeCurrent.Text = TimeSpan.FromSeconds(currentSecond).ToString(songCurrentTime < 0 ? @"\-m\:ss" : @"m\:ss"); timeCurrent.Text = formatTime(TimeSpan.FromSeconds(currentSecond));
timeLeft.Text = TimeSpan.FromMilliseconds(endTime - AudioClock.CurrentTime).ToString(@"\-m\:ss"); timeLeft.Text = formatTime(TimeSpan.FromMilliseconds(endTime - AudioClock.CurrentTime));
previousSecond = currentSecond; previousSecond = currentSecond;
} }
} }
private string formatTime(TimeSpan timeSpan) => $"{(timeSpan < TimeSpan.Zero ? "-" : "")}{timeSpan.Duration().TotalMinutes:N0}:{timeSpan.Duration().Seconds:D2}";
} }
} }

View File

@ -88,17 +88,27 @@ namespace osu.Game.Screens.Select
private void loadBeatmap() private void loadBeatmap()
{ {
void updateState()
{
State = beatmap == null ? Visibility.Hidden : Visibility.Visible;
Info?.FadeOut(250);
Info?.Expire();
}
if (beatmap == null)
{
updateState();
return;
}
LoadComponentAsync(new BufferedWedgeInfo(beatmap, ruleset.Value) LoadComponentAsync(new BufferedWedgeInfo(beatmap, ruleset.Value)
{ {
Shear = -Shear, Shear = -Shear,
Depth = Info?.Depth + 1 ?? 0, Depth = Info?.Depth + 1 ?? 0,
}, newInfo => }, newInfo =>
{ {
State = beatmap == null ? Visibility.Hidden : Visibility.Visible; updateState();
Info?.FadeOut(250);
Info?.Expire();
Add(Info = newInfo); Add(Info = newInfo);
}); });
} }