// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. #nullable disable 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 partial class FailingLayer : HealthDisplay { /// /// Whether the current player health should be shown on screen. /// public readonly Bindable ShowHealth = new Bindable(); protected override bool PlayInitialIncreaseAnimation => false; private const float max_alpha = 0.4f; private const int fade_time = 400; private const float gradient_size = 0.2f; /// /// The threshold under which the current player life should be considered low and the layer should start fading in. /// private const double low_health_threshold = 0.20f; private readonly Container boxes; private Bindable fadePlayfieldWhenHealthLow; public FailingLayer() { RelativeSizeAxes = Axes.Both; InternalChildren = new Drawable[] { boxes = new Container { Alpha = 0, Blending = BlendingParameters.Additive, RelativeSizeAxes = Axes.Both, Children = new Drawable[] { new Box { RelativeSizeAxes = Axes.Both, Colour = ColourInfo.GradientHorizontal(Color4.White, Color4.White.Opacity(0)), Width = gradient_size, }, new Box { RelativeSizeAxes = Axes.Both, Width = gradient_size, Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(0), Color4.White), Anchor = Anchor.TopRight, Origin = Anchor.TopRight, }, } }, }; } [BackgroundDependencyLoader] private void load(OsuColour color, OsuConfigManager config) { boxes.Colour = color.Red; fadePlayfieldWhenHealthLow = config.GetBindable(OsuSetting.FadePlayfieldWhenHealthLow); fadePlayfieldWhenHealthLow.BindValueChanged(_ => updateState()); ShowHealth.BindValueChanged(_ => updateState()); } protected override void LoadComplete() { base.LoadComplete(); updateState(); } private void updateState() { // Don't display ever if the ruleset is not using a draining health display. bool showLayer = HealthProcessor is DrainingHealthProcessor && fadePlayfieldWhenHealthLow.Value && ShowHealth.Value; this.FadeTo(showLayer ? 1 : 0, fade_time, Easing.OutQuint); } protected override void Update() { double target = Math.Clamp(max_alpha * (1 - Current.Value / low_health_threshold), 0, max_alpha); boxes.Alpha = (float)Interpolation.Lerp(boxes.Alpha, target, Clock.ElapsedFrameTime * 0.01f); base.Update(); } } }