Skip to content

Commit

Permalink
Merge branch 'master' into slider_fix_lazyjump
Browse files Browse the repository at this point in the history
  • Loading branch information
Givikap120 committed Nov 13, 2024
2 parents ab56649 + 85d292a commit 60efb5e
Show file tree
Hide file tree
Showing 37 changed files with 406 additions and 160 deletions.
2 changes: 1 addition & 1 deletion osu.Desktop/osu.Desktop.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
<ItemGroup Label="Package References">
<PackageReference Include="System.IO.Packaging" Version="8.0.1" />
<PackageReference Include="DiscordRichPresence" Version="1.2.1.24" />
<PackageReference Include="Velopack" Version="0.0.630-g9c52e40" />
<PackageReference Include="Velopack" Version="0.0.869" />
</ItemGroup>
<ItemGroup Label="Resources">
<EmbeddedResource Include="lazer.ico" />
Expand Down
3 changes: 2 additions & 1 deletion osu.Game.Rulesets.Mania/Difficulty/Skills/Strain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using osu.Framework.Utils;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Difficulty.Utils;
using osu.Game.Rulesets.Mania.Difficulty.Preprocessing;
using osu.Game.Rulesets.Mods;

Expand Down Expand Up @@ -73,7 +74,7 @@ protected override double StrainValueOf(DifficultyHitObject current)
// 0.0 +--------+-+---------------> Release Difference / ms
// release_threshold
if (isOverlapping)
holdAddition = 1 / (1 + Math.Exp(0.27 * (release_threshold - closestEndTime)));
holdAddition = DifficultyCalculationUtils.Logistic(x: closestEndTime, multiplier: 0.27, midpointOffset: release_threshold);

