Merge branch 'master' into notification-fling-right

This commit is contained in:
Dan Balasescu 2022-09-13 20:01:56 +09:00 committed by GitHub
commit 4a49433e2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 142 additions and 36 deletions

View File

@ -0,0 +1,75 @@
// 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.Game.Beatmaps;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Tests.Beatmaps;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Editing
{
public class TestSceneSelectionBlueprintDeselection : EditorTestScene
{
protected override Ruleset CreateEditorRuleset() => new OsuRuleset();
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false);
[Test]
public void TestSingleDeleteAtSameTime()
{
HitCircle? circle1 = null;
AddStep("add two circles at the same time", () =>
{
EditorClock.Seek(0);
circle1 = new HitCircle();
var circle2 = new HitCircle();
EditorBeatmap.Add(circle1);
EditorBeatmap.Add(circle2);
EditorBeatmap.SelectedHitObjects.Add(circle1);
EditorBeatmap.SelectedHitObjects.Add(circle2);
});
AddStep("delete the first circle", () => EditorBeatmap.Remove(circle1));
AddAssert("one hitobject remains", () => EditorBeatmap.HitObjects.Count == 1);
AddAssert("one hitobject selected", () => EditorBeatmap.SelectedHitObjects.Count == 1);
}
[Test]
public void TestBigStackDeleteAtSameTime()
{
AddStep("add 20 circles at the same time", () =>
{
EditorClock.Seek(0);
for (int i = 0; i < 20; i++)
{
EditorBeatmap.Add(new HitCircle());
}
});
AddStep("select half of the circles", () =>
{
foreach (var hitObject in EditorBeatmap.HitObjects.SkipLast(10).Reverse())
{
EditorBeatmap.SelectedHitObjects.Add(hitObject);
}
});
AddStep("delete all selected circles", () =>
{
InputManager.PressKey(Key.Delete);
InputManager.ReleaseKey(Key.Delete);
});
AddAssert("10 hitobjects remain", () => EditorBeatmap.HitObjects.Count == 10);
AddAssert("no hitobjects selected", () => EditorBeatmap.SelectedHitObjects.Count == 0);
}
}
}

View File

