1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-21 16:07:24 +08:00

Merge branch 'master' into results-screen-component-metrics

This commit is contained in:
Bartłomiej Dach 2023-07-13 19:53:52 +02:00
commit d5912165e9
No known key found for this signature in database
14 changed files with 156 additions and 43 deletions

View File

@ -26,6 +26,8 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Replays.Types;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking.Statistics;
using osu.Game.Skinning; using osu.Game.Skinning;
namespace osu.Game.Rulesets.Catch namespace osu.Game.Rulesets.Catch
@ -218,5 +220,17 @@ namespace osu.Game.Rulesets.Catch
public override HitObjectComposer CreateHitObjectComposer() => new CatchHitObjectComposer(this); public override HitObjectComposer CreateHitObjectComposer() => new CatchHitObjectComposer(this);
public override IBeatmapVerifier CreateBeatmapVerifier() => new CatchBeatmapVerifier(); public override IBeatmapVerifier CreateBeatmapVerifier() => new CatchBeatmapVerifier();
public override StatisticItem[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap)
{
return new[]
{
new StatisticItem("Performance Breakdown", () => new PerformanceBreakdownChart(score, playableBeatmap)
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y
}),
};
}
} }
} }

View File

@ -412,7 +412,7 @@ namespace osu.Game.Rulesets.Mania
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Height = 250 Height = 250
}, true), }, true),
new StatisticItem(string.Empty, () => new SimpleStatisticTable(2, new SimpleStatisticItem[] new StatisticItem("Statistics", () => new SimpleStatisticTable(2, new SimpleStatisticItem[]
{ {
new AverageHitError(score.HitEvents), new AverageHitError(score.HitEvents),
new UnstableRate(score.HitEvents) new UnstableRate(score.HitEvents)

View File

@ -319,7 +319,7 @@ namespace osu.Game.Rulesets.Osu
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Height = 250 Height = 250
}, true), }, true),
new StatisticItem(string.Empty, () => new SimpleStatisticTable(2, new SimpleStatisticItem[] new StatisticItem("Statistics", () => new SimpleStatisticTable(2, new SimpleStatisticItem[]
{ {
new AverageHitError(timedHitEvents), new AverageHitError(timedHitEvents),
new UnstableRate(timedHitEvents) new UnstableRate(timedHitEvents)

View File

@ -114,5 +114,75 @@ namespace osu.Game.Rulesets.Taiko.Tests.Judgements
AddAssert("all tick offsets are 0", () => JudgementResults.Where(r => r.HitObject is SwellTick).All(r => r.TimeOffset == 0)); AddAssert("all tick offsets are 0", () => JudgementResults.Where(r => r.HitObject is SwellTick).All(r => r.TimeOffset == 0));
} }
/// <summary>
/// Ensure input is correctly sent to subsequent hits if a swell is fully completed.
/// </summary>
[Test]
public void TestHitSwellThenHitHit()
{
const double swell_time = 1000;
const double hit_time = 1150;
Swell swell = new Swell
{
StartTime = swell_time,
Duration = 100,
RequiredHits = 1
};
Hit hit = new Hit
{
StartTime = hit_time
};
List<ReplayFrame> frames = new List<ReplayFrame>
{
new TaikoReplayFrame(0),
new TaikoReplayFrame(swell_time, TaikoAction.LeftRim),
new TaikoReplayFrame(hit_time, TaikoAction.RightCentre),
};
PerformTest(frames, CreateBeatmap(swell, hit));
AssertJudgementCount(3);
AssertResult<SwellTick>(0, HitResult.IgnoreHit);
AssertResult<Swell>(0, HitResult.LargeBonus);
AssertResult<Hit>(0, HitResult.Great);
}
[Test]
public void TestMissSwellThenHitHit()
{
const double swell_time = 1000;
const double hit_time = 1150;
Swell swell = new Swell
{
StartTime = swell_time,
Duration = 100,
RequiredHits = 1
};
Hit hit = new Hit
{
StartTime = hit_time
};
List<ReplayFrame> frames = new List<ReplayFrame>
{
new TaikoReplayFrame(0),
new TaikoReplayFrame(hit_time, TaikoAction.RightCentre),
};
PerformTest(frames, CreateBeatmap(swell, hit));
AssertJudgementCount(3);
AssertResult<SwellTick>(0, HitResult.IgnoreMiss);
AssertResult<Swell>(0, HitResult.IgnoreMiss);
AssertResult<Hit>(0, HitResult.Great);
}
} }
} }

View File

@ -276,6 +276,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
if (Time.Current < HitObject.StartTime) if (Time.Current < HitObject.StartTime)
return false; return false;
if (AllJudged)
return false;
bool isCentre = e.Action == TaikoAction.LeftCentre || e.Action == TaikoAction.RightCentre; bool isCentre = e.Action == TaikoAction.LeftCentre || e.Action == TaikoAction.RightCentre;
// Ensure alternating centre and rim hits // Ensure alternating centre and rim hits

View File

@ -256,7 +256,7 @@ namespace osu.Game.Rulesets.Taiko
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Height = 250 Height = 250
}, true), }, true),
new StatisticItem(string.Empty, () => new SimpleStatisticTable(3, new SimpleStatisticItem[] new StatisticItem("Statistics", () => new SimpleStatisticTable(3, new SimpleStatisticItem[]
{ {
new AverageHitError(timedHitEvents), new AverageHitError(timedHitEvents),
new UnstableRate(timedHitEvents) new UnstableRate(timedHitEvents)

View File

@ -69,6 +69,8 @@ namespace osu.Game.Tests.Visual.Ranking
})); }));
} }
private int onlineScoreID = 1;
[TestCase(1, ScoreRank.X)] [TestCase(1, ScoreRank.X)]
[TestCase(0.9999, ScoreRank.S)] [TestCase(0.9999, ScoreRank.S)]
[TestCase(0.975, ScoreRank.S)] [TestCase(0.975, ScoreRank.S)]
@ -81,14 +83,17 @@ namespace osu.Game.Tests.Visual.Ranking
{ {
TestResultsScreen screen = null; TestResultsScreen screen = null;
var score = TestResources.CreateTestScoreInfo(); loadResultsScreen(() =>
{
var score = TestResources.CreateTestScoreInfo();
score.OnlineID = 1234; score.OnlineID = onlineScoreID++;
score.HitEvents = TestSceneStatisticsPanel.CreatePositionDistributedHitEvents(); score.HitEvents = TestSceneStatisticsPanel.CreatePositionDistributedHitEvents();
score.Accuracy = accuracy; score.Accuracy = accuracy;
score.Rank = rank; score.Rank = rank;
loadResultsScreen(() => screen = createResultsScreen(score)); return screen = createResultsScreen(score);
});
AddUntilStep("wait for loaded", () => screen.IsLoaded); AddUntilStep("wait for loaded", () => screen.IsLoaded);
AddAssert("retry overlay present", () => screen.RetryOverlay != null); AddAssert("retry overlay present", () => screen.RetryOverlay != null);
} }

