Merge branch 'master' into ios-app-delegates

This commit is contained in:
Dean Herbert 2024-12-20 20:18:51 +09:00
commit f698284171
No known key found for this signature in database
42 changed files with 1947 additions and 311 deletions

View File

@ -10,7 +10,7 @@
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.1219.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.1220.0" />
</ItemGroup>
<PropertyGroup>
<!-- Fody does not handle Android build well, and warns when unchanged.

View File

@ -22,9 +22,11 @@ namespace osu.Game.Rulesets.Mania.Tests
[TestCase("basic")]
[TestCase("zero-length-slider")]
[TestCase("mania-specific-spinner")]
[TestCase("20544")]
[TestCase("100374")]
[TestCase("1450162")]
[TestCase("4869637")]
public void Test(string name) => base.Test(name);
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,60 @@
{
"Mappings": [
{
"RandomW": 273071671,
"RandomX": 842502087,
"RandomY": 3579807591,
"RandomZ": 273326509,
"StartTime": 11783.0,
"Objects": [
{
"StartTime": 11783.0,
"EndTime": 15116.0,
"Column": 0
}
]
},
{
"RandomW": 2659271247,
"RandomX": 3579807591,
"RandomY": 273326509,
"RandomZ": 273071671,
"StartTime": 91545.0,
"Objects": [
{
"StartTime": 91545.0,
"EndTime": 92735.0,
"Column": 0
}
]
},
{
"RandomW": 3083635271,
"RandomX": 273326509,
"RandomY": 273071671,
"RandomZ": 2659271247,
"StartTime": 152497.0,
"Objects": [
{
"StartTime": 152497.0,
"EndTime": 153687.0,
"Column": 1
}
]
},
{
"RandomW": 4073591514,
"RandomX": 273071671,
"RandomY": 2659271247,
"RandomZ": 3083635271,
"StartTime": 231545.0,
"Objects": [
{
"StartTime": 231545.0,
"EndTime": 232974.0,
"Column": 3
}
]
}
]
}

View File

@ -0,0 +1,27 @@
osu file format v14
[General]
Mode: 3
[Difficulty]
HPDrainRate:5
CircleSize:4
OverallDifficulty:5
ApproachRate:0
SliderMultiplier:2.6
SliderTickRate:1
[TimingPoints]
355,476.190476190476,4,2,1,60,1,0
60652,-100,4,2,1,60,0,1
92735,-100,4,2,1,60,0,0
121485,-100,4,2,1,60,0,1
153688,-100,4,2,1,60,0,0
182497,-100,4,2,1,60,0,1
213688,-100,4,2,1,60,0,0
[HitObjects]
256,192,11783,12,0,15116,0:0:0:0:
256,192,91545,12,0,92735,0:0:0:0:
256,192,152497,12,0,153687,0:0:0:0:
256,192,231545,12,0,232974,0:0:0:0:

View File

