Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

namespace osu.Game.Rulesets.Osu.Mods
{
public class OsuModHidden : ModHidden, IHidesApproachCircles
public class OsuModHidden : ModHidden, IHidesApproachCircles, IAdjustableWhenReplay
{
[SettingSource("Only fade approach circles", "The main object body will not fade when enabled.")]
public Bindable<bool> OnlyFadeApproachCircles { get; } = new BindableBool();
Expand All @@ -30,6 +30,8 @@ public class OsuModHidden : ModHidden, IHidesApproachCircles
public const double FADE_IN_DURATION_MULTIPLIER = 0.4;
public const double FADE_OUT_DURATION_MULTIPLIER = 0.3;

public BindableBool IsDisabled { get; } = new BindableBool();

protected override bool IsFirstAdjustableObject(HitObject hitObject) => !(hitObject is Spinner || hitObject is SpinnerTick);

public override void ApplyToBeatmap(IBeatmap beatmap)
Expand Down Expand Up @@ -62,6 +64,9 @@ protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject,

private void applyHiddenState(DrawableHitObject drawableObject, bool increaseVisibility)
{
if (IsDisabled.Value)
return;

if (!(drawableObject is DrawableOsuHitObject drawableOsuObject))
return;

Expand Down
55 changes: 55 additions & 0 deletions osu.Game.Tests/Visual/Mods/TestSceneModDisable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// 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;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.UI;

namespace osu.Game.Tests.Visual.Mods
{
public partial class TestSceneModDisable : ModTestScene
{
protected override Ruleset CreatePlayerRuleset() => new OsuRuleset();

[Test]
public void TestDisableAvailableInReplay()
{
CreateModTest(new ModTestData
{
Autoplay = true,
Mod = new OsuModFlashlight(),
PassCondition = () => getMod<OsuModFlashlight>().IsDisabled.Value,
});

clickMod<OsuModFlashlight>();
}

[Test]
public void TestDisableAvailableNotInReplay()
{
CreateModTest(new ModTestData
{
Autoplay = false,
Mod = new OsuModFlashlight(),
PassCondition = () => !getMod<OsuModFlashlight>().IsDisabled.Value,
});

clickMod<OsuModFlashlight>();
}

private void clickMod<T>()
where T : IAdjustableWhenReplay
{
AddStep("click mod", () => Player.ChildrenOfType<ModIcon>().Single(icon => icon.Mod is T).TriggerClick());
}

private T getMod<T>()
where T : IAdjustableWhenReplay
=> (T)Player.ChildrenOfType<ModIcon>().Single(icon => icon.Mod is T).Mod;
}
}
12 changes: 12 additions & 0 deletions osu.Game/Rulesets/Mods/IAdjustableWhenReplay.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using osu.Framework.Bindables;

namespace osu.Game.Rulesets.Mods
{
public interface IAdjustableWhenReplay : IApplicableMod
{
BindableBool IsDisabled { get; }
}
}
5 changes: 4 additions & 1 deletion osu.Game/Rulesets/Mods/ModDoubleTime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

namespace osu.Game.Rulesets.Mods
{
public abstract class ModDoubleTime : ModRateAdjust
public abstract class ModDoubleTime : ModRateAdjust, IAdjustableWhenReplay
{
public override string Name => "Double Time";
public override string Acronym => "DT";
Expand Down Expand Up @@ -44,12 +44,15 @@ public abstract class ModDoubleTime : ModRateAdjust
}
}

public BindableBool IsDisabled { get; } = new BindableBool();

private readonly RateAdjustModHelper rateAdjustHelper;

protected ModDoubleTime()
{
rateAdjustHelper = new RateAdjustModHelper(SpeedChange);
rateAdjustHelper.HandleAudioAdjustments(AdjustPitch);
rateAdjustHelper.DisableSpeedChange.BindTo(IsDisabled);
}

public override void ApplyToTrack(IAdjustableAudioComponent track)
Expand Down
9 changes: 8 additions & 1 deletion osu.Game/Rulesets/Mods/ModFlashlight.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public abstract class ModFlashlight : Mod
public abstract float DefaultFlashlightSize { get; }
}

public abstract partial class ModFlashlight<T> : ModFlashlight, IApplicableToDrawableRuleset<T>, IApplicableToScoreProcessor
public abstract partial class ModFlashlight<T> : ModFlashlight, IApplicableToDrawableRuleset<T>, IApplicableToScoreProcessor, IAdjustableWhenReplay
where T : HitObject
{
public const double FLASHLIGHT_FADE_DURATION = 800;
Expand Down Expand Up @@ -100,10 +100,17 @@ public virtual void ApplyToDrawableRuleset(DrawableRuleset<T> drawableRuleset)
// NegativeInfinity is not used to allow one more thing drawn on top (used in replay analysis overlay in osu!).
Depth = float.MinValue,
});

IsDisabled.BindValueChanged(_ =>
{
flashlight.FadeTo(IsDisabled.Value ? 0.3f : 1.0f, 50);
}, true);
}

protected abstract Flashlight CreateFlashlight();

public BindableBool IsDisabled { get; } = new BindableBool();

