// 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.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Utils; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Rulesets.Scoring; using osuTK.Graphics; namespace osu.Game.Screens.Play.HUD { /// /// An overlay layer on top of the playfield which fades to red when the current player health falls below a certain threshold defined by . /// public class FailingLayer : HealthDisplay { private const float max_alpha = 0.4f; private const int fade_time = 400; private const float gradient_size = 0.3f; /// /// The threshold under which the current player life should be considered low and the layer should start fading in. /// public double LowHealthThreshold = 0.20f; public readonly Bindable ShowHealth = new Bindable(); private readonly Bindable fadePlayfieldWhenHealthLow = new Bindable(); private readonly Container boxes; private Bindable fadePlayfieldWhenHealthLowSetting; private HealthProcessor healthProcessor; public FailingLayer() { RelativeSizeAxes = Axes.Both; Children = new Drawable[] { boxes = new Container { Alpha = 0, Blending = BlendingParameters.Additive, RelativeSizeAxes = Axes.Both, Children = new Drawable[] { new Box { RelativeSizeAxes = Axes.Both, Colour = ColourInfo.GradientVertical(Color4.White, Color4.White.Opacity(0)), Height = gradient_size, }, new Box { RelativeSizeAxes = Axes.Both, Height = gradient_size, Colour = ColourInfo.GradientVertical(Color4.White.Opacity(0), Color4.White), Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, }, } }, }; } [BackgroundDependencyLoader] private void load(OsuColour color, OsuConfigManager config) { boxes.Colour = color.Red; fadePlayfieldWhenHealthLowSetting = config.GetBindable(OsuSetting.FadePlayfieldWhenHealthLow); fadePlayfieldWhenHealthLow.BindValueChanged(e => TryToFade(fade_time, Easing.OutQuint, e.NewValue), true); ShowHealth.BindValueChanged(e => TryToFade(fade_time, Easing.OutQuint, e.NewValue), true); } protected override void LoadComplete() { base.LoadComplete(); updateBindings(); } public override void BindHealthProcessor(HealthProcessor processor) { base.BindHealthProcessor(processor); healthProcessor = processor; updateBindings(); } private void updateBindings() { if (LoadState < LoadState.Ready) return; fadePlayfieldWhenHealthLow.UnbindBindings(); // Don't display ever if the ruleset is not using a draining health display. if (healthProcessor is DrainingHealthProcessor) fadePlayfieldWhenHealthLow.BindTo(fadePlayfieldWhenHealthLowSetting); else fadePlayfieldWhenHealthLow.Value = false; } /// /// Tries to fade based on "Fade playfield when health is low" setting /// /// Duration of the fade /// Type of easing /// True when you want to fade in, false when you want to fade out public void TryToFade(float fadeDuration, Easing easing, bool fadeIn) { if (ShowHealth.Value) { if (fadeIn) { if (fadePlayfieldWhenHealthLow.Value) this.FadeIn(fadeDuration, easing); } else this.FadeOut(fadeDuration, easing); } else this.FadeOut(fadeDuration, easing); } protected override void Update() { double target = Math.Clamp(max_alpha * (1 - Current.Value / LowHealthThreshold), 0, max_alpha); boxes.Alpha = (float)Interpolation.Lerp(boxes.Alpha, target, Clock.ElapsedFrameTime * 0.01f); base.Update(); } } }