Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add strict mod #321

Merged
merged 19 commits into from
Aug 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions osu.Game.Rulesets.Tau.Tests/Objects/TestSceneSliderHardBeat.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Tau.Objects;
using osu.Game.Rulesets.Tau.Objects.Drawables;
using osu.Game.Rulesets.Tau.UI;

namespace osu.Game.Rulesets.Tau.Tests.Objects
{
[TestFixture]
public class TestSceneSliderHardBeat : TauTestScene
{
private int depthIndex;

private TauPlayfieldAdjustmentContainer container;

[Test]
public void TestSingleSlider()
{
AddStep("clear screen", Clear);
AddStep("add container", () => Add(container = new TauPlayfieldAdjustmentContainer()));

AddStep("Miss Single", () => container.Add(testSingle()));
AddStep("Hit Single", () => container.Add(testSingle(true)));
AddUntilStep("Wait for object despawn", () => !Children.Any(h => h is DrawableSlider { AllJudged: false }));
}

[Test]
public void TestSliderPerformance()
{
AddStep("clear screen", Clear);
AddStep("add container", () => Add(container = new TauPlayfieldAdjustmentContainer()));

AddStep("Miss Single", () => container.AddRange(testMultiple(100)));
AddStep("Hit Single", () => container.AddRange(testMultiple(100, true)));
}

private IEnumerable<Drawable> testMultiple(int count, bool auto = false)
=> Enumerable.Range(0, count).Select(x => testSingle(auto, 1000 + x * 100));

private Drawable testSingle(bool auto = false, double timeOffset = 1000)
{
var slider = createSlider(timeOffset);

slider.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());

return new TestDrawableSlider(slider, auto)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Depth = depthIndex++
};
}

private Slider createSlider(double timeOffset)
=> new()
{
StartTime = Time.Current + timeOffset,
IsHard = true,
Path = new PolarSliderPath(new SliderNode[]
{
new(0, 0),
new(500, 90),
new(1000, 180),
})
};

private class TestDrawableSlider : DrawableSlider
{
private readonly bool auto;

public TestDrawableSlider(Slider h, bool auto)
: base(h)
{
this.auto = auto;
}

protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (auto && !userTriggered && timeOffset > 0)
{
// force success
ApplyResult(r => r.Type = HitResult.Great);
}
else
base.CheckForResult(userTriggered, timeOffset);
}
}
}
}
81 changes: 81 additions & 0 deletions osu.Game.Rulesets.Tau.Tests/Objects/TestSceneStrictHardBeat.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Tau.Objects;
using osu.Game.Rulesets.Tau.Objects.Drawables;
using osu.Game.Rulesets.Tau.UI;

namespace osu.Game.Rulesets.Tau.Tests.Objects
{
[TestFixture]
public class TestSceneStrictHardBeat : TauTestScene
{
private int depthIndex;

public TestSceneStrictHardBeat()
{
TauPlayfieldAdjustmentContainer container;
Add(container = new TauPlayfieldAdjustmentContainer());

AddStep("Miss Single", () => container.Child = testSingle());
AddStep("Hit Single", () => container.Child = testSingle(true));
AddStep("Miss Stream", () => Add(testStream()));
AddStep("Hit Stream", () => Add(testStream(true)));
AddUntilStep("Wait for object despawn", () => !Children.Any(h => h is DrawableTauHitObject<StrictHardBeat> { AllJudged: false }));
}

private Drawable testSingle(bool auto = false, double timeOffset = 0, float angle = 0)
{
var strict = new StrictHardBeat
{
StartTime = Time.Current + 1000 + timeOffset,
Angle = angle
};

strict.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());

return new TestDrawableStrictHardBeat(strict, auto)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Depth = depthIndex++
};
}

private Drawable testStream(bool auto = false)
{
var playfield = new TauPlayfieldAdjustmentContainer();

for (int i = 0; i <= 1000; i += 100)
{
playfield.Add(testSingle(auto, i, i / 10f));
}

return playfield;
}

private class TestDrawableStrictHardBeat : DrawableStrictHardBeat
{
private readonly bool auto;

public TestDrawableStrictHardBeat(StrictHardBeat h, bool auto)
: base(h)
{
this.auto = auto;
}

protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (auto && !userTriggered && timeOffset > 0)
// Force success.
ApplyResult(r => r.Type = HitResult.Great);
else if (timeOffset > 0)
// We'll have to manually apply the result anyways because we have no way of checking if the paddle is in the correct spot.
ApplyResult(r => r.Type = HitResult.Miss);
}
}
}
}
24 changes: 22 additions & 2 deletions osu.Game.Rulesets.Tau/Beatmaps/TauBeatmapConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class TauBeatmapConverter : BeatmapConverter<TauHitObject>
protected override Beatmap<TauHitObject> CreateBeatmap() => new TauBeatmap();

