Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reimplement missing gameplay test hotkeys from stable #28705

Merged
merged 12 commits into from
Jul 4, 2024
36 changes: 36 additions & 0 deletions osu.Game.Rulesets.Catch.Tests/TestSceneCatchReplayHandling.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System.Linq;
using NUnit.Framework;
using osu.Framework.Testing;
using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Scoring;
using osu.Game.Tests.Visual;
using osuTK.Input;

namespace osu.Game.Rulesets.Catch.Tests
{
public partial class TestSceneCatchReplayHandling : OsuManualInputManagerTestScene
{
[Test]
public void TestReplayDetach()
{
DrawableCatchRuleset drawableRuleset = null!;
float catcherPosition = 0;

AddStep("create drawable ruleset", () => Child = drawableRuleset = new DrawableCatchRuleset(new CatchRuleset(), new CatchBeatmap(), []));
AddStep("attach replay", () => drawableRuleset.SetReplayScore(new Score()));
AddStep("store catcher position", () => catcherPosition = drawableRuleset.ChildrenOfType<Catcher>().Single().X);
AddStep("hold down left", () => InputManager.PressKey(Key.Left));
AddAssert("catcher didn't move", () => drawableRuleset.ChildrenOfType<Catcher>().Single().X, () => Is.EqualTo(catcherPosition));
AddStep("release left", () => InputManager.ReleaseKey(Key.Left));

AddStep("detach replay", () => drawableRuleset.SetReplayScore(null));
AddStep("hold down left", () => InputManager.PressKey(Key.Left));
AddUntilStep("catcher moved", () => drawableRuleset.ChildrenOfType<Catcher>().Single().X, () => Is.Not.EqualTo(catcherPosition));
AddStep("release left", () => InputManager.ReleaseKey(Key.Left));
}
}
}
4 changes: 2 additions & 2 deletions osu.Game.Rulesets.Mania/UI/ManiaTouchInputArea.cs
Original file line number Diff line number Diff line change
Expand Up @@ -192,12 +192,12 @@ private void updateButton(bool press)

if (press)
{
inputManager?.KeyBindingContainer?.TriggerPressed(Action.Value);
inputManager?.KeyBindingContainer.TriggerPressed(Action.Value);
highlightOverlay.FadeTo(0.1f, 80, Easing.OutQuint);
}
else
{
inputManager?.KeyBindingContainer?.TriggerReleased(Action.Value);
inputManager?.KeyBindingContainer.TriggerReleased(Action.Value);
highlightOverlay.FadeTo(0, 400, Easing.OutQuint);
}
}
Expand Down
111 changes: 111 additions & 0 deletions osu.Game.Tests/Visual/Editing/TestSceneEditorTestGameplay.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Components.Timelines.Summary;
Expand Down Expand Up @@ -224,6 +225,116 @@ public void TestClockTimeTransferIsOneDirectional()
AddAssert("time reverted to 00:01:00", () => EditorClock.CurrentTime, () => Is.EqualTo(60_000));
}

[Test]
public void TestAutoplayToggle()
{
AddStep("click test gameplay button", () =>
{
var button = Editor.ChildrenOfType<TestGameplayButton>().Single();

InputManager.MoveMouseTo(button);
InputManager.Click(MouseButton.Left);
});

EditorPlayer editorPlayer = null;
AddUntilStep("player pushed", () => (editorPlayer = Stack.CurrentScreen as EditorPlayer) != null);
AddUntilStep("no replay active", () => editorPlayer.ChildrenOfType<DrawableRuleset>().Single().ReplayScore, () => Is.Null);
AddStep("press Tab", () => InputManager.Key(Key.Tab));
AddUntilStep("replay active", () => editorPlayer.ChildrenOfType<DrawableRuleset>().Single().ReplayScore, () => Is.Not.Null);
AddStep("press Tab", () => InputManager.Key(Key.Tab));
AddUntilStep("no replay active", () => editorPlayer.ChildrenOfType<DrawableRuleset>().Single().ReplayScore, () => Is.Null);
AddStep("exit player", () => editorPlayer.Exit());
AddUntilStep("current screen is editor", () => Stack.CurrentScreen is Editor);
}

[Test]
public void TestQuickPause()
{
AddStep("click test gameplay button", () =>
{
var button = Editor.ChildrenOfType<TestGameplayButton>().Single();

InputManager.MoveMouseTo(button);
InputManager.Click(MouseButton.Left);
});

EditorPlayer editorPlayer = null;
AddUntilStep("player pushed", () => (editorPlayer = Stack.CurrentScreen as EditorPlayer) != null);
AddUntilStep("clock running", () => editorPlayer.ChildrenOfType<GameplayClockContainer>().Single().IsPaused.Value, () => Is.False);
AddStep("press Ctrl-P", () =>
{
InputManager.PressKey(Key.ControlLeft);
InputManager.Key(Key.P);
InputManager.ReleaseKey(Key.ControlLeft);
});
AddUntilStep("clock not running", () => editorPlayer.ChildrenOfType<GameplayClockContainer>().Single().IsPaused.Value, () => Is.True);
AddStep("press Ctrl-P", () =>
{
InputManager.PressKey(Key.ControlLeft);
InputManager.Key(Key.P);
InputManager.ReleaseKey(Key.ControlLeft);
});
AddUntilStep("clock running", () => editorPlayer.ChildrenOfType<GameplayClockContainer>().Single().IsPaused.Value, () => Is.False);
AddStep("exit player", () => editorPlayer.Exit());
AddUntilStep("current screen is editor", () => Stack.CurrentScreen is Editor);
}

