osu/osu.Game.Rulesets.Osu/Difficulty/Skills/Aim.cs

141 lines
7.6 KiB
C#
Raw Normal View History

// 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.
2018-04-13 09:19:50 +00:00
using System;
using osu.Game.Rulesets.Difficulty.Preprocessing;
using osu.Game.Rulesets.Mods;
2018-05-15 08:36:29 +00:00
using osu.Game.Rulesets.Osu.Difficulty.Preprocessing;
2019-02-18 05:58:33 +00:00
using osu.Game.Rulesets.Osu.Objects;
2021-09-25 03:02:33 +00:00
using osu.Framework.Utils;
2018-04-13 09:19:50 +00:00
2018-05-15 08:36:29 +00:00
namespace osu.Game.Rulesets.Osu.Difficulty.Skills
2018-04-13 09:19:50 +00:00
{
/// <summary>
/// Represents the skill required to correctly aim at every object in the map with a uniform CircleSize and normalized distances.
/// </summary>
2021-06-14 17:18:49 +00:00
public class Aim : OsuStrainSkill
2018-04-13 09:19:50 +00:00
{
public Aim(Mod[] mods)
: base(mods)
{
}
2021-09-25 03:02:33 +00:00
protected override int HistoryLength => 2;
2021-10-13 15:41:24 +00:00
private const double wide_angle_multiplier = 1.5;
private const double acute_angle_multiplier = 1.5;
2021-10-21 21:30:00 +00:00
private const double slider_multiplier = 1.75;
2021-10-21 17:07:56 +00:00
private const double vel_change_multiplier = 0.75;
2018-04-13 09:19:50 +00:00
2021-08-17 13:39:18 +00:00
private double currentStrain = 1;
2018-04-13 09:19:50 +00:00
2021-10-13 15:41:24 +00:00
private double skillMultiplier => 23.25;
2021-08-17 13:39:18 +00:00
private double strainDecayBase => 0.15;
2021-09-25 03:02:33 +00:00
private double strainValueOf(DifficultyHitObject current)
2018-12-08 06:01:26 +00:00
{
if (current.BaseObject is Spinner || Previous.Count <= 1 || Previous[0].BaseObject is Spinner)
2019-02-18 05:58:33 +00:00
return 0;
2021-09-25 03:02:33 +00:00
var osuCurrObj = (OsuDifficultyHitObject)current;
var osuPrevObj = (OsuDifficultyHitObject)Previous[0];
var osuLastObj = (OsuDifficultyHitObject)Previous[1];
2018-12-24 03:41:04 +00:00
2021-10-22 17:17:19 +00:00
double currVelocity = (osuCurrObj.JumpDistance + osuCurrObj.TravelDistance) / osuCurrObj.StrainTime; // Start with the base distance / time
2019-01-29 07:35:20 +00:00
2021-10-13 15:41:24 +00:00
if (osuPrevObj.BaseObject is Slider) // If object is a slider
{
double movementVelocity = osuCurrObj.MovementDistance / osuCurrObj.MovementTime; // calculate the movement velocity from slider end to next note
double travelVelocity = osuCurrObj.TravelDistance / osuCurrObj.TravelTime; // calculate the slider velocity from slider head to lazy end.
currVelocity = Math.Max(currVelocity, movementVelocity + travelVelocity); // take the larger total combined velocity.
}
2021-10-22 17:17:19 +00:00
double prevVelocity = (osuPrevObj.JumpDistance + osuPrevObj.TravelDistance) / osuPrevObj.StrainTime; // do the same for the previous velocity.
2019-01-29 07:35:20 +00:00
2021-10-13 15:41:24 +00:00
if (osuLastObj.BaseObject is Slider)
2021-09-25 03:02:33 +00:00
{
2021-10-13 15:41:24 +00:00
double movementVelocity = osuPrevObj.MovementDistance / osuPrevObj.MovementTime;
double travelVelocity = osuPrevObj.TravelDistance / osuPrevObj.TravelTime;
prevVelocity = Math.Max(prevVelocity, movementVelocity + travelVelocity);
}
2021-10-13 15:41:24 +00:00
double angleBonus = 0;
double sliderBonus = 0;
2021-10-21 17:07:56 +00:00
double velChangeBonus = 0;
2019-01-29 07:35:20 +00:00
2021-10-13 15:41:24 +00:00
double aimStrain = currVelocity; // Start strain with regular velocity.
2021-10-13 15:41:24 +00:00
if (Precision.AlmostEquals(osuCurrObj.StrainTime, osuPrevObj.StrainTime, 10)) // If rhythms are the same.
2021-09-25 03:02:33 +00:00
{
2021-10-13 15:41:24 +00:00
if (osuCurrObj.Angle != null && osuPrevObj.Angle != null)
2018-12-21 13:52:27 +00:00
{
2021-10-13 15:41:24 +00:00
double currAngle = osuCurrObj.Angle.Value;
double prevAngle = osuPrevObj.Angle.Value;
2021-09-25 03:02:33 +00:00
// Rewarding angles, take the smaller velocity as base.
2021-10-13 15:41:24 +00:00
angleBonus = Math.Min(currVelocity, prevVelocity);
2021-10-13 15:41:24 +00:00
double wideAngleBonus = calcWideAngleBonus(currAngle);
double acuteAngleBonus = calcAcuteAngleBonus(currAngle);
2021-09-25 03:02:33 +00:00
2021-10-13 15:41:24 +00:00
if (osuCurrObj.StrainTime > 100) // Only buff deltaTime exceeding 300 bpm 1/2.
2021-09-25 03:02:33 +00:00
acuteAngleBonus = 0;
else
2021-10-13 15:41:24 +00:00
acuteAngleBonus *= calcAcuteAngleBonus(prevAngle) // 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.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.Min(100, osuCurrObj.JumpDistance) - 50) / 50), 2); // Buff distance exceeding 50 (radius) up to 100 (diameter).
2021-09-25 03:02:33 +00:00
2021-10-13 15:41:24 +00:00
wideAngleBonus *= angleBonus * (1 - Math.Pow(calcWideAngleBonus(prevAngle), 3)); // Penalize wide angles if they're repeated, reducing the penalty as the prevAngle gets more acute.
2021-09-25 03:02:33 +00:00
2021-10-13 15:41:24 +00:00
angleBonus = acuteAngleBonus * acute_angle_multiplier + wideAngleBonus * wide_angle_multiplier; // add the anglebuffs together.
}
2018-12-21 05:52:43 +00:00
}
2021-09-25 03:02:33 +00:00
2021-10-21 17:07:56 +00:00
if (Math.Max(prevVelocity, currVelocity) != 0)
2021-09-25 03:02:33 +00:00
{
2021-10-22 17:18:34 +00:00
prevVelocity = (osuPrevObj.JumpDistance + osuPrevObj.TravelDistance) / osuPrevObj.StrainTime; // We want to use the average velocity when awarding differences, not necessarily combined.
currVelocity = (osuCurrObj.JumpDistance + osuCurrObj.TravelDistance) / osuCurrObj.StrainTime;
2021-10-21 17:07:56 +00:00
velChangeBonus = Math.Max(Math.Abs(prevVelocity - currVelocity) // reward for % distance slowed down compared to previous, paying attention to not award overlap
* Math.Pow(Math.Sin(Math.PI / 2 * Math.Min(1, osuCurrObj.JumpDistance / 100)), 2) // do not award overlap
* Math.Pow(Math.Sin(Math.PI / 2 * Math.Abs(prevVelocity - currVelocity) / Math.Max(prevVelocity, currVelocity)), 2), // scale with ratio of difference compared to max
Math.Min(125 / Math.Min(osuCurrObj.StrainTime, osuPrevObj.StrainTime), Math.Abs(prevVelocity - currVelocity)) // reward for % distance up to 125 / strainTime for overlaps where velocity is still changing.
* Math.Pow(Math.Sin(Math.PI / 2 * Math.Abs(prevVelocity - currVelocity) / Math.Max(prevVelocity, currVelocity)), 2)); // scale with ratio of difference compared to max
2018-12-21 05:52:43 +00:00
2021-10-21 17:07:56 +00:00
velChangeBonus *= Math.Pow(Math.Min(osuCurrObj.StrainTime, osuPrevObj.StrainTime) / Math.Max(osuCurrObj.StrainTime, osuPrevObj.StrainTime), 2); // penalize for rhythm changes.
}
2019-01-29 07:35:20 +00:00
2021-10-21 16:08:35 +00:00
if (osuCurrObj.TravelTime != 0)
2021-09-25 03:04:22 +00:00
{
sliderBonus = osuCurrObj.TravelDistance / osuCurrObj.TravelTime; // add some slider rewards
2021-09-25 03:02:33 +00:00
}
2021-10-21 17:07:56 +00:00
aimStrain += Math.Max(angleBonus, velChangeBonus * vel_change_multiplier); // Add in angle bonus or velchange bonus, whichever is larger.
aimStrain += sliderBonus * slider_multiplier; // Add in additional slider velocity.
2021-09-25 03:02:33 +00:00
return aimStrain;
}
2021-10-13 15:41:24 +00:00
private double calcWideAngleBonus(double angle) => Math.Pow(Math.Sin(3.0 / 4 * (Math.Min(5.0 / 6 * Math.PI, Math.Max(Math.PI / 6, angle)) - Math.PI / 6)), 2);
2021-09-25 03:02:33 +00:00
2021-10-13 15:41:24 +00:00
private double calcAcuteAngleBonus(double angle) => 1 - calcWideAngleBonus(angle);
private double applyDiminishingExp(double val) => Math.Pow(val, 0.99);
2021-08-17 13:39:18 +00:00
private double strainDecay(double ms) => Math.Pow(strainDecayBase, ms / 1000);
protected override double CalculateInitialStrain(double time) => currentStrain * strainDecay(time - Previous[0].StartTime);
2021-08-17 13:39:18 +00:00
protected override double StrainValueAt(DifficultyHitObject current)
2021-09-25 03:02:33 +00:00
{
2021-08-17 13:39:18 +00:00
currentStrain *= strainDecay(current.DeltaTime);
currentStrain += strainValueOf(current) * skillMultiplier;
2021-09-25 03:02:33 +00:00
2021-08-17 13:39:18 +00:00
return currentStrain;
2021-09-25 03:02:33 +00:00
}
2018-04-13 09:19:50 +00:00
}
}