diff --git a/osu.Game.Tests/Visual/Menus/TestSceneStarFountain.cs b/osu.Game.Tests/Visual/Menus/TestSceneStarFountain.cs new file mode 100644 index 0000000000..b12f3e7946 --- /dev/null +++ b/osu.Game.Tests/Visual/Menus/TestSceneStarFountain.cs @@ -0,0 +1,52 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Testing; +using osu.Framework.Utils; +using osu.Game.Screens.Menu; + +namespace osu.Game.Tests.Visual.Menus +{ + [TestFixture] + public partial class TestSceneStarFountain : OsuTestScene + { + [SetUpSteps] + public void SetUpSteps() + { + AddStep("make fountains", () => + { + Children = new[] + { + new StarFountain + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + X = 200, + }, + new StarFountain + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + X = -200, + }, + }; + }); + } + + [Test] + public void TestPew() + { + AddRepeatStep("activate fountains sometimes", () => + { + foreach (var fountain in Children.OfType()) + { + if (RNG.NextSingle() > 0.8f) + fountain.Shoot(); + } + }, 150); + } + } +} diff --git a/osu.Game/Screens/Menu/KiaiMenuFountains.cs b/osu.Game/Screens/Menu/KiaiMenuFountains.cs new file mode 100644 index 0000000000..a4d58e398a --- /dev/null +++ b/osu.Game/Screens/Menu/KiaiMenuFountains.cs @@ -0,0 +1,67 @@ +// 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.Audio.Track; +using osu.Framework.Graphics; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Screens.Menu +{ + public partial class KiaiMenuFountains : BeatSyncedContainer + { + [BackgroundDependencyLoader] + private void load() + { + RelativeSizeAxes = Axes.Both; + + Children = new[] + { + new StarFountain + { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + X = 250, + }, + new StarFountain + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + X = -250, + }, + }; + } + + private bool isTriggered; + + private double? lastTrigger; + + protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes) + { + base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes); + + if (effectPoint.KiaiMode && !isTriggered) + { + bool isNearEffectPoint = Math.Abs(BeatSyncSource.Clock.CurrentTime - effectPoint.Time) < 500; + if (isNearEffectPoint) + Shoot(); + } + + isTriggered = effectPoint.KiaiMode; + } + + public void Shoot() + { + if (lastTrigger != null && Clock.CurrentTime - lastTrigger < 500) + return; + + foreach (var fountain in Children.OfType()) + fountain.Shoot(); + + lastTrigger = Clock.CurrentTime; + } + } +} diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index f34a1954a5..baa34d4914 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -136,6 +136,7 @@ namespace osu.Game.Screens.Menu Origin = Anchor.TopRight, Margin = new MarginPadding { Right = 15, Top = 5 } }, + new KiaiMenuFountains(), holdToExitGameOverlay?.CreateProxy() ?? Empty() }); diff --git a/osu.Game/Screens/Menu/StarFountain.cs b/osu.Game/Screens/Menu/StarFountain.cs new file mode 100644 index 0000000000..e8feba4451 --- /dev/null +++ b/osu.Game/Screens/Menu/StarFountain.cs @@ -0,0 +1,102 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Pooling; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Utils; +using osuTK; + +namespace osu.Game.Screens.Menu +{ + public partial class StarFountain : CompositeDrawable + { + private DrawablePool starPool = null!; + private Container starContainer = null!; + + [BackgroundDependencyLoader] + private void load() + { + InternalChildren = new Drawable[] + { + starPool = new DrawablePool(192), + starContainer = new Container() + }; + } + + public void Shoot() + { + int dir = RNG.Next(-1, 2); + + for (int i = 0; i < 192; i++) + { + double offset = i * 3; + + starContainer.Add(starPool.Get(s => + { + s.Velocity = new Vector2(dir * 0.6f + RNG.NextSingle(-0.25f, 0.25f), -RNG.NextSingle(2.2f, 2.4f)); + s.Position = new Vector2(RNG.NextSingle(-5, 5), 50); + s.Hide(); + + using (s.BeginDelayedSequence(offset)) + { + s.FadeIn(); + s.ScaleTo(1); + + double duration = RNG.Next(300, 1300); + + s.FadeOutFromOne(duration, Easing.Out); + s.ScaleTo(RNG.NextSingle(1, 2.8f), duration, Easing.Out); + + s.Expire(); + } + })); + } + } + + private partial class Star : PoolableDrawable + { + public Vector2 Velocity = Vector2.Zero; + + private float rotation; + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + AutoSizeAxes = Axes.Both; + Origin = Anchor.Centre; + + InternalChildren = new Drawable[] + { + new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Blending = BlendingParameters.Additive, + Scale = new Vector2(0.5f), + Texture = textures.Get("Menu/fountain-star") + } + }; + + rotation = RNG.NextSingle(-2f, 2f); + } + + protected override void Update() + { + const float gravity = 0.004f; + + base.Update(); + + float elapsed = (float)Time.Elapsed; + + Position += Velocity * elapsed; + Velocity += new Vector2(0, elapsed * gravity); + + Rotation += rotation * elapsed; + } + } + } +}