@ -126,7 +126,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("select unchanged Difficulty Adjust mod", () =>
{
var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance().AsNonNull();
var difficultyAdjustMod = ruleset.CreateMod<ModDifficultyAdjust>();
var difficultyAdjustMod = ruleset.CreateMod<ModDifficultyAdjust>().AsNonNull();
difficultyAdjustMod.ReadFromDifficulty(advancedStats.BeatmapInfo.Difficulty);
SelectedMods.Value = new[] { difficultyAdjustMod };
});
@ -145,7 +145,7 @@ namespace osu.Game.Tests.Visual.SongSelect
AddStep("select changed Difficulty Adjust mod", () =>
{
var ruleset = advancedStats.BeatmapInfo.Ruleset.CreateInstance().AsNonNull();
var difficultyAdjustMod = ruleset.CreateMod<OsuModDifficultyAdjust>();
var difficultyAdjustMod = ruleset.CreateMod<OsuModDifficultyAdjust>().AsNonNull();
var originalDifficulty = advancedStats.BeatmapInfo.Difficulty;
difficultyAdjustMod.ReadFromDifficulty(originalDifficulty);

View File

@ -272,7 +272,24 @@ namespace osu.Game.Rulesets.Scoring
}
/// <summary>
/// Computes the total score of a given finalised <see cref="ScoreInfo"/>. This should be used when a score is known to be complete.
/// Computes the accuracy of a given <see cref="ScoreInfo"/>.
/// </summary>
/// <param name="scoreInfo">The <see cref="ScoreInfo"/> to compute the total score of.</param>
/// <returns>The score's accuracy.</returns>
[Pure]
public double ComputeAccuracy(ScoreInfo scoreInfo)
{
if (!ruleset.RulesetInfo.Equals(scoreInfo.Ruleset))
throw new ArgumentException($"Unexpected score ruleset. Expected \"{ruleset.RulesetInfo.ShortName}\" but was \"{scoreInfo.Ruleset.ShortName}\".");
// We only extract scoring values from the score's statistics. This is because accuracy is always relative to the point of pass or fail rather than relative to the whole beatmap.
extractScoringValues(scoreInfo.Statistics, out var current, out var maximum);
return maximum.BaseScore > 0 ? current.BaseScore / maximum.BaseScore : 1;
}
/// <summary>
/// Computes the total score of a given <see cref="ScoreInfo"/>.
/// </summary>
/// <remarks>
/// Does not require <see cref="JudgementProcessor.ApplyBeatmap"/> to have been called before use.

View File

@ -1,8 +1,6 @@
// 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.
#nullable disable
using System;
using System.Collections.Generic;
using System.IO;
@ -12,6 +10,7 @@ using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Bindables;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics.Rendering;
using osu.Framework.Graphics.Shaders;
using osu.Framework.Graphics.Textures;
@ -44,29 +43,26 @@ namespace osu.Game.Rulesets.UI
public ShaderManager ShaderManager { get; }
/// <summary>
/// The ruleset config manager.
/// The ruleset config manager. May be null if ruleset does not expose a configuration manager.
/// </summary>
public IRulesetConfigManager RulesetConfigManager { get; private set; }
public IRulesetConfigManager? RulesetConfigManager { get; }
public DrawableRulesetDependencies(Ruleset ruleset, IReadOnlyDependencyContainer parent)
: base(parent)
{
var resources = ruleset.CreateResourceStore();
if (resources != null)
{
var host = parent.Get<GameHost>();
var host = parent.Get<GameHost>();
TextureStore = new TextureStore(host.Renderer, parent.Get<GameHost>().CreateTextureLoaderStore(new NamespacedResourceStore<byte[]>(resources, @"Textures")));
CacheAs(TextureStore = new FallbackTextureStore(host.Renderer, TextureStore, parent.Get<TextureStore>()));
TextureStore = new TextureStore(host.Renderer, parent.Get<GameHost>().CreateTextureLoaderStore(new NamespacedResourceStore<byte[]>(resources, @"Textures")));
CacheAs(TextureStore = new FallbackTextureStore(host.Renderer, TextureStore, parent.Get<TextureStore>()));
SampleStore = parent.Get<AudioManager>().GetSampleStore(new NamespacedResourceStore<byte[]>(resources, @"Samples"));
SampleStore.PlaybackConcurrency = OsuGameBase.SAMPLE_CONCURRENCY;
CacheAs(SampleStore = new FallbackSampleStore(SampleStore, parent.Get<ISampleStore>()));
SampleStore = parent.Get<AudioManager>().GetSampleStore(new NamespacedResourceStore<byte[]>(resources, @"Samples"));
SampleStore.PlaybackConcurrency = OsuGameBase.SAMPLE_CONCURRENCY;
CacheAs(SampleStore = new FallbackSampleStore(SampleStore, parent.Get<ISampleStore>()));
ShaderManager = new ShaderManager(host.Renderer, new NamespacedResourceStore<byte[]>(resources, @"Shaders"));
CacheAs(ShaderManager = new FallbackShaderManager(host.Renderer, ShaderManager, parent.Get<ShaderManager>()));
}
ShaderManager = new ShaderManager(host.Renderer, new NamespacedResourceStore<byte[]>(resources, @"Shaders"));
CacheAs(ShaderManager = new FallbackShaderManager(host.Renderer, ShaderManager, parent.Get<ShaderManager>()));
RulesetConfigManager = parent.Get<IRulesetConfigCache>().GetConfigFor(ruleset);
if (RulesetConfigManager != null)
@ -96,10 +92,9 @@ namespace osu.Game.Rulesets.UI
isDisposed = true;
SampleStore?.Dispose();
TextureStore?.Dispose();
ShaderManager?.Dispose();
RulesetConfigManager = null;
if (ShaderManager.IsNotNull()) SampleStore.Dispose();
if (TextureStore.IsNotNull()) TextureStore.Dispose();
if (ShaderManager.IsNotNull()) ShaderManager.Dispose();
}
#endregion
@ -160,7 +155,7 @@ namespace osu.Game.Rulesets.UI
public void Dispose()
{
primary?.Dispose();
if (primary.IsNotNull()) primary.Dispose();
}
}
@ -185,7 +180,7 @@ namespace osu.Game.Rulesets.UI
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
primary?.Dispose();
if (primary.IsNotNull()) primary.Dispose();
}
}
@ -201,12 +196,12 @@ namespace osu.Game.Rulesets.UI
this.fallback = fallback;
}
public override byte[] LoadRaw(string name) => primary.LoadRaw(name) ?? fallback.LoadRaw(name);
public override byte[]? LoadRaw(string name) => primary.LoadRaw(name) ?? fallback.LoadRaw(name);
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
primary?.Dispose();
if (primary.IsNotNull()) primary.Dispose();
}
}
}

