mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 13:37:25 +08:00
Merge branch 'master' into hold-note-lighting
This commit is contained in:
commit
da34544fdc
@ -326,6 +326,16 @@ namespace osu.Game.Rulesets.Mania
|
||||
Height = 250
|
||||
}),
|
||||
}
|
||||
},
|
||||
new StatisticRow
|
||||
{
|
||||
Columns = new[]
|
||||
{
|
||||
new StatisticItem(string.Empty, new SimpleStatisticTable(3, new SimpleStatisticItem[]
|
||||
{
|
||||
new UnstableRate(score.HitEvents)
|
||||
}))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -65,6 +65,9 @@ namespace osu.Game.Rulesets.Mania.Skinning
|
||||
|
||||
direction.BindTo(scrollingInfo.Direction);
|
||||
direction.BindValueChanged(onDirectionChanged, true);
|
||||
|
||||
if (GetColumnSkinConfig<bool>(skin, LegacyManiaSkinConfigurationLookups.KeysUnderNotes)?.Value ?? false)
|
||||
Column.UnderlayElements.Add(CreateProxy());
|
||||
}
|
||||
|
||||
private void onDirectionChanged(ValueChangedEvent<ScrollingDirection> direction)
|
||||
|
@ -79,7 +79,6 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
columnFlow = new ColumnFlow<Column>(definition)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Padding = new MarginPadding { Left = COLUMN_SPACING, Right = COLUMN_SPACING },
|
||||
},
|
||||
new Container
|
||||
{
|
||||
|
@ -193,13 +193,18 @@ namespace osu.Game.Rulesets.Osu
|
||||
|
||||
public override IRulesetConfigManager CreateConfig(SettingsStore settings) => new OsuRulesetConfigManager(settings, RulesetInfo);
|
||||
|
||||
public override StatisticRow[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) => new[]
|
||||
public override StatisticRow[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap)
|
||||
{
|
||||
var timedHitEvents = score.HitEvents.Where(e => e.HitObject is HitCircle && !(e.HitObject is SliderTailCircle)).ToList();
|
||||
|
||||
return new[]
|
||||
{
|
||||
new StatisticRow
|
||||
{
|
||||
Columns = new[]
|
||||
{
|
||||
new StatisticItem("Timing Distribution", new HitEventTimingDistributionGraph(score.HitEvents.Where(e => e.HitObject is HitCircle && !(e.HitObject is SliderTailCircle)).ToList())
|
||||
new StatisticItem("Timing Distribution",
|
||||
new HitEventTimingDistributionGraph(timedHitEvents)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 250
|
||||
@ -216,7 +221,18 @@ namespace osu.Game.Rulesets.Osu
|
||||
Height = 250
|
||||
}),
|
||||
}
|
||||
},
|
||||
new StatisticRow
|
||||
{
|
||||
Columns = new[]
|
||||
{
|
||||
new StatisticItem(string.Empty, new SimpleStatisticTable(3, new SimpleStatisticItem[]
|
||||
{
|
||||
new UnstableRate(timedHitEvents)
|
||||
}))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -161,19 +161,34 @@ namespace osu.Game.Rulesets.Taiko
|
||||
|
||||
public override IConvertibleReplayFrame CreateConvertibleReplayFrame() => new TaikoReplayFrame();
|
||||
|
||||
public override StatisticRow[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) => new[]
|
||||
public override StatisticRow[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap)
|
||||
{
|
||||
var timedHitEvents = score.HitEvents.Where(e => e.HitObject is Hit).ToList();
|
||||
|
||||
return new[]
|
||||
{
|
||||
new StatisticRow
|
||||
{
|
||||
Columns = new[]
|
||||
{
|
||||
new StatisticItem("Timing Distribution", new HitEventTimingDistributionGraph(score.HitEvents.Where(e => e.HitObject is Hit).ToList())
|
||||
new StatisticItem("Timing Distribution", new HitEventTimingDistributionGraph(timedHitEvents)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 250
|
||||
}),
|
||||
}
|
||||
},
|
||||
new StatisticRow
|
||||
{
|
||||
Columns = new[]
|
||||
{
|
||||
new StatisticItem(string.Empty, new SimpleStatisticTable(3, new SimpleStatisticItem[]
|
||||
{
|
||||
new UnstableRate(timedHitEvents)
|
||||
}))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
70
osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs
Normal file
70
osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs
Normal file
@ -0,0 +1,70 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Catch;
|
||||
using osu.Game.Rulesets.Mania;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Rulesets.Taiko;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Scoring.Legacy;
|
||||
using osu.Game.Tests.Resources;
|
||||
|
||||
namespace osu.Game.Tests.Beatmaps.Formats
|
||||
{
|
||||
[TestFixture]
|
||||
public class LegacyScoreDecoderTest
|
||||
{
|
||||
[Test]
|
||||
public void TestDecodeManiaReplay()
|
||||
{
|
||||
var decoder = new TestLegacyScoreDecoder();
|
||||
|
||||
using (var resourceStream = TestResources.OpenResource("Replays/mania-replay.osr"))
|
||||
{
|
||||
var score = decoder.Parse(resourceStream);
|
||||
|
||||
Assert.AreEqual(3, score.ScoreInfo.Ruleset.ID);
|
||||
|
||||
Assert.AreEqual(2, score.ScoreInfo.Statistics[HitResult.Great]);
|
||||
Assert.AreEqual(1, score.ScoreInfo.Statistics[HitResult.Good]);
|
||||
|
||||
Assert.AreEqual(829_931, score.ScoreInfo.TotalScore);
|
||||
Assert.AreEqual(3, score.ScoreInfo.MaxCombo);
|
||||
Assert.IsTrue(Precision.AlmostEquals(0.8889, score.ScoreInfo.Accuracy, 0.0001));
|
||||
Assert.AreEqual(ScoreRank.B, score.ScoreInfo.Rank);
|
||||
|
||||
Assert.That(score.Replay.Frames, Is.Not.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
private class TestLegacyScoreDecoder : LegacyScoreDecoder
|
||||
{
|
||||
private static readonly Dictionary<int, Ruleset> rulesets = new Ruleset[]
|
||||
{
|
||||
new OsuRuleset(),
|
||||
new TaikoRuleset(),
|
||||
new CatchRuleset(),
|
||||
new ManiaRuleset()
|
||||
}.ToDictionary(ruleset => ((ILegacyRuleset)ruleset).LegacyID);
|
||||
|
||||
protected override Ruleset GetRuleset(int rulesetId) => rulesets[rulesetId];
|
||||
|
||||
protected override WorkingBeatmap GetBeatmap(string md5Hash) => new TestWorkingBeatmap(new Beatmap
|
||||
{
|
||||
BeatmapInfo = new BeatmapInfo
|
||||
{
|
||||
MD5Hash = md5Hash,
|
||||
Ruleset = new OsuRuleset().RulesetInfo,
|
||||
BaseDifficulty = new BeatmapDifficulty()
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
BIN
osu.Game.Tests/Resources/Replays/mania-replay.osr
Normal file
BIN
osu.Game.Tests/Resources/Replays/mania-replay.osr
Normal file
Binary file not shown.
@ -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 TestSceneSimpleStatisticTable : 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 SimpleStatisticTable(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 SimpleStatisticTable(columnCount, items));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -119,7 +119,7 @@ namespace osu.Game.Online.Chat
|
||||
case "http":
|
||||
case "https":
|
||||
// length > 3 since all these links need another argument to work
|
||||
if (args.Length > 3 && (args[1] == "osu.ppy.sh" || args[1] == "new.ppy.sh"))
|
||||
if (args.Length > 3 && args[1] == "osu.ppy.sh")
|
||||
{
|
||||
switch (args[2])
|
||||
{
|
||||
|
@ -13,7 +13,6 @@ using osu.Game.Replays.Legacy;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Users;
|
||||
using SharpCompress.Compressors.LZMA;
|
||||
|
||||
@ -123,12 +122,12 @@ namespace osu.Game.Scoring.Legacy
|
||||
|
||||
protected void CalculateAccuracy(ScoreInfo score)
|
||||
{
|
||||
score.Statistics.TryGetValue(HitResult.Miss, out int countMiss);
|
||||
score.Statistics.TryGetValue(HitResult.Meh, out int count50);
|
||||
score.Statistics.TryGetValue(HitResult.Good, out int count100);
|
||||
score.Statistics.TryGetValue(HitResult.Great, out int count300);
|
||||
score.Statistics.TryGetValue(HitResult.Perfect, out int countGeki);
|
||||
score.Statistics.TryGetValue(HitResult.Ok, out int countKatu);
|
||||
int countMiss = score.GetCountMiss() ?? 0;
|
||||
int count50 = score.GetCount50() ?? 0;
|
||||
int count100 = score.GetCount100() ?? 0;
|
||||
int count300 = score.GetCount300() ?? 0;
|
||||
int countGeki = score.GetCountGeki() ?? 0;
|
||||
int countKatu = score.GetCountKatu() ?? 0;
|
||||
|
||||
switch (score.Ruleset.ID)
|
||||
{
|
||||
@ -241,12 +240,15 @@ namespace osu.Game.Scoring.Legacy
|
||||
}
|
||||
|
||||
var diff = Parsing.ParseFloat(split[0]);
|
||||
var mouseX = Parsing.ParseFloat(split[1], Parsing.MAX_COORDINATE_VALUE);
|
||||
var mouseY = Parsing.ParseFloat(split[2], Parsing.MAX_COORDINATE_VALUE);
|
||||
|
||||
lastTime += diff;
|
||||
|
||||
if (i == 0 && diff == 0)
|
||||
// osu-stable adds a zero-time frame before potentially valid negative user frames.
|
||||
// we need to ignore this.
|
||||
if (i < 2 && mouseX == 256 && mouseY == -500)
|
||||
// at the start of the replay, stable places two replay frames, at time 0 and SkipBoundary - 1, respectively.
|
||||
// both frames use a position of (256, -500).
|
||||
// ignore these frames as they serve no real purpose (and can even mislead ruleset-specific handlers - see mania)
|
||||
continue;
|
||||
|
||||
// Todo: At some point we probably want to rewind and play back the negative-time frames
|
||||
@ -255,8 +257,8 @@ namespace osu.Game.Scoring.Legacy
|
||||
continue;
|
||||
|
||||
currentFrame = convertFrame(new LegacyReplayFrame(lastTime,
|
||||
Parsing.ParseFloat(split[1], Parsing.MAX_COORDINATE_VALUE),
|
||||
Parsing.ParseFloat(split[2], Parsing.MAX_COORDINATE_VALUE),
|
||||
mouseX,
|
||||
mouseY,
|
||||
(ReplayButtonState)Parsing.ParseInt(split[3])), currentFrame);
|
||||
|
||||
replay.Frames.Add(currentFrame);
|
||||
|
@ -205,6 +205,7 @@ namespace osu.Game.Screens.Menu
|
||||
const int line_end_offset = 120;
|
||||
|
||||
smallRing.Foreground.ResizeTo(1, line_duration, Easing.OutQuint);
|
||||
smallRing.Delay(400).FadeColour(Color4.Black, 300);
|
||||
|
||||
lineTopLeft.MoveTo(new Vector2(-line_end_offset, -line_end_offset), line_duration, Easing.OutQuint);
|
||||
lineTopRight.MoveTo(new Vector2(line_end_offset, -line_end_offset), line_duration, Easing.OutQuint);
|
||||
|
81
osu.Game/Screens/Ranking/Statistics/SimpleStatisticItem.cs
Normal file
81
osu.Game/Screens/Ranking/Statistics/SimpleStatisticItem.cs
Normal file
@ -0,0 +1,81 @@
|
||||
// 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,
|
||||
Font = OsuFont.GetFont(size: 14)
|
||||
},
|
||||
value = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
Font = OsuFont.GetFont(size: 14, 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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
123
osu.Game/Screens/Ranking/Statistics/SimpleStatisticTable.cs
Normal file
123
osu.Game/Screens/Ranking/Statistics/SimpleStatisticTable.cs
Normal file
@ -0,0 +1,123 @@
|
||||
// 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 JetBrains.Annotations;
|
||||
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 table 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 SimpleStatisticTable : 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 SimpleStatisticTable(int columnCount, [ItemNotNull] 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")
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -32,9 +32,35 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Content = new[]
|
||||
{
|
||||
new[]
|
||||
{
|
||||
createHeader(item)
|
||||
},
|
||||
new Drawable[]
|
||||
{
|
||||
new FillFlowContainer
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Margin = new MarginPadding { Top = 15 },
|
||||
Child = item.Content
|
||||
}
|
||||
},
|
||||
},
|
||||
RowDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static Drawable createHeader(StatisticItem item)
|
||||
{
|
||||
if (string.IsNullOrEmpty(item.Name))
|
||||
return Empty();
|
||||
|
||||
return new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
@ -58,24 +84,6 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold),
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
new Drawable[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Margin = new MarginPadding { Top = 15 },
|
||||
Child = item.Content
|
||||
}
|
||||
},
|
||||
},
|
||||
RowDimensions = new[]
|
||||
{
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
new Dimension(GridSizeMode.AutoSize),
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="StatisticItem"/>, to be displayed inside a <see cref="StatisticRow"/> in the results screen.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the item.</param>
|
||||
/// <param name="name">The name of the item. Can be <see cref="string.Empty"/> to hide the item header.</param>
|
||||
/// <param name="content">The <see cref="Drawable"/> content to be displayed.</param>
|
||||
/// <param name="dimension">The <see cref="Dimension"/> of this item. This can be thought of as the column dimension of an encompassing <see cref="GridContainer"/>.</param>
|
||||
public StatisticItem([NotNull] string name, [NotNull] Drawable content, [CanBeNull] Dimension dimension = null)
|
||||
|
@ -94,14 +94,15 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(30, 15),
|
||||
Alpha = 0
|
||||
};
|
||||
|
||||
foreach (var row in newScore.Ruleset.CreateInstance().CreateStatisticsForScore(newScore, playableBeatmap))
|
||||
{
|
||||
rows.Add(new GridContainer
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Content = new[]
|
||||
@ -125,6 +126,7 @@ namespace osu.Game.Screens.Ranking.Statistics
|
||||
|
||||
spinner.Hide();
|
||||
content.Add(d);
|
||||
d.FadeIn(250, Easing.OutQuint);
|
||||
}, localCancellationSource.Token);
|
||||
}), localCancellationSource.Token);
|
||||
}
|
||||
|
39
osu.Game/Screens/Ranking/Statistics/UnstableRate.cs
Normal file
39
osu.Game/Screens/Ranking/Statistics/UnstableRate.cs
Normal file
@ -0,0 +1,39 @@
|
||||
// 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.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Screens.Ranking.Statistics
|
||||
{
|
||||
/// <summary>
|
||||
/// Displays the unstable rate statistic for a given play.
|
||||
/// </summary>
|
||||
public class UnstableRate : SimpleStatisticItem<double>
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates and computes an <see cref="UnstableRate"/> statistic.
|
||||
/// </summary>
|
||||
/// <param name="hitEvents">Sequence of <see cref="HitEvent"/>s to calculate the unstable rate based on.</param>
|
||||
public UnstableRate(IEnumerable<HitEvent> hitEvents)
|
||||
: base("Unstable Rate")
|
||||
{
|
||||
var timeOffsets = hitEvents.Select(ev => ev.TimeOffset).ToArray();
|
||||
Value = 10 * standardDeviation(timeOffsets);
|
||||
}
|
||||
|
||||
private static double standardDeviation(double[] timeOffsets)
|
||||
{
|
||||
if (timeOffsets.Length == 0)
|
||||
return double.NaN;
|
||||
|
||||
var mean = timeOffsets.Average();
|
||||
var squares = timeOffsets.Select(offset => Math.Pow(offset - mean, 2)).Sum();
|
||||
return Math.Sqrt(squares / timeOffsets.Length);
|
||||
}
|
||||
|
||||
protected override string DisplayValue(double value) => double.IsNaN(value) ? "(not available)" : value.ToString("N2");
|
||||
}
|
||||
}
|
@ -36,6 +36,7 @@ namespace osu.Game.Skinning
|
||||
public float HitPosition = (480 - 402) * POSITION_SCALE_FACTOR;
|
||||
public float LightPosition = (480 - 413) * POSITION_SCALE_FACTOR;
|
||||
public bool ShowJudgementLine = true;
|
||||
public bool KeysUnderNotes;
|
||||
|
||||
public LegacyManiaSkinConfiguration(int keys)
|
||||
{
|
||||
|
@ -52,5 +52,6 @@ namespace osu.Game.Skinning
|
||||
Hit100,
|
||||
Hit50,
|
||||
Hit0,
|
||||
KeysUnderNotes,
|
||||
}
|
||||
}
|
||||
|
@ -97,6 +97,10 @@ namespace osu.Game.Skinning
|
||||
currentConfig.ShowJudgementLine = pair.Value == "1";
|
||||
break;
|
||||
|
||||
case "KeysUnderNotes":
|
||||
currentConfig.KeysUnderNotes = pair.Value == "1";
|
||||
break;
|
||||
|
||||
case "LightingNWidth":
|
||||
parseArrayValue(pair.Value, currentConfig.ExplosionWidth);
|
||||
break;
|
||||
|
@ -272,6 +272,9 @@ namespace osu.Game.Skinning
|
||||
case LegacyManiaSkinConfigurationLookups.Hit300:
|
||||
case LegacyManiaSkinConfigurationLookups.Hit300g:
|
||||
return SkinUtils.As<TValue>(getManiaImage(existing, maniaLookup.Lookup.ToString()));
|
||||
|
||||
case LegacyManiaSkinConfigurationLookups.KeysUnderNotes:
|
||||
return SkinUtils.As<TValue>(new Bindable<bool>(existing.KeysUnderNotes));
|
||||
}
|
||||
|
||||
return null;
|
||||
|
Loading…
Reference in New Issue
Block a user