mirror of
https://github.com/ppy/osu.git
synced 2025-02-15 15:23:14 +08:00
Merge branch 'master' into fix-score-import-fail-fail-fail
This commit is contained in:
commit
db893f3dc7
@ -10,7 +10,7 @@
|
||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.724.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.817.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||
|
@ -1,5 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="sh.ppy.osulazer" android:installLocation="auto">
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
|
||||
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!" android:icon="@drawable/lazer" />
|
||||
<!-- for editor usage -->
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
|
||||
</manifest>
|
@ -75,7 +75,7 @@ namespace osu.Desktop.LegacyIpc
|
||||
case LegacyIpcDifficultyCalculationRequest req:
|
||||
try
|
||||
{
|
||||
WorkingBeatmap beatmap = new FlatFileWorkingBeatmap(req.BeatmapFile);
|
||||
WorkingBeatmap beatmap = new FlatWorkingBeatmap(req.BeatmapFile);
|
||||
var ruleset = beatmap.BeatmapInfo.Ruleset.CreateInstance();
|
||||
Mod[] mods = ruleset.ConvertFromLegacyMods((LegacyMods)req.Mods).ToArray();
|
||||
|
||||
|
@ -85,7 +85,7 @@ namespace osu.Desktop
|
||||
}
|
||||
}
|
||||
|
||||
using (DesktopGameHost host = Host.GetSuitableDesktopHost(gameName, new HostOptions { BindIPC = true }))
|
||||
using (DesktopGameHost host = Host.GetSuitableDesktopHost(gameName, new HostOptions { BindIPC = !tournamentClient }))
|
||||
{
|
||||
if (!host.IsPrimaryInstance)
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- using a different name because package name cannot contain 'catch' -->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Catch_Tests.Android" android:installLocation="auto">
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
|
||||
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!catch Test" />
|
||||
</manifest>
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Mania.Tests.Android" android:installLocation="auto">
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
|
||||
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!mania Test" />
|
||||
</manifest>
|
@ -6,7 +6,6 @@ using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Mods;
|
||||
using osu.Game.Tests.Visual;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
@ -24,21 +23,6 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods
|
||||
Assert.False(testBeatmap.HitObjects.OfType<HoldNote>().Any());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCorrectNoteValues()
|
||||
{
|
||||
var testBeatmap = createRawBeatmap();
|
||||
var noteValues = new List<double>(testBeatmap.HitObjects.OfType<HoldNote>().Count());
|
||||
|
||||
foreach (HoldNote h in testBeatmap.HitObjects.OfType<HoldNote>())
|
||||
{
|
||||
noteValues.Add(ManiaModHoldOff.GetNoteDurationInBeatLength(h, testBeatmap));
|
||||
}
|
||||
|
||||
noteValues.Sort();
|
||||
Assert.AreEqual(noteValues, new List<double> { 0.125, 0.250, 0.500, 1.000, 2.000 });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCorrectObjectCount()
|
||||
{
|
||||
@ -47,25 +31,8 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods
|
||||
var rawBeatmap = createRawBeatmap();
|
||||
var testBeatmap = createModdedBeatmap();
|
||||
|
||||
// Calculate expected number of objects
|
||||
int expectedObjectCount = 0;
|
||||
|
||||
foreach (ManiaHitObject h in rawBeatmap.HitObjects)
|
||||
{
|
||||
// Both notes and hold notes account for at least one object
|
||||
expectedObjectCount++;
|
||||
|
||||
if (h.GetType() == typeof(HoldNote))
|
||||
{
|
||||
double noteValue = ManiaModHoldOff.GetNoteDurationInBeatLength((HoldNote)h, rawBeatmap);
|
||||
|
||||
if (noteValue >= ManiaModHoldOff.END_NOTE_ALLOW_THRESHOLD)
|
||||
{
|
||||
// Should generate an end note if it's longer than the minimum note value
|
||||
expectedObjectCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Both notes and hold notes account for at least one object
|
||||
int expectedObjectCount = rawBeatmap.HitObjects.Count;
|
||||
|
||||
Assert.That(testBeatmap.HitObjects.Count == expectedObjectCount);
|
||||
}
|
||||
|
@ -117,18 +117,16 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
|
||||
private void createBarLine(bool major)
|
||||
{
|
||||
foreach (var stage in stages)
|
||||
var obj = new BarLine
|
||||
{
|
||||
var obj = new BarLine
|
||||
{
|
||||
StartTime = Time.Current + 2000,
|
||||
Major = major,
|
||||
};
|
||||
StartTime = Time.Current + 2000,
|
||||
Major = major,
|
||||
};
|
||||
|
||||
obj.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
obj.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
|
||||
foreach (var stage in stages)
|
||||
stage.Add(obj);
|
||||
}
|
||||
}
|
||||
|
||||
private ScrollingTestContainer createStage(ScrollingDirection direction, ManiaAction action)
|
||||
|
@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
||||
{
|
||||
private const double individual_decay_base = 0.125;
|
||||
private const double overall_decay_base = 0.30;
|
||||
private const double release_threshold = 24;
|
||||
private const double release_threshold = 30;
|
||||
|
||||
protected override double SkillMultiplier => 1;
|
||||
protected override double StrainDecayBase => 1;
|
||||
@ -50,10 +50,13 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
||||
for (int i = 0; i < endTimes.Length; ++i)
|
||||
{
|
||||
// The current note is overlapped if a previous note or end is overlapping the current note body
|
||||
isOverlapping |= Precision.DefinitelyBigger(endTimes[i], startTime, 1) && Precision.DefinitelyBigger(endTime, endTimes[i], 1);
|
||||
isOverlapping |= Precision.DefinitelyBigger(endTimes[i], startTime, 1) &&
|
||||
Precision.DefinitelyBigger(endTime, endTimes[i], 1) &&
|
||||
Precision.DefinitelyBigger(startTime, startTimes[i], 1);
|
||||
|
||||
// We give a slight bonus to everything if something is held meanwhile
|
||||
if (Precision.DefinitelyBigger(endTimes[i], endTime, 1))
|
||||
if (Precision.DefinitelyBigger(endTimes[i], endTime, 1) &&
|
||||
Precision.DefinitelyBigger(startTime, startTimes[i], 1))
|
||||
holdFactor = 1.25;
|
||||
|
||||
closestEndTime = Math.Min(closestEndTime, Math.Abs(endTime - endTimes[i]));
|
||||
@ -70,7 +73,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills
|
||||
// 0.0 +--------+-+---------------> Release Difference / ms
|
||||
// release_threshold
|
||||
if (isOverlapping)
|
||||
holdAddition = 1 / (1 + Math.Exp(0.5 * (release_threshold - closestEndTime)));
|
||||
holdAddition = 1 / (1 + Math.Exp(0.27 * (release_threshold - closestEndTime)));
|
||||
|
||||
// Decay and increase individualStrains in own column
|
||||
individualStrains[column] = applyDecay(individualStrains[column], startTime - startTimes[column], individual_decay_base);
|
||||
|
@ -33,5 +33,6 @@ namespace osu.Game.Rulesets.Mania
|
||||
HitExplosion,
|
||||
StageBackground,
|
||||
StageForeground,
|
||||
BarLine
|
||||
}
|
||||
}
|
||||
|
@ -29,8 +29,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ManiaModInvert) };
|
||||
|
||||
public const double END_NOTE_ALLOW_THRESHOLD = 0.5;
|
||||
|
||||
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||
{
|
||||
var maniaBeatmap = (ManiaBeatmap)beatmap;
|
||||
@ -46,28 +44,9 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
StartTime = h.StartTime,
|
||||
Samples = h.GetNodeSamples(0)
|
||||
});
|
||||
|
||||
// Don't add an end note if the duration is shorter than the threshold
|
||||
double noteValue = GetNoteDurationInBeatLength(h, maniaBeatmap); // 1/1, 1/2, 1/4, etc.
|
||||
|
||||
if (noteValue >= END_NOTE_ALLOW_THRESHOLD)
|
||||
{
|
||||
newObjects.Add(new Note
|
||||
{
|
||||
Column = h.Column,
|
||||
StartTime = h.EndTime,
|
||||
Samples = h.GetNodeSamples((h.NodeSamples?.Count - 1) ?? 1)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
maniaBeatmap.HitObjects = maniaBeatmap.HitObjects.OfType<Note>().Concat(newObjects).OrderBy(h => h.StartTime).ToList();
|
||||
}
|
||||
|
||||
public static double GetNoteDurationInBeatLength(HoldNote holdNote, ManiaBeatmap beatmap)
|
||||
{
|
||||
double beatLength = beatmap.ControlPointInfo.TimingPointAt(holdNote.StartTime).BeatLength;
|
||||
return holdNote.Duration / beatLength;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 osu.Framework.Bindables;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
||||
@ -8,7 +9,15 @@ namespace osu.Game.Rulesets.Mania.Objects
|
||||
{
|
||||
public class BarLine : ManiaHitObject, IBarLine
|
||||
{
|
||||
public bool Major { get; set; }
|
||||
private HitObjectProperty<bool> major;
|
||||
|
||||
public Bindable<bool> MajorBindable => major.Bindable;
|
||||
|
||||
public bool Major
|
||||
{
|
||||
get => major.Value;
|
||||
set => major.Value = value;
|
||||
}
|
||||
|
||||
public override Judgement CreateJudgement() => new IgnoreJudgement();
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osuTK;
|
||||
using osu.Game.Rulesets.Mania.Skinning.Default;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
{
|
||||
@ -13,45 +15,41 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
|
||||
/// </summary>
|
||||
public partial class DrawableBarLine : DrawableManiaHitObject<BarLine>
|
||||
{
|
||||
public readonly Bindable<bool> Major = new Bindable<bool>();
|
||||
|
||||
public DrawableBarLine()
|
||||
: this(null!)
|
||||
{
|
||||
}
|
||||
|
||||
public DrawableBarLine(BarLine barLine)
|
||||
: base(barLine)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
Height = barLine.Major ? 1.7f : 1.2f;
|
||||
}
|
||||
|
||||
AddInternal(new Box
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
AddInternal(new SkinnableDrawable(new ManiaSkinComponentLookup(ManiaSkinComponents.BarLine), _ => new DefaultBarLine())
|
||||
{
|
||||
Name = "Bar line",
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = barLine.Major ? 0.5f : 0.2f
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
});
|
||||
|
||||
if (barLine.Major)
|
||||
{
|
||||
Vector2 size = new Vector2(22, 6);
|
||||
const float line_offset = 4;
|
||||
Major.BindValueChanged(major => Height = major.NewValue ? 1.7f : 1.2f, true);
|
||||
}
|
||||
|
||||
AddInternal(new Circle
|
||||
{
|
||||
Name = "Left line",
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreRight,
|
||||
protected override void OnApply()
|
||||
{
|
||||
base.OnApply();
|
||||
Major.BindTo(HitObject.MajorBindable);
|
||||
}
|
||||
|
||||
Size = size,
|
||||
X = -line_offset,
|
||||
});
|
||||
|
||||
AddInternal(new Circle
|
||||
{
|
||||
Name = "Right line",
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Size = size,
|
||||
X = line_offset,
|
||||
});
|
||||
}
|
||||
protected override void OnFree()
|
||||
{
|
||||
base.OnFree();
|
||||
Major.UnbindFrom(HitObject.MajorBindable);
|
||||
}
|
||||
|
||||
protected override void UpdateStartTimeStateTransforms() => this.FadeOut(150);
|
||||
|
72
osu.Game.Rulesets.Mania/Skinning/Default/DefaultBarLine.cs
Normal file
72
osu.Game.Rulesets.Mania/Skinning/Default/DefaultBarLine.cs
Normal file
@ -0,0 +1,72 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Skinning.Default
|
||||
{
|
||||
public partial class DefaultBarLine : CompositeDrawable
|
||||
{
|
||||
private Bindable<bool> major = null!;
|
||||
|
||||
private Drawable mainLine = null!;
|
||||
private Drawable leftAnchor = null!;
|
||||
private Drawable rightAnchor = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(DrawableHitObject drawableHitObject)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
AddInternal(mainLine = new Box
|
||||
{
|
||||
Name = "Bar line",
|
||||
Anchor = Anchor.BottomCentre,
|
||||
Origin = Anchor.BottomCentre,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
});
|
||||
|
||||
Vector2 size = new Vector2(22, 6);
|
||||
const float line_offset = 4;
|
||||
|
||||
AddInternal(leftAnchor = new Circle
|
||||
{
|
||||
Name = "Left anchor",
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreRight,
|
||||
Size = size,
|
||||
X = -line_offset,
|
||||
});
|
||||
|
||||
AddInternal(rightAnchor = new Circle
|
||||
{
|
||||
Name = "Right anchor",
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreLeft,
|
||||
Size = size,
|
||||
X = line_offset,
|
||||
});
|
||||
|
||||
major = ((DrawableBarLine)drawableHitObject).Major.GetBoundCopy();
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
major.BindValueChanged(updateMajor, true);
|
||||
}
|
||||
|
||||
private void updateMajor(ValueChangedEvent<bool> major)
|
||||
{
|
||||
mainLine.Alpha = major.NewValue ? 0.5f : 0.2f;
|
||||
leftAnchor.Alpha = rightAnchor.Alpha = major.NewValue ? 1 : 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -119,6 +119,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
||||
case ManiaSkinComponents.StageForeground:
|
||||
return new LegacyStageForeground();
|
||||
|
||||
case ManiaSkinComponents.BarLine:
|
||||
return null; // Not yet implemented.
|
||||
|
||||
default:
|
||||
throw new UnsupportedSkinComponentException(lookup);
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
@ -25,6 +26,22 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
private readonly List<Stage> stages = new List<Stage>();
|
||||
|
||||
public override Quad SkinnableComponentScreenSpaceDrawQuad
|
||||
{
|
||||
get
|
||||
{
|
||||
RectangleF totalArea = RectangleF.Empty;
|
||||
|
||||
for (int i = 0; i < Stages.Count; ++i)
|
||||
{
|
||||
var stageArea = Stages[i].ScreenSpaceDrawQuad.AABBFloat;
|
||||
totalArea = i == 0 ? stageArea : RectangleF.Union(totalArea, stageArea);
|
||||
}
|
||||
|
||||
return totalArea;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => stages.Any(s => s.ReceivePositionalInputAt(screenSpacePos));
|
||||
|
||||
public ManiaPlayfield(List<StageDefinition> stageDefinitions)
|
||||
|
@ -136,6 +136,8 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
columnFlow.SetContentForColumn(i, column);
|
||||
AddNested(column);
|
||||
}
|
||||
|
||||
RegisterPool<BarLine, DrawableBarLine>(50, 200);
|
||||
}
|
||||
|
||||
private ISkinSource currentSkin;
|
||||
@ -186,7 +188,7 @@ namespace osu.Game.Rulesets.Mania.UI
|
||||
|
||||
public override bool Remove(DrawableHitObject h) => Columns.ElementAt(((ManiaHitObject)h.HitObject).Column - firstColumnIndex).Remove(h);
|
||||
|
||||
public void Add(BarLine barLine) => base.Add(new DrawableBarLine(barLine));
|
||||
public void Add(BarLine barLine) => base.Add(barLine);
|
||||
|
||||
internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result)
|
||||
{
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Osu.Tests.Android" android:installLocation="auto">
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
|
||||
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!standard Test" />
|
||||
</manifest>
|
@ -9,6 +9,7 @@ using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input;
|
||||
@ -70,12 +71,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
base.Content.Children = new Drawable[]
|
||||
{
|
||||
editorClock = new EditorClock(editorBeatmap),
|
||||
snapProvider,
|
||||
new PopoverContainer { Child = snapProvider },
|
||||
Content
|
||||
};
|
||||
}
|
||||
|
||||
protected override Container<Drawable> Content { get; } = new Container { RelativeSizeAxes = Axes.Both };
|
||||
protected override Container<Drawable> Content { get; } = new PopoverContainer { RelativeSizeAxes = Axes.Both };
|
||||
|
||||
[SetUp]
|
||||
public void Setup() => Schedule(() =>
|
||||
|
@ -0,0 +1,95 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Edit;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Screens.Edit.Components.RadioButtons;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests.Editor
|
||||
{
|
||||
public partial class TestScenePreciseRotation : TestSceneOsuEditor
|
||||
{
|
||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset);
|
||||
|
||||
[Test]
|
||||
public void TestHotkeyHandling()
|
||||
{
|
||||
AddStep("select single circle", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.OfType<HitCircle>().First()));
|
||||
AddStep("press rotate hotkey", () =>
|
||||
{
|
||||
InputManager.PressKey(Key.ControlLeft);
|
||||
InputManager.Key(Key.R);
|
||||
InputManager.ReleaseKey(Key.ControlLeft);
|
||||
});
|
||||
AddUntilStep("no popover present", () => this.ChildrenOfType<PreciseRotationPopover>().Count(), () => Is.Zero);
|
||||
|
||||
AddStep("select first three objects", () =>
|
||||
{
|
||||
EditorBeatmap.SelectedHitObjects.Clear();
|
||||
EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects.Take(3));
|
||||
});
|
||||
AddStep("press rotate hotkey", () =>
|
||||
{
|
||||
InputManager.PressKey(Key.ControlLeft);
|
||||
InputManager.Key(Key.R);
|
||||
InputManager.ReleaseKey(Key.ControlLeft);
|
||||
});
|
||||
AddUntilStep("popover present", () => this.ChildrenOfType<PreciseRotationPopover>().Count(), () => Is.EqualTo(1));
|
||||
AddStep("press rotate hotkey", () =>
|
||||
{
|
||||
InputManager.PressKey(Key.ControlLeft);
|
||||
InputManager.Key(Key.R);
|
||||
InputManager.ReleaseKey(Key.ControlLeft);
|
||||
});
|
||||
AddUntilStep("no popover present", () => this.ChildrenOfType<PreciseRotationPopover>().Count(), () => Is.Zero);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestRotateCorrectness()
|
||||
{
|
||||
AddStep("replace objects", () =>
|
||||
{
|
||||
EditorBeatmap.Clear();
|
||||
EditorBeatmap.AddRange(new HitObject[]
|
||||
{
|
||||
new HitCircle { Position = new Vector2(100) },
|
||||
new HitCircle { Position = new Vector2(200) },
|
||||
});
|
||||
});
|
||||
AddStep("select both circles", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects));
|
||||
AddStep("press rotate hotkey", () =>
|
||||
{
|
||||
InputManager.PressKey(Key.ControlLeft);
|
||||
InputManager.Key(Key.R);
|
||||
InputManager.ReleaseKey(Key.ControlLeft);
|
||||
});
|
||||
AddUntilStep("popover present", getPopover, () => Is.Not.Null);
|
||||
|
||||
AddStep("rotate by 180deg", () => getPopover().ChildrenOfType<TextBox>().Single().Current.Value = "180");
|
||||
AddAssert("first object rotated 180deg around playfield centre",
|
||||
() => EditorBeatmap.HitObjects.OfType<HitCircle>().ElementAt(0).Position,
|
||||
() => Is.EqualTo(OsuPlayfield.BASE_SIZE - new Vector2(100)));
|
||||
AddAssert("second object rotated 180deg around playfield centre",
|
||||
() => EditorBeatmap.HitObjects.OfType<HitCircle>().ElementAt(1).Position,
|
||||
() => Is.EqualTo(OsuPlayfield.BASE_SIZE - new Vector2(200)));
|
||||
|
||||
AddStep("change rotation origin", () => getPopover().ChildrenOfType<EditorRadioButton>().ElementAt(1).TriggerClick());
|
||||
AddAssert("first object rotated 90deg around selection centre",
|
||||
() => EditorBeatmap.HitObjects.OfType<HitCircle>().ElementAt(0).Position, () => Is.EqualTo(new Vector2(200, 200)));
|
||||
AddAssert("second object rotated 90deg around selection centre",
|
||||
() => EditorBeatmap.HitObjects.OfType<HitCircle>().ElementAt(1).Position, () => Is.EqualTo(new Vector2(100, 100)));
|
||||
|
||||
PreciseRotationPopover? getPopover() => this.ChildrenOfType<PreciseRotationPopover>().SingleOrDefault();
|
||||
}
|
||||
}
|
||||
}
|
147
osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerJudgement.cs
Normal file
147
osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerJudgement.cs
Normal file
@ -0,0 +1,147 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Extensions.TypeExtensions;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Replays;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Replays;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Rulesets.Replays;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
public partial class TestSceneSpinnerJudgement : RateAdjustedBeatmapTestScene
|
||||
{
|
||||
private const double time_spinner_start = 2000;
|
||||
private const double time_spinner_end = 4000;
|
||||
|
||||
private List<JudgementResult> judgementResults = new List<JudgementResult>();
|
||||
private ScoreAccessibleReplayPlayer currentPlayer = null!;
|
||||
|
||||
[Test]
|
||||
public void TestHitNothing()
|
||||
{
|
||||
performTest(new List<ReplayFrame>());
|
||||
|
||||
AddAssert("all min judgements", () => judgementResults.All(result => result.Type == result.Judgement.MinResult));
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(2)]
|
||||
[TestCase(5)]
|
||||
public void TestNumberOfSpins(int spins)
|
||||
{
|
||||
performTest(generateReplay(spins));
|
||||
|
||||
for (int i = 0; i < spins; ++i)
|
||||
assertResult<SpinnerTick>(i, HitResult.SmallBonus);
|
||||
|
||||
assertResult<SpinnerTick>(spins, HitResult.IgnoreMiss);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestHitEverything()
|
||||
{
|
||||
performTest(generateReplay(20));
|
||||
|
||||
AddAssert("all max judgements", () => judgementResults.All(result => result.Type == result.Judgement.MaxResult));
|
||||
}
|
||||
|
||||
private static List<ReplayFrame> generateReplay(int spins)
|
||||
{
|
||||
var replayFrames = new List<ReplayFrame>();
|
||||
|
||||
const int frames_per_spin = 30;
|
||||
|
||||
for (int i = 0; i < spins * frames_per_spin; ++i)
|
||||
{
|
||||
float totalProgress = i / (float)(spins * frames_per_spin);
|
||||
float spinProgress = (i % frames_per_spin) / (float)frames_per_spin;
|
||||
double time = time_spinner_start + (time_spinner_end - time_spinner_start) * totalProgress;
|
||||
float posX = MathF.Cos(2 * MathF.PI * spinProgress);
|
||||
float posY = MathF.Sin(2 * MathF.PI * spinProgress);
|
||||
Vector2 finalPos = OsuPlayfield.BASE_SIZE / 2 + new Vector2(posX, posY) * 50;
|
||||
|
||||
replayFrames.Add(new OsuReplayFrame(time, finalPos, OsuAction.LeftButton));
|
||||
}
|
||||
|
||||
return replayFrames;
|
||||
}
|
||||
|
||||
private void performTest(List<ReplayFrame> frames)
|
||||
{
|
||||
AddStep("load player", () =>
|
||||
{
|
||||
Beatmap.Value = CreateWorkingBeatmap(new Beatmap<OsuHitObject>
|
||||
{
|
||||
HitObjects =
|
||||
{
|
||||
new Spinner
|
||||
{
|
||||
StartTime = time_spinner_start,
|
||||
EndTime = time_spinner_end,
|
||||
Position = OsuPlayfield.BASE_SIZE / 2
|
||||
}
|
||||
},
|
||||
BeatmapInfo =
|
||||
{
|
||||
Difficulty = new BeatmapDifficulty(),
|
||||
Ruleset = new OsuRuleset().RulesetInfo
|
||||
},
|
||||
});
|
||||
|
||||
var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } });
|
||||
|
||||
p.OnLoadComplete += _ =>
|
||||
{
|
||||
p.ScoreProcessor.NewJudgement += result =>
|
||||
{
|
||||
if (currentPlayer == p) judgementResults.Add(result);
|
||||
};
|
||||
};
|
||||
|
||||
LoadScreen(currentPlayer = p);
|
||||
judgementResults = new List<JudgementResult>();
|
||||
});
|
||||
|
||||
AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0);
|
||||
AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen());
|
||||
AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value);
|
||||
}
|
||||
|
||||
private void assertResult<T>(int index, HitResult expectedResult)
|
||||
{
|
||||
AddAssert($"{typeof(T).ReadableName()} ({index}) judged as {expectedResult}",
|
||||
() => judgementResults.Where(j => j.HitObject is T).OrderBy(j => j.HitObject.StartTime).ElementAt(index).Type,
|
||||
() => Is.EqualTo(expectedResult));
|
||||
}
|
||||
|
||||
private partial class ScoreAccessibleReplayPlayer : ReplayPlayer
|
||||
{
|
||||
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
|
||||
|
||||
protected override bool PauseOnFocusLost => false;
|
||||
|
||||
public ScoreAccessibleReplayPlayer(Score score)
|
||||
: base(score, new PlayerConfiguration
|
||||
{
|
||||
AllowPause = false,
|
||||
ShowResults = false,
|
||||
})
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -85,6 +85,11 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
|
||||
// we may be entering the screen with a selection already active
|
||||
updateDistanceSnapGrid();
|
||||
|
||||
RightToolbox.Add(new TransformToolboxGroup
|
||||
{
|
||||
RotationHandler = BlueprintContainer.SelectionHandler.RotationHandler
|
||||
});
|
||||
}
|
||||
|
||||
protected override ComposeBlueprintContainer CreateBlueprintContainer()
|
||||
|
@ -17,6 +17,7 @@ using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
using osu.Game.Utils;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
@ -27,11 +28,6 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
[Resolved(CanBeNull = true)]
|
||||
private IDistanceSnapProvider? snapProvider { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// During a transform, the initial origin is stored so it can be used throughout the operation.
|
||||
/// </summary>
|
||||
private Vector2? referenceOrigin;
|
||||
|
||||
/// <summary>
|
||||
/// During a transform, the initial path types of a single selected slider are stored so they
|
||||
/// can be maintained throughout the operation.
|
||||
@ -42,9 +38,8 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
base.OnSelectionChanged();
|
||||
|
||||
Quad quad = selectedMovableObjects.Length > 0 ? getSurroundingQuad(selectedMovableObjects) : new Quad();
|
||||
Quad quad = selectedMovableObjects.Length > 0 ? GeometryUtils.GetSurroundingQuad(selectedMovableObjects) : new Quad();
|
||||
|
||||
SelectionBox.CanRotate = quad.Width > 0 || quad.Height > 0;
|
||||
SelectionBox.CanFlipX = SelectionBox.CanScaleX = quad.Width > 0;
|
||||
SelectionBox.CanFlipY = SelectionBox.CanScaleY = quad.Height > 0;
|
||||
SelectionBox.CanReverse = EditorBeatmap.SelectedHitObjects.Count > 1 || EditorBeatmap.SelectedHitObjects.Any(s => s is Slider);
|
||||
@ -53,7 +48,6 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
protected override void OnOperationEnded()
|
||||
{
|
||||
base.OnOperationEnded();
|
||||
referenceOrigin = null;
|
||||
referencePathTypes = null;
|
||||
}
|
||||
|
||||
@ -109,13 +103,13 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
var hitObjects = selectedMovableObjects;
|
||||
|
||||
var flipQuad = flipOverOrigin ? new Quad(0, 0, OsuPlayfield.BASE_SIZE.X, OsuPlayfield.BASE_SIZE.Y) : getSurroundingQuad(hitObjects);
|
||||
var flipQuad = flipOverOrigin ? new Quad(0, 0, OsuPlayfield.BASE_SIZE.X, OsuPlayfield.BASE_SIZE.Y) : GeometryUtils.GetSurroundingQuad(hitObjects);
|
||||
|
||||
bool didFlip = false;
|
||||
|
||||
foreach (var h in hitObjects)
|
||||
{
|
||||
var flippedPosition = GetFlippedPosition(direction, flipQuad, h.Position);
|
||||
var flippedPosition = GeometryUtils.GetFlippedPosition(direction, flipQuad, h.Position);
|
||||
|
||||
if (!Precision.AlmostEquals(flippedPosition, h.Position))
|
||||
{
|
||||
@ -169,34 +163,13 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
if ((reference & Anchor.y0) > 0) scale.Y = -scale.Y;
|
||||
}
|
||||
|
||||
public override bool HandleRotation(float delta)
|
||||
{
|
||||
var hitObjects = selectedMovableObjects;
|
||||
|
||||
Quad quad = getSurroundingQuad(hitObjects);
|
||||
|
||||
referenceOrigin ??= quad.Centre;
|
||||
|
||||
foreach (var h in hitObjects)
|
||||
{
|
||||
h.Position = RotatePointAroundOrigin(h.Position, referenceOrigin.Value, delta);
|
||||
|
||||
if (h is IHasPath path)
|
||||
{
|
||||
foreach (PathControlPoint cp in path.Path.ControlPoints)
|
||||
cp.Position = RotatePointAroundOrigin(cp.Position, Vector2.Zero, delta);
|
||||
}
|
||||
}
|
||||
|
||||
// this isn't always the case but let's be lenient for now.
|
||||
return true;
|
||||
}
|
||||
public override SelectionRotationHandler CreateRotationHandler() => new OsuSelectionRotationHandler();
|
||||
|
||||
private void scaleSlider(Slider slider, Vector2 scale)
|
||||
{
|
||||
referencePathTypes ??= slider.Path.ControlPoints.Select(p => p.Type).ToList();
|
||||
|
||||
Quad sliderQuad = GetSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position));
|
||||
Quad sliderQuad = GeometryUtils.GetSurroundingQuad(slider.Path.ControlPoints.Select(p => p.Position));
|
||||
|
||||
// Limit minimum distance between control points after scaling to almost 0. Less than 0 causes the slider to flip, exactly 0 causes a crash through division by 0.
|
||||
scale = Vector2.ComponentMax(new Vector2(Precision.FLOAT_EPSILON), sliderQuad.Size + scale) - sliderQuad.Size;
|
||||
@ -222,7 +195,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
slider.SnapTo(snapProvider);
|
||||
|
||||
//if sliderhead or sliderend end up outside playfield, revert scaling.
|
||||
Quad scaledQuad = getSurroundingQuad(new OsuHitObject[] { slider });
|
||||
Quad scaledQuad = GeometryUtils.GetSurroundingQuad(new OsuHitObject[] { slider });
|
||||
(bool xInBounds, bool yInBounds) = isQuadInBounds(scaledQuad);
|
||||
|
||||
if (xInBounds && yInBounds && slider.Path.HasValidLength)
|
||||
@ -238,10 +211,10 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
private void scaleHitObjects(OsuHitObject[] hitObjects, Anchor reference, Vector2 scale)
|
||||
{
|
||||
scale = getClampedScale(hitObjects, reference, scale);
|
||||
Quad selectionQuad = getSurroundingQuad(hitObjects);
|
||||
Quad selectionQuad = GeometryUtils.GetSurroundingQuad(hitObjects);
|
||||
|
||||
foreach (var h in hitObjects)
|
||||
h.Position = GetScaledPosition(reference, scale, selectionQuad, h.Position);
|
||||
h.Position = GeometryUtils.GetScaledPosition(reference, scale, selectionQuad, h.Position);
|
||||
}
|
||||
|
||||
private (bool X, bool Y) isQuadInBounds(Quad quad)
|
||||
@ -256,7 +229,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
var hitObjects = selectedMovableObjects;
|
||||
|
||||
Quad quad = getSurroundingQuad(hitObjects);
|
||||
Quad quad = GeometryUtils.GetSurroundingQuad(hitObjects);
|
||||
|
||||
Vector2 delta = Vector2.Zero;
|
||||
|
||||
@ -286,7 +259,7 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
float xOffset = ((reference & Anchor.x0) > 0) ? -scale.X : 0;
|
||||
float yOffset = ((reference & Anchor.y0) > 0) ? -scale.Y : 0;
|
||||
|
||||
Quad selectionQuad = getSurroundingQuad(hitObjects);
|
||||
Quad selectionQuad = GeometryUtils.GetSurroundingQuad(hitObjects);
|
||||
|
||||
//todo: this is not always correct for selections involving sliders. This approximation assumes each point is scaled independently, but sliderends move with the sliderhead.
|
||||
Quad scaledQuad = new Quad(selectionQuad.TopLeft.X + xOffset, selectionQuad.TopLeft.Y + yOffset, selectionQuad.Width + scale.X, selectionQuad.Height + scale.Y);
|
||||
@ -311,26 +284,6 @@ namespace osu.Game.Rulesets.Osu.Edit
|
||||
return scale;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a gamefield-space quad surrounding the provided hit objects.
|
||||
/// </summary>
|
||||
/// <param name="hitObjects">The hit objects to calculate a quad for.</param>
|
||||
private Quad getSurroundingQuad(OsuHitObject[] hitObjects) =>
|
||||
GetSurroundingQuad(hitObjects.SelectMany(h =>
|
||||
{
|
||||
if (h is IHasPath path)
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
h.Position,
|
||||
// can't use EndPosition for reverse slider cases.
|
||||
h.Position + path.Path.PositionAt(1)
|
||||
};
|
||||
}
|
||||
|
||||
return new[] { h.Position };
|
||||
}));
|
||||
|
||||
/// <summary>
|
||||
/// All osu! hitobjects which can be moved/rotated/scaled.
|
||||
/// </summary>
|
||||
|
107
osu.Game.Rulesets.Osu/Edit/OsuSelectionRotationHandler.cs
Normal file
107
osu.Game.Rulesets.Osu/Edit/OsuSelectionRotationHandler.cs
Normal file
@ -0,0 +1,107 @@
|
||||
// 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.Diagnostics;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
using osu.Game.Utils;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
public partial class OsuSelectionRotationHandler : SelectionRotationHandler
|
||||
{
|
||||
[Resolved]
|
||||
private IEditorChangeHandler? changeHandler { get; set; }
|
||||
|
||||
private BindableList<HitObject> selectedItems { get; } = new BindableList<HitObject>();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(EditorBeatmap editorBeatmap)
|
||||
{
|
||||
selectedItems.BindTo(editorBeatmap.SelectedHitObjects);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
selectedItems.CollectionChanged += (_, __) => updateState();
|
||||
updateState();
|
||||
}
|
||||
|
||||
private void updateState()
|
||||
{
|
||||
var quad = GeometryUtils.GetSurroundingQuad(selectedMovableObjects);
|
||||
CanRotate.Value = quad.Width > 0 || quad.Height > 0;
|
||||
}
|
||||
|
||||
private OsuHitObject[]? objectsInRotation;
|
||||
|
||||
private Vector2? defaultOrigin;
|
||||
private Dictionary<OsuHitObject, Vector2>? originalPositions;
|
||||
private Dictionary<IHasPath, Vector2[]>? originalPathControlPointPositions;
|
||||
|
||||
public override void Begin()
|
||||
{
|
||||
if (objectsInRotation != null)
|
||||
throw new InvalidOperationException($"Cannot {nameof(Begin)} a rotate operation while another is in progress!");
|
||||
|
||||
changeHandler?.BeginChange();
|
||||
|
||||
objectsInRotation = selectedMovableObjects.ToArray();
|
||||
defaultOrigin = GeometryUtils.GetSurroundingQuad(objectsInRotation).Centre;
|
||||
originalPositions = objectsInRotation.ToDictionary(obj => obj, obj => obj.Position);
|
||||
originalPathControlPointPositions = objectsInRotation.OfType<IHasPath>().ToDictionary(
|
||||
obj => obj,
|
||||
obj => obj.Path.ControlPoints.Select(point => point.Position).ToArray());
|
||||
}
|
||||
|
||||
public override void Update(float rotation, Vector2? origin = null)
|
||||
{
|
||||
if (objectsInRotation == null)
|
||||
throw new InvalidOperationException($"Cannot {nameof(Update)} a rotate operation without calling {nameof(Begin)} first!");
|
||||
|
||||
Debug.Assert(originalPositions != null && originalPathControlPointPositions != null && defaultOrigin != null);
|
||||
|
||||
Vector2 actualOrigin = origin ?? defaultOrigin.Value;
|
||||
|
||||
foreach (var ho in objectsInRotation)
|
||||
{
|
||||
ho.Position = GeometryUtils.RotatePointAroundOrigin(originalPositions[ho], actualOrigin, rotation);
|
||||
|
||||
if (ho is IHasPath withPath)
|
||||
{
|
||||
var originalPath = originalPathControlPointPositions[withPath];
|
||||
|
||||
for (int i = 0; i < withPath.Path.ControlPoints.Count; ++i)
|
||||
withPath.Path.ControlPoints[i].Position = GeometryUtils.RotatePointAroundOrigin(originalPath[i], Vector2.Zero, rotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Commit()
|
||||
{
|
||||
if (objectsInRotation == null)
|
||||
throw new InvalidOperationException($"Cannot {nameof(Commit)} a rotate operation without calling {nameof(Begin)} first!");
|
||||
|
||||
changeHandler?.EndChange();
|
||||
|
||||
objectsInRotation = null;
|
||||
originalPositions = null;
|
||||
originalPathControlPointPositions = null;
|
||||
defaultOrigin = null;
|
||||
}
|
||||
|
||||
private IEnumerable<OsuHitObject> selectedMovableObjects => selectedItems.Cast<OsuHitObject>()
|
||||
.Where(h => h is not Spinner);
|
||||
}
|
||||
}
|
107
osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs
Normal file
107
osu.Game.Rulesets.Osu/Edit/PreciseRotationPopover.cs
Normal file
@ -0,0 +1,107 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Screens.Edit.Components.RadioButtons;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
public partial class PreciseRotationPopover : OsuPopover
|
||||
{
|
||||
private readonly SelectionRotationHandler rotationHandler;
|
||||
|
||||
private readonly Bindable<PreciseRotationInfo> rotationInfo = new Bindable<PreciseRotationInfo>(new PreciseRotationInfo(0, RotationOrigin.PlayfieldCentre));
|
||||
|
||||
private SliderWithTextBoxInput<float> angleInput = null!;
|
||||
private EditorRadioButtonCollection rotationOrigin = null!;
|
||||
|
||||
public PreciseRotationPopover(SelectionRotationHandler rotationHandler)
|
||||
{
|
||||
this.rotationHandler = rotationHandler;
|
||||
|
||||
AllowableAnchors = new[] { Anchor.CentreLeft, Anchor.CentreRight };
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
Width = 220,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(20),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
angleInput = new SliderWithTextBoxInput<float>("Angle (degrees):")
|
||||
{
|
||||
Current = new BindableNumber<float>
|
||||
{
|
||||
MinValue = -360,
|
||||
MaxValue = 360,
|
||||
Precision = 1
|
||||
},
|
||||
Instantaneous = true
|
||||
},
|
||||
rotationOrigin = new EditorRadioButtonCollection
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Items = new[]
|
||||
{
|
||||
new RadioButton("Playfield centre",
|
||||
() => rotationInfo.Value = rotationInfo.Value with { Origin = RotationOrigin.PlayfieldCentre },
|
||||
() => new SpriteIcon { Icon = FontAwesome.Regular.Square }),
|
||||
new RadioButton("Selection centre",
|
||||
() => rotationInfo.Value = rotationInfo.Value with { Origin = RotationOrigin.SelectionCentre },
|
||||
() => new SpriteIcon { Icon = FontAwesome.Solid.VectorSquare })
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
ScheduleAfterChildren(() => angleInput.TakeFocus());
|
||||
angleInput.Current.BindValueChanged(angle => rotationInfo.Value = rotationInfo.Value with { Degrees = angle.NewValue });
|
||||
rotationOrigin.Items.First().Select();
|
||||
|
||||
rotationInfo.BindValueChanged(rotation =>
|
||||
{
|
||||
rotationHandler.Update(rotation.NewValue.Degrees, rotation.NewValue.Origin == RotationOrigin.PlayfieldCentre ? OsuPlayfield.BASE_SIZE / 2 : null);
|
||||
});
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
base.PopIn();
|
||||
rotationHandler.Begin();
|
||||
}
|
||||
|
||||
protected override void PopOut()
|
||||
{
|
||||
base.PopOut();
|
||||
|
||||
if (IsLoaded)
|
||||
rotationHandler.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
public enum RotationOrigin
|
||||
{
|
||||
PlayfieldCentre,
|
||||
SelectionCentre
|
||||
}
|
||||
|
||||
public record PreciseRotationInfo(float Degrees, RotationOrigin Origin);
|
||||
}
|
80
osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs
Normal file
80
osu.Game.Rulesets.Osu/Edit/TransformToolboxGroup.cs
Normal file
@ -0,0 +1,80 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Screens.Edit.Components;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Edit
|
||||
{
|
||||
public partial class TransformToolboxGroup : EditorToolboxGroup, IKeyBindingHandler<GlobalAction>
|
||||
{
|
||||
private readonly Bindable<bool> canRotate = new BindableBool();
|
||||
|
||||
private EditorToolButton rotateButton = null!;
|
||||
|
||||
public SelectionRotationHandler RotationHandler { get; init; } = null!;
|
||||
|
||||
public TransformToolboxGroup()
|
||||
: base("transform")
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(5),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
rotateButton = new EditorToolButton("Rotate",
|
||||
() => new SpriteIcon { Icon = FontAwesome.Solid.Undo },
|
||||
() => new PreciseRotationPopover(RotationHandler)),
|
||||
// TODO: scale
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
// bindings to `Enabled` on the buttons are decoupled on purpose
|
||||
// due to the weird `OsuButton` behaviour of resetting `Enabled` to `false` when `Action` is set.
|
||||
canRotate.BindTo(RotationHandler.CanRotate);
|
||||
canRotate.BindValueChanged(_ => rotateButton.Enabled.Value = canRotate.Value, true);
|
||||
}
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
||||
{
|
||||
if (e.Repeat) return false;
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case GlobalAction.EditorToggleRotateControl:
|
||||
{
|
||||
rotateButton.TriggerClick();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -72,9 +72,9 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
|
||||
lastAngle = thisAngle;
|
||||
|
||||
IsSpinning.Value = isSpinnableTime && Math.Abs(currentRotation / 2 - Rotation) > 5f;
|
||||
IsSpinning.Value = isSpinnableTime && Math.Abs(currentRotation - Rotation) > 10f;
|
||||
|
||||
Rotation = (float)Interpolation.Damp(Rotation, currentRotation / 2, 0.99, Math.Abs(Time.Elapsed));
|
||||
Rotation = (float)Interpolation.Damp(Rotation, currentRotation, 0.99, Math.Abs(Time.Elapsed));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -257,7 +257,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
texture.Bind();
|
||||
|
||||
for (int i = 0; i < points.Count; i++)
|
||||
drawPointQuad(points[i], textureRect, i + firstVisiblePointIndex);
|
||||
drawPointQuad(renderer, points[i], textureRect, i + firstVisiblePointIndex);
|
||||
|
||||
UnbindTextureShader(renderer);
|
||||
renderer.PopLocalMatrix();
|
||||
@ -325,7 +325,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
|
||||
private float getRotation(int index) => max_rotation * (StatelessRNG.NextSingle(rotationSeed, index) * 2 - 1);
|
||||
|
||||
private void drawPointQuad(SmokePoint point, RectangleF textureRect, int index)
|
||||
private void drawPointQuad(IRenderer renderer, SmokePoint point, RectangleF textureRect, int index)
|
||||
{
|
||||
Debug.Assert(quadBatch != null);
|
||||
|
||||
@ -347,25 +347,25 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
var localBotLeft = point.Position + ortho - dir;
|
||||
var localBotRight = point.Position + ortho + dir;
|
||||
|
||||
quadBatch.Add(new TexturedVertex2D
|
||||
quadBatch.Add(new TexturedVertex2D(renderer)
|
||||
{
|
||||
Position = localTopLeft,
|
||||
TexturePosition = textureRect.TopLeft,
|
||||
Colour = Color4Extensions.Multiply(ColourAtPosition(localTopLeft), colour),
|
||||
});
|
||||
quadBatch.Add(new TexturedVertex2D
|
||||
quadBatch.Add(new TexturedVertex2D(renderer)
|
||||
{
|
||||
Position = localTopRight,
|
||||
TexturePosition = textureRect.TopRight,
|
||||
Colour = Color4Extensions.Multiply(ColourAtPosition(localTopRight), colour),
|
||||
});
|
||||
quadBatch.Add(new TexturedVertex2D
|
||||
quadBatch.Add(new TexturedVertex2D(renderer)
|
||||
{
|
||||
Position = localBotRight,
|
||||
TexturePosition = textureRect.BottomRight,
|
||||
Colour = Color4Extensions.Multiply(ColourAtPosition(localBotRight), colour),
|
||||
});
|
||||
quadBatch.Add(new TexturedVertex2D
|
||||
quadBatch.Add(new TexturedVertex2D(renderer)
|
||||
{
|
||||
Position = localBotLeft,
|
||||
TexturePosition = textureRect.BottomLeft,
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Rulesets.Taiko.Tests.Android" android:installLocation="auto">
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
|
||||
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!taiko Test" />
|
||||
</manifest>
|
@ -139,7 +139,6 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
||||
StartTime = obj.StartTime,
|
||||
Samples = obj.Samples,
|
||||
Duration = taikoDuration,
|
||||
SliderVelocity = obj is IHasSliderVelocity velocityData ? velocityData.SliderVelocity : 1
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,8 @@ namespace osu.Game.Rulesets.Taiko.Edit
|
||||
{
|
||||
public partial class TaikoHitObjectComposer : HitObjectComposer<TaikoHitObject>
|
||||
{
|
||||
protected override bool ApplyHorizontalCentering => false;
|
||||
|
||||
public TaikoHitObjectComposer(TaikoRuleset ruleset)
|
||||
: base(ruleset)
|
||||
{
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using System.Threading;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
@ -14,7 +13,7 @@ using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Objects
|
||||
{
|
||||
public class DrumRoll : TaikoStrongableHitObject, IHasPath, IHasSliderVelocity
|
||||
public class DrumRoll : TaikoStrongableHitObject, IHasPath
|
||||
{
|
||||
/// <summary>
|
||||
/// Drum roll distance that results in a duration of 1 speed-adjusted beat length.
|
||||
@ -34,19 +33,6 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
||||
/// </summary>
|
||||
public double Velocity { get; private set; }
|
||||
|
||||
public BindableNumber<double> SliderVelocityBindable { get; } = new BindableDouble(1)
|
||||
{
|
||||
Precision = 0.01,
|
||||
MinValue = 0.1,
|
||||
MaxValue = 10
|
||||
};
|
||||
|
||||
public double SliderVelocity
|
||||
{
|
||||
get => SliderVelocityBindable.Value;
|
||||
set => SliderVelocityBindable.Value = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Numer of ticks per beat length.
|
||||
/// </summary>
|
||||
@ -63,8 +49,9 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
|
||||
EffectControlPoint effectPoint = controlPointInfo.EffectPointAt(StartTime);
|
||||
|
||||
double scoringDistance = base_distance * difficulty.SliderMultiplier * SliderVelocity;
|
||||
double scoringDistance = base_distance * difficulty.SliderMultiplier * effectPoint.ScrollSpeed;
|
||||
Velocity = scoringDistance / timingPoint.BeatLength;
|
||||
|
||||
TickRate = difficulty.SliderTickRate == 3 ? 3 : 4;
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="osu.Game.Tests.Android" android:installLocation="auto">
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
|
||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
|
||||
<application android:allowBackup="true" android:supportsRtl="true" android:label="osu!visual Test" />
|
||||
</manifest>
|
@ -1,19 +1,23 @@
|
||||
// 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.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.Tests.Resources;
|
||||
|
||||
namespace osu.Game.Tests.Database
|
||||
{
|
||||
[TestFixture]
|
||||
public class LegacyBeatmapImporterTest
|
||||
public class LegacyBeatmapImporterTest : RealmTest
|
||||
{
|
||||
private readonly TestLegacyBeatmapImporter importer = new TestLegacyBeatmapImporter();
|
||||
|
||||
@ -60,6 +64,33 @@ namespace osu.Game.Tests.Database
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStableDateAddedApplied()
|
||||
{
|
||||
RunTestWithRealmAsync(async (realm, storage) =>
|
||||
{
|
||||
using (HeadlessGameHost host = new CleanRunHeadlessGameHost())
|
||||
using (var tmpStorage = new TemporaryNativeStorage("stable-songs-folder"))
|
||||
{
|
||||
var stableStorage = new StableStorage(tmpStorage.GetFullPath(""), host);
|
||||
var songsStorage = stableStorage.GetStorageForDirectory(StableStorage.STABLE_DEFAULT_SONGS_PATH);
|
||||
|
||||
ZipFile.ExtractToDirectory(TestResources.GetQuickTestBeatmapForImport(), songsStorage.GetFullPath("renatus"));
|
||||
|
||||
string[] beatmaps = Directory.GetFiles(songsStorage.GetFullPath("renatus"), "*.osu", SearchOption.TopDirectoryOnly);
|
||||
|
||||
File.SetLastWriteTimeUtc(beatmaps[beatmaps.Length / 2], new DateTime(2000, 1, 1, 12, 0, 0));
|
||||
|
||||
await new LegacyBeatmapImporter(new BeatmapImporter(storage, realm)).ImportFromStableAsync(stableStorage);
|
||||
|
||||
var importedSet = realm.Realm.All<BeatmapSetInfo>().Single();
|
||||
|
||||
Assert.NotNull(importedSet);
|
||||
Assert.AreEqual(new DateTimeOffset(new DateTime(2000, 1, 1, 12, 0, 0, DateTimeKind.Utc)), importedSet.DateAdded);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class TestLegacyBeatmapImporter : LegacyBeatmapImporter
|
||||
{
|
||||
public TestLegacyBeatmapImporter()
|
||||
|
@ -6,6 +6,7 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
@ -29,7 +30,7 @@ namespace osu.Game.Tests.Editing
|
||||
[Cached(typeof(IBeatSnapProvider))]
|
||||
private readonly EditorBeatmap editorBeatmap;
|
||||
|
||||
protected override Container<Drawable> Content { get; } = new Container { RelativeSizeAxes = Axes.Both };
|
||||
protected override Container<Drawable> Content { get; } = new PopoverContainer { RelativeSizeAxes = Axes.Both };
|
||||
|
||||
public TestSceneHitObjectComposerDistanceSnapping()
|
||||
{
|
||||
|
@ -125,13 +125,13 @@ namespace osu.Game.Tests.Visual.Background
|
||||
createFakeStoryboard();
|
||||
AddStep("Enable Storyboard", () =>
|
||||
{
|
||||
player.ReplacesBackground.Value = true;
|
||||
player.StoryboardReplacesBackground.Value = true;
|
||||
player.StoryboardEnabled.Value = true;
|
||||
});
|
||||
AddUntilStep("Background is invisible, storyboard is visible", () => songSelect.IsBackgroundInvisible() && player.IsStoryboardVisible);
|
||||
AddUntilStep("Background is black, storyboard is visible", () => songSelect.IsBackgroundVisible() && songSelect.IsBackgroundBlack() && player.IsStoryboardVisible);
|
||||
AddStep("Disable Storyboard", () =>
|
||||
{
|
||||
player.ReplacesBackground.Value = false;
|
||||
player.StoryboardReplacesBackground.Value = false;
|
||||
player.StoryboardEnabled.Value = false;
|
||||
});
|
||||
AddUntilStep("Background is visible, storyboard is invisible", () => songSelect.IsBackgroundVisible() && !player.IsStoryboardVisible);
|
||||
@ -173,7 +173,7 @@ namespace osu.Game.Tests.Visual.Background
|
||||
createFakeStoryboard();
|
||||
AddStep("Enable Storyboard", () =>
|
||||
{
|
||||
player.ReplacesBackground.Value = true;
|
||||
player.StoryboardReplacesBackground.Value = true;
|
||||
player.StoryboardEnabled.Value = true;
|
||||
});
|
||||
AddStep("Enable user dim", () => player.DimmableStoryboard.IgnoreUserSettings.Value = false);
|
||||
@ -188,7 +188,7 @@ namespace osu.Game.Tests.Visual.Background
|
||||
{
|
||||
performFullSetup();
|
||||
createFakeStoryboard();
|
||||
AddStep("Enable replacing background", () => player.ReplacesBackground.Value = true);
|
||||
AddStep("Enable replacing background", () => player.StoryboardReplacesBackground.Value = true);
|
||||
|
||||
AddUntilStep("Storyboard is invisible", () => !player.IsStoryboardVisible);
|
||||
AddUntilStep("Background is visible", () => songSelect.IsBackgroundVisible());
|
||||
@ -199,11 +199,11 @@ namespace osu.Game.Tests.Visual.Background
|
||||
player.DimmableStoryboard.IgnoreUserSettings.Value = true;
|
||||
});
|
||||
AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible);
|
||||
AddUntilStep("Background is invisible", () => songSelect.IsBackgroundInvisible());
|
||||
AddUntilStep("Background is dimmed", () => songSelect.IsBackgroundVisible() && songSelect.IsBackgroundBlack());
|
||||
|
||||
AddStep("Disable background replacement", () => player.ReplacesBackground.Value = false);
|
||||
AddStep("Disable background replacement", () => player.StoryboardReplacesBackground.Value = false);
|
||||
AddUntilStep("Storyboard is visible", () => player.IsStoryboardVisible);
|
||||
AddUntilStep("Background is visible", () => songSelect.IsBackgroundVisible());
|
||||
AddUntilStep("Background is visible", () => songSelect.IsBackgroundVisible() && !songSelect.IsBackgroundBlack());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -257,7 +257,7 @@ namespace osu.Game.Tests.Visual.Background
|
||||
private void createFakeStoryboard() => AddStep("Create storyboard", () =>
|
||||
{
|
||||
player.StoryboardEnabled.Value = false;
|
||||
player.ReplacesBackground.Value = false;
|
||||
player.StoryboardReplacesBackground.Value = false;
|
||||
player.DimmableStoryboard.Add(new OsuSpriteText
|
||||
{
|
||||
Size = new Vector2(500, 50),
|
||||
@ -323,6 +323,8 @@ namespace osu.Game.Tests.Visual.Background
|
||||
config.BindWith(OsuSetting.BlurLevel, BlurLevel);
|
||||
}
|
||||
|
||||
public bool IsBackgroundBlack() => background.CurrentColour == OsuColour.Gray(0);
|
||||
|
||||
public bool IsBackgroundDimmed() => background.CurrentColour == OsuColour.Gray(1f - background.CurrentDim);
|
||||
|
||||
public bool IsBackgroundUndimmed() => background.CurrentColour == Color4.White;
|
||||
@ -331,8 +333,6 @@ namespace osu.Game.Tests.Visual.Background
|
||||
|
||||
public bool IsUserBlurDisabled() => background.CurrentBlur == new Vector2(0);
|
||||
|
||||
public bool IsBackgroundInvisible() => background.CurrentAlpha == 0;
|
||||
|
||||
public bool IsBackgroundVisible() => background.CurrentAlpha == 1;
|
||||
|
||||
public bool IsBackgroundBlur() => Precision.AlmostEquals(background.CurrentBlur, new Vector2(BACKGROUND_BLUR), 0.1f);
|
||||
@ -367,7 +367,7 @@ namespace osu.Game.Tests.Visual.Background
|
||||
{
|
||||
base.OnEntering(e);
|
||||
|
||||
ApplyToBackground(b => ReplacesBackground.BindTo(b.StoryboardReplacesBackground));
|
||||
ApplyToBackground(b => StoryboardReplacesBackground.BindTo(b.StoryboardReplacesBackground));
|
||||
}
|
||||
|
||||
public new DimmableStoryboard DimmableStoryboard => base.DimmableStoryboard;
|
||||
@ -376,7 +376,7 @@ namespace osu.Game.Tests.Visual.Background
|
||||
public bool BlockLoad;
|
||||
|
||||
public Bindable<bool> StoryboardEnabled;
|
||||
public readonly Bindable<bool> ReplacesBackground = new Bindable<bool>();
|
||||
public readonly Bindable<bool> StoryboardReplacesBackground = new Bindable<bool>();
|
||||
public readonly Bindable<bool> IsPaused = new Bindable<bool>();
|
||||
|
||||
public LoadBlockingTestPlayer(bool allowPause = true)
|
||||
|
@ -3,8 +3,11 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
@ -20,6 +23,14 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
private Container selectionArea;
|
||||
private SelectionBox selectionBox;
|
||||
|
||||
[Cached(typeof(SelectionRotationHandler))]
|
||||
private TestSelectionRotationHandler rotationHandler;
|
||||
|
||||
public TestSceneComposeSelectBox()
|
||||
{
|
||||
rotationHandler = new TestSelectionRotationHandler(() => selectionArea);
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() =>
|
||||
{
|
||||
@ -34,13 +45,11 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
|
||||
CanRotate = true,
|
||||
CanScaleX = true,
|
||||
CanScaleY = true,
|
||||
CanFlipX = true,
|
||||
CanFlipY = true,
|
||||
|
||||
OnRotation = handleRotation,
|
||||
OnScale = handleScale
|
||||
}
|
||||
}
|
||||
@ -71,11 +80,48 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool handleRotation(float angle)
|
||||
private partial class TestSelectionRotationHandler : SelectionRotationHandler
|
||||
{
|
||||
// kinda silly and wrong, but just showing that the drag handles work.
|
||||
selectionArea.Rotation += angle;
|
||||
return true;
|
||||
private readonly Func<Container> getTargetContainer;
|
||||
|
||||
public TestSelectionRotationHandler(Func<Container> getTargetContainer)
|
||||
{
|
||||
this.getTargetContainer = getTargetContainer;
|
||||
|
||||
CanRotate.Value = true;
|
||||
}
|
||||
|
||||
[CanBeNull]
|
||||
private Container targetContainer;
|
||||
|
||||
private float? initialRotation;
|
||||
|
||||
public override void Begin()
|
||||
{
|
||||
if (targetContainer != null)
|
||||
throw new InvalidOperationException($"Cannot {nameof(Begin)} a rotate operation while another is in progress!");
|
||||
|
||||
targetContainer = getTargetContainer();
|
||||
initialRotation = targetContainer!.Rotation;
|
||||
}
|
||||
|
||||
public override void Update(float rotation, Vector2? origin = null)
|
||||
{
|
||||
if (targetContainer == null)
|
||||
throw new InvalidOperationException($"Cannot {nameof(Update)} a rotate operation without calling {nameof(Begin)} first!");
|
||||
|
||||
// kinda silly and wrong, but just showing that the drag handles work.
|
||||
targetContainer.Rotation = initialRotation!.Value + rotation;
|
||||
}
|
||||
|
||||
public override void Commit()
|
||||
{
|
||||
if (targetContainer == null)
|
||||
throw new InvalidOperationException($"Cannot {nameof(Commit)} a rotate operation without calling {nameof(Begin)} first!");
|
||||
|
||||
targetContainer = null;
|
||||
initialRotation = null;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -18,6 +18,7 @@ using osu.Game.Database;
|
||||
using osu.Game.Overlays.Dialog;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Catch;
|
||||
using osu.Game.Rulesets.Catch.Objects;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
@ -91,25 +92,6 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
}
|
||||
|
||||
[Test]
|
||||
[FlakyTest]
|
||||
/*
|
||||
* Fail rate around 1.2%.
|
||||
*
|
||||
* Failing with realm refetch occasionally being null.
|
||||
* My only guess is that the WorkingBeatmap at SetupScreen is dummy instead of the true one.
|
||||
* If it's something else, we have larger issues with realm, but I don't think that's the case.
|
||||
*
|
||||
* at osu.Framework.Logging.ThrowingTraceListener.Fail(String message1, String message2)
|
||||
* at System.Diagnostics.TraceInternal.Fail(String message, String detailMessage)
|
||||
* at System.Diagnostics.TraceInternal.TraceProvider.Fail(String message, String detailMessage)
|
||||
* at System.Diagnostics.Debug.Fail(String message, String detailMessage)
|
||||
* at osu.Game.Database.ModelManager`1.<>c__DisplayClass8_0.<performFileOperation>b__0(Realm realm) ModelManager.cs:line 50
|
||||
* at osu.Game.Database.RealmExtensions.Write(Realm realm, Action`1 function) RealmExtensions.cs:line 14
|
||||
* at osu.Game.Database.ModelManager`1.performFileOperation(TModel item, Action`1 operation) ModelManager.cs:line 47
|
||||
* at osu.Game.Database.ModelManager`1.AddFile(TModel item, Stream contents, String filename) ModelManager.cs:line 37
|
||||
* at osu.Game.Screens.Edit.Setup.ResourcesSection.ChangeAudioTrack(FileInfo source) ResourcesSection.cs:line 115
|
||||
* at osu.Game.Tests.Visual.Editing.TestSceneEditorBeatmapCreation.<TestAddAudioTrack>b__11_0() TestSceneEditorBeatmapCreation.cs:line 101
|
||||
*/
|
||||
public void TestAddAudioTrack()
|
||||
{
|
||||
AddAssert("track is virtual", () => Beatmap.Value.Track is TrackVirtual);
|
||||
@ -453,6 +435,51 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestExitBlockedWhenSavingBeatmapWithSameNamedDifficulties()
|
||||
{
|
||||
Guid setId = Guid.Empty;
|
||||
const string duplicate_difficulty_name = "duplicate";
|
||||
|
||||
AddStep("retrieve set ID", () => setId = EditorBeatmap.BeatmapInfo.BeatmapSet!.ID);
|
||||
AddStep("set difficulty name", () => EditorBeatmap.BeatmapInfo.DifficultyName = duplicate_difficulty_name);
|
||||
AddStep("save beatmap", () => Editor.Save());
|
||||
AddAssert("new beatmap persisted", () =>
|
||||
{
|
||||
var set = beatmapManager.QueryBeatmapSet(s => s.ID == setId);
|
||||
return set != null && set.PerformRead(s => s.Beatmaps.Count == 1 && s.Files.Count == 1);
|
||||
});
|
||||
|
||||
AddStep("create new difficulty", () => Editor.CreateNewDifficulty(new CatchRuleset().RulesetInfo));
|
||||
|
||||
AddUntilStep("wait for created", () =>
|
||||
{
|
||||
string? difficultyName = Editor.ChildrenOfType<EditorBeatmap>().SingleOrDefault()?.BeatmapInfo.DifficultyName;
|
||||
return difficultyName != null && difficultyName != duplicate_difficulty_name;
|
||||
});
|
||||
AddUntilStep("wait for editor load", () => Editor.IsLoaded && DialogOverlay.IsLoaded);
|
||||
|
||||
AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[]
|
||||
{
|
||||
new Fruit
|
||||
{
|
||||
StartTime = 0
|
||||
},
|
||||
new Fruit
|
||||
{
|
||||
StartTime = 1000
|
||||
}
|
||||
}));
|
||||
|
||||
AddStep("set difficulty name", () => EditorBeatmap.BeatmapInfo.DifficultyName = duplicate_difficulty_name);
|
||||
AddUntilStep("wait for has unsaved changes", () => Editor.HasUnsavedChanges);
|
||||
|
||||
AddStep("exit", () => Editor.Exit());
|
||||
AddUntilStep("wait for dialog", () => DialogOverlay.CurrentDialog is PromptForSaveDialog);
|
||||
AddStep("attempt to save", () => DialogOverlay.CurrentDialog.PerformOkAction());
|
||||
AddAssert("editor is still current", () => Editor.IsCurrentScreen());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCreateNewDifficultyForInconvertibleRuleset()
|
||||
{
|
||||
|
@ -8,7 +8,7 @@ using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
@ -178,7 +178,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
AddAssert("distance spacing increased by 0.5", () => editorBeatmap.BeatmapInfo.DistanceSpacing == originalSpacing + 0.5);
|
||||
}
|
||||
|
||||
public partial class EditorBeatmapContainer : Container
|
||||
public partial class EditorBeatmapContainer : PopoverContainer
|
||||
{
|
||||
private readonly IWorkingBeatmap working;
|
||||
|
||||
|
@ -21,9 +21,11 @@ using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Scoring;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
@ -147,6 +149,38 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddUntilStep("score in database", () => Realm.Run(r => r.Find<ScoreInfo>(Player.Score.ScoreInfo.ID) != null));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestReplayExport()
|
||||
{
|
||||
CreateTest();
|
||||
|
||||
AddUntilStep("wait for track to start running", () => Beatmap.Value.Track.IsRunning);
|
||||
|
||||
AddStep("seek to completion", () => Player.GameplayClockContainer.Seek(Player.DrawableRuleset.Objects.Last().GetEndTime()));
|
||||
|
||||
AddUntilStep("results displayed", () => (Player.GetChildScreen() as ResultsScreen)?.IsLoaded == true);
|
||||
AddUntilStep("score in database", () => Realm.Run(r => r.Find<ScoreInfo>(Player.Score.ScoreInfo.ID) != null));
|
||||
|
||||
AddUntilStep("wait for button clickable", () => ((OsuScreen)Player.GetChildScreen())
|
||||
.ChildrenOfType<ReplayDownloadButton>().FirstOrDefault()?
|
||||
.ChildrenOfType<OsuClickableContainer>().FirstOrDefault()?
|
||||
.Enabled.Value == true);
|
||||
|
||||
AddAssert("no export files", () => !LocalStorage.GetFiles("exports").Any());
|
||||
|
||||
AddStep("Export replay", () => InputManager.PressKey(Key.F2));
|
||||
|
||||
string? filePath = null;
|
||||
|
||||
// Files starting with _ are temporary, created by CreateFileSafely call.
|
||||
AddUntilStep("wait for export file", () => filePath = LocalStorage.GetFiles("exports").SingleOrDefault(f => !f.StartsWith("_", StringComparison.Ordinal)), () => Is.Not.Null);
|
||||
AddAssert("filesize is non-zero", () =>
|
||||
{
|
||||
using (var stream = LocalStorage.GetStream(filePath))
|
||||
return stream.Length;
|
||||
}, () => Is.Not.Zero);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestScoreStoredLocallyCustomRuleset()
|
||||
{
|
||||
|
@ -8,6 +8,7 @@ using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.SkinEditor;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
@ -19,7 +20,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Test]
|
||||
public void TestToggleEditor()
|
||||
{
|
||||
AddStep("show available components", () => SetContents(_ => new SkinComponentToolbox
|
||||
var skinComponentsContainer = new SkinComponentsContainer(new SkinComponentsContainerLookup(SkinComponentsContainerLookup.TargetArea.SongSelect));
|
||||
|
||||
AddStep("show available components", () => SetContents(_ => new SkinComponentToolbox(skinComponentsContainer, null)
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
|
@ -185,6 +185,37 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
AddStep("shorten to -10 length", () => path.ExpectedDistance.Value = -10);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGetSegmentEnds()
|
||||
{
|
||||
var positions = new[]
|
||||
{
|
||||
Vector2.Zero,
|
||||
new Vector2(100, 0),
|
||||
new Vector2(100),
|
||||
new Vector2(200, 100),
|
||||
};
|
||||
double[] distances = { 100d, 200d, 300d };
|
||||
|
||||
AddStep("create path", () => path.ControlPoints.AddRange(positions.Select(p => new PathControlPoint(p, PathType.Linear))));
|
||||
AddAssert("segment ends are correct", () => path.GetSegmentEnds(), () => Is.EqualTo(distances.Select(d => d / 300)));
|
||||
AddAssert("segment end positions recovered", () => path.GetSegmentEnds().Select(p => path.PositionAt(p)), () => Is.EqualTo(positions.Skip(1)));
|
||||
|
||||
AddStep("lengthen last segment", () => path.ExpectedDistance.Value = 400);
|
||||
AddAssert("segment ends are correct", () => path.GetSegmentEnds(), () => Is.EqualTo(distances.Select(d => d / 400)));
|
||||
AddAssert("segment end positions recovered", () => path.GetSegmentEnds().Select(p => path.PositionAt(p)), () => Is.EqualTo(positions.Skip(1)));
|
||||
|
||||
AddStep("shorten last segment", () => path.ExpectedDistance.Value = 150);
|
||||
AddAssert("segment ends are correct", () => path.GetSegmentEnds(), () => Is.EqualTo(distances.Select(d => d / 150)));
|
||||
// see remarks in `GetSegmentEnds()` xmldoc (`SliderPath.PositionAt()` clamps progress to [0,1]).
|
||||
AddAssert("segment end positions not recovered", () => path.GetSegmentEnds().Select(p => path.PositionAt(p)), () => Is.EqualTo(new[]
|
||||
{
|
||||
positions[1],
|
||||
new Vector2(100, 50),
|
||||
new Vector2(100, 50),
|
||||
}));
|
||||
}
|
||||
|
||||
private List<PathControlPoint> createSegment(PathType type, params Vector2[] controlPoints)
|
||||
{
|
||||
var points = controlPoints.Select(p => new PathControlPoint { Position = p }).ToList();
|
||||
|
@ -84,12 +84,12 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFocusOnTabKeyWhenExpanded()
|
||||
public void TestFocusOnEnterKeyWhenExpanded()
|
||||
{
|
||||
setLocalUserPlaying(true);
|
||||
|
||||
assertChatFocused(false);
|
||||
AddStep("press tab", () => InputManager.Key(Key.Tab));
|
||||
AddStep("press enter", () => InputManager.Key(Key.Enter));
|
||||
assertChatFocused(true);
|
||||
}
|
||||
|
||||
@ -99,19 +99,19 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
setLocalUserPlaying(true);
|
||||
|
||||
assertChatFocused(false);
|
||||
AddStep("press tab", () => InputManager.Key(Key.Tab));
|
||||
AddStep("press enter", () => InputManager.Key(Key.Enter));
|
||||
assertChatFocused(true);
|
||||
AddStep("press escape", () => InputManager.Key(Key.Escape));
|
||||
assertChatFocused(false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFocusOnTabKeyWhenNotExpanded()
|
||||
public void TestFocusOnEnterKeyWhenNotExpanded()
|
||||
{
|
||||
AddStep("set not expanded", () => chatDisplay.Expanded.Value = false);
|
||||
AddUntilStep("is not visible", () => !chatDisplay.IsPresent);
|
||||
|
||||
AddStep("press tab", () => InputManager.Key(Key.Tab));
|
||||
AddStep("press enter", () => InputManager.Key(Key.Enter));
|
||||
assertChatFocused(true);
|
||||
AddUntilStep("is visible", () => chatDisplay.IsPresent);
|
||||
|
||||
@ -120,21 +120,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddUntilStep("is not visible", () => !chatDisplay.IsPresent);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestFocusToggleViaAction()
|
||||
{
|
||||
AddStep("set not expanded", () => chatDisplay.Expanded.Value = false);
|
||||
AddUntilStep("is not visible", () => !chatDisplay.IsPresent);
|
||||
|
||||
AddStep("press tab", () => InputManager.Key(Key.Tab));
|
||||
assertChatFocused(true);
|
||||
AddUntilStep("is visible", () => chatDisplay.IsPresent);
|
||||
|
||||
AddStep("press tab", () => InputManager.Key(Key.Tab));
|
||||
assertChatFocused(false);
|
||||
AddUntilStep("is not visible", () => !chatDisplay.IsPresent);
|
||||
}
|
||||
|
||||
private void assertChatFocused(bool isFocused) =>
|
||||
AddAssert($"chat {(isFocused ? "focused" : "not focused")}", () => textBox.HasFocus == isFocused);
|
||||
|
||||
|
@ -49,6 +49,8 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
LoadComponentAsync(leaderboard = new MultiSpectatorLeaderboard(clocks.Keys.Select(id => new MultiplayerRoomUser(id)).ToArray())
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Expanded = { Value = true }
|
||||
}, Add);
|
||||
});
|
||||
|
@ -65,6 +65,47 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddStep("clear playing users", () => playingUsers.Clear());
|
||||
}
|
||||
|
||||
[TestCase(1)]
|
||||
[TestCase(4)]
|
||||
[TestCase(9)]
|
||||
public void TestGeneral(int count)
|
||||
{
|
||||
int[] userIds = getPlayerIds(count);
|
||||
|
||||
start(userIds);
|
||||
loadSpectateScreen();
|
||||
|
||||
sendFrames(userIds, 1000);
|
||||
AddWaitStep("wait a bit", 20);
|
||||
}
|
||||
|
||||
[TestCase(2)]
|
||||
[TestCase(16)]
|
||||
public void TestTeams(int count)
|
||||
{
|
||||
int[] userIds = getPlayerIds(count);
|
||||
|
||||
start(userIds, teams: true);
|
||||
loadSpectateScreen();
|
||||
|
||||
sendFrames(userIds, 1000);
|
||||
AddWaitStep("wait a bit", 20);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMultipleStartRequests()
|
||||
{
|
||||
int[] userIds = getPlayerIds(2);
|
||||
|
||||
start(userIds);
|
||||
loadSpectateScreen();
|
||||
|
||||
sendFrames(userIds, 1000);
|
||||
AddWaitStep("wait a bit", 20);
|
||||
|
||||
start(userIds);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDelayedStart()
|
||||
{
|
||||
@ -88,18 +129,6 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddUntilStep("two players added", () => spectatorScreen.ChildrenOfType<Player>().Count() == 2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestGeneral()
|
||||
{
|
||||
int[] userIds = getPlayerIds(4);
|
||||
|
||||
start(userIds);
|
||||
loadSpectateScreen();
|
||||
|
||||
sendFrames(userIds, 1000);
|
||||
AddWaitStep("wait a bit", 20);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSpectatorPlayerInteractiveElementsHidden()
|
||||
{
|
||||
@ -434,16 +463,18 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
|
||||
private void start(int userId, int? beatmapId = null) => start(new[] { userId }, beatmapId);
|
||||
|
||||
private void start(int[] userIds, int? beatmapId = null, APIMod[]? mods = null)
|
||||
private void start(int[] userIds, int? beatmapId = null, APIMod[]? mods = null, bool teams = false)
|
||||
{
|
||||
AddStep("start play", () =>
|
||||
{
|
||||
foreach (int id in userIds)
|
||||
for (int i = 0; i < userIds.Length; i++)
|
||||
{
|
||||
int id = userIds[i];
|
||||
var user = new MultiplayerRoomUser(id)
|
||||
{
|
||||
User = new APIUser { Id = id },
|
||||
Mods = mods ?? Array.Empty<APIMod>(),
|
||||
MatchState = teams ? new TeamVersusUserState { TeamID = i % 2 } : null,
|
||||
};
|
||||
|
||||
OnlinePlayDependencies.MultiplayerClient.AddUser(user, true);
|
||||
|
@ -170,6 +170,39 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
AddUntilStep("time is correct", () => getEditor().ChildrenOfType<EditorClock>().First().CurrentTime, () => Is.EqualTo(1234));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestAttemptGlobalMusicOperationFromEditor()
|
||||
{
|
||||
BeatmapSetInfo beatmapSet = null!;
|
||||
|
||||
AddStep("import test beatmap", () => Game.BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).WaitSafely());
|
||||
AddStep("retrieve beatmap", () => beatmapSet = Game.BeatmapManager.QueryBeatmapSet(set => !set.Protected).AsNonNull().Value.Detach());
|
||||
|
||||
AddStep("present beatmap", () => Game.PresentBeatmap(beatmapSet));
|
||||
AddUntilStep("wait for song select",
|
||||
() => Game.Beatmap.Value.BeatmapSetInfo.Equals(beatmapSet)
|
||||
&& Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect
|
||||
&& songSelect.IsLoaded);
|
||||
|
||||
AddUntilStep("wait for music playing", () => Game.MusicController.IsPlaying);
|
||||
AddStep("user request stop", () => Game.MusicController.Stop(requestedByUser: true));
|
||||
AddUntilStep("wait for music stopped", () => !Game.MusicController.IsPlaying);
|
||||
|
||||
AddStep("open editor", () => ((PlaySongSelect)Game.ScreenStack.CurrentScreen).Edit(beatmapSet.Beatmaps.First(beatmap => beatmap.Ruleset.OnlineID == 0)));
|
||||
AddUntilStep("wait for editor open", () => Game.ScreenStack.CurrentScreen is Editor editor && editor.ReadyForUse);
|
||||
|
||||
AddUntilStep("music still stopped", () => !Game.MusicController.IsPlaying);
|
||||
AddStep("user request play", () => Game.MusicController.Play(requestedByUser: true));
|
||||
AddUntilStep("music still stopped", () => !Game.MusicController.IsPlaying);
|
||||
|
||||
AddStep("exit to song select", () => Game.PerformFromScreen(_ => { }, typeof(PlaySongSelect).Yield()));
|
||||
AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect);
|
||||
|
||||
AddUntilStep("wait for music playing", () => Game.MusicController.IsPlaying);
|
||||
AddStep("user request stop", () => Game.MusicController.Stop(requestedByUser: true));
|
||||
AddUntilStep("wait for music stopped", () => !Game.MusicController.IsPlaying);
|
||||
}
|
||||
|
||||
private EditorBeatmap getEditorBeatmap() => getEditor().ChildrenOfType<EditorBeatmap>().Single();
|
||||
|
||||
private Editor getEditor() => (Editor)Game.ScreenStack.CurrentScreen;
|
||||
|
@ -13,6 +13,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Screens;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osu.Game.Screens.Play;
|
||||
@ -86,6 +87,29 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
AddAssert("did perform", () => actionPerformed);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPerformAtMenuFromPlayerLoaderWithAutoplayShortcut()
|
||||
{
|
||||
importAndWaitForSongSelect();
|
||||
|
||||
AddStep("press ctrl+enter", () =>
|
||||
{
|
||||
InputManager.PressKey(Key.ControlLeft);
|
||||
InputManager.Key(Key.Enter);
|
||||
InputManager.ReleaseKey(Key.ControlLeft);
|
||||
});
|
||||
|
||||
AddUntilStep("Wait for new screen", () => Game.ScreenStack.CurrentScreen is PlayerLoader);
|
||||
|
||||
AddAssert("Mods include autoplay", () => Game.SelectedMods.Value.Any(m => m is ModAutoplay));
|
||||
|
||||
AddStep("try to perform", () => Game.PerformFromScreen(_ => actionPerformed = true));
|
||||
AddUntilStep("returned to main menu", () => Game.ScreenStack.CurrentScreen is MainMenu);
|
||||
AddAssert("did perform", () => actionPerformed);
|
||||
|
||||
AddAssert("Mods don't include autoplay", () => !Game.SelectedMods.Value.Any(m => m is ModAutoplay));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestPerformEnsuresScreenIsLoaded()
|
||||
{
|
||||
|
@ -154,7 +154,14 @@ namespace osu.Game.Tests.Visual.Online
|
||||
Type = ChangelogEntryType.Misc,
|
||||
Category = "Code quality",
|
||||
Title = "Clean up another thing"
|
||||
}
|
||||
},
|
||||
new APIChangelogEntry
|
||||
{
|
||||
Type = ChangelogEntryType.Add,
|
||||
Category = "osu!",
|
||||
Title = "Add entry with news url",
|
||||
Url = "https://osu.ppy.sh/home/news/2023-07-27-summer-splash"
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -56,38 +56,38 @@ namespace osu.Game.Tests.Visual
|
||||
public void AllowTrackAdjustmentsTest()
|
||||
{
|
||||
AddStep("push allowing screen", () => stack.Push(loadNewScreen<AllowScreen>()));
|
||||
AddAssert("allows adjustments 1", () => musicController.AllowTrackAdjustments);
|
||||
AddAssert("allows adjustments 1", () => musicController.ApplyModTrackAdjustments);
|
||||
|
||||
AddStep("push inheriting screen", () => stack.Push(loadNewScreen<InheritScreen>()));
|
||||
AddAssert("allows adjustments 2", () => musicController.AllowTrackAdjustments);
|
||||
AddAssert("allows adjustments 2", () => musicController.ApplyModTrackAdjustments);
|
||||
|
||||
AddStep("push disallowing screen", () => stack.Push(loadNewScreen<DisallowScreen>()));
|
||||
AddAssert("disallows adjustments 3", () => !musicController.AllowTrackAdjustments);
|
||||
AddAssert("disallows adjustments 3", () => !musicController.ApplyModTrackAdjustments);
|
||||
|
||||
AddStep("push inheriting screen", () => stack.Push(loadNewScreen<InheritScreen>()));
|
||||
AddAssert("disallows adjustments 4", () => !musicController.AllowTrackAdjustments);
|
||||
AddAssert("disallows adjustments 4", () => !musicController.ApplyModTrackAdjustments);
|
||||
|
||||
AddStep("push inheriting screen", () => stack.Push(loadNewScreen<InheritScreen>()));
|
||||
AddAssert("disallows adjustments 5", () => !musicController.AllowTrackAdjustments);
|
||||
AddAssert("disallows adjustments 5", () => !musicController.ApplyModTrackAdjustments);
|
||||
|
||||
AddStep("push allowing screen", () => stack.Push(loadNewScreen<AllowScreen>()));
|
||||
AddAssert("allows adjustments 6", () => musicController.AllowTrackAdjustments);
|
||||
AddAssert("allows adjustments 6", () => musicController.ApplyModTrackAdjustments);
|
||||
|
||||
// Now start exiting from screens
|
||||
AddStep("exit screen", () => stack.Exit());
|
||||
AddAssert("disallows adjustments 7", () => !musicController.AllowTrackAdjustments);
|
||||
AddAssert("disallows adjustments 7", () => !musicController.ApplyModTrackAdjustments);
|
||||
|
||||
AddStep("exit screen", () => stack.Exit());
|
||||
AddAssert("disallows adjustments 8", () => !musicController.AllowTrackAdjustments);
|
||||
AddAssert("disallows adjustments 8", () => !musicController.ApplyModTrackAdjustments);
|
||||
|
||||
AddStep("exit screen", () => stack.Exit());
|
||||
AddAssert("disallows adjustments 9", () => !musicController.AllowTrackAdjustments);
|
||||
AddAssert("disallows adjustments 9", () => !musicController.ApplyModTrackAdjustments);
|
||||
|
||||
AddStep("exit screen", () => stack.Exit());
|
||||
AddAssert("allows adjustments 10", () => musicController.AllowTrackAdjustments);
|
||||
AddAssert("allows adjustments 10", () => musicController.ApplyModTrackAdjustments);
|
||||
|
||||
AddStep("exit screen", () => stack.Exit());
|
||||
AddAssert("allows adjustments 11", () => musicController.AllowTrackAdjustments);
|
||||
AddAssert("allows adjustments 11", () => musicController.ApplyModTrackAdjustments);
|
||||
}
|
||||
|
||||
public partial class TestScreen : ScreenWithBeatmapBackground
|
||||
@ -129,12 +129,12 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
private partial class AllowScreen : OsuScreen
|
||||
{
|
||||
public override bool? AllowTrackAdjustments => true;
|
||||
public override bool? ApplyModTrackAdjustments => true;
|
||||
}
|
||||
|
||||
public partial class DisallowScreen : OsuScreen
|
||||
{
|
||||
public override bool? AllowTrackAdjustments => false;
|
||||
public override bool? ApplyModTrackAdjustments => false;
|
||||
}
|
||||
|
||||
private partial class InheritScreen : OsuScreen
|
||||
|
@ -542,7 +542,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
|
||||
AddStep("clear search", () => modSelectOverlay.SearchTerm = string.Empty);
|
||||
AddStep("press enter", () => InputManager.Key(Key.Enter));
|
||||
AddAssert("mod select still visible", () => modSelectOverlay.State.Value == Visibility.Visible);
|
||||
AddAssert("mod select hidden", () => modSelectOverlay.State.Value == Visibility.Hidden);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -0,0 +1,130 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
public partial class TestSceneSliderWithTextBoxInput : OsuManualInputManagerTestScene
|
||||
{
|
||||
private SliderWithTextBoxInput<float> sliderWithTextBoxInput = null!;
|
||||
|
||||
private OsuSliderBar<float> slider => sliderWithTextBoxInput.ChildrenOfType<OsuSliderBar<float>>().Single();
|
||||
private Nub nub => sliderWithTextBoxInput.ChildrenOfType<Nub>().Single();
|
||||
private OsuTextBox textBox => sliderWithTextBoxInput.ChildrenOfType<OsuTextBox>().Single();
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("create slider", () => Child = sliderWithTextBoxInput = new SliderWithTextBoxInput<float>("Test Slider")
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Width = 0.5f,
|
||||
Current = new BindableFloat
|
||||
{
|
||||
MinValue = -5,
|
||||
MaxValue = 5,
|
||||
Precision = 0.2f
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNonInstantaneousMode()
|
||||
{
|
||||
AddStep("set instantaneous to false", () => sliderWithTextBoxInput.Instantaneous = false);
|
||||
|
||||
AddStep("focus textbox", () => InputManager.ChangeFocus(textBox));
|
||||
AddStep("change text", () => textBox.Text = "3");
|
||||
AddAssert("slider not moved", () => slider.Current.Value, () => Is.Zero);
|
||||
AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.Zero);
|
||||
|
||||
AddStep("commit text", () => InputManager.Key(Key.Enter));
|
||||
AddAssert("slider moved", () => slider.Current.Value, () => Is.EqualTo(3));
|
||||
AddAssert("current changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(3));
|
||||
|
||||
AddStep("move mouse to nub", () => InputManager.MoveMouseTo(nub));
|
||||
AddStep("hold left mouse", () => InputManager.PressButton(MouseButton.Left));
|
||||
AddStep("move mouse to minimum", () => InputManager.MoveMouseTo(sliderWithTextBoxInput.ScreenSpaceDrawQuad.BottomLeft));
|
||||
AddAssert("textbox not changed", () => textBox.Current.Value, () => Is.EqualTo("3"));
|
||||
AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(3));
|
||||
|
||||
AddStep("release left mouse", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
AddAssert("textbox changed", () => textBox.Current.Value, () => Is.EqualTo("-5"));
|
||||
AddAssert("current changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5));
|
||||
|
||||
AddStep("focus textbox", () => InputManager.ChangeFocus(textBox));
|
||||
AddStep("set text to invalid", () => textBox.Text = "garbage");
|
||||
AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5));
|
||||
AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5));
|
||||
|
||||
AddStep("commit text", () => InputManager.Key(Key.Enter));
|
||||
AddAssert("text restored", () => textBox.Text, () => Is.EqualTo("-5"));
|
||||
AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5));
|
||||
AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5));
|
||||
|
||||
AddStep("focus textbox", () => InputManager.ChangeFocus(textBox));
|
||||
AddStep("set text to invalid", () => textBox.Text = "garbage");
|
||||
AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5));
|
||||
AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5));
|
||||
|
||||
AddStep("lose focus", () => InputManager.ChangeFocus(null));
|
||||
AddAssert("text restored", () => textBox.Text, () => Is.EqualTo("-5"));
|
||||
AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5));
|
||||
AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestInstantaneousMode()
|
||||
{
|
||||
AddStep("set instantaneous to true", () => sliderWithTextBoxInput.Instantaneous = true);
|
||||
|
||||
AddStep("focus textbox", () => InputManager.ChangeFocus(textBox));
|
||||
AddStep("change text", () => textBox.Text = "3");
|
||||
AddAssert("slider moved", () => slider.Current.Value, () => Is.EqualTo(3));
|
||||
AddAssert("current changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(3));
|
||||
|
||||
AddStep("commit text", () => InputManager.Key(Key.Enter));
|
||||
AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(3));
|
||||
AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(3));
|
||||
|
||||
AddStep("move mouse to nub", () => InputManager.MoveMouseTo(nub));
|
||||
AddStep("hold left mouse", () => InputManager.PressButton(MouseButton.Left));
|
||||
AddStep("move mouse to minimum", () => InputManager.MoveMouseTo(sliderWithTextBoxInput.ScreenSpaceDrawQuad.BottomLeft));
|
||||
AddAssert("textbox changed", () => textBox.Current.Value, () => Is.EqualTo("-5"));
|
||||
AddAssert("current changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5));
|
||||
|
||||
AddStep("release left mouse", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
AddAssert("textbox not changed", () => textBox.Current.Value, () => Is.EqualTo("-5"));
|
||||
AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5));
|
||||
|
||||
AddStep("focus textbox", () => InputManager.ChangeFocus(textBox));
|
||||
AddStep("set text to invalid", () => textBox.Text = "garbage");
|
||||
AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5));
|
||||
AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5));
|
||||
|
||||
AddStep("commit text", () => InputManager.Key(Key.Enter));
|
||||
AddAssert("text restored", () => textBox.Text, () => Is.EqualTo("-5"));
|
||||
AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5));
|
||||
AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5));
|
||||
|
||||
AddStep("focus textbox", () => InputManager.ChangeFocus(textBox));
|
||||
AddStep("set text to invalid", () => textBox.Text = "garbage");
|
||||
AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5));
|
||||
AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5));
|
||||
|
||||
AddStep("lose focus", () => InputManager.ChangeFocus(null));
|
||||
AddAssert("text restored", () => textBox.Text, () => Is.EqualTo("-5"));
|
||||
AddAssert("slider not moved", () => slider.Current.Value, () => Is.EqualTo(-5));
|
||||
AddAssert("current not changed", () => sliderWithTextBoxInput.Current.Value, () => Is.EqualTo(-5));
|
||||
}
|
||||
}
|
||||
}
|
@ -99,16 +99,18 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
TestUpdateableOnlineBeatmapSetCover updateableCover = null;
|
||||
|
||||
AddStep("setup cover", () => Child = updateableCover = new TestUpdateableOnlineBeatmapSetCover
|
||||
AddStep("setup cover", () => Child = updateableCover = new TestUpdateableOnlineBeatmapSetCover(400)
|
||||
{
|
||||
OnlineInfo = CreateAPIBeatmapSet(),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
});
|
||||
|
||||
AddStep("change model", () => updateableCover.OnlineInfo = null);
|
||||
AddWaitStep("wait some", 5);
|
||||
AddAssert("no cover added", () => !updateableCover.ChildrenOfType<DelayedLoadUnloadWrapper>().Any());
|
||||
AddStep("change model to null", () => updateableCover.OnlineInfo = null);
|
||||
|
||||
AddUntilStep("wait for load", () => updateableCover.DelayedLoadFinished);
|
||||
|
||||
AddAssert("no cover added", () => !updateableCover.ChildrenOfType<TestOnlineBeatmapSetCover>().Any());
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -143,11 +145,19 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
private readonly int loadDelay;
|
||||
|
||||
public bool DelayedLoadFinished;
|
||||
|
||||
public TestUpdateableOnlineBeatmapSetCover(int loadDelay = 10000)
|
||||
{
|
||||
this.loadDelay = loadDelay;
|
||||
}
|
||||
|
||||
protected override void OnLoadFinished()
|
||||
{
|
||||
base.OnLoadFinished();
|
||||
DelayedLoadFinished = true;
|
||||
}
|
||||
|
||||
protected override Drawable CreateDrawable(IBeatmapSetOnlineInfo model)
|
||||
{
|
||||
if (model == null)
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osu.Game.Tournament.Components;
|
||||
@ -13,7 +11,7 @@ namespace osu.Game.Tournament.Tests.Components
|
||||
{
|
||||
public partial class TestSceneDateTextBox : OsuManualInputManagerTestScene
|
||||
{
|
||||
private DateTextBox textBox;
|
||||
private DateTextBox textBox = null!;
|
||||
|
||||
[SetUp]
|
||||
public void Setup() => Schedule(() =>
|
||||
|
@ -1,28 +1,36 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps.Legacy;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osu.Game.Tournament.Components;
|
||||
using osu.Game.Tournament.Models;
|
||||
|
||||
namespace osu.Game.Tournament.Tests.Components
|
||||
{
|
||||
[TestFixture]
|
||||
public partial class TestSceneSongBar : OsuTestScene
|
||||
public partial class TestSceneSongBar : TournamentTestScene
|
||||
{
|
||||
[Cached]
|
||||
private readonly LadderInfo ladder = new LadderInfo();
|
||||
private SongBar songBar = null!;
|
||||
private TournamentBeatmap ladderBeatmap = null!;
|
||||
|
||||
[Test]
|
||||
public void TestSongBar()
|
||||
[SetUpSteps]
|
||||
public override void SetUpSteps()
|
||||
{
|
||||
SongBar songBar = null;
|
||||
base.SetUpSteps();
|
||||
|
||||
AddStep("setup picks bans", () =>
|
||||
{
|
||||
ladderBeatmap = CreateSampleBeatmap();
|
||||
Ladder.CurrentMatch.Value!.PicksBans.Add(new BeatmapChoice
|
||||
{
|
||||
BeatmapID = ladderBeatmap.OnlineID,
|
||||
Team = TeamColour.Red,
|
||||
Type = ChoiceType.Pick,
|
||||
});
|
||||
});
|
||||
|
||||
AddStep("create bar", () => Child = songBar = new SongBar
|
||||
{
|
||||
@ -31,16 +39,22 @@ namespace osu.Game.Tournament.Tests.Components
|
||||
Origin = Anchor.Centre
|
||||
});
|
||||
AddUntilStep("wait for loaded", () => songBar.IsLoaded);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSongBar()
|
||||
{
|
||||
AddStep("set beatmap", () =>
|
||||
{
|
||||
var beatmap = CreateAPIBeatmap(Ruleset.Value);
|
||||
|
||||
beatmap.CircleSize = 3.4f;
|
||||
beatmap.ApproachRate = 6.8f;
|
||||
beatmap.OverallDifficulty = 5.5f;
|
||||
beatmap.StarRating = 4.56f;
|
||||
beatmap.Length = 123456;
|
||||
beatmap.BPM = 133;
|
||||
beatmap.OnlineID = ladderBeatmap.OnlineID;
|
||||
|
||||
songBar.Beatmap = new TournamentBeatmap(beatmap);
|
||||
});
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -19,12 +17,12 @@ namespace osu.Game.Tournament.Tests.Components
|
||||
public partial class TestSceneTournamentModDisplay : TournamentTestScene
|
||||
{
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private IRulesetStore rulesets { get; set; }
|
||||
private IRulesetStore rulesets { get; set; } = null!;
|
||||
|
||||
private FillFlowContainer<TournamentBeatmapPanel> fillFlow;
|
||||
private FillFlowContainer<TournamentBeatmapPanel> fillFlow = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
@ -45,7 +43,7 @@ namespace osu.Game.Tournament.Tests.Components
|
||||
|
||||
private void success(APIBeatmap beatmap)
|
||||
{
|
||||
var ruleset = rulesets.GetRuleset(Ladder.Ruleset.Value.OnlineID);
|
||||
var ruleset = rulesets.GetRuleset(Ladder.Ruleset.Value?.OnlineID ?? -1);
|
||||
|
||||
if (ruleset == null)
|
||||
return;
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
@ -81,11 +79,11 @@ namespace osu.Game.Tournament.Tests.NonVisual
|
||||
public partial class TestTournament : TournamentGameBase
|
||||
{
|
||||
private readonly bool resetRuleset;
|
||||
private readonly Action runOnLoadComplete;
|
||||
private readonly Action? runOnLoadComplete;
|
||||
|
||||
public new Task BracketLoadTask => base.BracketLoadTask;
|
||||
|
||||
public TestTournament(bool resetRuleset = false, [InstantHandle] Action runOnLoadComplete = null)
|
||||
public TestTournament(bool resetRuleset = false, [InstantHandle] Action? runOnLoadComplete = null)
|
||||
{
|
||||
this.resetRuleset = resetRuleset;
|
||||
this.runOnLoadComplete = runOnLoadComplete;
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
@ -36,11 +34,11 @@ namespace osu.Game.Tournament.Tests.NonVisual
|
||||
{
|
||||
var osu = LoadTournament(host);
|
||||
TournamentStorage storage = (TournamentStorage)osu.Dependencies.Get<Storage>();
|
||||
FileBasedIPC ipc = null;
|
||||
FileBasedIPC? ipc = null;
|
||||
|
||||
WaitForOrAssert(() => (ipc = osu.Dependencies.Get<MatchIPCInfo>() as FileBasedIPC)?.IsLoaded == true, @"ipc could not be populated in a reasonable amount of time");
|
||||
|
||||
Assert.True(ipc.SetIPCLocation(testStableInstallDirectory));
|
||||
Assert.True(ipc!.SetIPCLocation(testStableInstallDirectory));
|
||||
Assert.True(storage.AllTournaments.Exists("stable.json"));
|
||||
}
|
||||
finally
|
||||
|
@ -35,8 +35,8 @@ namespace osu.Game.Tournament.Tests.NonVisual
|
||||
PlayersPerTeam = { Value = 4 },
|
||||
Teams =
|
||||
{
|
||||
match.Team1.Value,
|
||||
match.Team2.Value,
|
||||
match.Team1.Value!,
|
||||
match.Team2.Value!,
|
||||
},
|
||||
Rounds =
|
||||
{
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@ -13,7 +11,7 @@ namespace osu.Game.Tournament.Tests.NonVisual
|
||||
{
|
||||
public abstract class TournamentHostTest
|
||||
{
|
||||
public static TournamentGameBase LoadTournament(GameHost host, TournamentGameBase tournament = null)
|
||||
public static TournamentGameBase LoadTournament(GameHost host, TournamentGameBase? tournament = null)
|
||||
{
|
||||
tournament ??= new TournamentGameBase();
|
||||
Task.Factory.StartNew(() => host.Run(tournament), TaskCreationOptions.LongRunning)
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
|
@ -94,7 +94,7 @@ namespace osu.Game.Tournament.Tests.Screens
|
||||
|
||||
AddStep("release mouse button", () => InputManager.ReleaseButton(MouseButton.Left));
|
||||
|
||||
AddAssert("assert ladder teams reset", () => Ladder.CurrentMatch.Value.Team1.Value == null && Ladder.CurrentMatch.Value.Team2.Value == null);
|
||||
AddAssert("assert ladder teams reset", () => Ladder.CurrentMatch.Value?.Team1.Value == null && Ladder.CurrentMatch.Value?.Team2.Value == null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
@ -16,7 +14,7 @@ namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
public partial class TestSceneMapPoolScreen : TournamentScreenTestScene
|
||||
{
|
||||
private MapPoolScreen screen;
|
||||
private MapPoolScreen screen = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
@ -32,7 +30,7 @@ namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
AddStep("load few maps", () =>
|
||||
{
|
||||
Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Clear();
|
||||
Ladder.CurrentMatch.Value!.Round.Value!.Beatmaps.Clear();
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
addBeatmap();
|
||||
@ -52,7 +50,7 @@ namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
AddStep("load just enough maps", () =>
|
||||
{
|
||||
Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Clear();
|
||||
Ladder.CurrentMatch.Value!.Round.Value!.Beatmaps.Clear();
|
||||
|
||||
for (int i = 0; i < 18; i++)
|
||||
addBeatmap();
|
||||
@ -72,7 +70,7 @@ namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
AddStep("load many maps", () =>
|
||||
{
|
||||
Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Clear();
|
||||
Ladder.CurrentMatch.Value!.Round.Value!.Beatmaps.Clear();
|
||||
|
||||
for (int i = 0; i < 19; i++)
|
||||
addBeatmap();
|
||||
@ -92,7 +90,7 @@ namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
AddStep("load many maps", () =>
|
||||
{
|
||||
Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Clear();
|
||||
Ladder.CurrentMatch.Value!.Round.Value!.Beatmaps.Clear();
|
||||
|
||||
for (int i = 0; i < 11; i++)
|
||||
addBeatmap(i > 4 ? Ruleset.Value.CreateInstance().AllMods.ElementAt(i).Acronym : "NM");
|
||||
@ -118,7 +116,7 @@ namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
AddStep("load many maps", () =>
|
||||
{
|
||||
Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Clear();
|
||||
Ladder.CurrentMatch.Value!.Round.Value!.Beatmaps.Clear();
|
||||
|
||||
for (int i = 0; i < 12; i++)
|
||||
addBeatmap(i > 4 ? Ruleset.Value.CreateInstance().AllMods.ElementAt(i).Acronym : "NM");
|
||||
@ -138,7 +136,7 @@ namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
AddStep("load many maps", () =>
|
||||
{
|
||||
Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Clear();
|
||||
Ladder.CurrentMatch.Value!.Round.Value!.Beatmaps.Clear();
|
||||
|
||||
for (int i = 0; i < 12; i++)
|
||||
addBeatmap(i > 4 ? Ruleset.Value.CreateInstance().AllMods.ElementAt(i).Acronym : "NM");
|
||||
@ -155,7 +153,7 @@ namespace osu.Game.Tournament.Tests.Screens
|
||||
|
||||
private void addBeatmap(string mods = "NM")
|
||||
{
|
||||
Ladder.CurrentMatch.Value.Round.Value.Beatmaps.Add(new RoundBeatmap
|
||||
Ladder.CurrentMatch.Value!.Round.Value!.Beatmaps.Add(new RoundBeatmap
|
||||
{
|
||||
Beatmap = CreateSampleBeatmap(),
|
||||
Mods = mods
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Game.Tournament.Models;
|
||||
using osu.Game.Tournament.Screens.Editors;
|
||||
|
||||
@ -17,7 +18,7 @@ namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
var match = CreateSampleMatch();
|
||||
|
||||
Add(new SeedingEditorScreen(match.Team1.Value, new TeamEditorScreen())
|
||||
Add(new SeedingEditorScreen(match.Team1.Value.AsNonNull(), new TeamEditorScreen())
|
||||
{
|
||||
Width = 0.85f // create room for control panel
|
||||
});
|
||||
|
@ -17,7 +17,7 @@ namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
var match = Ladder.CurrentMatch.Value!;
|
||||
|
||||
match.Round.Value = Ladder.Rounds.FirstOrDefault(g => g.Name.Value == "Finals");
|
||||
match.Round.Value = Ladder.Rounds.First(g => g.Name.Value == "Quarterfinals");
|
||||
match.Completed.Value = true;
|
||||
});
|
||||
|
||||
|
@ -40,10 +40,10 @@ namespace osu.Game.Tournament.Tests
|
||||
|
||||
match = CreateSampleMatch();
|
||||
|
||||
Ladder.Rounds.Add(match.Round.Value);
|
||||
Ladder.Rounds.Add(match.Round.Value!);
|
||||
Ladder.Matches.Add(match);
|
||||
Ladder.Teams.Add(match.Team1.Value);
|
||||
Ladder.Teams.Add(match.Team2.Value);
|
||||
Ladder.Teams.Add(match.Team1.Value!);
|
||||
Ladder.Teams.Add(match.Team2.Value!);
|
||||
|
||||
Ruleset.BindTo(Ladder.Ruleset);
|
||||
Dependencies.CacheAs(new StableInfo(storage));
|
||||
@ -152,7 +152,7 @@ namespace osu.Game.Tournament.Tests
|
||||
},
|
||||
Round =
|
||||
{
|
||||
Value = new TournamentRound { Name = { Value = "Quarterfinals" } }
|
||||
Value = new TournamentRound { Name = { Value = "Quarterfinals" } },
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@ -17,14 +15,14 @@ namespace osu.Game.Tournament.Components
|
||||
{
|
||||
public partial class DrawableTeamFlag : Container
|
||||
{
|
||||
private readonly TournamentTeam team;
|
||||
private readonly TournamentTeam? team;
|
||||
|
||||
[UsedImplicitly]
|
||||
private Bindable<string> flag;
|
||||
private Bindable<string>? flag;
|
||||
|
||||
private Sprite flagSprite;
|
||||
private Sprite? flagSprite;
|
||||
|
||||
public DrawableTeamFlag(TournamentTeam team)
|
||||
public DrawableTeamFlag(TournamentTeam? team)
|
||||
{
|
||||
this.team = team;
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@ -12,12 +10,12 @@ namespace osu.Game.Tournament.Components
|
||||
{
|
||||
public partial class DrawableTeamTitle : TournamentSpriteTextWithBackground
|
||||
{
|
||||
private readonly TournamentTeam team;
|
||||
private readonly TournamentTeam? team;
|
||||
|
||||
[UsedImplicitly]
|
||||
private Bindable<string> acronym;
|
||||
private Bindable<string>? acronym;
|
||||
|
||||
public DrawableTeamTitle(TournamentTeam team)
|
||||
public DrawableTeamTitle(TournamentTeam? team)
|
||||
{
|
||||
this.team = team;
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@ -14,15 +12,15 @@ namespace osu.Game.Tournament.Components
|
||||
{
|
||||
public abstract partial class DrawableTournamentTeam : CompositeDrawable
|
||||
{
|
||||
public readonly TournamentTeam Team;
|
||||
public readonly TournamentTeam? Team;
|
||||
|
||||
protected readonly Container Flag;
|
||||
protected readonly TournamentSpriteText AcronymText;
|
||||
|
||||
[UsedImplicitly]
|
||||
private Bindable<string> acronym;
|
||||
private Bindable<string>? acronym;
|
||||
|
||||
protected DrawableTournamentTeam(TournamentTeam team)
|
||||
protected DrawableTournamentTeam(TournamentTeam? team)
|
||||
{
|
||||
Team = team;
|
||||
|
||||
@ -36,7 +34,8 @@ namespace osu.Game.Tournament.Components
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
if (Team == null) return;
|
||||
if (Team == null)
|
||||
return;
|
||||
|
||||
(acronym = Team.Acronym.GetBoundCopy()).BindValueChanged(_ => AcronymText.Text = Team?.Acronym.Value?.ToUpperInvariant() ?? string.Empty, true);
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
@ -16,7 +14,6 @@ using osu.Game.Extensions;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osu.Game.Tournament.Models;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -24,14 +21,14 @@ namespace osu.Game.Tournament.Components
|
||||
{
|
||||
public partial class SongBar : CompositeDrawable
|
||||
{
|
||||
private TournamentBeatmap beatmap;
|
||||
private IBeatmapInfo? beatmap;
|
||||
|
||||
public const float HEIGHT = 145 / 2f;
|
||||
|
||||
[Resolved]
|
||||
private IBindable<RulesetInfo> ruleset { get; set; }
|
||||
private IBindable<RulesetInfo> ruleset { get; set; } = null!;
|
||||
|
||||
public TournamentBeatmap Beatmap
|
||||
public IBeatmapInfo? Beatmap
|
||||
{
|
||||
set
|
||||
{
|
||||
@ -39,7 +36,7 @@ namespace osu.Game.Tournament.Components
|
||||
return;
|
||||
|
||||
beatmap = value;
|
||||
update();
|
||||
refreshContent();
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,11 +48,11 @@ namespace osu.Game.Tournament.Components
|
||||
set
|
||||
{
|
||||
mods = value;
|
||||
update();
|
||||
refreshContent();
|
||||
}
|
||||
}
|
||||
|
||||
private FillFlowContainer flow;
|
||||
private FillFlowContainer flow = null!;
|
||||
|
||||
private bool expanded;
|
||||
|
||||
@ -73,19 +70,25 @@ namespace osu.Game.Tournament.Components
|
||||
protected override bool ComputeIsMaskedAway(RectangleF maskingBounds) => false;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
Masking = true;
|
||||
CornerRadius = 5;
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = colours.Gray3,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
flow = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
LayoutDuration = 500,
|
||||
LayoutEasing = Easing.OutQuint,
|
||||
Direction = FillDirection.Full,
|
||||
Anchor = Anchor.BottomRight,
|
||||
Origin = Anchor.BottomRight,
|
||||
@ -95,7 +98,7 @@ namespace osu.Game.Tournament.Components
|
||||
Expanded = true;
|
||||
}
|
||||
|
||||
private void update()
|
||||
private void refreshContent()
|
||||
{
|
||||
if (beatmap == null)
|
||||
{
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
@ -11,6 +9,7 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Drawables;
|
||||
using osu.Game.Graphics;
|
||||
@ -21,19 +20,18 @@ namespace osu.Game.Tournament.Components
|
||||
{
|
||||
public partial class TournamentBeatmapPanel : CompositeDrawable
|
||||
{
|
||||
public readonly TournamentBeatmap Beatmap;
|
||||
public readonly IBeatmapInfo? Beatmap;
|
||||
|
||||
private readonly string mod;
|
||||
|
||||
public const float HEIGHT = 50;
|
||||
|
||||
private readonly Bindable<TournamentMatch> currentMatch = new Bindable<TournamentMatch>();
|
||||
private Box flash;
|
||||
private readonly Bindable<TournamentMatch?> currentMatch = new Bindable<TournamentMatch?>();
|
||||
|
||||
public TournamentBeatmapPanel(TournamentBeatmap beatmap, string mod = null)
|
||||
private Box flash = null!;
|
||||
|
||||
public TournamentBeatmapPanel(IBeatmapInfo? beatmap, string mod = "")
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(beatmap);
|
||||
|
||||
Beatmap = beatmap;
|
||||
this.mod = mod;
|
||||
|
||||
@ -60,7 +58,7 @@ namespace osu.Game.Tournament.Components
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = OsuColour.Gray(0.5f),
|
||||
OnlineInfo = Beatmap,
|
||||
OnlineInfo = (Beatmap as IBeatmapSetOnlineInfo),
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
@ -73,7 +71,7 @@ namespace osu.Game.Tournament.Components
|
||||
{
|
||||
new TournamentSpriteText
|
||||
{
|
||||
Text = Beatmap.GetDisplayTitleRomanisable(false, false),
|
||||
Text = Beatmap?.GetDisplayTitleRomanisable(false, false) ?? (LocalisableString)@"unknown",
|
||||
Font = OsuFont.Torus.With(weight: FontWeight.Bold),
|
||||
},
|
||||
new FillFlowContainer
|
||||
@ -90,7 +88,7 @@ namespace osu.Game.Tournament.Components
|
||||
},
|
||||
new TournamentSpriteText
|
||||
{
|
||||
Text = Beatmap.Metadata.Author.Username,
|
||||
Text = Beatmap?.Metadata.Author.Username ?? "unknown",
|
||||
Padding = new MarginPadding { Right = 20 },
|
||||
Font = OsuFont.Torus.With(weight: FontWeight.Bold, size: 14)
|
||||
},
|
||||
@ -102,7 +100,7 @@ namespace osu.Game.Tournament.Components
|
||||
},
|
||||
new TournamentSpriteText
|
||||
{
|
||||
Text = Beatmap.DifficultyName,
|
||||
Text = Beatmap?.DifficultyName ?? "unknown",
|
||||
Font = OsuFont.Torus.With(weight: FontWeight.Bold, size: 14)
|
||||
},
|
||||
}
|
||||
@ -131,36 +129,42 @@ namespace osu.Game.Tournament.Components
|
||||
}
|
||||
}
|
||||
|
||||
private void matchChanged(ValueChangedEvent<TournamentMatch> match)
|
||||
private void matchChanged(ValueChangedEvent<TournamentMatch?> match)
|
||||
{
|
||||
if (match.OldValue != null)
|
||||
match.OldValue.PicksBans.CollectionChanged -= picksBansOnCollectionChanged;
|
||||
match.NewValue.PicksBans.CollectionChanged += picksBansOnCollectionChanged;
|
||||
updateState();
|
||||
if (match.NewValue != null)
|
||||
match.NewValue.PicksBans.CollectionChanged += picksBansOnCollectionChanged;
|
||||
|
||||
Scheduler.AddOnce(updateState);
|
||||
}
|
||||
|
||||
private void picksBansOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
=> updateState();
|
||||
private void picksBansOnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
=> Scheduler.AddOnce(updateState);
|
||||
|
||||
private BeatmapChoice choice;
|
||||
private BeatmapChoice? choice;
|
||||
|
||||
private void updateState()
|
||||
{
|
||||
var found = currentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == Beatmap.OnlineID);
|
||||
|
||||
bool doFlash = found != choice;
|
||||
choice = found;
|
||||
|
||||
if (found != null)
|
||||
if (currentMatch.Value == null)
|
||||
{
|
||||
if (doFlash)
|
||||
flash?.FadeOutFromOne(500).Loop(0, 10);
|
||||
return;
|
||||
}
|
||||
|
||||
var newChoice = currentMatch.Value.PicksBans.FirstOrDefault(p => p.BeatmapID == Beatmap?.OnlineID);
|
||||
|
||||
bool shouldFlash = newChoice != choice;
|
||||
|
||||
if (newChoice != null)
|
||||
{
|
||||
if (shouldFlash)
|
||||
flash.FadeOutFromOne(500).Loop(0, 10);
|
||||
|
||||
BorderThickness = 6;
|
||||
|
||||
BorderColour = TournamentGame.GetTeamColour(found.Team);
|
||||
BorderColour = TournamentGame.GetTeamColour(newChoice.Team);
|
||||
|
||||
switch (found.Type)
|
||||
switch (newChoice.Type)
|
||||
{
|
||||
case ChoiceType.Pick:
|
||||
Colour = Color4.White;
|
||||
@ -179,6 +183,8 @@ namespace osu.Game.Tournament.Components
|
||||
BorderThickness = 0;
|
||||
Alpha = 1;
|
||||
}
|
||||
|
||||
choice = newChoice;
|
||||
}
|
||||
|
||||
private partial class NoUnloadBeatmapSetCover : UpdateableOnlineBeatmapSetCover
|
||||
|
@ -92,9 +92,9 @@ namespace osu.Game.Tournament.Components
|
||||
{
|
||||
if (info.CurrentMatch.Value is TournamentMatch match)
|
||||
{
|
||||
if (match.Team1.Value.Players.Any(u => u.OnlineID == Message.Sender.OnlineID))
|
||||
if (match.Team1.Value?.Players.Any(u => u.OnlineID == Message.Sender.OnlineID) == true)
|
||||
UsernameColour = TournamentGame.COLOUR_RED;
|
||||
else if (match.Team2.Value.Players.Any(u => u.OnlineID == Message.Sender.OnlineID))
|
||||
else if (match.Team2.Value?.Players.Any(u => u.OnlineID == Message.Sender.OnlineID) == true)
|
||||
UsernameColour = TournamentGame.COLOUR_BLUE;
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
@ -19,8 +17,8 @@ namespace osu.Game.Tournament.Components
|
||||
{
|
||||
private readonly string filename;
|
||||
private readonly bool drawFallbackGradient;
|
||||
private Video video;
|
||||
private ManualClock manualClock;
|
||||
private Video? video;
|
||||
private ManualClock? manualClock;
|
||||
|
||||
public bool VideoAvailable => video != null;
|
||||
|
||||
|
@ -1,12 +1,9 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using Microsoft.Win32;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
@ -24,36 +21,35 @@ namespace osu.Game.Tournament.IPC
|
||||
{
|
||||
public partial class FileBasedIPC : MatchIPCInfo
|
||||
{
|
||||
public Storage IPCStorage { get; private set; }
|
||||
public Storage? IPCStorage { get; private set; }
|
||||
|
||||
[Resolved]
|
||||
protected IAPIProvider API { get; private set; }
|
||||
protected IAPIProvider API { get; private set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
protected IRulesetStore Rulesets { get; private set; }
|
||||
protected IRulesetStore Rulesets { get; private set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private GameHost host { get; set; }
|
||||
private GameHost host { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private LadderInfo ladder { get; set; }
|
||||
private LadderInfo ladder { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private StableInfo stableInfo { get; set; }
|
||||
private StableInfo stableInfo { get; set; } = null!;
|
||||
|
||||
private int lastBeatmapId;
|
||||
private ScheduledDelegate scheduled;
|
||||
private GetBeatmapRequest beatmapLookupRequest;
|
||||
private ScheduledDelegate? scheduled;
|
||||
private GetBeatmapRequest? beatmapLookupRequest;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
string stablePath = stableInfo.StablePath ?? findStablePath();
|
||||
string? stablePath = stableInfo.StablePath ?? findStablePath();
|
||||
initialiseIPCStorage(stablePath);
|
||||
}
|
||||
|
||||
[CanBeNull]
|
||||
private Storage initialiseIPCStorage(string path)
|
||||
private Storage? initialiseIPCStorage(string? path)
|
||||
{
|
||||
scheduled?.Cancel();
|
||||
|
||||
@ -89,7 +85,7 @@ namespace osu.Game.Tournament.IPC
|
||||
|
||||
lastBeatmapId = beatmapId;
|
||||
|
||||
var existing = ladder.CurrentMatch.Value?.Round.Value?.Beatmaps.FirstOrDefault(b => b.ID == beatmapId && b.Beatmap != null);
|
||||
var existing = ladder.CurrentMatch.Value?.Round.Value?.Beatmaps.FirstOrDefault(b => b.ID == beatmapId);
|
||||
|
||||
if (existing != null)
|
||||
Beatmap.Value = existing.Beatmap;
|
||||
@ -97,6 +93,7 @@ namespace osu.Game.Tournament.IPC
|
||||
{
|
||||
beatmapLookupRequest = new GetBeatmapRequest(new APIBeatmap { OnlineID = beatmapId });
|
||||
beatmapLookupRequest.Success += b => Beatmap.Value = new TournamentBeatmap(b);
|
||||
beatmapLookupRequest.Failure += _ => Beatmap.Value = null;
|
||||
API.Queue(beatmapLookupRequest);
|
||||
}
|
||||
}
|
||||
@ -114,7 +111,7 @@ namespace osu.Game.Tournament.IPC
|
||||
using (var stream = IPCStorage.GetStream(file_ipc_channel_filename))
|
||||
using (var sr = new StreamReader(stream))
|
||||
{
|
||||
ChatChannel.Value = sr.ReadLine();
|
||||
ChatChannel.Value = sr.ReadLine().AsNonNull();
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
@ -140,8 +137,8 @@ namespace osu.Game.Tournament.IPC
|
||||
using (var stream = IPCStorage.GetStream(file_ipc_scores_filename))
|
||||
using (var sr = new StreamReader(stream))
|
||||
{
|
||||
Score1.Value = int.Parse(sr.ReadLine());
|
||||
Score2.Value = int.Parse(sr.ReadLine());
|
||||
Score1.Value = int.Parse(sr.ReadLine().AsNonNull());
|
||||
Score2.Value = int.Parse(sr.ReadLine().AsNonNull());
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
@ -164,7 +161,7 @@ namespace osu.Game.Tournament.IPC
|
||||
/// </summary>
|
||||
/// <param name="path">Path to the IPC directory</param>
|
||||
/// <returns>Whether the supplied path was a valid IPC directory.</returns>
|
||||
public bool SetIPCLocation(string path)
|
||||
public bool SetIPCLocation(string? path)
|
||||
{
|
||||
if (path == null || !ipcFileExistsInDirectory(path))
|
||||
return false;
|
||||
@ -184,29 +181,28 @@ namespace osu.Game.Tournament.IPC
|
||||
/// <returns>Whether an IPC directory was successfully auto-detected.</returns>
|
||||
public bool AutoDetectIPCLocation() => SetIPCLocation(findStablePath());
|
||||
|
||||
private static bool ipcFileExistsInDirectory(string p) => p != null && File.Exists(Path.Combine(p, "ipc.txt"));
|
||||
private static bool ipcFileExistsInDirectory(string? p) => p != null && File.Exists(Path.Combine(p, "ipc.txt"));
|
||||
|
||||
[CanBeNull]
|
||||
private string findStablePath()
|
||||
private string? findStablePath()
|
||||
{
|
||||
string stableInstallPath = findFromEnvVar() ??
|
||||
findFromRegistry() ??
|
||||
findFromLocalAppData() ??
|
||||
findFromDotFolder();
|
||||
string? stableInstallPath = findFromEnvVar() ??
|
||||
findFromRegistry() ??
|
||||
findFromLocalAppData() ??
|
||||
findFromDotFolder();
|
||||
|
||||
Logger.Log($"Stable path for tourney usage: {stableInstallPath}");
|
||||
return stableInstallPath;
|
||||
}
|
||||
|
||||
private string findFromEnvVar()
|
||||
private string? findFromEnvVar()
|
||||
{
|
||||
try
|
||||
{
|
||||
Logger.Log("Trying to find stable with environment variables");
|
||||
string stableInstallPath = Environment.GetEnvironmentVariable("OSU_STABLE_PATH");
|
||||
string? stableInstallPath = Environment.GetEnvironmentVariable("OSU_STABLE_PATH");
|
||||
|
||||
if (ipcFileExistsInDirectory(stableInstallPath))
|
||||
return stableInstallPath;
|
||||
return stableInstallPath!;
|
||||
}
|
||||
catch
|
||||
{
|
||||
@ -215,7 +211,7 @@ namespace osu.Game.Tournament.IPC
|
||||
return null;
|
||||
}
|
||||
|
||||
private string findFromLocalAppData()
|
||||
private string? findFromLocalAppData()
|
||||
{
|
||||
Logger.Log("Trying to find stable in %LOCALAPPDATA%");
|
||||
string stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!");
|
||||
@ -226,7 +222,7 @@ namespace osu.Game.Tournament.IPC
|
||||
return null;
|
||||
}
|
||||
|
||||
private string findFromDotFolder()
|
||||
private string? findFromDotFolder()
|
||||
{
|
||||
Logger.Log("Trying to find stable in dotfolders");
|
||||
string stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu");
|
||||
@ -237,16 +233,16 @@ namespace osu.Game.Tournament.IPC
|
||||
return null;
|
||||
}
|
||||
|
||||
private string findFromRegistry()
|
||||
private string? findFromRegistry()
|
||||
{
|
||||
Logger.Log("Trying to find stable in registry");
|
||||
|
||||
try
|
||||
{
|
||||
string stableInstallPath;
|
||||
string? stableInstallPath;
|
||||
|
||||
#pragma warning disable CA1416
|
||||
using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu"))
|
||||
using (RegistryKey? key = Registry.ClassesRoot.OpenSubKey("osu"))
|
||||
stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(string.Empty)?.ToString()?.Split('"')[1].Replace("osu!.exe", "");
|
||||
#pragma warning restore CA1416
|
||||
|
||||
|
@ -10,11 +10,11 @@ namespace osu.Game.Tournament.IPC
|
||||
{
|
||||
public partial class MatchIPCInfo : Component
|
||||
{
|
||||
public Bindable<TournamentBeatmap> Beatmap { get; } = new Bindable<TournamentBeatmap>();
|
||||
public Bindable<TournamentBeatmap?> Beatmap { get; } = new Bindable<TournamentBeatmap?>();
|
||||
public Bindable<LegacyMods> Mods { get; } = new Bindable<LegacyMods>();
|
||||
public Bindable<TourneyState> State { get; } = new Bindable<TourneyState>();
|
||||
public Bindable<string> ChatChannel { get; } = new Bindable<string>();
|
||||
public BindableInt Score1 { get; } = new BindableInt();
|
||||
public BindableInt Score2 { get; } = new BindableInt();
|
||||
public BindableLong Score1 { get; } = new BindableLong();
|
||||
public BindableLong Score2 { get; } = new BindableLong();
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
@ -28,7 +26,7 @@ namespace osu.Game.Tournament
|
||||
if (reader.TokenType != JsonToken.StartObject)
|
||||
{
|
||||
// if there's no object present then this is using string representation (System.Drawing.Point serializes to "x,y")
|
||||
string str = (string)reader.Value;
|
||||
string? str = (string?)reader.Value;
|
||||
|
||||
Debug.Assert(str != null);
|
||||
|
||||
@ -45,9 +43,12 @@ namespace osu.Game.Tournament
|
||||
|
||||
if (reader.TokenType == JsonToken.PropertyName)
|
||||
{
|
||||
string name = reader.Value?.ToString();
|
||||
string? name = reader.Value?.ToString();
|
||||
int? val = reader.ReadAsInt32();
|
||||
|
||||
if (name == null)
|
||||
continue;
|
||||
|
||||
if (val == null)
|
||||
continue;
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
@ -17,7 +15,7 @@ namespace osu.Game.Tournament.Models
|
||||
[Serializable]
|
||||
public class LadderInfo
|
||||
{
|
||||
public Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
|
||||
public Bindable<RulesetInfo?> Ruleset = new Bindable<RulesetInfo?>();
|
||||
|
||||
public BindableList<TournamentMatch> Matches = new BindableList<TournamentMatch>();
|
||||
public BindableList<TournamentRound> Rounds = new BindableList<TournamentRound>();
|
||||
@ -27,7 +25,7 @@ namespace osu.Game.Tournament.Models
|
||||
public List<TournamentProgression> Progressions = new List<TournamentProgression>();
|
||||
|
||||
[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)
|
||||
{
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace osu.Game.Tournament.Models
|
||||
@ -10,9 +8,9 @@ namespace osu.Game.Tournament.Models
|
||||
public class RoundBeatmap
|
||||
{
|
||||
public int ID;
|
||||
public string Mods;
|
||||
public string Mods = string.Empty;
|
||||
|
||||
[JsonProperty("BeatmapInfo")]
|
||||
public TournamentBeatmap Beatmap;
|
||||
public TournamentBeatmap? Beatmap;
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using Newtonsoft.Json;
|
||||
@ -20,12 +18,12 @@ namespace osu.Game.Tournament.Models
|
||||
/// <summary>
|
||||
/// Path to the IPC directory used by the stable (cutting-edge) install.
|
||||
/// </summary>
|
||||
public string StablePath { get; set; }
|
||||
public string? StablePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Fired whenever stable info is successfully saved to file.
|
||||
/// </summary>
|
||||
public event Action OnStableInfoSaved;
|
||||
public event Action? OnStableInfoSaved;
|
||||
|
||||
private const string config_path = "stable.json";
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
@ -33,16 +31,16 @@ namespace osu.Game.Tournament.Models
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public readonly Bindable<TournamentTeam> Team1 = new Bindable<TournamentTeam>();
|
||||
public readonly Bindable<TournamentTeam?> Team1 = new Bindable<TournamentTeam?>();
|
||||
|
||||
public string Team1Acronym;
|
||||
public string? Team1Acronym;
|
||||
|
||||
public readonly Bindable<int?> Team1Score = new Bindable<int?>();
|
||||
|
||||
[JsonIgnore]
|
||||
public readonly Bindable<TournamentTeam> Team2 = new Bindable<TournamentTeam>();
|
||||
public readonly Bindable<TournamentTeam?> Team2 = new Bindable<TournamentTeam?>();
|
||||
|
||||
public string Team2Acronym;
|
||||
public string? Team2Acronym;
|
||||
|
||||
public readonly Bindable<int?> Team2Score = new Bindable<int?>();
|
||||
|
||||
@ -53,13 +51,13 @@ namespace osu.Game.Tournament.Models
|
||||
public readonly ObservableCollection<BeatmapChoice> PicksBans = new ObservableCollection<BeatmapChoice>();
|
||||
|
||||
[JsonIgnore]
|
||||
public readonly Bindable<TournamentRound> Round = new Bindable<TournamentRound>();
|
||||
public readonly Bindable<TournamentRound?> Round = new Bindable<TournamentRound?>();
|
||||
|
||||
[JsonIgnore]
|
||||
public readonly Bindable<TournamentMatch> Progression = new Bindable<TournamentMatch>();
|
||||
public readonly Bindable<TournamentMatch?> Progression = new Bindable<TournamentMatch?>();
|
||||
|
||||
[JsonIgnore]
|
||||
public readonly Bindable<TournamentMatch> LosersProgression = new Bindable<TournamentMatch>();
|
||||
public readonly Bindable<TournamentMatch?> LosersProgression = new Bindable<TournamentMatch?>();
|
||||
|
||||
/// <summary>
|
||||
/// Should not be set directly. Use LadderInfo.CurrentMatch.Value = this instead.
|
||||
@ -79,7 +77,7 @@ namespace osu.Game.Tournament.Models
|
||||
Team2.BindValueChanged(t => Team2Acronym = t.NewValue?.Acronym.Value, true);
|
||||
}
|
||||
|
||||
public TournamentMatch(TournamentTeam team1 = null, TournamentTeam team2 = null)
|
||||
public TournamentMatch(TournamentTeam? team1 = null, TournamentTeam? team2 = null)
|
||||
: this()
|
||||
{
|
||||
Team1.Value = team1;
|
||||
@ -87,10 +85,10 @@ namespace osu.Game.Tournament.Models
|
||||
}
|
||||
|
||||
[JsonIgnore]
|
||||
public TournamentTeam Winner => !Completed.Value ? null : Team1Score.Value > Team2Score.Value ? Team1.Value : Team2.Value;
|
||||
public TournamentTeam? Winner => !Completed.Value ? null : Team1Score.Value > Team2Score.Value ? Team1.Value : Team2.Value;
|
||||
|
||||
[JsonIgnore]
|
||||
public TournamentTeam Loser => !Completed.Value ? null : Team1Score.Value > Team2Score.Value ? Team2.Value : Team1.Value;
|
||||
public TournamentTeam? Loser => !Completed.Value ? null : Team1Score.Value > Team2Score.Value ? Team2.Value : Team1.Value;
|
||||
|
||||
public TeamColour WinnerColour => Winner == Team1.Value ? TeamColour.Red : TeamColour.Blue;
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
@ -39,7 +37,7 @@ namespace osu.Game.Tournament.Models
|
||||
{
|
||||
int[] ranks = Players.Select(p => p.Rank)
|
||||
.Where(i => i.HasValue)
|
||||
.Select(i => i.Value)
|
||||
.Select(i => i!.Value)
|
||||
.ToArray();
|
||||
|
||||
if (ranks.Length == 0)
|
||||
@ -53,7 +51,7 @@ namespace osu.Game.Tournament.Models
|
||||
|
||||
public Bindable<int> LastYearPlacing = new BindableInt
|
||||
{
|
||||
MinValue = 1,
|
||||
MinValue = 0,
|
||||
MaxValue = 256
|
||||
};
|
||||
|
||||
@ -66,14 +64,14 @@ namespace osu.Game.Tournament.Models
|
||||
{
|
||||
// use a sane default flag name based on acronym.
|
||||
if (val.OldValue.StartsWith(FlagName.Value, StringComparison.InvariantCultureIgnoreCase))
|
||||
FlagName.Value = val.NewValue.Length >= 2 ? val.NewValue?.Substring(0, 2).ToUpperInvariant() : string.Empty;
|
||||
FlagName.Value = val.NewValue?.Length >= 2 ? val.NewValue.Substring(0, 2).ToUpperInvariant() : string.Empty;
|
||||
};
|
||||
|
||||
FullName.ValueChanged += val =>
|
||||
{
|
||||
// use a sane acronym based on full name.
|
||||
if (val.OldValue.StartsWith(Acronym.Value, StringComparison.InvariantCultureIgnoreCase))
|
||||
Acronym.Value = val.NewValue.Length >= 3 ? val.NewValue?.Substring(0, 3).ToUpperInvariant() : string.Empty;
|
||||
Acronym.Value = val.NewValue?.Length >= 3 ? val.NewValue.Substring(0, 3).ToUpperInvariant() : string.Empty;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ namespace osu.Game.Tournament.Screens
|
||||
SongBar.Mods = mods.NewValue;
|
||||
}
|
||||
|
||||
private void beatmapChanged(ValueChangedEvent<TournamentBeatmap> beatmap)
|
||||
private void beatmapChanged(ValueChangedEvent<TournamentBeatmap?> beatmap)
|
||||
{
|
||||
SongBar.FadeInFromZero(300, Easing.OutQuint);
|
||||
SongBar.Beatmap = beatmap.NewValue;
|
||||
|
@ -84,7 +84,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
|
||||
|
||||
public bool ContainsTeam(string fullName)
|
||||
{
|
||||
return allTeams.Any(t => t.Team.FullName.Value == fullName);
|
||||
return allTeams.Any(t => t.Team?.FullName.Value == fullName);
|
||||
}
|
||||
|
||||
public bool RemoveTeam(TournamentTeam team)
|
||||
@ -112,7 +112,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (GroupTeam gt in allTeams)
|
||||
sb.AppendLine(gt.Team.FullName.Value);
|
||||
sb.AppendLine(gt.Team?.FullName.Value);
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,11 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -22,8 +21,8 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
|
||||
{
|
||||
public partial class ScrollingTeamContainer : Container
|
||||
{
|
||||
public event Action OnScrollStarted;
|
||||
public event Action<TournamentTeam> OnSelected;
|
||||
public event Action? OnScrollStarted;
|
||||
public event Action<TournamentTeam>? OnSelected;
|
||||
|
||||
private readonly List<TournamentTeam> availableTeams = new List<TournamentTeam>();
|
||||
|
||||
@ -42,7 +41,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
|
||||
|
||||
private double lastTime;
|
||||
|
||||
private ScheduledDelegate delayedStateChangeDelegate;
|
||||
private ScheduledDelegate? delayedStateChangeDelegate;
|
||||
|
||||
public ScrollingTeamContainer()
|
||||
{
|
||||
@ -117,7 +116,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
|
||||
if (!Children.Any())
|
||||
break;
|
||||
|
||||
ScrollingTeam closest = null;
|
||||
ScrollingTeam? closest = null;
|
||||
|
||||
foreach (var c in Children)
|
||||
{
|
||||
@ -137,9 +136,8 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
|
||||
closest = stc;
|
||||
}
|
||||
|
||||
Trace.Assert(closest != null, "closest != null");
|
||||
Debug.Assert(closest != null, "closest != null");
|
||||
|
||||
// ReSharper disable once PossibleNullReferenceException
|
||||
offset += DrawWidth / 2f - (closest.Position.X + closest.DrawWidth / 2f);
|
||||
|
||||
ScrollingTeam st = closest;
|
||||
@ -147,7 +145,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
|
||||
availableTeams.RemoveAll(at => at == st.Team);
|
||||
|
||||
st.Selected = true;
|
||||
OnSelected?.Invoke(st.Team);
|
||||
OnSelected?.Invoke(st.Team.AsNonNull());
|
||||
|
||||
delayedStateChangeDelegate = Scheduler.AddDelayed(() => setScrollState(ScrollState.Idle), 10000);
|
||||
break;
|
||||
@ -174,7 +172,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
|
||||
setScrollState(ScrollState.Idle);
|
||||
}
|
||||
|
||||
public void AddTeams(IEnumerable<TournamentTeam> teams)
|
||||
public void AddTeams(IEnumerable<TournamentTeam>? teams)
|
||||
{
|
||||
if (teams == null)
|
||||
return;
|
||||
@ -311,6 +309,8 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
|
||||
|
||||
public partial class ScrollingTeam : DrawableTournamentTeam
|
||||
{
|
||||
public new TournamentTeam Team => base.Team.AsNonNull();
|
||||
|
||||
public const float WIDTH = 58;
|
||||
public const float HEIGHT = 44;
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@ -39,7 +37,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
|
||||
{
|
||||
while (sr.Peek() != -1)
|
||||
{
|
||||
string line = sr.ReadLine()?.Trim();
|
||||
string? line = sr.ReadLine()?.Trim();
|
||||
|
||||
if (string.IsNullOrEmpty(line))
|
||||
continue;
|
||||
@ -56,7 +54,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
|
||||
teams.Add(new TournamentTeam
|
||||
{
|
||||
FullName = { Value = split[1], },
|
||||
Acronym = { Value = split.Length >= 3 ? split[2] : null, },
|
||||
Acronym = { Value = split.Length >= 3 ? split[2] : string.Empty, },
|
||||
FlagName = { Value = split[0] }
|
||||
});
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
@ -72,7 +70,7 @@ namespace osu.Game.Tournament.Screens.Drawings.Components
|
||||
|
||||
private float leftPos => -(float)((Time.Current + Offset) / CycleTime) + expiredCount;
|
||||
|
||||
private Texture texture;
|
||||
private Texture texture = null!;
|
||||
|
||||
private int expiredCount;
|
||||
|
||||
|
@ -1,14 +1,13 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Extensions.ObjectExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
@ -28,28 +27,29 @@ namespace osu.Game.Tournament.Screens.Drawings
|
||||
{
|
||||
private const string results_filename = "drawings_results.txt";
|
||||
|
||||
private ScrollingTeamContainer teamsContainer;
|
||||
private GroupContainer groupsContainer;
|
||||
private TournamentSpriteText fullTeamNameText;
|
||||
private ScrollingTeamContainer teamsContainer = null!;
|
||||
private GroupContainer groupsContainer = null!;
|
||||
private TournamentSpriteText fullTeamNameText = null!;
|
||||
|
||||
private readonly List<TournamentTeam> allTeams = new List<TournamentTeam>();
|
||||
|
||||
private DrawingsConfigManager drawingsConfig;
|
||||
private DrawingsConfigManager drawingsConfig = null!;
|
||||
|
||||
private Task writeOp;
|
||||
private Task? writeOp;
|
||||
|
||||
private Storage storage;
|
||||
private Storage storage = null!;
|
||||
|
||||
public ITeamList TeamList;
|
||||
public ITeamList TeamList = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(Storage storage)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
this.storage = storage;
|
||||
|
||||
TeamList ??= new StorageBackedTeamList(storage);
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
if (TeamList.IsNull())
|
||||
TeamList = new StorageBackedTeamList(storage);
|
||||
|
||||
if (!TeamList.Teams.Any())
|
||||
{
|
||||
@ -251,7 +251,7 @@ namespace osu.Game.Tournament.Screens.Drawings
|
||||
using (Stream stream = storage.GetStream(results_filename, FileAccess.Read, FileMode.Open))
|
||||
using (StreamReader sr = new StreamReader(stream))
|
||||
{
|
||||
string line;
|
||||
string? line;
|
||||
|
||||
while ((line = sr.ReadLine()?.Trim()) != null)
|
||||
{
|
||||
@ -261,8 +261,7 @@ namespace osu.Game.Tournament.Screens.Drawings
|
||||
if (line.ToUpperInvariant().StartsWith("GROUP", StringComparison.Ordinal))
|
||||
continue;
|
||||
|
||||
// ReSharper disable once AccessToModifiedClosure
|
||||
TournamentTeam teamToAdd = allTeams.FirstOrDefault(t => t.FullName.Value == line);
|
||||
TournamentTeam? teamToAdd = allTeams.FirstOrDefault(t => t.FullName.Value == line);
|
||||
|
||||
if (teamToAdd == null)
|
||||
continue;
|
||||
|
@ -1,12 +1,9 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -35,11 +32,12 @@ namespace osu.Game.Tournament.Screens.Editors
|
||||
[Cached]
|
||||
private LadderEditorInfo editorInfo = new LadderEditorInfo();
|
||||
|
||||
private WarningBox rightClickMessage;
|
||||
private WarningBox rightClickMessage = null!;
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
[CanBeNull]
|
||||
private IDialogOverlay dialogOverlay { get; set; }
|
||||
private RectangularPositionSnapGrid grid = null!;
|
||||
|
||||
[Resolved]
|
||||
private IDialogOverlay? dialogOverlay { get; set; }
|
||||
|
||||
protected override bool DrawLoserPaths => true;
|
||||
|
||||
@ -53,10 +51,12 @@ namespace osu.Game.Tournament.Screens.Editors
|
||||
|
||||
AddInternal(rightClickMessage = new WarningBox("Right click to place and link matches"));
|
||||
|
||||
ScrollContent.Add(new RectangularPositionSnapGrid(Vector2.Zero)
|
||||
ScrollContent.Add(grid = new RectangularPositionSnapGrid(Vector2.Zero)
|
||||
{
|
||||
Spacing = new Vector2(GRID_SPACING),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
BypassAutoSizeAxes = Axes.Both,
|
||||
Depth = float.MaxValue
|
||||
});
|
||||
|
||||
@ -64,6 +64,22 @@ namespace osu.Game.Tournament.Screens.Editors
|
||||
updateMessage();
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
// Expand grid with the content to allow going beyond the bounds of the screen.
|
||||
grid.Size = ScrollContent.Size + new Vector2(GRID_SPACING * 2);
|
||||
}
|
||||
|
||||
private Vector2 lastMatchesContainerMouseDownPosition;
|
||||
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
{
|
||||
lastMatchesContainerMouseDownPosition = MatchesContainer.ToLocalSpace(e.ScreenSpaceMouseDownPosition);
|
||||
return base.OnMouseDown(e);
|
||||
}
|
||||
|
||||
private void updateMessage()
|
||||
{
|
||||
rightClickMessage.Alpha = LadderInfo.Matches.Count > 0 ? 0 : 1;
|
||||
@ -74,35 +90,28 @@ namespace osu.Game.Tournament.Screens.Editors
|
||||
ScrollContent.Add(new JoinVisualiser(MatchesContainer, match, losers, UpdateLayout));
|
||||
}
|
||||
|
||||
public MenuItem[] ContextMenuItems
|
||||
{
|
||||
get
|
||||
public MenuItem[] ContextMenuItems =>
|
||||
new MenuItem[]
|
||||
{
|
||||
if (editorInfo == null)
|
||||
return Array.Empty<MenuItem>();
|
||||
|
||||
return new MenuItem[]
|
||||
new OsuMenuItem("Create new match", MenuItemType.Highlighted, () =>
|
||||
{
|
||||
new OsuMenuItem("Create new match", MenuItemType.Highlighted, () =>
|
||||
{
|
||||
Vector2 pos = MatchesContainer.ToLocalSpace(GetContainingInputManager().CurrentState.Mouse.Position);
|
||||
TournamentMatch newMatch = new TournamentMatch { Position = { Value = new Point((int)pos.X, (int)pos.Y) } };
|
||||
Vector2 pos = MatchesContainer.Count == 0 ? Vector2.Zero : lastMatchesContainerMouseDownPosition;
|
||||
|
||||
LadderInfo.Matches.Add(newMatch);
|
||||
TournamentMatch newMatch = new TournamentMatch { Position = { Value = new Point((int)pos.X, (int)pos.Y) } };
|
||||
|
||||
editorInfo.Selected.Value = newMatch;
|
||||
}),
|
||||
new OsuMenuItem("Reset teams", MenuItemType.Destructive, () =>
|
||||
LadderInfo.Matches.Add(newMatch);
|
||||
|
||||
editorInfo.Selected.Value = newMatch;
|
||||
}),
|
||||
new OsuMenuItem("Reset teams", MenuItemType.Destructive, () =>
|
||||
{
|
||||
dialogOverlay?.Push(new LadderResetTeamsDialog(() =>
|
||||
{
|
||||
dialogOverlay?.Push(new LadderResetTeamsDialog(() =>
|
||||
{
|
||||
foreach (var p in MatchesContainer)
|
||||
p.Match.Reset();
|
||||
}));
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
foreach (var p in MatchesContainer)
|
||||
p.Match.Reset();
|
||||
}));
|
||||
})
|
||||
};
|
||||
|
||||
public void Remove(TournamentMatch match)
|
||||
{
|
||||
@ -114,11 +123,11 @@ namespace osu.Game.Tournament.Screens.Editors
|
||||
private readonly Container<DrawableTournamentMatch> matchesContainer;
|
||||
public readonly TournamentMatch Source;
|
||||
private readonly bool losers;
|
||||
private readonly Action complete;
|
||||
private readonly Action? complete;
|
||||
|
||||
private ProgressionPath path;
|
||||
private ProgressionPath? path;
|
||||
|
||||
public JoinVisualiser(Container<DrawableTournamentMatch> matchesContainer, TournamentMatch source, bool losers, Action complete)
|
||||
public JoinVisualiser(Container<DrawableTournamentMatch> matchesContainer, TournamentMatch source, bool losers, Action? complete)
|
||||
{
|
||||
this.matchesContainer = matchesContainer;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
@ -132,7 +141,7 @@ namespace osu.Game.Tournament.Screens.Editors
|
||||
Source.Progression.Value = null;
|
||||
}
|
||||
|
||||
private DrawableTournamentMatch findTarget(InputState state)
|
||||
private DrawableTournamentMatch? findTarget(InputState state)
|
||||
{
|
||||
return matchesContainer.FirstOrDefault(d => d.ReceivePositionalInputAt(state.Mouse.Position));
|
||||
}
|
||||
|
@ -139,9 +139,11 @@ namespace osu.Game.Tournament.Screens.Editors
|
||||
|
||||
public void CreateNew()
|
||||
{
|
||||
var user = new RoundBeatmap();
|
||||
round.Beatmaps.Add(user);
|
||||
flow.Add(new RoundBeatmapRow(round, user));
|
||||
var b = new RoundBeatmap();
|
||||
|
||||
round.Beatmaps.Add(b);
|
||||
|
||||
flow.Add(new RoundBeatmapRow(round, b));
|
||||
}
|
||||
|
||||
public partial class RoundBeatmapRow : CompositeDrawable
|
||||
|
@ -10,7 +10,9 @@ using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Tournament.Models;
|
||||
@ -128,7 +130,7 @@ namespace osu.Game.Tournament.Screens.Editors
|
||||
Width = 0.2f,
|
||||
Current = Model.Seed
|
||||
},
|
||||
new SettingsSlider<int>
|
||||
new SettingsSlider<int, LastYearPlacementSlider>
|
||||
{
|
||||
LabelText = "Last Year Placement",
|
||||
Width = 0.33f,
|
||||
@ -175,6 +177,11 @@ namespace osu.Game.Tournament.Screens.Editors
|
||||
};
|
||||
}
|
||||
|
||||
private partial class LastYearPlacementSlider : RoundedSliderBar<int>
|
||||
{
|
||||
public override LocalisableString TooltipText => Current.Value == 0 ? "N/A" : base.TooltipText;
|
||||
}
|
||||
|
||||
public partial class PlayerEditor : CompositeDrawable
|
||||
{
|
||||
private readonly TournamentTeam team;
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -14,9 +12,9 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
|
||||
{
|
||||
public partial class MatchHeader : Container
|
||||
{
|
||||
private TeamScoreDisplay teamDisplay1;
|
||||
private TeamScoreDisplay teamDisplay2;
|
||||
private DrawableTournamentHeaderLogo logo;
|
||||
private TeamScoreDisplay teamDisplay1 = null!;
|
||||
private TeamScoreDisplay teamDisplay2 = null!;
|
||||
private DrawableTournamentHeaderLogo logo = null!;
|
||||
|
||||
private bool showScores = true;
|
||||
|
||||
|
@ -35,7 +35,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
|
||||
}
|
||||
}
|
||||
|
||||
public TeamDisplay(TournamentTeam team, TeamColour colour, Bindable<int?> currentTeamScore, int pointsToWin)
|
||||
public TeamDisplay(TournamentTeam? team, TeamColour colour, Bindable<int?> currentTeamScore, int pointsToWin)
|
||||
: base(team)
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
@ -17,16 +15,22 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
|
||||
{
|
||||
private readonly TeamColour teamColour;
|
||||
|
||||
private readonly Bindable<TournamentMatch> currentMatch = new Bindable<TournamentMatch>();
|
||||
private readonly Bindable<TournamentTeam> currentTeam = new Bindable<TournamentTeam>();
|
||||
private readonly Bindable<TournamentMatch?> currentMatch = new Bindable<TournamentMatch?>();
|
||||
private readonly Bindable<TournamentTeam?> currentTeam = new Bindable<TournamentTeam?>();
|
||||
private readonly Bindable<int?> currentTeamScore = new Bindable<int?>();
|
||||
|
||||
private TeamDisplay teamDisplay;
|
||||
private TeamDisplay? teamDisplay;
|
||||
|
||||
public bool ShowScore
|
||||
{
|
||||
get => teamDisplay.ShowScore;
|
||||
set => teamDisplay.ShowScore = value;
|
||||
get => teamDisplay?.ShowScore ?? false;
|
||||
set
|
||||
{
|
||||
if (teamDisplay != null)
|
||||
{
|
||||
teamDisplay.ShowScore = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TeamScoreDisplay(TeamColour teamColour)
|
||||
@ -48,7 +52,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
|
||||
updateMatch();
|
||||
}
|
||||
|
||||
private void matchChanged(ValueChangedEvent<TournamentMatch> match)
|
||||
private void matchChanged(ValueChangedEvent<TournamentMatch?> match)
|
||||
{
|
||||
currentTeamScore.UnbindBindings();
|
||||
currentTeam.UnbindBindings();
|
||||
@ -78,7 +82,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
|
||||
switch (e.Button)
|
||||
{
|
||||
case MouseButton.Left:
|
||||
if (currentTeamScore.Value < currentMatch.Value.PointsToWin)
|
||||
if (currentTeamScore.Value < currentMatch.Value?.PointsToWin)
|
||||
currentTeamScore.Value++;
|
||||
return true;
|
||||
|
||||
@ -91,7 +95,7 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
|
||||
return base.OnMouseDown(e);
|
||||
}
|
||||
|
||||
private void teamChanged(ValueChangedEvent<TournamentTeam> team)
|
||||
private void teamChanged(ValueChangedEvent<TournamentTeam?> team)
|
||||
{
|
||||
bool wasShowingScores = teamDisplay?.ShowScore ?? false;
|
||||
|
||||
|
@ -1,183 +1,19 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Tournament.IPC;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tournament.Screens.Gameplay.Components
|
||||
{
|
||||
// TODO: Update to derive from osu-side class?
|
||||
public partial class TournamentMatchScoreDisplay : CompositeDrawable
|
||||
public partial class TournamentMatchScoreDisplay : MatchScoreDisplay
|
||||
{
|
||||
private const float bar_height = 18;
|
||||
|
||||
private readonly BindableInt score1 = new BindableInt();
|
||||
private readonly BindableInt score2 = new BindableInt();
|
||||
|
||||
private readonly MatchScoreCounter score1Text;
|
||||
private readonly MatchScoreCounter score2Text;
|
||||
|
||||
private readonly MatchScoreDiffCounter scoreDiffText;
|
||||
|
||||
private readonly Drawable score1Bar;
|
||||
private readonly Drawable score2Bar;
|
||||
|
||||
public TournamentMatchScoreDisplay()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
InternalChildren = new[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Name = "top bar red (static)",
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = bar_height / 4,
|
||||
Width = 0.5f,
|
||||
Colour = TournamentGame.COLOUR_RED,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopRight
|
||||
},
|
||||
new Box
|
||||
{
|
||||
Name = "top bar blue (static)",
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = bar_height / 4,
|
||||
Width = 0.5f,
|
||||
Colour = TournamentGame.COLOUR_BLUE,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopLeft
|
||||
},
|
||||
score1Bar = new Box
|
||||
{
|
||||
Name = "top bar red",
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = bar_height,
|
||||
Width = 0,
|
||||
Colour = TournamentGame.COLOUR_RED,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopRight
|
||||
},
|
||||
score1Text = new MatchScoreCounter
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre
|
||||
},
|
||||
score2Bar = new Box
|
||||
{
|
||||
Name = "top bar blue",
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = bar_height,
|
||||
Width = 0,
|
||||
Colour = TournamentGame.COLOUR_BLUE,
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopLeft
|
||||
},
|
||||
score2Text = new MatchScoreCounter
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre
|
||||
},
|
||||
scoreDiffText = new MatchScoreDiffCounter
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Margin = new MarginPadding
|
||||
{
|
||||
Top = bar_height / 4,
|
||||
Horizontal = 8
|
||||
},
|
||||
Alpha = 0
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(MatchIPCInfo ipc)
|
||||
{
|
||||
score1.BindValueChanged(_ => updateScores());
|
||||
score1.BindTo(ipc.Score1);
|
||||
|
||||
score2.BindValueChanged(_ => updateScores());
|
||||
score2.BindTo(ipc.Score2);
|
||||
}
|
||||
|
||||
private void updateScores()
|
||||
{
|
||||
score1Text.Current.Value = score1.Value;
|
||||
score2Text.Current.Value = score2.Value;
|
||||
|
||||
var winningText = score1.Value > score2.Value ? score1Text : score2Text;
|
||||
var losingText = score1.Value <= score2.Value ? score1Text : score2Text;
|
||||
|
||||
winningText.Winning = true;
|
||||
losingText.Winning = false;
|
||||
|
||||
var winningBar = score1.Value > score2.Value ? score1Bar : score2Bar;
|
||||
var losingBar = score1.Value <= score2.Value ? score1Bar : score2Bar;
|
||||
|
||||
int diff = Math.Max(score1.Value, score2.Value) - Math.Min(score1.Value, score2.Value);
|
||||
|
||||
losingBar.ResizeWidthTo(0, 400, Easing.OutQuint);
|
||||
winningBar.ResizeWidthTo(Math.Min(0.4f, MathF.Pow(diff / 1500000f, 0.5f) / 2), 400, Easing.OutQuint);
|
||||
|
||||
scoreDiffText.Alpha = diff != 0 ? 1 : 0;
|
||||
scoreDiffText.Current.Value = -diff;
|
||||
scoreDiffText.Origin = score1.Value > score2.Value ? Anchor.TopLeft : Anchor.TopRight;
|
||||
}
|
||||
|
||||
protected override void UpdateAfterChildren()
|
||||
{
|
||||
base.UpdateAfterChildren();
|
||||
score1Text.X = -Math.Max(5 + score1Text.DrawWidth / 2, score1Bar.DrawWidth);
|
||||
score2Text.X = Math.Max(5 + score2Text.DrawWidth / 2, score2Bar.DrawWidth);
|
||||
}
|
||||
|
||||
private partial class MatchScoreCounter : CommaSeparatedScoreCounter
|
||||
{
|
||||
private OsuSpriteText displayedSpriteText;
|
||||
|
||||
public MatchScoreCounter()
|
||||
{
|
||||
Margin = new MarginPadding { Top = bar_height, Horizontal = 10 };
|
||||
}
|
||||
|
||||
public bool Winning
|
||||
{
|
||||
set => updateFont(value);
|
||||
}
|
||||
|
||||
protected override OsuSpriteText CreateSpriteText() => base.CreateSpriteText().With(s =>
|
||||
{
|
||||
displayedSpriteText = s;
|
||||
displayedSpriteText.Spacing = new Vector2(-6);
|
||||
updateFont(false);
|
||||
});
|
||||
|
||||
private void updateFont(bool winning)
|
||||
=> displayedSpriteText.Font = winning
|
||||
? OsuFont.Torus.With(weight: FontWeight.Bold, size: 50, fixedWidth: true)
|
||||
: OsuFont.Torus.With(weight: FontWeight.Regular, size: 40, fixedWidth: true);
|
||||
}
|
||||
|
||||
private partial class MatchScoreDiffCounter : CommaSeparatedScoreCounter
|
||||
{
|
||||
protected override OsuSpriteText CreateSpriteText() => base.CreateSpriteText().With(s =>
|
||||
{
|
||||
s.Spacing = new Vector2(-2);
|
||||
s.Font = OsuFont.Torus.With(weight: FontWeight.Regular, size: bar_height, fixedWidth: true);
|
||||
});
|
||||
Team1Score.BindTo(ipc.Score1);
|
||||
Team2Score.BindTo(ipc.Score2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
@ -26,16 +24,16 @@ namespace osu.Game.Tournament.Screens.Gameplay
|
||||
private readonly BindableBool warmup = new BindableBool();
|
||||
|
||||
public readonly Bindable<TourneyState> State = new Bindable<TourneyState>();
|
||||
private OsuButton warmupButton;
|
||||
private MatchIPCInfo ipc;
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private TournamentSceneManager sceneManager { get; set; }
|
||||
private OsuButton warmupButton = null!;
|
||||
private MatchIPCInfo ipc = null!;
|
||||
|
||||
[Resolved]
|
||||
private TournamentMatchChatDisplay chat { get; set; }
|
||||
private TournamentSceneManager? sceneManager { get; set; }
|
||||
|
||||
private Drawable chroma;
|
||||
[Resolved]
|
||||
private TournamentMatchChatDisplay chat { get; set; } = null!;
|
||||
|
||||
private Drawable chroma = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(LadderInfo ladder, MatchIPCInfo ipc)
|
||||
@ -142,7 +140,7 @@ namespace osu.Game.Tournament.Screens.Gameplay
|
||||
State.BindValueChanged(_ => updateState(), true);
|
||||
}
|
||||
|
||||
protected override void CurrentMatchChanged(ValueChangedEvent<TournamentMatch> match)
|
||||
protected override void CurrentMatchChanged(ValueChangedEvent<TournamentMatch?> match)
|
||||
{
|
||||
base.CurrentMatchChanged(match);
|
||||
|
||||
@ -153,29 +151,35 @@ namespace osu.Game.Tournament.Screens.Gameplay
|
||||
scheduledScreenChange?.Cancel();
|
||||
}
|
||||
|
||||
private ScheduledDelegate scheduledScreenChange;
|
||||
private ScheduledDelegate scheduledContract;
|
||||
private ScheduledDelegate? scheduledScreenChange;
|
||||
private ScheduledDelegate? scheduledContract;
|
||||
|
||||
private TournamentMatchScoreDisplay scoreDisplay;
|
||||
private TournamentMatchScoreDisplay scoreDisplay = null!;
|
||||
|
||||
private TourneyState lastState;
|
||||
private MatchHeader header;
|
||||
private MatchHeader header = null!;
|
||||
|
||||
private void contract()
|
||||
{
|
||||
if (!IsLoaded)
|
||||
return;
|
||||
|
||||
scheduledContract?.Cancel();
|
||||
|
||||
SongBar.Expanded = false;
|
||||
scoreDisplay.FadeOut(100);
|
||||
using (chat?.BeginDelayedSequence(500))
|
||||
chat?.Expand();
|
||||
using (chat.BeginDelayedSequence(500))
|
||||
chat.Expand();
|
||||
}
|
||||
|
||||
private void expand()
|
||||
{
|
||||
if (!IsLoaded)
|
||||
return;
|
||||
|
||||
scheduledContract?.Cancel();
|
||||
|
||||
chat?.Contract();
|
||||
chat.Contract();
|
||||
|
||||
using (BeginDelayedSequence(300))
|
||||
{
|
||||
@ -252,7 +256,7 @@ namespace osu.Game.Tournament.Screens.Gameplay
|
||||
private partial class ChromaArea : CompositeDrawable
|
||||
{
|
||||
[Resolved]
|
||||
private LadderInfo ladder { get; set; }
|
||||
private LadderInfo ladder { get; set; } = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@ -28,20 +26,20 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||
{
|
||||
private readonly TournamentMatch match;
|
||||
private readonly bool losers;
|
||||
private TournamentSpriteText scoreText;
|
||||
private Box background;
|
||||
private Box backgroundRight;
|
||||
private TournamentSpriteText scoreText = null!;
|
||||
private Box background = null!;
|
||||
private Box backgroundRight = null!;
|
||||
|
||||
private readonly Bindable<int?> score = new Bindable<int?>();
|
||||
private readonly BindableBool completed = new BindableBool();
|
||||
|
||||
private Color4 colourWinner;
|
||||
|
||||
private readonly Func<bool> isWinner;
|
||||
private LadderEditorScreen ladderEditor;
|
||||
private readonly Func<bool>? isWinner;
|
||||
private LadderEditorScreen ladderEditor = null!;
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private LadderInfo ladderInfo { get; set; }
|
||||
[Resolved]
|
||||
private LadderInfo? ladderInfo { get; set; }
|
||||
|
||||
private void setCurrent()
|
||||
{
|
||||
@ -55,10 +53,10 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||
ladderInfo.CurrentMatch.Value.Current.Value = true;
|
||||
}
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private LadderEditorInfo editorInfo { get; set; }
|
||||
[Resolved]
|
||||
private LadderEditorInfo? editorInfo { get; set; }
|
||||
|
||||
public DrawableMatchTeam(TournamentTeam team, TournamentMatch match, bool losers)
|
||||
public DrawableMatchTeam(TournamentTeam? team, TournamentMatch match, bool losers)
|
||||
: base(team)
|
||||
{
|
||||
this.match = match;
|
||||
@ -72,14 +70,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||
AcronymText.Padding = new MarginPadding { Left = 50 };
|
||||
AcronymText.Font = OsuFont.Torus.With(size: 22, weight: FontWeight.Bold);
|
||||
|
||||
if (match != null)
|
||||
{
|
||||
isWinner = () => match.Winner == Team;
|
||||
isWinner = () => match.Winner == Team;
|
||||
|
||||
completed.BindTo(match.Completed);
|
||||
if (team != null)
|
||||
score.BindTo(team == match.Team1.Value ? match.Team1Score : match.Team2Score);
|
||||
}
|
||||
completed.BindTo(match.Completed);
|
||||
if (team != null)
|
||||
score.BindTo(team == match.Team1.Value ? match.Team1Score : match.Team2Score);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
|
@ -1,10 +1,9 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@ -27,13 +26,13 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||
protected readonly FillFlowContainer<DrawableMatchTeam> Flow;
|
||||
private readonly Drawable selectionBox;
|
||||
private readonly Drawable currentMatchSelectionBox;
|
||||
private Bindable<TournamentMatch> globalSelection;
|
||||
private Bindable<TournamentMatch>? globalSelection;
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private LadderEditorInfo editorInfo { get; set; }
|
||||
[Resolved]
|
||||
private LadderEditorInfo? editorInfo { get; set; }
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private LadderInfo ladderInfo { get; set; }
|
||||
[Resolved]
|
||||
private LadderInfo? ladderInfo { get; set; }
|
||||
|
||||
public DrawableTournamentMatch(TournamentMatch match, bool editor = false)
|
||||
{
|
||||
@ -129,7 +128,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||
/// <summary>
|
||||
/// Fired when something changed that requires a ladder redraw.
|
||||
/// </summary>
|
||||
public Action Changed;
|
||||
public Action? Changed;
|
||||
|
||||
private readonly List<IUnbindable> refBindables = new List<IUnbindable>();
|
||||
|
||||
@ -201,20 +200,22 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
transferProgression(Match.Progression?.Value, Match.Winner);
|
||||
transferProgression(Match.LosersProgression?.Value, Match.Loser);
|
||||
Debug.Assert(Match.Winner != null);
|
||||
transferProgression(Match.Progression.Value, Match.Winner);
|
||||
Debug.Assert(Match.Loser != null);
|
||||
transferProgression(Match.LosersProgression.Value, Match.Loser);
|
||||
}
|
||||
|
||||
Changed?.Invoke();
|
||||
}
|
||||
|
||||
private void transferProgression(TournamentMatch destination, TournamentTeam team)
|
||||
private void transferProgression(TournamentMatch? destination, TournamentTeam team)
|
||||
{
|
||||
if (destination == null) return;
|
||||
|
||||
bool progressionAbove = destination.ID < Match.ID;
|
||||
|
||||
Bindable<TournamentTeam> destinationTeam;
|
||||
Bindable<TournamentTeam?> destinationTeam;
|
||||
|
||||
// check for the case where we have already transferred out value
|
||||
if (destination.Team1.Value == team)
|
||||
@ -268,8 +269,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||
{
|
||||
foreach (var conditional in Match.ConditionalMatches)
|
||||
{
|
||||
bool team1Match = conditional.Acronyms.Contains(Match.Team1Acronym);
|
||||
bool team2Match = conditional.Acronyms.Contains(Match.Team2Acronym);
|
||||
bool team1Match = Match.Team1Acronym != null && conditional.Acronyms.Contains(Match.Team1Acronym);
|
||||
bool team2Match = Match.Team2Acronym != null && conditional.Acronyms.Contains(Match.Team2Acronym);
|
||||
|
||||
if (team1Match && team2Match)
|
||||
Match.Date.Value = conditional.Date.Value;
|
||||
@ -344,6 +345,9 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||
Match.Progression.Value = null;
|
||||
Match.LosersProgression.Value = null;
|
||||
|
||||
if (ladderInfo == null)
|
||||
return;
|
||||
|
||||
ladderInfo.Matches.Remove(Match);
|
||||
|
||||
foreach (var m in ladderInfo.Matches)
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
@ -52,7 +50,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||
name.BindValueChanged(_ => textName.Text = ((losers ? "Losers " : "") + round.Name).ToUpperInvariant(), true);
|
||||
|
||||
description = round.Description.GetBoundCopy();
|
||||
description.BindValueChanged(_ => textDescription.Text = round.Description.Value?.ToUpperInvariant(), true);
|
||||
description.BindValueChanged(_ => textDescription.Text = round.Description.Value?.ToUpperInvariant() ?? string.Empty, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
// 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.
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Diagnostics;
|
||||
@ -23,17 +21,17 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||
{
|
||||
public partial class LadderEditorSettings : CompositeDrawable
|
||||
{
|
||||
private SettingsDropdown<TournamentRound> roundDropdown;
|
||||
private PlayerCheckbox losersCheckbox;
|
||||
private DateTextBox dateTimeBox;
|
||||
private SettingsTeamDropdown team1Dropdown;
|
||||
private SettingsTeamDropdown team2Dropdown;
|
||||
private SettingsDropdown<TournamentRound?> roundDropdown = null!;
|
||||
private PlayerCheckbox losersCheckbox = null!;
|
||||
private DateTextBox dateTimeBox = null!;
|
||||
private SettingsTeamDropdown team1Dropdown = null!;
|
||||
private SettingsTeamDropdown team2Dropdown = null!;
|
||||
|
||||
[Resolved]
|
||||
private LadderEditorInfo editorInfo { get; set; }
|
||||
private LadderEditorInfo editorInfo { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private LadderInfo ladderInfo { get; set; }
|
||||
private LadderInfo ladderInfo { get; set; } = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
@ -77,7 +75,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||
};
|
||||
}
|
||||
|
||||
private void roundDropdownChanged(ValueChangedEvent<TournamentRound> round)
|
||||
private void roundDropdownChanged(ValueChangedEvent<TournamentRound?> round)
|
||||
{
|
||||
if (editorInfo.Selected.Value?.Date.Value < round.NewValue?.StartDate.Value)
|
||||
{
|
||||
@ -101,11 +99,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||
{
|
||||
}
|
||||
|
||||
private partial class SettingsRoundDropdown : SettingsDropdown<TournamentRound>
|
||||
private partial class SettingsRoundDropdown : SettingsDropdown<TournamentRound?>
|
||||
{
|
||||
public SettingsRoundDropdown(BindableList<TournamentRound> rounds)
|
||||
{
|
||||
Current = new Bindable<TournamentRound>();
|
||||
Current = new Bindable<TournamentRound?>();
|
||||
|
||||
foreach (var r in rounds.Prepend(new TournamentRound()))
|
||||
add(r);
|
||||
|
@ -12,7 +12,7 @@ using osu.Game.Tournament.Models;
|
||||
|
||||
namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||
{
|
||||
public partial class SettingsTeamDropdown : SettingsDropdown<TournamentTeam>
|
||||
public partial class SettingsTeamDropdown : SettingsDropdown<TournamentTeam?>
|
||||
{
|
||||
public SettingsTeamDropdown(BindableList<TournamentTeam> teams)
|
||||
{
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user