// Decay and increase individualStrains in own column
individualStrains[column] = applyDecay(individualStrains[column], startTime - startTimes[column], individual_decay_base);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ private void load(ISkinSource skin, StageDefinition stageDefinition)
},
columnBackgrounds = new ColumnFlow<Drawable>(stageDefinition)
{
RelativeSizeAxes = Axes.Y
RelativeSizeAxes = Axes.Y,
Masking = false,
},
new HitTargetInsetContainer
{
Expand Down Expand Up @@ -126,8 +127,8 @@ private void load(ISkinSource skin)
},
new Container
{
X = isLastColumn ? -0.16f : 0,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
RelativeSizeAxes = Axes.Y,
Width = rightLineWidth,
Scale = new Vector2(0.740f, 1),
Expand Down
6 changes: 6 additions & 0 deletions osu.Game.Rulesets.Mania/UI/ColumnFlow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ public partial class ColumnFlow<TContent> : CompositeDrawable
private readonly FillFlowContainer<Container<TContent>> columns;
private readonly StageDefinition stageDefinition;

public new bool Masking
{
get => base.Masking;
set => base.Masking = value;
}

public ColumnFlow(StageDefinition stageDefinition)
{
this.stageDefinition = stageDefinition;
Expand Down
83 changes: 73 additions & 10 deletions osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderDrawing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using osu.Framework.Input;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.UI;
using osu.Game.Screens.Edit.Components.RadioButtons;
Expand All @@ -24,38 +25,93 @@ public partial class TestSceneSliderDrawing : TestSceneOsuEditor
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false);

[Test]
public void TestTouchInputAfterTouchingComposeArea()
public void TestTouchInputPlaceHitCircleDirectly()
{
AddStep("tap circle", () => tap(this.ChildrenOfType<EditorRadioButton>().Single(b => b.Button.Label == "HitCircle")));

AddStep("tap to place circle", () => tap(this.ChildrenOfType<Playfield>().Single()));
AddAssert("circle placed correctly", () =>
{
var circle = (HitCircle)EditorBeatmap.HitObjects.Single(h => h.StartTime == EditorClock.CurrentTimeAccurate);
Assert.Multiple(() =>
{
Assert.That(circle.Position.X, Is.EqualTo(256f).Within(0.01f));
Assert.That(circle.Position.Y, Is.EqualTo(192f).Within(0.01f));
});
return true;
});
}

[Test]
public void TestTouchInputPlaceCircleAfterTouchingComposeArea()
{
AddStep("tap circle", () => tap(this.ChildrenOfType<EditorRadioButton>().Single(b => b.Button.Label == "HitCircle")));

// this input is just for interacting with compose area
AddStep("tap playfield", () => tap(this.ChildrenOfType<Playfield>().Single()));
AddAssert("circle placed", () => EditorBeatmap.HitObjects.Single(h => h.StartTime == EditorClock.CurrentTimeAccurate) is HitCircle);

AddStep("move current time", () => InputManager.Key(Key.Right));
AddStep("move forward", () => InputManager.Key(Key.Right));

AddStep("tap to place circle", () => tap(this.ChildrenOfType<Playfield>().Single().ToScreenSpace(new Vector2(10, 10))));
AddStep("tap to place circle", () => tap(this.ChildrenOfType<Playfield>().Single()));
AddAssert("circle placed correctly", () =>
{
var circle = (HitCircle)EditorBeatmap.HitObjects.Single(h => h.StartTime == EditorClock.CurrentTimeAccurate);
Assert.Multiple(() =>
{
Assert.That(circle.Position.X, Is.EqualTo(10f).Within(0.01f));
Assert.That(circle.Position.Y, Is.EqualTo(10f).Within(0.01f));
Assert.That(circle.Position.X, Is.EqualTo(256f).Within(0.01f));
Assert.That(circle.Position.Y, Is.EqualTo(192f).Within(0.01f));
});
return true;
});
}

[Test]
public void TestTouchInputPlaceSliderDirectly()
{
AddStep("tap slider", () => tap(this.ChildrenOfType<EditorRadioButton>().Single(b => b.Button.Label == "Slider")));

AddStep("hold to draw slider", () => InputManager.BeginTouch(new Touch(TouchSource.Touch1, this.ChildrenOfType<Playfield>().Single().ToScreenSpace(new Vector2(50, 20)))));
AddStep("drag to draw", () => InputManager.MoveTouchTo(new Touch(TouchSource.Touch1, this.ChildrenOfType<Playfield>().Single().ToScreenSpace(new Vector2(200, 50)))));
AddAssert("selection not initiated", () => this.ChildrenOfType<DragBox>().All(d => d.State == Visibility.Hidden));
AddAssert("blueprint visible", () => this.ChildrenOfType<SliderPlacementBlueprint>().Single().Alpha > 0);
AddStep("end", () => InputManager.EndTouch(new Touch(TouchSource.Touch1, InputManager.CurrentState.Touch.GetTouchPosition(TouchSource.Touch1)!.Value)));
AddAssert("slider placed correctly", () =>
{
var slider = (Slider)EditorBeatmap.HitObjects.Single(h => h.StartTime == EditorClock.CurrentTimeAccurate);
Assert.Multiple(() =>
{
Assert.That(slider.Position.X, Is.EqualTo(50f).Within(0.01f));
Assert.That(slider.Position.Y, Is.EqualTo(20f).Within(0.01f));
Assert.That(slider.Path.ControlPoints.Count, Is.EqualTo(2));
Assert.That(slider.Path.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero));
// the final position may be slightly off from the mouse position when drawing, account for that.
Assert.That(slider.Path.ControlPoints[1].Position.X, Is.EqualTo(150).Within(5));
Assert.That(slider.Path.ControlPoints[1].Position.Y, Is.EqualTo(30).Within(5));
});
return true;
});
}

