diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 6752712be1..358553ac59 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -38,6 +38,7 @@ using osu.Game.Rulesets.Scoring.Legacy; using osu.Game.Rulesets.UI; using osu.Game.Scoring; using osu.Game.Screens.Edit.Setup; +using osu.Game.Screens.Play.PlayerSettings; using osu.Game.Screens.Ranking.Statistics; using osu.Game.Skinning; @@ -356,5 +357,7 @@ namespace osu.Game.Rulesets.Osu return adjustedDifficulty; } + + public override AnalysisSettings? CreateAnalysisSettings(DrawableRuleset drawableRuleset) => new OsuAnalysisSettings(drawableRuleset); } } diff --git a/osu.Game.Rulesets.Osu/UI/HitMarkerContainer.cs b/osu.Game.Rulesets.Osu/UI/HitMarkerContainer.cs new file mode 100644 index 0000000000..a9fc42e596 --- /dev/null +++ b/osu.Game.Rulesets.Osu/UI/HitMarkerContainer.cs @@ -0,0 +1,134 @@ +// Copyright (c) ppy Pty Ltd . 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.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; +using osuTK; + +namespace osu.Game.Rulesets.Osu.UI +{ + public partial class HitMarkerContainer : Container, IRequireHighFrequencyMousePosition, IKeyBindingHandler + { + private Vector2 lastMousePosition; + + public Bindable HitMarkerEnabled = new BindableBool(); + public Bindable AimMarkersEnabled = new BindableBool(); + + public override bool ReceivePositionalInputAt(Vector2 _) => true; + + public bool OnPressed(KeyBindingPressEvent e) + { + if (HitMarkerEnabled.Value && (e.Action == OsuAction.LeftButton || e.Action == OsuAction.RightButton)) + { + AddMarker(e.Action); + } + + return false; + } + + public void OnReleased(KeyBindingReleaseEvent e) { } + + protected override bool OnMouseMove(MouseMoveEvent e) + { + lastMousePosition = e.MousePosition; + + if (AimMarkersEnabled.Value) + { + AddMarker(null); + } + + return base.OnMouseMove(e); + } + + private void AddMarker(OsuAction? action) + { + Add(new HitMarkerDrawable(action) { Position = lastMousePosition }); + } + + private partial class HitMarkerDrawable : CompositeDrawable + { + private const double lifetime_duration = 1000; + private const double fade_out_time = 400; + + public override bool RemoveWhenNotAlive => true; + + public HitMarkerDrawable(OsuAction? action) + { + var colour = Colour4.Gray.Opacity(0.5F); + var length = 8; + var depth = float.MaxValue; + switch (action) + { + case OsuAction.LeftButton: + colour = Colour4.Orange; + length = 20; + depth = float.MinValue; + break; + case OsuAction.RightButton: + colour = Colour4.LightGreen; + length = 20; + depth = float.MinValue; + break; + } + + this.Depth = depth; + + InternalChildren = new Drawable[] + { + new Box + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(3, length), + Rotation = 45, + Colour = Colour4.Black.Opacity(0.5F) + }, + new Box + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(3, length), + Rotation = 135, + Colour = Colour4.Black.Opacity(0.5F) + }, + new Box + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(1, length), + Rotation = 45, + Colour = colour + }, + new Box + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(1, length), + Rotation = 135, + Colour = colour + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + LifetimeStart = Time.Current; + LifetimeEnd = LifetimeStart + lifetime_duration; + + Scheduler.AddDelayed(() => + { + this.FadeOut(fade_out_time); + }, lifetime_duration - fade_out_time); + } + } + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/UI/OsuAnalysisSettings.cs b/osu.Game.Rulesets.Osu/UI/OsuAnalysisSettings.cs new file mode 100644 index 0000000000..4694c1a560 --- /dev/null +++ b/osu.Game.Rulesets.Osu/UI/OsuAnalysisSettings.cs @@ -0,0 +1,81 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Logging; +using osu.Game.Localisation; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.UI; +using osu.Game.Screens.Play.PlayerSettings; + +namespace osu.Game.Rulesets.Osu.UI +{ + public partial class OsuAnalysisSettings : AnalysisSettings + { + private static readonly Logger logger = Logger.GetLogger("osu-analysis-settings"); + + protected new DrawableOsuRuleset drawableRuleset => (DrawableOsuRuleset)base.drawableRuleset; + + private readonly PlayerCheckbox hitMarkerToggle; + private readonly PlayerCheckbox aimMarkerToggle; + private readonly PlayerCheckbox hideCursorToggle; + private readonly PlayerCheckbox? hiddenToggle; + + public OsuAnalysisSettings(DrawableRuleset drawableRuleset) + : base(drawableRuleset) + { + Children = new Drawable[] + { + hitMarkerToggle = new PlayerCheckbox { LabelText = PlayerSettingsOverlayStrings.HitMarkers }, + aimMarkerToggle = new PlayerCheckbox { LabelText = PlayerSettingsOverlayStrings.AimMarkers }, + hideCursorToggle = new PlayerCheckbox { LabelText = PlayerSettingsOverlayStrings.HideCursor } + }; + + // hidden stuff is just here for testing at the moment; to create the mod disabling functionality + + foreach (var mod in drawableRuleset.Mods) + { + if (mod is OsuModHidden) + { + logger.Add("Hidden is enabled", LogLevel.Debug); + Add(hiddenToggle = new PlayerCheckbox { LabelText = "Disable hidden" }); + break; + } + } + } + + protected override void LoadComplete() + { + drawableRuleset.Playfield.MarkersContainer.HitMarkerEnabled.BindTo(hitMarkerToggle.Current); + drawableRuleset.Playfield.MarkersContainer.AimMarkersEnabled.BindTo(aimMarkerToggle.Current); + hideCursorToggle.Current.BindValueChanged(onCursorToggle); + hiddenToggle?.Current.BindValueChanged(onHiddenToggle); + } + + private void onCursorToggle(ValueChangedEvent hide) + { + // this only hides half the cursor + if (hide.NewValue) + { + drawableRuleset.Playfield.Cursor.Hide(); + } else + { + drawableRuleset.Playfield.Cursor.Show(); + } + } + + private void onHiddenToggle(ValueChangedEvent off) + { + if (off.NewValue) + { + logger.Add("Hidden off", LogLevel.Debug); + } else + { + logger.Add("Hidden on", LogLevel.Debug); + } + } + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 411a02c5af..d7e1732175 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -36,6 +36,9 @@ namespace osu.Game.Rulesets.Osu.UI private readonly JudgementPooler judgementPooler; public SmokeContainer Smoke { get; } + + public HitMarkerContainer MarkersContainer { get; } + public FollowPointRenderer FollowPoints { get; } public static readonly Vector2 BASE_SIZE = new Vector2(512, 384); @@ -59,6 +62,7 @@ namespace osu.Game.Rulesets.Osu.UI HitObjectContainer, judgementAboveHitObjectLayer = new Container { RelativeSizeAxes = Axes.Both }, approachCircles = new ProxyContainer { RelativeSizeAxes = Axes.Both }, + MarkersContainer = new HitMarkerContainer { RelativeSizeAxes = Axes.Both } }; HitPolicy = new StartTimeOrderedHitPolicy(); diff --git a/osu.Game/Localisation/PlayerSettingsOverlayStrings.cs b/osu.Game/Localisation/PlayerSettingsOverlayStrings.cs index 60874da561..f829fb4ac1 100644 --- a/osu.Game/Localisation/PlayerSettingsOverlayStrings.cs +++ b/osu.Game/Localisation/PlayerSettingsOverlayStrings.cs @@ -19,6 +19,21 @@ namespace osu.Game.Localisation /// public static LocalisableString StepForward => new TranslatableString(getKey(@"step_forward_frame"), @"Step forward one frame"); + /// + /// "Hit markers" + /// + public static LocalisableString HitMarkers => new TranslatableString(getKey(@"hit_markers"), @"Hit markers"); + + /// + /// "Aim markers" + /// + public static LocalisableString AimMarkers => new TranslatableString(getKey(@"aim_markers"), @"Aim markers"); + + /// + /// "Hide cursor" + /// + public static LocalisableString HideCursor => new TranslatableString(getKey(@"hide_cursor"), @"Hide cursor"); + /// /// "Seek backward {0} seconds" /// diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 37a35fd3ae..5fbb23f094 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -27,6 +27,7 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Scoring; using osu.Game.Screens.Edit.Setup; +using osu.Game.Screens.Play.PlayerSettings; using osu.Game.Screens.Ranking.Statistics; using osu.Game.Skinning; using osu.Game.Users; @@ -402,5 +403,7 @@ namespace osu.Game.Rulesets /// Can be overridden to alter the difficulty section to the editor beatmap setup screen. /// public virtual DifficultySection? CreateEditorDifficultySection() => null; + + public virtual AnalysisSettings? CreateAnalysisSettings(DrawableRuleset drawableRuleset) => null; } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index ad1f9ec897..0fa7143693 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -115,7 +115,7 @@ namespace osu.Game.Screens.Play public GameplayState GameplayState { get; private set; } - private Ruleset ruleset; + protected Ruleset ruleset; public BreakOverlay BreakOverlay; diff --git a/osu.Game/Screens/Play/PlayerSettings/AnalysisSettings.cs b/osu.Game/Screens/Play/PlayerSettings/AnalysisSettings.cs new file mode 100644 index 0000000000..122ca29142 --- /dev/null +++ b/osu.Game/Screens/Play/PlayerSettings/AnalysisSettings.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.UI; + +namespace osu.Game.Screens.Play.PlayerSettings +{ + public partial class AnalysisSettings : PlayerSettingsGroup + { + protected DrawableRuleset drawableRuleset; + + public AnalysisSettings(DrawableRuleset drawableRuleset) + : base("Analysis Settings") + { + this.drawableRuleset = drawableRuleset; + } + } +} \ No newline at end of file diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index 3c5b85662a..0d877785e7 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -71,6 +71,11 @@ namespace osu.Game.Screens.Play playbackSettings.UserPlaybackRate.BindTo(master.UserPlaybackRate); HUDOverlay.PlayerSettingsOverlay.AddAtStart(playbackSettings); + + var analysisSettings = ruleset.CreateAnalysisSettings(DrawableRuleset); + if (analysisSettings != null) { + HUDOverlay.PlayerSettingsOverlay.AddAtStart(analysisSettings); + } } protected override void PrepareReplay()