mirror of
https://github.com/SK-la/Ez2Lazer.git
synced 2026-03-13 11:20:28 +00:00
同步更新,优化ezmode逻辑
This commit is contained in:
@@ -147,7 +147,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void TestFilterIntersection()
|
||||
public void TestKeysFilterIntersection()
|
||||
{
|
||||
var criteria = new ManiaFilterCriteria();
|
||||
criteria.TryParseCustomKeywordCriteria("keys", Operator.Greater, "4");
|
||||
@@ -175,7 +175,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void TestInvalidFilters()
|
||||
public void TestInvalidKeysFilters()
|
||||
{
|
||||
var criteria = new ManiaFilterCriteria();
|
||||
|
||||
@@ -183,5 +183,132 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
Assert.False(criteria.TryParseCustomKeywordCriteria("keys", Operator.NotEqual, "4,some text"));
|
||||
Assert.False(criteria.TryParseCustomKeywordCriteria("keys", Operator.GreaterOrEqual, "4,5,6"));
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void TestLnsEqual()
|
||||
{
|
||||
var criteria = new ManiaFilterCriteria();
|
||||
var filterCriteria = new FilterCriteria
|
||||
{
|
||||
Ruleset = new ManiaRuleset().RulesetInfo
|
||||
};
|
||||
|
||||
criteria.TryParseCustomKeywordCriteria("lns", Operator.Equal, "0");
|
||||
BeatmapInfo beatmapInfo1 = new BeatmapInfo(new ManiaRuleset().RulesetInfo)
|
||||
{
|
||||
TotalObjectCount = 0,
|
||||
EndTimeObjectCount = 0
|
||||
};
|
||||
Assert.True(criteria.Matches(beatmapInfo1, filterCriteria));
|
||||
|
||||
criteria.TryParseCustomKeywordCriteria("lns", Operator.Equal, "0");
|
||||
BeatmapInfo beatmapInfo2 = new BeatmapInfo(new ManiaRuleset().RulesetInfo)
|
||||
{
|
||||
TotalObjectCount = 100,
|
||||
EndTimeObjectCount = 0
|
||||
};
|
||||
Assert.True(criteria.Matches(beatmapInfo2, filterCriteria));
|
||||
|
||||
criteria.TryParseCustomKeywordCriteria("lns", Operator.Equal, "100");
|
||||
BeatmapInfo beatmapInfo3 = new BeatmapInfo(new ManiaRuleset().RulesetInfo)
|
||||
{
|
||||
TotalObjectCount = 100,
|
||||
EndTimeObjectCount = 100
|
||||
};
|
||||
Assert.True(criteria.Matches(beatmapInfo3, filterCriteria));
|
||||
|
||||
criteria.TryParseCustomKeywordCriteria("lns", Operator.Equal, "1");
|
||||
BeatmapInfo beatmapInfo4 = new BeatmapInfo(new ManiaRuleset().RulesetInfo)
|
||||
{
|
||||
TotalObjectCount = 100,
|
||||
EndTimeObjectCount = 1
|
||||
};
|
||||
Assert.True(criteria.Matches(beatmapInfo4, filterCriteria));
|
||||
|
||||
criteria.TryParseCustomKeywordCriteria("lns", Operator.Equal, "0.1");
|
||||
BeatmapInfo beatmapInfo5 = new BeatmapInfo(new ManiaRuleset().RulesetInfo)
|
||||
{
|
||||
TotalObjectCount = 1000,
|
||||
EndTimeObjectCount = 1
|
||||
};
|
||||
Assert.True(criteria.Matches(beatmapInfo5, filterCriteria));
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void TestLnsGreaterOrEqual()
|
||||
{
|
||||
var criteria = new ManiaFilterCriteria();
|
||||
var filterCriteria = new FilterCriteria
|
||||
{
|
||||
Ruleset = new ManiaRuleset().RulesetInfo
|
||||
};
|
||||
|
||||
criteria.TryParseCustomKeywordCriteria("lns", Operator.GreaterOrEqual, "0");
|
||||
BeatmapInfo beatmapInfo1 = new BeatmapInfo(new ManiaRuleset().RulesetInfo)
|
||||
{
|
||||
TotalObjectCount = 0,
|
||||
EndTimeObjectCount = 0
|
||||
};
|
||||
Assert.True(criteria.Matches(beatmapInfo1, filterCriteria));
|
||||
|
||||
criteria.TryParseCustomKeywordCriteria("lns", Operator.GreaterOrEqual, "0");
|
||||
BeatmapInfo beatmapInfo2 = new BeatmapInfo(new ManiaRuleset().RulesetInfo)
|
||||
{
|
||||
TotalObjectCount = 100,
|
||||
EndTimeObjectCount = 0
|
||||
};
|
||||
Assert.True(criteria.Matches(beatmapInfo2, filterCriteria));
|
||||
|
||||
criteria.TryParseCustomKeywordCriteria("lns", Operator.GreaterOrEqual, "100");
|
||||
BeatmapInfo beatmapInfo3 = new BeatmapInfo(new ManiaRuleset().RulesetInfo)
|
||||
{
|
||||
TotalObjectCount = 100,
|
||||
EndTimeObjectCount = 100
|
||||
};
|
||||
Assert.True(criteria.Matches(beatmapInfo3, filterCriteria));
|
||||
|
||||
criteria.TryParseCustomKeywordCriteria("lns", Operator.GreaterOrEqual, "1");
|
||||
BeatmapInfo beatmapInfo4 = new BeatmapInfo(new ManiaRuleset().RulesetInfo)
|
||||
{
|
||||
TotalObjectCount = 100,
|
||||
EndTimeObjectCount = 1
|
||||
};
|
||||
Assert.True(criteria.Matches(beatmapInfo4, filterCriteria));
|
||||
|
||||
criteria.TryParseCustomKeywordCriteria("lns", Operator.GreaterOrEqual, "0.1");
|
||||
BeatmapInfo beatmapInfo5 = new BeatmapInfo(new ManiaRuleset().RulesetInfo)
|
||||
{
|
||||
TotalObjectCount = 1000,
|
||||
EndTimeObjectCount = 1
|
||||
};
|
||||
Assert.True(criteria.Matches(beatmapInfo5, filterCriteria));
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void TestLnsNotManiaRuleset()
|
||||
{
|
||||
var criteria = new ManiaFilterCriteria();
|
||||
var filterCriteria = new FilterCriteria
|
||||
{
|
||||
Ruleset = new ManiaRuleset().RulesetInfo
|
||||
};
|
||||
|
||||
criteria.TryParseCustomKeywordCriteria("lns", Operator.LessOrEqual, "100");
|
||||
BeatmapInfo beatmapInfo = new BeatmapInfo
|
||||
{
|
||||
TotalObjectCount = 100,
|
||||
EndTimeObjectCount = 50
|
||||
};
|
||||
Assert.False(criteria.Matches(beatmapInfo, filterCriteria));
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void TestInvalidLnsFilters()
|
||||
{
|
||||
var criteria = new ManiaFilterCriteria();
|
||||
|
||||
Assert.False(criteria.TryParseCustomKeywordCriteria("lns", Operator.Equal, "some text"));
|
||||
Assert.False(criteria.TryParseCustomKeywordCriteria("lns", Operator.GreaterOrEqual, "1some text"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// 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.Bindables;
|
||||
@@ -19,6 +20,7 @@ namespace osu.Game.Rulesets.Mania
|
||||
public class ManiaFilterCriteria : IRulesetFilterCriteria
|
||||
{
|
||||
private readonly HashSet<int> includedKeyCounts = Enumerable.Range(1, LegacyBeatmapDecoder.MAX_MANIA_KEY_COUNT).ToHashSet();
|
||||
private FilterCriteria.OptionalRange<float> longNotePercentage;
|
||||
|
||||
public bool Matches(BeatmapInfo beatmapInfo, FilterCriteria criteria)
|
||||
{
|
||||
@@ -40,7 +42,10 @@ namespace osu.Game.Rulesets.Mania
|
||||
}
|
||||
}
|
||||
|
||||
return includedKeyCounts.Contains(keyCount);
|
||||
bool keyCountMatch = includedKeyCounts.Contains(keyCount);
|
||||
bool longNotePercentageMatch = !longNotePercentage.HasFilter || (!isConvertedBeatmap(beatmapInfo) && longNotePercentage.IsInRange(calculateLongNotePercentage(beatmapInfo)));
|
||||
|
||||
return keyCountMatch && longNotePercentageMatch;
|
||||
}
|
||||
|
||||
public bool TryParseCustomKeywordCriteria(string key, Operator op, string strValues)
|
||||
@@ -100,6 +105,10 @@ namespace osu.Game.Rulesets.Mania
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
case "ln":
|
||||
case "lns":
|
||||
return FilterQueryParser.TryUpdateCriteriaRange(ref longNotePercentage, op, strValues);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -119,5 +128,18 @@ namespace osu.Game.Rulesets.Mania
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool isConvertedBeatmap(BeatmapInfo beatmapInfo)
|
||||
{
|
||||
return !beatmapInfo.Ruleset.Equals(new ManiaRuleset().RulesetInfo);
|
||||
}
|
||||
|
||||
private static float calculateLongNotePercentage(BeatmapInfo beatmapInfo)
|
||||
{
|
||||
int holdNotes = beatmapInfo.EndTimeObjectCount;
|
||||
int totalNotes = Math.Max(1, beatmapInfo.TotalObjectCount);
|
||||
|
||||
return holdNotes / (float)totalNotes * 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ using osu.Game.Storyboards;
|
||||
using osu.Game.Tests.Beatmaps.IO;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Multiplayer
|
||||
{
|
||||
@@ -302,9 +303,69 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddWaitStep("wait a bit", 10);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Explicit("Test relies on timing of arriving frames to exercise assertions which doesn't work headless.")]
|
||||
public void TestMaximisedUserIsAudioSource()
|
||||
{
|
||||
start(new[] { PLAYER_1_ID, PLAYER_2_ID });
|
||||
loadSpectateScreen();
|
||||
|
||||
// With no frames, the synchronisation state will be TooFarAhead.
|
||||
// In this state, all players should be muted.
|
||||
assertMuted(PLAYER_1_ID, true);
|
||||
assertMuted(PLAYER_2_ID, true);
|
||||
|
||||
// Send frames for both players.
|
||||
sendFrames(PLAYER_1_ID, 20);
|
||||
sendFrames(PLAYER_2_ID, 40);
|
||||
|
||||
waitUntilRunning(PLAYER_1_ID);
|
||||
AddStep("maximise player 1", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(getInstance(PLAYER_1_ID));
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
assertMuted(PLAYER_1_ID, false);
|
||||
assertMuted(PLAYER_2_ID, true);
|
||||
|
||||
waitUntilPaused(PLAYER_1_ID);
|
||||
assertMuted(PLAYER_1_ID, false);
|
||||
assertMuted(PLAYER_2_ID, true);
|
||||
|
||||
AddStep("minimise player 1", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(getInstance(PLAYER_1_ID));
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
assertMuted(PLAYER_1_ID, true);
|
||||
assertMuted(PLAYER_2_ID, false);
|
||||
|
||||
AddStep("maximise player 2", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(getInstance(PLAYER_2_ID));
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
assertMuted(PLAYER_1_ID, true);
|
||||
assertMuted(PLAYER_2_ID, false);
|
||||
|
||||
waitUntilPaused(PLAYER_2_ID);
|
||||
sendFrames(PLAYER_1_ID, 60);
|
||||
|
||||
assertMuted(PLAYER_1_ID, true);
|
||||
assertMuted(PLAYER_2_ID, false);
|
||||
|
||||
AddStep("minimise player 2", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(getInstance(PLAYER_2_ID));
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
assertMuted(PLAYER_1_ID, false);
|
||||
assertMuted(PLAYER_2_ID, true);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest]
|
||||
public void TestMostInSyncUserIsAudioSource()
|
||||
public void TestMostInSyncUserIsAudioSourceIfNoneMaximised()
|
||||
{
|
||||
start(new[] { PLAYER_1_ID, PLAYER_2_ID });
|
||||
loadSpectateScreen();
|
||||
|
||||
@@ -22,12 +22,16 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
{
|
||||
private BeatmapSetInfo baseTestBeatmap = null!;
|
||||
|
||||
private const int initial_filter_count = 3;
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
RemoveAllBeatmaps();
|
||||
CreateCarousel();
|
||||
WaitForFiltering();
|
||||
AddBeatmaps(1, 3);
|
||||
WaitForFiltering();
|
||||
AddStep("generate and add test beatmap", () =>
|
||||
{
|
||||
baseTestBeatmap = TestResources.CreateTestBeatmapSetInfo(3);
|
||||
@@ -42,8 +46,9 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
b.Metadata = metadata;
|
||||
BeatmapSets.Add(baseTestBeatmap);
|
||||
});
|
||||
|
||||
WaitForFiltering();
|
||||
|
||||
AddAssert("filter count correct", () => Carousel.FilterCount, () => Is.EqualTo(initial_filter_count));
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -81,12 +86,18 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
|
||||
AddUntilStep("is scrolled to end", () => Carousel.ChildrenOfType<UserTrackingScrollContainer>().Single().IsScrolledToEnd());
|
||||
|
||||
updateBeatmap(b => b.Metadata = new BeatmapMetadata
|
||||
updateBeatmap(b =>
|
||||
{
|
||||
Artist = "updated test",
|
||||
Title = $"beatmap {RNG.Next().ToString()}"
|
||||
// hash will be updated when important metadata changes, such as title, difficulty, author etc.
|
||||
b.Hash = "new hash";
|
||||
b.Metadata = new BeatmapMetadata
|
||||
{
|
||||
Artist = "updated test",
|
||||
Title = $"beatmap {RNG.Next().ToString()}"
|
||||
};
|
||||
});
|
||||
|
||||
assertDidFilter();
|
||||
WaitForFiltering();
|
||||
|
||||
AddAssert("scroll is still at end", () => Carousel.ChildrenOfType<UserTrackingScrollContainer>().Single().IsScrolledToEnd());
|
||||
@@ -113,8 +124,14 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
|
||||
AddStep("find panel", () => panel = Carousel.ChildrenOfType<PanelBeatmapSet>().Single(p => p.ChildrenOfType<OsuSpriteText>().Any(t => t.Text.ToString() == "beatmap")));
|
||||
|
||||
updateBeatmap(b => b.Metadata = metadata);
|
||||
updateBeatmap(b =>
|
||||
{
|
||||
b.Metadata = metadata;
|
||||
// hash will be updated when important metadata changes, such as title, difficulty, author etc.
|
||||
b.Hash = "new hash";
|
||||
});
|
||||
|
||||
assertDidFilter();
|
||||
WaitForFiltering();
|
||||
|
||||
AddAssert("drawables unchanged", () => Carousel.ChildrenOfType<Panel>(), () => Is.EqualTo(originalDrawables));
|
||||
@@ -123,7 +140,41 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSelectionHeld()
|
||||
public void TestOnlineStatusUpdated()
|
||||
{
|
||||
List<Panel> originalDrawables = new List<Panel>();
|
||||
|
||||
AddStep("store drawable references", () =>
|
||||
{
|
||||
originalDrawables.Clear();
|
||||
originalDrawables.AddRange(Carousel.ChildrenOfType<Panel>().ToList());
|
||||
});
|
||||
|
||||
updateBeatmap(b => b.Status = BeatmapOnlineStatus.Graveyard);
|
||||
|
||||
assertDidFilter();
|
||||
WaitForFiltering();
|
||||
|
||||
AddAssert("drawables unchanged", () => Carousel.ChildrenOfType<Panel>(), () => Is.EqualTo(originalDrawables));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNoUpdateTriggeredOnUserTagsChange()
|
||||
{
|
||||
var metadata = new BeatmapMetadata
|
||||
{
|
||||
Artist = "updated test",
|
||||
Title = "new beatmap title",
|
||||
UserTags = { "hi" }
|
||||
};
|
||||
|
||||
updateBeatmap(b => b.Metadata = metadata);
|
||||
assertDidNotFilter();
|
||||
}
|
||||
|
||||
[TestCase(false)]
|
||||
[TestCase(true)]
|
||||
public void TestSelectionHeld(bool hashChanged)
|
||||
{
|
||||
SelectNextSet();
|
||||
|
||||
@@ -131,7 +182,17 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
AddAssert("selection is updateable beatmap", () => Carousel.CurrentSelection, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0]));
|
||||
AddAssert("visible panel is updateable beatmap", () => GetSelectedPanel()?.Item?.Model, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0]));
|
||||
|
||||
updateBeatmap();
|
||||
updateBeatmap(b =>
|
||||
{
|
||||
if (hashChanged)
|
||||
b.Hash = "new hash";
|
||||
});
|
||||
|
||||
if (hashChanged)
|
||||
assertDidFilter();
|
||||
else
|
||||
assertDidNotFilter();
|
||||
|
||||
WaitForFiltering();
|
||||
|
||||
AddAssert("selection is updateable beatmap", () => Carousel.CurrentSelection, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0]));
|
||||
@@ -148,6 +209,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
AddAssert("visible panel is updateable beatmap", () => GetSelectedPanel()?.Item?.Model, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0]));
|
||||
|
||||
updateBeatmap(b => b.DifficultyName = "new name");
|
||||
assertDidFilter();
|
||||
WaitForFiltering();
|
||||
|
||||
AddAssert("selection is updateable beatmap", () => Carousel.CurrentSelection, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0]));
|
||||
@@ -164,6 +226,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
AddAssert("visible panel is updateable beatmap", () => GetSelectedPanel()?.Item?.Model, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0]));
|
||||
|
||||
updateBeatmap(b => b.OnlineID = b.OnlineID + 1);
|
||||
assertDidFilter();
|
||||
WaitForFiltering();
|
||||
|
||||
AddAssert("selection is updateable beatmap", () => Carousel.CurrentSelection, () => Is.EqualTo(baseTestBeatmap.Beatmaps[0]));
|
||||
@@ -339,6 +402,10 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
AddAssert("Order didn't change", () => Carousel.PostFilterBeatmaps.Select(b => b.ID), () => Is.EqualTo(originalOrder));
|
||||
}
|
||||
|
||||
private void assertDidFilter() => AddAssert("did filter", () => Carousel.FilterCount, () => Is.EqualTo(initial_filter_count + 1));
|
||||
|
||||
private void assertDidNotFilter() => AddAssert("did not filter", () => Carousel.FilterCount, () => Is.EqualTo(initial_filter_count));
|
||||
|
||||
private void updateBeatmap(Action<BeatmapInfo>? updateBeatmap = null, Action<BeatmapSetInfo>? updateSet = null)
|
||||
{
|
||||
AddStep("update beatmap with different reference", () =>
|
||||
|
||||
@@ -23,7 +23,9 @@ using osu.Game.Screens.Ranking;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Screens.Select.Leaderboards;
|
||||
using osu.Game.Screens.SelectV2;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osuTK.Input;
|
||||
using BeatmapCarousel = osu.Game.Screens.SelectV2.BeatmapCarousel;
|
||||
using FooterButtonMods = osu.Game.Screens.SelectV2.FooterButtonMods;
|
||||
using FooterButtonOptions = osu.Game.Screens.SelectV2.FooterButtonOptions;
|
||||
using FooterButtonRandom = osu.Game.Screens.SelectV2.FooterButtonRandom;
|
||||
@@ -302,6 +304,28 @@ namespace osu.Game.Tests.Visual.SongSelectV2
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Last played and rank achieved may have changed, so we want to make sure filtering runs on resume to song select.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void TestFilteringRunsAfterReturningFromGameplay()
|
||||
{
|
||||
AddStep("import actual beatmap", () => Beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()));
|
||||
LoadSongSelect();
|
||||
|
||||
AddUntilStep("wait for filtered", () => SongSelect.ChildrenOfType<BeatmapCarousel>().Single().FilterCount, () => Is.EqualTo(1));
|
||||
|
||||
AddStep("enter gameplay", () => InputManager.Key(Key.Enter));
|
||||
|
||||
AddUntilStep("wait for player", () => Stack.CurrentScreen is Player);
|
||||
AddUntilStep("wait for fail", () => ((Player)Stack.CurrentScreen).GameplayState.HasFailed);
|
||||
|
||||
AddStep("exit gameplay", () => InputManager.Key(Key.Escape));
|
||||
AddStep("exit gameplay", () => InputManager.Key(Key.Escape));
|
||||
|
||||
AddUntilStep("wait for filtered", () => SongSelect.ChildrenOfType<BeatmapCarousel>().Single().FilterCount, () => Is.EqualTo(2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAutoplayShortcut()
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@@ -79,7 +80,7 @@ namespace osu.Game.Graphics.Carousel
|
||||
/// <summary>
|
||||
/// The number of times filter operations have been triggered.
|
||||
/// </summary>
|
||||
internal int FilterCount { get; private set; }
|
||||
public int FilterCount { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The number of displayable items currently being tracked (before filtering).
|
||||
@@ -210,6 +211,12 @@ namespace osu.Game.Graphics.Carousel
|
||||
return filterTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when <see cref="Items"/> changes in any way.
|
||||
/// </summary>
|
||||
/// <returns>Whether a re-filter is required.</returns>
|
||||
protected virtual bool HandleItemsChanged(NotifyCollectionChangedEventArgs args) => true;
|
||||
|
||||
/// <summary>
|
||||
/// Fired after a filter operation completed.
|
||||
/// </summary>
|
||||
@@ -301,7 +308,11 @@ namespace osu.Game.Graphics.Carousel
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
};
|
||||
|
||||
Items.BindCollectionChanged((_, _) => filterAfterItemsChanged.Invalidate());
|
||||
Items.BindCollectionChanged((_, args) =>
|
||||
{
|
||||
if (HandleItemsChanged(args))
|
||||
filterAfterItemsChanged.Invalidate();
|
||||
});
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
|
||||
@@ -180,7 +180,7 @@ namespace osu.Game.Overlays
|
||||
notification.Closed += () => notificationClosed(notification);
|
||||
|
||||
if (notification is IHasCompletionTarget hasCompletionTarget)
|
||||
hasCompletionTarget.CompletionTarget = Post;
|
||||
hasCompletionTarget.CompletionTarget ??= Post;
|
||||
|
||||
playDebouncedSample(notification.PopInSampleName);
|
||||
|
||||
|
||||
@@ -59,11 +59,7 @@ namespace osu.Game.Rulesets.Scoring
|
||||
|
||||
protected override void RevertResultInternal(JudgementResult result)
|
||||
{
|
||||
// TODO: this is rudimentary as to make rewinding failed replays work,
|
||||
// but it also acts up (sometimes rewinding a replay several times around the fail boundary moves the point of fail forward).
|
||||
// needs further investigation.
|
||||
if (result.FailedAtJudgement)
|
||||
HasFailed = false;
|
||||
HasFailed = result.FailedAtJudgement;
|
||||
|
||||
if (HasFailed)
|
||||
return;
|
||||
|
||||
@@ -178,17 +178,31 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (!isCandidateAudioSource(currentAudioSource?.SpectatorPlayerClock))
|
||||
{
|
||||
currentAudioSource = instances.Where(i => isCandidateAudioSource(i.SpectatorPlayerClock)).MinBy(i => Math.Abs(i.SpectatorPlayerClock.CurrentTime - syncManager.CurrentMasterTime));
|
||||
checkAudioSource();
|
||||
}
|
||||
|
||||
// Only bind adjustments if there's actually a valid source, else just use the previous ones to ensure no sudden changes to audio.
|
||||
if (currentAudioSource != null)
|
||||
bindAudioAdjustments(currentAudioSource);
|
||||
private void checkAudioSource()
|
||||
{
|
||||
// always use the maximised player instance as the current audio source if there is one
|
||||
if (grid.MaximisedCell?.Content is PlayerArea maximisedPlayer && maximisedPlayer == currentAudioSource)
|
||||
return;
|
||||
|
||||
foreach (var instance in instances)
|
||||
instance.Mute = instance != currentAudioSource;
|
||||
}
|
||||
// if there is no maximised player instance and the previous audio source is still good to use, keep using it
|
||||
if (grid.MaximisedCell == null && isCandidateAudioSource(currentAudioSource?.SpectatorPlayerClock))
|
||||
return;
|
||||
|
||||
// at this point we're in one of the following scenarios:
|
||||
// - the maximised player instance is not the current audio source => we want to switch to the maximised player instance
|
||||
// - there is no maximised player instance, and the previous audio source is stopped => find another running audio source
|
||||
currentAudioSource = grid.MaximisedCell?.Content as PlayerArea
|
||||
?? instances.Where(i => isCandidateAudioSource(i.SpectatorPlayerClock)).MinBy(i => Math.Abs(i.SpectatorPlayerClock.CurrentTime - syncManager.CurrentMasterTime));
|
||||
|
||||
// Only bind adjustments if there's actually a valid source, else just use the previous ones to ensure no sudden changes to audio.
|
||||
if (currentAudioSource != null)
|
||||
bindAudioAdjustments(currentAudioSource);
|
||||
|
||||
foreach (var instance in instances)
|
||||
instance.Mute = instance != currentAudioSource;
|
||||
}
|
||||
|
||||
private void bindAudioAdjustments(PlayerArea first)
|
||||
|
||||
@@ -31,6 +31,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
/// </summary>
|
||||
public Facade MaximisedFacade { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The currently-maximised cell.
|
||||
/// </summary>
|
||||
public Cell? MaximisedCell { get; private set; }
|
||||
|
||||
private readonly Container paddingContainer;
|
||||
private readonly FillFlowContainer<Facade> facadeContainer;
|
||||
private readonly Container<Cell> cellContainer;
|
||||
@@ -99,7 +104,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
private void toggleMaximisationState(Cell target)
|
||||
{
|
||||
// in the case the target is the already maximised cell (or there is only one cell), no cell should be maximised.
|
||||
bool hasMaximised = !target.IsMaximised && cellContainer.Count > 1;
|
||||
bool hasMaximised = target != MaximisedCell && cellContainer.Count > 1;
|
||||
MaximisedCell = hasMaximised ? target : null;
|
||||
|
||||
// Iterate through all cells to ensure only one is maximised at any time.
|
||||
foreach (var cell in cellContainer.ToList())
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
/// <summary>
|
||||
/// A cell of the grid. Contains the content and tracks to the linked facade.
|
||||
/// </summary>
|
||||
private partial class Cell : CompositeDrawable
|
||||
public partial class Cell : CompositeDrawable
|
||||
{
|
||||
/// <summary>
|
||||
/// The index of the original facade of this cell.
|
||||
@@ -33,11 +33,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
/// </summary>
|
||||
public Action<Cell>? ToggleMaximisationState;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this cell is currently maximised.
|
||||
/// </summary>
|
||||
public bool IsMaximised { get; private set; }
|
||||
|
||||
private Facade facade;
|
||||
|
||||
private bool isAnimating;
|
||||
@@ -83,7 +78,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
|
||||
public void SetFacade(Facade newFacade, bool isMaximised)
|
||||
{
|
||||
facade = newFacade;
|
||||
IsMaximised = isMaximised;
|
||||
isAnimating = true;
|
||||
|
||||
TweenEdgeEffectTo(new EdgeEffectParameters
|
||||
|
||||
@@ -143,6 +143,9 @@ namespace osu.Game.Screens.SelectV2
|
||||
switch (changed.Action)
|
||||
{
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
if (!newItems!.Any())
|
||||
return;
|
||||
|
||||
Items.AddRange(newItems!.SelectMany(s => s.Beatmaps));
|
||||
break;
|
||||
|
||||
@@ -353,6 +356,57 @@ namespace osu.Game.Screens.SelectV2
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool HandleItemsChanged(NotifyCollectionChangedEventArgs args)
|
||||
{
|
||||
switch (args.Action)
|
||||
{
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
case NotifyCollectionChangedAction.Move:
|
||||
case NotifyCollectionChangedAction.Reset:
|
||||
return true;
|
||||
|
||||
case NotifyCollectionChangedAction.Replace:
|
||||
var oldBeatmaps = args.OldItems!.OfType<BeatmapInfo>().ToList();
|
||||
var newBeatmaps = args.NewItems!.OfType<BeatmapInfo>().ToList();
|
||||
|
||||
for (int i = 0; i < oldBeatmaps.Count; i++)
|
||||
{
|
||||
var oldBeatmap = oldBeatmaps[i];
|
||||
var newBeatmap = newBeatmaps[i];
|
||||
|
||||
// Ignore changes which don't concern us.
|
||||
//
|
||||
// Here are some examples of things that can go wrong:
|
||||
// - Background difficulty calculation runs and causes a realm update.
|
||||
// We use `BeatmapDifficultyCache` and don't want to know about these.
|
||||
// - Background user tag population runs and causes a realm update.
|
||||
// We don't display user tags so want to ignore this.
|
||||
bool equalForDisplayPurposes =
|
||||
// covers metadata changes
|
||||
oldBeatmap.Hash == newBeatmap.Hash &&
|
||||
// sanity check
|
||||
oldBeatmap.OnlineID == newBeatmap.OnlineID &&
|
||||
// displayed on panel
|
||||
oldBeatmap.Status == newBeatmap.Status &&
|
||||
// displayed on panel
|
||||
oldBeatmap.DifficultyName == newBeatmap.DifficultyName &&
|
||||
// hidden changed, needs re-filter
|
||||
oldBeatmap.Hidden == newBeatmap.Hidden &&
|
||||
// might be used for grouping, returning from gameplay
|
||||
oldBeatmap.LastPlayed == newBeatmap.LastPlayed;
|
||||
|
||||
if (equalForDisplayPurposes)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void HandleFilterCompleted()
|
||||
{
|
||||
base.HandleFilterCompleted();
|
||||
|
||||
@@ -162,6 +162,9 @@ namespace osu.Game.Screens.SelectV2
|
||||
[Resolved]
|
||||
private OverlayColourProvider colourProvider { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private IBindable<RulesetInfo> ruleset { get; set; } = null!;
|
||||
|
||||
public ShearedKeyModeTabControl()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
@@ -212,13 +215,13 @@ namespace osu.Game.Screens.SelectV2
|
||||
Left = LabelContainer.DrawWidth + 8,
|
||||
};
|
||||
|
||||
const int mode_id = 3;
|
||||
var keyModes = EzSelectModes.GetModesForRuleset(mode_id)
|
||||
int modeID = ruleset.Value.OnlineID;
|
||||
var keyModes = EzSelectModes.GetModesForRuleset(modeID)
|
||||
.OrderBy(m => m.Id == "All" ? -1 : m.KeyCount ?? 0)
|
||||
.Select(m => m.Id)
|
||||
.ToList();
|
||||
|
||||
GetCurrentModeId.Value = mode_id;
|
||||
GetCurrentModeId.Value = modeID;
|
||||
Items = keyModes;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user