@ -7,11 +7,13 @@ using System.Linq;
using System.Collections.Generic;
using System.Threading;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Mania.Beatmaps.Patterns;
using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Rulesets.Scoring.Legacy;
using osu.Game.Utils;
using osuTK;
@ -124,16 +126,109 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
protected override IEnumerable<ManiaHitObject> ConvertHitObject(HitObject original, IBeatmap beatmap, CancellationToken cancellationToken)
{
if (original is ManiaHitObject maniaOriginal)
{
yield return maniaOriginal;
LegacyHitObjectType legacyType;
yield break;
switch (original)
{
case ManiaHitObject maniaObj:
{
yield return maniaObj;
yield break;
}
case IHasLegacyHitObjectType legacy:
legacyType = legacy.LegacyType & LegacyHitObjectType.ObjectTypes;
break;
case IHasPath:
legacyType = LegacyHitObjectType.Slider;
break;
case IHasDuration:
legacyType = LegacyHitObjectType.Hold;
break;
default:
legacyType = LegacyHitObjectType.Circle;
break;
}
var objects = IsForCurrentRuleset ? generateSpecific(original, beatmap) : generateConverted(original, beatmap);
foreach (ManiaHitObject obj in objects)
yield return obj;
double startTime = original.StartTime;
double endTime = (original as IHasDuration)?.EndTime ?? startTime;
Vector2 position = (original as IHasPosition)?.Position ?? Vector2.Zero;
PatternGenerator conversion;
switch (legacyType)
{
case LegacyHitObjectType.Circle:
if (IsForCurrentRuleset)
{
conversion = new PassThroughPatternGenerator(Random, original, beatmap, TotalColumns, lastPattern);
recordNote(startTime, position);
}
else
{
// Note: The density is used during the pattern generator constructor, and intentionally computed first.
computeDensity(startTime);
conversion = new HitCirclePatternGenerator(Random, original, beatmap, TotalColumns, lastPattern, lastTime, lastPosition, density, lastStair);
recordNote(startTime, position);
}
break;
case LegacyHitObjectType.Slider:
if (IsForCurrentRuleset)
{
conversion = new PassThroughPatternGenerator(Random, original, beatmap, TotalColumns, lastPattern);
recordNote(original.StartTime, position);
}
else
{
var generator = new SliderPatternGenerator(Random, original, beatmap, TotalColumns, lastPattern);
conversion = generator;
for (int i = 0; i <= generator.SpanCount; i++)
{
double time = original.StartTime + generator.SegmentDuration * i;
recordNote(time, position);
computeDensity(time);
}
}
break;
case LegacyHitObjectType.Spinner:
// Note: Some older mania-specific beatmaps can have spinners that are converted rather than passed through.
// Newer beatmaps will usually use the "hold" hitobject type below.
conversion = new SpinnerPatternGenerator(Random, original, beatmap, TotalColumns, lastPattern);
recordNote(endTime, new Vector2(256, 192));
computeDensity(endTime);
break;
case LegacyHitObjectType.Hold:
conversion = new PassThroughPatternGenerator(Random, original, beatmap, TotalColumns, lastPattern);
recordNote(endTime, position);
computeDensity(endTime);
break;
default:
throw new ArgumentException($"Invalid legacy object type: {legacyType}", nameof(original));
}
foreach (var newPattern in conversion.Generate())
{
if (conversion is HitCirclePatternGenerator circleGenerator)
lastStair = circleGenerator.StairType;
if (conversion is HitCirclePatternGenerator || conversion is SliderPatternGenerator)
lastPattern = newPattern;
foreach (var obj in newPattern.HitObjects)
yield return obj;
}
}
private readonly LimitedCapacityQueue<double> prevNoteTimes = new LimitedCapacityQueue<double>(max_notes_for_density);
@ -156,135 +251,5 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
lastTime = time;
lastPosition = position;
}
/// <summary>
/// Method that generates hit objects for osu!mania specific beatmaps.
/// </summary>
/// <param name="original">The original hit object.</param>
/// <param name="originalBeatmap">The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap.</param>
/// <returns>The hit objects generated.</returns>
private IEnumerable<ManiaHitObject> generateSpecific(HitObject original, IBeatmap originalBeatmap)
{
var generator = new SpecificBeatmapPatternGenerator(Random, original, originalBeatmap, TotalColumns, lastPattern);
foreach (var newPattern in generator.Generate())
{
lastPattern = newPattern;
foreach (var obj in newPattern.HitObjects)
yield return obj;
}
}
/// <summary>
/// Method that generates hit objects for non-osu!mania beatmaps.
/// </summary>
/// <param name="original">The original hit object.</param>
/// <param name="originalBeatmap">The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap.</param>
/// <returns>The hit objects generated.</returns>
private IEnumerable<ManiaHitObject> generateConverted(HitObject original, IBeatmap originalBeatmap)
{
Patterns.PatternGenerator? conversion = null;
switch (original)
{
case IHasPath:
{
var generator = new PathObjectPatternGenerator(Random, original, originalBeatmap, TotalColumns, lastPattern);
conversion = generator;
var positionData = original as IHasPosition;
for (int i = 0; i <= generator.SpanCount; i++)
{
double time = original.StartTime + generator.SegmentDuration * i;
recordNote(time, positionData?.Position ?? Vector2.Zero);
computeDensity(time);
}
break;
}
case IHasDuration endTimeData:
{
conversion = new EndTimeObjectPatternGenerator(Random, original, originalBeatmap, TotalColumns, lastPattern);
recordNote(endTimeData.EndTime, new Vector2(256, 192));
computeDensity(endTimeData.EndTime);
break;
}
case IHasPosition positionData:
{
computeDensity(original.StartTime);
conversion = new HitObjectPatternGenerator(Random, original, originalBeatmap, TotalColumns, lastPattern, lastTime, lastPosition, density, lastStair);
recordNote(original.StartTime, positionData.Position);
break;
}
}
if (conversion == null)
yield break;
foreach (var newPattern in conversion.Generate())
{
lastPattern = conversion is EndTimeObjectPatternGenerator ? lastPattern : newPattern;
lastStair = (conversion as HitObjectPatternGenerator)?.StairType ?? lastStair;
foreach (var obj in newPattern.HitObjects)
yield return obj;
}
}
/// <summary>
/// A pattern generator for osu!mania-specific beatmaps.
/// </summary>
private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator
{
public SpecificBeatmapPatternGenerator(LegacyRandom random, HitObject hitObject, IBeatmap beatmap, int totalColumns, Pattern previousPattern)
: base(random, hitObject, beatmap, previousPattern, totalColumns)
{
}
public override IEnumerable<Pattern> Generate()
{
yield return generate();
}
private Pattern generate()
{
var positionData = HitObject as IHasXPosition;
int column = GetColumn(positionData?.X ?? 0);
var pattern = new Pattern();
if (HitObject is IHasDuration endTimeData)
{
pattern.Add(new HoldNote
{
StartTime = HitObject.StartTime,
Duration = endTimeData.Duration,
Column = column,
Samples = HitObject.Samples,
NodeSamples = (HitObject as IHasRepeats)?.NodeSamples ?? HoldNote.CreateDefaultNodeSamples(HitObject)
});
}
else if (HitObject is IHasXPosition)
{
pattern.Add(new Note
{
StartTime = HitObject.StartTime,
Samples = HitObject.Samples,
Column = column
});
}
return pattern;
}
}
}
}

View File