[Test]
public void TestQuickExitAtInitialPosition()
{
AddStep("seek to 00:01:00", () => EditorClock.Seek(60_000));
AddStep("click test gameplay button", () =>
{
var button = Editor.ChildrenOfType<TestGameplayButton>().Single();

InputManager.MoveMouseTo(button);
InputManager.Click(MouseButton.Left);
});

EditorPlayer editorPlayer = null;
AddUntilStep("player pushed", () => (editorPlayer = Stack.CurrentScreen as EditorPlayer) != null);

GameplayClockContainer gameplayClockContainer = null;
AddStep("fetch gameplay clock", () => gameplayClockContainer = editorPlayer.ChildrenOfType<GameplayClockContainer>().First());
AddUntilStep("gameplay clock running", () => gameplayClockContainer.IsRunning);
// when the gameplay test is entered, the clock is expected to continue from where it was in the main editor...
AddAssert("gameplay time past 00:01:00", () => gameplayClockContainer.CurrentTime >= 60_000);

AddWaitStep("wait some", 5);

AddStep("exit player", () => InputManager.PressKey(Key.F1));
AddUntilStep("current screen is editor", () => Stack.CurrentScreen is Editor);
AddAssert("time reverted to 00:01:00", () => EditorClock.CurrentTime, () => Is.EqualTo(60_000));
}

[Test]
public void TestQuickExitAtCurrentPosition()
{
AddStep("seek to 00:01:00", () => EditorClock.Seek(60_000));
AddStep("click test gameplay button", () =>
{
var button = Editor.ChildrenOfType<TestGameplayButton>().Single();

InputManager.MoveMouseTo(button);
InputManager.Click(MouseButton.Left);
});

EditorPlayer editorPlayer = null;
AddUntilStep("player pushed", () => (editorPlayer = Stack.CurrentScreen as EditorPlayer) != null);

GameplayClockContainer gameplayClockContainer = null;
AddStep("fetch gameplay clock", () => gameplayClockContainer = editorPlayer.ChildrenOfType<GameplayClockContainer>().First());
AddUntilStep("gameplay clock running", () => gameplayClockContainer.IsRunning);
// when the gameplay test is entered, the clock is expected to continue from where it was in the main editor...
AddAssert("gameplay time past 00:01:00", () => gameplayClockContainer.CurrentTime >= 60_000);

AddWaitStep("wait some", 5);

AddStep("exit player", () => InputManager.PressKey(Key.F2));
AddUntilStep("current screen is editor", () => Stack.CurrentScreen is Editor);
AddAssert("time moved forward", () => EditorClock.CurrentTime, () => Is.GreaterThan(60_000));
}

public override void TearDownSteps()
{
base.TearDownSteps();
Expand Down
29 changes: 27 additions & 2 deletions osu.Game/Input/Bindings/GlobalActionContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public GlobalActionContainer(OsuGameBase? game)
/// </remarks>
public override IEnumerable<IKeyBinding> DefaultKeyBindings => globalKeyBindings
.Concat(editorKeyBindings)
.Concat(editorTestPlayKeyBindings)
.Concat(inGameKeyBindings)
.Concat(replayKeyBindings)
.Concat(songSelectKeyBindings)
Expand Down Expand Up @@ -68,6 +69,9 @@ public static IEnumerable<KeyBinding> GetDefaultBindingsFor(GlobalActionCategory
case GlobalActionCategory.Overlays:
return overlayKeyBindings;

case GlobalActionCategory.EditorTestPlay:
return editorTestPlayKeyBindings;

default:
throw new ArgumentOutOfRangeException(nameof(category), category, $"Unexpected {nameof(GlobalActionCategory)}");
}
Expand Down Expand Up @@ -100,7 +104,6 @@ public static IEnumerable<GlobalAction> GetGlobalActionsFor(GlobalActionCategory
new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.F }, GlobalAction.ToggleFPSDisplay),
new KeyBinding(new[] { InputKey.Control, InputKey.T }, GlobalAction.ToggleToolbar),
new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.S }, GlobalAction.ToggleSkinEditor),
new KeyBinding(new[] { InputKey.Control, InputKey.P }, GlobalAction.ToggleProfile),
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is moved/deprioritised to the overlay toggle section because in its previous place it was blocking the quick pause hotkey.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checks out since it's an overlay toggle key binding in itself.


new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.R }, GlobalAction.ResetInputSettings),

Expand All @@ -118,6 +121,7 @@ public static IEnumerable<GlobalAction> GetGlobalActionsFor(GlobalActionCategory
new KeyBinding(new[] { InputKey.Control, InputKey.B }, GlobalAction.ToggleBeatmapListing),
new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings),
new KeyBinding(new[] { InputKey.Control, InputKey.N }, GlobalAction.ToggleNotifications),
new KeyBinding(new[] { InputKey.Control, InputKey.P }, GlobalAction.ToggleProfile),
};

