mirror of
https://github.com/ppy/osu.git
synced 2024-12-15 15:12:57 +08:00
Merge branch 'master' into lighting-n-custom-path
This commit is contained in:
commit
b98114d42f
@ -285,8 +285,6 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
|
|
||||||
private void runHyperDashStateTransition(bool hyperDashing)
|
private void runHyperDashStateTransition(bool hyperDashing)
|
||||||
{
|
{
|
||||||
trails.HyperDashTrailsColour = hyperDashColour;
|
|
||||||
trails.EndGlowSpritesColour = hyperDashEndGlowColour;
|
|
||||||
updateTrailVisibility();
|
updateTrailVisibility();
|
||||||
|
|
||||||
if (hyperDashing)
|
if (hyperDashing)
|
||||||
@ -403,6 +401,9 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDashAfterImage)?.Value ??
|
skin.GetConfig<CatchSkinColour, Color4>(CatchSkinColour.HyperDashAfterImage)?.Value ??
|
||||||
hyperDashColour;
|
hyperDashColour;
|
||||||
|
|
||||||
|
trails.HyperDashTrailsColour = hyperDashColour;
|
||||||
|
trails.EndGlowSpritesColour = hyperDashEndGlowColour;
|
||||||
|
|
||||||
runHyperDashStateTransition(HyperDashing);
|
runHyperDashStateTransition(HyperDashing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
private readonly Container<CatcherTrailSprite> hyperDashTrails;
|
private readonly Container<CatcherTrailSprite> hyperDashTrails;
|
||||||
private readonly Container<CatcherTrailSprite> endGlowSprites;
|
private readonly Container<CatcherTrailSprite> endGlowSprites;
|
||||||
|
|
||||||
private Color4 hyperDashTrailsColour;
|
private Color4 hyperDashTrailsColour = Catcher.DEFAULT_HYPER_DASH_COLOUR;
|
||||||
|
|
||||||
public Color4 HyperDashTrailsColour
|
public Color4 HyperDashTrailsColour
|
||||||
{
|
{
|
||||||
@ -35,11 +35,11 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
hyperDashTrailsColour = value;
|
hyperDashTrailsColour = value;
|
||||||
hyperDashTrails.FadeColour(hyperDashTrailsColour, Catcher.HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint);
|
hyperDashTrails.Colour = hyperDashTrailsColour;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Color4 endGlowSpritesColour;
|
private Color4 endGlowSpritesColour = Catcher.DEFAULT_HYPER_DASH_COLOUR;
|
||||||
|
|
||||||
public Color4 EndGlowSpritesColour
|
public Color4 EndGlowSpritesColour
|
||||||
{
|
{
|
||||||
@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Catch.UI
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
endGlowSpritesColour = value;
|
endGlowSpritesColour = value;
|
||||||
endGlowSprites.FadeColour(endGlowSpritesColour, Catcher.HYPER_DASH_TRANSITION_DURATION, Easing.OutQuint);
|
endGlowSprites.Colour = endGlowSpritesColour;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.UI.Components;
|
using osu.Game.Rulesets.Mania.UI.Components;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
@ -13,7 +14,8 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
SetContents(() => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground), _ => new DefaultStageBackground())
|
SetContents(() => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground, stageDefinition: new StageDefinition { Columns = 4 }),
|
||||||
|
_ => new DefaultStageBackground())
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
||||||
@ -12,7 +13,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
SetContents(() => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground), _ => null)
|
SetContents(() => new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground, stageDefinition: new StageDefinition { Columns = 4 }), _ => null)
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
|
@ -21,14 +21,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="column">The 0-based column index.</param>
|
/// <param name="column">The 0-based column index.</param>
|
||||||
/// <returns>Whether the column is a special column.</returns>
|
/// <returns>Whether the column is a special column.</returns>
|
||||||
public bool IsSpecialColumn(int column) => Columns % 2 == 1 && column == Columns / 2;
|
public readonly bool IsSpecialColumn(int column) => Columns % 2 == 1 && column == Columns / 2;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Get the type of column given a column index.
|
/// Get the type of column given a column index.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="column">The 0-based column index.</param>
|
/// <param name="column">The 0-based column index.</param>
|
||||||
/// <returns>The type of the column.</returns>
|
/// <returns>The type of the column.</returns>
|
||||||
public ColumnType GetTypeOfColumn(int column)
|
public readonly ColumnType GetTypeOfColumn(int column)
|
||||||
{
|
{
|
||||||
if (IsSpecialColumn(column))
|
if (IsSpecialColumn(column))
|
||||||
return ColumnType.Special;
|
return ColumnType.Special;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
@ -14,15 +15,23 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly int? TargetColumn;
|
public readonly int? TargetColumn;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The intended <see cref="StageDefinition"/> for this component.
|
||||||
|
/// May be null if the component is not a direct member of a <see cref="Stage"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly StageDefinition? StageDefinition;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new <see cref="ManiaSkinComponent"/>.
|
/// Creates a new <see cref="ManiaSkinComponent"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="component">The component.</param>
|
/// <param name="component">The component.</param>
|
||||||
/// <param name="targetColumn">The intended <see cref="Column"/> index for this component. May be null if the component does not exist in a <see cref="Column"/>.</param>
|
/// <param name="targetColumn">The intended <see cref="Column"/> index for this component. May be null if the component does not exist in a <see cref="Column"/>.</param>
|
||||||
public ManiaSkinComponent(ManiaSkinComponents component, int? targetColumn = null)
|
/// <param name="stageDefinition">The intended <see cref="StageDefinition"/> for this component. May be null if the component is not a direct member of a <see cref="Stage"/>.</param>
|
||||||
|
public ManiaSkinComponent(ManiaSkinComponents component, int? targetColumn = null, StageDefinition? stageDefinition = null)
|
||||||
: base(component)
|
: base(component)
|
||||||
{
|
{
|
||||||
TargetColumn = targetColumn;
|
TargetColumn = targetColumn;
|
||||||
|
StageDefinition = stageDefinition;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override string RulesetPrefix => ManiaRuleset.SHORT_NAME;
|
protected override string RulesetPrefix => ManiaRuleset.SHORT_NAME;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
|
||||||
@ -32,6 +33,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
private readonly Container<DrawableHoldNoteTail> tailContainer;
|
private readonly Container<DrawableHoldNoteTail> tailContainer;
|
||||||
private readonly Container<DrawableHoldNoteTick> tickContainer;
|
private readonly Container<DrawableHoldNoteTick> tickContainer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains the size of the hold note covering the whole head/tail bounds. The size of this container changes as the hold note is being pressed.
|
||||||
|
/// </summary>
|
||||||
|
private readonly Container sizingContainer;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains the contents of the hold note that should be masked as the hold note is being pressed. Follows changes in the size of <see cref="sizingContainer"/>.
|
||||||
|
/// </summary>
|
||||||
|
private readonly Container maskingContainer;
|
||||||
|
|
||||||
private readonly SkinnableDrawable bodyPiece;
|
private readonly SkinnableDrawable bodyPiece;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -44,24 +55,54 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool HasBroken { get; private set; }
|
public bool HasBroken { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the hold note has been released potentially without having caused a break.
|
||||||
|
/// </summary>
|
||||||
|
private double? releaseTime;
|
||||||
|
|
||||||
public DrawableHoldNote(HoldNote hitObject)
|
public DrawableHoldNote(HoldNote hitObject)
|
||||||
: base(hitObject)
|
: base(hitObject)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
|
|
||||||
|
Container maskedContents;
|
||||||
|
|
||||||
AddRangeInternal(new Drawable[]
|
AddRangeInternal(new Drawable[]
|
||||||
{
|
{
|
||||||
|
sizingContainer = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
maskingContainer = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = maskedContents = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Masking = true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
headContainer = new Container<DrawableHoldNoteHead> { RelativeSizeAxes = Axes.Both }
|
||||||
|
}
|
||||||
|
},
|
||||||
bodyPiece = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HoldNoteBody, hitObject.Column), _ => new DefaultBodyPiece
|
bodyPiece = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HoldNoteBody, hitObject.Column), _ => new DefaultBodyPiece
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both
|
RelativeSizeAxes = Axes.Both,
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X
|
RelativeSizeAxes = Axes.X
|
||||||
},
|
},
|
||||||
tickContainer = new Container<DrawableHoldNoteTick> { RelativeSizeAxes = Axes.Both },
|
tickContainer = new Container<DrawableHoldNoteTick> { RelativeSizeAxes = Axes.Both },
|
||||||
headContainer = new Container<DrawableHoldNoteHead> { RelativeSizeAxes = Axes.Both },
|
|
||||||
tailContainer = new Container<DrawableHoldNoteTail> { RelativeSizeAxes = Axes.Both },
|
tailContainer = new Container<DrawableHoldNoteTail> { RelativeSizeAxes = Axes.Both },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
maskedContents.AddRange(new[]
|
||||||
|
{
|
||||||
|
bodyPiece.CreateProxy(),
|
||||||
|
tickContainer.CreateProxy(),
|
||||||
|
tailContainer.CreateProxy(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void AddNestedHitObject(DrawableHitObject hitObject)
|
protected override void AddNestedHitObject(DrawableHitObject hitObject)
|
||||||
@ -127,7 +168,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
{
|
{
|
||||||
base.OnDirectionChanged(e);
|
base.OnDirectionChanged(e);
|
||||||
|
|
||||||
bodyPiece.Anchor = bodyPiece.Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopLeft : Anchor.BottomLeft;
|
if (e.NewValue == ScrollingDirection.Up)
|
||||||
|
{
|
||||||
|
bodyPiece.Anchor = bodyPiece.Origin = Anchor.TopLeft;
|
||||||
|
sizingContainer.Anchor = sizingContainer.Origin = Anchor.BottomLeft;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bodyPiece.Anchor = bodyPiece.Origin = Anchor.BottomLeft;
|
||||||
|
sizingContainer.Anchor = sizingContainer.Origin = Anchor.TopLeft;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PlaySamples()
|
public override void PlaySamples()
|
||||||
@ -145,9 +195,38 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
// Make the body piece not lie under the head note
|
if (Time.Current < releaseTime)
|
||||||
|
releaseTime = null;
|
||||||
|
|
||||||
|
// Pad the full size container so its contents (i.e. the masking container) reach under the tail.
|
||||||
|
// This is required for the tail to not be masked away, since it lies outside the bounds of the hold note.
|
||||||
|
sizingContainer.Padding = new MarginPadding
|
||||||
|
{
|
||||||
|
Top = Direction.Value == ScrollingDirection.Down ? -Tail.Height : 0,
|
||||||
|
Bottom = Direction.Value == ScrollingDirection.Up ? -Tail.Height : 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Pad the masking container to the starting position of the body piece (half-way under the head).
|
||||||
|
// This is required to make the body start getting masked immediately as soon as the note is held.
|
||||||
|
maskingContainer.Padding = new MarginPadding
|
||||||
|
{
|
||||||
|
Top = Direction.Value == ScrollingDirection.Up ? Head.Height / 2 : 0,
|
||||||
|
Bottom = Direction.Value == ScrollingDirection.Down ? Head.Height / 2 : 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Position and resize the body to lie half-way under the head and the tail notes.
|
||||||
bodyPiece.Y = (Direction.Value == ScrollingDirection.Up ? 1 : -1) * Head.Height / 2;
|
bodyPiece.Y = (Direction.Value == ScrollingDirection.Up ? 1 : -1) * Head.Height / 2;
|
||||||
bodyPiece.Height = DrawHeight - Head.Height / 2 + Tail.Height / 2;
|
bodyPiece.Height = DrawHeight - Head.Height / 2 + Tail.Height / 2;
|
||||||
|
|
||||||
|
// As the note is being held, adjust the size of the sizing container. This has two effects:
|
||||||
|
// 1. The contained masking container will mask the body and ticks.
|
||||||
|
// 2. The head note will move along with the new "head position" in the container.
|
||||||
|
if (Head.IsHit && releaseTime == null)
|
||||||
|
{
|
||||||
|
// How far past the hit target this hold note is. Always a positive value.
|
||||||
|
float yOffset = Math.Max(0, Direction.Value == ScrollingDirection.Up ? -Y : Y);
|
||||||
|
sizingContainer.Height = Math.Clamp(1 - yOffset / DrawHeight, 0, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void UpdateStateTransforms(ArmedState state)
|
protected override void UpdateStateTransforms(ArmedState state)
|
||||||
@ -212,6 +291,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
// If the key has been released too early, the user should not receive full score for the release
|
// If the key has been released too early, the user should not receive full score for the release
|
||||||
if (!Tail.IsHit)
|
if (!Tail.IsHit)
|
||||||
HasBroken = true;
|
HasBroken = true;
|
||||||
|
|
||||||
|
releaseTime = Time.Current;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void endHold()
|
private void endHold()
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -17,6 +19,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
|
|
||||||
public void UpdateResult() => base.UpdateResult(true);
|
public void UpdateResult() => base.UpdateResult(true);
|
||||||
|
|
||||||
|
protected override void UpdateStateTransforms(ArmedState state)
|
||||||
|
{
|
||||||
|
// This hitobject should never expire, so this is just a safe maximum.
|
||||||
|
LifetimeEnd = LifetimeStart + 30000;
|
||||||
|
}
|
||||||
|
|
||||||
public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note
|
public override bool OnPressed(ManiaAction action) => false; // Handled by the hold note
|
||||||
|
|
||||||
public override void OnReleased(ManiaAction action)
|
public override void OnReleased(ManiaAction action)
|
||||||
|
@ -120,7 +120,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ArmedState.Hit:
|
case ArmedState.Hit:
|
||||||
this.FadeOut(150, Easing.OutQuint);
|
this.FadeOut();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,8 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
|
||||||
using osu.Game.Rulesets.UI.Scrolling;
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -19,17 +17,12 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
|||||||
public class LegacyColumnBackground : LegacyManiaColumnElement, IKeyBindingHandler<ManiaAction>
|
public class LegacyColumnBackground : LegacyManiaColumnElement, IKeyBindingHandler<ManiaAction>
|
||||||
{
|
{
|
||||||
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||||
private readonly bool isLastColumn;
|
|
||||||
|
|
||||||
private Container borderLineContainer;
|
|
||||||
private Container lightContainer;
|
private Container lightContainer;
|
||||||
private Sprite light;
|
private Sprite light;
|
||||||
|
|
||||||
private float hitPosition;
|
public LegacyColumnBackground()
|
||||||
|
|
||||||
public LegacyColumnBackground(bool isLastColumn)
|
|
||||||
{
|
{
|
||||||
this.isLastColumn = isLastColumn;
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,62 +32,14 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
|||||||
string lightImage = skin.GetManiaSkinConfig<string>(LegacyManiaSkinConfigurationLookups.LightImage)?.Value
|
string lightImage = skin.GetManiaSkinConfig<string>(LegacyManiaSkinConfigurationLookups.LightImage)?.Value
|
||||||
?? "mania-stage-light";
|
?? "mania-stage-light";
|
||||||
|
|
||||||
float leftLineWidth = GetColumnSkinConfig<float>(skin, LegacyManiaSkinConfigurationLookups.LeftLineWidth)
|
|
||||||
?.Value ?? 1;
|
|
||||||
float rightLineWidth = GetColumnSkinConfig<float>(skin, LegacyManiaSkinConfigurationLookups.RightLineWidth)
|
|
||||||
?.Value ?? 1;
|
|
||||||
|
|
||||||
bool hasLeftLine = leftLineWidth > 0;
|
|
||||||
bool hasRightLine = rightLineWidth > 0 && skin.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version)?.Value >= 2.4m
|
|
||||||
|| isLastColumn;
|
|
||||||
|
|
||||||
hitPosition = GetColumnSkinConfig<float>(skin, LegacyManiaSkinConfigurationLookups.HitPosition)?.Value
|
|
||||||
?? Stage.HIT_TARGET_POSITION;
|
|
||||||
|
|
||||||
float lightPosition = GetColumnSkinConfig<float>(skin, LegacyManiaSkinConfigurationLookups.LightPosition)?.Value
|
float lightPosition = GetColumnSkinConfig<float>(skin, LegacyManiaSkinConfigurationLookups.LightPosition)?.Value
|
||||||
?? 0;
|
?? 0;
|
||||||
|
|
||||||
Color4 lineColour = GetColumnSkinConfig<Color4>(skin, LegacyManiaSkinConfigurationLookups.ColumnLineColour)?.Value
|
|
||||||
?? Color4.White;
|
|
||||||
|
|
||||||
Color4 backgroundColour = GetColumnSkinConfig<Color4>(skin, LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour)?.Value
|
|
||||||
?? Color4.Black;
|
|
||||||
|
|
||||||
Color4 lightColour = GetColumnSkinConfig<Color4>(skin, LegacyManiaSkinConfigurationLookups.ColumnLightColour)?.Value
|
Color4 lightColour = GetColumnSkinConfig<Color4>(skin, LegacyManiaSkinConfigurationLookups.ColumnLightColour)?.Value
|
||||||
?? Color4.White;
|
?? Color4.White;
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new[]
|
||||||
{
|
{
|
||||||
new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = backgroundColour
|
|
||||||
},
|
|
||||||
borderLineContainer = new Container
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Children = new[]
|
|
||||||
{
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Y,
|
|
||||||
Width = leftLineWidth,
|
|
||||||
Scale = new Vector2(0.740f, 1),
|
|
||||||
Colour = lineColour,
|
|
||||||
Alpha = hasLeftLine ? 1 : 0
|
|
||||||
},
|
|
||||||
new Box
|
|
||||||
{
|
|
||||||
Anchor = Anchor.TopRight,
|
|
||||||
Origin = Anchor.TopRight,
|
|
||||||
RelativeSizeAxes = Axes.Y,
|
|
||||||
Width = rightLineWidth,
|
|
||||||
Scale = new Vector2(0.740f, 1),
|
|
||||||
Colour = lineColour,
|
|
||||||
Alpha = hasRightLine ? 1 : 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
lightContainer = new Container
|
lightContainer = new Container
|
||||||
{
|
{
|
||||||
Origin = Anchor.BottomCentre,
|
Origin = Anchor.BottomCentre,
|
||||||
@ -123,15 +68,11 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
|||||||
{
|
{
|
||||||
lightContainer.Anchor = Anchor.TopCentre;
|
lightContainer.Anchor = Anchor.TopCentre;
|
||||||
lightContainer.Scale = new Vector2(1, -1);
|
lightContainer.Scale = new Vector2(1, -1);
|
||||||
|
|
||||||
borderLineContainer.Padding = new MarginPadding { Top = hitPosition };
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
lightContainer.Anchor = Anchor.BottomCentre;
|
lightContainer.Anchor = Anchor.BottomCentre;
|
||||||
lightContainer.Scale = Vector2.One;
|
lightContainer.Scale = Vector2.One;
|
||||||
|
|
||||||
borderLineContainer.Padding = new MarginPadding { Bottom = hitPosition };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,11 +20,6 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
|||||||
|
|
||||||
private Container directionContainer;
|
private Container directionContainer;
|
||||||
|
|
||||||
public LegacyHitTarget()
|
|
||||||
{
|
|
||||||
RelativeSizeAxes = Axes.Both;
|
|
||||||
}
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(ISkinSource skin, IScrollingInfo scrollingInfo)
|
private void load(ISkinSource skin, IScrollingInfo scrollingInfo)
|
||||||
{
|
{
|
||||||
|
@ -2,21 +2,31 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
using osu.Game.Rulesets.UI.Scrolling;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Skinning
|
namespace osu.Game.Rulesets.Mania.Skinning
|
||||||
{
|
{
|
||||||
public class LegacyStageBackground : CompositeDrawable
|
public class LegacyStageBackground : CompositeDrawable
|
||||||
{
|
{
|
||||||
|
private readonly StageDefinition stageDefinition;
|
||||||
|
|
||||||
private Drawable leftSprite;
|
private Drawable leftSprite;
|
||||||
private Drawable rightSprite;
|
private Drawable rightSprite;
|
||||||
|
private ColumnFlow<Drawable> columnBackgrounds;
|
||||||
|
|
||||||
public LegacyStageBackground()
|
public LegacyStageBackground(StageDefinition stageDefinition)
|
||||||
{
|
{
|
||||||
|
this.stageDefinition = stageDefinition;
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,8 +54,19 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
|||||||
Origin = Anchor.TopLeft,
|
Origin = Anchor.TopLeft,
|
||||||
X = -0.05f,
|
X = -0.05f,
|
||||||
Texture = skin.GetTexture(rightImage)
|
Texture = skin.GetTexture(rightImage)
|
||||||
|
},
|
||||||
|
columnBackgrounds = new ColumnFlow<Drawable>(stageDefinition)
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Y
|
||||||
|
},
|
||||||
|
new HitTargetInsetContainer
|
||||||
|
{
|
||||||
|
Child = new LegacyHitTarget { RelativeSizeAxes = Axes.Both }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < stageDefinition.Columns; i++)
|
||||||
|
columnBackgrounds.SetContentForColumn(i, new ColumnBackground(i, i == stageDefinition.Columns - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
@ -58,5 +79,104 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
|||||||
if (rightSprite?.Height > 0)
|
if (rightSprite?.Height > 0)
|
||||||
rightSprite.Scale = new Vector2(1, DrawHeight / rightSprite.Height);
|
rightSprite.Scale = new Vector2(1, DrawHeight / rightSprite.Height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class ColumnBackground : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly int columnIndex;
|
||||||
|
private readonly bool isLastColumn;
|
||||||
|
|
||||||
|
public ColumnBackground(int columnIndex, bool isLastColumn)
|
||||||
|
{
|
||||||
|
this.columnIndex = columnIndex;
|
||||||
|
this.isLastColumn = isLastColumn;
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(ISkinSource skin)
|
||||||
|
{
|
||||||
|
float leftLineWidth = skin.GetManiaSkinConfig<float>(LegacyManiaSkinConfigurationLookups.LeftLineWidth, columnIndex)?.Value ?? 1;
|
||||||
|
float rightLineWidth = skin.GetManiaSkinConfig<float>(LegacyManiaSkinConfigurationLookups.RightLineWidth, columnIndex)?.Value ?? 1;
|
||||||
|
|
||||||
|
bool hasLeftLine = leftLineWidth > 0;
|
||||||
|
bool hasRightLine = rightLineWidth > 0 && skin.GetConfig<LegacySkinConfiguration.LegacySetting, decimal>(LegacySkinConfiguration.LegacySetting.Version)?.Value >= 2.4m
|
||||||
|
|| isLastColumn;
|
||||||
|
|
||||||
|
Color4 lineColour = skin.GetManiaSkinConfig<Color4>(LegacyManiaSkinConfigurationLookups.ColumnLineColour, columnIndex)?.Value ?? Color4.White;
|
||||||
|
Color4 backgroundColour = skin.GetManiaSkinConfig<Color4>(LegacyManiaSkinConfigurationLookups.ColumnBackgroundColour, columnIndex)?.Value ?? Color4.Black;
|
||||||
|
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = backgroundColour
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new HitTargetInsetContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Width = leftLineWidth,
|
||||||
|
Scale = new Vector2(0.740f, 1),
|
||||||
|
Colour = lineColour,
|
||||||
|
Alpha = hasLeftLine ? 1 : 0
|
||||||
|
},
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Width = rightLineWidth,
|
||||||
|
Scale = new Vector2(0.740f, 1),
|
||||||
|
Colour = lineColour,
|
||||||
|
Alpha = hasRightLine ? 1 : 0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class HitTargetInsetContainer : Container
|
||||||
|
{
|
||||||
|
private readonly IBindable<ScrollingDirection> direction = new Bindable<ScrollingDirection>();
|
||||||
|
|
||||||
|
protected override Container<Drawable> Content => content;
|
||||||
|
private readonly Container content;
|
||||||
|
|
||||||
|
private float hitPosition;
|
||||||
|
|
||||||
|
public HitTargetInsetContainer()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
InternalChild = content = new Container { RelativeSizeAxes = Axes.Both };
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(ISkinSource skin, IScrollingInfo scrollingInfo)
|
||||||
|
{
|
||||||
|
hitPosition = skin.GetManiaSkinConfig<float>(LegacyManiaSkinConfigurationLookups.HitPosition)?.Value ?? Stage.HIT_TARGET_POSITION;
|
||||||
|
|
||||||
|
direction.BindTo(scrollingInfo.Direction);
|
||||||
|
direction.BindValueChanged(onDirectionChanged, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
||||||
|
{
|
||||||
|
content.Padding = direction.NewValue == ScrollingDirection.Up
|
||||||
|
? new MarginPadding { Top = hitPosition }
|
||||||
|
: new MarginPadding { Bottom = hitPosition };
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Rulesets.Objects.Legacy;
|
using osu.Game.Rulesets.Objects.Legacy;
|
||||||
@ -88,10 +89,12 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
|||||||
switch (maniaComponent.Component)
|
switch (maniaComponent.Component)
|
||||||
{
|
{
|
||||||
case ManiaSkinComponents.ColumnBackground:
|
case ManiaSkinComponents.ColumnBackground:
|
||||||
return new LegacyColumnBackground(maniaComponent.TargetColumn == beatmap.TotalColumns - 1);
|
return new LegacyColumnBackground();
|
||||||
|
|
||||||
case ManiaSkinComponents.HitTarget:
|
case ManiaSkinComponents.HitTarget:
|
||||||
return new LegacyHitTarget();
|
// Legacy skins sandwich the hit target between the column background and the column light.
|
||||||
|
// To preserve this ordering, it's created manually inside LegacyStageBackground.
|
||||||
|
return Drawable.Empty();
|
||||||
|
|
||||||
case ManiaSkinComponents.KeyArea:
|
case ManiaSkinComponents.KeyArea:
|
||||||
return new LegacyKeyArea();
|
return new LegacyKeyArea();
|
||||||
@ -112,7 +115,8 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
|||||||
return new LegacyHitExplosion();
|
return new LegacyHitExplosion();
|
||||||
|
|
||||||
case ManiaSkinComponents.StageBackground:
|
case ManiaSkinComponents.StageBackground:
|
||||||
return new LegacyStageBackground();
|
Debug.Assert(maniaComponent.StageDefinition != null);
|
||||||
|
return new LegacyStageBackground(maniaComponent.StageDefinition.Value);
|
||||||
|
|
||||||
case ManiaSkinComponents.StageForeground:
|
case ManiaSkinComponents.StageForeground:
|
||||||
return new LegacyStageForeground();
|
return new LegacyStageForeground();
|
||||||
|
@ -68,8 +68,6 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
TopLevelContainer.Add(HitObjectArea.Explosions.CreateProxy());
|
TopLevelContainer.Add(HitObjectArea.Explosions.CreateProxy());
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Axes RelativeSizeAxes => Axes.Y;
|
|
||||||
|
|
||||||
public ColumnType ColumnType { get; set; }
|
public ColumnType ColumnType { get; set; }
|
||||||
|
|
||||||
public bool IsSpecial => ColumnType == ColumnType.Special;
|
public bool IsSpecial => ColumnType == ColumnType.Special;
|
||||||
|
105
osu.Game.Rulesets.Mania/UI/ColumnFlow.cs
Normal file
105
osu.Game.Rulesets.Mania/UI/ColumnFlow.cs
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mania.Skinning;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.UI
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A <see cref="Drawable"/> which flows its contents according to the <see cref="Column"/>s in a <see cref="Stage"/>.
|
||||||
|
/// Content can be added to individual columns via <see cref="SetContentForColumn"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TContent">The type of content in each column.</typeparam>
|
||||||
|
public class ColumnFlow<TContent> : CompositeDrawable
|
||||||
|
where TContent : Drawable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// All contents added to this <see cref="ColumnFlow{TContent}"/>.
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyList<TContent> Content => columns.Children.Select(c => c.Count == 0 ? null : (TContent)c.Child).ToList();
|
||||||
|
|
||||||
|
private readonly FillFlowContainer<Container> columns;
|
||||||
|
private readonly StageDefinition stageDefinition;
|
||||||
|
|
||||||
|
public ColumnFlow(StageDefinition stageDefinition)
|
||||||
|
{
|
||||||
|
this.stageDefinition = stageDefinition;
|
||||||
|
|
||||||
|
AutoSizeAxes = Axes.X;
|
||||||
|
|
||||||
|
InternalChild = columns = new FillFlowContainer<Container>
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < stageDefinition.Columns; i++)
|
||||||
|
columns.Add(new Container { RelativeSizeAxes = Axes.Y });
|
||||||
|
}
|
||||||
|
|
||||||
|
private ISkinSource currentSkin;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(ISkinSource skin)
|
||||||
|
{
|
||||||
|
currentSkin = skin;
|
||||||
|
|
||||||
|
skin.SourceChanged += onSkinChanged;
|
||||||
|
onSkinChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onSkinChanged()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < stageDefinition.Columns; i++)
|
||||||
|
{
|
||||||
|
if (i > 0)
|
||||||
|
{
|
||||||
|
float spacing = currentSkin.GetConfig<ManiaSkinConfigurationLookup, float>(
|
||||||
|
new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.ColumnSpacing, i - 1))
|
||||||
|
?.Value ?? Stage.COLUMN_SPACING;
|
||||||
|
|
||||||
|
columns[i].Margin = new MarginPadding { Left = spacing };
|
||||||
|
}
|
||||||
|
|
||||||
|
float? width = currentSkin.GetConfig<ManiaSkinConfigurationLookup, float>(
|
||||||
|
new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.ColumnWidth, i))
|
||||||
|
?.Value;
|
||||||
|
|
||||||
|
if (width == null)
|
||||||
|
// only used by default skin (legacy skins get defaults set in LegacyManiaSkinConfiguration)
|
||||||
|
columns[i].Width = stageDefinition.IsSpecialColumn(i) ? Column.SPECIAL_COLUMN_WIDTH : Column.COLUMN_WIDTH;
|
||||||
|
else
|
||||||
|
columns[i].Width = width.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the content of one of the columns of this <see cref="ColumnFlow{TContent}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="column">The index of the column to set the content of.</param>
|
||||||
|
/// <param name="content">The content.</param>
|
||||||
|
public void SetContentForColumn(int column, TContent content) => columns[column].Child = content;
|
||||||
|
|
||||||
|
public new MarginPadding Padding
|
||||||
|
{
|
||||||
|
get => base.Padding;
|
||||||
|
set => base.Padding = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
|
if (currentSkin != null)
|
||||||
|
currentSkin.SourceChanged -= onSkinChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Pooling;
|
using osu.Framework.Graphics.Pooling;
|
||||||
@ -11,7 +10,6 @@ using osu.Game.Rulesets.Judgements;
|
|||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Mania.Skinning;
|
|
||||||
using osu.Game.Rulesets.Mania.UI.Components;
|
using osu.Game.Rulesets.Mania.UI.Components;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
@ -31,14 +29,13 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
|
|
||||||
public const float HIT_TARGET_POSITION = 110;
|
public const float HIT_TARGET_POSITION = 110;
|
||||||
|
|
||||||
public IReadOnlyList<Column> Columns => columnFlow.Children;
|
public IReadOnlyList<Column> Columns => columnFlow.Content;
|
||||||
private readonly FillFlowContainer<Column> columnFlow;
|
private readonly ColumnFlow<Column> columnFlow;
|
||||||
|
|
||||||
private readonly JudgementContainer<DrawableManiaJudgement> judgements;
|
private readonly JudgementContainer<DrawableManiaJudgement> judgements;
|
||||||
private readonly DrawablePool<DrawableManiaJudgement> judgementPool;
|
private readonly DrawablePool<DrawableManiaJudgement> judgementPool;
|
||||||
|
|
||||||
private readonly Drawable barLineContainer;
|
private readonly Drawable barLineContainer;
|
||||||
private readonly Container topLevelContainer;
|
|
||||||
|
|
||||||
private readonly Dictionary<ColumnType, Color4> columnColours = new Dictionary<ColumnType, Color4>
|
private readonly Dictionary<ColumnType, Color4> columnColours = new Dictionary<ColumnType, Color4>
|
||||||
{
|
{
|
||||||
@ -62,6 +59,8 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
RelativeSizeAxes = Axes.Y;
|
RelativeSizeAxes = Axes.Y;
|
||||||
AutoSizeAxes = Axes.X;
|
AutoSizeAxes = Axes.X;
|
||||||
|
|
||||||
|
Container topLevelContainer;
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
judgementPool = new DrawablePool<DrawableManiaJudgement>(2),
|
judgementPool = new DrawablePool<DrawableManiaJudgement>(2),
|
||||||
@ -73,16 +72,13 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
AutoSizeAxes = Axes.X,
|
AutoSizeAxes = Axes.X,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground), _ => new DefaultStageBackground())
|
new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageBackground, stageDefinition: definition), _ => new DefaultStageBackground())
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both
|
RelativeSizeAxes = Axes.Both
|
||||||
},
|
},
|
||||||
columnFlow = new FillFlowContainer<Column>
|
columnFlow = new ColumnFlow<Column>(definition)
|
||||||
{
|
{
|
||||||
Name = "Columns",
|
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
AutoSizeAxes = Axes.X,
|
|
||||||
Direction = FillDirection.Horizontal,
|
|
||||||
Padding = new MarginPadding { Left = COLUMN_SPACING, Right = COLUMN_SPACING },
|
Padding = new MarginPadding { Left = COLUMN_SPACING, Right = COLUMN_SPACING },
|
||||||
},
|
},
|
||||||
new Container
|
new Container
|
||||||
@ -102,7 +98,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground), _ => null)
|
new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.StageForeground, stageDefinition: definition), _ => null)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both
|
RelativeSizeAxes = Axes.Both
|
||||||
},
|
},
|
||||||
@ -121,60 +117,22 @@ namespace osu.Game.Rulesets.Mania.UI
|
|||||||
for (int i = 0; i < definition.Columns; i++)
|
for (int i = 0; i < definition.Columns; i++)
|
||||||
{
|
{
|
||||||
var columnType = definition.GetTypeOfColumn(i);
|
var columnType = definition.GetTypeOfColumn(i);
|
||||||
|
|
||||||
var column = new Column(firstColumnIndex + i)
|
var column = new Column(firstColumnIndex + i)
|
||||||
{
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Width = 1,
|
||||||
ColumnType = columnType,
|
ColumnType = columnType,
|
||||||
AccentColour = columnColours[columnType],
|
AccentColour = columnColours[columnType],
|
||||||
Action = { Value = columnType == ColumnType.Special ? specialColumnStartAction++ : normalColumnStartAction++ }
|
Action = { Value = columnType == ColumnType.Special ? specialColumnStartAction++ : normalColumnStartAction++ }
|
||||||
};
|
};
|
||||||
|
|
||||||
AddColumn(column);
|
topLevelContainer.Add(column.TopLevelContainer.CreateProxy());
|
||||||
|
columnFlow.SetContentForColumn(i, column);
|
||||||
|
AddNested(column);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ISkin currentSkin;
|
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(ISkinSource skin)
|
|
||||||
{
|
|
||||||
currentSkin = skin;
|
|
||||||
skin.SourceChanged += onSkinChanged;
|
|
||||||
|
|
||||||
onSkinChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onSkinChanged()
|
|
||||||
{
|
|
||||||
foreach (var col in columnFlow)
|
|
||||||
{
|
|
||||||
if (col.Index > 0)
|
|
||||||
{
|
|
||||||
float spacing = currentSkin.GetConfig<ManiaSkinConfigurationLookup, float>(
|
|
||||||
new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.ColumnSpacing, col.Index - 1))
|
|
||||||
?.Value ?? COLUMN_SPACING;
|
|
||||||
|
|
||||||
col.Margin = new MarginPadding { Left = spacing };
|
|
||||||
}
|
|
||||||
|
|
||||||
float? width = currentSkin.GetConfig<ManiaSkinConfigurationLookup, float>(
|
|
||||||
new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.ColumnWidth, col.Index))
|
|
||||||
?.Value;
|
|
||||||
|
|
||||||
if (width == null)
|
|
||||||
// only used by default skin (legacy skins get defaults set in LegacyManiaSkinConfiguration)
|
|
||||||
col.Width = col.IsSpecial ? Column.SPECIAL_COLUMN_WIDTH : Column.COLUMN_WIDTH;
|
|
||||||
else
|
|
||||||
col.Width = width.Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddColumn(Column c)
|
|
||||||
{
|
|
||||||
topLevelContainer.Add(c.TopLevelContainer.CreateProxy());
|
|
||||||
columnFlow.Add(c);
|
|
||||||
AddNested(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Add(DrawableHitObject h)
|
public override void Add(DrawableHitObject h)
|
||||||
{
|
{
|
||||||
var maniaObject = (ManiaHitObject)h.HitObject;
|
var maniaObject = (ManiaHitObject)h.HitObject;
|
||||||
|
@ -169,17 +169,17 @@ namespace osu.Game.Tests.Editing
|
|||||||
[Test]
|
[Test]
|
||||||
public void GetSnappedDistanceFromDistance()
|
public void GetSnappedDistanceFromDistance()
|
||||||
{
|
{
|
||||||
assertSnappedDistance(50, 100);
|
assertSnappedDistance(50, 0);
|
||||||
assertSnappedDistance(100, 100);
|
assertSnappedDistance(100, 100);
|
||||||
assertSnappedDistance(150, 200);
|
assertSnappedDistance(150, 100);
|
||||||
assertSnappedDistance(200, 200);
|
assertSnappedDistance(200, 200);
|
||||||
assertSnappedDistance(250, 300);
|
assertSnappedDistance(250, 200);
|
||||||
|
|
||||||
AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2);
|
AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 2);
|
||||||
|
|
||||||
assertSnappedDistance(50, 0);
|
assertSnappedDistance(50, 0);
|
||||||
assertSnappedDistance(100, 200);
|
assertSnappedDistance(100, 0);
|
||||||
assertSnappedDistance(150, 200);
|
assertSnappedDistance(150, 0);
|
||||||
assertSnappedDistance(200, 200);
|
assertSnappedDistance(200, 200);
|
||||||
assertSnappedDistance(250, 200);
|
assertSnappedDistance(250, 200);
|
||||||
|
|
||||||
@ -190,8 +190,8 @@ namespace osu.Game.Tests.Editing
|
|||||||
});
|
});
|
||||||
|
|
||||||
assertSnappedDistance(50, 0);
|
assertSnappedDistance(50, 0);
|
||||||
assertSnappedDistance(100, 200);
|
assertSnappedDistance(100, 0);
|
||||||
assertSnappedDistance(150, 200);
|
assertSnappedDistance(150, 0);
|
||||||
assertSnappedDistance(200, 200);
|
assertSnappedDistance(200, 200);
|
||||||
assertSnappedDistance(250, 200);
|
assertSnappedDistance(250, 200);
|
||||||
assertSnappedDistance(400, 400);
|
assertSnappedDistance(400, 400);
|
||||||
|
@ -7,8 +7,10 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.BeatmapSet;
|
using osu.Game.Overlays.BeatmapSet;
|
||||||
using osu.Game.Screens.Select.Details;
|
using osu.Game.Screens.Select.Details;
|
||||||
@ -72,6 +74,32 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestOnlyFailMetrics()
|
||||||
|
{
|
||||||
|
AddStep("set beatmap", () => successRate.Beatmap = new BeatmapInfo
|
||||||
|
{
|
||||||
|
Metrics = new BeatmapMetrics
|
||||||
|
{
|
||||||
|
Fails = Enumerable.Range(1, 100).ToArray(),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
AddAssert("graph max values correct",
|
||||||
|
() => successRate.ChildrenOfType<BarGraph>().All(graph => graph.MaxValue == 100));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestEmptyMetrics()
|
||||||
|
{
|
||||||
|
AddStep("set beatmap", () => successRate.Beatmap = new BeatmapInfo
|
||||||
|
{
|
||||||
|
Metrics = new BeatmapMetrics()
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("graph max values correct",
|
||||||
|
() => successRate.ChildrenOfType<BarGraph>().All(graph => graph.MaxValue == 0));
|
||||||
|
}
|
||||||
|
|
||||||
private class GraphExposingSuccessRate : SuccessRate
|
private class GraphExposingSuccessRate : SuccessRate
|
||||||
{
|
{
|
||||||
public new FailRetryGraph Graph => base.Graph;
|
public new FailRetryGraph Graph => base.Graph;
|
||||||
|
68
osu.Game.Tests/Visual/Ranking/TestSceneSimpleStatisticRow.cs
Normal file
68
osu.Game.Tests/Visual/Ranking/TestSceneSimpleStatisticRow.cs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
using Humanizer;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Screens.Ranking.Statistics;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Ranking
|
||||||
|
{
|
||||||
|
public class TestSceneSimpleStatisticRow : OsuTestScene
|
||||||
|
{
|
||||||
|
private Container container;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void SetUp() => Schedule(() =>
|
||||||
|
{
|
||||||
|
Child = new Container
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Width = 700,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Color4Extensions.FromHex("#333"),
|
||||||
|
},
|
||||||
|
container = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Padding = new MarginPadding(20)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestEmpty()
|
||||||
|
{
|
||||||
|
AddStep("create with no items",
|
||||||
|
() => container.Add(new SimpleStatisticRow(2, Enumerable.Empty<SimpleStatisticItem>())));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestManyItems(
|
||||||
|
[Values(1, 2, 3, 4, 12)] int itemCount,
|
||||||
|
[Values(1, 3, 5)] int columnCount)
|
||||||
|
{
|
||||||
|
AddStep($"create with {"item".ToQuantity(itemCount)}", () =>
|
||||||
|
{
|
||||||
|
var items = Enumerable.Range(1, itemCount)
|
||||||
|
.Select(i => new SimpleStatisticItem<int>($"Statistic #{i}")
|
||||||
|
{
|
||||||
|
Value = RNG.Next(100)
|
||||||
|
});
|
||||||
|
|
||||||
|
container.Add(new SimpleStatisticRow(columnCount, items));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -25,7 +25,7 @@ using osu.Game.Screens.Edit.Components.RadioButtons;
|
|||||||
using osu.Game.Screens.Edit.Compose;
|
using osu.Game.Screens.Edit.Compose;
|
||||||
using osu.Game.Screens.Edit.Compose.Components;
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using Key = osuTK.Input.Key;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Edit
|
namespace osu.Game.Rulesets.Edit
|
||||||
{
|
{
|
||||||
@ -293,7 +293,16 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
|
|
||||||
public override float GetSnappedDistanceFromDistance(double referenceTime, float distance)
|
public override float GetSnappedDistanceFromDistance(double referenceTime, float distance)
|
||||||
{
|
{
|
||||||
var snappedEndTime = BeatSnapProvider.SnapTime(referenceTime + DistanceToDuration(referenceTime, distance), referenceTime);
|
double actualDuration = referenceTime + DistanceToDuration(referenceTime, distance);
|
||||||
|
|
||||||
|
double snappedEndTime = BeatSnapProvider.SnapTime(actualDuration, referenceTime);
|
||||||
|
|
||||||
|
double beatLength = BeatSnapProvider.GetBeatLengthAtTime(referenceTime);
|
||||||
|
|
||||||
|
// we don't want to exceed the actual duration and snap to a point in the future.
|
||||||
|
// as we are snapping to beat length via SnapTime (which will round-to-nearest), check for snapping in the forward direction and reverse it.
|
||||||
|
if (snappedEndTime > actualDuration + 1)
|
||||||
|
snappedEndTime -= beatLength;
|
||||||
|
|
||||||
return DurationToDistance(referenceTime, snappedEndTime - referenceTime);
|
return DurationToDistance(referenceTime, snappedEndTime - referenceTime);
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,7 @@ namespace osu.Game.Rulesets.Edit
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts an unsnapped distance to a snapped distance.
|
/// Converts an unsnapped distance to a snapped distance.
|
||||||
|
/// The returned distance will always be floored (as to never exceed the provided <paramref name="distance"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="referenceTime">The time of the timing point which <paramref name="distance"/> resides in.</param>
|
/// <param name="referenceTime">The time of the timing point which <paramref name="distance"/> resides in.</param>
|
||||||
/// <param name="distance">The distance to convert.</param>
|
/// <param name="distance">The distance to convert.</param>
|
||||||
|
80
osu.Game/Screens/Ranking/Statistics/SimpleStatisticItem.cs
Normal file
80
osu.Game/Screens/Ranking/Statistics/SimpleStatisticItem.cs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Ranking.Statistics
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a simple statistic item (one that only needs textual display).
|
||||||
|
/// Richer visualisations should be done with <see cref="StatisticItem"/>s.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class SimpleStatisticItem : Container
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The text to display as the statistic's value.
|
||||||
|
/// </summary>
|
||||||
|
protected string Value
|
||||||
|
{
|
||||||
|
set => this.value.Text = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly OsuSpriteText value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new simple statistic item.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name">The name of the statistic.</param>
|
||||||
|
protected SimpleStatisticItem(string name)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
|
||||||
|
AddRange(new[]
|
||||||
|
{
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Text = Name,
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft
|
||||||
|
},
|
||||||
|
value = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.CentreRight,
|
||||||
|
Origin = Anchor.CentreRight,
|
||||||
|
Font = OsuFont.Torus.With(weight: FontWeight.Bold)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Strongly-typed generic specialisation for <see cref="SimpleStatisticItem"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class SimpleStatisticItem<TValue> : SimpleStatisticItem
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The statistic's value to be displayed.
|
||||||
|
/// </summary>
|
||||||
|
public new TValue Value
|
||||||
|
{
|
||||||
|
set => base.Value = DisplayValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to convert <see cref="Value"/> to a text representation.
|
||||||
|
/// Defaults to using <see cref="object.ToString"/>.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual string DisplayValue(TValue value) => value.ToString();
|
||||||
|
|
||||||
|
public SimpleStatisticItem(string name)
|
||||||
|
: base(name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
122
osu.Game/Screens/Ranking/Statistics/SimpleStatisticRow.cs
Normal file
122
osu.Game/Screens/Ranking/Statistics/SimpleStatisticRow.cs
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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 osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Ranking.Statistics
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a statistic row with simple statistics (ones that only need textual display).
|
||||||
|
/// Richer visualisations should be done with <see cref="StatisticRow"/>s and <see cref="StatisticItem"/>s.
|
||||||
|
/// </summary>
|
||||||
|
public class SimpleStatisticRow : CompositeDrawable
|
||||||
|
{
|
||||||
|
private readonly SimpleStatisticItem[] items;
|
||||||
|
private readonly int columnCount;
|
||||||
|
|
||||||
|
private FillFlowContainer[] columns;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a statistic row for the supplied <see cref="SimpleStatisticItem"/>s.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="columnCount">The number of columns to layout the <paramref name="items"/> into.</param>
|
||||||
|
/// <param name="items">The <see cref="SimpleStatisticItem"/>s to display in this row.</param>
|
||||||
|
public SimpleStatisticRow(int columnCount, IEnumerable<SimpleStatisticItem> items)
|
||||||
|
{
|
||||||
|
if (columnCount < 1)
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(columnCount));
|
||||||
|
|
||||||
|
this.columnCount = columnCount;
|
||||||
|
this.items = items.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
columns = new FillFlowContainer[columnCount];
|
||||||
|
|
||||||
|
RelativeSizeAxes = Axes.X;
|
||||||
|
AutoSizeAxes = Axes.Y;
|
||||||
|
|
||||||
|
InternalChild = new GridContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
RowDimensions = new[]
|
||||||
|
{
|
||||||
|
new Dimension(GridSizeMode.AutoSize)
|
||||||
|
},
|
||||||
|
ColumnDimensions = createColumnDimensions().ToArray(),
|
||||||
|
Content = new[] { createColumns().ToArray() }
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < items.Length; ++i)
|
||||||
|
columns[i % columnCount].Add(items[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<Dimension> createColumnDimensions()
|
||||||
|
{
|
||||||
|
for (int column = 0; column < columnCount; ++column)
|
||||||
|
{
|
||||||
|
if (column > 0)
|
||||||
|
yield return new Dimension(GridSizeMode.Absolute, 30);
|
||||||
|
|
||||||
|
yield return new Dimension();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<Drawable> createColumns()
|
||||||
|
{
|
||||||
|
for (int column = 0; column < columnCount; ++column)
|
||||||
|
{
|
||||||
|
if (column > 0)
|
||||||
|
{
|
||||||
|
yield return new Spacer
|
||||||
|
{
|
||||||
|
Alpha = items.Length > column ? 1 : 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return columns[column] = createColumn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private FillFlowContainer createColumn() => new FillFlowContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Vertical
|
||||||
|
};
|
||||||
|
|
||||||
|
private class Spacer : CompositeDrawable
|
||||||
|
{
|
||||||
|
public Spacer()
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
Padding = new MarginPadding { Vertical = 4 };
|
||||||
|
|
||||||
|
InternalChild = new CircularContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Width = 3,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
CornerRadius = 2,
|
||||||
|
Masking = true,
|
||||||
|
Child = new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = Color4Extensions.FromHex("#222")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -236,7 +236,7 @@ namespace osu.Game.Screens.Select
|
|||||||
private void updateMetrics()
|
private void updateMetrics()
|
||||||
{
|
{
|
||||||
var hasRatings = beatmap?.BeatmapSet?.Metrics?.Ratings?.Any() ?? false;
|
var hasRatings = beatmap?.BeatmapSet?.Metrics?.Ratings?.Any() ?? false;
|
||||||
var hasRetriesFails = (beatmap?.Metrics?.Retries?.Any() ?? false) && (beatmap?.Metrics.Fails?.Any() ?? false);
|
var hasRetriesFails = (beatmap?.Metrics?.Retries?.Any() ?? false) || (beatmap?.Metrics?.Fails?.Any() ?? false);
|
||||||
|
|
||||||
if (hasRatings)
|
if (hasRatings)
|
||||||
{
|
{
|
||||||
|
@ -29,16 +29,30 @@ namespace osu.Game.Screens.Select.Details
|
|||||||
|
|
||||||
var retries = Metrics?.Retries ?? Array.Empty<int>();
|
var retries = Metrics?.Retries ?? Array.Empty<int>();
|
||||||
var fails = Metrics?.Fails ?? Array.Empty<int>();
|
var fails = Metrics?.Fails ?? Array.Empty<int>();
|
||||||
|
var retriesAndFails = sumRetriesAndFails(retries, fails);
|
||||||
|
|
||||||
float maxValue = fails.Any() ? fails.Zip(retries, (fail, retry) => fail + retry).Max() : 0;
|
float maxValue = retriesAndFails.Any() ? retriesAndFails.Max() : 0;
|
||||||
failGraph.MaxValue = maxValue;
|
failGraph.MaxValue = maxValue;
|
||||||
retryGraph.MaxValue = maxValue;
|
retryGraph.MaxValue = maxValue;
|
||||||
|
|
||||||
failGraph.Values = fails.Select(f => (float)f);
|
failGraph.Values = fails.Select(v => (float)v);
|
||||||
retryGraph.Values = retries.Zip(fails, (retry, fail) => retry + Math.Clamp(fail, 0, maxValue));
|
retryGraph.Values = retriesAndFails.Select(v => (float)v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int[] sumRetriesAndFails(int[] retries, int[] fails)
|
||||||
|
{
|
||||||
|
var result = new int[Math.Max(retries.Length, fails.Length)];
|
||||||
|
|
||||||
|
for (int i = 0; i < retries.Length; ++i)
|
||||||
|
result[i] = retries[i];
|
||||||
|
|
||||||
|
for (int i = 0; i < fails.Length; ++i)
|
||||||
|
result[i] += fails[i];
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public FailRetryGraph()
|
public FailRetryGraph()
|
||||||
{
|
{
|
||||||
Children = new[]
|
Children = new[]
|
||||||
|
Loading…
Reference in New Issue
Block a user