1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-15 04:27:34 +08:00
osu-lazer/osu.Game.Rulesets.Catch/Difficulty/Skills/Movement.cs
Samuel Cattini-Schultz 5b2dcea8a8 Refactor to encapsulate strain logic into Skill class
As strains are an implementation detail of the current Skill calculations, it makes sense that strain related logic should be encapsulated within the Skill class.
2021-04-03 20:47:43 +11:00

95 lines
3.8 KiB
C#

// 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.Catch.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Difficulty.Skills;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Catch.Difficulty.Skills
{
public class Movement : Skill
{
private const float absolute_player_positioning_error = 16f;
private const float normalized_hitobject_radius = 41.0f;
private const double direction_change_bonus = 21.0;
protected override double SkillMultiplier => 900;
protected override double StrainDecayBase => 0.2;
protected override double DecayWeight => 0.94;
protected override int SectionLength => 750;
protected readonly float HalfCatcherWidth;
private float? lastPlayerPosition;
private float lastDistanceMoved;
private double lastStrainTime;
public Movement(Mod[] mods, float halfCatcherWidth)
: base(mods)
{
HalfCatcherWidth = halfCatcherWidth;
}
protected override double StrainValueOf(DifficultyHitObject current)
{
var catchCurrent = (CatchDifficultyHitObject)current;
lastPlayerPosition ??= catchCurrent.LastNormalizedPosition;
float playerPosition = Math.Clamp(
lastPlayerPosition.Value,
catchCurrent.NormalizedPosition - (normalized_hitobject_radius - absolute_player_positioning_error),
catchCurrent.NormalizedPosition + (normalized_hitobject_radius - absolute_player_positioning_error)
);
float distanceMoved = playerPosition - lastPlayerPosition.Value;
double weightedStrainTime = catchCurrent.StrainTime + 13 + (3 / catchCurrent.ClockRate);
double distanceAddition = (Math.Pow(Math.Abs(distanceMoved), 1.3) / 510);
double sqrtStrain = Math.Sqrt(weightedStrainTime);
double edgeDashBonus = 0;
// Direction change bonus.
if (Math.Abs(distanceMoved) > 0.1)
{
if (Math.Abs(lastDistanceMoved) > 0.1 && Math.Sign(distanceMoved) != Math.Sign(lastDistanceMoved))
{
double bonusFactor = Math.Min(50, Math.Abs(distanceMoved)) / 50;
double antiflowFactor = Math.Max(Math.Min(70, Math.Abs(lastDistanceMoved)) / 70, 0.38);
distanceAddition += direction_change_bonus / Math.Sqrt(lastStrainTime + 16) * bonusFactor * antiflowFactor * Math.Max(1 - Math.Pow(weightedStrainTime / 1000, 3), 0);
}
// Base bonus for every movement, giving some weight to streams.
distanceAddition += 12.5 * Math.Min(Math.Abs(distanceMoved), normalized_hitobject_radius * 2) / (normalized_hitobject_radius * 6) / sqrtStrain;
}
// Bonus for edge dashes.
if (catchCurrent.LastObject.DistanceToHyperDash <= 20.0f)
{
if (!catchCurrent.LastObject.HyperDash)
edgeDashBonus += 5.7;
else
{
// After a hyperdash we ARE in the correct position. Always!
playerPosition = catchCurrent.NormalizedPosition;
}
distanceAddition *= 1.0 + edgeDashBonus * ((20 - catchCurrent.LastObject.DistanceToHyperDash) / 20) * Math.Pow((Math.Min(catchCurrent.StrainTime * catchCurrent.ClockRate, 265) / 265), 1.5); // Edge Dashes are easier at lower ms values
}
lastPlayerPosition = playerPosition;
lastDistanceMoved = distanceMoved;
lastStrainTime = catchCurrent.StrainTime;
return distanceAddition / weightedStrainTime;
}
}
}