mirror of
https://github.com/ppy/osu.git
synced 2026-05-13 21:53:29 +08:00
Compare commits
8 Commits
pp-dev
...
2024.1219.1
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Screens.Menu;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Menus
|
||||
{
|
||||
[TestFixture]
|
||||
public partial class TestSceneIntroChristmas : IntroTestScene
|
||||
{
|
||||
protected override bool IntroReliesOnTrack => true;
|
||||
protected override IntroScreen CreateScreen() => new IntroChristmas();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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.Allocation;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Screens.Menu;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Menus
|
||||
{
|
||||
public partial class TestSceneMainMenuSeasonalLighting : OsuTestScene
|
||||
{
|
||||
[Resolved]
|
||||
private BeatmapManager beatmaps { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private RealmAccess realm { get; set; } = null!;
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("prepare beatmap", () =>
|
||||
{
|
||||
var setInfo = beatmaps.QueryBeatmapSet(b => b.Protected && b.Hash == "7e26183e72a496f672c3a21292e6b469fdecd084d31c259ea10a31df5b46cd77");
|
||||
|
||||
Beatmap.Value = beatmaps.GetWorkingBeatmap(setInfo!.Value.Beatmaps.First());
|
||||
});
|
||||
|
||||
AddStep("create lighting", () => Child = new MainMenuSeasonalLighting());
|
||||
|
||||
AddStep("restart beatmap", () =>
|
||||
{
|
||||
Beatmap.Value.Track.Start();
|
||||
Beatmap.Value.Track.Seek(4000);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBasic()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,22 +4,31 @@
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
public partial class TestSceneOsuLogo : OsuTestScene
|
||||
{
|
||||
private OsuLogo? logo;
|
||||
|
||||
[Test]
|
||||
public void TestBasic()
|
||||
{
|
||||
AddStep("Add logo", () =>
|
||||
{
|
||||
Child = new OsuLogo
|
||||
Child = logo = new OsuLogo
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
};
|
||||
});
|
||||
|
||||
AddSliderStep("scale", 0.1, 2, 1, scale =>
|
||||
{
|
||||
if (logo != null)
|
||||
Child.Scale = new Vector2((float)scale);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,9 @@ namespace osu.Game.Screens
|
||||
|
||||
private IntroScreen getIntroSequence()
|
||||
{
|
||||
if (SeasonalUI.ENABLED)
|
||||
return new IntroChristmas(createMainMenu);
|
||||
|
||||
if (introSequence == IntroSequence.Random)
|
||||
introSequence = (IntroSequence)RNG.Next(0, (int)IntroSequence.Random);
|
||||
|
||||
|
||||
@@ -0,0 +1,328 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. 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.Audio.Track;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Menu
|
||||
{
|
||||
public partial class IntroChristmas : IntroScreen
|
||||
{
|
||||
protected override string BeatmapHash => "7e26183e72a496f672c3a21292e6b469fdecd084d31c259ea10a31df5b46cd77";
|
||||
|
||||
protected override string BeatmapFile => "christmas2024.osz";
|
||||
|
||||
private const double beat_length = 60000 / 172.0;
|
||||
private const double offset = 5924;
|
||||
|
||||
protected override string SeeyaSampleName => "Intro/Welcome/seeya";
|
||||
|
||||
private TrianglesIntroSequence intro = null!;
|
||||
|
||||
public IntroChristmas(Func<MainMenu>? createNextScreen = null)
|
||||
: base(createNextScreen)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void LogoArriving(OsuLogo logo, bool resuming)
|
||||
{
|
||||
base.LogoArriving(logo, resuming);
|
||||
|
||||
if (!resuming)
|
||||
{
|
||||
PrepareMenuLoad();
|
||||
|
||||
var decouplingClock = new DecouplingFramedClock(UsingThemedIntro ? Track : null);
|
||||
|
||||
LoadComponentAsync(intro = new TrianglesIntroSequence(logo, () => FadeInBackground())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Clock = new InterpolatingFramedClock(decouplingClock),
|
||||
LoadMenu = LoadMenu
|
||||
}, _ =>
|
||||
{
|
||||
AddInternal(intro);
|
||||
|
||||
// There is a chance that the intro timed out before being displayed, and this scheduled callback could
|
||||
// happen during the outro rather than intro.
|
||||
// In such a scenario, we don't want to play the intro sample, nor attempt to start the intro track
|
||||
// (that may have already been since disposed by MusicController).
|
||||
if (DidLoadMenu)
|
||||
return;
|
||||
|
||||
// If the user has requested no theme, fallback to the same intro voice and delay as IntroCircles.
|
||||
// The triangles intro voice and theme are combined which makes it impossible to use.
|
||||
StartTrack();
|
||||
|
||||
// no-op for the case of themed intro, no harm in calling for both scenarios as a safety measure.
|
||||
decouplingClock.Start();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnSuspending(ScreenTransitionEvent e)
|
||||
{
|
||||
base.OnSuspending(e);
|
||||
|
||||
// important as there is a clock attached to a track which will likely be disposed before returning to this screen.
|
||||
intro.Expire();
|
||||
}
|
||||
|
||||
private partial class TrianglesIntroSequence : CompositeDrawable
|
||||
{
|
||||
private readonly OsuLogo logo;
|
||||
private readonly Action showBackgroundAction;
|
||||
private OsuSpriteText welcomeText = null!;
|
||||
|
||||
private Container logoContainerSecondary = null!;
|
||||
private LazerLogo lazerLogo = null!;
|
||||
|
||||
private Drawable triangles = null!;
|
||||
|
||||
public Action LoadMenu = null!;
|
||||
|
||||
[Resolved]
|
||||
private OsuGameBase game { get; set; } = null!;
|
||||
|
||||
public TrianglesIntroSequence(OsuLogo logo, Action showBackgroundAction)
|
||||
{
|
||||
this.logo = logo;
|
||||
this.showBackgroundAction = showBackgroundAction;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
InternalChildren = new[]
|
||||
{
|
||||
welcomeText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Padding = new MarginPadding { Bottom = 10 },
|
||||
Font = OsuFont.GetFont(weight: FontWeight.Light, size: 42),
|
||||
Alpha = 1,
|
||||
Spacing = new Vector2(5),
|
||||
},
|
||||
logoContainerSecondary = new Container
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Child = lazerLogo = new LazerLogo
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre
|
||||
}
|
||||
},
|
||||
triangles = new CircularContainer
|
||||
{
|
||||
Alpha = 0,
|
||||
Masking = true,
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(960),
|
||||
Child = new GlitchingTriangles
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static double getTimeForBeat(int beat) => offset + beat_length * beat;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
lazerLogo.Hide();
|
||||
|
||||
using (BeginAbsoluteSequence(0))
|
||||
{
|
||||
using (BeginDelayedSequence(getTimeForBeat(-16)))
|
||||
welcomeText.FadeIn().OnComplete(t => t.Text = "welcome to osu!");
|
||||
|
||||
using (BeginDelayedSequence(getTimeForBeat(-15)))
|
||||
welcomeText.FadeIn().OnComplete(t => t.Text = "");
|
||||
|
||||
using (BeginDelayedSequence(getTimeForBeat(-14)))
|
||||
welcomeText.FadeIn().OnComplete(t => t.Text = "welcome to osu!");
|
||||
|
||||
using (BeginDelayedSequence(getTimeForBeat(-13)))
|
||||
welcomeText.FadeIn().OnComplete(t => t.Text = "");
|
||||
|
||||
using (BeginDelayedSequence(getTimeForBeat(-12)))
|
||||
welcomeText.FadeIn().OnComplete(t => t.Text = "merry christmas!");
|
||||
|
||||
using (BeginDelayedSequence(getTimeForBeat(-11)))
|
||||
welcomeText.FadeIn().OnComplete(t => t.Text = "");
|
||||
|
||||
using (BeginDelayedSequence(getTimeForBeat(-10)))
|
||||
welcomeText.FadeIn().OnComplete(t => t.Text = "merry osumas!");
|
||||
|
||||
using (BeginDelayedSequence(getTimeForBeat(-9)))
|
||||
{
|
||||
welcomeText.FadeIn().OnComplete(t => t.Text = "");
|
||||
}
|
||||
|
||||
lazerLogo.Scale = new Vector2(0.2f);
|
||||
triangles.Scale = new Vector2(0.2f);
|
||||
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
using (BeginDelayedSequence(getTimeForBeat(-8 + i)))
|
||||
{
|
||||
triangles.FadeIn();
|
||||
|
||||
lazerLogo.ScaleTo(new Vector2(0.2f + (i + 1) / 8f * 0.3f), beat_length * 1, Easing.OutQuint);
|
||||
triangles.ScaleTo(new Vector2(0.2f + (i + 1) / 8f * 0.3f), beat_length * 1, Easing.OutQuint);
|
||||
lazerLogo.FadeTo((i + 1) * 0.06f);
|
||||
lazerLogo.TransformTo(nameof(LazerLogo.Progress), (i + 1) / 10f);
|
||||
}
|
||||
}
|
||||
|
||||
GameWideFlash flash = new GameWideFlash();
|
||||
|
||||
using (BeginDelayedSequence(getTimeForBeat(-2)))
|
||||
{
|
||||
lazerLogo.FadeIn().OnComplete(_ => game.Add(flash));
|
||||
}
|
||||
|
||||
flash.FadeInCompleted = () =>
|
||||
{
|
||||
logoContainerSecondary.Remove(lazerLogo, true);
|
||||
triangles.FadeOut();
|
||||
logo.FadeIn();
|
||||
showBackgroundAction();
|
||||
LoadMenu();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private partial class GameWideFlash : Box
|
||||
{
|
||||
public Action? FadeInCompleted;
|
||||
|
||||
public GameWideFlash()
|
||||
{
|
||||
Colour = Color4.White;
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
Blending = BlendingParameters.Additive;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Alpha = 0;
|
||||
|
||||
this.FadeTo(0.5f, beat_length * 2, Easing.In)
|
||||
.OnComplete(_ => FadeInCompleted?.Invoke());
|
||||
|
||||
this.Delay(beat_length * 2)
|
||||
.Then()
|
||||
.FadeOutFromOne(3000, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
|
||||
private partial class LazerLogo : CompositeDrawable
|
||||
{
|
||||
private LogoAnimation highlight = null!;
|
||||
private LogoAnimation background = null!;
|
||||
|
||||
public float Progress
|
||||
{
|
||||
get => background.AnimationProgress;
|
||||
set
|
||||
{
|
||||
background.AnimationProgress = value;
|
||||
highlight.AnimationProgress = value;
|
||||
}
|
||||
}
|
||||
|
||||
public LazerLogo()
|
||||
{
|
||||
Size = new Vector2(960);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(LargeTextureStore textures)
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
highlight = new LogoAnimation
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Texture = textures.Get(@"Intro/Triangles/logo-highlight"),
|
||||
Colour = Color4.White,
|
||||
},
|
||||
background = new LogoAnimation
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Texture = textures.Get(@"Intro/Triangles/logo-background"),
|
||||
Colour = OsuColour.Gray(0.6f),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private partial class GlitchingTriangles : BeatSyncedContainer
|
||||
{
|
||||
private int beatsHandled;
|
||||
|
||||
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
|
||||
{
|
||||
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);
|
||||
|
||||
Divisor = beatsHandled < 4 ? 1 : 4;
|
||||
|
||||
for (int i = 0; i < (beatsHandled + 1); i++)
|
||||
{
|
||||
float angle = (float)(RNG.NextDouble() * 2 * Math.PI);
|
||||
float randomRadius = (float)(Math.Sqrt(RNG.NextDouble()));
|
||||
|
||||
float x = 0.5f + 0.5f * randomRadius * (float)Math.Cos(angle);
|
||||
float y = 0.5f + 0.5f * randomRadius * (float)Math.Sin(angle);
|
||||
|
||||
Color4 christmasColour = RNG.NextBool() ? SeasonalUI.PRIMARY_COLOUR_1 : SeasonalUI.PRIMARY_COLOUR_2;
|
||||
|
||||
Drawable triangle = new Triangle
|
||||
{
|
||||
Size = new Vector2(RNG.NextSingle() + 1.2f) * 80,
|
||||
Origin = Anchor.Centre,
|
||||
RelativePositionAxes = Axes.Both,
|
||||
Position = new Vector2(x, y),
|
||||
Colour = christmasColour
|
||||
};
|
||||
|
||||
if (beatsHandled >= 10)
|
||||
triangle.Blending = BlendingParameters.Additive;
|
||||
|
||||
AddInternal(triangle);
|
||||
triangle
|
||||
.ScaleTo(0.9f)
|
||||
.ScaleTo(1, beat_length / 2, Easing.Out);
|
||||
triangle.FadeInFromZero(100, Easing.OutQuint);
|
||||
}
|
||||
|
||||
beatsHandled += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -207,7 +207,7 @@ namespace osu.Game.Screens.Menu
|
||||
Text = NotificationsStrings.AudioPlaybackIssue
|
||||
});
|
||||
}
|
||||
}, 5000);
|
||||
}, 8000);
|
||||
}
|
||||
|
||||
public override void OnResuming(ScreenTransitionEvent e)
|
||||
|
||||
@@ -8,6 +8,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Menu
|
||||
{
|
||||
@@ -36,6 +37,8 @@ namespace osu.Game.Screens.Menu
|
||||
X = -250,
|
||||
},
|
||||
};
|
||||
|
||||
Colour = SeasonalUI.ENABLED ? SeasonalUI.AMBIENT_COLOUR_2 : Color4.White;
|
||||
}
|
||||
|
||||
private bool isTriggered;
|
||||
|
||||
@@ -124,6 +124,7 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
AddRangeInternal(new[]
|
||||
{
|
||||
SeasonalUI.ENABLED ? new MainMenuSeasonalLighting() : Empty(),
|
||||
buttonsContainer = new ParallaxContainer
|
||||
{
|
||||
ParallaxAmount = 0.01f,
|
||||
@@ -166,7 +167,8 @@ namespace osu.Game.Screens.Menu
|
||||
Origin = Anchor.TopRight,
|
||||
Margin = new MarginPadding { Right = 15, Top = 5 }
|
||||
},
|
||||
new KiaiMenuFountains(),
|
||||
// For now, this is too much alongside the seasonal lighting.
|
||||
SeasonalUI.ENABLED ? Empty() : new KiaiMenuFountains(),
|
||||
bottomElementsFlow = new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Menu
|
||||
{
|
||||
public partial class MainMenuSeasonalLighting : CompositeDrawable
|
||||
{
|
||||
private IBindable<WorkingBeatmap> working = null!;
|
||||
|
||||
private InterpolatingFramedClock beatmapClock = null!;
|
||||
|
||||
private List<HitObject> hitObjects = null!;
|
||||
|
||||
[Resolved]
|
||||
private RulesetStore rulesets { get; set; } = null!;
|
||||
|
||||
public MainMenuSeasonalLighting()
|
||||
{
|
||||
RelativeChildSize = new Vector2(512, 384);
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IBindable<WorkingBeatmap> working)
|
||||
{
|
||||
this.working = working.GetBoundCopy();
|
||||
this.working.BindValueChanged(_ => Scheduler.AddOnce(updateBeatmap), true);
|
||||
}
|
||||
|
||||
private void updateBeatmap()
|
||||
{
|
||||
lastObjectIndex = null;
|
||||
beatmapClock = new InterpolatingFramedClock(new FramedClock(working.Value.Track));
|
||||
hitObjects = working.Value.GetPlayableBeatmap(rulesets.GetRuleset(0)).HitObjects.SelectMany(h => h.NestedHitObjects.Prepend(h))
|
||||
.OrderBy(h => h.StartTime)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private int? lastObjectIndex;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
Height = DrawWidth / 16 * 10;
|
||||
|
||||
beatmapClock.ProcessFrame();
|
||||
|
||||
// intentionally slightly early since we are doing fades on the lighting.
|
||||
double time = beatmapClock.CurrentTime + 50;
|
||||
|
||||
// handle seeks or OOB by skipping to current.
|
||||
if (lastObjectIndex == null || lastObjectIndex >= hitObjects.Count || (lastObjectIndex >= 0 && hitObjects[lastObjectIndex.Value].StartTime > time)
|
||||
|| Math.Abs(beatmapClock.ElapsedFrameTime) > 500)
|
||||
lastObjectIndex = hitObjects.Count(h => h.StartTime < time) - 1;
|
||||
|
||||
while (lastObjectIndex < hitObjects.Count - 1)
|
||||
{
|
||||
var h = hitObjects[lastObjectIndex.Value + 1];
|
||||
|
||||
if (h.StartTime > time)
|
||||
break;
|
||||
|
||||
// Don't add lighting if the game is running too slow.
|
||||
if (Clock.ElapsedFrameTime < 20)
|
||||
addLight(h);
|
||||
|
||||
lastObjectIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
private void addLight(HitObject h)
|
||||
{
|
||||
var light = new Light
|
||||
{
|
||||
RelativePositionAxes = Axes.Both,
|
||||
Position = ((IHasPosition)h).Position
|
||||
};
|
||||
|
||||
AddInternal(light);
|
||||
|
||||
if (h.GetType().Name.Contains("Tick"))
|
||||
{
|
||||
light.Colour = SeasonalUI.AMBIENT_COLOUR_1;
|
||||
light.Scale = new Vector2(0.5f);
|
||||
light
|
||||
.FadeInFromZero(250)
|
||||
.Then()
|
||||
.FadeOutFromOne(1000, Easing.Out);
|
||||
|
||||
light.MoveToOffset(new Vector2(RNG.Next(-20, 20), RNG.Next(-20, 20)), 1400, Easing.Out);
|
||||
}
|
||||
else
|
||||
{
|
||||
// default green
|
||||
Color4 col = SeasonalUI.PRIMARY_COLOUR_2;
|
||||
|
||||
// whistle red
|
||||
if (h.Samples.Any(s => s.Name == HitSampleInfo.HIT_WHISTLE))
|
||||
col = SeasonalUI.PRIMARY_COLOUR_1;
|
||||
// clap is third colour
|
||||
else if (h.Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP))
|
||||
col = SeasonalUI.AMBIENT_COLOUR_1;
|
||||
|
||||
light.Colour = col;
|
||||
|
||||
// finish larger lighting
|
||||
if (h.Samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH))
|
||||
light.Scale = new Vector2(3);
|
||||
|
||||
light
|
||||
.FadeInFromZero(150)
|
||||
.Then()
|
||||
.FadeOutFromOne(1000, Easing.In);
|
||||
|
||||
light.Expire();
|
||||
}
|
||||
}
|
||||
|
||||
public partial class Light : CompositeDrawable
|
||||
{
|
||||
private readonly Circle circle;
|
||||
|
||||
public new Color4 Colour
|
||||
{
|
||||
set
|
||||
{
|
||||
circle.Colour = value.Darken(0.8f);
|
||||
circle.EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = value,
|
||||
Radius = 80,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public Light()
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
circle = new Circle
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(12),
|
||||
Colour = SeasonalUI.AMBIENT_COLOUR_1,
|
||||
Blending = BlendingParameters.Additive,
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = SeasonalUI.AMBIENT_COLOUR_2,
|
||||
Radius = 80,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Origin = Anchor.Centre;
|
||||
Alpha = 0.5f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,12 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osuTK.Graphics;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Menu
|
||||
{
|
||||
@@ -29,7 +29,9 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
private void updateColour()
|
||||
{
|
||||
if (user.Value?.IsSupporter ?? false)
|
||||
if (SeasonalUI.ENABLED)
|
||||
Colour = SeasonalUI.AMBIENT_COLOUR_1;
|
||||
else if (user.Value?.IsSupporter ?? false)
|
||||
Colour = skin.Value.GetConfig<GlobalSkinColours, Color4>(GlobalSkinColours.MenuGlow)?.Value ?? Color4.White;
|
||||
else
|
||||
Colour = Color4.White;
|
||||
|
||||
@@ -3,22 +3,23 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using osuTK.Graphics;
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Online.API;
|
||||
using System;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Online.API.Requests.Responses;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Menu
|
||||
{
|
||||
@@ -67,7 +68,7 @@ namespace osu.Game.Screens.Menu
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = box_width * 2,
|
||||
Width = box_width * (SeasonalUI.ENABLED ? 4 : 2),
|
||||
Height = 1.5f,
|
||||
// align off-screen to make sure our edges don't become visible during parallax.
|
||||
X = -box_width,
|
||||
@@ -79,7 +80,7 @@ namespace osu.Game.Screens.Menu
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = box_width * 2,
|
||||
Width = box_width * (SeasonalUI.ENABLED ? 4 : 2),
|
||||
Height = 1.5f,
|
||||
X = box_width,
|
||||
Alpha = 0,
|
||||
@@ -104,7 +105,11 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
private void flash(Drawable d, double beatLength, bool kiai, ChannelAmplitudes amplitudes)
|
||||
{
|
||||
d.FadeTo(Math.Max(0, ((ReferenceEquals(d, leftBox) ? amplitudes.LeftChannel : amplitudes.RightChannel) - amplitude_dead_zone) / (kiai ? kiai_multiplier : alpha_multiplier)), box_fade_in_time)
|
||||
if (SeasonalUI.ENABLED)
|
||||
updateColour();
|
||||
|
||||
d.FadeTo(Math.Clamp(0.1f + ((ReferenceEquals(d, leftBox) ? amplitudes.LeftChannel : amplitudes.RightChannel) - amplitude_dead_zone) / (kiai ? kiai_multiplier : alpha_multiplier), 0.1f, 1),
|
||||
box_fade_in_time)
|
||||
.Then()
|
||||
.FadeOut(beatLength, Easing.In);
|
||||
}
|
||||
@@ -113,7 +118,9 @@ namespace osu.Game.Screens.Menu
|
||||
{
|
||||
Color4 baseColour = colours.Blue;
|
||||
|
||||
if (user.Value?.IsSupporter ?? false)
|
||||
if (SeasonalUI.ENABLED)
|
||||
baseColour = RNG.NextBool() ? SeasonalUI.PRIMARY_COLOUR_1 : SeasonalUI.PRIMARY_COLOUR_2;
|
||||
else if (user.Value?.IsSupporter ?? false)
|
||||
baseColour = skin.Value.GetConfig<GlobalSkinColours, Color4>(GlobalSkinColours.MenuGlow)?.Value ?? baseColour;
|
||||
|
||||
// linear colour looks better in this case, so let's use it for now.
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
@@ -211,6 +212,15 @@ namespace osu.Game.Screens.Menu
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
SeasonalUI.ENABLED
|
||||
? hat = new Sprite
|
||||
{
|
||||
BypassAutoSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Scale = new Vector2(-1, 1),
|
||||
}
|
||||
: Empty(),
|
||||
}
|
||||
},
|
||||
impactContainer = new CircularContainer
|
||||
@@ -271,11 +281,21 @@ namespace osu.Game.Screens.Menu
|
||||
private void load(TextureStore textures, AudioManager audio)
|
||||
{
|
||||
sampleClick = audio.Samples.Get(@"Menu/osu-logo-select");
|
||||
sampleBeat = audio.Samples.Get(@"Menu/osu-logo-heartbeat");
|
||||
sampleDownbeat = audio.Samples.Get(@"Menu/osu-logo-downbeat");
|
||||
|
||||
if (SeasonalUI.ENABLED)
|
||||
{
|
||||
sampleDownbeat = sampleBeat = audio.Samples.Get(@"Menu/osu-logo-heartbeat-bell");
|
||||
}
|
||||
else
|
||||
{
|
||||
sampleBeat = audio.Samples.Get(@"Menu/osu-logo-heartbeat");
|
||||
sampleDownbeat = audio.Samples.Get(@"Menu/osu-logo-downbeat");
|
||||
}
|
||||
|
||||
logo.Texture = textures.Get(@"Menu/logo");
|
||||
ripple.Texture = textures.Get(@"Menu/logo");
|
||||
if (hat != null)
|
||||
hat.Texture = textures.Get(@"Menu/hat");
|
||||
}
|
||||
|
||||
private int lastBeatIndex;
|
||||
@@ -303,7 +323,10 @@ namespace osu.Game.Screens.Menu
|
||||
else
|
||||
{
|
||||
var channel = sampleBeat.GetChannel();
|
||||
channel.Frequency.Value = 0.95 + RNG.NextDouble(0.1);
|
||||
if (SeasonalUI.ENABLED)
|
||||
channel.Frequency.Value = 0.99 + RNG.NextDouble(0.02);
|
||||
else
|
||||
channel.Frequency.Value = 0.95 + RNG.NextDouble(0.1);
|
||||
channel.Play();
|
||||
}
|
||||
});
|
||||
@@ -358,6 +381,9 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
const float scale_adjust_cutoff = 0.4f;
|
||||
|
||||
if (SeasonalUI.ENABLED)
|
||||
updateHat();
|
||||
|
||||
if (musicController.CurrentTrack.IsRunning)
|
||||
{
|
||||
float maxAmplitude = lastBeatIndex >= 0 ? musicController.CurrentTrack.CurrentAmplitudes.Maximum : 0;
|
||||
@@ -371,6 +397,38 @@ namespace osu.Game.Screens.Menu
|
||||
}
|
||||
}
|
||||
|
||||
private bool hasHat;
|
||||
|
||||
private void updateHat()
|
||||
{
|
||||
if (hat == null)
|
||||
return;
|
||||
|
||||
bool shouldHat = DrawWidth * Scale.X < 400;
|
||||
|
||||
if (shouldHat != hasHat)
|
||||
{
|
||||
hasHat = shouldHat;
|
||||
|
||||
if (hasHat)
|
||||
{
|
||||
hat.Delay(400)
|
||||
.Then()
|
||||
.MoveTo(new Vector2(120, 160))
|
||||
.RotateTo(0)
|
||||
.RotateTo(-20, 500, Easing.OutQuint)
|
||||
.FadeIn(250, Easing.OutQuint);
|
||||
}
|
||||
else
|
||||
{
|
||||
hat.Delay(100)
|
||||
.Then()
|
||||
.MoveToOffset(new Vector2(0, -5), 500, Easing.OutQuint)
|
||||
.FadeOut(500, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool HandlePositionalInput => base.HandlePositionalInput && Alpha > 0.2f;
|
||||
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
@@ -448,6 +506,9 @@ namespace osu.Game.Screens.Menu
|
||||
private Container currentProxyTarget;
|
||||
private Drawable proxy;
|
||||
|
||||
[CanBeNull]
|
||||
private readonly Sprite hat;
|
||||
|
||||
public void StopSamplePlayback() => sampleClickChannel?.Stop();
|
||||
|
||||
public Drawable ProxyToContainer(Container c)
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens
|
||||
{
|
||||
public static class SeasonalUI
|
||||
{
|
||||
public static readonly bool ENABLED = true;
|
||||
|
||||
public static readonly Color4 PRIMARY_COLOUR_1 = Color4Extensions.FromHex("D32F2F");
|
||||
|
||||
public static readonly Color4 PRIMARY_COLOUR_2 = Color4Extensions.FromHex("388E3C");
|
||||
|
||||
public static readonly Color4 AMBIENT_COLOUR_1 = Color4Extensions.FromHex("FFC");
|
||||
|
||||
public static readonly Color4 AMBIENT_COLOUR_2 = Color4Extensions.FromHex("FFE4B5");
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@
|
||||
</PackageReference>
|
||||
<PackageReference Include="Realm" Version="11.5.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2024.1206.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2024.1219.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2024.1219.1" />
|
||||
<PackageReference Include="Sentry" Version="4.13.0" />
|
||||
<!-- Held back due to 0.34.0 failing AOT compilation on ZstdSharp.dll dependency. -->
|
||||
<PackageReference Include="SharpCompress" Version="0.38.0" />
|
||||
|
||||
Reference in New Issue
Block a user