[Test]
public void TestTouchInputPlaceSliderAfterTouchingComposeArea()
{
AddStep("tap slider", () => tap(this.ChildrenOfType<EditorRadioButton>().Single(b => b.Button.Label == "Slider")));

// this input is just for interacting with compose area
AddStep("tap playfield", () => tap(this.ChildrenOfType<Playfield>().Single()));
AddStep("tap and hold another spot", () => hold(this.ChildrenOfType<Playfield>().Single(), new Vector2(50, 0)));
AddUntilStep("wait for slider placement", () => EditorBeatmap.HitObjects.SingleOrDefault(h => h.StartTime == EditorClock.CurrentTimeAccurate) is Slider);
AddStep("end", () => InputManager.EndTouch(new Touch(TouchSource.Touch1, InputManager.CurrentState.Touch.GetTouchPosition(TouchSource.Touch1)!.Value)));

AddStep("move current time", () => InputManager.Key(Key.Right));
AddStep("move forward", () => InputManager.Key(Key.Right));

AddStep("hold to draw slider", () => InputManager.BeginTouch(new Touch(TouchSource.Touch1, this.ChildrenOfType<Playfield>().Single().ToScreenSpace(new Vector2(50, 20)))));
AddStep("drag to draw", () => InputManager.MoveTouchTo(new Touch(TouchSource.Touch1, this.ChildrenOfType<Playfield>().Single().ToScreenSpace(new Vector2(200, 50)))));
AddAssert("selection not initiated", () => this.ChildrenOfType<DragBox>().All(d => d.State == Visibility.Hidden));
AddAssert("blueprint visible", () => this.ChildrenOfType<SliderPlacementBlueprint>().Single().IsPresent);
AddStep("end", () => InputManager.EndTouch(new Touch(TouchSource.Touch1, InputManager.CurrentState.Touch.GetTouchPosition(TouchSource.Touch1)!.Value)));
AddAssert("slider placed correctly", () =>
{
Expand All @@ -76,12 +132,19 @@ public void TestTouchInputAfterTouchingComposeArea()
});
}

private void tap(Drawable drawable) => tap(drawable.ScreenSpaceDrawQuad.Centre);
private void tap(Drawable drawable, Vector2 offset = default) => tap(drawable.ToScreenSpace(drawable.LayoutRectangle.Centre + offset));

private void tap(Vector2 position)
{
InputManager.BeginTouch(new Touch(TouchSource.Touch1, position));
hold(position);
InputManager.EndTouch(new Touch(TouchSource.Touch1, position));
}

private void hold(Drawable drawable, Vector2 offset = default) => hold(drawable.ToScreenSpace(drawable.LayoutRectangle.Centre + offset));

private void hold(Vector2 position)
{
InputManager.BeginTouch(new Touch(TouchSource.Touch1, position));
}
}
}
12 changes: 8 additions & 4 deletions osu.Game.Rulesets.Osu/Difficulty/Evaluators/AimEvaluator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Utils;
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
using osu.Game.Rulesets.Osu.Objects;

Expand Down Expand Up @@ -33,6 +34,9 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool with
var osuLastObj = (OsuDifficultyHitObject)current.Previous(0);
var osuLastLastObj = (OsuDifficultyHitObject)current.Previous(1);

const int radius = OsuDifficultyHitObject.NORMALISED_RADIUS;
const int diameter = OsuDifficultyHitObject.NORMALISED_DIAMETER;

// Calculate the velocity to the current hitobject, which starts with a base distance / time assuming the last object is a hitcircle.
double currDistance = withSliderTravelDistance ? osuCurrObj.LazyJumpFromEndDistance : osuCurrObj.JumpDistance;
double currVelocity = currDistance / osuCurrObj.StrainTime;
Expand Down Expand Up @@ -79,14 +83,14 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool with
wideAngleBonus = calcWideAngleBonus(currAngle);
acuteAngleBonus = calcAcuteAngleBonus(currAngle);

