public void Judge(Note note, NoteGrade grade, double error, double greatGradeWeight) { if (IsCompleted || IsFailed) { return; } if (Judgements[note.Model.id].IsJudged) { return; Debug.LogWarning($"Trying to judge note {note.Model.id} which is already judged."); } ClearCount++; Judgements[note.Model.id].Apply(it => { it.IsJudged = true; it.Grade = grade; it.Error = error; }); if (Mode == GameMode.Practice) { if (grade != NoteGrade.Perfect && grade != NoteGrade.Great) { isFullScorePossible = false; } } else { if (grade != NoteGrade.Perfect) { isFullScorePossible = false; } } // Combo var miss = grade == NoteGrade.Bad || grade == NoteGrade.Miss; if (miss) { Combo = 0; } else { Combo++; } if (Combo > MaxCombo) { MaxCombo = Combo; } if (Mode == GameMode.Tier) { if (miss) { Context.TierState.Combo = 0; } else { Context.TierState.Combo++; } if (Context.TierState.Combo > Context.TierState.MaxCombo) { Context.TierState.MaxCombo = Context.TierState.Combo; } } // Score multiplier if (Mode != GameMode.Practice) { switch (grade) { case NoteGrade.Perfect: NoteScoreMultiplier += 0.004D * noteScoreMultiplierFactor; break; case NoteGrade.Great: NoteScoreMultiplier += 0.002D * noteScoreMultiplierFactor; break; case NoteGrade.Good: NoteScoreMultiplier += 0.001D * noteScoreMultiplierFactor; break; case NoteGrade.Bad: NoteScoreMultiplier -= 0.025D * noteScoreMultiplierFactor; break; case NoteGrade.Miss: NoteScoreMultiplier -= 0.05D * noteScoreMultiplierFactor; break; } if (NoteScoreMultiplier > 1) { NoteScoreMultiplier = 1; } if (NoteScoreMultiplier < 0) { NoteScoreMultiplier = 0; } } // Score if (Mode == GameMode.Practice) { Score += 900000.0 / NoteCount * grade.GetScoreWeight(false) + 100000.0 / (NoteCount * (NoteCount + 1) / 2.0) * Combo; } else { var maxNoteScore = 1000000.0 / NoteCount; double noteScore; if (grade == NoteGrade.Great) { noteScore = maxNoteScore * (NoteGrade.Great.GetScoreWeight(true) + (NoteGrade.Perfect.GetScoreWeight(true) - NoteGrade.Great.GetScoreWeight(true)) * greatGradeWeight); } else { noteScore = maxNoteScore * grade.GetScoreWeight(true); } noteScore *= NoteScoreMultiplier; Score += noteScore; } if (Score > 999500) { if (ClearCount == NoteCount && isFullScorePossible) { Score = 1000000; } } if (Score > 1000000) { Score = 1000000; } if (Score == 1000000 && !isFullScorePossible) { Score = 999999; // In case of double inaccuracy } // Accuracy if (Mode == GameMode.Practice || grade != NoteGrade.Great) { accumulatedAccuracy += 1.0 * grade.GetAccuracyWeight(); } else { accumulatedAccuracy += 1.0 * (NoteGrade.Great.GetAccuracyWeight() + (NoteGrade.Perfect.GetAccuracyWeight() - NoteGrade.Great.GetAccuracyWeight()) * greatGradeWeight); } Accuracy = accumulatedAccuracy / ClearCount; // Health mods if (UseHealthSystem) { var mods = Mods.Contains(Mod.ExHard) ? exHardHpMods : hardHpMods; if (Mode == GameMode.Tier) { mods = tierHpMods; } var mod = mods .Select[note.Type] .Select[Mode == GameMode.Practice ? unrankedGradingIndex[grade] : rankedGradingIndex[grade]]; double change = 0; switch (mod.Type) { case HpModType.Absolute: change = mod.Value; break; case HpModType.Percentage: change = mod.Value / 100f * MaxHealth; break; case HpModType.DivideByNoteCount: change = mod.Value / NoteCount / 100f * MaxHealth; break; } if (change < 0 && mod.UseHealthBuffer) { double a; if (HealthPercentage > 0.3) { a = 1; } else { a = 0.25 + 2.5 * HealthPercentage; } change *= a; } Health += change; Health = Math.Min(Math.Max(Health, 0), MaxHealth); if (Health <= 0) { ShouldFail = true; } if (Mode == GameMode.Tier) { Context.TierState.Health = Health; } } if ( Mods.Contains(Mod.AP) && grade != NoteGrade.Perfect || Mods.Contains(Mod.FC) && (grade == NoteGrade.Bad || grade == NoteGrade.Miss) ) { ShouldFail = true; } }