// 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 System.Linq; using osu.Framework.Allocation; using osu.Framework.Localisation; using osu.Game.Rulesets.Mania.UI; using osu.Framework.Bindables; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Game.Rulesets.Mania.Skinning; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Screens.Play; using osu.Game.Skinning; namespace osu.Game.Rulesets.Mania.Mods { public partial class ManiaModHidden : ManiaModWithPlayfieldCover, IApplicableToPlayer, IUpdatableByPlayfield { /// /// osu!stable is referenced to 768px. /// private const float reference_playfield_height = 768; public const float MIN_COVERAGE = 160f; public const float MAX_COVERAGE = 400f; private const float coverage_increase_per_combo = 0.5f; public override LocalisableString Description => @"Keys fade out before you hit them!"; public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(ManiaModFadeIn), typeof(ManiaModCover) }).ToArray(); public override BindableNumber Coverage { get; } = new BindableFloat(MIN_COVERAGE); protected override CoverExpandDirection ExpandDirection => CoverExpandDirection.AgainstScroll; private readonly IBindable isBreakTime = new Bindable(); private readonly BindableInt combo = new BindableInt(); public override void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) { base.ApplyToScoreProcessor(scoreProcessor); combo.UnbindAll(); combo.BindTo(scoreProcessor.Combo); } public void ApplyToPlayer(Player player) { isBreakTime.UnbindAll(); isBreakTime.BindTo(player.IsBreakTime); } public void Update(Playfield playfield) { Coverage.Value = isBreakTime.Value ? 0 : Math.Min(MAX_COVERAGE, MIN_COVERAGE + combo.Value * coverage_increase_per_combo) / reference_playfield_height; } protected override PlayfieldCoveringWrapper CreateCover(Drawable content) => new LegacyPlayfieldCover(content); private partial class LegacyPlayfieldCover : PlayfieldCoveringWrapper { [Resolved] private ISkinSource skin { get; set; } = null!; private IBindable? hitPosition; public LegacyPlayfieldCover(Drawable content) : base(content) { } protected override void LoadComplete() { base.LoadComplete(); skin.SourceChanged += onSkinChanged; onSkinChanged(); } private void onSkinChanged() { hitPosition = skin.GetManiaSkinConfig(LegacyManiaSkinConfigurationLookups.HitPosition); } protected override float GetHeight(float coverage) { // In osu!stable, the cover is applied in absolute (x768) coordinates from the hit position. float availablePlayfieldHeight = Math.Abs(reference_playfield_height - (hitPosition?.Value ?? Stage.HIT_TARGET_POSITION)); if (availablePlayfieldHeight == 0) return base.GetHeight(coverage); return base.GetHeight(coverage) * reference_playfield_height / availablePlayfieldHeight; } protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); if (skin.IsNotNull()) skin.SourceChanged -= onSkinChanged; } } } }