2020-03-17 15:37:56 +08:00
|
|
|
// 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.
|
|
|
|
|
2022-06-17 15:37:17 +08:00
|
|
|
#nullable disable
|
|
|
|
|
2020-03-17 15:37:56 +08:00
|
|
|
using System;
|
2020-07-02 08:37:38 +08:00
|
|
|
using System.Linq;
|
2020-03-17 15:37:56 +08:00
|
|
|
using osu.Framework.Allocation;
|
2020-10-29 15:11:37 +08:00
|
|
|
using osu.Framework.Audio;
|
2021-05-27 14:04:22 +08:00
|
|
|
using osu.Framework.Bindables;
|
2020-03-17 15:37:56 +08:00
|
|
|
using osu.Framework.Extensions.Color4Extensions;
|
|
|
|
using osu.Framework.Graphics;
|
2024-01-27 06:46:43 +08:00
|
|
|
using osu.Framework.Graphics.Audio;
|
2020-03-17 15:37:56 +08:00
|
|
|
using osu.Framework.Graphics.Colour;
|
|
|
|
using osu.Framework.Graphics.Containers;
|
2022-12-03 21:39:44 +08:00
|
|
|
using osu.Framework.Graphics.UserInterface;
|
2020-03-17 15:37:56 +08:00
|
|
|
using osu.Framework.Utils;
|
2021-06-09 17:15:45 +08:00
|
|
|
using osu.Game.Audio;
|
2020-03-17 15:37:56 +08:00
|
|
|
using osu.Game.Graphics;
|
2020-07-02 08:37:38 +08:00
|
|
|
using osu.Game.Rulesets.Mods;
|
2023-02-09 14:41:45 +08:00
|
|
|
using osu.Game.Rulesets.Scoring;
|
2020-03-17 15:37:56 +08:00
|
|
|
using osu.Game.Scoring;
|
2021-05-28 18:27:55 +08:00
|
|
|
using osu.Game.Skinning;
|
2020-03-17 15:37:56 +08:00
|
|
|
using osuTK;
|
2024-01-22 20:31:17 +08:00
|
|
|
using osuTK.Graphics;
|
2020-03-17 15:37:56 +08:00
|
|
|
|
|
|
|
namespace osu.Game.Screens.Ranking.Expanded.Accuracy
|
|
|
|
{
|
|
|
|
/// <summary>
|
|
|
|
/// The component that displays the player's accuracy on the results screen.
|
|
|
|
/// </summary>
|
|
|
|
public partial class AccuracyCircle : CompositeDrawable
|
|
|
|
{
|
|
|
|
/// <summary>
|
|
|
|
/// Duration for the transforms causing this component to appear.
|
|
|
|
/// </summary>
|
|
|
|
public const double APPEAR_DURATION = 200;
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Delay before the accuracy circle starts filling.
|
|
|
|
/// </summary>
|
|
|
|
public const double ACCURACY_TRANSFORM_DELAY = 450;
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Duration for the accuracy circle fill.
|
|
|
|
/// </summary>
|
|
|
|
public const double ACCURACY_TRANSFORM_DURATION = 3000;
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Delay after <see cref="ACCURACY_TRANSFORM_DURATION"/> for the rank text (A/B/C/D/S/SS) to appear.
|
|
|
|
/// </summary>
|
|
|
|
public const double TEXT_APPEAR_DELAY = ACCURACY_TRANSFORM_DURATION / 2;
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Delay before the rank circles start filling.
|
|
|
|
/// </summary>
|
|
|
|
public const double RANK_CIRCLE_TRANSFORM_DELAY = 150;
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Duration for the rank circle fills.
|
|
|
|
/// </summary>
|
|
|
|
public const double RANK_CIRCLE_TRANSFORM_DURATION = 800;
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Relative width of the rank circles.
|
|
|
|
/// </summary>
|
|
|
|
public const float RANK_CIRCLE_RADIUS = 0.06f;
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Relative width of the circle showing the accuracy.
|
|
|
|
/// </summary>
|
|
|
|
private const float accuracy_circle_radius = 0.2f;
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// SS is displayed as a 1% region, otherwise it would be invisible.
|
|
|
|
/// </summary>
|
|
|
|
private const double virtual_ss_percentage = 0.01;
|
|
|
|
|
2023-02-07 18:16:36 +08:00
|
|
|
/// <summary>
|
|
|
|
/// The width of a <see cref="RankNotch"/> in terms of accuracy.
|
|
|
|
/// </summary>
|
2023-02-09 09:20:05 +08:00
|
|
|
public const double NOTCH_WIDTH_PERCENTAGE = 1.0 / 360;
|
2023-02-07 18:16:36 +08:00
|
|
|
|
2020-03-17 15:37:56 +08:00
|
|
|
/// <summary>
|
|
|
|
/// The easing for the circle filling transforms.
|
|
|
|
/// </summary>
|
|
|
|
public static readonly Easing ACCURACY_TRANSFORM_EASING = Easing.OutPow10;
|
|
|
|
|
|
|
|
private readonly ScoreInfo score;
|
|
|
|
|
2022-12-03 21:39:44 +08:00
|
|
|
private CircularProgress accuracyCircle;
|
|
|
|
private CircularProgress innerMask;
|
2020-03-17 15:37:56 +08:00
|
|
|
private Container<RankBadge> badges;
|
|
|
|
private RankText rankText;
|
|
|
|
|
2021-06-09 17:15:45 +08:00
|
|
|
private PoolableSkinnableSample scoreTickSound;
|
|
|
|
private PoolableSkinnableSample badgeTickSound;
|
|
|
|
private PoolableSkinnableSample badgeMaxSound;
|
|
|
|
private PoolableSkinnableSample swooshUpSound;
|
|
|
|
private PoolableSkinnableSample rankImpactSound;
|
|
|
|
private PoolableSkinnableSample rankApplauseSound;
|
2024-01-27 06:46:43 +08:00
|
|
|
private DrawableSample rankFailSound;
|
2021-05-27 14:04:22 +08:00
|
|
|
|
2021-06-09 18:24:30 +08:00
|
|
|
private readonly Bindable<double> tickPlaybackRate = new Bindable<double>();
|
2021-06-09 18:09:00 +08:00
|
|
|
|
2021-05-27 14:04:22 +08:00
|
|
|
private double lastTickPlaybackTime;
|
|
|
|
private bool isTicking;
|
|
|
|
|
2023-11-29 18:05:24 +08:00
|
|
|
private readonly double accuracyX;
|
|
|
|
private readonly double accuracyS;
|
|
|
|
private readonly double accuracyA;
|
|
|
|
private readonly double accuracyB;
|
|
|
|
private readonly double accuracyC;
|
|
|
|
private readonly double accuracyD;
|
2021-06-09 17:46:38 +08:00
|
|
|
private readonly bool withFlair;
|
2021-05-27 14:04:22 +08:00
|
|
|
|
2024-01-22 20:31:17 +08:00
|
|
|
private readonly bool isFailedSDueToMisses;
|
|
|
|
private RankText failedSRankText;
|
|
|
|
|
2021-06-09 17:46:38 +08:00
|
|
|
public AccuracyCircle(ScoreInfo score, bool withFlair = false)
|
2020-03-17 15:37:56 +08:00
|
|
|
{
|
|
|
|
this.score = score;
|
2021-06-09 17:46:38 +08:00
|
|
|
this.withFlair = withFlair;
|
2023-11-29 18:05:24 +08:00
|
|
|
|
|
|
|
ScoreProcessor scoreProcessor = score.Ruleset.CreateInstance().CreateScoreProcessor();
|
|
|
|
accuracyX = scoreProcessor.AccuracyCutoffFromRank(ScoreRank.X);
|
|
|
|
accuracyS = scoreProcessor.AccuracyCutoffFromRank(ScoreRank.S);
|
2024-01-22 20:31:17 +08:00
|
|
|
|
2023-11-29 18:05:24 +08:00
|
|
|
accuracyA = scoreProcessor.AccuracyCutoffFromRank(ScoreRank.A);
|
|
|
|
accuracyB = scoreProcessor.AccuracyCutoffFromRank(ScoreRank.B);
|
|
|
|
accuracyC = scoreProcessor.AccuracyCutoffFromRank(ScoreRank.C);
|
|
|
|
accuracyD = scoreProcessor.AccuracyCutoffFromRank(ScoreRank.D);
|
2024-01-22 20:31:17 +08:00
|
|
|
|
|
|
|
isFailedSDueToMisses = score.Accuracy >= accuracyS && score.Rank == ScoreRank.A;
|
2021-05-27 14:04:22 +08:00
|
|
|
}
|
|
|
|
|
2020-03-17 15:37:56 +08:00
|
|
|
[BackgroundDependencyLoader]
|
2024-01-27 06:46:43 +08:00
|
|
|
private void load(AudioManager audio)
|
2020-03-17 15:37:56 +08:00
|
|
|
{
|
|
|
|
InternalChildren = new Drawable[]
|
|
|
|
{
|
2022-12-03 21:39:44 +08:00
|
|
|
new CircularProgress
|
2020-03-17 15:37:56 +08:00
|
|
|
{
|
|
|
|
Name = "Background circle",
|
|
|
|
Anchor = Anchor.Centre,
|
|
|
|
Origin = Anchor.Centre,
|
|
|
|
RelativeSizeAxes = Axes.Both,
|
|
|
|
Colour = OsuColour.Gray(47),
|
|
|
|
Alpha = 0.5f,
|
|
|
|
InnerRadius = accuracy_circle_radius + 0.01f, // Extends a little bit into the circle
|
|
|
|
Current = { Value = 1 },
|
|
|
|
},
|
2022-12-03 21:39:44 +08:00
|
|
|
accuracyCircle = new CircularProgress
|
2020-03-17 15:37:56 +08:00
|
|
|
{
|
|
|
|
Name = "Accuracy circle",
|
|
|
|
Anchor = Anchor.Centre,
|
|
|
|
Origin = Anchor.Centre,
|
|
|
|
RelativeSizeAxes = Axes.Both,
|
|
|
|
Colour = ColourInfo.GradientVertical(Color4Extensions.FromHex("#7CF6FF"), Color4Extensions.FromHex("#BAFFA9")),
|
|
|
|
InnerRadius = accuracy_circle_radius,
|
|
|
|
},
|
|
|
|
new BufferedContainer
|
|
|
|
{
|
|
|
|
Name = "Graded circles",
|
|
|
|
Anchor = Anchor.Centre,
|
|
|
|
Origin = Anchor.Centre,
|
|
|
|
RelativeSizeAxes = Axes.Both,
|
|
|
|
Size = new Vector2(0.8f),
|
|
|
|
Padding = new MarginPadding(2),
|
|
|
|
Children = new Drawable[]
|
|
|
|
{
|
2022-12-03 21:39:44 +08:00
|
|
|
new CircularProgress
|
2020-03-17 15:37:56 +08:00
|
|
|
{
|
|
|
|
RelativeSizeAxes = Axes.Both,
|
2020-03-18 01:55:30 +08:00
|
|
|
Colour = OsuColour.ForRank(ScoreRank.X),
|
2020-03-17 15:37:56 +08:00
|
|
|
InnerRadius = RANK_CIRCLE_RADIUS,
|
2023-11-29 18:05:24 +08:00
|
|
|
Current = { Value = accuracyX }
|
2020-03-17 15:37:56 +08:00
|
|
|
},
|
2022-12-03 21:39:44 +08:00
|
|
|
new CircularProgress
|
2020-03-17 15:37:56 +08:00
|
|
|
{
|
|
|
|
RelativeSizeAxes = Axes.Both,
|
2020-03-18 01:55:30 +08:00
|
|
|
Colour = OsuColour.ForRank(ScoreRank.S),
|
2020-03-17 15:37:56 +08:00
|
|
|
InnerRadius = RANK_CIRCLE_RADIUS,
|
2023-11-29 18:05:24 +08:00
|
|
|
Current = { Value = accuracyX - virtual_ss_percentage }
|
2020-03-17 15:37:56 +08:00
|
|
|
},
|
2022-12-03 21:39:44 +08:00
|
|
|
new CircularProgress
|
2020-03-17 15:37:56 +08:00
|
|
|
{
|
|
|
|
RelativeSizeAxes = Axes.Both,
|
2020-03-18 01:55:30 +08:00
|
|
|
Colour = OsuColour.ForRank(ScoreRank.A),
|
2020-03-17 15:37:56 +08:00
|
|
|
InnerRadius = RANK_CIRCLE_RADIUS,
|
2023-11-29 18:05:24 +08:00
|
|
|
Current = { Value = accuracyS }
|
2020-03-17 15:37:56 +08:00
|
|
|
},
|
2022-12-03 21:39:44 +08:00
|
|
|
new CircularProgress
|
2020-03-17 15:37:56 +08:00
|
|
|
{
|
|
|
|
RelativeSizeAxes = Axes.Both,
|
2020-03-18 01:55:30 +08:00
|
|
|
Colour = OsuColour.ForRank(ScoreRank.B),
|
2020-03-17 15:37:56 +08:00
|
|
|
InnerRadius = RANK_CIRCLE_RADIUS,
|
2023-11-29 18:05:24 +08:00
|
|
|
Current = { Value = accuracyA }
|
2020-03-17 15:37:56 +08:00
|
|
|
},
|
2022-12-03 21:39:44 +08:00
|
|
|
new CircularProgress
|
2020-03-17 15:37:56 +08:00
|
|
|
{
|
|
|
|
RelativeSizeAxes = Axes.Both,
|
2020-03-18 01:55:30 +08:00
|
|
|
Colour = OsuColour.ForRank(ScoreRank.C),
|
2020-03-17 15:37:56 +08:00
|
|
|
InnerRadius = RANK_CIRCLE_RADIUS,
|
2023-11-29 18:05:24 +08:00
|
|
|
Current = { Value = accuracyB }
|
2020-03-17 15:37:56 +08:00
|
|
|
},
|
2022-12-03 21:39:44 +08:00
|
|
|
new CircularProgress
|
2020-03-17 15:37:56 +08:00
|
|
|
{
|
|
|
|
RelativeSizeAxes = Axes.Both,
|
2020-03-18 01:55:30 +08:00
|
|
|
Colour = OsuColour.ForRank(ScoreRank.D),
|
2020-03-17 15:37:56 +08:00
|
|
|
InnerRadius = RANK_CIRCLE_RADIUS,
|
2023-11-29 18:05:24 +08:00
|
|
|
Current = { Value = accuracyC }
|
2020-03-17 15:37:56 +08:00
|
|
|
},
|
2023-11-29 18:05:24 +08:00
|
|
|
new RankNotch((float)accuracyX),
|
|
|
|
new RankNotch((float)(accuracyX - virtual_ss_percentage)),
|
|
|
|
new RankNotch((float)accuracyS),
|
|
|
|
new RankNotch((float)accuracyA),
|
|
|
|
new RankNotch((float)accuracyB),
|
|
|
|
new RankNotch((float)accuracyC),
|
2020-03-17 15:37:56 +08:00
|
|
|
new BufferedContainer
|
|
|
|
{
|
|
|
|
Name = "Graded circle mask",
|
|
|
|
RelativeSizeAxes = Axes.Both,
|
|
|
|
Padding = new MarginPadding(1),
|
|
|
|
Blending = new BlendingParameters
|
|
|
|
{
|
|
|
|
Source = BlendingType.DstColor,
|
2022-12-03 21:39:44 +08:00
|
|
|
Destination = BlendingType.OneMinusSrcColor,
|
2020-03-17 15:37:56 +08:00
|
|
|
SourceAlpha = BlendingType.One,
|
|
|
|
DestinationAlpha = BlendingType.SrcAlpha
|
|
|
|
},
|
2022-12-03 21:39:44 +08:00
|
|
|
Child = innerMask = new CircularProgress
|
2020-03-17 15:37:56 +08:00
|
|
|
{
|
|
|
|
RelativeSizeAxes = Axes.Both,
|
2022-12-03 21:39:44 +08:00
|
|
|
InnerRadius = RANK_CIRCLE_RADIUS - 0.02f,
|
2020-03-17 15:37:56 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
badges = new Container<RankBadge>
|
|
|
|
{
|
|
|
|
Name = "Rank badges",
|
|
|
|
RelativeSizeAxes = Axes.Both,
|
|
|
|
Padding = new MarginPadding { Vertical = -15, Horizontal = -20 },
|
|
|
|
Children = new[]
|
|
|
|
{
|
2023-11-29 19:29:50 +08:00
|
|
|
new RankBadge(accuracyD, Interpolation.Lerp(accuracyD, accuracyC, 0.5), getRank(ScoreRank.D)),
|
|
|
|
new RankBadge(accuracyC, Interpolation.Lerp(accuracyC, accuracyB, 0.5), getRank(ScoreRank.C)),
|
|
|
|
new RankBadge(accuracyB, Interpolation.Lerp(accuracyB, accuracyA, 0.5), getRank(ScoreRank.B)),
|
2023-02-09 07:34:28 +08:00
|
|
|
// The S and A badges are moved down slightly to prevent collision with the SS badge.
|
2023-11-29 18:05:24 +08:00
|
|
|
new RankBadge(accuracyA, Interpolation.Lerp(accuracyA, accuracyS, 0.25), getRank(ScoreRank.A)),
|
2023-11-29 19:29:50 +08:00
|
|
|
new RankBadge(accuracyS, Interpolation.Lerp(accuracyS, (accuracyX - virtual_ss_percentage), 0.25), getRank(ScoreRank.S)),
|
|
|
|
new RankBadge(accuracyX, accuracyX, getRank(ScoreRank.X)),
|
2020-03-17 15:37:56 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
rankText = new RankText(score.Rank)
|
|
|
|
};
|
2021-05-27 14:04:22 +08:00
|
|
|
|
2021-06-09 17:46:38 +08:00
|
|
|
if (withFlair)
|
2021-05-27 14:04:22 +08:00
|
|
|
{
|
2024-01-22 20:31:17 +08:00
|
|
|
if (isFailedSDueToMisses)
|
|
|
|
AddInternal(failedSRankText = new RankText(ScoreRank.S));
|
|
|
|
|
2021-06-09 18:06:37 +08:00
|
|
|
AddRangeInternal(new Drawable[]
|
|
|
|
{
|
|
|
|
rankImpactSound = new PoolableSkinnableSample(new SampleInfo(impactSampleName)),
|
|
|
|
rankApplauseSound = new PoolableSkinnableSample(new SampleInfo(@"applause", applauseSampleName)),
|
|
|
|
scoreTickSound = new PoolableSkinnableSample(new SampleInfo(@"Results/score-tick")),
|
|
|
|
badgeTickSound = new PoolableSkinnableSample(new SampleInfo(@"Results/badge-dink")),
|
|
|
|
badgeMaxSound = new PoolableSkinnableSample(new SampleInfo(@"Results/badge-dink-max")),
|
|
|
|
swooshUpSound = new PoolableSkinnableSample(new SampleInfo(@"Results/swoosh-up")),
|
2024-01-27 06:46:43 +08:00
|
|
|
rankFailSound = new DrawableSample(audio.Samples.Get(results_applause_d_sound)),
|
2021-06-09 18:06:37 +08:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-17 15:37:56 +08:00
|
|
|
protected override void LoadComplete()
|
|
|
|
{
|
|
|
|
base.LoadComplete();
|
|
|
|
|
|
|
|
this.ScaleTo(0).Then().ScaleTo(1, APPEAR_DURATION, Easing.OutQuint);
|
|
|
|
|
2021-06-09 17:46:38 +08:00
|
|
|
if (withFlair)
|
2021-05-28 18:27:55 +08:00
|
|
|
{
|
2021-06-09 18:24:30 +08:00
|
|
|
const double swoosh_pre_delay = 443f;
|
|
|
|
const double swoosh_volume = 0.4f;
|
|
|
|
|
|
|
|
this.Delay(swoosh_pre_delay).Schedule(() =>
|
2021-05-28 18:27:55 +08:00
|
|
|
{
|
2021-06-09 18:24:30 +08:00
|
|
|
swooshUpSound.VolumeTo(swoosh_volume);
|
2021-05-28 18:27:55 +08:00
|
|
|
swooshUpSound.Play();
|
|
|
|
});
|
|
|
|
}
|
2021-05-27 14:04:22 +08:00
|
|
|
|
2021-05-28 18:27:55 +08:00
|
|
|
using (BeginDelayedSequence(RANK_CIRCLE_TRANSFORM_DELAY))
|
2020-03-17 15:37:56 +08:00
|
|
|
innerMask.FillTo(1f, RANK_CIRCLE_TRANSFORM_DURATION, ACCURACY_TRANSFORM_EASING);
|
|
|
|
|
2021-05-28 18:27:55 +08:00
|
|
|
using (BeginDelayedSequence(ACCURACY_TRANSFORM_DELAY))
|
2020-03-17 15:37:56 +08:00
|
|
|
{
|
2023-02-07 18:16:36 +08:00
|
|
|
double targetAccuracy = score.Accuracy;
|
2023-02-09 07:34:28 +08:00
|
|
|
double[] notchPercentages =
|
|
|
|
{
|
2023-11-29 18:05:24 +08:00
|
|
|
accuracyS,
|
|
|
|
accuracyA,
|
|
|
|
accuracyB,
|
|
|
|
accuracyC,
|
2023-02-09 07:34:28 +08:00
|
|
|
};
|
2023-02-07 18:16:36 +08:00
|
|
|
|
|
|
|
// Ensure the gauge overshoots or undershoots a bit so it doesn't land in the gaps of the inner graded circle (caused by `RankNotch`es),
|
|
|
|
// to prevent ambiguity on what grade it's pointing at.
|
|
|
|
foreach (double p in notchPercentages)
|
|
|
|
{
|
2023-02-09 06:59:19 +08:00
|
|
|
if (Precision.AlmostEquals(p, targetAccuracy, NOTCH_WIDTH_PERCENTAGE / 2))
|
2023-02-07 18:16:36 +08:00
|
|
|
{
|
|
|
|
int tippingDirection = targetAccuracy - p >= 0 ? 1 : -1; // We "round up" here to match rank criteria
|
|
|
|
targetAccuracy = p + tippingDirection * (NOTCH_WIDTH_PERCENTAGE / 2);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The final gap between 99.999...% (S) and 100% (SS) is exaggerated by `virtual_ss_percentage`. We don't want to land there either.
|
|
|
|
if (score.Rank == ScoreRank.X || score.Rank == ScoreRank.XH)
|
|
|
|
targetAccuracy = 1;
|
|
|
|
else
|
2023-11-29 18:05:24 +08:00
|
|
|
targetAccuracy = Math.Min(accuracyX - virtual_ss_percentage - NOTCH_WIDTH_PERCENTAGE / 2, targetAccuracy);
|
2023-02-07 18:16:36 +08:00
|
|
|
|
|
|
|
// The accuracy circle gauge visually fills up a bit too much.
|
|
|
|
// This wouldn't normally matter but we want it to align properly with the inner graded circle in the above cases.
|
|
|
|
const double visual_alignment_offset = 0.001;
|
|
|
|
|
|
|
|
if (targetAccuracy < 1 && targetAccuracy >= visual_alignment_offset)
|
|
|
|
targetAccuracy -= visual_alignment_offset;
|
2020-03-17 15:37:56 +08:00
|
|
|
|
|
|
|
accuracyCircle.FillTo(targetAccuracy, ACCURACY_TRANSFORM_DURATION, ACCURACY_TRANSFORM_EASING);
|
|
|
|
|
2021-06-09 17:46:38 +08:00
|
|
|
if (withFlair)
|
2021-05-27 14:04:22 +08:00
|
|
|
{
|
2021-06-02 15:57:09 +08:00
|
|
|
Schedule(() =>
|
2021-05-28 18:27:55 +08:00
|
|
|
{
|
2021-06-09 18:24:30 +08:00
|
|
|
const double score_tick_debounce_rate_start = 18f;
|
|
|
|
const double score_tick_debounce_rate_end = 300f;
|
|
|
|
const double score_tick_volume_start = 0.6f;
|
|
|
|
const double score_tick_volume_end = 1.0f;
|
|
|
|
|
|
|
|
this.TransformBindableTo(tickPlaybackRate, score_tick_debounce_rate_start);
|
|
|
|
this.TransformBindableTo(tickPlaybackRate, score_tick_debounce_rate_end, ACCURACY_TRANSFORM_DURATION, Easing.OutSine);
|
|
|
|
|
|
|
|
scoreTickSound.FrequencyTo(1 + targetAccuracy, ACCURACY_TRANSFORM_DURATION, Easing.OutSine);
|
|
|
|
scoreTickSound.VolumeTo(score_tick_volume_start).Then().VolumeTo(score_tick_volume_end, ACCURACY_TRANSFORM_DURATION, Easing.OutSine);
|
2021-05-27 14:04:22 +08:00
|
|
|
|
2021-06-02 15:57:09 +08:00
|
|
|
isTicking = true;
|
|
|
|
});
|
|
|
|
}
|
2021-05-27 14:04:22 +08:00
|
|
|
|
|
|
|
int badgeNum = 0;
|
|
|
|
|
2024-01-27 06:46:12 +08:00
|
|
|
if (score.Rank != ScoreRank.F)
|
2020-03-17 15:37:56 +08:00
|
|
|
{
|
2024-01-27 06:46:12 +08:00
|
|
|
foreach (var badge in badges)
|
2020-10-29 15:11:37 +08:00
|
|
|
{
|
2024-01-27 06:46:12 +08:00
|
|
|
if (badge.Accuracy > score.Accuracy)
|
|
|
|
continue;
|
2021-05-28 18:27:55 +08:00
|
|
|
|
2024-01-27 06:46:12 +08:00
|
|
|
using (BeginDelayedSequence(
|
|
|
|
inverseEasing(ACCURACY_TRANSFORM_EASING, Math.Min(accuracyX - virtual_ss_percentage, badge.Accuracy) / targetAccuracy) * ACCURACY_TRANSFORM_DURATION))
|
2021-05-27 14:04:22 +08:00
|
|
|
{
|
2024-01-27 06:46:12 +08:00
|
|
|
badge.Appear();
|
|
|
|
|
|
|
|
if (withFlair)
|
2021-06-02 15:57:09 +08:00
|
|
|
{
|
2024-01-27 06:46:12 +08:00
|
|
|
Schedule(() =>
|
|
|
|
{
|
|
|
|
var dink = badgeNum < badges.Count - 1 ? badgeTickSound : badgeMaxSound;
|
2021-06-09 17:15:45 +08:00
|
|
|
|
2024-01-27 06:46:12 +08:00
|
|
|
dink.FrequencyTo(1 + badgeNum++ * 0.05);
|
|
|
|
dink.Play();
|
|
|
|
});
|
|
|
|
}
|
2021-06-02 15:57:09 +08:00
|
|
|
}
|
2020-10-29 15:11:37 +08:00
|
|
|
}
|
2020-03-17 15:37:56 +08:00
|
|
|
}
|
|
|
|
|
2021-05-28 18:27:55 +08:00
|
|
|
using (BeginDelayedSequence(TEXT_APPEAR_DELAY))
|
2020-10-29 15:11:37 +08:00
|
|
|
{
|
2020-03-17 15:37:56 +08:00
|
|
|
rankText.Appear();
|
2021-05-28 18:27:55 +08:00
|
|
|
|
2021-06-09 17:46:38 +08:00
|
|
|
if (!withFlair) return;
|
2021-06-02 15:57:09 +08:00
|
|
|
|
2021-05-27 14:04:22 +08:00
|
|
|
Schedule(() =>
|
|
|
|
{
|
|
|
|
isTicking = false;
|
2021-06-02 15:57:09 +08:00
|
|
|
rankImpactSound.Play();
|
2021-05-27 14:04:22 +08:00
|
|
|
});
|
|
|
|
|
2021-06-09 18:24:30 +08:00
|
|
|
const double applause_pre_delay = 545f;
|
|
|
|
const double applause_volume = 0.8f;
|
|
|
|
|
|
|
|
using (BeginDelayedSequence(applause_pre_delay))
|
2021-05-27 14:04:22 +08:00
|
|
|
{
|
|
|
|
Schedule(() =>
|
|
|
|
{
|
2024-01-27 06:46:43 +08:00
|
|
|
if (score.Rank != ScoreRank.F)
|
|
|
|
{
|
|
|
|
rankApplauseSound.VolumeTo(applause_volume);
|
|
|
|
rankApplauseSound.Play();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
rankFailSound.VolumeTo(applause_volume);
|
|
|
|
rankFailSound.Play();
|
|
|
|
}
|
2021-05-27 14:04:22 +08:00
|
|
|
});
|
|
|
|
}
|
2020-10-29 15:11:37 +08:00
|
|
|
}
|
2024-01-22 20:31:17 +08:00
|
|
|
|
|
|
|
if (isFailedSDueToMisses)
|
|
|
|
{
|
|
|
|
const double adjust_duration = 200;
|
|
|
|
|
|
|
|
using (BeginDelayedSequence(TEXT_APPEAR_DELAY - adjust_duration))
|
|
|
|
{
|
|
|
|
failedSRankText.FadeIn(adjust_duration);
|
|
|
|
|
|
|
|
using (BeginDelayedSequence(adjust_duration))
|
|
|
|
{
|
|
|
|
failedSRankText
|
|
|
|
.FadeColour(Color4.Red, 800, Easing.Out)
|
|
|
|
.RotateTo(10, 1000, Easing.Out)
|
|
|
|
.MoveToY(100, 1000, Easing.In)
|
|
|
|
.FadeOut(800, Easing.Out);
|
|
|
|
|
|
|
|
accuracyCircle
|
2024-01-23 18:16:55 +08:00
|
|
|
.FillTo(accuracyS - NOTCH_WIDTH_PERCENTAGE / 2 - visual_alignment_offset, 70, Easing.OutQuint);
|
2024-01-22 20:31:17 +08:00
|
|
|
|
2024-01-25 13:57:42 +08:00
|
|
|
badges.Single(b => b.Rank == getRank(ScoreRank.S))
|
2024-01-22 20:31:17 +08:00
|
|
|
.FadeOut(70, Easing.OutQuint);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-03-17 15:37:56 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-09 18:31:53 +08:00
|
|
|
protected override void Update()
|
|
|
|
{
|
|
|
|
base.Update();
|
|
|
|
|
|
|
|
if (isTicking && Clock.CurrentTime - lastTickPlaybackTime >= tickPlaybackRate.Value)
|
|
|
|
{
|
|
|
|
scoreTickSound?.Play();
|
|
|
|
lastTickPlaybackTime = Clock.CurrentTime;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-27 06:46:43 +08:00
|
|
|
private const string results_applause_d_sound = @"Results/applause-d";
|
|
|
|
|
2021-06-09 18:15:55 +08:00
|
|
|
private string applauseSampleName
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
switch (score.Rank)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
case ScoreRank.D:
|
2024-01-27 06:46:43 +08:00
|
|
|
return results_applause_d_sound;
|
2021-06-09 18:15:55 +08:00
|
|
|
|
|
|
|
case ScoreRank.C:
|
2021-06-09 18:35:05 +08:00
|
|
|
return @"Results/applause-c";
|
2021-06-09 18:15:55 +08:00
|
|
|
|
|
|
|
case ScoreRank.B:
|
2021-06-09 18:35:05 +08:00
|
|
|
return @"Results/applause-b";
|
2021-06-09 18:15:55 +08:00
|
|
|
|
|
|
|
case ScoreRank.A:
|
2021-06-09 18:35:05 +08:00
|
|
|
return @"Results/applause-a";
|
2021-06-09 18:15:55 +08:00
|
|
|
|
|
|
|
case ScoreRank.S:
|
|
|
|
case ScoreRank.SH:
|
|
|
|
case ScoreRank.X:
|
|
|
|
case ScoreRank.XH:
|
2021-06-09 18:35:05 +08:00
|
|
|
return @"Results/applause-s";
|
2021-06-09 18:15:55 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-09 18:06:37 +08:00
|
|
|
private string impactSampleName
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
switch (score.Rank)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
case ScoreRank.D:
|
|
|
|
return @"Results/rank-impact-fail-d";
|
|
|
|
|
|
|
|
case ScoreRank.C:
|
|
|
|
case ScoreRank.B:
|
|
|
|
return @"Results/rank-impact-fail";
|
|
|
|
|
|
|
|
case ScoreRank.A:
|
|
|
|
case ScoreRank.S:
|
|
|
|
case ScoreRank.SH:
|
|
|
|
return @"Results/rank-impact-pass";
|
|
|
|
|
|
|
|
case ScoreRank.X:
|
|
|
|
case ScoreRank.XH:
|
|
|
|
return @"Results/rank-impact-pass-ss";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private ScoreRank getRank(ScoreRank rank)
|
|
|
|
{
|
|
|
|
foreach (var mod in score.Mods.OfType<IApplicableToScoreProcessor>())
|
|
|
|
rank = mod.AdjustRank(rank, score.Accuracy);
|
|
|
|
|
|
|
|
return rank;
|
|
|
|
}
|
|
|
|
|
2020-03-17 15:37:56 +08:00
|
|
|
private double inverseEasing(Easing easing, double targetValue)
|
|
|
|
{
|
|
|
|
double test = 0;
|
|
|
|
double result = 0;
|
|
|
|
int count = 2;
|
|
|
|
|
|
|
|
while (Math.Abs(result - targetValue) > 0.005)
|
|
|
|
{
|
|
|
|
int dir = Math.Sign(targetValue - result);
|
|
|
|
|
|
|
|
test += dir * 1.0 / count;
|
|
|
|
result = Interpolation.ApplyEasing(easing, test);
|
|
|
|
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return test;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|