mirror of
https://github.com/ppy/osu.git
synced 2025-02-14 03:22:54 +08:00
Merge branch 'master' into slider-splitter
This commit is contained in:
commit
03dcfed384
@ -21,7 +21,7 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"ppy.localisationanalyser.tools": {
|
"ppy.localisationanalyser.tools": {
|
||||||
"version": "2022.607.0",
|
"version": "2022.809.0",
|
||||||
"commands": [
|
"commands": [
|
||||||
"localisation"
|
"localisation"
|
||||||
]
|
]
|
||||||
|
@ -53,3 +53,7 @@ dotnet_diagnostic.CA2225.severity = none
|
|||||||
|
|
||||||
# Banned APIs
|
# Banned APIs
|
||||||
dotnet_diagnostic.RS0030.severity = error
|
dotnet_diagnostic.RS0030.severity = error
|
||||||
|
|
||||||
|
# Temporarily disable analysing CanBeNull = true in NRT contexts due to mobile issues.
|
||||||
|
# See: https://github.com/ppy/osu/pull/19677
|
||||||
|
dotnet_diagnostic.OSUF001.severity = none
|
@ -21,8 +21,11 @@ namespace osu.Game.Rulesets.Pippidon.Beatmaps
|
|||||||
public PippidonBeatmapConverter(IBeatmap beatmap, Ruleset ruleset)
|
public PippidonBeatmapConverter(IBeatmap beatmap, Ruleset ruleset)
|
||||||
: base(beatmap, ruleset)
|
: base(beatmap, ruleset)
|
||||||
{
|
{
|
||||||
minPosition = beatmap.HitObjects.Min(getUsablePosition);
|
if (beatmap.HitObjects.Any())
|
||||||
maxPosition = beatmap.HitObjects.Max(getUsablePosition);
|
{
|
||||||
|
minPosition = beatmap.HitObjects.Min(getUsablePosition);
|
||||||
|
maxPosition = beatmap.HitObjects.Max(getUsablePosition);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool CanConvert() => Beatmap.HitObjects.All(h => h is IHasXPosition && h is IHasYPosition);
|
public override bool CanConvert() => Beatmap.HitObjects.All(h => h is IHasXPosition && h is IHasYPosition);
|
||||||
|
@ -51,8 +51,8 @@
|
|||||||
<Reference Include="Java.Interop" />
|
<Reference Include="Java.Interop" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.810.0" />
|
<PackageReference Include="ppy.osu.Game.Resources" Version="2022.819.0" />
|
||||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.816.0" />
|
<PackageReference Include="ppy.osu.Framework.Android" Version="2022.819.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup Label="Transitive Dependencies">
|
<ItemGroup Label="Transitive Dependencies">
|
||||||
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
<!-- Realm needs to be directly referenced in all Xamarin projects, as it will not pull in its transitive dependencies otherwise. -->
|
||||||
|
@ -23,6 +23,7 @@ using osu.Game.Rulesets.Difficulty;
|
|||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using System;
|
using System;
|
||||||
using osu.Framework.Extensions.EnumExtensions;
|
using osu.Framework.Extensions.EnumExtensions;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Rulesets.Catch.Edit;
|
using osu.Game.Rulesets.Catch.Edit;
|
||||||
using osu.Game.Rulesets.Catch.Skinning.Legacy;
|
using osu.Game.Rulesets.Catch.Skinning.Legacy;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
@ -162,7 +163,7 @@ namespace osu.Game.Rulesets.Catch
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string GetDisplayNameForHitResult(HitResult result)
|
public override LocalisableString GetDisplayNameForHitResult(HitResult result)
|
||||||
{
|
{
|
||||||
switch (result)
|
switch (result)
|
||||||
{
|
{
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
// 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.Localisation;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Catch.Mods
|
namespace osu.Game.Rulesets.Catch.Mods
|
||||||
{
|
{
|
||||||
public class CatchModEasy : ModEasyWithExtraLives
|
public class CatchModEasy : ModEasyWithExtraLives
|
||||||
{
|
{
|
||||||
public override string Description => @"Larger fruits, more forgiving HP drain, less accuracy required, and three lives!";
|
public override LocalisableString Description => @"Larger fruits, more forgiving HP drain, less accuracy required, and three lives!";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
@ -14,7 +15,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
{
|
{
|
||||||
public override string Name => "Floating Fruits";
|
public override string Name => "Floating Fruits";
|
||||||
public override string Acronym => "FF";
|
public override string Acronym => "FF";
|
||||||
public override string Description => "The fruits are... floating?";
|
public override LocalisableString Description => "The fruits are... floating?";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
public override IconUsage? Icon => FontAwesome.Solid.Cloud;
|
public override IconUsage? Icon => FontAwesome.Solid.Cloud;
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
using osu.Game.Rulesets.Catch.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
@ -14,7 +15,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
{
|
{
|
||||||
public class CatchModHidden : ModHidden, IApplicableToDrawableRuleset<CatchHitObject>
|
public class CatchModHidden : ModHidden, IApplicableToDrawableRuleset<CatchHitObject>
|
||||||
{
|
{
|
||||||
public override string Description => @"Play with fading fruits.";
|
public override LocalisableString Description => @"Play with fading fruits.";
|
||||||
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1;
|
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1;
|
||||||
|
|
||||||
private const double fade_out_offset_multiplier = 0.6;
|
private const double fade_out_offset_multiplier = 0.6;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// 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 System.Linq;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
using osu.Game.Rulesets.Catch.Beatmaps;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
@ -14,7 +15,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
{
|
{
|
||||||
public class CatchModMirror : ModMirror, IApplicableToBeatmap
|
public class CatchModMirror : ModMirror, IApplicableToBeatmap
|
||||||
{
|
{
|
||||||
public override string Description => "Fruits are flipped horizontally.";
|
public override LocalisableString Description => "Fruits are flipped horizontally.";
|
||||||
|
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// <see cref="IApplicableToBeatmap"/> is used instead of <see cref="IApplicableToHitObject"/>,
|
/// <see cref="IApplicableToBeatmap"/> is used instead of <see cref="IApplicableToHitObject"/>,
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
@ -14,7 +15,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
{
|
{
|
||||||
public class CatchModNoScope : ModNoScope, IUpdatableByPlayfield
|
public class CatchModNoScope : ModNoScope, IUpdatableByPlayfield
|
||||||
{
|
{
|
||||||
public override string Description => "Where's the catcher?";
|
public override LocalisableString Description => "Where's the catcher?";
|
||||||
|
|
||||||
[SettingSource(
|
[SettingSource(
|
||||||
"Hidden at combo",
|
"Hidden at combo",
|
||||||
|
@ -5,6 +5,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Framework.Input;
|
using osu.Framework.Input;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Rulesets.Catch.Objects;
|
using osu.Game.Rulesets.Catch.Objects;
|
||||||
using osu.Game.Rulesets.Catch.UI;
|
using osu.Game.Rulesets.Catch.UI;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
@ -16,7 +17,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
|||||||
{
|
{
|
||||||
public class CatchModRelax : ModRelax, IApplicableToDrawableRuleset<CatchHitObject>, IApplicableToPlayer
|
public class CatchModRelax : ModRelax, IApplicableToDrawableRuleset<CatchHitObject>, IApplicableToPlayer
|
||||||
{
|
{
|
||||||
public override string Description => @"Use the mouse to control the catcher.";
|
public override LocalisableString Description => @"Use the mouse to control the catcher.";
|
||||||
|
|
||||||
private DrawableRuleset<CatchHitObject> drawableRuleset = null!;
|
private DrawableRuleset<CatchHitObject> drawableRuleset = null!;
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ 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;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Mania.Replays;
|
using osu.Game.Rulesets.Mania.Replays;
|
||||||
using osu.Game.Rulesets.Replays.Types;
|
using osu.Game.Rulesets.Replays.Types;
|
||||||
@ -311,7 +312,7 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
return Array.Empty<KeyBinding>();
|
return Array.Empty<KeyBinding>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string GetVariantName(int variant)
|
public override LocalisableString GetVariantName(int variant)
|
||||||
{
|
{
|
||||||
switch (getPlayfieldType(variant))
|
switch (getPlayfieldType(variant))
|
||||||
{
|
{
|
||||||
@ -356,7 +357,7 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string GetDisplayNameForHitResult(HitResult result)
|
public override LocalisableString GetDisplayNameForHitResult(HitResult result)
|
||||||
{
|
{
|
||||||
switch (result)
|
switch (result)
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// 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.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
@ -18,7 +19,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
|
|
||||||
public override double ScoreMultiplier => 0.9;
|
public override double ScoreMultiplier => 0.9;
|
||||||
|
|
||||||
public override string Description => "No more tricky speed changes!";
|
public override LocalisableString Description => "No more tricky speed changes!";
|
||||||
|
|
||||||
public override IconUsage? Icon => FontAwesome.Solid.Equals;
|
public override IconUsage? Icon => FontAwesome.Solid.Equals;
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
@ -11,7 +12,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
{
|
{
|
||||||
public override string Name => "Dual Stages";
|
public override string Name => "Dual Stages";
|
||||||
public override string Acronym => "DS";
|
public override string Acronym => "DS";
|
||||||
public override string Description => @"Double the stages, double the fun!";
|
public override LocalisableString Description => @"Double the stages, double the fun!";
|
||||||
public override ModType Type => ModType.Conversion;
|
public override ModType Type => ModType.Conversion;
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
// 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.Localisation;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModEasy : ModEasyWithExtraLives
|
public class ManiaModEasy : ModEasyWithExtraLives
|
||||||
{
|
{
|
||||||
public override string Description => @"More forgiving HP drain, less accuracy required, and three lives!";
|
public override LocalisableString Description => @"More forgiving HP drain, less accuracy required, and three lives!";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
@ -11,7 +12,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
{
|
{
|
||||||
public override string Name => "Fade In";
|
public override string Name => "Fade In";
|
||||||
public override string Acronym => "FI";
|
public override string Acronym => "FI";
|
||||||
public override string Description => @"Keys appear out of nowhere!";
|
public override LocalisableString Description => @"Keys appear out of nowhere!";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ManiaModHidden)).ToArray();
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ManiaModHidden)).ToArray();
|
||||||
|
@ -3,13 +3,14 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Rulesets.Mania.UI;
|
using osu.Game.Rulesets.Mania.UI;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModHidden : ManiaModPlayfieldCover
|
public class ManiaModHidden : ManiaModPlayfieldCover
|
||||||
{
|
{
|
||||||
public override string Description => @"Keys fade out before you hit them!";
|
public override LocalisableString Description => @"Keys fade out before you hit them!";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ManiaModFadeIn)).ToArray();
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ManiaModFadeIn)).ToArray();
|
||||||
|
@ -8,6 +8,7 @@ using osu.Game.Rulesets.Mania.Objects;
|
|||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
@ -20,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
|
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
public override string Description => @"Replaces all hold notes with normal notes.";
|
public override LocalisableString Description => @"Replaces all hold notes with normal notes.";
|
||||||
|
|
||||||
public override IconUsage? Icon => FontAwesome.Solid.DotCircle;
|
public override IconUsage? Icon => FontAwesome.Solid.DotCircle;
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
@ -20,7 +21,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
public override string Acronym => "IN";
|
public override string Acronym => "IN";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
public override string Description => "Hold the keys. To the beat.";
|
public override LocalisableString Description => "Hold the keys. To the beat.";
|
||||||
|
|
||||||
public override IconUsage? Icon => FontAwesome.Solid.YinYang;
|
public override IconUsage? Icon => FontAwesome.Solid.YinYang;
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModKey1 : ManiaKeyMod
|
public class ManiaModKey1 : ManiaKeyMod
|
||||||
@ -8,6 +10,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
public override int KeyCount => 1;
|
public override int KeyCount => 1;
|
||||||
public override string Name => "One Key";
|
public override string Name => "One Key";
|
||||||
public override string Acronym => "1K";
|
public override string Acronym => "1K";
|
||||||
public override string Description => @"Play with one key.";
|
public override LocalisableString Description => @"Play with one key.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModKey10 : ManiaKeyMod
|
public class ManiaModKey10 : ManiaKeyMod
|
||||||
@ -8,6 +10,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
public override int KeyCount => 10;
|
public override int KeyCount => 10;
|
||||||
public override string Name => "Ten Keys";
|
public override string Name => "Ten Keys";
|
||||||
public override string Acronym => "10K";
|
public override string Acronym => "10K";
|
||||||
public override string Description => @"Play with ten keys.";
|
public override LocalisableString Description => @"Play with ten keys.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModKey2 : ManiaKeyMod
|
public class ManiaModKey2 : ManiaKeyMod
|
||||||
@ -8,6 +10,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
public override int KeyCount => 2;
|
public override int KeyCount => 2;
|
||||||
public override string Name => "Two Keys";
|
public override string Name => "Two Keys";
|
||||||
public override string Acronym => "2K";
|
public override string Acronym => "2K";
|
||||||
public override string Description => @"Play with two keys.";
|
public override LocalisableString Description => @"Play with two keys.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModKey3 : ManiaKeyMod
|
public class ManiaModKey3 : ManiaKeyMod
|
||||||
@ -8,6 +10,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
public override int KeyCount => 3;
|
public override int KeyCount => 3;
|
||||||
public override string Name => "Three Keys";
|
public override string Name => "Three Keys";
|
||||||
public override string Acronym => "3K";
|
public override string Acronym => "3K";
|
||||||
public override string Description => @"Play with three keys.";
|
public override LocalisableString Description => @"Play with three keys.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModKey4 : ManiaKeyMod
|
public class ManiaModKey4 : ManiaKeyMod
|
||||||
@ -8,6 +10,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
public override int KeyCount => 4;
|
public override int KeyCount => 4;
|
||||||
public override string Name => "Four Keys";
|
public override string Name => "Four Keys";
|
||||||
public override string Acronym => "4K";
|
public override string Acronym => "4K";
|
||||||
public override string Description => @"Play with four keys.";
|
public override LocalisableString Description => @"Play with four keys.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModKey5 : ManiaKeyMod
|
public class ManiaModKey5 : ManiaKeyMod
|
||||||
@ -8,6 +10,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
public override int KeyCount => 5;
|
public override int KeyCount => 5;
|
||||||
public override string Name => "Five Keys";
|
public override string Name => "Five Keys";
|
||||||
public override string Acronym => "5K";
|
public override string Acronym => "5K";
|
||||||
public override string Description => @"Play with five keys.";
|
public override LocalisableString Description => @"Play with five keys.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModKey6 : ManiaKeyMod
|
public class ManiaModKey6 : ManiaKeyMod
|
||||||
@ -8,6 +10,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
public override int KeyCount => 6;
|
public override int KeyCount => 6;
|
||||||
public override string Name => "Six Keys";
|
public override string Name => "Six Keys";
|
||||||
public override string Acronym => "6K";
|
public override string Acronym => "6K";
|
||||||
public override string Description => @"Play with six keys.";
|
public override LocalisableString Description => @"Play with six keys.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModKey7 : ManiaKeyMod
|
public class ManiaModKey7 : ManiaKeyMod
|
||||||
@ -8,6 +10,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
public override int KeyCount => 7;
|
public override int KeyCount => 7;
|
||||||
public override string Name => "Seven Keys";
|
public override string Name => "Seven Keys";
|
||||||
public override string Acronym => "7K";
|
public override string Acronym => "7K";
|
||||||
public override string Description => @"Play with seven keys.";
|
public override LocalisableString Description => @"Play with seven keys.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModKey8 : ManiaKeyMod
|
public class ManiaModKey8 : ManiaKeyMod
|
||||||
@ -8,6 +10,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
public override int KeyCount => 8;
|
public override int KeyCount => 8;
|
||||||
public override string Name => "Eight Keys";
|
public override string Name => "Eight Keys";
|
||||||
public override string Acronym => "8K";
|
public override string Acronym => "8K";
|
||||||
public override string Description => @"Play with eight keys.";
|
public override LocalisableString Description => @"Play with eight keys.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Mania.Mods
|
namespace osu.Game.Rulesets.Mania.Mods
|
||||||
{
|
{
|
||||||
public class ManiaModKey9 : ManiaKeyMod
|
public class ManiaModKey9 : ManiaKeyMod
|
||||||
@ -8,6 +10,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
public override int KeyCount => 9;
|
public override int KeyCount => 9;
|
||||||
public override string Name => "Nine Keys";
|
public override string Name => "Nine Keys";
|
||||||
public override string Acronym => "9K";
|
public override string Acronym => "9K";
|
||||||
public override string Description => @"Play with nine keys.";
|
public override LocalisableString Description => @"Play with nine keys.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ using osu.Framework.Extensions.IEnumerableExtensions;
|
|||||||
using osu.Game.Rulesets.Mania.Objects;
|
using osu.Game.Rulesets.Mania.Objects;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
|
|
||||||
@ -12,7 +13,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
{
|
{
|
||||||
public class ManiaModMirror : ModMirror, IApplicableToBeatmap
|
public class ManiaModMirror : ModMirror, IApplicableToBeatmap
|
||||||
{
|
{
|
||||||
public override string Description => "Notes are flipped horizontally.";
|
public override LocalisableString Description => "Notes are flipped horizontally.";
|
||||||
|
|
||||||
public void ApplyToBeatmap(IBeatmap beatmap)
|
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||||
{
|
{
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Extensions.IEnumerableExtensions;
|
using osu.Framework.Extensions.IEnumerableExtensions;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||||
@ -14,7 +15,7 @@ namespace osu.Game.Rulesets.Mania.Mods
|
|||||||
{
|
{
|
||||||
public class ManiaModRandom : ModRandom, IApplicableToBeatmap
|
public class ManiaModRandom : ModRandom, IApplicableToBeatmap
|
||||||
{
|
{
|
||||||
public override string Description => @"Shuffle around the keys!";
|
public override LocalisableString Description => @"Shuffle around the keys!";
|
||||||
|
|
||||||
public void ApplyToBeatmap(IBeatmap beatmap)
|
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||||
{
|
{
|
||||||
|
@ -12,6 +12,7 @@ using osu.Game.Beatmaps;
|
|||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Osu.Edit;
|
using osu.Game.Rulesets.Osu.Edit;
|
||||||
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
|
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
@ -55,9 +56,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
{
|
{
|
||||||
ControlPoints =
|
ControlPoints =
|
||||||
{
|
{
|
||||||
new PathControlPoint(Vector2.Zero),
|
new PathControlPoint(Vector2.Zero, PathType.PerfectCurve),
|
||||||
new PathControlPoint(OsuPlayfield.BASE_SIZE * 2 / 5),
|
new PathControlPoint(new Vector2(136, 205)),
|
||||||
new PathControlPoint(OsuPlayfield.BASE_SIZE * 3 / 5)
|
new PathControlPoint(new Vector2(-4, 226))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
@ -99,8 +100,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
AddStep("move mouse to new point location", () =>
|
AddStep("move mouse to new point location", () =>
|
||||||
{
|
{
|
||||||
var firstPiece = this.ChildrenOfType<PathControlPointPiece>().Single(piece => piece.ControlPoint == slider.Path.ControlPoints[0]);
|
var firstPiece = this.ChildrenOfType<PathControlPointPiece>().Single(piece => piece.ControlPoint == slider.Path.ControlPoints[0]);
|
||||||
var secondPiece = this.ChildrenOfType<PathControlPointPiece>().Single(piece => piece.ControlPoint == slider.Path.ControlPoints[1]);
|
var pos = slider.Path.PositionAt(0.25d) + slider.Position;
|
||||||
InputManager.MoveMouseTo((firstPiece.ScreenSpaceDrawQuad.Centre + secondPiece.ScreenSpaceDrawQuad.Centre) / 2);
|
InputManager.MoveMouseTo(firstPiece.Parent.ToScreenSpace(pos));
|
||||||
});
|
});
|
||||||
AddStep("move slider end", () =>
|
AddStep("move slider end", () =>
|
||||||
{
|
{
|
||||||
@ -175,6 +176,23 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
assertSliderSnapped(false);
|
assertSliderSnapped(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestRotatingSliderRetainsPerfectControlPointType()
|
||||||
|
{
|
||||||
|
OsuSelectionHandler selectionHandler;
|
||||||
|
|
||||||
|
AddAssert("first control point perfect", () => slider.Path.ControlPoints[0].Type == PathType.PerfectCurve);
|
||||||
|
|
||||||
|
AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
|
||||||
|
AddStep("rotate 90 degrees ccw", () =>
|
||||||
|
{
|
||||||
|
selectionHandler = this.ChildrenOfType<OsuSelectionHandler>().Single();
|
||||||
|
selectionHandler.HandleRotation(-90);
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("first control point still perfect", () => slider.Path.ControlPoints[0].Type == PathType.PerfectCurve);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestFlippingSliderDoesNotSnap()
|
public void TestFlippingSliderDoesNotSnap()
|
||||||
{
|
{
|
||||||
@ -200,6 +218,23 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
|
|||||||
assertSliderSnapped(false);
|
assertSliderSnapped(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestFlippingSliderRetainsPerfectControlPointType()
|
||||||
|
{
|
||||||
|
OsuSelectionHandler selectionHandler;
|
||||||
|
|
||||||
|
AddAssert("first control point perfect", () => slider.Path.ControlPoints[0].Type == PathType.PerfectCurve);
|
||||||
|
|
||||||
|
AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider));
|
||||||
|
AddStep("flip slider horizontally", () =>
|
||||||
|
{
|
||||||
|
selectionHandler = this.ChildrenOfType<OsuSelectionHandler>().Single();
|
||||||
|
selectionHandler.OnPressed(new KeyBindingPressEvent<GlobalAction>(InputManager.CurrentState, GlobalAction.EditorFlipVertically));
|
||||||
|
});
|
||||||
|
|
||||||
|
AddAssert("first control point still perfect", () => slider.Path.ControlPoints[0].Type == PathType.PerfectCurve);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestReversingSliderDoesNotSnap()
|
public void TestReversingSliderDoesNotSnap()
|
||||||
{
|
{
|
||||||
|
@ -199,14 +199,14 @@ namespace osu.Game.Rulesets.Osu.Tests
|
|||||||
AddStep("adjust track rate", () => ((MasterGameplayClockContainer)Player.GameplayClockContainer).UserPlaybackRate.Value = rate);
|
AddStep("adjust track rate", () => ((MasterGameplayClockContainer)Player.GameplayClockContainer).UserPlaybackRate.Value = rate);
|
||||||
|
|
||||||
addSeekStep(1000);
|
addSeekStep(1000);
|
||||||
AddAssert("progress almost same", () => expectedProgress, () => Is.EqualTo(drawableSpinner.Progress).Within(0.05));
|
AddAssert("progress almost same", () => drawableSpinner.Progress, () => Is.EqualTo(expectedProgress).Within(0.05));
|
||||||
AddAssert("spm almost same", () => expectedSpm, () => Is.EqualTo(drawableSpinner.SpinsPerMinute.Value).Within(2.0));
|
AddAssert("spm almost same", () => drawableSpinner.SpinsPerMinute.Value, () => Is.EqualTo(expectedSpm).Within(2.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addSeekStep(double time)
|
private void addSeekStep(double time)
|
||||||
{
|
{
|
||||||
AddStep($"seek to {time}", () => Player.GameplayClockContainer.Seek(time));
|
AddStep($"seek to {time}", () => Player.GameplayClockContainer.Seek(time));
|
||||||
AddUntilStep("wait for seek to finish", () => time, () => Is.EqualTo(Player.DrawableRuleset.FrameStableClock.CurrentTime).Within(100));
|
AddUntilStep("wait for seek to finish", () => Player.DrawableRuleset.FrameStableClock.CurrentTime, () => Is.EqualTo(time).Within(100));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void transformReplay(Func<Replay, Replay> replayTransformation) => AddStep("set replay", () =>
|
private void transformReplay(Func<Replay, Replay> replayTransformation) => AddStep("set replay", () =>
|
||||||
|
@ -166,8 +166,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
|||||||
case NotifyCollectionChangedAction.Remove:
|
case NotifyCollectionChangedAction.Remove:
|
||||||
foreach (var point in e.OldItems.Cast<PathControlPoint>())
|
foreach (var point in e.OldItems.Cast<PathControlPoint>())
|
||||||
{
|
{
|
||||||
Pieces.RemoveAll(p => p.ControlPoint == point);
|
foreach (var piece in Pieces.Where(p => p.ControlPoint == point).ToArray())
|
||||||
Connections.RemoveAll(c => c.ControlPoint == point);
|
piece.RemoveAndDisposeImmediately();
|
||||||
|
foreach (var connection in Connections.Where(c => c.ControlPoint == point).ToArray())
|
||||||
|
connection.RemoveAndDisposeImmediately();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If removing before the end of the path,
|
// If removing before the end of the path,
|
||||||
|
@ -127,13 +127,16 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
{
|
{
|
||||||
didFlip = true;
|
didFlip = true;
|
||||||
|
|
||||||
foreach (var point in slider.Path.ControlPoints)
|
var controlPoints = slider.Path.ControlPoints.Select(p =>
|
||||||
{
|
new PathControlPoint(new Vector2(
|
||||||
point.Position = new Vector2(
|
(direction == Direction.Horizontal ? -1 : 1) * p.Position.X,
|
||||||
(direction == Direction.Horizontal ? -1 : 1) * point.Position.X,
|
(direction == Direction.Vertical ? -1 : 1) * p.Position.Y
|
||||||
(direction == Direction.Vertical ? -1 : 1) * point.Position.Y
|
), p.Type)).ToArray();
|
||||||
);
|
|
||||||
}
|
// Importantly, update as a single operation so automatic adjustment of control points to different
|
||||||
|
// curve types does not unexpectedly trigger and change the slider's shape.
|
||||||
|
slider.Path.ControlPoints.Clear();
|
||||||
|
slider.Path.ControlPoints.AddRange(controlPoints);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,8 +186,13 @@ namespace osu.Game.Rulesets.Osu.Edit
|
|||||||
|
|
||||||
if (h is IHasPath path)
|
if (h is IHasPath path)
|
||||||
{
|
{
|
||||||
foreach (var point in path.Path.ControlPoints)
|
var controlPoints = path.Path.ControlPoints.Select(p =>
|
||||||
point.Position = RotatePointAroundOrigin(point.Position, Vector2.Zero, delta);
|
new PathControlPoint(RotatePointAroundOrigin(p.Position, Vector2.Zero, delta), p.Type)).ToArray();
|
||||||
|
|
||||||
|
// Importantly, update as a single operation so automatic adjustment of control points to different
|
||||||
|
// curve types does not unexpectedly trigger and change the slider's shape.
|
||||||
|
path.Path.ControlPoints.Clear();
|
||||||
|
path.Path.ControlPoints.AddRange(controlPoints);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
{
|
{
|
||||||
@ -11,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public override string Name => @"Alternate";
|
public override string Name => @"Alternate";
|
||||||
public override string Acronym => @"AL";
|
public override string Acronym => @"AL";
|
||||||
public override string Description => @"Don't use the same key twice in a row!";
|
public override LocalisableString Description => @"Don't use the same key twice in a row!";
|
||||||
public override IconUsage? Icon => FontAwesome.Solid.Keyboard;
|
public override IconUsage? Icon => FontAwesome.Solid.Keyboard;
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModSingleTap) }).ToArray();
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModSingleTap) }).ToArray();
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
@ -16,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public override string Name => "Approach Different";
|
public override string Name => "Approach Different";
|
||||||
public override string Acronym => "AD";
|
public override string Acronym => "AD";
|
||||||
public override string Description => "Never trust the approach circles...";
|
public override LocalisableString Description => "Never trust the approach circles...";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
public override IconUsage? Icon { get; } = FontAwesome.Regular.Circle;
|
public override IconUsage? Icon { get; } = FontAwesome.Regular.Circle;
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Input.StateChanges;
|
using osu.Framework.Input.StateChanges;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
@ -20,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public override string Acronym => "AP";
|
public override string Acronym => "AP";
|
||||||
public override IconUsage? Icon => OsuIcon.ModAutopilot;
|
public override IconUsage? Icon => OsuIcon.ModAutopilot;
|
||||||
public override ModType Type => ModType.Automation;
|
public override ModType Type => ModType.Automation;
|
||||||
public override string Description => @"Automatic cursor movement - just follow the rhythm.";
|
public override LocalisableString Description => @"Automatic cursor movement - just follow the rhythm.";
|
||||||
public override double ScoreMultiplier => 0.1;
|
public override double ScoreMultiplier => 0.1;
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModFailCondition), typeof(ModNoFail), typeof(ModAutoplay), typeof(OsuModMagnetised), typeof(OsuModRepel) };
|
public override Type[] IncompatibleMods => new[] { typeof(OsuModSpunOut), typeof(ModRelax), typeof(ModFailCondition), typeof(ModNoFail), typeof(ModAutoplay), typeof(OsuModMagnetised), typeof(OsuModRepel) };
|
||||||
|
|
||||||
@ -30,8 +31,6 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
private OsuInputManager inputManager = null!;
|
private OsuInputManager inputManager = null!;
|
||||||
|
|
||||||
private IFrameStableClock gameplayClock = null!;
|
|
||||||
|
|
||||||
private List<OsuReplayFrame> replayFrames = null!;
|
private List<OsuReplayFrame> replayFrames = null!;
|
||||||
|
|
||||||
private int currentFrame;
|
private int currentFrame;
|
||||||
@ -40,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
if (currentFrame == replayFrames.Count - 1) return;
|
if (currentFrame == replayFrames.Count - 1) return;
|
||||||
|
|
||||||
double time = gameplayClock.CurrentTime;
|
double time = playfield.Clock.CurrentTime;
|
||||||
|
|
||||||
// Very naive implementation of autopilot based on proximity to replay frames.
|
// Very naive implementation of autopilot based on proximity to replay frames.
|
||||||
// TODO: this needs to be based on user interactions to better match stable (pausing until judgement is registered).
|
// TODO: this needs to be based on user interactions to better match stable (pausing until judgement is registered).
|
||||||
@ -55,8 +54,6 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
||||||
{
|
{
|
||||||
gameplayClock = drawableRuleset.FrameStableClock;
|
|
||||||
|
|
||||||
// Grab the input manager to disable the user's cursor, and for future use
|
// Grab the input manager to disable the user's cursor, and for future use
|
||||||
inputManager = (OsuInputManager)drawableRuleset.KeyBindingInputManager;
|
inputManager = (OsuInputManager)drawableRuleset.KeyBindingInputManager;
|
||||||
inputManager.AllowUserCursorMovement = false;
|
inputManager.AllowUserCursorMovement = false;
|
||||||
|
@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Shapes;
|
using osu.Framework.Graphics.Shapes;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
@ -22,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public class OsuModBlinds : Mod, IApplicableToDrawableRuleset<OsuHitObject>, IApplicableToHealthProcessor
|
public class OsuModBlinds : Mod, IApplicableToDrawableRuleset<OsuHitObject>, IApplicableToHealthProcessor
|
||||||
{
|
{
|
||||||
public override string Name => "Blinds";
|
public override string Name => "Blinds";
|
||||||
public override string Description => "Play with blinds on your screen.";
|
public override LocalisableString Description => "Play with blinds on your screen.";
|
||||||
public override string Acronym => "BL";
|
public override string Acronym => "BL";
|
||||||
|
|
||||||
public override IconUsage? Icon => FontAwesome.Solid.Adjust;
|
public override IconUsage? Icon => FontAwesome.Solid.Adjust;
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
@ -15,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
public override IconUsage? Icon => FontAwesome.Solid.CompressArrowsAlt;
|
public override IconUsage? Icon => FontAwesome.Solid.CompressArrowsAlt;
|
||||||
|
|
||||||
public override string Description => "Hit them at the right size!";
|
public override LocalisableString Description => "Hit them at the right size!";
|
||||||
|
|
||||||
[SettingSource("Starting Size", "The initial size multiplier applied to all objects.")]
|
[SettingSource("Starting Size", "The initial size multiplier applied to all objects.")]
|
||||||
public override BindableNumber<float> StartScale { get; } = new BindableFloat
|
public override BindableNumber<float> StartScale { get; } = new BindableFloat
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
// 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.Localisation;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
{
|
{
|
||||||
public class OsuModEasy : ModEasyWithExtraLives
|
public class OsuModEasy : ModEasyWithExtraLives
|
||||||
{
|
{
|
||||||
public override string Description => @"Larger circles, more forgiving HP drain, less accuracy required, and three lives!";
|
public override LocalisableString Description => @"Larger circles, more forgiving HP drain, less accuracy required, and three lives!";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
@ -15,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
public override IconUsage? Icon => FontAwesome.Solid.ArrowsAltV;
|
public override IconUsage? Icon => FontAwesome.Solid.ArrowsAltV;
|
||||||
|
|
||||||
public override string Description => "Hit them at the right size!";
|
public override LocalisableString Description => "Hit them at the right size!";
|
||||||
|
|
||||||
[SettingSource("Starting Size", "The initial size multiplier applied to all objects.")]
|
[SettingSource("Starting Size", "The initial size multiplier applied to all objects.")]
|
||||||
public override BindableNumber<float> StartScale { get; } = new BindableFloat
|
public override BindableNumber<float> StartScale { get; } = new BindableFloat
|
||||||
|
@ -6,6 +6,7 @@ using System.Diagnostics;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
@ -22,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
[SettingSource("Only fade approach circles", "The main object body will not fade when enabled.")]
|
[SettingSource("Only fade approach circles", "The main object body will not fade when enabled.")]
|
||||||
public Bindable<bool> OnlyFadeApproachCircles { get; } = new BindableBool();
|
public Bindable<bool> OnlyFadeApproachCircles { get; } = new BindableBool();
|
||||||
|
|
||||||
public override string Description => @"Play with no approach circles and fading circles/sliders.";
|
public override LocalisableString Description => @"Play with no approach circles and fading circles/sliders.";
|
||||||
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1;
|
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1;
|
||||||
|
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles), typeof(OsuModSpinIn) };
|
public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles), typeof(OsuModSpinIn) };
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Framework.Timing;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
@ -22,12 +24,10 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public override string Acronym => "MG";
|
public override string Acronym => "MG";
|
||||||
public override IconUsage? Icon => FontAwesome.Solid.Magnet;
|
public override IconUsage? Icon => FontAwesome.Solid.Magnet;
|
||||||
public override ModType Type => ModType.Fun;
|
public override ModType Type => ModType.Fun;
|
||||||
public override string Description => "No need to chase the circles – your cursor is a magnet!";
|
public override LocalisableString Description => "No need to chase the circles – your cursor is a magnet!";
|
||||||
public override double ScoreMultiplier => 0.5;
|
public override double ScoreMultiplier => 0.5;
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModRelax), typeof(OsuModRepel) };
|
public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModRelax), typeof(OsuModRepel) };
|
||||||
|
|
||||||
private IFrameStableClock gameplayClock = null!;
|
|
||||||
|
|
||||||
[SettingSource("Attraction strength", "How strong the pull is.", 0)]
|
[SettingSource("Attraction strength", "How strong the pull is.", 0)]
|
||||||
public BindableFloat AttractionStrength { get; } = new BindableFloat(0.5f)
|
public BindableFloat AttractionStrength { get; } = new BindableFloat(0.5f)
|
||||||
{
|
{
|
||||||
@ -38,8 +38,6 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
||||||
{
|
{
|
||||||
gameplayClock = drawableRuleset.FrameStableClock;
|
|
||||||
|
|
||||||
// Hide judgment displays and follow points as they won't make any sense.
|
// Hide judgment displays and follow points as they won't make any sense.
|
||||||
// Judgements can potentially be turned on in a future where they display at a position relative to their drawable counterpart.
|
// Judgements can potentially be turned on in a future where they display at a position relative to their drawable counterpart.
|
||||||
drawableRuleset.Playfield.DisplayJudgements.Value = false;
|
drawableRuleset.Playfield.DisplayJudgements.Value = false;
|
||||||
@ -55,27 +53,27 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
switch (drawable)
|
switch (drawable)
|
||||||
{
|
{
|
||||||
case DrawableHitCircle circle:
|
case DrawableHitCircle circle:
|
||||||
easeTo(circle, cursorPos);
|
easeTo(playfield.Clock, circle, cursorPos);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DrawableSlider slider:
|
case DrawableSlider slider:
|
||||||
|
|
||||||
if (!slider.HeadCircle.Result.HasResult)
|
if (!slider.HeadCircle.Result.HasResult)
|
||||||
easeTo(slider, cursorPos);
|
easeTo(playfield.Clock, slider, cursorPos);
|
||||||
else
|
else
|
||||||
easeTo(slider, cursorPos - slider.Ball.DrawPosition);
|
easeTo(playfield.Clock, slider, cursorPos - slider.Ball.DrawPosition);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void easeTo(DrawableHitObject hitObject, Vector2 destination)
|
private void easeTo(IFrameBasedClock clock, DrawableHitObject hitObject, Vector2 destination)
|
||||||
{
|
{
|
||||||
double dampLength = Interpolation.Lerp(3000, 40, AttractionStrength.Value);
|
double dampLength = Interpolation.Lerp(3000, 40, AttractionStrength.Value);
|
||||||
|
|
||||||
float x = (float)Interpolation.DampContinuously(hitObject.X, destination.X, dampLength, gameplayClock.ElapsedFrameTime);
|
float x = (float)Interpolation.DampContinuously(hitObject.X, destination.X, dampLength, clock.ElapsedFrameTime);
|
||||||
float y = (float)Interpolation.DampContinuously(hitObject.Y, destination.Y, dampLength, gameplayClock.ElapsedFrameTime);
|
float y = (float)Interpolation.DampContinuously(hitObject.Y, destination.Y, dampLength, clock.ElapsedFrameTime);
|
||||||
|
|
||||||
hitObject.Position = new Vector2(x, y);
|
hitObject.Position = new Vector2(x, y);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
@ -13,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public class OsuModMirror : ModMirror, IApplicableToHitObject
|
public class OsuModMirror : ModMirror, IApplicableToHitObject
|
||||||
{
|
{
|
||||||
public override string Description => "Flip objects on the chosen axes.";
|
public override LocalisableString Description => "Flip objects on the chosen axes.";
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModHardRock) };
|
public override Type[] IncompatibleMods => new[] { typeof(ModHardRock) };
|
||||||
|
|
||||||
[SettingSource("Mirrored axes", "Choose which axes objects are mirrored over.")]
|
[SettingSource("Mirrored axes", "Choose which axes objects are mirrored over.")]
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
@ -17,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public class OsuModNoScope : ModNoScope, IUpdatableByPlayfield, IApplicableToBeatmap
|
public class OsuModNoScope : ModNoScope, IUpdatableByPlayfield, IApplicableToBeatmap
|
||||||
{
|
{
|
||||||
public override string Description => "Where's the cursor?";
|
public override LocalisableString Description => "Where's the cursor?";
|
||||||
|
|
||||||
private PeriodTracker spinnerPeriods = null!;
|
private PeriodTracker spinnerPeriods = null!;
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
@ -18,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class OsuModRandom : ModRandom, IApplicableToBeatmap
|
public class OsuModRandom : ModRandom, IApplicableToBeatmap
|
||||||
{
|
{
|
||||||
public override string Description => "It never gets boring!";
|
public override LocalisableString Description => "It never gets boring!";
|
||||||
|
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModTarget)).ToArray();
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModTarget)).ToArray();
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ 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.Localisation;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
@ -18,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public class OsuModRelax : ModRelax, IUpdatableByPlayfield, IApplicableToDrawableRuleset<OsuHitObject>, IApplicableToPlayer
|
public class OsuModRelax : ModRelax, IUpdatableByPlayfield, IApplicableToDrawableRuleset<OsuHitObject>, IApplicableToPlayer
|
||||||
{
|
{
|
||||||
public override string Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things.";
|
public override LocalisableString Description => @"You don't need to click. Give your clicking/tapping fingers a break from the heat of things.";
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot), typeof(OsuModMagnetised), typeof(OsuModAlternate), typeof(OsuModSingleTap) }).ToArray();
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot), typeof(OsuModMagnetised), typeof(OsuModAlternate), typeof(OsuModSingleTap) }).ToArray();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -2,8 +2,9 @@
|
|||||||
// 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;
|
||||||
using System.Diagnostics;
|
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Framework.Timing;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
@ -22,12 +23,10 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public override string Name => "Repel";
|
public override string Name => "Repel";
|
||||||
public override string Acronym => "RP";
|
public override string Acronym => "RP";
|
||||||
public override ModType Type => ModType.Fun;
|
public override ModType Type => ModType.Fun;
|
||||||
public override string Description => "Hit objects run away!";
|
public override LocalisableString Description => "Hit objects run away!";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModMagnetised) };
|
public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModMagnetised) };
|
||||||
|
|
||||||
private IFrameStableClock? gameplayClock;
|
|
||||||
|
|
||||||
[SettingSource("Repulsion strength", "How strong the repulsion is.", 0)]
|
[SettingSource("Repulsion strength", "How strong the repulsion is.", 0)]
|
||||||
public BindableFloat RepulsionStrength { get; } = new BindableFloat(0.5f)
|
public BindableFloat RepulsionStrength { get; } = new BindableFloat(0.5f)
|
||||||
{
|
{
|
||||||
@ -38,8 +37,6 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
|
|
||||||
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
public void ApplyToDrawableRuleset(DrawableRuleset<OsuHitObject> drawableRuleset)
|
||||||
{
|
{
|
||||||
gameplayClock = drawableRuleset.FrameStableClock;
|
|
||||||
|
|
||||||
// Hide judgment displays and follow points as they won't make any sense.
|
// Hide judgment displays and follow points as they won't make any sense.
|
||||||
// Judgements can potentially be turned on in a future where they display at a position relative to their drawable counterpart.
|
// Judgements can potentially be turned on in a future where they display at a position relative to their drawable counterpart.
|
||||||
drawableRuleset.Playfield.DisplayJudgements.Value = false;
|
drawableRuleset.Playfield.DisplayJudgements.Value = false;
|
||||||
@ -68,29 +65,27 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
switch (drawable)
|
switch (drawable)
|
||||||
{
|
{
|
||||||
case DrawableHitCircle circle:
|
case DrawableHitCircle circle:
|
||||||
easeTo(circle, destination, cursorPos);
|
easeTo(playfield.Clock, circle, destination, cursorPos);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DrawableSlider slider:
|
case DrawableSlider slider:
|
||||||
|
|
||||||
if (!slider.HeadCircle.Result.HasResult)
|
if (!slider.HeadCircle.Result.HasResult)
|
||||||
easeTo(slider, destination, cursorPos);
|
easeTo(playfield.Clock, slider, destination, cursorPos);
|
||||||
else
|
else
|
||||||
easeTo(slider, destination - slider.Ball.DrawPosition, cursorPos);
|
easeTo(playfield.Clock, slider, destination - slider.Ball.DrawPosition, cursorPos);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void easeTo(DrawableHitObject hitObject, Vector2 destination, Vector2 cursorPos)
|
private void easeTo(IFrameBasedClock clock, DrawableHitObject hitObject, Vector2 destination, Vector2 cursorPos)
|
||||||
{
|
{
|
||||||
Debug.Assert(gameplayClock != null);
|
|
||||||
|
|
||||||
double dampLength = Vector2.Distance(hitObject.Position, cursorPos) / (0.04 * RepulsionStrength.Value + 0.04);
|
double dampLength = Vector2.Distance(hitObject.Position, cursorPos) / (0.04 * RepulsionStrength.Value + 0.04);
|
||||||
|
|
||||||
float x = (float)Interpolation.DampContinuously(hitObject.X, destination.X, dampLength, gameplayClock.ElapsedFrameTime);
|
float x = (float)Interpolation.DampContinuously(hitObject.X, destination.X, dampLength, clock.ElapsedFrameTime);
|
||||||
float y = (float)Interpolation.DampContinuously(hitObject.Y, destination.Y, dampLength, gameplayClock.ElapsedFrameTime);
|
float y = (float)Interpolation.DampContinuously(hitObject.Y, destination.Y, dampLength, clock.ElapsedFrameTime);
|
||||||
|
|
||||||
hitObject.Position = new Vector2(x, y);
|
hitObject.Position = new Vector2(x, y);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
{
|
{
|
||||||
@ -10,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public override string Name => @"Single Tap";
|
public override string Name => @"Single Tap";
|
||||||
public override string Acronym => @"SG";
|
public override string Acronym => @"SG";
|
||||||
public override string Description => @"You must only use one key!";
|
public override LocalisableString Description => @"You must only use one key!";
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAlternate) }).ToArray();
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAlternate) }).ToArray();
|
||||||
|
|
||||||
protected override bool CheckValidNewAction(OsuAction action) => LastAcceptedAction == null || LastAcceptedAction == action;
|
protected override bool CheckValidNewAction(OsuAction action) => LastAcceptedAction == null || LastAcceptedAction == action;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
@ -18,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public override string Acronym => "SI";
|
public override string Acronym => "SI";
|
||||||
public override IconUsage? Icon => FontAwesome.Solid.Undo;
|
public override IconUsage? Icon => FontAwesome.Solid.Undo;
|
||||||
public override ModType Type => ModType.Fun;
|
public override ModType Type => ModType.Fun;
|
||||||
public override string Description => "Circles spin in. No approach circles.";
|
public override LocalisableString Description => "Circles spin in. No approach circles.";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
// todo: this mod needs to be incompatible with "hidden" due to forcing the circle to remain opaque,
|
// todo: this mod needs to be incompatible with "hidden" due to forcing the circle to remain opaque,
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
@ -18,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public override string Acronym => "SO";
|
public override string Acronym => "SO";
|
||||||
public override IconUsage? Icon => OsuIcon.ModSpunOut;
|
public override IconUsage? Icon => OsuIcon.ModSpunOut;
|
||||||
public override ModType Type => ModType.Automation;
|
public override ModType Type => ModType.Automation;
|
||||||
public override string Description => @"Spinners will be automatically completed.";
|
public override LocalisableString Description => @"Spinners will be automatically completed.";
|
||||||
public override double ScoreMultiplier => 0.9;
|
public override double ScoreMultiplier => 0.9;
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot), typeof(OsuModTarget) };
|
public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot), typeof(OsuModTarget) };
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
@ -23,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public override string Name => @"Strict Tracking";
|
public override string Name => @"Strict Tracking";
|
||||||
public override string Acronym => @"ST";
|
public override string Acronym => @"ST";
|
||||||
public override ModType Type => ModType.DifficultyIncrease;
|
public override ModType Type => ModType.DifficultyIncrease;
|
||||||
public override string Description => @"Once you start a slider, follow precisely or get a miss.";
|
public override LocalisableString Description => @"Once you start a slider, follow precisely or get a miss.";
|
||||||
public override double ScoreMultiplier => 1.0;
|
public override double ScoreMultiplier => 1.0;
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModClassic), typeof(OsuModTarget) };
|
public override Type[] IncompatibleMods => new[] { typeof(ModClassic), typeof(OsuModTarget) };
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ using System.Linq;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -39,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public override string Acronym => "TP";
|
public override string Acronym => "TP";
|
||||||
public override ModType Type => ModType.Conversion;
|
public override ModType Type => ModType.Conversion;
|
||||||
public override IconUsage? Icon => OsuIcon.ModTarget;
|
public override IconUsage? Icon => OsuIcon.ModTarget;
|
||||||
public override string Description => @"Practice keeping up with the beat of the song.";
|
public override LocalisableString Description => @"Practice keeping up with the beat of the song.";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[]
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[]
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Mods
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
@ -9,7 +10,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
{
|
{
|
||||||
public override string Name => "Touch Device";
|
public override string Name => "Touch Device";
|
||||||
public override string Acronym => "TD";
|
public override string Acronym => "TD";
|
||||||
public override string Description => "Automatically applied to plays on devices with a touchscreen.";
|
public override LocalisableString Description => "Automatically applied to plays on devices with a touchscreen.";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
public override ModType Type => ModType.System;
|
public override ModType Type => ModType.System;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using osu.Framework.Extensions.Color4Extensions;
|
using osu.Framework.Extensions.Color4Extensions;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||||
@ -16,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public override string Name => "Traceable";
|
public override string Name => "Traceable";
|
||||||
public override string Acronym => "TC";
|
public override string Acronym => "TC";
|
||||||
public override ModType Type => ModType.Fun;
|
public override ModType Type => ModType.Fun;
|
||||||
public override string Description => "Put your faith in the approach circles...";
|
public override LocalisableString Description => "Put your faith in the approach circles...";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(IHidesApproachCircles) };
|
public override Type[] IncompatibleMods => new[] { typeof(IHidesApproachCircles) };
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
@ -18,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public override string Acronym => "TR";
|
public override string Acronym => "TR";
|
||||||
public override IconUsage? Icon => FontAwesome.Solid.ArrowsAlt;
|
public override IconUsage? Icon => FontAwesome.Solid.ArrowsAlt;
|
||||||
public override ModType Type => ModType.Fun;
|
public override ModType Type => ModType.Fun;
|
||||||
public override string Description => "Everything rotates. EVERYTHING.";
|
public override LocalisableString Description => "Everything rotates. EVERYTHING.";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModWiggle), typeof(OsuModMagnetised), typeof(OsuModRepel) };
|
public override Type[] IncompatibleMods => new[] { typeof(OsuModWiggle), typeof(OsuModMagnetised), typeof(OsuModRepel) };
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
@ -20,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
|||||||
public override string Acronym => "WG";
|
public override string Acronym => "WG";
|
||||||
public override IconUsage? Icon => FontAwesome.Solid.Certificate;
|
public override IconUsage? Icon => FontAwesome.Solid.Certificate;
|
||||||
public override ModType Type => ModType.Fun;
|
public override ModType Type => ModType.Fun;
|
||||||
public override string Description => "They just won't stay still...";
|
public override LocalisableString Description => "They just won't stay still...";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(OsuModTransform), typeof(OsuModMagnetised), typeof(OsuModRepel) };
|
public override Type[] IncompatibleMods => new[] { typeof(OsuModTransform), typeof(OsuModMagnetised), typeof(OsuModRepel) };
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ using osu.Game.Skinning;
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Extensions.EnumExtensions;
|
using osu.Framework.Extensions.EnumExtensions;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Rulesets.Osu.Edit.Setup;
|
using osu.Game.Rulesets.Osu.Edit.Setup;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
using osu.Game.Rulesets.Osu.Skinning.Legacy;
|
using osu.Game.Rulesets.Osu.Skinning.Legacy;
|
||||||
@ -253,7 +254,7 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string GetDisplayNameForHitResult(HitResult result)
|
public override LocalisableString GetDisplayNameForHitResult(HitResult result)
|
||||||
{
|
{
|
||||||
switch (result)
|
switch (result)
|
||||||
{
|
{
|
||||||
|
@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers;
|
|||||||
using osu.Framework.Graphics.Cursor;
|
using osu.Framework.Graphics.Cursor;
|
||||||
using osu.Framework.Input.Bindings;
|
using osu.Framework.Input.Bindings;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Rulesets.Osu.UI.Cursor;
|
using osu.Game.Rulesets.Osu.UI.Cursor;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
@ -28,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.UI
|
|||||||
|
|
||||||
public override CursorContainer LocalCursor => State.Value == Visibility.Visible ? localCursorContainer : null;
|
public override CursorContainer LocalCursor => State.Value == Visibility.Visible ? localCursorContainer : null;
|
||||||
|
|
||||||
protected override string Message => "Click the orange cursor to resume";
|
protected override LocalisableString Message => "Click the orange cursor to resume";
|
||||||
|
|
||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
@ -36,7 +34,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
TimeRange = { Value = 5000 },
|
TimeRange = { Value = 5000 },
|
||||||
};
|
};
|
||||||
|
|
||||||
private TaikoScoreProcessor scoreProcessor;
|
private TaikoScoreProcessor scoreProcessor = null!;
|
||||||
|
|
||||||
private IEnumerable<DrawableTaikoMascot> mascots => this.ChildrenOfType<DrawableTaikoMascot>();
|
private IEnumerable<DrawableTaikoMascot> mascots => this.ChildrenOfType<DrawableTaikoMascot>();
|
||||||
|
|
||||||
@ -65,6 +63,8 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestInitialState()
|
public void TestInitialState()
|
||||||
{
|
{
|
||||||
|
AddStep("set beatmap", () => setBeatmap());
|
||||||
|
|
||||||
AddStep("create mascot", () => SetContents(_ => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both }));
|
AddStep("create mascot", () => SetContents(_ => new DrawableTaikoMascot { RelativeSizeAxes = Axes.Both }));
|
||||||
|
|
||||||
AddAssert("mascot initially idle", () => allMascotsIn(TaikoMascotAnimationState.Idle));
|
AddAssert("mascot initially idle", () => allMascotsIn(TaikoMascotAnimationState.Idle));
|
||||||
@ -89,9 +89,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestIdleState()
|
public void TestIdleState()
|
||||||
{
|
{
|
||||||
AddStep("set beatmap", () => setBeatmap());
|
prepareDrawableRulesetAndBeatmap(false);
|
||||||
|
|
||||||
createDrawableRuleset();
|
|
||||||
|
|
||||||
assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Great }, TaikoMascotAnimationState.Idle);
|
assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Great }, TaikoMascotAnimationState.Idle);
|
||||||
assertStateAfterResult(new JudgementResult(new Hit.StrongNestedHit(), new TaikoStrongJudgement()) { Type = HitResult.IgnoreMiss }, TaikoMascotAnimationState.Idle);
|
assertStateAfterResult(new JudgementResult(new Hit.StrongNestedHit(), new TaikoStrongJudgement()) { Type = HitResult.IgnoreMiss }, TaikoMascotAnimationState.Idle);
|
||||||
@ -100,9 +98,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestKiaiState()
|
public void TestKiaiState()
|
||||||
{
|
{
|
||||||
AddStep("set beatmap", () => setBeatmap(true));
|
prepareDrawableRulesetAndBeatmap(true);
|
||||||
|
|
||||||
createDrawableRuleset();
|
|
||||||
|
|
||||||
assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Ok }, TaikoMascotAnimationState.Kiai);
|
assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Ok }, TaikoMascotAnimationState.Kiai);
|
||||||
assertStateAfterResult(new JudgementResult(new Hit(), new TaikoStrongJudgement()) { Type = HitResult.IgnoreMiss }, TaikoMascotAnimationState.Kiai);
|
assertStateAfterResult(new JudgementResult(new Hit(), new TaikoStrongJudgement()) { Type = HitResult.IgnoreMiss }, TaikoMascotAnimationState.Kiai);
|
||||||
@ -112,9 +108,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
[Test]
|
[Test]
|
||||||
public void TestMissState()
|
public void TestMissState()
|
||||||
{
|
{
|
||||||
AddStep("set beatmap", () => setBeatmap());
|
prepareDrawableRulesetAndBeatmap(false);
|
||||||
|
|
||||||
createDrawableRuleset();
|
|
||||||
|
|
||||||
assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Great }, TaikoMascotAnimationState.Idle);
|
assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Great }, TaikoMascotAnimationState.Idle);
|
||||||
assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Miss }, TaikoMascotAnimationState.Fail);
|
assertStateAfterResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Miss }, TaikoMascotAnimationState.Fail);
|
||||||
@ -126,9 +120,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
[TestCase(false)]
|
[TestCase(false)]
|
||||||
public void TestClearStateOnComboMilestone(bool kiai)
|
public void TestClearStateOnComboMilestone(bool kiai)
|
||||||
{
|
{
|
||||||
AddStep("set beatmap", () => setBeatmap(kiai));
|
prepareDrawableRulesetAndBeatmap(kiai);
|
||||||
|
|
||||||
createDrawableRuleset();
|
|
||||||
|
|
||||||
AddRepeatStep("reach 49 combo", () => applyNewResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Great }), 49);
|
AddRepeatStep("reach 49 combo", () => applyNewResult(new JudgementResult(new Hit(), new TaikoJudgement()) { Type = HitResult.Great }), 49);
|
||||||
|
|
||||||
@ -139,9 +131,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
[TestCase(false, TaikoMascotAnimationState.Idle)]
|
[TestCase(false, TaikoMascotAnimationState.Idle)]
|
||||||
public void TestClearStateOnClearedSwell(bool kiai, TaikoMascotAnimationState expectedStateAfterClear)
|
public void TestClearStateOnClearedSwell(bool kiai, TaikoMascotAnimationState expectedStateAfterClear)
|
||||||
{
|
{
|
||||||
AddStep("set beatmap", () => setBeatmap(kiai));
|
prepareDrawableRulesetAndBeatmap(kiai);
|
||||||
|
|
||||||
createDrawableRuleset();
|
|
||||||
|
|
||||||
assertStateAfterResult(new JudgementResult(new Swell(), new TaikoSwellJudgement()) { Type = HitResult.Great }, TaikoMascotAnimationState.Clear);
|
assertStateAfterResult(new JudgementResult(new Swell(), new TaikoSwellJudgement()) { Type = HitResult.Great }, TaikoMascotAnimationState.Clear);
|
||||||
AddUntilStep($"state reverts to {expectedStateAfterClear.ToString().ToLowerInvariant()}", () => allMascotsIn(expectedStateAfterClear));
|
AddUntilStep($"state reverts to {expectedStateAfterClear.ToString().ToLowerInvariant()}", () => allMascotsIn(expectedStateAfterClear));
|
||||||
@ -175,25 +165,27 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
scoreProcessor.ApplyBeatmap(Beatmap.Value.Beatmap);
|
scoreProcessor.ApplyBeatmap(Beatmap.Value.Beatmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createDrawableRuleset()
|
private void prepareDrawableRulesetAndBeatmap(bool kiai)
|
||||||
{
|
{
|
||||||
AddUntilStep("wait for beatmap to be loaded", () => Beatmap.Value.Track.IsLoaded);
|
AddStep("set beatmap", () => setBeatmap(kiai));
|
||||||
|
|
||||||
AddStep("create drawable ruleset", () =>
|
AddStep("create drawable ruleset", () =>
|
||||||
{
|
{
|
||||||
Beatmap.Value.Track.Start();
|
|
||||||
|
|
||||||
SetContents(_ =>
|
SetContents(_ =>
|
||||||
{
|
{
|
||||||
var ruleset = new TaikoRuleset();
|
var ruleset = new TaikoRuleset();
|
||||||
return new DrawableTaikoRuleset(ruleset, Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo));
|
return new DrawableTaikoRuleset(ruleset, Beatmap.Value.GetPlayableBeatmap(ruleset.RulesetInfo));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
AddUntilStep("wait for track to be loaded", () => MusicController.TrackLoaded);
|
||||||
|
AddStep("start track", () => MusicController.CurrentTrack.Restart());
|
||||||
|
AddUntilStep("wait for track started", () => MusicController.IsPlaying);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertStateAfterResult(JudgementResult judgementResult, TaikoMascotAnimationState expectedState)
|
private void assertStateAfterResult(JudgementResult judgementResult, TaikoMascotAnimationState expectedState)
|
||||||
{
|
{
|
||||||
TaikoMascotAnimationState[] mascotStates = null;
|
TaikoMascotAnimationState[] mascotStates = null!;
|
||||||
|
|
||||||
AddStep($"{judgementResult.Type.ToString().ToLowerInvariant()} result for {judgementResult.Judgement.GetType().Name.Humanize(LetterCasing.LowerCase)}",
|
AddStep($"{judgementResult.Type.ToString().ToLowerInvariant()} result for {judgementResult.Judgement.GetType().Name.Humanize(LetterCasing.LowerCase)}",
|
||||||
() =>
|
() =>
|
||||||
@ -204,7 +196,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
|||||||
Schedule(() => mascotStates = animatedMascots.Select(mascot => mascot.State.Value).ToArray());
|
Schedule(() => mascotStates = animatedMascots.Select(mascot => mascot.State.Value).ToArray());
|
||||||
});
|
});
|
||||||
|
|
||||||
AddAssert($"state is {expectedState.ToString().ToLowerInvariant()}", () => mascotStates.All(state => state == expectedState));
|
AddAssert($"state is {expectedState.ToString().ToLowerInvariant()}", () => mascotStates.Distinct(), () => Is.EquivalentTo(new[] { expectedState }));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applyNewResult(JudgementResult judgementResult)
|
private void applyNewResult(JudgementResult judgementResult)
|
||||||
|
@ -16,13 +16,13 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko";
|
||||||
|
|
||||||
[TestCase(1.9971301024093662d, 200, "diffcalc-test")]
|
[TestCase(3.1098944660126882d, 200, "diffcalc-test")]
|
||||||
[TestCase(1.9971301024093662d, 200, "diffcalc-test-strong")]
|
[TestCase(3.1098944660126882d, 200, "diffcalc-test-strong")]
|
||||||
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
|
public void Test(double expectedStarRating, int expectedMaxCombo, string name)
|
||||||
=> base.Test(expectedStarRating, expectedMaxCombo, name);
|
=> base.Test(expectedStarRating, expectedMaxCombo, name);
|
||||||
|
|
||||||
[TestCase(3.1645810961313674d, 200, "diffcalc-test")]
|
[TestCase(4.0974106752474251d, 200, "diffcalc-test")]
|
||||||
[TestCase(3.1645810961313674d, 200, "diffcalc-test-strong")]
|
[TestCase(4.0974106752474251d, 200, "diffcalc-test-strong")]
|
||||||
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
|
public void TestClockRateAdjusted(double expectedStarRating, int expectedMaxCombo, string name)
|
||||||
=> Test(expectedStarRating, expectedMaxCombo, name, new TaikoModDoubleTime());
|
=> Test(expectedStarRating, expectedMaxCombo, name, new TaikoModDoubleTime());
|
||||||
|
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
|
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
|
||||||
|
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour;
|
||||||
|
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators
|
||||||
|
{
|
||||||
|
public class ColourEvaluator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A sigmoid function. It gives a value between (middle - height/2) and (middle + height/2).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="val">The input value.</param>
|
||||||
|
/// <param name="center">The center of the sigmoid, where the largest gradient occurs and value is equal to middle.</param>
|
||||||
|
/// <param name="width">The radius of the sigmoid, outside of which values are near the minimum/maximum.</param>
|
||||||
|
/// <param name="middle">The middle of the sigmoid output.</param>
|
||||||
|
/// <param name="height">The height of the sigmoid output. This will be equal to max value - min value.</param>
|
||||||
|
private static double sigmoid(double val, double center, double width, double middle, double height)
|
||||||
|
{
|
||||||
|
double sigmoid = Math.Tanh(Math.E * -(val - center) / width);
|
||||||
|
return sigmoid * (height / 2) + middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Evaluate the difficulty of the first note of a <see cref="MonoStreak"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static double EvaluateDifficultyOf(MonoStreak monoStreak)
|
||||||
|
{
|
||||||
|
return sigmoid(monoStreak.Index, 2, 2, 0.5, 1) * EvaluateDifficultyOf(monoStreak.Parent) * 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Evaluate the difficulty of the first note of a <see cref="AlternatingMonoPattern"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static double EvaluateDifficultyOf(AlternatingMonoPattern alternatingMonoPattern)
|
||||||
|
{
|
||||||
|
return sigmoid(alternatingMonoPattern.Index, 2, 2, 0.5, 1) * EvaluateDifficultyOf(alternatingMonoPattern.Parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Evaluate the difficulty of the first note of a <see cref="RepeatingHitPatterns"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static double EvaluateDifficultyOf(RepeatingHitPatterns repeatingHitPattern)
|
||||||
|
{
|
||||||
|
return 2 * (1 - sigmoid(repeatingHitPattern.RepetitionInterval, 2, 2, 0.5, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static double EvaluateDifficultyOf(DifficultyHitObject hitObject)
|
||||||
|
{
|
||||||
|
TaikoDifficultyHitObjectColour colour = ((TaikoDifficultyHitObject)hitObject).Colour;
|
||||||
|
double difficulty = 0.0d;
|
||||||
|
|
||||||
|
if (colour.MonoStreak != null) // Difficulty for MonoStreak
|
||||||
|
difficulty += EvaluateDifficultyOf(colour.MonoStreak);
|
||||||
|
if (colour.AlternatingMonoPattern != null) // Difficulty for AlternatingMonoPattern
|
||||||
|
difficulty += EvaluateDifficultyOf(colour.AlternatingMonoPattern);
|
||||||
|
if (colour.RepeatingHitPattern != null) // Difficulty for RepeatingHitPattern
|
||||||
|
difficulty += EvaluateDifficultyOf(colour.RepeatingHitPattern);
|
||||||
|
|
||||||
|
return difficulty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
|
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Difficulty.Evaluators
|
||||||
|
{
|
||||||
|
public class StaminaEvaluator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Applies a speed bonus dependent on the time since the last hit performed using this key.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="interval">The interval between the current and previous note hit using the same key.</param>
|
||||||
|
private static double speedBonus(double interval)
|
||||||
|
{
|
||||||
|
// Cap to 600bpm 1/4, 25ms note interval, 50ms key interval
|
||||||
|
// Interval will be capped at a very small value to avoid infinite/negative speed bonuses.
|
||||||
|
// TODO - This is a temporary measure as we need to implement methods of detecting playstyle-abuse of SpeedBonus.
|
||||||
|
interval = Math.Max(interval, 50);
|
||||||
|
|
||||||
|
return 30 / interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Evaluates the minimum mechanical stamina required to play the current object. This is calculated using the
|
||||||
|
/// maximum possible interval between two hits using the same key, by alternating 2 keys for each colour.
|
||||||
|
/// </summary>
|
||||||
|
public static double EvaluateDifficultyOf(DifficultyHitObject current)
|
||||||
|
{
|
||||||
|
if (current.BaseObject is not Hit)
|
||||||
|
{
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the previous hit object hit by the current key, which is two notes of the same colour prior.
|
||||||
|
TaikoDifficultyHitObject taikoCurrent = (TaikoDifficultyHitObject)current;
|
||||||
|
TaikoDifficultyHitObject? keyPrevious = taikoCurrent.PreviousMono(1);
|
||||||
|
|
||||||
|
if (keyPrevious == null)
|
||||||
|
{
|
||||||
|
// There is no previous hit object hit by the current key
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double objectStrain = 0.5; // Add a base strain to all objects
|
||||||
|
objectStrain += speedBonus(taikoCurrent.StartTime - keyPrevious.StartTime);
|
||||||
|
return objectStrain;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Encodes a list of <see cref="MonoStreak"/>s.
|
||||||
|
/// <see cref="MonoStreak"/>s with the same <see cref="MonoStreak.RunLength"/> are grouped together.
|
||||||
|
/// </summary>
|
||||||
|
public class AlternatingMonoPattern
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="MonoStreak"/>s that are grouped together within this <see cref="AlternatingMonoPattern"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly List<MonoStreak> MonoStreaks = new List<MonoStreak>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The parent <see cref="RepeatingHitPatterns"/> that contains this <see cref="AlternatingMonoPattern"/>
|
||||||
|
/// </summary>
|
||||||
|
public RepeatingHitPatterns Parent = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Index of this <see cref="AlternatingMonoPattern"/> within it's parent <see cref="RepeatingHitPatterns"/>
|
||||||
|
/// </summary>
|
||||||
|
public int Index;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The first <see cref="TaikoDifficultyHitObject"/> in this <see cref="AlternatingMonoPattern"/>.
|
||||||
|
/// </summary>
|
||||||
|
public TaikoDifficultyHitObject FirstHitObject => MonoStreaks[0].FirstHitObject;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determine if this <see cref="AlternatingMonoPattern"/> is a repetition of another <see cref="AlternatingMonoPattern"/>. This
|
||||||
|
/// is a strict comparison and is true if and only if the colour sequence is exactly the same.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsRepetitionOf(AlternatingMonoPattern other)
|
||||||
|
{
|
||||||
|
return HasIdenticalMonoLength(other) &&
|
||||||
|
other.MonoStreaks.Count == MonoStreaks.Count &&
|
||||||
|
other.MonoStreaks[0].HitType == MonoStreaks[0].HitType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determine if this <see cref="AlternatingMonoPattern"/> has the same mono length of another <see cref="AlternatingMonoPattern"/>.
|
||||||
|
/// </summary>
|
||||||
|
public bool HasIdenticalMonoLength(AlternatingMonoPattern other)
|
||||||
|
{
|
||||||
|
return other.MonoStreaks[0].RunLength == MonoStreaks[0].RunLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Encode colour information for a sequence of <see cref="TaikoDifficultyHitObject"/>s. Consecutive <see cref="TaikoDifficultyHitObject"/>s
|
||||||
|
/// of the same <see cref="Objects.HitType"/> are encoded within the same <see cref="MonoStreak"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class MonoStreak
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// List of <see cref="DifficultyHitObject"/>s that are encoded within this <see cref="MonoStreak"/>.
|
||||||
|
/// </summary>
|
||||||
|
public List<TaikoDifficultyHitObject> HitObjects { get; private set; } = new List<TaikoDifficultyHitObject>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The parent <see cref="AlternatingMonoPattern"/> that contains this <see cref="MonoStreak"/>
|
||||||
|
/// </summary>
|
||||||
|
public AlternatingMonoPattern Parent = null!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Index of this <see cref="MonoStreak"/> within it's parent <see cref="AlternatingMonoPattern"/>
|
||||||
|
/// </summary>
|
||||||
|
public int Index;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The first <see cref="TaikoDifficultyHitObject"/> in this <see cref="MonoStreak"/>.
|
||||||
|
/// </summary>
|
||||||
|
public TaikoDifficultyHitObject FirstHitObject => HitObjects[0];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The hit type of all objects encoded within this <see cref="MonoStreak"/>
|
||||||
|
/// </summary>
|
||||||
|
public HitType? HitType => (HitObjects[0].BaseObject as Hit)?.Type;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How long the mono pattern encoded within is
|
||||||
|
/// </summary>
|
||||||
|
public int RunLength => HitObjects.Count;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,94 @@
|
|||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Encodes a list of <see cref="AlternatingMonoPattern"/>s, grouped together by back and forth repetition of the same
|
||||||
|
/// <see cref="AlternatingMonoPattern"/>. Also stores the repetition interval between this and the previous <see cref="RepeatingHitPatterns"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class RepeatingHitPatterns
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum amount of <see cref="RepeatingHitPatterns"/>s to look back to find a repetition.
|
||||||
|
/// </summary>
|
||||||
|
private const int max_repetition_interval = 16;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="AlternatingMonoPattern"/>s that are grouped together within this <see cref="RepeatingHitPatterns"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly List<AlternatingMonoPattern> AlternatingMonoPatterns = new List<AlternatingMonoPattern>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The parent <see cref="TaikoDifficultyHitObject"/> in this <see cref="RepeatingHitPatterns"/>
|
||||||
|
/// </summary>
|
||||||
|
public TaikoDifficultyHitObject FirstHitObject => AlternatingMonoPatterns[0].FirstHitObject;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The previous <see cref="RepeatingHitPatterns"/>. This is used to determine the repetition interval.
|
||||||
|
/// </summary>
|
||||||
|
public readonly RepeatingHitPatterns? Previous;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How many <see cref="RepeatingHitPatterns"/> between the current and previous identical <see cref="RepeatingHitPatterns"/>.
|
||||||
|
/// If no repetition is found this will have a value of <see cref="max_repetition_interval"/> + 1.
|
||||||
|
/// </summary>
|
||||||
|
public int RepetitionInterval { get; private set; } = max_repetition_interval + 1;
|
||||||
|
|
||||||
|
public RepeatingHitPatterns(RepeatingHitPatterns? previous)
|
||||||
|
{
|
||||||
|
Previous = previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if other is considered a repetition of this pattern. This is true if other's first two payloads
|
||||||
|
/// have identical mono lengths.
|
||||||
|
/// </summary>
|
||||||
|
private bool isRepetitionOf(RepeatingHitPatterns other)
|
||||||
|
{
|
||||||
|
if (AlternatingMonoPatterns.Count != other.AlternatingMonoPatterns.Count) return false;
|
||||||
|
|
||||||
|
for (int i = 0; i < Math.Min(AlternatingMonoPatterns.Count, 2); i++)
|
||||||
|
{
|
||||||
|
if (!AlternatingMonoPatterns[i].HasIdenticalMonoLength(other.AlternatingMonoPatterns[i])) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finds the closest previous <see cref="RepeatingHitPatterns"/> that has the identical <see cref="AlternatingMonoPatterns"/>.
|
||||||
|
/// Interval is defined as the amount of <see cref="RepeatingHitPatterns"/> chunks between the current and repeated patterns.
|
||||||
|
/// </summary>
|
||||||
|
public void FindRepetitionInterval()
|
||||||
|
{
|
||||||
|
if (Previous == null)
|
||||||
|
{
|
||||||
|
RepetitionInterval = max_repetition_interval + 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RepeatingHitPatterns? other = Previous;
|
||||||
|
int interval = 1;
|
||||||
|
|
||||||
|
while (interval < max_repetition_interval)
|
||||||
|
{
|
||||||
|
if (isRepetitionOf(other))
|
||||||
|
{
|
||||||
|
RepetitionInterval = Math.Min(interval, max_repetition_interval);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
other = other.Previous;
|
||||||
|
if (other == null) break;
|
||||||
|
|
||||||
|
++interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
RepetitionInterval = max_repetition_interval + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,167 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
|
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Utility class to perform various encodings.
|
||||||
|
/// </summary>
|
||||||
|
public static class TaikoColourDifficultyPreprocessor
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Processes and encodes a list of <see cref="TaikoDifficultyHitObject"/>s into a list of <see cref="TaikoDifficultyHitObjectColour"/>s,
|
||||||
|
/// assigning the appropriate <see cref="TaikoDifficultyHitObjectColour"/>s to each <see cref="TaikoDifficultyHitObject"/>,
|
||||||
|
/// and pre-evaluating colour difficulty of each <see cref="TaikoDifficultyHitObject"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static void ProcessAndAssign(List<DifficultyHitObject> hitObjects)
|
||||||
|
{
|
||||||
|
List<RepeatingHitPatterns> hitPatterns = encode(hitObjects);
|
||||||
|
|
||||||
|
// Assign indexing and encoding data to all relevant objects. Only the first note of each encoding type is
|
||||||
|
// assigned with the relevant encodings.
|
||||||
|
foreach (var repeatingHitPattern in hitPatterns)
|
||||||
|
{
|
||||||
|
repeatingHitPattern.FirstHitObject.Colour.RepeatingHitPattern = repeatingHitPattern;
|
||||||
|
|
||||||
|
// The outermost loop is kept a ForEach loop since it doesn't need index information, and we want to
|
||||||
|
// keep i and j for AlternatingMonoPattern's and MonoStreak's index respectively, to keep it in line with
|
||||||
|
// documentation.
|
||||||
|
for (int i = 0; i < repeatingHitPattern.AlternatingMonoPatterns.Count; ++i)
|
||||||
|
{
|
||||||
|
AlternatingMonoPattern monoPattern = repeatingHitPattern.AlternatingMonoPatterns[i];
|
||||||
|
monoPattern.Parent = repeatingHitPattern;
|
||||||
|
monoPattern.Index = i;
|
||||||
|
monoPattern.FirstHitObject.Colour.AlternatingMonoPattern = monoPattern;
|
||||||
|
|
||||||
|
for (int j = 0; j < monoPattern.MonoStreaks.Count; ++j)
|
||||||
|
{
|
||||||
|
MonoStreak monoStreak = monoPattern.MonoStreaks[j];
|
||||||
|
monoStreak.Parent = monoPattern;
|
||||||
|
monoStreak.Index = j;
|
||||||
|
monoStreak.FirstHitObject.Colour.MonoStreak = monoStreak;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encodes a list of <see cref="TaikoDifficultyHitObject"/>s into a list of <see cref="RepeatingHitPatterns"/>s.
|
||||||
|
/// </summary>
|
||||||
|
private static List<RepeatingHitPatterns> encode(List<DifficultyHitObject> data)
|
||||||
|
{
|
||||||
|
List<MonoStreak> monoStreaks = encodeMonoStreak(data);
|
||||||
|
List<AlternatingMonoPattern> alternatingMonoPatterns = encodeAlternatingMonoPattern(monoStreaks);
|
||||||
|
List<RepeatingHitPatterns> repeatingHitPatterns = encodeRepeatingHitPattern(alternatingMonoPatterns);
|
||||||
|
|
||||||
|
return repeatingHitPatterns;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encodes a list of <see cref="TaikoDifficultyHitObject"/>s into a list of <see cref="MonoStreak"/>s.
|
||||||
|
/// </summary>
|
||||||
|
private static List<MonoStreak> encodeMonoStreak(List<DifficultyHitObject> data)
|
||||||
|
{
|
||||||
|
List<MonoStreak> monoStreaks = new List<MonoStreak>();
|
||||||
|
MonoStreak? currentMonoStreak = null;
|
||||||
|
|
||||||
|
for (int i = 0; i < data.Count; i++)
|
||||||
|
{
|
||||||
|
TaikoDifficultyHitObject taikoObject = (TaikoDifficultyHitObject)data[i];
|
||||||
|
|
||||||
|
// This ignores all non-note objects, which may or may not be the desired behaviour
|
||||||
|
TaikoDifficultyHitObject? previousObject = taikoObject.PreviousNote(0);
|
||||||
|
|
||||||
|
// If this is the first object in the list or the colour changed, create a new mono streak
|
||||||
|
if (currentMonoStreak == null || previousObject == null || (taikoObject.BaseObject as Hit)?.Type != (previousObject.BaseObject as Hit)?.Type)
|
||||||
|
{
|
||||||
|
currentMonoStreak = new MonoStreak();
|
||||||
|
monoStreaks.Add(currentMonoStreak);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the current object to the encoded payload.
|
||||||
|
currentMonoStreak.HitObjects.Add(taikoObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
return monoStreaks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encodes a list of <see cref="MonoStreak"/>s into a list of <see cref="AlternatingMonoPattern"/>s.
|
||||||
|
/// </summary>
|
||||||
|
private static List<AlternatingMonoPattern> encodeAlternatingMonoPattern(List<MonoStreak> data)
|
||||||
|
{
|
||||||
|
List<AlternatingMonoPattern> monoPatterns = new List<AlternatingMonoPattern>();
|
||||||
|
AlternatingMonoPattern? currentMonoPattern = null;
|
||||||
|
|
||||||
|
for (int i = 0; i < data.Count; i++)
|
||||||
|
{
|
||||||
|
// Start a new AlternatingMonoPattern if the previous MonoStreak has a different mono length, or if this is the first MonoStreak in the list.
|
||||||
|
if (currentMonoPattern == null || data[i].RunLength != data[i - 1].RunLength)
|
||||||
|
{
|
||||||
|
currentMonoPattern = new AlternatingMonoPattern();
|
||||||
|
monoPatterns.Add(currentMonoPattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the current MonoStreak to the encoded payload.
|
||||||
|
currentMonoPattern.MonoStreaks.Add(data[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return monoPatterns;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Encodes a list of <see cref="AlternatingMonoPattern"/>s into a list of <see cref="RepeatingHitPatterns"/>s.
|
||||||
|
/// </summary>
|
||||||
|
private static List<RepeatingHitPatterns> encodeRepeatingHitPattern(List<AlternatingMonoPattern> data)
|
||||||
|
{
|
||||||
|
List<RepeatingHitPatterns> hitPatterns = new List<RepeatingHitPatterns>();
|
||||||
|
RepeatingHitPatterns? currentHitPattern = null;
|
||||||
|
|
||||||
|
for (int i = 0; i < data.Count; i++)
|
||||||
|
{
|
||||||
|
// Start a new RepeatingHitPattern. AlternatingMonoPatterns that should be grouped together will be handled later within this loop.
|
||||||
|
currentHitPattern = new RepeatingHitPatterns(currentHitPattern);
|
||||||
|
|
||||||
|
// Determine if future AlternatingMonoPatterns should be grouped.
|
||||||
|
bool isCoupled = i < data.Count - 2 && data[i].IsRepetitionOf(data[i + 2]);
|
||||||
|
|
||||||
|
if (!isCoupled)
|
||||||
|
{
|
||||||
|
// If not, add the current AlternatingMonoPattern to the encoded payload and continue.
|
||||||
|
currentHitPattern.AlternatingMonoPatterns.Add(data[i]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If so, add the current AlternatingMonoPattern to the encoded payload and start repeatedly checking if the
|
||||||
|
// subsequent AlternatingMonoPatterns should be grouped by increasing i and doing the appropriate isCoupled check.
|
||||||
|
while (isCoupled)
|
||||||
|
{
|
||||||
|
currentHitPattern.AlternatingMonoPatterns.Add(data[i]);
|
||||||
|
i++;
|
||||||
|
isCoupled = i < data.Count - 2 && data[i].IsRepetitionOf(data[i + 2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip over viewed data and add the rest to the payload
|
||||||
|
currentHitPattern.AlternatingMonoPatterns.Add(data[i]);
|
||||||
|
currentHitPattern.AlternatingMonoPatterns.Add(data[i + 1]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
hitPatterns.Add(currentHitPattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Final pass to find repetition intervals
|
||||||
|
for (int i = 0; i < hitPatterns.Count; i++)
|
||||||
|
{
|
||||||
|
hitPatterns[i].FindRepetitionInterval();
|
||||||
|
}
|
||||||
|
|
||||||
|
return hitPatterns;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour.Data;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Stores colour compression information for a <see cref="TaikoDifficultyHitObject"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class TaikoDifficultyHitObjectColour
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="MonoStreak"/> that encodes this note, only present if this is the first note within a <see cref="MonoStreak"/>
|
||||||
|
/// </summary>
|
||||||
|
public MonoStreak? MonoStreak;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="AlternatingMonoPattern"/> that encodes this note, only present if this is the first note within a <see cref="AlternatingMonoPattern"/>
|
||||||
|
/// </summary>
|
||||||
|
public AlternatingMonoPattern? AlternatingMonoPattern;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="RepeatingHitPattern"/> that encodes this note, only present if this is the first note within a <see cref="RepeatingHitPattern"/>
|
||||||
|
/// </summary>
|
||||||
|
public RepeatingHitPatterns? RepeatingHitPattern;
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Rhythm
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Represents a rhythm change in a taiko map.
|
/// Represents a rhythm change in a taiko map.
|
@ -1,14 +1,14 @@
|
|||||||
// 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.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour;
|
||||||
|
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Rhythm;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
|
namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
|
||||||
{
|
{
|
||||||
@ -17,21 +17,36 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class TaikoDifficultyHitObject : DifficultyHitObject
|
public class TaikoDifficultyHitObject : DifficultyHitObject
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The list of all <see cref="TaikoDifficultyHitObject"/> of the same colour as this <see cref="TaikoDifficultyHitObject"/> in the beatmap.
|
||||||
|
/// </summary>
|
||||||
|
private readonly IReadOnlyList<TaikoDifficultyHitObject>? monoDifficultyHitObjects;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The index of this <see cref="TaikoDifficultyHitObject"/> in <see cref="monoDifficultyHitObjects"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly int MonoIndex;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The list of all <see cref="TaikoDifficultyHitObject"/> that is either a regular note or finisher in the beatmap
|
||||||
|
/// </summary>
|
||||||
|
private readonly IReadOnlyList<TaikoDifficultyHitObject> noteDifficultyHitObjects;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The index of this <see cref="TaikoDifficultyHitObject"/> in <see cref="noteDifficultyHitObjects"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly int NoteIndex;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The rhythm required to hit this hit object.
|
/// The rhythm required to hit this hit object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly TaikoDifficultyHitObjectRhythm Rhythm;
|
public readonly TaikoDifficultyHitObjectRhythm Rhythm;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The hit type of this hit object.
|
/// Colour data for this hit object. This is used by colour evaluator to calculate colour difficulty, but can be used
|
||||||
|
/// by other skills in the future.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly HitType? HitType;
|
public readonly TaikoDifficultyHitObjectColour Colour;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether the object should carry a penalty due to being hittable using special techniques
|
|
||||||
/// making it easier to do so.
|
|
||||||
/// </summary>
|
|
||||||
public bool StaminaCheese;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new difficulty hit object.
|
/// Creates a new difficulty hit object.
|
||||||
@ -40,15 +55,44 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
|
|||||||
/// <param name="lastObject">The gameplay <see cref="HitObject"/> preceding <paramref name="hitObject"/>.</param>
|
/// <param name="lastObject">The gameplay <see cref="HitObject"/> preceding <paramref name="hitObject"/>.</param>
|
||||||
/// <param name="lastLastObject">The gameplay <see cref="HitObject"/> preceding <paramref name="lastObject"/>.</param>
|
/// <param name="lastLastObject">The gameplay <see cref="HitObject"/> preceding <paramref name="lastObject"/>.</param>
|
||||||
/// <param name="clockRate">The rate of the gameplay clock. Modified by speed-changing mods.</param>
|
/// <param name="clockRate">The rate of the gameplay clock. Modified by speed-changing mods.</param>
|
||||||
/// <param name="objects">The list of <see cref="DifficultyHitObject"/>s in the current beatmap.</param>
|
/// <param name="objects">The list of all <see cref="DifficultyHitObject"/>s in the current beatmap.</param>
|
||||||
/// /// <param name="index">The position of this <see cref="DifficultyHitObject"/> in the <paramref name="objects"/> list.</param>
|
/// <param name="centreHitObjects">The list of centre (don) <see cref="DifficultyHitObject"/>s in the current beatmap.</param>
|
||||||
public TaikoDifficultyHitObject(HitObject hitObject, HitObject lastObject, HitObject lastLastObject, double clockRate, List<DifficultyHitObject> objects, int index)
|
/// <param name="rimHitObjects">The list of rim (kat) <see cref="DifficultyHitObject"/>s in the current beatmap.</param>
|
||||||
|
/// <param name="noteObjects">The list of <see cref="DifficultyHitObject"/>s that is a hit (i.e. not a drumroll or swell) in the current beatmap.</param>
|
||||||
|
/// <param name="index">The position of this <see cref="DifficultyHitObject"/> in the <paramref name="objects"/> list.</param>
|
||||||
|
public TaikoDifficultyHitObject(HitObject hitObject, HitObject lastObject, HitObject lastLastObject, double clockRate,
|
||||||
|
List<DifficultyHitObject> objects,
|
||||||
|
List<TaikoDifficultyHitObject> centreHitObjects,
|
||||||
|
List<TaikoDifficultyHitObject> rimHitObjects,
|
||||||
|
List<TaikoDifficultyHitObject> noteObjects, int index)
|
||||||
: base(hitObject, lastObject, clockRate, objects, index)
|
: base(hitObject, lastObject, clockRate, objects, index)
|
||||||
{
|
{
|
||||||
var currentHit = hitObject as Hit;
|
noteDifficultyHitObjects = noteObjects;
|
||||||
|
|
||||||
|
// Create the Colour object, its properties should be filled in by TaikoDifficultyPreprocessor
|
||||||
|
Colour = new TaikoDifficultyHitObjectColour();
|
||||||
Rhythm = getClosestRhythm(lastObject, lastLastObject, clockRate);
|
Rhythm = getClosestRhythm(lastObject, lastLastObject, clockRate);
|
||||||
HitType = currentHit?.Type;
|
|
||||||
|
switch ((hitObject as Hit)?.Type)
|
||||||
|
{
|
||||||
|
case HitType.Centre:
|
||||||
|
MonoIndex = centreHitObjects.Count;
|
||||||
|
centreHitObjects.Add(this);
|
||||||
|
monoDifficultyHitObjects = centreHitObjects;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HitType.Rim:
|
||||||
|
MonoIndex = rimHitObjects.Count;
|
||||||
|
rimHitObjects.Add(this);
|
||||||
|
monoDifficultyHitObjects = rimHitObjects;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hitObject is Hit)
|
||||||
|
{
|
||||||
|
NoteIndex = noteObjects.Count;
|
||||||
|
noteObjects.Add(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -87,5 +131,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
|
|||||||
|
|
||||||
return common_rhythms.OrderBy(x => Math.Abs(x.Ratio - ratio)).First();
|
return common_rhythms.OrderBy(x => Math.Abs(x.Ratio - ratio)).First();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TaikoDifficultyHitObject? PreviousMono(int backwardsIndex) => monoDifficultyHitObjects?.ElementAtOrDefault(MonoIndex - (backwardsIndex + 1));
|
||||||
|
|
||||||
|
public TaikoDifficultyHitObject? NextMono(int forwardsIndex) => monoDifficultyHitObjects?.ElementAtOrDefault(MonoIndex + (forwardsIndex + 1));
|
||||||
|
|
||||||
|
public TaikoDifficultyHitObject? PreviousNote(int backwardsIndex) => noteDifficultyHitObjects.ElementAtOrDefault(NoteIndex - (backwardsIndex + 1));
|
||||||
|
|
||||||
|
public TaikoDifficultyHitObject? NextNote(int forwardsIndex) => noteDifficultyHitObjects.ElementAtOrDefault(NoteIndex + (forwardsIndex + 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,10 @@
|
|||||||
// 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 osu.Game.Rulesets.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Difficulty.Skills;
|
using osu.Game.Rulesets.Difficulty.Skills;
|
||||||
using osu.Game.Rulesets.Difficulty.Utils;
|
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Taiko.Difficulty.Evaluators;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
||||||
{
|
{
|
||||||
@ -18,29 +13,12 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class Colour : StrainDecaySkill
|
public class Colour : StrainDecaySkill
|
||||||
{
|
{
|
||||||
protected override double SkillMultiplier => 1;
|
protected override double SkillMultiplier => 0.12;
|
||||||
protected override double StrainDecayBase => 0.4;
|
|
||||||
|
|
||||||
/// <summary>
|
// This is set to decay slower than other skills, due to the fact that only the first note of each encoding class
|
||||||
/// Maximum number of entries to keep in <see cref="monoHistory"/>.
|
// having any difficulty values, and we want to allow colour difficulty to be able to build up even on
|
||||||
/// </summary>
|
// slower maps.
|
||||||
private const int mono_history_max_length = 5;
|
protected override double StrainDecayBase => 0.8;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Queue with the lengths of the last <see cref="mono_history_max_length"/> most recent mono (single-colour) patterns,
|
|
||||||
/// with the most recent value at the end of the queue.
|
|
||||||
/// </summary>
|
|
||||||
private readonly LimitedCapacityQueue<int> monoHistory = new LimitedCapacityQueue<int>(mono_history_max_length);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="HitType"/> of the last object hit before the one being considered.
|
|
||||||
/// </summary>
|
|
||||||
private HitType? previousHitType;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Length of the current mono pattern.
|
|
||||||
/// </summary>
|
|
||||||
private int currentMonoLength;
|
|
||||||
|
|
||||||
public Colour(Mod[] mods)
|
public Colour(Mod[] mods)
|
||||||
: base(mods)
|
: base(mods)
|
||||||
@ -49,95 +27,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
|||||||
|
|
||||||
protected override double StrainValueOf(DifficultyHitObject current)
|
protected override double StrainValueOf(DifficultyHitObject current)
|
||||||
{
|
{
|
||||||
// changing from/to a drum roll or a swell does not constitute a colour change.
|
return ColourEvaluator.EvaluateDifficultyOf(current);
|
||||||
// hits spaced more than a second apart are also exempt from colour strain.
|
|
||||||
if (!(current.LastObject is Hit && current.BaseObject is Hit && current.DeltaTime < 1000))
|
|
||||||
{
|
|
||||||
monoHistory.Clear();
|
|
||||||
|
|
||||||
var currentHit = current.BaseObject as Hit;
|
|
||||||
currentMonoLength = currentHit != null ? 1 : 0;
|
|
||||||
previousHitType = currentHit?.Type;
|
|
||||||
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
var taikoCurrent = (TaikoDifficultyHitObject)current;
|
|
||||||
|
|
||||||
double objectStrain = 0.0;
|
|
||||||
|
|
||||||
if (previousHitType != null && taikoCurrent.HitType != previousHitType)
|
|
||||||
{
|
|
||||||
// The colour has changed.
|
|
||||||
objectStrain = 1.0;
|
|
||||||
|
|
||||||
if (monoHistory.Count < 2)
|
|
||||||
{
|
|
||||||
// There needs to be at least two streaks to determine a strain.
|
|
||||||
objectStrain = 0.0;
|
|
||||||
}
|
|
||||||
else if ((monoHistory[^1] + currentMonoLength) % 2 == 0)
|
|
||||||
{
|
|
||||||
// The last streak in the history is guaranteed to be a different type to the current streak.
|
|
||||||
// If the total number of notes in the two streaks is even, nullify this object's strain.
|
|
||||||
objectStrain = 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
objectStrain *= repetitionPenalties();
|
|
||||||
currentMonoLength = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
currentMonoLength += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
previousHitType = taikoCurrent.HitType;
|
|
||||||
return objectStrain;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The penalty to apply due to the length of repetition in colour streaks.
|
|
||||||
/// </summary>
|
|
||||||
private double repetitionPenalties()
|
|
||||||
{
|
|
||||||
const int most_recent_patterns_to_compare = 2;
|
|
||||||
double penalty = 1.0;
|
|
||||||
|
|
||||||
monoHistory.Enqueue(currentMonoLength);
|
|
||||||
|
|
||||||
for (int start = monoHistory.Count - most_recent_patterns_to_compare - 1; start >= 0; start--)
|
|
||||||
{
|
|
||||||
if (!isSamePattern(start, most_recent_patterns_to_compare))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int notesSince = 0;
|
|
||||||
for (int i = start; i < monoHistory.Count; i++) notesSince += monoHistory[i];
|
|
||||||
penalty *= repetitionPenalty(notesSince);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return penalty;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the last <paramref name="mostRecentPatternsToCompare"/> patterns have repeated in the history
|
|
||||||
/// of single-colour note sequences, starting from <paramref name="start"/>.
|
|
||||||
/// </summary>
|
|
||||||
private bool isSamePattern(int start, int mostRecentPatternsToCompare)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < mostRecentPatternsToCompare; i++)
|
|
||||||
{
|
|
||||||
if (monoHistory[start + i] != monoHistory[monoHistory.Count - mostRecentPatternsToCompare + i])
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Calculates the strain penalty for a colour pattern repetition.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="notesSince">The number of notes since the last repetition of the pattern.</param>
|
|
||||||
private double repetitionPenalty(int notesSince) => Math.Min(1.0, 0.032 * notesSince);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
93
osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs
Normal file
93
osu.Game.Rulesets.Taiko/Difficulty/Skills/Peaks.cs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
// 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.OrderByDescending(d => d))
|
||||||
|
{
|
||||||
|
difficulty += strain * weight;
|
||||||
|
weight *= 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
|
return difficulty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,44 +0,0 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
|
||||||
using osu.Game.Rulesets.Difficulty.Skills;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Stamina of a single key, calculated based on repetition speed.
|
|
||||||
/// </summary>
|
|
||||||
public class SingleKeyStamina
|
|
||||||
{
|
|
||||||
private double? previousHitTime;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Similar to <see cref="StrainDecaySkill.StrainValueOf"/>
|
|
||||||
/// </summary>
|
|
||||||
public double StrainValueOf(DifficultyHitObject current)
|
|
||||||
{
|
|
||||||
if (previousHitTime == null)
|
|
||||||
{
|
|
||||||
previousHitTime = current.StartTime;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
double objectStrain = 0.5;
|
|
||||||
objectStrain += speedBonus(current.StartTime - previousHitTime.Value);
|
|
||||||
previousHitTime = current.StartTime;
|
|
||||||
return objectStrain;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Applies a speed bonus dependent on the time since the last hit performed using this key.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="notePairDuration">The duration between the current and previous note hit using the same key.</param>
|
|
||||||
private double speedBonus(double notePairDuration)
|
|
||||||
{
|
|
||||||
return 175 / (notePairDuration + 100);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,8 +6,7 @@
|
|||||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Difficulty.Skills;
|
using osu.Game.Rulesets.Difficulty.Skills;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Taiko.Difficulty.Evaluators;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
||||||
{
|
{
|
||||||
@ -19,31 +18,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
public class Stamina : StrainDecaySkill
|
public class Stamina : StrainDecaySkill
|
||||||
{
|
{
|
||||||
protected override double SkillMultiplier => 1;
|
protected override double SkillMultiplier => 1.1;
|
||||||
protected override double StrainDecayBase => 0.4;
|
protected override double StrainDecayBase => 0.4;
|
||||||
|
|
||||||
private readonly SingleKeyStamina[] centreKeyStamina =
|
|
||||||
{
|
|
||||||
new SingleKeyStamina(),
|
|
||||||
new SingleKeyStamina()
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly SingleKeyStamina[] rimKeyStamina =
|
|
||||||
{
|
|
||||||
new SingleKeyStamina(),
|
|
||||||
new SingleKeyStamina()
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Current index into <see cref="centreKeyStamina" /> for a centre hit.
|
|
||||||
/// </summary>
|
|
||||||
private int centreKeyIndex;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Current index into <see cref="rimKeyStamina" /> for a rim hit.
|
|
||||||
/// </summary>
|
|
||||||
private int rimKeyIndex;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a <see cref="Stamina"/> skill.
|
/// Creates a <see cref="Stamina"/> skill.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -53,32 +30,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the next <see cref="SingleKeyStamina"/> to use for the given <see cref="TaikoDifficultyHitObject"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="current">The current <see cref="TaikoDifficultyHitObject"/>.</param>
|
|
||||||
private SingleKeyStamina getNextSingleKeyStamina(TaikoDifficultyHitObject current)
|
|
||||||
{
|
|
||||||
// Alternate key for the same color.
|
|
||||||
if (current.HitType == HitType.Centre)
|
|
||||||
{
|
|
||||||
centreKeyIndex = (centreKeyIndex + 1) % 2;
|
|
||||||
return centreKeyStamina[centreKeyIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
rimKeyIndex = (rimKeyIndex + 1) % 2;
|
|
||||||
return rimKeyStamina[rimKeyIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override double StrainValueOf(DifficultyHitObject current)
|
protected override double StrainValueOf(DifficultyHitObject current)
|
||||||
{
|
{
|
||||||
if (!(current.BaseObject is Hit))
|
return StaminaEvaluator.EvaluateDifficultyOf(current);
|
||||||
{
|
|
||||||
return 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
TaikoDifficultyHitObject hitObject = (TaikoDifficultyHitObject)current;
|
|
||||||
return getNextSingleKeyStamina(hitObject).StrainValueOf(hitObject);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,13 +29,10 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
public double ColourDifficulty { get; set; }
|
public double ColourDifficulty { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The perceived approach rate inclusive of rate-adjusting mods (DT/HT/etc).
|
/// The difficulty corresponding to the hardest parts of the map.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
[JsonProperty("peak_difficulty")]
|
||||||
/// Rate-adjusting mods don't directly affect the approach rate difficulty value, but have a perceived effect as a result of adjusting audio timing.
|
public double PeakDifficulty { get; set; }
|
||||||
/// </remarks>
|
|
||||||
[JsonProperty("approach_rate")]
|
|
||||||
public double ApproachRate { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The perceived hit window for a GREAT hit inclusive of rate-adjusting mods (DT/HT/etc).
|
/// The perceived hit window for a GREAT hit inclusive of rate-adjusting mods (DT/HT/etc).
|
||||||
|
@ -13,6 +13,7 @@ using osu.Game.Rulesets.Difficulty.Skills;
|
|||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
|
||||||
|
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing.Colour;
|
||||||
using osu.Game.Rulesets.Taiko.Difficulty.Skills;
|
using osu.Game.Rulesets.Taiko.Difficulty.Skills;
|
||||||
using osu.Game.Rulesets.Taiko.Mods;
|
using osu.Game.Rulesets.Taiko.Mods;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
@ -22,9 +23,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
{
|
{
|
||||||
public class TaikoDifficultyCalculator : DifficultyCalculator
|
public class TaikoDifficultyCalculator : DifficultyCalculator
|
||||||
{
|
{
|
||||||
private const double rhythm_skill_multiplier = 0.014;
|
private const double difficulty_multiplier = 1.35;
|
||||||
private const double colour_skill_multiplier = 0.01;
|
|
||||||
private const double stamina_skill_multiplier = 0.021;
|
|
||||||
|
|
||||||
public override int Version => 20220701;
|
public override int Version => 20220701;
|
||||||
|
|
||||||
@ -33,12 +32,13 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[]
|
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate)
|
||||||
{
|
{
|
||||||
new Colour(mods),
|
return new Skill[]
|
||||||
new Rhythm(mods),
|
{
|
||||||
new Stamina(mods)
|
new Peaks(mods)
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
|
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
|
||||||
{
|
{
|
||||||
@ -50,18 +50,23 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
|
|
||||||
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate)
|
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double clockRate)
|
||||||
{
|
{
|
||||||
List<DifficultyHitObject> taikoDifficultyHitObjects = new List<DifficultyHitObject>();
|
List<DifficultyHitObject> difficultyHitObjects = new List<DifficultyHitObject>();
|
||||||
|
List<TaikoDifficultyHitObject> centreObjects = new List<TaikoDifficultyHitObject>();
|
||||||
|
List<TaikoDifficultyHitObject> rimObjects = new List<TaikoDifficultyHitObject>();
|
||||||
|
List<TaikoDifficultyHitObject> noteObjects = new List<TaikoDifficultyHitObject>();
|
||||||
|
|
||||||
for (int i = 2; i < beatmap.HitObjects.Count; i++)
|
for (int i = 2; i < beatmap.HitObjects.Count; i++)
|
||||||
{
|
{
|
||||||
taikoDifficultyHitObjects.Add(
|
difficultyHitObjects.Add(
|
||||||
new TaikoDifficultyHitObject(
|
new TaikoDifficultyHitObject(
|
||||||
beatmap.HitObjects[i], beatmap.HitObjects[i - 1], beatmap.HitObjects[i - 2], clockRate, taikoDifficultyHitObjects, taikoDifficultyHitObjects.Count
|
beatmap.HitObjects[i], beatmap.HitObjects[i - 1], beatmap.HitObjects[i - 2], clockRate, difficultyHitObjects,
|
||||||
)
|
centreObjects, rimObjects, noteObjects, difficultyHitObjects.Count)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return taikoDifficultyHitObjects;
|
TaikoColourDifficultyPreprocessor.ProcessAndAssign(difficultyHitObjects);
|
||||||
|
|
||||||
|
return difficultyHitObjects;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
|
protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beatmap, Mod[] mods, Skill[] skills, double clockRate)
|
||||||
@ -69,28 +74,24 @@ 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 colour = (Colour)skills[0];
|
var combined = (Peaks)skills[0];
|
||||||
var rhythm = (Rhythm)skills[1];
|
|
||||||
var stamina = (Stamina)skills[2];
|
|
||||||
|
|
||||||
double colourRating = colour.DifficultyValue() * colour_skill_multiplier;
|
double colourRating = combined.ColourDifficultyValue * difficulty_multiplier;
|
||||||
double rhythmRating = rhythm.DifficultyValue() * rhythm_skill_multiplier;
|
double rhythmRating = combined.RhythmDifficultyValue * difficulty_multiplier;
|
||||||
double staminaRating = stamina.DifficultyValue() * stamina_skill_multiplier;
|
double staminaRating = combined.StaminaDifficultyValue * difficulty_multiplier;
|
||||||
|
|
||||||
double staminaPenalty = simpleColourPenalty(staminaRating, colourRating);
|
double combinedRating = combined.DifficultyValue() * difficulty_multiplier;
|
||||||
staminaRating *= staminaPenalty;
|
double starRating = rescale(combinedRating * 1.4);
|
||||||
|
|
||||||
//TODO : This is a temporary fix for the stamina rating of converts, due to their low colour variance.
|
// TODO: This is temporary measure as we don't detect abuse of multiple-input playstyles of converts within the current system.
|
||||||
if (beatmap.BeatmapInfo.Ruleset.OnlineID == 0 && colourRating < 0.05)
|
if (beatmap.BeatmapInfo.Ruleset.OnlineID == 0)
|
||||||
{
|
{
|
||||||
staminaPenalty *= 0.25;
|
starRating *= 0.925;
|
||||||
|
// For maps with low colour variance and high stamina requirement, multiple inputs are more likely to be abused.
|
||||||
|
if (colourRating < 2 && staminaRating > 8)
|
||||||
|
starRating *= 0.80;
|
||||||
}
|
}
|
||||||
|
|
||||||
double combinedRating = locallyCombinedDifficulty(colour, rhythm, stamina, staminaPenalty);
|
|
||||||
double separatedRating = norm(1.5, colourRating, rhythmRating, staminaRating);
|
|
||||||
double starRating = 1.4 * separatedRating + 0.5 * combinedRating;
|
|
||||||
starRating = rescale(starRating);
|
|
||||||
|
|
||||||
HitWindows hitWindows = new TaikoHitWindows();
|
HitWindows hitWindows = new TaikoHitWindows();
|
||||||
hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty);
|
hitWindows.SetDifficulty(beatmap.Difficulty.OverallDifficulty);
|
||||||
|
|
||||||
@ -101,75 +102,14 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
StaminaDifficulty = staminaRating,
|
StaminaDifficulty = staminaRating,
|
||||||
RhythmDifficulty = rhythmRating,
|
RhythmDifficulty = rhythmRating,
|
||||||
ColourDifficulty = colourRating,
|
ColourDifficulty = colourRating,
|
||||||
|
PeakDifficulty = combinedRating,
|
||||||
GreatHitWindow = hitWindows.WindowFor(HitResult.Great) / clockRate,
|
GreatHitWindow = hitWindows.WindowFor(HitResult.Great) / clockRate,
|
||||||
MaxCombo = beatmap.HitObjects.Count(h => h is Hit),
|
MaxCombo = beatmap.HitObjects.Count(h => h is Hit),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Calculates the penalty for the stamina skill for maps with low colour difficulty.
|
/// Applies a final re-scaling of the star rating.
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Some maps (especially converts) can be easy to read despite a high note density.
|
|
||||||
/// This penalty aims to reduce the star rating of such maps by factoring in colour difficulty to the stamina skill.
|
|
||||||
/// </remarks>
|
|
||||||
private double simpleColourPenalty(double staminaDifficulty, double colorDifficulty)
|
|
||||||
{
|
|
||||||
if (colorDifficulty <= 0) return 0.79 - 0.25;
|
|
||||||
|
|
||||||
return 0.79 - Math.Atan(staminaDifficulty / colorDifficulty - 12) / Math.PI / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns the partial 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 locallyCombinedDifficulty(Colour colour, Rhythm rhythm, Stamina stamina, double staminaPenalty)
|
|
||||||
{
|
|
||||||
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 * staminaPenalty;
|
|
||||||
|
|
||||||
double peak = norm(2, colourPeak, rhythmPeak, staminaPeak);
|
|
||||||
|
|
||||||
// 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.OrderByDescending(d => d))
|
|
||||||
{
|
|
||||||
difficulty += strain * weight;
|
|
||||||
weight *= 0.9;
|
|
||||||
}
|
|
||||||
|
|
||||||
return difficulty;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Applies a final re-scaling of the star rating to bring maps with recorded full combos below 9.5 stars.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sr">The raw star rating value before re-scaling.</param>
|
/// <param name="sr">The raw star rating value before re-scaling.</param>
|
||||||
private double rescale(double sr)
|
private double rescale(double sr)
|
||||||
|
@ -17,6 +17,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
[JsonProperty("accuracy")]
|
[JsonProperty("accuracy")]
|
||||||
public double Accuracy { get; set; }
|
public double Accuracy { get; set; }
|
||||||
|
|
||||||
|
[JsonProperty("effective_miss_count")]
|
||||||
|
public double EffectiveMissCount { get; set; }
|
||||||
|
|
||||||
public override IEnumerable<PerformanceDisplayAttribute> GetAttributesForDisplay()
|
public override IEnumerable<PerformanceDisplayAttribute> GetAttributesForDisplay()
|
||||||
{
|
{
|
||||||
foreach (var attribute in base.GetAttributesForDisplay())
|
foreach (var attribute in base.GetAttributesForDisplay())
|
||||||
|
@ -21,6 +21,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
private int countMeh;
|
private int countMeh;
|
||||||
private int countMiss;
|
private int countMiss;
|
||||||
|
|
||||||
|
private double effectiveMissCount;
|
||||||
|
|
||||||
public TaikoPerformanceCalculator()
|
public TaikoPerformanceCalculator()
|
||||||
: base(new TaikoRuleset())
|
: base(new TaikoRuleset())
|
||||||
{
|
{
|
||||||
@ -35,7 +37,11 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
|
countMeh = score.Statistics.GetValueOrDefault(HitResult.Meh);
|
||||||
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
|
countMiss = score.Statistics.GetValueOrDefault(HitResult.Miss);
|
||||||
|
|
||||||
double multiplier = 1.12; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things
|
// The effectiveMissCount is calculated by gaining a ratio for totalSuccessfulHits and increasing the miss penalty for shorter object counts lower than 1000.
|
||||||
|
if (totalSuccessfulHits > 0)
|
||||||
|
effectiveMissCount = Math.Max(1.0, 1000.0 / totalSuccessfulHits) * countMiss;
|
||||||
|
|
||||||
|
double multiplier = 1.13;
|
||||||
|
|
||||||
if (score.Mods.Any(m => m is ModHidden))
|
if (score.Mods.Any(m => m is ModHidden))
|
||||||
multiplier *= 1.075;
|
multiplier *= 1.075;
|
||||||
@ -55,6 +61,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
{
|
{
|
||||||
Difficulty = difficultyValue,
|
Difficulty = difficultyValue,
|
||||||
Accuracy = accuracyValue,
|
Accuracy = accuracyValue,
|
||||||
|
EffectiveMissCount = effectiveMissCount,
|
||||||
Total = totalValue
|
Total = totalValue
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -66,18 +73,21 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
double lengthBonus = 1 + 0.1 * Math.Min(1.0, totalHits / 1500.0);
|
double lengthBonus = 1 + 0.1 * Math.Min(1.0, totalHits / 1500.0);
|
||||||
difficultyValue *= lengthBonus;
|
difficultyValue *= lengthBonus;
|
||||||
|
|
||||||
difficultyValue *= Math.Pow(0.986, countMiss);
|
difficultyValue *= Math.Pow(0.986, effectiveMissCount);
|
||||||
|
|
||||||
if (score.Mods.Any(m => m is ModEasy))
|
if (score.Mods.Any(m => m is ModEasy))
|
||||||
difficultyValue *= 0.980;
|
difficultyValue *= 0.985;
|
||||||
|
|
||||||
if (score.Mods.Any(m => m is ModHidden))
|
if (score.Mods.Any(m => m is ModHidden))
|
||||||
difficultyValue *= 1.025;
|
difficultyValue *= 1.025;
|
||||||
|
|
||||||
if (score.Mods.Any(m => m is ModFlashlight<TaikoHitObject>))
|
if (score.Mods.Any(m => m is ModHardRock))
|
||||||
difficultyValue *= 1.05 * lengthBonus;
|
difficultyValue *= 1.050;
|
||||||
|
|
||||||
return difficultyValue * Math.Pow(score.Accuracy, 1.5);
|
if (score.Mods.Any(m => m is ModFlashlight<TaikoHitObject>))
|
||||||
|
difficultyValue *= 1.050 * lengthBonus;
|
||||||
|
|
||||||
|
return difficultyValue * Math.Pow(score.Accuracy, 2.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private double computeAccuracyValue(ScoreInfo score, TaikoDifficultyAttributes attributes)
|
private double computeAccuracyValue(ScoreInfo score, TaikoDifficultyAttributes attributes)
|
||||||
@ -85,18 +95,20 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
if (attributes.GreatHitWindow <= 0)
|
if (attributes.GreatHitWindow <= 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
double accuracyValue = Math.Pow(140.0 / attributes.GreatHitWindow, 1.1) * Math.Pow(score.Accuracy, 12.0) * 27;
|
double accuracyValue = Math.Pow(60.0 / attributes.GreatHitWindow, 1.1) * Math.Pow(score.Accuracy, 8.0) * Math.Pow(attributes.StarRating, 0.4) * 27.0;
|
||||||
|
|
||||||
double lengthBonus = Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
|
double lengthBonus = Math.Min(1.15, Math.Pow(totalHits / 1500.0, 0.3));
|
||||||
accuracyValue *= lengthBonus;
|
accuracyValue *= lengthBonus;
|
||||||
|
|
||||||
// Slight HDFL Bonus for accuracy.
|
// Slight HDFL Bonus for accuracy. A clamp is used to prevent against negative values
|
||||||
if (score.Mods.Any(m => m is ModFlashlight<TaikoHitObject>) && score.Mods.Any(m => m is ModHidden))
|
if (score.Mods.Any(m => m is ModFlashlight<TaikoHitObject>) && score.Mods.Any(m => m is ModHidden))
|
||||||
accuracyValue *= 1.10 * lengthBonus;
|
accuracyValue *= Math.Max(1.050, 1.075 * lengthBonus);
|
||||||
|
|
||||||
return accuracyValue;
|
return accuracyValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int totalHits => countGreat + countOk + countMeh + countMiss;
|
private int totalHits => countGreat + countOk + countMeh + countMiss;
|
||||||
|
|
||||||
|
private int totalSuccessfulHits => countGreat + countOk + countMeh;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
base.OnMouseUp(e);
|
base.OnMouseUp(e);
|
||||||
EndPlacement(true);
|
EndPlacement(spanPlacementObject.Duration > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void UpdateTimeAndPosition(SnapResult result)
|
public override void UpdateTimeAndPosition(SnapResult result)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
@ -8,7 +9,7 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
{
|
{
|
||||||
public class TaikoModEasy : ModEasy
|
public class TaikoModEasy : ModEasy
|
||||||
{
|
{
|
||||||
public override string Description => @"Beats move slower, and less accuracy required!";
|
public override LocalisableString Description => @"Beats move slower, and less accuracy required!";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Multiplier factor added to the scrolling speed.
|
/// Multiplier factor added to the scrolling speed.
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// 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;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
@ -15,7 +16,7 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
{
|
{
|
||||||
public class TaikoModHidden : ModHidden, IApplicableToDrawableRuleset<TaikoHitObject>
|
public class TaikoModHidden : ModHidden, IApplicableToDrawableRuleset<TaikoHitObject>
|
||||||
{
|
{
|
||||||
public override string Description => @"Beats fade out before you hit them!";
|
public override LocalisableString Description => @"Beats fade out before you hit them!";
|
||||||
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1;
|
public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
@ -13,7 +14,7 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
{
|
{
|
||||||
public class TaikoModRandom : ModRandom, IApplicableToBeatmap
|
public class TaikoModRandom : ModRandom, IApplicableToBeatmap
|
||||||
{
|
{
|
||||||
public override string Description => @"Shuffle around the colours!";
|
public override LocalisableString Description => @"Shuffle around the colours!";
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(TaikoModSwap)).ToArray();
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(TaikoModSwap)).ToArray();
|
||||||
|
|
||||||
public void ApplyToBeatmap(IBeatmap beatmap)
|
public void ApplyToBeatmap(IBeatmap beatmap)
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
// 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.Localisation;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Mods
|
namespace osu.Game.Rulesets.Taiko.Mods
|
||||||
{
|
{
|
||||||
public class TaikoModRelax : ModRelax
|
public class TaikoModRelax : ModRelax
|
||||||
{
|
{
|
||||||
public override string Description => @"No ninja-like spinners, demanding drumrolls or unexpected katu's.";
|
public override LocalisableString Description => @"No ninja-like spinners, demanding drumrolls or unexpected katu's.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Taiko.Beatmaps;
|
using osu.Game.Rulesets.Taiko.Beatmaps;
|
||||||
@ -14,7 +15,7 @@ namespace osu.Game.Rulesets.Taiko.Mods
|
|||||||
{
|
{
|
||||||
public override string Name => "Swap";
|
public override string Name => "Swap";
|
||||||
public override string Acronym => "SW";
|
public override string Acronym => "SW";
|
||||||
public override string Description => @"Dons become kats, kats become dons";
|
public override LocalisableString Description => @"Dons become kats, kats become dons";
|
||||||
public override ModType Type => ModType.Conversion;
|
public override ModType Type => ModType.Conversion;
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModRandom)).ToArray();
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModRandom)).ToArray();
|
||||||
|
@ -13,6 +13,7 @@ using osu.Game.Rulesets.Objects.Drawables;
|
|||||||
using osuTK.Graphics;
|
using osuTK.Graphics;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Primitives;
|
||||||
using osu.Framework.Input.Events;
|
using osu.Framework.Input.Events;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
@ -30,6 +31,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private const int rolling_hits_for_engaged_colour = 5;
|
private const int rolling_hits_for_engaged_colour = 5;
|
||||||
|
|
||||||
|
public override Quad ScreenSpaceDrawQuad => MainPiece.Drawable.ScreenSpaceDrawQuad;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Rolling number of tick hits. This increases for hits and decreases for misses.
|
/// Rolling number of tick hits. This increases for hits and decreases for misses.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -7,6 +7,7 @@ using osu.Framework.Allocation;
|
|||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Graphics.Animations;
|
using osu.Framework.Graphics.Animations;
|
||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Primitives;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Rulesets.Objects.Drawables;
|
using osu.Game.Rulesets.Objects.Drawables;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
@ -20,6 +21,9 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
|||||||
{
|
{
|
||||||
private Drawable backgroundLayer;
|
private Drawable backgroundLayer;
|
||||||
|
|
||||||
|
// required for editor blueprints (not sure why these circle pieces are zero size).
|
||||||
|
public override Quad ScreenSpaceDrawQuad => backgroundLayer.ScreenSpaceDrawQuad;
|
||||||
|
|
||||||
public LegacyCirclePiece()
|
public LegacyCirclePiece()
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both;
|
RelativeSizeAxes = Axes.Both;
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
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.Framework.Graphics.Sprites;
|
using osu.Framework.Graphics.Sprites;
|
||||||
using osu.Framework.Graphics.Textures;
|
using osu.Framework.Graphics.Textures;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
@ -16,11 +17,22 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
|||||||
{
|
{
|
||||||
public class LegacyDrumRoll : CompositeDrawable, IHasAccentColour
|
public class LegacyDrumRoll : CompositeDrawable, IHasAccentColour
|
||||||
{
|
{
|
||||||
|
public override Quad ScreenSpaceDrawQuad
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var headDrawQuad = headCircle.ScreenSpaceDrawQuad;
|
||||||
|
var tailDrawQuad = tailCircle.ScreenSpaceDrawQuad;
|
||||||
|
|
||||||
|
return new Quad(headDrawQuad.TopLeft, tailDrawQuad.TopRight, headDrawQuad.BottomLeft, tailDrawQuad.BottomRight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private LegacyCirclePiece headCircle;
|
private LegacyCirclePiece headCircle;
|
||||||
|
|
||||||
private Sprite body;
|
private Sprite body;
|
||||||
|
|
||||||
private Sprite end;
|
private Sprite tailCircle;
|
||||||
|
|
||||||
public LegacyDrumRoll()
|
public LegacyDrumRoll()
|
||||||
{
|
{
|
||||||
@ -32,7 +44,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
|||||||
{
|
{
|
||||||
InternalChildren = new Drawable[]
|
InternalChildren = new Drawable[]
|
||||||
{
|
{
|
||||||
end = new Sprite
|
tailCircle = new Sprite
|
||||||
{
|
{
|
||||||
Anchor = Anchor.CentreRight,
|
Anchor = Anchor.CentreRight,
|
||||||
Origin = Anchor.CentreLeft,
|
Origin = Anchor.CentreLeft,
|
||||||
@ -82,7 +94,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
|||||||
|
|
||||||
headCircle.AccentColour = colour;
|
headCircle.AccentColour = colour;
|
||||||
body.Colour = colour;
|
body.Colour = colour;
|
||||||
end.Colour = colour;
|
tailCircle.Colour = colour;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ using osu.Game.Scoring;
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Extensions.EnumExtensions;
|
using osu.Framework.Extensions.EnumExtensions;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Rulesets.Edit;
|
using osu.Game.Rulesets.Edit;
|
||||||
using osu.Game.Rulesets.Taiko.Edit;
|
using osu.Game.Rulesets.Taiko.Edit;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
@ -192,7 +193,7 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string GetDisplayNameForHitResult(HitResult result)
|
public override LocalisableString GetDisplayNameForHitResult(HitResult result)
|
||||||
{
|
{
|
||||||
switch (result)
|
switch (result)
|
||||||
{
|
{
|
||||||
|
@ -5,6 +5,7 @@ using System;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Moq;
|
using Moq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Osu.Mods;
|
using osu.Game.Rulesets.Osu.Mods;
|
||||||
using osu.Game.Utils;
|
using osu.Game.Utils;
|
||||||
@ -320,7 +321,7 @@ namespace osu.Game.Tests.Mods
|
|||||||
public class InvalidMultiplayerMod : Mod
|
public class InvalidMultiplayerMod : Mod
|
||||||
{
|
{
|
||||||
public override string Name => string.Empty;
|
public override string Name => string.Empty;
|
||||||
public override string Description => string.Empty;
|
public override LocalisableString Description => string.Empty;
|
||||||
public override string Acronym => string.Empty;
|
public override string Acronym => string.Empty;
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
public override bool HasImplementation => true;
|
public override bool HasImplementation => true;
|
||||||
@ -331,7 +332,7 @@ namespace osu.Game.Tests.Mods
|
|||||||
private class InvalidMultiplayerFreeMod : Mod
|
private class InvalidMultiplayerFreeMod : Mod
|
||||||
{
|
{
|
||||||
public override string Name => string.Empty;
|
public override string Name => string.Empty;
|
||||||
public override string Description => string.Empty;
|
public override LocalisableString Description => string.Empty;
|
||||||
public override string Acronym => string.Empty;
|
public override string Acronym => string.Empty;
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
public override bool HasImplementation => true;
|
public override bool HasImplementation => true;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
@ -60,7 +61,7 @@ namespace osu.Game.Tests.Mods
|
|||||||
{
|
{
|
||||||
public override double ScoreMultiplier => 1.0;
|
public override double ScoreMultiplier => 1.0;
|
||||||
|
|
||||||
public override string Description => "This is a customisable test mod.";
|
public override LocalisableString Description => "This is a customisable test mod.";
|
||||||
|
|
||||||
public override ModType Type => ModType.Conversion;
|
public override ModType Type => ModType.Conversion;
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Difficulty;
|
using osu.Game.Rulesets.Difficulty;
|
||||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
@ -160,7 +161,7 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
{
|
{
|
||||||
public override string Name => nameof(ModA);
|
public override string Name => nameof(ModA);
|
||||||
public override string Acronym => nameof(ModA);
|
public override string Acronym => nameof(ModA);
|
||||||
public override string Description => string.Empty;
|
public override LocalisableString Description => string.Empty;
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModIncompatibleWithA), typeof(ModIncompatibleWithAAndB) };
|
public override Type[] IncompatibleMods => new[] { typeof(ModIncompatibleWithA), typeof(ModIncompatibleWithAAndB) };
|
||||||
@ -169,7 +170,7 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
private class ModB : Mod
|
private class ModB : Mod
|
||||||
{
|
{
|
||||||
public override string Name => nameof(ModB);
|
public override string Name => nameof(ModB);
|
||||||
public override string Description => string.Empty;
|
public override LocalisableString Description => string.Empty;
|
||||||
public override string Acronym => nameof(ModB);
|
public override string Acronym => nameof(ModB);
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
@ -180,7 +181,7 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
{
|
{
|
||||||
public override string Name => nameof(ModC);
|
public override string Name => nameof(ModC);
|
||||||
public override string Acronym => nameof(ModC);
|
public override string Acronym => nameof(ModC);
|
||||||
public override string Description => string.Empty;
|
public override LocalisableString Description => string.Empty;
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +189,7 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
{
|
{
|
||||||
public override string Name => $"Incompatible With {nameof(ModA)}";
|
public override string Name => $"Incompatible With {nameof(ModA)}";
|
||||||
public override string Acronym => $"Incompatible With {nameof(ModA)}";
|
public override string Acronym => $"Incompatible With {nameof(ModA)}";
|
||||||
public override string Description => string.Empty;
|
public override LocalisableString Description => string.Empty;
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModA) };
|
public override Type[] IncompatibleMods => new[] { typeof(ModA) };
|
||||||
@ -207,7 +208,7 @@ namespace osu.Game.Tests.NonVisual
|
|||||||
{
|
{
|
||||||
public override string Name => $"Incompatible With {nameof(ModA)} and {nameof(ModB)}";
|
public override string Name => $"Incompatible With {nameof(ModA)} and {nameof(ModB)}";
|
||||||
public override string Acronym => $"Incompatible With {nameof(ModA)} and {nameof(ModB)}";
|
public override string Acronym => $"Incompatible With {nameof(ModA)} and {nameof(ModB)}";
|
||||||
public override string Description => string.Empty;
|
public override LocalisableString Description => string.Empty;
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
public override Type[] IncompatibleMods => new[] { typeof(ModA), typeof(ModB) };
|
public override Type[] IncompatibleMods => new[] { typeof(ModA), typeof(ModB) };
|
||||||
|
@ -1,39 +1,31 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Bindables;
|
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
|
|
||||||
namespace osu.Game.Tests.NonVisual
|
namespace osu.Game.Tests.NonVisual
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class GameplayClockTest
|
public class GameplayClockContainerTest
|
||||||
{
|
{
|
||||||
[TestCase(0)]
|
[TestCase(0)]
|
||||||
[TestCase(1)]
|
[TestCase(1)]
|
||||||
public void TestTrueGameplayRateWithZeroAdjustment(double underlyingClockRate)
|
public void TestTrueGameplayRateWithZeroAdjustment(double underlyingClockRate)
|
||||||
{
|
{
|
||||||
var framedClock = new FramedClock(new ManualClock { Rate = underlyingClockRate });
|
var framedClock = new FramedClock(new ManualClock { Rate = underlyingClockRate });
|
||||||
var gameplayClock = new TestGameplayClock(framedClock);
|
var gameplayClock = new TestGameplayClockContainer(framedClock);
|
||||||
|
|
||||||
gameplayClock.MutableNonGameplayAdjustments.Add(new BindableDouble());
|
|
||||||
|
|
||||||
Assert.That(gameplayClock.TrueGameplayRate, Is.EqualTo(0));
|
Assert.That(gameplayClock.TrueGameplayRate, Is.EqualTo(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestGameplayClock : GameplayClock
|
private class TestGameplayClockContainer : GameplayClockContainer
|
||||||
{
|
{
|
||||||
public List<Bindable<double>> MutableNonGameplayAdjustments { get; } = new List<Bindable<double>>();
|
public override IEnumerable<double> NonGameplayAdjustments => new[] { 0.0 };
|
||||||
|
|
||||||
public override IEnumerable<double> NonGameplayAdjustments => MutableNonGameplayAdjustments.Select(b => b.Value);
|
public TestGameplayClockContainer(IFrameBasedClock underlyingClock)
|
||||||
|
|
||||||
public TestGameplayClock(IFrameBasedClock underlyingClock)
|
|
||||||
: base(underlyingClock)
|
: base(underlyingClock)
|
||||||
{
|
{
|
||||||
}
|
}
|
@ -9,6 +9,7 @@ using Newtonsoft.Json;
|
|||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
@ -182,7 +183,7 @@ namespace osu.Game.Tests.Online
|
|||||||
{
|
{
|
||||||
public override string Name => "Test Mod";
|
public override string Name => "Test Mod";
|
||||||
public override string Acronym => "TM";
|
public override string Acronym => "TM";
|
||||||
public override string Description => "This is a test mod.";
|
public override LocalisableString Description => "This is a test mod.";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
[SettingSource("Test")]
|
[SettingSource("Test")]
|
||||||
@ -199,7 +200,7 @@ namespace osu.Game.Tests.Online
|
|||||||
{
|
{
|
||||||
public override string Name => "Test Mod";
|
public override string Name => "Test Mod";
|
||||||
public override string Acronym => "TMTR";
|
public override string Acronym => "TMTR";
|
||||||
public override string Description => "This is a test mod.";
|
public override LocalisableString Description => "This is a test mod.";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
[SettingSource("Initial rate", "The starting speed of the track")]
|
[SettingSource("Initial rate", "The starting speed of the track")]
|
||||||
|
@ -7,6 +7,7 @@ using System.Collections.Generic;
|
|||||||
using MessagePack;
|
using MessagePack;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Online.API;
|
using osu.Game.Online.API;
|
||||||
@ -102,7 +103,7 @@ namespace osu.Game.Tests.Online
|
|||||||
{
|
{
|
||||||
public override string Name => "Test Mod";
|
public override string Name => "Test Mod";
|
||||||
public override string Acronym => "TM";
|
public override string Acronym => "TM";
|
||||||
public override string Description => "This is a test mod.";
|
public override LocalisableString Description => "This is a test mod.";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
[SettingSource("Test")]
|
[SettingSource("Test")]
|
||||||
@ -119,7 +120,7 @@ namespace osu.Game.Tests.Online
|
|||||||
{
|
{
|
||||||
public override string Name => "Test Mod";
|
public override string Name => "Test Mod";
|
||||||
public override string Acronym => "TMTR";
|
public override string Acronym => "TMTR";
|
||||||
public override string Description => "This is a test mod.";
|
public override LocalisableString Description => "This is a test mod.";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
[SettingSource("Initial rate", "The starting speed of the track")]
|
[SettingSource("Initial rate", "The starting speed of the track")]
|
||||||
@ -154,7 +155,7 @@ namespace osu.Game.Tests.Online
|
|||||||
{
|
{
|
||||||
public override string Name => "Test Mod";
|
public override string Name => "Test Mod";
|
||||||
public override string Acronym => "TM";
|
public override string Acronym => "TM";
|
||||||
public override string Description => "This is a test mod.";
|
public override LocalisableString Description => "This is a test mod.";
|
||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
|
|
||||||
[SettingSource("Test")]
|
[SettingSource("Test")]
|
||||||
|
@ -4,11 +4,13 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Framework.Timing;
|
using osu.Framework.Timing;
|
||||||
using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate;
|
using osu.Game.Screens.OnlinePlay.Multiplayer.Spectate;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
namespace osu.Game.Tests.OnlinePlay
|
namespace osu.Game.Tests.OnlinePlay
|
||||||
@ -16,20 +18,34 @@ namespace osu.Game.Tests.OnlinePlay
|
|||||||
[HeadlessTest]
|
[HeadlessTest]
|
||||||
public class TestSceneCatchUpSyncManager : OsuTestScene
|
public class TestSceneCatchUpSyncManager : OsuTestScene
|
||||||
{
|
{
|
||||||
private TestManualClock master;
|
private GameplayClockContainer master;
|
||||||
private CatchUpSyncManager syncManager;
|
private SpectatorSyncManager syncManager;
|
||||||
|
|
||||||
private TestSpectatorPlayerClock player1;
|
private Dictionary<SpectatorPlayerClock, int> clocksById;
|
||||||
private TestSpectatorPlayerClock player2;
|
private SpectatorPlayerClock player1;
|
||||||
|
private SpectatorPlayerClock player2;
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
syncManager = new CatchUpSyncManager(master = new TestManualClock());
|
syncManager = new SpectatorSyncManager(master = new GameplayClockContainer(new TestManualClock()));
|
||||||
syncManager.AddPlayerClock(player1 = new TestSpectatorPlayerClock(1));
|
player1 = syncManager.CreateManagedClock();
|
||||||
syncManager.AddPlayerClock(player2 = new TestSpectatorPlayerClock(2));
|
player2 = syncManager.CreateManagedClock();
|
||||||
|
|
||||||
Schedule(() => Child = syncManager);
|
clocksById = new Dictionary<SpectatorPlayerClock, int>
|
||||||
|
{
|
||||||
|
{ player1, 1 },
|
||||||
|
{ player2, 2 }
|
||||||
|
};
|
||||||
|
|
||||||
|
Schedule(() =>
|
||||||
|
{
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
syncManager,
|
||||||
|
master
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
@ -48,7 +64,7 @@ namespace osu.Game.Tests.OnlinePlay
|
|||||||
public void TestReadyPlayersStartWhenReadyForMaximumDelayTime()
|
public void TestReadyPlayersStartWhenReadyForMaximumDelayTime()
|
||||||
{
|
{
|
||||||
setWaiting(() => player1, false);
|
setWaiting(() => player1, false);
|
||||||
AddWaitStep($"wait {CatchUpSyncManager.MAXIMUM_START_DELAY} milliseconds", (int)Math.Ceiling(CatchUpSyncManager.MAXIMUM_START_DELAY / TimePerAction));
|
AddWaitStep($"wait {SpectatorSyncManager.MAXIMUM_START_DELAY} milliseconds", (int)Math.Ceiling(SpectatorSyncManager.MAXIMUM_START_DELAY / TimePerAction));
|
||||||
assertPlayerClockState(() => player1, true);
|
assertPlayerClockState(() => player1, true);
|
||||||
assertPlayerClockState(() => player2, false);
|
assertPlayerClockState(() => player2, false);
|
||||||
}
|
}
|
||||||
@ -58,7 +74,7 @@ namespace osu.Game.Tests.OnlinePlay
|
|||||||
{
|
{
|
||||||
setAllWaiting(false);
|
setAllWaiting(false);
|
||||||
|
|
||||||
setMasterTime(CatchUpSyncManager.SYNC_TARGET + 1);
|
setMasterTime(SpectatorSyncManager.SYNC_TARGET + 1);
|
||||||
assertCatchingUp(() => player1, false);
|
assertCatchingUp(() => player1, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +83,7 @@ namespace osu.Game.Tests.OnlinePlay
|
|||||||
{
|
{
|
||||||
setAllWaiting(false);
|
setAllWaiting(false);
|
||||||
|
|
||||||
setMasterTime(CatchUpSyncManager.MAX_SYNC_OFFSET + 1);
|
setMasterTime(SpectatorSyncManager.MAX_SYNC_OFFSET + 1);
|
||||||
assertCatchingUp(() => player1, true);
|
assertCatchingUp(() => player1, true);
|
||||||
assertCatchingUp(() => player2, true);
|
assertCatchingUp(() => player2, true);
|
||||||
}
|
}
|
||||||
@ -77,8 +93,8 @@ namespace osu.Game.Tests.OnlinePlay
|
|||||||
{
|
{
|
||||||
setAllWaiting(false);
|
setAllWaiting(false);
|
||||||
|
|
||||||
setMasterTime(CatchUpSyncManager.MAX_SYNC_OFFSET + 1);
|
setMasterTime(SpectatorSyncManager.MAX_SYNC_OFFSET + 1);
|
||||||
setPlayerClockTime(() => player1, CatchUpSyncManager.SYNC_TARGET + 1);
|
setPlayerClockTime(() => player1, SpectatorSyncManager.SYNC_TARGET + 1);
|
||||||
assertCatchingUp(() => player1, true);
|
assertCatchingUp(() => player1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,8 +103,8 @@ namespace osu.Game.Tests.OnlinePlay
|
|||||||
{
|
{
|
||||||
setAllWaiting(false);
|
setAllWaiting(false);
|
||||||
|
|
||||||
setMasterTime(CatchUpSyncManager.MAX_SYNC_OFFSET + 2);
|
setMasterTime(SpectatorSyncManager.MAX_SYNC_OFFSET + 2);
|
||||||
setPlayerClockTime(() => player1, CatchUpSyncManager.SYNC_TARGET);
|
setPlayerClockTime(() => player1, SpectatorSyncManager.SYNC_TARGET);
|
||||||
assertCatchingUp(() => player1, false);
|
assertCatchingUp(() => player1, false);
|
||||||
assertCatchingUp(() => player2, true);
|
assertCatchingUp(() => player2, true);
|
||||||
}
|
}
|
||||||
@ -98,7 +114,7 @@ namespace osu.Game.Tests.OnlinePlay
|
|||||||
{
|
{
|
||||||
setAllWaiting(false);
|
setAllWaiting(false);
|
||||||
|
|
||||||
setPlayerClockTime(() => player1, -CatchUpSyncManager.SYNC_TARGET);
|
setPlayerClockTime(() => player1, -SpectatorSyncManager.SYNC_TARGET);
|
||||||
assertCatchingUp(() => player1, false);
|
assertCatchingUp(() => player1, false);
|
||||||
assertPlayerClockState(() => player1, true);
|
assertPlayerClockState(() => player1, true);
|
||||||
}
|
}
|
||||||
@ -108,7 +124,7 @@ namespace osu.Game.Tests.OnlinePlay
|
|||||||
{
|
{
|
||||||
setAllWaiting(false);
|
setAllWaiting(false);
|
||||||
|
|
||||||
setPlayerClockTime(() => player1, -CatchUpSyncManager.SYNC_TARGET - 1);
|
setPlayerClockTime(() => player1, -SpectatorSyncManager.SYNC_TARGET - 1);
|
||||||
|
|
||||||
// This is a silent catchup, where IsCatchingUp = false but IsRunning = false also.
|
// This is a silent catchup, where IsCatchingUp = false but IsRunning = false also.
|
||||||
assertCatchingUp(() => player1, false);
|
assertCatchingUp(() => player1, false);
|
||||||
@ -129,13 +145,13 @@ namespace osu.Game.Tests.OnlinePlay
|
|||||||
assertPlayerClockState(() => player1, false);
|
assertPlayerClockState(() => player1, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setWaiting(Func<TestSpectatorPlayerClock> playerClock, bool waiting)
|
private void setWaiting(Func<SpectatorPlayerClock> playerClock, bool waiting)
|
||||||
=> AddStep($"set player clock {playerClock().Id} waiting = {waiting}", () => playerClock().WaitingOnFrames.Value = waiting);
|
=> AddStep($"set player clock {clocksById[playerClock()]} waiting = {waiting}", () => playerClock().WaitingOnFrames = waiting);
|
||||||
|
|
||||||
private void setAllWaiting(bool waiting) => AddStep($"set all player clocks waiting = {waiting}", () =>
|
private void setAllWaiting(bool waiting) => AddStep($"set all player clocks waiting = {waiting}", () =>
|
||||||
{
|
{
|
||||||
player1.WaitingOnFrames.Value = waiting;
|
player1.WaitingOnFrames = waiting;
|
||||||
player2.WaitingOnFrames.Value = waiting;
|
player2.WaitingOnFrames = waiting;
|
||||||
});
|
});
|
||||||
|
|
||||||
private void setMasterTime(double time)
|
private void setMasterTime(double time)
|
||||||
@ -144,51 +160,14 @@ namespace osu.Game.Tests.OnlinePlay
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// clock.Time = master.Time - offsetFromMaster
|
/// clock.Time = master.Time - offsetFromMaster
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void setPlayerClockTime(Func<TestSpectatorPlayerClock> playerClock, double offsetFromMaster)
|
private void setPlayerClockTime(Func<SpectatorPlayerClock> playerClock, double offsetFromMaster)
|
||||||
=> AddStep($"set player clock {playerClock().Id} = master - {offsetFromMaster}", () => playerClock().Seek(master.CurrentTime - offsetFromMaster));
|
=> AddStep($"set player clock {clocksById[playerClock()]} = master - {offsetFromMaster}", () => playerClock().Seek(master.CurrentTime - offsetFromMaster));
|
||||||
|
|
||||||
private void assertCatchingUp(Func<TestSpectatorPlayerClock> playerClock, bool catchingUp) =>
|
private void assertCatchingUp(Func<SpectatorPlayerClock> playerClock, bool catchingUp) =>
|
||||||
AddAssert($"player clock {playerClock().Id} {(catchingUp ? "is" : "is not")} catching up", () => playerClock().IsCatchingUp == catchingUp);
|
AddAssert($"player clock {clocksById[playerClock()]} {(catchingUp ? "is" : "is not")} catching up", () => playerClock().IsCatchingUp == catchingUp);
|
||||||
|
|
||||||
private void assertPlayerClockState(Func<TestSpectatorPlayerClock> playerClock, bool running)
|
private void assertPlayerClockState(Func<SpectatorPlayerClock> playerClock, bool running)
|
||||||
=> AddAssert($"player clock {playerClock().Id} {(running ? "is" : "is not")} running", () => playerClock().IsRunning == running);
|
=> AddAssert($"player clock {clocksById[playerClock()]} {(running ? "is" : "is not")} running", () => playerClock().IsRunning == running);
|
||||||
|
|
||||||
private class TestSpectatorPlayerClock : TestManualClock, ISpectatorPlayerClock
|
|
||||||
{
|
|
||||||
public Bindable<bool> WaitingOnFrames { get; } = new Bindable<bool>(true);
|
|
||||||
|
|
||||||
public bool IsCatchingUp { get; set; }
|
|
||||||
|
|
||||||
public IFrameBasedClock Source
|
|
||||||
{
|
|
||||||
set => throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly int Id;
|
|
||||||
|
|
||||||
public TestSpectatorPlayerClock(int id)
|
|
||||||
{
|
|
||||||
Id = id;
|
|
||||||
|
|
||||||
WaitingOnFrames.BindValueChanged(waiting =>
|
|
||||||
{
|
|
||||||
if (waiting.NewValue)
|
|
||||||
Stop();
|
|
||||||
else
|
|
||||||
Start();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ProcessFrame()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public double ElapsedFrameTime => 0;
|
|
||||||
|
|
||||||
public double FramesPerSecond => 0;
|
|
||||||
|
|
||||||
public FrameTimeInfo TimeInfo => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TestManualClock : ManualClock, IAdjustableClock
|
private class TestManualClock : ManualClock, IAdjustableClock
|
||||||
{
|
{
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
@ -314,15 +315,55 @@ namespace osu.Game.Tests.Rulesets.Scoring
|
|||||||
}), Is.EqualTo(expectedScore).Within(0.5d));
|
}), Is.EqualTo(expectedScore).Within(0.5d));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma warning disable CS0618
|
||||||
|
[Test]
|
||||||
|
public void TestLegacyComboIncrease()
|
||||||
|
{
|
||||||
|
Assert.That(HitResult.LegacyComboIncrease.IncreasesCombo(), Is.True);
|
||||||
|
Assert.That(HitResult.LegacyComboIncrease.BreaksCombo(), Is.False);
|
||||||
|
Assert.That(HitResult.LegacyComboIncrease.AffectsCombo(), Is.True);
|
||||||
|
Assert.That(HitResult.LegacyComboIncrease.AffectsAccuracy(), Is.False);
|
||||||
|
Assert.That(HitResult.LegacyComboIncrease.IsBasic(), Is.False);
|
||||||
|
Assert.That(HitResult.LegacyComboIncrease.IsTick(), Is.False);
|
||||||
|
Assert.That(HitResult.LegacyComboIncrease.IsBonus(), Is.False);
|
||||||
|
Assert.That(HitResult.LegacyComboIncrease.IsHit(), Is.True);
|
||||||
|
Assert.That(HitResult.LegacyComboIncrease.IsScorable(), Is.True);
|
||||||
|
Assert.That(HitResultExtensions.ALL_TYPES, Does.Not.Contain(HitResult.LegacyComboIncrease));
|
||||||
|
|
||||||
|
// Cannot be used to apply results.
|
||||||
|
Assert.Throws<ArgumentException>(() => scoreProcessor.ApplyBeatmap(new Beatmap
|
||||||
|
{
|
||||||
|
HitObjects = { new TestHitObject(HitResult.LegacyComboIncrease) }
|
||||||
|
}));
|
||||||
|
|
||||||
|
ScoreInfo testScore = new ScoreInfo
|
||||||
|
{
|
||||||
|
MaxCombo = 1,
|
||||||
|
Statistics = new Dictionary<HitResult, int>
|
||||||
|
{
|
||||||
|
{ HitResult.Great, 1 }
|
||||||
|
},
|
||||||
|
MaximumStatistics = new Dictionary<HitResult, int>
|
||||||
|
{
|
||||||
|
{ HitResult.Great, 1 },
|
||||||
|
{ HitResult.LegacyComboIncrease, 1 }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
double totalScore = new TestScoreProcessor().ComputeFinalScore(ScoringMode.Standardised, testScore);
|
||||||
|
Assert.That(totalScore, Is.EqualTo(750_000)); // 500K from accuracy (100%), and 250K from combo (50%).
|
||||||
|
}
|
||||||
|
#pragma warning restore CS0618
|
||||||
|
|
||||||
private class TestRuleset : Ruleset
|
private class TestRuleset : Ruleset
|
||||||
{
|
{
|
||||||
public override IEnumerable<Mod> GetModsFor(ModType type) => throw new System.NotImplementedException();
|
public override IEnumerable<Mod> GetModsFor(ModType type) => throw new NotImplementedException();
|
||||||
|
|
||||||
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => throw new System.NotImplementedException();
|
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => throw new NotImplementedException();
|
||||||
|
|
||||||
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new System.NotImplementedException();
|
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new NotImplementedException();
|
||||||
|
|
||||||
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => throw new System.NotImplementedException();
|
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => throw new NotImplementedException();
|
||||||
|
|
||||||
public override string Description => string.Empty;
|
public override string Description => string.Empty;
|
||||||
public override string ShortName => string.Empty;
|
public override string ShortName => string.Empty;
|
||||||
@ -352,5 +393,33 @@ namespace osu.Game.Tests.Rulesets.Scoring
|
|||||||
this.maxResult = maxResult;
|
this.maxResult = maxResult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class TestScoreProcessor : ScoreProcessor
|
||||||
|
{
|
||||||
|
protected override double DefaultAccuracyPortion => 0.5;
|
||||||
|
protected override double DefaultComboPortion => 0.5;
|
||||||
|
|
||||||
|
public TestScoreProcessor()
|
||||||
|
: base(new TestRuleset())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReSharper disable once MemberHidesStaticFromOuterClass
|
||||||
|
private class TestRuleset : Ruleset
|
||||||
|
{
|
||||||
|
protected override IEnumerable<HitResult> GetValidHitResults() => new[] { HitResult.Great };
|
||||||
|
|
||||||
|
public override IEnumerable<Mod> GetModsFor(ModType type) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public override DifficultyCalculator CreateDifficultyCalculator(IWorkingBeatmap beatmap) => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public override string Description => string.Empty;
|
||||||
|
public override string ShortName => string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -173,6 +173,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
reset();
|
reset();
|
||||||
|
|
||||||
AddStep("Seek(49)", () => Clock.Seek(49));
|
AddStep("Seek(49)", () => Clock.Seek(49));
|
||||||
|
checkTime(49);
|
||||||
AddStep("SeekForward, Snap", () => Clock.SeekForward(true));
|
AddStep("SeekForward, Snap", () => Clock.SeekForward(true));
|
||||||
checkTime(50);
|
checkTime(50);
|
||||||
AddStep("Seek(49.999)", () => Clock.Seek(49.999));
|
AddStep("Seek(49.999)", () => Clock.Seek(49.999));
|
||||||
@ -207,6 +208,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
reset();
|
reset();
|
||||||
|
|
||||||
AddStep("Seek(450)", () => Clock.Seek(450));
|
AddStep("Seek(450)", () => Clock.Seek(450));
|
||||||
|
checkTime(450);
|
||||||
AddStep("SeekBackward", () => Clock.SeekBackward());
|
AddStep("SeekBackward", () => Clock.SeekBackward());
|
||||||
checkTime(400);
|
checkTime(400);
|
||||||
AddStep("SeekBackward", () => Clock.SeekBackward());
|
AddStep("SeekBackward", () => Clock.SeekBackward());
|
||||||
@ -228,6 +230,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
reset();
|
reset();
|
||||||
|
|
||||||
AddStep("Seek(450)", () => Clock.Seek(450));
|
AddStep("Seek(450)", () => Clock.Seek(450));
|
||||||
|
checkTime(450);
|
||||||
AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true));
|
AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true));
|
||||||
checkTime(400);
|
checkTime(400);
|
||||||
AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true));
|
AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true));
|
||||||
@ -252,6 +255,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
reset();
|
reset();
|
||||||
|
|
||||||
AddStep("Seek(451)", () => Clock.Seek(451));
|
AddStep("Seek(451)", () => Clock.Seek(451));
|
||||||
|
checkTime(451);
|
||||||
AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true));
|
AddStep("SeekBackward, Snap", () => Clock.SeekBackward(true));
|
||||||
checkTime(450);
|
checkTime(450);
|
||||||
AddStep("Seek(450.999)", () => Clock.Seek(450.999));
|
AddStep("Seek(450.999)", () => Clock.Seek(450.999));
|
||||||
@ -276,6 +280,7 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
double lastTime = 0;
|
double lastTime = 0;
|
||||||
|
|
||||||
AddStep("Seek(0)", () => Clock.Seek(0));
|
AddStep("Seek(0)", () => Clock.Seek(0));
|
||||||
|
checkTime(0);
|
||||||
|
|
||||||
for (int i = 0; i < 9; i++)
|
for (int i = 0; i < 9; i++)
|
||||||
{
|
{
|
||||||
|
@ -1,13 +1,9 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||||
// See the LICENCE file in the repository root for full licence text.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Game.Beatmaps;
|
|
||||||
using osu.Game.Screens.Edit;
|
|
||||||
using osu.Game.Screens.Edit.Components;
|
using osu.Game.Screens.Edit.Components;
|
||||||
using osuTK;
|
using osuTK;
|
||||||
|
|
||||||
@ -19,19 +15,12 @@ namespace osu.Game.Tests.Visual.Editing
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
var clock = new EditorClock { IsCoupled = false };
|
Child = new PlaybackControl
|
||||||
Dependencies.CacheAs(clock);
|
|
||||||
|
|
||||||
var playback = new PlaybackControl
|
|
||||||
{
|
{
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Size = new Vector2(200, 100)
|
Size = new Vector2(200, 100)
|
||||||
};
|
};
|
||||||
|
|
||||||
Beatmap.Value = CreateWorkingBeatmap(new Beatmap());
|
|
||||||
|
|
||||||
Child = playback;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
private GameplayState gameplayState = TestGameplayState.Create(new OsuRuleset());
|
private GameplayState gameplayState = TestGameplayState.Create(new OsuRuleset());
|
||||||
|
|
||||||
[Cached(typeof(IGameplayClock))]
|
[Cached(typeof(IGameplayClock))]
|
||||||
private readonly IGameplayClock gameplayClock = new GameplayClock(new FramedClock());
|
private readonly IGameplayClock gameplayClock = new GameplayClockContainer(new FramedClock());
|
||||||
|
|
||||||
// best way to check without exposing.
|
// best way to check without exposing.
|
||||||
private Drawable hideTarget => hudOverlay.KeyCounter;
|
private Drawable hideTarget => hudOverlay.KeyCounter;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user