1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-16 04:32:57 +08:00

Merge branch 'master' of ppy/osu into menu-flashes

This commit is contained in:
ColdVolcano 2017-05-20 11:39:39 -05:00
commit 2014d35301
75 changed files with 1660 additions and 786 deletions

@ -1 +1 @@
Subproject commit 67f39580365f7d0a42f8788eae2b60881dde1c67 Subproject commit d00a7df902074d0b3f1479904b7f322db9d39c1f

View File

@ -85,25 +85,25 @@ namespace osu.Desktop.VisualTests.Tests
Clock = new FramedClock(), Clock = new FramedClock(),
Children = new Drawable[] Children = new Drawable[]
{ {
new OsuHitRenderer(beatmap) new OsuHitRenderer(beatmap, false)
{ {
Scale = new Vector2(0.5f), Scale = new Vector2(0.5f),
Anchor = Anchor.TopLeft, Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft Origin = Anchor.TopLeft
}, },
new TaikoHitRenderer(beatmap) new TaikoHitRenderer(beatmap, false)
{ {
Scale = new Vector2(0.5f), Scale = new Vector2(0.5f),
Anchor = Anchor.TopRight, Anchor = Anchor.TopRight,
Origin = Anchor.TopRight Origin = Anchor.TopRight
}, },
new CatchHitRenderer(beatmap) new CatchHitRenderer(beatmap, false)
{ {
Scale = new Vector2(0.5f), Scale = new Vector2(0.5f),
Anchor = Anchor.BottomLeft, Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft Origin = Anchor.BottomLeft
}, },
new ManiaHitRenderer(beatmap) new ManiaHitRenderer(beatmap, false)
{ {
Scale = new Vector2(0.5f), Scale = new Vector2(0.5f),
Anchor = Anchor.BottomRight, Anchor = Anchor.BottomRight,

View File

@ -129,8 +129,6 @@ namespace osu.Desktop.VisualTests.Tests
}; };
Add(clockAdjustContainer); Add(clockAdjustContainer);
load(mode);
} }
private int depth; private int depth;

View File

@ -43,13 +43,8 @@ namespace osu.Desktop.VisualTests.Tests
RelativeCoordinateSpace = new Vector2(1, 10000), RelativeCoordinateSpace = new Vector2(1, 10000),
Children = new[] Children = new[]
{ {
new DrawableNote(new Note new DrawableNote(new Note { StartTime = 5000 }) { AccentColour = Color4.Red },
{ new DrawableNote(new Note { StartTime = 6000 }) { AccentColour = Color4.Red }
StartTime = 5000
})
{
AccentColour = Color4.Red
}
} }
} }
} }
@ -74,10 +69,7 @@ namespace osu.Desktop.VisualTests.Tests
{ {
StartTime = 5000, StartTime = 5000,
Duration = 1000 Duration = 1000
}) }) { AccentColour = Color4.Red }
{
AccentColour = Color4.Red
}
} }
} }
} }

View File

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

View File

@ -16,7 +16,7 @@ namespace osu.Desktop.VisualTests.Tests
base.Reset(); base.Reset();
Add(new BackButton()); Add(new BackButton());
Add(new SkipButton()); Add(new SkipButton(Clock.CurrentTime + 5000));
} }
} }
} }

View File

@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Catch
{ {
public class CatchRuleset : Ruleset public class CatchRuleset : Ruleset
{ {
public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap) => new CatchHitRenderer(beatmap); public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new CatchHitRenderer(beatmap, isForCurrentRuleset);
public override IEnumerable<Mod> GetModsFor(ModType type) public override IEnumerable<Mod> GetModsFor(ModType type)
{ {

View File

@ -15,8 +15,8 @@ namespace osu.Game.Rulesets.Catch.UI
{ {
public class CatchHitRenderer : HitRenderer<CatchBaseHit, CatchJudgement> public class CatchHitRenderer : HitRenderer<CatchBaseHit, CatchJudgement>
{ {
public CatchHitRenderer(WorkingBeatmap beatmap) public CatchHitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(beatmap) : base(beatmap, isForCurrentRuleset)
{ {
} }

View File

@ -1,35 +1,153 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 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.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using System.Collections.Generic;
using System;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Beatmaps; using osu.Game.Rulesets.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using System;
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using OpenTK; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Mania.Beatmaps.Patterns;
using osu.Game.Rulesets.Mania.MathUtils;
using osu.Game.Database;
namespace osu.Game.Rulesets.Mania.Beatmaps namespace osu.Game.Rulesets.Mania.Beatmaps
{ {
internal class ManiaBeatmapConverter : BeatmapConverter<ManiaHitObject> public class ManiaBeatmapConverter : BeatmapConverter<ManiaHitObject>
{ {
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) }; protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
private Pattern lastPattern = new Pattern();
private FastRandom random;
private Beatmap beatmap;
private bool isForCurrentRuleset;
protected override Beatmap<ManiaHitObject> ConvertBeatmap(Beatmap original, bool isForCurrentRuleset)
{
this.isForCurrentRuleset = isForCurrentRuleset;
beatmap = original;
BeatmapDifficulty difficulty = original.BeatmapInfo.Difficulty;
int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate);
random = new FastRandom(seed);
return base.ConvertBeatmap(original, isForCurrentRuleset);
}
protected override IEnumerable<ManiaHitObject> ConvertHitObject(HitObject original, Beatmap beatmap) protected override IEnumerable<ManiaHitObject> ConvertHitObject(HitObject original, Beatmap beatmap)
{ {
int availableColumns = (int)Math.Round(beatmap.BeatmapInfo.Difficulty.CircleSize); var maniaOriginal = original as ManiaHitObject;
if (maniaOriginal != null)
var positionData = original as IHasXPosition;
float localWDivisor = 512.0f / availableColumns;
int column = MathHelper.Clamp((int)Math.Floor((positionData?.X ?? 1) / localWDivisor), 0, availableColumns - 1);
yield return new Note
{ {
StartTime = original.StartTime, yield return maniaOriginal;
Column = column, yield break;
}; }
var objects = isForCurrentRuleset ? generateSpecific(original) : generateConverted(original);
if (objects == null)
yield break;
foreach (ManiaHitObject obj in objects)
yield return obj;
}
/// <summary>
/// Method that generates hit objects for osu!mania specific beatmaps.
/// </summary>
/// <param name="original">The original hit object.</param>
/// <returns>The hit objects generated.</returns>
private IEnumerable<ManiaHitObject> generateSpecific(HitObject original)
{
var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern);
Pattern newPattern = generator.Generate();
lastPattern = newPattern;
return newPattern.HitObjects;
}
/// <summary>
/// Method that generates hit objects for non-osu!mania beatmaps.
/// </summary>
/// <param name="original">The original hit object.</param>
/// <returns>The hit objects generated.</returns>
private IEnumerable<ManiaHitObject> generateConverted(HitObject original)
{
var endTimeData = original as IHasEndTime;
var distanceData = original as IHasDistance;
var positionData = original as IHasPosition;
// Following lines currently commented out to appease resharper
//Patterns.PatternGenerator conversion = null;
if (distanceData != null)
{
// Slider
}
else if (endTimeData != null)
{
// Spinner
}
else if (positionData != null)
{
// Circle
}
//if (conversion == null)
return null;
//Pattern newPattern = conversion.Generate();
//lastPattern = newPattern;
//return newPattern.HitObjects;
}
/// <summary>
/// A pattern generator for osu!mania-specific beatmaps.
/// </summary>
private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator
{
public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern)
: base(random, hitObject, beatmap, previousPattern)
{
}
public override Pattern Generate()
{
var endTimeData = HitObject as IHasEndTime;
var positionData = HitObject as IHasXPosition;
int column = GetColumn(positionData?.X ?? 0);
var pattern = new Pattern();
if (endTimeData != null)
{
pattern.Add(new HoldNote
{
StartTime = HitObject.StartTime,
Samples = HitObject.Samples,
Duration = endTimeData.Duration,
Column = column,
});
}
else if (positionData != null)
{
pattern.Add(new Note
{
StartTime = HitObject.StartTime,
Samples = HitObject.Samples,
Column = column
});
}
return pattern;
}
} }
} }
} }

View File

@ -0,0 +1,106 @@
// 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.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Rulesets.Mania.MathUtils;
using osu.Game.Rulesets.Objects;
using OpenTK;
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
/// <summary>
/// A pattern generator for legacy hit objects.
/// </summary>
internal abstract class PatternGenerator : Patterns.PatternGenerator
{
/// <summary>
/// The column index at which to start generating random notes.
/// </summary>
protected readonly int RandomStart;
/// <summary>
/// The random number generator to use.
/// </summary>
protected readonly FastRandom Random;
protected PatternGenerator(FastRandom random, HitObject hitObject, Beatmap beatmap, Pattern previousPattern)
: base(hitObject, beatmap, previousPattern)
{
Random = random;
RandomStart = AvailableColumns == 8 ? 1 : 0;
}
/// <summary>
/// Converts an x-position into a column.
/// </summary>
/// <param name="position">The x-position.</param>
/// <param name="allowSpecial">Whether to treat as 7K + 1.</param>
/// <returns>The column.</returns>
protected int GetColumn(float position, bool allowSpecial = false)
{
if (allowSpecial && AvailableColumns == 8)
{
const float local_x_divisor = 512f / 7;
return MathHelper.Clamp((int)Math.Floor(position / local_x_divisor), 0, 6) + 1;
}
float localXDivisor = 512f / AvailableColumns;
return MathHelper.Clamp((int)Math.Floor(position / localXDivisor), 0, AvailableColumns - 1);
}
/// <summary>
/// Generates a count of notes to be generated from probabilities.
/// </summary>
/// <param name="p2">Probability for 2 notes to be generated.</param>
/// <param name="p3">Probability for 3 notes to be generated.</param>
/// <param name="p4">Probability for 4 notes to be generated.</param>
/// <param name="p5">Probability for 5 notes to be generated.</param>
/// <param name="p6">Probability for 6 notes to be generated.</param>
/// <returns>The amount of notes to be generated.</returns>
protected int GetRandomNoteCount(double p2, double p3, double p4 = 0, double p5 = 0, double p6 = 0)
{
double val = Random.NextDouble();
if (val >= 1 - p6)
return 6;
if (val >= 1 - p5)
return 5;
if (val >= 1 - p4)
return 4;
if (val >= 1 - p3)
return 3;
return val >= 1 - p2 ? 2 : 1;
}
private double? conversionDifficulty;
/// <summary>
/// A difficulty factor used for various conversion methods from osu!stable.
/// </summary>
protected double ConversionDifficulty
{
get
{
if (conversionDifficulty != null)
return conversionDifficulty.Value;
HitObject lastObject = Beatmap.HitObjects.LastOrDefault();
HitObject firstObject = Beatmap.HitObjects.FirstOrDefault();
double drainTime = (lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0);
drainTime -= Beatmap.EventInfo.TotalBreakTime;
if (drainTime == 0)
drainTime = 10000;
BeatmapDifficulty difficulty = Beatmap.BeatmapInfo.Difficulty;
conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + Beatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15;
conversionDifficulty = Math.Min(conversionDifficulty.Value, 12);
return conversionDifficulty.Value;
}
}
}
}

View File

@ -0,0 +1,65 @@
// 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.Beatmaps.Patterns.Legacy
{
/// <summary>
/// The type of pattern to generate. Used for legacy patterns.
/// </summary>
[Flags]
internal enum PatternType
{
None = 0,
/// <summary>
/// Keep the same as last row.
/// </summary>
ForceStack = 1,
/// <summary>
/// Keep different from last row.
/// </summary>
ForceNotStack = 2,
/// <summary>
/// Keep as single note at its original position.
/// </summary>
KeepSingle = 4,
/// <summary>
/// Use a lower random value.
/// </summary>
LowProbability = 8,
/// <summary>
/// Reserved.
/// </summary>
Alternate = 16,
/// <summary>
/// Ignore the repeat count.
/// </summary>
ForceSigSlider = 32,
/// <summary>
/// Convert slider to circle.
/// </summary>
ForceNotSlider = 64,
/// <summary>
/// Notes gathered together.
/// </summary>
Gathered = 128,
Mirror = 256,
/// <summary>
/// Change 0 -> 6.
/// </summary>
Reverse = 512,
/// <summary>
/// 1 -> 5 -> 1 -> 5 like reverse.
/// </summary>
Cycle = 1024,
/// <summary>
/// Next note will be at column + 1.
/// </summary>
Stair = 2048,
/// <summary>
/// Next note will be at column - 1.
/// </summary>
ReverseStair = 4096
}
}

View File

@ -0,0 +1,61 @@
// 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.Extensions.IEnumerableExtensions;
using osu.Game.Rulesets.Mania.Objects;
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
{
/// <summary>
/// Creates a pattern containing hit objects.
/// </summary>
internal class Pattern
{
private readonly List<ManiaHitObject> hitObjects = new List<ManiaHitObject>();
/// <summary>
/// All the hit objects contained in this pattern.
/// </summary>
public IEnumerable<ManiaHitObject> HitObjects => hitObjects;
/// <summary>
/// Whether this pattern already contains a hit object in a code.
/// </summary>
/// <param name="column">The column index.</param>
/// <returns>Whether this pattern already contains a hit object in <paramref name="column"/></returns>
public bool IsFilled(int column) => hitObjects.Exists(h => h.Column == column);
/// <summary>
/// Amount of columns taken up by hit objects in this pattern.
/// </summary>
public int ColumnsFilled => HitObjects.GroupBy(h => h.Column).Count();
/// <summary>
/// Adds a hit object to this pattern.
/// </summary>
/// <param name="hitObject">The hit object to add.</param>
public void Add(ManiaHitObject hitObject) => hitObjects.Add(hitObject);
/// <summary>
/// Copies hit object from another pattern to this one.
/// </summary>
/// <param name="other">The other pattern.</param>
public void Add(Pattern other)
{
other.HitObjects.ForEach(Add);
}
/// <summary>
/// Clears this pattern, removing all hit objects.
/// </summary>
public void Clear() => hitObjects.Clear();
/// <summary>
/// Removes a hit object from this pattern.
/// </summary>
/// <param name="hitObject">The hit object to remove.</param>
public bool Remove(ManiaHitObject hitObject) => hitObjects.Remove(hitObject);
}
}

View File

@ -0,0 +1,50 @@
// 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.Game.Beatmaps;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
{
/// <summary>
/// Generator to create a pattern <see cref="Pattern"/> from a hit object.
/// </summary>
internal abstract class PatternGenerator
{
/// <summary>
/// The number of columns available to create the pattern.
/// </summary>
protected readonly int AvailableColumns;
/// <summary>
/// The last pattern.
/// </summary>
protected readonly Pattern PreviousPattern;
/// <summary>
/// The hit object to create the pattern for.
/// </summary>
protected readonly HitObject HitObject;
/// <summary>
/// The beatmap which <see cref="HitObject"/> is a part of.
/// </summary>
protected readonly Beatmap Beatmap;
protected PatternGenerator(HitObject hitObject, Beatmap beatmap, Pattern previousPattern)
{
PreviousPattern = previousPattern;
HitObject = hitObject;
Beatmap = beatmap;
AvailableColumns = (int)Math.Round(beatmap.BeatmapInfo.Difficulty.CircleSize);
}
/// <summary>
/// Generates the pattern for <see cref="HitObject"/>, filled with hit objects.
/// </summary>
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
public abstract Pattern Generate();
}
}

View File

@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania
{ {
public class ManiaRuleset : Ruleset public class ManiaRuleset : Ruleset
{ {
public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap) => new ManiaHitRenderer(beatmap); public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new ManiaHitRenderer(beatmap, isForCurrentRuleset);
public override IEnumerable<Mod> GetModsFor(ModType type) public override IEnumerable<Mod> GetModsFor(ModType type)
{ {

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 / (uint.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

@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
tailPiece = new NotePiece tailPiece = new NotePiece
{ {
Anchor = Anchor.BottomCentre, Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre Origin = Anchor.TopCentre
} }
}); });
} }
@ -61,5 +61,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
protected override void UpdateState(ArmedState state) protected override void UpdateState(ArmedState state)
{ {
} }
protected override void Update()
{
if (Time.Current > HitObject.StartTime)
headPiece.Colour = Color4.Green;
if (Time.Current > HitObject.EndTime)
{
bodyPiece.Colour = Color4.Green;
tailPiece.Colour = Color4.Green;
}
}
} }
} }

View File

@ -3,8 +3,6 @@
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
@ -15,8 +13,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{ {
public new TObject HitObject; public new TObject HitObject;
private readonly Container glowContainer;
protected DrawableManiaHitObject(TObject hitObject) protected DrawableManiaHitObject(TObject hitObject)
: base(hitObject) : base(hitObject)
{ {
@ -24,21 +20,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
RelativePositionAxes = Axes.Y; RelativePositionAxes = Axes.Y;
Y = (float)HitObject.StartTime; Y = (float)HitObject.StartTime;
Add(glowContainer = new Container
{
RelativeSizeAxes = Axes.Both,
Masking = true,
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
});
} }
public override Color4 AccentColour public override Color4 AccentColour
@ -49,13 +30,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (base.AccentColour == value) if (base.AccentColour == value)
return; return;
base.AccentColour = value; base.AccentColour = value;
glowContainer.EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Glow,
Radius = 5,
Colour = value
};
} }
} }

View File

@ -19,7 +19,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces
public BodyPiece() public BodyPiece()
{ {
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
Masking = true;
Children = new[] Children = new[]
{ {

View File

@ -9,5 +9,11 @@ namespace osu.Game.Rulesets.Mania.Objects
public abstract class ManiaHitObject : HitObject, IHasColumn public abstract class ManiaHitObject : HitObject, IHasColumn
{ {
public int Column { get; set; } public int Column { get; set; }
/// <summary>
/// The number of other <see cref="ManiaHitObject"/> that start at
/// the same time as this hit object.
/// </summary>
public int Siblings { get; set; }
} }
} }

View File

@ -24,8 +24,8 @@ namespace osu.Game.Rulesets.Mania.UI
{ {
public int? Columns; public int? Columns;
public ManiaHitRenderer(WorkingBeatmap beatmap) public ManiaHitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(beatmap) : base(beatmap, isForCurrentRuleset)
{ {
} }
@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Mania.UI
return t; return t;
}); });
double lastObjectTime = (Objects.Last() as IHasEndTime)?.EndTime ?? Objects.Last().StartTime; double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;
// Perform some post processing of the timing changes // Perform some post processing of the timing changes
timingChanges = timingChanges timingChanges = timingChanges
@ -76,6 +76,10 @@ namespace osu.Game.Rulesets.Mania.UI
protected override DrawableHitObject<ManiaHitObject, ManiaJudgement> GetVisualRepresentation(ManiaHitObject h) protected override DrawableHitObject<ManiaHitObject, ManiaJudgement> GetVisualRepresentation(ManiaHitObject h)
{ {
var holdNote = h as HoldNote;
if (holdNote != null)
return new DrawableHoldNote(holdNote);
var note = h as Note; var note = h as Note;
if (note != null) if (note != null)
return new DrawableNote(note); return new DrawableNote(note);

View File

@ -47,7 +47,12 @@
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Beatmaps\Patterns\Legacy\PatternGenerator.cs" />
<Compile Include="Beatmaps\Patterns\PatternGenerator.cs" />
<Compile Include="Beatmaps\Patterns\Legacy\PatternType.cs" />
<Compile Include="Beatmaps\ManiaBeatmapConverter.cs" /> <Compile Include="Beatmaps\ManiaBeatmapConverter.cs" />
<Compile Include="Beatmaps\Patterns\Pattern.cs" />
<Compile Include="MathUtils\FastRandom.cs" />
<Compile Include="Judgements\HitWindows.cs" /> <Compile Include="Judgements\HitWindows.cs" />
<Compile Include="Judgements\ManiaJudgement.cs" /> <Compile Include="Judgements\ManiaJudgement.cs" />
<Compile Include="ManiaDifficultyCalculator.cs" /> <Compile Include="ManiaDifficultyCalculator.cs" />

View File

@ -1,15 +1,16 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 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.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Rulesets.Osu.UI; using osu.Game.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Allocation;
using osu.Game.Screens.Ranking;
namespace osu.Game.Rulesets.Osu.Objects.Drawables namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
@ -18,9 +19,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private readonly Spinner spinner; private readonly Spinner spinner;
private readonly SpinnerDisc disc; private readonly SpinnerDisc disc;
private readonly SpinnerTicks ticks;
private readonly Container mainContainer;
private readonly SpinnerBackground background; private readonly SpinnerBackground background;
private readonly Container circleContainer; private readonly Container circleContainer;
private readonly DrawableHitCircle circle; private readonly CirclePiece circle;
private readonly GlowPiece glow;
private readonly TextAwesome symbol;
private Color4 normalColour;
private Color4 completeColour;
public DrawableSpinner(Spinner s) : base(s) public DrawableSpinner(Spinner s) : base(s)
{ {
@ -29,57 +40,91 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Origin = Anchor.Centre; Origin = Anchor.Centre;
Position = s.Position; Position = s.Position;
//take up full playfield. RelativeSizeAxes = Axes.Both;
Size = new Vector2(OsuPlayfield.BASE_SIZE.X);
// we are slightly bigger than our parent, to clip the top and bottom of the circle
Height = 1.3f;
spinner = s; spinner = s;
Children = new Drawable[] Children = new Drawable[]
{ {
background = new SpinnerBackground
{
Alpha = 0,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
DiscColour = Color4.Black
},
disc = new SpinnerDisc
{
Alpha = 0,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
DiscColour = AccentColour
},
circleContainer = new Container circleContainer = new Container
{ {
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Children = new [] Children = new Drawable[]
{ {
circle = new DrawableHitCircle(s) glow = new GlowPiece(),
circle = new CirclePiece
{ {
Interactive = false,
Position = Vector2.Zero, Position = Vector2.Zero,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
} },
new RingPiece(),
symbol = new TextAwesome
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
UseFullGlyphHeight = true,
TextSize = 48,
Icon = FontAwesome.fa_asterisk,
Shadow = false,
},
} }
} },
mainContainer = new AspectContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
Children = new Drawable[]
{
background = new SpinnerBackground
{
Alpha = 0.6f,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
disc = new SpinnerDisc(spinner)
{
Scale = Vector2.Zero,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
circleContainer.CreateProxy(),
ticks = new SpinnerTicks
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
}
},
}; };
background.Scale = scaleToCircle;
disc.Scale = scaleToCircle;
} }
public float Progress => MathHelper.Clamp(disc.RotationAbsolute / 360 / spinner.SpinsRequired, 0, 1);
protected override void CheckJudgement(bool userTriggered) protected override void CheckJudgement(bool userTriggered)
{ {
if (Time.Current < HitObject.StartTime) return; if (Time.Current < HitObject.StartTime) return;
disc.ScaleTo(Interpolation.ValueAt(Math.Sqrt(Progress), scaleToCircle, Vector2.One, 0, 1), 100); if (Progress >= 1 && !disc.Complete)
{
if (Progress >= 1)
disc.Complete = true; disc.Complete = true;
const float duration = 200;
disc.FadeAccent(completeColour, duration);
background.FadeAccent(completeColour, duration);
background.FadeOut(duration);
circle.FadeColour(completeColour, duration);
glow.FadeColour(completeColour, duration);
}
if (!userTriggered && Time.Current >= spinner.EndTime) if (!userTriggered && Time.Current >= spinner.EndTime)
{ {
if (Progress >= 1) if (Progress >= 1)
@ -106,26 +151,48 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
} }
} }
private Vector2 scaleToCircle => circle.Scale * circle.DrawWidth / DrawWidth * 0.95f; [BackgroundDependencyLoader]
private void load(OsuColour colours)
{
normalColour = colours.SpinnerBase;
public float Progress => MathHelper.Clamp(disc.RotationAbsolute / 360 / spinner.SpinsRequired, 0, 1); background.AccentColour = normalColour;
completeColour = colours.YellowLight.Opacity(0.6f);
disc.AccentColour = colours.SpinnerFill;
circle.Colour = colours.BlueDark;
glow.Colour = colours.BlueDark;
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
circle.Rotation = disc.Rotation;
ticks.Rotation = disc.Rotation;
float relativeCircleScale = spinner.Scale * circle.DrawHeight / mainContainer.DrawHeight;
disc.ScaleTo(relativeCircleScale + (1 - relativeCircleScale) * Progress, 200, EasingTypes.OutQuint);
symbol.RotateTo(disc.Rotation / 2, 500, EasingTypes.OutQuint);
}
protected override void UpdatePreemptState() protected override void UpdatePreemptState()
{ {
base.UpdatePreemptState(); base.UpdatePreemptState();
circleContainer.ScaleTo(1, 400, EasingTypes.OutElastic); circleContainer.ScaleTo(spinner.Scale * 0.3f);
circleContainer.ScaleTo(spinner.Scale, TIME_PREEMPT / 1.4f, EasingTypes.OutQuint);
background.Delay(TIME_PREEMPT - 500); disc.RotateTo(-720);
symbol.RotateTo(-720);
background.ScaleTo(scaleToCircle * 1.2f, 400, EasingTypes.OutQuint); mainContainer.ScaleTo(0);
background.FadeIn(200); mainContainer.ScaleTo(spinner.Scale * circle.DrawHeight / DrawHeight * 1.4f, TIME_PREEMPT - 150, EasingTypes.OutQuint);
background.Delay(400); mainContainer.Delay(TIME_PREEMPT - 150);
background.ScaleTo(1, 250, EasingTypes.OutQuint); mainContainer.ScaleTo(1, 500, EasingTypes.OutQuint);
disc.Delay(TIME_PREEMPT - 50);
disc.FadeIn(200);
} }
protected override void UpdateCurrentState(ArmedState state) protected override void UpdateCurrentState(ArmedState state)

View File

@ -16,7 +16,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{ {
private readonly Sprite disc; private readonly Sprite disc;
public Func<bool> Hit; public Func<bool> Hit;
public CirclePiece() public CirclePiece()

View File

@ -1,10 +1,55 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 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 OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{ {
public class SpinnerBackground : SpinnerDisc public class SpinnerBackground : CircularContainer, IHasAccentColour
{ {
public override bool HandleInput => false; public override bool HandleInput => false;
protected Box Disc;
public Color4 AccentColour
{
get
{
return Disc.Colour;
}
set
{
Disc.Colour = value;
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Glow,
Radius = 14,
Colour = value.Opacity(0.3f),
};
}
}
public SpinnerBackground()
{
RelativeSizeAxes = Axes.Both;
Masking = true;
Children = new Drawable[]
{
Disc = new Box
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Alpha = 1,
},
};
}
} }
} }

View File

@ -2,13 +2,8 @@
// 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.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms; using osu.Framework.Graphics.Transforms;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Game.Graphics; using osu.Game.Graphics;
@ -17,104 +12,31 @@ using OpenTK.Graphics;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{ {
public class SpinnerDisc : CircularContainer public class SpinnerDisc : CircularContainer, IHasAccentColour
{ {
protected Sprite Disc; private readonly Spinner spinner;
public SRGBColour DiscColour public Color4 AccentColour
{ {
get { return Disc.Colour; } get { return background.AccentColour; }
set { Disc.Colour = value; } set { background.AccentColour = value; }
} }
private Color4 completeColour; private readonly SpinnerBackground background;
[BackgroundDependencyLoader] private const float idle_alpha = 0.2f;
private void load(OsuColour colours) private const float tracking_alpha = 0.4f;
public SpinnerDisc(Spinner s)
{ {
completeColour = colours.YellowLight.Opacity(0.8f); spinner = s;
Masking = true;
}
private class SpinnerBorder : Container
{
public SpinnerBorder()
{
Origin = Anchor.Centre;
Anchor = Anchor.Centre;
RelativeSizeAxes = Axes.Both;
layout();
}
private int lastLayoutDotCount;
private void layout()
{
int count = (int)(MathHelper.Pi * ScreenSpaceDrawQuad.Width / 9);
if (count == lastLayoutDotCount) return;
lastLayoutDotCount = count;
while (Children.Count() < count)
{
Add(new CircularContainer
{
Colour = Color4.White,
RelativePositionAxes = Axes.Both,
Masking = true,
Origin = Anchor.Centre,
Size = new Vector2(1 / ScreenSpaceDrawQuad.Width * 2000),
Children = new[]
{
new Box
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
}
}
});
}
var size = new Vector2(1 / ScreenSpaceDrawQuad.Width * 2000);
int i = 0;
foreach (var d in Children)
{
d.Size = size;
d.Position = new Vector2(
0.5f + (float)Math.Sin((float)i / count * 2 * MathHelper.Pi) / 2,
0.5f + (float)Math.Cos((float)i / count * 2 * MathHelper.Pi) / 2
);
i++;
}
}
protected override void Update()
{
base.Update();
layout();
}
}
public SpinnerDisc()
{
AlwaysReceiveInput = true; AlwaysReceiveInput = true;
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
Children = new Drawable[] Children = new Drawable[]
{ {
Disc = new Box background = new SpinnerBackground { Alpha = idle_alpha },
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Alpha = 0.2f,
},
new SpinnerBorder()
}; };
} }
@ -125,10 +47,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
set set
{ {
if (value == tracking) return; if (value == tracking) return;
tracking = value; tracking = value;
Disc.FadeTo(tracking ? 0.5f : 0.2f, 100); background.FadeTo(tracking ? tracking_alpha : idle_alpha, 100);
} }
} }
@ -139,31 +60,28 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
set set
{ {
if (value == complete) return; if (value == complete) return;
complete = value; complete = value;
Disc.FadeColour(completeColour, 200);
updateCompleteTick(); updateCompleteTick();
} }
} }
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{ {
Tracking = true; Tracking |= state.Mouse.HasMainButtonPressed;
return base.OnMouseDown(state, args); return base.OnMouseDown(state, args);
} }
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{ {
Tracking = false; Tracking &= state.Mouse.HasMainButtonPressed;
return base.OnMouseUp(state, args); return base.OnMouseUp(state, args);
} }
protected override bool OnMouseMove(InputState state) protected override bool OnMouseMove(InputState state)
{ {
Tracking |= state.Mouse.HasMainButtonPressed; Tracking |= state.Mouse.HasMainButtonPressed;
mousePosition = state.Mouse.Position; mousePosition = Parent.ToLocalSpace(state.Mouse.NativeState.Position);
return base.OnMouseMove(state); return base.OnMouseMove(state);
} }
@ -177,13 +95,24 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
private bool updateCompleteTick() => completeTick != (completeTick = (int)(RotationAbsolute / 360)); private bool updateCompleteTick() => completeTick != (completeTick = (int)(RotationAbsolute / 360));
private bool rotationTransferred;
protected override void Update() protected override void Update()
{ {
base.Update(); base.Update();
var thisAngle = -(float)MathHelper.RadiansToDegrees(Math.Atan2(mousePosition.X - DrawSize.X / 2, mousePosition.Y - DrawSize.Y / 2)); var thisAngle = -(float)MathHelper.RadiansToDegrees(Math.Atan2(mousePosition.X - DrawSize.X / 2, mousePosition.Y - DrawSize.Y / 2));
if (tracking)
bool validAndTracking = tracking && spinner.StartTime <= Time.Current && spinner.EndTime > Time.Current;
if (validAndTracking)
{ {
if (!rotationTransferred)
{
currentRotation = Rotation * 2;
rotationTransferred = true;
}
if (thisAngle - lastAngle > 180) if (thisAngle - lastAngle > 180)
lastAngle += 360; lastAngle += 360;
else if (lastAngle - thisAngle > 180) else if (lastAngle - thisAngle > 180)
@ -192,17 +121,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
currentRotation += thisAngle - lastAngle; currentRotation += thisAngle - lastAngle;
RotationAbsolute += Math.Abs(thisAngle - lastAngle); RotationAbsolute += Math.Abs(thisAngle - lastAngle);
} }
lastAngle = thisAngle; lastAngle = thisAngle;
if (Complete && updateCompleteTick()) if (Complete && updateCompleteTick())
{ {
Disc.Flush(flushType: typeof(TransformAlpha)); background.Flush(flushType: typeof(TransformAlpha));
Disc.FadeTo(0.75f, 30, EasingTypes.OutExpo); background.FadeTo(tracking_alpha + 0.4f, 60, EasingTypes.OutExpo);
Disc.Delay(30); background.Delay(60);
Disc.FadeTo(0.5f, 250, EasingTypes.OutQuint); background.FadeTo(tracking_alpha, 250, EasingTypes.OutQuint);
} }
RotateTo(currentRotation, 100, EasingTypes.OutExpo); RotateTo(currentRotation / 2, validAndTracking ? 500 : 1500, EasingTypes.OutExpo);
} }
} }
} }

View File

@ -0,0 +1,71 @@
// 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.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
public class SpinnerTicks : Container
{
private Color4 glowColour;
public SpinnerTicks()
{
Origin = Anchor.Centre;
Anchor = Anchor.Centre;
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
glowColour = colours.BlueDarker.Opacity(0.4f);
layout();
}
private void layout()
{
const int count = 18;
for (int i = 0; i < count; i++)
{
Add(new Container
{
Colour = Color4.Black,
Alpha = 0.4f,
EdgeEffect = new EdgeEffect
{
Type = EdgeEffectType.Glow,
Radius = 20,
Colour = glowColour,
},
RelativePositionAxes = Axes.Both,
Masking = true,
CornerRadius = 5,
Size = new Vector2(60, 10),
Origin = Anchor.Centre,
Position = new Vector2(
0.5f + (float)Math.Sin((float)i / count * 2 * MathHelper.Pi) / 2 * 0.86f,
0.5f + (float)Math.Cos((float)i / count * 2 * MathHelper.Pi) / 2 * 0.86f
),
Rotation = -(float)i / count * 360 + 90,
Children = new[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
}
}
});
}
}
}
}

View File

@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu
{ {
public class OsuRuleset : Ruleset public class OsuRuleset : Ruleset
{ {
public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap) => new OsuHitRenderer(beatmap); public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new OsuHitRenderer(beatmap, isForCurrentRuleset);
public override IEnumerable<BeatmapStatistic> GetBeatmapStatistics(WorkingBeatmap beatmap) => new[] public override IEnumerable<BeatmapStatistic> GetBeatmapStatistics(WorkingBeatmap beatmap) => new[]
{ {

View File

@ -91,25 +91,25 @@ namespace osu.Game.Rulesets.Osu.Replays
// Make the cursor stay at a hitObject as long as possible (mainly for autopilot). // Make the cursor stay at a hitObject as long as possible (mainly for autopilot).
if (h.StartTime - h.HitWindowFor(OsuScoreResult.Miss) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50) if (h.StartTime - h.HitWindowFor(OsuScoreResult.Miss) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50)
{ {
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.EndPosition.X, prev.EndPosition.Y, ReplayButtonState.None)); if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None));
if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Miss), h.Position.X, h.Position.Y, ReplayButtonState.None)); if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Miss), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None));
} }
else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50) else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50)
{ {
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.EndPosition.X, prev.EndPosition.Y, ReplayButtonState.None)); if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None));
if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50), h.Position.X, h.Position.Y, ReplayButtonState.None)); if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None));
} }
else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100) > endTime + h.HitWindowFor(OsuScoreResult.Hit100) + 50) else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100) > endTime + h.HitWindowFor(OsuScoreResult.Hit100) + 50)
{ {
if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit100), prev.EndPosition.X, prev.EndPosition.Y, ReplayButtonState.None)); if (!(prev is Spinner) && h.StartTime - endTime < 1000) AddFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit100), prev.StackedEndPosition.X, prev.StackedEndPosition.Y, ReplayButtonState.None));
if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.Position.X, h.Position.Y, ReplayButtonState.None)); if (!(h is Spinner)) AddFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.StackedPosition.X, h.StackedPosition.Y, ReplayButtonState.None));
} }
} }
private void addHitObjectReplay(OsuHitObject h) private void addHitObjectReplay(OsuHitObject h)
{ {
// Default values for circles/sliders // Default values for circles/sliders
Vector2 startPosition = h.Position; Vector2 startPosition = h.StackedPosition;
EasingTypes easing = preferredEasing; EasingTypes easing = preferredEasing;
float spinnerDirection = -1; float spinnerDirection = -1;
@ -238,7 +238,7 @@ namespace osu.Game.Rulesets.Osu.Replays
// TODO: Why do we delay 1 ms if the object is a spinner? There already is KEY_UP_DELAY from hEndTime. // TODO: Why do we delay 1 ms if the object is a spinner? There already is KEY_UP_DELAY from hEndTime.
double hEndTime = ((h as IHasEndTime)?.EndTime ?? h.StartTime) + KEY_UP_DELAY; double hEndTime = ((h as IHasEndTime)?.EndTime ?? h.StartTime) + KEY_UP_DELAY;
int endDelay = h is Spinner ? 1 : 0; int endDelay = h is Spinner ? 1 : 0;
ReplayFrame endFrame = new ReplayFrame(hEndTime + endDelay, h.EndPosition.X, h.EndPosition.Y, ReplayButtonState.None); ReplayFrame endFrame = new ReplayFrame(hEndTime + endDelay, h.StackedEndPosition.X, h.StackedEndPosition.Y, ReplayButtonState.None);
// Decrement because we want the previous frame, not the next one // Decrement because we want the previous frame, not the next one
int index = FindInsertionIndex(startFrame) - 1; int index = FindInsertionIndex(startFrame) - 1;

View File

@ -18,8 +18,8 @@ namespace osu.Game.Rulesets.Osu.UI
{ {
public class OsuHitRenderer : HitRenderer<OsuHitObject, OsuJudgement> public class OsuHitRenderer : HitRenderer<OsuHitObject, OsuJudgement>
{ {
public OsuHitRenderer(WorkingBeatmap beatmap) public OsuHitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(beatmap) : base(beatmap, isForCurrentRuleset)
{ {
} }

View File

@ -64,6 +64,7 @@
<Compile Include="Objects\Drawables\Pieces\RingPiece.cs" /> <Compile Include="Objects\Drawables\Pieces\RingPiece.cs" />
<Compile Include="Objects\Drawables\Pieces\SliderBouncer.cs" /> <Compile Include="Objects\Drawables\Pieces\SliderBouncer.cs" />
<Compile Include="Objects\Drawables\Pieces\SpinnerDisc.cs" /> <Compile Include="Objects\Drawables\Pieces\SpinnerDisc.cs" />
<Compile Include="Objects\Drawables\Pieces\SpinnerTicks.cs" />
<Compile Include="Objects\Drawables\Pieces\TrianglesPiece.cs" /> <Compile Include="Objects\Drawables\Pieces\TrianglesPiece.cs" />
<Compile Include="Objects\Drawables\Pieces\SliderBall.cs" /> <Compile Include="Objects\Drawables\Pieces\SliderBall.cs" />
<Compile Include="Objects\Drawables\Pieces\SliderBody.cs" /> <Compile Include="Objects\Drawables\Pieces\SliderBody.cs" />
@ -103,9 +104,7 @@
<None Include="packages.config" /> <None Include="packages.config" />
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup />
<ItemGroup> <ItemGroup />
<Folder Include="Replays\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -41,13 +41,13 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(HitObject) }; protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(HitObject) };
protected override Beatmap<TaikoHitObject> ConvertBeatmap(Beatmap original) protected override Beatmap<TaikoHitObject> ConvertBeatmap(Beatmap original, bool isForCurrentRuleset)
{ {
// Rewrite the beatmap info to add the slider velocity multiplier // Rewrite the beatmap info to add the slider velocity multiplier
BeatmapInfo info = original.BeatmapInfo.DeepClone(); BeatmapInfo info = original.BeatmapInfo.DeepClone();
info.Difficulty.SliderMultiplier *= legacy_velocity_multiplier; info.Difficulty.SliderMultiplier *= legacy_velocity_multiplier;
Beatmap<TaikoHitObject> converted = base.ConvertBeatmap(original); Beatmap<TaikoHitObject> converted = base.ConvertBeatmap(original, isForCurrentRuleset);
// Post processing step to transform hit objects with the same start time into strong hits // Post processing step to transform hit objects with the same start time into strong hits
converted.HitObjects = converted.HitObjects.GroupBy(t => t.StartTime).Select(x => converted.HitObjects = converted.HitObjects.GroupBy(t => t.StartTime).Select(x =>

View File

@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko
{ {
public class TaikoRuleset : Ruleset public class TaikoRuleset : Ruleset
{ {
public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap) => new TaikoHitRenderer(beatmap); public override HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new TaikoHitRenderer(beatmap, isForCurrentRuleset);
public override IEnumerable<Mod> GetModsFor(ModType type) public override IEnumerable<Mod> GetModsFor(ModType type)
{ {

View File

@ -22,8 +22,8 @@ namespace osu.Game.Rulesets.Taiko.UI
{ {
public class TaikoHitRenderer : HitRenderer<TaikoHitObject, TaikoJudgement> public class TaikoHitRenderer : HitRenderer<TaikoHitObject, TaikoJudgement>
{ {
public TaikoHitRenderer(WorkingBeatmap beatmap) public TaikoHitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(beatmap) : base(beatmap, isForCurrentRuleset)
{ {
} }

View File

@ -2,6 +2,7 @@
// 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 OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Beatmaps.Events;
using osu.Game.Beatmaps.Timing; using osu.Game.Beatmaps.Timing;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
@ -17,6 +18,7 @@ namespace osu.Game.Beatmaps
{ {
public BeatmapInfo BeatmapInfo; public BeatmapInfo BeatmapInfo;
public TimingInfo TimingInfo = new TimingInfo(); public TimingInfo TimingInfo = new TimingInfo();
public EventInfo EventInfo = new EventInfo();
public readonly List<Color4> ComboColors = new List<Color4> public readonly List<Color4> ComboColors = new List<Color4>
{ {
new Color4(17, 136, 170, 255), new Color4(17, 136, 170, 255),
@ -40,6 +42,7 @@ namespace osu.Game.Beatmaps
{ {
BeatmapInfo = original?.BeatmapInfo ?? BeatmapInfo; BeatmapInfo = original?.BeatmapInfo ?? BeatmapInfo;
TimingInfo = original?.TimingInfo ?? TimingInfo; TimingInfo = original?.TimingInfo ?? TimingInfo;
EventInfo = original?.EventInfo ?? EventInfo;
ComboColors = original?.ComboColors ?? ComboColors; ComboColors = original?.ComboColors ?? ComboColors;
} }
} }

View File

@ -34,7 +34,7 @@ namespace osu.Game.Beatmaps
protected DifficultyCalculator(Beatmap beatmap) protected DifficultyCalculator(Beatmap beatmap)
{ {
Objects = CreateBeatmapConverter().Convert(beatmap).HitObjects; Objects = CreateBeatmapConverter().Convert(beatmap, true).HitObjects;
foreach (var h in Objects) foreach (var h in Objects)
h.ApplyDefaults(beatmap.TimingInfo, beatmap.BeatmapInfo.Difficulty); h.ApplyDefaults(beatmap.TimingInfo, beatmap.BeatmapInfo.Difficulty);

View File

@ -3,14 +3,11 @@
namespace osu.Game.Beatmaps.Events namespace osu.Game.Beatmaps.Events
{ {
public enum EventType public class BackgroundEvent : Event
{ {
Background = 0, /// <summary>
Video = 1, /// The file name.
Break = 2, /// </summary>
Colour = 3, public string Filename;
Sprite = 4,
Sample = 5,
Animation = 6
} }
} }

View File

@ -0,0 +1,28 @@
// 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.Beatmaps.Events
{
public class BreakEvent : Event
{
/// <summary>
/// The minimum duration required for a break to have any effect.
/// </summary>
private const double min_break_duration = 650;
/// <summary>
/// The break end time.
/// </summary>
public double EndTime;
/// <summary>
/// The duration of the break.
/// </summary>
public double Duration => EndTime - StartTime;
/// <summary>
/// Whether the break has any effect. Breaks that are too short are culled before they reach the EventInfo.
/// </summary>
public bool HasEffect => Duration >= min_break_duration;
}
}

View File

@ -0,0 +1,13 @@
// 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.Beatmaps.Events
{
public abstract class Event
{
/// <summary>
/// The event start time.
/// </summary>
public double StartTime;
}
}

View File

@ -0,0 +1,33 @@
// 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;
namespace osu.Game.Beatmaps.Events
{
public class EventInfo
{
/// <summary>
/// All the background events.
/// </summary>
public readonly List<BackgroundEvent> Backgrounds = new List<BackgroundEvent>();
/// <summary>
/// All the break events.
/// </summary>
public readonly List<BreakEvent> Breaks = new List<BreakEvent>();
/// <summary>
/// Total duration of all breaks.
/// </summary>
public double TotalBreakTime => Breaks.Sum(b => b.Duration);
/// <summary>
/// Retrieves the active background at a time.
/// </summary>
/// <param name="time">The time to retrieve the background at.</param>
/// <returns>The background.</returns>
public BackgroundEvent BackgroundAt(double time) => Backgrounds.FirstOrDefault(b => b.StartTime <= time);
}
}

View File

@ -204,23 +204,42 @@ namespace osu.Game.Beatmaps.Formats
private void handleEvents(Beatmap beatmap, string val) private void handleEvents(Beatmap beatmap, string val)
{ {
if (val.StartsWith(@"//"))
return;
if (val.StartsWith(@" "))
return; // TODO
string[] split = val.Split(','); string[] split = val.Split(',');
EventType type; EventType type;
int intType; if (!Enum.TryParse(split[0], out type))
if (!int.TryParse(split[0], out intType)) throw new InvalidDataException($@"Unknown event type {split[0]}");
// Todo: Implement the rest
switch (type)
{ {
if (!Enum.TryParse(split[0], out type)) case EventType.Video:
throw new InvalidDataException($@"Unknown event type {split[0]}"); case EventType.Background:
string filename = split[2].Trim('"');
beatmap.EventInfo.Backgrounds.Add(new BackgroundEvent
{
StartTime = double.Parse(split[1], NumberFormatInfo.InvariantInfo),
Filename = filename
});
if (type == EventType.Background)
beatmap.BeatmapInfo.Metadata.BackgroundFile = filename;
break;
case EventType.Break:
var breakEvent = new BreakEvent
{
StartTime = double.Parse(split[1], NumberFormatInfo.InvariantInfo),
EndTime = double.Parse(split[2], NumberFormatInfo.InvariantInfo)
};
if (!breakEvent.HasEffect)
return;
beatmap.EventInfo.Breaks.Add(breakEvent);
break;
} }
else
type = (EventType)intType;
// TODO: Parse and store the rest of the event
if (type == EventType.Background)
beatmap.BeatmapInfo.Metadata.BackgroundFile = split[2].Trim('"');
} }
private void handleTimingPoints(Beatmap beatmap, string val) private void handleTimingPoints(Beatmap beatmap, string val)
@ -330,6 +349,9 @@ namespace osu.Game.Beatmaps.Formats
if (string.IsNullOrEmpty(line)) if (string.IsNullOrEmpty(line))
continue; continue;
if (line.StartsWith(" ") || line.StartsWith("_") || line.StartsWith("//"))
continue;
if (line.StartsWith(@"osu file format v")) if (line.StartsWith(@"osu file format v"))
{ {
beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17)); beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17));
@ -390,5 +412,16 @@ namespace osu.Game.Beatmaps.Formats
Soft = 2, Soft = 2,
Drum = 3 Drum = 3
} }
internal enum EventType
{
Background = 0,
Video = 1,
Break = 2,
Colour = 3,
Sprite = 4,
Sample = 5,
Animation = 6
}
} }
} }

View File

@ -26,6 +26,7 @@ namespace osu.Game.Database
public string[] SearchableTerms => new[] public string[] SearchableTerms => new[]
{ {
Author,
Artist, Artist,
ArtistUnicode, ArtistUnicode,
Title, Title,

View File

@ -87,5 +87,8 @@ namespace osu.Game.Graphics
public readonly Color4 RedDarker = FromHex(@"870000"); public readonly Color4 RedDarker = FromHex(@"870000");
public readonly Color4 ChatBlue = FromHex(@"17292e"); public readonly Color4 ChatBlue = FromHex(@"17292e");
public readonly Color4 SpinnerBase = FromHex(@"002c3c");
public readonly Color4 SpinnerFill = FromHex(@"005b7c");
} }
} }

View File

@ -146,7 +146,7 @@ namespace osu.Game
{ {
base.LoadComplete(); base.LoadComplete();
AddInternal(ratioContainer = new RatioAdjust base.Content.Add(ratioContainer = new RatioAdjust
{ {
Children = new Drawable[] Children = new Drawable[]
{ {

View File

@ -206,18 +206,12 @@ namespace osu.Game.Overlays
private long? lastMessageId; private long? lastMessageId;
private List<Channel> careChannels; private readonly List<Channel> careChannels = new List<Channel>();
private readonly List<DrawableChannel> loadedChannels = new List<DrawableChannel>(); private readonly List<DrawableChannel> loadedChannels = new List<DrawableChannel>();
private void initializeChannels() private void initializeChannels()
{ {
currentChannelContainer.Clear();
loadedChannels.Clear();
careChannels = new List<Channel>();
SpriteText loading; SpriteText loading;
Add(loading = new OsuSpriteText Add(loading = new OsuSpriteText
{ {
@ -232,8 +226,6 @@ namespace osu.Game.Overlays
ListChannelsRequest req = new ListChannelsRequest(); ListChannelsRequest req = new ListChannelsRequest();
req.Success += delegate (List<Channel> channels) req.Success += delegate (List<Channel> channels)
{ {
Debug.Assert(careChannels.Count == 0);
Scheduler.Add(delegate Scheduler.Add(delegate
{ {
loading.FadeOut(100); loading.FadeOut(100);

View File

@ -16,6 +16,7 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using System; using System;
using System.Linq; using System.Linq;
using osu.Game.Graphics;
namespace osu.Game.Overlays.Mods namespace osu.Game.Overlays.Mods
{ {
@ -23,7 +24,7 @@ namespace osu.Game.Overlays.Mods
/// <summary> /// <summary>
/// Represents a clickable button which can cycle through one of more mods. /// Represents a clickable button which can cycle through one of more mods.
/// </summary> /// </summary>
public class ModButton : ModButtonEmpty public class ModButton : ModButtonEmpty, IHasTooltip
{ {
private ModIcon foregroundIcon; private ModIcon foregroundIcon;
private readonly SpriteText text; private readonly SpriteText text;
@ -32,6 +33,8 @@ namespace osu.Game.Overlays.Mods
public Action<Mod> Action; // Passed the selected mod or null if none public Action<Mod> Action; // Passed the selected mod or null if none
public string TooltipText => (SelectedMod?.Description ?? Mods.FirstOrDefault()?.Description) ?? string.Empty;
private int _selectedIndex = -1; private int _selectedIndex = -1;
private int selectedIndex private int selectedIndex
{ {

View File

@ -7,10 +7,10 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Framework.Localisation; using osu.Framework.Localisation;
using osu.Framework.Graphics.Sprites;
using System.Collections.Generic;
namespace osu.Game.Overlays.Music namespace osu.Game.Overlays.Music
{ {
@ -19,9 +19,13 @@ namespace osu.Game.Overlays.Music
private const float fade_duration = 100; private const float fade_duration = 100;
private Color4 hoverColour; private Color4 hoverColour;
private Color4 artistColour;
private TextAwesome handle; private TextAwesome handle;
private OsuSpriteText title; private Paragraph text;
private IEnumerable<SpriteText> titleSprites;
private UnicodeBindableString titleBind;
private UnicodeBindableString artistBind;
public readonly BeatmapSetInfo BeatmapSetInfo; public readonly BeatmapSetInfo BeatmapSetInfo;
@ -37,7 +41,8 @@ namespace osu.Game.Overlays.Music
selected = value; selected = value;
Flush(true); Flush(true);
title.FadeColour(Selected ? hoverColour : Color4.White, fade_duration); foreach (SpriteText s in titleSprites)
s.FadeColour(Selected ? hoverColour : Color4.White, fade_duration);
} }
} }
@ -53,8 +58,10 @@ namespace osu.Game.Overlays.Music
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuColour colours, LocalisationEngine localisation) private void load(OsuColour colours, LocalisationEngine localisation)
{ {
BeatmapMetadata metadata = BeatmapSetInfo.Metadata; hoverColour = colours.Yellow;
artistColour = colours.Gray9;
var metadata = BeatmapSetInfo.Metadata;
FilterTerms = metadata.SearchableTerms; FilterTerms = metadata.SearchableTerms;
Children = new Drawable[] Children = new Drawable[]
@ -70,33 +77,40 @@ namespace osu.Game.Overlays.Music
Margin = new MarginPadding { Left = 5 }, Margin = new MarginPadding { Left = 5 },
Padding = new MarginPadding { Top = 2 }, Padding = new MarginPadding { Top = 2 },
}, },
new FillFlowContainer<OsuSpriteText> text = new Paragraph
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Left = 20 }, Padding = new MarginPadding { Left = 20 },
Spacing = new Vector2(5f, 0f), ContentIndent = 10f,
Children = new []
{
title = new OsuSpriteText
{
TextSize = 16,
Font = @"Exo2.0-Regular",
Current = localisation.GetUnicodePreference(metadata.TitleUnicode, metadata.Title),
},
new OsuSpriteText
{
TextSize = 14,
Font = @"Exo2.0-Bold",
Colour = colours.Gray9,
Padding = new MarginPadding { Top = 1 },
Current = localisation.GetUnicodePreference(metadata.ArtistUnicode, metadata.Artist),
}
}
}, },
}; };
hoverColour = colours.Yellow; titleBind = localisation.GetUnicodePreference(metadata.TitleUnicode, metadata.Title);
artistBind = localisation.GetUnicodePreference(metadata.ArtistUnicode, metadata.Artist);
artistBind.ValueChanged += newText => recreateText();
artistBind.TriggerChange();
}
private void recreateText()
{
text.Clear();
//space after the title to put a space between the title and artist
titleSprites = text.AddText(titleBind.Value + @" ", sprite =>
{
sprite.TextSize = 16;
sprite.Font = @"Exo2.0-Regular";
});
text.AddText(artistBind.Value, sprite =>
{
sprite.TextSize = 14;
sprite.Font = @"Exo2.0-Bold";
sprite.Colour = artistColour;
sprite.Padding = new MarginPadding { Top = 1 };
});
} }
protected override bool OnHover(Framework.Input.InputState state) protected override bool OnHover(Framework.Input.InputState state)

View File

@ -76,6 +76,7 @@ namespace osu.Game.Overlays
{ {
TextSize = 24, TextSize = 24,
Font = @"Exo2.0-Light", Font = @"Exo2.0-Light",
Padding = new MarginPadding { Left = 10, Right = 10 },
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.BottomCentre, Origin = Anchor.BottomCentre,
}, },
@ -116,7 +117,7 @@ namespace osu.Game.Overlays
private void load(FrameworkConfigManager frameworkConfig) private void load(FrameworkConfigManager frameworkConfig)
{ {
trackSetting(frameworkConfig.GetBindable<FrameSync>(FrameworkSetting.FrameSync), v => display(v, "Frame Limiter", v.GetDescription(), "Ctrl+F7")); trackSetting(frameworkConfig.GetBindable<FrameSync>(FrameworkSetting.FrameSync), v => display(v, "Frame Limiter", v.GetDescription(), "Ctrl+F7"));
trackSetting(frameworkConfig.GetBindable<string>(FrameworkSetting.AudioDevice), v => display(v, "Audio Device", v, v)); trackSetting(frameworkConfig.GetBindable<string>(FrameworkSetting.AudioDevice), v => display(v, "Audio Device", string.IsNullOrEmpty(v) ? "Default" : v, v));
trackSetting(frameworkConfig.GetBindable<bool>(FrameworkSetting.ShowLogOverlay), v => display(v, "Debug Logs", v ? "visible" : "hidden", "Ctrl+F10")); trackSetting(frameworkConfig.GetBindable<bool>(FrameworkSetting.ShowLogOverlay), v => display(v, "Debug Logs", v ? "visible" : "hidden", "Ctrl+F10"));
Action displayResolution = delegate { display(null, "Screen Resolution", frameworkConfig.Get<int>(FrameworkSetting.Width) + "x" + frameworkConfig.Get<int>(FrameworkSetting.Height)); }; Action displayResolution = delegate { display(null, "Screen Resolution", frameworkConfig.Get<int>(FrameworkSetting.Width) + "x" + frameworkConfig.Get<int>(FrameworkSetting.Height)); };

View File

@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Settings.Sections.Debug
new SettingsEnumDropdown<GCLatencyMode> new SettingsEnumDropdown<GCLatencyMode>
{ {
LabelText = "Active mode", LabelText = "Active mode",
Bindable = config.GetBindable<GCLatencyMode>(FrameworkDebugConfig.ActiveGCMode) Bindable = config.GetBindable<GCLatencyMode>(DebugSetting.ActiveGCMode)
}, },
new OsuButton new OsuButton
{ {

View File

@ -12,14 +12,19 @@ namespace osu.Game.Overlays.Settings.Sections.Debug
protected override string Header => "General"; protected override string Header => "General";
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(FrameworkDebugConfigManager config) private void load(FrameworkDebugConfigManager config, FrameworkConfigManager frameworkConfig)
{ {
Children = new Drawable[] Children = new Drawable[]
{ {
new SettingsCheckbox new SettingsCheckbox
{ {
LabelText = "Bypass caching", LabelText = "Bypass caching",
Bindable = config.GetBindable<bool>(FrameworkDebugConfig.BypassCaching) Bindable = config.GetBindable<bool>(DebugSetting.BypassCaching)
},
new SettingsCheckbox
{
LabelText = "Debug logs",
Bindable = frameworkConfig.GetBindable<bool>(FrameworkSetting.ShowLogOverlay)
} }
}; };
} }

View File

@ -26,19 +26,21 @@ namespace osu.Game.Rulesets.Beatmaps
/// Converts a Beatmap using this Beatmap Converter. /// Converts a Beatmap using this Beatmap Converter.
/// </summary> /// </summary>
/// <param name="original">The un-converted Beatmap.</param> /// <param name="original">The un-converted Beatmap.</param>
/// <param name="isForCurrentRuleset">Whether to assume the beatmap is for the current ruleset.</param>
/// <returns>The converted Beatmap.</returns> /// <returns>The converted Beatmap.</returns>
public Beatmap<T> Convert(Beatmap original) public Beatmap<T> Convert(Beatmap original, bool isForCurrentRuleset)
{ {
// We always operate on a clone of the original beatmap, to not modify it game-wide // We always operate on a clone of the original beatmap, to not modify it game-wide
return ConvertBeatmap(new Beatmap(original)); return ConvertBeatmap(new Beatmap(original), isForCurrentRuleset);
} }
/// <summary> /// <summary>
/// Performs the conversion of a Beatmap using this Beatmap Converter. /// Performs the conversion of a Beatmap using this Beatmap Converter.
/// </summary> /// </summary>
/// <param name="original">The un-converted Beatmap.</param> /// <param name="original">The un-converted Beatmap.</param>
/// <param name="isForCurrentRuleset">Whether to assume the beatmap is for the current ruleset.</param>
/// <returns>The converted Beatmap.</returns> /// <returns>The converted Beatmap.</returns>
protected virtual Beatmap<T> ConvertBeatmap(Beatmap original) protected virtual Beatmap<T> ConvertBeatmap(Beatmap original, bool isForCurrentRuleset)
{ {
return new Beatmap<T> return new Beatmap<T>
{ {

View File

@ -43,5 +43,10 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
EndTime = endTime EndTime = endTime
}; };
} }
protected override HitObject CreateHold(Vector2 position, bool newCombo, double endTime)
{
return null;
}
} }
} }

View File

@ -8,6 +8,7 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Formats;
using osu.Game.Audio; using osu.Game.Audio;
using System.Linq;
namespace osu.Game.Rulesets.Objects.Legacy namespace osu.Game.Rulesets.Objects.Legacy
{ {
@ -26,7 +27,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
var soundType = (LegacySoundType)int.Parse(split[4]); var soundType = (LegacySoundType)int.Parse(split[4]);
var bankInfo = new SampleBankInfo(); var bankInfo = new SampleBankInfo();
HitObject result; HitObject result = null;
if ((type & ConvertHitObjectType.Circle) > 0) if ((type & ConvertHitObjectType.Circle) > 0)
{ {
@ -140,17 +141,20 @@ namespace osu.Game.Rulesets.Objects.Legacy
{ {
// Note: Hold is generated by BMS converts // Note: Hold is generated by BMS converts
// Todo: Apparently end time is determined by samples?? double endTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture);
// Shouldn't need implementation until mania
result = new ConvertHold if (split.Length > 5 && !string.IsNullOrEmpty(split[5]))
{ {
Position = new Vector2(int.Parse(split[0]), int.Parse(split[1])), string[] ss = split[5].Split(':');
NewCombo = combo endTime = Convert.ToDouble(ss[0], CultureInfo.InvariantCulture);
}; readCustomSampleBanks(string.Join(":", ss.Skip(1)), bankInfo);
}
result = CreateHold(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo, endTime);
} }
else
throw new InvalidOperationException($@"Unknown hit object type {type}"); if (result == null)
throw new InvalidOperationException($@"Unknown hit object type {type}.");
result.StartTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture); result.StartTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture);
result.Samples = convertSoundType(soundType, bankInfo); result.Samples = convertSoundType(soundType, bankInfo);
@ -214,6 +218,14 @@ namespace osu.Game.Rulesets.Objects.Legacy
/// <returns>The hit object.</returns> /// <returns>The hit object.</returns>
protected abstract HitObject CreateSpinner(Vector2 position, double endTime); protected abstract HitObject CreateSpinner(Vector2 position, double endTime);
/// <summary>
/// Creates a legacy Hold-type hit object.
/// </summary>
/// <param name="position">The position of the hit object.</param>
/// <param name="newCombo">Whether the hit object creates a new combo.</param>
/// <param name="endTime">The hold end time.</param>
protected abstract HitObject CreateHold(Vector2 position, bool newCombo, double endTime);
private SampleInfoList convertSoundType(LegacySoundType type, SampleBankInfo bankInfo) private SampleInfoList convertSoundType(LegacySoundType type, SampleBankInfo bankInfo)
{ {
var soundTypes = new SampleInfoList var soundTypes = new SampleInfoList

View File

@ -1,22 +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 OpenTK;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Objects.Legacy
{
/// <summary>
/// Legacy Hold-type, used for parsing "specials" in beatmaps.
/// </summary>
internal sealed class ConvertHold : HitObject, IHasPosition, IHasCombo, IHasHold
{
public Vector2 Position { get; set; }
public float X => Position.X;
public float Y => Position.Y;
public bool NewCombo { get; set; }
}
}

View File

@ -44,5 +44,14 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
EndTime = endTime EndTime = endTime
}; };
} }
protected override HitObject CreateHold(Vector2 position, bool newCombo, double endTime)
{
return new ConvertHold
{
X = position.X,
EndTime = endTime
};
}
} }
} }

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.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Objects.Legacy.Mania
{
internal sealed class ConvertHold : HitObject, IHasXPosition, IHasEndTime
{
public float X { get; set; }
public double EndTime { get; set; }
public double Duration => EndTime - StartTime;
}
}

View File

@ -44,5 +44,10 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
EndTime = endTime EndTime = endTime
}; };
} }
protected override HitObject CreateHold(Vector2 position, bool newCombo, double endTime)
{
return null;
}
} }
} }

View File

@ -41,5 +41,10 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
EndTime = endTime EndTime = endTime
}; };
} }
protected override HitObject CreateHold(Vector2 position, bool newCombo, double endTime)
{
return null;
}
} }
} }

View File

@ -8,5 +8,9 @@ namespace osu.Game.Rulesets.Objects.Types
/// </summary> /// </summary>
public interface IHasHold public interface IHasHold
{ {
/// <summary>
/// The time at which the hold ends.
/// </summary>
double EndTime { get; }
} }
} }

View File

@ -18,12 +18,13 @@ namespace osu.Game.Rulesets
public abstract IEnumerable<Mod> GetModsFor(ModType type); public abstract IEnumerable<Mod> GetModsFor(ModType type);
/// <summary> /// <summary>
/// Attempt to create a HitRenderer for the provided beatmap. /// Attempt to create a hit renderer for a beatmap
/// </summary> /// </summary>
/// <param name="beatmap"></param> /// <param name="beatmap">The beatmap to create the hit renderer for.</param>
/// <param name="isForCurrentRuleset">Whether the hit renderer should assume the beatmap is for the current ruleset.</param>
/// <exception cref="BeatmapInvalidForRulesetException">Unable to successfully load the beatmap to be usable with this ruleset.</exception> /// <exception cref="BeatmapInvalidForRulesetException">Unable to successfully load the beatmap to be usable with this ruleset.</exception>
/// <returns></returns> /// <returns></returns>
public abstract HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap); public abstract HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap, bool isForCurrentRuleset);
public abstract DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap); public abstract DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap);

View File

@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.UI
/// </summary> /// </summary>
protected abstract bool AllObjectsJudged { get; } protected abstract bool AllObjectsJudged { get; }
protected HitRenderer() internal HitRenderer()
{ {
KeyConversionInputManager = CreateKeyConversionInputManager(); KeyConversionInputManager = CreateKeyConversionInputManager();
KeyConversionInputManager.RelativeSizeAxes = Axes.Both; KeyConversionInputManager.RelativeSizeAxes = Axes.Both;
@ -120,7 +120,12 @@ namespace osu.Game.Rulesets.UI
/// </summary> /// </summary>
public Beatmap<TObject> Beatmap; public Beatmap<TObject> Beatmap;
protected HitRenderer(WorkingBeatmap beatmap) /// <summary>
/// Creates a hit renderer for a beatmap.
/// </summary>
/// <param name="beatmap">The beatmap to create the hit renderer for.</param>
/// <param name="isForCurrentRuleset">Whether to assume the beatmap is for the current ruleset.</param>
internal HitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset)
{ {
Debug.Assert(beatmap != null, "HitRenderer initialized with a null beatmap."); Debug.Assert(beatmap != null, "HitRenderer initialized with a null beatmap.");
@ -134,7 +139,7 @@ namespace osu.Game.Rulesets.UI
throw new BeatmapInvalidForRulesetException($"{nameof(Beatmap)} can't be converted for the current ruleset."); throw new BeatmapInvalidForRulesetException($"{nameof(Beatmap)} can't be converted for the current ruleset.");
// Convert the beatmap // Convert the beatmap
Beatmap = converter.Convert(beatmap.Beatmap); Beatmap = converter.Convert(beatmap.Beatmap, isForCurrentRuleset);
// Apply defaults // Apply defaults
foreach (var h in Beatmap.HitObjects) foreach (var h in Beatmap.HitObjects)
@ -201,8 +206,13 @@ namespace osu.Game.Rulesets.UI
private readonly List<DrawableHitObject<TObject, TJudgement>> drawableObjects = new List<DrawableHitObject<TObject, TJudgement>>(); private readonly List<DrawableHitObject<TObject, TJudgement>> drawableObjects = new List<DrawableHitObject<TObject, TJudgement>>();
protected HitRenderer(WorkingBeatmap beatmap) /// <summary>
: base(beatmap) /// Creates a hit renderer for a beatmap.
/// </summary>
/// <param name="beatmap">The beatmap to create the hit renderer for.</param>
/// <param name="isForCurrentRuleset">Whether to assume the beatmap is for the current ruleset.</param>
protected HitRenderer(WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(beatmap, isForCurrentRuleset)
{ {
InputManager.Add(content = new Container InputManager.Add(content = new Container
{ {

View File

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

View File

@ -8,11 +8,11 @@ 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.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Screens.Play.Pause;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
namespace osu.Game.Screens.Play namespace osu.Game.Screens.Play
@ -89,7 +89,7 @@ namespace osu.Game.Screens.Play
protected void AddButton(string text, Color4 colour, Action action) protected void AddButton(string text, Color4 colour, Action action)
{ {
Buttons.Add(new PauseButton Buttons.Add(new Button
{ {
Text = text, Text = text,
ButtonColour = colour, ButtonColour = colour,
@ -179,12 +179,6 @@ namespace osu.Game.Screens.Play
} }
} }
}, },
new PauseProgressBar
{
Origin = Anchor.BottomCentre,
Anchor = Anchor.BottomCentre,
Width = 1f
}
}; };
Retries = 0; Retries = 0;
@ -195,5 +189,15 @@ namespace osu.Game.Screens.Play
AlwaysReceiveInput = true; AlwaysReceiveInput = true;
RelativeSizeAxes = Axes.Both; 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 ShowOverlays => false;
internal override bool HasLocalCursorDisplayed => !IsPaused && !HasFailed && HitRenderer.ProvidingUserCursor; internal override bool HasLocalCursorDisplayed => !pauseContainer.IsPaused && !HasFailed && HitRenderer.ProvidingUserCursor;
public BeatmapInfo BeatmapInfo; public BeatmapInfo BeatmapInfo;
public Action RestartRequested; public Action RestartRequested;
public bool IsPaused => !decoupledClock.IsRunning;
internal override bool AllowRulesetChange => false; internal override bool AllowRulesetChange => false;
public bool HasFailed { get; private set; } public bool HasFailed { get; private set; }
public int RestartCount; 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 IAdjustableClock adjustableSourceClock;
private FramedOffsetClock offsetClock; private FramedOffsetClock offsetClock;
private DecoupleableInterpolatingFramedClock decoupledClock; private DecoupleableInterpolatingFramedClock decoupledClock;
private PauseContainer pauseContainer;
private RulesetInfo ruleset; private RulesetInfo ruleset;
private ScoreProcessor scoreProcessor; private ScoreProcessor scoreProcessor;
@ -68,12 +63,7 @@ namespace osu.Game.Screens.Play
#endregion #endregion
private SkipButton skipButton;
private Container hitRendererContainer;
private HUDOverlay hudOverlay; private HUDOverlay hudOverlay;
private PauseOverlay pauseOverlay;
private FailOverlay failOverlay; private FailOverlay failOverlay;
[BackgroundDependencyLoader(permitNulls: true)] [BackgroundDependencyLoader(permitNulls: true)]
@ -97,7 +87,7 @@ namespace osu.Game.Screens.Play
try try
{ {
HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap); HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap, ruleset.ID == Beatmap.BeatmapInfo.Ruleset.ID);
} }
catch (BeatmapInvalidForRulesetException) catch (BeatmapInvalidForRulesetException)
{ {
@ -105,7 +95,7 @@ namespace osu.Game.Screens.Play
// let's try again forcing the beatmap's ruleset. // let's try again forcing the beatmap's ruleset.
ruleset = Beatmap.BeatmapInfo.Ruleset; ruleset = Beatmap.BeatmapInfo.Ruleset;
rulesetInstance = ruleset.CreateInstance(); rulesetInstance = ruleset.CreateInstance();
HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap); HitRenderer = rulesetInstance.CreateHitRendererWith(Beatmap, true);
} }
if (!HitRenderer.Objects.Any()) if (!HitRenderer.Objects.Any())
@ -152,14 +142,59 @@ namespace osu.Game.Screens.Play
decoupledClock.ChangeSource(adjustableSourceClock); decoupledClock.ChangeSource(adjustableSourceClock);
}); });
scoreProcessor = HitRenderer.CreateScoreProcessor(); Children = new Drawable[]
hudOverlay = new StandardHUDOverlay()
{ {
Anchor = Anchor.Centre, pauseContainer = new PauseContainer
Origin = Anchor.Centre {
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 SkipButton(firstObjectTime) { AudioClock = decoupledClock },
new Container
{
RelativeSizeAxes = Axes.Both,
Clock = offsetClock,
Children = new Drawable[]
{
HitRenderer,
}
},
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.KeyCounter.Add(rulesetInstance.CreateGameplayKeys());
hudOverlay.BindProcessor(scoreProcessor); hudOverlay.BindProcessor(scoreProcessor);
hudOverlay.BindHitRenderer(HitRenderer); hudOverlay.BindHitRenderer(HitRenderer);
@ -176,126 +211,6 @@ namespace osu.Game.Screens.Play
//bind ScoreProcessor to ourselves (for a fail situation) //bind ScoreProcessor to ourselves (for a fail situation)
scoreProcessor.Failed += onFail; 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()
{
const double skip_required_cutoff = 3000;
const double fade_time = 300;
double firstHitObject = Beatmap.Beatmap.HitObjects.First().StartTime;
if (firstHitObject < skip_required_cutoff)
{
skipButton.Alpha = 0;
skipButton.Expire();
return;
}
skipButton.FadeInFromZero(fade_time);
skipButton.Action = () =>
{
decoupledClock.Seek(firstHitObject - skip_required_cutoff - fade_time);
skipButton.Action = null;
};
skipButton.Delay(firstHitObject - skip_required_cutoff - fade_time);
skipButton.FadeOut(fade_time);
skipButton.Expire();
}
public void Pause(bool force = false)
{
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)
{
decoupledClock.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();
});
}
public void Resume()
{
lastPauseActionTime = Time.Current;
hudOverlay.KeyCounter.IsCounting = true;
hudOverlay.Progress.Hide();
pauseOverlay.Hide();
decoupledClock.Start();
} }
public void Restart() public void Restart()
@ -315,18 +230,20 @@ namespace osu.Game.Screens.Play
ValidForResume = false; ValidForResume = false;
Delay(1000); using (BeginDelayedSequence(1000))
onCompletionEvent = Schedule(delegate
{ {
var score = new Score onCompletionEvent = Schedule(delegate
{ {
Beatmap = Beatmap.BeatmapInfo, var score = new Score
Ruleset = ruleset {
}; Beatmap = Beatmap.BeatmapInfo,
scoreProcessor.PopulateScore(score); Ruleset = ruleset
score.User = HitRenderer.Replay?.User ?? (Game as OsuGame)?.API?.LocalUser?.Value; };
Push(new Results(score)); scoreProcessor.PopulateScore(score);
}); score.User = HitRenderer.Replay?.User ?? (Game as OsuGame)?.API?.LocalUser?.Value;
Push(new Results(score));
});
}
} }
private void onFail() private void onFail()
@ -351,20 +268,21 @@ namespace osu.Game.Screens.Play
Content.ScaleTo(0.7f); Content.ScaleTo(0.7f);
Content.Delay(250); using (Content.BeginDelayedSequence(250))
Content.FadeIn(250); Content.FadeIn(250);
Content.ScaleTo(1, 750, EasingTypes.OutQuint); Content.ScaleTo(1, 750, EasingTypes.OutQuint);
Delay(750); using (BeginDelayedSequence(750))
Schedule(() => Schedule(() =>
{ {
decoupledClock.Start(); if (!pauseContainer.IsPaused)
initializeSkipButton(); decoupledClock.Start();
});
hitRendererContainer.Alpha = 0; });
hitRendererContainer.FadeIn(750, EasingTypes.OutQuint);
pauseContainer.Alpha = 0;
pauseContainer.FadeIn(750, EasingTypes.OutQuint);
} }
protected override void OnSuspending(Screen next) protected override void OnSuspending(Screen next)
@ -375,23 +293,14 @@ namespace osu.Game.Screens.Play
protected override bool OnExiting(Screen next) protected override bool OnExiting(Screen next)
{ {
if (!HasFailed && ValidForResume) if (HasFailed || !ValidForResume || pauseContainer.AllowExit || HitRenderer.HasReplayLoaded)
{ {
if (pauseOverlay != null && !HitRenderer.HasReplayLoaded) fadeOut();
{ return base.OnExiting(next);
//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(); pauseContainer.Pause();
return base.OnExiting(next); return true;
} }
private void fadeOut() private void fadeOut()
@ -406,6 +315,6 @@ namespace osu.Game.Screens.Play
Background?.FadeTo(1f, fade_out_duration); 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

@ -1,32 +1,123 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 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;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.Threading;
using osu.Framework.Timing;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.Sprites;
using osu.Game.Screens.Ranking;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Input; using OpenTK.Input;
using osu.Framework.Audio.Sample;
namespace osu.Game.Screens.Play namespace osu.Game.Screens.Play
{ {
public class SkipButton : TwoLayerButton public class SkipButton : Container
{ {
public SkipButton() private readonly double startTime;
public IAdjustableClock AudioClock;
private Button button;
private Box remainingTimeBox;
private FadeContainer fadeContainer;
private double displayTime;
public SkipButton(double startTime)
{ {
Text = @"Skip"; AlwaysReceiveInput = true;
Icon = FontAwesome.fa_osu_right_o;
Anchor = Anchor.BottomRight; this.startTime = startTime;
Origin = Anchor.BottomRight;
RelativePositionAxes = Axes.Both;
RelativeSizeAxes = Axes.Both;
Position = new Vector2(0.5f, 0.7f);
Size = new Vector2(1, 0.14f);
Origin = Anchor.Centre;
}
protected override bool OnMouseMove(InputState state)
{
fadeContainer.State = Visibility.Visible;
return base.OnMouseMove(state);
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(AudioManager audio, OsuColour colours) private void load(OsuColour colours)
{ {
ActivationSound = audio.Sample.Get(@"Menu/menuhit"); var baseClock = Clock;
BackgroundColour = colours.Yellow;
HoverColour = colours.YellowDark; if (AudioClock != null)
Clock = new FramedClock(AudioClock) { ProcessSourceClockFrames = false };
Children = new Drawable[]
{
fadeContainer = new FadeContainer
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
button = new Button
{
Clock = baseClock,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
remainingTimeBox = new Box
{
Height = 5,
RelativeSizeAxes = Axes.X,
Colour = colours.Yellow,
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
}
}
}
};
}
private const double skip_required_cutoff = 3000;
private const double fade_time = 300;
private double beginFadeTime => startTime - skip_required_cutoff - fade_time;
protected override void LoadComplete()
{
base.LoadComplete();
if (startTime < skip_required_cutoff)
{
Alpha = 0;
Expire();
return;
}
FadeInFromZero(fade_time);
using (BeginAbsoluteSequence(beginFadeTime))
FadeOut(fade_time);
button.Action = () => AudioClock?.Seek(startTime - skip_required_cutoff - fade_time);
displayTime = Time.Current;
Expire();
}
protected override void Update()
{
base.Update();
remainingTimeBox.ResizeWidthTo((float)Math.Max(0, 1 - (Time.Current - displayTime) / (beginFadeTime - displayTime)), 120, EasingTypes.OutQuint);
} }
protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) protected override bool OnKeyDown(InputState state, KeyDownEventArgs args)
@ -36,11 +127,171 @@ namespace osu.Game.Screens.Play
switch (args.Key) switch (args.Key)
{ {
case Key.Space: case Key.Space:
TriggerClick(); button.TriggerClick();
return true; return true;
} }
return base.OnKeyDown(state, args); return base.OnKeyDown(state, args);
} }
private class FadeContainer : Container, IStateful<Visibility>
{
private Visibility state;
private ScheduledDelegate scheduledHide;
public Visibility State
{
get
{
return state;
}
set
{
var lastState = state;
state = value;
scheduledHide?.Cancel();
switch (state)
{
case Visibility.Visible:
if (lastState == Visibility.Hidden)
FadeIn(500, EasingTypes.OutExpo);
if (!Hovering)
using (BeginDelayedSequence(1000))
scheduledHide = Schedule(() => State = Visibility.Hidden);
break;
case Visibility.Hidden:
FadeOut(1000, EasingTypes.OutExpo);
break;
}
}
}
protected override void LoadComplete()
{
base.LoadComplete();
State = Visibility.Visible;
}
}
private class Button : Container
{
public Action Action;
private Color4 colourNormal;
private Color4 colourHover;
private Box box;
private FillFlowContainer flow;
private Box background;
private AspectContainer aspect;
private SampleChannel activationSound;
public Button()
{
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours, AudioManager audio)
{
activationSound = audio.Sample.Get(@"Menu/menuhit");
colourNormal = colours.Yellow;
colourHover = colours.YellowDark;
Children = new Drawable[]
{
background = new Box
{
Alpha = 0.2f,
Colour = Color4.Black,
RelativeSizeAxes = Axes.Both,
},
aspect = new AspectContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Y,
Height = 0.6f,
Masking = true,
CornerRadius = 15,
Children = new Drawable[]
{
box = new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colourNormal,
},
flow = new FillFlowContainer
{
Anchor = Anchor.TopCentre,
RelativePositionAxes = Axes.Y,
Y = 0.4f,
AutoSizeAxes = Axes.Both,
Origin = Anchor.Centre,
Direction = FillDirection.Horizontal,
Children = new []
{
new TextAwesome { Icon = FontAwesome.fa_chevron_right },
new TextAwesome { Icon = FontAwesome.fa_chevron_right },
new TextAwesome { Icon = FontAwesome.fa_chevron_right },
}
},
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
RelativePositionAxes = Axes.Y,
Y = 0.7f,
TextSize = 12,
Font = @"Exo2.0-Bold",
Origin = Anchor.Centre,
Text = @"SKIP",
},
}
}
};
}
protected override bool OnHover(InputState state)
{
flow.TransformSpacingTo(new Vector2(5), 500, EasingTypes.OutQuint);
box.FadeColour(colourHover, 500, EasingTypes.OutQuint);
background.FadeTo(0.4f, 500, EasingTypes.OutQuint);
return base.OnHover(state);
}
protected override void OnHoverLost(InputState state)
{
flow.TransformSpacingTo(new Vector2(0), 500, EasingTypes.OutQuint);
box.FadeColour(colourNormal, 500, EasingTypes.OutQuint);
background.FadeTo(0.2f, 500, EasingTypes.OutQuint);
base.OnHoverLost(state);
}
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{
aspect.ScaleTo(0.75f, 2000, EasingTypes.OutQuint);
return base.OnMouseDown(state, args);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
aspect.ScaleTo(1, 1000, EasingTypes.OutElastic);
return base.OnMouseUp(state, args);
}
protected override bool OnClick(InputState state)
{
Action?.Invoke();
activationSound.Play();
box.FlashColour(Color4.White, 500, EasingTypes.OutQuint);
aspect.ScaleTo(1.2f, 2000, EasingTypes.OutQuint);
return true;
}
}
} }
} }

View File

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

View File

@ -107,6 +107,8 @@ namespace osu.Game.Screens.Select
return; return;
} }
if (beatmap == SelectedBeatmap) return;
foreach (BeatmapGroup group in groups) foreach (BeatmapGroup group in groups)
{ {
var panel = group.BeatmapPanels.FirstOrDefault(p => p.Beatmap.Equals(beatmap)); var panel = group.BeatmapPanels.FirstOrDefault(p => p.Beatmap.Equals(beatmap));
@ -204,7 +206,7 @@ namespace osu.Game.Screens.Select
if (selectedGroup == null || selectedGroup.State == BeatmapGroupState.Hidden) if (selectedGroup == null || selectedGroup.State == BeatmapGroupState.Hidden)
SelectNext(); SelectNext();
else else
selectGroup(selectedGroup); selectGroup(selectedGroup, selectedPanel);
}; };
filterTask?.Cancel(); filterTask?.Cancel();
@ -339,6 +341,8 @@ namespace osu.Game.Screens.Select
selectedGroup.State = BeatmapGroupState.Collapsed; selectedGroup.State = BeatmapGroupState.Collapsed;
group.State = BeatmapGroupState.Expanded; group.State = BeatmapGroupState.Expanded;
group.SelectedPanel = panel;
panel.State = PanelSelectedState.Selected; panel.State = PanelSelectedState.Selected;
if (selectedPanel == panel) return; if (selectedPanel == panel) return;

View File

@ -49,6 +49,8 @@ namespace osu.Game.Screens.Select
get { return beatmap; } get { return beatmap; }
set set
{ {
if (beatmap == value) return;
beatmap = value; beatmap = value;
pendingBeatmapSwitch?.Cancel(); pendingBeatmapSwitch?.Cancel();

View File

@ -100,6 +100,8 @@ namespace osu.Game.Screens.Select.Leaderboards
get { return beatmap; } get { return beatmap; }
set set
{ {
if (beatmap == value) return;
beatmap = value; beatmap = value;
Scores = null; Scores = null;

View File

@ -198,8 +198,11 @@ namespace osu.Game.Screens.Select
var pendingSelection = selectionChangedDebounce; var pendingSelection = selectionChangedDebounce;
selectionChangedDebounce = null; selectionChangedDebounce = null;
pendingSelection?.RunTask(); if (pendingSelection?.Completed == false)
pendingSelection?.Cancel(); // cancel the already scheduled task. {
pendingSelection?.RunTask();
pendingSelection?.Cancel(); // cancel the already scheduled task.
}
if (Beatmap == null) return; if (Beatmap == null) return;
@ -313,15 +316,15 @@ namespace osu.Game.Screens.Select
{ {
bool beatmapSetChange = false; bool beatmapSetChange = false;
if (!beatmap.Equals(Beatmap?.BeatmapInfo)) if (beatmap.Equals(Beatmap?.BeatmapInfo))
return;
if (beatmap.BeatmapSetInfoID == selectionChangeNoBounce?.BeatmapSetInfoID)
sampleChangeDifficulty.Play();
else
{ {
if (beatmap.BeatmapSetInfoID == selectionChangeNoBounce?.BeatmapSetInfoID) sampleChangeBeatmap.Play();
sampleChangeDifficulty.Play(); beatmapSetChange = true;
else
{
sampleChangeBeatmap.Play();
beatmapSetChange = true;
}
} }
selectionChangeNoBounce = beatmap; selectionChangeNoBounce = beatmap;

View File

@ -74,7 +74,10 @@
<Compile Include="Audio\SampleInfoList.cs" /> <Compile Include="Audio\SampleInfoList.cs" />
<Compile Include="Beatmaps\Drawables\BeatmapBackgroundSprite.cs" /> <Compile Include="Beatmaps\Drawables\BeatmapBackgroundSprite.cs" />
<Compile Include="Beatmaps\DifficultyCalculator.cs" /> <Compile Include="Beatmaps\DifficultyCalculator.cs" />
<Compile Include="Graphics\Containers\BeatSyncedContainer.cs" /> <Compile Include="Beatmaps\Events\BackgroundEvent.cs" />
<Compile Include="Beatmaps\Events\BreakEvent.cs" />
<Compile Include="Beatmaps\Events\Event.cs" />
<Compile Include="Beatmaps\Events\EventInfo.cs" />
<Compile Include="Online\API\Requests\PostMessageRequest.cs" /> <Compile Include="Online\API\Requests\PostMessageRequest.cs" />
<Compile Include="Online\Chat\ErrorMessage.cs" /> <Compile Include="Online\Chat\ErrorMessage.cs" />
<Compile Include="Overlays\Chat\ChatTabControl.cs" /> <Compile Include="Overlays\Chat\ChatTabControl.cs" />
@ -126,6 +129,7 @@
<Compile Include="Rulesets\Objects\Legacy\ConvertSlider.cs" /> <Compile Include="Rulesets\Objects\Legacy\ConvertSlider.cs" />
<Compile Include="Rulesets\Objects\Legacy\Mania\ConvertHit.cs" /> <Compile Include="Rulesets\Objects\Legacy\Mania\ConvertHit.cs" />
<Compile Include="Rulesets\Objects\Legacy\Mania\ConvertHitObjectParser.cs" /> <Compile Include="Rulesets\Objects\Legacy\Mania\ConvertHitObjectParser.cs" />
<Compile Include="Rulesets\Objects\Legacy\Mania\ConvertHold.cs" />
<Compile Include="Rulesets\Objects\Legacy\Mania\ConvertSlider.cs" /> <Compile Include="Rulesets\Objects\Legacy\Mania\ConvertSlider.cs" />
<Compile Include="Rulesets\Objects\Legacy\Mania\ConvertSpinner.cs" /> <Compile Include="Rulesets\Objects\Legacy\Mania\ConvertSpinner.cs" />
<Compile Include="Rulesets\Objects\Legacy\Osu\ConvertHitObjectParser.cs" /> <Compile Include="Rulesets\Objects\Legacy\Osu\ConvertHitObjectParser.cs" />
@ -162,7 +166,6 @@
<Compile Include="Rulesets\Objects\CircularArcApproximator.cs" /> <Compile Include="Rulesets\Objects\CircularArcApproximator.cs" />
<Compile Include="Rulesets\Objects\Legacy\Osu\ConvertHit.cs" /> <Compile Include="Rulesets\Objects\Legacy\Osu\ConvertHit.cs" />
<Compile Include="Rulesets\Objects\Legacy\ConvertHitObjectParser.cs" /> <Compile Include="Rulesets\Objects\Legacy\ConvertHitObjectParser.cs" />
<Compile Include="Rulesets\Objects\Legacy\ConvertHold.cs" />
<Compile Include="Rulesets\Objects\Legacy\Osu\ConvertSlider.cs" /> <Compile Include="Rulesets\Objects\Legacy\Osu\ConvertSlider.cs" />
<Compile Include="Rulesets\Objects\Legacy\Osu\ConvertSpinner.cs" /> <Compile Include="Rulesets\Objects\Legacy\Osu\ConvertSpinner.cs" />
<Compile Include="Rulesets\Objects\SliderCurve.cs" /> <Compile Include="Rulesets\Objects\SliderCurve.cs" />
@ -183,7 +186,6 @@
<Compile Include="Database\RulesetDatabase.cs" /> <Compile Include="Database\RulesetDatabase.cs" />
<Compile Include="Rulesets\Scoring\Score.cs" /> <Compile Include="Rulesets\Scoring\Score.cs" />
<Compile Include="Rulesets\Scoring\ScoreProcessor.cs" /> <Compile Include="Rulesets\Scoring\ScoreProcessor.cs" />
<Compile Include="Screens\Menu\MenuSideFlashes.cs" />
<Compile Include="Screens\Play\HUD\HealthDisplay.cs" /> <Compile Include="Screens\Play\HUD\HealthDisplay.cs" />
<Compile Include="Screens\Play\HUDOverlay.cs" /> <Compile Include="Screens\Play\HUDOverlay.cs" />
<Compile Include="Screens\Play\HUD\StandardHealthDisplay.cs" /> <Compile Include="Screens\Play\HUD\StandardHealthDisplay.cs" />
@ -233,6 +235,7 @@
<Compile Include="Screens\Charts\ChartInfo.cs" /> <Compile Include="Screens\Charts\ChartInfo.cs" />
<Compile Include="Screens\Edit\Editor.cs" /> <Compile Include="Screens\Edit\Editor.cs" />
<Compile Include="Screens\Play\HotkeyRetryOverlay.cs" /> <Compile Include="Screens\Play\HotkeyRetryOverlay.cs" />
<Compile Include="Screens\Play\PauseContainer.cs" />
<Compile Include="Screens\Play\SongProgressInfo.cs" /> <Compile Include="Screens\Play\SongProgressInfo.cs" />
<Compile Include="Screens\Play\HUD\ModDisplay.cs" /> <Compile Include="Screens\Play\HUD\ModDisplay.cs" />
<Compile Include="Screens\Play\SquareGraph.cs" /> <Compile Include="Screens\Play\SquareGraph.cs" />
@ -256,8 +259,6 @@
<Compile Include="Screens\Play\FailOverlay.cs" /> <Compile Include="Screens\Play\FailOverlay.cs" />
<Compile Include="Screens\Play\MenuOverlay.cs" /> <Compile Include="Screens\Play\MenuOverlay.cs" />
<Compile Include="Screens\Play\KeyConversionInputManager.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\PlayerInputManager.cs" />
<Compile Include="Screens\Play\PlayerLoader.cs" /> <Compile Include="Screens\Play\PlayerLoader.cs" />
<Compile Include="Screens\Play\ReplayPlayer.cs" /> <Compile Include="Screens\Play\ReplayPlayer.cs" />
@ -344,7 +345,6 @@
<Compile Include="Beatmaps\Formats\BeatmapDecoder.cs" /> <Compile Include="Beatmaps\Formats\BeatmapDecoder.cs" />
<Compile Include="Beatmaps\Formats\OsuLegacyDecoder.cs" /> <Compile Include="Beatmaps\Formats\OsuLegacyDecoder.cs" />
<Compile Include="Beatmaps\IO\OszArchiveReader.cs" /> <Compile Include="Beatmaps\IO\OszArchiveReader.cs" />
<Compile Include="Beatmaps\Events\EventType.cs" />
<Compile Include="Graphics\UserInterface\Volume\VolumeMeter.cs" /> <Compile Include="Graphics\UserInterface\Volume\VolumeMeter.cs" />
<Compile Include="Database\BeatmapSetInfo.cs" /> <Compile Include="Database\BeatmapSetInfo.cs" />
<Compile Include="Database\BeatmapMetadata.cs" /> <Compile Include="Database\BeatmapMetadata.cs" />
@ -397,8 +397,6 @@
<Compile Include="Screens\Play\SongProgress.cs" /> <Compile Include="Screens\Play\SongProgress.cs" />
<Compile Include="Screens\Play\SongProgressGraph.cs" /> <Compile Include="Screens\Play\SongProgressGraph.cs" />
<Compile Include="Screens\Play\SongProgressBar.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="Overlays\Mods\ModSelectOverlay.cs" />
<Compile Include="Rulesets\Mods\Mod.cs" /> <Compile Include="Rulesets\Mods\Mod.cs" />
<Compile Include="Overlays\Mods\ModButtonEmpty.cs" /> <Compile Include="Overlays\Mods\ModButtonEmpty.cs" />