mirror of
https://github.com/ppy/osu.git
synced 2026-06-09 20:54:47 +08:00
new features + improvements
Hit & aim markers are skinnable. Hidden can be toggled off. Aim line with skinnable color was added. The fadeout time is based on the approach rate. Cursor hide fixed.
This commit is contained in:
@@ -2,9 +2,11 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Transforms;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
@@ -12,13 +14,14 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public class OsuModHidden : ModHidden, IHidesApproachCircles
|
||||
public class OsuModHidden : ModHidden, IHidesApproachCircles, IToggleableVisibility
|
||||
{
|
||||
[SettingSource("Only fade approach circles", "The main object body will not fade when enabled.")]
|
||||
public Bindable<bool> OnlyFadeApproachCircles { get; } = new BindableBool();
|
||||
@@ -28,24 +31,41 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles), typeof(OsuModSpinIn), typeof(OsuModDepth) };
|
||||
|
||||
private bool toggledOff = false;
|
||||
private IBeatmap? appliedBeatmap;
|
||||
|
||||
public const double FADE_IN_DURATION_MULTIPLIER = 0.4;
|
||||
public const double FADE_OUT_DURATION_MULTIPLIER = 0.3;
|
||||
|
||||
protected override bool IsFirstAdjustableObject(HitObject hitObject) => !(hitObject is Spinner || hitObject is SpinnerTick);
|
||||
|
||||
public override void ApplyToBeatmap(IBeatmap beatmap)
|
||||
public override void ApplyToBeatmap(IBeatmap? beatmap = null)
|
||||
{
|
||||
if (beatmap is not null)
|
||||
appliedBeatmap = beatmap;
|
||||
else if (appliedBeatmap is null)
|
||||
return;
|
||||
else
|
||||
beatmap = appliedBeatmap;
|
||||
|
||||
base.ApplyToBeatmap(beatmap);
|
||||
|
||||
foreach (var obj in beatmap.HitObjects.OfType<OsuHitObject>())
|
||||
applyFadeInAdjustment(obj);
|
||||
}
|
||||
|
||||
static void applyFadeInAdjustment(OsuHitObject osuObject)
|
||||
{
|
||||
osuObject.TimeFadeIn = osuObject.TimePreempt * FADE_IN_DURATION_MULTIPLIER;
|
||||
foreach (var nested in osuObject.NestedHitObjects.OfType<OsuHitObject>())
|
||||
applyFadeInAdjustment(nested);
|
||||
}
|
||||
private static void applyFadeInAdjustment(OsuHitObject osuObject)
|
||||
{
|
||||
osuObject.TimeFadeIn = osuObject.TimePreempt * FADE_IN_DURATION_MULTIPLIER;
|
||||
foreach (var nested in osuObject.NestedHitObjects.OfType<OsuHitObject>())
|
||||
applyFadeInAdjustment(nested);
|
||||
}
|
||||
|
||||
private static void revertFadeInAdjustment(OsuHitObject osuObject)
|
||||
{
|
||||
osuObject.TimeFadeIn = OsuHitObject.CalculateTimeFadeIn(osuObject.TimePreempt);
|
||||
foreach (var nested in osuObject.NestedHitObjects.OfType<OsuHitObject>())
|
||||
revertFadeInAdjustment(nested);
|
||||
}
|
||||
|
||||
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
||||
@@ -60,7 +80,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
private void applyHiddenState(DrawableHitObject drawableObject, bool increaseVisibility)
|
||||
{
|
||||
if (!(drawableObject is DrawableOsuHitObject drawableOsuObject))
|
||||
if (!(drawableObject is DrawableOsuHitObject drawableOsuObject) || toggledOff)
|
||||
return;
|
||||
|
||||
OsuHitObject hitObject = drawableOsuObject.HitObject;
|
||||
@@ -183,8 +203,11 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
}
|
||||
}
|
||||
|
||||
private static void hideSpinnerApproachCircle(DrawableSpinner spinner)
|
||||
private void hideSpinnerApproachCircle(DrawableSpinner spinner)
|
||||
{
|
||||
if (toggledOff)
|
||||
return;
|
||||
|
||||
var approachCircle = (spinner.Body.Drawable as IHasApproachCircle)?.ApproachCircle;
|
||||
if (approachCircle == null)
|
||||
return;
|
||||
@@ -192,5 +215,39 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
using (spinner.BeginAbsoluteSequence(spinner.HitObject.StartTime - spinner.HitObject.TimePreempt))
|
||||
approachCircle.Hide();
|
||||
}
|
||||
|
||||
public void ToggleOffVisibility(Playfield playfield)
|
||||
{
|
||||
if (toggledOff)
|
||||
return;
|
||||
|
||||
toggledOff = true;
|
||||
|
||||
if (appliedBeatmap is not null)
|
||||
foreach (var obj in appliedBeatmap.HitObjects.OfType<OsuHitObject>())
|
||||
revertFadeInAdjustment(obj);
|
||||
|
||||
foreach (var dho in playfield.AllHitObjects)
|
||||
{
|
||||
dho.RefreshStateTransforms();
|
||||
}
|
||||
}
|
||||
|
||||
public void ToggleOnVisibility(Playfield playfield)
|
||||
{
|
||||
if (!toggledOff)
|
||||
return;
|
||||
|
||||
toggledOff = false;
|
||||
|
||||
if (appliedBeatmap is not null)
|
||||
ApplyToBeatmap();
|
||||
|
||||
foreach (var dho in playfield.AllHitObjects)
|
||||
{
|
||||
dho.RefreshStateTransforms();
|
||||
ApplyToDrawableHitObject(dho);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,17 +154,19 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
});
|
||||
}
|
||||
|
||||
// Preempt time can go below 450ms. Normally, this is achieved via the DT mod which uniformly speeds up all animations game wide regardless of AR.
|
||||
// This uniform speedup is hard to match 1:1, however we can at least make AR>10 (via mods) feel good by extending the upper linear function above.
|
||||
// Note that this doesn't exactly match the AR>10 visuals as they're classically known, but it feels good.
|
||||
// This adjustment is necessary for AR>10, otherwise TimePreempt can become smaller leading to hitcircles not fully fading in.
|
||||
static public double CalculateTimeFadeIn(double timePreempt) => 400 * Math.Min(1, timePreempt / PREEMPT_MIN);
|
||||
|
||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, PREEMPT_MAX, PREEMPT_MID, PREEMPT_MIN);
|
||||
|
||||
// Preempt time can go below 450ms. Normally, this is achieved via the DT mod which uniformly speeds up all animations game wide regardless of AR.
|
||||
// This uniform speedup is hard to match 1:1, however we can at least make AR>10 (via mods) feel good by extending the upper linear function above.
|
||||
// Note that this doesn't exactly match the AR>10 visuals as they're classically known, but it feels good.
|
||||
// This adjustment is necessary for AR>10, otherwise TimePreempt can become smaller leading to hitcircles not fully fading in.
|
||||
TimeFadeIn = 400 * Math.Min(1, TimePreempt / PREEMPT_MIN);
|
||||
TimeFadeIn = CalculateTimeFadeIn(TimePreempt);
|
||||
|
||||
Scale = LegacyRulesetExtensions.CalculateScaleFromCircleSize(difficulty.CircleSize, true);
|
||||
}
|
||||
|
||||
@@ -22,5 +22,8 @@ namespace osu.Game.Rulesets.Osu
|
||||
SpinnerBody,
|
||||
CursorSmoke,
|
||||
ApproachCircle,
|
||||
HitMarkerLeft,
|
||||
HitMarkerRight,
|
||||
AimMarker
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
{
|
||||
public partial class DefaultHitMarker : CompositeDrawable
|
||||
{
|
||||
public DefaultHitMarker(OsuAction? action)
|
||||
{
|
||||
var (colour, length, hasBorder) = getConfig(action);
|
||||
|
||||
if (hasBorder)
|
||||
{
|
||||
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)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
AddRangeInternal(new Drawable[]
|
||||
{
|
||||
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
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private (Colour4 colour, float length, bool hasBorder) getConfig(OsuAction? action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case OsuAction.LeftButton:
|
||||
return (Colour4.Orange, 20, true);
|
||||
case OsuAction.RightButton:
|
||||
return (Colour4.LightGreen, 20, true);
|
||||
default:
|
||||
return (Colour4.Gray.Opacity(0.3F), 8, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Skinning.Default
|
||||
{
|
||||
public partial class HitMarker : Sprite
|
||||
{
|
||||
private readonly OsuAction? action;
|
||||
|
||||
public HitMarker(OsuAction? action = null)
|
||||
{
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case OsuAction.LeftButton:
|
||||
Texture = skin.GetTexture(@"hitmarker-left");
|
||||
break;
|
||||
case OsuAction.RightButton:
|
||||
Texture = skin.GetTexture(@"hitmarker-right");
|
||||
break;
|
||||
default:
|
||||
Texture = skin.GetTexture(@"aimmarker");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using System;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Skinning.Default;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
|
||||
@@ -168,6 +169,24 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.HitMarkerLeft:
|
||||
if (GetTexture(@"hitmarker-left") != null)
|
||||
return new HitMarker(OsuAction.LeftButton);
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.HitMarkerRight:
|
||||
if (GetTexture(@"hitmarker-right") != null)
|
||||
return new HitMarker(OsuAction.RightButton);
|
||||
|
||||
return null;
|
||||
|
||||
case OsuSkinComponents.AimMarker:
|
||||
if (GetTexture(@"aimmarker") != null)
|
||||
return new HitMarker();
|
||||
|
||||
return null;
|
||||
|
||||
default:
|
||||
throw new UnsupportedSkinComponentException(lookup);
|
||||
}
|
||||
|
||||
@@ -10,5 +10,6 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
SliderBall,
|
||||
SpinnerBackground,
|
||||
StarBreakAdditive,
|
||||
ReplayAimLine,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,111 +10,128 @@ using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Skinning;
|
||||
using osu.Game.Rulesets.Osu.Skinning.Default;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.UI
|
||||
{
|
||||
public partial class HitMarkerContainer : Container, IRequireHighFrequencyMousePosition, IKeyBindingHandler<OsuAction>
|
||||
{
|
||||
private Vector2 lastMousePosition;
|
||||
private Vector2? lastlastMousePosition;
|
||||
private double? timePreempt;
|
||||
private double TimePreempt
|
||||
{
|
||||
get => timePreempt ?? default_time_preempt;
|
||||
set => timePreempt = value;
|
||||
}
|
||||
|
||||
public Bindable<bool> HitMarkerEnabled = new BindableBool();
|
||||
public Bindable<bool> AimMarkersEnabled = new BindableBool();
|
||||
public Bindable<bool> AimLinesEnabled = new BindableBool();
|
||||
|
||||
private const double default_time_preempt = 1000;
|
||||
|
||||
private readonly HitObjectContainer hitObjectContainer;
|
||||
|
||||
public override bool ReceivePositionalInputAt(Vector2 _) => true;
|
||||
|
||||
public HitMarkerContainer(HitObjectContainer hitObjectContainer)
|
||||
{
|
||||
this.hitObjectContainer = hitObjectContainer;
|
||||
}
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<OsuAction> e)
|
||||
{
|
||||
if (HitMarkerEnabled.Value && (e.Action == OsuAction.LeftButton || e.Action == OsuAction.RightButton))
|
||||
{
|
||||
updateTimePreempt();
|
||||
AddMarker(e.Action);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnReleased(KeyBindingReleaseEvent<OsuAction> e) { }
|
||||
public void OnReleased(KeyBindingReleaseEvent<OsuAction> e)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool OnMouseMove(MouseMoveEvent e)
|
||||
{
|
||||
lastlastMousePosition = lastMousePosition;
|
||||
lastMousePosition = e.MousePosition;
|
||||
|
||||
if (AimMarkersEnabled.Value)
|
||||
{
|
||||
updateTimePreempt();
|
||||
AddMarker(null);
|
||||
}
|
||||
|
||||
if (AimLinesEnabled.Value && lastlastMousePosition != null && lastlastMousePosition != lastMousePosition)
|
||||
{
|
||||
if (!AimMarkersEnabled.Value)
|
||||
updateTimePreempt();
|
||||
Add(new AimLineDrawable((Vector2)lastlastMousePosition, lastMousePosition, TimePreempt));
|
||||
}
|
||||
|
||||
return base.OnMouseMove(e);
|
||||
}
|
||||
|
||||
private void AddMarker(OsuAction? action)
|
||||
{
|
||||
Add(new HitMarkerDrawable(action) { Position = lastMousePosition });
|
||||
var component = OsuSkinComponents.AimMarker;
|
||||
switch(action)
|
||||
{
|
||||
case OsuAction.LeftButton:
|
||||
component = OsuSkinComponents.HitMarkerLeft;
|
||||
break;
|
||||
case OsuAction.RightButton:
|
||||
component = OsuSkinComponents.HitMarkerRight;
|
||||
break;
|
||||
}
|
||||
|
||||
Add(new HitMarkerDrawable(action, component, TimePreempt)
|
||||
{
|
||||
Position = lastMousePosition,
|
||||
Origin = Anchor.Centre,
|
||||
Depth = action == null ? float.MaxValue : float.MinValue
|
||||
});
|
||||
}
|
||||
|
||||
private partial class HitMarkerDrawable : CompositeDrawable
|
||||
private void updateTimePreempt()
|
||||
{
|
||||
private const double lifetime_duration = 1000;
|
||||
private const double fade_out_time = 400;
|
||||
var hitObject = getHitObject();
|
||||
if (hitObject == null)
|
||||
return;
|
||||
|
||||
TimePreempt = hitObject.TimePreempt;
|
||||
}
|
||||
|
||||
private OsuHitObject? getHitObject()
|
||||
{
|
||||
foreach (var dho in hitObjectContainer.Objects)
|
||||
return (dho as DrawableOsuHitObject)?.HitObject;
|
||||
return null;
|
||||
}
|
||||
|
||||
private partial class HitMarkerDrawable : SkinnableDrawable
|
||||
{
|
||||
private readonly double lifetimeDuration;
|
||||
private readonly double fadeOutTime;
|
||||
|
||||
public override bool RemoveWhenNotAlive => true;
|
||||
|
||||
public HitMarkerDrawable(OsuAction? action)
|
||||
public HitMarkerDrawable(OsuAction? action, OsuSkinComponents componenet, double timePreempt)
|
||||
: base(new OsuSkinComponentLookup(componenet), _ => new DefaultHitMarker(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
|
||||
}
|
||||
};
|
||||
fadeOutTime = timePreempt / 2;
|
||||
lifetimeDuration = timePreempt + fadeOutTime;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
@@ -122,12 +139,57 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
base.LoadComplete();
|
||||
|
||||
LifetimeStart = Time.Current;
|
||||
LifetimeEnd = LifetimeStart + lifetime_duration;
|
||||
LifetimeEnd = LifetimeStart + lifetimeDuration;
|
||||
|
||||
Scheduler.AddDelayed(() =>
|
||||
{
|
||||
this.FadeOut(fade_out_time);
|
||||
}, lifetime_duration - fade_out_time);
|
||||
this.FadeOut(fadeOutTime);
|
||||
}, lifetimeDuration - fadeOutTime);
|
||||
}
|
||||
}
|
||||
|
||||
private partial class AimLineDrawable : CompositeDrawable
|
||||
{
|
||||
private readonly double lifetimeDuration;
|
||||
private readonly double fadeOutTime;
|
||||
|
||||
public override bool RemoveWhenNotAlive => true;
|
||||
|
||||
public AimLineDrawable(Vector2 fromP, Vector2 toP, double timePreempt)
|
||||
{
|
||||
fadeOutTime = timePreempt / 2;
|
||||
lifetimeDuration = timePreempt + fadeOutTime;
|
||||
|
||||
float distance = Vector2.Distance(fromP, toP);
|
||||
Vector2 direction = (toP - fromP);
|
||||
InternalChild = new Box
|
||||
{
|
||||
Position = fromP + (direction / 2),
|
||||
Size = new Vector2(distance, 1),
|
||||
Rotation = (float)(Math.Atan(direction.Y / direction.X) * (180 / Math.PI)),
|
||||
Origin = Anchor.Centre
|
||||
};
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(ISkinSource skin)
|
||||
{
|
||||
var color = skin.GetConfig<OsuSkinColour, Color4>(OsuSkinColour.ReplayAimLine)?.Value ?? Color4.White;
|
||||
color.A = 127;
|
||||
InternalChild.Colour = color;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
LifetimeStart = Time.Current;
|
||||
LifetimeEnd = LifetimeStart + lifetimeDuration;
|
||||
|
||||
Scheduler.AddDelayed(() =>
|
||||
{
|
||||
this.FadeOut(fadeOutTime);
|
||||
}, lifetimeDuration - fadeOutTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.Osu.Mods;
|
||||
@@ -15,14 +17,13 @@ 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;
|
||||
private readonly PlayerCheckbox aimLinesToggle;
|
||||
private readonly FillFlowContainer modTogglesContainer;
|
||||
|
||||
public OsuAnalysisSettings(DrawableRuleset drawableRuleset)
|
||||
: base(drawableRuleset)
|
||||
@@ -31,18 +32,36 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
{
|
||||
hitMarkerToggle = new PlayerCheckbox { LabelText = PlayerSettingsOverlayStrings.HitMarkers },
|
||||
aimMarkerToggle = new PlayerCheckbox { LabelText = PlayerSettingsOverlayStrings.AimMarkers },
|
||||
hideCursorToggle = new PlayerCheckbox { LabelText = PlayerSettingsOverlayStrings.HideCursor }
|
||||
aimLinesToggle = new PlayerCheckbox { LabelText = PlayerSettingsOverlayStrings.AimLines },
|
||||
hideCursorToggle = new PlayerCheckbox { LabelText = PlayerSettingsOverlayStrings.HideCursor },
|
||||
new OsuScrollContainer(Direction.Horizontal)
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = ModSwitchSmall.DEFAULT_SIZE,
|
||||
ScrollbarOverlapsContent = false,
|
||||
Child = modTogglesContainer = new FillFlowContainer
|
||||
{
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
Direction = FillDirection.Horizontal,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
AutoSizeAxes = Axes.X
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 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)
|
||||
if (mod is IToggleableVisibility toggleableMod)
|
||||
{
|
||||
logger.Add("Hidden is enabled", LogLevel.Debug);
|
||||
Add(hiddenToggle = new PlayerCheckbox { LabelText = "Disable hidden" });
|
||||
break;
|
||||
var modSwitch = new SelectableModSwitchSmall(mod)
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Active = { Value = true }
|
||||
};
|
||||
modSwitch.Active.BindValueChanged((v) => onModToggle(toggleableMod, v));
|
||||
modTogglesContainer.Add(modSwitch);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,8 +70,8 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
{
|
||||
drawableRuleset.Playfield.MarkersContainer.HitMarkerEnabled.BindTo(hitMarkerToggle.Current);
|
||||
drawableRuleset.Playfield.MarkersContainer.AimMarkersEnabled.BindTo(aimMarkerToggle.Current);
|
||||
drawableRuleset.Playfield.MarkersContainer.AimLinesEnabled.BindTo(aimLinesToggle.Current);
|
||||
hideCursorToggle.Current.BindValueChanged(onCursorToggle);
|
||||
hiddenToggle?.Current.BindValueChanged(onHiddenToggle);
|
||||
}
|
||||
|
||||
private void onCursorToggle(ValueChangedEvent<bool> hide)
|
||||
@@ -60,21 +79,34 @@ namespace osu.Game.Rulesets.Osu.UI
|
||||
// this only hides half the cursor
|
||||
if (hide.NewValue)
|
||||
{
|
||||
drawableRuleset.Playfield.Cursor.Hide();
|
||||
drawableRuleset.Playfield.Cursor.FadeOut();
|
||||
} else
|
||||
{
|
||||
drawableRuleset.Playfield.Cursor.Show();
|
||||
drawableRuleset.Playfield.Cursor.FadeIn();
|
||||
}
|
||||
}
|
||||
|
||||
private void onHiddenToggle(ValueChangedEvent<bool> off)
|
||||
private void onModToggle(IToggleableVisibility mod, ValueChangedEvent<bool> toggled)
|
||||
{
|
||||
if (off.NewValue)
|
||||
if (toggled.NewValue)
|
||||
{
|
||||
logger.Add("Hidden off", LogLevel.Debug);
|
||||
mod.ToggleOnVisibility(drawableRuleset.Playfield);
|
||||
} else
|
||||
{
|
||||
logger.Add("Hidden on", LogLevel.Debug);
|
||||
mod.ToggleOffVisibility(drawableRuleset.Playfield);
|
||||
}
|
||||
}
|
||||
|
||||
private partial class SelectableModSwitchSmall : ModSwitchSmall
|
||||
{
|
||||
public SelectableModSwitchSmall(IMod mod)
|
||||
: base(mod)
|
||||
{}
|
||||
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
{
|
||||
Active.Value = !Active.Value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +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 }
|
||||
MarkersContainer = new HitMarkerContainer(HitObjectContainer) { RelativeSizeAxes = Axes.Both }
|
||||
};
|
||||
|
||||
HitPolicy = new StartTimeOrderedHitPolicy();
|
||||
|
||||
@@ -34,6 +34,11 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString HideCursor => new TranslatableString(getKey(@"hide_cursor"), @"Hide cursor");
|
||||
|
||||
/// <summary>
|
||||
/// "Aim lines"
|
||||
/// </summary>
|
||||
public static LocalisableString AimLines => new TranslatableString(getKey(@"aim_lines"), @"Aim lines");
|
||||
|
||||
/// <summary>
|
||||
/// "Seek backward {0} seconds"
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using osu.Game.Rulesets.UI;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
public interface IToggleableVisibility
|
||||
{
|
||||
public void ToggleOffVisibility(Playfield playfield);
|
||||
|
||||
public void ToggleOnVisibility(Playfield playfield);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user