private static IEnumerable<KeyBinding> editorKeyBindings => new[]
Expand Down Expand Up @@ -145,6 +149,14 @@ public static IEnumerable<GlobalAction> GetGlobalActionsFor(GlobalActionCategory
new KeyBinding(new[] { InputKey.Control, InputKey.E }, GlobalAction.EditorToggleScaleControl),
};

private static IEnumerable<KeyBinding> editorTestPlayKeyBindings => new[]
{
new KeyBinding(new[] { InputKey.Tab }, GlobalAction.EditorTestPlayToggleAutoplay),
new KeyBinding(new[] { InputKey.Control, InputKey.P }, GlobalAction.EditorTestPlayToggleQuickPause),
new KeyBinding(new[] { InputKey.F1 }, GlobalAction.EditorTestPlayQuickExitToInitialTime),
new KeyBinding(new[] { InputKey.F2 }, GlobalAction.EditorTestPlayQuickExitToCurrentTime),
};

private static IEnumerable<KeyBinding> inGameKeyBindings => new[]
{
new KeyBinding(InputKey.Space, GlobalAction.SkipCutscene),
Expand Down Expand Up @@ -432,6 +444,18 @@ public enum GlobalAction

[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorToggleScaleControl))]
EditorToggleScaleControl,

[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorTestPlayToggleAutoplay))]
EditorTestPlayToggleAutoplay,

[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorTestPlayToggleQuickPause))]
EditorTestPlayToggleQuickPause,

[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorTestPlayQuickExitToInitialTime))]
EditorTestPlayQuickExitToInitialTime,

[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorTestPlayQuickExitToCurrentTime))]
EditorTestPlayQuickExitToCurrentTime,
}

public enum GlobalActionCategory
Expand All @@ -442,6 +466,7 @@ public enum GlobalActionCategory
Replay,
SongSelect,
AudioControl,
Overlays
Overlays,
EditorTestPlay,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These getting a separate section will likely look gratuitous but it bypasses several issues:

  • If they were to be implemented as non-rebindable, they wouldn't be listed anywhere, so there'd probably need to be text that explains that these exist
  • F1 and F2 are already used in the editor section (for compose/design tabs), and keybindings must be unique within a section
  • This also solves various input priority weirdness. As it stands, KeyBindingContainer takes precedence over plain OnKeyDown() which meant that other various bindings like leaderboard toggle were eating these hotkeys when implementing them via OnKeyDown().

}
}
20 changes: 20 additions & 0 deletions osu.Game/Localisation/GlobalActionKeyBindingStrings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,26 @@ public static class GlobalActionKeyBindingStrings
/// </summary>
public static LocalisableString EditorToggleScaleControl => new TranslatableString(getKey(@"editor_toggle_scale_control"), @"Toggle scale control");

/// <summary>
/// "Toggle autoplay"
/// </summary>
public static LocalisableString EditorTestPlayToggleAutoplay => new TranslatableString(getKey(@"editor_test_play_toggle_autoplay"), @"Toggle autoplay");

/// <summary>
/// "Toggle quick pause"
/// </summary>
public static LocalisableString EditorTestPlayToggleQuickPause => new TranslatableString(getKey(@"editor_test_play_toggle_quick_pause"), @"Toggle quick pause");

/// <summary>
/// "Quick exit to initial time"
/// </summary>
public static LocalisableString EditorTestPlayQuickExitToInitialTime => new TranslatableString(getKey(@"editor_test_play_quick_exit_to_initial_time"), @"Quick exit to initial time");

/// <summary>
/// "Quick exit to current time"
/// </summary>
public static LocalisableString EditorTestPlayQuickExitToCurrentTime => new TranslatableString(getKey(@"editor_test_play_quick_exit_to_current_time"), @"Quick exit to current time");

/// <summary>
/// "Increase mod speed"
/// </summary>
Expand Down
5 changes: 5 additions & 0 deletions osu.Game/Localisation/InputSettingsStrings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ public static class InputSettingsStrings
/// </summary>
public static LocalisableString EditorSection => new TranslatableString(getKey(@"editor_section"), @"Editor");

/// <summary>
/// "Editor: Test play"
/// </summary>
public static LocalisableString EditorTestPlaySection => new TranslatableString(getKey(@"editor_test_play_section"), @"Editor: Test play");

/// <summary>
/// "Reset all bindings in section"
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ private void load()
new GlobalKeyBindingsSubsection(InputSettingsStrings.InGameSection, GlobalActionCategory.InGame),
new GlobalKeyBindingsSubsection(InputSettingsStrings.ReplaySection, GlobalActionCategory.Replay),
new GlobalKeyBindingsSubsection(InputSettingsStrings.EditorSection, GlobalActionCategory.Editor),
new GlobalKeyBindingsSubsection(InputSettingsStrings.EditorTestPlaySection, GlobalActionCategory.EditorTestPlay),
});
}
}
Expand Down
Loading
Loading