1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 23:12:56 +08:00

Merge branch 'master' into show-mod-settings-tooltip

This commit is contained in:
Dean Herbert 2020-03-23 14:57:54 +09:00
commit 4f9ac7b81e
58 changed files with 576 additions and 277 deletions

View File

@ -4,29 +4,25 @@
using System; using System;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning; using osu.Game.Skinning;
using osu.Game.Tests.Visual;
using osuTK; using osuTK;
namespace osu.Game.Rulesets.Osu.Tests namespace osu.Game.Rulesets.Osu.Tests
{ {
public class TestSceneHitCircleArea : ManualInputManagerTestScene public class TestSceneHitCircleArea : OsuManualInputManagerTestScene
{ {
private HitCircle hitCircle; private HitCircle hitCircle;
private DrawableHitCircle drawableHitCircle; private DrawableHitCircle drawableHitCircle;
private DrawableHitCircle.HitReceptor hitAreaReceptor => drawableHitCircle.HitArea; private DrawableHitCircle.HitReceptor hitAreaReceptor => drawableHitCircle.HitArea;
[SetUp] [SetUp]
public new void SetUp() public void SetUp() => Schedule(() =>
{
base.SetUp();
Schedule(() =>
{ {
hitCircle = new HitCircle hitCircle = new HitCircle
{ {
@ -45,7 +41,6 @@ namespace osu.Game.Rulesets.Osu.Tests
} }
}; };
}); });
}
[Test] [Test]
public void TestCircleHitCentre() public void TestCircleHitCentre()

View File

@ -23,7 +23,7 @@ using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Tests namespace osu.Game.Rulesets.Osu.Tests
{ {
public class TestSceneOsuDistanceSnapGrid : ManualInputManagerTestScene public class TestSceneOsuDistanceSnapGrid : OsuManualInputManagerTestScene
{ {
private const double beat_length = 100; private const double beat_length = 100;
private static readonly Vector2 grid_position = new Vector2(512, 384); private static readonly Vector2 grid_position = new Vector2(512, 384);

View File

@ -12,7 +12,7 @@ using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Osu.Tests namespace osu.Game.Rulesets.Osu.Tests
{ {
public class TestSceneResumeOverlay : ManualInputManagerTestScene public class TestSceneResumeOverlay : OsuManualInputManagerTestScene
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {

View File

@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
.FadeTo(tracking_alpha, 250, Easing.OutQuint); .FadeTo(tracking_alpha, 250, Easing.OutQuint);
} }
this.RotateTo(currentRotation / 2, validAndTracking ? 500 : 1500, Easing.OutExpo); Rotation = (float)Interpolation.Lerp(Rotation, currentRotation / 2, Math.Clamp(Math.Abs(Time.Elapsed) / 40, 0, 1));
} }
} }
} }

View File

@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Mods
[TestCase(false)] [TestCase(false)]
[TestCase(true)] [TestCase(true)]
public void TestHit(bool shouldMiss) => CreateHitObjectTest(new HitObjectTestData(new CentreHit { StartTime = 1000 }), shouldMiss); public void TestHit(bool shouldMiss) => CreateHitObjectTest(new HitObjectTestData(new Hit { StartTime = 1000, Type = HitType.Centre }), shouldMiss);
[TestCase(false)] [TestCase(false)]
[TestCase(true)] [TestCase(true)]

View File

@ -27,8 +27,8 @@ namespace osu.Game.Rulesets.Taiko.Tests
{ {
StartTime = hitObject.StartTime, StartTime = hitObject.StartTime,
EndTime = hitObject.GetEndTime(), EndTime = hitObject.GetEndTime(),
IsRim = hitObject is RimHit, IsRim = (hitObject as Hit)?.Type == HitType.Rim,
IsCentre = hitObject is CentreHit, IsCentre = (hitObject as Hit)?.Type == HitType.Centre,
IsDrumRoll = hitObject is DrumRoll, IsDrumRoll = hitObject is DrumRoll,
IsSwell = hitObject is Swell, IsSwell = hitObject is Swell,
IsStrong = ((TaikoHitObject)hitObject).IsStrong IsStrong = ((TaikoHitObject)hitObject).IsStrong

View File

@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
WorkingBeatmap beatmap = CreateWorkingBeatmap(new Beatmap WorkingBeatmap beatmap = CreateWorkingBeatmap(new Beatmap
{ {
HitObjects = new List<HitObject> { new CentreHit() }, HitObjects = new List<HitObject> { new Hit { Type = HitType.Centre } },
BeatmapInfo = new BeatmapInfo BeatmapInfo = new BeatmapInfo
{ {
BaseDifficulty = new BeatmapDifficulty(), BaseDifficulty = new BeatmapDifficulty(),

View File

@ -124,24 +124,13 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
bool isRim = currentSamples.Any(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE); bool isRim = currentSamples.Any(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE);
strong = currentSamples.Any(s => s.Name == HitSampleInfo.HIT_FINISH); strong = currentSamples.Any(s => s.Name == HitSampleInfo.HIT_FINISH);
if (isRim) yield return new Hit
{
yield return new RimHit
{ {
StartTime = j, StartTime = j,
Type = isRim ? HitType.Rim : HitType.Centre,
Samples = currentSamples, Samples = currentSamples,
IsStrong = strong IsStrong = strong
}; };
}
else
{
yield return new CentreHit
{
StartTime = j,
Samples = currentSamples,
IsStrong = strong
};
}
i = (i + 1) % allSamples.Count; i = (i + 1) % allSamples.Count;
} }
@ -180,24 +169,13 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
{ {
bool isRim = samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE); bool isRim = samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP || s.Name == HitSampleInfo.HIT_WHISTLE);
if (isRim) yield return new Hit
{
yield return new RimHit
{ {
StartTime = obj.StartTime, StartTime = obj.StartTime,
Type = isRim ? HitType.Rim : HitType.Centre,
Samples = obj.Samples, Samples = obj.Samples,
IsStrong = strong IsStrong = strong
}; };
}
else
{
yield return new CentreHit
{
StartTime = obj.StartTime,
Samples = obj.Samples,
IsStrong = strong
};
}
break; break;
} }

View File

@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
public TaikoDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate) public TaikoDifficultyHitObject(HitObject hitObject, HitObject lastObject, double clockRate)
: base(hitObject, lastObject, clockRate) : base(hitObject, lastObject, clockRate)
{ {
HasTypeChange = lastObject is RimHit != hitObject is RimHit; HasTypeChange = (lastObject as Hit)?.Type != (hitObject as Hit)?.Type;
} }
} }
} }

View File

@ -1,9 +0,0 @@
// 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.
namespace osu.Game.Rulesets.Taiko.Objects
{
public class CentreHit : Hit
{
}
}

