mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 08:27:49 +08:00
Implement new difficulty calculator for Rulesets.Taiko
This commit is contained in:
parent
a8faa942a6
commit
25d85b6eb4
@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
|||||||
public void Test(double expected, string name)
|
public void Test(double expected, string name)
|
||||||
=> base.Test(expected, name);
|
=> base.Test(expected, name);
|
||||||
|
|
||||||
protected override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoLegacyDifficultyCalculator(new TaikoRuleset(), beatmap);
|
protected override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoDifficultyCalculator(new TaikoRuleset(), beatmap);
|
||||||
|
|
||||||
protected override Ruleset CreateRuleset() => new TaikoRuleset();
|
protected override Ruleset CreateRuleset() => new TaikoRuleset();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,20 @@
|
|||||||
|
// 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.Objects;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Difficulty.Preprocessing
|
||||||
|
{
|
||||||
|
public class TaikoDifficultyHitObject : DifficultyHitObject
|
||||||
|
{
|
||||||
|
public readonly bool HasTypeChange;
|
||||||
|
|
||||||
|
public TaikoDifficultyHitObject(HitObject hitObject, HitObject lastObject, double timeRate)
|
||||||
|
: base(hitObject, lastObject, timeRate)
|
||||||
|
{
|
||||||
|
HasTypeChange = lastObject is RimHit != hitObject is RimHit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
90
osu.Game.Rulesets.Taiko/Difficulty/Skills/Strain.cs
Normal file
90
osu.Game.Rulesets.Taiko/Difficulty/Skills/Strain.cs
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
// 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.Difficulty.Preprocessing;
|
||||||
|
using osu.Game.Rulesets.Difficulty.Skills;
|
||||||
|
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
|
||||||
|
{
|
||||||
|
public class Strain : Skill
|
||||||
|
{
|
||||||
|
private const double rhythm_change_base_threshold = 0.2;
|
||||||
|
private const double rhythm_change_base = 2.0;
|
||||||
|
|
||||||
|
protected override double SkillMultiplier => 1;
|
||||||
|
protected override double StrainDecayBase => 0.3;
|
||||||
|
|
||||||
|
private ColourSwitch lastColourSwitch = ColourSwitch.None;
|
||||||
|
|
||||||
|
private int sameTypeCount;
|
||||||
|
|
||||||
|
protected override double StrainValueOf(DifficultyHitObject current)
|
||||||
|
{
|
||||||
|
double addition = 1;
|
||||||
|
|
||||||
|
// We get an extra addition if we are not a slider or spinner
|
||||||
|
if (Previous[0].BaseObject is Hit && current.BaseObject is Hit && current.BaseObject.StartTime - Previous[0].BaseObject.StartTime < 1000)
|
||||||
|
{
|
||||||
|
if (hasRhythmChange(current))
|
||||||
|
addition += 1;
|
||||||
|
|
||||||
|
if (hasColourChange(current))
|
||||||
|
addition += 0.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
double additionFactor = 1;
|
||||||
|
|
||||||
|
// Scale the addition factor linearly from 0.4 to 1 for DeltaTime from 0 to 50
|
||||||
|
if (current.DeltaTime < 50)
|
||||||
|
additionFactor = 0.4 + 0.6 * current.DeltaTime / 50;
|
||||||
|
|
||||||
|
return additionFactor * addition;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool hasRhythmChange(DifficultyHitObject current)
|
||||||
|
{
|
||||||
|
// We don't want a division by zero if some random mapper decides to put two HitObjects at the same time.
|
||||||
|
if (current.DeltaTime == 0 || Previous[0].DeltaTime == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
double timeElapsedRatio = Math.Max(Previous[0].DeltaTime / current.DeltaTime, current.DeltaTime / Previous[0].DeltaTime);
|
||||||
|
|
||||||
|
if (timeElapsedRatio >= 8)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
double difference = Math.Log(timeElapsedRatio, rhythm_change_base) % 1.0;
|
||||||
|
|
||||||
|
return difference > rhythm_change_base_threshold && difference < 1 - rhythm_change_base_threshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool hasColourChange(DifficultyHitObject current)
|
||||||
|
{
|
||||||
|
var taikoCurrent = (TaikoDifficultyHitObject)current;
|
||||||
|
|
||||||
|
if (!taikoCurrent.HasTypeChange)
|
||||||
|
{
|
||||||
|
sameTypeCount++;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var oldColourSwitch = lastColourSwitch;
|
||||||
|
var newColourSwitch = sameTypeCount % 2 == 0 ? ColourSwitch.Even : ColourSwitch.Odd;
|
||||||
|
|
||||||
|
lastColourSwitch = newColourSwitch;
|
||||||
|
sameTypeCount = 1;
|
||||||
|
|
||||||
|
// We only want a bonus if the parity of the color switch changes
|
||||||
|
return oldColourSwitch != ColourSwitch.None && oldColourSwitch != newColourSwitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum ColourSwitch
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Even,
|
||||||
|
Odd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -11,8 +11,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
|
|||||||
public double GreatHitWindow;
|
public double GreatHitWindow;
|
||||||
public int MaxCombo;
|
public int MaxCombo;
|
||||||
|
|
||||||
public TaikoDifficultyAttributes(Mod[] mods, double starRating)
|
public TaikoDifficultyAttributes(Mod[] mods)
|
||||||
: base(mods, starRating)
|
: base(mods)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,56 @@
|
|||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Rulesets.Difficulty;
|
||||||
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
|
using osu.Game.Rulesets.Difficulty.Skills;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Taiko.Difficulty.Preprocessing;
|
||||||
|
using osu.Game.Rulesets.Taiko.Difficulty.Skills;
|
||||||
|
using osu.Game.Rulesets.Taiko.Mods;
|
||||||
|
using osu.Game.Rulesets.Taiko.Objects;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Taiko.Difficulty
|
||||||
|
{
|
||||||
|
public class TaikoDifficultyCalculator : DifficultyCalculator
|
||||||
|
{
|
||||||
|
private const double star_scaling_factor = 0.018;
|
||||||
|
|
||||||
|
public TaikoDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
|
||||||
|
: base(ruleset, beatmap)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void PopulateAttributes(DifficultyAttributes attributes, IBeatmap beatmap, Skill[] skills, double timeRate)
|
||||||
|
{
|
||||||
|
var taikoAttributes = (TaikoDifficultyAttributes)attributes;
|
||||||
|
|
||||||
|
taikoAttributes.StarRating = skills.Single().DifficultyValue() * star_scaling_factor;
|
||||||
|
|
||||||
|
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future
|
||||||
|
taikoAttributes.GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate;
|
||||||
|
taikoAttributes.MaxCombo = beatmap.HitObjects.Count(h => h is Hit);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IEnumerable<DifficultyHitObject> CreateDifficultyHitObjects(IBeatmap beatmap, double timeRate)
|
||||||
|
{
|
||||||
|
for (int i = 1; i < beatmap.HitObjects.Count; i++)
|
||||||
|
yield return new TaikoDifficultyHitObject(beatmap.HitObjects[i], beatmap.HitObjects[i - 1], timeRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Skill[] CreateSkills() => new Skill[] { new Strain() };
|
||||||
|
|
||||||
|
protected override DifficultyAttributes CreateDifficultyAttributes(Mod[] mods) => new TaikoDifficultyAttributes(mods);
|
||||||
|
|
||||||
|
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
|
||||||
|
{
|
||||||
|
new TaikoModDoubleTime(),
|
||||||
|
new TaikoModHalfTime(),
|
||||||
|
new TaikoModEasy(),
|
||||||
|
new TaikoModHardRock(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -1,144 +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 System.Linq;
|
|
||||||
using osu.Game.Beatmaps;
|
|
||||||
using osu.Game.Rulesets.Difficulty;
|
|
||||||
using osu.Game.Rulesets.Mods;
|
|
||||||
using osu.Game.Rulesets.Taiko.Mods;
|
|
||||||
using osu.Game.Rulesets.Taiko.Objects;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Difficulty
|
|
||||||
{
|
|
||||||
internal class TaikoLegacyDifficultyCalculator : LegacyDifficultyCalculator
|
|
||||||
{
|
|
||||||
private const double star_scaling_factor = 0.04125;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size STRAIN_STEP.
|
|
||||||
/// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain.
|
|
||||||
/// The higher this value, the less strains there will be, indirectly giving long beatmaps an advantage.
|
|
||||||
/// </summary>
|
|
||||||
private const double strain_step = 400;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The weighting of each strain value decays to this number * it's previous value
|
|
||||||
/// </summary>
|
|
||||||
private const double decay_weight = 0.9;
|
|
||||||
|
|
||||||
public TaikoLegacyDifficultyCalculator(Ruleset ruleset, WorkingBeatmap beatmap)
|
|
||||||
: base(ruleset, beatmap)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override DifficultyAttributes Calculate(IBeatmap beatmap, Mod[] mods, double timeRate)
|
|
||||||
{
|
|
||||||
if (!beatmap.HitObjects.Any())
|
|
||||||
return new TaikoDifficultyAttributes(mods, 0);
|
|
||||||
|
|
||||||
var difficultyHitObjects = new List<TaikoHitObjectDifficulty>();
|
|
||||||
|
|
||||||
foreach (var hitObject in beatmap.HitObjects)
|
|
||||||
difficultyHitObjects.Add(new TaikoHitObjectDifficulty((TaikoHitObject)hitObject));
|
|
||||||
|
|
||||||
// Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure.
|
|
||||||
difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
|
|
||||||
|
|
||||||
if (!calculateStrainValues(difficultyHitObjects, timeRate))
|
|
||||||
return new TaikoDifficultyAttributes(mods, 0);
|
|
||||||
|
|
||||||
double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor;
|
|
||||||
|
|
||||||
return new TaikoDifficultyAttributes(mods, starRating)
|
|
||||||
{
|
|
||||||
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be remoevd in the future
|
|
||||||
GreatHitWindow = (int)(beatmap.HitObjects.First().HitWindows.Great / 2) / timeRate,
|
|
||||||
MaxCombo = beatmap.HitObjects.Count(h => h is Hit)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool calculateStrainValues(List<TaikoHitObjectDifficulty> objects, double timeRate)
|
|
||||||
{
|
|
||||||
// Traverse hitObjects in pairs to calculate the strain value of NextHitObject from the strain value of CurrentHitObject and environment.
|
|
||||||
using (var hitObjectsEnumerator = objects.GetEnumerator())
|
|
||||||
{
|
|
||||||
if (!hitObjectsEnumerator.MoveNext()) return false;
|
|
||||||
|
|
||||||
TaikoHitObjectDifficulty current = hitObjectsEnumerator.Current;
|
|
||||||
|
|
||||||
// First hitObject starts at strain 1. 1 is the default for strain values, so we don't need to set it here. See DifficultyHitObject.
|
|
||||||
while (hitObjectsEnumerator.MoveNext())
|
|
||||||
{
|
|
||||||
var next = hitObjectsEnumerator.Current;
|
|
||||||
next?.CalculateStrains(current, timeRate);
|
|
||||||
current = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private double calculateDifficulty(List<TaikoHitObjectDifficulty> objects, double timeRate)
|
|
||||||
{
|
|
||||||
double actualStrainStep = strain_step * timeRate;
|
|
||||||
|
|
||||||
// Find the highest strain value within each strain step
|
|
||||||
List<double> highestStrains = new List<double>();
|
|
||||||
double intervalEndTime = actualStrainStep;
|
|
||||||
double maximumStrain = 0; // We need to keep track of the maximum strain in the current interval
|
|
||||||
|
|
||||||
TaikoHitObjectDifficulty previousHitObject = null;
|
|
||||||
foreach (var hitObject in objects)
|
|
||||||
{
|
|
||||||
// While we are beyond the current interval push the currently available maximum to our strain list
|
|
||||||
while (hitObject.BaseHitObject.StartTime > intervalEndTime)
|
|
||||||
{
|
|
||||||
highestStrains.Add(maximumStrain);
|
|
||||||
|
|
||||||
// The maximum strain of the next interval is not zero by default! We need to take the last hitObject we encountered, take its strain and apply the decay
|
|
||||||
// until the beginning of the next interval.
|
|
||||||
if (previousHitObject == null)
|
|
||||||
{
|
|
||||||
maximumStrain = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
double decay = Math.Pow(TaikoHitObjectDifficulty.DECAY_BASE, (intervalEndTime - previousHitObject.BaseHitObject.StartTime) / 1000);
|
|
||||||
maximumStrain = previousHitObject.Strain * decay;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Go to the next time interval
|
|
||||||
intervalEndTime += actualStrainStep;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Obtain maximum strain
|
|
||||||
maximumStrain = Math.Max(hitObject.Strain, maximumStrain);
|
|
||||||
|
|
||||||
previousHitObject = hitObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the weighted sum over the highest strains for each interval
|
|
||||||
double difficulty = 0;
|
|
||||||
double weight = 1;
|
|
||||||
highestStrains.Sort((a, b) => b.CompareTo(a)); // Sort from highest to lowest strain.
|
|
||||||
|
|
||||||
foreach (double strain in highestStrains)
|
|
||||||
{
|
|
||||||
difficulty += weight * strain;
|
|
||||||
weight *= decay_weight;
|
|
||||||
}
|
|
||||||
|
|
||||||
return difficulty;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override Mod[] DifficultyAdjustmentMods => new Mod[]
|
|
||||||
{
|
|
||||||
new TaikoModDoubleTime(),
|
|
||||||
new TaikoModHalfTime(),
|
|
||||||
new TaikoModEasy(),
|
|
||||||
new TaikoModHardRock(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,127 +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;
|
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Objects
|
|
||||||
{
|
|
||||||
internal class TaikoHitObjectDifficulty
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Factor by how much individual / overall strain decays per second.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// These values are results of tweaking a lot and taking into account general feedback.
|
|
||||||
/// </remarks>
|
|
||||||
internal const double DECAY_BASE = 0.30;
|
|
||||||
|
|
||||||
private const double type_change_bonus = 0.75;
|
|
||||||
private const double rhythm_change_bonus = 1.0;
|
|
||||||
private const double rhythm_change_base_threshold = 0.2;
|
|
||||||
private const double rhythm_change_base = 2.0;
|
|
||||||
|
|
||||||
internal TaikoHitObject BaseHitObject;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Measures note density in a way
|
|
||||||
/// </summary>
|
|
||||||
internal double Strain = 1;
|
|
||||||
|
|
||||||
private double timeElapsed;
|
|
||||||
private int sameTypeSince = 1;
|
|
||||||
|
|
||||||
private bool isRim => BaseHitObject is RimHit;
|
|
||||||
|
|
||||||
public TaikoHitObjectDifficulty(TaikoHitObject baseHitObject)
|
|
||||||
{
|
|
||||||
BaseHitObject = baseHitObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void CalculateStrains(TaikoHitObjectDifficulty previousHitObject, double timeRate)
|
|
||||||
{
|
|
||||||
// Rather simple, but more specialized things are inherently inaccurate due to the big difference playstyles and opinions make.
|
|
||||||
// See Taiko feedback thread.
|
|
||||||
timeElapsed = (BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime) / timeRate;
|
|
||||||
double decay = Math.Pow(DECAY_BASE, timeElapsed / 1000);
|
|
||||||
|
|
||||||
double addition = 1;
|
|
||||||
|
|
||||||
// Only if we are no slider or spinner we get an extra addition
|
|
||||||
if (previousHitObject.BaseHitObject is Hit && BaseHitObject is Hit
|
|
||||||
&& BaseHitObject.StartTime - previousHitObject.BaseHitObject.StartTime < 1000) // And we only want to check out hitobjects which aren't so far in the past
|
|
||||||
{
|
|
||||||
addition += typeChangeAddition(previousHitObject);
|
|
||||||
addition += rhythmChangeAddition(previousHitObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
double additionFactor = 1.0;
|
|
||||||
// Scale AdditionFactor linearly from 0.4 to 1 for TimeElapsed from 0 to 50
|
|
||||||
if (timeElapsed < 50.0)
|
|
||||||
additionFactor = 0.4 + 0.6 * timeElapsed / 50.0;
|
|
||||||
|
|
||||||
Strain = previousHitObject.Strain * decay + addition * additionFactor;
|
|
||||||
}
|
|
||||||
|
|
||||||
private TypeSwitch lastTypeSwitchEven = TypeSwitch.None;
|
|
||||||
private double typeChangeAddition(TaikoHitObjectDifficulty previousHitObject)
|
|
||||||
{
|
|
||||||
// If we don't have the same hit type, trigger a type change!
|
|
||||||
if (previousHitObject.isRim != isRim)
|
|
||||||
{
|
|
||||||
lastTypeSwitchEven = previousHitObject.sameTypeSince % 2 == 0 ? TypeSwitch.Even : TypeSwitch.Odd;
|
|
||||||
|
|
||||||
// We only want a bonus if the parity of the type switch changes!
|
|
||||||
switch (previousHitObject.lastTypeSwitchEven)
|
|
||||||
{
|
|
||||||
case TypeSwitch.Even:
|
|
||||||
if (lastTypeSwitchEven == TypeSwitch.Odd)
|
|
||||||
return type_change_bonus;
|
|
||||||
break;
|
|
||||||
case TypeSwitch.Odd:
|
|
||||||
if (lastTypeSwitchEven == TypeSwitch.Even)
|
|
||||||
return type_change_bonus;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// No type change? Increment counter and keep track of last type switch
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lastTypeSwitchEven = previousHitObject.lastTypeSwitchEven;
|
|
||||||
sameTypeSince = previousHitObject.sameTypeSince + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private double rhythmChangeAddition(TaikoHitObjectDifficulty previousHitObject)
|
|
||||||
{
|
|
||||||
// We don't want a division by zero if some random mapper decides to put 2 HitObjects at the same time.
|
|
||||||
if (timeElapsed == 0 || previousHitObject.timeElapsed == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
double timeElapsedRatio = Math.Max(previousHitObject.timeElapsed / timeElapsed, timeElapsed / previousHitObject.timeElapsed);
|
|
||||||
|
|
||||||
if (timeElapsedRatio >= 8)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
double difference = Math.Log(timeElapsedRatio, rhythm_change_base) % 1.0;
|
|
||||||
|
|
||||||
if (isWithinChangeThreshold(difference))
|
|
||||||
return rhythm_change_bonus;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool isWithinChangeThreshold(double value)
|
|
||||||
{
|
|
||||||
return value > rhythm_change_base_threshold && value < 1 - rhythm_change_base_threshold;
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum TypeSwitch
|
|
||||||
{
|
|
||||||
None,
|
|
||||||
Even,
|
|
||||||
Odd
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
|
|
||||||
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_taiko_o };
|
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_taiko_o };
|
||||||
|
|
||||||
public override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoLegacyDifficultyCalculator(this, beatmap);
|
public override LegacyDifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoDifficultyCalculator(this, beatmap);
|
||||||
|
|
||||||
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new TaikoPerformanceCalculator(this, beatmap, score);
|
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new TaikoPerformanceCalculator(this, beatmap, score);
|
||||||
|
|
||||||
|
@ -10,4 +10,7 @@
|
|||||||
<ItemGroup Label="Project References">
|
<ItemGroup Label="Project References">
|
||||||
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
|
<ProjectReference Include="..\osu.Game\osu.Game.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Difficulty2" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
Loading…
Reference in New Issue
Block a user