mirror of
https://github.com/ppy/osu.git
synced 2025-02-21 20:12:57 +08:00
Merge branch 'master' into ios-app-delegates
This commit is contained in:
commit
f698284171
@ -10,7 +10,7 @@
|
|||||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.1219.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.1220.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||||
|
@ -22,9 +22,11 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
|
|
||||||
[TestCase("basic")]
|
[TestCase("basic")]
|
||||||
[TestCase("zero-length-slider")]
|
[TestCase("zero-length-slider")]
|
||||||
|
[TestCase("mania-specific-spinner")]
|
||||||
[TestCase("20544")]
|
[TestCase("20544")]
|
||||||
[TestCase("100374")]
|
[TestCase("100374")]
|
||||||
[TestCase("1450162")]
|
[TestCase("1450162")]
|
||||||
|
[TestCase("4869637")]
|
||||||
public void Test(string name) => base.Test(name);
|
public void Test(string name) => base.Test(name);
|
||||||
|
|
||||||
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)
|
||||||
|
File diff suppressed because one or more lines are too long
1442
osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/4869637.osu
Normal file
1442
osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/4869637.osu
Normal file
File diff suppressed because it is too large
Load Diff
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -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:
|
@ -7,11 +7,13 @@ using System.Linq;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.Legacy;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps.Patterns;
|
using osu.Game.Rulesets.Mania.Beatmaps.Patterns;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy;
|
using osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects.Legacy;
|
||||||
using osu.Game.Rulesets.Scoring.Legacy;
|
using osu.Game.Rulesets.Scoring.Legacy;
|
||||||
using osu.Game.Utils;
|
using osu.Game.Utils;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -124,16 +126,109 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
|
|
||||||
protected override IEnumerable<ManiaHitObject> ConvertHitObject(HitObject original, IBeatmap beatmap, CancellationToken cancellationToken)
|
protected override IEnumerable<ManiaHitObject> ConvertHitObject(HitObject original, IBeatmap beatmap, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (original is ManiaHitObject maniaOriginal)
|
LegacyHitObjectType legacyType;
|
||||||
{
|
|
||||||
yield return maniaOriginal;
|
|
||||||
|
|
||||||
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);
|
double startTime = original.StartTime;
|
||||||
foreach (ManiaHitObject obj in objects)
|
double endTime = (original as IHasDuration)?.EndTime ?? startTime;
|
||||||
yield return obj;
|
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);
|
private readonly LimitedCapacityQueue<double> prevNoteTimes = new LimitedCapacityQueue<double>(max_notes_for_density);
|
||||||
@ -156,135 +251,5 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
lastTime = time;
|
lastTime = time;
|
||||||
lastPosition = position;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,13 +16,16 @@ using osu.Game.Utils;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
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; }
|
public PatternType StairType { get; private set; }
|
||||||
|
|
||||||
private readonly PatternType convertType;
|
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)
|
double density, PatternType lastStair)
|
||||||
: base(random, hitObject, beatmap, previousPattern, totalColumns)
|
: 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 (convertType.HasFlag(PatternType.Cycle) && PreviousPattern.HitObjects.Count() == 1
|
||||||
// If we convert to 7K + 1, let's not overload the special key
|
// If we convert to 7K + 1, let's not overload the special key
|
||||||
&& (TotalColumns != 8 || lastColumn != 0)
|
&& (TotalColumns != 8 || lastColumn != 0)
|
||||||
// Make sure the last column was not the centre column
|
// Make sure the last column was not the centre column
|
||||||
&& (TotalColumns % 2 == 0 || lastColumn != TotalColumns / 2))
|
&& (TotalColumns % 2 == 0 || lastColumn != TotalColumns / 2))
|
||||||
{
|
{
|
||||||
// Generate a new pattern by cycling backwards (similar to Reverse but for only one hit object)
|
// Generate a new pattern by cycling backwards (similar to Reverse but for only one hit object)
|
||||||
int column = RandomStart + TotalColumns - lastColumn - 1;
|
int column = RandomStart + TotalColumns - lastColumn - 1;
|
@ -1,8 +1,6 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
@ -15,7 +13,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A pattern generator for legacy hit objects.
|
/// A pattern generator for legacy hit objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal abstract class PatternGenerator : Patterns.PatternGenerator
|
internal abstract class LegacyPatternGenerator : PatternGenerator
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The column index at which to start generating random notes.
|
/// The column index at which to start generating random notes.
|
||||||
@ -27,7 +25,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected readonly LegacyRandom Random;
|
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)
|
: base(hitObject, beatmap, totalColumns, previousPattern)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(random);
|
ArgumentNullException.ThrowIfNull(random);
|
||||||
@ -96,8 +94,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
if (conversionDifficulty != null)
|
if (conversionDifficulty != null)
|
||||||
return conversionDifficulty.Value;
|
return conversionDifficulty.Value;
|
||||||
|
|
||||||
HitObject lastObject = Beatmap.HitObjects.LastOrDefault();
|
HitObject? lastObject = Beatmap.HitObjects.LastOrDefault();
|
||||||
HitObject firstObject = Beatmap.HitObjects.FirstOrDefault();
|
HitObject? firstObject = Beatmap.HitObjects.FirstOrDefault();
|
||||||
|
|
||||||
// Drain time in seconds
|
// Drain time in seconds
|
||||||
int drainTime = (int)(((lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0) - Beatmap.TotalBreakTime) / 1000);
|
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="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="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="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.
|
/// <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>
|
/// 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
|
/// <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>
|
/// <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>
|
/// <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)
|
params Pattern[] patterns)
|
||||||
{
|
{
|
||||||
lowerBound ??= RandomStart;
|
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"/>).
|
/// Returns a random column index in the range [<paramref name="lowerBound"/>, <paramref name="upperBound"/>).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="lowerBound">The minimum column index. If null, <see cref="RandomStart"/> is used.</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"/> 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);
|
protected int GetRandomColumn(int? lowerBound = null, int? upperBound = null) => Random.Next(lowerBound ?? RandomStart, upperBound ?? TotalColumns);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,8 +1,6 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
@ -19,9 +17,9 @@ using osu.Game.Utils;
|
|||||||
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A pattern generator for IHasDistance hit objects.
|
/// Converter for legacy "Slider" hit objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class PathObjectPatternGenerator : PatternGenerator
|
internal class SliderPatternGenerator : LegacyPatternGenerator
|
||||||
{
|
{
|
||||||
public readonly int StartTime;
|
public readonly int StartTime;
|
||||||
public readonly int EndTime;
|
public readonly int EndTime;
|
||||||
@ -30,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
|
|
||||||
private PatternType convertType;
|
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)
|
: base(random, hitObject, beatmap, previousPattern, totalColumns)
|
||||||
{
|
{
|
||||||
convertType = PatternType.None;
|
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"/>.
|
/// Retrieves the list of node samples that occur at time greater than or equal to <paramref name="time"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="time">The time to retrieve node samples at.</param>
|
/// <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;
|
return null;
|
||||||
|
|
||||||
int index = SegmentDuration == 0 ? 0 : (time - StartTime) / SegmentDuration;
|
int index = SegmentDuration == 0 ? 0 : (time - StartTime) / SegmentDuration;
|
@ -12,12 +12,15 @@ using osu.Game.Utils;
|
|||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
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 int endTime;
|
||||||
private readonly PatternType convertType;
|
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)
|
: base(random, hitObject, beatmap, previousPattern, totalColumns)
|
||||||
{
|
{
|
||||||
endTime = (int)((HitObject as IHasDuration)?.EndTime ?? 0);
|
endTime = (int)((HitObject as IHasDuration)?.EndTime ?? 0);
|
@ -1,9 +1,8 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
|
||||||
@ -14,8 +13,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal class Pattern
|
internal class Pattern
|
||||||
{
|
{
|
||||||
private List<ManiaHitObject> hitObjects;
|
private List<ManiaHitObject>? hitObjects;
|
||||||
private HashSet<int> containedColumns;
|
private HashSet<int>? containedColumns;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All the hit objects contained in this pattern.
|
/// All the hit objects contained in this pattern.
|
||||||
@ -72,6 +71,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
|
|||||||
containedColumns?.Clear();
|
containedColumns?.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MemberNotNull(nameof(hitObjects), nameof(containedColumns))]
|
||||||
private void prepareStorage()
|
private void prepareStorage()
|
||||||
{
|
{
|
||||||
hitObjects ??= new List<ManiaHitObject>();
|
hitObjects ??= new List<ManiaHitObject>();
|
||||||
|
@ -76,6 +76,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
base.OnDragEnd(e);
|
base.OnDragEnd(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool OnMouseDown(MouseDownEvent e) => true;
|
||||||
|
|
||||||
|
protected override bool OnClick(ClickEvent e) => true;
|
||||||
|
|
||||||
private void updateState()
|
private void updateState()
|
||||||
{
|
{
|
||||||
Colour = IsHovered || IsDragged ? colours.Red : colours.Yellow;
|
Colour = IsHovered || IsDragged ? colours.Red : colours.Yellow;
|
||||||
|
@ -69,7 +69,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
|
|
||||||
protected override ResumeOverlay CreateResumeOverlay()
|
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 DelayedResumeOverlay { Scale = new Vector2(0.65f) };
|
||||||
|
|
||||||
return new OsuResumeOverlay();
|
return new OsuResumeOverlay();
|
||||||
|
@ -131,21 +131,6 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
assertNoBackgrounds();
|
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)
|
private void registerBackgroundsResponse(DateTimeOffset endDate)
|
||||||
=> AddStep("setup request handler", () =>
|
=> AddStep("setup request handler", () =>
|
||||||
{
|
{
|
||||||
@ -185,7 +170,8 @@ namespace osu.Game.Tests.Visual.Background
|
|||||||
{
|
{
|
||||||
previousBackground = (SeasonalBackground)backgroundContainer.SingleOrDefault();
|
previousBackground = (SeasonalBackground)backgroundContainer.SingleOrDefault();
|
||||||
background = backgroundLoader.LoadNextBackground();
|
background = backgroundLoader.LoadNextBackground();
|
||||||
LoadComponentAsync(background, bg => backgroundContainer.Child = bg);
|
if (background != null)
|
||||||
|
LoadComponentAsync(background, bg => backgroundContainer.Child = bg);
|
||||||
});
|
});
|
||||||
|
|
||||||
AddUntilStep("background loaded", () => background.IsLoaded);
|
AddUntilStep("background loaded", () => background.IsLoaded);
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
@ -58,7 +56,11 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
|
|
||||||
// First scroll makes volume controls appear, second adjusts volume.
|
// First scroll makes volume controls appear, second adjusts volume.
|
||||||
AddRepeatStep("Adjust volume using mouse wheel", () => InputManager.ScrollVerticalBy(5), 10);
|
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]
|
[Test]
|
||||||
@ -80,8 +82,8 @@ namespace osu.Game.Tests.Visual.Navigation
|
|||||||
|
|
||||||
private void loadToPlayerNonBreakTime()
|
private void loadToPlayerNonBreakTime()
|
||||||
{
|
{
|
||||||
Player player = null;
|
Player? player = null;
|
||||||
Screens.Select.SongSelect songSelect = null;
|
Screens.Select.SongSelect songSelect = null!;
|
||||||
PushAndConfirm(() => songSelect = new TestSceneScreenNavigation.TestPlaySongSelect());
|
PushAndConfirm(() => songSelect = new TestSceneScreenNavigation.TestPlaySongSelect());
|
||||||
AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded);
|
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;
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Colour;
|
using osu.Framework.Graphics.Colour;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Overlays.Volume;
|
using osu.Game.Overlays.Volume;
|
||||||
@ -59,13 +60,12 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestAltScrollNotBlocked()
|
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,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Depth = float.MaxValue,
|
Depth = float.MaxValue,
|
||||||
ScrollActionRequested = (_, _, _) => scrollReceived = true,
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
AddStep("hold alt", () => InputManager.PressKey(Key.AltLeft));
|
AddStep("hold alt", () => InputManager.PressKey(Key.AltLeft));
|
||||||
@ -75,10 +75,21 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
InputManager.ScrollVerticalBy(10);
|
InputManager.ScrollVerticalBy(10);
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert("receptor received scroll input", () => scrollReceived);
|
AddAssert("receptor received scroll input", () => volumeAdjust.ScrollReceived);
|
||||||
AddStep("release alt", () => InputManager.ReleaseKey(Key.AltLeft));
|
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
|
private partial class TestOverlay : OsuFocusedOverlayContainer
|
||||||
{
|
{
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
#nullable disable
|
using osu.Framework.Allocation;
|
||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Volume;
|
using osu.Game.Overlays.Volume;
|
||||||
@ -11,7 +10,14 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
{
|
{
|
||||||
public partial class TestSceneVolumeOverlay : OsuTestScene
|
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()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
@ -19,12 +25,10 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
|
|
||||||
AddRange(new Drawable[]
|
AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
volume = new VolumeOverlay(),
|
volume,
|
||||||
new VolumeControlReceptor
|
new GlobalScrollAdjustsVolume
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
ActionRequested = action => volume.Adjust(action),
|
|
||||||
ScrollActionRequested = (action, amount, isPrecise) => volume.Adjust(action, amount, isPrecise),
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -152,6 +152,12 @@ namespace osu.Game.Tournament.Tests.Components
|
|||||||
AddStep("change channel to 2", () => chatDisplay.Channel.Value = testChannel2);
|
AddStep("change channel to 2", () => chatDisplay.Channel.Value = testChannel2);
|
||||||
|
|
||||||
AddStep("change channel to 1", () => chatDisplay.Channel.Value = testChannel);
|
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;
|
private int messageId;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// 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;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
@ -72,7 +73,13 @@ namespace osu.Game.Tournament.Components
|
|||||||
|
|
||||||
public void Contract() => this.FadeOut(200);
|
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);
|
protected override StandAloneDrawableChannel CreateDrawableChannel(Channel channel) => new MatchChannel(channel);
|
||||||
|
|
||||||
|
@ -13,6 +13,8 @@ namespace osu.Game.Beatmaps.Legacy
|
|||||||
NewCombo = 1 << 2,
|
NewCombo = 1 << 2,
|
||||||
Spinner = 1 << 3,
|
Spinner = 1 << 3,
|
||||||
ComboOffset = (1 << 4) | (1 << 5) | (1 << 6),
|
ComboOffset = (1 << 4) | (1 << 5) | (1 << 6),
|
||||||
Hold = 1 << 7
|
Hold = 1 << 7,
|
||||||
|
|
||||||
|
ObjectTypes = Circle | Slider | Spinner | Hold
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,6 @@ namespace osu.Game.Graphics.Backgrounds
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private IAPIProvider api { get; set; }
|
private IAPIProvider api { get; set; }
|
||||||
|
|
||||||
private readonly IBindable<APIState> apiState = new Bindable<APIState>();
|
|
||||||
private Bindable<SeasonalBackgroundMode> seasonalBackgroundMode;
|
private Bindable<SeasonalBackgroundMode> seasonalBackgroundMode;
|
||||||
private Bindable<APISeasonalBackgrounds> seasonalBackgrounds;
|
private Bindable<APISeasonalBackgrounds> seasonalBackgrounds;
|
||||||
|
|
||||||
@ -47,13 +46,12 @@ namespace osu.Game.Graphics.Backgrounds
|
|||||||
SeasonalBackgroundChanged?.Invoke();
|
SeasonalBackgroundChanged?.Invoke();
|
||||||
});
|
});
|
||||||
|
|
||||||
apiState.BindTo(api.State);
|
fetchSeasonalBackgrounds();
|
||||||
apiState.BindValueChanged(fetchSeasonalBackgrounds, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fetchSeasonalBackgrounds(ValueChangedEvent<APIState> stateChanged)
|
private void fetchSeasonalBackgrounds()
|
||||||
{
|
{
|
||||||
if (seasonalBackgrounds.Value != null || stateChanged.NewValue != APIState.Online)
|
if (seasonalBackgrounds.Value != null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var request = new GetSeasonalBackgroundsRequest();
|
var request = new GetSeasonalBackgroundsRequest();
|
||||||
|
@ -62,8 +62,12 @@ namespace osu.Game.IO.Serialization.Converters
|
|||||||
if (tok["$type"] == null)
|
if (tok["$type"] == null)
|
||||||
throw new JsonException("Expected $type token.");
|
throw new JsonException("Expected $type token.");
|
||||||
|
|
||||||
string typeName = lookupTable[(int)tok["$type"]];
|
// Prevent instantiation of types that do not inherit the type targetted by this converter
|
||||||
var instance = (T)Activator.CreateInstance(Type.GetType(typeName).AsNonNull())!;
|
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);
|
serializer.Populate(itemReader, instance);
|
||||||
|
|
||||||
list.Add(instance);
|
list.Add(instance);
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
// 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.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -21,18 +20,18 @@ using osuTK.Input;
|
|||||||
namespace osu.Game.Online.Chat
|
namespace osu.Game.Online.Chat
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Display a chat channel in an insolated region.
|
/// Display a chat channel in an isolated region.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class StandAloneChatDisplay : CompositeDrawable
|
public partial class StandAloneChatDisplay : CompositeDrawable
|
||||||
{
|
{
|
||||||
[Cached]
|
[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;
|
private readonly bool postingTextBox;
|
||||||
|
|
||||||
@ -93,6 +92,8 @@ namespace osu.Game.Online.Chat
|
|||||||
|
|
||||||
private void postMessage(TextBox sender, bool newText)
|
private void postMessage(TextBox sender, bool newText)
|
||||||
{
|
{
|
||||||
|
Debug.Assert(TextBox != null);
|
||||||
|
|
||||||
string text = TextBox.Text.Trim();
|
string text = TextBox.Text.Trim();
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(text))
|
if (string.IsNullOrWhiteSpace(text))
|
||||||
@ -106,9 +107,9 @@ namespace osu.Game.Online.Chat
|
|||||||
TextBox.Text = string.Empty;
|
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();
|
drawableChannel?.Expire();
|
||||||
|
|
||||||
@ -128,8 +129,8 @@ namespace osu.Game.Online.Chat
|
|||||||
|
|
||||||
public partial class ChatTextBox : HistoryTextBox
|
public partial class ChatTextBox : HistoryTextBox
|
||||||
{
|
{
|
||||||
public Action Focus;
|
public Action? Focus;
|
||||||
public Action FocusLost;
|
public Action? FocusLost;
|
||||||
|
|
||||||
protected override bool OnKeyDown(KeyDownEvent e)
|
protected override bool OnKeyDown(KeyDownEvent e)
|
||||||
{
|
{
|
||||||
@ -171,14 +172,14 @@ namespace osu.Game.Online.Chat
|
|||||||
|
|
||||||
public partial class StandAloneDrawableChannel : DrawableChannel
|
public partial class StandAloneDrawableChannel : DrawableChannel
|
||||||
{
|
{
|
||||||
public Func<Message, ChatLine> CreateChatLineAction;
|
public Func<Message, ChatLine?>? CreateChatLineAction;
|
||||||
|
|
||||||
public StandAloneDrawableChannel(Channel channel)
|
public StandAloneDrawableChannel(Channel channel)
|
||||||
: base(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);
|
protected override DaySeparator CreateDaySeparator(DateTimeOffset time) => new StandAloneDaySeparator(time);
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,6 @@ using osu.Game.Overlays.Notifications;
|
|||||||
using osu.Game.Overlays.OSD;
|
using osu.Game.Overlays.OSD;
|
||||||
using osu.Game.Overlays.SkinEditor;
|
using osu.Game.Overlays.SkinEditor;
|
||||||
using osu.Game.Overlays.Toolbar;
|
using osu.Game.Overlays.Toolbar;
|
||||||
using osu.Game.Overlays.Volume;
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens;
|
using osu.Game.Screens;
|
||||||
@ -980,12 +979,6 @@ namespace osu.Game
|
|||||||
|
|
||||||
AddRange(new Drawable[]
|
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
|
ScreenOffsetContainer = new Container
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
@ -1432,6 +1425,19 @@ namespace osu.Game
|
|||||||
|
|
||||||
switch (e.Action)
|
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:
|
case GlobalAction.ToggleFPSDisplay:
|
||||||
fpsCounter.ToggleVisibility();
|
fpsCounter.ToggleVisibility();
|
||||||
return true;
|
return true;
|
||||||
|
@ -7,6 +7,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
@ -132,6 +133,7 @@ namespace osu.Game.Overlays.Chat
|
|||||||
Channel.PendingMessageResolved -= pendingMessageResolved;
|
Channel.PendingMessageResolved -= pendingMessageResolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CanBeNull]
|
||||||
protected virtual ChatLine CreateChatLine(Message m) => new ChatLine(m);
|
protected virtual ChatLine CreateChatLine(Message m) => new ChatLine(m);
|
||||||
|
|
||||||
protected virtual DaySeparator CreateDaySeparator(DateTimeOffset time) => new DaySeparator(time);
|
protected virtual DaySeparator CreateDaySeparator(DateTimeOffset time) => new DaySeparator(time);
|
||||||
@ -155,8 +157,13 @@ namespace osu.Game.Overlays.Chat
|
|||||||
{
|
{
|
||||||
addDaySeparatorIfRequired(lastMessage, message);
|
addDaySeparatorIfRequired(lastMessage, message);
|
||||||
|
|
||||||
ChatLineFlow.Add(CreateChatLine(message));
|
var chatLine = CreateChatLine(message);
|
||||||
lastMessage = message;
|
|
||||||
|
if (chatLine != null)
|
||||||
|
{
|
||||||
|
ChatLineFlow.Add(chatLine);
|
||||||
|
lastMessage = message;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var staleMessages = chatLines.Where(c => c.LifetimeEnd == double.MaxValue).ToArray();
|
var staleMessages = chatLines.Where(c => c.LifetimeEnd == double.MaxValue).ToArray();
|
||||||
|
37
osu.Game/Overlays/Volume/GlobalScrollAdjustsVolume.cs
Normal file
37
osu.Game/Overlays/Volume/GlobalScrollAdjustsVolume.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
|
|
||||||
public Vector2 Position { get; set; }
|
public Vector2 Position { get; set; }
|
||||||
|
|
||||||
public LegacyHitObjectType LegacyType { get; set; }
|
public LegacyHitObjectType LegacyType { get; set; } = LegacyHitObjectType.Circle;
|
||||||
|
|
||||||
public override Judgement CreateJudgement() => new IgnoreJudgement();
|
public override Judgement CreateJudgement() => new IgnoreJudgement();
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// 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 osu.Game.Beatmaps.Legacy;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Objects.Legacy
|
namespace osu.Game.Rulesets.Objects.Legacy
|
||||||
@ -16,5 +17,10 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
public double Duration { get; set; }
|
public double Duration { get; set; }
|
||||||
|
|
||||||
public double EndTime => StartTime + Duration;
|
public double EndTime => StartTime + Duration;
|
||||||
|
|
||||||
|
public ConvertHold()
|
||||||
|
{
|
||||||
|
LegacyType = LegacyHitObjectType.Hold;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Beatmaps.Legacy;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Objects.Legacy
|
namespace osu.Game.Rulesets.Objects.Legacy
|
||||||
{
|
{
|
||||||
@ -56,6 +57,11 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
|
|
||||||
public bool GenerateTicks { get; set; } = true;
|
public bool GenerateTicks { get; set; } = true;
|
||||||
|
|
||||||
|
public ConvertSlider()
|
||||||
|
{
|
||||||
|
LegacyType = LegacyHitObjectType.Slider;
|
||||||
|
}
|
||||||
|
|
||||||
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
|
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty)
|
||||||
{
|
{
|
||||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// 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 osu.Game.Beatmaps.Legacy;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Objects.Legacy
|
namespace osu.Game.Rulesets.Objects.Legacy
|
||||||
@ -16,5 +17,10 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
public double Duration { get; set; }
|
public double Duration { get; set; }
|
||||||
|
|
||||||
public double EndTime => StartTime + Duration;
|
public double EndTime => StartTime + Duration;
|
||||||
|
|
||||||
|
public ConvertSpinner()
|
||||||
|
{
|
||||||
|
LegacyType = LegacyHitObjectType.Spinner;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ using osu.Game.Online.API;
|
|||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Dialog;
|
using osu.Game.Overlays.Dialog;
|
||||||
using osu.Game.Overlays.SkinEditor;
|
using osu.Game.Overlays.SkinEditor;
|
||||||
|
using osu.Game.Overlays.Volume;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Screens.Backgrounds;
|
using osu.Game.Screens.Backgrounds;
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
@ -124,6 +125,7 @@ namespace osu.Game.Screens.Menu
|
|||||||
|
|
||||||
AddRangeInternal(new[]
|
AddRangeInternal(new[]
|
||||||
{
|
{
|
||||||
|
new GlobalScrollAdjustsVolume(),
|
||||||
buttonsContainer = new ParallaxContainer
|
buttonsContainer = new ParallaxContainer
|
||||||
{
|
{
|
||||||
ParallaxAmount = 0.01f,
|
ParallaxAmount = 0.01f,
|
||||||
|
@ -19,6 +19,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
[Resolved(CanBeNull = true)]
|
[Resolved(CanBeNull = true)]
|
||||||
private ILocalUserPlayInfo? localUserInfo { get; set; }
|
private ILocalUserPlayInfo? localUserInfo { get; set; }
|
||||||
|
|
||||||
|
protected new ChatTextBox TextBox => base.TextBox!;
|
||||||
|
|
||||||
private readonly IBindable<LocalUserPlayingState> localUserPlaying = new Bindable<LocalUserPlayingState>();
|
private readonly IBindable<LocalUserPlayingState> localUserPlaying = new Bindable<LocalUserPlayingState>();
|
||||||
|
|
||||||
public override bool PropagatePositionalInputSubTree => localUserPlaying.Value != LocalUserPlayingState.Playing;
|
public override bool PropagatePositionalInputSubTree => localUserPlaying.Value != LocalUserPlayingState.Playing;
|
||||||
@ -58,7 +60,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer
|
|||||||
|
|
||||||
localUserPlaying.BindValueChanged(playing =>
|
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.
|
// note that this is done within this callback as it triggers an un-focus as well.
|
||||||
TextBox.HoldFocus = false;
|
TextBox.HoldFocus = false;
|
||||||
|
|
||||||
|
44
osu.Game/Screens/Play/GameplayScrollWheelHandling.cs
Normal file
44
osu.Game/Screens/Play/GameplayScrollWheelHandling.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -15,7 +15,6 @@ using osu.Framework.Bindables;
|
|||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Input.Events;
|
|
||||||
using osu.Framework.Logging;
|
using osu.Framework.Logging;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
using osu.Framework.Threading;
|
using osu.Framework.Threading;
|
||||||
@ -88,8 +87,6 @@ namespace osu.Game.Screens.Play
|
|||||||
private bool isRestarting;
|
private bool isRestarting;
|
||||||
private bool skipExitTransition;
|
private bool skipExitTransition;
|
||||||
|
|
||||||
private Bindable<bool> mouseWheelDisabled;
|
|
||||||
|
|
||||||
private readonly Bindable<bool> storyboardReplacesBackground = new Bindable<bool>();
|
private readonly Bindable<bool> storyboardReplacesBackground = new Bindable<bool>();
|
||||||
|
|
||||||
public IBindable<bool> LocalUserPlaying => localUserPlaying;
|
public IBindable<bool> LocalUserPlaying => localUserPlaying;
|
||||||
@ -228,8 +225,6 @@ namespace osu.Game.Screens.Play
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mouseWheelDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableWheel);
|
|
||||||
|
|
||||||
if (game != null)
|
if (game != null)
|
||||||
gameActive.BindTo(game.IsActive);
|
gameActive.BindTo(game.IsActive);
|
||||||
|
|
||||||
@ -251,7 +246,10 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
dependencies.CacheAs(HealthProcessor);
|
dependencies.CacheAs(HealthProcessor);
|
||||||
|
|
||||||
InternalChild = GameplayClockContainer = CreateGameplayClockContainer(Beatmap.Value, DrawableRuleset.GameplayStartTime);
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
GameplayClockContainer = CreateGameplayClockContainer(Beatmap.Value, DrawableRuleset.GameplayStartTime),
|
||||||
|
};
|
||||||
|
|
||||||
AddInternal(screenSuspension = new ScreenSuspensionHandler(GameplayClockContainer));
|
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));
|
dependencies.CacheAs(GameplayState = new GameplayState(playableBeatmap, ruleset, gameplayMods, Score, ScoreProcessor, HealthProcessor, Beatmap.Value.Storyboard));
|
||||||
|
|
||||||
var rulesetSkinProvider = new RulesetSkinProvidingContainer(ruleset, playableBeatmap, Beatmap.Value.Skin);
|
var rulesetSkinProvider = new RulesetSkinProvidingContainer(ruleset, playableBeatmap, Beatmap.Value.Skin);
|
||||||
|
GameplayClockContainer.Add(new GameplayScrollWheelHandling());
|
||||||
|
|
||||||
// load the skinning hierarchy first.
|
// 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.
|
// 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
|
#region Gameplay leaderboard
|
||||||
|
|
||||||
protected readonly Bindable<bool> LeaderboardExpandedState = new BindableBool();
|
protected readonly Bindable<bool> LeaderboardExpandedState = new BindableBool();
|
||||||
|
@ -27,6 +27,7 @@ using osu.Game.Input;
|
|||||||
using osu.Game.Localisation;
|
using osu.Game.Localisation;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Notifications;
|
using osu.Game.Overlays.Notifications;
|
||||||
|
using osu.Game.Overlays.Volume;
|
||||||
using osu.Game.Performance;
|
using osu.Game.Performance;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
using osu.Game.Screens.Menu;
|
using osu.Game.Screens.Menu;
|
||||||
@ -190,6 +191,7 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
|
new GlobalScrollAdjustsVolume(),
|
||||||
(content = new LogoTrackingContainer
|
(content = new LogoTrackingContainer
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
|
@ -31,6 +31,7 @@ using osu.Game.Graphics.UserInterface;
|
|||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
using osu.Game.Overlays.Mods;
|
using osu.Game.Overlays.Mods;
|
||||||
|
using osu.Game.Overlays.Volume;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Screens.Backgrounds;
|
using osu.Game.Screens.Backgrounds;
|
||||||
@ -169,10 +170,12 @@ namespace osu.Game.Screens.Select
|
|||||||
|
|
||||||
AddRangeInternal(new Drawable[]
|
AddRangeInternal(new Drawable[]
|
||||||
{
|
{
|
||||||
|
new GlobalScrollAdjustsVolume(),
|
||||||
new VerticalMaskingContainer
|
new VerticalMaskingContainer
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
|
new GlobalScrollAdjustsVolume(),
|
||||||
new GridContainer // used for max width implementation
|
new GridContainer // used for max width implementation
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
@ -35,8 +35,8 @@
|
|||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Realm" Version="11.5.0" />
|
<PackageReference Include="Realm" Version="11.5.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework" Version="2024.1219.0" />
|
<PackageReference Include="ppy.osu.Framework" Version="2024.1220.0" />
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2024.1202.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2024.1219.1" />
|
||||||
<PackageReference Include="Sentry" Version="4.13.0" />
|
<PackageReference Include="Sentry" Version="4.13.0" />
|
||||||
<!-- Held back due to 0.34.0 failing AOT compilation on ZstdSharp.dll dependency. -->
|
<!-- Held back due to 0.34.0 failing AOT compilation on ZstdSharp.dll dependency. -->
|
||||||
<PackageReference Include="SharpCompress" Version="0.38.0" />
|
<PackageReference Include="SharpCompress" Version="0.38.0" />
|
||||||
|
@ -17,6 +17,6 @@
|
|||||||
<MtouchInterpreter>-all</MtouchInterpreter>
|
<MtouchInterpreter>-all</MtouchInterpreter>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2024.1219.0" />
|
<PackageReference Include="ppy.osu.Framework.iOS" Version="2024.1220.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -34,9 +34,11 @@
|
|||||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>NSCameraUsageDescription</key>
|
<key>NSCameraUsageDescription</key>
|
||||||
<string>We don't really use the camera.</string>
|
<string>We don't use the camera.</string>
|
||||||
<key>NSMicrophoneUsageDescription</key>
|
<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>
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
<array>
|
<array>
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
@ -153,5 +155,7 @@
|
|||||||
</array>
|
</array>
|
||||||
<key>LSApplicationCategoryType</key>
|
<key>LSApplicationCategoryType</key>
|
||||||
<string>public.app-category.music-games</string>
|
<string>public.app-category.music-games</string>
|
||||||
|
<key>LSSupportsOpeningDocumentsInPlace</key>
|
||||||
|
<false/>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
Loading…
Reference in New Issue
Block a user