mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 07:42:57 +08:00
Merge branch 'master' into editor-metrics
This commit is contained in:
commit
ca3d1538ae
1
.gitignore
vendored
1
.gitignore
vendored
@ -339,6 +339,5 @@ inspectcode
|
||||
|
||||
# Fody (pulled in by Realm) - schema file
|
||||
FodyWeavers.xsd
|
||||
**/FodyWeavers.xml
|
||||
|
||||
.idea/.idea.osu.Desktop/.idea/misc.xml
|
@ -10,7 +10,7 @@
|
||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.716.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.720.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||
|
@ -1,26 +1,24 @@
|
||||
// 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 NUnit.Framework;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
|
||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.UI;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
@ -82,7 +80,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
[Test]
|
||||
public void TestNudgeSelection()
|
||||
{
|
||||
HitCircle[] addedObjects = null;
|
||||
HitCircle[] addedObjects = null!;
|
||||
|
||||
AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects = new[]
|
||||
{
|
||||
@ -104,7 +102,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
[Test]
|
||||
public void TestRotateHotkeys()
|
||||
{
|
||||
HitCircle[] addedObjects = null;
|
||||
HitCircle[] addedObjects = null!;
|
||||
|
||||
AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects = new[]
|
||||
{
|
||||
@ -136,7 +134,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
[Test]
|
||||
public void TestGlobalFlipHotkeys()
|
||||
{
|
||||
HitCircle addedObject = null;
|
||||
HitCircle addedObject = null!;
|
||||
|
||||
AddStep("add hitobjects", () => EditorBeatmap.Add(addedObject = new HitCircle { StartTime = 100 }));
|
||||
|
||||
@ -217,11 +215,173 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
AddAssert("2 hitobjects selected", () => EditorBeatmap.SelectedHitObjects.Count == 2 && !EditorBeatmap.SelectedHitObjects.Contains(addedObjects[1]));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNearestSelection()
|
||||
{
|
||||
var firstObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 0 };
|
||||
var secondObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 600 };
|
||||
|
||||
AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[] { firstObject, secondObject }));
|
||||
|
||||
moveMouseToObject(() => firstObject);
|
||||
|
||||
AddStep("seek near first", () => EditorClock.Seek(100));
|
||||
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject));
|
||||
|
||||
AddStep("deselect", () => EditorBeatmap.SelectedHitObjects.Clear());
|
||||
|
||||
AddStep("seek near second", () => EditorClock.Seek(500));
|
||||
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("second selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(secondObject));
|
||||
|
||||
AddStep("deselect", () => EditorBeatmap.SelectedHitObjects.Clear());
|
||||
|
||||
AddStep("seek halfway", () => EditorClock.Seek(300));
|
||||
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNearestSelectionWithEndTime()
|
||||
{
|
||||
var firstObject = new Slider
|
||||
{
|
||||
Position = new Vector2(256, 192),
|
||||
StartTime = 0,
|
||||
Path = new SliderPath(new[]
|
||||
{
|
||||
new PathControlPoint(),
|
||||
new PathControlPoint(new Vector2(50, 0)),
|
||||
})
|
||||
};
|
||||
|
||||
var secondObject = new HitCircle
|
||||
{
|
||||
Position = new Vector2(256, 192),
|
||||
StartTime = 600
|
||||
};
|
||||
|
||||
AddStep("add hitobjects", () => EditorBeatmap.AddRange(new HitObject[] { firstObject, secondObject }));
|
||||
|
||||
moveMouseToObject(() => firstObject);
|
||||
|
||||
AddStep("seek near first", () => EditorClock.Seek(100));
|
||||
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject));
|
||||
|
||||
AddStep("deselect", () => EditorBeatmap.SelectedHitObjects.Clear());
|
||||
|
||||
AddStep("seek near second", () => EditorClock.Seek(500));
|
||||
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("second selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(secondObject));
|
||||
|
||||
AddStep("deselect", () => EditorBeatmap.SelectedHitObjects.Clear());
|
||||
|
||||
AddStep("seek roughly halfway", () => EditorClock.Seek(350));
|
||||
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||
// Slider gets priority due to end time.
|
||||
AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCyclicSelection()
|
||||
{
|
||||
var firstObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 0 };
|
||||
var secondObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 300 };
|
||||
var thirdObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 600 };
|
||||
|
||||
AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[] { firstObject, secondObject, thirdObject }));
|
||||
|
||||
moveMouseToObject(() => firstObject);
|
||||
|
||||
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject));
|
||||
|
||||
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("second selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(secondObject));
|
||||
|
||||
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("third selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(thirdObject));
|
||||
|
||||
// cycle around
|
||||
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCyclicSelectionOutwards()
|
||||
{
|
||||
var firstObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 0 };
|
||||
var secondObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 300 };
|
||||
var thirdObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 600 };
|
||||
|
||||
AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[] { firstObject, secondObject, thirdObject }));
|
||||
|
||||
moveMouseToObject(() => firstObject);
|
||||
|
||||
AddStep("seek near second", () => EditorClock.Seek(320));
|
||||
|
||||
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("second selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(secondObject));
|
||||
|
||||
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("third selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(thirdObject));
|
||||
|
||||
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject));
|
||||
|
||||
// cycle around
|
||||
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("second selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(secondObject));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestCyclicSelectionBackwards()
|
||||
{
|
||||
var firstObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 0 };
|
||||
var secondObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 300 };
|
||||
var thirdObject = new HitCircle { Position = new Vector2(256, 192), StartTime = 600 };
|
||||
|
||||
AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[] { firstObject, secondObject, thirdObject }));
|
||||
|
||||
moveMouseToObject(() => firstObject);
|
||||
|
||||
AddStep("seek to third", () => EditorClock.Seek(600));
|
||||
|
||||
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("third selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(thirdObject));
|
||||
|
||||
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("second selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(secondObject));
|
||||
|
||||
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("first selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(firstObject));
|
||||
|
||||
// cycle around
|
||||
AddStep("left click", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("third selected", () => EditorBeatmap.SelectedHitObjects.Single(), () => Is.EqualTo(thirdObject));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDoubleClickToSeek()
|
||||
{
|
||||
var hitCircle = new HitCircle { Position = new Vector2(256, 192), StartTime = 600 };
|
||||
|
||||
AddStep("add hitobjects", () => EditorBeatmap.AddRange(new[] { hitCircle }));
|
||||
|
||||
moveMouseToObject(() => hitCircle);
|
||||
|
||||
AddRepeatStep("double click", () => InputManager.Click(MouseButton.Left), 2);
|
||||
|
||||
AddUntilStep("seeked to circle", () => EditorClock.CurrentTime, () => Is.EqualTo(600));
|
||||
}
|
||||
|
||||
[TestCase(false)]
|
||||
[TestCase(true)]
|
||||
public void TestMultiSelectFromDrag(bool alreadySelectedBeforeDrag)
|
||||
{
|
||||
HitCircle[] addedObjects = null;
|
||||
HitCircle[] addedObjects = null!;
|
||||
|
||||
AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects = new[]
|
||||
{
|
||||
@ -320,7 +480,7 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
[Test]
|
||||
public void TestQuickDeleteRemovesSliderControlPoint()
|
||||
{
|
||||
Slider slider = null;
|
||||
Slider slider = null!;
|
||||
|
||||
PathControlPoint[] points =
|
||||
{
|
||||
|
@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
private HUDOverlay hudOverlay = null!;
|
||||
|
||||
[Cached(typeof(ScoreProcessor))]
|
||||
private ScoreProcessor scoreProcessor => gameplayState.ScoreProcessor;
|
||||
private ScoreProcessor scoreProcessor { get; set; }
|
||||
|
||||
[Cached(typeof(HealthProcessor))]
|
||||
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
|
||||
@ -47,6 +47,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
private Drawable hideTarget => hudOverlay.ChildrenOfType<SkinComponentsContainer>().First();
|
||||
private Drawable keyCounterFlow => hudOverlay.ChildrenOfType<KeyCounterDisplay>().First().ChildrenOfType<FillFlowContainer<KeyCounter>>().Single();
|
||||
|
||||
public TestSceneHUDOverlay()
|
||||
{
|
||||
scoreProcessor = gameplayState.ScoreProcessor;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
|
@ -138,24 +138,28 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Test]
|
||||
public void TestCyclicSelection()
|
||||
{
|
||||
SkinBlueprint[] blueprints = null!;
|
||||
List<SkinBlueprint> blueprints = new List<SkinBlueprint>();
|
||||
|
||||
AddStep("Add big black boxes", () =>
|
||||
AddStep("clear list", () => blueprints.Clear());
|
||||
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
InputManager.MoveMouseTo(skinEditor.ChildrenOfType<BigBlackBox>().First());
|
||||
InputManager.Click(MouseButton.Left);
|
||||
InputManager.Click(MouseButton.Left);
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
AddStep("Add big black box", () =>
|
||||
{
|
||||
InputManager.MoveMouseTo(skinEditor.ChildrenOfType<BigBlackBox>().First());
|
||||
InputManager.Click(MouseButton.Left);
|
||||
});
|
||||
|
||||
AddStep("store box", () =>
|
||||
{
|
||||
// Add blueprints one-by-one so we have a stable order for testing reverse cyclic selection against.
|
||||
blueprints.Add(skinEditor.ChildrenOfType<SkinBlueprint>().Single(s => s.IsSelected));
|
||||
});
|
||||
}
|
||||
|
||||
AddAssert("Three black boxes added", () => targetContainer.Components.OfType<BigBlackBox>().Count(), () => Is.EqualTo(3));
|
||||
|
||||
AddStep("Store black box blueprints", () =>
|
||||
{
|
||||
blueprints = skinEditor.ChildrenOfType<SkinBlueprint>().Where(b => b.Item is BigBlackBox).ToArray();
|
||||
});
|
||||
|
||||
AddAssert("Selection is black box 1", () => skinEditor.SelectedComponents.Single(), () => Is.EqualTo(blueprints[0].Item));
|
||||
AddAssert("Selection is last", () => skinEditor.SelectedComponents.Single(), () => Is.EqualTo(blueprints[2].Item));
|
||||
|
||||
AddStep("move cursor to black box", () =>
|
||||
{
|
||||
@ -164,13 +168,13 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
});
|
||||
|
||||
AddStep("click on black box stack", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("Selection is black box 2", () => skinEditor.SelectedComponents.Single(), () => Is.EqualTo(blueprints[1].Item));
|
||||
AddAssert("Selection is second last", () => skinEditor.SelectedComponents.Single(), () => Is.EqualTo(blueprints[1].Item));
|
||||
|
||||
AddStep("click on black box stack", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("Selection is black box 3", () => skinEditor.SelectedComponents.Single(), () => Is.EqualTo(blueprints[2].Item));
|
||||
AddAssert("Selection is last", () => skinEditor.SelectedComponents.Single(), () => Is.EqualTo(blueprints[0].Item));
|
||||
|
||||
AddStep("click on black box stack", () => InputManager.Click(MouseButton.Left));
|
||||
AddAssert("Selection is black box 1", () => skinEditor.SelectedComponents.Single(), () => Is.EqualTo(blueprints[0].Item));
|
||||
AddAssert("Selection is first", () => skinEditor.SelectedComponents.Single(), () => Is.EqualTo(blueprints[2].Item));
|
||||
|
||||
AddStep("select all boxes", () =>
|
||||
{
|
||||
|
@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
public partial class TestSceneSkinEditorMultipleSkins : SkinnableTestScene
|
||||
{
|
||||
[Cached(typeof(ScoreProcessor))]
|
||||
private ScoreProcessor scoreProcessor => gameplayState.ScoreProcessor;
|
||||
private ScoreProcessor scoreProcessor { get; set; }
|
||||
|
||||
[Cached(typeof(HealthProcessor))]
|
||||
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
|
||||
@ -37,6 +37,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Cached]
|
||||
public readonly EditorClipboard Clipboard = new EditorClipboard();
|
||||
|
||||
public TestSceneSkinEditorMultipleSkins()
|
||||
{
|
||||
scoreProcessor = gameplayState.ScoreProcessor;
|
||||
}
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
|
@ -30,7 +30,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
private HUDOverlay hudOverlay;
|
||||
|
||||
[Cached(typeof(ScoreProcessor))]
|
||||
private ScoreProcessor scoreProcessor => gameplayState.ScoreProcessor;
|
||||
private ScoreProcessor scoreProcessor { get; set; }
|
||||
|
||||
[Cached(typeof(HealthProcessor))]
|
||||
private HealthProcessor healthProcessor = new DrainingHealthProcessor(0);
|
||||
@ -47,6 +47,11 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
private Drawable hideTarget => hudOverlay.ChildrenOfType<SkinComponentsContainer>().First();
|
||||
private Drawable keyCounterFlow => hudOverlay.ChildrenOfType<KeyCounterDisplay>().First().ChildrenOfType<FillFlowContainer<KeyCounter>>().Single();
|
||||
|
||||
public TestSceneSkinnableHUDOverlay()
|
||||
{
|
||||
scoreProcessor = gameplayState.ScoreProcessor;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestComboCounterIncrementing()
|
||||
{
|
||||
|
@ -67,6 +67,14 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
||||
AddUntilStep("wait for present", () => songSelect.IsCurrentScreen() && songSelect.BeatmapSetsLoaded);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSelectFreeMods()
|
||||
{
|
||||
AddStep("set some freemods", () => songSelect.FreeMods.Value = new OsuRuleset().GetModsFor(ModType.Fun).ToArray());
|
||||
AddStep("set all freemods", () => songSelect.FreeMods.Value = new OsuRuleset().CreateAllMods().ToArray());
|
||||
AddStep("set no freemods", () => songSelect.FreeMods.Value = Array.Empty<Mod>());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBeatmapConfirmed()
|
||||
{
|
||||
|
@ -14,6 +14,7 @@ using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Tournament.Components;
|
||||
using osu.Game.Tournament.Models;
|
||||
using osu.Game.Tournament.Screens.Ladder;
|
||||
using osu.Game.Tournament.Screens.Ladder.Components;
|
||||
@ -35,11 +36,9 @@ namespace osu.Game.Tournament.Screens.Editors
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Content.Add(new LadderEditorSettings
|
||||
AddInternal(new ControlPanel
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
Origin = Anchor.TopRight,
|
||||
Margin = new MarginPadding(5)
|
||||
Child = new LadderEditorSettings(),
|
||||
});
|
||||
|
||||
AddInternal(rightClickMessage = new WarningBox("Right click to place and link matches"));
|
||||
|
@ -213,7 +213,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||
int instantWinAmount = Match.Round.Value.BestOf.Value / 2;
|
||||
|
||||
Match.Completed.Value = Match.Round.Value.BestOf.Value > 0
|
||||
&& (Match.Team1Score.Value + Match.Team2Score.Value >= Match.Round.Value.BestOf.Value || Match.Team1Score.Value > instantWinAmount || Match.Team2Score.Value > instantWinAmount);
|
||||
&& (Match.Team1Score.Value + Match.Team2Score.Value >= Match.Round.Value.BestOf.Value || Match.Team1Score.Value > instantWinAmount
|
||||
|| Match.Team2Score.Value > instantWinAmount);
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@ -265,8 +266,6 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||
|
||||
protected override bool OnMouseDown(MouseDownEvent e) => e.Button == MouseButton.Left && editorInfo != null;
|
||||
|
||||
protected override bool OnDragStart(DragStartEvent e) => editorInfo != null;
|
||||
|
||||
protected override bool OnKeyDown(KeyDownEvent e)
|
||||
{
|
||||
if (Selected && editorInfo != null && e.Key == Key.Delete)
|
||||
@ -287,17 +286,36 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||
return true;
|
||||
}
|
||||
|
||||
private Vector2 positionAtStartOfDrag;
|
||||
|
||||
protected override bool OnDragStart(DragStartEvent e)
|
||||
{
|
||||
if (editorInfo != null)
|
||||
{
|
||||
positionAtStartOfDrag = Position;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void OnDrag(DragEvent e)
|
||||
{
|
||||
base.OnDrag(e);
|
||||
|
||||
Selected = true;
|
||||
this.MoveToOffset(e.Delta);
|
||||
|
||||
var pos = Position;
|
||||
Match.Position.Value = new Point((int)pos.X, (int)pos.Y);
|
||||
this.MoveTo(snapToGrid(positionAtStartOfDrag + (e.MousePosition - e.MouseDownPosition)));
|
||||
|
||||
Match.Position.Value = new Point((int)Position.X, (int)Position.Y);
|
||||
}
|
||||
|
||||
private Vector2 snapToGrid(Vector2 pos) =>
|
||||
new Vector2(
|
||||
(int)(pos.X / 10) * 10,
|
||||
(int)(pos.Y / 10) * 10
|
||||
);
|
||||
|
||||
public void Remove()
|
||||
{
|
||||
Selected = false;
|
||||
|
@ -11,15 +11,17 @@ using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Overlays.Settings;
|
||||
using osu.Game.Screens.Play.PlayerSettings;
|
||||
using osu.Game.Tournament.Components;
|
||||
using osu.Game.Tournament.Models;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||
{
|
||||
public partial class LadderEditorSettings : PlayerSettingsGroup
|
||||
public partial class LadderEditorSettings : CompositeDrawable
|
||||
{
|
||||
private SettingsDropdown<TournamentRound> roundDropdown;
|
||||
private PlayerCheckbox losersCheckbox;
|
||||
@ -33,21 +35,26 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||
[Resolved]
|
||||
private LadderInfo ladderInfo { get; set; }
|
||||
|
||||
public LadderEditorSettings()
|
||||
: base("ladder")
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Children = new Drawable[]
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
InternalChild = new FillFlowContainer
|
||||
{
|
||||
team1Dropdown = new SettingsTeamDropdown(ladderInfo.Teams) { LabelText = "Team 1" },
|
||||
team2Dropdown = new SettingsTeamDropdown(ladderInfo.Teams) { LabelText = "Team 2" },
|
||||
roundDropdown = new SettingsRoundDropdown(ladderInfo.Rounds) { LabelText = "Round" },
|
||||
losersCheckbox = new PlayerCheckbox { LabelText = "Losers Bracket" },
|
||||
dateTimeBox = new DateTextBox { LabelText = "Match Time" },
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(5),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
team1Dropdown = new SettingsTeamDropdown(ladderInfo.Teams) { LabelText = "Team 1" },
|
||||
team2Dropdown = new SettingsTeamDropdown(ladderInfo.Teams) { LabelText = "Team 2" },
|
||||
roundDropdown = new SettingsRoundDropdown(ladderInfo.Rounds) { LabelText = "Round" },
|
||||
losersCheckbox = new PlayerCheckbox { LabelText = "Losers Bracket" },
|
||||
dateTimeBox = new DateTextBox { LabelText = "Match Time" },
|
||||
},
|
||||
};
|
||||
|
||||
editorInfo.Selected.ValueChanged += selection =>
|
||||
|
@ -36,8 +36,8 @@ namespace osu.Game.Tournament.Screens.Ladder
|
||||
{
|
||||
float newScale = Math.Clamp(scale + e.ScrollDelta.Y / 15 * scale, min_scale, max_scale);
|
||||
|
||||
this.MoveTo(target -= e.MousePosition * (newScale - scale), 2000, Easing.OutQuint);
|
||||
this.ScaleTo(scale = newScale, 2000, Easing.OutQuint);
|
||||
this.MoveTo(target -= e.MousePosition * (newScale - scale), 1000, Easing.OutQuint);
|
||||
this.ScaleTo(scale = newScale, 1000, Easing.OutQuint);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ namespace osu.Game.Tournament.Screens.Ladder
|
||||
InternalChild = Content = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new TourneyVideo("ladder")
|
||||
|
@ -38,7 +38,7 @@ namespace osu.Game.Tournament
|
||||
private Container screens;
|
||||
private TourneyVideo video;
|
||||
|
||||
public const float CONTROL_AREA_WIDTH = 160;
|
||||
public const float CONTROL_AREA_WIDTH = 200;
|
||||
|
||||
public const float STREAM_AREA_WIDTH = 1366;
|
||||
|
||||
|
3
osu.Game/FodyWeavers.xml
Normal file
3
osu.Game/FodyWeavers.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
|
||||
<Realm DisableAnalytics="true" />
|
||||
</Weavers>
|
@ -111,7 +111,7 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
private readonly Bindable<Dictionary<ModType, IReadOnlyList<Mod>>> globalAvailableMods = new Bindable<Dictionary<ModType, IReadOnlyList<Mod>>>();
|
||||
|
||||
private IEnumerable<ModState> allAvailableMods => AvailableMods.Value.SelectMany(pair => pair.Value);
|
||||
public IEnumerable<ModState> AllAvailableMods => AvailableMods.Value.SelectMany(pair => pair.Value);
|
||||
|
||||
private readonly BindableBool customisationVisible = new BindableBool();
|
||||
|
||||
@ -382,7 +382,7 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
private void filterMods()
|
||||
{
|
||||
foreach (var modState in allAvailableMods)
|
||||
foreach (var modState in AllAvailableMods)
|
||||
modState.ValidForSelection.Value = modState.Mod.HasImplementation && IsValidMod.Invoke(modState.Mod);
|
||||
}
|
||||
|
||||
@ -407,7 +407,7 @@ namespace osu.Game.Overlays.Mods
|
||||
bool anyCustomisableModActive = false;
|
||||
bool anyModPendingConfiguration = false;
|
||||
|
||||
foreach (var modState in allAvailableMods)
|
||||
foreach (var modState in AllAvailableMods)
|
||||
{
|
||||
anyCustomisableModActive |= modState.Active.Value && modState.Mod.GetSettingsSourceProperties().Any();
|
||||
anyModPendingConfiguration |= modState.PendingConfiguration;
|
||||
@ -464,7 +464,7 @@ namespace osu.Game.Overlays.Mods
|
||||
|
||||
var newSelection = new List<Mod>();
|
||||
|
||||
foreach (var modState in allAvailableMods)
|
||||
foreach (var modState in AllAvailableMods)
|
||||
{
|
||||
var matchingSelectedMod = SelectedMods.Value.SingleOrDefault(selected => selected.GetType() == modState.Mod.GetType());
|
||||
|
||||
@ -491,7 +491,7 @@ namespace osu.Game.Overlays.Mods
|
||||
if (externalSelectionUpdateInProgress)
|
||||
return;
|
||||
|
||||
var candidateSelection = allAvailableMods.Where(modState => modState.Active.Value)
|
||||
var candidateSelection = AllAvailableMods.Where(modState => modState.Active.Value)
|
||||
.Select(modState => modState.Mod)
|
||||
.ToArray();
|
||||
|
||||
|
@ -25,8 +25,6 @@ namespace osu.Game.Overlays.SkinEditor
|
||||
[Resolved]
|
||||
private SkinEditor editor { get; set; } = null!;
|
||||
|
||||
protected override bool AllowCyclicSelection => true;
|
||||
|
||||
public SkinBlueprintContainer(ISerialisableDrawableContainer targetContainer)
|
||||
{
|
||||
this.targetContainer = targetContainer;
|
||||
|
@ -46,15 +46,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
|
||||
protected readonly BindableList<T> SelectedItems = new BindableList<T>();
|
||||
|
||||
/// <summary>
|
||||
/// Whether to allow cyclic selection on clicking multiple times.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Disabled by default as it does not work well with editors that support double-clicking or other advanced interactions.
|
||||
/// Can probably be made to work with more thought.
|
||||
/// </remarks>
|
||||
protected virtual bool AllowCyclicSelection => false;
|
||||
|
||||
protected BlueprintContainer()
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
@ -167,6 +158,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
if (ClickedBlueprint == null || SelectionHandler.SelectedBlueprints.FirstOrDefault(b => b.IsHovered) != ClickedBlueprint)
|
||||
return false;
|
||||
|
||||
doubleClickHandled = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -177,6 +169,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
{
|
||||
endClickSelection(e);
|
||||
clickSelectionHandled = false;
|
||||
doubleClickHandled = false;
|
||||
isDraggingBlueprint = false;
|
||||
wasDragStarted = false;
|
||||
});
|
||||
@ -376,11 +369,22 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
/// </summary>
|
||||
private bool clickSelectionHandled;
|
||||
|
||||
/// <summary>
|
||||
/// Whether a blueprint was double-clicked since last mouse down.
|
||||
/// </summary>
|
||||
private bool doubleClickHandled;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the selected blueprint(s) were already selected on mouse down. Generally used to perform selection cycling on mouse up in such a case.
|
||||
/// </summary>
|
||||
private bool selectedBlueprintAlreadySelectedOnMouseDown;
|
||||
|
||||
/// <summary>
|
||||
/// Sorts the supplied <paramref name="blueprints"/> by the order of preference when making a selection.
|
||||
/// Blueprints at the start of the list will be prioritised over later items if the selection requested is ambiguous due to spatial overlap.
|
||||
/// </summary>
|
||||
protected virtual IEnumerable<SelectionBlueprint<T>> ApplySelectionOrder(IEnumerable<SelectionBlueprint<T>> blueprints) => blueprints.Reverse();
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to select any hovered blueprints.
|
||||
/// </summary>
|
||||
@ -390,15 +394,28 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
{
|
||||
// Iterate from the top of the input stack (blueprints closest to the front of the screen first).
|
||||
// Priority is given to already-selected blueprints.
|
||||
foreach (SelectionBlueprint<T> blueprint in SelectionBlueprints.AliveChildren.Reverse().OrderByDescending(b => b.IsSelected))
|
||||
foreach (SelectionBlueprint<T> blueprint in SelectionBlueprints.AliveChildren.Where(b => b.IsSelected))
|
||||
{
|
||||
if (!blueprint.IsHovered) continue;
|
||||
if (runForBlueprint(blueprint))
|
||||
return true;
|
||||
}
|
||||
|
||||
selectedBlueprintAlreadySelectedOnMouseDown = blueprint.State == SelectionState.Selected;
|
||||
return clickSelectionHandled = SelectionHandler.MouseDownSelectionRequested(blueprint, e);
|
||||
foreach (SelectionBlueprint<T> blueprint in ApplySelectionOrder(SelectionBlueprints.AliveChildren))
|
||||
{
|
||||
if (runForBlueprint(blueprint))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
bool runForBlueprint(SelectionBlueprint<T> blueprint)
|
||||
{
|
||||
if (!blueprint.IsHovered) return false;
|
||||
|
||||
selectedBlueprintAlreadySelectedOnMouseDown = blueprint.State == SelectionState.Selected;
|
||||
clickSelectionHandled = SelectionHandler.MouseDownSelectionRequested(blueprint, e);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -408,8 +425,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
/// <returns>Whether a click selection was active.</returns>
|
||||
private bool endClickSelection(MouseButtonEvent e)
|
||||
{
|
||||
// If already handled a selection or drag, we don't want to perform a mouse up / click action.
|
||||
if (clickSelectionHandled || isDraggingBlueprint) return true;
|
||||
// If already handled a selection, double-click, or drag, we don't want to perform a mouse up / click action.
|
||||
if (clickSelectionHandled || doubleClickHandled || isDraggingBlueprint) return true;
|
||||
|
||||
if (e.Button != MouseButton.Left) return false;
|
||||
|
||||
@ -425,20 +442,20 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!wasDragStarted && selectedBlueprintAlreadySelectedOnMouseDown && SelectedItems.Count == 1 && AllowCyclicSelection)
|
||||
if (!wasDragStarted && selectedBlueprintAlreadySelectedOnMouseDown && SelectedItems.Count == 1)
|
||||
{
|
||||
// If a click occurred and was handled by the currently selected blueprint but didn't result in a drag,
|
||||
// cycle between other blueprints which are also under the cursor.
|
||||
|
||||
// The depth of blueprints is constantly changing (see above where selected blueprints are brought to the front).
|
||||
// For this logic, we want a stable sort order so we can correctly cycle, thus using the blueprintMap instead.
|
||||
IEnumerable<SelectionBlueprint<T>> cyclingSelectionBlueprints = blueprintMap.Values;
|
||||
IEnumerable<SelectionBlueprint<T>> cyclingSelectionBlueprints = ApplySelectionOrder(blueprintMap.Values);
|
||||
|
||||
// If there's already a selection, let's start from the blueprint after the selection.
|
||||
cyclingSelectionBlueprints = cyclingSelectionBlueprints.SkipWhile(b => !b.IsSelected).Skip(1);
|
||||
|
||||
// Add the blueprints from before the selection to the end of the enumerable to allow for cyclic selection.
|
||||
cyclingSelectionBlueprints = cyclingSelectionBlueprints.Concat(blueprintMap.Values.TakeWhile(b => !b.IsSelected));
|
||||
cyclingSelectionBlueprints = cyclingSelectionBlueprints.Concat(ApplySelectionOrder(blueprintMap.Values).TakeWhile(b => !b.IsSelected));
|
||||
|
||||
foreach (SelectionBlueprint<T> blueprint in cyclingSelectionBlueprints)
|
||||
{
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
@ -129,6 +130,10 @@ namespace osu.Game.Screens.Edit.Compose.Components
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override IEnumerable<SelectionBlueprint<HitObject>> ApplySelectionOrder(IEnumerable<SelectionBlueprint<HitObject>> blueprints) =>
|
||||
base.ApplySelectionOrder(blueprints)
|
||||
.OrderBy(b => Math.Min(Math.Abs(EditorClock.CurrentTime - b.Item.GetEndTime()), Math.Abs(EditorClock.CurrentTime - b.Item.StartTime)));
|
||||
|
||||
protected override Container<SelectionBlueprint<HitObject>> CreateSelectionBlueprintContainer() => new HitObjectOrderedSelectionContainer { RelativeSizeAxes = Axes.Both };
|
||||
|
||||
protected override SelectionHandler<HitObject> CreateSelectionHandler() => new EditorSelectionHandler();
|
||||
|
@ -1,15 +1,21 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Screens.Play.HUD;
|
||||
using osu.Game.Screens.Select;
|
||||
using osuTK;
|
||||
|
||||
@ -17,28 +23,60 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
{
|
||||
public partial class FooterButtonFreeMods : FooterButton, IHasCurrentValue<IReadOnlyList<Mod>>
|
||||
{
|
||||
public Bindable<IReadOnlyList<Mod>> Current
|
||||
public Bindable<IReadOnlyList<Mod>> Current { get; set; } = new BindableWithCurrent<IReadOnlyList<Mod>>();
|
||||
|
||||
private OsuSpriteText count = null!;
|
||||
|
||||
private Circle circle = null!;
|
||||
|
||||
private readonly FreeModSelectOverlay freeModSelectOverlay;
|
||||
|
||||
public FooterButtonFreeMods(FreeModSelectOverlay freeModSelectOverlay)
|
||||
{
|
||||
get => modDisplay.Current;
|
||||
set => modDisplay.Current = value;
|
||||
this.freeModSelectOverlay = freeModSelectOverlay;
|
||||
}
|
||||
|
||||
private readonly ModDisplay modDisplay;
|
||||
|
||||
public FooterButtonFreeMods()
|
||||
{
|
||||
ButtonContentContainer.Add(modDisplay = new ModDisplay
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Scale = new Vector2(0.8f),
|
||||
ExpansionMode = ExpansionMode.AlwaysContracted,
|
||||
});
|
||||
}
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
private void load()
|
||||
{
|
||||
ButtonContentContainer.AddRange(new[]
|
||||
{
|
||||
new Container
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
circle = new Circle
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Colour = colours.YellowDark,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
count = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Padding = new MarginPadding(5),
|
||||
UseFullGlyphHeight = false,
|
||||
}
|
||||
}
|
||||
},
|
||||
new IconButton
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Scale = new Vector2(0.8f),
|
||||
Icon = FontAwesome.Solid.Bars,
|
||||
Action = () => freeModSelectOverlay.ToggleVisibility()
|
||||
}
|
||||
});
|
||||
|
||||
SelectedColour = colours.Yellow;
|
||||
DeselectedColour = SelectedColour.Opacity(0.5f);
|
||||
Text = @"freemods";
|
||||
@ -49,14 +87,49 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
base.LoadComplete();
|
||||
|
||||
Current.BindValueChanged(_ => updateModDisplay(), true);
|
||||
|
||||
// Overwrite any external behaviour as we delegate the main toggle action to a sub-button.
|
||||
Action = toggleAllFreeMods;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Immediately toggle all free mods on/off.
|
||||
/// </summary>
|
||||
private void toggleAllFreeMods()
|
||||
{
|
||||
var availableMods = allAvailableAndValidMods.ToArray();
|
||||
|
||||
Current.Value = Current.Value.Count == availableMods.Length
|
||||
? Array.Empty<Mod>()
|
||||
: availableMods;
|
||||
}
|
||||
|
||||
private void updateModDisplay()
|
||||
{
|
||||
if (Current.Value?.Count > 0)
|
||||
modDisplay.FadeIn();
|
||||
int current = Current.Value.Count;
|
||||
|
||||
if (current == allAvailableAndValidMods.Count())
|
||||
{
|
||||
count.Text = "all";
|
||||
count.FadeColour(colours.Gray2, 200, Easing.OutQuint);
|
||||
circle.FadeColour(colours.Yellow, 200, Easing.OutQuint);
|
||||
}
|
||||
else if (current > 0)
|
||||
{
|
||||
count.Text = $"{current} mods";
|
||||
count.FadeColour(colours.Gray2, 200, Easing.OutQuint);
|
||||
circle.FadeColour(colours.YellowDark, 200, Easing.OutQuint);
|
||||
}
|
||||
else
|
||||
modDisplay.FadeOut();
|
||||
{
|
||||
count.Text = "off";
|
||||
count.FadeColour(colours.GrayF, 200, Easing.OutQuint);
|
||||
circle.FadeColour(colours.Gray4, 200, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
|
||||
private IEnumerable<Mod> allAvailableAndValidMods => freeModSelectOverlay.AllAvailableMods
|
||||
.Where(state => state.ValidForSelection.Value)
|
||||
.Select(state => state.Mod);
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ using osu.Framework.Screens;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Mods;
|
||||
@ -76,6 +77,9 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
[Resolved]
|
||||
private RulesetStore rulesets { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
protected OnlinePlayScreen ParentScreen { get; private set; }
|
||||
|
||||
@ -284,6 +288,8 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
[Resolved(canBeNull: true)]
|
||||
private IDialogOverlay dialogOverlay { get; set; }
|
||||
|
||||
protected virtual bool IsConnected => api.State.Value == APIState.Online;
|
||||
|
||||
public override bool OnBackButton()
|
||||
{
|
||||
if (Room.RoomID.Value == null)
|
||||
@ -356,7 +362,12 @@ namespace osu.Game.Screens.OnlinePlay.Match
|
||||
if (ExitConfirmed)
|
||||
return true;
|
||||
|
||||
if (dialogOverlay == null || Room.RoomID.Value != null || Room.Playlist.Count == 0)
|
||||
if (!IsConnected)
|
||||
return true;
|
||||
|
||||
bool hasUnsavedChanges = Room.RoomID.Value == null && Room.Playlist.Count > 0;
|
||||
|
||||
if (dialogOverlay == null || !hasUnsavedChanges)
|
||||
return true;
|
||||
|
||||
// if the dialog is already displayed, block exiting until the user explicitly makes a decision.
|
||||
|
@ -41,7 +41,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
// To work around this, temporarily remove the room and trigger an immediate listing poll.
|
||||
if (e.Last is MultiplayerMatchSubScreen match)
|
||||
{
|
||||
RoomManager.RemoveRoom(match.Room);
|
||||
RoomManager?.RemoveRoom(match.Room);
|
||||
ListingPollingComponent.PollImmediately();
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Online;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Online.Rooms;
|
||||
using osu.Game.Overlays;
|
||||
@ -50,9 +49,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
[Resolved]
|
||||
private MultiplayerClient client { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private OsuGame game { get; set; }
|
||||
|
||||
@ -79,6 +75,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
handleRoomLost();
|
||||
}
|
||||
|
||||
protected override bool IsConnected => base.IsConnected && client.IsConnected.Value;
|
||||
|
||||
protected override Drawable CreateMainContent() => new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
@ -254,13 +252,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
||||
|
||||
public override bool OnExiting(ScreenExitEvent e)
|
||||
{
|
||||
// the room may not be left immediately after a disconnection due to async flow,
|
||||
// so checking the MultiplayerClient / IAPIAccess statuses is also required.
|
||||
if (client.Room == null || !client.IsConnected.Value || api.State.Value != APIState.Online)
|
||||
{
|
||||
// room has not been created yet or we're offline; exit immediately.
|
||||
// room has not been created yet or we're offline; exit immediately.
|
||||
if (client.Room == null || !IsConnected)
|
||||
return base.OnExiting(e);
|
||||
}
|
||||
|
||||
if (!exitConfirmed && dialogOverlay != null)
|
||||
{
|
||||
|
@ -175,9 +175,12 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
|
||||
protected override IEnumerable<(FooterButton, OverlayContainer?)> CreateFooterButtons()
|
||||
{
|
||||
var buttons = base.CreateFooterButtons().ToList();
|
||||
buttons.Insert(buttons.FindIndex(b => b.Item1 is FooterButtonMods) + 1, (new FooterButtonFreeMods { Current = FreeMods }, freeModSelectOverlay));
|
||||
return buttons;
|
||||
var baseButtons = base.CreateFooterButtons().ToList();
|
||||
var freeModsButton = new FooterButtonFreeMods(freeModSelectOverlay) { Current = FreeMods };
|
||||
|
||||
baseButtons.Insert(baseButtons.FindIndex(b => b.Item1 is FooterButtonMods) + 1, (freeModsButton, freeModSelectOverlay));
|
||||
|
||||
return baseButtons;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -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.Screens;
|
||||
@ -15,8 +13,8 @@ namespace osu.Game.Screens.OnlinePlay
|
||||
|
||||
public virtual string ShortTitle => Title;
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
protected IRoomManager RoomManager { get; private set; }
|
||||
[Resolved]
|
||||
protected IRoomManager? RoomManager { get; private set; }
|
||||
|
||||
protected OnlinePlaySubScreen()
|
||||
{
|
||||
|
@ -162,17 +162,20 @@ namespace osu.Game.Screens.Play.PlayerSettings
|
||||
|
||||
realmWriteTask = realm.WriteAsync(r =>
|
||||
{
|
||||
var settings = r.Find<BeatmapInfo>(beatmap.Value.BeatmapInfo.ID)?.UserSettings;
|
||||
var setInfo = r.Find<BeatmapSetInfo>(beatmap.Value.BeatmapSetInfo.ID);
|
||||
|
||||
if (settings == null) // only the case for tests.
|
||||
if (setInfo == null) // only the case for tests.
|
||||
return;
|
||||
|
||||
double val = Current.Value;
|
||||
// Apply to all difficulties in a beatmap set for now (they generally always share timing).
|
||||
foreach (var b in setInfo.Beatmaps)
|
||||
{
|
||||
BeatmapUserSettings settings = b.UserSettings;
|
||||
double val = Current.Value;
|
||||
|
||||
if (settings.Offset == val)
|
||||
return;
|
||||
|
||||
settings.Offset = val;
|
||||
if (settings.Offset != val)
|
||||
settings.Offset = val;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="11.1.2" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2023.716.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2023.720.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2023.707.0" />
|
||||
<PackageReference Include="Sentry" Version="3.28.1" />
|
||||
<PackageReference Include="SharpCompress" Version="0.32.2" />
|
||||
|
@ -16,6 +16,6 @@
|
||||
<RuntimeIdentifier>iossimulator-x64</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2023.716.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2023.720.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -140,6 +140,8 @@
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ParameterHidesMember/@EntryIndexedValue">HINT</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ParameterOnlyUsedForPreconditionCheck_002EGlobal/@EntryIndexedValue">HINT</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ParameterOnlyUsedForPreconditionCheck_002ELocal/@EntryIndexedValue">HINT</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=PartialMethodWithSinglePart/@EntryIndexedValue">HINT</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=PartialTypeWithSinglePart/@EntryIndexedValue">HINT</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=PatternAlwaysOfType/@EntryIndexedValue">DO_NOT_SHOW</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=PossibleInterfaceMemberAmbiguity/@EntryIndexedValue">HINT</s:String>
|
||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=PossibleMultipleEnumeration/@EntryIndexedValue">HINT</s:String>
|
||||
|
Loading…
Reference in New Issue
Block a user