Created
March 24, 2017 03:52
-
-
Save smoogipoo/d51d651fd03b110181c17be30278f231 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright (c) 2007-2017 ppy Pty Ltd <[email protected]>. | |
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE | |
using System; | |
using osu.Game.Beatmaps; | |
using osu.Game.Database; | |
using osu.Game.Modes.Objects.Drawables; | |
using osu.Game.Modes.Scoring; | |
using osu.Game.Modes.Taiko.Judgements; | |
using osu.Game.Modes.Taiko.Objects; | |
using osu.Game.Modes.UI; | |
using OpenTK; | |
namespace osu.Game.Modes.Taiko.Scoring | |
{ | |
internal class TaikoScoreProcessor : ScoreProcessor<TaikoHitObject, TaikoJudgement> | |
{ | |
/// <summary> | |
/// The maximum score achievable. | |
/// Does _not_ include bonus score - for bonus score see <see cref="bonusScore"/>. | |
/// </summary> | |
private const int max_score = 1000000; | |
/// <summary> | |
/// The amount of the score attributed to combo. | |
/// </summary> | |
private const double combo_portion_max = max_score * 0.2; | |
/// <summary> | |
/// The amount of the score attributed to accuracy. | |
/// </summary> | |
private const double accuracy_portion_max = max_score * 0.8; | |
/// <summary> | |
/// The factor used to determine relevance of combos. | |
/// </summary> | |
private const double combo_base = 4; | |
/// <summary> | |
/// The HP awarded by a <see cref="TaikoHitResult.Great"/> hit. | |
/// </summary> | |
private const double hp_hit_great = 0.03; | |
/// <summary> | |
/// The HP awarded for a <see cref="TaikoHitResult.Good"/> hit. | |
/// </summary> | |
private const double hp_hit_good = 0.011; | |
/// <summary> | |
/// The minimum HP deducted for a <see cref="HitResult.Miss"/>. | |
/// This occurs when HP Drain = 0. | |
/// </summary> | |
private const double hp_miss_min = -0.0018; | |
/// <summary> | |
/// The median HP deducted for a <see cref="HitResult.Miss"/>. | |
/// This occurs when HP Drain = 5. | |
/// </summary> | |
private const double hp_miss_mid = -0.0075; | |
/// <summary> | |
/// The maximum HP deducted for a <see cref="HitResult.Miss"/>. | |
/// This occurs when HP Drain = 10. | |
/// </summary> | |
private const double hp_miss_max = -0.12; | |
/// <summary> | |
/// The HP awarded for a <see cref="DrumRollTick"/> hit. | |
/// <para> | |
/// <see cref="DrumRollTick"/> hits award less HP as they're more spammable, although in hindsight | |
/// this probably awards too little HP and is kept at this value for now for compatibility. | |
/// </para> | |
/// </summary> | |
private const double hp_hit_tick = 0.00000003; | |
/// <summary> | |
/// Taiko fails at the end of the map if the player has not half-filled their HP bar. | |
/// </summary> | |
public override bool HasFailed => totalJudgements == maxJudgements && Health.Value <= 0.5; | |
/// <summary> | |
/// The cumulative combo portion of the score. | |
/// </summary> | |
private double comboScore => combo_portion_max * comboPortion / maxComboPortion; | |
/// <summary> | |
/// The cumulative accuracy portion of the score. | |
/// </summary> | |
private double accuracyScore => accuracy_portion_max * Math.Pow(Accuracy, 3.6) * totalJudgements / maxJudgements; | |
/// <summary> | |
/// The cumulative bonus score. | |
/// This is added on top of <see cref="max_score"/>, thus the total score can exceed <see cref="max_score"/>. | |
/// </summary> | |
private double bonusScore; | |
/// <summary> | |
/// The multiple of the original score added to the combo portion of the score | |
/// for correctly hitting an accented hit object with both keys. | |
/// </summary> | |
private double accentedHitScale; | |
private double hpIncreaseTick; | |
private double hpIncreaseGreat; | |
private double hpIncreaseGood; | |
private double hpIncreaseMiss; | |
private double maxComboPortion; | |
private double comboPortion; | |
private int maxJudgements; | |
private int totalJudgements; | |
public TaikoScoreProcessor() | |
{ | |
} | |
public TaikoScoreProcessor(HitRenderer<TaikoHitObject, TaikoJudgement> hitRenderer) | |
: base(hitRenderer) | |
{ | |
} | |
protected override void ComputeTargets(Beatmap<TaikoHitObject> beatmap) | |
{ | |
double hpMultiplierNormal = 1 / (hp_hit_great * beatmap.HitObjects.FindAll(o => o is Hit).Count * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.Difficulty.DrainRate, 0.5, 0.75, 0.98)); | |
hpIncreaseTick = hp_hit_tick; | |
hpIncreaseGreat = hpMultiplierNormal * hp_hit_great; | |
hpIncreaseGood = hpMultiplierNormal * hp_hit_good; | |
hpIncreaseMiss = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.Difficulty.DrainRate, hp_miss_min, hp_miss_mid, hp_miss_max); | |
var accentedHits = beatmap.HitObjects.FindAll(o => o is Hit && o.Accented); | |
// This is a linear function that awards: | |
// 10 times bonus points for hitting an accented hit object with both keys with 30 accented hit objects in the map | |
// 3 times bonus points for hitting an accented hit object with both keys with 120 accented hit objects in the map | |
accentedHitScale = -7d / 90d * MathHelper.Clamp(accentedHits.Count, 30, 120) + 111d / 9d; | |
foreach (var obj in beatmap.HitObjects) | |
{ | |
if (obj is Hit) | |
{ | |
AddJudgement(new TaikoJudgement | |
{ | |
Result = HitResult.Hit, | |
TaikoResult = TaikoHitResult.Great, | |
SecondHit = obj.Accented | |
}); | |
} | |
else if (obj is DrumRoll) | |
{ | |
for (int i = 0; i < ((DrumRoll)obj).TotalTicks; i++) | |
{ | |
AddJudgement(new TaikoDrumRollTickJudgement | |
{ | |
Result = HitResult.Hit, | |
TaikoResult = TaikoHitResult.Great, | |
SecondHit = obj.Accented | |
}); | |
} | |
AddJudgement(new TaikoJudgement | |
{ | |
Result = HitResult.Hit, | |
TaikoResult = TaikoHitResult.Great, | |
SecondHit = obj.Accented | |
}); | |
} | |
else if (obj is Bash) | |
{ | |
AddJudgement(new TaikoJudgement | |
{ | |
Result = HitResult.Hit, | |
TaikoResult = TaikoHitResult.Great | |
}); | |
} | |
} | |
maxJudgements = totalJudgements; | |
maxComboPortion = comboPortion; | |
} | |
protected override void OnNewJugement(TaikoJudgement judgement) | |
{ | |
if (judgement is TaikoDrumRollTickJudgement) | |
onTickJudgement(judgement); | |
else | |
onHitJudgement(judgement); | |
// Compute the new score + accuracy | |
int scoreForAccuracy = 0; | |
int maxScoreForAccuracy = 0; | |
foreach (var j in Judgements) | |
{ | |
scoreForAccuracy += j.ResultValueForAccuracy; | |
maxScoreForAccuracy = j.MaxResultValueForAccuracy; | |
} | |
Accuracy.Value = (double)scoreForAccuracy / maxScoreForAccuracy; | |
TotalScore.Value = comboScore + accuracyScore + bonusScore; | |
} | |
private void onTickJudgement(TaikoJudgement judgement) | |
{ | |
if (judgement.Result != HitResult.Hit) | |
return; | |
double baseScoreIncrease = judgement.ResultValueForScore; | |
if (judgement.SecondHit) | |
baseScoreIncrease += baseScoreIncrease * accentedHitScale; | |
bonusScore += baseScoreIncrease; | |
Health.Value += hpIncreaseTick; | |
} | |
private void onHitJudgement(TaikoJudgement judgement) | |
{ | |
double baseScoreIncrease = judgement.ResultValueForScore; | |
if (judgement.SecondHit) | |
baseScoreIncrease += baseScoreIncrease * accentedHitScale; | |
switch (judgement.Result) | |
{ | |
case HitResult.Miss: | |
Combo.Value = 0; | |
Health.Value += hpIncreaseMiss; | |
break; | |
case HitResult.Hit: | |
Combo.Value++; | |
// A relevance factor that needs to be applied to make higher combos more relevant | |
// Value is capped at 400 combo | |
double comboRelevance = Math.Min(Math.Log(400, combo_base), Math.Max(0.5, Math.Log(Combo.Value, combo_base))); | |
comboPortion += baseScoreIncrease * comboRelevance; | |
switch (judgement.TaikoResult) | |
{ | |
case TaikoHitResult.Good: | |
Health.Value += hpIncreaseGood; | |
break; | |
case TaikoHitResult.Great: | |
Health.Value += hpIncreaseGreat; | |
break; | |
} | |
break; | |
} | |
totalJudgements++; | |
} | |
protected override void Reset() | |
{ | |
base.Reset(); | |
Health.Value = 0; | |
bonusScore = 0; | |
comboPortion = 0; | |
totalJudgements = 0; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment