diff --git a/osu.Desktop/Properties/launchSettings.json b/osu.Desktop/Properties/launchSettings.json
new file mode 100644
index 0000000000..5e768ec9fa
--- /dev/null
+++ b/osu.Desktop/Properties/launchSettings.json
@@ -0,0 +1,11 @@
+{
+ "profiles": {
+ "osu! Desktop": {
+ "commandName": "Project"
+ },
+ "osu! Tournament": {
+ "commandName": "Project",
+ "commandLineArgs": "--tournament"
+ }
+ }
+}
\ No newline at end of file
diff --git a/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs b/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs
index c721ff862a..46e427e1b7 100644
--- a/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs
+++ b/osu.Game.Rulesets.Catch/MathUtils/FastRandom.cs
@@ -12,14 +12,14 @@ namespace osu.Game.Rulesets.Catch.MathUtils
{
private const double int_to_real = 1.0 / (int.MaxValue + 1.0);
private const uint int_mask = 0x7FFFFFFF;
- private const uint y = 842502087;
- private const uint z = 3579807591;
- private const uint w = 273326509;
- private uint _x, _y = y, _z = z, _w = w;
+ private const uint y_initial = 842502087;
+ private const uint z_initial = 3579807591;
+ private const uint w_initial = 273326509;
+ private uint x, y = y_initial, z = z_initial, w = w_initial;
public FastRandom(int seed)
{
- _x = (uint)seed;
+ x = (uint)seed;
}
public FastRandom()
@@ -33,11 +33,11 @@ namespace osu.Game.Rulesets.Catch.MathUtils
/// The random value.
public uint NextUInt()
{
- uint t = _x ^ (_x << 11);
- _x = _y;
- _y = _z;
- _z = _w;
- return _w = _w ^ (_w >> 19) ^ t ^ (t >> 8);
+ uint t = x ^ (x << 11);
+ x = y;
+ y = z;
+ z = w;
+ return w = w ^ (w >> 19) ^ t ^ (t >> 8);
}
///
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs
index 4fe02135c4..c2aefac587 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs
@@ -11,6 +11,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Objects;
using osuTK;
using osuTK.Graphics;
@@ -20,10 +21,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
{
public class PathControlPointPiece : BlueprintPiece
{
- public Action RequestSelection;
+ public Action RequestSelection;
public readonly BindableBool IsSelected = new BindableBool();
- public readonly int Index;
+
+ public readonly PathControlPoint ControlPoint;
private readonly Slider slider;
private readonly Path path;
@@ -36,10 +38,14 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
[Resolved]
private OsuColour colours { get; set; }
- public PathControlPointPiece(Slider slider, int index)
+ private IBindable sliderPosition;
+ private IBindable pathVersion;
+
+ public PathControlPointPiece(Slider slider, PathControlPoint controlPoint)
{
this.slider = slider;
- Index = index;
+
+ ControlPoint = controlPoint;
Origin = Anchor.Centre;
AutoSizeAxes = Axes.Both;
@@ -85,48 +91,41 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
};
}
- protected override void Update()
+ protected override void LoadComplete()
{
- base.Update();
+ base.LoadComplete();
- Position = slider.StackedPosition + slider.Path.ControlPoints[Index].Position.Value;
+ sliderPosition = slider.PositionBindable.GetBoundCopy();
+ sliderPosition.BindValueChanged(_ => updateDisplay());
+ pathVersion = slider.Path.Version.GetBoundCopy();
+ pathVersion.BindValueChanged(_ => updateDisplay());
+
+ IsSelected.BindValueChanged(_ => updateMarkerDisplay());
+
+ updateDisplay();
+ }
+
+ private void updateDisplay()
+ {
updateMarkerDisplay();
updateConnectingPath();
}
- ///
- /// Updates the state of the circular control point marker.
- ///
- private void updateMarkerDisplay()
- {
- markerRing.Alpha = IsSelected.Value ? 1 : 0;
-
- Color4 colour = slider.Path.ControlPoints[Index].Type.Value.HasValue ? colours.Red : colours.Yellow;
- if (IsHovered || IsSelected.Value)
- colour = Color4.White;
- marker.Colour = colour;
- }
-
- ///
- /// Updates the path connecting this control point to the previous one.
- ///
- private void updateConnectingPath()
- {
- path.ClearVertices();
-
- if (Index != slider.Path.ControlPoints.Count - 1)
- {
- path.AddVertex(Vector2.Zero);
- path.AddVertex(slider.Path.ControlPoints[Index + 1].Position.Value - slider.Path.ControlPoints[Index].Position.Value);
- }
-
- path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero);
- }
-
// The connecting path is excluded from positional input
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => marker.ReceivePositionalInputAt(screenSpacePos);
+ protected override bool OnHover(HoverEvent e)
+ {
+ updateMarkerDisplay();
+ return false;
+ }
+
+ protected override void OnHoverLost(HoverLostEvent e)
+ {
+ updateMarkerDisplay();
+ }
+
protected override bool OnMouseDown(MouseDownEvent e)
{
if (RequestSelection == null)
@@ -135,12 +134,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
switch (e.Button)
{
case MouseButton.Left:
- RequestSelection.Invoke(Index, e);
+ RequestSelection.Invoke(this, e);
return true;
case MouseButton.Right:
if (!IsSelected.Value)
- RequestSelection.Invoke(Index, e);
+ RequestSelection.Invoke(this, e);
return false; // Allow context menu to show
}
@@ -155,7 +154,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
protected override bool OnDrag(DragEvent e)
{
- if (Index == 0)
+ if (ControlPoint == slider.Path.ControlPoints[0])
{
// Special handling for the head control point - the position of the slider changes which means the snapped position and time have to be taken into account
(Vector2 snappedPosition, double snappedTime) = snapProvider?.GetSnappedPosition(e.MousePosition, slider.StartTime) ?? (e.MousePosition, slider.StartTime);
@@ -169,11 +168,47 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
slider.Path.ControlPoints[i].Position.Value -= movementDelta;
}
else
- slider.Path.ControlPoints[Index].Position.Value += e.Delta;
+ ControlPoint.Position.Value += e.Delta;
return true;
}
protected override bool OnDragEnd(DragEndEvent e) => true;
+
+ ///
+ /// Updates the state of the circular control point marker.
+ ///
+ private void updateMarkerDisplay()
+ {
+ Position = slider.StackedPosition + ControlPoint.Position.Value;
+
+ markerRing.Alpha = IsSelected.Value ? 1 : 0;
+
+ Color4 colour = ControlPoint.Type.Value != null ? colours.Red : colours.Yellow;
+ if (IsHovered || IsSelected.Value)
+ colour = Color4.White;
+ marker.Colour = colour;
+ }
+
+ ///
+ /// Updates the path connecting this control point to the previous one.
+ ///
+ private void updateConnectingPath()
+ {
+ path.ClearVertices();
+
+ int index = slider.Path.ControlPoints.IndexOf(ControlPoint);
+
+ if (index == -1)
+ return;
+
+ if (++index != slider.Path.ControlPoints.Count)
+ {
+ path.AddVertex(Vector2.Zero);
+ path.AddVertex(slider.Path.ControlPoints[index].Position.Value - ControlPoint.Position.Value);
+ }
+
+ path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero);
+ }
}
}
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
index ced4af4e28..cd19653a2e 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs
@@ -1,10 +1,11 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using System.Collections.Generic;
using System.Linq;
using Humanizer;
-using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
@@ -14,9 +15,8 @@ using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu.Objects;
-using osu.Game.Screens.Edit.Compose;
-using osuTK;
using osuTK.Input;
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
@@ -24,13 +24,16 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
public class PathControlPointVisualiser : CompositeDrawable, IKeyBindingHandler, IHasContextMenu
{
internal readonly Container Pieces;
+
private readonly Slider slider;
+
private readonly bool allowSelection;
private InputManager inputManager;
- [Resolved(CanBeNull = true)]
- private IPlacementHandler placementHandler { get; set; }
+ private IBindableList controlPoints;
+
+ public Action> RemoveControlPointsRequested;
public PathControlPointVisualiser(Slider slider, bool allowSelection)
{
@@ -47,30 +50,40 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
base.LoadComplete();
inputManager = GetContainingInputManager();
+
+ controlPoints = slider.Path.ControlPoints.GetBoundCopy();
+ controlPoints.ItemsAdded += addControlPoints;
+ controlPoints.ItemsRemoved += removeControlPoints;
+
+ addControlPoints(controlPoints);
}
- protected override void Update()
+ private void addControlPoints(IEnumerable controlPoints)
{
- base.Update();
-
- while (slider.Path.ControlPoints.Count > Pieces.Count)
+ foreach (var point in controlPoints)
{
- var piece = new PathControlPointPiece(slider, Pieces.Count);
+ var piece = new PathControlPointPiece(slider, point);
if (allowSelection)
piece.RequestSelection = selectPiece;
Pieces.Add(piece);
}
+ }
- while (slider.Path.ControlPoints.Count < Pieces.Count)
- Pieces.Remove(Pieces[Pieces.Count - 1]);
+ private void removeControlPoints(IEnumerable controlPoints)
+ {
+ foreach (var point in controlPoints)
+ Pieces.RemoveAll(p => p.ControlPoint == point);
}
protected override bool OnClick(ClickEvent e)
{
foreach (var piece in Pieces)
+ {
piece.IsSelected.Value = false;
+ }
+
return false;
}
@@ -87,48 +100,26 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
public bool OnReleased(PlatformAction action) => action.ActionMethod == PlatformActionMethod.Delete;
- private void selectPiece(int index, MouseButtonEvent e)
+ private void selectPiece(PathControlPointPiece piece, MouseButtonEvent e)
{
if (e.Button == MouseButton.Left && inputManager.CurrentState.Keyboard.ControlPressed)
- Pieces[index].IsSelected.Toggle();
+ piece.IsSelected.Toggle();
else
{
- foreach (var piece in Pieces)
- piece.IsSelected.Value = piece.Index == index;
+ foreach (var p in Pieces)
+ p.IsSelected.Value = p == piece;
}
}
private bool deleteSelected()
{
- List toRemove = Pieces.Where(p => p.IsSelected.Value).Select(p => slider.Path.ControlPoints[p.Index]).ToList();
+ List toRemove = Pieces.Where(p => p.IsSelected.Value).Select(p => p.ControlPoint).ToList();
// Ensure that there are any points to be deleted
if (toRemove.Count == 0)
return false;
- foreach (var c in toRemove)
- {
- // The first control point in the slider must have a type, so take it from the previous "first" one
- // Todo: Should be handled within SliderPath itself
- if (c == slider.Path.ControlPoints[0] && slider.Path.ControlPoints.Count > 1 && slider.Path.ControlPoints[1].Type.Value == null)
- slider.Path.ControlPoints[1].Type.Value = slider.Path.ControlPoints[0].Type.Value;
-
- slider.Path.ControlPoints.Remove(c);
- }
-
- // If there are 0 or 1 remaining control points, the slider is in a degenerate (single point) form and should be deleted
- if (slider.Path.ControlPoints.Count <= 1)
- {
- placementHandler?.Delete(slider);
- return true;
- }
-
- // The path will have a non-zero offset if the head is removed, but sliders don't support this behaviour since the head is positioned at the slider's position
- // So the slider needs to be offset by this amount instead, and all control points offset backwards such that the path is re-positioned at (0, 0)
- Vector2 first = slider.Path.ControlPoints[0].Position.Value;
- foreach (var c in slider.Path.ControlPoints)
- c.Position.Value -= first;
- slider.Position += first;
+ RemoveControlPointsRequested?.Invoke(toRemove);
// Since pieces are re-used, they will not point to the deleted control points while remaining selected
foreach (var piece in Pieces)
@@ -144,16 +135,63 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
if (!Pieces.Any(p => p.IsHovered))
return null;
- int selectedPoints = Pieces.Count(p => p.IsSelected.Value);
+ var selectedPieces = Pieces.Where(p => p.IsSelected.Value).ToList();
+ int count = selectedPieces.Count;
- if (selectedPoints == 0)
+ if (count == 0)
return null;
+ List
protected float DistanceSpacing { get; private set; }
- ///
- /// The snapping time at .
- ///
- protected double StartTime { get; private set; }
-
///
/// The maximum number of distance snapping intervals allowed.
///
protected int MaxIntervals { get; private set; }
///
- /// The position which the grid is centred on.
- /// The first beat snapping tick is located at + in the desired direction.
+ /// The position which the grid should start.
+ /// The first beat snapping tick is located at + away from this point.
///
- protected readonly Vector2 CentrePosition;
+ protected readonly Vector2 StartPosition;
+
+ ///
+ /// The snapping time at .
+ ///
+ protected readonly double StartTime;
[Resolved]
protected OsuColour Colours { get; private set; }
@@ -53,25 +51,23 @@ namespace osu.Game.Screens.Edit.Compose.Components
private BindableBeatDivisor beatDivisor { get; set; }
private readonly Cached gridCache = new Cached();
- private readonly HitObject hitObject;
- private readonly HitObject nextHitObject;
+ private readonly double? endTime;
- protected DistanceSnapGrid(HitObject hitObject, [CanBeNull] HitObject nextHitObject, Vector2 centrePosition)
+ ///
+ /// Creates a new .
+ ///
+ /// The position at which the grid should start. The first tick is located one distance spacing length away from this point.
+ /// The snapping time at .
+ /// The time at which the snapping grid should end. If null, the grid will continue until the bounds of the screen are exceeded.
+ protected DistanceSnapGrid(Vector2 startPosition, double startTime, double? endTime = null)
{
- this.hitObject = hitObject;
- this.nextHitObject = nextHitObject;
-
- CentrePosition = centrePosition;
+ this.endTime = endTime;
+ StartPosition = startPosition;
+ StartTime = startTime;
RelativeSizeAxes = Axes.Both;
}
- [BackgroundDependencyLoader]
- private void load()
- {
- StartTime = hitObject.GetEndTime();
- }
-
protected override void LoadComplete()
{
base.LoadComplete();
@@ -83,12 +79,12 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
DistanceSpacing = SnapProvider.GetBeatSnapDistanceAt(StartTime);
- if (nextHitObject == null)
+ if (endTime == null)
MaxIntervals = int.MaxValue;
else
{
// +1 is added since a snapped hitobject may have its start time slightly less than the snapped time due to floating point errors
- double maxDuration = nextHitObject.StartTime - StartTime + 1;
+ double maxDuration = endTime.Value - StartTime + 1;
MaxIntervals = (int)(maxDuration / SnapProvider.DistanceToDuration(StartTime, DistanceSpacing));
}
@@ -110,7 +106,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
if (!gridCache.IsValid)
{
ClearInternal();
- CreateContent(CentrePosition);
+ CreateContent(StartPosition);
gridCache.Validate();
}
}
@@ -118,7 +114,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
///
/// Creates the content which visualises the grid ticks.
///
- protected abstract void CreateContent(Vector2 centrePosition);
+ protected abstract void CreateContent(Vector2 startPosition);
///
/// Snaps a position to this grid.
diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs
index f54d638584..adfbe977a4 100644
--- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs
+++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs
@@ -188,26 +188,22 @@ namespace osu.Game.Screens.Play
InternalButtons.Add(button);
}
- private int _selectionIndex = -1;
+ private int selectionIndex = -1;
- private int selectionIndex
+ private void setSelected(int value)
{
- get => _selectionIndex;
- set
- {
- if (_selectionIndex == value)
- return;
+ if (selectionIndex == value)
+ return;
- // Deselect the previously-selected button
- if (_selectionIndex != -1)
- InternalButtons[_selectionIndex].Selected.Value = false;
+ // Deselect the previously-selected button
+ if (selectionIndex != -1)
+ InternalButtons[selectionIndex].Selected.Value = false;
- _selectionIndex = value;
+ selectionIndex = value;
- // Select the newly-selected button
- if (_selectionIndex != -1)
- InternalButtons[_selectionIndex].Selected.Value = true;
- }
+ // Select the newly-selected button
+ if (selectionIndex != -1)
+ InternalButtons[selectionIndex].Selected.Value = true;
}
protected override bool OnKeyDown(KeyDownEvent e)
@@ -218,16 +214,16 @@ namespace osu.Game.Screens.Play
{
case Key.Up:
if (selectionIndex == -1 || selectionIndex == 0)
- selectionIndex = InternalButtons.Count - 1;
+ setSelected(InternalButtons.Count - 1);
else
- selectionIndex--;
+ setSelected(selectionIndex - 1);
return true;
case Key.Down:
if (selectionIndex == -1 || selectionIndex == InternalButtons.Count - 1)
- selectionIndex = 0;
+ setSelected(0);
else
- selectionIndex++;
+ setSelected(selectionIndex + 1);
return true;
}
}
@@ -266,9 +262,9 @@ namespace osu.Game.Screens.Play
private void buttonSelectionChanged(DialogButton button, bool isSelected)
{
if (!isSelected)
- selectionIndex = -1;
+ setSelected(-1);
else
- selectionIndex = InternalButtons.IndexOf(button);
+ setSelected(InternalButtons.IndexOf(button));
}
private void updateRetryCount()
diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index d40c448452..9feee82989 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -135,7 +135,7 @@ namespace osu.Game.Screens.Play
addGameplayComponents(GameplayClockContainer, working);
addOverlayComponents(GameplayClockContainer, working);
- DrawableRuleset.HasReplayLoaded.BindValueChanged(e => HUDOverlay.HoldToQuit.PauseOnFocusLost = !e.NewValue && PauseOnFocusLost, true);
+ DrawableRuleset.HasReplayLoaded.BindValueChanged(_ => updatePauseOnFocusLostState(), true);
// bind clock into components that require it
DrawableRuleset.IsPaused.BindTo(GameplayClockContainer.IsPaused);
@@ -146,6 +146,7 @@ namespace osu.Game.Screens.Play
foreach (var mod in Mods.Value.OfType())
mod.ApplyToScoreProcessor(ScoreProcessor);
+ breakOverlay.IsBreakTime.ValueChanged += _ => updatePauseOnFocusLostState();
}
private void addUnderlayComponents(Container target)
@@ -241,6 +242,11 @@ namespace osu.Game.Screens.Play
});
}
+ private void updatePauseOnFocusLostState() =>
+ HUDOverlay.HoldToQuit.PauseOnFocusLost = PauseOnFocusLost
+ && !DrawableRuleset.HasReplayLoaded.Value
+ && !breakOverlay.IsBreakTime.Value;
+
private WorkingBeatmap loadBeatmap()
{
WorkingBeatmap working = Beatmap.Value;
diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs
index c5bdc230d0..3e7d4e244b 100644
--- a/osu.Game/Screens/Select/Details/AdvancedStats.cs
+++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs
@@ -12,11 +12,18 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using System;
using osu.Game.Beatmaps;
+using osu.Framework.Bindables;
+using System.Collections.Generic;
+using osu.Game.Rulesets.Mods;
+using System.Linq;
namespace osu.Game.Screens.Select.Details
{
public class AdvancedStats : Container
{
+ [Resolved]
+ private IBindable> mods { get; set; }
+
private readonly StatisticRow firstValue, hpDrain, accuracy, approachRate, starDifficulty;
private BeatmapInfo beatmap;
@@ -30,22 +37,7 @@ namespace osu.Game.Screens.Select.Details
beatmap = value;
- //mania specific
- if ((Beatmap?.Ruleset?.ID ?? 0) == 3)
- {
- firstValue.Title = "Key Amount";
- firstValue.Value = (int)MathF.Round(Beatmap?.BaseDifficulty?.CircleSize ?? 0);
- }
- else
- {
- firstValue.Title = "Circle Size";
- firstValue.Value = Beatmap?.BaseDifficulty?.CircleSize ?? 0;
- }
-
- hpDrain.Value = Beatmap?.BaseDifficulty?.DrainRate ?? 0;
- accuracy.Value = Beatmap?.BaseDifficulty?.OverallDifficulty ?? 0;
- approachRate.Value = Beatmap?.BaseDifficulty?.ApproachRate ?? 0;
- starDifficulty.Value = (float)(Beatmap?.StarDifficulty ?? 0);
+ updateStatistics();
}
}
@@ -71,6 +63,46 @@ namespace osu.Game.Screens.Select.Details
private void load(OsuColour colours)
{
starDifficulty.AccentColour = colours.Yellow;
+ mods.ValueChanged += _ => updateStatistics();
+ }
+
+ private void updateStatistics()
+ {
+ BeatmapInfo processed = Beatmap?.Clone();
+
+ if (processed != null && mods.Value.Any(m => m is IApplicableToDifficulty))
+ {
+ processed.BaseDifficulty = processed.BaseDifficulty.Clone();
+
+ foreach (var mod in mods.Value.OfType())
+ mod.ApplyToDifficulty(processed.BaseDifficulty);
+ }
+
+ BeatmapDifficulty baseDifficulty = Beatmap?.BaseDifficulty;
+ BeatmapDifficulty moddedDifficulty = processed?.BaseDifficulty;
+
+ //mania specific
+ if ((processed?.Ruleset?.ID ?? 0) == 3)
+ {
+ firstValue.Title = "Key Amount";
+ firstValue.BaseValue = (int)MathF.Round(baseDifficulty?.CircleSize ?? 0);
+ firstValue.ModdedValue = (int)MathF.Round(moddedDifficulty?.CircleSize ?? 0);
+ }
+ else
+ {
+ firstValue.Title = "Circle Size";
+ firstValue.BaseValue = baseDifficulty?.CircleSize ?? 0;
+ firstValue.ModdedValue = moddedDifficulty?.CircleSize ?? 0;
+ }
+
+ hpDrain.BaseValue = baseDifficulty?.DrainRate ?? 0;
+ accuracy.BaseValue = baseDifficulty?.OverallDifficulty ?? 0;
+ approachRate.BaseValue = baseDifficulty?.ApproachRate ?? 0;
+ starDifficulty.BaseValue = (float)(processed?.StarDifficulty ?? 0);
+
+ hpDrain.ModdedValue = moddedDifficulty?.DrainRate ?? 0;
+ accuracy.ModdedValue = moddedDifficulty?.OverallDifficulty ?? 0;
+ approachRate.ModdedValue = moddedDifficulty?.ApproachRate ?? 0;
}
private class StatisticRow : Container, IHasAccentColour
@@ -81,7 +113,10 @@ namespace osu.Game.Screens.Select.Details
private readonly float maxValue;
private readonly bool forceDecimalPlaces;
private readonly OsuSpriteText name, value;
- private readonly Bar bar;
+ private readonly Bar bar, modBar;
+
+ [Resolved]
+ private OsuColour colours { get; set; }
public string Title
{
@@ -89,19 +124,39 @@ namespace osu.Game.Screens.Select.Details
set => name.Text = value;
}
- private float difficultyValue;
+ private float baseValue;
- public float Value
+ private float moddedValue;
+
+ public float BaseValue
{
- get => difficultyValue;
+ get => baseValue;
set
{
- difficultyValue = value;
+ baseValue = value;
bar.Length = value / maxValue;
this.value.Text = value.ToString(forceDecimalPlaces ? "0.00" : "0.##");
}
}
+ public float ModdedValue
+ {
+ get => moddedValue;
+ set
+ {
+ moddedValue = value;
+ modBar.Length = value / maxValue;
+ this.value.Text = value.ToString(forceDecimalPlaces ? "0.00" : "0.##");
+
+ if (moddedValue > baseValue)
+ modBar.AccentColour = this.value.Colour = colours.Red;
+ else if (moddedValue < baseValue)
+ modBar.AccentColour = this.value.Colour = colours.BlueDark;
+ else
+ modBar.AccentColour = this.value.Colour = Color4.White;
+ }
+ }
+
public Color4 AccentColour
{
get => bar.AccentColour;
@@ -135,6 +190,15 @@ namespace osu.Game.Screens.Select.Details
BackgroundColour = Color4.White.Opacity(0.5f),
Padding = new MarginPadding { Left = name_width + 10, Right = value_width + 10 },
},
+ modBar = new Bar
+ {
+ Origin = Anchor.CentreLeft,
+ Anchor = Anchor.CentreLeft,
+ RelativeSizeAxes = Axes.X,
+ Alpha = 0.5f,
+ Height = 5,
+ Padding = new MarginPadding { Left = name_width + 10, Right = value_width + 10 },
+ },
new Container
{
Anchor = Anchor.TopRight,
diff --git a/osu.Game/Tests/Visual/TestPlayer.cs b/osu.Game/Tests/Visual/TestPlayer.cs
index 31f6edadec..8e3821f1a0 100644
--- a/osu.Game/Tests/Visual/TestPlayer.cs
+++ b/osu.Game/Tests/Visual/TestPlayer.cs
@@ -8,13 +8,16 @@ namespace osu.Game.Tests.Visual
{
public class TestPlayer : Player
{
- protected override bool PauseOnFocusLost => false;
+ protected override bool PauseOnFocusLost { get; }
public new DrawableRuleset DrawableRuleset => base.DrawableRuleset;
- public TestPlayer(bool allowPause = true, bool showResults = true)
+ public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer;
+
+ public TestPlayer(bool allowPause = true, bool showResults = true, bool pauseOnFocusLost = false)
: base(allowPause, showResults)
{
+ PauseOnFocusLost = pauseOnFocusLost;
}
}
}
diff --git a/osu.Game/Users/Country.cs b/osu.Game/Users/Country.cs
index 1dcce6e870..a9fcd69286 100644
--- a/osu.Game/Users/Country.cs
+++ b/osu.Game/Users/Country.cs
@@ -1,11 +1,12 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using Newtonsoft.Json;
namespace osu.Game.Users
{
- public class Country
+ public class Country : IEquatable
{
///
/// The name of this country.
@@ -18,5 +19,7 @@ namespace osu.Game.Users
///
[JsonProperty(@"code")]
public string FlagName;
+
+ public bool Equals(Country other) => FlagName == other?.FlagName;
}
}
diff --git a/osu.Game/Users/Drawables/UpdateableFlag.cs b/osu.Game/Users/Drawables/UpdateableFlag.cs
index abc16b2390..1d30720889 100644
--- a/osu.Game/Users/Drawables/UpdateableFlag.cs
+++ b/osu.Game/Users/Drawables/UpdateableFlag.cs
@@ -1,8 +1,11 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
+using osu.Framework.Input.Events;
+using osu.Game.Overlays;
namespace osu.Game.Users.Drawables
{
@@ -34,5 +37,14 @@ namespace osu.Game.Users.Drawables
RelativeSizeAxes = Axes.Both,
};
}
+
+ [Resolved(canBeNull: true)]
+ private RankingsOverlay rankingsOverlay { get; set; }
+
+ protected override bool OnClick(ClickEvent e)
+ {
+ rankingsOverlay?.ShowCountry(Country);
+ return true;
+ }
}
}