mirror of
https://github.com/ppy/osu.git
synced 2025-02-05 16:43:21 +08:00
Merge branch 'master' into slider-anchor-type-switching
This commit is contained in:
commit
88375694a3
1
.gitignore
vendored
1
.gitignore
vendored
@ -265,6 +265,7 @@ __pycache__/
|
|||||||
.idea/**/usage.statistics.xml
|
.idea/**/usage.statistics.xml
|
||||||
.idea/**/dictionaries
|
.idea/**/dictionaries
|
||||||
.idea/**/shelf
|
.idea/**/shelf
|
||||||
|
.idea/*/.idea/projectSettingsUpdater.xml
|
||||||
|
|
||||||
# Generated files
|
# Generated files
|
||||||
.idea/**/contentModel.xml
|
.idea/**/contentModel.xml
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="RiderProjectSettingsUpdater">
|
|
||||||
<option name="vcsConfiguration" value="2" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="RiderProjectSettingsUpdater">
|
|
||||||
<option name="vcsConfiguration" value="2" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="RiderProjectSettingsUpdater">
|
|
||||||
<option name="vcsConfiguration" value="2" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="RiderProjectSettingsUpdater">
|
|
||||||
<option name="vcsConfiguration" value="2" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
@ -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.
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2024.618.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.
|
||||||
|
@ -0,0 +1,66 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
using osuTK;
|
||||||
|
using osuTK.Input;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Catch.Tests.Editor
|
||||||
|
{
|
||||||
|
public partial class TestSceneCatchEditorSaving : EditorSavingTestScene
|
||||||
|
{
|
||||||
|
protected override Ruleset CreateRuleset() => new CatchRuleset();
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestCatchJuiceStreamTickCorrect()
|
||||||
|
{
|
||||||
|
AddStep("enter timing mode", () => InputManager.Key(Key.F3));
|
||||||
|
AddStep("add timing point", () => EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint()));
|
||||||
|
AddStep("enter compose mode", () => InputManager.Key(Key.F1));
|
||||||
|
|
||||||
|
Vector2 startPoint = Vector2.Zero;
|
||||||
|
float increment = 0;
|
||||||
|
|
||||||
|
AddUntilStep("wait for playfield", () => this.ChildrenOfType<CatchPlayfield>().FirstOrDefault()?.IsLoaded == true);
|
||||||
|
AddStep("move to centre", () =>
|
||||||
|
{
|
||||||
|
var playfield = this.ChildrenOfType<CatchPlayfield>().Single();
|
||||||
|
startPoint = playfield.ScreenSpaceDrawQuad.Centre + new Vector2(0, playfield.ScreenSpaceDrawQuad.Height / 3);
|
||||||
|
increment = playfield.ScreenSpaceDrawQuad.Height / 10;
|
||||||
|
InputManager.MoveMouseTo(startPoint);
|
||||||
|
});
|
||||||
|
AddStep("choose juice stream placing tool", () => InputManager.Key(Key.Number3));
|
||||||
|
AddStep("start placement", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
|
AddStep("move to next", () => InputManager.MoveMouseTo(startPoint + new Vector2(2 * increment, -increment)));
|
||||||
|
AddStep("add node", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
|
AddStep("move to next", () => InputManager.MoveMouseTo(startPoint + new Vector2(-2 * increment, -2 * increment)));
|
||||||
|
AddStep("add node", () => InputManager.Click(MouseButton.Left));
|
||||||
|
|
||||||
|
AddStep("move to next", () => InputManager.MoveMouseTo(startPoint + new Vector2(0, -3 * increment)));
|
||||||
|
AddStep("end placement", () => InputManager.Click(MouseButton.Right));
|
||||||
|
|
||||||
|
AddUntilStep("juice stream placed", () => EditorBeatmap.HitObjects, () => Has.Count.EqualTo(1));
|
||||||
|
|
||||||
|
int largeDropletCount = 0, tinyDropletCount = 0;
|
||||||
|
AddStep("store droplet count", () =>
|
||||||
|
{
|
||||||
|
largeDropletCount = EditorBeatmap.HitObjects[0].NestedHitObjects.Count(t => t.GetType() == typeof(Droplet));
|
||||||
|
tinyDropletCount = EditorBeatmap.HitObjects[0].NestedHitObjects.Count(t => t.GetType() == typeof(TinyDroplet));
|
||||||
|
});
|
||||||
|
|
||||||
|
SaveEditor();
|
||||||
|
ReloadEditorToSameBeatmap();
|
||||||
|
|
||||||
|
AddAssert("large droplet count is the same", () => EditorBeatmap.HitObjects[0].NestedHitObjects.Count(t => t.GetType() == typeof(Droplet)), () => Is.EqualTo(largeDropletCount));
|
||||||
|
AddAssert("tiny droplet count is the same", () => EditorBeatmap.HitObjects[0].NestedHitObjects.Count(t => t.GetType() == typeof(TinyDroplet)), () => Is.EqualTo(tinyDropletCount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
|||||||
LegacyConvertedY = yPositionData?.Y ?? CatchHitObject.DEFAULT_LEGACY_CONVERT_Y,
|
LegacyConvertedY = yPositionData?.Y ?? CatchHitObject.DEFAULT_LEGACY_CONVERT_Y,
|
||||||
// prior to v8, speed multipliers don't adjust for how many ticks are generated over the same distance.
|
// prior to v8, speed multipliers don't adjust for how many ticks are generated over the same distance.
|
||||||
// this results in more (or less) ticks being generated in <v8 maps for the same time duration.
|
// this results in more (or less) ticks being generated in <v8 maps for the same time duration.
|
||||||
TickDistanceMultiplier = beatmap.BeatmapInfo.BeatmapVersion < 8 ? 1 : ((LegacyControlPointInfo)beatmap.ControlPointInfo).DifficultyPointAt(obj.StartTime).SliderVelocity,
|
TickDistanceMultiplier = beatmap.BeatmapInfo.BeatmapVersion < 8 ? 1f / ((LegacyControlPointInfo)beatmap.ControlPointInfo).DifficultyPointAt(obj.StartTime).SliderVelocity : 1,
|
||||||
SliderVelocityMultiplier = sliderVelocityData?.SliderVelocityMultiplier ?? 1
|
SliderVelocityMultiplier = sliderVelocityData?.SliderVelocityMultiplier ?? 1
|
||||||
}.Yield();
|
}.Yield();
|
||||||
|
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -12,6 +12,7 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Legacy;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Objects
|
namespace osu.Game.Rulesets.Catch.Objects
|
||||||
@ -46,16 +47,10 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
public double TickDistanceMultiplier = 1;
|
public double TickDistanceMultiplier = 1;
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
private double velocityFactor;
|
public double Velocity { get; private set; }
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
private double tickDistanceFactor;
|
public double TickDistance { get; private set; }
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public double Velocity => velocityFactor * SliderVelocityMultiplier;
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public double TickDistance => tickDistanceFactor * TickDistanceMultiplier;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The length of one span of this <see cref="JuiceStream"/>.
|
/// The length of one span of this <see cref="JuiceStream"/>.
|
||||||
@ -68,8 +63,13 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
|
|
||||||
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
|
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
|
||||||
|
|
||||||
velocityFactor = base_scoring_distance * difficulty.SliderMultiplier / timingPoint.BeatLength;
|
Velocity = base_scoring_distance * difficulty.SliderMultiplier / LegacyRulesetExtensions.GetPrecisionAdjustedBeatLength(this, timingPoint, CatchRuleset.SHORT_NAME);
|
||||||
tickDistanceFactor = base_scoring_distance * difficulty.SliderMultiplier / difficulty.SliderTickRate;
|
|
||||||
|
// WARNING: this is intentionally not computed as `BASE_SCORING_DISTANCE * difficulty.SliderMultiplier`
|
||||||
|
// for backwards compatibility reasons (intentionally introducing floating point errors to match stable).
|
||||||
|
double scoringDistance = Velocity * timingPoint.BeatLength;
|
||||||
|
|
||||||
|
TickDistance = scoringDistance / difficulty.SliderTickRate * TickDistanceMultiplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
|
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
// 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 NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.Timing;
|
||||||
using osu.Game.Rulesets.Mania.Mods;
|
using osu.Game.Rulesets.Mania.Mods;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
@ -17,5 +19,22 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods
|
|||||||
Mod = new ManiaModInvert(),
|
Mod = new ManiaModInvert(),
|
||||||
PassCondition = () => Player.ScoreProcessor.JudgedHits >= 2
|
PassCondition = () => Player.ScoreProcessor.JudgedHits >= 2
|
||||||
});
|
});
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBreaksPreservedOnOriginalBeatmap()
|
||||||
|
{
|
||||||
|
var beatmap = CreateBeatmap(new ManiaRuleset().RulesetInfo);
|
||||||
|
beatmap.Breaks.Clear();
|
||||||
|
beatmap.Breaks.Add(new BreakPeriod(0, 1000));
|
||||||
|
|
||||||
|
var workingBeatmap = new FlatWorkingBeatmap(beatmap);
|
||||||
|
|
||||||
|
var playableWithInvert = workingBeatmap.GetPlayableBeatmap(new ManiaRuleset().RulesetInfo, new[] { new ManiaModInvert() });
|
||||||
|
Assert.That(playableWithInvert.Breaks.Count, Is.Zero);
|
||||||
|
|
||||||
|
var playableWithoutInvert = workingBeatmap.GetPlayableBeatmap(new ManiaRuleset().RulesetInfo);
|
||||||
|
Assert.That(playableWithoutInvert.Breaks.Count, Is.Not.Zero);
|
||||||
|
Assert.That(playableWithoutInvert.Breaks[0], Is.EqualTo(new BreakPeriod(0, 1000)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
643
osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModNoRelease.cs
Normal file
643
osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModNoRelease.cs
Normal file
@ -0,0 +1,643 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Screens;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Replays;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Mania.Mods;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Rulesets.Mania.Replays;
|
||||||
|
using osu.Game.Rulesets.Mania.Scoring;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Replays;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Tests.Mods
|
||||||
|
{
|
||||||
|
public partial class TestSceneManiaModNoRelease : RateAdjustedBeatmapTestScene
|
||||||
|
{
|
||||||
|
private const double time_before_head = 250;
|
||||||
|
private const double time_head = 1500;
|
||||||
|
private const double time_during_hold_1 = 2500;
|
||||||
|
private const double time_tail = 4000;
|
||||||
|
private const double time_after_tail = 5250;
|
||||||
|
|
||||||
|
private List<JudgementResult> judgementResults = new List<JudgementResult>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// -----[ ]-----
|
||||||
|
/// o o
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestNoInput()
|
||||||
|
{
|
||||||
|
performTest(new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new ManiaReplayFrame(time_before_head),
|
||||||
|
new ManiaReplayFrame(time_after_tail),
|
||||||
|
});
|
||||||
|
|
||||||
|
assertHeadJudgement(HitResult.Miss);
|
||||||
|
assertTailJudgement(HitResult.Miss);
|
||||||
|
assertNoteJudgement(HitResult.IgnoreMiss);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// -----[ ]-----
|
||||||
|
/// x o
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestCorrectInput()
|
||||||
|
{
|
||||||
|
performTest(new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new ManiaReplayFrame(time_head, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(time_tail),
|
||||||
|
});
|
||||||
|
|
||||||
|
assertHeadJudgement(HitResult.Perfect);
|
||||||
|
assertTailJudgement(HitResult.Perfect);
|
||||||
|
assertNoteJudgement(HitResult.IgnoreHit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// -----[ ]-----
|
||||||
|
/// x o
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestLateRelease()
|
||||||
|
{
|
||||||
|
performTest(new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new ManiaReplayFrame(time_head, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(time_after_tail),
|
||||||
|
});
|
||||||
|
|
||||||
|
assertHeadJudgement(HitResult.Perfect);
|
||||||
|
assertTailJudgement(HitResult.Perfect);
|
||||||
|
assertNoteJudgement(HitResult.IgnoreHit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// -----[ ]-----
|
||||||
|
/// x o
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestPressTooEarlyAndReleaseAfterTail()
|
||||||
|
{
|
||||||
|
performTest(new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new ManiaReplayFrame(time_before_head, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(time_after_tail, ManiaAction.Key1),
|
||||||
|
});
|
||||||
|
|
||||||
|
assertHeadJudgement(HitResult.Miss);
|
||||||
|
assertTailJudgement(HitResult.Miss);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// -----[ ]-----
|
||||||
|
/// x o
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestPressTooEarlyAndReleaseAtTail()
|
||||||
|
{
|
||||||
|
performTest(new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new ManiaReplayFrame(time_before_head, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(time_tail),
|
||||||
|
});
|
||||||
|
|
||||||
|
assertHeadJudgement(HitResult.Miss);
|
||||||
|
assertTailJudgement(HitResult.Miss);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// -----[ ]-----
|
||||||
|
/// xo x o
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestPressTooEarlyThenPressAtStartAndReleaseAfterTail()
|
||||||
|
{
|
||||||
|
performTest(new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new ManiaReplayFrame(time_before_head, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(time_before_head + 10),
|
||||||
|
new ManiaReplayFrame(time_head, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(time_after_tail),
|
||||||
|
});
|
||||||
|
|
||||||
|
assertHeadJudgement(HitResult.Perfect);
|
||||||
|
assertTailJudgement(HitResult.Perfect);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// -----[ ]-----
|
||||||
|
/// xo x o
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestPressTooEarlyThenPressAtStartAndReleaseAtTail()
|
||||||
|
{
|
||||||
|
performTest(new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new ManiaReplayFrame(time_before_head, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(time_before_head + 10),
|
||||||
|
new ManiaReplayFrame(time_head, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(time_tail),
|
||||||
|
});
|
||||||
|
|
||||||
|
assertHeadJudgement(HitResult.Perfect);
|
||||||
|
assertTailJudgement(HitResult.Perfect);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// -----[ ]-----
|
||||||
|
/// xo o
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestPressAtStartAndBreak()
|
||||||
|
{
|
||||||
|
performTest(new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new ManiaReplayFrame(time_head, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(time_head + 10),
|
||||||
|
new ManiaReplayFrame(time_after_tail),
|
||||||
|
});
|
||||||
|
|
||||||
|
assertHeadJudgement(HitResult.Perfect);
|
||||||
|
assertTailJudgement(HitResult.Miss);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// -----[ ]-----
|
||||||
|
/// xox o
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestPressAtStartThenReleaseAndImmediatelyRepress()
|
||||||
|
{
|
||||||
|
performTest(new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new ManiaReplayFrame(time_head, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(time_head + 1),
|
||||||
|
new ManiaReplayFrame(time_head + 2, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(time_tail),
|
||||||
|
});
|
||||||
|
|
||||||
|
assertHeadJudgement(HitResult.Perfect);
|
||||||
|
assertComboAtJudgement(0, 1);
|
||||||
|
assertTailJudgement(HitResult.Meh);
|
||||||
|
assertComboAtJudgement(1, 0);
|
||||||
|
assertComboAtJudgement(3, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// -----[ ]-----
|
||||||
|
/// xo x o
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestPressAtStartThenBreakThenRepressAndReleaseAfterTail()
|
||||||
|
{
|
||||||
|
performTest(new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new ManiaReplayFrame(time_head, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(time_head + 10),
|
||||||
|
new ManiaReplayFrame(time_during_hold_1, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(time_after_tail),
|
||||||
|
});
|
||||||
|
|
||||||
|
assertHeadJudgement(HitResult.Perfect);
|
||||||
|
assertTailJudgement(HitResult.Meh);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// -----[ ]-----
|
||||||
|
/// xo x o o
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestPressAtStartThenBreakThenRepressAndReleaseAtTail()
|
||||||
|
{
|
||||||
|
performTest(new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new ManiaReplayFrame(time_head, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(time_head + 10),
|
||||||
|
new ManiaReplayFrame(time_during_hold_1, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(time_tail),
|
||||||
|
});
|
||||||
|
|
||||||
|
assertHeadJudgement(HitResult.Perfect);
|
||||||
|
assertTailJudgement(HitResult.Meh);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// -----[ ]-----
|
||||||
|
/// x o
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestPressDuringNoteAndReleaseAfterTail()
|
||||||
|
{
|
||||||
|
performTest(new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new ManiaReplayFrame(time_during_hold_1, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(time_after_tail),
|
||||||
|
});
|
||||||
|
|
||||||
|
assertHeadJudgement(HitResult.Miss);
|
||||||
|
assertTailJudgement(HitResult.Meh);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// -----[ ]-----
|
||||||
|
/// x o o
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestPressDuringNoteAndReleaseAtTail()
|
||||||
|
{
|
||||||
|
performTest(new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new ManiaReplayFrame(time_during_hold_1, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(time_tail),
|
||||||
|
});
|
||||||
|
|
||||||
|
assertHeadJudgement(HitResult.Miss);
|
||||||
|
assertTailJudgement(HitResult.Meh);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// -----[ ]--------------
|
||||||
|
/// xo
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestPressAndReleaseAfterTailWithCloseByHead()
|
||||||
|
{
|
||||||
|
const int duration = 30;
|
||||||
|
|
||||||
|
var beatmap = new Beatmap<ManiaHitObject>
|
||||||
|
{
|
||||||
|
HitObjects =
|
||||||
|
{
|
||||||
|
// hold note is very short, to make the head still in range
|
||||||
|
new HoldNote
|
||||||
|
{
|
||||||
|
StartTime = time_head,
|
||||||
|
Duration = duration,
|
||||||
|
Column = 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
BeatmapInfo =
|
||||||
|
{
|
||||||
|
Difficulty = new BeatmapDifficulty { SliderTickRate = 4 },
|
||||||
|
Ruleset = new ManiaRuleset().RulesetInfo
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
performTest(new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new ManiaReplayFrame(time_head + duration + 60, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(time_head + duration + 70),
|
||||||
|
}, beatmap);
|
||||||
|
|
||||||
|
assertHeadJudgement(HitResult.Ok);
|
||||||
|
assertTailJudgement(HitResult.Perfect);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// -----[ ]-O-------------
|
||||||
|
/// xo o
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestPressAndReleaseJustBeforeTailWithNearbyNoteAndCloseByHead()
|
||||||
|
{
|
||||||
|
Note note;
|
||||||
|
|
||||||
|
const int duration = 50;
|
||||||
|
|
||||||
|
var beatmap = new Beatmap<ManiaHitObject>
|
||||||
|
{
|
||||||
|
HitObjects =
|
||||||
|
{
|
||||||
|
// hold note is very short, to make the head still in range
|
||||||
|
new HoldNote
|
||||||
|
{
|
||||||
|
StartTime = time_head,
|
||||||
|
Duration = duration,
|
||||||
|
Column = 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Next note within tail lenience
|
||||||
|
note = new Note
|
||||||
|
{
|
||||||
|
StartTime = time_head + duration + 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
BeatmapInfo =
|
||||||
|
{
|
||||||
|
Difficulty = new BeatmapDifficulty { SliderTickRate = 4 },
|
||||||
|
Ruleset = new ManiaRuleset().RulesetInfo
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
performTest(new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new ManiaReplayFrame(time_head + duration, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(time_head + duration + 10),
|
||||||
|
}, beatmap);
|
||||||
|
|
||||||
|
assertHeadJudgement(HitResult.Good);
|
||||||
|
assertTailJudgement(HitResult.Perfect);
|
||||||
|
|
||||||
|
assertHitObjectJudgement(note, HitResult.Miss);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// -----[ ]--O--
|
||||||
|
/// xo o
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestPressAndReleaseJustBeforeTailWithNearbyNote()
|
||||||
|
{
|
||||||
|
Note note;
|
||||||
|
|
||||||
|
var beatmap = new Beatmap<ManiaHitObject>
|
||||||
|
{
|
||||||
|
HitObjects =
|
||||||
|
{
|
||||||
|
new HoldNote
|
||||||
|
{
|
||||||
|
StartTime = time_head,
|
||||||
|
Duration = time_tail - time_head,
|
||||||
|
Column = 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Next note within tail lenience
|
||||||
|
note = new Note
|
||||||
|
{
|
||||||
|
StartTime = time_tail + 50
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
BeatmapInfo =
|
||||||
|
{
|
||||||
|
Difficulty = new BeatmapDifficulty { SliderTickRate = 4 },
|
||||||
|
Ruleset = new ManiaRuleset().RulesetInfo
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
performTest(new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new ManiaReplayFrame(time_tail - 10, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(time_tail),
|
||||||
|
}, beatmap);
|
||||||
|
|
||||||
|
assertHeadJudgement(HitResult.Miss);
|
||||||
|
assertTailJudgement(HitResult.Miss);
|
||||||
|
|
||||||
|
assertHitObjectJudgement(note, HitResult.Good);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// -----[ ]-----
|
||||||
|
/// xo
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestPressAndReleaseJustAfterTail()
|
||||||
|
{
|
||||||
|
performTest(new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new ManiaReplayFrame(time_tail + 20, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(time_tail + 30),
|
||||||
|
});
|
||||||
|
|
||||||
|
assertHeadJudgement(HitResult.Miss);
|
||||||
|
assertTailJudgement(HitResult.Meh);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// -----[ ]--O--
|
||||||
|
/// xo o
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestPressAndReleaseJustAfterTailWithNearbyNote()
|
||||||
|
{
|
||||||
|
// Next note within tail lenience
|
||||||
|
Note note = new Note { StartTime = time_tail + 50 };
|
||||||
|
|
||||||
|
var beatmap = new Beatmap<ManiaHitObject>
|
||||||
|
{
|
||||||
|
HitObjects =
|
||||||
|
{
|
||||||
|
new HoldNote
|
||||||
|
{
|
||||||
|
StartTime = time_head,
|
||||||
|
Duration = time_tail - time_head,
|
||||||
|
Column = 0,
|
||||||
|
},
|
||||||
|
note
|
||||||
|
},
|
||||||
|
BeatmapInfo =
|
||||||
|
{
|
||||||
|
Difficulty = new BeatmapDifficulty { SliderTickRate = 4 },
|
||||||
|
Ruleset = new ManiaRuleset().RulesetInfo
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
performTest(new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new ManiaReplayFrame(time_tail + 10, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(time_tail + 20),
|
||||||
|
}, beatmap);
|
||||||
|
|
||||||
|
assertHeadJudgement(HitResult.Miss);
|
||||||
|
assertTailJudgement(HitResult.Miss);
|
||||||
|
|
||||||
|
assertHitObjectJudgement(note, HitResult.Great);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// -----[ ]-----
|
||||||
|
/// xo o
|
||||||
|
/// </summary>
|
||||||
|
[Test]
|
||||||
|
public void TestPressAndReleaseAtTail()
|
||||||
|
{
|
||||||
|
performTest(new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new ManiaReplayFrame(time_tail, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(time_tail + 10),
|
||||||
|
});
|
||||||
|
|
||||||
|
assertHeadJudgement(HitResult.Miss);
|
||||||
|
assertTailJudgement(HitResult.Meh);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestMissReleaseAndHitSecondRelease()
|
||||||
|
{
|
||||||
|
var windows = new ManiaHitWindows();
|
||||||
|
windows.SetDifficulty(10);
|
||||||
|
|
||||||
|
var beatmap = new Beatmap<ManiaHitObject>
|
||||||
|
{
|
||||||
|
HitObjects =
|
||||||
|
{
|
||||||
|
new HoldNote
|
||||||
|
{
|
||||||
|
StartTime = 1000,
|
||||||
|
Duration = 500,
|
||||||
|
Column = 0,
|
||||||
|
},
|
||||||
|
new HoldNote
|
||||||
|
{
|
||||||
|
StartTime = 1000 + 500 + windows.WindowFor(HitResult.Miss) + 10,
|
||||||
|
Duration = 500,
|
||||||
|
Column = 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
BeatmapInfo =
|
||||||
|
{
|
||||||
|
Difficulty = new BeatmapDifficulty
|
||||||
|
{
|
||||||
|
SliderTickRate = 4,
|
||||||
|
OverallDifficulty = 10,
|
||||||
|
},
|
||||||
|
Ruleset = new ManiaRuleset().RulesetInfo
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
performTest(new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new ManiaReplayFrame(beatmap.HitObjects[1].StartTime, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(beatmap.HitObjects[1].GetEndTime()),
|
||||||
|
}, beatmap);
|
||||||
|
|
||||||
|
AddAssert("first hold note missed", () => judgementResults.Where(j => beatmap.HitObjects[0].NestedHitObjects.Contains(j.HitObject))
|
||||||
|
.All(j => !j.Type.IsHit()));
|
||||||
|
|
||||||
|
AddAssert("second hold note hit", () => judgementResults.Where(j => beatmap.HitObjects[1].NestedHitObjects.Contains(j.HitObject))
|
||||||
|
.All(j => j.Type.IsHit()));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestZeroLength()
|
||||||
|
{
|
||||||
|
var beatmap = new Beatmap<ManiaHitObject>
|
||||||
|
{
|
||||||
|
HitObjects =
|
||||||
|
{
|
||||||
|
new HoldNote
|
||||||
|
{
|
||||||
|
StartTime = 1000,
|
||||||
|
Duration = 0,
|
||||||
|
Column = 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo },
|
||||||
|
};
|
||||||
|
|
||||||
|
performTest(new List<ReplayFrame>
|
||||||
|
{
|
||||||
|
new ManiaReplayFrame(beatmap.HitObjects[0].StartTime, ManiaAction.Key1),
|
||||||
|
new ManiaReplayFrame(beatmap.HitObjects[0].GetEndTime() + 1),
|
||||||
|
}, beatmap);
|
||||||
|
|
||||||
|
AddAssert("hold note hit", () => judgementResults.Where(j => beatmap.HitObjects[0].NestedHitObjects.Contains(j.HitObject))
|
||||||
|
.All(j => j.Type.IsHit()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertHitObjectJudgement(HitObject hitObject, HitResult result)
|
||||||
|
=> AddAssert($"object judged as {result}", () => judgementResults.First(j => j.HitObject == hitObject).Type, () => Is.EqualTo(result));
|
||||||
|
|
||||||
|
private void assertHeadJudgement(HitResult result)
|
||||||
|
=> AddAssert($"head judged as {result}", () => judgementResults.First(j => j.HitObject is Note).Type, () => Is.EqualTo(result));
|
||||||
|
|
||||||
|
private void assertTailJudgement(HitResult result)
|
||||||
|
=> AddAssert($"tail judged as {result}", () => judgementResults.Single(j => j.HitObject is TailNote).Type, () => Is.EqualTo(result));
|
||||||
|
|
||||||
|
private void assertNoteJudgement(HitResult result)
|
||||||
|
=> AddAssert($"hold note judged as {result}", () => judgementResults.Single(j => j.HitObject is HoldNote).Type, () => Is.EqualTo(result));
|
||||||
|
|
||||||
|
private void assertComboAtJudgement(int judgementIndex, int combo)
|
||||||
|
=> AddAssert($"combo at judgement {judgementIndex} is {combo}", () => judgementResults.ElementAt(judgementIndex).ComboAfterJudgement, () => Is.EqualTo(combo));
|
||||||
|
|
||||||
|
private ScoreAccessibleReplayPlayer currentPlayer = null!;
|
||||||
|
|
||||||
|
private void performTest(List<ReplayFrame> frames, Beatmap<ManiaHitObject>? beatmap = null)
|
||||||
|
{
|
||||||
|
if (beatmap == null)
|
||||||
|
{
|
||||||
|
beatmap = new Beatmap<ManiaHitObject>
|
||||||
|
{
|
||||||
|
HitObjects =
|
||||||
|
{
|
||||||
|
new HoldNote
|
||||||
|
{
|
||||||
|
StartTime = time_head,
|
||||||
|
Duration = time_tail - time_head,
|
||||||
|
Column = 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
BeatmapInfo =
|
||||||
|
{
|
||||||
|
Difficulty = new BeatmapDifficulty { SliderTickRate = 4 },
|
||||||
|
Ruleset = new ManiaRuleset().RulesetInfo,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
beatmap.ControlPointInfo.Add(0, new EffectControlPoint { ScrollSpeed = 0.1f });
|
||||||
|
}
|
||||||
|
|
||||||
|
AddStep("load player", () =>
|
||||||
|
{
|
||||||
|
SelectedMods.Value = new List<Mod>
|
||||||
|
{
|
||||||
|
new ManiaModNoRelease()
|
||||||
|
};
|
||||||
|
|
||||||
|
Beatmap.Value = CreateWorkingBeatmap(beatmap);
|
||||||
|
|
||||||
|
var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } });
|
||||||
|
|
||||||
|
p.OnLoadComplete += _ =>
|
||||||
|
{
|
||||||
|
p.ScoreProcessor.NewJudgement += result =>
|
||||||
|
{
|
||||||
|
if (currentPlayer == p) judgementResults.Add(result);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
LoadScreen(currentPlayer = p);
|
||||||
|
judgementResults = new List<JudgementResult>();
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0);
|
||||||
|
AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen());
|
||||||
|
|
||||||
|
AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private partial class ScoreAccessibleReplayPlayer : ReplayPlayer
|
||||||
|
{
|
||||||
|
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
|
||||||
|
|
||||||
|
protected override bool PauseOnFocusLost => false;
|
||||||
|
|
||||||
|
public ScoreAccessibleReplayPlayer(Score score)
|
||||||
|
: base(score, new PlayerConfiguration
|
||||||
|
{
|
||||||
|
AllowPause = false,
|
||||||
|
ShowResults = false,
|
||||||
|
})
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -474,8 +474,8 @@ namespace osu.Game.Rulesets.Mania.Tests
|
|||||||
AddAssert("first hold note missed", () => judgementResults.Where(j => beatmap.HitObjects[0].NestedHitObjects.Contains(j.HitObject))
|
AddAssert("first hold note missed", () => judgementResults.Where(j => beatmap.HitObjects[0].NestedHitObjects.Contains(j.HitObject))
|
||||||
.All(j => !j.Type.IsHit()));
|
.All(j => !j.Type.IsHit()));
|
||||||
|
|
||||||
AddAssert("second hold note missed", () => judgementResults.Where(j => beatmap.HitObjects[1].NestedHitObjects.Contains(j.HitObject))
|
AddAssert("second hold note hit", () => judgementResults.Where(j => beatmap.HitObjects[1].NestedHitObjects.Contains(j.HitObject))
|
||||||
.All(j => j.Type.IsHit()));
|
.All(j => j.Type.IsHit()));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -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));
|
||||||
|
@ -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();
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,6 +240,7 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
new ManiaModEasy(),
|
new ManiaModEasy(),
|
||||||
new ManiaModNoFail(),
|
new ManiaModNoFail(),
|
||||||
new MultiMod(new ManiaModHalfTime(), new ManiaModDaycore()),
|
new MultiMod(new ManiaModHalfTime(), new ManiaModDaycore()),
|
||||||
|
new ManiaModNoRelease(),
|
||||||
};
|
};
|
||||||
|
|
||||||
case ModType.DifficultyIncrease:
|
case ModType.DifficultyIncrease:
|
||||||
|
107
osu.Game.Rulesets.Mania/Mods/ManiaModNoRelease.cs
Normal file
107
osu.Game.Rulesets.Mania/Mods/ManiaModNoRelease.cs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Rulesets.UI;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
|
{
|
||||||
|
public partial class ManiaModNoRelease : Mod, IApplicableAfterBeatmapConversion, IApplicableToDrawableRuleset<ManiaHitObject>
|
||||||
|
{
|
||||||
|
public override string Name => "No Release";
|
||||||
|
|
||||||
|
public override string Acronym => "NR";
|
||||||
|
|
||||||
|
public override LocalisableString Description => "No more timing the end of hold notes.";
|
||||||
|
|
||||||
|
public override double ScoreMultiplier => 0.9;
|
||||||
|
|
||||||
|
public override ModType Type => ModType.DifficultyReduction;
|
||||||
|
|
||||||
|
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||||
|
{
|
||||||
|
var maniaBeatmap = (ManiaBeatmap)beatmap;
|
||||||
|
var hitObjects = maniaBeatmap.HitObjects.Select(obj =>
|
||||||
|
{
|
||||||
|
if (obj is HoldNote hold)
|
||||||
|
return new NoReleaseHoldNote(hold);
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}).ToList();
|
||||||
|
|
||||||
|
maniaBeatmap.HitObjects = hitObjects;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyToDrawableRuleset(DrawableRuleset<ManiaHitObject> drawableRuleset)
|
||||||
|
{
|
||||||
|
var maniaRuleset = (DrawableManiaRuleset)drawableRuleset;
|
||||||
|
|
||||||
|
foreach (var stage in maniaRuleset.Playfield.Stages)
|
||||||
|
{
|
||||||
|
foreach (var column in stage.Columns)
|
||||||
|
{
|
||||||
|
column.RegisterPool<NoReleaseTailNote, NoReleaseDrawableHoldNoteTail>(10, 50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private partial class NoReleaseDrawableHoldNoteTail : DrawableHoldNoteTail
|
||||||
|
{
|
||||||
|
protected override void CheckForResult(bool userTriggered, double timeOffset)
|
||||||
|
{
|
||||||
|
// apply perfect once the tail is reached
|
||||||
|
if (HoldNote.HoldStartTime != null && timeOffset >= 0)
|
||||||
|
ApplyResult(GetCappedResult(HitResult.Perfect));
|
||||||
|
else
|
||||||
|
base.CheckForResult(userTriggered, timeOffset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class NoReleaseTailNote : TailNote
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private class NoReleaseHoldNote : HoldNote
|
||||||
|
{
|
||||||
|
public NoReleaseHoldNote(HoldNote hold)
|
||||||
|
{
|
||||||
|
StartTime = hold.StartTime;
|
||||||
|
Duration = hold.Duration;
|
||||||
|
Column = hold.Column;
|
||||||
|
NodeSamples = hold.NodeSamples;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void CreateNestedHitObjects(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
AddNested(Head = new HeadNote
|
||||||
|
{
|
||||||
|
StartTime = StartTime,
|
||||||
|
Column = Column,
|
||||||
|
Samples = GetNodeSamples(0),
|
||||||
|
});
|
||||||
|
|
||||||
|
AddNested(Tail = new NoReleaseTailNote
|
||||||
|
{
|
||||||
|
StartTime = EndTime,
|
||||||
|
Column = Column,
|
||||||
|
Samples = GetNodeSamples((NodeSamples?.Count - 1) ?? 1),
|
||||||
|
});
|
||||||
|
|
||||||
|
AddNested(Body = new HoldNoteBody
|
||||||
|
{
|
||||||
|
StartTime = StartTime,
|
||||||
|
Column = Column
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -72,18 +72,18 @@ namespace osu.Game.Rulesets.Mania.Objects
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The head note of the hold.
|
/// The head note of the hold.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public HeadNote Head { get; private set; }
|
public HeadNote Head { get; protected set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The tail note of the hold.
|
/// The tail note of the hold.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public TailNote Tail { get; private set; }
|
public TailNote Tail { get; protected set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The body of the hold.
|
/// The body of the hold.
|
||||||
/// This is an invisible and silent object that tracks the holding state of the <see cref="HoldNote"/>.
|
/// This is an invisible and silent object that tracks the holding state of the <see cref="HoldNote"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public HoldNoteBody Body { get; private set; }
|
public HoldNoteBody Body { get; protected set; }
|
||||||
|
|
||||||
public override double MaximumJudgementOffset => Tail.MaximumJudgementOffset;
|
public override double MaximumJudgementOffset => Tail.MaximumJudgementOffset;
|
||||||
|
|
||||||
|
@ -239,7 +239,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy
|
|||||||
// i dunno this looks about right??
|
// i dunno this looks about right??
|
||||||
// the guard against zero draw height is intended for zero-length hold notes. yes, such cases have been spotted in the wild.
|
// the guard against zero draw height is intended for zero-length hold notes. yes, such cases have been spotted in the wild.
|
||||||
if (sprite.DrawHeight > 0)
|
if (sprite.DrawHeight > 0)
|
||||||
bodySprite.Scale = new Vector2(1, MathF.Max(1, scaleDirection * 32800 / sprite.DrawHeight));
|
bodySprite.Scale = new Vector2(1, scaleDirection * MathF.Max(1, 32800 / sprite.DrawHeight));
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -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
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ 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.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
@ -16,7 +15,6 @@ using osu.Game.Rulesets.Osu.Skinning.Default;
|
|||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
using osuTK.Graphics;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components
|
namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components
|
||||||
{
|
{
|
||||||
@ -48,13 +46,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Children = new Drawable[]
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new Circle
|
|
||||||
{
|
|
||||||
Anchor = Anchor.Centre,
|
|
||||||
Origin = Anchor.Centre,
|
|
||||||
RelativeSizeAxes = Axes.Both,
|
|
||||||
Colour = Color4.White,
|
|
||||||
},
|
|
||||||
ring = new RingPiece
|
ring = new RingPiece
|
||||||
{
|
{
|
||||||
BorderThickness = 4,
|
BorderThickness = 4,
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
// 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.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Primitives;
|
using osu.Framework.Graphics.Primitives;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
|
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
@ -16,6 +19,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles
|
|||||||
|
|
||||||
protected readonly HitCirclePiece CirclePiece;
|
protected readonly HitCirclePiece CirclePiece;
|
||||||
private readonly HitCircleOverlapMarker marker;
|
private readonly HitCircleOverlapMarker marker;
|
||||||
|
private readonly Bindable<bool> showHitMarkers = new Bindable<bool>();
|
||||||
|
|
||||||
public HitCircleSelectionBlueprint(HitCircle circle)
|
public HitCircleSelectionBlueprint(HitCircle circle)
|
||||||
: base(circle)
|
: base(circle)
|
||||||
@ -27,12 +31,32 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuConfigManager config)
|
||||||
|
{
|
||||||
|
config.BindWith(OsuSetting.EditorShowHitMarkers, showHitMarkers);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
showHitMarkers.BindValueChanged(_ =>
|
||||||
|
{
|
||||||
|
if (!showHitMarkers.Value)
|
||||||
|
DrawableObject.RestoreHitAnimations();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
{
|
{
|
||||||
base.Update();
|
base.Update();
|
||||||
|
|
||||||
CirclePiece.UpdateFrom(HitObject);
|
CirclePiece.UpdateFrom(HitObject);
|
||||||
marker.UpdateFrom(HitObject);
|
marker.UpdateFrom(HitObject);
|
||||||
|
|
||||||
|
if (showHitMarkers.Value)
|
||||||
|
DrawableObject.SuppressHitAnimations();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => DrawableObject.HitArea.ReceivePositionalInputAt(screenSpacePos);
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => DrawableObject.HitArea.ReceivePositionalInputAt(screenSpacePos);
|
||||||
|
@ -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.Graphics;
|
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
|
using osu.Game.Rulesets.Osu.Edit.Blueprints.HitCircles.Components;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
@ -14,18 +13,17 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
|
|
||||||
private readonly Slider slider;
|
private readonly Slider slider;
|
||||||
private readonly SliderPosition position;
|
private readonly SliderPosition position;
|
||||||
private readonly HitCircleOverlapMarker marker;
|
private readonly HitCircleOverlapMarker? marker;
|
||||||
|
|
||||||
public SliderCircleOverlay(Slider slider, SliderPosition position)
|
public SliderCircleOverlay(Slider slider, SliderPosition position)
|
||||||
{
|
{
|
||||||
this.slider = slider;
|
this.slider = slider;
|
||||||
this.position = position;
|
this.position = position;
|
||||||
|
|
||||||
InternalChildren = new Drawable[]
|
if (position == SliderPosition.Start)
|
||||||
{
|
AddInternal(marker = new HitCircleOverlapMarker());
|
||||||
marker = new HitCircleOverlapMarker(),
|
|
||||||
CirclePiece = new HitCirclePiece(),
|
AddInternal(CirclePiece = new HitCirclePiece());
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Update()
|
protected override void Update()
|
||||||
@ -35,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
var circle = position == SliderPosition.Start ? (HitCircle)slider.HeadCircle : slider.TailCircle;
|
var circle = position == SliderPosition.Start ? (HitCircle)slider.HeadCircle : slider.TailCircle;
|
||||||
|
|
||||||
CirclePiece.UpdateFrom(circle);
|
CirclePiece.UpdateFrom(circle);
|
||||||
marker.UpdateFrom(circle);
|
marker?.UpdateFrom(circle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Hide()
|
public override void Hide()
|
||||||
|
@ -14,6 +14,7 @@ using osu.Framework.Graphics.UserInterface;
|
|||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
@ -54,11 +55,26 @@ 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>();
|
||||||
private readonly BindableList<HitObject> selectedObjects = new BindableList<HitObject>();
|
private readonly BindableList<HitObject> selectedObjects = new BindableList<HitObject>();
|
||||||
|
private readonly Bindable<bool> showHitMarkers = new Bindable<bool>();
|
||||||
|
|
||||||
public SliderSelectionBlueprint(Slider slider)
|
public SliderSelectionBlueprint(Slider slider)
|
||||||
: base(slider)
|
: base(slider)
|
||||||
@ -66,7 +82,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load(OsuConfigManager config)
|
||||||
{
|
{
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
@ -74,6 +90,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
HeadOverlay = CreateCircleOverlay(HitObject, SliderPosition.Start),
|
HeadOverlay = CreateCircleOverlay(HitObject, SliderPosition.Start),
|
||||||
TailOverlay = CreateCircleOverlay(HitObject, SliderPosition.End),
|
TailOverlay = CreateCircleOverlay(HitObject, SliderPosition.End),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
config.BindWith(OsuSetting.EditorShowHitMarkers, showHitMarkers);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -90,6 +108,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
if (editorBeatmap != null)
|
if (editorBeatmap != null)
|
||||||
selectedObjects.BindTo(editorBeatmap.SelectedHitObjects);
|
selectedObjects.BindTo(editorBeatmap.SelectedHitObjects);
|
||||||
selectedObjects.BindCollectionChanged((_, _) => updateVisualDefinition(), true);
|
selectedObjects.BindCollectionChanged((_, _) => updateVisualDefinition(), true);
|
||||||
|
showHitMarkers.BindValueChanged(_ =>
|
||||||
|
{
|
||||||
|
if (!showHitMarkers.Value)
|
||||||
|
DrawableObject.RestoreHitAnimations();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool HandleQuickDeletion()
|
public override bool HandleQuickDeletion()
|
||||||
@ -110,6 +133,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
|||||||
|
|
||||||
if (IsSelected)
|
if (IsSelected)
|
||||||
BodyPiece.UpdateFrom(HitObject);
|
BodyPiece.UpdateFrom(HitObject);
|
||||||
|
|
||||||
|
if (showHitMarkers.Value)
|
||||||
|
DrawableObject.SuppressHitAnimations();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnHover(HoverEvent e)
|
protected override bool OnHover(HoverEvent e)
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
@ -51,6 +50,8 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
|
|
||||||
private readonly Bindable<TernaryState> rectangularGridSnapToggle = new Bindable<TernaryState>();
|
private readonly Bindable<TernaryState> rectangularGridSnapToggle = new Bindable<TernaryState>();
|
||||||
|
|
||||||
|
protected override Drawable CreateHitObjectInspector() => new OsuHitObjectInspector();
|
||||||
|
|
||||||
protected override IEnumerable<TernaryButton> CreateTernaryButtons()
|
protected override IEnumerable<TernaryButton> CreateTernaryButtons()
|
||||||
=> base.CreateTernaryButtons()
|
=> base.CreateTernaryButtons()
|
||||||
.Concat(DistanceSnapProvider.CreateTernaryButtons())
|
.Concat(DistanceSnapProvider.CreateTernaryButtons())
|
||||||
@ -99,9 +100,9 @@ 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 EditorToolboxGroup[]
|
RightToolbox.AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
OsuGridToolboxGroup,
|
OsuGridToolboxGroup,
|
||||||
new TransformToolboxGroup
|
new TransformToolboxGroup
|
||||||
@ -114,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);
|
||||||
@ -192,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
|
||||||
@ -202,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))
|
||||||
@ -214,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)
|
||||||
{
|
{
|
||||||
@ -225,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)
|
||||||
{
|
{
|
||||||
|
42
osu.Game.Rulesets.Osu/Edit/OsuHitObjectInspector.cs
Normal file
42
osu.Game.Rulesets.Osu/Edit/OsuHitObjectInspector.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// 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.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Screens.Edit.Compose.Components;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Edit
|
||||||
|
{
|
||||||
|
public partial class OsuHitObjectInspector : HitObjectInspector
|
||||||
|
{
|
||||||
|
protected override void AddInspectorValues()
|
||||||
|
{
|
||||||
|
base.AddInspectorValues();
|
||||||
|
|
||||||
|
if (EditorBeatmap.SelectedHitObjects.Count > 0)
|
||||||
|
{
|
||||||
|
var firstInSelection = (OsuHitObject)EditorBeatmap.SelectedHitObjects.MinBy(ho => ho.StartTime)!;
|
||||||
|
var lastInSelection = (OsuHitObject)EditorBeatmap.SelectedHitObjects.MaxBy(ho => ho.GetEndTime())!;
|
||||||
|
|
||||||
|
Debug.Assert(firstInSelection != null && lastInSelection != null);
|
||||||
|
|
||||||
|
var precedingObject = (OsuHitObject?)EditorBeatmap.HitObjects.LastOrDefault(ho => ho.GetEndTime() < firstInSelection.StartTime);
|
||||||
|
var nextObject = (OsuHitObject?)EditorBeatmap.HitObjects.FirstOrDefault(ho => ho.StartTime > lastInSelection.GetEndTime());
|
||||||
|
|
||||||
|
if (precedingObject != null && precedingObject is not Spinner)
|
||||||
|
{
|
||||||
|
AddHeader("To previous");
|
||||||
|
AddValue($"{(firstInSelection.StackedPosition - precedingObject.StackedEndPosition).Length:#,0.##}px");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nextObject != null && nextObject is not Spinner)
|
||||||
|
{
|
||||||
|
AddHeader("To next");
|
||||||
|
AddValue($"{(nextObject.StackedPosition - lastInSelection.StackedEndPosition).Length:#,0.##}px");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -53,9 +53,11 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
|
|
||||||
public override void Begin()
|
public override void Begin()
|
||||||
{
|
{
|
||||||
if (objectsInRotation != null)
|
if (OperationInProgress.Value)
|
||||||
throw new InvalidOperationException($"Cannot {nameof(Begin)} a rotate operation while another is in progress!");
|
throw new InvalidOperationException($"Cannot {nameof(Begin)} a rotate operation while another is in progress!");
|
||||||
|
|
||||||
|
base.Begin();
|
||||||
|
|
||||||
changeHandler?.BeginChange();
|
changeHandler?.BeginChange();
|
||||||
|
|
||||||
objectsInRotation = selectedMovableObjects.ToArray();
|
objectsInRotation = selectedMovableObjects.ToArray();
|
||||||
@ -68,10 +70,10 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
|
|
||||||
public override void Update(float rotation, Vector2? origin = null)
|
public override void Update(float rotation, Vector2? origin = null)
|
||||||
{
|
{
|
||||||
if (objectsInRotation == null)
|
if (!OperationInProgress.Value)
|
||||||
throw new InvalidOperationException($"Cannot {nameof(Update)} a rotate operation without calling {nameof(Begin)} first!");
|
throw new InvalidOperationException($"Cannot {nameof(Update)} a rotate operation without calling {nameof(Begin)} first!");
|
||||||
|
|
||||||
Debug.Assert(originalPositions != null && originalPathControlPointPositions != null && defaultOrigin != null);
|
Debug.Assert(objectsInRotation != null && originalPositions != null && originalPathControlPointPositions != null && defaultOrigin != null);
|
||||||
|
|
||||||
Vector2 actualOrigin = origin ?? defaultOrigin.Value;
|
Vector2 actualOrigin = origin ?? defaultOrigin.Value;
|
||||||
|
|
||||||
@ -91,11 +93,13 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
|
|
||||||
public override void Commit()
|
public override void Commit()
|
||||||
{
|
{
|
||||||
if (objectsInRotation == null)
|
if (!OperationInProgress.Value)
|
||||||
throw new InvalidOperationException($"Cannot {nameof(Commit)} a rotate operation without calling {nameof(Begin)} first!");
|
throw new InvalidOperationException($"Cannot {nameof(Commit)} a rotate operation without calling {nameof(Begin)} first!");
|
||||||
|
|
||||||
changeHandler?.EndChange();
|
changeHandler?.EndChange();
|
||||||
|
|
||||||
|
base.Commit();
|
||||||
|
|
||||||
objectsInRotation = null;
|
objectsInRotation = null;
|
||||||
originalPositions = null;
|
originalPositions = null;
|
||||||
originalPathControlPointPositions = null;
|
originalPathControlPointPositions = null;
|
||||||
|
@ -72,9 +72,11 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
|
|
||||||
public override void Begin()
|
public override void Begin()
|
||||||
{
|
{
|
||||||
if (objectsInScale != null)
|
if (OperationInProgress.Value)
|
||||||
throw new InvalidOperationException($"Cannot {nameof(Begin)} a scale operation while another is in progress!");
|
throw new InvalidOperationException($"Cannot {nameof(Begin)} a scale operation while another is in progress!");
|
||||||
|
|
||||||
|
base.Begin();
|
||||||
|
|
||||||
changeHandler?.BeginChange();
|
changeHandler?.BeginChange();
|
||||||
|
|
||||||
objectsInScale = selectedMovableObjects.ToDictionary(ho => ho, ho => new OriginalHitObjectState(ho));
|
objectsInScale = selectedMovableObjects.ToDictionary(ho => ho, ho => new OriginalHitObjectState(ho));
|
||||||
@ -86,10 +88,10 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
|
|
||||||
public override void Update(Vector2 scale, Vector2? origin = null, Axes adjustAxis = Axes.Both)
|
public override void Update(Vector2 scale, Vector2? origin = null, Axes adjustAxis = Axes.Both)
|
||||||
{
|
{
|
||||||
if (objectsInScale == null)
|
if (!OperationInProgress.Value)
|
||||||
throw new InvalidOperationException($"Cannot {nameof(Update)} a scale operation without calling {nameof(Begin)} first!");
|
throw new InvalidOperationException($"Cannot {nameof(Update)} a scale operation without calling {nameof(Begin)} first!");
|
||||||
|
|
||||||
Debug.Assert(defaultOrigin != null && OriginalSurroundingQuad != null);
|
Debug.Assert(objectsInScale != null && defaultOrigin != null && OriginalSurroundingQuad != null);
|
||||||
|
|
||||||
Vector2 actualOrigin = origin ?? defaultOrigin.Value;
|
Vector2 actualOrigin = origin ?? defaultOrigin.Value;
|
||||||
|
|
||||||
@ -117,11 +119,13 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
|
|
||||||
public override void Commit()
|
public override void Commit()
|
||||||
{
|
{
|
||||||
if (objectsInScale == null)
|
if (!OperationInProgress.Value)
|
||||||
throw new InvalidOperationException($"Cannot {nameof(Commit)} a rotate operation without calling {nameof(Begin)} first!");
|
throw new InvalidOperationException($"Cannot {nameof(Commit)} a rotate operation without calling {nameof(Begin)} first!");
|
||||||
|
|
||||||
changeHandler?.EndChange();
|
changeHandler?.EndChange();
|
||||||
|
|
||||||
|
base.Commit();
|
||||||
|
|
||||||
objectsInScale = null;
|
objectsInScale = null;
|
||||||
OriginalSurroundingQuad = null;
|
OriginalSurroundingQuad = null;
|
||||||
defaultOrigin = null;
|
defaultOrigin = null;
|
||||||
|
@ -77,13 +77,15 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
{
|
{
|
||||||
case GlobalAction.EditorToggleRotateControl:
|
case GlobalAction.EditorToggleRotateControl:
|
||||||
{
|
{
|
||||||
rotateButton.TriggerClick();
|
if (!RotationHandler.OperationInProgress.Value || rotateButton.Selected.Value)
|
||||||
|
rotateButton.TriggerClick();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
case GlobalAction.EditorToggleScaleControl:
|
case GlobalAction.EditorToggleScaleControl:
|
||||||
{
|
{
|
||||||
scaleButton.TriggerClick();
|
if (!ScaleHandler.OperationInProgress.Value || scaleButton.Selected.Value)
|
||||||
|
scaleButton.TriggerClick();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,9 @@ using osu.Framework.Extensions.Color4Extensions;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Skinning.Default;
|
using osu.Game.Rulesets.Osu.Skinning.Default;
|
||||||
|
|
||||||
@ -23,6 +25,8 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(IHidesApproachCircles), typeof(OsuModDepth) };
|
public override Type[] IncompatibleMods => new[] { typeof(IHidesApproachCircles), typeof(OsuModDepth) };
|
||||||
|
|
||||||
|
protected override bool IsFirstAdjustableObject(HitObject hitObject) => !(hitObject is Spinner || hitObject is SpinnerTick);
|
||||||
|
|
||||||
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Graphics.Containers;
|
using osu.Game.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
@ -19,6 +20,7 @@ using osu.Game.Rulesets.Osu.UI;
|
|||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||||
{
|
{
|
||||||
@ -319,5 +321,32 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region FOR EDITOR USE ONLY, DO NOT USE FOR ANY OTHER PURPOSE
|
||||||
|
|
||||||
|
internal void SuppressHitAnimations()
|
||||||
|
{
|
||||||
|
UpdateState(ArmedState.Idle);
|
||||||
|
UpdateComboColour();
|
||||||
|
|
||||||
|
// This method is called every frame in editor contexts, thus the lack of need for transforms.
|
||||||
|
|
||||||
|
if (Time.Current >= HitStateUpdateTime)
|
||||||
|
{
|
||||||
|
// More or less matches stable (see https://github.com/peppy/osu-stable-reference/blob/bb57924c1552adbed11ee3d96cdcde47cf96f2b6/osu!/GameplayElements/HitObjects/Osu/HitCircleOsu.cs#L336-L338)
|
||||||
|
AccentColour.Value = Color4.White;
|
||||||
|
Alpha = Interpolation.ValueAt(Time.Current, 1f, 0f, HitStateUpdateTime, HitStateUpdateTime + 700);
|
||||||
|
}
|
||||||
|
|
||||||
|
LifetimeEnd = HitStateUpdateTime + 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void RestoreHitAnimations()
|
||||||
|
{
|
||||||
|
UpdateState(ArmedState.Hit, force: true);
|
||||||
|
UpdateComboColour();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,10 +48,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
|
|
||||||
if (!positionTransferred && JudgedObject is DrawableOsuHitObject osuObject && JudgedObject.IsInUse)
|
if (!positionTransferred && JudgedObject is DrawableOsuHitObject osuObject && JudgedObject.IsInUse)
|
||||||
{
|
{
|
||||||
Position = osuObject.ToSpaceOfOtherDrawable(osuObject.OriginPosition, Parent!);
|
switch (osuObject)
|
||||||
Scale = new Vector2(osuObject.HitObject.Scale);
|
{
|
||||||
|
case DrawableSlider slider:
|
||||||
|
Position = slider.TailCircle.ToSpaceOfOtherDrawable(slider.TailCircle.OriginPosition, Parent!);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Position = osuObject.ToSpaceOfOtherDrawable(osuObject.OriginPosition, Parent!);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
positionTransferred = true;
|
positionTransferred = true;
|
||||||
|
|
||||||
|
Scale = new Vector2(osuObject.HitObject.Scale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,5 +370,23 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
private partial class DefaultSliderBody : PlaySliderBody
|
private partial class DefaultSliderBody : PlaySliderBody
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region FOR EDITOR USE ONLY, DO NOT USE FOR ANY OTHER PURPOSE
|
||||||
|
|
||||||
|
internal void SuppressHitAnimations()
|
||||||
|
{
|
||||||
|
UpdateState(ArmedState.Idle);
|
||||||
|
HeadCircle.SuppressHitAnimations();
|
||||||
|
TailCircle.SuppressHitAnimations();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void RestoreHitAnimations()
|
||||||
|
{
|
||||||
|
UpdateState(ArmedState.Hit);
|
||||||
|
HeadCircle.RestoreHitAnimations();
|
||||||
|
TailCircle.RestoreHitAnimations();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,12 @@ using JetBrains.Annotations;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Skinning;
|
using osu.Game.Skinning;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||||
{
|
{
|
||||||
@ -125,5 +127,32 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
|||||||
if (Slider != null)
|
if (Slider != null)
|
||||||
Position = Slider.CurvePositionAt(HitObject.RepeatIndex % 2 == 0 ? 1 : 0);
|
Position = Slider.CurvePositionAt(HitObject.RepeatIndex % 2 == 0 ? 1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region FOR EDITOR USE ONLY, DO NOT USE FOR ANY OTHER PURPOSE
|
||||||
|
|
||||||
|
internal void SuppressHitAnimations()
|
||||||
|
{
|
||||||
|
UpdateState(ArmedState.Idle);
|
||||||
|
UpdateComboColour();
|
||||||
|
|
||||||
|
// This method is called every frame in editor contexts, thus the lack of need for transforms.
|
||||||
|
|
||||||
|
if (Time.Current >= HitStateUpdateTime)
|
||||||
|
{
|
||||||
|
// More or less matches stable (see https://github.com/peppy/osu-stable-reference/blob/bb57924c1552adbed11ee3d96cdcde47cf96f2b6/osu!/GameplayElements/HitObjects/Osu/HitCircleOsu.cs#L336-L338)
|
||||||
|
AccentColour.Value = Color4.White;
|
||||||
|
Alpha = Interpolation.ValueAt(Time.Current, 1f, 0f, HitStateUpdateTime, HitStateUpdateTime + 700);
|
||||||
|
}
|
||||||
|
|
||||||
|
LifetimeEnd = HitStateUpdateTime + 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void RestoreHitAnimations()
|
||||||
|
{
|
||||||
|
UpdateState(ArmedState.Hit);
|
||||||
|
UpdateComboColour();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -8,6 +8,7 @@ using System.Linq;
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
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.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
@ -47,6 +48,8 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
|
|
||||||
protected override GameplayCursorContainer? CreateCursor() => new OsuCursorContainer();
|
protected override GameplayCursorContainer? CreateCursor() => new OsuCursorContainer();
|
||||||
|
|
||||||
|
public override Quad SkinnableComponentScreenSpaceDrawQuad => playfieldBorder.ScreenSpaceDrawQuad;
|
||||||
|
|
||||||
private readonly Container judgementAboveHitObjectLayer;
|
private readonly Container judgementAboveHitObjectLayer;
|
||||||
|
|
||||||
public OsuPlayfield()
|
public OsuPlayfield()
|
||||||
|
@ -1,93 +0,0 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
|
||||||
using osu.Game.Rulesets.Difficulty.Skills;
|
|
||||||
using osu.Game.Rulesets.Mods;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
|
||||||
{
|
|
||||||
public class Peaks : Skill
|
|
||||||
{
|
|
||||||
private const double rhythm_skill_multiplier = 0.2 * final_multiplier;
|
|
||||||
private const double colour_skill_multiplier = 0.375 * final_multiplier;
|
|
||||||
private const double stamina_skill_multiplier = 0.375 * final_multiplier;
|
|
||||||
|
|
||||||
private const double final_multiplier = 0.0625;
|
|
||||||
|
|
||||||
private readonly Rhythm rhythm;
|
|
||||||
private readonly Colour colour;
|
|
||||||
private readonly Stamina stamina;
|
|
||||||
|
|
||||||
public double ColourDifficultyValue => colour.DifficultyValue() * colour_skill_multiplier;
|
|
||||||
public double RhythmDifficultyValue => rhythm.DifficultyValue() * rhythm_skill_multiplier;
|
|
||||||
public double StaminaDifficultyValue => stamina.DifficultyValue() * stamina_skill_multiplier;
|
|
||||||
|
|
||||||
public Peaks(Mod[] mods)
|
|
||||||
: base(mods)
|
|
||||||
{
|
|
||||||
rhythm = new Rhythm(mods);
|
|
||||||
colour = new Colour(mods);
|
|
||||||
stamina = new Stamina(mods);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the <i>p</i>-norm of an <i>n</i>-dimensional vector.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="p">The value of <i>p</i> to calculate the norm for.</param>
|
|
||||||
/// <param name="values">The coefficients of the vector.</param>
|
|
||||||
private double norm(double p, params double[] values) => Math.Pow(values.Sum(x => Math.Pow(x, p)), 1 / p);
|
|
||||||
|
|
||||||
public override void Process(DifficultyHitObject current)
|
|
||||||
{
|
|
||||||
rhythm.Process(current);
|
|
||||||
colour.Process(current);
|
|
||||||
stamina.Process(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the combined star rating of the beatmap, calculated using peak strains from all sections of the map.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// For each section, the peak strains of all separate skills are combined into a single peak strain for the section.
|
|
||||||
/// The resulting partial rating of the beatmap is a weighted sum of the combined peaks (higher peaks are weighted more).
|
|
||||||
/// </remarks>
|
|
||||||
public override double DifficultyValue()
|
|
||||||
{
|
|
||||||
List<double> peaks = new List<double>();
|
|
||||||
|
|
||||||
var colourPeaks = colour.GetCurrentStrainPeaks().ToList();
|
|
||||||
var rhythmPeaks = rhythm.GetCurrentStrainPeaks().ToList();
|
|
||||||
var staminaPeaks = stamina.GetCurrentStrainPeaks().ToList();
|
|
||||||
|
|
||||||
for (int i = 0; i < colourPeaks.Count; i++)
|
|
||||||
{
|
|
||||||
double colourPeak = colourPeaks[i] * colour_skill_multiplier;
|
|
||||||
double rhythmPeak = rhythmPeaks[i] * rhythm_skill_multiplier;
|
|
||||||
double staminaPeak = staminaPeaks[i] * stamina_skill_multiplier;
|
|
||||||
|
|
||||||
double peak = norm(1.5, colourPeak, staminaPeak);
|
|
||||||
peak = norm(2, peak, rhythmPeak);
|
|
||||||
|
|
||||||
// Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871).
|
|
||||||
// These sections will not contribute to the difficulty.
|
|
||||||
if (peak > 0)
|
|
||||||
peaks.Add(peak);
|
|
||||||
}
|
|
||||||
|
|
||||||
double difficulty = 0;
|
|
||||||
double weight = 1;
|
|
||||||
|
|
||||||
foreach (double strain in peaks.OrderDescending())
|
|
||||||
{
|
|
||||||
difficulty += strain * weight;
|
|
||||||
weight *= 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
return difficulty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -23,6 +23,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
{
|
{
|
||||||
private const double difficulty_multiplier = 1.35;
|
private const double difficulty_multiplier = 1.35;
|
||||||
|
|
||||||
|
private const double final_multiplier = 0.0625;
|
||||||
|
private const double rhythm_skill_multiplier = 0.2 * final_multiplier;
|
||||||
|
private const double colour_skill_multiplier = 0.375 * final_multiplier;
|
||||||
|
private const double stamina_skill_multiplier = 0.375 * final_multiplier;
|
||||||
|
|
||||||
public override int Version => 20221107;
|
public override int Version => 20221107;
|
||||||
|
|
||||||
public TaikoDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
|
public TaikoDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
|
||||||
@ -34,7 +39,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
{
|
{
|
||||||
return new Skill[]
|
return new Skill[]
|
||||||
{
|
{
|
||||||
new Peaks(mods)
|
new Rhythm(mods),
|
||||||
|
new Colour(mods),
|
||||||
|
new Stamina(mods)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,13 +79,15 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
if (beatmap.HitObjects.Count == 0)
|
if (beatmap.HitObjects.Count == 0)
|
||||||
return new TaikoDifficultyAttributes { Mods = mods };
|
return new TaikoDifficultyAttributes { Mods = mods };
|
||||||
|
|
||||||
var combined = (Peaks)skills[0];
|
Colour colour = (Colour)skills.First(x => x is Colour);
|
||||||
|
Rhythm rhythm = (Rhythm)skills.First(x => x is Rhythm);
|
||||||
|
Stamina stamina = (Stamina)skills.First(x => x is Stamina);
|
||||||
|
|
||||||
double colourRating = combined.ColourDifficultyValue * difficulty_multiplier;
|
double colourRating = colour.DifficultyValue() * colour_skill_multiplier * difficulty_multiplier;
|
||||||
double rhythmRating = combined.RhythmDifficultyValue * difficulty_multiplier;
|
double rhythmRating = rhythm.DifficultyValue() * rhythm_skill_multiplier * difficulty_multiplier;
|
||||||
double staminaRating = combined.StaminaDifficultyValue * difficulty_multiplier;
|
double staminaRating = stamina.DifficultyValue() * stamina_skill_multiplier * difficulty_multiplier;
|
||||||
|
|
||||||
double combinedRating = combined.DifficultyValue() * difficulty_multiplier;
|
double combinedRating = combinedDifficultyValue(rhythm, colour, stamina) * difficulty_multiplier;
|
||||||
double starRating = rescale(combinedRating * 1.4);
|
double starRating = rescale(combinedRating * 1.4);
|
||||||
|
|
||||||
HitWindows hitWindows = new TaikoHitWindows();
|
HitWindows hitWindows = new TaikoHitWindows();
|
||||||
@ -109,5 +118,54 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
|
|
||||||
return 10.43 * Math.Log(sr / 8 + 1);
|
return 10.43 * Math.Log(sr / 8 + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the combined star rating of the beatmap, calculated using peak strains from all sections of the map.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// For each section, the peak strains of all separate skills are combined into a single peak strain for the section.
|
||||||
|
/// The resulting partial rating of the beatmap is a weighted sum of the combined peaks (higher peaks are weighted more).
|
||||||
|
/// </remarks>
|
||||||
|
private double combinedDifficultyValue(Rhythm rhythm, Colour colour, Stamina stamina)
|
||||||
|
{
|
||||||
|
List<double> peaks = new List<double>();
|
||||||
|
|
||||||
|
var colourPeaks = colour.GetCurrentStrainPeaks().ToList();
|
||||||
|
var rhythmPeaks = rhythm.GetCurrentStrainPeaks().ToList();
|
||||||
|
var staminaPeaks = stamina.GetCurrentStrainPeaks().ToList();
|
||||||
|
|
||||||
|
for (int i = 0; i < colourPeaks.Count; i++)
|
||||||
|
{
|
||||||
|
double colourPeak = colourPeaks[i] * colour_skill_multiplier;
|
||||||
|
double rhythmPeak = rhythmPeaks[i] * rhythm_skill_multiplier;
|
||||||
|
double staminaPeak = staminaPeaks[i] * stamina_skill_multiplier;
|
||||||
|
|
||||||
|
double peak = norm(1.5, colourPeak, staminaPeak);
|
||||||
|
peak = norm(2, peak, rhythmPeak);
|
||||||
|
|
||||||
|
// Sections with 0 strain are excluded to avoid worst-case time complexity of the following sort (e.g. /b/2351871).
|
||||||
|
// These sections will not contribute to the difficulty.
|
||||||
|
if (peak > 0)
|
||||||
|
peaks.Add(peak);
|
||||||
|
}
|
||||||
|
|
||||||
|
double difficulty = 0;
|
||||||
|
double weight = 1;
|
||||||
|
|
||||||
|
foreach (double strain in peaks.OrderDescending())
|
||||||
|
{
|
||||||
|
difficulty += strain * weight;
|
||||||
|
weight *= 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
return difficulty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the <i>p</i>-norm of an <i>n</i>-dimensional vector.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="p">The value of <i>p</i> to calculate the norm for.</param>
|
||||||
|
/// <param name="values">The coefficients of the vector.</param>
|
||||||
|
private double norm(double p, params double[] values) => Math.Pow(values.Sum(x => Math.Pow(x, p)), 1 / p);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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];
|
||||||
|
43
osu.Game.Tests/Editing/EditorTimestampParserTest.cs
Normal file
43
osu.Game.Tests/Editing/EditorTimestampParserTest.cs
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// 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 NUnit.Framework;
|
||||||
|
using osu.Game.Rulesets.Edit;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Editing
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class EditorTimestampParserTest
|
||||||
|
{
|
||||||
|
private static readonly object?[][] test_cases =
|
||||||
|
{
|
||||||
|
new object?[] { ":", false, null, null },
|
||||||
|
new object?[] { "1", true, TimeSpan.FromMilliseconds(1), null },
|
||||||
|
new object?[] { "99", true, TimeSpan.FromMilliseconds(99), null },
|
||||||
|
new object?[] { "320000", true, TimeSpan.FromMilliseconds(320000), null },
|
||||||
|
new object?[] { "1:2", true, new TimeSpan(0, 0, 1, 2), null },
|
||||||
|
new object?[] { "1:02", true, new TimeSpan(0, 0, 1, 2), null },
|
||||||
|
new object?[] { "1:92", false, null, null },
|
||||||
|
new object?[] { "1:002", false, null, null },
|
||||||
|
new object?[] { "1:02:3", true, new TimeSpan(0, 0, 1, 2, 3), null },
|
||||||
|
new object?[] { "1:02:300", true, new TimeSpan(0, 0, 1, 2, 300), null },
|
||||||
|
new object?[] { "1:02:3000", false, null, null },
|
||||||
|
new object?[] { "1:02:300 ()", false, null, null },
|
||||||
|
new object?[] { "1:02:300 (1,2,3)", true, new TimeSpan(0, 0, 1, 2, 300), "1,2,3" },
|
||||||
|
};
|
||||||
|
|
||||||
|
[TestCaseSource(nameof(test_cases))]
|
||||||
|
public void TestTryParse(string timestamp, bool expectedSuccess, TimeSpan? expectedParsedTime, string? expectedSelection)
|
||||||
|
{
|
||||||
|
bool actualSuccess = EditorTimestampParser.TryParse(timestamp, out var actualParsedTime, out string? actualSelection);
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(actualSuccess, Is.EqualTo(expectedSuccess));
|
||||||
|
Assert.That(actualParsedTime, Is.EqualTo(expectedParsedTime));
|
||||||
|
Assert.That(actualSelection, Is.EqualTo(expectedSelection));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,8 @@ using NUnit.Framework;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Beatmaps.Timing;
|
using osu.Game.Beatmaps.Timing;
|
||||||
|
using osu.Game.Rulesets.Mania;
|
||||||
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Osu;
|
using osu.Game.Rulesets.Osu;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Screens.Edit;
|
using osu.Game.Screens.Edit;
|
||||||
@ -19,10 +21,11 @@ namespace osu.Game.Tests.Editing
|
|||||||
{
|
{
|
||||||
var controlPoints = new ControlPointInfo();
|
var controlPoints = new ControlPointInfo();
|
||||||
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
var beatmap = new Beatmap
|
var beatmap = new EditorBeatmap(new Beatmap
|
||||||
{
|
{
|
||||||
ControlPointInfo = controlPoints,
|
ControlPointInfo = controlPoints,
|
||||||
};
|
BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo },
|
||||||
|
});
|
||||||
|
|
||||||
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
||||||
beatmapProcessor.PreProcess();
|
beatmapProcessor.PreProcess();
|
||||||
@ -36,14 +39,15 @@ namespace osu.Game.Tests.Editing
|
|||||||
{
|
{
|
||||||
var controlPoints = new ControlPointInfo();
|
var controlPoints = new ControlPointInfo();
|
||||||
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
var beatmap = new Beatmap
|
var beatmap = new EditorBeatmap(new Beatmap
|
||||||
{
|
{
|
||||||
ControlPointInfo = controlPoints,
|
ControlPointInfo = controlPoints,
|
||||||
|
BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo },
|
||||||
HitObjects =
|
HitObjects =
|
||||||
{
|
{
|
||||||
new HitCircle { StartTime = 1000 },
|
new HitCircle { StartTime = 1000 },
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
||||||
beatmapProcessor.PreProcess();
|
beatmapProcessor.PreProcess();
|
||||||
@ -57,15 +61,16 @@ namespace osu.Game.Tests.Editing
|
|||||||
{
|
{
|
||||||
var controlPoints = new ControlPointInfo();
|
var controlPoints = new ControlPointInfo();
|
||||||
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
var beatmap = new Beatmap
|
var beatmap = new EditorBeatmap(new Beatmap
|
||||||
{
|
{
|
||||||
ControlPointInfo = controlPoints,
|
ControlPointInfo = controlPoints,
|
||||||
|
BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo },
|
||||||
HitObjects =
|
HitObjects =
|
||||||
{
|
{
|
||||||
new HitCircle { StartTime = 1000 },
|
new HitCircle { StartTime = 1000 },
|
||||||
new HitCircle { StartTime = 2000 },
|
new HitCircle { StartTime = 2000 },
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
||||||
beatmapProcessor.PreProcess();
|
beatmapProcessor.PreProcess();
|
||||||
@ -74,20 +79,67 @@ namespace osu.Game.Tests.Editing
|
|||||||
Assert.That(beatmap.Breaks, Is.Empty);
|
Assert.That(beatmap.Breaks, Is.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHoldNote()
|
||||||
|
{
|
||||||
|
var controlPoints = new ControlPointInfo();
|
||||||
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
|
var beatmap = new EditorBeatmap(new Beatmap
|
||||||
|
{
|
||||||
|
ControlPointInfo = controlPoints,
|
||||||
|
BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo },
|
||||||
|
HitObjects =
|
||||||
|
{
|
||||||
|
new HoldNote { StartTime = 1000, Duration = 10000 },
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new ManiaRuleset());
|
||||||
|
beatmapProcessor.PreProcess();
|
||||||
|
beatmapProcessor.PostProcess();
|
||||||
|
|
||||||
|
Assert.That(beatmap.Breaks, Has.Count.EqualTo(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestHoldNoteWithOverlappingNote()
|
||||||
|
{
|
||||||
|
var controlPoints = new ControlPointInfo();
|
||||||
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
|
var beatmap = new EditorBeatmap(new Beatmap
|
||||||
|
{
|
||||||
|
ControlPointInfo = controlPoints,
|
||||||
|
BeatmapInfo = { Ruleset = new ManiaRuleset().RulesetInfo },
|
||||||
|
HitObjects =
|
||||||
|
{
|
||||||
|
new HoldNote { StartTime = 1000, Duration = 10000 },
|
||||||
|
new Note { StartTime = 2000 },
|
||||||
|
new Note { StartTime = 12000 },
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new ManiaRuleset());
|
||||||
|
beatmapProcessor.PreProcess();
|
||||||
|
beatmapProcessor.PostProcess();
|
||||||
|
|
||||||
|
Assert.That(beatmap.Breaks, Has.Count.EqualTo(0));
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestTwoObjectsFarApart()
|
public void TestTwoObjectsFarApart()
|
||||||
{
|
{
|
||||||
var controlPoints = new ControlPointInfo();
|
var controlPoints = new ControlPointInfo();
|
||||||
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
var beatmap = new Beatmap
|
var beatmap = new EditorBeatmap(new Beatmap
|
||||||
{
|
{
|
||||||
ControlPointInfo = controlPoints,
|
ControlPointInfo = controlPoints,
|
||||||
|
BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo },
|
||||||
HitObjects =
|
HitObjects =
|
||||||
{
|
{
|
||||||
new HitCircle { StartTime = 1000 },
|
new HitCircle { StartTime = 1000 },
|
||||||
new HitCircle { StartTime = 5000 },
|
new HitCircle { StartTime = 5000 },
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
||||||
beatmapProcessor.PreProcess();
|
beatmapProcessor.PreProcess();
|
||||||
@ -106,9 +158,10 @@ namespace osu.Game.Tests.Editing
|
|||||||
{
|
{
|
||||||
var controlPoints = new ControlPointInfo();
|
var controlPoints = new ControlPointInfo();
|
||||||
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
var beatmap = new Beatmap
|
var beatmap = new EditorBeatmap(new Beatmap
|
||||||
{
|
{
|
||||||
ControlPointInfo = controlPoints,
|
ControlPointInfo = controlPoints,
|
||||||
|
BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo },
|
||||||
HitObjects =
|
HitObjects =
|
||||||
{
|
{
|
||||||
new HitCircle { StartTime = 1000 },
|
new HitCircle { StartTime = 1000 },
|
||||||
@ -119,7 +172,7 @@ namespace osu.Game.Tests.Editing
|
|||||||
new BreakPeriod(1200, 4000),
|
new BreakPeriod(1200, 4000),
|
||||||
new BreakPeriod(5200, 8000),
|
new BreakPeriod(5200, 8000),
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
||||||
beatmapProcessor.PreProcess();
|
beatmapProcessor.PreProcess();
|
||||||
@ -138,9 +191,10 @@ namespace osu.Game.Tests.Editing
|
|||||||
{
|
{
|
||||||
var controlPoints = new ControlPointInfo();
|
var controlPoints = new ControlPointInfo();
|
||||||
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
var beatmap = new Beatmap
|
var beatmap = new EditorBeatmap(new Beatmap
|
||||||
{
|
{
|
||||||
ControlPointInfo = controlPoints,
|
ControlPointInfo = controlPoints,
|
||||||
|
BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo },
|
||||||
HitObjects =
|
HitObjects =
|
||||||
{
|
{
|
||||||
new HitCircle { StartTime = 1000 },
|
new HitCircle { StartTime = 1000 },
|
||||||
@ -151,7 +205,7 @@ namespace osu.Game.Tests.Editing
|
|||||||
{
|
{
|
||||||
new BreakPeriod(1200, 8000),
|
new BreakPeriod(1200, 8000),
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
||||||
beatmapProcessor.PreProcess();
|
beatmapProcessor.PreProcess();
|
||||||
@ -172,9 +226,10 @@ namespace osu.Game.Tests.Editing
|
|||||||
{
|
{
|
||||||
var controlPoints = new ControlPointInfo();
|
var controlPoints = new ControlPointInfo();
|
||||||
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
var beatmap = new Beatmap
|
var beatmap = new EditorBeatmap(new Beatmap
|
||||||
{
|
{
|
||||||
ControlPointInfo = controlPoints,
|
ControlPointInfo = controlPoints,
|
||||||
|
BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo },
|
||||||
HitObjects =
|
HitObjects =
|
||||||
{
|
{
|
||||||
new HitCircle { StartTime = 1100 },
|
new HitCircle { StartTime = 1100 },
|
||||||
@ -184,7 +239,7 @@ namespace osu.Game.Tests.Editing
|
|||||||
{
|
{
|
||||||
new BreakPeriod(1200, 8000),
|
new BreakPeriod(1200, 8000),
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
||||||
beatmapProcessor.PreProcess();
|
beatmapProcessor.PreProcess();
|
||||||
@ -203,9 +258,10 @@ namespace osu.Game.Tests.Editing
|
|||||||
{
|
{
|
||||||
var controlPoints = new ControlPointInfo();
|
var controlPoints = new ControlPointInfo();
|
||||||
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
var beatmap = new Beatmap
|
var beatmap = new EditorBeatmap(new Beatmap
|
||||||
{
|
{
|
||||||
ControlPointInfo = controlPoints,
|
ControlPointInfo = controlPoints,
|
||||||
|
BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo },
|
||||||
HitObjects =
|
HitObjects =
|
||||||
{
|
{
|
||||||
new HitCircle { StartTime = 1000 },
|
new HitCircle { StartTime = 1000 },
|
||||||
@ -216,7 +272,7 @@ namespace osu.Game.Tests.Editing
|
|||||||
new ManualBreakPeriod(1200, 4000),
|
new ManualBreakPeriod(1200, 4000),
|
||||||
new ManualBreakPeriod(5200, 8000),
|
new ManualBreakPeriod(5200, 8000),
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
||||||
beatmapProcessor.PreProcess();
|
beatmapProcessor.PreProcess();
|
||||||
@ -237,9 +293,10 @@ namespace osu.Game.Tests.Editing
|
|||||||
{
|
{
|
||||||
var controlPoints = new ControlPointInfo();
|
var controlPoints = new ControlPointInfo();
|
||||||
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
var beatmap = new Beatmap
|
var beatmap = new EditorBeatmap(new Beatmap
|
||||||
{
|
{
|
||||||
ControlPointInfo = controlPoints,
|
ControlPointInfo = controlPoints,
|
||||||
|
BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo },
|
||||||
HitObjects =
|
HitObjects =
|
||||||
{
|
{
|
||||||
new HitCircle { StartTime = 1000 },
|
new HitCircle { StartTime = 1000 },
|
||||||
@ -250,7 +307,7 @@ namespace osu.Game.Tests.Editing
|
|||||||
{
|
{
|
||||||
new ManualBreakPeriod(1200, 8000),
|
new ManualBreakPeriod(1200, 8000),
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
||||||
beatmapProcessor.PreProcess();
|
beatmapProcessor.PreProcess();
|
||||||
@ -271,9 +328,10 @@ namespace osu.Game.Tests.Editing
|
|||||||
{
|
{
|
||||||
var controlPoints = new ControlPointInfo();
|
var controlPoints = new ControlPointInfo();
|
||||||
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
var beatmap = new Beatmap
|
var beatmap = new EditorBeatmap(new Beatmap
|
||||||
{
|
{
|
||||||
ControlPointInfo = controlPoints,
|
ControlPointInfo = controlPoints,
|
||||||
|
BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo },
|
||||||
HitObjects =
|
HitObjects =
|
||||||
{
|
{
|
||||||
new HitCircle { StartTime = 1000 },
|
new HitCircle { StartTime = 1000 },
|
||||||
@ -283,7 +341,7 @@ namespace osu.Game.Tests.Editing
|
|||||||
{
|
{
|
||||||
new ManualBreakPeriod(1200, 8800),
|
new ManualBreakPeriod(1200, 8800),
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
|
||||||
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
||||||
beatmapProcessor.PreProcess();
|
beatmapProcessor.PreProcess();
|
||||||
@ -296,5 +354,140 @@ namespace osu.Game.Tests.Editing
|
|||||||
Assert.That(beatmap.Breaks[0].EndTime, Is.EqualTo(8800));
|
Assert.That(beatmap.Breaks[0].EndTime, Is.EqualTo(8800));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBreaksAtEndOfBeatmapAreRemoved()
|
||||||
|
{
|
||||||
|
var controlPoints = new ControlPointInfo();
|
||||||
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
|
var beatmap = new EditorBeatmap(new Beatmap
|
||||||
|
{
|
||||||
|
ControlPointInfo = controlPoints,
|
||||||
|
BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo },
|
||||||
|
HitObjects =
|
||||||
|
{
|
||||||
|
new HitCircle { StartTime = 1000 },
|
||||||
|
new HitCircle { StartTime = 2000 },
|
||||||
|
},
|
||||||
|
Breaks =
|
||||||
|
{
|
||||||
|
new BreakPeriod(10000, 15000),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
||||||
|
beatmapProcessor.PreProcess();
|
||||||
|
beatmapProcessor.PostProcess();
|
||||||
|
|
||||||
|
Assert.That(beatmap.Breaks, Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestManualBreaksAtEndOfBeatmapAreRemoved()
|
||||||
|
{
|
||||||
|
var controlPoints = new ControlPointInfo();
|
||||||
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
|
var beatmap = new EditorBeatmap(new Beatmap
|
||||||
|
{
|
||||||
|
ControlPointInfo = controlPoints,
|
||||||
|
BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo },
|
||||||
|
HitObjects =
|
||||||
|
{
|
||||||
|
new HitCircle { StartTime = 1000 },
|
||||||
|
new HitCircle { StartTime = 2000 },
|
||||||
|
},
|
||||||
|
Breaks =
|
||||||
|
{
|
||||||
|
new ManualBreakPeriod(10000, 15000),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
||||||
|
beatmapProcessor.PreProcess();
|
||||||
|
beatmapProcessor.PostProcess();
|
||||||
|
|
||||||
|
Assert.That(beatmap.Breaks, Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestManualBreaksAtEndOfBeatmapAreRemovedCorrectlyEvenWithConcurrentObjects()
|
||||||
|
{
|
||||||
|
var controlPoints = new ControlPointInfo();
|
||||||
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
|
var beatmap = new EditorBeatmap(new Beatmap
|
||||||
|
{
|
||||||
|
ControlPointInfo = controlPoints,
|
||||||
|
BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo },
|
||||||
|
HitObjects =
|
||||||
|
{
|
||||||
|
new HoldNote { StartTime = 1000, EndTime = 20000 },
|
||||||
|
new HoldNote { StartTime = 2000, EndTime = 3000 },
|
||||||
|
},
|
||||||
|
Breaks =
|
||||||
|
{
|
||||||
|
new ManualBreakPeriod(10000, 15000),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
||||||
|
beatmapProcessor.PreProcess();
|
||||||
|
beatmapProcessor.PostProcess();
|
||||||
|
|
||||||
|
Assert.That(beatmap.Breaks, Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBreaksAtStartOfBeatmapAreRemoved()
|
||||||
|
{
|
||||||
|
var controlPoints = new ControlPointInfo();
|
||||||
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
|
var beatmap = new EditorBeatmap(new Beatmap
|
||||||
|
{
|
||||||
|
ControlPointInfo = controlPoints,
|
||||||
|
BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo },
|
||||||
|
HitObjects =
|
||||||
|
{
|
||||||
|
new HitCircle { StartTime = 10000 },
|
||||||
|
new HitCircle { StartTime = 11000 },
|
||||||
|
},
|
||||||
|
Breaks =
|
||||||
|
{
|
||||||
|
new BreakPeriod(0, 9000),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
||||||
|
beatmapProcessor.PreProcess();
|
||||||
|
beatmapProcessor.PostProcess();
|
||||||
|
|
||||||
|
Assert.That(beatmap.Breaks, Is.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestManualBreaksAtStartOfBeatmapAreRemoved()
|
||||||
|
{
|
||||||
|
var controlPoints = new ControlPointInfo();
|
||||||
|
controlPoints.Add(0, new TimingControlPoint { BeatLength = 500 });
|
||||||
|
var beatmap = new EditorBeatmap(new Beatmap
|
||||||
|
{
|
||||||
|
ControlPointInfo = controlPoints,
|
||||||
|
BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo },
|
||||||
|
HitObjects =
|
||||||
|
{
|
||||||
|
new HitCircle { StartTime = 10000 },
|
||||||
|
new HitCircle { StartTime = 11000 },
|
||||||
|
},
|
||||||
|
Breaks =
|
||||||
|
{
|
||||||
|
new ManualBreakPeriod(0, 9000),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var beatmapProcessor = new EditorBeatmapProcessor(beatmap, new OsuRuleset());
|
||||||
|
beatmapProcessor.PreProcess();
|
||||||
|
beatmapProcessor.PostProcess();
|
||||||
|
|
||||||
|
Assert.That(beatmap.Breaks, Is.Empty);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
public class TestSceneTimedDifficultyCalculation
|
public class TestSceneTimedDifficultyCalculation
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public void TestAttributesGeneratedForAllNonSkippedObjects()
|
public void TestAttributesGeneratedForEachObjectOnce()
|
||||||
{
|
{
|
||||||
var beatmap = new Beatmap<TestHitObject>
|
var beatmap = new Beatmap<TestHitObject>
|
||||||
{
|
{
|
||||||
@ -40,15 +40,14 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
|
|
||||||
List<TimedDifficultyAttributes> attribs = new TestDifficultyCalculator(new TestWorkingBeatmap(beatmap)).CalculateTimed();
|
List<TimedDifficultyAttributes> attribs = new TestDifficultyCalculator(new TestWorkingBeatmap(beatmap)).CalculateTimed();
|
||||||
|
|
||||||
Assert.That(attribs.Count, Is.EqualTo(4));
|
Assert.That(attribs.Count, Is.EqualTo(3));
|
||||||
assertEquals(attribs[0], beatmap.HitObjects[0]);
|
assertEquals(attribs[0], beatmap.HitObjects[0]);
|
||||||
assertEquals(attribs[1], beatmap.HitObjects[0], beatmap.HitObjects[1]);
|
assertEquals(attribs[1], beatmap.HitObjects[0], beatmap.HitObjects[1]);
|
||||||
assertEquals(attribs[2], beatmap.HitObjects[0], beatmap.HitObjects[1]); // From the nested object.
|
assertEquals(attribs[2], beatmap.HitObjects[0], beatmap.HitObjects[1], beatmap.HitObjects[2]);
|
||||||
assertEquals(attribs[3], beatmap.HitObjects[0], beatmap.HitObjects[1], beatmap.HitObjects[2]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestAttributesNotGeneratedForSkippedObjects()
|
public void TestAttributesGeneratedForSkippedObjects()
|
||||||
{
|
{
|
||||||
var beatmap = new Beatmap<TestHitObject>
|
var beatmap = new Beatmap<TestHitObject>
|
||||||
{
|
{
|
||||||
@ -72,35 +71,14 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
|
|
||||||
List<TimedDifficultyAttributes> attribs = new TestDifficultyCalculator(new TestWorkingBeatmap(beatmap)).CalculateTimed();
|
List<TimedDifficultyAttributes> attribs = new TestDifficultyCalculator(new TestWorkingBeatmap(beatmap)).CalculateTimed();
|
||||||
|
|
||||||
Assert.That(attribs.Count, Is.EqualTo(1));
|
Assert.That(attribs.Count, Is.EqualTo(3));
|
||||||
assertEquals(attribs[0], beatmap.HitObjects[0], beatmap.HitObjects[1], beatmap.HitObjects[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void TestNestedObjectOnlyAddsParentOnce()
|
|
||||||
{
|
|
||||||
var beatmap = new Beatmap<TestHitObject>
|
|
||||||
{
|
|
||||||
HitObjects =
|
|
||||||
{
|
|
||||||
new TestHitObject
|
|
||||||
{
|
|
||||||
StartTime = 1,
|
|
||||||
Skip = true,
|
|
||||||
Nested = 2
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
List<TimedDifficultyAttributes> attribs = new TestDifficultyCalculator(new TestWorkingBeatmap(beatmap)).CalculateTimed();
|
|
||||||
|
|
||||||
Assert.That(attribs.Count, Is.EqualTo(2));
|
|
||||||
assertEquals(attribs[0], beatmap.HitObjects[0]);
|
assertEquals(attribs[0], beatmap.HitObjects[0]);
|
||||||
assertEquals(attribs[1], beatmap.HitObjects[0]);
|
assertEquals(attribs[1], beatmap.HitObjects[0], beatmap.HitObjects[1]);
|
||||||
|
assertEquals(attribs[2], beatmap.HitObjects[0], beatmap.HitObjects[1], beatmap.HitObjects[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestSkippedLastObjectAddedInLastIteration()
|
public void TestAttributesGeneratedOnceForSkippedObjects()
|
||||||
{
|
{
|
||||||
var beatmap = new Beatmap<TestHitObject>
|
var beatmap = new Beatmap<TestHitObject>
|
||||||
{
|
{
|
||||||
@ -110,6 +88,7 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
new TestHitObject
|
new TestHitObject
|
||||||
{
|
{
|
||||||
StartTime = 2,
|
StartTime = 2,
|
||||||
|
Nested = 5,
|
||||||
Skip = true
|
Skip = true
|
||||||
},
|
},
|
||||||
new TestHitObject
|
new TestHitObject
|
||||||
@ -122,8 +101,10 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
|
|
||||||
List<TimedDifficultyAttributes> attribs = new TestDifficultyCalculator(new TestWorkingBeatmap(beatmap)).CalculateTimed();
|
List<TimedDifficultyAttributes> attribs = new TestDifficultyCalculator(new TestWorkingBeatmap(beatmap)).CalculateTimed();
|
||||||
|
|
||||||
Assert.That(attribs.Count, Is.EqualTo(1));
|
Assert.That(attribs.Count, Is.EqualTo(3));
|
||||||
assertEquals(attribs[0], beatmap.HitObjects[0], beatmap.HitObjects[1], beatmap.HitObjects[2]);
|
assertEquals(attribs[0], beatmap.HitObjects[0]);
|
||||||
|
assertEquals(attribs[1], beatmap.HitObjects[0], beatmap.HitObjects[1]);
|
||||||
|
assertEquals(attribs[2], beatmap.HitObjects[0], beatmap.HitObjects[1], beatmap.HitObjects[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertEquals(TimedDifficultyAttributes attribs, params HitObject[] expected)
|
private void assertEquals(TimedDifficultyAttributes attribs, params HitObject[] expected)
|
||||||
|
22
osu.Game.Tests/Resources/per-slider-node-sample-settings.osu
Normal file
22
osu.Game.Tests/Resources/per-slider-node-sample-settings.osu
Normal 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:
|
@ -0,0 +1,176 @@
|
|||||||
|
// 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 NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Online.Rooms;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Screens.OnlinePlay.DailyChallenge;
|
||||||
|
using osu.Game.Tests.Resources;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.DailyChallenge
|
||||||
|
{
|
||||||
|
public partial class TestSceneDailyChallengeCarousel : OsuTestScene
|
||||||
|
{
|
||||||
|
[Cached]
|
||||||
|
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Plum);
|
||||||
|
|
||||||
|
private readonly Bindable<Room> room = new Bindable<Room>(new Room());
|
||||||
|
|
||||||
|
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => new CachedModelDependencyContainer<Room>(base.CreateChildDependencies(parent))
|
||||||
|
{
|
||||||
|
Model = { BindTarget = room }
|
||||||
|
};
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBasicAppearance()
|
||||||
|
{
|
||||||
|
DailyChallengeCarousel carousel = null!;
|
||||||
|
|
||||||
|
AddStep("create content", () => Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = colourProvider.Background4,
|
||||||
|
},
|
||||||
|
carousel = new DailyChallengeCarousel
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
AddSliderStep("adjust width", 0.1f, 1, 1, width =>
|
||||||
|
{
|
||||||
|
if (carousel.IsNotNull())
|
||||||
|
carousel.Width = width;
|
||||||
|
});
|
||||||
|
AddSliderStep("adjust height", 0.1f, 1, 1, height =>
|
||||||
|
{
|
||||||
|
if (carousel.IsNotNull())
|
||||||
|
carousel.Height = height;
|
||||||
|
});
|
||||||
|
AddRepeatStep("add content", () => carousel.Add(new FakeContent()), 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestIntegration()
|
||||||
|
{
|
||||||
|
GridContainer grid = null!;
|
||||||
|
DailyChallengeEventFeed feed = null!;
|
||||||
|
DailyChallengeScoreBreakdown breakdown = null!;
|
||||||
|
|
||||||
|
AddStep("create content", () => Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = colourProvider.Background4,
|
||||||
|
},
|
||||||
|
grid = new GridContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RowDimensions =
|
||||||
|
[
|
||||||
|
new Dimension(),
|
||||||
|
new Dimension()
|
||||||
|
],
|
||||||
|
Content = new[]
|
||||||
|
{
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
new DailyChallengeCarousel
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new DailyChallengeTimeRemainingRing(),
|
||||||
|
breakdown = new DailyChallengeScoreBreakdown(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
feed = new DailyChallengeEventFeed
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
AddSliderStep("adjust width", 0.1f, 1, 1, width =>
|
||||||
|
{
|
||||||
|
if (grid.IsNotNull())
|
||||||
|
grid.Width = width;
|
||||||
|
});
|
||||||
|
AddSliderStep("adjust height", 0.1f, 1, 1, height =>
|
||||||
|
{
|
||||||
|
if (grid.IsNotNull())
|
||||||
|
grid.Height = height;
|
||||||
|
});
|
||||||
|
AddSliderStep("update time remaining", 0f, 1f, 0f, progress =>
|
||||||
|
{
|
||||||
|
var startedTimeAgo = TimeSpan.FromHours(24) * progress;
|
||||||
|
room.Value.StartDate.Value = DateTimeOffset.Now - startedTimeAgo;
|
||||||
|
room.Value.EndDate.Value = room.Value.StartDate.Value.Value.AddDays(1);
|
||||||
|
});
|
||||||
|
AddStep("add normal score", () =>
|
||||||
|
{
|
||||||
|
var testScore = TestResources.CreateTestScoreInfo();
|
||||||
|
testScore.TotalScore = RNG.Next(1_000_000);
|
||||||
|
|
||||||
|
feed.AddNewScore(new DailyChallengeEventFeed.NewScoreEvent(testScore, null));
|
||||||
|
breakdown.AddNewScore(testScore);
|
||||||
|
});
|
||||||
|
AddStep("add new user best", () =>
|
||||||
|
{
|
||||||
|
var testScore = TestResources.CreateTestScoreInfo();
|
||||||
|
testScore.TotalScore = RNG.Next(1_000_000);
|
||||||
|
|
||||||
|
feed.AddNewScore(new DailyChallengeEventFeed.NewScoreEvent(testScore, RNG.Next(1, 1000)));
|
||||||
|
breakdown.AddNewScore(testScore);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private partial class FakeContent : CompositeDrawable
|
||||||
|
{
|
||||||
|
private OsuSpriteText text = null!;
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
InternalChildren = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = new Colour4(RNG.NextSingle(), RNG.NextSingle(), RNG.NextSingle(), 1),
|
||||||
|
},
|
||||||
|
text = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Text = "Fake Content " + (char)('A' + RNG.Next(26)),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
text.FadeOut(500, Easing.OutQuint)
|
||||||
|
.Then().FadeIn(500, Easing.OutQuint)
|
||||||
|
.Loop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
// 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 NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Extensions.ObjectExtensions;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Screens.OnlinePlay.DailyChallenge;
|
||||||
|
using osu.Game.Tests.Resources;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.DailyChallenge
|
||||||
|
{
|
||||||
|
public partial class TestSceneDailyChallengeScoreBreakdown : OsuTestScene
|
||||||
|
{
|
||||||
|
[Cached]
|
||||||
|
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Plum);
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBasicAppearance()
|
||||||
|
{
|
||||||
|
DailyChallengeScoreBreakdown breakdown = null!;
|
||||||
|
|
||||||
|
AddStep("create content", () => Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new Box
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = colourProvider.Background4,
|
||||||
|
},
|
||||||
|
breakdown = new DailyChallengeScoreBreakdown
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
AddSliderStep("adjust width", 0.1f, 1, 1, width =>
|
||||||
|
{
|
||||||
|
if (breakdown.IsNotNull())
|
||||||
|
breakdown.Width = width;
|
||||||
|
});
|
||||||
|
AddSliderStep("adjust height", 0.1f, 1, 1, height =>
|
||||||
|
{
|
||||||
|
if (breakdown.IsNotNull())
|
||||||
|
breakdown.Height = height;
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("set initial data", () => breakdown.SetInitialCounts([1, 4, 9, 16, 25, 36, 49, 36, 25, 16, 9, 4, 1]));
|
||||||
|
AddStep("add new score", () =>
|
||||||
|
{
|
||||||
|
var testScore = TestResources.CreateTestScoreInfo();
|
||||||
|
testScore.TotalScore = RNG.Next(1_000_000);
|
||||||
|
|
||||||
|
breakdown.AddNewScore(testScore);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -84,6 +84,8 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
targetContainer = getTargetContainer();
|
targetContainer = getTargetContainer();
|
||||||
initialRotation = targetContainer!.Rotation;
|
initialRotation = targetContainer!.Rotation;
|
||||||
|
|
||||||
|
base.Begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Update(float rotation, Vector2? origin = null)
|
public override void Update(float rotation, Vector2? origin = null)
|
||||||
@ -102,6 +104,8 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
|
|
||||||
targetContainer = null;
|
targetContainer = null;
|
||||||
initialRotation = null;
|
initialRotation = null;
|
||||||
|
|
||||||
|
base.Commit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,5 +193,20 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddUntilStep("Wait for song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect);
|
AddUntilStep("Wait for song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect);
|
||||||
AddAssert("Tags reverted correctly", () => Game.Beatmap.Value.BeatmapInfo.Metadata.Tags == tags_to_save);
|
AddAssert("Tags reverted correctly", () => Game.Beatmap.Value.BeatmapInfo.Metadata.Tags == tags_to_save);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBeatDivisor()
|
||||||
|
{
|
||||||
|
AddStep("Set custom beat divisor", () => Editor.Dependencies.Get<BindableBeatDivisor>().SetArbitraryDivisor(7));
|
||||||
|
|
||||||
|
SaveEditor();
|
||||||
|
AddAssert("Hash updated", () => !string.IsNullOrEmpty(EditorBeatmap.BeatmapInfo.BeatmapSet?.Hash));
|
||||||
|
AddAssert("Beatmap has correct beat divisor", () => EditorBeatmap.BeatmapInfo.BeatDivisor, () => Is.EqualTo(7));
|
||||||
|
|
||||||
|
ReloadEditorToSameBeatmap();
|
||||||
|
|
||||||
|
AddAssert("Beatmap still has correct beat divisor", () => EditorBeatmap.BeatmapInfo.BeatDivisor, () => Is.EqualTo(7));
|
||||||
|
AddAssert("Correct beat divisor actually active", () => Editor.BeatDivisor, () => Is.EqualTo(7));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,6 +126,24 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
AddAssert("sample playback re-enabled", () => !Editor.SamplePlaybackDisabled.Value);
|
AddAssert("sample playback re-enabled", () => !Editor.SamplePlaybackDisabled.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[TestCase(2000)] // chosen to be after last object in the map
|
||||||
|
[TestCase(22000)] // chosen to be in the middle of the last spinner
|
||||||
|
public void TestGameplayTestAtEndOfBeatmap(int offsetFromEnd)
|
||||||
|
{
|
||||||
|
AddStep($"seek to end minus {offsetFromEnd}ms", () => EditorClock.Seek(importedBeatmapSet.MaxLength - offsetFromEnd));
|
||||||
|
AddStep("click test gameplay button", () =>
|
||||||
|
{
|
||||||
|
var button = Editor.ChildrenOfType<TestGameplayButton>().Single();
|
||||||
|
|
||||||
|
InputManager.MoveMouseTo(button);
|
||||||
|
InputManager.Click(MouseButton.Left);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("player pushed", () => Stack.CurrentScreen is EditorPlayer);
|
||||||
|
|
||||||
|
AddUntilStep("current screen is editor", () => Stack.CurrentScreen is Editor);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestCancelGameplayTestWithUnsavedChanges()
|
public void TestCancelGameplayTestWithUnsavedChanges()
|
||||||
{
|
{
|
||||||
|
@ -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));
|
||||||
|
@ -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;
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@ namespace osu.Game.Tests.Visual.Menus
|
|||||||
{
|
{
|
||||||
base.SetUpSteps();
|
base.SetUpSteps();
|
||||||
AddStep("don't fetch online content", () => onlineMenuBanner.FetchOnlineContent = false);
|
AddStep("don't fetch online content", () => onlineMenuBanner.FetchOnlineContent = false);
|
||||||
|
AddStep("disable return to top on idle", () => Game.ChildrenOfType<ButtonSystem>().Single().ReturnToTopOnIdle = false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Settings
|
|||||||
{
|
{
|
||||||
AddStep("create", () =>
|
AddStep("create", () =>
|
||||||
{
|
{
|
||||||
Cell(0, 0).Children = new Drawable[]
|
ContentContainer.Children = new Drawable[]
|
||||||
{
|
{
|
||||||
new Box
|
new Box
|
||||||
{
|
{
|
||||||
|
@ -6,23 +6,51 @@ using NUnit.Framework;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Input.Events;
|
|
||||||
using osu.Framework.Input.States;
|
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Graphics.UserInterface;
|
using osu.Game.Graphics.UserInterface;
|
||||||
using osu.Game.Input.Bindings;
|
using osuTK.Input;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.UserInterface
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
{
|
{
|
||||||
public partial class TestSceneOsuDropdown : ThemeComparisonTestScene
|
public partial class TestSceneOsuDropdown : ThemeComparisonTestScene
|
||||||
{
|
{
|
||||||
protected override Drawable CreateContent() =>
|
protected override Drawable CreateContent() => new OsuEnumDropdown<TestEnum>
|
||||||
new OsuEnumDropdown<TestEnum>
|
{
|
||||||
{
|
Anchor = Anchor.Centre,
|
||||||
Anchor = Anchor.Centre,
|
Origin = Anchor.TopCentre,
|
||||||
Origin = Anchor.TopCentre,
|
Width = 150
|
||||||
Width = 150
|
};
|
||||||
};
|
|
||||||
|
[Test]
|
||||||
|
public void TestBackAction()
|
||||||
|
{
|
||||||
|
AddStep("open", () => dropdownMenu.Open());
|
||||||
|
AddStep("press back", () => InputManager.Key(Key.Escape));
|
||||||
|
AddAssert("closed", () => dropdownMenu.State == MenuState.Closed);
|
||||||
|
|
||||||
|
AddStep("open", () => dropdownMenu.Open());
|
||||||
|
AddStep("type something", () => dropdownSearchBar.SearchTerm.Value = "something");
|
||||||
|
AddAssert("search bar visible", () => dropdownSearchBar.State.Value == Visibility.Visible);
|
||||||
|
AddStep("press back", () => InputManager.Key(Key.Escape));
|
||||||
|
AddAssert("text clear", () => dropdownSearchBar.SearchTerm.Value == string.Empty);
|
||||||
|
AddAssert("search bar hidden", () => dropdownSearchBar.State.Value == Visibility.Hidden);
|
||||||
|
AddAssert("still open", () => dropdownMenu.State == MenuState.Open);
|
||||||
|
AddStep("press back", () => InputManager.Key(Key.Escape));
|
||||||
|
AddAssert("closed", () => dropdownMenu.State == MenuState.Closed);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestSelectAction()
|
||||||
|
{
|
||||||
|
AddStep("open", () => dropdownMenu.Open());
|
||||||
|
AddStep("press down", () => InputManager.Key(Key.Down));
|
||||||
|
AddStep("press enter", () => InputManager.Key(Key.Enter));
|
||||||
|
AddAssert("second selected", () => dropdown.Current.Value == TestEnum.ReallyLongOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OsuEnumDropdown<TestEnum> dropdown => this.ChildrenOfType<OsuEnumDropdown<TestEnum>>().Last();
|
||||||
|
private Menu dropdownMenu => dropdown.ChildrenOfType<Menu>().Single();
|
||||||
|
private DropdownSearchBar dropdownSearchBar => dropdown.ChildrenOfType<DropdownSearchBar>().Single();
|
||||||
|
|
||||||
private enum TestEnum
|
private enum TestEnum
|
||||||
{
|
{
|
||||||
@ -32,26 +60,5 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
[System.ComponentModel.Description("Really lonnnnnnng option")]
|
[System.ComponentModel.Description("Really lonnnnnnng option")]
|
||||||
ReallyLongOption,
|
ReallyLongOption,
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
|
||||||
// todo: this can be written much better if ThemeComparisonTestScene has a manual input manager
|
|
||||||
public void TestBackAction()
|
|
||||||
{
|
|
||||||
AddStep("open", () => dropdown().ChildrenOfType<Menu>().Single().Open());
|
|
||||||
AddStep("press back", () => dropdown().OnPressed(new KeyBindingPressEvent<GlobalAction>(new InputState(), GlobalAction.Back)));
|
|
||||||
AddAssert("closed", () => dropdown().ChildrenOfType<Menu>().Single().State == MenuState.Closed);
|
|
||||||
|
|
||||||
AddStep("open", () => dropdown().ChildrenOfType<Menu>().Single().Open());
|
|
||||||
AddStep("type something", () => dropdown().ChildrenOfType<DropdownSearchBar>().Single().SearchTerm.Value = "something");
|
|
||||||
AddAssert("search bar visible", () => dropdown().ChildrenOfType<DropdownSearchBar>().Single().State.Value == Visibility.Visible);
|
|
||||||
AddStep("press back", () => dropdown().OnPressed(new KeyBindingPressEvent<GlobalAction>(new InputState(), GlobalAction.Back)));
|
|
||||||
AddAssert("text clear", () => dropdown().ChildrenOfType<DropdownSearchBar>().Single().SearchTerm.Value == string.Empty);
|
|
||||||
AddAssert("search bar hidden", () => dropdown().ChildrenOfType<DropdownSearchBar>().Single().State.Value == Visibility.Hidden);
|
|
||||||
AddAssert("still open", () => dropdown().ChildrenOfType<Menu>().Single().State == MenuState.Open);
|
|
||||||
AddStep("press back", () => dropdown().OnPressed(new KeyBindingPressEvent<GlobalAction>(new InputState(), GlobalAction.Back)));
|
|
||||||
AddAssert("closed", () => dropdown().ChildrenOfType<Menu>().Single().State == MenuState.Closed);
|
|
||||||
|
|
||||||
OsuEnumDropdown<TestEnum> dropdown() => this.ChildrenOfType<OsuEnumDropdown<TestEnum>>().First();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,8 +53,8 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
public void TestBackgroundColour()
|
public void TestBackgroundColour()
|
||||||
{
|
{
|
||||||
AddStep("set red scheme", () => CreateThemedContent(OverlayColourScheme.Red));
|
AddStep("set red scheme", () => CreateThemedContent(OverlayColourScheme.Red));
|
||||||
AddAssert("rounded button has correct colour", () => Cell(0, 1).ChildrenOfType<RoundedButton>().First().BackgroundColour == new OverlayColourProvider(OverlayColourScheme.Red).Colour3);
|
AddAssert("rounded button has correct colour", () => ContentContainer.ChildrenOfType<RoundedButton>().First().BackgroundColour == new OverlayColourProvider(OverlayColourScheme.Red).Colour3);
|
||||||
AddAssert("settings button has correct colour", () => Cell(0, 1).ChildrenOfType<SettingsButton>().First().BackgroundColour == new OverlayColourProvider(OverlayColourScheme.Red).Colour3);
|
AddAssert("settings button has correct colour", () => ContentContainer.ChildrenOfType<SettingsButton>().First().BackgroundColour == new OverlayColourProvider(OverlayColourScheme.Red).Colour3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,18 +6,21 @@ using System.Linq;
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Overlays;
|
using osu.Game.Overlays;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
namespace osu.Game.Tests.Visual.UserInterface
|
namespace osu.Game.Tests.Visual.UserInterface
|
||||||
{
|
{
|
||||||
public abstract partial class ThemeComparisonTestScene : OsuGridTestScene
|
public abstract partial class ThemeComparisonTestScene : OsuManualInputManagerTestScene
|
||||||
{
|
{
|
||||||
private readonly bool showWithoutColourProvider;
|
private readonly bool showWithoutColourProvider;
|
||||||
|
|
||||||
|
public Container ContentContainer { get; private set; } = null!;
|
||||||
|
|
||||||
protected ThemeComparisonTestScene(bool showWithoutColourProvider = true)
|
protected ThemeComparisonTestScene(bool showWithoutColourProvider = true)
|
||||||
: base(1, showWithoutColourProvider ? 2 : 1)
|
|
||||||
{
|
{
|
||||||
this.showWithoutColourProvider = showWithoutColourProvider;
|
this.showWithoutColourProvider = showWithoutColourProvider;
|
||||||
}
|
}
|
||||||
@ -25,16 +28,32 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load(OsuColour colours)
|
private void load(OsuColour colours)
|
||||||
{
|
{
|
||||||
|
Child = ContentContainer = new Container
|
||||||
|
{
|
||||||
|
Anchor = Anchor.TopRight,
|
||||||
|
Origin = Anchor.TopRight,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
};
|
||||||
|
|
||||||
if (showWithoutColourProvider)
|
if (showWithoutColourProvider)
|
||||||
{
|
{
|
||||||
Cell(0, 0).AddRange(new[]
|
ContentContainer.Size = new Vector2(0.5f, 1f);
|
||||||
|
|
||||||
|
Add(new Container
|
||||||
{
|
{
|
||||||
new Box
|
Anchor = Anchor.TopLeft,
|
||||||
|
Origin = Anchor.TopLeft,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Size = new Vector2(0.5f, 1f),
|
||||||
|
Children = new[]
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
new Box
|
||||||
Colour = colours.GreySeaFoam
|
{
|
||||||
},
|
RelativeSizeAxes = Axes.Both,
|
||||||
CreateContent()
|
Colour = colours.GreySeaFoam
|
||||||
|
},
|
||||||
|
CreateContent()
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -43,10 +62,8 @@ namespace osu.Game.Tests.Visual.UserInterface
|
|||||||
{
|
{
|
||||||
var colourProvider = new OverlayColourProvider(colourScheme);
|
var colourProvider = new OverlayColourProvider(colourScheme);
|
||||||
|
|
||||||
int col = showWithoutColourProvider ? 1 : 0;
|
ContentContainer.Clear();
|
||||||
|
ContentContainer.Add(new DependencyProvidingContainer
|
||||||
Cell(0, col).Clear();
|
|
||||||
Cell(0, col).Add(new DependencyProvidingContainer
|
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
CachedDependencies = new (Type, object)[]
|
CachedDependencies = new (Type, object)[]
|
||||||
|
@ -8,7 +8,6 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Game.IO.Serialization.Converters;
|
using osu.Game.IO.Serialization.Converters;
|
||||||
|
|
||||||
namespace osu.Game.Beatmaps
|
namespace osu.Game.Beatmaps
|
||||||
@ -62,7 +61,7 @@ namespace osu.Game.Beatmaps
|
|||||||
|
|
||||||
public ControlPointInfo ControlPointInfo { get; set; } = new ControlPointInfo();
|
public ControlPointInfo ControlPointInfo { get; set; } = new ControlPointInfo();
|
||||||
|
|
||||||
public BindableList<BreakPeriod> Breaks { get; set; } = new BindableList<BreakPeriod>();
|
public List<BreakPeriod> Breaks { get; set; } = new List<BreakPeriod>();
|
||||||
|
|
||||||
public List<string> UnhandledEventLines { get; set; } = new List<string>();
|
public List<string> UnhandledEventLines { get; set; } = new List<string>();
|
||||||
|
|
||||||
|
@ -49,6 +49,9 @@ namespace osu.Game.Beatmaps
|
|||||||
original.BeatmapInfo = original.BeatmapInfo.Clone();
|
original.BeatmapInfo = original.BeatmapInfo.Clone();
|
||||||
original.ControlPointInfo = original.ControlPointInfo.DeepClone();
|
original.ControlPointInfo = original.ControlPointInfo.DeepClone();
|
||||||
|
|
||||||
|
// Used in osu!mania conversion.
|
||||||
|
original.Breaks = original.Breaks.ToList();
|
||||||
|
|
||||||
return ConvertBeatmap(original, cancellationToken);
|
return ConvertBeatmap(original, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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)
|
||||||
|
@ -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.Bindables;
|
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Beatmaps.Timing;
|
using osu.Game.Beatmaps.Timing;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
@ -41,11 +40,11 @@ namespace osu.Game.Beatmaps
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The breaks in this beatmap.
|
/// The breaks in this beatmap.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
BindableList<BreakPeriod> Breaks { get; }
|
List<BreakPeriod> Breaks { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All lines from the [Events] section which aren't handled in the encoding process yet.
|
/// All lines from the [Events] section which aren't handled in the encoding process yet.
|
||||||
/// These lines shoule be written out to the beatmap file on save or export.
|
/// These lines should be written out to the beatmap file on save or export.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
List<string> UnhandledEventLines { get; }
|
List<string> UnhandledEventLines { get; }
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ using System.Text;
|
|||||||
using osu.Framework.Platform;
|
using osu.Framework.Platform;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.Formats;
|
using osu.Game.Beatmaps.Formats;
|
||||||
|
using osu.Game.Beatmaps.Timing;
|
||||||
using osu.Game.IO;
|
using osu.Game.IO;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
@ -59,9 +60,13 @@ namespace osu.Game.Database
|
|||||||
|
|
||||||
// Convert beatmap elements to be compatible with legacy format
|
// Convert beatmap elements to be compatible with legacy format
|
||||||
// So we truncate time and position values to integers, and convert paths with multiple segments to bezier curves
|
// So we truncate time and position values to integers, and convert paths with multiple segments to bezier curves
|
||||||
|
|
||||||
foreach (var controlPoint in playableBeatmap.ControlPointInfo.AllControlPoints)
|
foreach (var controlPoint in playableBeatmap.ControlPointInfo.AllControlPoints)
|
||||||
controlPoint.Time = Math.Floor(controlPoint.Time);
|
controlPoint.Time = Math.Floor(controlPoint.Time);
|
||||||
|
|
||||||
|
for (int i = 0; i < playableBeatmap.Breaks.Count; i++)
|
||||||
|
playableBeatmap.Breaks[i] = new BreakPeriod(Math.Floor(playableBeatmap.Breaks[i].StartTime), Math.Floor(playableBeatmap.Breaks[i].EndTime));
|
||||||
|
|
||||||
foreach (var hitObject in playableBeatmap.HitObjects)
|
foreach (var hitObject in playableBeatmap.HitObjects)
|
||||||
{
|
{
|
||||||
// Truncate end time before truncating start time because end time is dependent on start time
|
// Truncate end time before truncating start time because end time is dependent on start time
|
||||||
|
@ -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);
|
||||||
|
@ -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();
|
||||||
|
@ -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(
|
||||||
|
@ -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;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -418,16 +418,19 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
FontSize = OsuFont.Default.Size,
|
FontSize = OsuFont.Default.Size,
|
||||||
};
|
};
|
||||||
|
|
||||||
private partial class DropdownSearchTextBox : SearchTextBox
|
private partial class DropdownSearchTextBox : OsuTextBox
|
||||||
{
|
{
|
||||||
public override bool OnPressed(KeyBindingPressEvent<GlobalAction> e)
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OverlayColourProvider? colourProvider)
|
||||||
{
|
{
|
||||||
if (e.Action == GlobalAction.Back)
|
BackgroundUnfocused = colourProvider?.Background5 ?? new Color4(10, 10, 10, 255);
|
||||||
// this method is blocking Dropdown from receiving the back action, despite this text box residing in a separate input manager.
|
BackgroundFocused = colourProvider?.Background5 ?? new Color4(10, 10, 10, 255);
|
||||||
// to fix this properly, a local global action container needs to be added as well, but for simplicity, just don't handle the back action here.
|
}
|
||||||
return false;
|
|
||||||
|
|
||||||
return base.OnPressed(e);
|
protected override void OnFocus(FocusEvent e)
|
||||||
|
{
|
||||||
|
base.OnFocus(e);
|
||||||
|
BorderThickness = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -412,9 +412,6 @@ namespace osu.Game.Input.Bindings
|
|||||||
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorToggleRotateControl))]
|
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorToggleRotateControl))]
|
||||||
EditorToggleRotateControl,
|
EditorToggleRotateControl,
|
||||||
|
|
||||||
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorToggleScaleControl))]
|
|
||||||
EditorToggleScaleControl,
|
|
||||||
|
|
||||||
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.IncreaseOffset))]
|
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.IncreaseOffset))]
|
||||||
IncreaseOffset,
|
IncreaseOffset,
|
||||||
|
|
||||||
@ -432,6 +429,9 @@ namespace osu.Game.Input.Bindings
|
|||||||
|
|
||||||
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.DecreaseModSpeed))]
|
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.DecreaseModSpeed))]
|
||||||
DecreaseModSpeed,
|
DecreaseModSpeed,
|
||||||
|
|
||||||
|
[LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorToggleScaleControl))]
|
||||||
|
EditorToggleScaleControl,
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum GlobalActionCategory
|
public enum GlobalActionCategory
|
||||||
|
@ -271,7 +271,7 @@ namespace osu.Game.Online.Chat
|
|||||||
handleAdvanced(advanced_link_regex, result, startIndex);
|
handleAdvanced(advanced_link_regex, result, startIndex);
|
||||||
|
|
||||||
// handle editor times
|
// handle editor times
|
||||||
handleMatches(EditorTimestampParser.TIME_REGEX, "{0}", $@"{OsuGameBase.OSU_PROTOCOL}edit/{{0}}", result, startIndex, LinkAction.OpenEditorTimestamp);
|
handleMatches(EditorTimestampParser.TIME_REGEX_STRICT, "{0}", $@"{OsuGameBase.OSU_PROTOCOL}edit/{{0}}", result, startIndex, LinkAction.OpenEditorTimestamp);
|
||||||
|
|
||||||
// handle channels
|
// handle channels
|
||||||
handleMatches(channel_regex, "{0}", $@"{OsuGameBase.OSU_PROTOCOL}chan/{{0}}", result, startIndex, LinkAction.OpenChannel);
|
handleMatches(channel_regex, "{0}", $@"{OsuGameBase.OSU_PROTOCOL}chan/{{0}}", result, startIndex, LinkAction.OpenChannel);
|
||||||
|
@ -26,5 +26,11 @@ namespace osu.Game.Online.Metadata
|
|||||||
/// Null value means there is no "daily challenge" currently active.
|
/// Null value means there is no "daily challenge" currently active.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task DailyChallengeUpdated(DailyChallengeInfo? info);
|
Task DailyChallengeUpdated(DailyChallengeInfo? info);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Delivers information that a multiplayer score was set in a watched room.
|
||||||
|
/// To receive these, the client must call <see cref="IMetadataServer.BeginWatchingMultiplayerRoom"/> for a given room first.
|
||||||
|
/// </summary>
|
||||||
|
Task MultiplayerRoomScoreSet(MultiplayerRoomScoreSetEvent roomScoreSetEvent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,5 +43,15 @@ namespace osu.Game.Online.Metadata
|
|||||||
/// Signals to the server that the current user would like to stop receiving updates on other users' online presence.
|
/// Signals to the server that the current user would like to stop receiving updates on other users' online presence.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Task EndWatchingUserPresence();
|
Task EndWatchingUserPresence();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Signals to the server that the current user would like to begin receiving updates about the state of the multiplayer room with the given <paramref name="id"/>.
|
||||||
|
/// </summary>
|
||||||
|
Task<MultiplayerPlaylistItemStats[]> BeginWatchingMultiplayerRoom(long id);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Signals to the server that the current user would like to stop receiving updates about the state of the multiplayer room with the given <paramref name="id"/>.
|
||||||
|
/// </summary>
|
||||||
|
Task EndWatchingMultiplayerRoom(long id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,24 @@ namespace osu.Game.Online.Metadata
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Multiplayer room watching
|
||||||
|
|
||||||
|
public abstract Task<MultiplayerPlaylistItemStats[]> BeginWatchingMultiplayerRoom(long id);
|
||||||
|
|
||||||
|
public abstract Task EndWatchingMultiplayerRoom(long id);
|
||||||
|
|
||||||
|
public event Action<MultiplayerRoomScoreSetEvent>? MultiplayerRoomScoreSet;
|
||||||
|
|
||||||
|
Task IMetadataClient.MultiplayerRoomScoreSet(MultiplayerRoomScoreSetEvent roomScoreSetEvent)
|
||||||
|
{
|
||||||
|
if (MultiplayerRoomScoreSet != null)
|
||||||
|
Schedule(MultiplayerRoomScoreSet, roomScoreSetEvent);
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Disconnection handling
|
#region Disconnection handling
|
||||||
|
|
||||||
public event Action? Disconnecting;
|
public event Action? Disconnecting;
|
||||||
|
29
osu.Game/Online/Metadata/MultiplayerPlaylistItemStats.cs
Normal file
29
osu.Game/Online/Metadata/MultiplayerPlaylistItemStats.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// 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 MessagePack;
|
||||||
|
|
||||||
|
namespace osu.Game.Online.Metadata
|
||||||
|
{
|
||||||
|
[MessagePackObject]
|
||||||
|
[Serializable]
|
||||||
|
public class MultiplayerPlaylistItemStats
|
||||||
|
{
|
||||||
|
public const int TOTAL_SCORE_DISTRIBUTION_BINS = 13;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The ID of the playlist item which these stats pertain to.
|
||||||
|
/// </summary>
|
||||||
|
[Key(0)]
|
||||||
|
public long PlaylistItemID { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The count of scores with given total ranges in the room.
|
||||||
|
/// The ranges are bracketed into <see cref="TOTAL_SCORE_DISTRIBUTION_BINS"/> bins, each of 100,000 score width.
|
||||||
|
/// The last bin will contain count of all scores with total of 1,200,000 or larger.
|
||||||
|
/// </summary>
|
||||||
|
[Key(1)]
|
||||||
|
public long[] TotalScoreDistribution { get; set; } = new long[TOTAL_SCORE_DISTRIBUTION_BINS];
|
||||||
|
}
|
||||||
|
}
|
50
osu.Game/Online/Metadata/MultiplayerRoomScoreSetEvent.cs
Normal file
50
osu.Game/Online/Metadata/MultiplayerRoomScoreSetEvent.cs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// 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 MessagePack;
|
||||||
|
|
||||||
|
namespace osu.Game.Online.Metadata
|
||||||
|
{
|
||||||
|
[Serializable]
|
||||||
|
[MessagePackObject]
|
||||||
|
public class MultiplayerRoomScoreSetEvent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The ID of the room in which the score was set.
|
||||||
|
/// </summary>
|
||||||
|
[Key(0)]
|
||||||
|
public long RoomID { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The ID of the playlist item on which the score was set.
|
||||||
|
/// </summary>
|
||||||
|
[Key(1)]
|
||||||
|
public long PlaylistItemID { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The ID of the score set.
|
||||||
|
/// </summary>
|
||||||
|
[Key(2)]
|
||||||
|
public long ScoreID { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The ID of the user who set the score.
|
||||||
|
/// </summary>
|
||||||
|
[Key(3)]
|
||||||
|
public int UserID { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The total score set by the player.
|
||||||
|
/// </summary>
|
||||||
|
[Key(4)]
|
||||||
|
public long TotalScore { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If the set score is the user's new best on a playlist item, this member will contain the user's new rank in the room overall.
|
||||||
|
/// Otherwise, it will contain <see langword="null"/>.
|
||||||
|
/// </summary>
|
||||||
|
[Key(5)]
|
||||||
|
public int? NewRank { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -62,6 +62,7 @@ namespace osu.Game.Online.Metadata
|
|||||||
connection.On<BeatmapUpdates>(nameof(IMetadataClient.BeatmapSetsUpdated), ((IMetadataClient)this).BeatmapSetsUpdated);
|
connection.On<BeatmapUpdates>(nameof(IMetadataClient.BeatmapSetsUpdated), ((IMetadataClient)this).BeatmapSetsUpdated);
|
||||||
connection.On<int, UserPresence?>(nameof(IMetadataClient.UserPresenceUpdated), ((IMetadataClient)this).UserPresenceUpdated);
|
connection.On<int, UserPresence?>(nameof(IMetadataClient.UserPresenceUpdated), ((IMetadataClient)this).UserPresenceUpdated);
|
||||||
connection.On<DailyChallengeInfo?>(nameof(IMetadataClient.DailyChallengeUpdated), ((IMetadataClient)this).DailyChallengeUpdated);
|
connection.On<DailyChallengeInfo?>(nameof(IMetadataClient.DailyChallengeUpdated), ((IMetadataClient)this).DailyChallengeUpdated);
|
||||||
|
connection.On<MultiplayerRoomScoreSetEvent>(nameof(IMetadataClient.MultiplayerRoomScoreSet), ((IMetadataClient)this).MultiplayerRoomScoreSet);
|
||||||
connection.On(nameof(IStatefulUserHubClient.DisconnectRequested), ((IMetadataClient)this).DisconnectRequested);
|
connection.On(nameof(IStatefulUserHubClient.DisconnectRequested), ((IMetadataClient)this).DisconnectRequested);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -240,6 +241,24 @@ namespace osu.Game.Online.Metadata
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override async Task<MultiplayerPlaylistItemStats[]> BeginWatchingMultiplayerRoom(long id)
|
||||||
|
{
|
||||||
|
if (connector?.IsConnected.Value != true)
|
||||||
|
throw new OperationCanceledException();
|
||||||
|
|
||||||
|
Debug.Assert(connection != null);
|
||||||
|
return await connection.InvokeAsync<MultiplayerPlaylistItemStats[]>(nameof(IMetadataServer.BeginWatchingMultiplayerRoom), id).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async Task EndWatchingMultiplayerRoom(long id)
|
||||||
|
{
|
||||||
|
if (connector?.IsConnected.Value != true)
|
||||||
|
throw new OperationCanceledException();
|
||||||
|
|
||||||
|
Debug.Assert(connection != null);
|
||||||
|
await connection.InvokeAsync(nameof(IMetadataServer.EndWatchingMultiplayerRoom)).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
|
||||||
public override async Task DisconnectRequested()
|
public override async Task DisconnectRequested()
|
||||||
{
|
{
|
||||||
await base.DisconnectRequested().ConfigureAwait(false);
|
await base.DisconnectRequested().ConfigureAwait(false);
|
||||||
|
@ -6,6 +6,7 @@ using System.Linq;
|
|||||||
using Humanizer;
|
using Humanizer;
|
||||||
using Humanizer.Localisation;
|
using Humanizer.Localisation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Utils;
|
using osu.Game.Utils;
|
||||||
|
|
||||||
namespace osu.Game.Online.Rooms
|
namespace osu.Game.Online.Rooms
|
||||||
@ -42,14 +43,14 @@ namespace osu.Game.Online.Rooms
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the total duration from the <see cref="PlaylistItem"/> in playlist order from the supplied <paramref name="playlist"/>,
|
/// Returns the total duration from the <see cref="PlaylistItem"/> in playlist order from the supplied <paramref name="playlist"/>,
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string GetTotalDuration(this BindableList<PlaylistItem> playlist) =>
|
public static string GetTotalDuration(this BindableList<PlaylistItem> playlist, RulesetStore rulesetStore) =>
|
||||||
playlist.Select(p =>
|
playlist.Select(p =>
|
||||||
{
|
{
|
||||||
double rate = 1;
|
double rate = 1;
|
||||||
|
|
||||||
if (p.RequiredMods.Length > 0)
|
if (p.RequiredMods.Length > 0)
|
||||||
{
|
{
|
||||||
var ruleset = p.Beatmap.Ruleset.CreateInstance();
|
var ruleset = rulesetStore.GetRuleset(p.RulesetID)!.CreateInstance();
|
||||||
rate = ModUtils.CalculateRateWithMods(p.RequiredMods.Select(mod => mod.ToMod(ruleset)));
|
rate = ModUtils.CalculateRateWithMods(p.RequiredMods.Select(mod => mod.ToMod(ruleset)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ using System.Collections.Generic;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
@ -595,7 +596,7 @@ namespace osu.Game
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
editor.HandleTimestamp(timestamp);
|
editor.HandleTimestamp(timestamp, notifyOnError: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -871,6 +872,9 @@ namespace osu.Game
|
|||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
|
|
||||||
|
if (RuntimeInfo.EntryAssembly.GetCustomAttribute<OfficialBuildAttribute>() == null)
|
||||||
|
Logger.Log(NotificationsStrings.NotOfficialBuild.ToString());
|
||||||
|
|
||||||
var languages = Enum.GetValues<Language>();
|
var languages = Enum.GetValues<Language>();
|
||||||
|
|
||||||
var mappings = languages.Select(language =>
|
var mappings = languages.Select(language =>
|
||||||
|
@ -56,8 +56,6 @@ namespace osu.Game.Overlays.BeatmapListing
|
|||||||
[Resolved(canBeNull: true)]
|
[Resolved(canBeNull: true)]
|
||||||
private IDialogOverlay dialogOverlay { get; set; }
|
private IDialogOverlay dialogOverlay { get; set; }
|
||||||
|
|
||||||
protected override Color4 GetStateColour() => colours.Orange1;
|
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
@ -65,6 +63,9 @@ namespace osu.Game.Overlays.BeatmapListing
|
|||||||
disclaimerShown = sessionStatics.GetBindable<bool>(Static.FeaturedArtistDisclaimerShownOnce);
|
disclaimerShown = sessionStatics.GetBindable<bool>(Static.FeaturedArtistDisclaimerShownOnce);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override Color4 ColourNormal => colours.Orange1;
|
||||||
|
protected override Color4 ColourActive => colours.Orange2;
|
||||||
|
|
||||||
protected override bool OnClick(ClickEvent e)
|
protected override bool OnClick(ClickEvent e)
|
||||||
{
|
{
|
||||||
if (!disclaimerShown.Value && dialogOverlay != null)
|
if (!disclaimerShown.Value && dialogOverlay != null)
|
||||||
|
@ -1,21 +1,23 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
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;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Framework.Localisation;
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Graphics;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
using osuTK.Graphics;
|
||||||
|
using FontWeight = osu.Game.Graphics.FontWeight;
|
||||||
|
|
||||||
namespace osu.Game.Overlays.BeatmapListing
|
namespace osu.Game.Overlays.BeatmapListing
|
||||||
{
|
{
|
||||||
@ -24,7 +26,7 @@ namespace osu.Game.Overlays.BeatmapListing
|
|||||||
{
|
{
|
||||||
public new readonly BindableList<T> Current = new BindableList<T>();
|
public new readonly BindableList<T> Current = new BindableList<T>();
|
||||||
|
|
||||||
private MultipleSelectionFilter filter;
|
private MultipleSelectionFilter filter = null!;
|
||||||
|
|
||||||
public BeatmapSearchMultipleSelectionFilterRow(LocalisableString header)
|
public BeatmapSearchMultipleSelectionFilterRow(LocalisableString header)
|
||||||
: base(header)
|
: base(header)
|
||||||
@ -42,7 +44,6 @@ namespace osu.Game.Overlays.BeatmapListing
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a filter control that can be used to simultaneously select multiple values of type <typeparamref name="T"/>.
|
/// Creates a filter control that can be used to simultaneously select multiple values of type <typeparamref name="T"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[NotNull]
|
|
||||||
protected virtual MultipleSelectionFilter CreateMultipleSelectionFilter() => new MultipleSelectionFilter();
|
protected virtual MultipleSelectionFilter CreateMultipleSelectionFilter() => new MultipleSelectionFilter();
|
||||||
|
|
||||||
protected partial class MultipleSelectionFilter : FillFlowContainer<MultipleSelectionFilterTabItem>
|
protected partial class MultipleSelectionFilter : FillFlowContainer<MultipleSelectionFilterTabItem>
|
||||||
@ -54,7 +55,7 @@ namespace osu.Game.Overlays.BeatmapListing
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
AutoSizeAxes = Axes.Y;
|
AutoSizeAxes = Axes.Y;
|
||||||
Spacing = new Vector2(10, 0);
|
Spacing = new Vector2(10, 5);
|
||||||
|
|
||||||
AddRange(GetValues().Select(CreateTabItem));
|
AddRange(GetValues().Select(CreateTabItem));
|
||||||
}
|
}
|
||||||
@ -69,7 +70,7 @@ namespace osu.Game.Overlays.BeatmapListing
|
|||||||
Current.BindCollectionChanged(currentChanged, true);
|
Current.BindCollectionChanged(currentChanged, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void currentChanged(object sender, NotifyCollectionChangedEventArgs e)
|
private void currentChanged(object? sender, NotifyCollectionChangedEventArgs e)
|
||||||
{
|
{
|
||||||
foreach (var c in Children)
|
foreach (var c in Children)
|
||||||
c.Active.Value = Current.Contains(c.Value);
|
c.Active.Value = Current.Contains(c.Value);
|
||||||
@ -99,30 +100,91 @@ namespace osu.Game.Overlays.BeatmapListing
|
|||||||
|
|
||||||
protected partial class MultipleSelectionFilterTabItem : FilterTabItem<T>
|
protected partial class MultipleSelectionFilterTabItem : FilterTabItem<T>
|
||||||
{
|
{
|
||||||
private readonly Box selectedUnderline;
|
private Drawable activeContent = null!;
|
||||||
|
private Circle background = null!;
|
||||||
protected override bool HighlightOnHoverWhenActive => true;
|
|
||||||
|
|
||||||
public MultipleSelectionFilterTabItem(T value)
|
public MultipleSelectionFilterTabItem(T value)
|
||||||
: base(value)
|
: base(value)
|
||||||
{
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
AutoSizeDuration = 100;
|
||||||
|
AutoSizeEasing = Easing.OutQuint;
|
||||||
|
|
||||||
// This doesn't match any actual design, but should make it easier for the user to understand
|
// This doesn't match any actual design, but should make it easier for the user to understand
|
||||||
// that filters are applied until we settle on a final design.
|
// that filters are applied until we settle on a final design.
|
||||||
AddInternal(selectedUnderline = new Box
|
AddInternal(activeContent = new Container
|
||||||
{
|
{
|
||||||
Depth = float.MaxValue,
|
Depth = float.MaxValue,
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Height = 1.5f,
|
Alpha = 0,
|
||||||
Anchor = Anchor.BottomLeft,
|
Padding = new MarginPadding
|
||||||
Origin = Anchor.CentreLeft,
|
{
|
||||||
|
Left = -16,
|
||||||
|
Right = -4,
|
||||||
|
Vertical = -2
|
||||||
|
},
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
background = new Circle
|
||||||
|
{
|
||||||
|
Colour = Color4.White,
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
new SpriteIcon
|
||||||
|
{
|
||||||
|
Icon = FontAwesome.Solid.TimesCircle,
|
||||||
|
Size = new Vector2(10),
|
||||||
|
Colour = ColourProvider.Background4,
|
||||||
|
Position = new Vector2(3, 0.5f),
|
||||||
|
Anchor = Anchor.CentreLeft,
|
||||||
|
Origin = Anchor.CentreLeft,
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override Color4 ColourActive => ColourProvider.Light1;
|
||||||
|
|
||||||
|
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos)
|
||||||
|
{
|
||||||
|
return Active.Value
|
||||||
|
? background.ReceivePositionalInputAt(screenSpacePos)
|
||||||
|
: base.ReceivePositionalInputAt(screenSpacePos);
|
||||||
|
}
|
||||||
|
|
||||||
protected override void UpdateState()
|
protected override void UpdateState()
|
||||||
{
|
{
|
||||||
base.UpdateState();
|
Color4 colour = Active.Value ? ColourActive : ColourNormal;
|
||||||
selectedUnderline.FadeTo(Active.Value ? 1 : 0, 200, Easing.OutQuint);
|
|
||||||
selectedUnderline.FadeColour(IsHovered ? ColourProvider.Content2 : GetStateColour(), 200, Easing.OutQuint);
|
if (IsHovered)
|
||||||
|
colour = Active.Value ? colour.Darken(0.2f) : colour.Lighten(0.2f);
|
||||||
|
|
||||||
|
if (Active.Value)
|
||||||
|
{
|
||||||
|
// This just allows enough spacing for adjacent tab items to show the "x".
|
||||||
|
Padding = new MarginPadding { Left = 12 };
|
||||||
|
|
||||||
|
activeContent.FadeIn(200, Easing.OutQuint);
|
||||||
|
background.FadeColour(colour, 200, Easing.OutQuint);
|
||||||
|
|
||||||
|
// flipping colours
|
||||||
|
Text.FadeColour(ColourProvider.Background4, 200, Easing.OutQuint);
|
||||||
|
Text.Font = Text.Font.With(weight: FontWeight.SemiBold);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Padding = new MarginPadding();
|
||||||
|
|
||||||
|
activeContent.FadeOut();
|
||||||
|
|
||||||
|
background.FadeColour(colour, 200, Easing.OutQuint);
|
||||||
|
Text.FadeColour(colour, 200, Easing.OutQuint);
|
||||||
|
Text.Font = Text.Font.With(weight: FontWeight.Regular);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool OnClick(ClickEvent e)
|
protected override bool OnClick(ClickEvent e)
|
||||||
|
@ -8,6 +8,7 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
using osu.Framework.Extensions;
|
using osu.Framework.Extensions;
|
||||||
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.UserInterface;
|
using osu.Framework.Graphics.UserInterface;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
@ -24,7 +25,7 @@ namespace osu.Game.Overlays.BeatmapListing
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
protected OverlayColourProvider ColourProvider { get; private set; }
|
protected OverlayColourProvider ColourProvider { get; private set; }
|
||||||
|
|
||||||
private OsuSpriteText text;
|
protected OsuSpriteText Text;
|
||||||
|
|
||||||
protected Sample SelectSample { get; private set; } = null!;
|
protected Sample SelectSample { get; private set; } = null!;
|
||||||
|
|
||||||
@ -39,7 +40,7 @@ namespace osu.Game.Overlays.BeatmapListing
|
|||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
AddRangeInternal(new Drawable[]
|
AddRangeInternal(new Drawable[]
|
||||||
{
|
{
|
||||||
text = new OsuSpriteText
|
Text = new OsuSpriteText
|
||||||
{
|
{
|
||||||
Font = OsuFont.GetFont(size: 13, weight: FontWeight.Regular),
|
Font = OsuFont.GetFont(size: 13, weight: FontWeight.Regular),
|
||||||
Text = LabelFor(Value)
|
Text = LabelFor(Value)
|
||||||
@ -84,16 +85,18 @@ namespace osu.Game.Overlays.BeatmapListing
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual LocalisableString LabelFor(T value) => (value as Enum)?.GetLocalisableDescription() ?? value.ToString();
|
protected virtual LocalisableString LabelFor(T value) => (value as Enum)?.GetLocalisableDescription() ?? value.ToString();
|
||||||
|
|
||||||
protected virtual bool HighlightOnHoverWhenActive => false;
|
protected virtual Color4 ColourActive => ColourProvider.Content1;
|
||||||
|
protected virtual Color4 ColourNormal => ColourProvider.Light2;
|
||||||
|
|
||||||
protected virtual void UpdateState()
|
protected virtual void UpdateState()
|
||||||
{
|
{
|
||||||
bool highlightHover = IsHovered && (!Active.Value || HighlightOnHoverWhenActive);
|
Color4 colour = Active.Value ? ColourActive : ColourNormal;
|
||||||
|
|
||||||
text.FadeColour(highlightHover ? ColourProvider.Content2 : GetStateColour(), 200, Easing.OutQuint);
|
if (IsHovered)
|
||||||
text.Font = text.Font.With(weight: Active.Value ? FontWeight.Bold : FontWeight.Regular);
|
colour = colour.Lighten(0.2f);
|
||||||
|
|
||||||
|
Text.FadeColour(colour, 200, Easing.OutQuint);
|
||||||
|
Text.Font = Text.Font.With(weight: Active.Value ? FontWeight.Bold : FontWeight.Regular);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual Color4 GetStateColour() => Active.Value ? ColourProvider.Content1 : ColourProvider.Light2;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
using System;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Audio;
|
using osu.Framework.Audio;
|
||||||
using osu.Framework.Audio.Sample;
|
using osu.Framework.Audio.Sample;
|
||||||
@ -71,7 +72,7 @@ namespace osu.Game.Overlays.Dialog
|
|||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
{
|
{
|
||||||
base.LoadComplete();
|
base.LoadComplete();
|
||||||
Progress.BindValueChanged(progressChanged);
|
Progress.BindValueChanged(progressChanged, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void AbortConfirm()
|
protected override void AbortConfirm()
|
||||||
@ -122,11 +123,13 @@ namespace osu.Game.Overlays.Dialog
|
|||||||
|
|
||||||
private void progressChanged(ValueChangedEvent<double> progress)
|
private void progressChanged(ValueChangedEvent<double> progress)
|
||||||
{
|
{
|
||||||
if (progress.NewValue < progress.OldValue) return;
|
lowPassFilter.Cutoff = Math.Max(1, (int)(progress.NewValue * AudioFilter.MAX_LOWPASS_CUTOFF * 0.5));
|
||||||
|
|
||||||
if (Clock.CurrentTime - lastTickPlaybackTime < 30) return;
|
if (progress.NewValue < progress.OldValue)
|
||||||
|
return;
|
||||||
|
|
||||||
lowPassFilter.CutoffTo((int)(progress.NewValue * AudioFilter.MAX_LOWPASS_CUTOFF * 0.5));
|
if (Clock.CurrentTime - lastTickPlaybackTime < 30)
|
||||||
|
return;
|
||||||
|
|
||||||
var channel = tickSample.GetChannel();
|
var channel = tickSample.GetChannel();
|
||||||
|
|
||||||
|
@ -16,6 +16,9 @@ namespace osu.Game.Overlays.Mods
|
|||||||
{
|
{
|
||||||
private readonly BindableBool incompatible = new BindableBool();
|
private readonly BindableBool incompatible = new BindableBool();
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private OverlayColourProvider overlayColourProvider { get; set; } = null!;
|
||||||
|
|
||||||
[Resolved]
|
[Resolved]
|
||||||
private Bindable<IReadOnlyList<Mod>> selectedMods { get; set; } = null!;
|
private Bindable<IReadOnlyList<Mod>> selectedMods { get; set; } = null!;
|
||||||
|
|
||||||
@ -55,7 +58,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
#region IHasCustomTooltip
|
#region IHasCustomTooltip
|
||||||
|
|
||||||
public ITooltip<Mod> GetCustomTooltip() => new IncompatibilityDisplayingTooltip();
|
public ITooltip<Mod> GetCustomTooltip() => new IncompatibilityDisplayingTooltip(overlayColourProvider);
|
||||||
|
|
||||||
public Mod TooltipContent => Mod;
|
public Mod TooltipContent => Mod;
|
||||||
|
|
||||||
|
@ -24,13 +24,15 @@ namespace osu.Game.Overlays.Mods
|
|||||||
[Resolved]
|
[Resolved]
|
||||||
private Bindable<RulesetInfo> ruleset { get; set; } = null!;
|
private Bindable<RulesetInfo> ruleset { get; set; } = null!;
|
||||||
|
|
||||||
public IncompatibilityDisplayingTooltip()
|
public IncompatibilityDisplayingTooltip(OverlayColourProvider colourProvider)
|
||||||
|
: base(colourProvider)
|
||||||
{
|
{
|
||||||
AddRange(new Drawable[]
|
AddRange(new Drawable[]
|
||||||
{
|
{
|
||||||
incompatibleText = new OsuSpriteText
|
incompatibleText = new OsuSpriteText
|
||||||
{
|
{
|
||||||
Margin = new MarginPadding { Top = 5 },
|
Margin = new MarginPadding { Top = 5 },
|
||||||
|
Colour = colourProvider.Content2,
|
||||||
Font = OsuFont.GetFont(weight: FontWeight.Regular),
|
Font = OsuFont.GetFont(weight: FontWeight.Regular),
|
||||||
Text = "Incompatible with:"
|
Text = "Incompatible with:"
|
||||||
},
|
},
|
||||||
@ -43,12 +45,6 @@ namespace osu.Game.Overlays.Mods
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OsuColour colours)
|
|
||||||
{
|
|
||||||
incompatibleText.Colour = colours.BlueLight;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void UpdateDisplay(Mod mod)
|
protected override void UpdateDisplay(Mod mod)
|
||||||
{
|
{
|
||||||
base.UpdateDisplay(mod);
|
base.UpdateDisplay(mod);
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using osu.Framework.Allocation;
|
|
||||||
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.Cursor;
|
||||||
@ -18,11 +15,10 @@ namespace osu.Game.Overlays.Mods
|
|||||||
public partial class ModButtonTooltip : VisibilityContainer, ITooltip<Mod>
|
public partial class ModButtonTooltip : VisibilityContainer, ITooltip<Mod>
|
||||||
{
|
{
|
||||||
private readonly OsuSpriteText descriptionText;
|
private readonly OsuSpriteText descriptionText;
|
||||||
private readonly Box background;
|
|
||||||
|
|
||||||
protected override Container<Drawable> Content { get; }
|
protected override Container<Drawable> Content { get; }
|
||||||
|
|
||||||
public ModButtonTooltip()
|
public ModButtonTooltip(OverlayColourProvider colourProvider)
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both;
|
AutoSizeAxes = Axes.Both;
|
||||||
Masking = true;
|
Masking = true;
|
||||||
@ -30,9 +26,10 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
background = new Box
|
new Box
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Colour = colourProvider.Background6,
|
||||||
},
|
},
|
||||||
Content = new FillFlowContainer
|
Content = new FillFlowContainer
|
||||||
{
|
{
|
||||||
@ -43,6 +40,7 @@ namespace osu.Game.Overlays.Mods
|
|||||||
{
|
{
|
||||||
descriptionText = new OsuSpriteText
|
descriptionText = new OsuSpriteText
|
||||||
{
|
{
|
||||||
|
Colour = colourProvider.Content1,
|
||||||
Font = OsuFont.GetFont(weight: FontWeight.Regular),
|
Font = OsuFont.GetFont(weight: FontWeight.Regular),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -50,17 +48,10 @@ namespace osu.Game.Overlays.Mods
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
|
||||||
private void load(OsuColour colours)
|
|
||||||
{
|
|
||||||
background.Colour = colours.Gray3;
|
|
||||||
descriptionText.Colour = colours.BlueLighter;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void PopIn() => this.FadeIn(200, Easing.OutQuint);
|
protected override void PopIn() => this.FadeIn(200, Easing.OutQuint);
|
||||||
protected override void PopOut() => this.FadeOut(200, Easing.OutQuint);
|
protected override void PopOut() => this.FadeOut(200, Easing.OutQuint);
|
||||||
|
|
||||||
private Mod lastMod;
|
private Mod? lastMod;
|
||||||
|
|
||||||
public void SetContent(Mod mod)
|
public void SetContent(Mod mod)
|
||||||
{
|
{
|
||||||
|
@ -6,6 +6,8 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -17,6 +19,8 @@ namespace osu.Game.Overlays.Mods
|
|||||||
|
|
||||||
private const double transition_duration = 200;
|
private const double transition_duration = 200;
|
||||||
|
|
||||||
|
private readonly OsuSpriteText descriptionText;
|
||||||
|
|
||||||
public ModPresetTooltip(OverlayColourProvider colourProvider)
|
public ModPresetTooltip(OverlayColourProvider colourProvider)
|
||||||
{
|
{
|
||||||
Width = 250;
|
Width = 250;
|
||||||
@ -36,8 +40,16 @@ namespace osu.Game.Overlays.Mods
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
AutoSizeAxes = Axes.Y,
|
AutoSizeAxes = Axes.Y,
|
||||||
Padding = new MarginPadding(7),
|
Padding = new MarginPadding { Left = 10, Right = 10, Top = 5, Bottom = 5 },
|
||||||
Spacing = new Vector2(7)
|
Spacing = new Vector2(7),
|
||||||
|
Children = new[]
|
||||||
|
{
|
||||||
|
descriptionText = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Font = OsuFont.GetFont(weight: FontWeight.Regular),
|
||||||
|
Colour = colourProvider.Content1,
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -49,8 +61,12 @@ namespace osu.Game.Overlays.Mods
|
|||||||
if (ReferenceEquals(preset, lastPreset))
|
if (ReferenceEquals(preset, lastPreset))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
descriptionText.Text = preset.Description;
|
||||||
|
|
||||||
lastPreset = preset;
|
lastPreset = preset;
|
||||||
Content.ChildrenEnumerable = preset.Mods.AsOrdered().Select(mod => new ModPresetRow(mod));
|
|
||||||
|
Content.RemoveAll(d => d is ModPresetRow, true);
|
||||||
|
Content.AddRange(preset.Mods.AsOrdered().Select(mod => new ModPresetRow(mod)));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void PopIn() => this.FadeIn(transition_duration, Easing.OutQuint);
|
protected override void PopIn() => this.FadeIn(transition_duration, Easing.OutQuint);
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -61,6 +61,8 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
originalRotations = objectsInRotation.ToDictionary(d => d, d => d.Rotation);
|
originalRotations = objectsInRotation.ToDictionary(d => d, d => d.Rotation);
|
||||||
originalPositions = objectsInRotation.ToDictionary(d => d, d => d.ToScreenSpace(d.OriginPosition));
|
originalPositions = objectsInRotation.ToDictionary(d => d, d => d.ToScreenSpace(d.OriginPosition));
|
||||||
defaultOrigin = GeometryUtils.GetSurroundingQuad(objectsInRotation.SelectMany(d => d.ScreenSpaceDrawQuad.GetVertices().ToArray())).Centre;
|
defaultOrigin = GeometryUtils.GetSurroundingQuad(objectsInRotation.SelectMany(d => d.ScreenSpaceDrawQuad.GetVertices().ToArray())).Centre;
|
||||||
|
|
||||||
|
base.Begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Update(float rotation, Vector2? origin = null)
|
public override void Update(float rotation, Vector2? origin = null)
|
||||||
@ -99,6 +101,8 @@ namespace osu.Game.Overlays.SkinEditor
|
|||||||
originalPositions = null;
|
originalPositions = null;
|
||||||
originalRotations = null;
|
originalRotations = null;
|
||||||
defaultOrigin = null;
|
defaultOrigin = null;
|
||||||
|
|
||||||
|
base.Commit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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[]
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
|
@ -9,7 +9,6 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using osu.Framework.Audio.Track;
|
using osu.Framework.Audio.Track;
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
@ -110,26 +109,24 @@ namespace osu.Game.Rulesets.Difficulty
|
|||||||
var progressiveBeatmap = new ProgressiveCalculationBeatmap(Beatmap);
|
var progressiveBeatmap = new ProgressiveCalculationBeatmap(Beatmap);
|
||||||
var difficultyObjects = getDifficultyHitObjects().ToArray();
|
var difficultyObjects = getDifficultyHitObjects().ToArray();
|
||||||
|
|
||||||
foreach (var obj in difficultyObjects)
|
int currentIndex = 0;
|
||||||
|
|
||||||
|
foreach (var obj in Beatmap.HitObjects)
|
||||||
{
|
{
|
||||||
// Implementations expect the progressive beatmap to only contain top-level objects from the original beatmap.
|
progressiveBeatmap.HitObjects.Add(obj);
|
||||||
// At the same time, we also need to consider the possibility DHOs may not be generated for any given object,
|
|
||||||
// so we'll add all remaining objects up to the current point in time to the progressive beatmap.
|
|
||||||
for (int i = progressiveBeatmap.HitObjects.Count; i < Beatmap.HitObjects.Count; i++)
|
|
||||||
{
|
|
||||||
if (obj != difficultyObjects[^1] && Beatmap.HitObjects[i].StartTime > obj.BaseObject.StartTime)
|
|
||||||
break;
|
|
||||||
|
|
||||||
progressiveBeatmap.HitObjects.Add(Beatmap.HitObjects[i]);
|
while (currentIndex < difficultyObjects.Length && difficultyObjects[currentIndex].BaseObject.GetEndTime() <= obj.GetEndTime())
|
||||||
|
{
|
||||||
|
foreach (var skill in skills)
|
||||||
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
skill.Process(difficultyObjects[currentIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
currentIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var skill in skills)
|
attribs.Add(new TimedDifficultyAttributes(obj.GetEndTime(), CreateDifficultyAttributes(progressiveBeatmap, playableMods, skills, clockRate)));
|
||||||
{
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
skill.Process(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
attribs.Add(new TimedDifficultyAttributes(obj.EndTime * clockRate, CreateDifficultyAttributes(progressiveBeatmap, playableMods, skills, clockRate)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return attribs;
|
return attribs;
|
||||||
@ -330,7 +327,12 @@ namespace osu.Game.Rulesets.Difficulty
|
|||||||
set => baseBeatmap.Difficulty = value;
|
set => baseBeatmap.Difficulty = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BindableList<BreakPeriod> Breaks => baseBeatmap.Breaks;
|
public List<BreakPeriod> Breaks
|
||||||
|
{
|
||||||
|
get => baseBeatmap.Breaks;
|
||||||
|
set => baseBeatmap.Breaks = value;
|
||||||
|
}
|
||||||
|
|
||||||
public List<string> UnhandledEventLines => baseBeatmap.UnhandledEventLines;
|
public List<string> UnhandledEventLines => baseBeatmap.UnhandledEventLines;
|
||||||
|
|
||||||
public double TotalBreakTime => baseBeatmap.TotalBreakTime;
|
public double TotalBreakTime => baseBeatmap.TotalBreakTime;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user