1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 10:12:54 +08:00

Merge branch 'master' into tournament-mappool-linebreak

This commit is contained in:
Bartłomiej Dach 2023-06-14 21:33:24 +02:00
commit 0c622ec895
No known key found for this signature in database
32 changed files with 457 additions and 97 deletions

View File

@ -1,22 +1,29 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Testing; using osu.Framework.Testing;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Skinning.Default; using osu.Game.Rulesets.Osu.Skinning.Default;
using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.Scoring;
using osuTK;
namespace osu.Game.Rulesets.Osu.Tests.Mods namespace osu.Game.Rulesets.Osu.Tests.Mods
{ {
public partial class TestSceneOsuModAutoplay : OsuModTestScene public partial class TestSceneOsuModAutoplay : OsuModTestScene
{ {
protected override bool AllowFail => true;
[Test] [Test]
public void TestCursorPositionStoredToJudgement() public void TestCursorPositionStoredToJudgement()
{ {
@ -44,6 +51,36 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods
FinalRate = { Value = 1.3 } FinalRate = { Value = 1.3 }
}); });
[Test]
public void TestPerfectScoreOnShortSliderWithRepeat()
{
AddStep("set score to standardised", () => LocalConfig.SetValue(OsuSetting.ScoreDisplayMode, ScoringMode.Standardised));
CreateModTest(new ModTestData
{
Autoplay = true,
Beatmap = new Beatmap
{
HitObjects = new List<HitObject>
{
new Slider
{
StartTime = 500,
Position = new Vector2(256, 192),
Path = new SliderPath(new[]
{
new PathControlPoint(),
new PathControlPoint(new Vector2(0, 6.25f))
}),
RepeatCount = 1,
SliderVelocity = 10
}
}
},
PassCondition = () => Player.ScoreProcessor.TotalScore.Value == 1_000_000
});
}
private void runSpmTest(Mod mod) private void runSpmTest(Mod mod)
{ {
SpinnerSpmCalculator? spmCalculator = null; SpinnerSpmCalculator? spmCalculator = null;

View File

@ -75,18 +75,24 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
tailContainer = new Container<DrawableSliderTail> { RelativeSizeAxes = Axes.Both };
AddRangeInternal(new Drawable[] AddRangeInternal(new Drawable[]
{ {
shakeContainer = new ShakeContainer shakeContainer = new ShakeContainer
{ {
ShakeDuration = 30, ShakeDuration = 30,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Children = new Drawable[] Children = new[]
{ {
Body = new SkinnableDrawable(new OsuSkinComponentLookup(OsuSkinComponents.SliderBody), _ => new DefaultSliderBody(), confineMode: ConfineMode.NoScaling), Body = new SkinnableDrawable(new OsuSkinComponentLookup(OsuSkinComponents.SliderBody), _ => new DefaultSliderBody(), confineMode: ConfineMode.NoScaling),
tailContainer = new Container<DrawableSliderTail> { RelativeSizeAxes = Axes.Both }, // proxied here so that the tail is drawn under repeats/ticks - legacy skins rely on this
tailContainer.CreateProxy(),
tickContainer = new Container<DrawableSliderTick> { RelativeSizeAxes = Axes.Both }, tickContainer = new Container<DrawableSliderTick> { RelativeSizeAxes = Axes.Both },
repeatContainer = new Container<DrawableSliderRepeat> { RelativeSizeAxes = Axes.Both }, repeatContainer = new Container<DrawableSliderRepeat> { RelativeSizeAxes = Axes.Both },
// actual tail container is placed here to ensure that tail hitobjects are processed after ticks/repeats.
// this is required for the correct operation of Score V2.
tailContainer,
} }
}, },
// slider head is not included in shake as it handles hit detection, and handles its own shaking. // slider head is not included in shake as it handles hit detection, and handles its own shaking.

View File

@ -48,21 +48,26 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
private Bindable<bool> configHitLighting = null!; private Bindable<bool> configHitLighting = null!;
private static readonly Vector2 circle_size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
[Resolved] [Resolved]
private DrawableHitObject drawableObject { get; set; } = null!; private DrawableHitObject drawableObject { get; set; } = null!;
public ArgonMainCirclePiece(bool withOuterFill) public ArgonMainCirclePiece(bool withOuterFill)
{ {
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); Size = circle_size;
Anchor = Anchor.Centre; Anchor = Anchor.Centre;
Origin = Anchor.Centre; Origin = Anchor.Centre;
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
outerFill = new Circle // renders white outer border and dark fill outerFill = new Circle // renders dark fill
{ {
Size = Size, Anchor = Anchor.Centre,
Origin = Anchor.Centre,
// Slightly inset to prevent bleeding outside the ring
Size = circle_size - new Vector2(1),
Alpha = withOuterFill ? 1 : 0, Alpha = withOuterFill ? 1 : 0,
}, },
outerGradient = new Circle // renders the outer bright gradient outerGradient = new Circle // renders the outer bright gradient
@ -88,7 +93,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon
Masking = true, Masking = true,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Size = Size, Size = circle_size,
Child = new KiaiFlash Child = new KiaiFlash
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,

View File

@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon
private const double pre_beat_transition_time = 80; private const double pre_beat_transition_time = 80;
private const float flash_opacity = 0.3f; private const float kiai_flash_opacity = 0.15f;
private ColourInfo accentColour; private ColourInfo accentColour;
@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon
if (drawableHitObject.State.Value == ArmedState.Idle) if (drawableHitObject.State.Value == ArmedState.Idle)
{ {
flash flash
.FadeTo(flash_opacity) .FadeTo(kiai_flash_opacity)
.Then() .Then()
.FadeOut(timingPoint.BeatLength * 0.75, Easing.OutSine); .FadeOut(timingPoint.BeatLength * 0.75, Easing.OutSine);
} }

View File

@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Default
private const double pre_beat_transition_time = 80; private const double pre_beat_transition_time = 80;
private const float flash_opacity = 0.3f; private const float kiai_flash_opacity = 0.15f;
[Resolved] [Resolved]
private DrawableHitObject drawableHitObject { get; set; } = null!; private DrawableHitObject drawableHitObject { get; set; } = null!;
@ -187,7 +187,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Default
if (drawableHitObject.State.Value == ArmedState.Idle) if (drawableHitObject.State.Value == ArmedState.Idle)
{ {
flashBox flashBox
.FadeTo(flash_opacity) .FadeTo(kiai_flash_opacity)
.Then() .Then()
.FadeOut(timingPoint.BeatLength * 0.75, Easing.OutSine); .FadeOut(timingPoint.BeatLength * 0.75, Easing.OutSine);
} }

View File

@ -17,9 +17,8 @@ namespace osu.Game.Beatmaps
// If issues are found it's worth checking to make sure similar issues exist there. // If issues are found it's worth checking to make sure similar issues exist there.
public class BeatmapPanelBackgroundTextureLoaderStore : IResourceStore<TextureUpload> public class BeatmapPanelBackgroundTextureLoaderStore : IResourceStore<TextureUpload>
{ {
// These numbers are taken from the draw visualiser size requirements for song select panel textures at extreme aspect ratios. // The aspect ratio of SetPanelBackground at its maximum size (very tall window).
private const int max_height = 130; private const float minimum_display_ratio = 512 / 80f;
private const int max_width = 1280;
private readonly IResourceStore<TextureUpload>? textureStore; private readonly IResourceStore<TextureUpload>? textureStore;
@ -66,14 +65,21 @@ namespace osu.Game.Beatmaps
textureUpload.Dispose(); textureUpload.Dispose();
Size size = image.Size(); Size size = image.Size();
int usableWidth = Math.Min(max_width, size.Width);
int usableHeight = Math.Min(max_height, size.Height); // Assume that panel backgrounds are always displayed using `FillMode.Fill`.
// Also assume that all backgrounds are wider than they are tall, so the
// fill is always going to be based on width.
//
// We need to include enough height to make this work for all ratio panels are displayed at.
int usableHeight = (int)Math.Ceiling(size.Width * 1 / minimum_display_ratio);
usableHeight = Math.Min(size.Height, usableHeight);
// Crop the centre region of the background for now. // Crop the centre region of the background for now.
Rectangle cropRectangle = new Rectangle( Rectangle cropRectangle = new Rectangle(
(size.Width - usableWidth) / 2, 0,
(size.Height - usableHeight) / 2, (size.Height - usableHeight) / 2,
usableWidth, size.Width,
usableHeight usableHeight
); );

View File

@ -106,12 +106,11 @@ namespace osu.Game.Beatmaps.Drawables.Cards
{ {
new Drawable[] new Drawable[]
{ {
new OsuSpriteText new TruncatingSpriteText
{ {
Text = new RomanisableString(BeatmapSet.TitleUnicode, BeatmapSet.Title), Text = new RomanisableString(BeatmapSet.TitleUnicode, BeatmapSet.Title),
Font = OsuFont.Default.With(size: 22.5f, weight: FontWeight.SemiBold), Font = OsuFont.Default.With(size: 22.5f, weight: FontWeight.SemiBold),
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Truncate = true
}, },
titleBadgeArea = new FillFlowContainer titleBadgeArea = new FillFlowContainer
{ {
@ -140,21 +139,19 @@ namespace osu.Game.Beatmaps.Drawables.Cards
{ {
new[] new[]
{ {
new OsuSpriteText new TruncatingSpriteText
{ {
Text = createArtistText(), Text = createArtistText(),
Font = OsuFont.Default.With(size: 17.5f, weight: FontWeight.SemiBold), Font = OsuFont.Default.With(size: 17.5f, weight: FontWeight.SemiBold),
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Truncate = true
}, },
Empty() Empty()
}, },
} }
}, },
new OsuSpriteText new TruncatingSpriteText
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Truncate = true,
Text = BeatmapSet.Source, Text = BeatmapSet.Source,
Shadow = false, Shadow = false,
Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold), Font = OsuFont.GetFont(size: 14, weight: FontWeight.SemiBold),

View File

@ -107,12 +107,11 @@ namespace osu.Game.Beatmaps.Drawables.Cards
{ {
new Drawable[] new Drawable[]
{ {
new OsuSpriteText new TruncatingSpriteText
{ {
Text = new RomanisableString(BeatmapSet.TitleUnicode, BeatmapSet.Title), Text = new RomanisableString(BeatmapSet.TitleUnicode, BeatmapSet.Title),
Font = OsuFont.Default.With(size: 22.5f, weight: FontWeight.SemiBold), Font = OsuFont.Default.With(size: 22.5f, weight: FontWeight.SemiBold),
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Truncate = true
}, },
titleBadgeArea = new FillFlowContainer titleBadgeArea = new FillFlowContainer
{ {
@ -141,12 +140,11 @@ namespace osu.Game.Beatmaps.Drawables.Cards
{ {
new[] new[]
{ {
new OsuSpriteText new TruncatingSpriteText
{ {
Text = createArtistText(), Text = createArtistText(),
Font = OsuFont.Default.With(size: 17.5f, weight: FontWeight.SemiBold), Font = OsuFont.Default.With(size: 17.5f, weight: FontWeight.SemiBold),
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Truncate = true
}, },
Empty() Empty()
}, },

View File

@ -264,7 +264,7 @@ namespace osu.Game.Beatmaps
if (beatmapFileStream == null) if (beatmapFileStream == null)
{ {
Logger.Log($"Beatmap failed to load (file {BeatmapInfo.Path} not found on disk at expected location {fileStorePath})", level: LogLevel.Error); Logger.Log($"Beatmap failed to load (file {BeatmapInfo.Path} not found on disk at expected location {fileStorePath})", level: LogLevel.Error);
return null; return new Storyboard();
} }
using (var reader = new LineBufferedReader(beatmapFileStream)) using (var reader = new LineBufferedReader(beatmapFileStream))

View File

@ -6,6 +6,7 @@
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.Mods;
namespace osu.Game.Configuration namespace osu.Game.Configuration
{ {
@ -21,6 +22,7 @@ namespace osu.Game.Configuration
SetDefault(Static.LowBatteryNotificationShownOnce, false); SetDefault(Static.LowBatteryNotificationShownOnce, false);
SetDefault(Static.FeaturedArtistDisclaimerShownOnce, false); SetDefault(Static.FeaturedArtistDisclaimerShownOnce, false);
SetDefault(Static.LastHoverSoundPlaybackTime, (double?)null); SetDefault(Static.LastHoverSoundPlaybackTime, (double?)null);
SetDefault(Static.LastModSelectPanelSamplePlaybackTime, (double?)null);
SetDefault<APISeasonalBackgrounds>(Static.SeasonalBackgrounds, null); SetDefault<APISeasonalBackgrounds>(Static.SeasonalBackgrounds, null);
} }
@ -56,5 +58,11 @@ namespace osu.Game.Configuration
/// Used to debounce hover sounds game-wide to avoid volume saturation, especially in scrolling views with many UI controls like <see cref="SettingsOverlay"/>. /// Used to debounce hover sounds game-wide to avoid volume saturation, especially in scrolling views with many UI controls like <see cref="SettingsOverlay"/>.
/// </summary> /// </summary>
LastHoverSoundPlaybackTime, LastHoverSoundPlaybackTime,
/// <summary>
/// The last playback time in milliseconds of an on/off sample (from <see cref="ModSelectPanel"/>).
/// Used to debounce <see cref="ModSelectPanel"/> on/off sounds game-wide to avoid volume saturation, especially in activating mod presets with many mods.
/// </summary>
LastModSelectPanelSamplePlaybackTime
} }
} }

View File

@ -76,8 +76,9 @@ namespace osu.Game.Database
/// 26 2023-02-05 Added BeatmapHash to ScoreInfo. /// 26 2023-02-05 Added BeatmapHash to ScoreInfo.
/// 27 2023-06-06 Added EditorTimestamp to BeatmapInfo. /// 27 2023-06-06 Added EditorTimestamp to BeatmapInfo.
/// 28 2023-06-08 Added IsLegacyScore to ScoreInfo, parsed from replay files. /// 28 2023-06-08 Added IsLegacyScore to ScoreInfo, parsed from replay files.
/// 29 2023-06-12 Run migration of old lazer scores to be best-effort in the new scoring number space. No actual realm changes.
/// </summary> /// </summary>
private const int schema_version = 28; private const int schema_version = 29;
/// <summary> /// <summary>
/// Lock object which is held during <see cref="BlockAllOperations"/> sections, blocking realm retrieval during blocking periods. /// Lock object which is held during <see cref="BlockAllOperations"/> sections, blocking realm retrieval during blocking periods.
@ -724,6 +725,11 @@ namespace osu.Game.Database
private void applyMigrationsForVersion(Migration migration, ulong targetVersion) private void applyMigrationsForVersion(Migration migration, ulong targetVersion)
{ {
Logger.Log($"Running realm migration to version {targetVersion}...");
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
switch (targetVersion) switch (targetVersion)
{ {
case 7: case 7:
@ -930,7 +936,38 @@ namespace osu.Game.Database
break; break;
} }
case 29:
{
var scores = migration.NewRealm
.All<ScoreInfo>()
.Where(s => !s.IsLegacyScore);
foreach (var score in scores)
{
// Recalculate the old-style standardised score to see if this was an old lazer score.
bool oldScoreMatchesExpectations = StandardisedScoreMigrationTools.GetOldStandardised(score) == score.TotalScore;
// Some older scores don't have correct statistics populated, so let's give them benefit of doubt.
bool scoreIsVeryOld = score.Date < new DateTime(2023, 1, 1, 0, 0, 0);
if (oldScoreMatchesExpectations || scoreIsVeryOld)
{
try
{
long calculatedNew = StandardisedScoreMigrationTools.GetNewStandardised(score);
score.TotalScore = calculatedNew;
}
catch
{
}
}
}
break;
}
} }
Logger.Log($"Migration completed in {stopwatch.ElapsedMilliseconds}ms");
} }
private string? getRulesetShortNameFromLegacyID(long rulesetId) private string? getRulesetShortNameFromLegacyID(long rulesetId)

View File

@ -0,0 +1,196 @@
// 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.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
namespace osu.Game.Database
{
public static class StandardisedScoreMigrationTools
{
public static long GetNewStandardised(ScoreInfo score)
{
int maxJudgementIndex = 0;
// Avoid retrieving from realm inside loops.
int maxCombo = score.MaxCombo;
var ruleset = score.Ruleset.CreateInstance();
var processor = ruleset.CreateScoreProcessor();
processor.TrackHitEvents = false;
var beatmap = new Beatmap();
HitResult maxRulesetJudgement = ruleset.GetHitResults().First().result;
// This is a list of all results, ordered from best to worst.
// We are constructing a "best possible" score from the statistics provided because it's the best we can do.
List<HitResult> sortedHits = score.Statistics
.Where(kvp => kvp.Key.AffectsCombo())
.OrderByDescending(kvp => Judgement.ToNumericResult(kvp.Key))
.SelectMany(kvp => Enumerable.Repeat(kvp.Key, kvp.Value))
.ToList();
// Attempt to use maximum statistics from the database.
var maximumJudgements = score.MaximumStatistics
.Where(kvp => kvp.Key.AffectsCombo())
.OrderByDescending(kvp => Judgement.ToNumericResult(kvp.Key))
.SelectMany(kvp => Enumerable.Repeat(new FakeJudgement(kvp.Key), kvp.Value))
.ToList();
// Some older scores may not have maximum statistics populated correctly.
// In this case we need to fill them with best-known-defaults.
if (maximumJudgements.Count != sortedHits.Count)
{
maximumJudgements = sortedHits
.Select(r => new FakeJudgement(getMaxJudgementFor(r, maxRulesetJudgement)))
.ToList();
}
// This is required to get the correct maximum combo portion.
foreach (var judgement in maximumJudgements)
beatmap.HitObjects.Add(new FakeHit(judgement));
processor.ApplyBeatmap(beatmap);
processor.Mods.Value = score.Mods;
// Insert all misses into a queue.
// These will be nibbled at whenever we need to reset the combo.
Queue<HitResult> misses = new Queue<HitResult>(score.Statistics
.Where(kvp => kvp.Key == HitResult.Miss || kvp.Key == HitResult.LargeTickMiss)
.SelectMany(kvp => Enumerable.Repeat(kvp.Key, kvp.Value)));
foreach (var result in sortedHits)
{
// For the main part of this loop, ignore all misses, as they will be inserted from the queue.
if (result == HitResult.Miss || result == HitResult.LargeTickMiss)
continue;
// Reset combo if required.
if (processor.Combo.Value == maxCombo)
insertMiss();
processor.ApplyResult(new JudgementResult(null!, maximumJudgements[maxJudgementIndex++])
{
Type = result
});
}
// Ensure we haven't forgotten any misses.
while (misses.Count > 0)
insertMiss();
var bonusHits = score.Statistics
.Where(kvp => kvp.Key.IsBonus())
.SelectMany(kvp => Enumerable.Repeat(kvp.Key, kvp.Value));
foreach (var result in bonusHits)
processor.ApplyResult(new JudgementResult(null!, new FakeJudgement(result)) { Type = result });
// Not true for all scores for whatever reason. Oh well.
// Debug.Assert(processor.HighestCombo.Value == score.MaxCombo);
return processor.TotalScore.Value;
void insertMiss()
{
if (misses.Count > 0)
{
processor.ApplyResult(new JudgementResult(null!, maximumJudgements[maxJudgementIndex++])
{
Type = misses.Dequeue(),
});
}
else
{
// We ran out of misses. But we can't let max combo increase beyond the known value,
// so let's forge a miss.
processor.ApplyResult(new JudgementResult(null!, new FakeJudgement(getMaxJudgementFor(HitResult.Miss, maxRulesetJudgement)))
{
Type = HitResult.Miss,
});
}
}
}
private static HitResult getMaxJudgementFor(HitResult hitResult, HitResult max)
{
switch (hitResult)
{
case HitResult.Miss:
case HitResult.Meh:
case HitResult.Ok:
case HitResult.Good:
case HitResult.Great:
case HitResult.Perfect:
return max;
case HitResult.SmallTickMiss:
case HitResult.SmallTickHit:
return HitResult.SmallTickHit;
case HitResult.LargeTickMiss:
case HitResult.LargeTickHit:
return HitResult.LargeTickHit;
}
return HitResult.IgnoreHit;
}
public static long GetOldStandardised(ScoreInfo score)
{
double accuracyScore =
(double)score.Statistics.Where(kvp => kvp.Key.AffectsAccuracy()).Sum(kvp => Judgement.ToNumericResult(kvp.Key) * kvp.Value)
/ score.MaximumStatistics.Where(kvp => kvp.Key.AffectsAccuracy()).Sum(kvp => Judgement.ToNumericResult(kvp.Key) * kvp.Value);
double comboScore = (double)score.MaxCombo / score.MaximumStatistics.Where(kvp => kvp.Key.AffectsCombo()).Sum(kvp => kvp.Value);
double bonusScore = score.Statistics.Where(kvp => kvp.Key.IsBonus()).Sum(kvp => Judgement.ToNumericResult(kvp.Key) * kvp.Value);
double accuracyPortion = 0.3;
switch (score.RulesetID)
{
case 1:
accuracyPortion = 0.75;
break;
case 3:
accuracyPortion = 0.99;
break;
}
double modMultiplier = 1;
foreach (var mod in score.Mods)
modMultiplier *= mod.ScoreMultiplier;
return (long)((1000000 * (accuracyPortion * accuracyScore + (1 - accuracyPortion) * comboScore) + bonusScore) * modMultiplier);
}
private class FakeHit : HitObject
{
private readonly Judgement judgement;
public override Judgement CreateJudgement() => judgement;
public FakeHit(Judgement judgement)
{
this.judgement = judgement;
}
}
private class FakeJudgement : Judgement
{
public override HitResult MaxResult { get; }
public FakeJudgement(HitResult maxResult)
{
MaxResult = maxResult;
}
}
}
}

View File

@ -3,12 +3,19 @@
#nullable disable #nullable disable
using System;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
namespace osu.Game.Graphics.Sprites namespace osu.Game.Graphics.Sprites
{ {
public partial class OsuSpriteText : SpriteText public partial class OsuSpriteText : SpriteText
{ {
[Obsolete("Use TruncatingSpriteText instead.")]
public new bool Truncate
{
set => throw new InvalidOperationException($"Use {nameof(TruncatingSpriteText)} instead.");
}
public OsuSpriteText() public OsuSpriteText()
{ {
Shadow = true; Shadow = true;

View File

@ -0,0 +1,29 @@
// 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.Graphics.Cursor;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
namespace osu.Game.Graphics.Sprites
{
/// <summary>
/// A derived version of <see cref="OsuSpriteText"/> which automatically shows non-truncated text in tooltip when required.
/// </summary>
public sealed partial class TruncatingSpriteText : OsuSpriteText, IHasTooltip
{
/// <summary>
/// Whether a tooltip should be shown with non-truncated text on hover.
/// </summary>
public bool ShowTooltip { get; init; } = true;
public LocalisableString TooltipText => Text;
public override bool HandlePositionalInput => IsTruncated && ShowTooltip;
public TruncatingSpriteText()
{
((SpriteText)this).Truncate = true;
}
}
}

View File

@ -335,12 +335,11 @@ namespace osu.Game.Graphics.UserInterface
{ {
new Drawable[] new Drawable[]
{ {
Text = new OsuSpriteText Text = new TruncatingSpriteText
{ {
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Truncate = true,
}, },
Icon = new SpriteIcon Icon = new SpriteIcon
{ {

View File

@ -85,7 +85,7 @@ namespace osu.Game.Overlays.Chat.ChannelList
new Drawable?[] new Drawable?[]
{ {
createIcon(), createIcon(),
text = new OsuSpriteText text = new TruncatingSpriteText
{ {
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,
@ -94,7 +94,6 @@ namespace osu.Game.Overlays.Chat.ChannelList
Colour = colourProvider.Light3, Colour = colourProvider.Light3,
Margin = new MarginPadding { Bottom = 2 }, Margin = new MarginPadding { Bottom = 2 },
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Truncate = true,
}, },
createMentionPill(), createMentionPill(),
close = createCloseButton(), close = createCloseButton(),

View File

@ -73,14 +73,13 @@ namespace osu.Game.Overlays.Chat
Width = chatting_text_width, Width = chatting_text_width,
Masking = true, Masking = true,
Padding = new MarginPadding { Horizontal = padding }, Padding = new MarginPadding { Horizontal = padding },
Child = chattingText = new OsuSpriteText Child = chattingText = new TruncatingSpriteText
{ {
MaxWidth = chatting_text_width - padding * 2, MaxWidth = chatting_text_width - padding * 2,
Font = OsuFont.Torus.With(size: 20), Font = OsuFont.Torus.With(size: 20),
Colour = colourProvider.Background1, Colour = colourProvider.Background1,
Anchor = Anchor.CentreRight, Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight, Origin = Anchor.CentreRight,
Truncate = true,
}, },
}, },
searchIconContainer = new Container searchIconContainer = new Container

View File

@ -83,11 +83,9 @@ namespace osu.Game.Overlays.Chat
Action = openUserProfile; Action = openUserProfile;
drawableText = new OsuSpriteText drawableText = new TruncatingSpriteText
{ {
Shadow = false, Shadow = false,
Truncate = true,
EllipsisString = "…",
Anchor = Anchor.TopRight, Anchor = Anchor.TopRight,
Origin = Anchor.TopRight, Origin = Anchor.TopRight,
}; };

View File

@ -100,17 +100,15 @@ namespace osu.Game.Overlays.Dashboard.Home
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
Children = new Drawable[] Children = new Drawable[]
{ {
new OsuSpriteText new TruncatingSpriteText
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Truncate = true,
Font = OsuFont.GetFont(weight: FontWeight.Regular), Font = OsuFont.GetFont(weight: FontWeight.Regular),
Text = BeatmapSet.Title Text = BeatmapSet.Title
}, },
new OsuSpriteText new TruncatingSpriteText
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Truncate = true,
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular), Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular),
Text = BeatmapSet.Artist Text = BeatmapSet.Artist
}, },

View File

@ -176,7 +176,7 @@ namespace osu.Game.Overlays.Mods
dequeuedAction(); dequeuedAction();
// each time we play an animation, we decrease the time until the next animation (to ramp the visual and audible elements). // each time we play an animation, we decrease the time until the next animation (to ramp the visual and audible elements).
selectionDelay = Math.Max(30, selectionDelay * 0.8f); selectionDelay = Math.Max(ModSelectPanel.SAMPLE_PLAYBACK_DELAY, selectionDelay * 0.8f);
lastSelection = Time.Current; lastSelection = Time.Current;
} }
else else

View File

@ -14,6 +14,7 @@ using osu.Framework.Input.Events;
using osu.Framework.Localisation; using osu.Framework.Localisation;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Configuration;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
@ -45,6 +46,8 @@ namespace osu.Game.Overlays.Mods
public const float CORNER_RADIUS = 7; public const float CORNER_RADIUS = 7;
public const float HEIGHT = 42; public const float HEIGHT = 42;
public const double SAMPLE_PLAYBACK_DELAY = 30;
protected virtual float IdleSwitchWidth => 14; protected virtual float IdleSwitchWidth => 14;
protected virtual float ExpandedSwitchWidth => 30; protected virtual float ExpandedSwitchWidth => 30;
protected virtual Colour4 BackgroundColour => Active.Value ? AccentColour.Darken(0.3f) : ColourProvider.Background3; protected virtual Colour4 BackgroundColour => Active.Value ? AccentColour.Darken(0.3f) : ColourProvider.Background3;
@ -69,6 +72,8 @@ namespace osu.Game.Overlays.Mods
private Sample? sampleOff; private Sample? sampleOff;
private Sample? sampleOn; private Sample? sampleOn;
private Bindable<double?> lastPlaybackTime = null!;
protected ModSelectPanel() protected ModSelectPanel()
{ {
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
@ -118,23 +123,23 @@ namespace osu.Game.Overlays.Mods
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
Children = new[] Children = new[]
{ {
titleText = new OsuSpriteText titleText = new TruncatingSpriteText
{ {
Font = OsuFont.TorusAlternate.With(size: 18, weight: FontWeight.SemiBold), Font = OsuFont.TorusAlternate.With(size: 18, weight: FontWeight.SemiBold),
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Truncate = true,
Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0),
Margin = new MarginPadding Margin = new MarginPadding
{ {
Left = -18 * ShearedOverlayContainer.SHEAR Left = -18 * ShearedOverlayContainer.SHEAR
} },
ShowTooltip = false, // Tooltip is handled by `IncompatibilityDisplayingModPanel`.
}, },
descriptionText = new OsuSpriteText descriptionText = new TruncatingSpriteText
{ {
Font = OsuFont.Default.With(size: 12), Font = OsuFont.Default.With(size: 12),
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Truncate = true, Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0),
Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0) ShowTooltip = false, // Tooltip is handled by `IncompatibilityDisplayingModPanel`.
} }
} }
} }
@ -163,13 +168,15 @@ namespace osu.Game.Overlays.Mods
protected abstract void Deselect(); protected abstract void Deselect();
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(AudioManager audio, ISamplePlaybackDisabler? samplePlaybackDisabler) private void load(AudioManager audio, SessionStatics statics, ISamplePlaybackDisabler? samplePlaybackDisabler)
{ {
sampleOn = audio.Samples.Get(@"UI/check-on"); sampleOn = audio.Samples.Get(@"UI/check-on");
sampleOff = audio.Samples.Get(@"UI/check-off"); sampleOff = audio.Samples.Get(@"UI/check-off");
if (samplePlaybackDisabler != null) if (samplePlaybackDisabler != null)
((IBindable<bool>)samplePlaybackDisabled).BindTo(samplePlaybackDisabler.SamplePlaybackDisabled); ((IBindable<bool>)samplePlaybackDisabled).BindTo(samplePlaybackDisabler.SamplePlaybackDisabled);
lastPlaybackTime = statics.GetBindable<double?>(Static.LastHoverSoundPlaybackTime);
} }
protected sealed override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds(sampleSet); protected sealed override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds(sampleSet);
@ -192,10 +199,17 @@ namespace osu.Game.Overlays.Mods
if (samplePlaybackDisabled.Value) if (samplePlaybackDisabled.Value)
return; return;
if (Active.Value) bool enoughTimePassedSinceLastPlayback = !lastPlaybackTime.Value.HasValue || Time.Current - lastPlaybackTime.Value >= SAMPLE_PLAYBACK_DELAY;
sampleOn?.Play();
else if (enoughTimePassedSinceLastPlayback)
sampleOff?.Play(); {
if (Active.Value)
sampleOn?.Play();
else
sampleOff?.Play();
lastPlaybackTime.Value = Time.Current;
}
} }
protected override bool OnHover(HoverEvent e) protected override bool OnHover(HoverEvent e)

View File

@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio
{ {
protected override LocalisableString Header => AudioSettingsStrings.OffsetHeader; protected override LocalisableString Header => AudioSettingsStrings.OffsetHeader;
public override IEnumerable<LocalisableString> FilterTerms => base.FilterTerms.Concat(new LocalisableString[] { "universal", "uo", "timing" }); public override IEnumerable<LocalisableString> FilterTerms => base.FilterTerms.Concat(new LocalisableString[] { "universal", "uo", "timing", "delay", "latency" });
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuConfigManager config) private void load(OsuConfigManager config)

View File

@ -30,6 +30,11 @@ namespace osu.Game.Rulesets.Scoring
/// </summary> /// </summary>
protected int MaxHits { get; private set; } protected int MaxHits { get; private set; }
/// <summary>
/// Whether <see cref="SimulateAutoplay"/> is currently running.
/// </summary>
protected bool IsSimulating { get; private set; }
/// <summary> /// <summary>
/// The total number of judged <see cref="HitObject"/>s at the current point in time. /// The total number of judged <see cref="HitObject"/>s at the current point in time.
/// </summary> /// </summary>
@ -146,6 +151,8 @@ namespace osu.Game.Rulesets.Scoring
/// <param name="beatmap">The <see cref="IBeatmap"/> to simulate.</param> /// <param name="beatmap">The <see cref="IBeatmap"/> to simulate.</param>
protected virtual void SimulateAutoplay(IBeatmap beatmap) protected virtual void SimulateAutoplay(IBeatmap beatmap)
{ {
IsSimulating = true;
foreach (var obj in beatmap.HitObjects) foreach (var obj in beatmap.HitObjects)
simulate(obj); simulate(obj);
@ -163,6 +170,8 @@ namespace osu.Game.Rulesets.Scoring
result.Type = GetSimulatedHitResult(judgement); result.Type = GetSimulatedHitResult(judgement);
ApplyResult(result); ApplyResult(result);
} }
IsSimulating = false;
} }
protected override void Update() protected override void Update()

View File

@ -30,6 +30,14 @@ namespace osu.Game.Rulesets.Scoring
private const double accuracy_cutoff_c = 0.7; private const double accuracy_cutoff_c = 0.7;
private const double accuracy_cutoff_d = 0; private const double accuracy_cutoff_d = 0;
/// <summary>
/// Whether <see cref="HitEvents"/> should be populated during application of results.
/// </summary>
/// <remarks>
/// Should only be disabled for special cases.
/// When disabled, <see cref="JudgementProcessor.RevertResult"/> cannot be used.</remarks>
internal bool TrackHitEvents = true;
/// <summary> /// <summary>
/// Invoked when this <see cref="ScoreProcessor"/> was reset from a replay frame. /// Invoked when this <see cref="ScoreProcessor"/> was reset from a replay frame.
/// </summary> /// </summary>
@ -226,10 +234,16 @@ namespace osu.Game.Rulesets.Scoring
ApplyScoreChange(result); ApplyScoreChange(result);
hitEvents.Add(CreateHitEvent(result)); if (!IsSimulating)
lastHitObject = result.HitObject; {
if (TrackHitEvents)
{
hitEvents.Add(CreateHitEvent(result));
lastHitObject = result.HitObject;
}
updateScore(); updateScore();
}
} }
/// <summary> /// <summary>
@ -242,6 +256,9 @@ namespace osu.Game.Rulesets.Scoring
protected sealed override void RevertResultInternal(JudgementResult result) protected sealed override void RevertResultInternal(JudgementResult result)
{ {
if (!TrackHitEvents)
throw new InvalidOperationException(@$"Rewind is not supported when {nameof(TrackHitEvents)} is disabled.");
Combo.Value = result.ComboAtJudgement; Combo.Value = result.ComboAtJudgement;
HighestCombo.Value = result.HighestComboAtJudgement; HighestCombo.Value = result.HighestComboAtJudgement;
@ -311,6 +328,9 @@ namespace osu.Game.Rulesets.Scoring
/// <param name="storeResults">Whether to store the current state of the <see cref="ScoreProcessor"/> for future use.</param> /// <param name="storeResults">Whether to store the current state of the <see cref="ScoreProcessor"/> for future use.</param>
protected override void Reset(bool storeResults) protected override void Reset(bool storeResults)
{ {
// Run one last time to store max values.
updateScore();
base.Reset(storeResults); base.Reset(storeResults);
hitEvents.Clear(); hitEvents.Clear();

View File

@ -76,6 +76,9 @@ namespace osu.Game.Screens.Edit.Components
protected override bool OnKeyDown(KeyDownEvent e) protected override bool OnKeyDown(KeyDownEvent e)
{ {
if (e.Repeat)
return false;
switch (e.Key) switch (e.Key)
{ {
case Key.Space: case Key.Space:

View File

@ -533,6 +533,9 @@ namespace osu.Game.Screens.Edit
// Track traversal keys. // Track traversal keys.
// Matching osu-stable implementations. // Matching osu-stable implementations.
case Key.Z: case Key.Z:
if (e.Repeat)
return false;
// Seek to first object time, or track start if already there. // Seek to first object time, or track start if already there.
double? firstObjectTime = editorBeatmap.HitObjects.FirstOrDefault()?.StartTime; double? firstObjectTime = editorBeatmap.HitObjects.FirstOrDefault()?.StartTime;
@ -543,12 +546,18 @@ namespace osu.Game.Screens.Edit
return true; return true;
case Key.X: case Key.X:
if (e.Repeat)
return false;
// Restart playback from beginning of track. // Restart playback from beginning of track.
clock.Seek(0); clock.Seek(0);
clock.Start(); clock.Start();
return true; return true;
case Key.C: case Key.C:
if (e.Repeat)
return false;
// Pause or resume. // Pause or resume.
if (clock.IsRunning) if (clock.IsRunning)
clock.Stop(); clock.Stop();
@ -557,6 +566,9 @@ namespace osu.Game.Screens.Edit
return true; return true;
case Key.V: case Key.V:
if (e.Repeat)
return false;
// Seek to last object time, or track end if already there. // Seek to last object time, or track end if already there.
// Note that in osu-stable subsequent presses when at track end won't return to last object. // Note that in osu-stable subsequent presses when at track end won't return to last object.
// This has intentionally been changed to make it more useful. // This has intentionally been changed to make it more useful.

View File

@ -103,29 +103,19 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
CornerRadius = CORNER_RADIUS, CornerRadius = CORNER_RADIUS,
Children = new Drawable[] Children = new Drawable[]
{ {
new GridContainer new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
ColumnDimensions = new[] Colour = colours.Background5,
{ Width = 0.2f,
new Dimension(GridSizeMode.Relative, 0.2f) },
}, new Box
Content = new[] {
{ Anchor = Anchor.TopRight,
new Drawable[] Origin = Anchor.TopRight,
{ RelativeSizeAxes = Axes.Both,
new Box Colour = ColourInfo.GradientHorizontal(colours.Background5, colours.Background5.Opacity(0.3f)),
{ Width = 0.8f,
RelativeSizeAxes = Axes.Both,
Colour = colours.Background5,
},
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientHorizontal(colours.Background5, colours.Background5.Opacity(0.3f))
},
}
}
}, },
new Container new Container
{ {

View File

@ -235,7 +235,7 @@ namespace osu.Game.Screens.Play.HUD
} }
} }
}, },
usernameText = new OsuSpriteText usernameText = new TruncatingSpriteText
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Width = 0.6f, Width = 0.6f,
@ -244,7 +244,6 @@ namespace osu.Game.Screens.Play.HUD
Colour = Color4.White, Colour = Color4.White,
Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold), Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold),
Text = User?.Username ?? string.Empty, Text = User?.Username ?? string.Empty,
Truncate = true,
Shadow = false, Shadow = false,
} }
} }

View File

@ -101,23 +101,21 @@ namespace osu.Game.Screens.Ranking.Expanded
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
Children = new Drawable[] Children = new Drawable[]
{ {
new OsuSpriteText new TruncatingSpriteText
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
Text = new RomanisableString(metadata.TitleUnicode, metadata.Title), Text = new RomanisableString(metadata.TitleUnicode, metadata.Title),
Font = OsuFont.Torus.With(size: 20, weight: FontWeight.SemiBold), Font = OsuFont.Torus.With(size: 20, weight: FontWeight.SemiBold),
MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2, MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2,
Truncate = true,
}, },
new OsuSpriteText new TruncatingSpriteText
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
Text = new RomanisableString(metadata.ArtistUnicode, metadata.Artist), Text = new RomanisableString(metadata.ArtistUnicode, metadata.Artist),
Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold), Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold),
MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2, MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2,
Truncate = true,
}, },
new Container new Container
{ {
@ -156,14 +154,13 @@ namespace osu.Game.Screens.Ranking.Expanded
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Children = new Drawable[] Children = new Drawable[]
{ {
new OsuSpriteText new TruncatingSpriteText
{ {
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
Text = beatmap.DifficultyName, Text = beatmap.DifficultyName,
Font = OsuFont.Torus.With(size: 16, weight: FontWeight.SemiBold), Font = OsuFont.Torus.With(size: 16, weight: FontWeight.SemiBold),
MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2, MaxWidth = ScorePanel.EXPANDED_WIDTH - padding * 2,
Truncate = true,
}, },
new OsuTextFlowContainer(s => s.Font = OsuFont.Torus.With(size: 12)) new OsuTextFlowContainer(s => s.Font = OsuFont.Torus.With(size: 12))
{ {

View File

@ -233,12 +233,11 @@ namespace osu.Game.Screens.Select
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Children = new Drawable[] Children = new Drawable[]
{ {
VersionLabel = new OsuSpriteText VersionLabel = new TruncatingSpriteText
{ {
Text = beatmapInfo.DifficultyName, Text = beatmapInfo.DifficultyName,
Font = OsuFont.GetFont(size: 24, italics: true), Font = OsuFont.GetFont(size: 24, italics: true),
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Truncate = true,
}, },
} }
}, },
@ -286,19 +285,17 @@ namespace osu.Game.Screens.Select
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Children = new Drawable[] Children = new Drawable[]
{ {
TitleLabel = new OsuSpriteText TitleLabel = new TruncatingSpriteText
{ {
Current = { BindTarget = titleBinding }, Current = { BindTarget = titleBinding },
Font = OsuFont.GetFont(size: 28, italics: true), Font = OsuFont.GetFont(size: 28, italics: true),
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Truncate = true,
}, },
ArtistLabel = new OsuSpriteText ArtistLabel = new TruncatingSpriteText
{ {
Current = { BindTarget = artistBinding }, Current = { BindTarget = artistBinding },
Font = OsuFont.GetFont(size: 17, italics: true), Font = OsuFont.GetFont(size: 17, italics: true),
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Truncate = true,
}, },
MapperContainer = new FillFlowContainer MapperContainer = new FillFlowContainer
{ {

View File

@ -112,11 +112,11 @@ namespace osu.Game.Screens.Select.Carousel
background = new DelayedLoadWrapper(() => new SetPanelBackground(manager.GetWorkingBeatmap(beatmapSet.Beatmaps.MinBy(b => b.OnlineID))) background = new DelayedLoadWrapper(() => new SetPanelBackground(manager.GetWorkingBeatmap(beatmapSet.Beatmaps.MinBy(b => b.OnlineID)))
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}, 300) }, 200)
{ {
RelativeSizeAxes = Axes.Both RelativeSizeAxes = Axes.Both
}, },
mainFlow = new DelayedLoadWrapper(() => new SetPanelContent((CarouselBeatmapSet)Item), 100) mainFlow = new DelayedLoadWrapper(() => new SetPanelContent((CarouselBeatmapSet)Item), 50)
{ {
RelativeSizeAxes = Axes.Both RelativeSizeAxes = Axes.Both
}, },
@ -126,7 +126,7 @@ namespace osu.Game.Screens.Select.Carousel
mainFlow.DelayedLoadComplete += fadeContentIn; mainFlow.DelayedLoadComplete += fadeContentIn;
} }
private void fadeContentIn(Drawable d) => d.FadeInFromZero(750, Easing.OutQuint); private void fadeContentIn(Drawable d) => d.FadeInFromZero(150);
protected override void Deselected() protected override void Deselected()
{ {

View File

@ -37,7 +37,7 @@
</PackageReference> </PackageReference>
<PackageReference Include="Realm" Version="10.20.0" /> <PackageReference Include="Realm" Version="10.20.0" />
<PackageReference Include="ppy.osu.Framework" Version="2023.608.0" /> <PackageReference Include="ppy.osu.Framework" Version="2023.608.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2023.510.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2023.605.0" />
<PackageReference Include="Sentry" Version="3.28.1" /> <PackageReference Include="Sentry" Version="3.28.1" />
<PackageReference Include="SharpCompress" Version="0.32.2" /> <PackageReference Include="SharpCompress" Version="0.32.2" />
<PackageReference Include="NUnit" Version="3.13.3" /> <PackageReference Include="NUnit" Version="3.13.3" />