if (osuCurrObj.StrainTime > 100) // Only buff deltaTime exceeding 300 bpm 1/2.
if (DifficultyCalculationUtils.MillisecondsToBPM(osuCurrObj.StrainTime, 2) < 300) // Only buff deltaTime exceeding 300 bpm 1/2.
acuteAngleBonus = 0;
else
{
acuteAngleBonus *= calcAcuteAngleBonus(lastAngle) // Multiply by previous angle, we don't want to buff unless this is a wiggle type pattern.
* Math.Min(angleBonus, 125 / osuCurrObj.StrainTime) // The maximum velocity we buff is equal to 125 / strainTime
* Math.Min(angleBonus, diameter * 1.25 / osuCurrObj.StrainTime) // The maximum velocity we buff is equal to 125 / strainTime
* Math.Pow(Math.Sin(Math.PI / 2 * Math.Min(1, (100 - osuCurrObj.StrainTime) / 25)), 2) // scale buff from 150 bpm 1/4 to 200 bpm 1/4
* Math.Pow(Math.Sin(Math.PI / 2 * (Math.Clamp(currDistance, 50, 100) - 50) / 50), 2); // Buff distance exceeding 50 (radius) up to 100 (diameter).
* Math.Pow(Math.Sin(Math.PI / 2 * (Math.Clamp(currDistance, radius, diameter) - radius) / radius), 2); // Buff distance exceeding radius up to diameter.
}

// Penalize wide angles if they're repeated, reducing the penalty as the lastAngle gets more acute.
Expand All @@ -109,7 +113,7 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current, bool with
double distRatio = Math.Pow(Math.Sin(Math.PI / 2 * Math.Abs(prevVelocity - currVelocity) / Math.Max(prevVelocity, currVelocity)), 2);

// Reward for % distance up to 125 / strainTime for overlaps where velocity is still changing.
double overlapVelocityBuff = Math.Min(125 / Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime), Math.Abs(prevVelocity - currVelocity));
double overlapVelocityBuff = Math.Min(diameter * 1.25 / Math.Min(osuCurrObj.StrainTime, osuLastObj.StrainTime), Math.Abs(prevVelocity - currVelocity));

velocityChangeBonus = overlapVelocityBuff * distRatio;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Collections.Generic;
using System.Linq;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Utils;
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
using osu.Game.Rulesets.Osu.Objects;

Expand Down Expand Up @@ -120,7 +121,7 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current)
islandCount.Count++;

// repeated island (ex: triplet -> triplet)
double power = logistic(island.Delta, 2.75, 0.24, 14);
double power = DifficultyCalculationUtils.Logistic(island.Delta, maxValue: 2.75, multiplier: 0.24, midpointOffset: 58.33);
effectiveRatio *= Math.Min(3.0 / islandCount.Count, Math.Pow(1.0 / islandCount.Count, power));

islandCounts[countIndex] = (islandCount.Island, islandCount.Count);
Expand Down Expand Up @@ -172,8 +173,6 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current)
return Math.Sqrt(4 + rhythmComplexitySum * rhythm_overall_multiplier) / 2.0; // produces multiplier that can be applied to strain. range [1, infinity) (not really though)
}

private static double logistic(double x, double maxValue, double multiplier, double offset) => (maxValue / (1 + Math.Pow(Math.E, offset - (multiplier * x))));