@ -16,13 +16,16 @@ using osu.Game.Utils;
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
internal class HitObjectPatternGenerator : PatternGenerator
/// <summary>
/// Converter for legacy "HitCircle" hit objects.
/// </summary>
internal class HitCirclePatternGenerator : LegacyPatternGenerator
{
public PatternType StairType { get; private set; }
private readonly PatternType convertType;
public HitObjectPatternGenerator(LegacyRandom random, HitObject hitObject, IBeatmap beatmap, int totalColumns, Pattern previousPattern, double previousTime, Vector2 previousPosition,
public HitCirclePatternGenerator(LegacyRandom random, HitObject hitObject, IBeatmap beatmap, int totalColumns, Pattern previousPattern, double previousTime, Vector2 previousPosition,
double density, PatternType lastStair)
: base(random, hitObject, beatmap, previousPattern, totalColumns)
{
@ -114,10 +117,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
}
if (convertType.HasFlag(PatternType.Cycle) && PreviousPattern.HitObjects.Count() == 1
// If we convert to 7K + 1, let's not overload the special key
&& (TotalColumns != 8 || lastColumn != 0)
// Make sure the last column was not the centre column
&& (TotalColumns % 2 == 0 || lastColumn != TotalColumns / 2))
// If we convert to 7K + 1, let's not overload the special key
&& (TotalColumns != 8 || lastColumn != 0)
// Make sure the last column was not the centre column
&& (TotalColumns % 2 == 0 || lastColumn != TotalColumns / 2))
{
// Generate a new pattern by cycling backwards (similar to Reverse but for only one hit object)
int column = RandomStart + TotalColumns - lastColumn - 1;

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.Linq;
using JetBrains.Annotations;
@ -15,7 +13,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// <summary>
/// A pattern generator for legacy hit objects.
/// </summary>
internal abstract class PatternGenerator : Patterns.PatternGenerator
internal abstract class LegacyPatternGenerator : PatternGenerator
{
/// <summary>
/// The column index at which to start generating random notes.
@ -27,7 +25,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// </summary>
protected readonly LegacyRandom Random;
protected PatternGenerator(LegacyRandom random, HitObject hitObject, IBeatmap beatmap, Pattern previousPattern, int totalColumns)
protected LegacyPatternGenerator(LegacyRandom random, HitObject hitObject, IBeatmap beatmap, Pattern previousPattern, int totalColumns)
: base(hitObject, beatmap, totalColumns, previousPattern)
{
ArgumentNullException.ThrowIfNull(random);
@ -96,8 +94,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (conversionDifficulty != null)
return conversionDifficulty.Value;
HitObject lastObject = Beatmap.HitObjects.LastOrDefault();
HitObject firstObject = Beatmap.HitObjects.FirstOrDefault();
HitObject? lastObject = Beatmap.HitObjects.LastOrDefault();
HitObject? firstObject = Beatmap.HitObjects.FirstOrDefault();
// Drain time in seconds
int drainTime = (int)(((lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0) - Beatmap.TotalBreakTime) / 1000);
@ -132,13 +130,13 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// <param name="nextColumn">A function to retrieve the next column. If null, a randomisation scheme will be used.</param>
/// <param name="validation">A function to perform additional validation checks to determine if a column is a valid candidate for a <see cref="HitObject"/>.</param>
/// <param name="lowerBound">The minimum column index. If null, <see cref="RandomStart"/> is used.</param>
/// <param name="upperBound">The maximum column index. If null, <see cref="Patterns.PatternGenerator.TotalColumns">TotalColumns</see> is used.</param>
/// <param name="upperBound">The maximum column index. If null, <see cref="PatternGenerator.TotalColumns">TotalColumns</see> is used.</param>
/// <param name="patterns">A list of patterns for which the validity of a column should be checked against.
/// A column is not a valid candidate if a <see cref="HitObject"/> occupies the same column in any of the patterns.</param>
/// <returns>A column which has passed the <paramref name="validation"/> check and for which there are no
/// <see cref="HitObject"/>s in any of <paramref name="patterns"/> occupying the same column.</returns>
/// <exception cref="NotEnoughColumnsException">If there are no valid candidate columns.</exception>
protected int FindAvailableColumn(int initialColumn, int? lowerBound = null, int? upperBound = null, Func<int, int> nextColumn = null, [InstantHandle] Func<int, bool> validation = null,
protected int FindAvailableColumn(int initialColumn, int? lowerBound = null, int? upperBound = null, Func<int, int>? nextColumn = null, [InstantHandle] Func<int, bool>? validation = null,
params Pattern[] patterns)
{
lowerBound ??= RandomStart;
@ -189,7 +187,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// Returns a random column index in the range [<paramref name="lowerBound"/>, <paramref name="upperBound"/>).
/// </summary>
/// <param name="lowerBound">The minimum column index. If null, <see cref="RandomStart"/> is used.</param>
/// <param name="upperBound">The maximum column index. If null, <see cref="Patterns.PatternGenerator.TotalColumns"/> is used.</param>
/// <param name="upperBound">The maximum column index. If null, <see cref="PatternGenerator.TotalColumns"/> is used.</param>
protected int GetRandomColumn(int? lowerBound = null, int? upperBound = null) => Random.Next(lowerBound ?? RandomStart, upperBound ?? TotalColumns);
/// <summary>

View File

@ -0,0 +1,55 @@
// 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 osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Utils;
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
/// <summary>
/// A simple generator which, for any object, if the hitobject has an end time
/// it becomes a <see cref="HoldNote"/> or otherwise a <see cref="Note"/>.
/// </summary>
internal class PassThroughPatternGenerator : LegacyPatternGenerator
{
public PassThroughPatternGenerator(LegacyRandom random, HitObject hitObject, IBeatmap beatmap, int totalColumns, Pattern previousPattern)
: base(random, hitObject, beatmap, previousPattern, totalColumns)
{
}
public override IEnumerable<Pattern> Generate()
{
var positionData = HitObject as IHasXPosition;
int column = GetColumn(positionData?.X ?? 0);
var pattern = new Pattern();
if (HitObject is IHasDuration endTimeData)
{
pattern.Add(new HoldNote
{
StartTime = HitObject.StartTime,
Duration = endTimeData.Duration,
Column = column,
Samples = HitObject.Samples,
NodeSamples = (HitObject as IHasRepeats)?.NodeSamples ?? HoldNote.CreateDefaultNodeSamples(HitObject)
});
}
else
{
pattern.Add(new Note
{
StartTime = HitObject.StartTime,
Samples = HitObject.Samples,
Column = column
});
}
yield return pattern;
}
}
}

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.Diagnostics;
@ -19,9 +17,9 @@ using osu.Game.Utils;
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
/// <summary>
/// A pattern generator for IHasDistance hit objects.
/// Converter for legacy "Slider" hit objects.
/// </summary>
internal class PathObjectPatternGenerator : PatternGenerator
internal class SliderPatternGenerator : LegacyPatternGenerator
{
public readonly int StartTime;
public readonly int EndTime;
@ -30,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
private PatternType convertType;
public PathObjectPatternGenerator(LegacyRandom random, HitObject hitObject, IBeatmap beatmap, int totalColumns, Pattern previousPattern)
public SliderPatternGenerator(LegacyRandom random, HitObject hitObject, IBeatmap beatmap, int totalColumns, Pattern previousPattern)
: base(random, hitObject, beatmap, previousPattern, totalColumns)
{
convertType = PatternType.None;
@ -484,9 +482,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// Retrieves the list of node samples that occur at time greater than or equal to <paramref name="time"/>.
/// </summary>
/// <param name="time">The time to retrieve node samples at.</param>
private IList<IList<HitSampleInfo>> nodeSamplesAt(int time)
private IList<IList<HitSampleInfo>>? nodeSamplesAt(int time)
{
if (!(HitObject is IHasPathWithRepeats curveData))
if (HitObject is not IHasPathWithRepeats curveData)
return null;
int index = SegmentDuration == 0 ? 0 : (time - StartTime) / SegmentDuration;

View File

@ -12,12 +12,15 @@ using osu.Game.Utils;
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
internal class EndTimeObjectPatternGenerator : PatternGenerator
/// <summary>
/// Converter for legacy "Spinner" hit objects.
/// </summary>
internal class SpinnerPatternGenerator : LegacyPatternGenerator
{
private readonly int endTime;
private readonly PatternType convertType;
public EndTimeObjectPatternGenerator(LegacyRandom random, HitObject hitObject, IBeatmap beatmap, int totalColumns, Pattern previousPattern)
public SpinnerPatternGenerator(LegacyRandom random, HitObject hitObject, IBeatmap beatmap, int totalColumns, Pattern previousPattern)
: base(random, hitObject, beatmap, previousPattern, totalColumns)
{
endTime = (int)((HitObject as IHasDuration)?.EndTime ?? 0);

View File

@ -1,9 +1,8 @@
// 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.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using osu.Game.Rulesets.Mania.Objects;
@ -14,8 +13,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
/// </summary>
internal class Pattern
{
private List<ManiaHitObject> hitObjects;
private HashSet<int> containedColumns;
private List<ManiaHitObject>? hitObjects;
private HashSet<int>? containedColumns;
/// <summary>
/// All the hit objects contained in this pattern.
@ -72,6 +71,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
containedColumns?.Clear();
}
[MemberNotNull(nameof(hitObjects), nameof(containedColumns))]
private void prepareStorage()
{
hitObjects ??= new List<ManiaHitObject>();

View File

@ -76,6 +76,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
base.OnDragEnd(e);
}
protected override bool OnMouseDown(MouseDownEvent e) => true;
protected override bool OnClick(ClickEvent e) => true;
private void updateState()
{
Colour = IsHovered || IsDragged ? colours.Red : colours.Yellow;

View File

@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Osu.UI
protected override ResumeOverlay CreateResumeOverlay()
{
if (Mods.Any(m => m is OsuModAutopilot))
if (Mods.Any(m => m is OsuModAutopilot or OsuModTouchDevice))
return new DelayedResumeOverlay { Scale = new Vector2(0.65f) };
return new OsuResumeOverlay();

View File

@ -131,21 +131,6 @@ namespace osu.Game.Tests.Visual.Background
assertNoBackgrounds();
}
[Test]
public void TestDelayedConnectivity()
{
registerBackgroundsResponse(DateTimeOffset.Now.AddDays(30));
setSeasonalBackgroundMode(SeasonalBackgroundMode.Always);
AddStep("go offline", () => dummyAPI.SetState(APIState.Offline));
createLoader();
assertNoBackgrounds();
AddStep("go online", () => dummyAPI.SetState(APIState.Online));
assertAnyBackground();
}
private void registerBackgroundsResponse(DateTimeOffset endDate)
=> AddStep("setup request handler", () =>
{
@ -185,7 +170,8 @@ namespace osu.Game.Tests.Visual.Background
{
previousBackground = (SeasonalBackground)backgroundContainer.SingleOrDefault();
background = backgroundLoader.LoadNextBackground();
LoadComponentAsync(background, bg => backgroundContainer.Child = bg);
if (background != null)
LoadComponentAsync(background, bg => backgroundContainer.Child = bg);
});
AddUntilStep("background loaded", () => background.IsLoaded);

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 NUnit.Framework;
using osu.Framework.Extensions;
using osu.Game.Configuration;
@ -58,7 +56,11 @@ namespace osu.Game.Tests.Visual.Navigation
// First scroll makes volume controls appear, second adjusts volume.
AddRepeatStep("Adjust volume using mouse wheel", () => InputManager.ScrollVerticalBy(5), 10);
AddAssert("Volume is still zero", () => Game.Audio.Volume.Value == 0);
AddAssert("Volume is still zero", () => Game.Audio.Volume.Value, () => Is.Zero);
AddStep("Pause", () => InputManager.PressKey(Key.Escape));
AddRepeatStep("Adjust volume using mouse wheel", () => InputManager.ScrollVerticalBy(5), 10);
AddAssert("Volume is above zero", () => Game.Audio.Volume.Value > 0);
}
[Test]
@ -80,8 +82,8 @@ namespace osu.Game.Tests.Visual.Navigation
private void loadToPlayerNonBreakTime()
{
Player player = null;
Screens.Select.SongSelect songSelect = null;
Player? player = null;
Screens.Select.SongSelect songSelect = null!;
PushAndConfirm(() => songSelect = new TestSceneScreenNavigation.TestPlaySongSelect());
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
@ -95,7 +97,7 @@ namespace osu.Game.Tests.Visual.Navigation
return (player = Game.ScreenStack.CurrentScreen as Player) != null;
});
AddUntilStep("wait for play time active", () => !player.IsBreakTime.Value);
AddUntilStep("wait for play time active", () => player!.IsBreakTime.Value, () => Is.False);
}
}
}

View File

@ -6,6 +6,7 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays.Volume;
@ -59,13 +60,12 @@ namespace osu.Game.Tests.Visual.UserInterface
[Test]
public void TestAltScrollNotBlocked()
{
bool scrollReceived = false;
TestGlobalScrollAdjustsVolume volumeAdjust = null!;
AddStep("add volume control receptor", () => Add(new VolumeControlReceptor
AddStep("add volume control receptor", () => Add(volumeAdjust = new TestGlobalScrollAdjustsVolume
{
RelativeSizeAxes = Axes.Both,
Depth = float.MaxValue,
ScrollActionRequested = (_, _, _) => scrollReceived = true,
}));
AddStep("hold alt", () => InputManager.PressKey(Key.AltLeft));
@ -75,10 +75,21 @@ namespace osu.Game.Tests.Visual.UserInterface
InputManager.ScrollVerticalBy(10);
});
AddAssert("receptor received scroll input", () => scrollReceived);
AddAssert("receptor received scroll input", () => volumeAdjust.ScrollReceived);
AddStep("release alt", () => InputManager.ReleaseKey(Key.AltLeft));
}
public partial class TestGlobalScrollAdjustsVolume : GlobalScrollAdjustsVolume
{
public bool ScrollReceived { get; private set; }
protected override bool OnScroll(ScrollEvent e)
{
ScrollReceived = true;
return base.OnScroll(e);
}
}
private partial class TestOverlay : OsuFocusedOverlayContainer
{
[BackgroundDependencyLoader]

View File

@ -1,8 +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.
#nullable disable
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Overlays;
using osu.Game.Overlays.Volume;
@ -11,7 +10,14 @@ namespace osu.Game.Tests.Visual.UserInterface
{
public partial class TestSceneVolumeOverlay : OsuTestScene
{
private VolumeOverlay volume;
private VolumeOverlay volume = null!;
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
dependencies.CacheAs(volume = new VolumeOverlay());
return dependencies;
}
protected override void LoadComplete()
{
@ -19,12 +25,10 @@ namespace osu.Game.Tests.Visual.UserInterface
AddRange(new Drawable[]
{
volume = new VolumeOverlay(),
new VolumeControlReceptor
volume,
new GlobalScrollAdjustsVolume
{
RelativeSizeAxes = Axes.Both,
ActionRequested = action => volume.Adjust(action),
ScrollActionRequested = (action, amount, isPrecise) => volume.Adjust(action, amount, isPrecise),
},
});

View File

@ -152,6 +152,12 @@ namespace osu.Game.Tournament.Tests.Components
AddStep("change channel to 2", () => chatDisplay.Channel.Value = testChannel2);
AddStep("change channel to 1", () => chatDisplay.Channel.Value = testChannel);
AddStep("!mp message (shouldn't display)", () => testChannel.AddNewMessages(new Message(nextMessageId())
{
Sender = redUser.ToAPIUser(),
Content = "!mp wangs"
}));
}
private int messageId;

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;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
@ -72,7 +73,13 @@ namespace osu.Game.Tournament.Components
public void Contract() => this.FadeOut(200);
protected override ChatLine CreateMessage(Message message) => new MatchMessage(message, ladderInfo);
protected override ChatLine? CreateMessage(Message message)
{
if (message.Content.StartsWith("!mp", StringComparison.Ordinal))
return null;
return new MatchMessage(message, ladderInfo);
}
protected override StandAloneDrawableChannel CreateDrawableChannel(Channel channel) => new MatchChannel(channel);

View File

@ -13,6 +13,8 @@ namespace osu.Game.Beatmaps.Legacy
NewCombo = 1 << 2,
Spinner = 1 << 3,
ComboOffset = (1 << 4) | (1 << 5) | (1 << 6),
Hold = 1 << 7
Hold = 1 << 7,
ObjectTypes = Circle | Slider | Spinner | Hold
}
}

View File

@ -28,7 +28,6 @@ namespace osu.Game.Graphics.Backgrounds
[Resolved]
private IAPIProvider api { get; set; }
private readonly IBindable<APIState> apiState = new Bindable<APIState>();
private Bindable<SeasonalBackgroundMode> seasonalBackgroundMode;
private Bindable<APISeasonalBackgrounds> seasonalBackgrounds;
@ -47,13 +46,12 @@ namespace osu.Game.Graphics.Backgrounds
SeasonalBackgroundChanged?.Invoke();
});
apiState.BindTo(api.State);
apiState.BindValueChanged(fetchSeasonalBackgrounds, true);
fetchSeasonalBackgrounds();
}
private void fetchSeasonalBackgrounds(ValueChangedEvent<APIState> stateChanged)
private void fetchSeasonalBackgrounds()
{
if (seasonalBackgrounds.Value != null || stateChanged.NewValue != APIState.Online)
if (seasonalBackgrounds.Value != null)
return;
var request = new GetSeasonalBackgroundsRequest();

View File

@ -62,8 +62,12 @@ namespace osu.Game.IO.Serialization.Converters
if (tok["$type"] == null)
throw new JsonException("Expected $type token.");
string typeName = lookupTable[(int)tok["$type"]];
var instance = (T)Activator.CreateInstance(Type.GetType(typeName).AsNonNull())!;
// Prevent instantiation of types that do not inherit the type targetted by this converter
Type type = Type.GetType(lookupTable[(int)tok["$type"]]).AsNonNull();
if (!type.IsAssignableTo(typeof(T)))
continue;
var instance = (T)Activator.CreateInstance(type)!;
serializer.Populate(itemReader, instance);
list.Add(instance);

View File

@ -1,9 +1,8 @@
// 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.Diagnostics;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@ -21,18 +20,18 @@ using osuTK.Input;
namespace osu.Game.Online.Chat
{
/// <summary>
/// Display a chat channel in an insolated region.
/// Display a chat channel in an isolated region.
/// </summary>
public partial class StandAloneChatDisplay : CompositeDrawable
{
[Cached]
public readonly Bindable<Channel> Channel = new Bindable<Channel>();
public readonly Bindable<Channel?> Channel = new Bindable<Channel?>();
protected readonly ChatTextBox TextBox;
protected readonly ChatTextBox? TextBox;
private ChannelManager channelManager;
private ChannelManager? channelManager;
private StandAloneDrawableChannel drawableChannel;
private StandAloneDrawableChannel? drawableChannel;
private readonly bool postingTextBox;
@ -93,6 +92,8 @@ namespace osu.Game.Online.Chat
private void postMessage(TextBox sender, bool newText)
{
Debug.Assert(TextBox != null);
string text = TextBox.Text.Trim();
if (string.IsNullOrWhiteSpace(text))
@ -106,9 +107,9 @@ namespace osu.Game.Online.Chat
TextBox.Text = string.Empty;
}
protected virtual ChatLine CreateMessage(Message message) => new StandAloneMessage(message);
protected virtual ChatLine? CreateMessage(Message message) => new StandAloneMessage(message);
private void channelChanged(ValueChangedEvent<Channel> e)
private void channelChanged(ValueChangedEvent<Channel?> e)
{
drawableChannel?.Expire();
@ -128,8 +129,8 @@ namespace osu.Game.Online.Chat
public partial class ChatTextBox : HistoryTextBox
{
public Action Focus;
public Action FocusLost;
public Action? Focus;
public Action? FocusLost;
protected override bool OnKeyDown(KeyDownEvent e)
{
@ -171,14 +172,14 @@ namespace osu.Game.Online.Chat
public partial class StandAloneDrawableChannel : DrawableChannel
{
public Func<Message, ChatLine> CreateChatLineAction;
public Func<Message, ChatLine?>? CreateChatLineAction;
public StandAloneDrawableChannel(Channel channel)
: base(channel)
{
}
protected override ChatLine CreateChatLine(Message m) => CreateChatLineAction(m);
protected override ChatLine? CreateChatLine(Message m) => CreateChatLineAction?.Invoke(m) ?? null;
protected override DaySeparator CreateDaySeparator(DateTimeOffset time) => new StandAloneDaySeparator(time);
}

View File

@ -57,7 +57,6 @@ using osu.Game.Overlays.Notifications;
using osu.Game.Overlays.OSD;
using osu.Game.Overlays.SkinEditor;
using osu.Game.Overlays.Toolbar;
using osu.Game.Overlays.Volume;
using osu.Game.Rulesets.Mods;
using osu.Game.Scoring;
using osu.Game.Screens;
@ -980,12 +979,6 @@ namespace osu.Game
AddRange(new Drawable[]
{
new VolumeControlReceptor
{
RelativeSizeAxes = Axes.Both,
ActionRequested = action => volume.Adjust(action),
ScrollActionRequested = (action, amount, isPrecise) => volume.Adjust(action, amount, isPrecise),
},
ScreenOffsetContainer = new Container
{
RelativeSizeAxes = Axes.Both,
@ -1432,6 +1425,19 @@ namespace osu.Game
switch (e.Action)
{
case GlobalAction.DecreaseVolume:
case GlobalAction.IncreaseVolume:
return volume.Adjust(e.Action);
case GlobalAction.ToggleMute:
case GlobalAction.NextVolumeMeter:
case GlobalAction.PreviousVolumeMeter:
if (e.Repeat)
return true;
return volume.Adjust(e.Action);
case GlobalAction.ToggleFPSDisplay:
fpsCounter.ToggleVisibility();
return true;

View File

@ -7,6 +7,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@ -132,6 +133,7 @@ namespace osu.Game.Overlays.Chat
Channel.PendingMessageResolved -= pendingMessageResolved;
}
[CanBeNull]
protected virtual ChatLine CreateChatLine(Message m) => new ChatLine(m);
protected virtual DaySeparator CreateDaySeparator(DateTimeOffset time) => new DaySeparator(time);
@ -155,8 +157,13 @@ namespace osu.Game.Overlays.Chat
{
addDaySeparatorIfRequired(lastMessage, message);
ChatLineFlow.Add(CreateChatLine(message));
lastMessage = message;
var chatLine = CreateChatLine(message);
if (chatLine != null)
{
ChatLineFlow.Add(chatLine);
lastMessage = message;
}
}
var staleMessages = chatLines.Where(c => c.LifetimeEnd == double.MaxValue).ToArray();

View File

@ -0,0 +1,37 @@
// 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.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
using osu.Game.Input.Bindings;
namespace osu.Game.Overlays.Volume
{
/// <summary>
/// Add to a container or screen to make scrolling anywhere in the container cause the global game volume to be adjusted.
/// </summary>
/// <remarks>
/// This is generally expected behaviour in many locations in osu!stable.
/// </remarks>
public partial class GlobalScrollAdjustsVolume : Container
{
[Resolved]
private VolumeOverlay? volumeOverlay { get; set; }
public GlobalScrollAdjustsVolume()
{
RelativeSizeAxes = Axes.Both;
}
protected override bool OnScroll(ScrollEvent e)
{
if (e.ScrollDelta.Y == 0)
return false;
// forward any unhandled mouse scroll events to the volume control.
return volumeOverlay?.Adjust(GlobalAction.IncreaseVolume, e.ScrollDelta.Y, e.IsPrecise) ?? false;
}
}
}

View File

@ -1,57 +0,0 @@
// 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 osu.Framework.Graphics.Containers;
using osu.Framework.Input;
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Game.Input.Bindings;
namespace osu.Game.Overlays.Volume
{
public partial class VolumeControlReceptor : Container, IScrollBindingHandler<GlobalAction>, IHandleGlobalKeyboardInput
{
public Func<GlobalAction, bool> ActionRequested;
public Func<GlobalAction, float, bool, bool> ScrollActionRequested;
public bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
{
switch (e.Action)
{
case GlobalAction.DecreaseVolume:
case GlobalAction.IncreaseVolume:
return ActionRequested?.Invoke(e.Action) == true;
case GlobalAction.ToggleMute:
case GlobalAction.NextVolumeMeter:
case GlobalAction.PreviousVolumeMeter:
if (!e.Repeat)
return ActionRequested?.Invoke(e.Action) == true;
return false;
}
return false;
}
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
{
}
protected override bool OnScroll(ScrollEvent e)
{
if (e.ScrollDelta.Y == 0)
return false;
// forward any unhandled mouse scroll events to the volume control.
ScrollActionRequested?.Invoke(GlobalAction.IncreaseVolume, e.ScrollDelta.Y, e.IsPrecise);
return true;
}
public bool OnScroll(KeyBindingScrollEvent<GlobalAction> e) =>
ScrollActionRequested?.Invoke(e.Action, e.ScrollAmount, e.IsPrecise) ?? false;
}
}

View File

@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
public Vector2 Position { get; set; }
public LegacyHitObjectType LegacyType { get; set; }
public LegacyHitObjectType LegacyType { get; set; } = LegacyHitObjectType.Circle;
public override Judgement CreateJudgement() => new IgnoreJudgement();

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 osu.Game.Beatmaps.Legacy;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Objects.Legacy
@ -16,5 +17,10 @@ namespace osu.Game.Rulesets.Objects.Legacy
public double Duration { get; set; }
public double EndTime => StartTime + Duration;
public ConvertHold()
{
LegacyType = LegacyHitObjectType.Hold;
}
}
}

View File

@ -8,6 +8,7 @@ using osu.Framework.Bindables;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps.Legacy;
namespace osu.Game.Rulesets.Objects.Legacy
{
@ -56,6 +57,11 @@ namespace osu.Game.Rulesets.Objects.Legacy
public bool GenerateTicks { get; set; } = true;
public ConvertSlider()
{
LegacyType = LegacyHitObjectType.Slider;
}
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);

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 osu.Game.Beatmaps.Legacy;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Objects.Legacy
@ -16,5 +17,10 @@ namespace osu.Game.Rulesets.Objects.Legacy
public double Duration { get; set; }
public double EndTime => StartTime + Duration;
public ConvertSpinner()
{
LegacyType = LegacyHitObjectType.Spinner;
}
}
}

View File

@ -28,6 +28,7 @@ using osu.Game.Online.API;
using osu.Game.Overlays;
using osu.Game.Overlays.Dialog;
using osu.Game.Overlays.SkinEditor;
using osu.Game.Overlays.Volume;
using osu.Game.Rulesets;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Edit;
@ -124,6 +125,7 @@ namespace osu.Game.Screens.Menu
AddRangeInternal(new[]
{
new GlobalScrollAdjustsVolume(),
buttonsContainer = new ParallaxContainer
{
ParallaxAmount = 0.01f,

View File

@ -19,6 +19,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
[Resolved(CanBeNull = true)]
private ILocalUserPlayInfo? localUserInfo { get; set; }
protected new ChatTextBox TextBox => base.TextBox!;
private readonly IBindable<LocalUserPlayingState> localUserPlaying = new Bindable<LocalUserPlayingState>();
public override bool PropagatePositionalInputSubTree => localUserPlaying.Value != LocalUserPlayingState.Playing;
@ -58,7 +60,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
localUserPlaying.BindValueChanged(playing =>
{
// for now let's never hold focus. this avoid misdirected gameplay keys entering chat.
// for now let's never hold focus. this avoids misdirected gameplay keys entering chat.
// note that this is done within this callback as it triggers an un-focus as well.
TextBox.HoldFocus = false;

View File

@ -0,0 +1,44 @@
// 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.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Input.Events;
using osu.Game.Configuration;
using osu.Game.Overlays.Volume;
namespace osu.Game.Screens.Play
{
/// <summary>
/// Primarily handles volume adjustment in gameplay.
///
/// - If the user has mouse wheel disabled, only allow during break time or when holding alt. Also block scroll from parent handling.
/// - Otherwise always allow, as per <see cref="GlobalScrollAdjustsVolume"/> implementation.
/// </summary>
internal partial class GameplayScrollWheelHandling : GlobalScrollAdjustsVolume
{
private Bindable<bool> mouseWheelDisabled = null!;
[Resolved]
private IGameplayClock gameplayClock { get; set; } = null!;
[BackgroundDependencyLoader]
private void load(OsuConfigManager config)
{
mouseWheelDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableWheel);
}
protected override bool OnScroll(ScrollEvent e)
{
// During pause, allow global volume adjust regardless of settings.
if (gameplayClock.IsPaused.Value)
return base.OnScroll(e);
// Block any parent handling of scroll if the user has asked for it (special case when holding "Alt").
if (mouseWheelDisabled.Value && !e.AltPressed)
return true;
return base.OnScroll(e);
}
}
}

View File

@ -15,7 +15,6 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Events;
using osu.Framework.Logging;
using osu.Framework.Screens;
using osu.Framework.Threading;
@ -88,8 +87,6 @@ namespace osu.Game.Screens.Play
private bool isRestarting;
private bool skipExitTransition;
private Bindable<bool> mouseWheelDisabled;
private readonly Bindable<bool> storyboardReplacesBackground = new Bindable<bool>();
public IBindable<bool> LocalUserPlaying => localUserPlaying;
@ -228,8 +225,6 @@ namespace osu.Game.Screens.Play
return;
}
mouseWheelDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableWheel);
if (game != null)
gameActive.BindTo(game.IsActive);
@ -251,7 +246,10 @@ namespace osu.Game.Screens.Play
dependencies.CacheAs(HealthProcessor);
InternalChild = GameplayClockContainer = CreateGameplayClockContainer(Beatmap.Value, DrawableRuleset.GameplayStartTime);
InternalChildren = new Drawable[]
{
GameplayClockContainer = CreateGameplayClockContainer(Beatmap.Value, DrawableRuleset.GameplayStartTime),
};
AddInternal(screenSuspension = new ScreenSuspensionHandler(GameplayClockContainer));
@ -266,6 +264,7 @@ namespace osu.Game.Screens.Play
dependencies.CacheAs(GameplayState = new GameplayState(playableBeatmap, ruleset, gameplayMods, Score, ScoreProcessor, HealthProcessor, Beatmap.Value.Storyboard));
var rulesetSkinProvider = new RulesetSkinProvidingContainer(ruleset, playableBeatmap, Beatmap.Value.Skin);
GameplayClockContainer.Add(new GameplayScrollWheelHandling());
// load the skinning hierarchy first.
// this is intentionally done in two stages to ensure things are in a loaded state before exposing the ruleset to skin sources.
@ -894,16 +893,6 @@ namespace osu.Game.Screens.Play
});
}
protected override bool OnScroll(ScrollEvent e)
{
// During pause, allow global volume adjust regardless of settings.
if (GameplayClockContainer.IsPaused.Value)
return false;
// Block global volume adjust if the user has asked for it (special case when holding "Alt").
return mouseWheelDisabled.Value && !e.AltPressed;
}
#region Gameplay leaderboard
protected readonly Bindable<bool> LeaderboardExpandedState = new BindableBool();

View File

@ -27,6 +27,7 @@ using osu.Game.Input;
using osu.Game.Localisation;
using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
using osu.Game.Overlays.Volume;
using osu.Game.Performance;
using osu.Game.Scoring;
using osu.Game.Screens.Menu;
@ -190,6 +191,7 @@ namespace osu.Game.Screens.Play
InternalChildren = new Drawable[]
{
new GlobalScrollAdjustsVolume(),
(content = new LogoTrackingContainer
{
Anchor = Anchor.Centre,

View File

@ -31,6 +31,7 @@ using osu.Game.Graphics.UserInterface;
using osu.Game.Input.Bindings;
using osu.Game.Overlays;
using osu.Game.Overlays.Mods;
using osu.Game.Overlays.Volume;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Screens.Backgrounds;
@ -169,10 +170,12 @@ namespace osu.Game.Screens.Select
AddRangeInternal(new Drawable[]
{
new GlobalScrollAdjustsVolume(),
new VerticalMaskingContainer
{
Children = new Drawable[]
{
new GlobalScrollAdjustsVolume(),
new GridContainer // used for max width implementation
{
RelativeSizeAxes = Axes.Both,

View File

@ -35,8 +35,8 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Realm" Version="11.5.0" />
<PackageReference Include="ppy.osu.Framework" Version="2024.1219.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2024.1202.0" />
<PackageReference Include="ppy.osu.Framework" Version="2024.1220.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" />

View File

@ -17,6 +17,6 @@
<MtouchInterpreter>-all</MtouchInterpreter>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Framework.iOS" Version="2024.1219.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2024.1220.0" />
</ItemGroup>
</Project>

View File

@ -34,9 +34,11 @@
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>NSCameraUsageDescription</key>
<string>We don't really use the camera.</string>
<string>We don't use the camera.</string>
<key>NSMicrophoneUsageDescription</key>
<string>We don't really use the microphone.</string>
<string>We don't use the microphone.</string>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>We don't use Bluetooth.</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationLandscapeRight</string>
@ -153,5 +155,7 @@
</array>
<key>LSApplicationCategoryType</key>
<string>public.app-category.music-games</string>
<key>LSSupportsOpeningDocumentsInPlace</key>
<false/>
</dict>
</plist>