mirror of
https://github.com/ppy/osu.git
synced 2026-05-22 21:40:49 +08:00
Compare commits
118 Commits
+1
-1
@@ -10,7 +10,7 @@
|
||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.1206.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.1224.0" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup>
|
||||
<!-- Fody does not handle Android build well, and warns when unchanged.
|
||||
|
||||
@@ -134,7 +134,6 @@ namespace osu.Desktop
|
||||
if (iconStream != null)
|
||||
host.Window.SetIconFromStream(iconStream);
|
||||
|
||||
host.Window.CursorState |= CursorState.Hidden;
|
||||
host.Window.Title = Name;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// 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 Foundation;
|
||||
using osu.Framework.iOS;
|
||||
using osu.Game.Tests;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests.iOS
|
||||
{
|
||||
[Register("AppDelegate")]
|
||||
public class AppDelegate : GameApplicationDelegate
|
||||
{
|
||||
protected override Framework.Game CreateGame() => new OsuTestBrowser();
|
||||
}
|
||||
}
|
||||
+3
-4
@@ -1,16 +1,15 @@
|
||||
// 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.iOS;
|
||||
using osu.Game.Tests;
|
||||
using UIKit;
|
||||
|
||||
namespace osu.Game.Rulesets.Catch.Tests.iOS
|
||||
{
|
||||
public static class Application
|
||||
public static class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
GameApplication.Main(new OsuTestBrowser());
|
||||
UIApplication.Main(args, null, typeof(AppDelegate));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// 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 Foundation;
|
||||
using osu.Framework.iOS;
|
||||
using osu.Game.Tests;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests.iOS
|
||||
{
|
||||
[Register("AppDelegate")]
|
||||
public class AppDelegate : GameApplicationDelegate
|
||||
{
|
||||
protected override Framework.Game CreateGame() => new OsuTestBrowser();
|
||||
}
|
||||
}
|
||||
+3
-4
@@ -1,16 +1,15 @@
|
||||
// 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.iOS;
|
||||
using osu.Game.Tests;
|
||||
using UIKit;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Tests.iOS
|
||||
{
|
||||
public static class Application
|
||||
public static class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
GameApplication.Main(new OsuTestBrowser());
|
||||
UIApplication.Main(args, null, typeof(AppDelegate));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
+1
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
+60
@@ -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.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-6
@@ -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;
|
||||
+7
-9
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-7
@@ -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;
|
||||
+5
-2
@@ -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);
|
||||
@@ -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>();
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// 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 Foundation;
|
||||
using osu.Framework.iOS;
|
||||
using osu.Game.Tests;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests.iOS
|
||||
{
|
||||
[Register("AppDelegate")]
|
||||
public class AppDelegate : GameApplicationDelegate
|
||||
{
|
||||
protected override Framework.Game CreateGame() => new OsuTestBrowser();
|
||||
}
|
||||
}
|
||||
+3
-4
@@ -1,16 +1,15 @@
|
||||
// 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.iOS;
|
||||
using osu.Game.Tests;
|
||||
using UIKit;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests.iOS
|
||||
{
|
||||
public static class Application
|
||||
public static class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
GameApplication.Main(new OsuTestBrowser());
|
||||
UIApplication.Main(args, null, typeof(AppDelegate));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -61,13 +61,16 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
|
||||
var drawableOsuObject = (DrawableOsuHitObject?)drawableObject;
|
||||
|
||||
// As a precondition, ensure that any prefix lookups are run against the skin which is providing "hitcircle".
|
||||
// As a precondition, prefer that any *prefix* lookups are run against the skin which is providing "hitcircle".
|
||||
// This is to correctly handle a case such as:
|
||||
//
|
||||
// - Beatmap provides `hitcircle`
|
||||
// - User skin provides `sliderstartcircle`
|
||||
//
|
||||
// In such a case, the `hitcircle` should be used for slider start circles rather than the user's skin override.
|
||||
//
|
||||
// Of note, this consideration should only be used to decide whether to continue looking up the prefixed name or not.
|
||||
// The final lookups must still run on the full skin hierarchy as per usual in order to correctly handle fallback cases.
|
||||
var provider = skin.FindProvider(s => s.GetTexture(base_lookup) != null) ?? skin;
|
||||
|
||||
// if a base texture for the specified prefix exists, continue using it for subsequent lookups.
|
||||
@@ -81,7 +84,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
// expected behaviour in this scenario is not showing the overlay, rather than using hitcircleoverlay.png.
|
||||
InternalChildren = new[]
|
||||
{
|
||||
CircleSprite = new LegacyKiaiFlashingDrawable(() => new Sprite { Texture = provider.GetTexture(circleName)?.WithMaximumSize(maxSize) })
|
||||
CircleSprite = new LegacyKiaiFlashingDrawable(() => new Sprite { Texture = skin.GetTexture(circleName)?.WithMaximumSize(maxSize) })
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
@@ -90,7 +93,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Child = OverlaySprite = new LegacyKiaiFlashingDrawable(() => new Sprite { Texture = provider.GetTexture(@$"{circleName}overlay")?.WithMaximumSize(maxSize) })
|
||||
Child = OverlaySprite = new LegacyKiaiFlashingDrawable(() => new Sprite { Texture = skin.GetTexture(@$"{circleName}overlay")?.WithMaximumSize(maxSize) })
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// 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 Foundation;
|
||||
using osu.Framework.iOS;
|
||||
using osu.Game.Tests;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Tests.iOS
|
||||
{
|
||||
[Register("AppDelegate")]
|
||||
public class AppDelegate : GameApplicationDelegate
|
||||
{
|
||||
protected override Framework.Game CreateGame() => new OsuTestBrowser();
|
||||
}
|
||||
}
|
||||
+3
-4
@@ -1,16 +1,15 @@
|
||||
// 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.iOS;
|
||||
using osu.Game.Tests;
|
||||
using UIKit;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Tests.iOS
|
||||
{
|
||||
public static class Application
|
||||
public static class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
GameApplication.Main(new OsuTestBrowser());
|
||||
UIApplication.Main(args, null, typeof(AppDelegate));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// 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 Foundation;
|
||||
using osu.Framework.iOS;
|
||||
|
||||
namespace osu.Game.Tests.iOS
|
||||
{
|
||||
[Register("AppDelegate")]
|
||||
public class AppDelegate : GameApplicationDelegate
|
||||
{
|
||||
protected override Framework.Game CreateGame() => new OsuTestBrowser();
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,15 @@
|
||||
// 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.iOS;
|
||||
using UIKit;
|
||||
|
||||
namespace osu.Game.Tests.iOS
|
||||
{
|
||||
public static class Application
|
||||
public static class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
GameApplication.Main(new OsuTestBrowser());
|
||||
UIApplication.Main(args, null, typeof(AppDelegate));
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -68,7 +68,9 @@ namespace osu.Game.Tests.Skins
|
||||
// Covers legacy rank display
|
||||
"Archives/modified-classic-20230809.osk",
|
||||
// Covers legacy key counter
|
||||
"Archives/modified-classic-20240724.osk"
|
||||
"Archives/modified-classic-20240724.osk",
|
||||
// Covers skinnable mod display
|
||||
"Archives/modified-default-20241207.osk",
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -12,6 +12,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
@@ -19,6 +20,7 @@ using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Storyboards;
|
||||
using osuTK;
|
||||
using osuTK.Input;
|
||||
|
||||
@@ -28,6 +30,12 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
protected new PausePlayer Player => (PausePlayer)base.Player;
|
||||
|
||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
|
||||
{
|
||||
beatmap.AudioLeadIn = 4000;
|
||||
return base.CreateWorkingBeatmap(beatmap, storyboard);
|
||||
}
|
||||
|
||||
private readonly Container content;
|
||||
|
||||
protected override Container<Drawable> Content => content;
|
||||
@@ -200,8 +208,10 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Ignore("Fails on github runners if they happen to skip too far forward in time.")]
|
||||
public void TestUserPauseDuringCooldownTooSoon()
|
||||
{
|
||||
AddStep("seek to gameplay", () => Player.GameplayClockContainer.Seek(0));
|
||||
AddStep("move cursor outside", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopLeft - new Vector2(10)));
|
||||
|
||||
pauseAndConfirm();
|
||||
@@ -213,9 +223,23 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
confirmNotExited();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestUserPauseDuringIntroSkipsCooldown()
|
||||
{
|
||||
AddStep("seek before gameplay", () => Player.GameplayClockContainer.Seek(-5000));
|
||||
AddStep("move cursor outside", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopLeft - new Vector2(10)));
|
||||
|
||||
pauseAndConfirm();
|
||||
|
||||
resume();
|
||||
pauseViaBackAction();
|
||||
confirmPaused();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestQuickExitDuringCooldownTooSoon()
|
||||
{
|
||||
AddStep("seek to gameplay", () => Player.GameplayClockContainer.Seek(0));
|
||||
AddStep("move cursor outside", () => InputManager.MoveMouseTo(Player.ScreenSpaceDrawQuad.TopLeft - new Vector2(10)));
|
||||
|
||||
pauseAndConfirm();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osu.Game.Seasonal;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Menus
|
||||
{
|
||||
|
||||
@@ -6,8 +6,7 @@ using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osu.Game.Seasonal;
|
||||
|
||||
namespace osu.Game.Tests.Visual.Menus
|
||||
{
|
||||
@@ -16,17 +15,15 @@ namespace osu.Game.Tests.Visual.Menus
|
||||
[Resolved]
|
||||
private BeatmapManager beatmaps { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private RealmAccess realm { get; set; } = null!;
|
||||
|
||||
[SetUpSteps]
|
||||
public void SetUpSteps()
|
||||
{
|
||||
AddStep("prepare beatmap", () =>
|
||||
{
|
||||
var setInfo = beatmaps.QueryBeatmapSet(b => b.Protected && b.Hash == "7e26183e72a496f672c3a21292e6b469fdecd084d31c259ea10a31df5b46cd77");
|
||||
var setInfo = beatmaps.QueryBeatmapSet(b => b.Protected && b.Hash == IntroChristmas.CHRISTMAS_BEATMAP_SET_HASH);
|
||||
|
||||
Beatmap.Value = beatmaps.GetWorkingBeatmap(setInfo!.Value.Beatmaps.First());
|
||||
if (setInfo != null)
|
||||
Beatmap.Value = beatmaps.GetWorkingBeatmap(setInfo.Value.Beatmaps.First());
|
||||
});
|
||||
|
||||
AddStep("create lighting", () => Child = new MainMenuSeasonalLighting());
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osu.Game.Seasonal;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Tests.Visual.UserInterface
|
||||
@@ -12,6 +13,19 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
private OsuLogo? logo;
|
||||
|
||||
private float scale = 1;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
AddSliderStep("scale", 0.1, 2, 1, scale =>
|
||||
{
|
||||
if (logo != null)
|
||||
Child.Scale = new Vector2(this.scale = (float)scale);
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestBasic()
|
||||
{
|
||||
@@ -21,13 +35,22 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Scale = new Vector2(scale),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
AddSliderStep("scale", 0.1, 2, 1, scale =>
|
||||
[Test]
|
||||
public void TestChristmas()
|
||||
{
|
||||
AddStep("Add logo", () =>
|
||||
{
|
||||
if (logo != null)
|
||||
Child.Scale = new Vector2((float)scale);
|
||||
Child = logo = new OsuLogoChristmas
|
||||
{
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Scale = new Vector2(scale),
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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),
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -533,6 +533,16 @@ namespace osu.Game.Beatmaps
|
||||
}
|
||||
}
|
||||
|
||||
public void MarkPlayed(BeatmapInfo beatmapSetInfo) => Realm.Run(r =>
|
||||
{
|
||||
using var transaction = r.BeginWrite();
|
||||
|
||||
var beatmap = r.Find<BeatmapInfo>(beatmapSetInfo.ID)!;
|
||||
beatmap.LastPlayed = DateTimeOffset.Now;
|
||||
|
||||
transaction.Commit();
|
||||
});
|
||||
|
||||
#region Implementation of ICanAcceptFiles
|
||||
|
||||
public Task Import(params string[] paths) => beatmapImporter.Import(paths);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ namespace osu.Game.Configuration
|
||||
SetDefault(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2f, 1f, 0.01f);
|
||||
|
||||
SetDefault(OsuSetting.BeatmapListingCardSize, BeatmapCardSize.Normal);
|
||||
SetDefault(OsuSetting.BeatmapListingFeaturedArtistFilter, true);
|
||||
|
||||
SetDefault(OsuSetting.ProfileCoverExpanded, true);
|
||||
|
||||
@@ -450,5 +451,6 @@ namespace osu.Game.Configuration
|
||||
EditorAdjustExistingObjectsOnTimingChanges,
|
||||
AlwaysRequireHoldingForPause,
|
||||
MultiplayerShowInProgressFilter,
|
||||
BeatmapListingFeaturedArtistFilter,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,8 +95,9 @@ namespace osu.Game.Database
|
||||
/// 42 2024-08-07 Update mania key bindings to reflect changes to ManiaAction
|
||||
/// 43 2024-10-14 Reset keybind for toggling FPS display to avoid conflict with "convert to stream" in the editor, if not already changed by user.
|
||||
/// 44 2024-11-22 Removed several properties from BeatmapInfo which did not need to be persisted to realm.
|
||||
/// 45 2024-12-23 Change beat snap divisor adjust defaults to be Ctrl+Scroll instead of Ctrl+Shift+Scroll, if not already changed by user.
|
||||
/// </summary>
|
||||
private const int schema_version = 44;
|
||||
private const int schema_version = 45;
|
||||
|
||||
/// <summary>
|
||||
/// Lock object which is held during <see cref="BlockAllOperations"/> sections, blocking realm retrieval during blocking periods.
|
||||
@@ -1205,6 +1206,22 @@ namespace osu.Game.Database
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 45:
|
||||
{
|
||||
// Cycling beat snap divisors no longer requires holding shift (just control).
|
||||
var keyBindings = migration.NewRealm.All<RealmKeyBinding>();
|
||||
|
||||
var nextBeatSnapBinding = keyBindings.FirstOrDefault(k => k.ActionInt == (int)GlobalAction.EditorCycleNextBeatSnapDivisor);
|
||||
if (nextBeatSnapBinding != null && nextBeatSnapBinding.KeyCombination.Keys.SequenceEqual(new[] { InputKey.Shift, InputKey.Control, InputKey.MouseWheelLeft }))
|
||||
migration.NewRealm.Remove(nextBeatSnapBinding);
|
||||
|
||||
var previousBeatSnapBinding = keyBindings.FirstOrDefault(k => k.ActionInt == (int)GlobalAction.EditorCyclePreviousBeatSnapDivisor);
|
||||
if (previousBeatSnapBinding != null && previousBeatSnapBinding.KeyCombination.Keys.SequenceEqual(new[] { InputKey.Shift, InputKey.Control, InputKey.MouseWheelRight }))
|
||||
migration.NewRealm.Remove(previousBeatSnapBinding);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.Log($"Migration completed in {stopwatch.ElapsedMilliseconds}ms");
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -142,10 +142,8 @@ namespace osu.Game.Input.Bindings
|
||||
new KeyBinding(new[] { InputKey.Control, InputKey.J }, GlobalAction.EditorFlipVertically),
|
||||
new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.MouseWheelDown }, GlobalAction.EditorDecreaseDistanceSpacing),
|
||||
new KeyBinding(new[] { InputKey.Control, InputKey.Alt, InputKey.MouseWheelUp }, GlobalAction.EditorIncreaseDistanceSpacing),
|
||||
// Framework automatically converts wheel up/down to left/right when shift is held.
|
||||
// See https://github.com/ppy/osu-framework/blob/master/osu.Framework/Input/StateChanges/MouseScrollRelativeInput.cs#L37-L38.
|
||||
new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.MouseWheelRight }, GlobalAction.EditorCyclePreviousBeatSnapDivisor),
|
||||
new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.MouseWheelLeft }, GlobalAction.EditorCycleNextBeatSnapDivisor),
|
||||
new KeyBinding(new[] { InputKey.Control, InputKey.MouseWheelUp }, GlobalAction.EditorCyclePreviousBeatSnapDivisor),
|
||||
new KeyBinding(new[] { InputKey.Control, InputKey.MouseWheelDown }, GlobalAction.EditorCycleNextBeatSnapDivisor),
|
||||
new KeyBinding(new[] { InputKey.Control, InputKey.R }, GlobalAction.EditorToggleRotateControl),
|
||||
new KeyBinding(new[] { InputKey.Control, InputKey.E }, GlobalAction.EditorToggleScaleControl),
|
||||
new KeyBinding(new[] { InputKey.Control, InputKey.Left }, GlobalAction.EditorSeekToPreviousHitObject),
|
||||
|
||||
@@ -28,6 +28,11 @@ This includes content that may not be correctly licensed for osu! usage. Browse
|
||||
/// </summary>
|
||||
public static LocalisableString UserContentConfirmButtonText => new TranslatableString(getKey(@"understood"), @"I understand");
|
||||
|
||||
/// <summary>
|
||||
/// "Featured Artists are music artists who have collaborated with osu! to make a selection of their tracks available for use in beatmaps. For some osu! releases, we showcase only featured artist beatmaps to better support the surrounding ecosystem."
|
||||
/// </summary>
|
||||
public static LocalisableString FeaturedArtistsTooltip => new TranslatableString(getKey(@"featured_artists_disabled_tooltip"), @"Featured Artists are music artists who have collaborated with osu! to make a selection of their tracks available for use in beatmaps. For some osu! releases, we showcase only featured artist beatmaps to better support the surrounding ecosystem.");
|
||||
|
||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,6 +119,11 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString AutoplayBeatmapShortcut => new TranslatableString(getKey(@"autoplay_beatmap_shortcut"), @"Ctrl-Enter at song select will start a beatmap in autoplay mode!");
|
||||
|
||||
/// <summary>
|
||||
/// ""Lazer" is not an English word. The correct spelling for the bright light is "laser"."
|
||||
/// </summary>
|
||||
public static LocalisableString LazerIsNotAWord => new TranslatableString(getKey(@"lazer_is_not_a_word"), @"""Lazer"" is not an English word. The correct spelling for the bright light is ""laser"".");
|
||||
|
||||
/// <summary>
|
||||
/// "Multithreading support means that even with low "FPS" your input and judgements will be accurate!"
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// 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.Localisation;
|
||||
|
||||
namespace osu.Game.Localisation.SkinComponents
|
||||
{
|
||||
public static class SkinnableModDisplayStrings
|
||||
{
|
||||
private const string prefix = @"osu.Game.Resources.Localisation.SkinnableModDisplay";
|
||||
|
||||
/// <summary>
|
||||
/// "Show extended information"
|
||||
/// </summary>
|
||||
public static LocalisableString ShowExtendedInformation => new TranslatableString(getKey(@"show_extended_information"), @"Show extended information");
|
||||
|
||||
/// <summary>
|
||||
/// "Whether to show extended information for each mod."
|
||||
/// </summary>
|
||||
public static LocalisableString ShowExtendedInformationDescription => new TranslatableString(getKey(@"whether_to_show_extended_information"), @"Whether to show extended information for each mod.");
|
||||
|
||||
/// <summary>
|
||||
/// "Expansion mode"
|
||||
/// </summary>
|
||||
public static LocalisableString ExpansionMode => new TranslatableString(getKey(@"expansion_mode"), @"Expansion mode");
|
||||
|
||||
/// <summary>
|
||||
/// "How the mod display expands when interacted with."
|
||||
/// </summary>
|
||||
public static LocalisableString ExpansionModeDescription => new TranslatableString(getKey(@"how_the_mod_display_expands"), @"How the mod display expands when interacted with.");
|
||||
|
||||
/// <summary>
|
||||
/// "Expand on hover"
|
||||
/// </summary>
|
||||
public static LocalisableString ExpandOnHover => new TranslatableString(getKey(@"expand_on_hover"), @"Expand on hover");
|
||||
|
||||
/// <summary>
|
||||
/// "Always contracted"
|
||||
/// </summary>
|
||||
public static LocalisableString AlwaysContracted => new TranslatableString(getKey(@"always_contracted"), @"Always contracted");
|
||||
|
||||
/// <summary>
|
||||
/// "Always expanded"
|
||||
/// </summary>
|
||||
public static LocalisableString AlwaysExpanded => new TranslatableString(getKey(@"always_expanded"), @"Always expanded");
|
||||
|
||||
private static string getKey(string key) => $@"{prefix}:{key}";
|
||||
}
|
||||
}
|
||||
@@ -54,16 +54,6 @@ namespace osu.Game.Localisation
|
||||
/// </summary>
|
||||
public static LocalisableString BeatmapHitsounds => new TranslatableString(getKey(@"beatmap_hitsounds"), @"Beatmap hitsounds");
|
||||
|
||||
/// <summary>
|
||||
/// "Export selected skin"
|
||||
/// </summary>
|
||||
public static LocalisableString ExportSkinButton => new TranslatableString(getKey(@"export_skin_button"), @"Export selected skin");
|
||||
|
||||
/// <summary>
|
||||
/// "Delete selected skin"
|
||||
/// </summary>
|
||||
public static LocalisableString DeleteSkinButton => new TranslatableString(getKey(@"delete_skin_button"), @"Delete selected skin");
|
||||
|
||||
private static string getKey(string key) => $"{prefix}:{key}";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+24
-8
@@ -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;
|
||||
@@ -69,6 +68,7 @@ using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
using osu.Game.Screens.Play;
|
||||
using osu.Game.Screens.Ranking;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Seasonal;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Updater;
|
||||
using osu.Game.Users;
|
||||
@@ -221,6 +221,11 @@ namespace osu.Game
|
||||
|
||||
private readonly List<OverlayContainer> visibleBlockingOverlays = new List<OverlayContainer>();
|
||||
|
||||
/// <summary>
|
||||
/// Whether the game should be limited to only display officially licensed content.
|
||||
/// </summary>
|
||||
public virtual bool HideUnlicensedContent => false;
|
||||
|
||||
public OsuGame(string[] args = null)
|
||||
{
|
||||
this.args = args;
|
||||
@@ -320,6 +325,7 @@ namespace osu.Game
|
||||
|
||||
if (host.Window != null)
|
||||
{
|
||||
host.Window.CursorState |= CursorState.Hidden;
|
||||
host.Window.DragDrop += path =>
|
||||
{
|
||||
// on macOS/iOS, URL associations are handled via SDL_DROPFILE events.
|
||||
@@ -362,7 +368,10 @@ namespace osu.Game
|
||||
{
|
||||
SentryLogger.AttachUser(API.LocalUser);
|
||||
|
||||
dependencies.Cache(osuLogo = new OsuLogo { Alpha = 0 });
|
||||
if (SeasonalUIConfig.ENABLED)
|
||||
dependencies.CacheAs(osuLogo = new OsuLogoChristmas { Alpha = 0 });
|
||||
else
|
||||
dependencies.CacheAs(osuLogo = new OsuLogo { Alpha = 0 });
|
||||
|
||||
// bind config int to database RulesetInfo
|
||||
configRuleset = LocalConfig.GetBindable<string>(OsuSetting.Ruleset);
|
||||
@@ -980,12 +989,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 +1435,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;
|
||||
|
||||
@@ -5,6 +5,7 @@ using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
@@ -113,7 +114,7 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
}
|
||||
}
|
||||
|
||||
private partial class FeaturedArtistsTabItem : MultipleSelectionFilterTabItem
|
||||
private partial class FeaturedArtistsTabItem : MultipleSelectionFilterTabItem, IHasTooltip
|
||||
{
|
||||
private Bindable<bool> disclaimerShown = null!;
|
||||
|
||||
@@ -125,17 +126,36 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
[Resolved]
|
||||
private OsuColour colours { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private OsuConfigManager config { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private SessionStatics sessionStatics { get; set; } = null!;
|
||||
|
||||
[Resolved]
|
||||
private IDialogOverlay? dialogOverlay { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private OsuGame? game { get; set; }
|
||||
|
||||
public LocalisableString TooltipText => BeatmapOverlayStrings.FeaturedArtistsTooltip;
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
config.BindWith(OsuSetting.BeatmapListingFeaturedArtistFilter, Active);
|
||||
disclaimerShown = sessionStatics.GetBindable<bool>(Static.FeaturedArtistDisclaimerShownOnce);
|
||||
|
||||
// no need to show the disclaimer if the user already had it toggled off in config.
|
||||
if (!Active.Value)
|
||||
disclaimerShown.Value = true;
|
||||
|
||||
if (game?.HideUnlicensedContent == true)
|
||||
{
|
||||
Enabled.Value = false;
|
||||
Active.Disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
protected override Color4 ColourNormal => colours.Orange1;
|
||||
@@ -143,6 +163,9 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
|
||||
protected override bool OnClick(ClickEvent e)
|
||||
{
|
||||
if (!Enabled.Value)
|
||||
return true;
|
||||
|
||||
if (!disclaimerShown.Value && dialogOverlay != null)
|
||||
{
|
||||
dialogOverlay.Push(new FeaturedArtistConfirmDialog(() =>
|
||||
|
||||
@@ -73,7 +73,10 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
private void currentChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
foreach (var c in Children)
|
||||
c.Active.Value = Current.Contains(c.Value);
|
||||
{
|
||||
if (!c.Active.Disabled)
|
||||
c.Active.Value = Current.Contains(c.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -100,7 +103,7 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
|
||||
protected partial class MultipleSelectionFilterTabItem : FilterTabItem<T>
|
||||
{
|
||||
private Drawable activeContent = null!;
|
||||
private Container activeContent = null!;
|
||||
private Circle background = null!;
|
||||
|
||||
public MultipleSelectionFilterTabItem(T value)
|
||||
@@ -160,7 +163,9 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
{
|
||||
Color4 colour = Active.Value ? ColourActive : ColourNormal;
|
||||
|
||||
if (IsHovered)
|
||||
if (!Enabled.Value)
|
||||
colour = colour.Darken(1f);
|
||||
else if (IsHovered)
|
||||
colour = Active.Value ? colour.Darken(0.2f) : colour.Lighten(0.2f);
|
||||
|
||||
if (Active.Value)
|
||||
|
||||
@@ -57,7 +57,9 @@ namespace osu.Game.Overlays.BeatmapListing
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
Enabled.BindValueChanged(_ => UpdateState());
|
||||
UpdateState();
|
||||
|
||||
FinishTransforms(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -9,17 +9,23 @@ using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Cursor;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Framework.Logging;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Graphics.UserInterfaceV2;
|
||||
using osu.Game.Localisation;
|
||||
using osu.Game.Overlays.SkinEditor;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
using Realms;
|
||||
|
||||
namespace osu.Game.Overlays.Settings.Sections
|
||||
@@ -64,13 +70,26 @@ namespace osu.Game.Overlays.Settings.Sections
|
||||
Current = skins.CurrentSkinInfo,
|
||||
Keywords = new[] { @"skins" },
|
||||
},
|
||||
new FillFlowContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Direction = FillDirection.Horizontal,
|
||||
Spacing = new Vector2(5, 0),
|
||||
Padding = new MarginPadding { Left = SettingsPanel.CONTENT_MARGINS, Right = SettingsPanel.CONTENT_MARGINS },
|
||||
Children = new Drawable[]
|
||||
{
|
||||
// This is all super-temporary until we move skin settings to their own panel / overlay.
|
||||
new RenameSkinButton { Padding = new MarginPadding(), RelativeSizeAxes = Axes.None, Width = 120 },
|
||||
new ExportSkinButton { Padding = new MarginPadding(), RelativeSizeAxes = Axes.None, Width = 120 },
|
||||
new DeleteSkinButton { Padding = new MarginPadding(), RelativeSizeAxes = Axes.None, Width = 110 },
|
||||
}
|
||||
},
|
||||
new SettingsButton
|
||||
{
|
||||
Text = SkinSettingsStrings.SkinLayoutEditor,
|
||||
Action = () => skinEditor?.ToggleVisibility(),
|
||||
},
|
||||
new ExportSkinButton(),
|
||||
new DeleteSkinButton(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -136,6 +155,34 @@ namespace osu.Game.Overlays.Settings.Sections
|
||||
}
|
||||
}
|
||||
|
||||
public partial class RenameSkinButton : SettingsButton, IHasPopover
|
||||
{
|
||||
[Resolved]
|
||||
private SkinManager skins { get; set; }
|
||||
|
||||
private Bindable<Skin> currentSkin;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Text = "Rename";
|
||||
Action = this.ShowPopover;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
currentSkin = skins.CurrentSkin.GetBoundCopy();
|
||||
currentSkin.BindValueChanged(skin => Enabled.Value = skin.NewValue.SkinInfo.PerformRead(s => !s.Protected), true);
|
||||
}
|
||||
|
||||
public Popover GetPopover()
|
||||
{
|
||||
return new RenameSkinPopover();
|
||||
}
|
||||
}
|
||||
|
||||
public partial class ExportSkinButton : SettingsButton
|
||||
{
|
||||
[Resolved]
|
||||
@@ -146,7 +193,7 @@ namespace osu.Game.Overlays.Settings.Sections
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Text = SkinSettingsStrings.ExportSkinButton;
|
||||
Text = "Export";
|
||||
Action = export;
|
||||
}
|
||||
|
||||
@@ -184,7 +231,7 @@ namespace osu.Game.Overlays.Settings.Sections
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Text = SkinSettingsStrings.DeleteSkinButton;
|
||||
Text = "Delete";
|
||||
Action = delete;
|
||||
}
|
||||
|
||||
@@ -201,5 +248,63 @@ namespace osu.Game.Overlays.Settings.Sections
|
||||
dialogOverlay?.Push(new SkinDeleteDialog(currentSkin.Value));
|
||||
}
|
||||
}
|
||||
|
||||
public partial class RenameSkinPopover : OsuPopover
|
||||
{
|
||||
[Resolved]
|
||||
private SkinManager skins { get; set; }
|
||||
|
||||
private readonly FocusedTextBox textBox;
|
||||
|
||||
public RenameSkinPopover()
|
||||
{
|
||||
AutoSizeAxes = Axes.Both;
|
||||
Origin = Anchor.TopCentre;
|
||||
|
||||
RoundedButton renameButton;
|
||||
|
||||
Child = new FillFlowContainer
|
||||
{
|
||||
Direction = FillDirection.Vertical,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
Width = 250,
|
||||
Spacing = new Vector2(10f),
|
||||
Children = new Drawable[]
|
||||
{
|
||||
textBox = new FocusedTextBox
|
||||
{
|
||||
PlaceholderText = @"Skin name",
|
||||
FontSize = OsuFont.DEFAULT_FONT_SIZE,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
SelectAllOnFocus = true,
|
||||
},
|
||||
renameButton = new RoundedButton
|
||||
{
|
||||
Height = 40,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
MatchingFilter = true,
|
||||
Text = "Save",
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
renameButton.Action += rename;
|
||||
textBox.OnCommit += (_, _) => rename();
|
||||
}
|
||||
|
||||
protected override void PopIn()
|
||||
{
|
||||
textBox.Text = skins.CurrentSkinInfo.Value.Value.Name;
|
||||
textBox.TakeFocus();
|
||||
|
||||
base.PopIn();
|
||||
}
|
||||
|
||||
private void rename() => skins.CurrentSkinInfo.Value.PerformWrite(skin =>
|
||||
{
|
||||
skin.Name = textBox.Text;
|
||||
PopOut();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,11 +36,13 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface
|
||||
},
|
||||
new SettingsCheckbox
|
||||
{
|
||||
Keywords = new[] { "intro", "welcome" },
|
||||
LabelText = UserInterfaceStrings.InterfaceVoices,
|
||||
Current = config.GetBindable<bool>(OsuSetting.MenuVoice)
|
||||
},
|
||||
new SettingsCheckbox
|
||||
{
|
||||
Keywords = new[] { "intro", "welcome" },
|
||||
LabelText = UserInterfaceStrings.OsuMusicTheme,
|
||||
Current = config.GetBindable<bool>(OsuSetting.MenuMusic)
|
||||
},
|
||||
|
||||
@@ -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 LegacyHitObjectType LegacyType { get; set; }
|
||||
public LegacyHitObjectType LegacyType { get; set; } = LegacyHitObjectType.Circle;
|
||||
|
||||
public override Judgement CreateJudgement() => new IgnoreJudgement();
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,18 @@ namespace osu.Game.Rulesets.UI
|
||||
private IMod mod;
|
||||
|
||||
private readonly bool showTooltip;
|
||||
private readonly bool showExtendedInformation;
|
||||
|
||||
private bool showExtendedInformation;
|
||||
|
||||
public bool ShowExtendedInformation
|
||||
{
|
||||
get => showExtendedInformation;
|
||||
set
|
||||
{
|
||||
showExtendedInformation = value;
|
||||
updateExtendedInformation();
|
||||
}
|
||||
}
|
||||
|
||||
public IMod Mod
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Development;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shaders;
|
||||
@@ -15,6 +16,7 @@ using osu.Framework.Screens;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Seasonal;
|
||||
using IntroSequence = osu.Game.Configuration.IntroSequence;
|
||||
|
||||
namespace osu.Game.Screens
|
||||
@@ -37,7 +39,9 @@ namespace osu.Game.Screens
|
||||
|
||||
private IntroScreen getIntroSequence()
|
||||
{
|
||||
if (SeasonalUI.ENABLED)
|
||||
// Headless tests run too fast to load non-circles intros correctly.
|
||||
// They will hit the "audio can't play" notification and cause random test failures.
|
||||
if (SeasonalUIConfig.ENABLED && !DebugUtils.IsNUnitRunning)
|
||||
return new IntroChristmas(createMainMenu);
|
||||
|
||||
if (introSequence == IntroSequence.Random)
|
||||
|
||||
@@ -24,6 +24,7 @@ using osu.Game.Localisation;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Overlays.Notifications;
|
||||
using osu.Game.Overlays.Volume;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
using osu.Game.Skinning;
|
||||
@@ -174,6 +175,8 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
return UsingThemedIntro = initialBeatmap != null;
|
||||
}
|
||||
|
||||
AddInternal(new GlobalScrollAdjustsVolume());
|
||||
}
|
||||
|
||||
public override void OnEntering(ScreenTransitionEvent e)
|
||||
|
||||
@@ -8,7 +8,6 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Menu
|
||||
{
|
||||
@@ -37,8 +36,6 @@ namespace osu.Game.Screens.Menu
|
||||
X = -250,
|
||||
},
|
||||
};
|
||||
|
||||
Colour = SeasonalUI.ENABLED ? SeasonalUI.AMBIENT_COLOUR_2 : Color4.White;
|
||||
}
|
||||
|
||||
private bool isTriggered;
|
||||
|
||||
@@ -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;
|
||||
@@ -35,6 +36,7 @@ using osu.Game.Screens.OnlinePlay.DailyChallenge;
|
||||
using osu.Game.Screens.OnlinePlay.Multiplayer;
|
||||
using osu.Game.Screens.OnlinePlay.Playlists;
|
||||
using osu.Game.Screens.Select;
|
||||
using osu.Game.Seasonal;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@@ -124,7 +126,8 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
AddRangeInternal(new[]
|
||||
{
|
||||
SeasonalUI.ENABLED ? new MainMenuSeasonalLighting() : Empty(),
|
||||
SeasonalUIConfig.ENABLED ? new MainMenuSeasonalLighting() : Empty(),
|
||||
new GlobalScrollAdjustsVolume(),
|
||||
buttonsContainer = new ParallaxContainer
|
||||
{
|
||||
ParallaxAmount = 0.01f,
|
||||
@@ -160,7 +163,7 @@ namespace osu.Game.Screens.Menu
|
||||
}
|
||||
},
|
||||
logoTarget = new Container { RelativeSizeAxes = Axes.Both, },
|
||||
sideFlashes = new MenuSideFlashes(),
|
||||
sideFlashes = SeasonalUIConfig.ENABLED ? new SeasonalMenuSideFlashes() : new MenuSideFlashes(),
|
||||
songTicker = new SongTicker
|
||||
{
|
||||
Anchor = Anchor.TopRight,
|
||||
@@ -168,7 +171,7 @@ namespace osu.Game.Screens.Menu
|
||||
Margin = new MarginPadding { Right = 15, Top = 5 }
|
||||
},
|
||||
// For now, this is too much alongside the seasonal lighting.
|
||||
SeasonalUI.ENABLED ? Empty() : new KiaiMenuFountains(),
|
||||
SeasonalUIConfig.ENABLED ? Empty() : new KiaiMenuFountains(),
|
||||
bottomElementsFlow = new FillFlowContainer
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
@@ -199,18 +202,20 @@ namespace osu.Game.Screens.Menu
|
||||
holdToExitGameOverlay?.CreateProxy() ?? Empty()
|
||||
});
|
||||
|
||||
float baseDim = SeasonalUIConfig.ENABLED ? 0.84f : 1;
|
||||
|
||||
Buttons.StateChanged += state =>
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case ButtonSystemState.Initial:
|
||||
case ButtonSystemState.Exit:
|
||||
ApplyToBackground(b => b.FadeColour(Color4.White, 500, Easing.OutSine));
|
||||
ApplyToBackground(b => b.FadeColour(OsuColour.Gray(baseDim), 500, Easing.OutSine));
|
||||
onlineMenuBanner.State.Value = Visibility.Hidden;
|
||||
break;
|
||||
|
||||
default:
|
||||
ApplyToBackground(b => b.FadeColour(OsuColour.Gray(0.8f), 500, Easing.OutSine));
|
||||
ApplyToBackground(b => b.FadeColour(OsuColour.Gray(baseDim * 0.8f), 500, Easing.OutSine));
|
||||
onlineMenuBanner.State.Value = Visibility.Visible;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -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 osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Online.API;
|
||||
@@ -12,10 +10,10 @@ using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Menu
|
||||
{
|
||||
internal partial class MenuLogoVisualisation : LogoVisualisation
|
||||
public partial class MenuLogoVisualisation : LogoVisualisation
|
||||
{
|
||||
private IBindable<APIUser> user;
|
||||
private Bindable<Skin> skin;
|
||||
private IBindable<APIUser> user = null!;
|
||||
private Bindable<Skin> skin = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IAPIProvider api, SkinManager skinManager)
|
||||
@@ -23,15 +21,13 @@ namespace osu.Game.Screens.Menu
|
||||
user = api.LocalUser.GetBoundCopy();
|
||||
skin = skinManager.CurrentSkin.GetBoundCopy();
|
||||
|
||||
user.ValueChanged += _ => updateColour();
|
||||
skin.BindValueChanged(_ => updateColour(), true);
|
||||
user.ValueChanged += _ => UpdateColour();
|
||||
skin.BindValueChanged(_ => UpdateColour(), true);
|
||||
}
|
||||
|
||||
private void updateColour()
|
||||
protected virtual void UpdateColour()
|
||||
{
|
||||
if (SeasonalUI.ENABLED)
|
||||
Colour = SeasonalUI.AMBIENT_COLOUR_1;
|
||||
else if (user.Value?.IsSupporter ?? false)
|
||||
if (user.Value?.IsSupporter ?? false)
|
||||
Colour = skin.Value.GetConfig<GlobalSkinColours, Color4>(GlobalSkinColours.MenuGlow)?.Value ?? Color4.White;
|
||||
else
|
||||
Colour = Color4.White;
|
||||
|
||||
@@ -11,7 +11,6 @@ using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics;
|
||||
@@ -25,6 +24,10 @@ namespace osu.Game.Screens.Menu
|
||||
{
|
||||
public partial class MenuSideFlashes : BeatSyncedContainer
|
||||
{
|
||||
protected virtual bool RefreshColoursEveryFlash => false;
|
||||
|
||||
protected virtual float Intensity => 2;
|
||||
|
||||
private readonly IBindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
|
||||
|
||||
private Box leftBox;
|
||||
@@ -68,7 +71,7 @@ namespace osu.Game.Screens.Menu
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = box_width * (SeasonalUI.ENABLED ? 4 : 2),
|
||||
Width = box_width * Intensity,
|
||||
Height = 1.5f,
|
||||
// align off-screen to make sure our edges don't become visible during parallax.
|
||||
X = -box_width,
|
||||
@@ -80,7 +83,7 @@ namespace osu.Game.Screens.Menu
|
||||
Anchor = Anchor.CentreRight,
|
||||
Origin = Anchor.CentreRight,
|
||||
RelativeSizeAxes = Axes.Y,
|
||||
Width = box_width * (SeasonalUI.ENABLED ? 4 : 2),
|
||||
Width = box_width * Intensity,
|
||||
Height = 1.5f,
|
||||
X = box_width,
|
||||
Alpha = 0,
|
||||
@@ -88,8 +91,11 @@ namespace osu.Game.Screens.Menu
|
||||
}
|
||||
};
|
||||
|
||||
user.ValueChanged += _ => updateColour();
|
||||
skin.BindValueChanged(_ => updateColour(), true);
|
||||
if (!RefreshColoursEveryFlash)
|
||||
{
|
||||
user.ValueChanged += _ => updateColour();
|
||||
skin.BindValueChanged(_ => updateColour(), true);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnNewBeat(int beatIndex, TimingControlPoint timingPoint, EffectControlPoint effectPoint, ChannelAmplitudes amplitudes)
|
||||
@@ -105,7 +111,7 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
private void flash(Drawable d, double beatLength, bool kiai, ChannelAmplitudes amplitudes)
|
||||
{
|
||||
if (SeasonalUI.ENABLED)
|
||||
if (RefreshColoursEveryFlash)
|
||||
updateColour();
|
||||
|
||||
d.FadeTo(Math.Clamp(0.1f + ((ReferenceEquals(d, leftBox) ? amplitudes.LeftChannel : amplitudes.RightChannel) - amplitude_dead_zone) / (kiai ? kiai_multiplier : alpha_multiplier), 0.1f, 1),
|
||||
@@ -114,15 +120,19 @@ namespace osu.Game.Screens.Menu
|
||||
.FadeOut(beatLength, Easing.In);
|
||||
}
|
||||
|
||||
private void updateColour()
|
||||
protected virtual Color4 GetBaseColour()
|
||||
{
|
||||
Color4 baseColour = colours.Blue;
|
||||
|
||||
if (SeasonalUI.ENABLED)
|
||||
baseColour = RNG.NextBool() ? SeasonalUI.PRIMARY_COLOUR_1 : SeasonalUI.PRIMARY_COLOUR_2;
|
||||
else if (user.Value?.IsSupporter ?? false)
|
||||
if (user.Value?.IsSupporter ?? false)
|
||||
baseColour = skin.Value.GetConfig<GlobalSkinColours, Color4>(GlobalSkinColours.MenuGlow)?.Value ?? baseColour;
|
||||
|
||||
return baseColour;
|
||||
}
|
||||
|
||||
private void updateColour()
|
||||
{
|
||||
var baseColour = GetBaseColour();
|
||||
// linear colour looks better in this case, so let's use it for now.
|
||||
Color4 gradientDark = baseColour.Opacity(0).ToLinear();
|
||||
Color4 gradientLight = baseColour.Opacity(0.6f).ToLinear();
|
||||
|
||||
@@ -122,7 +122,8 @@ namespace osu.Game.Screens.Menu
|
||||
MenuTipStrings.RandomSkinShortcut,
|
||||
MenuTipStrings.ToggleReplaySettingsShortcut,
|
||||
MenuTipStrings.CopyModsFromScore,
|
||||
MenuTipStrings.AutoplayBeatmapShortcut
|
||||
MenuTipStrings.AutoplayBeatmapShortcut,
|
||||
MenuTipStrings.LazerIsNotAWord
|
||||
};
|
||||
|
||||
return tips[RNG.Next(0, tips.Length)];
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#nullable disable
|
||||
|
||||
using System;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
@@ -54,8 +53,12 @@ namespace osu.Game.Screens.Menu
|
||||
private Sample sampleClick;
|
||||
private SampleChannel sampleClickChannel;
|
||||
|
||||
private Sample sampleBeat;
|
||||
private Sample sampleDownbeat;
|
||||
protected virtual MenuLogoVisualisation CreateMenuLogoVisualisation() => new MenuLogoVisualisation();
|
||||
|
||||
protected virtual double BeatSampleVariance => 0.1;
|
||||
|
||||
protected Sample SampleBeat;
|
||||
protected Sample SampleDownbeat;
|
||||
|
||||
private readonly Container colourAndTriangles;
|
||||
private readonly TrianglesV2 triangles;
|
||||
@@ -152,15 +155,15 @@ namespace osu.Game.Screens.Menu
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
visualizer = new MenuLogoVisualisation
|
||||
visualizer = CreateMenuLogoVisualisation().With(v =>
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Origin = Anchor.Centre,
|
||||
Anchor = Anchor.Centre,
|
||||
Alpha = visualizer_default_alpha,
|
||||
Size = SCALE_ADJUST
|
||||
},
|
||||
new Container
|
||||
v.RelativeSizeAxes = Axes.Both;
|
||||
v.Origin = Anchor.Centre;
|
||||
v.Anchor = Anchor.Centre;
|
||||
v.Alpha = visualizer_default_alpha;
|
||||
v.Size = SCALE_ADJUST;
|
||||
}),
|
||||
LogoElements = new Container
|
||||
{
|
||||
AutoSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
@@ -212,15 +215,6 @@ namespace osu.Game.Screens.Menu
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
},
|
||||
SeasonalUI.ENABLED
|
||||
? hat = new Sprite
|
||||
{
|
||||
BypassAutoSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Scale = new Vector2(-1, 1),
|
||||
}
|
||||
: Empty(),
|
||||
}
|
||||
},
|
||||
impactContainer = new CircularContainer
|
||||
@@ -253,6 +247,8 @@ namespace osu.Game.Screens.Menu
|
||||
};
|
||||
}
|
||||
|
||||
public Container LogoElements { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Schedule a new external animation. Handled queueing and finishing previous animations in a sane way.
|
||||
/// </summary>
|
||||
@@ -282,20 +278,11 @@ namespace osu.Game.Screens.Menu
|
||||
{
|
||||
sampleClick = audio.Samples.Get(@"Menu/osu-logo-select");
|
||||
|
||||
if (SeasonalUI.ENABLED)
|
||||
{
|
||||
sampleDownbeat = sampleBeat = audio.Samples.Get(@"Menu/osu-logo-heartbeat-bell");
|
||||
}
|
||||
else
|
||||
{
|
||||
sampleBeat = audio.Samples.Get(@"Menu/osu-logo-heartbeat");
|
||||
sampleDownbeat = audio.Samples.Get(@"Menu/osu-logo-downbeat");
|
||||
}
|
||||
SampleBeat = audio.Samples.Get(@"Menu/osu-logo-heartbeat");
|
||||
SampleDownbeat = audio.Samples.Get(@"Menu/osu-logo-downbeat");
|
||||
|
||||
logo.Texture = textures.Get(@"Menu/logo");
|
||||
ripple.Texture = textures.Get(@"Menu/logo");
|
||||
if (hat != null)
|
||||
hat.Texture = textures.Get(@"Menu/hat");
|
||||
}
|
||||
|
||||
private int lastBeatIndex;
|
||||
@@ -318,15 +305,13 @@ namespace osu.Game.Screens.Menu
|
||||
{
|
||||
if (beatIndex % timingPoint.TimeSignature.Numerator == 0)
|
||||
{
|
||||
sampleDownbeat?.Play();
|
||||
SampleDownbeat?.Play();
|
||||
}
|
||||
else
|
||||
{
|
||||
var channel = sampleBeat.GetChannel();
|
||||
if (SeasonalUI.ENABLED)
|
||||
channel.Frequency.Value = 0.99 + RNG.NextDouble(0.02);
|
||||
else
|
||||
channel.Frequency.Value = 0.95 + RNG.NextDouble(0.1);
|
||||
var channel = SampleBeat.GetChannel();
|
||||
|
||||
channel.Frequency.Value = 1 - BeatSampleVariance / 2 + RNG.NextDouble(BeatSampleVariance);
|
||||
channel.Play();
|
||||
}
|
||||
});
|
||||
@@ -381,9 +366,6 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
const float scale_adjust_cutoff = 0.4f;
|
||||
|
||||
if (SeasonalUI.ENABLED)
|
||||
updateHat();
|
||||
|
||||
if (musicController.CurrentTrack.IsRunning)
|
||||
{
|
||||
float maxAmplitude = lastBeatIndex >= 0 ? musicController.CurrentTrack.CurrentAmplitudes.Maximum : 0;
|
||||
@@ -397,38 +379,6 @@ namespace osu.Game.Screens.Menu
|
||||
}
|
||||
}
|
||||
|
||||
private bool hasHat;
|
||||
|
||||
private void updateHat()
|
||||
{
|
||||
if (hat == null)
|
||||
return;
|
||||
|
||||
bool shouldHat = DrawWidth * Scale.X < 400;
|
||||
|
||||
if (shouldHat != hasHat)
|
||||
{
|
||||
hasHat = shouldHat;
|
||||
|
||||
if (hasHat)
|
||||
{
|
||||
hat.Delay(400)
|
||||
.Then()
|
||||
.MoveTo(new Vector2(120, 160))
|
||||
.RotateTo(0)
|
||||
.RotateTo(-20, 500, Easing.OutQuint)
|
||||
.FadeIn(250, Easing.OutQuint);
|
||||
}
|
||||
else
|
||||
{
|
||||
hat.Delay(100)
|
||||
.Then()
|
||||
.MoveToOffset(new Vector2(0, -5), 500, Easing.OutQuint)
|
||||
.FadeOut(500, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool HandlePositionalInput => base.HandlePositionalInput && Alpha > 0.2f;
|
||||
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
@@ -506,9 +456,6 @@ namespace osu.Game.Screens.Menu
|
||||
private Container currentProxyTarget;
|
||||
private Drawable proxy;
|
||||
|
||||
[CanBeNull]
|
||||
private readonly Sprite hat;
|
||||
|
||||
public void StopSamplePlayback() => sampleClickChannel?.Stop();
|
||||
|
||||
public Drawable ProxyToContainer(Container c)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,9 @@ using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.UserInterface;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Localisation.SkinComponents;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osuTK;
|
||||
@@ -20,9 +22,24 @@ namespace osu.Game.Screens.Play.HUD
|
||||
/// </summary>
|
||||
public partial class ModDisplay : CompositeDrawable, IHasCurrentValue<IReadOnlyList<Mod>>
|
||||
{
|
||||
private const int fade_duration = 1000;
|
||||
public const float MOD_ICON_SCALE = 0.6f;
|
||||
|
||||
public ExpansionMode ExpansionMode = ExpansionMode.ExpandOnHover;
|
||||
private ExpansionMode expansionMode = ExpansionMode.ExpandOnHover;
|
||||
|
||||
public ExpansionMode ExpansionMode
|
||||
{
|
||||
get => expansionMode;
|
||||
set
|
||||
{
|
||||
if (expansionMode == value)
|
||||
return;
|
||||
|
||||
expansionMode = value;
|
||||
|
||||
if (IsLoaded)
|
||||
updateExpansionMode();
|
||||
}
|
||||
}
|
||||
|
||||
private readonly BindableWithCurrent<IReadOnlyList<Mod>> current = new BindableWithCurrent<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
||||
|
||||
@@ -37,7 +54,19 @@ namespace osu.Game.Screens.Play.HUD
|
||||
}
|
||||
}
|
||||
|
||||
private readonly bool showExtendedInformation;
|
||||
private bool showExtendedInformation;
|
||||
|
||||
public bool ShowExtendedInformation
|
||||
{
|
||||
get => showExtendedInformation;
|
||||
set
|
||||
{
|
||||
showExtendedInformation = value;
|
||||
foreach (var icon in iconsContainer)
|
||||
icon.ShowExtendedInformation = value;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly FillFlowContainer<ModIcon> iconsContainer;
|
||||
|
||||
public ModDisplay(bool showExtendedInformation = true)
|
||||
@@ -58,11 +87,7 @@ namespace osu.Game.Screens.Play.HUD
|
||||
base.LoadComplete();
|
||||
|
||||
Current.BindValueChanged(updateDisplay, true);
|
||||
|
||||
iconsContainer.FadeInFromZero(fade_duration, Easing.OutQuint);
|
||||
|
||||
if (ExpansionMode == ExpansionMode.AlwaysExpanded || ExpansionMode == ExpansionMode.AlwaysContracted)
|
||||
FinishTransforms(true);
|
||||
updateExpansionMode(0);
|
||||
}
|
||||
|
||||
private void updateDisplay(ValueChangedEvent<IReadOnlyList<Mod>> mods)
|
||||
@@ -70,29 +95,40 @@ namespace osu.Game.Screens.Play.HUD
|
||||
iconsContainer.Clear();
|
||||
|
||||
foreach (Mod mod in mods.NewValue.AsOrdered())
|
||||
iconsContainer.Add(new ModIcon(mod, showExtendedInformation: showExtendedInformation) { Scale = new Vector2(0.6f) });
|
||||
|
||||
appearTransform();
|
||||
iconsContainer.Add(new ModIcon(mod, showExtendedInformation: showExtendedInformation) { Scale = new Vector2(MOD_ICON_SCALE) });
|
||||
}
|
||||
|
||||
private void appearTransform()
|
||||
private void updateExpansionMode(double duration = 500)
|
||||
{
|
||||
expand();
|
||||
switch (expansionMode)
|
||||
{
|
||||
case ExpansionMode.AlwaysExpanded:
|
||||
expand(duration);
|
||||
break;
|
||||
|
||||
using (iconsContainer.BeginDelayedSequence(1200))
|
||||
contract();
|
||||
case ExpansionMode.AlwaysContracted:
|
||||
contract(duration);
|
||||
break;
|
||||
|
||||
case ExpansionMode.ExpandOnHover:
|
||||
if (IsHovered)
|
||||
expand(duration);
|
||||
else
|
||||
contract(duration);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void expand()
|
||||
private void expand(double duration = 500)
|
||||
{
|
||||
if (ExpansionMode != ExpansionMode.AlwaysContracted)
|
||||
iconsContainer.TransformSpacingTo(new Vector2(5, 0), 500, Easing.OutQuint);
|
||||
iconsContainer.TransformSpacingTo(new Vector2(5, 0), duration, Easing.OutQuint);
|
||||
}
|
||||
|
||||
private void contract()
|
||||
private void contract(double duration = 500)
|
||||
{
|
||||
if (ExpansionMode != ExpansionMode.AlwaysExpanded)
|
||||
iconsContainer.TransformSpacingTo(new Vector2(-25, 0), 500, Easing.OutQuint);
|
||||
iconsContainer.TransformSpacingTo(new Vector2(-25, 0), duration, Easing.OutQuint);
|
||||
}
|
||||
|
||||
protected override bool OnHover(HoverEvent e)
|
||||
@@ -113,16 +149,19 @@ namespace osu.Game.Screens.Play.HUD
|
||||
/// <summary>
|
||||
/// The <see cref="ModDisplay"/> will expand only when hovered.
|
||||
/// </summary>
|
||||
[LocalisableDescription(typeof(SkinnableModDisplayStrings), nameof(SkinnableModDisplayStrings.ExpandOnHover))]
|
||||
ExpandOnHover,
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ModDisplay"/> will always be expanded.
|
||||
/// </summary>
|
||||
[LocalisableDescription(typeof(SkinnableModDisplayStrings), nameof(SkinnableModDisplayStrings.AlwaysExpanded))]
|
||||
AlwaysExpanded,
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="ModDisplay"/> will always be contracted.
|
||||
/// </summary>
|
||||
AlwaysContracted
|
||||
[LocalisableDescription(typeof(SkinnableModDisplayStrings), nameof(SkinnableModDisplayStrings.AlwaysContracted))]
|
||||
AlwaysContracted,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
// 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.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Localisation.SkinComponents;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Screens.Play.HUD
|
||||
{
|
||||
/// <summary>
|
||||
/// Displays a single-line horizontal auto-sized flow of mods. For cases where wrapping is required, use <see cref="ModFlowDisplay"/> instead.
|
||||
/// </summary>
|
||||
public partial class SkinnableModDisplay : CompositeDrawable, ISerialisableDrawable
|
||||
{
|
||||
private ModDisplay modDisplay = null!;
|
||||
|
||||
[Resolved]
|
||||
private Bindable<IReadOnlyList<Mod>> mods { get; set; } = null!;
|
||||
|
||||
[SettingSource(typeof(SkinnableModDisplayStrings), nameof(SkinnableModDisplayStrings.ShowExtendedInformation), nameof(SkinnableModDisplayStrings.ShowExtendedInformationDescription))]
|
||||
public Bindable<bool> ShowExtendedInformation { get; } = new Bindable<bool>(true);
|
||||
|
||||
[SettingSource(typeof(SkinnableModDisplayStrings), nameof(SkinnableModDisplayStrings.ExpansionMode), nameof(SkinnableModDisplayStrings.ExpansionModeDescription))]
|
||||
public Bindable<ExpansionMode> ExpansionModeSetting { get; } = new Bindable<ExpansionMode>();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
// Provide a minimum autosize.
|
||||
new Container { Size = ModIcon.MOD_ICON_SIZE * ModDisplay.MOD_ICON_SCALE },
|
||||
modDisplay = new ModDisplay(),
|
||||
};
|
||||
|
||||
modDisplay.Current = mods;
|
||||
AutoSizeAxes = Axes.Both;
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
ShowExtendedInformation.BindValueChanged(_ => modDisplay.ShowExtendedInformation = ShowExtendedInformation.Value, true);
|
||||
ExpansionModeSetting.BindValueChanged(_ => modDisplay.ExpansionMode = ExpansionModeSetting.Value, true);
|
||||
|
||||
FinishTransforms(true);
|
||||
}
|
||||
|
||||
public bool UsesFixedAnchor { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -85,7 +85,6 @@ namespace osu.Game.Screens.Play
|
||||
private readonly BindableBool replayLoaded = new BindableBool();
|
||||
|
||||
private static bool hasShownNotificationOnce;
|
||||
|
||||
private readonly FillFlowContainer bottomRightElements;
|
||||
|
||||
internal readonly FillFlowContainer TopRightElements;
|
||||
@@ -238,7 +237,7 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
if (e.NewValue)
|
||||
{
|
||||
ModDisplay.FadeIn(200);
|
||||
ModDisplay.FadeIn(1000, FADE_EASING);
|
||||
InputCountController.Margin = new MarginPadding(10) { Bottom = 30 };
|
||||
}
|
||||
else
|
||||
@@ -249,6 +248,9 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
updateVisibility();
|
||||
}, true);
|
||||
|
||||
ModDisplay.ExpansionMode = ExpansionMode.AlwaysExpanded;
|
||||
Scheduler.AddDelayed(() => ModDisplay.ExpansionMode = ExpansionMode.ExpandOnHover, 1200);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
|
||||
@@ -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();
|
||||
@@ -1032,7 +1021,7 @@ namespace osu.Game.Screens.Play
|
||||
private double? lastPauseActionTime;
|
||||
|
||||
protected bool PauseCooldownActive =>
|
||||
lastPauseActionTime.HasValue && GameplayClockContainer.CurrentTime < lastPauseActionTime + PauseCooldownDuration;
|
||||
PlayingState.Value == LocalUserPlayingState.Playing && lastPauseActionTime.HasValue && GameplayClockContainer.CurrentTime < lastPauseActionTime + PauseCooldownDuration;
|
||||
|
||||
/// <summary>
|
||||
/// A set of conditionals which defines whether the current game state and configuration allows for
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -88,6 +88,9 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
[Resolved]
|
||||
private OsuGame? game { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private BeatmapManager? manager { get; set; }
|
||||
|
||||
private IBindable<StarDifficulty?> starDifficultyBindable = null!;
|
||||
private CancellationTokenSource? starDifficultyCancellationSource;
|
||||
|
||||
@@ -98,7 +101,7 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(BeatmapManager? manager, SongSelect? songSelect)
|
||||
private void load(SongSelect? songSelect)
|
||||
{
|
||||
Header.Height = height;
|
||||
|
||||
@@ -300,6 +303,9 @@ namespace osu.Game.Screens.Select.Carousel
|
||||
if (beatmapInfo.GetOnlineURL(api, ruleset.Value) is string url)
|
||||
items.Add(new OsuMenuItem(CommonStrings.CopyLink, MenuItemType.Standard, () => game?.CopyUrlToClipboard(url)));
|
||||
|
||||
if (manager != null)
|
||||
items.Add(new OsuMenuItem("Mark as played", MenuItemType.Standard, () => manager.MarkPlayed(beatmapInfo)));
|
||||
|
||||
if (hideRequested != null)
|
||||
items.Add(new OsuMenuItem(WebCommonStrings.ButtonsHide.ToSentence(), MenuItemType.Destructive, () => hideRequested(beatmapInfo)));
|
||||
|
||||
|
||||
@@ -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,
|
||||
@@ -375,7 +378,7 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
BeatmapOptions.AddButton(@"Manage", @"collections", FontAwesome.Solid.Book, colours.Green, () => manageCollectionsDialog?.Show());
|
||||
BeatmapOptions.AddButton(@"Delete", @"all difficulties", FontAwesome.Solid.Trash, colours.Pink, () => DeleteBeatmap(Beatmap.Value.BeatmapSetInfo));
|
||||
BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.Regular.TimesCircle, colours.Purple, null);
|
||||
BeatmapOptions.AddButton(@"Mark", @"as played", FontAwesome.Regular.TimesCircle, colours.Purple, () => beatmaps.MarkPlayed(Beatmap.Value.BeatmapInfo));
|
||||
BeatmapOptions.AddButton(@"Clear", @"local scores", FontAwesome.Solid.Eraser, colours.Purple, () => ClearScores(Beatmap.Value.BeatmapInfo));
|
||||
}
|
||||
|
||||
|
||||
@@ -15,14 +15,18 @@ using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Menu
|
||||
namespace osu.Game.Seasonal
|
||||
{
|
||||
public partial class IntroChristmas : IntroScreen
|
||||
{
|
||||
protected override string BeatmapHash => "7e26183e72a496f672c3a21292e6b469fdecd084d31c259ea10a31df5b46cd77";
|
||||
// nekodex - circle the halls
|
||||
public const string CHRISTMAS_BEATMAP_SET_HASH = "7e26183e72a496f672c3a21292e6b469fdecd084d31c259ea10a31df5b46cd77";
|
||||
|
||||
protected override string BeatmapHash => CHRISTMAS_BEATMAP_SET_HASH;
|
||||
|
||||
protected override string BeatmapFile => "christmas2024.osz";
|
||||
|
||||
@@ -299,7 +303,7 @@ namespace osu.Game.Screens.Menu
|
||||
float x = 0.5f + 0.5f * randomRadius * (float)Math.Cos(angle);
|
||||
float y = 0.5f + 0.5f * randomRadius * (float)Math.Sin(angle);
|
||||
|
||||
Color4 christmasColour = RNG.NextBool() ? SeasonalUI.PRIMARY_COLOUR_1 : SeasonalUI.PRIMARY_COLOUR_2;
|
||||
Color4 christmasColour = RNG.NextBool() ? SeasonalUIConfig.PRIMARY_COLOUR_1 : SeasonalUIConfig.PRIMARY_COLOUR_2;
|
||||
|
||||
Drawable triangle = new Triangle
|
||||
{
|
||||
+38
-19
@@ -21,21 +21,23 @@ using osu.Game.Rulesets.Objects.Types;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Menu
|
||||
namespace osu.Game.Seasonal
|
||||
{
|
||||
public partial class MainMenuSeasonalLighting : CompositeDrawable
|
||||
{
|
||||
private IBindable<WorkingBeatmap> working = null!;
|
||||
|
||||
private InterpolatingFramedClock beatmapClock = null!;
|
||||
private InterpolatingFramedClock? beatmapClock;
|
||||
|
||||
private List<HitObject> hitObjects = null!;
|
||||
|
||||
[Resolved]
|
||||
private RulesetStore rulesets { get; set; } = null!;
|
||||
private RulesetInfo? osuRuleset;
|
||||
|
||||
private int? lastObjectIndex;
|
||||
|
||||
public MainMenuSeasonalLighting()
|
||||
{
|
||||
// match beatmap playfield
|
||||
RelativeChildSize = new Vector2(512, 384);
|
||||
|
||||
RelativeSizeAxes = Axes.X;
|
||||
@@ -45,8 +47,11 @@ namespace osu.Game.Screens.Menu
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IBindable<WorkingBeatmap> working)
|
||||
private void load(IBindable<WorkingBeatmap> working, RulesetStore rulesets)
|
||||
{
|
||||
// operate in osu! ruleset to keep things simple for now.
|
||||
osuRuleset = rulesets.GetRuleset(0);
|
||||
|
||||
this.working = working.GetBoundCopy();
|
||||
this.working.BindValueChanged(_ => Scheduler.AddOnce(updateBeatmap), true);
|
||||
}
|
||||
@@ -54,18 +59,32 @@ namespace osu.Game.Screens.Menu
|
||||
private void updateBeatmap()
|
||||
{
|
||||
lastObjectIndex = null;
|
||||
|
||||
if (osuRuleset == null)
|
||||
{
|
||||
beatmapClock = new InterpolatingFramedClock(Clock);
|
||||
hitObjects = new List<HitObject>();
|
||||
return;
|
||||
}
|
||||
|
||||
// Intentionally maintain separately so the lighting is not in audio clock space (it shouldn't rewind etc.)
|
||||
beatmapClock = new InterpolatingFramedClock(new FramedClock(working.Value.Track));
|
||||
hitObjects = working.Value.GetPlayableBeatmap(rulesets.GetRuleset(0)).HitObjects.SelectMany(h => h.NestedHitObjects.Prepend(h))
|
||||
|
||||
hitObjects = working.Value
|
||||
.GetPlayableBeatmap(osuRuleset)
|
||||
.HitObjects
|
||||
.SelectMany(h => h.NestedHitObjects.Prepend(h))
|
||||
.OrderBy(h => h.StartTime)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private int? lastObjectIndex;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (osuRuleset == null || beatmapClock == null)
|
||||
return;
|
||||
|
||||
Height = DrawWidth / 16 * 10;
|
||||
|
||||
beatmapClock.ProcessFrame();
|
||||
@@ -105,7 +124,7 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
if (h.GetType().Name.Contains("Tick"))
|
||||
{
|
||||
light.Colour = SeasonalUI.AMBIENT_COLOUR_1;
|
||||
light.Colour = SeasonalUIConfig.AMBIENT_COLOUR_1;
|
||||
light.Scale = new Vector2(0.5f);
|
||||
light
|
||||
.FadeInFromZero(250)
|
||||
@@ -116,19 +135,19 @@ namespace osu.Game.Screens.Menu
|
||||
}
|
||||
else
|
||||
{
|
||||
// default green
|
||||
Color4 col = SeasonalUI.PRIMARY_COLOUR_2;
|
||||
// default are green
|
||||
Color4 col = SeasonalUIConfig.PRIMARY_COLOUR_2;
|
||||
|
||||
// whistle red
|
||||
// whistles are red
|
||||
if (h.Samples.Any(s => s.Name == HitSampleInfo.HIT_WHISTLE))
|
||||
col = SeasonalUI.PRIMARY_COLOUR_1;
|
||||
// clap is third colour
|
||||
col = SeasonalUIConfig.PRIMARY_COLOUR_1;
|
||||
// clap is third ambient (yellow) colour
|
||||
else if (h.Samples.Any(s => s.Name == HitSampleInfo.HIT_CLAP))
|
||||
col = SeasonalUI.AMBIENT_COLOUR_1;
|
||||
col = SeasonalUIConfig.AMBIENT_COLOUR_1;
|
||||
|
||||
light.Colour = col;
|
||||
|
||||
// finish larger lighting
|
||||
// finish results in larger lighting
|
||||
if (h.Samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH))
|
||||
light.Scale = new Vector2(3);
|
||||
|
||||
@@ -141,7 +160,7 @@ namespace osu.Game.Screens.Menu
|
||||
light.Expire();
|
||||
}
|
||||
|
||||
public partial class Light : CompositeDrawable
|
||||
private partial class Light : CompositeDrawable
|
||||
{
|
||||
private readonly Circle circle;
|
||||
|
||||
@@ -168,12 +187,12 @@ namespace osu.Game.Screens.Menu
|
||||
Anchor = Anchor.Centre,
|
||||
Origin = Anchor.Centre,
|
||||
Size = new Vector2(12),
|
||||
Colour = SeasonalUI.AMBIENT_COLOUR_1,
|
||||
Colour = SeasonalUIConfig.AMBIENT_COLOUR_1,
|
||||
Blending = BlendingParameters.Additive,
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Glow,
|
||||
Colour = SeasonalUI.AMBIENT_COLOUR_2,
|
||||
Colour = SeasonalUIConfig.AMBIENT_COLOUR_2,
|
||||
Radius = 80,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// 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.Audio;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Seasonal
|
||||
{
|
||||
public partial class OsuLogoChristmas : OsuLogo
|
||||
{
|
||||
protected override double BeatSampleVariance => 0.02;
|
||||
|
||||
private Sprite? hat;
|
||||
|
||||
private bool hasHat;
|
||||
|
||||
protected override MenuLogoVisualisation CreateMenuLogoVisualisation() => new SeasonalMenuLogoVisualisation();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(TextureStore textures, AudioManager audio)
|
||||
{
|
||||
LogoElements.Add(hat = new Sprite
|
||||
{
|
||||
BypassAutoSizeAxes = Axes.Both,
|
||||
Alpha = 0,
|
||||
Origin = Anchor.BottomCentre,
|
||||
Scale = new Vector2(-1, 1),
|
||||
Texture = textures.Get(@"Menu/hat"),
|
||||
});
|
||||
|
||||
// override base samples with our preferred ones.
|
||||
SampleDownbeat = SampleBeat = audio.Samples.Get(@"Menu/osu-logo-heartbeat-bell");
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
updateHat();
|
||||
}
|
||||
|
||||
private void updateHat()
|
||||
{
|
||||
if (hat == null)
|
||||
return;
|
||||
|
||||
bool shouldHat = DrawWidth * Scale.X < 400;
|
||||
|
||||
if (shouldHat != hasHat)
|
||||
{
|
||||
hasHat = shouldHat;
|
||||
|
||||
if (hasHat)
|
||||
{
|
||||
hat.Delay(400)
|
||||
.Then()
|
||||
.MoveTo(new Vector2(120, 160))
|
||||
.RotateTo(0)
|
||||
.RotateTo(-20, 500, Easing.OutQuint)
|
||||
.FadeIn(250, Easing.OutQuint);
|
||||
}
|
||||
else
|
||||
{
|
||||
hat.Delay(100)
|
||||
.Then()
|
||||
.MoveToOffset(new Vector2(0, -5), 500, Easing.OutQuint)
|
||||
.FadeOut(500, Easing.OutQuint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// 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.Screens.Menu;
|
||||
|
||||
namespace osu.Game.Seasonal
|
||||
{
|
||||
internal partial class SeasonalMenuLogoVisualisation : MenuLogoVisualisation
|
||||
{
|
||||
protected override void UpdateColour() => Colour = SeasonalUIConfig.AMBIENT_COLOUR_1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// 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.Utils;
|
||||
using osu.Game.Screens.Menu;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Seasonal
|
||||
{
|
||||
public partial class SeasonalMenuSideFlashes : MenuSideFlashes
|
||||
{
|
||||
protected override bool RefreshColoursEveryFlash => true;
|
||||
|
||||
protected override float Intensity => 4;
|
||||
|
||||
protected override Color4 GetBaseColour() => RNG.NextBool() ? SeasonalUIConfig.PRIMARY_COLOUR_1 : SeasonalUIConfig.PRIMARY_COLOUR_2;
|
||||
}
|
||||
}
|
||||
@@ -4,18 +4,21 @@
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Screens
|
||||
namespace osu.Game.Seasonal
|
||||
{
|
||||
public static class SeasonalUI
|
||||
/// <summary>
|
||||
/// General configuration setting for seasonal event adjustments to the game.
|
||||
/// </summary>
|
||||
public static class SeasonalUIConfig
|
||||
{
|
||||
public static readonly bool ENABLED = true;
|
||||
|
||||
public static readonly Color4 PRIMARY_COLOUR_1 = Color4Extensions.FromHex("D32F2F");
|
||||
public static readonly Color4 PRIMARY_COLOUR_1 = Color4Extensions.FromHex(@"D32F2F");
|
||||
|
||||
public static readonly Color4 PRIMARY_COLOUR_2 = Color4Extensions.FromHex("388E3C");
|
||||
public static readonly Color4 PRIMARY_COLOUR_2 = Color4Extensions.FromHex(@"388E3C");
|
||||
|
||||
public static readonly Color4 AMBIENT_COLOUR_1 = Color4Extensions.FromHex("FFC");
|
||||
public static readonly Color4 AMBIENT_COLOUR_1 = Color4Extensions.FromHex(@"FFFFCC");
|
||||
|
||||
public static readonly Color4 AMBIENT_COLOUR_2 = Color4Extensions.FromHex("FFE4B5");
|
||||
public static readonly Color4 AMBIENT_COLOUR_2 = Color4Extensions.FromHex(@"FFE4B5");
|
||||
}
|
||||
}
|
||||
@@ -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.1206.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2024.1219.1" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2024.1224.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2024.1224.0" />
|
||||
<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" />
|
||||
|
||||
+1
-1
@@ -17,6 +17,6 @@
|
||||
<MtouchInterpreter>-all</MtouchInterpreter>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2024.1206.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2024.1224.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// 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 Foundation;
|
||||
using osu.Framework.iOS;
|
||||
|
||||
namespace osu.iOS
|
||||
{
|
||||
[Register("AppDelegate")]
|
||||
public class AppDelegate : GameApplicationDelegate
|
||||
{
|
||||
protected override Framework.Game CreateGame() => new OsuGameIOS();
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,8 @@ namespace osu.iOS
|
||||
{
|
||||
public override Version AssemblyVersion => new Version(NSBundle.MainBundle.InfoDictionary["CFBundleVersion"].ToString());
|
||||
|
||||
public override bool HideUnlicensedContent => true;
|
||||
|
||||
protected override UpdateManager CreateUpdateManager() => new MobileUpdateNotifier();
|
||||
|
||||
protected override BatteryInfo CreateBatteryInfo() => new IOSBatteryInfo();
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
// 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.iOS;
|
||||
using UIKit;
|
||||
|
||||
namespace osu.iOS
|
||||
{
|
||||
public static class Application
|
||||
public static class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
GameApplication.Main(new OsuGameIOS());
|
||||
UIApplication.Main(args, null, typeof(AppDelegate));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user