public bool CanConvertToHardBeats { get; set; } = true;
public bool HardBeatsAreStrict { get; set; } = false;
public bool CanConvertToSliders { get; set; } = true;
public bool CanConvertImpossibleSliders { get; set; }
public int SliderDivisor { get; set; } = 4;
Expand Down Expand Up @@ -71,12 +72,13 @@ private float nextAngle(float target)

private TauHitObject convertToNonSlider(HitObject original)
{
bool isHard = (original is IHasPathWithRepeats tmp ? tmp.NodeSamples[0] : original.Samples).Any(s => s.Name == HitSampleInfo.HIT_FINISH);
var comboData = original as IHasCombo;
var sample = original is IHasPathWithRepeats c ? c.NodeSamples[0] : null;

if (isHard && CanConvertToHardBeats)
if (original.IsHardBeat() && CanConvertToHardBeats && !HardBeatsAreStrict)
return convertToHardBeat(original, comboData, sample);
if (original.IsHardBeat() && CanConvertToHardBeats)
return convertToStrictHardBeat(original, comboData, sample);

return convertToBeat(original, comboData, sample);
}
Expand Down Expand Up @@ -110,6 +112,16 @@ private TauHitObject convertToHardBeat(HitObject original, IHasCombo comboData,
ComboOffset = comboData?.ComboOffset ?? 0,
};

private TauHitObject convertToStrictHardBeat(HitObject original, IHasCombo comboData, IList<HitSampleInfo> samples = null)
=> new StrictHardBeat
{
Samples = samples ?? original.Samples,
StartTime = original.StartTime,
Angle = nextAngle(getHitObjectAngle(original)),
NewCombo = comboData?.NewCombo ?? false,
ComboOffset = comboData?.ComboOffset ?? 0,
};

private TauHitObject convertToSlider(HitObject original, IHasCombo comboData, IHasPathWithRepeats data, IBeatmap beatmap)
{
float? startLockedAngle = lastLockedAngle;
Expand Down Expand Up @@ -172,6 +184,7 @@ TauHitObject convertToNonSlider()
Path = new PolarSliderPath(nodes),
NewCombo = comboData?.NewCombo ?? false,
ComboOffset = comboData?.ComboOffset ?? 0,
IsHard = HardBeatsAreStrict && original.IsHardBeat()
};

if (beatmap.ControlPointInfo is LegacyControlPointInfo legacyControlPointInfo)
Expand Down Expand Up @@ -262,5 +275,12 @@ public static class TauBeatmapConverterExtensions
/// <param name="target">The target <see cref="HitObject"/> position.</param>
public static float GetHitObjectAngle(this Vector2 target)
=> TauBeatmapConverter.STANDARD_PLAYFIELD_CENTER.GetDegreesFromPosition(target);

