// Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; using osu.Framework.Caching; using OpenTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; namespace osu.Game.Rulesets.Mania.Objects.Drawables.Pieces { /// /// Represents length-wise portion of a hold note. /// public class BodyPiece : Container, IHasAccentColour { private readonly Container subtractionLayer; private readonly Drawable background; private readonly BufferedContainer foreground; private readonly BufferedContainer subtractionContainer; public BodyPiece() { Blending = BlendingMode.Additive; Children = new[] { background = new Box { RelativeSizeAxes = Axes.Both }, foreground = new BufferedContainer { Blending = BlendingMode.Additive, RelativeSizeAxes = Axes.Both, CacheDrawnFrameBuffer = true, Children = new Drawable[] { new Box { RelativeSizeAxes = Axes.Both }, subtractionContainer = new BufferedContainer { RelativeSizeAxes = Axes.Both, // This is needed because we're blending with another object BackgroundColour = Color4.White.Opacity(0), CacheDrawnFrameBuffer = true, // The 'hole' is achieved by subtracting the result of this container with the parent Blending = new BlendingParameters { AlphaEquation = BlendingEquation.ReverseSubtract }, Child = subtractionLayer = new CircularContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, // Height computed in Update Width = 1, Masking = true, Child = new Box { RelativeSizeAxes = Axes.Both, Alpha = 0, AlwaysPresent = true } } } } } }; } protected override void LoadComplete() { base.LoadComplete(); updateAccentColour(); } private Color4 accentColour; public Color4 AccentColour { get { return accentColour; } set { if (accentColour == value) return; accentColour = value; updateAccentColour(); } } public bool Hitting { get { return hitting; } set { hitting = value; updateAccentColour(); } } private Cached subtractionCache = new Cached(); public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true) { if ((invalidation & Invalidation.DrawSize) > 0) subtractionCache.Invalidate(); return base.Invalidate(invalidation, source, shallPropagate); } protected override void Update() { base.Update(); if (!subtractionCache.IsValid) { subtractionLayer.Width = 5; subtractionLayer.Height = Math.Max(0, DrawHeight - DrawWidth); subtractionLayer.EdgeEffect = new EdgeEffectParameters { Colour = Color4.White, Type = EdgeEffectType.Glow, Radius = DrawWidth }; foreground.ForceRedraw(); subtractionContainer.ForceRedraw(); subtractionCache.Validate(); } } private bool hitting; private void updateAccentColour() { if (!IsLoaded) return; foreground.Colour = AccentColour.Opacity(0.5f); background.Colour = AccentColour.Opacity(0.7f); const float animation_length = 50; foreground.ClearTransforms(false, nameof(foreground.Colour)); if (hitting) { // wait for the next sync point double synchronisedOffset = animation_length * 2 - Time.Current % (animation_length * 2); using (foreground.BeginDelayedSequence(synchronisedOffset)) foreground.FadeColour(AccentColour.Lighten(0.2f), animation_length).Then().FadeColour(foreground.Colour, animation_length).Loop(); } subtractionCache.Invalidate(); } } }