1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-11 15:47:26 +08:00

Merge branch 'master' into editor-test-play

This commit is contained in:
Bartłomiej Dach 2024-07-03 13:54:47 +02:00
commit e73faaefd2
No known key found for this signature in database
52 changed files with 859 additions and 236 deletions

View File

@ -7,7 +7,6 @@ T:SixLabors.ImageSharp.IDeepCloneable`1;Use osu.Game.Utils.IDeepCloneable<T> ins
M:osu.Framework.Graphics.Sprites.SpriteText.#ctor;Use OsuSpriteText. M:osu.Framework.Graphics.Sprites.SpriteText.#ctor;Use OsuSpriteText.
M:osu.Framework.Bindables.IBindableList`1.GetBoundCopy();Fails on iOS. Use manual ctor + BindTo instead. (see https://github.com/mono/mono/issues/19900) M:osu.Framework.Bindables.IBindableList`1.GetBoundCopy();Fails on iOS. Use manual ctor + BindTo instead. (see https://github.com/mono/mono/issues/19900)
T:NuGet.Packaging.CollectionExtensions;Don't use internal extension methods. T:NuGet.Packaging.CollectionExtensions;Don't use internal extension methods.
M:System.Enum.HasFlag(System.Enum);Use osu.Framework.Extensions.EnumExtensions.HasFlagFast<T>() instead.
M:Realms.IRealmCollection`1.SubscribeForNotifications`1(Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IRealmCollection<T>,NotificationCallbackDelegate<T>) instead. M:Realms.IRealmCollection`1.SubscribeForNotifications`1(Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IRealmCollection<T>,NotificationCallbackDelegate<T>) instead.
M:System.Guid.#ctor;Probably meaning to use Guid.NewGuid() instead. If actually wanting empty, use Guid.Empty. M:System.Guid.#ctor;Probably meaning to use Guid.NewGuid() instead. If actually wanting empty, use Guid.Empty.
M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Linq.IQueryable{``0},Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IQueryable<T>,NotificationCallbackDelegate<T>) instead. M:Realms.CollectionExtensions.SubscribeForNotifications`1(System.Linq.IQueryable{``0},Realms.NotificationCallbackDelegate{``0});Use osu.Game.Database.RealmObjectExtensions.QueryAsyncWithNotifications(IQueryable<T>,NotificationCallbackDelegate<T>) instead.

View File

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

View File

@ -3,7 +3,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
@ -62,43 +61,43 @@ namespace osu.Game.Rulesets.Catch
public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods) public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods)
{ {
if (mods.HasFlagFast(LegacyMods.Nightcore)) if (mods.HasFlag(LegacyMods.Nightcore))
yield return new CatchModNightcore(); yield return new CatchModNightcore();
else if (mods.HasFlagFast(LegacyMods.DoubleTime)) else if (mods.HasFlag(LegacyMods.DoubleTime))
yield return new CatchModDoubleTime(); yield return new CatchModDoubleTime();
if (mods.HasFlagFast(LegacyMods.Perfect)) if (mods.HasFlag(LegacyMods.Perfect))
yield return new CatchModPerfect(); yield return new CatchModPerfect();
else if (mods.HasFlagFast(LegacyMods.SuddenDeath)) else if (mods.HasFlag(LegacyMods.SuddenDeath))
yield return new CatchModSuddenDeath(); yield return new CatchModSuddenDeath();
if (mods.HasFlagFast(LegacyMods.Cinema)) if (mods.HasFlag(LegacyMods.Cinema))
yield return new CatchModCinema(); yield return new CatchModCinema();
else if (mods.HasFlagFast(LegacyMods.Autoplay)) else if (mods.HasFlag(LegacyMods.Autoplay))
yield return new CatchModAutoplay(); yield return new CatchModAutoplay();
if (mods.HasFlagFast(LegacyMods.Easy)) if (mods.HasFlag(LegacyMods.Easy))
yield return new CatchModEasy(); yield return new CatchModEasy();
if (mods.HasFlagFast(LegacyMods.Flashlight)) if (mods.HasFlag(LegacyMods.Flashlight))
yield return new CatchModFlashlight(); yield return new CatchModFlashlight();
if (mods.HasFlagFast(LegacyMods.HalfTime)) if (mods.HasFlag(LegacyMods.HalfTime))
yield return new CatchModHalfTime(); yield return new CatchModHalfTime();
if (mods.HasFlagFast(LegacyMods.HardRock)) if (mods.HasFlag(LegacyMods.HardRock))
yield return new CatchModHardRock(); yield return new CatchModHardRock();
if (mods.HasFlagFast(LegacyMods.Hidden)) if (mods.HasFlag(LegacyMods.Hidden))
yield return new CatchModHidden(); yield return new CatchModHidden();
if (mods.HasFlagFast(LegacyMods.NoFail)) if (mods.HasFlag(LegacyMods.NoFail))
yield return new CatchModNoFail(); yield return new CatchModNoFail();
if (mods.HasFlagFast(LegacyMods.Relax)) if (mods.HasFlag(LegacyMods.Relax))
yield return new CatchModRelax(); yield return new CatchModRelax();
if (mods.HasFlagFast(LegacyMods.ScoreV2)) if (mods.HasFlag(LegacyMods.ScoreV2))
yield return new ModScoreV2(); yield return new ModScoreV2();
} }

View File

@ -5,7 +5,6 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
@ -121,7 +120,7 @@ namespace osu.Game.Rulesets.Catch.Edit
result.ScreenSpacePosition.X = screenSpacePosition.X; result.ScreenSpacePosition.X = screenSpacePosition.X;
if (snapType.HasFlagFast(SnapType.RelativeGrids)) if (snapType.HasFlag(SnapType.RelativeGrids))
{ {
if (distanceSnapGrid.IsPresent && distanceSnapGrid.GetSnappedPosition(result.ScreenSpacePosition) is SnapResult snapResult && if (distanceSnapGrid.IsPresent && distanceSnapGrid.GetSnappedPosition(result.ScreenSpacePosition) is SnapResult snapResult &&
Vector2.Distance(snapResult.ScreenSpacePosition, result.ScreenSpacePosition) < distance_snap_radius) Vector2.Distance(snapResult.ScreenSpacePosition, result.ScreenSpacePosition) < distance_snap_radius)

View File

@ -8,7 +8,6 @@ using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
@ -100,7 +99,7 @@ namespace osu.Game.Rulesets.Mania.Tests
} }
private bool verifyAnchors(DrawableHitObject hitObject, Anchor expectedAnchor) private bool verifyAnchors(DrawableHitObject hitObject, Anchor expectedAnchor)
=> hitObject.Anchor.HasFlagFast(expectedAnchor) && hitObject.Origin.HasFlagFast(expectedAnchor); => hitObject.Anchor.HasFlag(expectedAnchor) && hitObject.Origin.HasFlag(expectedAnchor);
private bool verifyAnchors(DrawableHoldNote holdNote, Anchor expectedAnchor) private bool verifyAnchors(DrawableHoldNote holdNote, Anchor expectedAnchor)
=> verifyAnchors((DrawableHitObject)holdNote, expectedAnchor) && holdNote.NestedHitObjects.All(n => verifyAnchors(n, expectedAnchor)); => verifyAnchors((DrawableHitObject)holdNote, expectedAnchor) && holdNote.NestedHitObjects.All(n => verifyAnchors(n, expectedAnchor));

View File

@ -4,7 +4,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Extensions.EnumExtensions;
using osuTK; using osuTK;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
@ -79,7 +78,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
else else
convertType |= PatternType.LowProbability; convertType |= PatternType.LowProbability;
if (!convertType.HasFlagFast(PatternType.KeepSingle)) if (!convertType.HasFlag(PatternType.KeepSingle))
{ {
if (HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH) && TotalColumns != 8) if (HitObject.Samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH) && TotalColumns != 8)
convertType |= PatternType.Mirror; convertType |= PatternType.Mirror;
@ -102,7 +101,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int lastColumn = PreviousPattern.HitObjects.FirstOrDefault()?.Column ?? 0; int lastColumn = PreviousPattern.HitObjects.FirstOrDefault()?.Column ?? 0;
if (convertType.HasFlagFast(PatternType.Reverse) && PreviousPattern.HitObjects.Any()) if (convertType.HasFlag(PatternType.Reverse) && PreviousPattern.HitObjects.Any())
{ {
// Generate a new pattern by copying the last hit objects in reverse-column order // Generate a new pattern by copying the last hit objects in reverse-column order
for (int i = RandomStart; i < TotalColumns; i++) for (int i = RandomStart; i < TotalColumns; i++)
@ -114,7 +113,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
return pattern; return pattern;
} }
if (convertType.HasFlagFast(PatternType.Cycle) && PreviousPattern.HitObjects.Count() == 1 if (convertType.HasFlag(PatternType.Cycle) && PreviousPattern.HitObjects.Count() == 1
// If we convert to 7K + 1, let's not overload the special key // If we convert to 7K + 1, let's not overload the special key
&& (TotalColumns != 8 || lastColumn != 0) && (TotalColumns != 8 || lastColumn != 0)
// Make sure the last column was not the centre column // Make sure the last column was not the centre column
@ -127,7 +126,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
return pattern; return pattern;
} }
if (convertType.HasFlagFast(PatternType.ForceStack) && PreviousPattern.HitObjects.Any()) if (convertType.HasFlag(PatternType.ForceStack) && PreviousPattern.HitObjects.Any())
{ {
// Generate a new pattern by placing on the already filled columns // Generate a new pattern by placing on the already filled columns
for (int i = RandomStart; i < TotalColumns; i++) for (int i = RandomStart; i < TotalColumns; i++)
@ -141,7 +140,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (PreviousPattern.HitObjects.Count() == 1) if (PreviousPattern.HitObjects.Count() == 1)
{ {
if (convertType.HasFlagFast(PatternType.Stair)) if (convertType.HasFlag(PatternType.Stair))
{ {
// Generate a new pattern by placing on the next column, cycling back to the start if there is no "next" // Generate a new pattern by placing on the next column, cycling back to the start if there is no "next"
int targetColumn = lastColumn + 1; int targetColumn = lastColumn + 1;
@ -152,7 +151,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
return pattern; return pattern;
} }
if (convertType.HasFlagFast(PatternType.ReverseStair)) if (convertType.HasFlag(PatternType.ReverseStair))
{ {
// Generate a new pattern by placing on the previous column, cycling back to the end if there is no "previous" // Generate a new pattern by placing on the previous column, cycling back to the end if there is no "previous"
int targetColumn = lastColumn - 1; int targetColumn = lastColumn - 1;
@ -164,10 +163,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
} }
} }
if (convertType.HasFlagFast(PatternType.KeepSingle)) if (convertType.HasFlag(PatternType.KeepSingle))
return generateRandomNotes(1); return generateRandomNotes(1);
if (convertType.HasFlagFast(PatternType.Mirror)) if (convertType.HasFlag(PatternType.Mirror))
{ {
if (ConversionDifficulty > 6.5) if (ConversionDifficulty > 6.5)
return generateRandomPatternWithMirrored(0.12, 0.38, 0.12); return generateRandomPatternWithMirrored(0.12, 0.38, 0.12);
@ -179,7 +178,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (ConversionDifficulty > 6.5) if (ConversionDifficulty > 6.5)
{ {
if (convertType.HasFlagFast(PatternType.LowProbability)) if (convertType.HasFlag(PatternType.LowProbability))
return generateRandomPattern(0.78, 0.42, 0, 0); return generateRandomPattern(0.78, 0.42, 0, 0);
return generateRandomPattern(1, 0.62, 0, 0); return generateRandomPattern(1, 0.62, 0, 0);
@ -187,7 +186,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (ConversionDifficulty > 4) if (ConversionDifficulty > 4)
{ {
if (convertType.HasFlagFast(PatternType.LowProbability)) if (convertType.HasFlag(PatternType.LowProbability))
return generateRandomPattern(0.35, 0.08, 0, 0); return generateRandomPattern(0.35, 0.08, 0, 0);
return generateRandomPattern(0.52, 0.15, 0, 0); return generateRandomPattern(0.52, 0.15, 0, 0);
@ -195,7 +194,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (ConversionDifficulty > 2) if (ConversionDifficulty > 2)
{ {
if (convertType.HasFlagFast(PatternType.LowProbability)) if (convertType.HasFlag(PatternType.LowProbability))
return generateRandomPattern(0.18, 0, 0, 0); return generateRandomPattern(0.18, 0, 0, 0);
return generateRandomPattern(0.45, 0, 0, 0); return generateRandomPattern(0.45, 0, 0, 0);
@ -208,9 +207,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
foreach (var obj in p.HitObjects) foreach (var obj in p.HitObjects)
{ {
if (convertType.HasFlagFast(PatternType.Stair) && obj.Column == TotalColumns - 1) if (convertType.HasFlag(PatternType.Stair) && obj.Column == TotalColumns - 1)
StairType = PatternType.ReverseStair; StairType = PatternType.ReverseStair;
if (convertType.HasFlagFast(PatternType.ReverseStair) && obj.Column == RandomStart) if (convertType.HasFlag(PatternType.ReverseStair) && obj.Column == RandomStart)
StairType = PatternType.Stair; StairType = PatternType.Stair;
} }
@ -230,7 +229,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{ {
var pattern = new Pattern(); var pattern = new Pattern();
bool allowStacking = !convertType.HasFlagFast(PatternType.ForceNotStack); bool allowStacking = !convertType.HasFlag(PatternType.ForceNotStack);
if (!allowStacking) if (!allowStacking)
noteCount = Math.Min(noteCount, TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects); noteCount = Math.Min(noteCount, TotalColumns - RandomStart - PreviousPattern.ColumnWithObjects);
@ -250,7 +249,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int getNextColumn(int last) int getNextColumn(int last)
{ {
if (convertType.HasFlagFast(PatternType.Gathered)) if (convertType.HasFlag(PatternType.Gathered))
{ {
last++; last++;
if (last == TotalColumns) if (last == TotalColumns)
@ -297,7 +296,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns> /// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
private Pattern generateRandomPatternWithMirrored(double centreProbability, double p2, double p3) private Pattern generateRandomPatternWithMirrored(double centreProbability, double p2, double p3)
{ {
if (convertType.HasFlagFast(PatternType.ForceNotStack)) if (convertType.HasFlag(PatternType.ForceNotStack))
return generateRandomPattern(1 / 2f + p2 / 2, p2, (p2 + p3) / 2, p3); return generateRandomPattern(1 / 2f + p2 / 2, p2, (p2 + p3) / 2, p3);
var pattern = new Pattern(); var pattern = new Pattern();

View File

@ -7,7 +7,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using osu.Framework.Extensions.EnumExtensions;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
@ -139,7 +138,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (ConversionDifficulty > 6.5) if (ConversionDifficulty > 6.5)
{ {
if (convertType.HasFlagFast(PatternType.LowProbability)) if (convertType.HasFlag(PatternType.LowProbability))
return generateNRandomNotes(StartTime, 0.78, 0.3, 0); return generateNRandomNotes(StartTime, 0.78, 0.3, 0);
return generateNRandomNotes(StartTime, 0.85, 0.36, 0.03); return generateNRandomNotes(StartTime, 0.85, 0.36, 0.03);
@ -147,7 +146,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (ConversionDifficulty > 4) if (ConversionDifficulty > 4)
{ {
if (convertType.HasFlagFast(PatternType.LowProbability)) if (convertType.HasFlag(PatternType.LowProbability))
return generateNRandomNotes(StartTime, 0.43, 0.08, 0); return generateNRandomNotes(StartTime, 0.43, 0.08, 0);
return generateNRandomNotes(StartTime, 0.56, 0.18, 0); return generateNRandomNotes(StartTime, 0.56, 0.18, 0);
@ -155,13 +154,13 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
if (ConversionDifficulty > 2.5) if (ConversionDifficulty > 2.5)
{ {
if (convertType.HasFlagFast(PatternType.LowProbability)) if (convertType.HasFlag(PatternType.LowProbability))
return generateNRandomNotes(StartTime, 0.3, 0, 0); return generateNRandomNotes(StartTime, 0.3, 0, 0);
return generateNRandomNotes(StartTime, 0.37, 0.08, 0); return generateNRandomNotes(StartTime, 0.37, 0.08, 0);
} }
if (convertType.HasFlagFast(PatternType.LowProbability)) if (convertType.HasFlag(PatternType.LowProbability))
return generateNRandomNotes(StartTime, 0.17, 0, 0); return generateNRandomNotes(StartTime, 0.17, 0, 0);
return generateNRandomNotes(StartTime, 0.27, 0, 0); return generateNRandomNotes(StartTime, 0.27, 0, 0);
@ -219,7 +218,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
var pattern = new Pattern(); var pattern = new Pattern();
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
if (convertType.HasFlagFast(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns) if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
nextColumn = FindAvailableColumn(nextColumn, PreviousPattern); nextColumn = FindAvailableColumn(nextColumn, PreviousPattern);
int lastColumn = nextColumn; int lastColumn = nextColumn;
@ -371,7 +370,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
static bool isDoubleSample(HitSampleInfo sample) => sample.Name == HitSampleInfo.HIT_CLAP || sample.Name == HitSampleInfo.HIT_FINISH; static bool isDoubleSample(HitSampleInfo sample) => sample.Name == HitSampleInfo.HIT_CLAP || sample.Name == HitSampleInfo.HIT_FINISH;
bool canGenerateTwoNotes = !convertType.HasFlagFast(PatternType.LowProbability); bool canGenerateTwoNotes = !convertType.HasFlag(PatternType.LowProbability);
canGenerateTwoNotes &= HitObject.Samples.Any(isDoubleSample) || sampleInfoListAt(StartTime).Any(isDoubleSample); canGenerateTwoNotes &= HitObject.Samples.Any(isDoubleSample) || sampleInfoListAt(StartTime).Any(isDoubleSample);
if (canGenerateTwoNotes) if (canGenerateTwoNotes)
@ -404,7 +403,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int endTime = startTime + SegmentDuration * SpanCount; int endTime = startTime + SegmentDuration * SpanCount;
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
if (convertType.HasFlagFast(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns) if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
nextColumn = FindAvailableColumn(nextColumn, PreviousPattern); nextColumn = FindAvailableColumn(nextColumn, PreviousPattern);
for (int i = 0; i < columnRepeat; i++) for (int i = 0; i < columnRepeat; i++)
@ -433,7 +432,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
var pattern = new Pattern(); var pattern = new Pattern();
int holdColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); int holdColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
if (convertType.HasFlagFast(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns) if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
holdColumn = FindAvailableColumn(holdColumn, PreviousPattern); holdColumn = FindAvailableColumn(holdColumn, PreviousPattern);
// Create the hold note // Create the hold note

View File

@ -4,7 +4,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
@ -89,79 +88,79 @@ namespace osu.Game.Rulesets.Mania
public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods) public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods)
{ {
if (mods.HasFlagFast(LegacyMods.Nightcore)) if (mods.HasFlag(LegacyMods.Nightcore))
yield return new ManiaModNightcore(); yield return new ManiaModNightcore();
else if (mods.HasFlagFast(LegacyMods.DoubleTime)) else if (mods.HasFlag(LegacyMods.DoubleTime))
yield return new ManiaModDoubleTime(); yield return new ManiaModDoubleTime();
if (mods.HasFlagFast(LegacyMods.Perfect)) if (mods.HasFlag(LegacyMods.Perfect))
yield return new ManiaModPerfect(); yield return new ManiaModPerfect();
else if (mods.HasFlagFast(LegacyMods.SuddenDeath)) else if (mods.HasFlag(LegacyMods.SuddenDeath))
yield return new ManiaModSuddenDeath(); yield return new ManiaModSuddenDeath();
if (mods.HasFlagFast(LegacyMods.Cinema)) if (mods.HasFlag(LegacyMods.Cinema))
yield return new ManiaModCinema(); yield return new ManiaModCinema();
else if (mods.HasFlagFast(LegacyMods.Autoplay)) else if (mods.HasFlag(LegacyMods.Autoplay))
yield return new ManiaModAutoplay(); yield return new ManiaModAutoplay();
if (mods.HasFlagFast(LegacyMods.Easy)) if (mods.HasFlag(LegacyMods.Easy))
yield return new ManiaModEasy(); yield return new ManiaModEasy();
if (mods.HasFlagFast(LegacyMods.FadeIn)) if (mods.HasFlag(LegacyMods.FadeIn))
yield return new ManiaModFadeIn(); yield return new ManiaModFadeIn();
if (mods.HasFlagFast(LegacyMods.Flashlight)) if (mods.HasFlag(LegacyMods.Flashlight))
yield return new ManiaModFlashlight(); yield return new ManiaModFlashlight();
if (mods.HasFlagFast(LegacyMods.HalfTime)) if (mods.HasFlag(LegacyMods.HalfTime))
yield return new ManiaModHalfTime(); yield return new ManiaModHalfTime();
if (mods.HasFlagFast(LegacyMods.HardRock)) if (mods.HasFlag(LegacyMods.HardRock))
yield return new ManiaModHardRock(); yield return new ManiaModHardRock();
if (mods.HasFlagFast(LegacyMods.Hidden)) if (mods.HasFlag(LegacyMods.Hidden))
yield return new ManiaModHidden(); yield return new ManiaModHidden();
if (mods.HasFlagFast(LegacyMods.Key1)) if (mods.HasFlag(LegacyMods.Key1))
yield return new ManiaModKey1(); yield return new ManiaModKey1();
if (mods.HasFlagFast(LegacyMods.Key2)) if (mods.HasFlag(LegacyMods.Key2))
yield return new ManiaModKey2(); yield return new ManiaModKey2();
if (mods.HasFlagFast(LegacyMods.Key3)) if (mods.HasFlag(LegacyMods.Key3))
yield return new ManiaModKey3(); yield return new ManiaModKey3();
if (mods.HasFlagFast(LegacyMods.Key4)) if (mods.HasFlag(LegacyMods.Key4))
yield return new ManiaModKey4(); yield return new ManiaModKey4();
if (mods.HasFlagFast(LegacyMods.Key5)) if (mods.HasFlag(LegacyMods.Key5))
yield return new ManiaModKey5(); yield return new ManiaModKey5();
if (mods.HasFlagFast(LegacyMods.Key6)) if (mods.HasFlag(LegacyMods.Key6))
yield return new ManiaModKey6(); yield return new ManiaModKey6();
if (mods.HasFlagFast(LegacyMods.Key7)) if (mods.HasFlag(LegacyMods.Key7))
yield return new ManiaModKey7(); yield return new ManiaModKey7();
if (mods.HasFlagFast(LegacyMods.Key8)) if (mods.HasFlag(LegacyMods.Key8))
yield return new ManiaModKey8(); yield return new ManiaModKey8();
if (mods.HasFlagFast(LegacyMods.Key9)) if (mods.HasFlag(LegacyMods.Key9))
yield return new ManiaModKey9(); yield return new ManiaModKey9();
if (mods.HasFlagFast(LegacyMods.KeyCoop)) if (mods.HasFlag(LegacyMods.KeyCoop))
yield return new ManiaModDualStages(); yield return new ManiaModDualStages();
if (mods.HasFlagFast(LegacyMods.NoFail)) if (mods.HasFlag(LegacyMods.NoFail))
yield return new ManiaModNoFail(); yield return new ManiaModNoFail();
if (mods.HasFlagFast(LegacyMods.Random)) if (mods.HasFlag(LegacyMods.Random))
yield return new ManiaModRandom(); yield return new ManiaModRandom();
if (mods.HasFlagFast(LegacyMods.Mirror)) if (mods.HasFlag(LegacyMods.Mirror))
yield return new ManiaModMirror(); yield return new ManiaModMirror();
if (mods.HasFlagFast(LegacyMods.ScoreV2)) if (mods.HasFlag(LegacyMods.ScoreV2))
yield return new ModScoreV2(); yield return new ModScoreV2();
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System;
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Testing; using osu.Framework.Testing;
@ -160,6 +161,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
return grid switch return grid switch
{ {
RectangularPositionSnapGrid rectangular => rectangular.StartPosition.Value + GeometryUtils.RotateVector(rectangular.Spacing.Value, -rectangular.GridLineRotation.Value), RectangularPositionSnapGrid rectangular => rectangular.StartPosition.Value + GeometryUtils.RotateVector(rectangular.Spacing.Value, -rectangular.GridLineRotation.Value),
TriangularPositionSnapGrid triangular => triangular.StartPosition.Value + GeometryUtils.RotateVector(new Vector2(triangular.Spacing.Value / 2, triangular.Spacing.Value / 2 * MathF.Sqrt(3)), -triangular.GridLineRotation.Value),
CircularPositionSnapGrid circular => circular.StartPosition.Value + GeometryUtils.RotateVector(new Vector2(circular.Spacing.Value, 0), -45),
_ => Vector2.Zero _ => Vector2.Zero
}; };
} }

View File

@ -55,7 +55,21 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private BindableBeatDivisor beatDivisor { get; set; } private BindableBeatDivisor beatDivisor { get; set; }
public override Quad SelectionQuad => BodyPiece.ScreenSpaceDrawQuad; public override Quad SelectionQuad
{
get
{
var result = BodyPiece.ScreenSpaceDrawQuad.AABBFloat;
if (ControlPointVisualiser != null)
{
foreach (var piece in ControlPointVisualiser.Pieces)
result = RectangleF.Union(result, piece.ScreenSpaceDrawQuad.AABBFloat);
}
return result;
}
}
private readonly BindableList<PathControlPoint> controlPoints = new BindableList<PathControlPoint>(); private readonly BindableList<PathControlPoint> controlPoints = new BindableList<PathControlPoint>();
private readonly IBindable<int> pathVersion = new Bindable<int>(); private readonly IBindable<int> pathVersion = new Bindable<int>();

View File

@ -1,17 +1,24 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Input.Bindings; using osu.Game.Input.Bindings;
using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Osu.UI;
using osu.Game.Screens.Edit; using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Components.RadioButtons;
using osuTK; using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Edit namespace osu.Game.Rulesets.Osu.Edit
{ {
@ -20,6 +27,9 @@ namespace osu.Game.Rulesets.Osu.Edit
[Resolved] [Resolved]
private EditorBeatmap editorBeatmap { get; set; } = null!; private EditorBeatmap editorBeatmap { get; set; } = null!;
[Resolved]
private IExpandingContainer? expandingContainer { get; set; }
/// <summary> /// <summary>
/// X position of the grid's origin. /// X position of the grid's origin.
/// </summary> /// </summary>
@ -55,8 +65,8 @@ namespace osu.Game.Rulesets.Osu.Edit
/// </summary> /// </summary>
public BindableFloat GridLinesRotation { get; } = new BindableFloat(0f) public BindableFloat GridLinesRotation { get; } = new BindableFloat(0f)
{ {
MinValue = -45f, MinValue = -180f,
MaxValue = 45f, MaxValue = 180f,
Precision = 1f Precision = 1f
}; };
@ -72,10 +82,13 @@ namespace osu.Game.Rulesets.Osu.Edit
/// </summary> /// </summary>
public Bindable<Vector2> SpacingVector { get; } = new Bindable<Vector2>(); public Bindable<Vector2> SpacingVector { get; } = new Bindable<Vector2>();
public Bindable<PositionSnapGridType> GridType { get; } = new Bindable<PositionSnapGridType>();
private ExpandableSlider<float> startPositionXSlider = null!; private ExpandableSlider<float> startPositionXSlider = null!;
private ExpandableSlider<float> startPositionYSlider = null!; private ExpandableSlider<float> startPositionYSlider = null!;
private ExpandableSlider<float> spacingSlider = null!; private ExpandableSlider<float> spacingSlider = null!;
private ExpandableSlider<float> gridLinesRotationSlider = null!; private ExpandableSlider<float> gridLinesRotationSlider = null!;
private EditorRadioButtonCollection gridTypeButtons = null!;
public OsuGridToolboxGroup() public OsuGridToolboxGroup()
: base("grid") : base("grid")
@ -109,6 +122,31 @@ namespace osu.Game.Rulesets.Osu.Edit
Current = GridLinesRotation, Current = GridLinesRotation,
KeyboardStep = 1, KeyboardStep = 1,
}, },
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Spacing = new Vector2(0f, 10f),
Children = new Drawable[]
{
gridTypeButtons = new EditorRadioButtonCollection
{
RelativeSizeAxes = Axes.X,
Items = new[]
{
new RadioButton("Square",
() => GridType.Value = PositionSnapGridType.Square,
() => new SpriteIcon { Icon = FontAwesome.Regular.Square }),
new RadioButton("Triangle",
() => GridType.Value = PositionSnapGridType.Triangle,
() => new OutlineTriangle(true, 20)),
new RadioButton("Circle",
() => GridType.Value = PositionSnapGridType.Circle,
() => new SpriteIcon { Icon = FontAwesome.Regular.Circle }),
}
},
}
},
}; };
Spacing.Value = editorBeatmap.BeatmapInfo.GridSize; Spacing.Value = editorBeatmap.BeatmapInfo.GridSize;
@ -118,6 +156,8 @@ namespace osu.Game.Rulesets.Osu.Edit
{ {
base.LoadComplete(); base.LoadComplete();
gridTypeButtons.Items.First().Select();
StartPositionX.BindValueChanged(x => StartPositionX.BindValueChanged(x =>
{ {
startPositionXSlider.ContractedLabelText = $"X: {x.NewValue:N0}"; startPositionXSlider.ContractedLabelText = $"X: {x.NewValue:N0}";
@ -145,6 +185,32 @@ namespace osu.Game.Rulesets.Osu.Edit
gridLinesRotationSlider.ContractedLabelText = $"R: {rotation.NewValue:#,0.##}"; gridLinesRotationSlider.ContractedLabelText = $"R: {rotation.NewValue:#,0.##}";
gridLinesRotationSlider.ExpandedLabelText = $"Rotation: {rotation.NewValue:#,0.##}"; gridLinesRotationSlider.ExpandedLabelText = $"Rotation: {rotation.NewValue:#,0.##}";
}, true); }, true);
expandingContainer?.Expanded.BindValueChanged(v =>
{
gridTypeButtons.FadeTo(v.NewValue ? 1f : 0f, 500, Easing.OutQuint);
gridTypeButtons.BypassAutoSizeAxes = !v.NewValue ? Axes.Y : Axes.None;
}, true);
GridType.BindValueChanged(v =>
{
GridLinesRotation.Disabled = v.NewValue == PositionSnapGridType.Circle;
switch (v.NewValue)
{
case PositionSnapGridType.Square:
GridLinesRotation.Value = ((GridLinesRotation.Value + 405) % 90) - 45;
GridLinesRotation.MinValue = -45;
GridLinesRotation.MaxValue = 45;
break;
case PositionSnapGridType.Triangle:
GridLinesRotation.Value = ((GridLinesRotation.Value + 390) % 60) - 30;
GridLinesRotation.MinValue = -30;
GridLinesRotation.MaxValue = 30;
break;
}
}, true);
} }
private void nextGridSize() private void nextGridSize()
@ -167,5 +233,42 @@ namespace osu.Game.Rulesets.Osu.Edit
public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e) public void OnReleased(KeyBindingReleaseEvent<GlobalAction> e)
{ {
} }
public partial class OutlineTriangle : BufferedContainer
{
public OutlineTriangle(bool outlineOnly, float size)
: base(cachedFrameBuffer: true)
{
Size = new Vector2(size);
InternalChildren = new Drawable[]
{
new EquilateralTriangle { RelativeSizeAxes = Axes.Both },
};
if (outlineOnly)
{
AddInternal(new EquilateralTriangle
{
Anchor = Anchor.TopCentre,
Origin = Anchor.Centre,
RelativePositionAxes = Axes.Y,
Y = 0.48f,
Colour = Color4.Black,
Size = new Vector2(size - 7),
Blending = BlendingParameters.None,
});
}
Blending = BlendingParameters.Additive;
}
}
}
public enum PositionSnapGridType
{
Square,
Triangle,
Circle,
} }
} }

View File

@ -10,7 +10,6 @@ using System.Text.RegularExpressions;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Caching; using osu.Framework.Caching;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
@ -101,7 +100,7 @@ namespace osu.Game.Rulesets.Osu.Edit
// we may be entering the screen with a selection already active // we may be entering the screen with a selection already active
updateDistanceSnapGrid(); updateDistanceSnapGrid();
updatePositionSnapGrid(); OsuGridToolboxGroup.GridType.BindValueChanged(updatePositionSnapGrid, true);
RightToolbox.AddRange(new Drawable[] RightToolbox.AddRange(new Drawable[]
{ {
@ -116,18 +115,45 @@ namespace osu.Game.Rulesets.Osu.Edit
); );
} }
private void updatePositionSnapGrid() private void updatePositionSnapGrid(ValueChangedEvent<PositionSnapGridType> obj)
{ {
if (positionSnapGrid != null) if (positionSnapGrid != null)
LayerBelowRuleset.Remove(positionSnapGrid, true); LayerBelowRuleset.Remove(positionSnapGrid, true);
var rectangularPositionSnapGrid = new RectangularPositionSnapGrid(); switch (obj.NewValue)
{
case PositionSnapGridType.Square:
var rectangularPositionSnapGrid = new RectangularPositionSnapGrid();
rectangularPositionSnapGrid.StartPosition.BindTo(OsuGridToolboxGroup.StartPosition); rectangularPositionSnapGrid.Spacing.BindTo(OsuGridToolboxGroup.SpacingVector);
rectangularPositionSnapGrid.Spacing.BindTo(OsuGridToolboxGroup.SpacingVector); rectangularPositionSnapGrid.GridLineRotation.BindTo(OsuGridToolboxGroup.GridLinesRotation);
rectangularPositionSnapGrid.GridLineRotation.BindTo(OsuGridToolboxGroup.GridLinesRotation);
positionSnapGrid = rectangularPositionSnapGrid; positionSnapGrid = rectangularPositionSnapGrid;
break;
case PositionSnapGridType.Triangle:
var triangularPositionSnapGrid = new TriangularPositionSnapGrid();
triangularPositionSnapGrid.Spacing.BindTo(OsuGridToolboxGroup.Spacing);
triangularPositionSnapGrid.GridLineRotation.BindTo(OsuGridToolboxGroup.GridLinesRotation);
positionSnapGrid = triangularPositionSnapGrid;
break;
case PositionSnapGridType.Circle:
var circularPositionSnapGrid = new CircularPositionSnapGrid();
circularPositionSnapGrid.Spacing.BindTo(OsuGridToolboxGroup.Spacing);
positionSnapGrid = circularPositionSnapGrid;
break;
default:
throw new ArgumentOutOfRangeException(nameof(OsuGridToolboxGroup.GridType), OsuGridToolboxGroup.GridType, "Unsupported grid type.");
}
// Bind the start position to the toolbox sliders.
positionSnapGrid.StartPosition.BindTo(OsuGridToolboxGroup.StartPosition);
positionSnapGrid.RelativeSizeAxes = Axes.Both; positionSnapGrid.RelativeSizeAxes = Axes.Both;
LayerBelowRuleset.Add(positionSnapGrid); LayerBelowRuleset.Add(positionSnapGrid);
@ -194,7 +220,7 @@ namespace osu.Game.Rulesets.Osu.Edit
public override SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All) public override SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All)
{ {
if (snapType.HasFlagFast(SnapType.NearbyObjects) && snapToVisibleBlueprints(screenSpacePosition, out var snapResult)) if (snapType.HasFlag(SnapType.NearbyObjects) && snapToVisibleBlueprints(screenSpacePosition, out var snapResult))
{ {
// In the case of snapping to nearby objects, a time value is not provided. // In the case of snapping to nearby objects, a time value is not provided.
// This matches the stable editor (which also uses current time), but with the introduction of time-snapping distance snap // This matches the stable editor (which also uses current time), but with the introduction of time-snapping distance snap
@ -204,7 +230,7 @@ namespace osu.Game.Rulesets.Osu.Edit
// We want to ensure that in this particular case, the time-snapping component of distance snap is still applied. // We want to ensure that in this particular case, the time-snapping component of distance snap is still applied.
// The easiest way to ensure this is to attempt application of distance snap after a nearby object is found, and copy over // The easiest way to ensure this is to attempt application of distance snap after a nearby object is found, and copy over
// the time value if the proposed positions are roughly the same. // the time value if the proposed positions are roughly the same.
if (snapType.HasFlagFast(SnapType.RelativeGrids) && DistanceSnapProvider.DistanceSnapToggle.Value == TernaryState.True && distanceSnapGrid != null) if (snapType.HasFlag(SnapType.RelativeGrids) && DistanceSnapProvider.DistanceSnapToggle.Value == TernaryState.True && distanceSnapGrid != null)
{ {
(Vector2 distanceSnappedPosition, double distanceSnappedTime) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(snapResult.ScreenSpacePosition)); (Vector2 distanceSnappedPosition, double distanceSnappedTime) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(snapResult.ScreenSpacePosition));
if (Precision.AlmostEquals(distanceSnapGrid.ToScreenSpace(distanceSnappedPosition), snapResult.ScreenSpacePosition, 1)) if (Precision.AlmostEquals(distanceSnapGrid.ToScreenSpace(distanceSnappedPosition), snapResult.ScreenSpacePosition, 1))
@ -216,7 +242,7 @@ namespace osu.Game.Rulesets.Osu.Edit
SnapResult result = base.FindSnappedPositionAndTime(screenSpacePosition, snapType); SnapResult result = base.FindSnappedPositionAndTime(screenSpacePosition, snapType);
if (snapType.HasFlagFast(SnapType.RelativeGrids)) if (snapType.HasFlag(SnapType.RelativeGrids))
{ {
if (DistanceSnapProvider.DistanceSnapToggle.Value == TernaryState.True && distanceSnapGrid != null) if (DistanceSnapProvider.DistanceSnapToggle.Value == TernaryState.True && distanceSnapGrid != null)
{ {
@ -227,7 +253,7 @@ namespace osu.Game.Rulesets.Osu.Edit
} }
} }
if (snapType.HasFlagFast(SnapType.GlobalGrids)) if (snapType.HasFlag(SnapType.GlobalGrids))
{ {
if (rectangularGridSnapToggle.Value == TernaryState.True) if (rectangularGridSnapToggle.Value == TernaryState.True)
{ {

View File

@ -4,7 +4,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
@ -70,55 +69,55 @@ namespace osu.Game.Rulesets.Osu
public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods) public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods)
{ {
if (mods.HasFlagFast(LegacyMods.Nightcore)) if (mods.HasFlag(LegacyMods.Nightcore))
yield return new OsuModNightcore(); yield return new OsuModNightcore();
else if (mods.HasFlagFast(LegacyMods.DoubleTime)) else if (mods.HasFlag(LegacyMods.DoubleTime))
yield return new OsuModDoubleTime(); yield return new OsuModDoubleTime();
if (mods.HasFlagFast(LegacyMods.Perfect)) if (mods.HasFlag(LegacyMods.Perfect))
yield return new OsuModPerfect(); yield return new OsuModPerfect();
else if (mods.HasFlagFast(LegacyMods.SuddenDeath)) else if (mods.HasFlag(LegacyMods.SuddenDeath))
yield return new OsuModSuddenDeath(); yield return new OsuModSuddenDeath();
if (mods.HasFlagFast(LegacyMods.Autopilot)) if (mods.HasFlag(LegacyMods.Autopilot))
yield return new OsuModAutopilot(); yield return new OsuModAutopilot();
if (mods.HasFlagFast(LegacyMods.Cinema)) if (mods.HasFlag(LegacyMods.Cinema))
yield return new OsuModCinema(); yield return new OsuModCinema();
else if (mods.HasFlagFast(LegacyMods.Autoplay)) else if (mods.HasFlag(LegacyMods.Autoplay))
yield return new OsuModAutoplay(); yield return new OsuModAutoplay();
if (mods.HasFlagFast(LegacyMods.Easy)) if (mods.HasFlag(LegacyMods.Easy))
yield return new OsuModEasy(); yield return new OsuModEasy();
if (mods.HasFlagFast(LegacyMods.Flashlight)) if (mods.HasFlag(LegacyMods.Flashlight))
yield return new OsuModFlashlight(); yield return new OsuModFlashlight();
if (mods.HasFlagFast(LegacyMods.HalfTime)) if (mods.HasFlag(LegacyMods.HalfTime))
yield return new OsuModHalfTime(); yield return new OsuModHalfTime();
if (mods.HasFlagFast(LegacyMods.HardRock)) if (mods.HasFlag(LegacyMods.HardRock))
yield return new OsuModHardRock(); yield return new OsuModHardRock();
if (mods.HasFlagFast(LegacyMods.Hidden)) if (mods.HasFlag(LegacyMods.Hidden))
yield return new OsuModHidden(); yield return new OsuModHidden();
if (mods.HasFlagFast(LegacyMods.NoFail)) if (mods.HasFlag(LegacyMods.NoFail))
yield return new OsuModNoFail(); yield return new OsuModNoFail();
if (mods.HasFlagFast(LegacyMods.Relax)) if (mods.HasFlag(LegacyMods.Relax))
yield return new OsuModRelax(); yield return new OsuModRelax();
if (mods.HasFlagFast(LegacyMods.SpunOut)) if (mods.HasFlag(LegacyMods.SpunOut))
yield return new OsuModSpunOut(); yield return new OsuModSpunOut();
if (mods.HasFlagFast(LegacyMods.Target)) if (mods.HasFlag(LegacyMods.Target))
yield return new OsuModTargetPractice(); yield return new OsuModTargetPractice();
if (mods.HasFlagFast(LegacyMods.TouchDevice)) if (mods.HasFlag(LegacyMods.TouchDevice))
yield return new OsuModTouchDevice(); yield return new OsuModTouchDevice();
if (mods.HasFlagFast(LegacyMods.ScoreV2)) if (mods.HasFlag(LegacyMods.ScoreV2))
yield return new ModScoreV2(); yield return new ModScoreV2();
} }

View File

@ -7,7 +7,6 @@ using System;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Rendering; using osu.Framework.Graphics.Rendering;
@ -243,14 +242,14 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
originPosition = Vector2.Zero; originPosition = Vector2.Zero;
if (Source.TrailOrigin.HasFlagFast(Anchor.x1)) if (Source.TrailOrigin.HasFlag(Anchor.x1))
originPosition.X = 0.5f; originPosition.X = 0.5f;
else if (Source.TrailOrigin.HasFlagFast(Anchor.x2)) else if (Source.TrailOrigin.HasFlag(Anchor.x2))
originPosition.X = 1f; originPosition.X = 1f;
if (Source.TrailOrigin.HasFlagFast(Anchor.y1)) if (Source.TrailOrigin.HasFlag(Anchor.y1))
originPosition.Y = 0.5f; originPosition.Y = 0.5f;
else if (Source.TrailOrigin.HasFlagFast(Anchor.y2)) else if (Source.TrailOrigin.HasFlag(Anchor.y2))
originPosition.Y = 1f; originPosition.Y = 1f;
Source.parts.CopyTo(parts, 0); Source.parts.CopyTo(parts, 0);

View File

@ -4,7 +4,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
@ -79,43 +78,43 @@ namespace osu.Game.Rulesets.Taiko
public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods) public override IEnumerable<Mod> ConvertFromLegacyMods(LegacyMods mods)
{ {
if (mods.HasFlagFast(LegacyMods.Nightcore)) if (mods.HasFlag(LegacyMods.Nightcore))
yield return new TaikoModNightcore(); yield return new TaikoModNightcore();
else if (mods.HasFlagFast(LegacyMods.DoubleTime)) else if (mods.HasFlag(LegacyMods.DoubleTime))
yield return new TaikoModDoubleTime(); yield return new TaikoModDoubleTime();
if (mods.HasFlagFast(LegacyMods.Perfect)) if (mods.HasFlag(LegacyMods.Perfect))
yield return new TaikoModPerfect(); yield return new TaikoModPerfect();
else if (mods.HasFlagFast(LegacyMods.SuddenDeath)) else if (mods.HasFlag(LegacyMods.SuddenDeath))
yield return new TaikoModSuddenDeath(); yield return new TaikoModSuddenDeath();
if (mods.HasFlagFast(LegacyMods.Cinema)) if (mods.HasFlag(LegacyMods.Cinema))
yield return new TaikoModCinema(); yield return new TaikoModCinema();
else if (mods.HasFlagFast(LegacyMods.Autoplay)) else if (mods.HasFlag(LegacyMods.Autoplay))
yield return new TaikoModAutoplay(); yield return new TaikoModAutoplay();
if (mods.HasFlagFast(LegacyMods.Easy)) if (mods.HasFlag(LegacyMods.Easy))
yield return new TaikoModEasy(); yield return new TaikoModEasy();
if (mods.HasFlagFast(LegacyMods.Flashlight)) if (mods.HasFlag(LegacyMods.Flashlight))
yield return new TaikoModFlashlight(); yield return new TaikoModFlashlight();
if (mods.HasFlagFast(LegacyMods.HalfTime)) if (mods.HasFlag(LegacyMods.HalfTime))
yield return new TaikoModHalfTime(); yield return new TaikoModHalfTime();
if (mods.HasFlagFast(LegacyMods.HardRock)) if (mods.HasFlag(LegacyMods.HardRock))
yield return new TaikoModHardRock(); yield return new TaikoModHardRock();
if (mods.HasFlagFast(LegacyMods.Hidden)) if (mods.HasFlag(LegacyMods.Hidden))
yield return new TaikoModHidden(); yield return new TaikoModHidden();
if (mods.HasFlagFast(LegacyMods.NoFail)) if (mods.HasFlag(LegacyMods.NoFail))
yield return new TaikoModNoFail(); yield return new TaikoModNoFail();
if (mods.HasFlagFast(LegacyMods.Relax)) if (mods.HasFlag(LegacyMods.Relax))
yield return new TaikoModRelax(); yield return new TaikoModRelax();
if (mods.HasFlagFast(LegacyMods.ScoreV2)) if (mods.HasFlag(LegacyMods.ScoreV2))
yield return new ModScoreV2(); yield return new ModScoreV2();
} }

View File

@ -528,8 +528,17 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual("Gameplay/normal-hitnormal2", getTestableSampleInfo(hitObjects[2]).LookupNames.First()); Assert.AreEqual("Gameplay/normal-hitnormal2", getTestableSampleInfo(hitObjects[2]).LookupNames.First());
Assert.AreEqual("Gameplay/normal-hitnormal", getTestableSampleInfo(hitObjects[3]).LookupNames.First()); Assert.AreEqual("Gameplay/normal-hitnormal", getTestableSampleInfo(hitObjects[3]).LookupNames.First());
// The control point at the end time of the slider should be applied // The fourth object is a slider.
Assert.AreEqual("Gameplay/soft-hitnormal8", getTestableSampleInfo(hitObjects[4]).LookupNames.First()); // `Samples` of a slider are presumed to control the volume of sounds that last the entire duration of the slider
// (such as ticks, slider slide sounds, etc.)
// Thus, the point of query of control points used for `Samples` is just beyond the start time of the slider.
Assert.AreEqual("Gameplay/soft-hitnormal11", getTestableSampleInfo(hitObjects[4]).LookupNames.First());
// That said, the `NodeSamples` of the slider are responsible for the sounds of the slider's head / tail / repeats / large ticks etc.
// Therefore, they should be read at the time instant correspondent to the given node.
// This means that the tail should use bank 8 rather than 11.
Assert.AreEqual("Gameplay/soft-hitnormal11", ((ConvertSlider)hitObjects[4]).NodeSamples[0][0].LookupNames.First());
Assert.AreEqual("Gameplay/soft-hitnormal8", ((ConvertSlider)hitObjects[4]).NodeSamples[1][0].LookupNames.First());
} }
static HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.Samples[0]; static HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.Samples[0];

View File

@ -0,0 +1,22 @@
osu file format v128
[General]
SampleSet: Normal
[TimingPoints]
15,1000,4,1,0,100,1,0
2271,-100,4,1,0,5,0,0
6021,-100,4,1,0,100,0,0
8515,-100,4,1,0,5,0,0
12765,-100,4,1,0,100,0,0
14764,-100,4,1,0,5,0,0
14770,-100,4,1,0,50,0,0
17264,-100,4,1,0,5,0,0
17270,-100,4,1,0,50,0,0
22264,-100,4,1,0,100,0,0
[HitObjects]
113,54,2265,6,0,L|422:55,1,300,0|0,1:0|1:0,1:0:0:0:
82,206,6015,2,0,L|457:204,1,350,0|0,2:0|2:0,2:0:0:0:
75,310,10265,2,0,L|435:312,1,350,0|0,3:0|3:0,3:0:0:0:
75,310,14764,2,0,L|435:312,3,350,0|0|0|0,3:0|3:0|3:0|3:0,3:0:0:0:

View File

@ -402,6 +402,88 @@ namespace osu.Game.Tests.Visual.Editing
void checkPlacementSample(string expected) => AddAssert($"Placement sample is {expected}", () => EditorBeatmap.PlacementObject.Value.Samples.First().Bank, () => Is.EqualTo(expected)); void checkPlacementSample(string expected) => AddAssert($"Placement sample is {expected}", () => EditorBeatmap.PlacementObject.Value.Samples.First().Bank, () => Is.EqualTo(expected));
} }
[Test]
public void TestHotkeysAffectNodeSamples()
{
AddStep("add slider", () =>
{
EditorBeatmap.Add(new Slider
{
Position = new Vector2(256, 256),
StartTime = 1000,
Path = new SliderPath(new[] { new PathControlPoint(Vector2.Zero), new PathControlPoint(new Vector2(250, 0)) }),
Samples =
{
new HitSampleInfo(HitSampleInfo.HIT_NORMAL)
},
NodeSamples = new List<IList<HitSampleInfo>>
{
new List<HitSampleInfo>
{
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, bank: HitSampleInfo.BANK_DRUM),
new HitSampleInfo(HitSampleInfo.HIT_CLAP, bank: HitSampleInfo.BANK_DRUM),
},
new List<HitSampleInfo>
{
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, bank: HitSampleInfo.BANK_SOFT),
new HitSampleInfo(HitSampleInfo.HIT_WHISTLE, bank: HitSampleInfo.BANK_SOFT),
},
}
});
});
AddStep("select everything", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects));
AddStep("add clap addition", () => InputManager.Key(Key.R));
hitObjectHasSampleBank(0, HitSampleInfo.BANK_NORMAL);
hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_CLAP);
hitObjectHasSampleBank(1, HitSampleInfo.BANK_SOFT);
hitObjectHasSamples(1, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_CLAP);
hitObjectHasSampleBank(2, HitSampleInfo.BANK_NORMAL);
hitObjectHasSamples(2, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_CLAP);
hitObjectNodeHasSampleBank(2, 0, HitSampleInfo.BANK_DRUM);
hitObjectNodeHasSamples(2, 0, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_CLAP);
hitObjectNodeHasSampleBank(2, 1, HitSampleInfo.BANK_SOFT);
hitObjectNodeHasSamples(2, 1, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_WHISTLE, HitSampleInfo.HIT_CLAP);
AddStep("remove clap addition", () => InputManager.Key(Key.R));
hitObjectHasSampleBank(0, HitSampleInfo.BANK_NORMAL);
hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL);
hitObjectHasSampleBank(1, HitSampleInfo.BANK_SOFT);
hitObjectHasSamples(1, HitSampleInfo.HIT_NORMAL);
hitObjectHasSampleBank(2, HitSampleInfo.BANK_NORMAL);
hitObjectHasSamples(2, HitSampleInfo.HIT_NORMAL);
hitObjectNodeHasSampleBank(2, 0, HitSampleInfo.BANK_DRUM);
hitObjectNodeHasSamples(2, 0, HitSampleInfo.HIT_NORMAL);
hitObjectNodeHasSampleBank(2, 1, HitSampleInfo.BANK_SOFT);
hitObjectNodeHasSamples(2, 1, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_WHISTLE);
AddStep("set drum bank", () =>
{
InputManager.PressKey(Key.LShift);
InputManager.Key(Key.R);
InputManager.ReleaseKey(Key.LShift);
});
hitObjectHasSampleBank(0, HitSampleInfo.BANK_DRUM);
hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL);
hitObjectHasSampleBank(1, HitSampleInfo.BANK_DRUM);
hitObjectHasSamples(1, HitSampleInfo.HIT_NORMAL);
hitObjectHasSampleBank(2, HitSampleInfo.BANK_DRUM);
hitObjectHasSamples(2, HitSampleInfo.HIT_NORMAL);
hitObjectNodeHasSampleBank(2, 0, HitSampleInfo.BANK_DRUM);
hitObjectNodeHasSamples(2, 0, HitSampleInfo.HIT_NORMAL);
hitObjectNodeHasSampleBank(2, 1, HitSampleInfo.BANK_DRUM);
hitObjectNodeHasSamples(2, 1, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_WHISTLE);
}
private void clickSamplePiece(int objectIndex) => AddStep($"click {objectIndex.ToOrdinalWords()} sample piece", () => private void clickSamplePiece(int objectIndex) => AddStep($"click {objectIndex.ToOrdinalWords()} sample piece", () =>
{ {
var samplePiece = this.ChildrenOfType<SamplePointPiece>().Single(piece => piece.HitObject == EditorBeatmap.HitObjects.ElementAt(objectIndex)); var samplePiece = this.ChildrenOfType<SamplePointPiece>().Single(piece => piece.HitObject == EditorBeatmap.HitObjects.ElementAt(objectIndex));

View File

@ -70,6 +70,51 @@ namespace osu.Game.Tests.Visual.Editing
})); }));
} }
[TestCaseSource(nameof(test_cases))]
public void TestTriangularGrid(Vector2 position, Vector2 spacing, float rotation)
{
TriangularPositionSnapGrid grid = null;
AddStep("create grid", () =>
{
Child = grid = new TriangularPositionSnapGrid
{
RelativeSizeAxes = Axes.Both,
};
grid.StartPosition.Value = position;
grid.Spacing.Value = spacing.X;
grid.GridLineRotation.Value = rotation;
});
AddStep("add snapping cursor", () => Add(new SnappingCursorContainer
{
RelativeSizeAxes = Axes.Both,
GetSnapPosition = pos => grid.GetSnappedPosition(grid.ToLocalSpace(pos))
}));
}
[TestCaseSource(nameof(test_cases))]
public void TestCircularGrid(Vector2 position, Vector2 spacing, float rotation)
{
CircularPositionSnapGrid grid = null;
AddStep("create grid", () =>
{
Child = grid = new CircularPositionSnapGrid
{
RelativeSizeAxes = Axes.Both,
};
grid.StartPosition.Value = position;
grid.Spacing.Value = spacing.X;
});
AddStep("add snapping cursor", () => Add(new SnappingCursorContainer
{
RelativeSizeAxes = Axes.Both,
GetSnapPosition = pos => grid.GetSnappedPosition(grid.ToLocalSpace(pos))
}));
}
private partial class SnappingCursorContainer : CompositeDrawable private partial class SnappingCursorContainer : CompositeDrawable
{ {
public Func<Vector2, Vector2> GetSnapPosition; public Func<Vector2, Vector2> GetSnapPosition;

View File

@ -403,6 +403,28 @@ namespace osu.Game.Tests.Visual.Editing
AddAssert("placement committed", () => EditorBeatmap.HitObjects, () => Has.Count.EqualTo(2)); AddAssert("placement committed", () => EditorBeatmap.HitObjects, () => Has.Count.EqualTo(2));
} }
[Test]
public void TestBreakRemoval()
{
var addedObjects = new[]
{
new HitCircle { StartTime = 0 },
new HitCircle { StartTime = 5000 },
};
AddStep("add hitobjects", () => EditorBeatmap.AddRange(addedObjects));
AddAssert("beatmap has one break", () => EditorBeatmap.Breaks, () => Has.Count.EqualTo(1));
AddStep("move mouse to break", () => InputManager.MoveMouseTo(this.ChildrenOfType<TimelineBreak>().Single()));
AddStep("right click", () => InputManager.Click(MouseButton.Right));
AddStep("move mouse to delete menu item", () => InputManager.MoveMouseTo(this.ChildrenOfType<OsuContextMenu>().First().ChildrenOfType<DrawableOsuMenuItem>().First()));
AddStep("click", () => InputManager.Click(MouseButton.Left));
AddAssert("beatmap has no breaks", () => EditorBeatmap.Breaks, () => Is.Empty);
AddAssert("break piece went away", () => this.ChildrenOfType<TimelineBreak>().Count(), () => Is.Zero);
}
private void assertSelectionIs(IEnumerable<HitObject> hitObjects) private void assertSelectionIs(IEnumerable<HitObject> hitObjects)
=> AddAssert("correct hitobjects selected", () => EditorBeatmap.SelectedHitObjects.OrderBy(h => h.StartTime).SequenceEqual(hitObjects)); => AddAssert("correct hitobjects selected", () => EditorBeatmap.SelectedHitObjects.OrderBy(h => h.StartTime).SequenceEqual(hitObjects));
} }

View File

@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
@ -33,7 +32,7 @@ namespace osu.Game.Beatmaps.Formats
/// <remarks> /// <remarks>
/// Compare: https://github.com/peppy/osu-stable-reference/blob/master/osu!/GameplayElements/HitObjects/HitObject.cs#L319 /// Compare: https://github.com/peppy/osu-stable-reference/blob/master/osu!/GameplayElements/HitObjects/HitObject.cs#L319
/// </remarks> /// </remarks>
private const double control_point_leniency = 5; public const double CONTROL_POINT_LENIENCY = 5;
internal static RulesetStore? RulesetStore; internal static RulesetStore? RulesetStore;
@ -160,20 +159,24 @@ namespace osu.Game.Beatmaps.Formats
private void applySamples(HitObject hitObject) private void applySamples(HitObject hitObject)
{ {
SampleControlPoint sampleControlPoint = (beatmap.ControlPointInfo as LegacyControlPointInfo)?.SamplePointAt(hitObject.GetEndTime() + control_point_leniency) ?? SampleControlPoint.DEFAULT;
hitObject.Samples = hitObject.Samples.Select(o => sampleControlPoint.ApplyTo(o)).ToList();
if (hitObject is IHasRepeats hasRepeats) if (hitObject is IHasRepeats hasRepeats)
{ {
SampleControlPoint sampleControlPoint = (beatmap.ControlPointInfo as LegacyControlPointInfo)?.SamplePointAt(hitObject.StartTime + CONTROL_POINT_LENIENCY + 1) ?? SampleControlPoint.DEFAULT;
hitObject.Samples = hitObject.Samples.Select(o => sampleControlPoint.ApplyTo(o)).ToList();
for (int i = 0; i < hasRepeats.NodeSamples.Count; i++) for (int i = 0; i < hasRepeats.NodeSamples.Count; i++)
{ {
double time = hitObject.StartTime + i * hasRepeats.Duration / hasRepeats.SpanCount() + control_point_leniency; double time = hitObject.StartTime + i * hasRepeats.Duration / hasRepeats.SpanCount() + CONTROL_POINT_LENIENCY;
var nodeSamplePoint = (beatmap.ControlPointInfo as LegacyControlPointInfo)?.SamplePointAt(time) ?? SampleControlPoint.DEFAULT; var nodeSamplePoint = (beatmap.ControlPointInfo as LegacyControlPointInfo)?.SamplePointAt(time) ?? SampleControlPoint.DEFAULT;
hasRepeats.NodeSamples[i] = hasRepeats.NodeSamples[i].Select(o => nodeSamplePoint.ApplyTo(o)).ToList(); hasRepeats.NodeSamples[i] = hasRepeats.NodeSamples[i].Select(o => nodeSamplePoint.ApplyTo(o)).ToList();
} }
} }
else
{
SampleControlPoint sampleControlPoint = (beatmap.ControlPointInfo as LegacyControlPointInfo)?.SamplePointAt(hitObject.GetEndTime() + CONTROL_POINT_LENIENCY) ?? SampleControlPoint.DEFAULT;
hitObject.Samples = hitObject.Samples.Select(o => sampleControlPoint.ApplyTo(o)).ToList();
}
} }
/// <summary> /// <summary>
@ -528,8 +531,8 @@ namespace osu.Game.Beatmaps.Formats
if (split.Length >= 8) if (split.Length >= 8)
{ {
LegacyEffectFlags effectFlags = (LegacyEffectFlags)Parsing.ParseInt(split[7]); LegacyEffectFlags effectFlags = (LegacyEffectFlags)Parsing.ParseInt(split[7]);
kiaiMode = effectFlags.HasFlagFast(LegacyEffectFlags.Kiai); kiaiMode = effectFlags.HasFlag(LegacyEffectFlags.Kiai);
omitFirstBarSignature = effectFlags.HasFlagFast(LegacyEffectFlags.OmitFirstBarLine); omitFirstBarSignature = effectFlags.HasFlag(LegacyEffectFlags.OmitFirstBarLine);
} }
string stringSampleSet = sampleSet.ToString().ToLowerInvariant(); string stringSampleSet = sampleSet.ToString().ToLowerInvariant();

View File

@ -282,19 +282,39 @@ namespace osu.Game.Beatmaps.Formats
{ {
foreach (var hitObject in hitObjects) foreach (var hitObject in hitObjects)
{ {
if (hitObject.Samples.Count > 0) if (hitObject is IHasRepeats hasNodeSamples)
{ {
int volume = hitObject.Samples.Max(o => o.Volume); double spanDuration = hasNodeSamples.Duration / hasNodeSamples.SpanCount();
int customIndex = hitObject.Samples.Any(o => o is ConvertHitObjectParser.LegacyHitSampleInfo)
? hitObject.Samples.OfType<ConvertHitObjectParser.LegacyHitSampleInfo>().Max(o => o.CustomSampleBank)
: -1;
yield return new LegacyBeatmapDecoder.LegacySampleControlPoint { Time = hitObject.GetEndTime(), SampleVolume = volume, CustomSampleBank = customIndex }; for (int i = 0; i < hasNodeSamples.NodeSamples.Count; ++i)
{
double nodeTime = hitObject.StartTime + i * spanDuration;
if (hasNodeSamples.NodeSamples[i].Count > 0)
yield return createSampleControlPointFor(nodeTime, hasNodeSamples.NodeSamples[i]);
if (spanDuration > LegacyBeatmapDecoder.CONTROL_POINT_LENIENCY + 1 && hitObject.Samples.Count > 0 && i < hasNodeSamples.NodeSamples.Count - 1)
yield return createSampleControlPointFor(nodeTime + LegacyBeatmapDecoder.CONTROL_POINT_LENIENCY + 1, hitObject.Samples);
}
}
else if (hitObject.Samples.Count > 0)
{
yield return createSampleControlPointFor(hitObject.GetEndTime(), hitObject.Samples);
} }
foreach (var nested in collectSampleControlPoints(hitObject.NestedHitObjects)) foreach (var nested in collectSampleControlPoints(hitObject.NestedHitObjects))
yield return nested; yield return nested;
} }
SampleControlPoint createSampleControlPointFor(double time, IList<HitSampleInfo> samples)
{
int volume = samples.Max(o => o.Volume);
int customIndex = samples.Any(o => o is ConvertHitObjectParser.LegacyHitSampleInfo)
? samples.OfType<ConvertHitObjectParser.LegacyHitSampleInfo>().Max(o => o.CustomSampleBank)
: -1;
return new LegacyBeatmapDecoder.LegacySampleControlPoint { Time = time, SampleVolume = volume, CustomSampleBank = customIndex };
}
} }
void extractSampleControlPoints(IEnumerable<HitObject> hitObject) void extractSampleControlPoints(IEnumerable<HitObject> hitObject)

View File

@ -10,7 +10,6 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using osu.Framework; using osu.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
@ -164,13 +163,13 @@ namespace osu.Game.Database
var importTasks = new List<Task>(); var importTasks = new List<Task>();
Task beatmapImportTask = Task.CompletedTask; Task beatmapImportTask = Task.CompletedTask;
if (content.HasFlagFast(StableContent.Beatmaps)) if (content.HasFlag(StableContent.Beatmaps))
importTasks.Add(beatmapImportTask = new LegacyBeatmapImporter(beatmaps).ImportFromStableAsync(stableStorage)); importTasks.Add(beatmapImportTask = new LegacyBeatmapImporter(beatmaps).ImportFromStableAsync(stableStorage));
if (content.HasFlagFast(StableContent.Skins)) if (content.HasFlag(StableContent.Skins))
importTasks.Add(new LegacySkinImporter(skins).ImportFromStableAsync(stableStorage)); importTasks.Add(new LegacySkinImporter(skins).ImportFromStableAsync(stableStorage));
if (content.HasFlagFast(StableContent.Collections)) if (content.HasFlag(StableContent.Collections))
{ {
importTasks.Add(beatmapImportTask.ContinueWith(_ => new LegacyCollectionImporter(realmAccess) importTasks.Add(beatmapImportTask.ContinueWith(_ => new LegacyCollectionImporter(realmAccess)
{ {
@ -180,7 +179,7 @@ namespace osu.Game.Database
}.ImportFromStorage(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion)); }.ImportFromStorage(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion));
} }
if (content.HasFlagFast(StableContent.Scores)) if (content.HasFlag(StableContent.Scores))
importTasks.Add(beatmapImportTask.ContinueWith(_ => new LegacyScoreImporter(scores).ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion)); importTasks.Add(beatmapImportTask.ContinueWith(_ => new LegacyScoreImporter(scores).ImportFromStableAsync(stableStorage), TaskContinuationOptions.OnlyOnRanToCompletion));
await Task.WhenAll(importTasks.ToArray()).ConfigureAwait(false); await Task.WhenAll(importTasks.ToArray()).ConfigureAwait(false);

View File

@ -15,7 +15,6 @@ using osu.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Development; using osu.Framework.Development;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Framework.Platform; using osu.Framework.Platform;
@ -1035,7 +1034,7 @@ namespace osu.Game.Database
var legacyMods = (LegacyMods)sr.ReadInt32(); var legacyMods = (LegacyMods)sr.ReadInt32();
if (!legacyMods.HasFlagFast(LegacyMods.ScoreV2) || score.APIMods.Any(mod => mod.Acronym == @"SV2")) if (!legacyMods.HasFlag(LegacyMods.ScoreV2) || score.APIMods.Any(mod => mod.Acronym == @"SV2"))
return; return;
score.APIMods = score.APIMods.Append(new APIMod(new ModScoreV2())).ToArray(); score.APIMods = score.APIMods.Append(new APIMod(new ModScoreV2())).ToArray();

View File

@ -6,7 +6,6 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.Rendering; using osu.Framework.Graphics.Rendering;
@ -190,9 +189,9 @@ namespace osu.Game.Graphics
float width = Texture.DisplayWidth * scale; float width = Texture.DisplayWidth * scale;
float height = Texture.DisplayHeight * scale; float height = Texture.DisplayHeight * scale;
if (relativePositionAxes.HasFlagFast(Axes.X)) if (relativePositionAxes.HasFlag(Axes.X))
position.X *= sourceSize.X; position.X *= sourceSize.X;
if (relativePositionAxes.HasFlagFast(Axes.Y)) if (relativePositionAxes.HasFlag(Axes.Y))
position.Y *= sourceSize.Y; position.Y *= sourceSize.Y;
return new RectangleF( return new RectangleF(

View File

@ -119,9 +119,14 @@ namespace osu.Game.Graphics.UserInterface
Expanded.BindValueChanged(v => Expanded.BindValueChanged(v =>
{ {
label.Text = v.NewValue ? expandedLabelText : contractedLabelText; label.Text = v.NewValue ? expandedLabelText : contractedLabelText;
slider.FadeTo(v.NewValue ? 1f : 0f, 500, Easing.OutQuint); slider.FadeTo(v.NewValue ? Current.Disabled ? 0.3f : 1f : 0f, 500, Easing.OutQuint);
slider.BypassAutoSizeAxes = !v.NewValue ? Axes.Y : Axes.None; slider.BypassAutoSizeAxes = !v.NewValue ? Axes.Y : Axes.None;
}, true); }, true);
Current.BindDisabledChanged(disabled =>
{
slider.Alpha = Expanded.Value ? disabled ? 0.3f : 1 : 0f;
});
} }
} }

View File

@ -12,7 +12,6 @@ using osu.Game.Graphics.Containers;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Framework.Audio.Track; using osu.Framework.Audio.Track;
using System; using System;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
@ -57,15 +56,15 @@ namespace osu.Game.Graphics.UserInterface
set set
{ {
base.Origin = value; base.Origin = value;
c1.Origin = c1.Anchor = value.HasFlagFast(Anchor.x2) ? Anchor.TopLeft : Anchor.TopRight; c1.Origin = c1.Anchor = value.HasFlag(Anchor.x2) ? Anchor.TopLeft : Anchor.TopRight;
c2.Origin = c2.Anchor = value.HasFlagFast(Anchor.x2) ? Anchor.TopRight : Anchor.TopLeft; c2.Origin = c2.Anchor = value.HasFlag(Anchor.x2) ? Anchor.TopRight : Anchor.TopLeft;
X = value.HasFlagFast(Anchor.x2) ? SIZE_RETRACTED.X * shear.X * 0.5f : 0; X = value.HasFlag(Anchor.x2) ? SIZE_RETRACTED.X * shear.X * 0.5f : 0;
Remove(c1, false); Remove(c1, false);
Remove(c2, false); Remove(c2, false);
c1.Depth = value.HasFlagFast(Anchor.x2) ? 0 : 1; c1.Depth = value.HasFlag(Anchor.x2) ? 0 : 1;
c2.Depth = value.HasFlagFast(Anchor.x2) ? 1 : 0; c2.Depth = value.HasFlag(Anchor.x2) ? 1 : 0;
Add(c1); Add(c1);
Add(c2); Add(c2);
} }

View File

@ -8,7 +8,6 @@ using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Effects;
@ -131,7 +130,7 @@ namespace osu.Game.Overlays.Music
filter.Search.HoldFocus = true; filter.Search.HoldFocus = true;
Schedule(() => filter.Search.TakeFocus()); Schedule(() => filter.Search.TakeFocus());
this.ResizeTo(new Vector2(1, RelativeSizeAxes.HasFlagFast(Axes.Y) ? 1f : PLAYLIST_HEIGHT), transition_duration, Easing.OutQuint); this.ResizeTo(new Vector2(1, RelativeSizeAxes.HasFlag(Axes.Y) ? 1f : PLAYLIST_HEIGHT), transition_duration, Easing.OutQuint);
this.FadeIn(transition_duration, Easing.OutQuint); this.FadeIn(transition_duration, Easing.OutQuint);
} }

View File

@ -4,7 +4,6 @@
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Caching; using osu.Framework.Caching;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
@ -161,7 +160,7 @@ namespace osu.Game.Overlays
protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source) protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source)
{ {
if (invalidation.HasFlagFast(Invalidation.DrawSize)) if (invalidation.HasFlag(Invalidation.DrawSize))
headerTextVisibilityCache.Invalidate(); headerTextVisibilityCache.Invalidate();
return base.OnInvalidate(invalidation, source); return base.OnInvalidate(invalidation, source);

View File

@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Primitives;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
@ -145,9 +144,9 @@ namespace osu.Game.Overlays.SkinEditor
var blueprintItem = ((Drawable)blueprint.Item); var blueprintItem = ((Drawable)blueprint.Item);
blueprintItem.Scale = Vector2.One; blueprintItem.Scale = Vector2.One;
if (blueprintItem.RelativeSizeAxes.HasFlagFast(Axes.X)) if (blueprintItem.RelativeSizeAxes.HasFlag(Axes.X))
blueprintItem.Width = 1; blueprintItem.Width = 1;
if (blueprintItem.RelativeSizeAxes.HasFlagFast(Axes.Y)) if (blueprintItem.RelativeSizeAxes.HasFlag(Axes.Y))
blueprintItem.Height = 1; blueprintItem.Height = 1;
} }
}); });

View File

@ -7,7 +7,6 @@ using System.Diagnostics;
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Utils; using osu.Framework.Utils;
@ -51,7 +50,7 @@ namespace osu.Game.Overlays.SkinEditor
CanScaleDiagonally.Value = true; CanScaleDiagonally.Value = true;
} }
private bool allSelectedSupportManualSizing(Axes axis) => selectedItems.All(b => (b as CompositeDrawable)?.AutoSizeAxes.HasFlagFast(axis) == false); private bool allSelectedSupportManualSizing(Axes axis) => selectedItems.All(b => (b as CompositeDrawable)?.AutoSizeAxes.HasFlag(axis) == false);
private Dictionary<Drawable, OriginalDrawableState>? objectsInScale; private Dictionary<Drawable, OriginalDrawableState>? objectsInScale;
private Vector2? defaultOrigin; private Vector2? defaultOrigin;

View File

@ -4,7 +4,6 @@
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
@ -150,9 +149,9 @@ namespace osu.Game.Overlays.Toolbar
{ {
Direction = FillDirection.Vertical, Direction = FillDirection.Vertical,
RelativeSizeAxes = Axes.Both, // stops us being considered in parent's autosize RelativeSizeAxes = Axes.Both, // stops us being considered in parent's autosize
Anchor = TooltipAnchor.HasFlagFast(Anchor.x0) ? Anchor.BottomLeft : Anchor.BottomRight, Anchor = TooltipAnchor.HasFlag(Anchor.x0) ? Anchor.BottomLeft : Anchor.BottomRight,
Origin = TooltipAnchor, Origin = TooltipAnchor,
Position = new Vector2(TooltipAnchor.HasFlagFast(Anchor.x0) ? 5 : -5, 5), Position = new Vector2(TooltipAnchor.HasFlag(Anchor.x0) ? 5 : -5, 5),
Alpha = 0, Alpha = 0,
Children = new Drawable[] Children = new Drawable[]
{ {

View File

@ -3,7 +3,6 @@
using MessagePack; using MessagePack;
using Newtonsoft.Json; using Newtonsoft.Json;
using osu.Framework.Extensions.EnumExtensions;
using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays;
using osuTK; using osuTK;
@ -32,23 +31,23 @@ namespace osu.Game.Replays.Legacy
[JsonIgnore] [JsonIgnore]
[IgnoreMember] [IgnoreMember]
public bool MouseLeft1 => ButtonState.HasFlagFast(ReplayButtonState.Left1); public bool MouseLeft1 => ButtonState.HasFlag(ReplayButtonState.Left1);
[JsonIgnore] [JsonIgnore]
[IgnoreMember] [IgnoreMember]
public bool MouseRight1 => ButtonState.HasFlagFast(ReplayButtonState.Right1); public bool MouseRight1 => ButtonState.HasFlag(ReplayButtonState.Right1);
[JsonIgnore] [JsonIgnore]
[IgnoreMember] [IgnoreMember]
public bool MouseLeft2 => ButtonState.HasFlagFast(ReplayButtonState.Left2); public bool MouseLeft2 => ButtonState.HasFlag(ReplayButtonState.Left2);
[JsonIgnore] [JsonIgnore]
[IgnoreMember] [IgnoreMember]
public bool MouseRight2 => ButtonState.HasFlagFast(ReplayButtonState.Right2); public bool MouseRight2 => ButtonState.HasFlag(ReplayButtonState.Right2);
[JsonIgnore] [JsonIgnore]
[IgnoreMember] [IgnoreMember]
public bool Smoke => ButtonState.HasFlagFast(ReplayButtonState.Smoke); public bool Smoke => ButtonState.HasFlag(ReplayButtonState.Smoke);
[Key(3)] [Key(3)]
public ReplayButtonState ButtonState; public ReplayButtonState ButtonState;

View File

@ -10,7 +10,6 @@ using System.Linq;
using JetBrains.Annotations; using JetBrains.Annotations;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
@ -505,7 +504,7 @@ namespace osu.Game.Rulesets.Edit
var playfield = PlayfieldAtScreenSpacePosition(screenSpacePosition); var playfield = PlayfieldAtScreenSpacePosition(screenSpacePosition);
double? targetTime = null; double? targetTime = null;
if (snapType.HasFlagFast(SnapType.GlobalGrids)) if (snapType.HasFlag(SnapType.GlobalGrids))
{ {
if (playfield is ScrollingPlayfield scrollingPlayfield) if (playfield is ScrollingPlayfield scrollingPlayfield)
{ {

View File

@ -12,7 +12,6 @@ using osu.Game.Beatmaps.Formats;
using osu.Game.Audio; using osu.Game.Audio;
using System.Linq; using System.Linq;
using JetBrains.Annotations; using JetBrains.Annotations;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps.Legacy; using osu.Game.Beatmaps.Legacy;
@ -58,7 +57,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
int comboOffset = (int)(type & LegacyHitObjectType.ComboOffset) >> 4; int comboOffset = (int)(type & LegacyHitObjectType.ComboOffset) >> 4;
type &= ~LegacyHitObjectType.ComboOffset; type &= ~LegacyHitObjectType.ComboOffset;
bool combo = type.HasFlagFast(LegacyHitObjectType.NewCombo); bool combo = type.HasFlag(LegacyHitObjectType.NewCombo);
type &= ~LegacyHitObjectType.NewCombo; type &= ~LegacyHitObjectType.NewCombo;
var soundType = (LegacyHitSoundType)Parsing.ParseInt(split[4]); var soundType = (LegacyHitSoundType)Parsing.ParseInt(split[4]);
@ -66,14 +65,14 @@ namespace osu.Game.Rulesets.Objects.Legacy
HitObject result = null; HitObject result = null;
if (type.HasFlagFast(LegacyHitObjectType.Circle)) if (type.HasFlag(LegacyHitObjectType.Circle))
{ {
result = CreateHit(pos, combo, comboOffset); result = CreateHit(pos, combo, comboOffset);
if (split.Length > 5) if (split.Length > 5)
readCustomSampleBanks(split[5], bankInfo); readCustomSampleBanks(split[5], bankInfo);
} }
else if (type.HasFlagFast(LegacyHitObjectType.Slider)) else if (type.HasFlag(LegacyHitObjectType.Slider))
{ {
double? length = null; double? length = null;
@ -145,7 +144,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
result = CreateSlider(pos, combo, comboOffset, convertPathString(split[5], pos), length, repeatCount, nodeSamples); result = CreateSlider(pos, combo, comboOffset, convertPathString(split[5], pos), length, repeatCount, nodeSamples);
} }
else if (type.HasFlagFast(LegacyHitObjectType.Spinner)) else if (type.HasFlag(LegacyHitObjectType.Spinner))
{ {
double duration = Math.Max(0, Parsing.ParseDouble(split[5]) + Offset - startTime); double duration = Math.Max(0, Parsing.ParseDouble(split[5]) + Offset - startTime);
@ -154,7 +153,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
if (split.Length > 6) if (split.Length > 6)
readCustomSampleBanks(split[6], bankInfo); readCustomSampleBanks(split[6], bankInfo);
} }
else if (type.HasFlagFast(LegacyHitObjectType.Hold)) else if (type.HasFlag(LegacyHitObjectType.Hold))
{ {
// Note: Hold is generated by BMS converts // Note: Hold is generated by BMS converts
@ -472,7 +471,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_NORMAL, bankInfo.BankForNormal, bankInfo.Volume, bankInfo.CustomSampleBank, soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_NORMAL, bankInfo.BankForNormal, bankInfo.Volume, bankInfo.CustomSampleBank,
// if the sound type doesn't have the Normal flag set, attach it anyway as a layered sample. // if the sound type doesn't have the Normal flag set, attach it anyway as a layered sample.
// None also counts as a normal non-layered sample: https://osu.ppy.sh/help/wiki/osu!_File_Formats/Osu_(file_format)#hitsounds // None also counts as a normal non-layered sample: https://osu.ppy.sh/help/wiki/osu!_File_Formats/Osu_(file_format)#hitsounds
type != LegacyHitSoundType.None && !type.HasFlagFast(LegacyHitSoundType.Normal))); type != LegacyHitSoundType.None && !type.HasFlag(LegacyHitSoundType.Normal)));
} }
else else
{ {
@ -480,13 +479,13 @@ namespace osu.Game.Rulesets.Objects.Legacy
soundTypes.Add(new FileHitSampleInfo(bankInfo.Filename, bankInfo.Volume)); soundTypes.Add(new FileHitSampleInfo(bankInfo.Filename, bankInfo.Volume));
} }
if (type.HasFlagFast(LegacyHitSoundType.Finish)) if (type.HasFlag(LegacyHitSoundType.Finish))
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_FINISH, bankInfo.BankForAdditions, bankInfo.Volume, bankInfo.CustomSampleBank)); soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_FINISH, bankInfo.BankForAdditions, bankInfo.Volume, bankInfo.CustomSampleBank));
if (type.HasFlagFast(LegacyHitSoundType.Whistle)) if (type.HasFlag(LegacyHitSoundType.Whistle))
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_WHISTLE, bankInfo.BankForAdditions, bankInfo.Volume, bankInfo.CustomSampleBank)); soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_WHISTLE, bankInfo.BankForAdditions, bankInfo.Volume, bankInfo.CustomSampleBank));
if (type.HasFlagFast(LegacyHitSoundType.Clap)) if (type.HasFlag(LegacyHitSoundType.Clap))
soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_CLAP, bankInfo.BankForAdditions, bankInfo.Volume, bankInfo.CustomSampleBank)); soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_CLAP, bankInfo.BankForAdditions, bankInfo.Volume, bankInfo.CustomSampleBank));
return soundTypes; return soundTypes;

View File

@ -0,0 +1,98 @@
// 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.Collections.Generic;
using System.Linq;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Utils;
using osuTK;
namespace osu.Game.Screens.Edit.Compose.Components
{
public partial class CircularPositionSnapGrid : PositionSnapGrid
{
/// <summary>
/// The spacing between grid lines of this <see cref="CircularPositionSnapGrid"/>.
/// </summary>
public BindableFloat Spacing { get; } = new BindableFloat(1f)
{
MinValue = 0f,
};
public CircularPositionSnapGrid()
{
Spacing.BindValueChanged(_ => GridCache.Invalidate());
}
protected override void CreateContent()
{
var drawSize = DrawSize;
// Calculate the required number of circles based on the maximum distance from the origin to the edge of the grid.
float dx = Math.Max(StartPosition.Value.X, DrawWidth - StartPosition.Value.X);
float dy = Math.Max(StartPosition.Value.Y, DrawHeight - StartPosition.Value.Y);
float maxDistance = new Vector2(dx, dy).Length;
// We need to add one because the first circle starts at zero radius.
int requiredCircles = (int)(maxDistance / Spacing.Value) + 1;
generateCircles(requiredCircles);
GenerateOutline(drawSize);
}
private void generateCircles(int count)
{
// Make lines the same width independent of display resolution.
float lineWidth = 2 * DrawWidth / ScreenSpaceDrawQuad.Width;
List<CircularProgress> generatedCircles = new List<CircularProgress>();
for (int i = 0; i < count; i++)
{
// Add a minimum diameter so the center circle is clearly visible.
float diameter = MathF.Max(lineWidth * 1.5f, i * Spacing.Value * 2);
var gridCircle = new CircularProgress
{
Position = StartPosition.Value,
Origin = Anchor.Centre,
Size = new Vector2(diameter),
InnerRadius = lineWidth * 1f / diameter,
Colour = Colour4.White,
Alpha = 0.2f,
Progress = 1,
};
generatedCircles.Add(gridCircle);
}
if (generatedCircles.Count == 0)
return;
generatedCircles.First().Alpha = 0.8f;
AddInternal(new Container
{
Masking = true,
RelativeSizeAxes = Axes.Both,
Children = generatedCircles,
});
}
public override Vector2 GetSnappedPosition(Vector2 original)
{
Vector2 relativeToStart = original - StartPosition.Value;
if (relativeToStart.LengthSquared < Precision.FLOAT_EPSILON)
return StartPosition.Value;
float length = relativeToStart.Length;
float wantedLength = MathF.Round(length / Spacing.Value) * Spacing.Value;
return StartPosition.Value + Vector2.Multiply(relativeToStart, wantedLength / length);
}
}
}

View File

@ -117,7 +117,7 @@ namespace osu.Game.Screens.Edit.Compose.Components
break; break;
} }
AddSampleBank(bankName); SetSampleBank(bankName);
} }
break; break;
@ -177,14 +177,27 @@ namespace osu.Game.Screens.Edit.Compose.Components
{ {
SelectionNewComboState.Value = GetStateFromSelection(SelectedItems.OfType<IHasComboInformation>(), h => h.NewCombo); SelectionNewComboState.Value = GetStateFromSelection(SelectedItems.OfType<IHasComboInformation>(), h => h.NewCombo);
var samplesInSelection = SelectedItems.SelectMany(enumerateAllSamples).ToArray();
foreach ((string sampleName, var bindable) in SelectionSampleStates) foreach ((string sampleName, var bindable) in SelectionSampleStates)
{ {
bindable.Value = GetStateFromSelection(SelectedItems, h => h.Samples.Any(s => s.Name == sampleName)); bindable.Value = GetStateFromSelection(samplesInSelection, h => h.Any(s => s.Name == sampleName));
} }
foreach ((string bankName, var bindable) in SelectionBankStates) foreach ((string bankName, var bindable) in SelectionBankStates)
{ {
bindable.Value = GetStateFromSelection(SelectedItems, h => h.Samples.All(s => s.Bank == bankName)); bindable.Value = GetStateFromSelection(samplesInSelection, h => h.Any(s => s.Bank == bankName));
}
IEnumerable<IList<HitSampleInfo>> enumerateAllSamples(HitObject hitObject)
{
yield return hitObject.Samples;
if (hitObject is IHasRepeats withRepeats)
{
foreach (var node in withRepeats.NodeSamples)
yield return node;
}
} }
} }
@ -193,12 +206,25 @@ namespace osu.Game.Screens.Edit.Compose.Components
#region Ternary state changes #region Ternary state changes
/// <summary> /// <summary>
/// Adds a sample bank to all selected <see cref="HitObject"/>s. /// Sets the sample bank for all selected <see cref="HitObject"/>s.
/// </summary> /// </summary>
/// <param name="bankName">The name of the sample bank.</param> /// <param name="bankName">The name of the sample bank.</param>
public void AddSampleBank(string bankName) public void SetSampleBank(string bankName)
{ {
if (SelectedItems.All(h => h.Samples.All(s => s.Bank == bankName))) bool hasRelevantBank(HitObject hitObject)
{
bool result = hitObject.Samples.All(s => s.Bank == bankName);
if (hitObject is IHasRepeats hasRepeats)
{
foreach (var node in hasRepeats.NodeSamples)
result &= node.All(s => s.Bank == bankName);
}
return result;
}
if (SelectedItems.All(hasRelevantBank))
return; return;
EditorBeatmap.PerformOnSelection(h => EditorBeatmap.PerformOnSelection(h =>
@ -207,17 +233,37 @@ namespace osu.Game.Screens.Edit.Compose.Components
return; return;
h.Samples = h.Samples.Select(s => s.With(newBank: bankName)).ToList(); h.Samples = h.Samples.Select(s => s.With(newBank: bankName)).ToList();
if (h is IHasRepeats hasRepeats)
{
for (int i = 0; i < hasRepeats.NodeSamples.Count; ++i)
hasRepeats.NodeSamples[i] = hasRepeats.NodeSamples[i].Select(s => s.With(newBank: bankName)).ToList();
}
EditorBeatmap.Update(h); EditorBeatmap.Update(h);
}); });
} }
private bool hasRelevantSample(HitObject hitObject, string sampleName)
{
bool result = hitObject.Samples.Any(s => s.Name == sampleName);
if (hitObject is IHasRepeats hasRepeats)
{
foreach (var node in hasRepeats.NodeSamples)
result &= node.Any(s => s.Name == sampleName);
}
return result;
}
/// <summary> /// <summary>
/// Adds a hit sample to all selected <see cref="HitObject"/>s. /// Adds a hit sample to all selected <see cref="HitObject"/>s.
/// </summary> /// </summary>
/// <param name="sampleName">The name of the hit sample.</param> /// <param name="sampleName">The name of the hit sample.</param>
public void AddHitSample(string sampleName) public void AddHitSample(string sampleName)
{ {
if (SelectedItems.All(h => h.Samples.Any(s => s.Name == sampleName))) if (SelectedItems.All(h => hasRelevantSample(h, sampleName)))
return; return;
EditorBeatmap.PerformOnSelection(h => EditorBeatmap.PerformOnSelection(h =>
@ -228,6 +274,23 @@ namespace osu.Game.Screens.Edit.Compose.Components
h.Samples.Add(h.CreateHitSampleInfo(sampleName)); h.Samples.Add(h.CreateHitSampleInfo(sampleName));
if (h is IHasRepeats hasRepeats)
{
foreach (var node in hasRepeats.NodeSamples)
{
if (node.Any(s => s.Name == sampleName))
continue;
var hitSample = h.CreateHitSampleInfo(sampleName);
string? existingAdditionBank = node.FirstOrDefault(s => s.Name != HitSampleInfo.HIT_NORMAL)?.Bank;
if (existingAdditionBank != null)
hitSample = hitSample.With(newBank: existingAdditionBank);
node.Add(hitSample);
}
}
EditorBeatmap.Update(h); EditorBeatmap.Update(h);
}); });
} }
@ -238,12 +301,19 @@ namespace osu.Game.Screens.Edit.Compose.Components
/// <param name="sampleName">The name of the hit sample.</param> /// <param name="sampleName">The name of the hit sample.</param>
public void RemoveHitSample(string sampleName) public void RemoveHitSample(string sampleName)
{ {
if (SelectedItems.All(h => h.Samples.All(s => s.Name != sampleName))) if (SelectedItems.All(h => !hasRelevantSample(h, sampleName)))
return; return;
EditorBeatmap.PerformOnSelection(h => EditorBeatmap.PerformOnSelection(h =>
{ {
h.SamplesBindable.RemoveAll(s => s.Name == sampleName); h.SamplesBindable.RemoveAll(s => s.Name == sampleName);
if (h is IHasRepeats hasRepeats)
{
for (int i = 0; i < hasRepeats.NodeSamples.Count; ++i)
hasRepeats.NodeSamples[i] = hasRepeats.NodeSamples[i].Where(s => s.Name != sampleName).ToList();
}
EditorBeatmap.Update(h); EditorBeatmap.Update(h);
}); });
} }

View File

@ -4,7 +4,6 @@
using System; using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
@ -298,13 +297,13 @@ namespace osu.Game.Screens.Edit.Compose.Components
/// </remarks> /// </remarks>
public void PerformFlipFromScaleHandles(Axes axes) public void PerformFlipFromScaleHandles(Axes axes)
{ {
if (axes.HasFlagFast(Axes.X)) if (axes.HasFlag(Axes.X))
{ {
dragHandles.FlipScaleHandles(Direction.Horizontal); dragHandles.FlipScaleHandles(Direction.Horizontal);
OnFlip?.Invoke(Direction.Horizontal, false); OnFlip?.Invoke(Direction.Horizontal, false);
} }
if (axes.HasFlagFast(Axes.Y)) if (axes.HasFlag(Axes.Y))
{ {
dragHandles.FlipScaleHandles(Direction.Vertical); dragHandles.FlipScaleHandles(Direction.Vertical);
OnFlip?.Invoke(Direction.Vertical, false); OnFlip?.Invoke(Direction.Vertical, false);

View File

@ -7,7 +7,6 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using JetBrains.Annotations; using JetBrains.Annotations;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
@ -74,9 +73,9 @@ namespace osu.Game.Screens.Edit.Compose.Components
{ {
foreach (var handle in scaleHandles) foreach (var handle in scaleHandles)
{ {
if (direction == Direction.Horizontal && !handle.Anchor.HasFlagFast(Anchor.x1)) if (direction == Direction.Horizontal && !handle.Anchor.HasFlag(Anchor.x1))
handle.Anchor ^= Anchor.x0 | Anchor.x2; handle.Anchor ^= Anchor.x0 | Anchor.x2;
if (direction == Direction.Vertical && !handle.Anchor.HasFlagFast(Anchor.y1)) if (direction == Direction.Vertical && !handle.Anchor.HasFlag(Anchor.y1))
handle.Anchor ^= Anchor.y0 | Anchor.y2; handle.Anchor ^= Anchor.y0 | Anchor.y2;
} }
} }

View File

@ -4,7 +4,6 @@
using System; using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
@ -46,8 +45,8 @@ namespace osu.Game.Screens.Edit.Compose.Components
Icon = FontAwesome.Solid.Redo, Icon = FontAwesome.Solid.Redo,
Scale = new Vector2 Scale = new Vector2
{ {
X = Anchor.HasFlagFast(Anchor.x0) ? 1f : -1f, X = Anchor.HasFlag(Anchor.x0) ? 1f : -1f,
Y = Anchor.HasFlagFast(Anchor.y0) ? 1f : -1f Y = Anchor.HasFlag(Anchor.y0) ? 1f : -1f
} }
}); });
} }

View File

@ -3,7 +3,6 @@
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Framework.Utils; using osu.Framework.Utils;
@ -128,6 +127,6 @@ namespace osu.Game.Screens.Edit.Compose.Components
} }
} }
private bool isCornerAnchor(Anchor anchor) => !anchor.HasFlagFast(Anchor.x1) && !anchor.HasFlagFast(Anchor.y1); private bool isCornerAnchor(Anchor anchor) => !anchor.HasFlag(Anchor.x1) && !anchor.HasFlag(Anchor.y1);
} }
} }

View File

@ -9,20 +9,31 @@ using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Game.Beatmaps.Timing; using osu.Game.Beatmaps.Timing;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Resources.Localisation.Web;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osuTK; using osuTK;
namespace osu.Game.Screens.Edit.Compose.Components.Timeline namespace osu.Game.Screens.Edit.Compose.Components.Timeline
{ {
public partial class TimelineBreak : CompositeDrawable public partial class TimelineBreak : CompositeDrawable, IHasContextMenu
{ {
public Bindable<BreakPeriod> Break { get; } = new Bindable<BreakPeriod>(); public Bindable<BreakPeriod> Break { get; } = new Bindable<BreakPeriod>();
public Action<BreakPeriod>? OnDeleted { get; init; }
private Box background = null!;
[Resolved]
private OsuColour colours { get; set; } = null!;
public TimelineBreak(BreakPeriod b) public TimelineBreak(BreakPeriod b)
{ {
Break.Value = b; Break.Value = b;
@ -42,7 +53,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = 5 }, Padding = new MarginPadding { Horizontal = 5 },
Child = new Box Child = background = new Box
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = colours.Gray5, Colour = colours.Gray5,
@ -77,6 +88,28 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
}, true); }, true);
} }
protected override bool OnHover(HoverEvent e)
{
updateState();
return true;
}
protected override void OnHoverLost(HoverLostEvent e)
{
updateState();
base.OnHoverLost(e);
}
private void updateState()
{
background.FadeColour(IsHovered ? colours.Gray6 : colours.Gray5, 400, Easing.OutQuint);
}
public MenuItem[] ContextMenuItems => new MenuItem[]
{
new OsuMenuItem(CommonStrings.ButtonsDelete, MenuItemType.Destructive, () => OnDeleted?.Invoke(Break.Value)),
};
private partial class DragHandle : FillFlowContainer private partial class DragHandle : FillFlowContainer
{ {
public Bindable<BreakPeriod> Break { get; } = new Bindable<BreakPeriod>(); public Bindable<BreakPeriod> Break { get; } = new Bindable<BreakPeriod>();

View File

@ -15,6 +15,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
[Resolved] [Resolved]
private Timeline timeline { get; set; } = null!; private Timeline timeline { get; set; } = null!;
[Resolved]
private IEditorChangeHandler? editorChangeHandler { get; set; }
/// <summary> /// <summary>
/// The visible time/position range of the timeline. /// The visible time/position range of the timeline.
/// </summary> /// </summary>
@ -71,7 +74,15 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
if (!shouldBeVisible(breakPeriod)) if (!shouldBeVisible(breakPeriod))
continue; continue;
Add(new TimelineBreak(breakPeriod)); Add(new TimelineBreak(breakPeriod)
{
OnDeleted = b =>
{
editorChangeHandler?.BeginChange();
breaks.Remove(b);
editorChangeHandler?.EndChange();
},
});
} }
} }

View File

@ -0,0 +1,89 @@
// 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 osu.Framework.Bindables;
using osu.Game.Utils;
using osuTK;
namespace osu.Game.Screens.Edit.Compose.Components
{
public partial class TriangularPositionSnapGrid : LinedPositionSnapGrid
{
/// <summary>
/// The spacing between grid lines of this <see cref="TriangularPositionSnapGrid"/>.
/// </summary>
public BindableFloat Spacing { get; } = new BindableFloat(1f)
{
MinValue = 0f,
};
/// <summary>
/// The rotation in degrees of the grid lines of this <see cref="TriangularPositionSnapGrid"/>.
/// </summary>
public BindableFloat GridLineRotation { get; } = new BindableFloat();
public TriangularPositionSnapGrid()
{
Spacing.BindValueChanged(_ => GridCache.Invalidate());
GridLineRotation.BindValueChanged(_ => GridCache.Invalidate());
}
private static readonly float sqrt3 = float.Sqrt(3);
private static readonly float sqrt3_over2 = sqrt3 / 2;
private static readonly float one_over_sqrt3 = 1 / sqrt3;
protected override void CreateContent()
{
var drawSize = DrawSize;
float stepSpacing = Spacing.Value * sqrt3_over2;
var step1 = GeometryUtils.RotateVector(new Vector2(stepSpacing, 0), -GridLineRotation.Value - 30);
var step2 = GeometryUtils.RotateVector(new Vector2(stepSpacing, 0), -GridLineRotation.Value - 90);
var step3 = GeometryUtils.RotateVector(new Vector2(stepSpacing, 0), -GridLineRotation.Value - 150);
GenerateGridLines(step1, drawSize);
GenerateGridLines(-step1, drawSize);
GenerateGridLines(step2, drawSize);
GenerateGridLines(-step2, drawSize);
GenerateGridLines(step3, drawSize);
GenerateGridLines(-step3, drawSize);
GenerateOutline(drawSize);
}
public override Vector2 GetSnappedPosition(Vector2 original)
{
Vector2 relativeToStart = GeometryUtils.RotateVector(original - StartPosition.Value, GridLineRotation.Value);
Vector2 hex = pixelToHex(relativeToStart);
return StartPosition.Value + GeometryUtils.RotateVector(hexToPixel(hex), -GridLineRotation.Value);
}
private Vector2 pixelToHex(Vector2 pixel)
{
float x = pixel.X / Spacing.Value;
float y = pixel.Y / Spacing.Value;
// Algorithm from Charles Chambers
// with modifications and comments by Chris Cox 2023
// <https://gitlab.com/chriscox/hex-coordinates>
float t = sqrt3 * y + 1; // scaled y, plus phase
float temp1 = MathF.Floor(t + x); // (y+x) diagonal, this calc needs floor
float temp2 = t - x; // (y-x) diagonal, no floor needed
float temp3 = 2 * x + 1; // scaled horizontal, no floor needed, needs +1 to get correct phase
float qf = (temp1 + temp3) / 3.0f; // pseudo x with fraction
float rf = (temp1 + temp2) / 3.0f; // pseudo y with fraction
float q = MathF.Floor(qf); // pseudo x, quantized and thus requires floor
float r = MathF.Floor(rf); // pseudo y, quantized and thus requires floor
return new Vector2(q, r);
}
private Vector2 hexToPixel(Vector2 hex)
{
// Taken from <https://www.redblobgames.com/grids/hexagons/#hex-to-pixel>
// with modifications for the different definition of size.
return new Vector2(Spacing.Value * (hex.X - hex.Y / 2), Spacing.Value * one_over_sqrt3 * 1.5f * hex.Y);
}
}
}

View File

@ -8,7 +8,6 @@ using System.Collections.Generic;
using JetBrains.Annotations; using JetBrains.Annotations;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Primitives;
@ -295,7 +294,7 @@ namespace osu.Game.Screens.Play
Drawable drawable = (Drawable)element; Drawable drawable = (Drawable)element;
// for now align some top components with the bottom-edge of the lowest top-anchored hud element. // for now align some top components with the bottom-edge of the lowest top-anchored hud element.
if (drawable.Anchor.HasFlagFast(Anchor.y0)) if (drawable.Anchor.HasFlag(Anchor.y0))
{ {
// health bars are excluded for the sake of hacky legacy skins which extend the health bar to take up the full screen area. // health bars are excluded for the sake of hacky legacy skins which extend the health bar to take up the full screen area.
if (element is LegacyHealthDisplay) if (element is LegacyHealthDisplay)
@ -305,20 +304,20 @@ namespace osu.Game.Screens.Play
bool isRelativeX = drawable.RelativeSizeAxes == Axes.X; bool isRelativeX = drawable.RelativeSizeAxes == Axes.X;
if (drawable.Anchor.HasFlagFast(Anchor.TopRight) || isRelativeX) if (drawable.Anchor.HasFlag(Anchor.TopRight) || isRelativeX)
{ {
if (lowestTopScreenSpaceRight == null || bottom > lowestTopScreenSpaceRight.Value) if (lowestTopScreenSpaceRight == null || bottom > lowestTopScreenSpaceRight.Value)
lowestTopScreenSpaceRight = bottom; lowestTopScreenSpaceRight = bottom;
} }
if (drawable.Anchor.HasFlagFast(Anchor.TopLeft) || isRelativeX) if (drawable.Anchor.HasFlag(Anchor.TopLeft) || isRelativeX)
{ {
if (lowestTopScreenSpaceLeft == null || bottom > lowestTopScreenSpaceLeft.Value) if (lowestTopScreenSpaceLeft == null || bottom > lowestTopScreenSpaceLeft.Value)
lowestTopScreenSpaceLeft = bottom; lowestTopScreenSpaceLeft = bottom;
} }
} }
// and align bottom-right components with the top-edge of the highest bottom-anchored hud element. // and align bottom-right components with the top-edge of the highest bottom-anchored hud element.
else if (drawable.Anchor.HasFlagFast(Anchor.BottomRight) || (drawable.Anchor.HasFlagFast(Anchor.y2) && drawable.RelativeSizeAxes == Axes.X)) else if (drawable.Anchor.HasFlag(Anchor.BottomRight) || (drawable.Anchor.HasFlag(Anchor.y2) && drawable.RelativeSizeAxes == Axes.X))
{ {
var topLeft = element.ScreenSpaceDrawQuad.TopLeft; var topLeft = element.ScreenSpaceDrawQuad.TopLeft;
if (highestBottomScreenSpace == null || topLeft.Y < highestBottomScreenSpace.Value.Y) if (highestBottomScreenSpace == null || topLeft.Y < highestBottomScreenSpace.Value.Y)

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
@ -282,7 +281,7 @@ namespace osu.Game.Screens.Ranking.Statistics
protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source) protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source)
{ {
if (invalidation.HasFlagFast(Invalidation.DrawSize)) if (invalidation.HasFlag(Invalidation.DrawSize))
{ {
if (lastDrawHeight != null && lastDrawHeight != DrawHeight) if (lastDrawHeight != null && lastDrawHeight != DrawHeight)
Scheduler.AddOnce(updateMetrics, false); Scheduler.AddOnce(updateMetrics, false);

View File

@ -10,7 +10,6 @@ using osu.Framework.Audio;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Caching; using osu.Framework.Caching;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Pooling; using osu.Framework.Graphics.Pooling;
@ -828,7 +827,7 @@ namespace osu.Game.Screens.Select
protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source) protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source)
{ {
// handles the vertical size of the carousel changing (ie. on window resize when aspect ratio has changed). // handles the vertical size of the carousel changing (ie. on window resize when aspect ratio has changed).
if (invalidation.HasFlagFast(Invalidation.DrawSize)) if (invalidation.HasFlag(Invalidation.DrawSize))
itemsCache.Invalidate(); itemsCache.Invalidate();
return base.OnInvalidate(invalidation, source); return base.OnInvalidate(invalidation, source);

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Configuration; using osu.Game.Configuration;
@ -19,9 +18,9 @@ namespace osu.Game.Skinning
// todo: can probably make this better via deserialisation directly using a common interface. // todo: can probably make this better via deserialisation directly using a common interface.
component.Position = drawableInfo.Position; component.Position = drawableInfo.Position;
component.Rotation = drawableInfo.Rotation; component.Rotation = drawableInfo.Rotation;
if (drawableInfo.Width is float width && width != 0 && (component as CompositeDrawable)?.AutoSizeAxes.HasFlagFast(Axes.X) != true) if (drawableInfo.Width is float width && width != 0 && (component as CompositeDrawable)?.AutoSizeAxes.HasFlag(Axes.X) != true)
component.Width = width; component.Width = width;
if (drawableInfo.Height is float height && height != 0 && (component as CompositeDrawable)?.AutoSizeAxes.HasFlagFast(Axes.Y) != true) if (drawableInfo.Height is float height && height != 0 && (component as CompositeDrawable)?.AutoSizeAxes.HasFlag(Axes.Y) != true)
component.Height = height; component.Height = height;
component.Scale = drawableInfo.Scale; component.Scale = drawableInfo.Scale;
component.Anchor = drawableInfo.Anchor; component.Anchor = drawableInfo.Anchor;

View File

@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Logging; using osu.Framework.Logging;
@ -68,10 +67,10 @@ namespace osu.Game.Skinning
Rotation = component.Rotation; Rotation = component.Rotation;
Scale = component.Scale; Scale = component.Scale;
if ((component as CompositeDrawable)?.AutoSizeAxes.HasFlagFast(Axes.X) != true) if ((component as CompositeDrawable)?.AutoSizeAxes.HasFlag(Axes.X) != true)
Width = component.Width; Width = component.Width;
if ((component as CompositeDrawable)?.AutoSizeAxes.HasFlagFast(Axes.Y) != true) if ((component as CompositeDrawable)?.AutoSizeAxes.HasFlag(Axes.Y) != true)
Height = component.Height; Height = component.Height;
Anchor = component.Anchor; Anchor = component.Anchor;

View File

@ -1,7 +1,6 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Extensions.EnumExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osuTK; using osuTK;
@ -22,18 +21,18 @@ namespace osu.Game.Storyboards
// Either flip horizontally or negative X scale, but not both. // Either flip horizontally or negative X scale, but not both.
if (flipH ^ (vectorScale.X < 0)) if (flipH ^ (vectorScale.X < 0))
{ {
if (origin.HasFlagFast(Anchor.x0)) if (origin.HasFlag(Anchor.x0))
origin = Anchor.x2 | (origin & (Anchor.y0 | Anchor.y1 | Anchor.y2)); origin = Anchor.x2 | (origin & (Anchor.y0 | Anchor.y1 | Anchor.y2));
else if (origin.HasFlagFast(Anchor.x2)) else if (origin.HasFlag(Anchor.x2))
origin = Anchor.x0 | (origin & (Anchor.y0 | Anchor.y1 | Anchor.y2)); origin = Anchor.x0 | (origin & (Anchor.y0 | Anchor.y1 | Anchor.y2));
} }
// Either flip vertically or negative Y scale, but not both. // Either flip vertically or negative Y scale, but not both.
if (flipV ^ (vectorScale.Y < 0)) if (flipV ^ (vectorScale.Y < 0))
{ {
if (origin.HasFlagFast(Anchor.y0)) if (origin.HasFlag(Anchor.y0))
origin = Anchor.y2 | (origin & (Anchor.x0 | Anchor.x1 | Anchor.x2)); origin = Anchor.y2 | (origin & (Anchor.x0 | Anchor.x1 | Anchor.x2));
else if (origin.HasFlagFast(Anchor.y2)) else if (origin.HasFlag(Anchor.y2))
origin = Anchor.y0 | (origin & (Anchor.x0 | Anchor.x1 | Anchor.x2)); origin = Anchor.y0 | (origin & (Anchor.x0 | Anchor.x1 | Anchor.x2));
} }

View File

@ -35,7 +35,7 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Realm" Version="11.5.0" /> <PackageReference Include="Realm" Version="11.5.0" />
<PackageReference Include="ppy.osu.Framework" Version="2024.627.0" /> <PackageReference Include="ppy.osu.Framework" Version="2024.702.0" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2024.622.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2024.622.0" />
<PackageReference Include="Sentry" Version="4.3.0" /> <PackageReference Include="Sentry" Version="4.3.0" />
<!-- Held back due to 0.34.0 failing AOT compilation on ZstdSharp.dll dependency. --> <!-- Held back due to 0.34.0 failing AOT compilation on ZstdSharp.dll dependency. -->

View File

@ -23,6 +23,6 @@
<RuntimeIdentifier>iossimulator-x64</RuntimeIdentifier> <RuntimeIdentifier>iossimulator-x64</RuntimeIdentifier>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ppy.osu.Framework.iOS" Version="2024.627.0" /> <PackageReference Include="ppy.osu.Framework.iOS" Version="2024.702.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>