View File

@ -5,5 +5,9 @@ namespace osu.Game.Rulesets.Taiko.Objects
{ {
public class Hit : TaikoHitObject public class Hit : TaikoHitObject
{ {
/// <summary>
/// The <see cref="HitType"/> that actuates this <see cref="Hit"/>.
/// </summary>
public HitType Type { get; set; }
} }
} }

View File

@ -0,0 +1,21 @@
// 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.
namespace osu.Game.Rulesets.Taiko.Objects
{
/// <summary>
/// The type of a <see cref="Hit"/>.
/// </summary>
public enum HitType
{
/// <summary>
/// A <see cref="Hit"/> that can be hit by the centre portion of the drum.
/// </summary>
Centre,
/// <summary>
/// A <see cref="Hit"/> that can be hit by the rim portion of the drum.
/// </summary>
Rim
}
}

View File

@ -1,9 +0,0 @@
// 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.
namespace osu.Game.Rulesets.Taiko.Objects
{
public class RimHit : Hit
{
}
}

View File

@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Taiko.Replays
{ {
TaikoAction[] actions; TaikoAction[] actions;
if (hit is CentreHit) if (hit.Type == HitType.Centre)
{ {
actions = h.IsStrong actions = h.IsStrong
? new[] { TaikoAction.LeftCentre, TaikoAction.RightCentre } ? new[] { TaikoAction.LeftCentre, TaikoAction.RightCentre }

View File

@ -48,11 +48,11 @@ namespace osu.Game.Rulesets.Taiko.UI
{ {
switch (h) switch (h)
{ {
case CentreHit centreHit: case Hit hit:
return new DrawableCentreHit(centreHit); if (hit.Type == HitType.Centre)
return new DrawableCentreHit(hit);
case RimHit rimHit: else
return new DrawableRimHit(rimHit); return new DrawableRimHit(hit);
case DrumRoll drumRoll: case DrumRoll drumRoll:
return new DrawableDrumRoll(drumRoll); return new DrawableDrumRoll(drumRoll);

View File

@ -14,9 +14,9 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Rulesets.UI.Scrolling;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Rulesets.Taiko.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -245,7 +245,7 @@ namespace osu.Game.Rulesets.Taiko.UI
if (!result.IsHit) if (!result.IsHit)
break; break;
bool isRim = judgedObject.HitObject is RimHit; bool isRim = (judgedObject.HitObject as Hit)?.Type == HitType.Rim;
hitExplosionContainer.Add(new HitExplosion(judgedObject, isRim)); hitExplosionContainer.Add(new HitExplosion(judgedObject, isRim));

View File

@ -37,7 +37,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Background namespace osu.Game.Tests.Visual.Background
{ {
[TestFixture] [TestFixture]
public class TestSceneUserDimBackgrounds : ManualInputManagerTestScene public class TestSceneUserDimBackgrounds : OsuManualInputManagerTestScene
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {
@ -278,6 +278,7 @@ namespace osu.Game.Tests.Visual.Background
private void setupUserSettings() private void setupUserSettings()
{ {
AddUntilStep("Song select is current", () => songSelect.IsCurrentScreen());
AddUntilStep("Song select has selection", () => songSelect.Carousel?.SelectedBeatmap != null); AddUntilStep("Song select has selection", () => songSelect.Carousel?.SelectedBeatmap != null);
AddStep("Set default user settings", () => AddStep("Set default user settings", () =>
{ {

View File

@ -12,7 +12,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Components namespace osu.Game.Tests.Visual.Components
{ {
[TestFixture] [TestFixture]
public class TestSceneIdleTracker : ManualInputManagerTestScene public class TestSceneIdleTracker : OsuManualInputManagerTestScene
{ {
private IdleTrackingBox box1; private IdleTrackingBox box1;
private IdleTrackingBox box2; private IdleTrackingBox box2;

View File

@ -16,7 +16,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editor
{ {
public class TestSceneBeatDivisorControl : ManualInputManagerTestScene public class TestSceneBeatDivisorControl : OsuManualInputManagerTestScene
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(BindableBeatDivisor) }; public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(BindableBeatDivisor) };
private BeatDivisorControl beatDivisorControl; private BeatDivisorControl beatDivisorControl;

View File

@ -17,7 +17,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editor
{ {
public class TestSceneZoomableScrollContainer : ManualInputManagerTestScene public class TestSceneZoomableScrollContainer : OsuManualInputManagerTestScene
{ {
private ZoomableScrollContainer scrollContainer; private ZoomableScrollContainer scrollContainer;
private Drawable innerBox; private Drawable innerBox;

View File

@ -18,7 +18,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
[Description("player pause/fail screens")] [Description("player pause/fail screens")]
public class TestSceneGameplayMenuOverlay : ManualInputManagerTestScene public class TestSceneGameplayMenuOverlay : OsuManualInputManagerTestScene
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseOverlay) }; public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseOverlay) };

View File

@ -15,7 +15,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
public class TestSceneHUDOverlay : ManualInputManagerTestScene public class TestSceneHUDOverlay : OsuManualInputManagerTestScene
{ {
private HUDOverlay hudOverlay; private HUDOverlay hudOverlay;

View File

@ -13,7 +13,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
[Description("'Hold to Quit' UI element")] [Description("'Hold to Quit' UI element")]
public class TestSceneHoldForMenuButton : ManualInputManagerTestScene public class TestSceneHoldForMenuButton : OsuManualInputManagerTestScene
{ {
private bool exitAction; private bool exitAction;

View File

@ -13,7 +13,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
[TestFixture] [TestFixture]
public class TestSceneKeyCounter : ManualInputManagerTestScene public class TestSceneKeyCounter : OsuManualInputManagerTestScene
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {

View File

@ -29,7 +29,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
public class TestScenePlayerLoader : ManualInputManagerTestScene public class TestScenePlayerLoader : OsuManualInputManagerTestScene
{ {
private TestPlayerLoader loader; private TestPlayerLoader loader;
private TestPlayerLoaderContainer container; private TestPlayerLoaderContainer container;

View File

@ -14,7 +14,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
[TestFixture] [TestFixture]
public class TestSceneSkipOverlay : ManualInputManagerTestScene public class TestSceneSkipOverlay : OsuManualInputManagerTestScene
{ {
private SkipOverlay skip; private SkipOverlay skip;
private int requestCount; private int requestCount;

View File

@ -20,7 +20,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Multiplayer namespace osu.Game.Tests.Visual.Multiplayer
{ {
public class TestSceneDrawableRoomPlaylist : ManualInputManagerTestScene public class TestSceneDrawableRoomPlaylist : OsuManualInputManagerTestScene
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {

View File

@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Navigation
/// <summary> /// <summary>
/// A scene which tests full game flow. /// A scene which tests full game flow.
/// </summary> /// </summary>
public abstract class OsuGameTestScene : ManualInputManagerTestScene public abstract class OsuGameTestScene : OsuManualInputManagerTestScene
{ {
private GameHost host; private GameHost host;

View File

@ -22,7 +22,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Online namespace osu.Game.Tests.Visual.Online
{ {
public class TestSceneChatOverlay : ManualInputManagerTestScene public class TestSceneChatOverlay : OsuManualInputManagerTestScene
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {

View File

@ -227,6 +227,34 @@ namespace osu.Game.Tests.Visual.SongSelect
waitForSelection(set_count); waitForSelection(set_count);
} }
[Test]
public void TestSelectionEnteringFromEmptyRuleset()
{
var sets = new List<BeatmapSetInfo>();
AddStep("Create beatmaps for taiko only", () =>
{
sets.Clear();
var rulesetBeatmapSet = createTestBeatmapSet(1);
var taikoRuleset = rulesets.AvailableRulesets.ElementAt(1);
rulesetBeatmapSet.Beatmaps.ForEach(b =>
{
b.Ruleset = taikoRuleset;
b.RulesetID = 1;
});
sets.Add(rulesetBeatmapSet);
});
loadBeatmaps(sets, () => new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(0) });
AddStep("Set non-empty mode filter", () =>
carousel.Filter(new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(1) }, false));
AddAssert("Something is selected", () => carousel.SelectedBeatmap != null);
}
/// <summary> /// <summary>
/// Test sorting /// Test sorting
/// </summary> /// </summary>
@ -399,27 +427,32 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("filter to ruleset 0", () => AddStep("filter to ruleset 0", () =>
carousel.Filter(new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(0) }, false)); carousel.Filter(new FilterCriteria { Ruleset = rulesets.AvailableRulesets.ElementAt(0) }, false));
AddStep("select filtered map skipping filtered", () => carousel.SelectBeatmap(testMixed.Beatmaps[1], false)); AddStep("select filtered map skipping filtered", () => carousel.SelectBeatmap(testMixed.Beatmaps[1], false));
AddAssert("unfiltered beatmap not selected", () => carousel.SelectedBeatmap == null); AddAssert("unfiltered beatmap not selected", () => carousel.SelectedBeatmap.RulesetID == 0);
AddStep("remove mixed set", () => AddStep("remove mixed set", () =>
{ {
carousel.RemoveBeatmapSet(testMixed); carousel.RemoveBeatmapSet(testMixed);
testMixed = null; testMixed = null;
}); });
var testSingle = createTestBeatmapSet(set_count + 2); BeatmapSetInfo testSingle = null;
AddStep("add single ruleset beatmapset", () =>
{
testSingle = createTestBeatmapSet(set_count + 2);
testSingle.Beatmaps.ForEach(b => testSingle.Beatmaps.ForEach(b =>
{ {
b.Ruleset = rulesets.AvailableRulesets.ElementAt(1); b.Ruleset = rulesets.AvailableRulesets.ElementAt(1);
b.RulesetID = b.Ruleset.ID ?? 1; b.RulesetID = b.Ruleset.ID ?? 1;
}); });
AddStep("add single ruleset beatmapset", () => carousel.UpdateBeatmapSet(testSingle));
carousel.UpdateBeatmapSet(testSingle);
});
AddStep("select filtered map skipping filtered", () => carousel.SelectBeatmap(testSingle.Beatmaps[0], false)); AddStep("select filtered map skipping filtered", () => carousel.SelectBeatmap(testSingle.Beatmaps[0], false));
checkNoSelection(); checkNoSelection();
AddStep("remove single ruleset set", () => carousel.RemoveBeatmapSet(testSingle)); AddStep("remove single ruleset set", () => carousel.RemoveBeatmapSet(testSingle));
} }
[Test] [Test]
public void TestCarouselRootIsRandom() public void TestCarouselRemembersSelection()
{ {
List<BeatmapSetInfo> manySets = new List<BeatmapSetInfo>(); List<BeatmapSetInfo> manySets = new List<BeatmapSetInfo>();
@ -429,12 +462,74 @@ namespace osu.Game.Tests.Visual.SongSelect
loadBeatmaps(manySets); loadBeatmaps(manySets);
advanceSelection(direction: 1, diff: false); advanceSelection(direction: 1, diff: false);
checkNonmatchingFilter();
checkNonmatchingFilter(); for (int i = 0; i < 5; i++)
checkNonmatchingFilter(); {
checkNonmatchingFilter(); AddStep("Toggle non-matching filter", () =>
checkNonmatchingFilter(); {
AddAssert("Selection was random", () => eagerSelectedIDs.Count > 1); carousel.Filter(new FilterCriteria { SearchText = Guid.NewGuid().ToString() }, false);
});
AddStep("Restore no filter", () =>
{
carousel.Filter(new FilterCriteria(), false);
eagerSelectedIDs.Add(carousel.SelectedBeatmapSet.ID);
});
}
// always returns to same selection as long as it's available.
AddAssert("Selection was remembered", () => eagerSelectedIDs.Count == 1);
}
[Test]
public void TestRandomFallbackOnNonMatchingPrevious()
{
List<BeatmapSetInfo> manySets = new List<BeatmapSetInfo>();
AddStep("populate maps", () =>
{
for (int i = 0; i < 10; i++)
{
var set = createTestBeatmapSet(i);
foreach (var b in set.Beatmaps)
{
// all taiko except for first
int ruleset = i > 0 ? 1 : 0;
b.Ruleset = rulesets.GetRuleset(ruleset);
b.RulesetID = ruleset;
}
manySets.Add(set);
}
});
loadBeatmaps(manySets);
for (int i = 0; i < 10; i++)
{
AddStep("Reset filter", () => carousel.Filter(new FilterCriteria(), false));
AddStep("select first beatmap", () => carousel.SelectBeatmap(manySets.First().Beatmaps.First()));
AddStep("Toggle non-matching filter", () =>
{
carousel.Filter(new FilterCriteria { SearchText = Guid.NewGuid().ToString() }, false);
});
AddAssert("selection lost", () => carousel.SelectedBeatmap == null);
AddStep("Restore different ruleset filter", () =>
{
carousel.Filter(new FilterCriteria { Ruleset = rulesets.GetRuleset(1) }, false);
eagerSelectedIDs.Add(carousel.SelectedBeatmapSet.ID);
});
AddAssert("selection changed", () => carousel.SelectedBeatmap != manySets.First().Beatmaps.First());
}
AddAssert("Selection was random", () => eagerSelectedIDs.Count > 2);
} }
[Test] [Test]
@ -484,7 +579,7 @@ namespace osu.Game.Tests.Visual.SongSelect
checkVisibleItemCount(true, 15); checkVisibleItemCount(true, 15);
} }
private void loadBeatmaps(List<BeatmapSetInfo> beatmapSets = null) private void loadBeatmaps(List<BeatmapSetInfo> beatmapSets = null, Func<FilterCriteria> initialCriteria = null)
{ {
createCarousel(); createCarousel();
@ -499,7 +594,7 @@ namespace osu.Game.Tests.Visual.SongSelect
bool changed = false; bool changed = false;
AddStep($"Load {(beatmapSets.Count > 0 ? beatmapSets.Count.ToString() : "some")} beatmaps", () => AddStep($"Load {(beatmapSets.Count > 0 ? beatmapSets.Count.ToString() : "some")} beatmaps", () =>
{ {
carousel.Filter(new FilterCriteria()); carousel.Filter(initialCriteria?.Invoke() ?? new FilterCriteria());
carousel.BeatmapSetsChanged = () => changed = true; carousel.BeatmapSetsChanged = () => changed = true;
carousel.BeatmapSets = beatmapSets; carousel.BeatmapSets = beatmapSets;
}); });
@ -593,16 +688,6 @@ namespace osu.Game.Tests.Visual.SongSelect
AddAssert("Selection is visible", selectedBeatmapVisible); AddAssert("Selection is visible", selectedBeatmapVisible);
} }
private void checkNonmatchingFilter()
{
AddStep("Toggle non-matching filter", () =>
{
carousel.Filter(new FilterCriteria { SearchText = "Dingo" }, false);
carousel.Filter(new FilterCriteria(), false);
eagerSelectedIDs.Add(carousel.SelectedBeatmapSet.ID);
});
}
private BeatmapSetInfo createTestBeatmapSet(int id) private BeatmapSetInfo createTestBeatmapSet(int id)
{ {
return new BeatmapSetInfo return new BeatmapSetInfo

View File

@ -15,7 +15,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface namespace osu.Game.Tests.Visual.UserInterface
{ {
public class TestSceneCommentEditor : ManualInputManagerTestScene public class TestSceneCommentEditor : OsuManualInputManagerTestScene
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {

View File

@ -17,7 +17,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface namespace osu.Game.Tests.Visual.UserInterface
{ {
[TestFixture] [TestFixture]
public class TestSceneCursors : ManualInputManagerTestScene public class TestSceneCursors : OsuManualInputManagerTestScene
{ {
private readonly MenuCursorContainer menuCursorContainer; private readonly MenuCursorContainer menuCursorContainer;
private readonly CustomCursorBox[] cursorBoxes = new CustomCursorBox[6]; private readonly CustomCursorBox[] cursorBoxes = new CustomCursorBox[6];

View File

@ -27,7 +27,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface namespace osu.Game.Tests.Visual.UserInterface
{ {
public class TestSceneDeleteLocalScore : ManualInputManagerTestScene public class TestSceneDeleteLocalScore : OsuManualInputManagerTestScene
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {

View File

@ -12,7 +12,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface namespace osu.Game.Tests.Visual.UserInterface
{ {
[TestFixture] [TestFixture]
public class TestSceneOsuHoverContainer : ManualInputManagerTestScene public class TestSceneOsuHoverContainer : OsuManualInputManagerTestScene
{ {
private OsuHoverTestContainer hoverContainer; private OsuHoverTestContainer hoverContainer;
private Box colourContainer; private Box colourContainer;

View File

@ -12,7 +12,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface namespace osu.Game.Tests.Visual.UserInterface
{ {
public class TestSceneStatefulMenuItem : ManualInputManagerTestScene public class TestSceneStatefulMenuItem : OsuManualInputManagerTestScene
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {

View File

@ -9,7 +9,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface namespace osu.Game.Tests.Visual.UserInterface
{ {
public class TestSceneSwitchButton : ManualInputManagerTestScene public class TestSceneSwitchButton : OsuManualInputManagerTestScene
{ {
private SwitchButton switchButton; private SwitchButton switchButton;

View File

@ -1,16 +1,147 @@
// 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.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Utils;
using osu.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Tournament.Models; using osu.Game.Tournament.Models;
using osu.Game.Users;
namespace osu.Game.Tournament.Tests namespace osu.Game.Tournament.Tests
{ {
[TestFixture] [TestFixture]
public abstract class LadderTestScene : TournamentTestScene public abstract class LadderTestScene : TournamentTestScene
{ {
[Cached]
protected LadderInfo Ladder { get; private set; } = new LadderInfo();
[Resolved] [Resolved]
protected LadderInfo Ladder { get; private set; } private RulesetStore rulesetStore { get; set; }
[BackgroundDependencyLoader]
private void load()
{
if (Ladder.Ruleset.Value == null)
Ladder.Ruleset.Value = rulesetStore.AvailableRulesets.First();
Ruleset.BindTo(Ladder.Ruleset);
}
protected override void LoadComplete()
{
base.LoadComplete();
TournamentMatch match = CreateSampleMatch();
Ladder.Rounds.Add(match.Round.Value);
Ladder.Matches.Add(match);
Ladder.Teams.Add(match.Team1.Value);
Ladder.Teams.Add(match.Team2.Value);
Ladder.CurrentMatch.Value = match;
}
public static TournamentMatch CreateSampleMatch() => new TournamentMatch
{
Team1 =
{
Value = new TournamentTeam
{
FlagName = { Value = "JP" },
FullName = { Value = "Japan" },
LastYearPlacing = { Value = 10 },
Seed = { Value = "Low" },
SeedingResults =
{
new SeedingResult
{
Mod = { Value = "NM" },
Seed = { Value = 10 },
Beatmaps =
{
new SeedingBeatmap
{
BeatmapInfo = CreateSampleBeatmapInfo(),
Score = 12345672,
Seed = { Value = 24 },
},
new SeedingBeatmap
{
BeatmapInfo = CreateSampleBeatmapInfo(),
Score = 1234567,
Seed = { Value = 12 },
},
new SeedingBeatmap
{
BeatmapInfo = CreateSampleBeatmapInfo(),
Score = 1234567,
Seed = { Value = 16 },
}
}
},
new SeedingResult
{
Mod = { Value = "DT" },
Seed = { Value = 5 },
Beatmaps =
{
new SeedingBeatmap
{
BeatmapInfo = CreateSampleBeatmapInfo(),
Score = 234567,
Seed = { Value = 3 },
},
new SeedingBeatmap
{
BeatmapInfo = CreateSampleBeatmapInfo(),
Score = 234567,
Seed = { Value = 6 },
},
new SeedingBeatmap
{
BeatmapInfo = CreateSampleBeatmapInfo(),
Score = 234567,
Seed = { Value = 12 },
}
}
}
},
Players =
{
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 12 } } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 16 } } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 20 } } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 24 } } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 30 } } },
}
}
},
Team2 =
{
Value = new TournamentTeam
{
FlagName = { Value = "US" },
FullName = { Value = "United States" },
Players =
{
new User { Username = "Hello" },
new User { Username = "Hello" },
new User { Username = "Hello" },
new User { Username = "Hello" },
new User { Username = "Hello" },
}
}
},
Round =
{
Value = new TournamentRound { Name = { Value = "Quarterfinals" } }
}
};
public static BeatmapInfo CreateSampleBeatmapInfo() =>
new BeatmapInfo { Metadata = new BeatmapMetadata { Title = "Test Title", Artist = "Test Artist", ID = RNG.Next(0, 1000000) } };
} }
} }

View File

@ -1,24 +1,140 @@
// 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 System.Linq;
using System.Collections.Generic; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
using osu.Framework.Testing;
using osu.Game.Tournament.Components;
using osu.Game.Tournament.Models;
using osu.Game.Tournament.Screens.MapPool; using osu.Game.Tournament.Screens.MapPool;
namespace osu.Game.Tournament.Tests.Screens namespace osu.Game.Tournament.Tests.Screens
{ {
public class TestSceneMapPoolScreen : LadderTestScene public class TestSceneMapPoolScreen : LadderTestScene
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] private MapPoolScreen screen;
{
typeof(MapPoolScreen)
};
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
Add(new MapPoolScreen { Width = 0.7f }); Add(screen = new MapPoolScreen { Width = 0.7f });
}
[Test]
public void TestFewMaps()
{
AddStep("load few maps", () =>
{
Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Clear();
for (int i = 0; i < 8; i++)
addBeatmap();
});
AddStep("reset match", () =>
{
Ladder.CurrentMatch.Value = new TournamentMatch();
Ladder.CurrentMatch.Value = Ladder.Matches.First();
});
assertTwoWide();
}
[Test]
public void TestJustEnoughMaps()
{
AddStep("load just enough maps", () =>
{
Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Clear();
for (int i = 0; i < 18; i++)
addBeatmap();
});
AddStep("reset match", () =>
{
Ladder.CurrentMatch.Value = new TournamentMatch();
Ladder.CurrentMatch.Value = Ladder.Matches.First();
});
assertTwoWide();
}
[Test]
public void TestManyMaps()
{
AddStep("load many maps", () =>
{
Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Clear();
for (int i = 0; i < 19; i++)
addBeatmap();
});
AddStep("reset match", () =>
{
Ladder.CurrentMatch.Value = new TournamentMatch();
Ladder.CurrentMatch.Value = Ladder.Matches.First();
});
assertThreeWide();
}
[Test]
public void TestJustEnoughMods()
{
AddStep("load many maps", () =>
{
Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Clear();
for (int i = 0; i < 11; i++)
addBeatmap(i > 4 ? $"M{i}" : "NM");
});
AddStep("reset match", () =>
{
Ladder.CurrentMatch.Value = new TournamentMatch();
Ladder.CurrentMatch.Value = Ladder.Matches.First();
});
assertTwoWide();
}
private void assertTwoWide() =>
AddAssert("ensure layout width is 2", () => screen.ChildrenOfType<FillFlowContainer<FillFlowContainer<TournamentBeatmapPanel>>>().First().Padding.Left > 0);
private void assertThreeWide() =>
AddAssert("ensure layout width is 3", () => screen.ChildrenOfType<FillFlowContainer<FillFlowContainer<TournamentBeatmapPanel>>>().First().Padding.Left == 0);
[Test]
public void TestManyMods()
{
AddStep("load many maps", () =>
{
Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Clear();
for (int i = 0; i < 12; i++)
addBeatmap(i > 4 ? $"M{i}" : "NM");
});
AddStep("reset match", () =>
{
Ladder.CurrentMatch.Value = new TournamentMatch();
Ladder.CurrentMatch.Value = Ladder.Matches.First();
});
assertThreeWide();
}
private void addBeatmap(string mods = "nm")
{
Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Add(new RoundBeatmap
{
BeatmapInfo = CreateSampleBeatmapInfo(),
Mods = mods
});
} }
} }
} }

View File

@ -14,7 +14,7 @@ namespace osu.Game.Tournament.Tests.Screens
public TestSceneSeedingEditorScreen() public TestSceneSeedingEditorScreen()
{ {
var match = TestSceneSeedingScreen.CreateSampleSeededMatch(); var match = CreateSampleMatch();
Add(new SeedingEditorScreen(match.Team1.Value) Add(new SeedingEditorScreen(match.Team1.Value)
{ {

View File

@ -3,10 +3,8 @@
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Tournament.Models; using osu.Game.Tournament.Models;
using osu.Game.Tournament.Screens.TeamIntro; using osu.Game.Tournament.Screens.TeamIntro;
using osu.Game.Users;
namespace osu.Game.Tournament.Tests.Screens namespace osu.Game.Tournament.Tests.Screens
{ {
@ -18,110 +16,11 @@ namespace osu.Game.Tournament.Tests.Screens
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
ladder.CurrentMatch.Value = CreateSampleSeededMatch();
Add(new SeedingScreen Add(new SeedingScreen
{ {
FillMode = FillMode.Fit, FillMode = FillMode.Fit,
FillAspectRatio = 16 / 9f FillAspectRatio = 16 / 9f
}); });
} }
public static TournamentMatch CreateSampleSeededMatch() => new TournamentMatch
{
Team1 =
{
Value = new TournamentTeam
{
FlagName = { Value = "JP" },
FullName = { Value = "Japan" },
LastYearPlacing = { Value = 10 },
Seed = { Value = "Low" },
SeedingResults =
{
new SeedingResult
{
Mod = { Value = "NM" },
Seed = { Value = 10 },
Beatmaps =
{
new SeedingBeatmap
{
BeatmapInfo = new BeatmapInfo { Metadata = new BeatmapMetadata { Title = "Test Title", Artist = "Test Artist" } },
Score = 12345672,
Seed = { Value = 24 },
},
new SeedingBeatmap
{
BeatmapInfo = new BeatmapInfo { Metadata = new BeatmapMetadata { Title = "Test Title", Artist = "Test Artist" } },
Score = 1234567,
Seed = { Value = 12 },
},
new SeedingBeatmap
{
BeatmapInfo = new BeatmapInfo { Metadata = new BeatmapMetadata { Title = "Test Title", Artist = "Test Artist" } },
Score = 1234567,
Seed = { Value = 16 },
}
}
},
new SeedingResult
{
Mod = { Value = "DT" },
Seed = { Value = 5 },
Beatmaps =
{
new SeedingBeatmap
{
BeatmapInfo = new BeatmapInfo { Metadata = new BeatmapMetadata { Title = "Test Title", Artist = "Test Artist" } },
Score = 234567,
Seed = { Value = 3 },
},
new SeedingBeatmap
{
BeatmapInfo = new BeatmapInfo { Metadata = new BeatmapMetadata { Title = "Test Title", Artist = "Test Artist" } },
Score = 234567,
Seed = { Value = 6 },
},
new SeedingBeatmap
{
BeatmapInfo = new BeatmapInfo { Metadata = new BeatmapMetadata { Title = "Test Title", Artist = "Test Artist" } },
Score = 234567,
Seed = { Value = 12 },
}
}
}
},
Players =
{
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 12 } } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 16 } } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 20 } } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 24 } } },
new User { Username = "Hello", Statistics = new UserStatistics { Ranks = new UserStatistics.UserRanks { Global = 30 } } },
}
}
},
Team2 =
{
Value = new TournamentTeam
{
FlagName = { Value = "US" },
FullName = { Value = "United States" },
Players =
{
new User { Username = "Hello" },
new User { Username = "Hello" },
new User { Username = "Hello" },
new User { Username = "Hello" },
new User { Username = "Hello" },
}
}
},
Round =
{
Value = new TournamentRound { Name = { Value = "Quarterfinals" } }
}
};
} }
} }

View File

@ -70,6 +70,17 @@ namespace osu.Game.Tournament.Components
protected override ChatLine CreateMessage(Message message) => new MatchMessage(message); protected override ChatLine CreateMessage(Message message) => new MatchMessage(message);
protected override StandAloneDrawableChannel CreateDrawableChannel(Channel channel) => new MatchChannel(channel);
public class MatchChannel : StandAloneDrawableChannel
{
public MatchChannel(Channel channel)
: base(channel)
{
ScrollbarVisible = false;
}
}
protected class MatchMessage : StandAloneMessage protected class MatchMessage : StandAloneMessage
{ {
public MatchMessage(Message message) public MatchMessage(Message message)

View File

@ -24,7 +24,13 @@ namespace osu.Game.Tournament.Models
// only used for serialisation // only used for serialisation
public List<TournamentProgression> Progressions = new List<TournamentProgression>(); public List<TournamentProgression> Progressions = new List<TournamentProgression>();
[JsonIgnore] [JsonIgnore] // updated manually in TournamentGameBase
public Bindable<TournamentMatch> CurrentMatch = new Bindable<TournamentMatch>(); public Bindable<TournamentMatch> CurrentMatch = new Bindable<TournamentMatch>();
public Bindable<int> ChromaKeyWidth = new BindableInt(1024)
{
MinValue = 640,
MaxValue = 1366,
};
} }
} }

View File

@ -9,6 +9,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Settings;
using osu.Game.Tournament.Components; using osu.Game.Tournament.Components;
using osu.Game.Tournament.IPC; using osu.Game.Tournament.IPC;
using osu.Game.Tournament.Models; using osu.Game.Tournament.Models;
@ -35,6 +36,8 @@ namespace osu.Game.Tournament.Screens.Gameplay
[Resolved] [Resolved]
private TournamentMatchChatDisplay chat { get; set; } private TournamentMatchChatDisplay chat { get; set; }
private Box chroma;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(LadderInfo ladder, MatchIPCInfo ipc, Storage storage) private void load(LadderInfo ladder, MatchIPCInfo ipc, Storage storage)
{ {
@ -60,11 +63,10 @@ namespace osu.Game.Tournament.Screens.Gameplay
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
Children = new Drawable[] Children = new Drawable[]
{ {
new Box chroma = new Box
{ {
// chroma key area for stable gameplay // chroma key area for stable gameplay
Name = "chroma", Name = "chroma",
RelativeSizeAxes = Axes.X,
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
Height = 512, Height = 512,
@ -93,6 +95,12 @@ namespace osu.Game.Tournament.Screens.Gameplay
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Text = "Toggle chat", Text = "Toggle chat",
Action = () => { State.Value = State.Value == TourneyState.Idle ? TourneyState.Playing : TourneyState.Idle; } Action = () => { State.Value = State.Value == TourneyState.Idle ? TourneyState.Playing : TourneyState.Idle; }
},
new SettingsSlider<int>
{
LabelText = "Chroma Width",
Bindable = LadderInfo.ChromaKeyWidth,
KeyboardStep = 1,
} }
} }
} }
@ -101,6 +109,8 @@ namespace osu.Game.Tournament.Screens.Gameplay
State.BindTo(ipc.State); State.BindTo(ipc.State);
State.BindValueChanged(stateChanged, true); State.BindValueChanged(stateChanged, true);
ladder.ChromaKeyWidth.BindValueChanged(width => chroma.Width = width.NewValue, true);
currentMatch.BindValueChanged(m => currentMatch.BindValueChanged(m =>
{ {
warmup.Value = m.NewValue.Team1Score.Value + m.NewValue.Team2Score.Value == 0; warmup.Value = m.NewValue.Team1Score.Value + m.NewValue.Team2Score.Value == 0;

View File

@ -50,11 +50,11 @@ namespace osu.Game.Tournament.Screens.MapPool
new MatchHeader(), new MatchHeader(),
mapFlows = new FillFlowContainer<FillFlowContainer<TournamentBeatmapPanel>> mapFlows = new FillFlowContainer<FillFlowContainer<TournamentBeatmapPanel>>
{ {
Y = 140, Y = 160,
Spacing = new Vector2(10, 10), Spacing = new Vector2(10, 10),
Padding = new MarginPadding(25),
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
}, },
new ControlPanel new ControlPanel
{ {
@ -95,6 +95,7 @@ namespace osu.Game.Tournament.Screens.MapPool
Text = "Reset", Text = "Reset",
Action = reset Action = reset
}, },
new ControlPanel.Spacer(),
} }
} }
}; };
@ -211,11 +212,15 @@ namespace osu.Game.Tournament.Screens.MapPool
{ {
mapFlows.Clear(); mapFlows.Clear();
int totalRows = 0;
if (match.NewValue.Round.Value != null) if (match.NewValue.Round.Value != null)
{ {
FillFlowContainer<TournamentBeatmapPanel> currentFlow = null; FillFlowContainer<TournamentBeatmapPanel> currentFlow = null;
string currentMod = null; string currentMod = null;
int flowCount = 0;
foreach (var b in match.NewValue.Round.Value.Beatmaps) foreach (var b in match.NewValue.Round.Value.Beatmaps)
{ {
if (currentFlow == null || currentMod != b.Mods) if (currentFlow == null || currentMod != b.Mods)
@ -229,6 +234,15 @@ namespace osu.Game.Tournament.Screens.MapPool
}); });
currentMod = b.Mods; currentMod = b.Mods;
totalRows++;
flowCount = 0;
}
if (++flowCount > 2)
{
totalRows++;
flowCount = 1;
} }
currentFlow.Add(new TournamentBeatmapPanel(b.BeatmapInfo, b.Mods) currentFlow.Add(new TournamentBeatmapPanel(b.BeatmapInfo, b.Mods)
@ -239,6 +253,12 @@ namespace osu.Game.Tournament.Screens.MapPool
}); });
} }
} }
mapFlows.Padding = new MarginPadding(5)
{
// remove horizontal padding to increase flow width to 3 panels
Horizontal = totalRows > 9 ? 0 : 100
};
} }
} }
} }

View File

@ -116,7 +116,7 @@ namespace osu.Game.Tournament.Screens
{ {
windowSize.Value = new Size((int)(1920 / TournamentSceneManager.STREAM_AREA_WIDTH * TournamentSceneManager.REQUIRED_WIDTH), 1080); windowSize.Value = new Size((int)(1920 / TournamentSceneManager.STREAM_AREA_WIDTH * TournamentSceneManager.REQUIRED_WIDTH), 1080);
} }
} },
}; };
} }

View File

@ -26,7 +26,7 @@ namespace osu.Game.Online.Chat
protected ChannelManager ChannelManager; protected ChannelManager ChannelManager;
private DrawableChannel drawableChannel; private StandAloneDrawableChannel drawableChannel;
private readonly bool postingTextbox; private readonly bool postingTextbox;
@ -77,6 +77,9 @@ namespace osu.Game.Online.Chat
ChannelManager = manager; ChannelManager = manager;
} }
protected virtual StandAloneDrawableChannel CreateDrawableChannel(Channel channel) =>
new StandAloneDrawableChannel(channel);
private void postMessage(TextBox sender, bool newtext) private void postMessage(TextBox sender, bool newtext)
{ {
var text = textbox.Text.Trim(); var text = textbox.Text.Trim();
@ -100,14 +103,14 @@ namespace osu.Game.Online.Chat
if (e.NewValue == null) return; if (e.NewValue == null) return;
AddInternal(drawableChannel = new StandAloneDrawableChannel(e.NewValue) drawableChannel = CreateDrawableChannel(e.NewValue);
{ drawableChannel.CreateChatLineAction = CreateMessage;
CreateChatLineAction = CreateMessage, drawableChannel.Padding = new MarginPadding { Bottom = postingTextbox ? textbox_height : 0 };
Padding = new MarginPadding { Bottom = postingTextbox ? textbox_height : 0 }
}); AddInternal(drawableChannel);
} }
protected class StandAloneDrawableChannel : DrawableChannel public class StandAloneDrawableChannel : DrawableChannel
{ {
public Func<Message, ChatLine> CreateChatLineAction; public Func<Message, ChatLine> CreateChatLineAction;

View File

@ -47,6 +47,8 @@ namespace osu.Game
{ {
public const string CLIENT_STREAM_NAME = "lazer"; public const string CLIENT_STREAM_NAME = "lazer";
public const int SAMPLE_CONCURRENCY = 6;
protected OsuConfigManager LocalConfig; protected OsuConfigManager LocalConfig;
protected BeatmapManager BeatmapManager; protected BeatmapManager BeatmapManager;
@ -153,6 +155,8 @@ namespace osu.Game
AddFont(Resources, @"Fonts/Venera-Bold"); AddFont(Resources, @"Fonts/Venera-Bold");
AddFont(Resources, @"Fonts/Venera-Black"); AddFont(Resources, @"Fonts/Venera-Black");
Audio.Samples.PlaybackConcurrency = SAMPLE_CONCURRENCY;
runMigrations(); runMigrations();
dependencies.Cache(SkinManager = new SkinManager(Storage, contextFactory, Host, Audio, new NamespacedResourceStore<byte[]>(Resources, "Skins/Legacy"))); dependencies.Cache(SkinManager = new SkinManager(Storage, contextFactory, Host, Audio, new NamespacedResourceStore<byte[]>(Resources, "Skins/Legacy")));

View File

@ -26,6 +26,20 @@ namespace osu.Game.Overlays.Chat
protected FillFlowContainer ChatLineFlow; protected FillFlowContainer ChatLineFlow;
private OsuScrollContainer scroll; private OsuScrollContainer scroll;
private bool scrollbarVisible = true;
public bool ScrollbarVisible
{
set
{
if (scrollbarVisible == value) return;
scrollbarVisible = value;
if (scroll != null)
scroll.ScrollbarVisible = value;
}
}
[Resolved] [Resolved]
private OsuColour colours { get; set; } private OsuColour colours { get; set; }
@ -44,6 +58,7 @@ namespace osu.Game.Overlays.Chat
Masking = true, Masking = true,
Child = scroll = new OsuScrollContainer Child = scroll = new OsuScrollContainer
{ {
ScrollbarVisible = scrollbarVisible,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
// Some chat lines have effects that slightly protrude to the bottom, // Some chat lines have effects that slightly protrude to the bottom,
// which we do not want to mask away, hence the padding. // which we do not want to mask away, hence the padding.

View File

@ -8,16 +8,14 @@ namespace osu.Game.Overlays.Settings
{ {
public class SettingsCheckbox : SettingsItem<bool> public class SettingsCheckbox : SettingsItem<bool>
{ {
private OsuCheckbox checkbox;
private string labelText; private string labelText;
protected override Drawable CreateControl() => checkbox = new OsuCheckbox(); protected override Drawable CreateControl() => new OsuCheckbox();
public override string LabelText public override string LabelText
{ {
get => labelText; get => labelText;
set => checkbox.LabelText = labelText = value; set => ((OsuCheckbox)Control).LabelText = labelText = value;
} }
} }
} }

View File

@ -33,22 +33,24 @@ namespace osu.Game.Overlays.Settings
protected readonly FillFlowContainer FlowContent; protected readonly FillFlowContainer FlowContent;
private SpriteText text; private SpriteText labelText;
public bool ShowsDefaultIndicator = true; public bool ShowsDefaultIndicator = true;
public virtual string LabelText public virtual string LabelText
{ {
get => text?.Text ?? string.Empty; get => labelText?.Text ?? string.Empty;
set set
{ {
if (text == null) if (labelText == null)
{ {
// construct lazily for cases where the label is not needed (may be provided by the Control). // construct lazily for cases where the label is not needed (may be provided by the Control).
FlowContent.Insert(-1, text = new OsuSpriteText()); FlowContent.Insert(-1, labelText = new OsuSpriteText());
updateDisabled();
} }
text.Text = value; labelText.Text = value;
} }
} }
@ -96,13 +98,19 @@ namespace osu.Game.Overlays.Settings
if (controlWithCurrent != null) if (controlWithCurrent != null)
{ {
controlWithCurrent.Current.ValueChanged += _ => SettingChanged?.Invoke(); controlWithCurrent.Current.ValueChanged += _ => SettingChanged?.Invoke();
controlWithCurrent.Current.DisabledChanged += disabled => { Colour = disabled ? Color4.Gray : Color4.White; }; controlWithCurrent.Current.DisabledChanged += _ => updateDisabled();
if (ShowsDefaultIndicator) if (ShowsDefaultIndicator)
restoreDefaultButton.Bindable = controlWithCurrent.Current; restoreDefaultButton.Bindable = controlWithCurrent.Current;
} }
} }
private void updateDisabled()
{
if (labelText != null)
labelText.Alpha = controlWithCurrent.Current.Disabled ? 0.3f : 1;
}
private class RestoreDefaultValueButton : Container, IHasTooltip private class RestoreDefaultValueButton : Container, IHasTooltip
{ {
private Bindable<T> bindable; private Bindable<T> bindable;

View File

@ -158,6 +158,7 @@ namespace osu.Game.Rulesets.UI
dependencies.Cache(textureStore); dependencies.Cache(textureStore);
localSampleStore = dependencies.Get<AudioManager>().GetSampleStore(new NamespacedResourceStore<byte[]>(resources, "Samples")); localSampleStore = dependencies.Get<AudioManager>().GetSampleStore(new NamespacedResourceStore<byte[]>(resources, "Samples"));
localSampleStore.PlaybackConcurrency = OsuGameBase.SAMPLE_CONCURRENCY;
dependencies.CacheAs<ISampleStore>(new FallbackSampleStore(localSampleStore, dependencies.Get<ISampleStore>())); dependencies.CacheAs<ISampleStore>(new FallbackSampleStore(localSampleStore, dependencies.Get<ISampleStore>()));
} }

View File

@ -333,8 +333,7 @@ namespace osu.Game.Screens.Select
else else
set = visibleSets.ElementAt(RNG.Next(visibleSets.Count)); set = visibleSets.ElementAt(RNG.Next(visibleSets.Count));
var visibleBeatmaps = set.Beatmaps.Where(s => !s.Filtered.Value).ToList(); select(set);
select(visibleBeatmaps[RNG.Next(visibleBeatmaps.Count)]);
return true; return true;
} }
@ -751,13 +750,17 @@ namespace osu.Game.Screens.Select
public CarouselRoot(BeatmapCarousel carousel) public CarouselRoot(BeatmapCarousel carousel)
{ {
// root should always remain selected. if not, PerformSelection will not be called.
State.Value = CarouselItemState.Selected;
State.ValueChanged += state => State.Value = CarouselItemState.Selected;
this.carousel = carousel; this.carousel = carousel;
} }
protected override void PerformSelection() protected override void PerformSelection()
{ {
if (LastSelected == null) if (LastSelected == null || LastSelected.Filtered.Value)
carousel.SelectNextRandom(); carousel?.SelectNextRandom();
else else
base.PerformSelection(); base.PerformSelection();
} }

View File

@ -104,6 +104,7 @@ namespace osu.Game.Screens.Select.Carousel
private void updateSelected(CarouselItem newSelection) private void updateSelected(CarouselItem newSelection)
{ {
if (newSelection != null)
LastSelected = newSelection; LastSelected = newSelection;
updateSelectedIndex(); updateSelectedIndex();
} }

View File

@ -150,6 +150,7 @@ namespace osu.Game.Screens.Select
}, },
Child = Carousel = new BeatmapCarousel Child = Carousel = new BeatmapCarousel
{ {
AllowSelection = false, // delay any selection until our bindables are ready to make a good choice.
Anchor = Anchor.CentreRight, Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight, Origin = Anchor.CentreRight,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
@ -655,6 +656,8 @@ namespace osu.Game.Screens.Select
{ {
bindBindables(); bindBindables();
Carousel.AllowSelection = true;
// If a selection was already obtained, do not attempt to update the selected beatmap. // If a selection was already obtained, do not attempt to update the selected beatmap.
if (Carousel.SelectedBeatmapSet != null) if (Carousel.SelectedBeatmapSet != null)
return; return;

View File

@ -52,7 +52,11 @@ namespace osu.Game.Skinning
if (storage != null) if (storage != null)
{ {
Samples = audioManager?.GetSampleStore(storage); var samples = audioManager?.GetSampleStore(storage);
if (samples != null)
samples.PlaybackConcurrency = OsuGameBase.SAMPLE_CONCURRENCY;
Samples = samples;
Textures = new TextureStore(new TextureLoaderStore(storage)); Textures = new TextureStore(new TextureLoaderStore(storage));
(storage as ResourceStore<byte[]>)?.AddExtension("ogg"); (storage as ResourceStore<byte[]>)?.AddExtension("ogg");

View File

@ -14,7 +14,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
public abstract class ManualInputManagerTestScene : OsuTestScene public abstract class OsuManualInputManagerTestScene : OsuTestScene
{ {
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;
private readonly Container content; private readonly Container content;
@ -24,7 +24,7 @@ namespace osu.Game.Tests.Visual
private readonly TriangleButton buttonTest; private readonly TriangleButton buttonTest;
private readonly TriangleButton buttonLocal; private readonly TriangleButton buttonLocal;
protected ManualInputManagerTestScene() protected OsuManualInputManagerTestScene()
{ {
base.Content.AddRange(new Drawable[] base.Content.AddRange(new Drawable[]
{ {

View File

@ -11,7 +11,7 @@ namespace osu.Game.Tests.Visual
/// <summary> /// <summary>
/// A test case which can be used to test a screen (that relies on OnEntering being called to execute startup instructions). /// A test case which can be used to test a screen (that relies on OnEntering being called to execute startup instructions).
/// </summary> /// </summary>
public abstract class ScreenTestScene : ManualInputManagerTestScene public abstract class ScreenTestScene : OsuManualInputManagerTestScene
{ {
protected readonly OsuScreenStack Stack; protected readonly OsuScreenStack Stack;

View File

@ -8,7 +8,7 @@ using osu.Game.Rulesets.Edit;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
public abstract class SelectionBlueprintTestScene : ManualInputManagerTestScene public abstract class SelectionBlueprintTestScene : OsuManualInputManagerTestScene
{ {
protected override Container<Drawable> Content => content ?? base.Content; protected override Container<Drawable> Content => content ?? base.Content;
private readonly Container content; private readonly Container content;