/// <summary>
/// Determines whether the hit object should be considered as an emphasis.
/// </summary>
/// <param name="original">The original hit object.</param>
public static bool IsHardBeat(this HitObject original)
=> (original is IHasPathWithRepeats tmp ? tmp.NodeSamples[0] : original.Samples).Any(s => s.Name == HitSampleInfo.HIT_FINISH);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public static double EvaluateDifficulty(TauAngledDifficultyHitObject current, Ta
{
double velocity = calculateVelocity(current.Distance, current.StrainTime);

if (!allowedHitObjects.Any(t => t == typeof(Slider) && last.BaseObject is Slider))
if (allowedHitObjects.Any(t => t == typeof(Slider) && last.BaseObject is not Slider))
return velocity;

// Slider calculation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,15 @@ private static double repetitionPenalty(int notesSince)
=> Math.Min(1.0, 0.032 * notesSince);

private static HitType getHitType(TauDifficultyHitObject hitObject)
=> hitObject.BaseObject is AngledTauHitObject ? HitType.Angled : HitType.HardBeat;
{
if (hitObject.BaseObject is StrictHardBeat)
return HitType.HardBeat;

if (hitObject.BaseObject.NestedHitObjects.Count > 0 && hitObject.BaseObject.NestedHitObjects[0] is SliderHardBeat)
return HitType.HardBeat;

return hitObject.BaseObject is AngledTauHitObject ? HitType.Angled : HitType.HardBeat;
}

private enum HitType
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,14 @@ public TauAngledDifficultyHitObject(HitObject hitObject, HitObject lastObject, d
float offset = 0;

if (lastAngled.BaseObject is IHasOffsetAngle offsetAngle)
offset = offsetAngle.GetOffsetAngle();
offset += offsetAngle.GetOffsetAngle();

Distance = Math.Abs(Extensions.GetDeltaAngle(firstAngled.Angle, (lastAngled.BaseObject.Angle + offset)));
StrainTime = Math.Max(StrainTime, StartTime - lastAngled.StartTime);

// Have to aim the entirety of the strict hard beat, so let's increase the distance manually
if (lastAngled.BaseObject is StrictHardBeat strict)
Distance += (float)(strict.Range / 2);
}

if (hitObject is Slider slider)
Expand Down
5 changes: 3 additions & 2 deletions osu.Game.Rulesets.Tau/Difficulty/TauDifficultyCalculator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clo
hitWindowGreat = hitWindows.WindowFor(HitResult.Great) / clockRate;
return new Skill[]
{
new Aim(mods, new[] { typeof(Beat), typeof(SliderRepeat), typeof(Slider) }),
new Aim(mods, new[] { typeof(Beat) }),
new Aim(mods, new[] { typeof(Beat), typeof(StrictHardBeat), typeof(SliderRepeat), typeof(Slider) }),
new Aim(mods, new[] { typeof(Beat), typeof(StrictHardBeat) }),
new Speed(mods, hitWindowGreat),
new Complexity(mods)
};
Expand All @@ -130,6 +130,7 @@ protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clo
new TauModHardRock(),
new TauModDoubleTime(),
new TauModNightcore(),
new TauModStrict(),

// Automation
new TauModRelax(),
Expand Down
5 changes: 5 additions & 0 deletions osu.Game.Rulesets.Tau/Mods/TauModFadeIn.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ namespace osu.Game.Rulesets.Tau.Mods
public class TauModFadeIn : TauModHidden
{
public override IconUsage? Icon => TauIcons.ModFadeIn;

public override string Acronym => "FI";

// Modification from osu!mania's description of Hidden mod.
public override string Description => "Beats appear out of nowhere!";
protected override MaskingMode Mode => MaskingMode.FadeIn;
protected override float InitialCoverage => 0.25f;
}
Expand Down
4 changes: 4 additions & 0 deletions osu.Game.Rulesets.Tau/Mods/TauModFadeOut.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ namespace osu.Game.Rulesets.Tau.Mods
public class TauModFadeOut : TauModHidden
{
public override IconUsage? Icon => TauIcons.ModFadeOut;
public override string Acronym => "FO";

// Modification from osu!mania's description of Hidden mod.
public override string Description => "Beats fade out before you hit them!";
protected override MaskingMode Mode => MaskingMode.FadeOut;
protected override float InitialCoverage => 0.4f;
}
Expand Down
2 changes: 1 addition & 1 deletion osu.Game.Rulesets.Tau/Mods/TauModFlashlight.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Tau.Mods
{
public class TauModFlashlight : TauModFlashlight<TauHitObject>
{
public override double ScoreMultiplier => 1.12;
public override double ScoreMultiplier => 1.16;

public override float DefaultFlashlightSize => 0;

Expand Down
6 changes: 4 additions & 2 deletions osu.Game.Rulesets.Tau/Mods/TauModLenience.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Diagnostics;
using System;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Input.Events;
using osu.Game.Beatmaps;
Expand All @@ -15,9 +16,10 @@ public class TauModLenience : Mod, IApplicableAfterBeatmapConversion, IApplicabl
{
public override string Name => "Lenience";
public override string Description => "Hard beats are more forgiving";
public override double ScoreMultiplier => 0.5;
public override double ScoreMultiplier => 0.6;
public override string Acronym => "LN";
public override ModType Type => ModType.DifficultyReduction;
public override Type[] IncompatibleMods => new[] { typeof(TauModStrict), typeof(TauModLite) };

public void ApplyToBeatmap(IBeatmap beatmap)
{
Expand Down
5 changes: 4 additions & 1 deletion osu.Game.Rulesets.Tau/Mods/TauModLite.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using osu.Framework.Bindables;
using System;
using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
Expand All @@ -16,6 +17,8 @@ public class TauModLite : Mod, IApplicableToBeatmapConverter
public override string Description => "Removes certain aspects of the game.";
public override ModType Type => ModType.Conversion;

public override Type[] IncompatibleMods => new[] { typeof(TauModStrict), typeof(TauModLenience) };

[SettingSource("Sliders conversion", "Completely disables sliders altogether.")]
public Bindable<bool> ToggleSliders { get; } = new Bindable<bool>(false);

Expand Down
Loading