mirror of
https://github.com/ppy/osu
synced 2025-03-20 09:57:01 +00:00
Merge pull request #18208 from Lawtrohux/stamina-merge
Rewrite of the Stamina Skill within osu!taiko
This commit is contained in:
commit
0ca56835b0
@ -14,13 +14,13 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
{
|
{
|
||||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko";
|
protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko";
|
||||||
|
|
||||||
[TestCase(2.2420075288523802d, 200, "diffcalc-test")]
|
[TestCase(1.9971301024093662d, 200, "diffcalc-test")]
|
||||||
[TestCase(2.2420075288523802d, 200, "diffcalc-test-strong")]
|
[TestCase(1.9971301024093662d, 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.134084469440479d, 200, "diffcalc-test")]
|
[TestCase(3.1645810961313674d, 200, "diffcalc-test")]
|
||||||
[TestCase(3.134084469440479d, 200, "diffcalc-test-strong")]
|
[TestCase(3.1645810961313674d, 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());
|
||||||
|
|
||||||
|
@ -1,145 +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.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using osu.Game.Rulesets.Difficulty.Utils;
|
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Detects special hit object patterns which are easier to hit using special techniques
|
|
||||||
/// than normally assumed in the fully-alternating play style.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This component detects two basic types of patterns, leveraged by the following techniques:
|
|
||||||
/// <list>
|
|
||||||
/// <item>Rolling allows hitting patterns with quickly and regularly alternating notes with a single hand.</item>
|
|
||||||
/// <item>TL tapping makes hitting longer sequences of consecutive same-colour notes with little to no colour changes in-between.</item>
|
|
||||||
/// </list>
|
|
||||||
/// </remarks>
|
|
||||||
public class StaminaCheeseDetector
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The minimum number of consecutive objects with repeating patterns that can be classified as hittable using a roll.
|
|
||||||
/// </summary>
|
|
||||||
private const int roll_min_repetitions = 12;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The minimum number of consecutive objects with repeating patterns that can be classified as hittable using a TL tap.
|
|
||||||
/// </summary>
|
|
||||||
private const int tl_min_repetitions = 16;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The list of all <see cref="TaikoDifficultyHitObject"/>s in the map.
|
|
||||||
/// </summary>
|
|
||||||
private readonly List<TaikoDifficultyHitObject> hitObjects;
|
|
||||||
|
|
||||||
public StaminaCheeseDetector(List<TaikoDifficultyHitObject> hitObjects)
|
|
||||||
{
|
|
||||||
this.hitObjects = hitObjects;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Finds and marks all objects in <see cref="hitObjects"/> that special difficulty-reducing techiques apply to
|
|
||||||
/// with the <see cref="TaikoDifficultyHitObject.StaminaCheese"/> flag.
|
|
||||||
/// </summary>
|
|
||||||
public void FindCheese()
|
|
||||||
{
|
|
||||||
findRolls(3);
|
|
||||||
findRolls(4);
|
|
||||||
|
|
||||||
findTlTap(0, HitType.Rim);
|
|
||||||
findTlTap(1, HitType.Rim);
|
|
||||||
findTlTap(0, HitType.Centre);
|
|
||||||
findTlTap(1, HitType.Centre);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Finds and marks all sequences hittable using a roll.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="patternLength">The length of a single repeating pattern to consider (triplets/quadruplets).</param>
|
|
||||||
private void findRolls(int patternLength)
|
|
||||||
{
|
|
||||||
var history = new LimitedCapacityQueue<TaikoDifficultyHitObject>(2 * patternLength);
|
|
||||||
|
|
||||||
// for convenience, we're tracking the index of the item *before* our suspected repeat's start,
|
|
||||||
// as that index can be simply subtracted from the current index to get the number of elements in between
|
|
||||||
// without off-by-one errors
|
|
||||||
int indexBeforeLastRepeat = -1;
|
|
||||||
int lastMarkEnd = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < hitObjects.Count; i++)
|
|
||||||
{
|
|
||||||
history.Enqueue(hitObjects[i]);
|
|
||||||
if (!history.Full)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!containsPatternRepeat(history, patternLength))
|
|
||||||
{
|
|
||||||
// we're setting this up for the next iteration, hence the +1.
|
|
||||||
// right here this index will point at the queue's front (oldest item),
|
|
||||||
// but that item is about to be popped next loop with an enqueue.
|
|
||||||
indexBeforeLastRepeat = i - history.Count + 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int repeatedLength = i - indexBeforeLastRepeat;
|
|
||||||
if (repeatedLength < roll_min_repetitions)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
markObjectsAsCheese(Math.Max(lastMarkEnd, i - repeatedLength + 1), i);
|
|
||||||
lastMarkEnd = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether the objects stored in <paramref name="history"/> contain a repetition of a pattern of length <paramref name="patternLength"/>.
|
|
||||||
/// </summary>
|
|
||||||
private static bool containsPatternRepeat(LimitedCapacityQueue<TaikoDifficultyHitObject> history, int patternLength)
|
|
||||||
{
|
|
||||||
for (int j = 0; j < patternLength; j++)
|
|
||||||
{
|
|
||||||
if (history[j].HitType != history[j + patternLength].HitType)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Finds and marks all sequences hittable using a TL tap.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="parity">Whether sequences starting with an odd- (1) or even-indexed (0) hit object should be checked.</param>
|
|
||||||
/// <param name="type">The type of hit to check for TL taps.</param>
|
|
||||||
private void findTlTap(int parity, HitType type)
|
|
||||||
{
|
|
||||||
int tlLength = -2;
|
|
||||||
int lastMarkEnd = 0;
|
|
||||||
|
|
||||||
for (int i = parity; i < hitObjects.Count; i += 2)
|
|
||||||
{
|
|
||||||
if (hitObjects[i].HitType == type)
|
|
||||||
tlLength += 2;
|
|
||||||
else
|
|
||||||
tlLength = -2;
|
|
||||||
|
|
||||||
if (tlLength < tl_min_repetitions)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
markObjectsAsCheese(Math.Max(lastMarkEnd, i - tlLength + 1), i);
|
|
||||||
lastMarkEnd = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Marks all objects from <paramref name="start"/> to <paramref name="end"/> (inclusive) as <see cref="TaikoDifficultyHitObject.StaminaCheese"/>.
|
|
||||||
/// </summary>
|
|
||||||
private void markObjectsAsCheese(int start, int end)
|
|
||||||
{
|
|
||||||
for (int i = start; i <= end; i++)
|
|
||||||
hitObjects[i].StaminaCheese = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,42 @@
|
|||||||
|
// 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.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +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 System.Linq;
|
|
||||||
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.Preprocessing;
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
@ -22,39 +20,52 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
|||||||
protected override double SkillMultiplier => 1;
|
protected override double SkillMultiplier => 1;
|
||||||
protected override double StrainDecayBase => 0.4;
|
protected override double StrainDecayBase => 0.4;
|
||||||
|
|
||||||
/// <summary>
|
private readonly SingleKeyStamina[] centreKeyStamina =
|
||||||
/// Maximum number of entries to keep in <see cref="notePairDurationHistory"/>.
|
{
|
||||||
/// </summary>
|
new SingleKeyStamina(),
|
||||||
private const int max_history_length = 2;
|
new SingleKeyStamina()
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly SingleKeyStamina[] rimKeyStamina =
|
||||||
|
{
|
||||||
|
new SingleKeyStamina(),
|
||||||
|
new SingleKeyStamina()
|
||||||
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The index of the hand this <see cref="Stamina"/> instance is associated with.
|
/// Current index into <see cref="centreKeyStamina" /> for a centre hit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
private int centreKeyIndex;
|
||||||
/// The value of 0 indicates the left hand (full alternating gameplay starting with left hand is assumed).
|
|
||||||
/// This naturally translates onto index offsets of the objects in the map.
|
|
||||||
/// </remarks>
|
|
||||||
private readonly int hand;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stores the last <see cref="max_history_length"/> durations between notes hit with the hand indicated by <see cref="hand"/>.
|
/// Current index into <see cref="rimKeyStamina" /> for a rim hit.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly LimitedCapacityQueue<double> notePairDurationHistory = new LimitedCapacityQueue<double>(max_history_length);
|
private int rimKeyIndex;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stores the <see cref="DifficultyHitObject.DeltaTime"/> of the last object that was hit by the <i>other</i> hand.
|
|
||||||
/// </summary>
|
|
||||||
private double offhandObjectDuration = double.MaxValue;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a <see cref="Stamina"/> skill.
|
/// Creates a <see cref="Stamina"/> skill.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="mods">Mods for use in skill calculations.</param>
|
/// <param name="mods">Mods for use in skill calculations.</param>
|
||||||
/// <param name="rightHand">Whether this instance is performing calculations for the right hand.</param>
|
public Stamina(Mod[] mods)
|
||||||
public Stamina(Mod[] mods, bool rightHand)
|
|
||||||
: base(mods)
|
: base(mods)
|
||||||
{
|
{
|
||||||
hand = rightHand ? 1 : 0;
|
}
|
||||||
|
|
||||||
|
/// <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)
|
||||||
@ -65,52 +76,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
|||||||
}
|
}
|
||||||
|
|
||||||
TaikoDifficultyHitObject hitObject = (TaikoDifficultyHitObject)current;
|
TaikoDifficultyHitObject hitObject = (TaikoDifficultyHitObject)current;
|
||||||
|
return getNextSingleKeyStamina(hitObject).StrainValueOf(hitObject);
|
||||||
if (hitObject.ObjectIndex % 2 == hand)
|
|
||||||
{
|
|
||||||
double objectStrain = 1;
|
|
||||||
|
|
||||||
if (hitObject.ObjectIndex == 1)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
notePairDurationHistory.Enqueue(hitObject.DeltaTime + offhandObjectDuration);
|
|
||||||
|
|
||||||
double shortestRecentNote = notePairDurationHistory.Min();
|
|
||||||
objectStrain += speedBonus(shortestRecentNote);
|
|
||||||
|
|
||||||
if (hitObject.StaminaCheese)
|
|
||||||
objectStrain *= cheesePenalty(hitObject.DeltaTime + offhandObjectDuration);
|
|
||||||
|
|
||||||
return objectStrain;
|
|
||||||
}
|
|
||||||
|
|
||||||
offhandObjectDuration = hitObject.DeltaTime;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Applies a penalty for hit objects marked with <see cref="TaikoDifficultyHitObject.StaminaCheese"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="notePairDuration">The duration between the current and previous note hit using the hand indicated by <see cref="hand"/>.</param>
|
|
||||||
private double cheesePenalty(double notePairDuration)
|
|
||||||
{
|
|
||||||
if (notePairDuration > 125) return 1;
|
|
||||||
if (notePairDuration < 100) return 0.6;
|
|
||||||
|
|
||||||
return 0.6 + (notePairDuration - 100) * 0.016;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Applies a speed bonus dependent on the time since the last hit performed using this hand.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="notePairDuration">The duration between the current and previous note hit using the hand indicated by <see cref="hand"/>.</param>
|
|
||||||
private double speedBonus(double notePairDuration)
|
|
||||||
{
|
|
||||||
if (notePairDuration >= 200) return 0;
|
|
||||||
|
|
||||||
double bonus = 200 - notePairDuration;
|
|
||||||
bonus *= bonus;
|
|
||||||
return bonus / 100000;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
{
|
{
|
||||||
private const double rhythm_skill_multiplier = 0.014;
|
private const double rhythm_skill_multiplier = 0.014;
|
||||||
private const double colour_skill_multiplier = 0.01;
|
private const double colour_skill_multiplier = 0.01;
|
||||||
private const double stamina_skill_multiplier = 0.02;
|
private const double stamina_skill_multiplier = 0.021;
|
||||||
|
|
||||||
public TaikoDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
|
public TaikoDifficultyCalculator(IRulesetInfo ruleset, IWorkingBeatmap beatmap)
|
||||||
: base(ruleset, beatmap)
|
: base(ruleset, beatmap)
|
||||||
@ -33,8 +33,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
{
|
{
|
||||||
new Colour(mods),
|
new Colour(mods),
|
||||||
new Rhythm(mods),
|
new Rhythm(mods),
|
||||||
new Stamina(mods, true),
|
new Stamina(mods)
|
||||||
new Stamina(mods, false),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
|
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
|
||||||
@ -58,7 +57,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
new StaminaCheeseDetector(taikoDifficultyHitObjects).FindCheese();
|
|
||||||
return taikoDifficultyHitObjects;
|
return taikoDifficultyHitObjects;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,17 +67,22 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
|
|
||||||
var colour = (Colour)skills[0];
|
var colour = (Colour)skills[0];
|
||||||
var rhythm = (Rhythm)skills[1];
|
var rhythm = (Rhythm)skills[1];
|
||||||
var staminaRight = (Stamina)skills[2];
|
var stamina = (Stamina)skills[2];
|
||||||
var staminaLeft = (Stamina)skills[3];
|
|
||||||
|
|
||||||
double colourRating = colour.DifficultyValue() * colour_skill_multiplier;
|
double colourRating = colour.DifficultyValue() * colour_skill_multiplier;
|
||||||
double rhythmRating = rhythm.DifficultyValue() * rhythm_skill_multiplier;
|
double rhythmRating = rhythm.DifficultyValue() * rhythm_skill_multiplier;
|
||||||
double staminaRating = (staminaRight.DifficultyValue() + staminaLeft.DifficultyValue()) * stamina_skill_multiplier;
|
double staminaRating = stamina.DifficultyValue() * stamina_skill_multiplier;
|
||||||
|
|
||||||
double staminaPenalty = simpleColourPenalty(staminaRating, colourRating);
|
double staminaPenalty = simpleColourPenalty(staminaRating, colourRating);
|
||||||
staminaRating *= staminaPenalty;
|
staminaRating *= staminaPenalty;
|
||||||
|
|
||||||
double combinedRating = locallyCombinedDifficulty(colour, rhythm, staminaRight, staminaLeft, staminaPenalty);
|
//TODO : This is a temporary fix for the stamina rating of converts, due to their low colour variance.
|
||||||
|
if (beatmap.BeatmapInfo.Ruleset.OnlineID == 0 && colourRating < 0.05)
|
||||||
|
{
|
||||||
|
staminaPenalty *= 0.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
double combinedRating = locallyCombinedDifficulty(colour, rhythm, stamina, staminaPenalty);
|
||||||
double separatedRating = norm(1.5, colourRating, rhythmRating, staminaRating);
|
double separatedRating = norm(1.5, colourRating, rhythmRating, staminaRating);
|
||||||
double starRating = 1.4 * separatedRating + 0.5 * combinedRating;
|
double starRating = 1.4 * separatedRating + 0.5 * combinedRating;
|
||||||
starRating = rescale(starRating);
|
starRating = rescale(starRating);
|
||||||
@ -127,20 +130,19 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
/// For each section, the peak strains of all separate skills are combined into a single peak strain for the section.
|
/// 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).
|
/// The resulting partial rating of the beatmap is a weighted sum of the combined peaks (higher peaks are weighted more).
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
private double locallyCombinedDifficulty(Colour colour, Rhythm rhythm, Stamina staminaRight, Stamina staminaLeft, double staminaPenalty)
|
private double locallyCombinedDifficulty(Colour colour, Rhythm rhythm, Stamina stamina, double staminaPenalty)
|
||||||
{
|
{
|
||||||
List<double> peaks = new List<double>();
|
List<double> peaks = new List<double>();
|
||||||
|
|
||||||
var colourPeaks = colour.GetCurrentStrainPeaks().ToList();
|
var colourPeaks = colour.GetCurrentStrainPeaks().ToList();
|
||||||
var rhythmPeaks = rhythm.GetCurrentStrainPeaks().ToList();
|
var rhythmPeaks = rhythm.GetCurrentStrainPeaks().ToList();
|
||||||
var staminaRightPeaks = staminaRight.GetCurrentStrainPeaks().ToList();
|
var staminaPeaks = stamina.GetCurrentStrainPeaks().ToList();
|
||||||
var staminaLeftPeaks = staminaLeft.GetCurrentStrainPeaks().ToList();
|
|
||||||
|
|
||||||
for (int i = 0; i < colourPeaks.Count; i++)
|
for (int i = 0; i < colourPeaks.Count; i++)
|
||||||
{
|
{
|
||||||
double colourPeak = colourPeaks[i] * colour_skill_multiplier;
|
double colourPeak = colourPeaks[i] * colour_skill_multiplier;
|
||||||
double rhythmPeak = rhythmPeaks[i] * rhythm_skill_multiplier;
|
double rhythmPeak = rhythmPeaks[i] * rhythm_skill_multiplier;
|
||||||
double staminaPeak = (staminaRightPeaks[i] + staminaLeftPeaks[i]) * stamina_skill_multiplier * staminaPenalty;
|
double staminaPeak = staminaPeaks[i] * stamina_skill_multiplier * staminaPenalty;
|
||||||
|
|
||||||
double peak = norm(2, colourPeak, rhythmPeak, staminaPeak);
|
double peak = norm(2, colourPeak, rhythmPeak, staminaPeak);
|
||||||
|
|
||||||
|
@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
|
|
||||||
private double computeDifficultyValue(ScoreInfo score, TaikoDifficultyAttributes attributes)
|
private double computeDifficultyValue(ScoreInfo score, TaikoDifficultyAttributes attributes)
|
||||||
{
|
{
|
||||||
double difficultyValue = Math.Pow(5.0 * Math.Max(1.0, attributes.StarRating / 0.0075) - 4.0, 2.0) / 100000.0;
|
double difficultyValue = Math.Pow(5 * Math.Max(1.0, attributes.StarRating / 0.175) - 4.0, 2.25) / 450.0;
|
||||||
|
|
||||||
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;
|
||||||
|
Loading…
Reference in New Issue
Block a user