private class Island : IEquatable<Island>
{
private readonly double deltaDifferenceEpsilon;
Expand Down
9 changes: 5 additions & 4 deletions osu.Game.Rulesets.Osu/Difficulty/Evaluators/SpeedEvaluator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@

using System;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Utils;
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
using osu.Game.Rulesets.Osu.Objects;

namespace osu.Game.Rulesets.Osu.Difficulty.Evaluators
{
public static class SpeedEvaluator
{
private const double single_spacing_threshold = 125; // 1.25 circles distance between centers
private const double min_speed_bonus = 75; // ~200BPM
private const double single_spacing_threshold = OsuDifficultyHitObject.NORMALISED_DIAMETER * 1.25; // 1.25 circles distance between centers
private const double min_speed_bonus = 200; // 200 BPM 1/4th
private const double speed_balancing_factor = 40;
private const double distance_multiplier = 0.94;

Expand Down Expand Up @@ -43,8 +44,8 @@ public static double EvaluateDifficultyOf(DifficultyHitObject current)
double speedBonus = 0.0;

// Add additional scaling bonus for streams/bursts higher than 200bpm
if (strainTime < min_speed_bonus)
speedBonus = 0.75 * Math.Pow((min_speed_bonus - strainTime) / speed_balancing_factor, 2);
if (DifficultyCalculationUtils.MillisecondsToBPM(strainTime) > min_speed_bonus)
speedBonus = 0.75 * Math.Pow((DifficultyCalculationUtils.BPMToMilliseconds(min_speed_bonus) - strainTime) / speed_balancing_factor, 2);

double travelDistance = osuPrevObj?.TravelDistance ?? 0;
double distance = travelDistance + osuCurrObj.MinimumJumpDistance;
Expand Down
4 changes: 2 additions & 2 deletions osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyCalculator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ protected override DifficultyAttributes CreateDifficultyAttributes(IBeatmap beat

double sliderFactor = aimRating > 0 ? Math.Min(aimRating, aimRatingNoSliders) / aimRating : 1;

double aimDifficultyStrainCount = ((OsuStrainSkill)skills[0]).CountDifficultStrains();
double speedDifficultyStrainCount = ((OsuStrainSkill)skills[2]).CountDifficultStrains();
double aimDifficultyStrainCount = ((OsuStrainSkill)skills[0]).CountTopWeightedStrains();
double speedDifficultyStrainCount = ((OsuStrainSkill)skills[2]).CountTopWeightedStrains();

if (mods.Any(m => m is OsuModTouchDevice))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ public class OsuDifficultyHitObject : DifficultyHitObject
/// </summary>
public const int NORMALISED_RADIUS = 50; // Change radius to 50 to make 100 the diameter. Easier for mental maths.

public const int NORMALISED_DIAMETER = NORMALISED_RADIUS * 2;

public const int MIN_DELTA_TIME = 25;

private const float maximum_slider_radius = NORMALISED_RADIUS * 2.4f;
Expand Down
1 change: 0 additions & 1 deletion osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ protected override double StrainValueAt(DifficultyHitObject current)
{
currentStrain *= strainDecay(current.DeltaTime);
currentStrain += AimEvaluator.EvaluateDifficultyOf(current, withSliders) * skillMultiplier;
ObjectStrains.Add(currentStrain);

return currentStrain;
}
Expand Down
15 changes: 0 additions & 15 deletions osu.Game.Rulesets.Osu/Difficulty/Skills/OsuStrainSkill.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ public abstract class OsuStrainSkill : StrainSkill
/// </summary>
protected virtual double ReducedStrainBaseline => 0.75;

protected List<double> ObjectStrains = new List<double>();
protected double Difficulty;

protected OsuStrainSkill(Mod[] mods)
Expand Down Expand Up @@ -60,20 +59,6 @@ public override double DifficultyValue()
return Difficulty;
}

/// <summary>
/// Returns the number of strains weighted against the top strain.
/// The result is scaled by clock rate as it affects the total number of strains.
/// </summary>
public double CountDifficultStrains()
{
if (Difficulty == 0)
return 0.0;

double consistentTopStrain = Difficulty / 10; // What would the top strain be if all strain values were identical
// Use a weighted sum of all strains. Constants are arbitrary and give nice values
return ObjectStrains.Sum(s => 1.1 / (1 + Math.Exp(-10 * (s / consistentTopStrain - 0.88))));
}

public static double DifficultyToPerformance(double difficulty) => Math.Pow(5.0 * Math.Max(1.0, difficulty / 0.0675) - 4.0, 3.0) / 100000.0;
}
}
1 change: 0 additions & 1 deletion osu.Game.Rulesets.Osu/Difficulty/Skills/Speed.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ protected override double StrainValueAt(DifficultyHitObject current)
currentRhythm = RhythmEvaluator.EvaluateDifficultyOf(current);

double totalStrain = currentStrain * currentRhythm;
ObjectStrains.Add(totalStrain);

return totalStrain;
}
Expand Down
Loading

0 comments on commit 60efb5e

Please sign in to comment.