mirror of
https://github.com/ppy/osu.git
synced 2024-12-13 03:42:57 +08:00
Merge branch 'master' into tourney-dangerous-action-confirm
This commit is contained in:
commit
2c5a329b04
@ -10,7 +10,7 @@
|
||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.720.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2023.724.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||
|
@ -5,7 +5,7 @@
|
||||
<key>CFBundleName</key>
|
||||
<string>osu.Game.Rulesets.Catch.Tests.iOS</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>ppy.osu-Game-Rulesets-Catch-Tests-iOS</string>
|
||||
<string>sh.ppy.catch-ruleset-tests</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
@ -42,4 +42,4 @@
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
</plist>
|
@ -5,7 +5,7 @@
|
||||
<key>CFBundleName</key>
|
||||
<string>osu.Game.Rulesets.Mania.Tests.iOS</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>ppy.osu-Game-Rulesets-Mania-Tests-iOS</string>
|
||||
<string>sh.ppy.mania-ruleset-tests</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
@ -42,4 +42,4 @@
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
</plist>
|
22
osu.Game.Rulesets.Mania/Edit/Setup/ManiaDifficultySection.cs
Normal file
22
osu.Game.Rulesets.Mania/Edit/Setup/ManiaDifficultySection.cs
Normal file
@ -0,0 +1,22 @@
|
||||
// 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.Game.Resources.Localisation.Web;
|
||||
using osu.Game.Screens.Edit.Setup;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Edit.Setup
|
||||
{
|
||||
public partial class ManiaDifficultySection : DifficultySection
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
CircleSizeSlider.Label = BeatmapsetsStrings.ShowStatsCsMania;
|
||||
CircleSizeSlider.Description = "The number of columns in the beatmap";
|
||||
if (CircleSizeSlider.Current is BindableNumber<float> circleSizeFloat)
|
||||
circleSizeFloat.Precision = 1;
|
||||
}
|
||||
}
|
||||
}
|
@ -425,6 +425,8 @@ namespace osu.Game.Rulesets.Mania
|
||||
}
|
||||
|
||||
public override RulesetSetupSection CreateEditorSetupSection() => new ManiaSetupSection();
|
||||
|
||||
public override DifficultySection CreateEditorDifficultySection() => new ManiaDifficultySection();
|
||||
}
|
||||
|
||||
public enum PlayfieldType
|
||||
|
@ -5,7 +5,7 @@
|
||||
<key>CFBundleName</key>
|
||||
<string>osu.Game.Rulesets.Osu.Tests.iOS</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>ppy.osu-Game-Rulesets-Osu-Tests-iOS</string>
|
||||
<string>sh.ppy.osu-ruleset-tests</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
@ -42,4 +42,4 @@
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
</plist>
|
@ -5,7 +5,7 @@
|
||||
<key>CFBundleName</key>
|
||||
<string>osu.Game.Rulesets.Taiko.Tests.iOS</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>ppy.osu-Game-Rulesets-Taiko-Tests-iOS</string>
|
||||
<string>sh.ppy.taiko-ruleset-tests</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
@ -42,4 +42,4 @@
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
</plist>
|
@ -5,7 +5,7 @@
|
||||
<key>CFBundleName</key>
|
||||
<string>osu.Game.Tests.iOS</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>ppy.osu-Game-Tests-iOS</string>
|
||||
<string>sh.ppy.osu-tests</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
@ -42,4 +42,4 @@
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
</plist>
|
183
osu.Game.Tests/Editing/Checks/CheckBreaksTest.cs
Normal file
183
osu.Game.Tests/Editing/Checks/CheckBreaksTest.cs
Normal file
@ -0,0 +1,183 @@
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Edit.Checks;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Tests.Beatmaps;
|
||||
|
||||
namespace osu.Game.Tests.Editing.Checks
|
||||
{
|
||||
public class CheckBreaksTest
|
||||
{
|
||||
private CheckBreaks check = null!;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
check = new CheckBreaks();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBreakTooShort()
|
||||
{
|
||||
var beatmap = new Beatmap<HitObject>
|
||||
{
|
||||
Breaks = new List<BreakPeriod>
|
||||
{
|
||||
new BreakPeriod(0, 649)
|
||||
}
|
||||
};
|
||||
var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
|
||||
|
||||
var issues = check.Run(context).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckBreaks.IssueTemplateTooShort);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBreakStartsEarly()
|
||||
{
|
||||
var beatmap = new Beatmap<HitObject>
|
||||
{
|
||||
HitObjects =
|
||||
{
|
||||
new HitCircle { StartTime = 0 },
|
||||
new HitCircle { StartTime = 1_200 }
|
||||
},
|
||||
Breaks = new List<BreakPeriod>
|
||||
{
|
||||
new BreakPeriod(100, 751)
|
||||
}
|
||||
};
|
||||
var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
|
||||
|
||||
var issues = check.Run(context).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckBreaks.IssueTemplateEarlyStart);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBreakEndsLate()
|
||||
{
|
||||
var beatmap = new Beatmap<HitObject>
|
||||
{
|
||||
HitObjects =
|
||||
{
|
||||
new HitCircle { StartTime = 0 },
|
||||
new HitCircle { StartTime = 1_298 }
|
||||
},
|
||||
Breaks = new List<BreakPeriod>
|
||||
{
|
||||
new BreakPeriod(200, 850)
|
||||
}
|
||||
};
|
||||
var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
|
||||
|
||||
var issues = check.Run(context).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckBreaks.IssueTemplateLateEnd);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBreakAfterLastObjectStartsEarly()
|
||||
{
|
||||
var beatmap = new Beatmap<HitObject>
|
||||
{
|
||||
HitObjects =
|
||||
{
|
||||
new HitCircle { StartTime = 0 },
|
||||
new HitCircle { StartTime = 1200 }
|
||||
},
|
||||
Breaks = new List<BreakPeriod>
|
||||
{
|
||||
new BreakPeriod(1398, 2300)
|
||||
}
|
||||
};
|
||||
var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
|
||||
|
||||
var issues = check.Run(context).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckBreaks.IssueTemplateEarlyStart);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBreakBeforeFirstObjectEndsLate()
|
||||
{
|
||||
var beatmap = new Beatmap<HitObject>
|
||||
{
|
||||
HitObjects =
|
||||
{
|
||||
new HitCircle { StartTime = 1100 },
|
||||
new HitCircle { StartTime = 1500 }
|
||||
},
|
||||
Breaks = new List<BreakPeriod>
|
||||
{
|
||||
new BreakPeriod(0, 652)
|
||||
}
|
||||
};
|
||||
var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
|
||||
|
||||
var issues = check.Run(context).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckBreaks.IssueTemplateLateEnd);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBreakMultipleObjectsEarly()
|
||||
{
|
||||
var beatmap = new Beatmap<HitObject>
|
||||
{
|
||||
HitObjects =
|
||||
{
|
||||
new HitCircle { StartTime = 0 },
|
||||
new HitCircle { StartTime = 1_297 },
|
||||
new HitCircle { StartTime = 1_298 }
|
||||
},
|
||||
Breaks = new List<BreakPeriod>
|
||||
{
|
||||
new BreakPeriod(200, 850)
|
||||
}
|
||||
};
|
||||
var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
|
||||
|
||||
var issues = check.Run(context).ToList();
|
||||
|
||||
Assert.That(issues, Has.Count.EqualTo(1));
|
||||
Assert.That(issues.Single().Template is CheckBreaks.IssueTemplateLateEnd);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBreaksCorrect()
|
||||
{
|
||||
var beatmap = new Beatmap<HitObject>
|
||||
{
|
||||
HitObjects =
|
||||
{
|
||||
new HitCircle { StartTime = 0 },
|
||||
new HitCircle { StartTime = 1_300 }
|
||||
},
|
||||
Breaks = new List<BreakPeriod>
|
||||
{
|
||||
new BreakPeriod(200, 850)
|
||||
}
|
||||
};
|
||||
var context = new BeatmapVerifierContext(beatmap, new TestWorkingBeatmap(beatmap));
|
||||
|
||||
var issues = check.Run(context).ToList();
|
||||
|
||||
Assert.That(issues, Is.Empty);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +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 System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Tournament.Models;
|
||||
using osu.Game.Tournament.Screens.Ladder.Components;
|
||||
|
||||
@ -10,60 +13,67 @@ namespace osu.Game.Tournament.Tests.Components
|
||||
{
|
||||
public partial class TestSceneDrawableTournamentMatch : TournamentTestScene
|
||||
{
|
||||
public TestSceneDrawableTournamentMatch()
|
||||
[Test]
|
||||
public void TestBasic()
|
||||
{
|
||||
Container<DrawableTournamentMatch> level1;
|
||||
Container<DrawableTournamentMatch> level2;
|
||||
Container<DrawableTournamentMatch> level1 = null!;
|
||||
Container<DrawableTournamentMatch> level2 = null!;
|
||||
|
||||
var match1 = new TournamentMatch(
|
||||
new TournamentTeam { FlagName = { Value = "AU" }, FullName = { Value = "Australia" }, },
|
||||
new TournamentTeam { FlagName = { Value = "JP" }, FullName = { Value = "Japan" }, Acronym = { Value = "JPN" } })
|
||||
TournamentMatch match1 = null!;
|
||||
TournamentMatch match2 = null!;
|
||||
|
||||
AddStep("setup test", () =>
|
||||
{
|
||||
Team1Score = { Value = 4 },
|
||||
Team2Score = { Value = 1 },
|
||||
};
|
||||
|
||||
var match2 = new TournamentMatch(
|
||||
new TournamentTeam
|
||||
match1 = new TournamentMatch(
|
||||
new TournamentTeam { FlagName = { Value = "AU" }, FullName = { Value = "Australia" }, },
|
||||
new TournamentTeam { FlagName = { Value = "JP" }, FullName = { Value = "Japan" }, Acronym = { Value = "JPN" } })
|
||||
{
|
||||
FlagName = { Value = "RO" },
|
||||
FullName = { Value = "Romania" },
|
||||
}
|
||||
);
|
||||
Team1Score = { Value = 4 },
|
||||
Team2Score = { Value = 1 },
|
||||
};
|
||||
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Children = new Drawable[]
|
||||
match2 = new TournamentMatch(
|
||||
new TournamentTeam
|
||||
{
|
||||
FlagName = { Value = "RO" },
|
||||
FullName = { Value = "Romania" },
|
||||
}
|
||||
);
|
||||
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
level1 = new FillFlowContainer<DrawableTournamentMatch>
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
AutoSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new[]
|
||||
level1 = new FillFlowContainer<DrawableTournamentMatch>
|
||||
{
|
||||
new DrawableTournamentMatch(match1),
|
||||
new DrawableTournamentMatch(match2),
|
||||
new DrawableTournamentMatch(new TournamentMatch()),
|
||||
}
|
||||
},
|
||||
level2 = new FillFlowContainer<DrawableTournamentMatch>
|
||||
{
|
||||
AutoSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Vertical,
|
||||
Margin = new MarginPadding(20),
|
||||
Children = new[]
|
||||
AutoSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Vertical,
|
||||
Children = new[]
|
||||
{
|
||||
new DrawableTournamentMatch(match1),
|
||||
new DrawableTournamentMatch(match2),
|
||||
new DrawableTournamentMatch(new TournamentMatch()),
|
||||
}
|
||||
},
|
||||
level2 = new FillFlowContainer<DrawableTournamentMatch>
|
||||
{
|
||||
new DrawableTournamentMatch(new TournamentMatch()),
|
||||
new DrawableTournamentMatch(new TournamentMatch())
|
||||
AutoSizeAxes = Axes.X,
|
||||
Direction = FillDirection.Vertical,
|
||||
Margin = new MarginPadding(20),
|
||||
Children = new[]
|
||||
{
|
||||
new DrawableTournamentMatch(new TournamentMatch()),
|
||||
new DrawableTournamentMatch(new TournamentMatch())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
level1.Children[0].Match.Progression.Value = level2.Children[0].Match;
|
||||
level1.Children[1].Match.Progression.Value = level2.Children[0].Match;
|
||||
level1.Children[0].Match.Progression.Value = level2.Children[0].Match;
|
||||
level1.Children[1].Match.Progression.Value = level2.Children[0].Match;
|
||||
});
|
||||
|
||||
AddRepeatStep("change scores", () => match1.Team2Score.Value++, 4);
|
||||
AddStep("add new team", () => match2.Team2.Value = new TournamentTeam { FlagName = { Value = "PT" }, FullName = { Value = "Portugal" } });
|
||||
@ -78,6 +88,9 @@ namespace osu.Game.Tournament.Tests.Components
|
||||
AddRepeatStep("change scores", () => level2.Children[0].Match.Team1Score.Value++, 5);
|
||||
|
||||
AddRepeatStep("change scores", () => level2.Children[0].Match.Team2Score.Value++, 4);
|
||||
|
||||
AddStep("select as current", () => match1.Current.Value = true);
|
||||
AddStep("select as editing", () => this.ChildrenOfType<DrawableTournamentMatch>().Last().Selected = true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ using osu.Game.Tournament.Screens.Drawings;
|
||||
|
||||
namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
public partial class TestSceneDrawingsScreen : TournamentTestScene
|
||||
public partial class TestSceneDrawingsScreen : TournamentScreenTestScene
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(Storage storage)
|
||||
|
@ -15,11 +15,25 @@ using osu.Game.Tournament.Screens.Gameplay.Components;
|
||||
|
||||
namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
public partial class TestSceneGameplayScreen : TournamentTestScene
|
||||
public partial class TestSceneGameplayScreen : TournamentScreenTestScene
|
||||
{
|
||||
[Cached]
|
||||
private TournamentMatchChatDisplay chat = new TournamentMatchChatDisplay { Width = 0.5f };
|
||||
|
||||
[Test]
|
||||
public void TestWarmup()
|
||||
{
|
||||
createScreen();
|
||||
|
||||
checkScoreVisibility(false);
|
||||
|
||||
toggleWarmup();
|
||||
checkScoreVisibility(true);
|
||||
|
||||
toggleWarmup();
|
||||
checkScoreVisibility(false);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestStartupState([Values] TourneyState state)
|
||||
{
|
||||
@ -35,20 +49,6 @@ namespace osu.Game.Tournament.Tests.Screens
|
||||
createScreen();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestWarmup()
|
||||
{
|
||||
createScreen();
|
||||
|
||||
checkScoreVisibility(false);
|
||||
|
||||
toggleWarmup();
|
||||
checkScoreVisibility(true);
|
||||
|
||||
toggleWarmup();
|
||||
checkScoreVisibility(false);
|
||||
}
|
||||
|
||||
private void createScreen()
|
||||
{
|
||||
AddStep("setup screen", () =>
|
||||
|
@ -18,7 +18,7 @@ using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
public partial class TestSceneLadderEditorScreen : TournamentTestScene
|
||||
public partial class TestSceneLadderEditorScreen : TournamentScreenTestScene
|
||||
{
|
||||
private LadderEditorScreen ladderEditorScreen = null!;
|
||||
private OsuContextMenuContainer? osuContextMenuContainer;
|
||||
|
@ -8,7 +8,7 @@ using osu.Game.Tournament.Screens.Ladder;
|
||||
|
||||
namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
public partial class TestSceneLadderScreen : TournamentTestScene
|
||||
public partial class TestSceneLadderScreen : TournamentScreenTestScene
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
|
@ -14,7 +14,7 @@ using osu.Game.Tournament.Screens.MapPool;
|
||||
|
||||
namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
public partial class TestSceneMapPoolScreen : TournamentTestScene
|
||||
public partial class TestSceneMapPoolScreen : TournamentScreenTestScene
|
||||
{
|
||||
private MapPoolScreen screen;
|
||||
|
||||
|
@ -6,7 +6,7 @@ using osu.Game.Tournament.Screens.Editors;
|
||||
|
||||
namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
public partial class TestSceneRoundEditorScreen : TournamentTestScene
|
||||
public partial class TestSceneRoundEditorScreen : TournamentScreenTestScene
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
|
@ -12,7 +12,7 @@ using osu.Game.Tournament.Screens.Schedule;
|
||||
|
||||
namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
public partial class TestSceneScheduleScreen : TournamentTestScene
|
||||
public partial class TestSceneScheduleScreen : TournamentScreenTestScene
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
|
@ -7,7 +7,7 @@ using osu.Game.Tournament.Screens.Editors;
|
||||
|
||||
namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
public partial class TestSceneSeedingEditorScreen : TournamentTestScene
|
||||
public partial class TestSceneSeedingEditorScreen : TournamentScreenTestScene
|
||||
{
|
||||
[Cached]
|
||||
private readonly LadderInfo ladder = new LadderInfo();
|
||||
|
@ -12,7 +12,7 @@ using osu.Game.Tournament.Screens.TeamIntro;
|
||||
|
||||
namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
public partial class TestSceneSeedingScreen : TournamentTestScene
|
||||
public partial class TestSceneSeedingScreen : TournamentScreenTestScene
|
||||
{
|
||||
[Cached]
|
||||
private readonly LadderInfo ladder = new LadderInfo
|
||||
|
@ -6,7 +6,7 @@ using osu.Game.Tournament.Screens.Setup;
|
||||
|
||||
namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
public partial class TestSceneSetupScreen : TournamentTestScene
|
||||
public partial class TestSceneSetupScreen : TournamentScreenTestScene
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
|
@ -6,7 +6,7 @@ using osu.Game.Tournament.Screens.Showcase;
|
||||
|
||||
namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
public partial class TestSceneShowcaseScreen : TournamentTestScene
|
||||
public partial class TestSceneShowcaseScreen : TournamentScreenTestScene
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
|
@ -5,7 +5,7 @@ using osu.Game.Tournament.Screens.Setup;
|
||||
|
||||
namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
public partial class TestSceneStablePathSelectScreen : TournamentTestScene
|
||||
public partial class TestSceneStablePathSelectScreen : TournamentScreenTestScene
|
||||
{
|
||||
public TestSceneStablePathSelectScreen()
|
||||
{
|
||||
|
@ -6,7 +6,7 @@ using osu.Game.Tournament.Screens.Editors;
|
||||
|
||||
namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
public partial class TestSceneTeamEditorScreen : TournamentTestScene
|
||||
public partial class TestSceneTeamEditorScreen : TournamentScreenTestScene
|
||||
{
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
|
@ -9,7 +9,7 @@ using osu.Game.Tournament.Screens.TeamIntro;
|
||||
|
||||
namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
public partial class TestSceneTeamIntroScreen : TournamentTestScene
|
||||
public partial class TestSceneTeamIntroScreen : TournamentScreenTestScene
|
||||
{
|
||||
[Cached]
|
||||
private readonly LadderInfo ladder = new LadderInfo();
|
||||
|
@ -8,7 +8,7 @@ using osu.Game.Tournament.Screens.TeamWin;
|
||||
|
||||
namespace osu.Game.Tournament.Tests.Screens
|
||||
{
|
||||
public partial class TestSceneTeamWinScreen : TournamentTestScene
|
||||
public partial class TestSceneTeamWinScreen : TournamentScreenTestScene
|
||||
{
|
||||
[Test]
|
||||
public void TestBasic()
|
||||
|
38
osu.Game.Tournament.Tests/TournamentScreenTestScene.cs
Normal file
38
osu.Game.Tournament.Tests/TournamentScreenTestScene.cs
Normal file
@ -0,0 +1,38 @@
|
||||
// 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 osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tournament.Tests
|
||||
{
|
||||
public abstract partial class TournamentScreenTestScene : TournamentTestScene
|
||||
{
|
||||
protected override Container<Drawable> Content { get; } = new TournamentScalingContainer();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
base.Content.Add(Content);
|
||||
}
|
||||
|
||||
private partial class TournamentScalingContainer : DrawSizePreservingFillContainer
|
||||
{
|
||||
public TournamentScalingContainer()
|
||||
{
|
||||
TargetDrawSize = new Vector2(1920, 1080);
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
Scale = new Vector2(Math.Min(1, Content.DrawWidth / (1920 + TournamentSceneManager.CONTROL_AREA_WIDTH)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@ namespace osu.Game.Tournament.Tests
|
||||
[STAThread]
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu", new HostOptions { BindIPC = true }))
|
||||
using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu-development", new HostOptions { BindIPC = true }))
|
||||
{
|
||||
host.Run(new TournamentTestBrowser());
|
||||
return 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.Linq;
|
||||
using System.Threading;
|
||||
using osu.Framework.Allocation;
|
||||
@ -21,18 +19,19 @@ namespace osu.Game.Tournament.Tests
|
||||
{
|
||||
public abstract partial class TournamentTestScene : OsuManualInputManagerTestScene
|
||||
{
|
||||
private TournamentMatch match;
|
||||
protected DialogOverlay DialogOverlay;
|
||||
protected DialogOverlay DialogOverlay = null!;
|
||||
|
||||
[Cached]
|
||||
protected LadderInfo Ladder { get; private set; } = new LadderInfo();
|
||||
|
||||
[Resolved]
|
||||
private RulesetStore rulesetStore { get; set; }
|
||||
|
||||
[Cached]
|
||||
protected MatchIPCInfo IPCInfo { get; private set; } = new MatchIPCInfo();
|
||||
|
||||
[Resolved]
|
||||
private RulesetStore rulesetStore { get; set; } = null!;
|
||||
|
||||
private TournamentMatch match = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TournamentStorage storage)
|
||||
{
|
||||
@ -173,7 +172,7 @@ namespace osu.Game.Tournament.Tests
|
||||
|
||||
public partial class TournamentTestSceneTestRunner : TournamentGameBase, ITestSceneTestRunner
|
||||
{
|
||||
private TestSceneTestRunner.TestRunner runner;
|
||||
private TestSceneTestRunner.TestRunner runner = null!;
|
||||
|
||||
protected override void LoadAsyncComplete()
|
||||
{
|
||||
|
@ -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.Bindables;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
@ -12,24 +10,21 @@ namespace osu.Game.Tournament.Components
|
||||
{
|
||||
public partial class DateTextBox : SettingsTextBox
|
||||
{
|
||||
public new Bindable<DateTimeOffset> Current
|
||||
private readonly BindableWithCurrent<DateTimeOffset> current = new BindableWithCurrent<DateTimeOffset>();
|
||||
|
||||
public new Bindable<DateTimeOffset>? Current
|
||||
{
|
||||
get => current;
|
||||
set
|
||||
{
|
||||
current = value.GetBoundCopy();
|
||||
current.BindValueChanged(dto =>
|
||||
base.Current.Value = dto.NewValue.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"), true);
|
||||
}
|
||||
set => current.Current = value;
|
||||
}
|
||||
|
||||
// hold a reference to the provided bindable so we don't have to in every settings section.
|
||||
private Bindable<DateTimeOffset> current = new Bindable<DateTimeOffset>();
|
||||
|
||||
public DateTextBox()
|
||||
{
|
||||
base.Current = new Bindable<string>(string.Empty);
|
||||
|
||||
current.BindValueChanged(dto =>
|
||||
base.Current.Value = dto.NewValue.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"), true);
|
||||
|
||||
((OsuTextBox)Control).OnCommit += (sender, _) =>
|
||||
{
|
||||
try
|
||||
|
@ -56,7 +56,7 @@ namespace osu.Game.Tournament.Components
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = Color4.Black,
|
||||
},
|
||||
new UpdateableOnlineBeatmapSetCover
|
||||
new NoUnloadBeatmapSetCover
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Colour = OsuColour.Gray(0.5f),
|
||||
@ -180,5 +180,15 @@ namespace osu.Game.Tournament.Components
|
||||
Alpha = 1;
|
||||
}
|
||||
}
|
||||
|
||||
private partial class NoUnloadBeatmapSetCover : UpdateableOnlineBeatmapSetCover
|
||||
{
|
||||
// As covers are displayed on stream, we want them to load as soon as possible.
|
||||
protected override double LoadDelay => 0;
|
||||
|
||||
// Use DelayedLoadWrapper to avoid content unloading when switching away to another screen.
|
||||
protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Func<Drawable> createContentFunc, double timeBeforeLoad)
|
||||
=> new DelayedLoadWrapper(createContentFunc, timeBeforeLoad);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Platform;
|
||||
@ -43,6 +45,6 @@ namespace osu.Game.Tournament.IO
|
||||
Logger.Log("Changing tournament storage: " + GetFullPath(string.Empty));
|
||||
}
|
||||
|
||||
public IEnumerable<string> ListTournaments() => AllTournaments.GetDirectories(string.Empty);
|
||||
public IEnumerable<string> ListTournaments() => AllTournaments.GetDirectories(string.Empty).OrderBy(directory => directory, StringComparer.CurrentCultureIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Input.States;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Screens.Edit.Compose.Components;
|
||||
using osu.Game.Tournament.Components;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Tournament.Models;
|
||||
@ -29,6 +30,8 @@ namespace osu.Game.Tournament.Screens.Editors
|
||||
[Cached]
|
||||
public partial class LadderEditorScreen : LadderScreen, IHasContextMenu
|
||||
{
|
||||
public const float GRID_SPACING = 10;
|
||||
|
||||
[Cached]
|
||||
private LadderEditorInfo editorInfo = new LadderEditorInfo();
|
||||
|
||||
@ -50,6 +53,13 @@ namespace osu.Game.Tournament.Screens.Editors
|
||||
|
||||
AddInternal(rightClickMessage = new WarningBox("Right click to place and link matches"));
|
||||
|
||||
ScrollContent.Add(new RectangularPositionSnapGrid(Vector2.Zero)
|
||||
{
|
||||
Spacing = new Vector2(GRID_SPACING),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Depth = float.MaxValue
|
||||
});
|
||||
|
||||
LadderInfo.Matches.CollectionChanged += (_, _) => updateMessage();
|
||||
updateMessage();
|
||||
}
|
||||
@ -75,8 +85,12 @@ namespace osu.Game.Tournament.Screens.Editors
|
||||
{
|
||||
new OsuMenuItem("Create new match", MenuItemType.Highlighted, () =>
|
||||
{
|
||||
var pos = MatchesContainer.ToLocalSpace(GetContainingInputManager().CurrentState.Mouse.Position);
|
||||
LadderInfo.Matches.Add(new TournamentMatch { Position = { Value = new Point((int)pos.X, (int)pos.Y) } });
|
||||
Vector2 pos = MatchesContainer.ToLocalSpace(GetContainingInputManager().CurrentState.Mouse.Position);
|
||||
TournamentMatch newMatch = new TournamentMatch { Position = { Value = new Point((int)pos.X, (int)pos.Y) } };
|
||||
|
||||
LadderInfo.Matches.Add(newMatch);
|
||||
|
||||
editorInfo.Selected.Value = newMatch;
|
||||
}),
|
||||
new OsuMenuItem("Reset teams", MenuItemType.Destructive, () =>
|
||||
{
|
||||
|
@ -28,6 +28,8 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
|
||||
private readonly MatchScoreCounter score1Text;
|
||||
private readonly MatchScoreCounter score2Text;
|
||||
|
||||
private readonly MatchScoreDiffCounter scoreDiffText;
|
||||
|
||||
private readonly Drawable score1Bar;
|
||||
private readonly Drawable score2Bar;
|
||||
|
||||
@ -88,6 +90,16 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
|
||||
Anchor = Anchor.TopCentre,
|
||||
Origin = Anchor.TopCentre
|
||||
},
|
||||
scoreDiffText = new MatchScoreDiffCounter
|
||||
{
|
||||
Anchor = Anchor.TopCentre,
|
||||
Margin = new MarginPadding
|
||||
{
|
||||
Top = bar_height / 4,
|
||||
Horizontal = 8
|
||||
},
|
||||
Alpha = 0
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -119,6 +131,10 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
|
||||
|
||||
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()
|
||||
@ -154,5 +170,14 @@ namespace osu.Game.Tournament.Screens.Gameplay.Components
|
||||
? 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -139,7 +139,7 @@ namespace osu.Game.Tournament.Screens.Gameplay
|
||||
base.LoadComplete();
|
||||
|
||||
State.BindTo(ipc.State);
|
||||
State.BindValueChanged(stateChanged, true);
|
||||
State.BindValueChanged(_ => updateState(), true);
|
||||
}
|
||||
|
||||
protected override void CurrentMatchChanged(ValueChangedEvent<TournamentMatch> match)
|
||||
@ -150,20 +150,47 @@ namespace osu.Game.Tournament.Screens.Gameplay
|
||||
return;
|
||||
|
||||
warmup.Value = match.NewValue.Team1Score.Value + match.NewValue.Team2Score.Value == 0;
|
||||
scheduledOperation?.Cancel();
|
||||
scheduledScreenChange?.Cancel();
|
||||
}
|
||||
|
||||
private ScheduledDelegate scheduledOperation;
|
||||
private ScheduledDelegate scheduledScreenChange;
|
||||
private ScheduledDelegate scheduledContract;
|
||||
|
||||
private TournamentMatchScoreDisplay scoreDisplay;
|
||||
|
||||
private TourneyState lastState;
|
||||
private MatchHeader header;
|
||||
|
||||
private void stateChanged(ValueChangedEvent<TourneyState> state)
|
||||
private void contract()
|
||||
{
|
||||
scheduledContract?.Cancel();
|
||||
|
||||
SongBar.Expanded = false;
|
||||
scoreDisplay.FadeOut(100);
|
||||
using (chat?.BeginDelayedSequence(500))
|
||||
chat?.Expand();
|
||||
}
|
||||
|
||||
private void expand()
|
||||
{
|
||||
scheduledContract?.Cancel();
|
||||
|
||||
chat?.Contract();
|
||||
|
||||
using (BeginDelayedSequence(300))
|
||||
{
|
||||
scoreDisplay.FadeIn(100);
|
||||
SongBar.Expanded = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateState()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (state.NewValue == TourneyState.Ranking)
|
||||
scheduledScreenChange?.Cancel();
|
||||
|
||||
if (State.Value == TourneyState.Ranking)
|
||||
{
|
||||
if (warmup.Value || CurrentMatch.Value == null) return;
|
||||
|
||||
@ -173,28 +200,7 @@ namespace osu.Game.Tournament.Screens.Gameplay
|
||||
CurrentMatch.Value.Team2Score.Value++;
|
||||
}
|
||||
|
||||
scheduledOperation?.Cancel();
|
||||
|
||||
void expand()
|
||||
{
|
||||
chat?.Contract();
|
||||
|
||||
using (BeginDelayedSequence(300))
|
||||
{
|
||||
scoreDisplay.FadeIn(100);
|
||||
SongBar.Expanded = true;
|
||||
}
|
||||
}
|
||||
|
||||
void contract()
|
||||
{
|
||||
SongBar.Expanded = false;
|
||||
scoreDisplay.FadeOut(100);
|
||||
using (chat?.BeginDelayedSequence(500))
|
||||
chat?.Expand();
|
||||
}
|
||||
|
||||
switch (state.NewValue)
|
||||
switch (State.Value)
|
||||
{
|
||||
case TourneyState.Idle:
|
||||
contract();
|
||||
@ -208,30 +214,41 @@ namespace osu.Game.Tournament.Screens.Gameplay
|
||||
if (lastState == TourneyState.Ranking && !warmup.Value)
|
||||
{
|
||||
if (CurrentMatch.Value?.Completed.Value == true)
|
||||
scheduledOperation = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(TeamWinScreen)); }, delay_before_progression);
|
||||
scheduledScreenChange = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(TeamWinScreen)); }, delay_before_progression);
|
||||
else if (CurrentMatch.Value?.Completed.Value == false)
|
||||
scheduledOperation = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(MapPoolScreen)); }, delay_before_progression);
|
||||
scheduledScreenChange = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(MapPoolScreen)); }, delay_before_progression);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case TourneyState.Ranking:
|
||||
scheduledOperation = Scheduler.AddDelayed(contract, 10000);
|
||||
scheduledContract = Scheduler.AddDelayed(contract, 10000);
|
||||
break;
|
||||
|
||||
default:
|
||||
chat.Contract();
|
||||
expand();
|
||||
break;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
lastState = state.NewValue;
|
||||
lastState = State.Value;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Hide()
|
||||
{
|
||||
scheduledScreenChange?.Cancel();
|
||||
base.Hide();
|
||||
}
|
||||
|
||||
public override void Show()
|
||||
{
|
||||
updateState();
|
||||
base.Show();
|
||||
}
|
||||
|
||||
private partial class ChromaArea : CompositeDrawable
|
||||
{
|
||||
[Resolved]
|
||||
|
@ -13,6 +13,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Tournament.Models;
|
||||
using osu.Game.Tournament.Screens.Editors;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osuTK.Input;
|
||||
@ -41,35 +42,60 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||
|
||||
AutoSizeAxes = Axes.Both;
|
||||
|
||||
Margin = new MarginPadding(5);
|
||||
const float border_thickness = 5;
|
||||
const float spacing = 2;
|
||||
|
||||
InternalChildren = new[]
|
||||
Margin = new MarginPadding(10);
|
||||
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
selectionBox = new Container
|
||||
{
|
||||
Scale = new Vector2(1.1f),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Alpha = 0,
|
||||
Colour = Color4.YellowGreen,
|
||||
Child = new Box { RelativeSizeAxes = Axes.Both }
|
||||
},
|
||||
CurrentMatchSelectionBox = new Container
|
||||
{
|
||||
Scale = new Vector2(1.05f, 1.1f),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Alpha = 0,
|
||||
Colour = Color4.White,
|
||||
Child = new Box { RelativeSizeAxes = Axes.Both }
|
||||
},
|
||||
Flow = new FillFlowContainer<DrawableMatchTeam>
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Direction = FillDirection.Vertical,
|
||||
Spacing = new Vector2(2)
|
||||
Spacing = new Vector2(spacing)
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding(-10),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Child = selectionBox = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
Masking = true,
|
||||
BorderColour = Color4.YellowGreen,
|
||||
BorderThickness = border_thickness,
|
||||
Child = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
AlwaysPresent = true,
|
||||
Alpha = 0,
|
||||
}
|
||||
},
|
||||
},
|
||||
new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Padding = new MarginPadding(-(spacing + border_thickness)),
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Child = CurrentMatchSelectionBox = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
BorderColour = Color4.White,
|
||||
BorderThickness = border_thickness,
|
||||
Masking = true,
|
||||
Child = new Box
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
AlwaysPresent = true,
|
||||
Alpha = 0,
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@ -97,12 +123,11 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||
Position = new Vector2(pos.NewValue.X, pos.NewValue.Y);
|
||||
Changed?.Invoke();
|
||||
}, true);
|
||||
|
||||
updateTeams();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fired when somethign changed that requires a ladder redraw.
|
||||
/// Fired when something changed that requires a ladder redraw.
|
||||
/// </summary>
|
||||
public Action Changed;
|
||||
|
||||
@ -225,10 +250,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||
if (editorInfo != null)
|
||||
{
|
||||
globalSelection = editorInfo.Selected.GetBoundCopy();
|
||||
globalSelection.BindValueChanged(s =>
|
||||
{
|
||||
if (s.NewValue != Match) Selected = false;
|
||||
});
|
||||
globalSelection.BindValueChanged(s => Selected = s.NewValue == Match, true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -312,8 +334,8 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||
|
||||
private Vector2 snapToGrid(Vector2 pos) =>
|
||||
new Vector2(
|
||||
(int)(pos.X / 10) * 10,
|
||||
(int)(pos.Y / 10) * 10
|
||||
(int)(pos.X / LadderEditorScreen.GRID_SPACING) * LadderEditorScreen.GRID_SPACING,
|
||||
(int)(pos.Y / LadderEditorScreen.GRID_SPACING) * LadderEditorScreen.GRID_SPACING
|
||||
);
|
||||
|
||||
public void Remove()
|
||||
|
@ -62,22 +62,28 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||
// ensure any ongoing edits are committed out to the *current* selection before changing to a new one.
|
||||
GetContainingInputManager().TriggerFocusContention(null);
|
||||
|
||||
roundDropdown.Current = selection.NewValue?.Round;
|
||||
losersCheckbox.Current = selection.NewValue?.Losers;
|
||||
dateTimeBox.Current = selection.NewValue?.Date;
|
||||
// Required to avoid cyclic failure in BindableWithCurrent (TriggerChange called during the Current_Set process).
|
||||
// Arguable a framework issue but since we haven't hit it anywhere else a local workaround seems best.
|
||||
roundDropdown.Current.ValueChanged -= roundDropdownChanged;
|
||||
|
||||
team1Dropdown.Current = selection.NewValue?.Team1;
|
||||
team2Dropdown.Current = selection.NewValue?.Team2;
|
||||
roundDropdown.Current = selection.NewValue.Round;
|
||||
losersCheckbox.Current = selection.NewValue.Losers;
|
||||
dateTimeBox.Current = selection.NewValue.Date;
|
||||
|
||||
team1Dropdown.Current = selection.NewValue.Team1;
|
||||
team2Dropdown.Current = selection.NewValue.Team2;
|
||||
|
||||
roundDropdown.Current.ValueChanged += roundDropdownChanged;
|
||||
};
|
||||
}
|
||||
|
||||
roundDropdown.Current.ValueChanged += round =>
|
||||
private void roundDropdownChanged(ValueChangedEvent<TournamentRound> round)
|
||||
{
|
||||
if (editorInfo.Selected.Value?.Date.Value < round.NewValue?.StartDate.Value)
|
||||
{
|
||||
if (editorInfo.Selected.Value?.Date.Value < round.NewValue?.StartDate.Value)
|
||||
{
|
||||
editorInfo.Selected.Value.Date.Value = round.NewValue.StartDate.Value;
|
||||
editorInfo.Selected.TriggerChange();
|
||||
}
|
||||
};
|
||||
editorInfo.Selected.Value.Date.Value = round.NewValue.StartDate.Value;
|
||||
editorInfo.Selected.TriggerChange();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
|
@ -60,6 +60,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components
|
||||
|
||||
var topLeft = new Vector2(minX, minY);
|
||||
|
||||
OriginPosition = new Vector2(PathRadius);
|
||||
Position = Parent.ToLocalSpace(topLeft);
|
||||
Vertices = points.Select(p => Parent.ToLocalSpace(p) - Parent.ToLocalSpace(topLeft)).ToList();
|
||||
}
|
||||
|
@ -37,6 +37,8 @@ namespace osu.Game.Tournament.Screens.MapPool
|
||||
private OsuButton buttonRedPick;
|
||||
private OsuButton buttonBluePick;
|
||||
|
||||
private ScheduledDelegate scheduledScreenChange;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(MatchIPCInfo ipc)
|
||||
{
|
||||
@ -188,8 +190,6 @@ namespace osu.Game.Tournament.Screens.MapPool
|
||||
setNextMode();
|
||||
}
|
||||
|
||||
private ScheduledDelegate scheduledChange;
|
||||
|
||||
private void addForBeatmap(int beatmapId)
|
||||
{
|
||||
if (CurrentMatch.Value == null)
|
||||
@ -216,12 +216,18 @@ namespace osu.Game.Tournament.Screens.MapPool
|
||||
{
|
||||
if (pickType == ChoiceType.Pick && CurrentMatch.Value.PicksBans.Any(i => i.Type == ChoiceType.Pick))
|
||||
{
|
||||
scheduledChange?.Cancel();
|
||||
scheduledChange = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(GameplayScreen)); }, 10000);
|
||||
scheduledScreenChange?.Cancel();
|
||||
scheduledScreenChange = Scheduler.AddDelayed(() => { sceneManager?.SetScreen(typeof(GameplayScreen)); }, 10000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Hide()
|
||||
{
|
||||
scheduledScreenChange?.Cancel();
|
||||
base.Hide();
|
||||
}
|
||||
|
||||
protected override void CurrentMatchChanged(ValueChangedEvent<TournamentMatch> match)
|
||||
{
|
||||
base.CurrentMatchChanged(match);
|
||||
|
@ -15,6 +15,7 @@ namespace osu.Game.Tournament.Screens.Setup
|
||||
{
|
||||
private OsuDropdown<string> dropdown;
|
||||
private OsuButton folderButton;
|
||||
private OsuButton reloadTournamentsButton;
|
||||
|
||||
[Resolved]
|
||||
private TournamentGameBase game { get; set; }
|
||||
@ -28,6 +29,8 @@ namespace osu.Game.Tournament.Screens.Setup
|
||||
dropdown.Items = storage.ListTournaments();
|
||||
dropdown.Current.BindValueChanged(v => Button.Enabled.Value = v.NewValue != startupTournament, true);
|
||||
|
||||
reloadTournamentsButton.Action = () => dropdown.Items = storage.ListTournaments();
|
||||
|
||||
Action = () =>
|
||||
{
|
||||
game.RestartAppWhenExited();
|
||||
@ -48,7 +51,13 @@ namespace osu.Game.Tournament.Screens.Setup
|
||||
Width = BUTTON_SIZE
|
||||
});
|
||||
|
||||
FlowContainer.Insert(-2, dropdown = new OsuDropdown<string>
|
||||
FlowContainer.Insert(-2, reloadTournamentsButton = new RoundedButton
|
||||
{
|
||||
Text = "Refresh",
|
||||
Width = BUTTON_SIZE
|
||||
});
|
||||
|
||||
FlowContainer.Insert(-3, dropdown = new OsuDropdown<string>
|
||||
{
|
||||
Width = 510
|
||||
});
|
||||
|
@ -36,8 +36,10 @@ namespace osu.Game.Tournament
|
||||
|
||||
public static readonly Color4 TEXT_COLOUR = Color4Extensions.FromHex("#fff");
|
||||
private Drawable heightWarning;
|
||||
private Bindable<Size> windowSize;
|
||||
|
||||
private Bindable<WindowMode> windowMode;
|
||||
private readonly BindableSize windowSize = new BindableSize();
|
||||
|
||||
private LoadingSpinner loadingSpinner;
|
||||
|
||||
private readonly DialogOverlay dialogOverlay = new DialogOverlay();
|
||||
@ -49,7 +51,10 @@ namespace osu.Game.Tournament
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(FrameworkConfigManager frameworkConfig, GameHost host)
|
||||
{
|
||||
windowSize = frameworkConfig.GetBindable<Size>(FrameworkSetting.WindowedSize);
|
||||
frameworkConfig.BindWith(FrameworkSetting.WindowedSize, windowSize);
|
||||
|
||||
windowSize.MinValue = new Size(TournamentSceneManager.REQUIRED_WIDTH, TournamentSceneManager.STREAM_AREA_HEIGHT);
|
||||
|
||||
windowMode = frameworkConfig.GetBindable<WindowMode>(FrameworkSetting.WindowMode);
|
||||
|
||||
Add(loadingSpinner = new LoadingSpinner(true, true)
|
||||
|
@ -38,11 +38,14 @@ namespace osu.Game.Tournament
|
||||
private Container screens;
|
||||
private TourneyVideo video;
|
||||
|
||||
public const float CONTROL_AREA_WIDTH = 200;
|
||||
public const int CONTROL_AREA_WIDTH = 200;
|
||||
|
||||
public const float STREAM_AREA_WIDTH = 1366;
|
||||
public const int STREAM_AREA_WIDTH = 1366;
|
||||
public const int STREAM_AREA_HEIGHT = (int)(STREAM_AREA_WIDTH / ASPECT_RATIO);
|
||||
|
||||
public const double REQUIRED_WIDTH = CONTROL_AREA_WIDTH * 2 + STREAM_AREA_WIDTH;
|
||||
public const float ASPECT_RATIO = 16 / 9f;
|
||||
|
||||
public const int REQUIRED_WIDTH = CONTROL_AREA_WIDTH * 2 + STREAM_AREA_WIDTH;
|
||||
|
||||
[Cached]
|
||||
private TournamentMatchChatDisplay chat = new TournamentMatchChatDisplay();
|
||||
@ -65,13 +68,20 @@ namespace osu.Game.Tournament
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
X = CONTROL_AREA_WIDTH,
|
||||
FillMode = FillMode.Fit,
|
||||
FillAspectRatio = 16 / 9f,
|
||||
FillAspectRatio = ASPECT_RATIO,
|
||||
Anchor = Anchor.TopLeft,
|
||||
Origin = Anchor.TopLeft,
|
||||
Width = STREAM_AREA_WIDTH,
|
||||
//Masking = true,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
new Box
|
||||
{
|
||||
Colour = new Color4(20, 20, 20, 255),
|
||||
Anchor = Anchor.TopRight,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Width = 10,
|
||||
},
|
||||
video = new TourneyVideo("main", true)
|
||||
{
|
||||
Loop = true,
|
||||
|
@ -14,6 +14,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Rulesets;
|
||||
@ -49,6 +50,9 @@ namespace osu.Game
|
||||
[Resolved]
|
||||
private INotificationOverlay? notificationOverlay { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; } = null!;
|
||||
|
||||
protected virtual int TimeToSleepDuringGameplay => 30000;
|
||||
|
||||
protected override void LoadComplete()
|
||||
@ -118,10 +122,27 @@ namespace osu.Game
|
||||
|
||||
realmAccess.Run(r =>
|
||||
{
|
||||
foreach (var b in r.All<BeatmapInfo>().Where(b => b.StarRating < 0 || (b.OnlineID > 0 && b.LastOnlineUpdate == null)))
|
||||
// BeatmapProcessor is responsible for both online and local processing.
|
||||
// In the case a user isn't logged in, it won't update LastOnlineUpdate and therefore re-queue,
|
||||
// causing overhead from the non-online processing to redundantly run every startup.
|
||||
//
|
||||
// We may eventually consider making the Process call more specific (or avoid this in any number
|
||||
// of other possible ways), but for now avoid queueing if the user isn't logged in at startup.
|
||||
if (api.IsLoggedIn)
|
||||
{
|
||||
Debug.Assert(b.BeatmapSet != null);
|
||||
beatmapSetIds.Add(b.BeatmapSet.ID);
|
||||
foreach (var b in r.All<BeatmapInfo>().Where(b => b.StarRating < 0 || (b.OnlineID > 0 && b.LastOnlineUpdate == null)))
|
||||
{
|
||||
Debug.Assert(b.BeatmapSet != null);
|
||||
beatmapSetIds.Add(b.BeatmapSet.ID);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var b in r.All<BeatmapInfo>().Where(b => b.StarRating < 0))
|
||||
{
|
||||
Debug.Assert(b.BeatmapSet != null);
|
||||
beatmapSetIds.Add(b.BeatmapSet.ID);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -29,7 +29,7 @@ namespace osu.Game.Beatmaps
|
||||
/// </summary>
|
||||
public class BeatmapImporter : RealmArchiveModelImporter<BeatmapSetInfo>
|
||||
{
|
||||
public override IEnumerable<string> HandledExtensions => new[] { ".osz" };
|
||||
public override IEnumerable<string> HandledExtensions => new[] { ".osz", ".olz" };
|
||||
|
||||
protected override string[] HashableFileTypes => new[] { ".osu" };
|
||||
|
||||
@ -145,7 +145,7 @@ namespace osu.Game.Beatmaps
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool ShouldDeleteArchive(string path) => Path.GetExtension(path).ToLowerInvariant() == ".osz";
|
||||
protected override bool ShouldDeleteArchive(string path) => HandledExtensions.Contains(Path.GetExtension(path).ToLowerInvariant());
|
||||
|
||||
protected override void Populate(BeatmapSetInfo beatmapSet, ArchiveReader? archive, Realm realm, CancellationToken cancellationToken = default)
|
||||
{
|
||||
|
@ -40,7 +40,9 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
private readonly WorkingBeatmapCache workingBeatmapCache;
|
||||
|
||||
private readonly LegacyBeatmapExporter beatmapExporter;
|
||||
private readonly BeatmapExporter beatmapExporter;
|
||||
|
||||
private readonly LegacyBeatmapExporter legacyBeatmapExporter;
|
||||
|
||||
public ProcessBeatmapDelegate? ProcessBeatmap { private get; set; }
|
||||
|
||||
@ -77,7 +79,12 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
workingBeatmapCache = CreateWorkingBeatmapCache(audioManager, gameResources, userResources, defaultBeatmap, host);
|
||||
|
||||
beatmapExporter = new LegacyBeatmapExporter(storage)
|
||||
beatmapExporter = new BeatmapExporter(storage)
|
||||
{
|
||||
PostNotification = obj => PostNotification?.Invoke(obj)
|
||||
};
|
||||
|
||||
legacyBeatmapExporter = new LegacyBeatmapExporter(storage)
|
||||
{
|
||||
PostNotification = obj => PostNotification?.Invoke(obj)
|
||||
};
|
||||
@ -402,6 +409,8 @@ namespace osu.Game.Beatmaps
|
||||
|
||||
public Task Export(BeatmapSetInfo beatmap) => beatmapExporter.ExportAsync(beatmap.ToLive(Realm));
|
||||
|
||||
public Task ExportLegacy(BeatmapSetInfo beatmap) => legacyBeatmapExporter.ExportAsync(beatmap.ToLive(Realm));
|
||||
|
||||
private void updateHashAndMarkDirty(BeatmapSetInfo setInfo)
|
||||
{
|
||||
setInfo.Hash = beatmapImporter.ComputeHash(setInfo);
|
||||
|
22
osu.Game/Database/BeatmapExporter.cs
Normal file
22
osu.Game/Database/BeatmapExporter.cs
Normal file
@ -0,0 +1,22 @@
|
||||
// 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.Platform;
|
||||
using osu.Game.Beatmaps;
|
||||
|
||||
namespace osu.Game.Database
|
||||
{
|
||||
/// <summary>
|
||||
/// Exporter for beatmap archives.
|
||||
/// This is not for legacy purposes and works for lazer only.
|
||||
/// </summary>
|
||||
public class BeatmapExporter : LegacyArchiveExporter<BeatmapSetInfo>
|
||||
{
|
||||
public BeatmapExporter(Storage storage)
|
||||
: base(storage)
|
||||
{
|
||||
}
|
||||
|
||||
protected override string FileExtension => @".olz";
|
||||
}
|
||||
}
|
@ -39,7 +39,7 @@ namespace osu.Game.Database
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
using (var stream = UserFileStorage.GetStream(file.File.GetStoragePath()))
|
||||
using (var stream = GetFileContents(model, file))
|
||||
{
|
||||
if (stream == null)
|
||||
{
|
||||
@ -65,5 +65,7 @@ namespace osu.Game.Database
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual Stream? GetFileContents(TModel model, INamedFileUsage file) => UserFileStorage.GetStream(file.File.GetStoragePath());
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,25 @@
|
||||
// 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.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using osu.Framework.Platform;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.IO;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Database
|
||||
{
|
||||
/// <summary>
|
||||
/// Exporter for osu!stable legacy beatmap archives.
|
||||
/// Converts all beatmaps in the set to legacy format and exports it as a legacy package.
|
||||
/// </summary>
|
||||
public class LegacyBeatmapExporter : LegacyArchiveExporter<BeatmapSetInfo>
|
||||
{
|
||||
public LegacyBeatmapExporter(Storage storage)
|
||||
@ -13,6 +27,72 @@ namespace osu.Game.Database
|
||||
{
|
||||
}
|
||||
|
||||
protected override Stream? GetFileContents(BeatmapSetInfo model, INamedFileUsage file)
|
||||
{
|
||||
bool isBeatmap = model.Beatmaps.Any(o => o.Hash == file.File.Hash);
|
||||
|
||||
if (!isBeatmap)
|
||||
return base.GetFileContents(model, file);
|
||||
|
||||
// Read the beatmap contents and skin
|
||||
using var contentStream = base.GetFileContents(model, file);
|
||||
|
||||
if (contentStream == null)
|
||||
return null;
|
||||
|
||||
using var contentStreamReader = new LineBufferedReader(contentStream);
|
||||
var beatmapContent = new LegacyBeatmapDecoder().Decode(contentStreamReader);
|
||||
|
||||
using var skinStream = base.GetFileContents(model, file);
|
||||
|
||||
if (skinStream == null)
|
||||
return null;
|
||||
|
||||
using var skinStreamReader = new LineBufferedReader(skinStream);
|
||||
var beatmapSkin = new LegacySkin(new SkinInfo(), null!)
|
||||
{
|
||||
Configuration = new LegacySkinDecoder().Decode(skinStreamReader)
|
||||
};
|
||||
|
||||
// Convert beatmap elements to be compatible with legacy format
|
||||
// So we truncate time and position values to integers, and convert paths with multiple segments to bezier curves
|
||||
foreach (var controlPoint in beatmapContent.ControlPointInfo.AllControlPoints)
|
||||
controlPoint.Time = Math.Floor(controlPoint.Time);
|
||||
|
||||
foreach (var hitObject in beatmapContent.HitObjects)
|
||||
{
|
||||
// Truncate end time before truncating start time because end time is dependent on start time
|
||||
if (hitObject is IHasDuration hasDuration && hitObject is not IHasPath)
|
||||
hasDuration.Duration = Math.Floor(hasDuration.EndTime) - Math.Floor(hitObject.StartTime);
|
||||
|
||||
hitObject.StartTime = Math.Floor(hitObject.StartTime);
|
||||
|
||||
if (hitObject is not IHasPath hasPath || BezierConverter.CountSegments(hasPath.Path.ControlPoints) <= 1) continue;
|
||||
|
||||
var newControlPoints = BezierConverter.ConvertToModernBezier(hasPath.Path.ControlPoints);
|
||||
|
||||
// Truncate control points to integer positions
|
||||
foreach (var pathControlPoint in newControlPoints)
|
||||
{
|
||||
pathControlPoint.Position = new Vector2(
|
||||
(float)Math.Floor(pathControlPoint.Position.X),
|
||||
(float)Math.Floor(pathControlPoint.Position.Y));
|
||||
}
|
||||
|
||||
hasPath.Path.ControlPoints.Clear();
|
||||
hasPath.Path.ControlPoints.AddRange(newControlPoints);
|
||||
}
|
||||
|
||||
// Encode to legacy format
|
||||
var stream = new MemoryStream();
|
||||
using (var sw = new StreamWriter(stream, Encoding.UTF8, 1024, true))
|
||||
new LegacyBeatmapEncoder(beatmapContent, beatmapSkin).Encode(sw);
|
||||
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
protected override string FileExtension => @".osz";
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,11 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString Default => new TranslatableString(getKey(@"default"), @"Default");
|
||||
|
||||
/// <summary>
|
||||
/// "Export"
|
||||
/// </summary>
|
||||
public static LocalisableString Export => new TranslatableString(getKey(@"export"), @"Export");
|
||||
|
||||
/// <summary>
|
||||
/// "Width"
|
||||
/// </summary>
|
||||
|
@ -35,9 +35,14 @@ namespace osu.Game.Localisation
|
||||
public static LocalisableString SetPreviewPointToCurrent => new TranslatableString(getKey(@"set_preview_point_to_current"), @"Set preview point to current time");
|
||||
|
||||
/// <summary>
|
||||
/// "Export package"
|
||||
/// "For editing (.olz)"
|
||||
/// </summary>
|
||||
public static LocalisableString ExportPackage => new TranslatableString(getKey(@"export_package"), @"Export package");
|
||||
public static LocalisableString ExportForEditing => new TranslatableString(getKey(@"export_for_editing"), @"For editing (.olz)");
|
||||
|
||||
/// <summary>
|
||||
/// "For compatibility (.osz)"
|
||||
/// </summary>
|
||||
public static LocalisableString ExportForCompatibility => new TranslatableString(getKey(@"export_for_compatibility"), @"For compatibility (.osz)");
|
||||
|
||||
/// <summary>
|
||||
/// "Create new difficulty"
|
||||
|
@ -425,7 +425,7 @@ namespace osu.Game.Online.Leaderboards
|
||||
|
||||
if (Score.Files.Count > 0)
|
||||
{
|
||||
items.Add(new OsuMenuItem("Export", MenuItemType.Standard, () => scoreManager.Export(Score)));
|
||||
items.Add(new OsuMenuItem(Localisation.CommonStrings.Export, MenuItemType.Standard, () => scoreManager.Export(Score)));
|
||||
items.Add(new OsuMenuItem(CommonStrings.ButtonsDelete, MenuItemType.Destructive, () => dialogOverlay?.Push(new LocalScoreDeleteDialog(Score))));
|
||||
}
|
||||
|
||||
|
@ -196,7 +196,7 @@ namespace osu.Game.Overlays.Settings
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Spacing = new Vector2(0, 10),
|
||||
Spacing = new Vector2(0, 5),
|
||||
Child = Control = CreateControl(),
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,9 @@ namespace osu.Game.Rulesets.Edit
|
||||
|
||||
// Timing
|
||||
new CheckPreviewTime(),
|
||||
|
||||
// Events
|
||||
new CheckBreaks()
|
||||
};
|
||||
|
||||
public IEnumerable<Issue> Run(BeatmapVerifierContext context)
|
||||
|
94
osu.Game/Rulesets/Edit/Checks/CheckBreaks.cs
Normal file
94
osu.Game/Rulesets/Edit/Checks/CheckBreaks.cs
Normal file
@ -0,0 +1,94 @@
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Rulesets.Edit.Checks.Components;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Edit.Checks
|
||||
{
|
||||
public class CheckBreaks : ICheck
|
||||
{
|
||||
// Breaks may be off by 1 ms.
|
||||
private const int leniency_threshold = 1;
|
||||
private const double minimum_gap_before_break = 200;
|
||||
|
||||
// Break end time depends on the upcoming object's pre-empt time.
|
||||
// As things stand, "pre-empt time" is only defined for osu! standard
|
||||
// This is a generic value representing AR=10
|
||||
// Relevant: https://github.com/ppy/osu/issues/14330#issuecomment-1002158551
|
||||
private const double min_end_threshold = 450;
|
||||
public CheckMetadata Metadata => new CheckMetadata(CheckCategory.Events, "Breaks not achievable using the editor");
|
||||
|
||||
public IEnumerable<IssueTemplate> PossibleTemplates => new IssueTemplate[]
|
||||
{
|
||||
new IssueTemplateEarlyStart(this),
|
||||
new IssueTemplateLateEnd(this),
|
||||
new IssueTemplateTooShort(this)
|
||||
};
|
||||
|
||||
public IEnumerable<Issue> Run(BeatmapVerifierContext context)
|
||||
{
|
||||
var startTimes = context.Beatmap.HitObjects.Select(ho => ho.StartTime).OrderBy(x => x).ToList();
|
||||
var endTimes = context.Beatmap.HitObjects.Select(ho => ho.GetEndTime()).OrderBy(x => x).ToList();
|
||||
|
||||
foreach (var breakPeriod in context.Beatmap.Breaks)
|
||||
{
|
||||
if (breakPeriod.Duration < BreakPeriod.MIN_BREAK_DURATION)
|
||||
yield return new IssueTemplateTooShort(this).Create(breakPeriod.StartTime);
|
||||
|
||||
int previousObjectEndTimeIndex = endTimes.BinarySearch(breakPeriod.StartTime);
|
||||
if (previousObjectEndTimeIndex < 0) previousObjectEndTimeIndex = ~previousObjectEndTimeIndex - 1;
|
||||
|
||||
if (previousObjectEndTimeIndex >= 0)
|
||||
{
|
||||
double gapBeforeBreak = breakPeriod.StartTime - endTimes[previousObjectEndTimeIndex];
|
||||
if (gapBeforeBreak < minimum_gap_before_break - leniency_threshold)
|
||||
yield return new IssueTemplateEarlyStart(this).Create(breakPeriod.StartTime, minimum_gap_before_break - gapBeforeBreak);
|
||||
}
|
||||
|
||||
int nextObjectStartTimeIndex = startTimes.BinarySearch(breakPeriod.EndTime);
|
||||
if (nextObjectStartTimeIndex < 0) nextObjectStartTimeIndex = ~nextObjectStartTimeIndex;
|
||||
|
||||
if (nextObjectStartTimeIndex < startTimes.Count)
|
||||
{
|
||||
double gapAfterBreak = startTimes[nextObjectStartTimeIndex] - breakPeriod.EndTime;
|
||||
if (gapAfterBreak < min_end_threshold - leniency_threshold)
|
||||
yield return new IssueTemplateLateEnd(this).Create(breakPeriod.StartTime, min_end_threshold - gapAfterBreak);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class IssueTemplateEarlyStart : IssueTemplate
|
||||
{
|
||||
public IssueTemplateEarlyStart(ICheck check)
|
||||
: base(check, IssueType.Problem, "Break starts {0} ms early.")
|
||||
{
|
||||
}
|
||||
|
||||
public Issue Create(double startTime, double diff) => new Issue(startTime, this, (int)diff);
|
||||
}
|
||||
|
||||
public class IssueTemplateLateEnd : IssueTemplate
|
||||
{
|
||||
public IssueTemplateLateEnd(ICheck check)
|
||||
: base(check, IssueType.Problem, "Break ends {0} ms late.")
|
||||
{
|
||||
}
|
||||
|
||||
public Issue Create(double startTime, double diff) => new Issue(startTime, this, (int)diff);
|
||||
}
|
||||
|
||||
public class IssueTemplateTooShort : IssueTemplate
|
||||
{
|
||||
public IssueTemplateTooShort(ICheck check)
|
||||
: base(check, IssueType.Warning, "Break is non-functional due to being less than {0} ms.")
|
||||
{
|
||||
}
|
||||
|
||||
public Issue Create(double startTime) => new Issue(startTime, this, BreakPeriod.MIN_BREAK_DURATION);
|
||||
}
|
||||
}
|
||||
}
|
@ -39,6 +39,13 @@ namespace osu.Game.Rulesets.Objects
|
||||
new[] { new Vector2d(1, 0), new Vector2d(1, 1.2447058f), new Vector2d(-0.8526471f, 2.118367f), new Vector2d(-2.6211002f, 7.854936e-06f), new Vector2d(-0.8526448f, -2.118357f), new Vector2d(1, -1.2447058f), new Vector2d(1, 0) })
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Counts the number of segments in a slider path.
|
||||
/// </summary>
|
||||
/// <param name="controlPoints">The control points of the path.</param>
|
||||
/// <returns>The number of segments in a slider path.</returns>
|
||||
public static int CountSegments(IList<PathControlPoint> controlPoints) => controlPoints.Where((t, i) => t.Type != null && i < controlPoints.Count - 1).Count();
|
||||
|
||||
/// <summary>
|
||||
/// Converts a slider path to bezier control point positions compatible with the legacy osu! client.
|
||||
/// </summary>
|
||||
|
@ -384,5 +384,10 @@ namespace osu.Game.Rulesets
|
||||
/// Can be overridden to add a ruleset-specific section to the editor beatmap setup screen.
|
||||
/// </summary>
|
||||
public virtual RulesetSetupSection? CreateEditorSetupSection() => null;
|
||||
|
||||
/// <summary>
|
||||
/// Can be overridden to alter the difficulty section to the editor beatmap setup screen.
|
||||
/// </summary>
|
||||
public virtual DifficultySection? CreateEditorDifficultySection() => null;
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
@ -200,7 +201,17 @@ namespace osu.Game.Screens.Edit.Components.Menus
|
||||
public DrawableSpacer(MenuItem item)
|
||||
: base(item)
|
||||
{
|
||||
Scale = new Vector2(1, 0.3f);
|
||||
Scale = new Vector2(1, 0.6f);
|
||||
|
||||
AddInternal(new Box
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Colour = BackgroundColourHover,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = 2f,
|
||||
Width = 0.8f,
|
||||
});
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e) => true;
|
||||
|
@ -997,23 +997,40 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
private List<MenuItem> createFileMenuItems() => new List<MenuItem>
|
||||
{
|
||||
new EditorMenuItem(WebCommonStrings.ButtonsSave, MenuItemType.Standard, () => Save()),
|
||||
new EditorMenuItem(EditorStrings.ExportPackage, MenuItemType.Standard, exportBeatmap) { Action = { Disabled = !RuntimeInfo.IsDesktop } },
|
||||
new EditorMenuItemSpacer(),
|
||||
createDifficultyCreationMenu(),
|
||||
createDifficultySwitchMenu(),
|
||||
new EditorMenuItemSpacer(),
|
||||
new EditorMenuItem(EditorStrings.DeleteDifficulty, MenuItemType.Standard, deleteDifficulty) { Action = { Disabled = Beatmap.Value.BeatmapSetInfo.Beatmaps.Count < 2 } },
|
||||
new EditorMenuItemSpacer(),
|
||||
new EditorMenuItem(WebCommonStrings.ButtonsSave, MenuItemType.Standard, () => Save()),
|
||||
createExportMenu(),
|
||||
new EditorMenuItemSpacer(),
|
||||
new EditorMenuItem(CommonStrings.Exit, MenuItemType.Standard, this.Exit)
|
||||
};
|
||||
|
||||
private EditorMenuItem createExportMenu()
|
||||
{
|
||||
var exportItems = new List<MenuItem>
|
||||
{
|
||||
new EditorMenuItem(EditorStrings.ExportForEditing, MenuItemType.Standard, exportBeatmap) { Action = { Disabled = !RuntimeInfo.IsDesktop } },
|
||||
new EditorMenuItem(EditorStrings.ExportForCompatibility, MenuItemType.Standard, exportLegacyBeatmap) { Action = { Disabled = !RuntimeInfo.IsDesktop } },
|
||||
};
|
||||
|
||||
return new EditorMenuItem(CommonStrings.Export) { Items = exportItems };
|
||||
}
|
||||
|
||||
private void exportBeatmap()
|
||||
{
|
||||
Save();
|
||||
beatmapManager.Export(Beatmap.Value.BeatmapSetInfo);
|
||||
}
|
||||
|
||||
private void exportLegacyBeatmap()
|
||||
{
|
||||
Save();
|
||||
beatmapManager.ExportLegacy(Beatmap.Value.BeatmapSetInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Beatmaps of the currently edited set, grouped by ruleset and ordered by difficulty.
|
||||
/// </summary>
|
||||
|
@ -13,14 +13,14 @@ using osu.Game.Localisation;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Setup
|
||||
{
|
||||
internal partial class DifficultySection : SetupSection
|
||||
public partial class DifficultySection : SetupSection
|
||||
{
|
||||
private LabelledSliderBar<float> circleSizeSlider = null!;
|
||||
private LabelledSliderBar<float> healthDrainSlider = null!;
|
||||
private LabelledSliderBar<float> approachRateSlider = null!;
|
||||
private LabelledSliderBar<float> overallDifficultySlider = null!;
|
||||
private LabelledSliderBar<double> baseVelocitySlider = null!;
|
||||
private LabelledSliderBar<double> tickRateSlider = null!;
|
||||
protected LabelledSliderBar<float> CircleSizeSlider { get; private set; } = null!;
|
||||
protected LabelledSliderBar<float> HealthDrainSlider { get; private set; } = null!;
|
||||
protected LabelledSliderBar<float> ApproachRateSlider { get; private set; } = null!;
|
||||
protected LabelledSliderBar<float> OverallDifficultySlider { get; private set; } = null!;
|
||||
protected LabelledSliderBar<double> BaseVelocitySlider { get; private set; } = null!;
|
||||
protected LabelledSliderBar<double> TickRateSlider { get; private set; } = null!;
|
||||
|
||||
public override LocalisableString Title => EditorSetupStrings.DifficultyHeader;
|
||||
|
||||
@ -29,7 +29,7 @@ namespace osu.Game.Screens.Edit.Setup
|
||||
{
|
||||
Children = new Drawable[]
|
||||
{
|
||||
circleSizeSlider = new LabelledSliderBar<float>
|
||||
CircleSizeSlider = new LabelledSliderBar<float>
|
||||
{
|
||||
Label = BeatmapsetsStrings.ShowStatsCs,
|
||||
FixedLabelWidth = LABEL_WIDTH,
|
||||
@ -42,7 +42,7 @@ namespace osu.Game.Screens.Edit.Setup
|
||||
Precision = 0.1f,
|
||||
}
|
||||
},
|
||||
healthDrainSlider = new LabelledSliderBar<float>
|
||||
HealthDrainSlider = new LabelledSliderBar<float>
|
||||
{
|
||||
Label = BeatmapsetsStrings.ShowStatsDrain,
|
||||
FixedLabelWidth = LABEL_WIDTH,
|
||||
@ -55,7 +55,7 @@ namespace osu.Game.Screens.Edit.Setup
|
||||
Precision = 0.1f,
|
||||
}
|
||||
},
|
||||
approachRateSlider = new LabelledSliderBar<float>
|
||||
ApproachRateSlider = new LabelledSliderBar<float>
|
||||
{
|
||||
Label = BeatmapsetsStrings.ShowStatsAr,
|
||||
FixedLabelWidth = LABEL_WIDTH,
|
||||
@ -68,7 +68,7 @@ namespace osu.Game.Screens.Edit.Setup
|
||||
Precision = 0.1f,
|
||||
}
|
||||
},
|
||||
overallDifficultySlider = new LabelledSliderBar<float>
|
||||
OverallDifficultySlider = new LabelledSliderBar<float>
|
||||
{
|
||||
Label = BeatmapsetsStrings.ShowStatsAccuracy,
|
||||
FixedLabelWidth = LABEL_WIDTH,
|
||||
@ -81,7 +81,7 @@ namespace osu.Game.Screens.Edit.Setup
|
||||
Precision = 0.1f,
|
||||
}
|
||||
},
|
||||
baseVelocitySlider = new LabelledSliderBar<double>
|
||||
BaseVelocitySlider = new LabelledSliderBar<double>
|
||||
{
|
||||
Label = EditorSetupStrings.BaseVelocity,
|
||||
FixedLabelWidth = LABEL_WIDTH,
|
||||
@ -94,7 +94,7 @@ namespace osu.Game.Screens.Edit.Setup
|
||||
Precision = 0.01f,
|
||||
}
|
||||
},
|
||||
tickRateSlider = new LabelledSliderBar<double>
|
||||
TickRateSlider = new LabelledSliderBar<double>
|
||||
{
|
||||
Label = EditorSetupStrings.TickRate,
|
||||
FixedLabelWidth = LABEL_WIDTH,
|
||||
@ -120,12 +120,12 @@ namespace osu.Game.Screens.Edit.Setup
|
||||
{
|
||||
// for now, update these on commit rather than making BeatmapMetadata bindables.
|
||||
// after switching database engines we can reconsider if switching to bindables is a good direction.
|
||||
Beatmap.Difficulty.CircleSize = circleSizeSlider.Current.Value;
|
||||
Beatmap.Difficulty.DrainRate = healthDrainSlider.Current.Value;
|
||||
Beatmap.Difficulty.ApproachRate = approachRateSlider.Current.Value;
|
||||
Beatmap.Difficulty.OverallDifficulty = overallDifficultySlider.Current.Value;
|
||||
Beatmap.Difficulty.SliderMultiplier = baseVelocitySlider.Current.Value;
|
||||
Beatmap.Difficulty.SliderTickRate = tickRateSlider.Current.Value;
|
||||
Beatmap.Difficulty.CircleSize = CircleSizeSlider.Current.Value;
|
||||
Beatmap.Difficulty.DrainRate = HealthDrainSlider.Current.Value;
|
||||
Beatmap.Difficulty.ApproachRate = ApproachRateSlider.Current.Value;
|
||||
Beatmap.Difficulty.OverallDifficulty = OverallDifficultySlider.Current.Value;
|
||||
Beatmap.Difficulty.SliderMultiplier = BaseVelocitySlider.Current.Value;
|
||||
Beatmap.Difficulty.SliderTickRate = TickRateSlider.Current.Value;
|
||||
|
||||
Beatmap.UpdateAllHitObjects();
|
||||
Beatmap.SaveState();
|
||||
|
@ -26,16 +26,18 @@ namespace osu.Game.Screens.Edit.Setup
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(EditorBeatmap beatmap, OverlayColourProvider colourProvider)
|
||||
{
|
||||
var ruleset = beatmap.BeatmapInfo.Ruleset.CreateInstance();
|
||||
|
||||
var sectionsEnumerable = new List<SetupSection>
|
||||
{
|
||||
new ResourcesSection(),
|
||||
new MetadataSection(),
|
||||
new DifficultySection(),
|
||||
ruleset.CreateEditorDifficultySection() ?? new DifficultySection(),
|
||||
new ColoursSection(),
|
||||
new DesignSection(),
|
||||
};
|
||||
|
||||
var rulesetSpecificSection = beatmap.BeatmapInfo.Ruleset.CreateInstance().CreateEditorSetupSection();
|
||||
var rulesetSpecificSection = ruleset.CreateEditorSetupSection();
|
||||
if (rulesetSpecificSection != null)
|
||||
sectionsEnumerable.Add(rulesetSpecificSection);
|
||||
|
||||
|
@ -17,8 +17,8 @@ namespace osu.Game.Skinning
|
||||
Anchor = Anchor.TopRight;
|
||||
Origin = Anchor.TopRight;
|
||||
|
||||
Scale = new Vector2(0.6f);
|
||||
Margin = new MarginPadding(10);
|
||||
Scale = new Vector2(0.6f * 0.96f);
|
||||
Margin = new MarginPadding { Vertical = 9, Horizontal = 17 };
|
||||
}
|
||||
|
||||
protected sealed override OsuSpriteText CreateSpriteText() => new LegacySpriteText(LegacyFont.Score)
|
||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Skinning
|
||||
Origin = Anchor.TopRight;
|
||||
|
||||
Scale = new Vector2(0.96f);
|
||||
Margin = new MarginPadding(10);
|
||||
Margin = new MarginPadding { Horizontal = 10 };
|
||||
}
|
||||
|
||||
protected sealed override OsuSpriteText CreateSpriteText() => new LegacySpriteText(LegacyFont.Score)
|
||||
|
@ -368,7 +368,7 @@ namespace osu.Game.Skinning
|
||||
{
|
||||
songProgress.Anchor = Anchor.TopRight;
|
||||
songProgress.Origin = Anchor.CentreRight;
|
||||
songProgress.X = -accuracy.ScreenSpaceDeltaToParentSpace(accuracy.ScreenSpaceDrawQuad.Size).X - 10;
|
||||
songProgress.X = -accuracy.ScreenSpaceDeltaToParentSpace(accuracy.ScreenSpaceDrawQuad.Size).X - 18;
|
||||
songProgress.Y = container.ToLocalSpace(accuracy.ScreenSpaceDrawQuad.TopLeft).Y + (accuracy.ScreenSpaceDeltaToParentSpace(accuracy.ScreenSpaceDrawQuad.Size).Y / 2);
|
||||
}
|
||||
|
||||
@ -397,8 +397,8 @@ namespace osu.Game.Skinning
|
||||
new LegacyComboCounter(),
|
||||
new LegacyScoreCounter(),
|
||||
new LegacyAccuracyCounter(),
|
||||
new LegacyHealthDisplay(),
|
||||
new LegacySongProgress(),
|
||||
new LegacyHealthDisplay(),
|
||||
new BarHitErrorMeter(),
|
||||
new DefaultKeyCounterDisplay()
|
||||
}
|
||||
|
@ -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.720.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2023.724.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" />
|
||||
|
@ -2,13 +2,20 @@
|
||||
<PropertyGroup>
|
||||
<CodesignKey>iPhone Developer</CodesignKey>
|
||||
<NullabilityInfoContextSupport>true</NullabilityInfoContextSupport>
|
||||
<!-- Workaround for an upstream issue which Realm suffers from (https://github.com/dotnet/runtime/issues/69410). -->
|
||||
<UseInterpreter>true</UseInterpreter>
|
||||
<!-- MT7091 occurs when referencing a .framework bundle that consists of a static library.
|
||||
It only warns about not copying the library to the app bundle to save space,
|
||||
so there's nothing to be worried about. -->
|
||||
<NoWarn>$(NoWarn);MT7091</NoWarn>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
|
||||
<!-- On debug configurations, we use Mono interpreter for faster compilation. -->
|
||||
<UseInterpreter>true</UseInterpreter>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||
<!-- On release configurations, we use AOT compiler for optimal performance, along with Mono Interpreter as a fallback for libraries such as AutoMapper. -->
|
||||
<UseInterpreter>false</UseInterpreter>
|
||||
<MtouchInterpreter>-all</MtouchInterpreter>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Platform)' == 'iPhone'">
|
||||
<RuntimeIdentifier>ios-arm64</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
@ -16,6 +23,6 @@
|
||||
<RuntimeIdentifier>iossimulator-x64</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2023.720.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2023.724.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
Loading…
Reference in New Issue
Block a user