diff --git a/osu-resources b/osu-resources index 651e598b01..694cb03f19 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit 651e598b016b43e31ab1c1b29d5b30c92361b8d9 +Subproject commit 694cb03f19c93106ed0f2593f3e506e835fb652a diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs b/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs index 21e09f991c..6592b8b313 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModFlashlight.cs @@ -1,12 +1,66 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Graphics; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; +using OpenTK; namespace osu.Game.Rulesets.Catch.Mods { - public class CatchModFlashlight : ModFlashlight + public class CatchModFlashlight : ModFlashlight { public override double ScoreMultiplier => 1.12; + + private const float default_flashlight_size = 350; + + public override Flashlight CreateFlashlight() => new CatchFlashlight(playfield); + + private CatchPlayfield playfield; + + public override void ApplyToRulesetContainer(RulesetContainer rulesetContainer) + { + playfield = (CatchPlayfield)rulesetContainer.Playfield; + base.ApplyToRulesetContainer(rulesetContainer); + } + + private class CatchFlashlight : Flashlight + { + private readonly CatchPlayfield playfield; + + public CatchFlashlight(CatchPlayfield playfield) + { + this.playfield = playfield; + FlashlightSize = new Vector2(0, getSizeFor(0)); + } + + protected override void Update() + { + base.Update(); + + var catcherArea = playfield.CatcherArea; + + FlashlightPosition = catcherArea.ToSpaceOfOtherDrawable(catcherArea.MovableCatcher.DrawPosition, this); + } + + private float getSizeFor(int combo) + { + if (combo > 200) + return default_flashlight_size * 0.8f; + else if (combo > 100) + return default_flashlight_size * 0.9f; + else + return default_flashlight_size; + } + + protected override void OnComboChange(int newCombo) + { + this.TransformTo(nameof(FlashlightSize), new Vector2(0, getSizeFor(newCombo)), FLASHLIGHT_FADE_DURATION); + } + + protected override string FragmentShader => "CircularFlashlight"; + } } } diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 4d1d9d7e5d..05b7cb23a2 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Catch.UI { public const float BASE_WIDTH = 512; - private readonly CatcherArea catcherArea; + internal readonly CatcherArea CatcherArea; public CatchPlayfield(BeatmapDifficulty difficulty, Func> getVisualRepresentation) { @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Catch.UI { RelativeSizeAxes = Axes.Both, }, - catcherArea = new CatcherArea(difficulty) + CatcherArea = new CatcherArea(difficulty) { GetVisualRepresentation = getVisualRepresentation, ExplodingFruitTarget = explodingFruitContainer, @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Catch.UI }; } - public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.AttemptCatch(obj); + public bool CheckIfWeCanCatch(CatchHitObject obj) => CatcherArea.AttemptCatch(obj); public override void Add(DrawableHitObject h) { @@ -63,6 +63,6 @@ namespace osu.Game.Rulesets.Catch.UI } private void onNewResult(DrawableHitObject judgedObject, JudgementResult result) - => catcherArea.OnResult((DrawableCatchHitObject)judgedObject, result); + => CatcherArea.OnResult((DrawableCatchHitObject)judgedObject, result); } } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 06453ac32d..8661a3c162 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Catch.UI { public const float CATCHER_SIZE = 100; - protected readonly Catcher MovableCatcher; + protected internal readonly Catcher MovableCatcher; public Func> GetVisualRepresentation; diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModFadeIn.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModFadeIn.cs index 08815ede09..73942cbb53 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModFadeIn.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModFadeIn.cs @@ -3,6 +3,7 @@ using System; using osu.Game.Graphics; +using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Mania.Mods @@ -16,6 +17,6 @@ namespace osu.Game.Rulesets.Mania.Mods public override string Description => @"Keys appear out of nowhere!"; public override double ScoreMultiplier => 1; public override bool Ranked => true; - public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) }; + public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) }; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs index d7a1bc4fbe..e0e395ce55 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModFlashlight.cs @@ -2,13 +2,60 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using osu.Framework.Caching; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mods; +using OpenTK; namespace osu.Game.Rulesets.Mania.Mods { - public class ManiaModFlashlight : ModFlashlight + public class ManiaModFlashlight : ModFlashlight { public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => new[] { typeof(ModHidden) }; + + private const float default_flashlight_size = 180; + + public override Flashlight CreateFlashlight() => new ManiaFlashlight(); + + private class ManiaFlashlight : Flashlight + { + private readonly Cached flashlightProperties = new Cached(); + + public ManiaFlashlight() + { + FlashlightSize = new Vector2(0, default_flashlight_size); + } + + public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true) + { + if ((invalidation & Invalidation.DrawSize) > 0) + { + flashlightProperties.Invalidate(); + } + + return base.Invalidate(invalidation, source, shallPropagate); + } + + protected override void Update() + { + base.Update(); + + if (!flashlightProperties.IsValid) + { + FlashlightSize = new Vector2(DrawWidth, FlashlightSize.Y); + + FlashlightPosition = DrawPosition + DrawSize / 2; + flashlightProperties.Validate(); + } + } + + protected override void OnComboChange(int newCombo) + { + } + + protected override string FragmentShader => "RectangularFlashlight"; + } } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModHidden.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModHidden.cs index 2ef68a35fa..9bc2502a8f 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModHidden.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModHidden.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Mania.Mods @@ -10,6 +11,6 @@ namespace osu.Game.Rulesets.Mania.Mods { public override string Description => @"Keys fade out before you hit them!"; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) }; + public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) }; } } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCirclePiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCirclePiece.cs index 9c33435285..3008be5e12 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitCircles/Components/HitCirclePiece.cs @@ -3,7 +3,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; @@ -11,11 +10,12 @@ using OpenTK; namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components { - public class HitCirclePiece : CompositeDrawable + public class HitCirclePiece : HitObjectPiece { private readonly HitCircle hitCircle; public HitCirclePiece(HitCircle hitCircle) + : base(hitCircle) { this.hitCircle = hitCircle; Origin = Anchor.Centre; @@ -25,10 +25,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components CornerRadius = Size.X / 2; InternalChild = new RingPiece(); - - hitCircle.PositionChanged += _ => UpdatePosition(); - hitCircle.StackHeightChanged += _ => UpdatePosition(); - hitCircle.ScaleChanged += _ => Scale = new Vector2(hitCircle.Scale); } [BackgroundDependencyLoader] @@ -36,7 +32,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components { Colour = colours.Yellow; - UpdatePosition(); + PositionBindable.BindValueChanged(_ => UpdatePosition(), true); + StackHeightBindable.BindValueChanged(_ => UpdatePosition()); + ScaleBindable.BindValueChanged(v => Scale = new Vector2(v), true); } protected virtual void UpdatePosition() => Position = hitCircle.StackedPosition; diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/HitObjectPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitObjectPiece.cs new file mode 100644 index 0000000000..21ec46895b --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/HitObjectPiece.cs @@ -0,0 +1,36 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Osu.Objects; +using OpenTK; + +namespace osu.Game.Rulesets.Osu.Edit.Blueprints +{ + /// + /// A piece of a blueprint which responds to changes in the state of a . + /// + public abstract class HitObjectPiece : CompositeDrawable + { + protected readonly IBindable PositionBindable = new Bindable(); + protected readonly IBindable StackHeightBindable = new Bindable(); + protected readonly IBindable ScaleBindable = new Bindable(); + + private readonly OsuHitObject hitObject; + + protected HitObjectPiece(OsuHitObject hitObject) + { + this.hitObject = hitObject; + } + + [BackgroundDependencyLoader] + private void load() + { + PositionBindable.BindTo(hitObject.PositionBindable); + StackHeightBindable.BindTo(hitObject.StackHeightBindable); + ScaleBindable.BindTo(hitObject.ScaleBindable); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/SliderPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/SliderPiece.cs new file mode 100644 index 0000000000..ef7254d9c9 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/SliderPiece.cs @@ -0,0 +1,32 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Objects; + +namespace osu.Game.Rulesets.Osu.Edit.Blueprints +{ + /// + /// A piece of a blueprint which responds to changes in the state of a . + /// + public abstract class SliderPiece : HitObjectPiece + { + protected readonly IBindable PathBindable = new Bindable(); + + private readonly Slider slider; + + protected SliderPiece(Slider slider) + : base(slider) + { + this.slider = slider; + } + + [BackgroundDependencyLoader] + private void load() + { + PathBindable.BindTo(slider.PathBindable); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index ab9d81574a..0089c2dddd 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -1,26 +1,31 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Osu.Objects; namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { - public class PathControlPointVisualiser : CompositeDrawable + public class PathControlPointVisualiser : SliderPiece { private readonly Slider slider; private readonly Container pieces; public PathControlPointVisualiser(Slider slider) + : base(slider) { this.slider = slider; InternalChild = pieces = new Container { RelativeSizeAxes = Axes.Both }; + } - slider.PathChanged += _ => updatePathControlPoints(); - updatePathControlPoints(); + [BackgroundDependencyLoader] + private void load() + { + PathBindable.BindValueChanged(_ => updatePathControlPoints(), true); } private void updatePathControlPoints() diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs index 06bc265258..f301dff61a 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderBodyPiece.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using osu.Framework.Allocation; -using osu.Framework.Graphics.Containers; using osu.Game.Graphics; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; @@ -12,12 +11,13 @@ using OpenTK.Graphics; namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { - public class SliderBodyPiece : CompositeDrawable + public class SliderBodyPiece : SliderPiece { private readonly Slider slider; private readonly ManualSliderBody body; public SliderBodyPiece(Slider slider) + : base(slider) { this.slider = slider; @@ -26,9 +26,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components AccentColour = Color4.Transparent, PathWidth = slider.Scale * 64 }; - - slider.PositionChanged += _ => updatePosition(); - slider.ScaleChanged += _ => body.PathWidth = slider.Scale * 64; } [BackgroundDependencyLoader] @@ -36,7 +33,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { body.BorderColour = colours.Yellow; - updatePosition(); + PositionBindable.BindValueChanged(_ => updatePosition(), true); + ScaleBindable.BindValueChanged(v => body.PathWidth = v * 64, true); } private void updatePosition() => Position = slider.StackedPosition; diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderCirclePiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderCirclePiece.cs index 1ee765f5e0..205ac6bea3 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/SliderCirclePiece.cs @@ -1,6 +1,9 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components; using osu.Game.Rulesets.Osu.Objects; @@ -8,6 +11,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { public class SliderCirclePiece : HitCirclePiece { + private readonly IBindable pathBindable = new Bindable(); + private readonly Slider slider; private readonly SliderPosition position; @@ -16,8 +21,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { this.slider = slider; this.position = position; + } - slider.PathChanged += _ => UpdatePosition(); + [BackgroundDependencyLoader] + private void load() + { + pathBindable.BindTo(slider.PathBindable); + pathBindable.BindValueChanged(_ => UpdatePosition(), true); } protected override void UpdatePosition() diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/Components/SpinnerPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/Components/SpinnerPiece.cs index bd63a3e607..77d42133d2 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/Components/SpinnerPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/Components/SpinnerPiece.cs @@ -12,12 +12,14 @@ using OpenTK; namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners.Components { - public class SpinnerPiece : CompositeDrawable + public class SpinnerPiece : HitObjectPiece { private readonly Spinner spinner; private readonly CircularContainer circle; + private readonly RingPiece ring; public SpinnerPiece(Spinner spinner) + : base(spinner) { this.spinner = spinner; @@ -27,7 +29,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners.Components FillMode = FillMode.Fit; Size = new Vector2(1.3f); - RingPiece ring; InternalChildren = new Drawable[] { circle = new CircularContainer @@ -45,18 +46,16 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners.Components }; ring.Scale = new Vector2(spinner.Scale); - - spinner.PositionChanged += _ => updatePosition(); - spinner.StackHeightChanged += _ => updatePosition(); - spinner.ScaleChanged += _ => ring.Scale = new Vector2(spinner.Scale); - - updatePosition(); } [BackgroundDependencyLoader] private void load(OsuColour colours) { Colour = colours.Yellow; + + PositionBindable.BindValueChanged(_ => updatePosition(), true); + StackHeightBindable.BindValueChanged(_ => updatePosition()); + ScaleBindable.BindValueChanged(v => ring.Scale = new Vector2(v), true); } private void updatePosition() => Position = spinner.Position; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs index a337439593..f425b3c53d 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs @@ -1,12 +1,52 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Graphics; +using osu.Framework.Input; +using osu.Framework.Input.Events; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Objects; +using OpenTK; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModFlashlight : ModFlashlight + public class OsuModFlashlight : ModFlashlight { public override double ScoreMultiplier => 1.12; + + private const float default_flashlight_size = 180; + + public override Flashlight CreateFlashlight() => new OsuFlashlight(); + + private class OsuFlashlight : Flashlight, IRequireHighFrequencyMousePosition + { + public OsuFlashlight() + { + FlashlightSize = new Vector2(0, getSizeFor(0)); + } + + protected override bool OnMouseMove(MouseMoveEvent e) + { + FlashlightPosition = e.MousePosition; + return base.OnMouseMove(e); + } + + private float getSizeFor(int combo) + { + if (combo > 200) + return default_flashlight_size * 0.8f; + else if (combo > 100) + return default_flashlight_size * 0.9f; + else + return default_flashlight_size; + } + + protected override void OnComboChange(int newCombo) + { + this.TransformTo(nameof(FlashlightSize), new Vector2(0, getSizeFor(newCombo)), FLASHLIGHT_FADE_DURATION); + } + + protected override string FragmentShader => "CircularFlashlight"; + } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index e663989eeb..bf662adf1e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -2,6 +2,8 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; @@ -21,6 +23,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private readonly NumberPiece number; private readonly GlowPiece glow; + private readonly IBindable positionBindable = new Bindable(); + private readonly IBindable stackHeightBindable = new Bindable(); + private readonly IBindable scaleBindable = new Bindable(); + public DrawableHitCircle(HitCircle h) : base(h) { @@ -59,10 +65,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables //may not be so correct Size = circle.DrawSize; + } - HitObject.PositionChanged += _ => Position = HitObject.StackedPosition; - HitObject.StackHeightChanged += _ => Position = HitObject.StackedPosition; - HitObject.ScaleChanged += s => Scale = new Vector2(s); + [BackgroundDependencyLoader] + private void load() + { + positionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition); + stackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition); + scaleBindable.BindValueChanged(v => Scale = new Vector2(v)); + + positionBindable.BindTo(HitObject.PositionBindable); + stackHeightBindable.BindTo(HitObject.StackHeightBindable); + scaleBindable.BindTo(HitObject.ScaleBindable); } public override Color4 AccentColour diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index a90182cecb..d304374614 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -8,8 +8,10 @@ using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Framework.Graphics.Containers; using osu.Game.Configuration; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; using OpenTK.Graphics; @@ -26,6 +28,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public readonly SnakingSliderBody Body; public readonly SliderBall Ball; + private readonly IBindable positionBindable = new Bindable(); + private readonly IBindable scaleBindable = new Bindable(); + private readonly IBindable pathBindable = new Bindable(); + public DrawableSlider(Slider s) : base(s) { @@ -83,15 +89,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables components.Add(drawableRepeatPoint); AddNested(drawableRepeatPoint); } + } - HitObject.PositionChanged += _ => Position = HitObject.StackedPosition; - HitObject.ScaleChanged += _ => + [BackgroundDependencyLoader] + private void load(OsuConfigManager config) + { + config.BindWith(OsuSetting.SnakingInSliders, Body.SnakingIn); + config.BindWith(OsuSetting.SnakingOutSliders, Body.SnakingOut); + + positionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition); + scaleBindable.BindValueChanged(v => { Body.PathWidth = HitObject.Scale * 64; Ball.Scale = new Vector2(HitObject.Scale); - }; + }); - slider.PathChanged += _ => Body.Refresh(); + positionBindable.BindTo(HitObject.PositionBindable); + scaleBindable.BindTo(HitObject.ScaleBindable); + pathBindable.BindTo(slider.PathBindable); + + pathBindable.BindValueChanged(_ => Body.Refresh()); } public override Color4 AccentColour @@ -108,13 +125,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } - [BackgroundDependencyLoader] - private void load(OsuConfigManager config) - { - config.BindWith(OsuSetting.SnakingInSliders, Body.SnakingIn); - config.BindWith(OsuSetting.SnakingOutSliders, Body.SnakingOut); - } - public bool Tracking; protected override void Update() diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs index b933364887..d3c006e74d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs @@ -2,6 +2,9 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using OpenTK; @@ -9,17 +12,25 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { public class DrawableSliderHead : DrawableHitCircle { + private readonly IBindable positionBindable = new Bindable(); + private readonly IBindable pathBindable = new Bindable(); + private readonly Slider slider; public DrawableSliderHead(Slider slider, HitCircle h) : base(h) { this.slider = slider; + } - h.PositionChanged += _ => updatePosition(); - slider.PathChanged += _ => updatePosition(); + [BackgroundDependencyLoader] + private void load() + { + positionBindable.BindTo(HitObject.PositionBindable); + pathBindable.BindTo(slider.PathBindable); - updatePosition(); + positionBindable.BindValueChanged(_ => updatePosition()); + pathBindable.BindValueChanged(_ => updatePosition(), true); } protected override void Update() diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs index 6946a55d8e..eb7a5964c5 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs @@ -1,8 +1,11 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; +using OpenTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -17,6 +20,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public bool Tracking { get; set; } + private readonly IBindable positionBindable = new Bindable(); + private readonly IBindable pathBindable = new Bindable(); + public DrawableSliderTail(Slider slider, SliderTailCircle hitCircle) : base(hitCircle) { @@ -29,10 +35,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables AlwaysPresent = true; - hitCircle.PositionChanged += _ => updatePosition(); - slider.PathChanged += _ => updatePosition(); + positionBindable.BindTo(hitCircle.PositionBindable); + pathBindable.BindTo(slider.PathBindable); - updatePosition(); + positionBindable.BindValueChanged(_ => updatePosition()); + pathBindable.BindValueChanged(_ => updatePosition(), true); } protected override void CheckForResult(bool userTriggered, double timeOffset) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index f3846bd52f..56a85c3983 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -11,6 +11,7 @@ using OpenTK.Graphics; using osu.Game.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Allocation; +using osu.Framework.Configuration; using osu.Game.Screens.Ranking; using osu.Game.Rulesets.Scoring; @@ -36,6 +37,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private readonly Color4 baseColour = OsuColour.FromHex(@"002c3c"); private readonly Color4 fillColour = OsuColour.FromHex(@"005b7c"); + private readonly IBindable positionBindable = new Bindable(); + private Color4 normalColour; private Color4 completeColour; @@ -112,8 +115,23 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Alpha = 0 } }; + } - s.PositionChanged += _ => Position = s.Position; + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + normalColour = baseColour; + + Background.AccentColour = normalColour; + + completeColour = colours.YellowLight.Opacity(0.75f); + + Disc.AccentColour = fillColour; + circle.Colour = colours.BlueDark; + glow.Colour = colours.BlueDark; + + positionBindable.BindValueChanged(v => Position = v); + positionBindable.BindTo(HitObject.PositionBindable); } public float Progress => MathHelper.Clamp(Disc.RotationAbsolute / 360 / Spinner.SpinsRequired, 0, 1); @@ -153,20 +171,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables }); } - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - normalColour = baseColour; - - Background.AccentColour = normalColour; - - completeColour = colours.YellowLight.Opacity(0.75f); - - Disc.AccentColour = fillColour; - circle.Colour = colours.BlueDark; - glow.Colour = colours.BlueDark; - } - protected override void Update() { Disc.Tracking = OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false; diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index 61d199a7dc..b7f9b2fa47 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -1,7 +1,7 @@ // 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.Configuration; using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; using OpenTK; @@ -14,26 +14,15 @@ namespace osu.Game.Rulesets.Osu.Objects { public const double OBJECT_RADIUS = 64; - public event Action PositionChanged; - public event Action StackHeightChanged; - public event Action ScaleChanged; - public double TimePreempt = 600; public double TimeFadeIn = 400; - private Vector2 position; + public readonly Bindable PositionBindable = new Bindable(); public virtual Vector2 Position { - get => position; - set - { - if (position == value) - return; - position = value; - - PositionChanged?.Invoke(value); - } + get => PositionBindable; + set => PositionBindable.Value = value; } public float X => Position.X; @@ -45,38 +34,24 @@ namespace osu.Game.Rulesets.Osu.Objects public Vector2 StackedEndPosition => EndPosition + StackOffset; - private int stackHeight; + public readonly Bindable StackHeightBindable = new Bindable(); public int StackHeight { - get => stackHeight; - set - { - if (stackHeight == value) - return; - stackHeight = value; - - StackHeightChanged?.Invoke(value); - } + get => StackHeightBindable; + set => StackHeightBindable.Value = value; } public Vector2 StackOffset => new Vector2(StackHeight * Scale * -6.4f); public double Radius => OBJECT_RADIUS * Scale; - private float scale = 1; + public readonly Bindable ScaleBindable = new Bindable(1); public float Scale { - get => scale; - set - { - if (scale == value) - return; - scale = value; - - ScaleChanged?.Invoke(value); - } + get => ScaleBindable; + set => ScaleBindable.Value = value; } public virtual bool NewCombo { get; set; } diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index cf57f24b83..6471c8c572 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -7,6 +7,7 @@ using osu.Game.Rulesets.Objects.Types; using System.Collections.Generic; using osu.Game.Rulesets.Objects; using System.Linq; +using osu.Framework.Configuration; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -22,8 +23,6 @@ namespace osu.Game.Rulesets.Osu.Objects /// private const float base_scoring_distance = 100; - public event Action PathChanged; - public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity; public double Duration => EndTime - StartTime; @@ -52,20 +51,12 @@ namespace osu.Game.Rulesets.Osu.Objects } } - private SliderPath path; + public readonly Bindable PathBindable = new Bindable(); public SliderPath Path { - get => path; - set - { - path = value; - - PathChanged?.Invoke(value); - - if (TailCircle != null) - TailCircle.Position = EndPosition; - } + get => PathBindable.Value; + set => PathBindable.Value = value; } public double Distance => Path.Distance; @@ -164,7 +155,7 @@ namespace osu.Game.Rulesets.Osu.Objects ComboIndex = ComboIndex, }; - TailCircle = new SliderTailCircle + TailCircle = new SliderTailCircle(this) { StartTime = EndTime, Position = EndPosition, diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs index b567bd8423..74a7a8d446 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs @@ -1,13 +1,23 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Configuration; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Judgements; namespace osu.Game.Rulesets.Osu.Objects { public class SliderTailCircle : SliderCircle { + private readonly IBindable pathBindable = new Bindable(); + + public SliderTailCircle(Slider slider) + { + pathBindable.BindTo(slider.PathBindable); + pathBindable.BindValueChanged(_ => Position = slider.EndPosition); + } + public override Judgement CreateJudgement() => new OsuSliderTailJudgement(); } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs index f530b6725c..734d5d0ad7 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs @@ -8,6 +8,7 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.Difficulty { @@ -82,7 +83,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty if (mods.Any(m => m is ModHidden)) strainValue *= 1.025; - if (mods.Any(m => m is ModFlashlight)) + if (mods.Any(m => m is ModFlashlight)) // Apply length bonus again if flashlight is on simply because it becomes a lot harder on longer maps. strainValue *= 1.05 * lengthBonus; diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs index 49f7786f59..5e865d7727 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModFlashlight.cs @@ -1,12 +1,80 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Caching; +using osu.Framework.Graphics; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.UI; +using osu.Game.Rulesets.UI; +using OpenTK; namespace osu.Game.Rulesets.Taiko.Mods { - public class TaikoModFlashlight : ModFlashlight + public class TaikoModFlashlight : ModFlashlight { public override double ScoreMultiplier => 1.12; + + private const float default_flashlight_size = 250; + + public override Flashlight CreateFlashlight() => new TaikoFlashlight(playfield); + + private TaikoPlayfield playfield; + + public override void ApplyToRulesetContainer(RulesetContainer rulesetContainer) + { + playfield = (TaikoPlayfield)rulesetContainer.Playfield; + base.ApplyToRulesetContainer(rulesetContainer); + } + + private class TaikoFlashlight : Flashlight + { + private readonly Cached flashlightProperties = new Cached(); + private readonly TaikoPlayfield taikoPlayfield; + + public TaikoFlashlight(TaikoPlayfield taikoPlayfield) + { + this.taikoPlayfield = taikoPlayfield; + FlashlightSize = new Vector2(0, getSizeFor(0)); + } + + private float getSizeFor(int combo) + { + if (combo > 200) + return default_flashlight_size * 0.8f; + else if (combo > 100) + return default_flashlight_size * 0.9f; + else + return default_flashlight_size; + } + + protected override void OnComboChange(int newCombo) + { + this.TransformTo(nameof(FlashlightSize), new Vector2(0, getSizeFor(newCombo)), FLASHLIGHT_FADE_DURATION); + } + + protected override string FragmentShader => "CircularFlashlight"; + + public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true) + { + if ((invalidation & Invalidation.DrawSize) > 0) + { + flashlightProperties.Invalidate(); + } + + return base.Invalidate(invalidation, source, shallPropagate); + } + + protected override void Update() + { + base.Update(); + + if (!flashlightProperties.IsValid) + { + FlashlightPosition = taikoPlayfield.HitTarget.ToSpaceOfOtherDrawable(taikoPlayfield.HitTarget.OriginPosition, this); + flashlightProperties.Validate(); + } + } + } } } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 561afb0180..84e48448e0 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -41,6 +41,7 @@ namespace osu.Game.Rulesets.Taiko.UI private readonly Container hitExplosionContainer; private readonly Container kiaiExplosionContainer; private readonly JudgementContainer judgementContainer; + internal readonly HitTarget HitTarget; private readonly Container topLevelHitContainer; @@ -102,7 +103,7 @@ namespace osu.Game.Rulesets.Taiko.UI FillMode = FillMode.Fit, Blending = BlendingMode.Additive, }, - new HitTarget + HitTarget = new HitTarget { Anchor = Anchor.CentreLeft, Origin = Anchor.Centre, diff --git a/osu.Game/Graphics/UserInterface/PercentageCounter.cs b/osu.Game/Graphics/UserInterface/PercentageCounter.cs index ef3fc156e7..b1514cb8c8 100644 --- a/osu.Game/Graphics/UserInterface/PercentageCounter.cs +++ b/osu.Game/Graphics/UserInterface/PercentageCounter.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using osu.Framework.Allocation; namespace osu.Game.Graphics.UserInterface { @@ -25,6 +26,9 @@ namespace osu.Game.Graphics.UserInterface Current.Value = DisplayedCount = 1.0f; } + [BackgroundDependencyLoader] + private void load(OsuColour colours) => AccentColour = colours.BlueLighter; + protected override string FormatCount(double count) { return $@"{count:P2}"; diff --git a/osu.Game/Graphics/UserInterface/ScoreCounter.cs b/osu.Game/Graphics/UserInterface/ScoreCounter.cs index df26f81629..8bf30c32c6 100644 --- a/osu.Game/Graphics/UserInterface/ScoreCounter.cs +++ b/osu.Game/Graphics/UserInterface/ScoreCounter.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Allocation; using osu.Framework.Graphics; namespace osu.Game.Graphics.UserInterface @@ -31,6 +32,9 @@ namespace osu.Game.Graphics.UserInterface LeadingZeroes = leading; } + [BackgroundDependencyLoader] + private void load(OsuColour colours) => AccentColour = colours.BlueLighter; + protected override double GetProportionalDuration(double currentValue, double newValue) { return currentValue > newValue ? currentValue - newValue : newValue - currentValue; diff --git a/osu.Game/Graphics/UserInterface/SimpleComboCounter.cs b/osu.Game/Graphics/UserInterface/SimpleComboCounter.cs index cf55240e71..ddc48c9be7 100644 --- a/osu.Game/Graphics/UserInterface/SimpleComboCounter.cs +++ b/osu.Game/Graphics/UserInterface/SimpleComboCounter.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using osu.Framework.Allocation; namespace osu.Game.Graphics.UserInterface { @@ -17,6 +18,9 @@ namespace osu.Game.Graphics.UserInterface Current.Value = DisplayedCount = 0; } + [BackgroundDependencyLoader] + private void load(OsuColour colours) => AccentColour = colours.BlueLighter; + protected override string FormatCount(int count) { return $@"{count}x"; diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index 223263195c..5e5353bfdd 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -1,11 +1,27 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.OpenGL.Vertices; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Shaders; +using osu.Framework.Graphics.Textures; +using osu.Game.Beatmaps.Timing; using osu.Game.Graphics; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using OpenTK; +using OpenTK.Graphics; namespace osu.Game.Rulesets.Mods { - public abstract class ModFlashlight : Mod + public abstract class ModFlashlight : Mod, IApplicableToRulesetContainer, IApplicableToScoreProcessor + where T : HitObject { public override string Name => "Flashlight"; public override string ShortenedName => "FL"; @@ -13,5 +29,125 @@ namespace osu.Game.Rulesets.Mods public override ModType Type => ModType.DifficultyIncrease; public override string Description => "Restricted view area."; public override bool Ranked => true; + + public const double FLASHLIGHT_FADE_DURATION = 800; + protected readonly BindableInt Combo = new BindableInt(); + + public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) + { + Combo.BindTo(scoreProcessor.Combo); + } + + public virtual void ApplyToRulesetContainer(RulesetContainer rulesetContainer) + { + var flashlight = CreateFlashlight(); + flashlight.Combo = Combo; + flashlight.RelativeSizeAxes = Axes.Both; + flashlight.Colour = Color4.Black; + rulesetContainer.KeyBindingInputManager.Add(flashlight); + + flashlight.Breaks = rulesetContainer.Beatmap.Breaks; + } + + public abstract Flashlight CreateFlashlight(); + + public abstract class Flashlight : Drawable + { + internal BindableInt Combo; + private Shader shader; + + protected override DrawNode CreateDrawNode() => new FlashlightDrawNode(); + + public override bool RemoveCompletedTransforms => false; + + public List Breaks; + + protected override void ApplyDrawNode(DrawNode node) + { + base.ApplyDrawNode(node); + + var flashNode = (FlashlightDrawNode)node; + + flashNode.Shader = shader; + flashNode.ScreenSpaceDrawQuad = ScreenSpaceDrawQuad; + flashNode.FlashlightPosition = Vector2Extensions.Transform(FlashlightPosition, DrawInfo.Matrix); + flashNode.FlashlightSize = Vector2Extensions.Transform(FlashlightSize, DrawInfo.Matrix); + } + + [BackgroundDependencyLoader] + private void load(ShaderManager shaderManager) + { + shader = shaderManager.Load("PositionAndColour", FragmentShader); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Combo.ValueChanged += OnComboChange; + + this.FadeInFromZero(FLASHLIGHT_FADE_DURATION); + + foreach (var breakPeriod in Breaks) + { + if (breakPeriod.Duration < FLASHLIGHT_FADE_DURATION * 2) continue; + + this.Delay(breakPeriod.StartTime + FLASHLIGHT_FADE_DURATION).FadeOutFromOne(FLASHLIGHT_FADE_DURATION); + this.Delay(breakPeriod.EndTime - FLASHLIGHT_FADE_DURATION).FadeInFromZero(FLASHLIGHT_FADE_DURATION); + } + } + + protected abstract void OnComboChange(int newCombo); + + protected abstract string FragmentShader { get; } + + private Vector2 flashlightPosition; + protected Vector2 FlashlightPosition + { + get => flashlightPosition; + set + { + if (flashlightPosition == value) return; + + flashlightPosition = value; + Invalidate(Invalidation.DrawNode); + } + } + + private Vector2 flashlightSize; + protected Vector2 FlashlightSize + { + get => flashlightSize; + set + { + if (flashlightSize == value) return; + + flashlightSize = value; + Invalidate(Invalidation.DrawNode); + } + } + } + + private class FlashlightDrawNode : DrawNode + { + public Shader Shader; + public Quad ScreenSpaceDrawQuad; + public Vector2 FlashlightPosition; + public Vector2 FlashlightSize; + + public override void Draw(Action vertexAction) + { + base.Draw(vertexAction); + + Shader.Bind(); + + Shader.GetUniform("flashlightPos").UpdateValue(ref FlashlightPosition); + Shader.GetUniform("flashlightSize").UpdateValue(ref FlashlightSize); + + Texture.WhitePixel.DrawQuad(ScreenSpaceDrawQuad, DrawColourInfo.Colour, vertexAction: vertexAction); + + Shader.Unbind(); + } + } } } diff --git a/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs b/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs index b551b1f7a6..850ab9f641 100644 --- a/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -92,6 +93,13 @@ namespace osu.Game.Screens.Play.HUD }; } + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + AccentColour = colours.BlueLighter; + GlowColour = colours.BlueDarker; + } + public void Flash(JudgementResult result) { if (result.Type == HitResult.Miss) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 93d92d062d..beed14b293 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -9,7 +9,6 @@ using osu.Framework.Input.Events; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Configuration; -using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; @@ -89,7 +88,7 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader(true)] - private void load(OsuConfigManager config, NotificationOverlay notificationOverlay, OsuColour colours) + private void load(OsuConfigManager config, NotificationOverlay notificationOverlay) { showHud = config.GetBindable(OsuSetting.ShowInterface); showHud.ValueChanged += hudVisibility => content.FadeTo(hudVisibility ? 1 : 0, duration); @@ -104,18 +103,6 @@ namespace osu.Game.Screens.Play Text = @"The score overlay is currently disabled. You can toggle this by pressing Shift+Tab." }); } - - // todo: the stuff below should probably not be in this base implementation, but in each individual class. - ComboCounter.AccentColour = colours.BlueLighter; - AccuracyCounter.AccentColour = colours.BlueLighter; - ScoreCounter.AccentColour = colours.BlueLighter; - - var shd = HealthDisplay as StandardHealthDisplay; - if (shd != null) - { - shd.AccentColour = colours.BlueLighter; - shd.GlowColour = colours.BlueDarker; - } } protected override void LoadComplete()