mirror of
https://github.com/ppy/osu.git
synced 2024-12-05 10:33:22 +08:00
Merge bfa02f9233
into ce4aac4184
This commit is contained in:
commit
f6e95f3aca
@ -0,0 +1,176 @@
|
|||||||
|
// 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 NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Extensions;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Screens.SelectV2.Wedge;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.SongSelectV2
|
||||||
|
{
|
||||||
|
public partial class TestSceneLengthAndBPMStatisticPill : SongSelectComponentsTestScene
|
||||||
|
{
|
||||||
|
[Resolved]
|
||||||
|
private OsuColour colours { get; set; } = null!;
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestValueColour()
|
||||||
|
{
|
||||||
|
AddStep("set pill", () => Child = new LocalLengthAndBPMStatisticPill
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("set beatmap", () =>
|
||||||
|
{
|
||||||
|
List<HitObject> objects = new List<HitObject>();
|
||||||
|
for (double i = 0; i < 50000; i += 1000)
|
||||||
|
objects.Add(new HitCircle { StartTime = i });
|
||||||
|
|
||||||
|
Beatmap.Value = CreateWorkingBeatmap(new Beatmap
|
||||||
|
{
|
||||||
|
BeatmapInfo = new BeatmapInfo
|
||||||
|
{
|
||||||
|
Length = 83000,
|
||||||
|
OnlineID = 1,
|
||||||
|
},
|
||||||
|
HitObjects = objects
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("set double time", () => SelectedMods.Value = new[] { new OsuModDoubleTime() });
|
||||||
|
|
||||||
|
AddAssert("length value is red", () => this.ChildrenOfType<LengthAndBPMStatisticPill.PillStatistic>().ElementAt(0).ValueColour,
|
||||||
|
() => Is.EqualTo(colours.ForModType(ModType.DifficultyIncrease)));
|
||||||
|
AddAssert("bpm value is red", () => this.ChildrenOfType<LengthAndBPMStatisticPill.PillStatistic>().ElementAt(1).ValueColour,
|
||||||
|
() => Is.EqualTo(colours.ForModType(ModType.DifficultyIncrease)));
|
||||||
|
|
||||||
|
AddStep("set half time", () => SelectedMods.Value = new[] { new OsuModHalfTime() });
|
||||||
|
|
||||||
|
AddAssert("length value is green", () => this.ChildrenOfType<LengthAndBPMStatisticPill.PillStatistic>().ElementAt(0).ValueColour,
|
||||||
|
() => Is.EqualTo(colours.ForModType(ModType.DifficultyReduction)));
|
||||||
|
AddAssert("bpm value is green", () => this.ChildrenOfType<LengthAndBPMStatisticPill.PillStatistic>().ElementAt(1).ValueColour,
|
||||||
|
() => Is.EqualTo(colours.ForModType(ModType.DifficultyReduction)));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestLengthUpdates()
|
||||||
|
{
|
||||||
|
OsuModDoubleTime? doubleTime = null;
|
||||||
|
|
||||||
|
List<HitObject> objects = new List<HitObject>();
|
||||||
|
for (double i = 0; i < 50000; i += 1000)
|
||||||
|
objects.Add(new HitCircle { StartTime = i });
|
||||||
|
|
||||||
|
Beatmap beatmap = new Beatmap
|
||||||
|
{
|
||||||
|
HitObjects = objects,
|
||||||
|
};
|
||||||
|
|
||||||
|
double drain = beatmap.CalculateDrainLength();
|
||||||
|
beatmap.BeatmapInfo.Length = drain;
|
||||||
|
|
||||||
|
AddStep("set pill", () => Child = new LocalLengthAndBPMStatisticPill
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("set beatmap", () =>
|
||||||
|
{
|
||||||
|
Beatmap.Value = CreateWorkingBeatmap(beatmap);
|
||||||
|
});
|
||||||
|
|
||||||
|
checkDisplayedLength(drain);
|
||||||
|
|
||||||
|
AddStep("select DT", () => SelectedMods.Value = new[] { doubleTime = new OsuModDoubleTime() });
|
||||||
|
checkDisplayedLength(Math.Round(drain / 1.5f));
|
||||||
|
|
||||||
|
AddStep("change DT rate", () => doubleTime!.SpeedChange.Value = 2);
|
||||||
|
checkDisplayedLength(Math.Round(drain / 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkDisplayedLength(double drain)
|
||||||
|
{
|
||||||
|
var displayedLength = drain.ToFormattedDuration();
|
||||||
|
|
||||||
|
AddAssert($"check map drain ({displayedLength})", () => this.ChildrenOfType<LengthAndBPMStatisticPill.PillStatistic>().ElementAt(0).Value, () => Is.EqualTo(displayedLength));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBPMUpdates()
|
||||||
|
{
|
||||||
|
const double bpm = 120;
|
||||||
|
OsuModDoubleTime? doubleTime = null;
|
||||||
|
|
||||||
|
AddStep("set pill", () => Child = new LocalLengthAndBPMStatisticPill
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("set beatmap", () =>
|
||||||
|
{
|
||||||
|
Beatmap beatmap = new Beatmap();
|
||||||
|
beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 60 * 1000 / bpm });
|
||||||
|
|
||||||
|
Beatmap.Value = CreateWorkingBeatmap(beatmap);
|
||||||
|
});
|
||||||
|
|
||||||
|
checkDisplayedBPM($"{bpm}");
|
||||||
|
|
||||||
|
AddStep("select DT", () => SelectedMods.Value = new[] { doubleTime = new OsuModDoubleTime() });
|
||||||
|
checkDisplayedBPM($"{bpm * 1.5f}");
|
||||||
|
|
||||||
|
AddStep("change DT rate", () => doubleTime!.SpeedChange.Value = 2);
|
||||||
|
checkDisplayedBPM($"{bpm * 2}");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase(120, 125, null, "120-125 (120)")]
|
||||||
|
[TestCase(120, 120.6, null, "120-121 (120)")]
|
||||||
|
[TestCase(120, 120.4, null, "120")]
|
||||||
|
[TestCase(120, 120.6, "DT", "180-182 (180)")]
|
||||||
|
[TestCase(120, 120.4, "DT", "180")]
|
||||||
|
public void TestVaryingBPM(double commonBpm, double otherBpm, string? mod, string expectedDisplay)
|
||||||
|
{
|
||||||
|
AddStep("set pill", () => Child = new LocalLengthAndBPMStatisticPill
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (mod != null)
|
||||||
|
AddStep($"select {mod}", () => SelectedMods.Value = new[] { Ruleset.Value.CreateInstance().CreateModFromAcronym(mod) });
|
||||||
|
|
||||||
|
AddStep("set beatmap", () =>
|
||||||
|
{
|
||||||
|
Beatmap beatmap = new Beatmap();
|
||||||
|
beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 60 * 1000 / commonBpm });
|
||||||
|
beatmap.ControlPointInfo.Add(100, new TimingControlPoint { BeatLength = 60 * 1000 / otherBpm });
|
||||||
|
beatmap.ControlPointInfo.Add(200, new TimingControlPoint { BeatLength = 60 * 1000 / commonBpm });
|
||||||
|
|
||||||
|
Beatmap.Value = CreateWorkingBeatmap(beatmap);
|
||||||
|
});
|
||||||
|
|
||||||
|
checkDisplayedBPM(expectedDisplay);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkDisplayedBPM(string target)
|
||||||
|
{
|
||||||
|
AddAssert($"displayed bpm is {target}", () => this.ChildrenOfType<LengthAndBPMStatisticPill.PillStatistic>().ElementAt(1).Value.ToString(), () => Is.EqualTo(target));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
143
osu.Game/Screens/SelectV2/Wedge/LengthAndBPMStatisticPill.cs
Normal file
143
osu.Game/Screens/SelectV2/Wedge/LengthAndBPMStatisticPill.cs
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
// 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.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Cursor;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Resources.Localisation.Web;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.SelectV2.Wedge
|
||||||
|
{
|
||||||
|
public abstract partial class LengthAndBPMStatisticPill : CompositeDrawable
|
||||||
|
{
|
||||||
|
protected PillStatistic LengthStatistic = null!;
|
||||||
|
protected PillStatistic BPMStatistic = null!;
|
||||||
|
|
||||||
|
protected LengthAndBPMStatisticPill()
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.X;
|
||||||
|
Height = 20;
|
||||||
|
Masking = true;
|
||||||
|
CornerRadius = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OverlayColourProvider colourProvider)
|
||||||
|
{
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = colourProvider.Background4
|
||||||
|
},
|
||||||
|
new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.X,
|
||||||
|
RelativeSizeAxes = Axes.Y,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(10),
|
||||||
|
Padding = new MarginPadding { Horizontal = 20 },
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
LengthStatistic = new PillStatistic(new BeatmapStatistic { Name = "Length" }),
|
||||||
|
BPMStatistic = new PillStatistic(new BeatmapStatistic { Name = BeatmapsetsStrings.ShowStatsBpm }),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class PillStatistic : CompositeDrawable, IHasTooltip
|
||||||
|
{
|
||||||
|
private readonly BeatmapStatistic statistic;
|
||||||
|
private OsuSpriteText valueSpriteText = null!;
|
||||||
|
|
||||||
|
private LocalisableString valueText;
|
||||||
|
|
||||||
|
public LocalisableString Value
|
||||||
|
{
|
||||||
|
get => valueText;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
valueText = value;
|
||||||
|
|
||||||
|
if (IsLoaded)
|
||||||
|
updateValueText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color4 valueColour;
|
||||||
|
|
||||||
|
public Color4 ValueColour
|
||||||
|
{
|
||||||
|
get => valueColour;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
valueColour = value;
|
||||||
|
|
||||||
|
if (IsLoaded)
|
||||||
|
updateValueText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PillStatistic(BeatmapStatistic statistic)
|
||||||
|
{
|
||||||
|
this.statistic = statistic;
|
||||||
|
|
||||||
|
Anchor = Anchor.CentreLeft;
|
||||||
|
Origin = Anchor.CentreLeft;
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OverlayColourProvider colourProvider)
|
||||||
|
{
|
||||||
|
valueColour = colourProvider.Content2;
|
||||||
|
|
||||||
|
InternalChild = new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Direction = FillDirection.Horizontal,
|
||||||
|
Spacing = new Vector2(5),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Text = statistic.Name,
|
||||||
|
Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 14),
|
||||||
|
},
|
||||||
|
valueSpriteText = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Text = statistic.Content,
|
||||||
|
Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 14),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
updateValueText();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateValueText()
|
||||||
|
{
|
||||||
|
valueSpriteText.Text = LocalisableString.IsNullOrEmpty(valueText) ? "-" : valueText;
|
||||||
|
valueSpriteText.Colour = valueColour;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalisableString TooltipText { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,100 @@
|
|||||||
|
// 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.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Extensions;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Resources.Localisation.Web;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Utils;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.SelectV2.Wedge
|
||||||
|
{
|
||||||
|
public partial class LocalLengthAndBPMStatisticPill : LengthAndBPMStatisticPill
|
||||||
|
{
|
||||||
|
private ModSettingChangeTracker? modSettingChangeTracker;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IBindable<IReadOnlyList<Mod>> mods { get; set; } = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IBindable<WorkingBeatmap> workingBeatmap { get; set; } = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OsuColour colours { get; set; } = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
workingBeatmap.BindValueChanged(_ => updateStatistics());
|
||||||
|
|
||||||
|
mods.BindValueChanged(m =>
|
||||||
|
{
|
||||||
|
modSettingChangeTracker?.Dispose();
|
||||||
|
|
||||||
|
updateStatistics();
|
||||||
|
|
||||||
|
modSettingChangeTracker = new ModSettingChangeTracker(m.NewValue);
|
||||||
|
modSettingChangeTracker.SettingChanged += _ => updateStatistics();
|
||||||
|
}, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateStatistics()
|
||||||
|
{
|
||||||
|
// TODO: consider mods which apply variable rates.
|
||||||
|
double rate = 1;
|
||||||
|
foreach (var mod in mods.Value.OfType<IApplicableToRate>())
|
||||||
|
rate = mod.ApplyToRate(0, rate);
|
||||||
|
|
||||||
|
var beatmap = workingBeatmap.Value.Beatmap;
|
||||||
|
|
||||||
|
int bpmMax = FormatUtils.RoundBPM(beatmap.ControlPointInfo.BPMMaximum, rate);
|
||||||
|
int bpmMin = FormatUtils.RoundBPM(beatmap.ControlPointInfo.BPMMinimum, rate);
|
||||||
|
int mostCommonBPM = FormatUtils.RoundBPM(60000 / beatmap.GetMostCommonBeatLength(), rate);
|
||||||
|
|
||||||
|
string labelText = bpmMin == bpmMax
|
||||||
|
? $"{bpmMin}"
|
||||||
|
: $"{bpmMin}-{bpmMax} ({mostCommonBPM})";
|
||||||
|
|
||||||
|
BPMStatistic.Value = labelText;
|
||||||
|
|
||||||
|
double drainLength = Math.Round(beatmap.CalculateDrainLength() / rate);
|
||||||
|
double hitLength = Math.Round(beatmap.BeatmapInfo.Length / rate);
|
||||||
|
|
||||||
|
LengthStatistic.Value = hitLength.ToFormattedDuration();
|
||||||
|
LengthStatistic.TooltipText = BeatmapsetsStrings.ShowStatsTotalLength(drainLength.ToFormattedDuration());
|
||||||
|
|
||||||
|
BPMStatistic.ValueColour = getColourByRate(rate);
|
||||||
|
LengthStatistic.ValueColour = getColourByRate(rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Colour4 getColourByRate(double rate)
|
||||||
|
{
|
||||||
|
if (rate < 1)
|
||||||
|
return colours.ForModType(ModType.DifficultyReduction);
|
||||||
|
|
||||||
|
if (rate > 1)
|
||||||
|
return colours.ForModType(ModType.DifficultyIncrease);
|
||||||
|
|
||||||
|
return colourProvider.Content2;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
modSettingChangeTracker?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user