public abstract partial class Flashlight : Drawable
{
public readonly BindableInt Combo = new BindableInt();
Expand Down
5 changes: 4 additions & 1 deletion osu.Game/Rulesets/Mods/ModHalfTime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

namespace osu.Game.Rulesets.Mods
{
public abstract class ModHalfTime : ModRateAdjust
public abstract class ModHalfTime : ModRateAdjust, IAdjustableWhenReplay
{
public override string Name => "Half Time";
public override string Acronym => "HT";
Expand Down Expand Up @@ -44,12 +44,15 @@ public abstract class ModHalfTime : ModRateAdjust
}
}

public BindableBool IsDisabled { get; } = new BindableBool();

private readonly RateAdjustModHelper rateAdjustHelper;

protected ModHalfTime()
{
rateAdjustHelper = new RateAdjustModHelper(SpeedChange);
rateAdjustHelper.HandleAudioAdjustments(AdjustPitch);
rateAdjustHelper.DisableSpeedChange.BindTo(IsDisabled);
}

public override void ApplyToTrack(IAdjustableAudioComponent track)
Expand Down
20 changes: 18 additions & 2 deletions osu.Game/Rulesets/Mods/RateAdjustModHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ public class RateAdjustModHelper : IApplicableToTrack
{
public readonly IBindableNumber<double> SpeedChange;

private readonly BindableNumber<double> speedChange = new BindableNumber<double>();

/// <summary>
/// Use with mods containing <see cref="IAdjustableWhenReplay"/>.
/// When it set to true, speed change will be 1.0.
/// </summary>
public BindableBool DisableSpeedChange = new BindableBool();

private IAdjustableAudioComponent? track;

private BindableBool? adjustPitch;
Expand All @@ -25,6 +33,14 @@ public class RateAdjustModHelper : IApplicableToTrack
public RateAdjustModHelper(IBindableNumber<double> speedChange)
{
SpeedChange = speedChange;

SpeedChange.BindValueChanged(_ => updateSpeedChange(), true);
DisableSpeedChange.BindValueChanged(_ => updateSpeedChange(), true);
}

private void updateSpeedChange()
{
speedChange.Value = DisableSpeedChange.Value ? 1 : SpeedChange.Value;
}

/// <summary>
Expand All @@ -39,8 +55,8 @@ public void HandleAudioAdjustments(BindableBool adjustPitch)
// When switching between pitch adjust, we need to update adjustments to time-shift or frequency-scale.
adjustPitch.BindValueChanged(adjustPitchSetting =>
{
track?.RemoveAdjustment(adjustmentForPitchSetting(adjustPitchSetting.OldValue), SpeedChange);
track?.AddAdjustment(adjustmentForPitchSetting(adjustPitchSetting.NewValue), SpeedChange);
track?.RemoveAdjustment(adjustmentForPitchSetting(adjustPitchSetting.OldValue), speedChange);
track?.AddAdjustment(adjustmentForPitchSetting(adjustPitchSetting.NewValue), speedChange);

AdjustableProperty adjustmentForPitchSetting(bool adjustPitchSettingValue)
=> adjustPitchSettingValue ? AdjustableProperty.Frequency : AdjustableProperty.Tempo;
Expand Down
28 changes: 28 additions & 0 deletions osu.Game/Rulesets/UI/ClickableModIcon.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// 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.Mods;
using osu.Framework.Input.Events;

namespace osu.Game.Rulesets.UI
{
/// <summary>
/// Display the specified mod at a fixed size.
/// </summary>
public partial class ClickableModIcon : ModIcon
{
public Action? Action;

public ClickableModIcon(IMod mod, bool showTooltip = true, bool showExtendedInformation = true)
: base(mod, showTooltip, showExtendedInformation)
{
}

protected override bool OnClick(ClickEvent e)
{
Action?.Invoke();
return true;
}
}
}
42 changes: 42 additions & 0 deletions osu.Game/Screens/Play/HUD/HudModDisplay.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using osu.Framework.Bindables;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using osuTK.Graphics;

namespace osu.Game.Screens.Play.HUD
{
public partial class HudModDisplay : ModDisplay
{
private readonly Bindable<bool> replayLoaded = new Bindable<bool>();

public HudModDisplay(Bindable<bool> replayLoaded, bool showExtendedInformation = true)
: base(showExtendedInformation)
{
this.replayLoaded.BindTo(replayLoaded);
}

protected override ModIcon CreateModIcon(Mod mod, bool showExtendedInformation)
{
var modIcon = new ClickableModIcon(mod, showExtendedInformation);

if (modIcon.Mod is IAdjustableWhenReplay dmod)
{
dmod.IsDisabled.BindValueChanged(_ => modIcon.Colour = dmod.IsDisabled.Value ? OsuColour.Gray(0.7f) : Color4.White, true);

modIcon.Action = () =>
{
if (!replayLoaded.Value)
return;

dmod.IsDisabled.Toggle();
};
}

return modIcon;
}
}
}
4 changes: 3 additions & 1 deletion osu.Game/Screens/Play/HUD/ModDisplay.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,11 @@ private void updateDisplay(ValueChangedEvent<IReadOnlyList<Mod>> mods)
iconsContainer.Clear();

foreach (Mod mod in mods.NewValue.AsOrdered())
iconsContainer.Add(new ModIcon(mod, showExtendedInformation: showExtendedInformation) { Scale = new Vector2(MOD_ICON_SCALE) });
iconsContainer.Add(CreateModIcon(mod, showExtendedInformation).With(m => m.Scale = new Vector2(MOD_ICON_SCALE)));
}

protected virtual ModIcon CreateModIcon(Mod mod, bool showExtendedInformation) => new ModIcon(mod, showExtendedInformation);

private void updateExpansionMode(double duration = 500)
{
switch (expansionMode)
Expand Down
2 changes: 1 addition & 1 deletion osu.Game/Screens/Play/HUDOverlay.cs
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ protected virtual void BindDrawableRuleset(DrawableRuleset drawableRuleset)
Origin = Anchor.BottomRight,
};

protected ModDisplay CreateModsContainer() => new ModDisplay
protected ModDisplay CreateModsContainer() => new HudModDisplay(replayLoaded)
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Expand Down
Loading