View File

@ -9,8 +9,8 @@ using osu.Framework.Allocation;
using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Cursor;
using osu.Framework.Testing;
using osu.Game.Graphics.Cursor; using osu.Game.Graphics.Cursor;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Graphics.UserInterfaceV2;
@ -304,11 +304,8 @@ namespace osu.Game.Tests.Visual.UserInterface
AddAssert("preset is not changed", () => panel.Preset.Value.Name == presetName); AddAssert("preset is not changed", () => panel.Preset.Value.Name == presetName);
AddUntilStep("popover is unchanged", () => this.ChildrenOfType<OsuPopover>().FirstOrDefault() == popover); AddUntilStep("popover is unchanged", () => this.ChildrenOfType<OsuPopover>().FirstOrDefault() == popover);
AddStep("edit preset name", () => popover.ChildrenOfType<LabelledTextBox>().First().Current.Value = "something new"); AddStep("edit preset name", () => popover.ChildrenOfType<LabelledTextBox>().First().Current.Value = "something new");
AddStep("attempt preset edit", () => AddStep("commit changes to textbox", () => InputManager.Key(Key.Enter));
{ AddStep("attempt preset edit via select binding", () => InputManager.Key(Key.Enter));
InputManager.MoveMouseTo(popover.ChildrenOfType<ShearedButton>().ElementAt(1));
InputManager.Click(MouseButton.Left);
});
AddUntilStep("popover closed", () => !this.ChildrenOfType<OsuPopover>().Any()); AddUntilStep("popover closed", () => !this.ChildrenOfType<OsuPopover>().Any());
AddAssert("preset is changed", () => panel.Preset.Value.Name != presetName); AddAssert("preset is changed", () => panel.Preset.Value.Name != presetName);
} }