View File

@ -24,11 +24,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
{
base.LoadComplete();
editorBeatmap.HitObjectUpdated += hitObjectUpdated;
editorBeatmap.BeatmapReprocessed += SortInternal;
}
private void hitObjectUpdated(HitObject _) => SortInternal();
public override void Add(SelectionBlueprint<HitObject> drawable)
{
SortInternal();
@ -72,7 +70,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
base.Dispose(isDisposing);
if (editorBeatmap != null)
editorBeatmap.HitObjectUpdated -= hitObjectUpdated;
editorBeatmap.BeatmapReprocessed -= SortInternal;
}
}
}

View File

@ -48,6 +48,15 @@ namespace osu.Game.Screens.Edit
/// </summary>
public event Action<HitObject> HitObjectUpdated;
/// <summary>
/// Invoked after any state changes occurred which triggered a beatmap reprocess via an <see cref="IBeatmapProcessor"/>.
/// </summary>
/// <remarks>
/// Beatmap processing may change the order of hitobjects. This event gives external components a chance to handle any changes
/// not covered by the <see cref="HitObjectAdded"/> / <see cref="HitObjectUpdated"/> / <see cref="HitObjectRemoved"/> events.
/// </remarks>
public event Action BeatmapReprocessed;
/// <summary>
/// All currently selected <see cref="HitObject"/>s.
/// </summary>
@ -331,6 +340,8 @@ namespace osu.Game.Screens.Edit
beatmapProcessor?.PostProcess();
BeatmapReprocessed?.Invoke();
// callbacks may modify the lists so let's be safe about it
var deletes = batchPendingDeletes.ToArray();
batchPendingDeletes.Clear();

View File

@ -1,6 +1,7 @@
// 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.Threading;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Game.Beatmaps;
@ -34,8 +35,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate
}
[BackgroundDependencyLoader]
private void load()
private void load(CancellationToken cancellationToken)
{
// HUD overlay may not be loaded if load has been cancelled early.
if (cancellationToken.IsCancellationRequested)
return;
HUDOverlay.PlayerSettingsOverlay.Expire();
HUDOverlay.HoldToQuit.Expire();
}

View File

@ -47,7 +47,10 @@ namespace osu.Game.Screens.Play.HUD
if (clock != null)
gameplayClock = clock;
AutoSizeAxes = Axes.Y;
// Lock height so changes in text autosize (if character height changes)
// don't cause parent invalidation.
Height = 14;
Children = new Drawable[]
{
new Container

View File

@ -56,7 +56,7 @@ namespace osu.Game.Skinning
if (result != HitResult.Miss)
{
//new judgement shows old as a temporary effect
AddInternal(temporaryOldStyle = new LegacyJudgementPieceOld(result, createMainDrawable, 1.05f)
AddInternal(temporaryOldStyle = new LegacyJudgementPieceOld(result, createMainDrawable, 1.05f, true)
{
Blending = BlendingParameters.Additive,
Anchor = Anchor.Centre,

View File

@ -18,11 +18,13 @@ namespace osu.Game.Skinning
private readonly HitResult result;
private readonly float finalScale;
private readonly bool forceTransforms;
public LegacyJudgementPieceOld(HitResult result, Func<Drawable> createMainDrawable, float finalScale = 1f)
public LegacyJudgementPieceOld(HitResult result, Func<Drawable> createMainDrawable, float finalScale = 1f, bool forceTransforms = false)
{
this.result = result;
this.finalScale = finalScale;
this.forceTransforms = forceTransforms;
AutoSizeAxes = Axes.Both;
Origin = Anchor.Centre;
@ -43,8 +45,8 @@ namespace osu.Game.Skinning
this.FadeInFromZero(fade_in_length);
this.Delay(fade_out_delay).FadeOut(fade_out_length);
// legacy judgements don't play any transforms if they are an animation.
if (animation?.FrameCount > 1)
// legacy judgements don't play any transforms if they are an animation.... UNLESS they are the temporary displayed judgement from new piece.
if (animation?.FrameCount > 1 && !forceTransforms)
return;
switch (result)