mirror of
https://github.com/osukey/osukey.git
synced 2025-07-02 00:40:09 +09:00
Merge branch 'master' into localisation-refactor-framework
This commit is contained in:
@ -1,18 +1,20 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using osu.Framework.Graphics.Sprites;
|
using System;
|
||||||
using osu.Game.Graphics;
|
using System.Linq;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModFadeIn : ManiaModHidden
|
public class ManiaModFadeIn : ManiaModPlayfieldCover
|
||||||
{
|
{
|
||||||
public override string Name => "Fade In";
|
public override string Name => "Fade In";
|
||||||
public override string Acronym => "FI";
|
public override string Acronym => "FI";
|
||||||
public override IconUsage? Icon => OsuIcon.ModHidden;
|
|
||||||
public override string Description => @"Keys appear out of nowhere!";
|
public override string Description => @"Keys appear out of nowhere!";
|
||||||
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ManiaModHidden)).ToArray();
|
||||||
|
|
||||||
protected override CoverExpandDirection ExpandDirection => CoverExpandDirection.AlongScroll;
|
protected override CoverExpandDirection ExpandDirection => CoverExpandDirection.AlongScroll;
|
||||||
}
|
}
|
||||||
|
@ -3,43 +3,17 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
using osu.Game.Rulesets.Mods;
|
|
||||||
using osu.Game.Rulesets.UI;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModHidden : ModHidden, IApplicableToDrawableRuleset<ManiaHitObject>
|
public class ManiaModHidden : ManiaModPlayfieldCover
|
||||||
{
|
{
|
||||||
public override string Description => @"Keys fade out before you hit them!";
|
public override string Description => @"Keys fade out before you hit them!";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight<ManiaHitObject>) };
|
|
||||||
|
|
||||||
/// <summary>
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ManiaModFadeIn)).ToArray();
|
||||||
/// The direction in which the cover should expand.
|
|
||||||
/// </summary>
|
|
||||||
protected virtual CoverExpandDirection ExpandDirection => CoverExpandDirection.AgainstScroll;
|
|
||||||
|
|
||||||
public virtual void ApplyToDrawableRuleset(DrawableRuleset<ManiaHitObject> drawableRuleset)
|
protected override CoverExpandDirection ExpandDirection => CoverExpandDirection.AgainstScroll;
|
||||||
{
|
|
||||||
ManiaPlayfield maniaPlayfield = (ManiaPlayfield)drawableRuleset.Playfield;
|
|
||||||
|
|
||||||
foreach (Column column in maniaPlayfield.Stages.SelectMany(stage => stage.Columns))
|
|
||||||
{
|
|
||||||
HitObjectContainer hoc = column.HitObjectArea.HitObjectContainer;
|
|
||||||
Container hocParent = (Container)hoc.Parent;
|
|
||||||
|
|
||||||
hocParent.Remove(hoc);
|
|
||||||
hocParent.Add(new PlayfieldCoveringWrapper(hoc).With(c =>
|
|
||||||
{
|
|
||||||
c.RelativeSizeAxes = Axes.Both;
|
|
||||||
c.Direction = ExpandDirection;
|
|
||||||
c.Coverage = 0.5f;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
43
osu.Game.Rulesets.Mania/Mods/ManiaModPlayfieldCover.cs
Normal file
43
osu.Game.Rulesets.Mania/Mods/ManiaModPlayfieldCover.cs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// 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.Linq;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
|
{
|
||||||
|
public abstract class ManiaModPlayfieldCover : ModHidden, IApplicableToDrawableRuleset<ManiaHitObject>
|
||||||
|
{
|
||||||
|
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight<ManiaHitObject>) };
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The direction in which the cover should expand.
|
||||||
|
/// </summary>
|
||||||
|
protected abstract CoverExpandDirection ExpandDirection { get; }
|
||||||
|
|
||||||
|
public virtual void ApplyToDrawableRuleset(DrawableRuleset<ManiaHitObject> drawableRuleset)
|
||||||
|
{
|
||||||
|
ManiaPlayfield maniaPlayfield = (ManiaPlayfield)drawableRuleset.Playfield;
|
||||||
|
|
||||||
|
foreach (Column column in maniaPlayfield.Stages.SelectMany(stage => stage.Columns))
|
||||||
|
{
|
||||||
|
HitObjectContainer hoc = column.HitObjectArea.HitObjectContainer;
|
||||||
|
Container hocParent = (Container)hoc.Parent;
|
||||||
|
|
||||||
|
hocParent.Remove(hoc);
|
||||||
|
hocParent.Add(new PlayfieldCoveringWrapper(hoc).With(c =>
|
||||||
|
{
|
||||||
|
c.RelativeSizeAxes = Axes.Both;
|
||||||
|
c.Direction = ExpandDirection;
|
||||||
|
c.Coverage = 0.5f;
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public override ModType Type => ModType.Automation;
|
public override ModType Type => ModType.Automation;
|
||||||
public override string Description => @"Automatic cursor movement - just follow the rhythm.";
|
public override string Description => @"Automatic cursor movement - just follow the rhythm.";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail), typeof(ModAutoplay) };
|
public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModFailCondition), typeof(ModNoFail), typeof(ModAutoplay) };
|
||||||
|
|
||||||
public bool PerformFail() => false;
|
public bool PerformFail() => false;
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ using osu.Game.Rulesets.Osu.Skinning.Default;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
{
|
{
|
||||||
internal class OsuModTraceable : ModWithVisibilityAdjustment
|
public class OsuModTraceable : ModWithVisibilityAdjustment
|
||||||
{
|
{
|
||||||
public override string Name => "Traceable";
|
public override string Name => "Traceable";
|
||||||
public override string Acronym => "TC";
|
public override string Acronym => "TC";
|
||||||
|
@ -852,6 +852,21 @@ namespace osu.Game.Tests.Beatmaps.IO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static async Task<BeatmapSetInfo> LoadQuickOszIntoOsu(OsuGameBase osu)
|
||||||
|
{
|
||||||
|
var temp = TestResources.GetQuickTestBeatmapForImport();
|
||||||
|
|
||||||
|
var manager = osu.Dependencies.Get<BeatmapManager>();
|
||||||
|
|
||||||
|
var importedSet = await manager.Import(new ImportTask(temp));
|
||||||
|
|
||||||
|
ensureLoaded(osu);
|
||||||
|
|
||||||
|
waitForOrAssert(() => !File.Exists(temp), "Temporary file still exists after standard import", 5000);
|
||||||
|
|
||||||
|
return manager.GetAllUsableBeatmapSets().Find(beatmapSet => beatmapSet.ID == importedSet.ID);
|
||||||
|
}
|
||||||
|
|
||||||
public static async Task<BeatmapSetInfo> LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false)
|
public static async Task<BeatmapSetInfo> LoadOszIntoOsu(OsuGameBase osu, string path = null, bool virtualTrack = false)
|
||||||
{
|
{
|
||||||
var temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack);
|
var temp = path ?? TestResources.GetTestBeatmapForImport(virtualTrack);
|
||||||
|
@ -13,6 +13,7 @@ using osu.Framework.Graphics.Audio;
|
|||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Framework.IO.Stores;
|
using osu.Framework.IO.Stores;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.IO;
|
using osu.Game.IO;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
@ -90,6 +91,7 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
public void TestSamplePlaybackWithRateMods(Type expectedMod, double expectedRate)
|
public void TestSamplePlaybackWithRateMods(Type expectedMod, double expectedRate)
|
||||||
{
|
{
|
||||||
GameplayClockContainer gameplayContainer = null;
|
GameplayClockContainer gameplayContainer = null;
|
||||||
|
StoryboardSampleInfo sampleInfo = null;
|
||||||
TestDrawableStoryboardSample sample = null;
|
TestDrawableStoryboardSample sample = null;
|
||||||
|
|
||||||
Mod testedMod = Activator.CreateInstance(expectedMod) as Mod;
|
Mod testedMod = Activator.CreateInstance(expectedMod) as Mod;
|
||||||
@ -101,7 +103,7 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ModTimeRamp m:
|
case ModTimeRamp m:
|
||||||
m.InitialRate.Value = m.FinalRate.Value = expectedRate;
|
m.FinalRate.Value = m.InitialRate.Value = expectedRate;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,7 +119,7 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
Child = beatmapSkinSourceContainer
|
Child = beatmapSkinSourceContainer
|
||||||
});
|
});
|
||||||
|
|
||||||
beatmapSkinSourceContainer.Add(sample = new TestDrawableStoryboardSample(new StoryboardSampleInfo("test-sample", 1, 1))
|
beatmapSkinSourceContainer.Add(sample = new TestDrawableStoryboardSample(sampleInfo = new StoryboardSampleInfo("test-sample", 1, 1))
|
||||||
{
|
{
|
||||||
Clock = gameplayContainer.GameplayClock
|
Clock = gameplayContainer.GameplayClock
|
||||||
});
|
});
|
||||||
@ -125,7 +127,10 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
|
|
||||||
AddStep("start", () => gameplayContainer.Start());
|
AddStep("start", () => gameplayContainer.Start());
|
||||||
|
|
||||||
AddAssert("sample playback rate matches mod rates", () => sample.ChildrenOfType<DrawableSample>().First().AggregateFrequency.Value == expectedRate);
|
AddAssert("sample playback rate matches mod rates", () =>
|
||||||
|
testedMod != null && Precision.AlmostEquals(
|
||||||
|
sample.ChildrenOfType<DrawableSample>().First().AggregateFrequency.Value,
|
||||||
|
((IApplicableToRate)testedMod).ApplyToRate(sampleInfo.StartTime)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestSkin : LegacySkin
|
private class TestSkin : LegacySkin
|
||||||
|
@ -52,7 +52,7 @@ namespace osu.Game.Tests.Online
|
|||||||
{
|
{
|
||||||
beatmaps.AllowImport = new TaskCompletionSource<bool>();
|
beatmaps.AllowImport = new TaskCompletionSource<bool>();
|
||||||
|
|
||||||
testBeatmapFile = TestResources.GetTestBeatmapForImport();
|
testBeatmapFile = TestResources.GetQuickTestBeatmapForImport();
|
||||||
|
|
||||||
testBeatmapInfo = getTestBeatmapInfo(testBeatmapFile);
|
testBeatmapInfo = getTestBeatmapInfo(testBeatmapFile);
|
||||||
testBeatmapSet = testBeatmapInfo.BeatmapSet;
|
testBeatmapSet = testBeatmapInfo.BeatmapSet;
|
||||||
|
Binary file not shown.
@ -15,6 +15,28 @@ namespace osu.Game.Tests.Resources
|
|||||||
|
|
||||||
public static Stream GetTestBeatmapStream(bool virtualTrack = false) => OpenResource($"Archives/241526 Soleily - Renatus{(virtualTrack ? "_virtual" : "")}.osz");
|
public static Stream GetTestBeatmapStream(bool virtualTrack = false) => OpenResource($"Archives/241526 Soleily - Renatus{(virtualTrack ? "_virtual" : "")}.osz");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieve a path to a copy of a shortened (~10 second) beatmap archive with a virtual track.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This is intended for use in tests which need to run to completion as soon as possible and don't need to test a full length beatmap.</remarks>
|
||||||
|
/// <returns>A path to a copy of a beatmap archive (osz). Should be deleted after use.</returns>
|
||||||
|
public static string GetQuickTestBeatmapForImport()
|
||||||
|
{
|
||||||
|
var tempPath = Path.GetTempFileName() + ".osz";
|
||||||
|
using (var stream = OpenResource("Archives/241526 Soleily - Renatus_virtual_quick.osz"))
|
||||||
|
using (var newFile = File.Create(tempPath))
|
||||||
|
stream.CopyTo(newFile);
|
||||||
|
|
||||||
|
Assert.IsTrue(File.Exists(tempPath));
|
||||||
|
return tempPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieve a path to a copy of a full-fledged beatmap archive.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="virtualTrack">Whether the audio track should be virtual.</param>
|
||||||
|
/// <returns>A path to a copy of a beatmap archive (osz). Should be deleted after use.</returns>
|
||||||
public static string GetTestBeatmapForImport(bool virtualTrack = false)
|
public static string GetTestBeatmapForImport(bool virtualTrack = false)
|
||||||
{
|
{
|
||||||
var tempPath = Path.GetTempFileName() + ".osz";
|
var tempPath = Path.GetTempFileName() + ".osz";
|
||||||
|
@ -51,7 +51,7 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default));
|
Dependencies.Cache(manager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default));
|
||||||
Dependencies.Cache(new OsuConfigManager(LocalStorage));
|
Dependencies.Cache(new OsuConfigManager(LocalStorage));
|
||||||
|
|
||||||
manager.Import(TestResources.GetTestBeatmapForImport()).Wait();
|
manager.Import(TestResources.GetQuickTestBeatmapForImport()).Wait();
|
||||||
|
|
||||||
Beatmap.SetDefault();
|
Beatmap.SetDefault();
|
||||||
}
|
}
|
||||||
|
@ -38,13 +38,13 @@ namespace osu.Game.Tests.Visual.Collections
|
|||||||
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
|
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
|
||||||
Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, Audio, host, Beatmap.Default));
|
Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, Audio, host, Beatmap.Default));
|
||||||
|
|
||||||
beatmapManager.Import(TestResources.GetTestBeatmapForImport()).Wait();
|
beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).Wait();
|
||||||
|
|
||||||
base.Content.AddRange(new Drawable[]
|
base.Content.AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
manager = new CollectionManager(LocalStorage),
|
manager = new CollectionManager(LocalStorage),
|
||||||
Content,
|
Content,
|
||||||
dialogOverlay = new DialogOverlay()
|
dialogOverlay = new DialogOverlay(),
|
||||||
});
|
});
|
||||||
|
|
||||||
Dependencies.Cache(manager);
|
Dependencies.Cache(manager);
|
||||||
@ -134,6 +134,27 @@ namespace osu.Game.Tests.Visual.Collections
|
|||||||
assertCollectionName(0, "2");
|
assertCollectionName(0, "2");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCollectionNameCollisions()
|
||||||
|
{
|
||||||
|
AddStep("add dropdown", () =>
|
||||||
|
{
|
||||||
|
Add(new CollectionFilterDropdown
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
Width = 0.4f,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
AddStep("add two collections with same name", () => manager.Collections.AddRange(new[]
|
||||||
|
{
|
||||||
|
new BeatmapCollection { Name = { Value = "1" } },
|
||||||
|
new BeatmapCollection { Name = { Value = "1" }, Beatmaps = { beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps[0] } },
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestRemoveCollectionViaButton()
|
public void TestRemoveCollectionViaButton()
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -8,21 +9,17 @@ using osu.Framework.Platform;
|
|||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Storyboards;
|
||||||
|
using osu.Game.Tests.Beatmaps;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.Gameplay
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
{
|
{
|
||||||
[HeadlessTest] // we alter unsafe properties on the game host to test inactive window state.
|
[HeadlessTest] // we alter unsafe properties on the game host to test inactive window state.
|
||||||
public class TestScenePauseWhenInactive : OsuPlayerTestScene
|
public class TestScenePauseWhenInactive : OsuPlayerTestScene
|
||||||
{
|
{
|
||||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
|
|
||||||
{
|
|
||||||
var beatmap = (Beatmap)base.CreateBeatmap(ruleset);
|
|
||||||
|
|
||||||
beatmap.HitObjects.RemoveAll(h => h.StartTime < 30000);
|
|
||||||
|
|
||||||
return beatmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private GameHost host { get; set; }
|
private GameHost host { get; set; }
|
||||||
|
|
||||||
@ -33,10 +30,57 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
|
|
||||||
AddStep("resume player", () => Player.GameplayClockContainer.Start());
|
AddStep("resume player", () => Player.GameplayClockContainer.Start());
|
||||||
AddAssert("ensure not paused", () => !Player.GameplayClockContainer.IsPaused.Value);
|
AddAssert("ensure not paused", () => !Player.GameplayClockContainer.IsPaused.Value);
|
||||||
|
|
||||||
|
AddStep("progress time to gameplay", () => Player.GameplayClockContainer.Seek(Player.DrawableRuleset.GameplayStartTime));
|
||||||
|
AddUntilStep("wait for pause", () => Player.GameplayClockContainer.IsPaused.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tests that if a pause from focus lose is performed while in pause cooldown,
|
||||||
|
/// the player will still pause after the cooldown is finished.
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestPauseWhileInCooldown()
|
||||||
|
{
|
||||||
|
AddStep("move cursor outside", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopLeft - new Vector2(10)));
|
||||||
|
|
||||||
|
AddStep("resume player", () => Player.GameplayClockContainer.Start());
|
||||||
|
AddStep("skip to gameplay", () => Player.GameplayClockContainer.Seek(Player.DrawableRuleset.GameplayStartTime));
|
||||||
|
|
||||||
|
AddStep("set inactive", () => ((Bindable<bool>)host.IsActive).Value = false);
|
||||||
|
AddUntilStep("wait for pause", () => Player.GameplayClockContainer.IsPaused.Value);
|
||||||
|
|
||||||
|
AddStep("set active", () => ((Bindable<bool>)host.IsActive).Value = true);
|
||||||
|
|
||||||
|
AddStep("resume player", () => Player.Resume());
|
||||||
|
AddAssert("unpaused", () => !Player.GameplayClockContainer.IsPaused.Value);
|
||||||
|
|
||||||
|
bool pauseCooldownActive = false;
|
||||||
|
|
||||||
|
AddStep("set inactive again", () =>
|
||||||
|
{
|
||||||
|
pauseCooldownActive = Player.PauseCooldownActive;
|
||||||
|
((Bindable<bool>)host.IsActive).Value = false;
|
||||||
|
});
|
||||||
|
AddAssert("pause cooldown active", () => pauseCooldownActive);
|
||||||
AddUntilStep("wait for pause", () => Player.GameplayClockContainer.IsPaused.Value);
|
AddUntilStep("wait for pause", () => Player.GameplayClockContainer.IsPaused.Value);
|
||||||
AddAssert("time of pause is after gameplay start time", () => Player.GameplayClockContainer.GameplayClock.CurrentTime >= Player.DrawableRuleset.GameplayStartTime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override TestPlayer CreatePlayer(Ruleset ruleset) => new TestPlayer(true, true, true);
|
protected override TestPlayer CreatePlayer(Ruleset ruleset) => new TestPlayer(true, true, true);
|
||||||
|
|
||||||
|
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
|
||||||
|
{
|
||||||
|
return new Beatmap
|
||||||
|
{
|
||||||
|
HitObjects = new List<HitObject>
|
||||||
|
{
|
||||||
|
new HitCircle { StartTime = 30000 },
|
||||||
|
new HitCircle { StartTime = 35000 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
|
||||||
|
=> new TestWorkingBeatmap(beatmap, storyboard, Audio);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,17 +7,23 @@ using System.Linq;
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
|
using osu.Framework.Extensions.TypeExtensions;
|
||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Overlays.Mods;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Catch;
|
using osu.Game.Rulesets.Catch;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Rulesets.Taiko;
|
using osu.Game.Rulesets.Taiko;
|
||||||
using osu.Game.Rulesets.Taiko.Mods;
|
using osu.Game.Rulesets.Taiko.Mods;
|
||||||
|
using osu.Game.Screens.OnlinePlay;
|
||||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||||
using osu.Game.Screens.Select;
|
using osu.Game.Screens.Select;
|
||||||
|
|
||||||
@ -137,8 +143,30 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
AddAssert("mods not changed", () => SelectedMods.Value.Single() is TaikoModDoubleTime);
|
AddAssert("mods not changed", () => SelectedMods.Value.Single() is TaikoModDoubleTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestCase(typeof(OsuModHidden), typeof(OsuModHidden))] // Same mod.
|
||||||
|
[TestCase(typeof(OsuModHidden), typeof(OsuModTraceable))] // Incompatible.
|
||||||
|
public void TestAllowedModDeselectedWhenRequired(Type allowedMod, Type requiredMod)
|
||||||
|
{
|
||||||
|
AddStep($"select {allowedMod.ReadableName()} as allowed", () => songSelect.FreeMods.Value = new[] { (Mod)Activator.CreateInstance(allowedMod) });
|
||||||
|
AddStep($"select {requiredMod.ReadableName()} as required", () => songSelect.Mods.Value = new[] { (Mod)Activator.CreateInstance(requiredMod) });
|
||||||
|
|
||||||
|
AddAssert("freemods empty", () => songSelect.FreeMods.Value.Count == 0);
|
||||||
|
assertHasFreeModButton(allowedMod, false);
|
||||||
|
assertHasFreeModButton(requiredMod, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertHasFreeModButton(Type type, bool hasButton = true)
|
||||||
|
{
|
||||||
|
AddAssert($"{type.ReadableName()} {(hasButton ? "displayed" : "not displayed")} in freemod overlay",
|
||||||
|
() => songSelect.ChildrenOfType<FreeModSelectOverlay>().Single().ChildrenOfType<ModButton>().All(b => b.Mod.GetType() != type));
|
||||||
|
}
|
||||||
|
|
||||||
private class TestMultiplayerMatchSongSelect : MultiplayerMatchSongSelect
|
private class TestMultiplayerMatchSongSelect : MultiplayerMatchSongSelect
|
||||||
{
|
{
|
||||||
|
public new Bindable<IReadOnlyList<Mod>> Mods => base.Mods;
|
||||||
|
|
||||||
|
public new Bindable<IReadOnlyList<Mod>> FreeMods => base.FreeMods;
|
||||||
|
|
||||||
public new BeatmapCarousel Carousel => base.Carousel;
|
public new BeatmapCarousel Carousel => base.Carousel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Multiplayer
|
|||||||
{
|
{
|
||||||
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
|
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
|
||||||
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default));
|
Dependencies.Cache(beatmaps = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, audio, host, Beatmap.Default));
|
||||||
beatmaps.Import(TestResources.GetTestBeatmapForImport(true)).Wait();
|
beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()).Wait();
|
||||||
|
|
||||||
Add(beatmapTracker = new OnlinePlayBeatmapAvailablilityTracker
|
Add(beatmapTracker = new OnlinePlayBeatmapAvailablilityTracker
|
||||||
{
|
{
|
||||||
|
@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
|
|
||||||
PushAndConfirm(() => new TestSongSelect());
|
PushAndConfirm(() => new TestSongSelect());
|
||||||
|
|
||||||
AddStep("import beatmap", () => ImportBeatmapTest.LoadOszIntoOsu(Game, virtualTrack: true).Wait());
|
AddStep("import beatmap", () => ImportBeatmapTest.LoadQuickOszIntoOsu(Game).Wait());
|
||||||
|
|
||||||
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
|
AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault);
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
|
|
||||||
AddStep("press enter", () => InputManager.Key(Key.Enter));
|
AddStep("press enter", () => InputManager.Key(Key.Enter));
|
||||||
AddUntilStep("wait for player", () => (player = Game.ScreenStack.CurrentScreen as Player) != null);
|
AddUntilStep("wait for player", () => (player = Game.ScreenStack.CurrentScreen as Player) != null);
|
||||||
AddStep("seek to end", () => beatmap().Track.Seek(beatmap().Track.Length));
|
AddStep("seek to end", () => player.ChildrenOfType<GameplayClockContainer>().First().Seek(beatmap().Track.Length));
|
||||||
AddUntilStep("wait for pass", () => (results = Game.ScreenStack.CurrentScreen as ResultsScreen) != null && results.IsLoaded);
|
AddUntilStep("wait for pass", () => (results = Game.ScreenStack.CurrentScreen as ResultsScreen) != null && results.IsLoaded);
|
||||||
AddStep("attempt to retry", () => results.ChildrenOfType<HotkeyRetryOverlay>().First().Action());
|
AddStep("attempt to retry", () => results.ChildrenOfType<HotkeyRetryOverlay>().First().Action());
|
||||||
AddUntilStep("wait for player", () => Game.ScreenStack.CurrentScreen != player && Game.ScreenStack.CurrentScreen is Player);
|
AddUntilStep("wait for player", () => Game.ScreenStack.CurrentScreen != player && Game.ScreenStack.CurrentScreen is Player);
|
||||||
@ -214,6 +214,21 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
AddAssert("Options overlay still visible", () => songSelect.BeatmapOptionsOverlay.State.Value == Visibility.Visible);
|
AddAssert("Options overlay still visible", () => songSelect.BeatmapOptionsOverlay.State.Value == Visibility.Visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSettingsViaHotkeyFromMainMenu()
|
||||||
|
{
|
||||||
|
AddAssert("toolbar not displayed", () => Game.Toolbar.State.Value == Visibility.Hidden);
|
||||||
|
|
||||||
|
AddStep("press settings hotkey", () =>
|
||||||
|
{
|
||||||
|
InputManager.PressKey(Key.ControlLeft);
|
||||||
|
InputManager.Key(Key.O);
|
||||||
|
InputManager.ReleaseKey(Key.ControlLeft);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("settings displayed", () => Game.Settings.State.Value == Visibility.Visible);
|
||||||
|
}
|
||||||
|
|
||||||
private void pushEscape() =>
|
private void pushEscape() =>
|
||||||
AddStep("Press escape", () => InputManager.Key(Key.Escape));
|
AddStep("Press escape", () => InputManager.Key(Key.Escape));
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Online
|
|||||||
ensureSoleilyRemoved();
|
ensureSoleilyRemoved();
|
||||||
createButtonWithBeatmap(createSoleily());
|
createButtonWithBeatmap(createSoleily());
|
||||||
AddAssert("button state not downloaded", () => downloadButton.DownloadState == DownloadState.NotDownloaded);
|
AddAssert("button state not downloaded", () => downloadButton.DownloadState == DownloadState.NotDownloaded);
|
||||||
AddStep("import soleily", () => beatmaps.Import(TestResources.GetTestBeatmapForImport()));
|
AddStep("import soleily", () => beatmaps.Import(TestResources.GetQuickTestBeatmapForImport()));
|
||||||
AddUntilStep("wait for beatmap import", () => beatmaps.GetAllUsableBeatmapSets().Any(b => b.OnlineBeatmapSetID == 241526));
|
AddUntilStep("wait for beatmap import", () => beatmaps.GetAllUsableBeatmapSets().Any(b => b.OnlineBeatmapSetID == 241526));
|
||||||
createButtonWithBeatmap(createSoleily());
|
createButtonWithBeatmap(createSoleily());
|
||||||
AddAssert("button state downloaded", () => downloadButton.DownloadState == DownloadState.LocallyAvailable);
|
AddAssert("button state downloaded", () => downloadButton.DownloadState == DownloadState.LocallyAvailable);
|
||||||
|
@ -5,7 +5,6 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading.Tasks;
|
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
@ -76,7 +75,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
AddStep("bind user score info handler", () =>
|
AddStep("bind user score info handler", () =>
|
||||||
{
|
{
|
||||||
userScore = new TestScoreInfo(new OsuRuleset().RulesetInfo) { OnlineScoreID = currentScoreId++ };
|
userScore = new TestScoreInfo(new OsuRuleset().RulesetInfo) { OnlineScoreID = currentScoreId++ };
|
||||||
bindHandler(3000, userScore);
|
bindHandler(true, userScore);
|
||||||
});
|
});
|
||||||
|
|
||||||
createResults(() => userScore);
|
createResults(() => userScore);
|
||||||
@ -89,7 +88,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestShowNullUserScoreWithDelay()
|
public void TestShowNullUserScoreWithDelay()
|
||||||
{
|
{
|
||||||
AddStep("bind delayed handler", () => bindHandler(3000));
|
AddStep("bind delayed handler", () => bindHandler(true));
|
||||||
|
|
||||||
createResults();
|
createResults();
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
@ -103,7 +102,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
createResults();
|
createResults();
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
AddStep("bind delayed handler", () => bindHandler(3000));
|
AddStep("bind delayed handler", () => bindHandler(true));
|
||||||
|
|
||||||
for (int i = 0; i < 2; i++)
|
for (int i = 0; i < 2; i++)
|
||||||
{
|
{
|
||||||
@ -134,7 +133,7 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
createResults(() => userScore);
|
createResults(() => userScore);
|
||||||
waitForDisplay();
|
waitForDisplay();
|
||||||
|
|
||||||
AddStep("bind delayed handler", () => bindHandler(3000));
|
AddStep("bind delayed handler", () => bindHandler(true));
|
||||||
|
|
||||||
for (int i = 0; i < 2; i++)
|
for (int i = 0; i < 2; i++)
|
||||||
{
|
{
|
||||||
@ -169,70 +168,47 @@ namespace osu.Game.Tests.Visual.Playlists
|
|||||||
AddWaitStep("wait for display", 5);
|
AddWaitStep("wait for display", 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void bindHandler(double delay = 0, ScoreInfo userScore = null, bool failRequests = false) => ((DummyAPIAccess)API).HandleRequest = request =>
|
private void bindHandler(bool delayed = false, ScoreInfo userScore = null, bool failRequests = false) => ((DummyAPIAccess)API).HandleRequest = request =>
|
||||||
{
|
{
|
||||||
requestComplete = false;
|
requestComplete = false;
|
||||||
|
|
||||||
if (failRequests)
|
double delay = delayed ? 3000 : 0;
|
||||||
{
|
|
||||||
triggerFail(request, delay);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (request)
|
Scheduler.AddDelayed(() =>
|
||||||
{
|
{
|
||||||
case ShowPlaylistUserScoreRequest s:
|
if (failRequests)
|
||||||
if (userScore == null)
|
{
|
||||||
triggerFail(s, delay);
|
triggerFail(request);
|
||||||
else
|
return;
|
||||||
triggerSuccess(s, createUserResponse(userScore), delay);
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
case IndexPlaylistScoresRequest i:
|
switch (request)
|
||||||
triggerSuccess(i, createIndexResponse(i), delay);
|
{
|
||||||
break;
|
case ShowPlaylistUserScoreRequest s:
|
||||||
}
|
if (userScore == null)
|
||||||
|
triggerFail(s);
|
||||||
|
else
|
||||||
|
triggerSuccess(s, createUserResponse(userScore));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IndexPlaylistScoresRequest i:
|
||||||
|
triggerSuccess(i, createIndexResponse(i));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}, delay);
|
||||||
};
|
};
|
||||||
|
|
||||||
private void triggerSuccess<T>(APIRequest<T> req, T result, double delay)
|
private void triggerSuccess<T>(APIRequest<T> req, T result)
|
||||||
where T : class
|
where T : class
|
||||||
{
|
{
|
||||||
if (delay == 0)
|
requestComplete = true;
|
||||||
success();
|
req.TriggerSuccess(result);
|
||||||
else
|
|
||||||
{
|
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await Task.Delay(TimeSpan.FromMilliseconds(delay));
|
|
||||||
Schedule(success);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void success()
|
|
||||||
{
|
|
||||||
requestComplete = true;
|
|
||||||
req.TriggerSuccess(result);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void triggerFail(APIRequest req, double delay)
|
private void triggerFail(APIRequest req)
|
||||||
{
|
{
|
||||||
if (delay == 0)
|
requestComplete = true;
|
||||||
fail();
|
req.TriggerFailure(new WebException("Failed."));
|
||||||
else
|
|
||||||
{
|
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await Task.Delay(TimeSpan.FromMilliseconds(delay));
|
|
||||||
Schedule(fail);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void fail()
|
|
||||||
{
|
|
||||||
requestComplete = true;
|
|
||||||
req.TriggerFailure(new WebException("Failed."));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private MultiplayerScore createUserResponse([NotNull] ScoreInfo userScore)
|
private MultiplayerScore createUserResponse([NotNull] ScoreInfo userScore)
|
||||||
|
@ -38,7 +38,7 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
|
Dependencies.Cache(rulesets = new RulesetStore(ContextFactory));
|
||||||
Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, Audio, host, Beatmap.Default));
|
Dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesets, null, Audio, host, Beatmap.Default));
|
||||||
|
|
||||||
beatmapManager.Import(TestResources.GetTestBeatmapForImport()).Wait();
|
beatmapManager.Import(TestResources.GetQuickTestBeatmapForImport()).Wait();
|
||||||
|
|
||||||
base.Content.AddRange(new Drawable[]
|
base.Content.AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
|
@ -84,7 +84,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, dependencies.Get<AudioManager>(), dependencies.Get<GameHost>(), Beatmap.Default));
|
dependencies.Cache(beatmapManager = new BeatmapManager(LocalStorage, ContextFactory, rulesetStore, null, dependencies.Get<AudioManager>(), dependencies.Get<GameHost>(), Beatmap.Default));
|
||||||
dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, null, ContextFactory));
|
dependencies.Cache(scoreManager = new ScoreManager(rulesetStore, () => beatmapManager, LocalStorage, null, ContextFactory));
|
||||||
|
|
||||||
beatmap = beatmapManager.Import(new ImportTask(TestResources.GetTestBeatmapForImport())).Result.Beatmaps[0];
|
beatmap = beatmapManager.Import(new ImportTask(TestResources.GetQuickTestBeatmapForImport())).Result.Beatmaps[0];
|
||||||
|
|
||||||
for (int i = 0; i < 50; i++)
|
for (int i = 0; i < 50; i++)
|
||||||
{
|
{
|
||||||
|
@ -36,7 +36,19 @@ namespace osu.Game.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bool Equals(CollectionFilterMenuItem other)
|
public bool Equals(CollectionFilterMenuItem other)
|
||||||
=> other != null && CollectionName.Value == other.CollectionName.Value;
|
{
|
||||||
|
if (other == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// collections may have the same name, so compare first on reference equality.
|
||||||
|
// this relies on the assumption that only one instance of the BeatmapCollection exists game-wide, managed by CollectionManager.
|
||||||
|
if (Collection != null)
|
||||||
|
return Collection == other.Collection;
|
||||||
|
|
||||||
|
// fallback to name-based comparison.
|
||||||
|
// this is required for special dropdown items which don't have a collection (all beatmaps / manage collections items below).
|
||||||
|
return CollectionName.Value == other.CollectionName.Value;
|
||||||
|
}
|
||||||
|
|
||||||
public override int GetHashCode() => CollectionName.Value.GetHashCode();
|
public override int GetHashCode() => CollectionName.Value.GetHashCode();
|
||||||
}
|
}
|
||||||
|
@ -138,10 +138,10 @@ namespace osu.Game.Collections
|
|||||||
|
|
||||||
PostNotification?.Invoke(notification);
|
PostNotification?.Invoke(notification);
|
||||||
|
|
||||||
var collection = readCollections(stream, notification);
|
var collections = readCollections(stream, notification);
|
||||||
await importCollections(collection);
|
await importCollections(collections);
|
||||||
|
|
||||||
notification.CompletionText = $"Imported {collection.Count} collections";
|
notification.CompletionText = $"Imported {collections.Count} collections";
|
||||||
notification.State = ProgressNotificationState.Completed;
|
notification.State = ProgressNotificationState.Completed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,7 +155,7 @@ namespace osu.Game.Collections
|
|||||||
{
|
{
|
||||||
foreach (var newCol in newCollections)
|
foreach (var newCol in newCollections)
|
||||||
{
|
{
|
||||||
var existing = Collections.FirstOrDefault(c => c.Name == newCol.Name);
|
var existing = Collections.FirstOrDefault(c => c.Name.Value == newCol.Name.Value);
|
||||||
if (existing == null)
|
if (existing == null)
|
||||||
Collections.Add(existing = new BeatmapCollection { Name = { Value = newCol.Name.Value } });
|
Collections.Add(existing = new BeatmapCollection { Name = { Value = newCol.Name.Value } });
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ using System.Net.Sockets;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
using osu.Framework;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Extensions.ExceptionExtensions;
|
using osu.Framework.Extensions.ExceptionExtensions;
|
||||||
using osu.Framework.Extensions.ObjectExtensions;
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
@ -246,7 +247,14 @@ namespace osu.Game.Online.API
|
|||||||
this.password = password;
|
this.password = password;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IHubClientConnector GetHubConnector(string clientName, string endpoint) => new HubClientConnector(clientName, endpoint, this, versionHash);
|
public IHubClientConnector GetHubConnector(string clientName, string endpoint)
|
||||||
|
{
|
||||||
|
// disabled until the underlying runtime issue is resolved, see https://github.com/mono/mono/issues/20805.
|
||||||
|
if (RuntimeInfo.OS == RuntimeInfo.Platform.iOS)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
return new HubClientConnector(clientName, endpoint, this, versionHash);
|
||||||
|
}
|
||||||
|
|
||||||
public RegistrationRequest.RegistrationRequestErrors CreateAccount(string email, string username, string password)
|
public RegistrationRequest.RegistrationRequestErrors CreateAccount(string email, string username, string password)
|
||||||
{
|
{
|
||||||
@ -373,7 +381,13 @@ namespace osu.Game.Online.API
|
|||||||
|
|
||||||
public void Queue(APIRequest request)
|
public void Queue(APIRequest request)
|
||||||
{
|
{
|
||||||
lock (queue) queue.Enqueue(request);
|
lock (queue)
|
||||||
|
{
|
||||||
|
if (state.Value == APIState.Offline)
|
||||||
|
return;
|
||||||
|
|
||||||
|
queue.Enqueue(request);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void flushQueue(bool failOldRequests = true)
|
private void flushQueue(bool failOldRequests = true)
|
||||||
@ -394,8 +408,6 @@ namespace osu.Game.Online.API
|
|||||||
|
|
||||||
public void Logout()
|
public void Logout()
|
||||||
{
|
{
|
||||||
flushQueue();
|
|
||||||
|
|
||||||
password = null;
|
password = null;
|
||||||
authentication.Clear();
|
authentication.Clear();
|
||||||
|
|
||||||
@ -407,6 +419,7 @@ namespace osu.Game.Online.API
|
|||||||
});
|
});
|
||||||
|
|
||||||
state.Value = APIState.Offline;
|
state.Value = APIState.Offline;
|
||||||
|
flushQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static User createGuestUser() => new GuestUser();
|
private static User createGuestUser() => new GuestUser();
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.IO.Network;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
|
||||||
namespace osu.Game.Online.API.Requests
|
namespace osu.Game.Online.API.Requests
|
||||||
@ -15,6 +16,13 @@ namespace osu.Game.Online.API.Requests
|
|||||||
this.noVideo = noVideo;
|
this.noVideo = noVideo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override WebRequest CreateWebRequest()
|
||||||
|
{
|
||||||
|
var req = base.CreateWebRequest();
|
||||||
|
req.Timeout = 60000;
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
protected override string Target => $@"beatmapsets/{Model.OnlineBeatmapSetID}/download{(noVideo ? "?noVideo=1" : "")}";
|
protected override string Target => $@"beatmapsets/{Model.OnlineBeatmapSetID}/download{(noVideo ? "?noVideo=1" : "")}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,13 +23,15 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
public FillFlowContainer<ModButtonEmpty> ButtonsContainer { get; }
|
public FillFlowContainer<ModButtonEmpty> ButtonsContainer { get; }
|
||||||
|
|
||||||
|
protected IReadOnlyList<ModButton> Buttons { get; private set; } = Array.Empty<ModButton>();
|
||||||
|
|
||||||
public Action<Mod> Action;
|
public Action<Mod> Action;
|
||||||
|
|
||||||
public Key[] ToggleKeys;
|
public Key[] ToggleKeys;
|
||||||
|
|
||||||
public readonly ModType ModType;
|
public readonly ModType ModType;
|
||||||
|
|
||||||
public IEnumerable<Mod> SelectedMods => buttons.Select(b => b.SelectedMod).Where(m => m != null);
|
public IEnumerable<Mod> SelectedMods => Buttons.Select(b => b.SelectedMod).Where(m => m != null);
|
||||||
|
|
||||||
private CancellationTokenSource modsLoadCts;
|
private CancellationTokenSource modsLoadCts;
|
||||||
|
|
||||||
@ -77,7 +79,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
ButtonsContainer.ChildrenEnumerable = c;
|
ButtonsContainer.ChildrenEnumerable = c;
|
||||||
}, (modsLoadCts = new CancellationTokenSource()).Token);
|
}, (modsLoadCts = new CancellationTokenSource()).Token);
|
||||||
|
|
||||||
buttons = modContainers.OfType<ModButton>().ToArray();
|
Buttons = modContainers.OfType<ModButton>().ToArray();
|
||||||
|
|
||||||
header.FadeIn(200);
|
header.FadeIn(200);
|
||||||
this.FadeIn(200);
|
this.FadeIn(200);
|
||||||
@ -88,8 +90,6 @@ namespace osu.Game.Overlays.Mods
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private ModButton[] buttons = Array.Empty<ModButton>();
|
|
||||||
|
|
||||||
protected override bool OnKeyDown(KeyDownEvent e)
|
protected override bool OnKeyDown(KeyDownEvent e)
|
||||||
{
|
{
|
||||||
if (e.ControlPressed) return false;
|
if (e.ControlPressed) return false;
|
||||||
@ -97,8 +97,8 @@ namespace osu.Game.Overlays.Mods
|
|||||||
if (ToggleKeys != null)
|
if (ToggleKeys != null)
|
||||||
{
|
{
|
||||||
var index = Array.IndexOf(ToggleKeys, e.Key);
|
var index = Array.IndexOf(ToggleKeys, e.Key);
|
||||||
if (index > -1 && index < buttons.Length)
|
if (index > -1 && index < Buttons.Count)
|
||||||
buttons[index].SelectNext(e.ShiftPressed ? -1 : 1);
|
Buttons[index].SelectNext(e.ShiftPressed ? -1 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.OnKeyDown(e);
|
return base.OnKeyDown(e);
|
||||||
@ -141,7 +141,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
{
|
{
|
||||||
pendingSelectionOperations.Clear();
|
pendingSelectionOperations.Clear();
|
||||||
|
|
||||||
foreach (var button in buttons.Where(b => !b.Selected))
|
foreach (var button in Buttons.Where(b => !b.Selected))
|
||||||
pendingSelectionOperations.Enqueue(() => button.SelectAt(0));
|
pendingSelectionOperations.Enqueue(() => button.SelectAt(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,7 +151,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
public void DeselectAll()
|
public void DeselectAll()
|
||||||
{
|
{
|
||||||
pendingSelectionOperations.Clear();
|
pendingSelectionOperations.Clear();
|
||||||
DeselectTypes(buttons.Select(b => b.SelectedMod?.GetType()).Where(t => t != null));
|
DeselectTypes(Buttons.Select(b => b.SelectedMod?.GetType()).Where(t => t != null));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -161,7 +161,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
/// <param name="immediate">Whether the deselection should happen immediately. Should only be used when required to ensure correct selection flow.</param>
|
/// <param name="immediate">Whether the deselection should happen immediately. Should only be used when required to ensure correct selection flow.</param>
|
||||||
public void DeselectTypes(IEnumerable<Type> modTypes, bool immediate = false)
|
public void DeselectTypes(IEnumerable<Type> modTypes, bool immediate = false)
|
||||||
{
|
{
|
||||||
foreach (var button in buttons)
|
foreach (var button in Buttons)
|
||||||
{
|
{
|
||||||
if (button.SelectedMod == null) continue;
|
if (button.SelectedMod == null) continue;
|
||||||
|
|
||||||
@ -184,7 +184,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
/// <param name="newSelectedMods">The new list of selected mods to select.</param>
|
/// <param name="newSelectedMods">The new list of selected mods to select.</param>
|
||||||
public void UpdateSelectedButtons(IReadOnlyList<Mod> newSelectedMods)
|
public void UpdateSelectedButtons(IReadOnlyList<Mod> newSelectedMods)
|
||||||
{
|
{
|
||||||
foreach (var button in buttons)
|
foreach (var button in Buttons)
|
||||||
updateButtonSelection(button, newSelectedMods);
|
updateButtonSelection(button, newSelectedMods);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,6 +456,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateSelectedButtons();
|
updateSelectedButtons();
|
||||||
|
OnAvailableModsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -533,6 +534,13 @@ namespace osu.Game.Overlays.Mods
|
|||||||
private void playSelectedSound() => sampleOn?.Play();
|
private void playSelectedSound() => sampleOn?.Play();
|
||||||
private void playDeselectedSound() => sampleOff?.Play();
|
private void playDeselectedSound() => sampleOff?.Play();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked after <see cref="availableMods"/> has changed.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual void OnAvailableModsChanged()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invoked when a new <see cref="Mod"/> has been selected.
|
/// Invoked when a new <see cref="Mod"/> has been selected.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -37,6 +37,15 @@ namespace osu.Game.Overlays.Toolbar
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
Size = new Vector2(1, HEIGHT);
|
Size = new Vector2(1, HEIGHT);
|
||||||
|
AlwaysPresent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateAfterChildren()
|
||||||
|
{
|
||||||
|
base.UpdateAfterChildren();
|
||||||
|
|
||||||
|
// this only needed to be set for the initial LoadComplete/Update, so layout completes and gets buttons in a state they can correctly handle keyboard input for hotkeys.
|
||||||
|
AlwaysPresent = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader(true)]
|
[BackgroundDependencyLoader(true)]
|
||||||
|
@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
|
|
||||||
public bool RestartOnFail => false;
|
public bool RestartOnFail => false;
|
||||||
|
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail) };
|
public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModFailCondition), typeof(ModNoFail) };
|
||||||
|
|
||||||
public override bool HasImplementation => GetType().GenericTypeArguments.Length == 0;
|
public override bool HasImplementation => GetType().GenericTypeArguments.Length == 0;
|
||||||
|
|
||||||
|
25
osu.Game/Rulesets/Mods/ModFailCondition.cs
Normal file
25
osu.Game/Rulesets/Mods/ModFailCondition.cs
Normal file
@ -0,0 +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 osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mods
|
||||||
|
{
|
||||||
|
public abstract class ModFailCondition : Mod, IApplicableToHealthProcessor, IApplicableFailOverride
|
||||||
|
{
|
||||||
|
public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModRelax), typeof(ModAutoplay) };
|
||||||
|
|
||||||
|
public virtual bool PerformFail() => true;
|
||||||
|
|
||||||
|
public virtual bool RestartOnFail => true;
|
||||||
|
|
||||||
|
public void ApplyToHealthProcessor(HealthProcessor healthProcessor)
|
||||||
|
{
|
||||||
|
healthProcessor.FailConditions += FailCondition;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract bool FailCondition(HealthProcessor healthProcessor, JudgementResult result);
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,6 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
public override string Description => "You can't fail, no matter what.";
|
public override string Description => "You can't fail, no matter what.";
|
||||||
public override double ScoreMultiplier => 0.5;
|
public override double ScoreMultiplier => 0.5;
|
||||||
public override bool Ranked => true;
|
public override bool Ranked => true;
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModAutoplay) };
|
public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModFailCondition), typeof(ModAutoplay) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
@ -8,13 +10,18 @@ using osu.Game.Rulesets.Scoring;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Mods
|
namespace osu.Game.Rulesets.Mods
|
||||||
{
|
{
|
||||||
public abstract class ModPerfect : ModSuddenDeath
|
public abstract class ModPerfect : ModFailCondition
|
||||||
{
|
{
|
||||||
public override string Name => "Perfect";
|
public override string Name => "Perfect";
|
||||||
public override string Acronym => "PF";
|
public override string Acronym => "PF";
|
||||||
public override IconUsage? Icon => OsuIcon.ModPerfect;
|
public override IconUsage? Icon => OsuIcon.ModPerfect;
|
||||||
|
public override ModType Type => ModType.DifficultyIncrease;
|
||||||
|
public override bool Ranked => true;
|
||||||
|
public override double ScoreMultiplier => 1;
|
||||||
public override string Description => "SS or quit.";
|
public override string Description => "SS or quit.";
|
||||||
|
|
||||||
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModSuddenDeath)).ToArray();
|
||||||
|
|
||||||
protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result)
|
protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result)
|
||||||
=> result.Type.AffectsAccuracy()
|
=> result.Type.AffectsAccuracy()
|
||||||
&& result.Type != result.Judgement.MaxResult;
|
&& result.Type != result.Judgement.MaxResult;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Audio.Track;
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -24,6 +25,8 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
|
|
||||||
public double ApplyToRate(double time, double rate) => rate * SpeedChange.Value;
|
public double ApplyToRate(double time, double rate) => rate * SpeedChange.Value;
|
||||||
|
|
||||||
|
public override Type[] IncompatibleMods => new[] { typeof(ModTimeRamp) };
|
||||||
|
|
||||||
public override string SettingDescription => SpeedChange.IsDefault ? string.Empty : $"{SpeedChange.Value:N2}x";
|
public override string SettingDescription => SpeedChange.IsDefault ? string.Empty : $"{SpeedChange.Value:N2}x";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,6 +14,6 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
public override IconUsage? Icon => OsuIcon.ModRelax;
|
public override IconUsage? Icon => OsuIcon.ModRelax;
|
||||||
public override ModType Type => ModType.Automation;
|
public override ModType Type => ModType.Automation;
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModNoFail), typeof(ModSuddenDeath) };
|
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModNoFail), typeof(ModFailCondition) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
@ -9,7 +10,7 @@ using osu.Game.Rulesets.Scoring;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Mods
|
namespace osu.Game.Rulesets.Mods
|
||||||
{
|
{
|
||||||
public abstract class ModSuddenDeath : Mod, IApplicableToHealthProcessor, IApplicableFailOverride
|
public abstract class ModSuddenDeath : ModFailCondition
|
||||||
{
|
{
|
||||||
public override string Name => "Sudden Death";
|
public override string Name => "Sudden Death";
|
||||||
public override string Acronym => "SD";
|
public override string Acronym => "SD";
|
||||||
@ -18,18 +19,10 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
public override string Description => "Miss and fail.";
|
public override string Description => "Miss and fail.";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
public override bool Ranked => true;
|
public override bool Ranked => true;
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModRelax), typeof(ModAutoplay) };
|
|
||||||
|
|
||||||
public bool PerformFail() => true;
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModPerfect)).ToArray();
|
||||||
|
|
||||||
public bool RestartOnFail => true;
|
protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result)
|
||||||
|
|
||||||
public void ApplyToHealthProcessor(HealthProcessor healthProcessor)
|
|
||||||
{
|
|
||||||
healthProcessor.FailConditions += FailCondition;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual bool FailCondition(HealthProcessor healthProcessor, JudgementResult result)
|
|
||||||
=> result.Type.AffectsCombo()
|
=> result.Type.AffectsCombo()
|
||||||
&& !result.IsHit;
|
&& !result.IsHit;
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,8 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
[SettingSource("Adjust pitch", "Should pitch be adjusted with speed")]
|
[SettingSource("Adjust pitch", "Should pitch be adjusted with speed")]
|
||||||
public abstract BindableBool AdjustPitch { get; }
|
public abstract BindableBool AdjustPitch { get; }
|
||||||
|
|
||||||
|
public override Type[] IncompatibleMods => new[] { typeof(ModRateAdjust) };
|
||||||
|
|
||||||
public override string SettingDescription => $"{InitialRate.Value:N2}x to {FinalRate.Value:N2}x";
|
public override string SettingDescription => $"{InitialRate.Value:N2}x to {FinalRate.Value:N2}x";
|
||||||
|
|
||||||
private double finalRateTime;
|
private double finalRateTime;
|
||||||
|
@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
[SettingSource("Initial rate", "The starting speed of the track")]
|
[SettingSource("Initial rate", "The starting speed of the track")]
|
||||||
public override BindableNumber<double> InitialRate { get; } = new BindableDouble
|
public override BindableNumber<double> InitialRate { get; } = new BindableDouble
|
||||||
{
|
{
|
||||||
MinValue = 1,
|
MinValue = 0.51,
|
||||||
MaxValue = 2,
|
MaxValue = 2,
|
||||||
Default = 1,
|
Default = 1,
|
||||||
Value = 1,
|
Value = 1,
|
||||||
@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
public override BindableNumber<double> FinalRate { get; } = new BindableDouble
|
public override BindableNumber<double> FinalRate { get; } = new BindableDouble
|
||||||
{
|
{
|
||||||
MinValue = 0.5,
|
MinValue = 0.5,
|
||||||
MaxValue = 0.99,
|
MaxValue = 1.99,
|
||||||
Default = 0.75,
|
Default = 0.75,
|
||||||
Value = 0.75,
|
Value = 0.75,
|
||||||
Precision = 0.01,
|
Precision = 0.01,
|
||||||
@ -45,5 +45,20 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
};
|
};
|
||||||
|
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModWindUp)).ToArray();
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModWindUp)).ToArray();
|
||||||
|
|
||||||
|
public ModWindDown()
|
||||||
|
{
|
||||||
|
InitialRate.BindValueChanged(val =>
|
||||||
|
{
|
||||||
|
if (val.NewValue <= FinalRate.Value)
|
||||||
|
FinalRate.Value = val.NewValue - FinalRate.Precision;
|
||||||
|
});
|
||||||
|
|
||||||
|
FinalRate.BindValueChanged(val =>
|
||||||
|
{
|
||||||
|
if (val.NewValue >= InitialRate.Value)
|
||||||
|
InitialRate.Value = val.NewValue + InitialRate.Precision;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
public override BindableNumber<double> InitialRate { get; } = new BindableDouble
|
public override BindableNumber<double> InitialRate { get; } = new BindableDouble
|
||||||
{
|
{
|
||||||
MinValue = 0.5,
|
MinValue = 0.5,
|
||||||
MaxValue = 1,
|
MaxValue = 1.99,
|
||||||
Default = 1,
|
Default = 1,
|
||||||
Value = 1,
|
Value = 1,
|
||||||
Precision = 0.01,
|
Precision = 0.01,
|
||||||
@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
[SettingSource("Final rate", "The speed increase to ramp towards")]
|
[SettingSource("Final rate", "The speed increase to ramp towards")]
|
||||||
public override BindableNumber<double> FinalRate { get; } = new BindableDouble
|
public override BindableNumber<double> FinalRate { get; } = new BindableDouble
|
||||||
{
|
{
|
||||||
MinValue = 1.01,
|
MinValue = 0.51,
|
||||||
MaxValue = 2,
|
MaxValue = 2,
|
||||||
Default = 1.5,
|
Default = 1.5,
|
||||||
Value = 1.5,
|
Value = 1.5,
|
||||||
@ -45,5 +45,20 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
};
|
};
|
||||||
|
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModWindDown)).ToArray();
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModWindDown)).ToArray();
|
||||||
|
|
||||||
|
public ModWindUp()
|
||||||
|
{
|
||||||
|
InitialRate.BindValueChanged(val =>
|
||||||
|
{
|
||||||
|
if (val.NewValue >= FinalRate.Value)
|
||||||
|
FinalRate.Value = val.NewValue + FinalRate.Precision;
|
||||||
|
});
|
||||||
|
|
||||||
|
FinalRate.BindValueChanged(val =>
|
||||||
|
{
|
||||||
|
if (val.NewValue <= InitialRate.Value)
|
||||||
|
InitialRate.Value = val.NewValue - InitialRate.Precision;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ using osu.Game.Configuration;
|
|||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
using osu.Game.Input.Handlers;
|
using osu.Game.Input.Handlers;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osuTK.Input;
|
|
||||||
using static osu.Game.Input.Handlers.ReplayInputHandler;
|
using static osu.Game.Input.Handlers.ReplayInputHandler;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.UI
|
namespace osu.Game.Rulesets.UI
|
||||||
@ -109,9 +108,9 @@ namespace osu.Game.Rulesets.UI
|
|||||||
{
|
{
|
||||||
switch (e)
|
switch (e)
|
||||||
{
|
{
|
||||||
case MouseDownEvent mouseDown when mouseDown.Button == MouseButton.Left || mouseDown.Button == MouseButton.Right:
|
case MouseDownEvent _:
|
||||||
if (mouseDisabled.Value)
|
if (mouseDisabled.Value)
|
||||||
return false;
|
return true; // importantly, block upwards propagation so global bindings also don't fire.
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -172,6 +172,18 @@ namespace osu.Game.Screens.Menu
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// disabled until the underlying runtime issue is resolved, see https://github.com/mono/mono/issues/20805.
|
||||||
|
if (RuntimeInfo.OS == RuntimeInfo.Platform.iOS)
|
||||||
|
{
|
||||||
|
notifications?.Post(new SimpleNotification
|
||||||
|
{
|
||||||
|
Text = "Multiplayer is temporarily unavailable on iOS as we figure out some low level issues.",
|
||||||
|
Icon = FontAwesome.Solid.AppleAlt,
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
OnMultiplayer?.Invoke();
|
OnMultiplayer?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +75,14 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
section.DeselectAll();
|
section.DeselectAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void OnAvailableModsChanged()
|
||||||
|
{
|
||||||
|
base.OnAvailableModsChanged();
|
||||||
|
|
||||||
|
foreach (var section in ModSectionsContainer.Children)
|
||||||
|
((FreeModSection)section).UpdateCheckboxState();
|
||||||
|
}
|
||||||
|
|
||||||
protected override ModSection CreateModSection(ModType type) => new FreeModSection(type);
|
protected override ModSection CreateModSection(ModType type) => new FreeModSection(type);
|
||||||
|
|
||||||
private class FreeModSection : ModSection
|
private class FreeModSection : ModSection
|
||||||
@ -108,10 +116,14 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
protected override void ModButtonStateChanged(Mod mod)
|
protected override void ModButtonStateChanged(Mod mod)
|
||||||
{
|
{
|
||||||
base.ModButtonStateChanged(mod);
|
base.ModButtonStateChanged(mod);
|
||||||
|
UpdateCheckboxState();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateCheckboxState()
|
||||||
|
{
|
||||||
if (!SelectionAnimationRunning)
|
if (!SelectionAnimationRunning)
|
||||||
{
|
{
|
||||||
var validButtons = ButtonsContainer.OfType<ModButton>().Where(b => b.Mod.HasImplementation);
|
var validButtons = Buttons.Where(b => b.Mod.HasImplementation);
|
||||||
checkbox.Current.Value = validButtons.All(b => b.Selected);
|
checkbox.Current.Value = validButtons.All(b => b.Selected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,9 +75,18 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
Mods.Value = selectedItem?.Value?.RequiredMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty<Mod>();
|
Mods.Value = selectedItem?.Value?.RequiredMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty<Mod>();
|
||||||
FreeMods.Value = selectedItem?.Value?.AllowedMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty<Mod>();
|
FreeMods.Value = selectedItem?.Value?.AllowedMods.Select(m => m.CreateCopy()).ToArray() ?? Array.Empty<Mod>();
|
||||||
|
|
||||||
|
Mods.BindValueChanged(onModsChanged);
|
||||||
Ruleset.BindValueChanged(onRulesetChanged);
|
Ruleset.BindValueChanged(onRulesetChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onModsChanged(ValueChangedEvent<IReadOnlyList<Mod>> mods)
|
||||||
|
{
|
||||||
|
FreeMods.Value = FreeMods.Value.Where(checkCompatibleFreeMod).ToList();
|
||||||
|
|
||||||
|
// Reset the validity delegate to update the overlay's display.
|
||||||
|
freeModSelectOverlay.IsValidMod = IsValidFreeMod;
|
||||||
|
}
|
||||||
|
|
||||||
private void onRulesetChanged(ValueChangedEvent<RulesetInfo> ruleset)
|
private void onRulesetChanged(ValueChangedEvent<RulesetInfo> ruleset)
|
||||||
{
|
{
|
||||||
FreeMods.Value = Array.Empty<Mod>();
|
FreeMods.Value = Array.Empty<Mod>();
|
||||||
@ -155,6 +164,10 @@ namespace osu.Game.Screens.OnlinePlay
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="mod">The <see cref="Mod"/> to check.</param>
|
/// <param name="mod">The <see cref="Mod"/> to check.</param>
|
||||||
/// <returns>Whether <paramref name="mod"/> is a selectable free-mod.</returns>
|
/// <returns>Whether <paramref name="mod"/> is a selectable free-mod.</returns>
|
||||||
protected virtual bool IsValidFreeMod(Mod mod) => IsValidMod(mod);
|
protected virtual bool IsValidFreeMod(Mod mod) => IsValidMod(mod) && checkCompatibleFreeMod(mod);
|
||||||
|
|
||||||
|
private bool checkCompatibleFreeMod(Mod mod)
|
||||||
|
=> Mods.Value.All(m => m.Acronym != mod.Acronym) // Mod must not be contained in the required mods.
|
||||||
|
&& ModUtils.CheckCompatibleSet(Mods.Value.Append(mod).ToArray()); // Mod must be compatible with all the required mods.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -427,11 +427,18 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
private void updatePauseOnFocusLostState()
|
private void updatePauseOnFocusLostState()
|
||||||
{
|
{
|
||||||
if (!PauseOnFocusLost || breakTracker.IsBreakTime.Value)
|
if (!PauseOnFocusLost || !pausingSupportedByCurrentState || breakTracker.IsBreakTime.Value)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (gameActive.Value == false)
|
if (gameActive.Value == false)
|
||||||
Pause();
|
{
|
||||||
|
bool paused = Pause();
|
||||||
|
|
||||||
|
// if the initial pause could not be satisfied, the pause cooldown may be active.
|
||||||
|
// reschedule the pause attempt until it can be achieved.
|
||||||
|
if (!paused)
|
||||||
|
Scheduler.AddOnce(updatePauseOnFocusLostState);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private IBeatmap loadPlayableBeatmap()
|
private IBeatmap loadPlayableBeatmap()
|
||||||
@ -674,6 +681,9 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
private double? lastPauseActionTime;
|
private double? lastPauseActionTime;
|
||||||
|
|
||||||
|
protected bool PauseCooldownActive =>
|
||||||
|
lastPauseActionTime.HasValue && GameplayClockContainer.GameplayClock.CurrentTime < lastPauseActionTime + pause_cooldown;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A set of conditionals which defines whether the current game state and configuration allows for
|
/// A set of conditionals which defines whether the current game state and configuration allows for
|
||||||
/// pausing to be attempted via <see cref="Pause"/>. If false, the game should generally exit if a user pause
|
/// pausing to be attempted via <see cref="Pause"/>. If false, the game should generally exit if a user pause
|
||||||
@ -684,11 +694,9 @@ namespace osu.Game.Screens.Play
|
|||||||
LoadedBeatmapSuccessfully && Configuration.AllowPause && ValidForResume
|
LoadedBeatmapSuccessfully && Configuration.AllowPause && ValidForResume
|
||||||
// replays cannot be paused and exit immediately
|
// replays cannot be paused and exit immediately
|
||||||
&& !DrawableRuleset.HasReplayLoaded.Value
|
&& !DrawableRuleset.HasReplayLoaded.Value
|
||||||
|
// cannot pause if we are already in a fail state
|
||||||
&& !HasFailed;
|
&& !HasFailed;
|
||||||
|
|
||||||
private bool pauseCooldownActive =>
|
|
||||||
lastPauseActionTime.HasValue && GameplayClockContainer.GameplayClock.CurrentTime < lastPauseActionTime + pause_cooldown;
|
|
||||||
|
|
||||||
private bool canResume =>
|
private bool canResume =>
|
||||||
// cannot resume from a non-paused state
|
// cannot resume from a non-paused state
|
||||||
GameplayClockContainer.IsPaused.Value
|
GameplayClockContainer.IsPaused.Value
|
||||||
@ -697,12 +705,12 @@ namespace osu.Game.Screens.Play
|
|||||||
// already resuming
|
// already resuming
|
||||||
&& !IsResuming;
|
&& !IsResuming;
|
||||||
|
|
||||||
public void Pause()
|
public bool Pause()
|
||||||
{
|
{
|
||||||
if (!pausingSupportedByCurrentState) return;
|
if (!pausingSupportedByCurrentState) return false;
|
||||||
|
|
||||||
if (!IsResuming && pauseCooldownActive)
|
if (!IsResuming && PauseCooldownActive)
|
||||||
return;
|
return false;
|
||||||
|
|
||||||
if (IsResuming)
|
if (IsResuming)
|
||||||
{
|
{
|
||||||
@ -713,6 +721,7 @@ namespace osu.Game.Screens.Play
|
|||||||
GameplayClockContainer.Stop();
|
GameplayClockContainer.Stop();
|
||||||
PauseOverlay.Show();
|
PauseOverlay.Show();
|
||||||
lastPauseActionTime = GameplayClockContainer.GameplayClock.CurrentTime;
|
lastPauseActionTime = GameplayClockContainer.GameplayClock.CurrentTime;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Resume()
|
public void Resume()
|
||||||
|
@ -15,6 +15,8 @@ namespace osu.Game.Screens.Ranking
|
|||||||
{
|
{
|
||||||
public class SoloResultsScreen : ResultsScreen
|
public class SoloResultsScreen : ResultsScreen
|
||||||
{
|
{
|
||||||
|
private GetScoresRequest getScoreRequest;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private RulesetStore rulesets { get; set; }
|
private RulesetStore rulesets { get; set; }
|
||||||
|
|
||||||
@ -28,9 +30,16 @@ namespace osu.Game.Screens.Ranking
|
|||||||
if (Score.Beatmap.OnlineBeatmapID == null || Score.Beatmap.Status <= BeatmapSetOnlineStatus.Pending)
|
if (Score.Beatmap.OnlineBeatmapID == null || Score.Beatmap.Status <= BeatmapSetOnlineStatus.Pending)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var req = new GetScoresRequest(Score.Beatmap, Score.Ruleset);
|
getScoreRequest = new GetScoresRequest(Score.Beatmap, Score.Ruleset);
|
||||||
req.Success += r => scoresCallback?.Invoke(r.Scores.Where(s => s.OnlineScoreID != Score.OnlineScoreID).Select(s => s.CreateScoreInfo(rulesets)));
|
getScoreRequest.Success += r => scoresCallback?.Invoke(r.Scores.Where(s => s.OnlineScoreID != Score.OnlineScoreID).Select(s => s.CreateScoreInfo(rulesets)));
|
||||||
return req;
|
return getScoreRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
|
getScoreRequest?.Cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,8 @@ namespace osu.Game.Tests.Visual
|
|||||||
|
|
||||||
public new HealthProcessor HealthProcessor => base.HealthProcessor;
|
public new HealthProcessor HealthProcessor => base.HealthProcessor;
|
||||||
|
|
||||||
|
public new bool PauseCooldownActive => base.PauseCooldownActive;
|
||||||
|
|
||||||
public readonly List<JudgementResult> Results = new List<JudgementResult>();
|
public readonly List<JudgementResult> Results = new List<JudgementResult>();
|
||||||
|
|
||||||
public TestPlayer(bool allowPause = true, bool showResults = true, bool pauseOnFocusLost = false)
|
public TestPlayer(bool allowPause = true, bool showResults = true, bool pauseOnFocusLost = false)
|
||||||
|
Reference in New Issue
Block a user