View File

@ -542,7 +542,7 @@ namespace osu.Game.Tests.Visual.UserInterface
AddStep("clear search", () => modSelectOverlay.SearchTerm = string.Empty); AddStep("clear search", () => modSelectOverlay.SearchTerm = string.Empty);
AddStep("press enter", () => InputManager.Key(Key.Enter)); AddStep("press enter", () => InputManager.Key(Key.Enter));
AddAssert("mod select hidden", () => modSelectOverlay.State.Value == Visibility.Hidden); AddAssert("mod select still visible", () => modSelectOverlay.State.Value == Visibility.Visible);
} }
[Test] [Test]

View File

@ -431,8 +431,9 @@ namespace osu.Game.Beatmaps
beatmapInfo.Status = BeatmapOnlineStatus.LocallyModified; beatmapInfo.Status = BeatmapOnlineStatus.LocallyModified;
beatmapInfo.ResetOnlineInfo(); beatmapInfo.ResetOnlineInfo();
using (var stream = new MemoryStream()) Realm.Write(r =>
{ {
using var stream = new MemoryStream();
using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true)) using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true))
new LegacyBeatmapEncoder(beatmapContent, beatmapSkin).Encode(sw); new LegacyBeatmapEncoder(beatmapContent, beatmapSkin).Encode(sw);
@ -458,23 +459,20 @@ namespace osu.Game.Beatmaps
updateHashAndMarkDirty(setInfo); updateHashAndMarkDirty(setInfo);
Realm.Write(r => var liveBeatmapSet = r.Find<BeatmapSetInfo>(setInfo.ID)!;
{
var liveBeatmapSet = r.Find<BeatmapSetInfo>(setInfo.ID)!;
setInfo.CopyChangesToRealm(liveBeatmapSet); setInfo.CopyChangesToRealm(liveBeatmapSet);
if (transferCollections) if (transferCollections)
beatmapInfo.TransferCollectionReferences(r, oldMd5Hash); beatmapInfo.TransferCollectionReferences(r, oldMd5Hash);
liveBeatmapSet.Beatmaps.Single(b => b.ID == beatmapInfo.ID) liveBeatmapSet.Beatmaps.Single(b => b.ID == beatmapInfo.ID)
.UpdateLocalScores(r); .UpdateLocalScores(r);
// do not look up metadata. // do not look up metadata.
// this is a locally-modified set now, so looking up metadata is busy work at best and harmful at worst. // this is a locally-modified set now, so looking up metadata is busy work at best and harmful at worst.
ProcessBeatmap?.Invoke(liveBeatmapSet, MetadataLookupScope.None); ProcessBeatmap?.Invoke(liveBeatmapSet, MetadataLookupScope.None);
}); });
}
Debug.Assert(beatmapInfo.BeatmapSet != null); Debug.Assert(beatmapInfo.BeatmapSet != null);

View File

@ -65,7 +65,7 @@ namespace osu.Game.Graphics.UserInterfaceV2
return base.OnKeyDown(e); return base.OnKeyDown(e);
} }
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e) public virtual bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
{ {
if (e.Repeat) if (e.Repeat)
return false; return false;

View File

@ -8,10 +8,12 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Input.Bindings;
using osu.Game.Localisation; using osu.Game.Localisation;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
@ -95,6 +97,18 @@ namespace osu.Game.Overlays.Mods
}, true); }, true);
} }
public override bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
{
switch (e.Action)
{
case GlobalAction.Select:
createButton.TriggerClick();
return true;
}
return base.OnPressed(e);
}
private void createPreset() private void createPreset()
{ {
realm.Write(r => r.Add(new ModPreset realm.Write(r => r.Add(new ModPreset

View File

@ -8,11 +8,13 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Graphics.UserInterfaceV2;
using osu.Game.Input.Bindings;
using osu.Game.Localisation; using osu.Game.Localisation;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osuTK; using osuTK;
@ -130,6 +132,25 @@ namespace osu.Game.Overlays.Mods
}, true); }, true);
} }
protected override void LoadComplete()
{
base.LoadComplete();
ScheduleAfterChildren(() => GetContainingInputManager().ChangeFocus(nameTextBox));
}
public override bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
{
switch (e.Action)
{
case GlobalAction.Select:
saveButton.TriggerClick();
return true;
}
return base.OnPressed(e);
}
private void useCurrentMods() private void useCurrentMods()
{ {
saveableMods = selectedMods.Value.ToHashSet(); saveableMods = selectedMods.Value.ToHashSet();
@ -150,13 +171,6 @@ namespace osu.Game.Overlays.Mods
return !saveableMods.SetEquals(selectedMods.Value); return !saveableMods.SetEquals(selectedMods.Value);
} }
protected override void LoadComplete()
{
base.LoadComplete();
ScheduleAfterChildren(() => GetContainingInputManager().ChangeFocus(nameTextBox));
}
private void save() private void save()
{ {
preset.PerformWrite(s => preset.PerformWrite(s =>

View File

@ -48,7 +48,8 @@ namespace osu.Game.Overlays.Mods
/// Contrary to <see cref="OsuGameBase.AvailableMods"/> and <see cref="globalAvailableMods"/>, the <see cref="Mod"/> instances /// Contrary to <see cref="OsuGameBase.AvailableMods"/> and <see cref="globalAvailableMods"/>, the <see cref="Mod"/> instances
/// inside the <see cref="ModState"/> objects are owned solely by this <see cref="ModSelectOverlay"/> instance. /// inside the <see cref="ModState"/> objects are owned solely by this <see cref="ModSelectOverlay"/> instance.
/// </remarks> /// </remarks>
public Bindable<Dictionary<ModType, IReadOnlyList<ModState>>> AvailableMods { get; } = new Bindable<Dictionary<ModType, IReadOnlyList<ModState>>>(new Dictionary<ModType, IReadOnlyList<ModState>>()); public Bindable<Dictionary<ModType, IReadOnlyList<ModState>>> AvailableMods { get; } =
new Bindable<Dictionary<ModType, IReadOnlyList<ModState>>>(new Dictionary<ModType, IReadOnlyList<ModState>>());
private Func<Mod, bool> isValidMod = _ => true; private Func<Mod, bool> isValidMod = _ => true;
@ -636,12 +637,9 @@ namespace osu.Game.Overlays.Mods
case GlobalAction.Select: case GlobalAction.Select:
{ {
// Pressing select should select first filtered mod or completely hide the overlay in one shot if search term is empty. // Pressing select should select first filtered mod if a search is in progress.
if (string.IsNullOrEmpty(SearchTerm)) if (string.IsNullOrEmpty(SearchTerm))
{
hideOverlay(true);
return true; return true;
}
ModState? firstMod = columnFlow.Columns.OfType<ModColumn>().FirstOrDefault(m => m.IsPresent)?.AvailableMods.FirstOrDefault(x => x.Visible); ModState? firstMod = columnFlow.Columns.OfType<ModColumn>().FirstOrDefault(m => m.IsPresent)?.AvailableMods.FirstOrDefault(x => x.Visible);