diff --git a/osu-framework b/osu-framework index 34c9f17a6a..51737ec132 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 34c9f17a6ac6fa5e9fd5569f9e119331316c1313 +Subproject commit 51737ec1320b4ea9dce4978f24e1d77a28f0a98e diff --git a/osu.Desktop.VisualTests/Tests/TestCaseLeaderboard.cs b/osu.Desktop.VisualTests/Tests/TestCaseLeaderboard.cs index cc30e3de95..329d5c5687 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseLeaderboard.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseLeaderboard.cs @@ -4,9 +4,9 @@ using OpenTK; using osu.Framework.Graphics; using osu.Framework.Screens.Testing; -using osu.Game.Modes; using osu.Game.Modes.Mods; using osu.Game.Modes.Osu.Mods; +using osu.Game.Modes.Scoring; using osu.Game.Screens.Select.Leaderboards; using osu.Game.Users; diff --git a/osu.Game.Modes.Catch/CatchRuleset.cs b/osu.Game.Modes.Catch/CatchRuleset.cs index 50224e3fdb..09d8bdb9e5 100644 --- a/osu.Game.Modes.Catch/CatchRuleset.cs +++ b/osu.Game.Modes.Catch/CatchRuleset.cs @@ -10,6 +10,8 @@ using osu.Game.Modes.Mods; using osu.Game.Modes.UI; using osu.Game.Screens.Play; using System.Collections.Generic; +using osu.Game.Modes.Catch.Scoring; +using osu.Game.Modes.Scoring; namespace osu.Game.Modes.Catch { diff --git a/osu.Game.Modes.Catch/Judgements/CatchJudgement.cs b/osu.Game.Modes.Catch/Judgements/CatchJudgement.cs index 8e18c68153..eaacedd7e0 100644 --- a/osu.Game.Modes.Catch/Judgements/CatchJudgement.cs +++ b/osu.Game.Modes.Catch/Judgements/CatchJudgement.cs @@ -7,8 +7,8 @@ namespace osu.Game.Modes.Catch.Judgements { public class CatchJudgement : Judgement { - public override string ScoreString => string.Empty; + public override string ResultString => string.Empty; - public override string MaxScoreString => string.Empty; + public override string MaxResultString => string.Empty; } } diff --git a/osu.Game.Modes.Catch/CatchScoreProcessor.cs b/osu.Game.Modes.Catch/Scoring/CatchScoreProcessor.cs similarity index 77% rename from osu.Game.Modes.Catch/CatchScoreProcessor.cs rename to osu.Game.Modes.Catch/Scoring/CatchScoreProcessor.cs index 6563949fbc..766a492bf4 100644 --- a/osu.Game.Modes.Catch/CatchScoreProcessor.cs +++ b/osu.Game.Modes.Catch/Scoring/CatchScoreProcessor.cs @@ -3,9 +3,10 @@ using osu.Game.Modes.Catch.Judgements; using osu.Game.Modes.Catch.Objects; +using osu.Game.Modes.Scoring; using osu.Game.Modes.UI; -namespace osu.Game.Modes.Catch +namespace osu.Game.Modes.Catch.Scoring { internal class CatchScoreProcessor : ScoreProcessor { @@ -18,7 +19,7 @@ namespace osu.Game.Modes.Catch { } - protected override void UpdateCalculations(CatchJudgement newJudgement) + protected override void OnNewJugement(CatchJudgement judgement) { } } diff --git a/osu.Game.Modes.Catch/UI/CatchHitRenderer.cs b/osu.Game.Modes.Catch/UI/CatchHitRenderer.cs index 751a8291d4..90bd61a39f 100644 --- a/osu.Game.Modes.Catch/UI/CatchHitRenderer.cs +++ b/osu.Game.Modes.Catch/UI/CatchHitRenderer.cs @@ -5,7 +5,9 @@ using osu.Game.Beatmaps; using osu.Game.Modes.Catch.Beatmaps; using osu.Game.Modes.Catch.Judgements; using osu.Game.Modes.Catch.Objects; +using osu.Game.Modes.Catch.Scoring; using osu.Game.Modes.Objects.Drawables; +using osu.Game.Modes.Scoring; using osu.Game.Modes.UI; namespace osu.Game.Modes.Catch.UI diff --git a/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj b/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj index 717e9175e4..593d8db4f6 100644 --- a/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj +++ b/osu.Game.Modes.Catch/osu.Game.Modes.Catch.csproj @@ -50,7 +50,7 @@ - + diff --git a/osu.Game.Modes.Mania/Judgements/ManiaJudgement.cs b/osu.Game.Modes.Mania/Judgements/ManiaJudgement.cs index 07c5fcbfef..3ef5b0f29b 100644 --- a/osu.Game.Modes.Mania/Judgements/ManiaJudgement.cs +++ b/osu.Game.Modes.Mania/Judgements/ManiaJudgement.cs @@ -7,8 +7,8 @@ namespace osu.Game.Modes.Mania.Judgements { public class ManiaJudgement : Judgement { - public override string ScoreString => string.Empty; + public override string ResultString => string.Empty; - public override string MaxScoreString => string.Empty; + public override string MaxResultString => string.Empty; } } diff --git a/osu.Game.Modes.Mania/ManiaRuleset.cs b/osu.Game.Modes.Mania/ManiaRuleset.cs index 27b3fcdf60..bd995d87d6 100644 --- a/osu.Game.Modes.Mania/ManiaRuleset.cs +++ b/osu.Game.Modes.Mania/ManiaRuleset.cs @@ -9,6 +9,8 @@ using osu.Game.Modes.Mods; using osu.Game.Modes.UI; using osu.Game.Screens.Play; using System.Collections.Generic; +using osu.Game.Modes.Mania.Scoring; +using osu.Game.Modes.Scoring; namespace osu.Game.Modes.Mania { diff --git a/osu.Game.Modes.Mania/ManiaScoreProcessor.cs b/osu.Game.Modes.Mania/Scoring/ManiaScoreProcessor.cs similarity index 77% rename from osu.Game.Modes.Mania/ManiaScoreProcessor.cs rename to osu.Game.Modes.Mania/Scoring/ManiaScoreProcessor.cs index c694717edb..c6b223af6d 100644 --- a/osu.Game.Modes.Mania/ManiaScoreProcessor.cs +++ b/osu.Game.Modes.Mania/Scoring/ManiaScoreProcessor.cs @@ -3,9 +3,10 @@ using osu.Game.Modes.Mania.Judgements; using osu.Game.Modes.Mania.Objects; +using osu.Game.Modes.Scoring; using osu.Game.Modes.UI; -namespace osu.Game.Modes.Mania +namespace osu.Game.Modes.Mania.Scoring { internal class ManiaScoreProcessor : ScoreProcessor { @@ -18,7 +19,7 @@ namespace osu.Game.Modes.Mania { } - protected override void UpdateCalculations(ManiaJudgement newJudgement) + protected override void OnNewJugement(ManiaJudgement judgement) { } } diff --git a/osu.Game.Modes.Mania/UI/ManiaHitRenderer.cs b/osu.Game.Modes.Mania/UI/ManiaHitRenderer.cs index 2a6629e25b..0415bc961a 100644 --- a/osu.Game.Modes.Mania/UI/ManiaHitRenderer.cs +++ b/osu.Game.Modes.Mania/UI/ManiaHitRenderer.cs @@ -5,7 +5,9 @@ using osu.Game.Beatmaps; using osu.Game.Modes.Mania.Beatmaps; using osu.Game.Modes.Mania.Judgements; using osu.Game.Modes.Mania.Objects; +using osu.Game.Modes.Mania.Scoring; using osu.Game.Modes.Objects.Drawables; +using osu.Game.Modes.Scoring; using osu.Game.Modes.UI; namespace osu.Game.Modes.Mania.UI diff --git a/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj b/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj index d9af517eee..cc925d417a 100644 --- a/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj +++ b/osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj @@ -51,7 +51,7 @@ - + diff --git a/osu.Game.Modes.Osu/Judgements/OsuJudgement.cs b/osu.Game.Modes.Osu/Judgements/OsuJudgement.cs index bbb1754ce6..e65d3dde3a 100644 --- a/osu.Game.Modes.Osu/Judgements/OsuJudgement.cs +++ b/osu.Game.Modes.Osu/Judgements/OsuJudgement.cs @@ -25,9 +25,9 @@ namespace osu.Game.Modes.Osu.Judgements /// public OsuScoreResult MaxScore = OsuScoreResult.Hit300; - public override string ScoreString => Score.GetDescription(); + public override string ResultString => Score.GetDescription(); - public override string MaxScoreString => MaxScore.GetDescription(); + public override string MaxResultString => MaxScore.GetDescription(); public int ScoreValue => scoreToInt(Score); diff --git a/osu.Game.Modes.Osu/Mods/OsuMod.cs b/osu.Game.Modes.Osu/Mods/OsuMod.cs index e80975aed1..db2ee26b7a 100644 --- a/osu.Game.Modes.Osu/Mods/OsuMod.cs +++ b/osu.Game.Modes.Osu/Mods/OsuMod.cs @@ -7,6 +7,7 @@ using osu.Game.Modes.Mods; using osu.Game.Modes.Osu.Objects; using System; using System.Linq; +using osu.Game.Modes.Scoring; namespace osu.Game.Modes.Osu.Mods { diff --git a/osu.Game.Modes.Osu/OsuRuleset.cs b/osu.Game.Modes.Osu/OsuRuleset.cs index bbaf7eed5f..12df7d3f3c 100644 --- a/osu.Game.Modes.Osu/OsuRuleset.cs +++ b/osu.Game.Modes.Osu/OsuRuleset.cs @@ -12,6 +12,8 @@ using osu.Game.Modes.UI; using osu.Game.Screens.Play; using System.Collections.Generic; using System.Linq; +using osu.Game.Modes.Osu.Scoring; +using osu.Game.Modes.Scoring; namespace osu.Game.Modes.Osu { diff --git a/osu.Game.Modes.Osu/OsuScore.cs b/osu.Game.Modes.Osu/Scoring/OsuScore.cs similarity index 72% rename from osu.Game.Modes.Osu/OsuScore.cs rename to osu.Game.Modes.Osu/Scoring/OsuScore.cs index dddf826887..a0a639a59e 100644 --- a/osu.Game.Modes.Osu/OsuScore.cs +++ b/osu.Game.Modes.Osu/Scoring/OsuScore.cs @@ -1,7 +1,9 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -namespace osu.Game.Modes.Osu +using osu.Game.Modes.Scoring; + +namespace osu.Game.Modes.Osu.Scoring { internal class OsuScore : Score { diff --git a/osu.Game.Modes.Osu/OsuScoreProcessor.cs b/osu.Game.Modes.Osu/Scoring/OsuScoreProcessor.cs similarity index 88% rename from osu.Game.Modes.Osu/OsuScoreProcessor.cs rename to osu.Game.Modes.Osu/Scoring/OsuScoreProcessor.cs index f473a578bd..b71e7dbadd 100644 --- a/osu.Game.Modes.Osu/OsuScoreProcessor.cs +++ b/osu.Game.Modes.Osu/Scoring/OsuScoreProcessor.cs @@ -4,9 +4,10 @@ using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Osu.Judgements; using osu.Game.Modes.Osu.Objects; +using osu.Game.Modes.Scoring; using osu.Game.Modes.UI; -namespace osu.Game.Modes.Osu +namespace osu.Game.Modes.Osu.Scoring { internal class OsuScoreProcessor : ScoreProcessor { @@ -27,7 +28,7 @@ namespace osu.Game.Modes.Osu Accuracy.Value = 1; } - protected override void UpdateCalculations(OsuJudgement judgement) + protected override void OnNewJugement(OsuJudgement judgement) { if (judgement != null) { diff --git a/osu.Game.Modes.Osu/UI/OsuHitRenderer.cs b/osu.Game.Modes.Osu/UI/OsuHitRenderer.cs index 18f8fbb8b9..ca9ff6fc61 100644 --- a/osu.Game.Modes.Osu/UI/OsuHitRenderer.cs +++ b/osu.Game.Modes.Osu/UI/OsuHitRenderer.cs @@ -7,6 +7,8 @@ using osu.Game.Modes.Osu.Beatmaps; using osu.Game.Modes.Osu.Judgements; using osu.Game.Modes.Osu.Objects; using osu.Game.Modes.Osu.Objects.Drawables; +using osu.Game.Modes.Osu.Scoring; +using osu.Game.Modes.Scoring; using osu.Game.Modes.UI; using osu.Game.Screens.Play; diff --git a/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj b/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj index a56bb799c4..55322e855e 100644 --- a/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj +++ b/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj @@ -72,8 +72,8 @@ - - + + diff --git a/osu.Game.Modes.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Modes.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 0606ee4d5a..b2676bf28a 100644 --- a/osu.Game.Modes.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Modes.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -3,6 +3,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; +using osu.Game.Beatmaps.Samples; using osu.Game.Modes.Objects; using osu.Game.Modes.Objects.Types; using osu.Game.Modes.Taiko.Objects; @@ -43,12 +44,15 @@ namespace osu.Game.Modes.Taiko.Beatmaps IHasRepeats repeatsData = original as IHasRepeats; IHasEndTime endTimeData = original as IHasEndTime; + bool accented = ((original.Sample?.Type ?? SampleType.None) & SampleType.Finish) > 0; + if (distanceData != null) { return new DrumRoll { StartTime = original.StartTime, Sample = original.Sample, + Accented = accented, Distance = distanceData.Distance * (repeatsData?.RepeatCount ?? 1) }; @@ -61,6 +65,7 @@ namespace osu.Game.Modes.Taiko.Beatmaps { StartTime = original.StartTime, Sample = original.Sample, + Accented = accented, EndTime = original.StartTime + endTimeData.Duration * bash_convert_factor }; @@ -70,6 +75,7 @@ namespace osu.Game.Modes.Taiko.Beatmaps { StartTime = original.StartTime, Sample = original.Sample, + Accented = accented }; } } diff --git a/osu.Game.Modes.Taiko/Judgements/TaikoDrumRollTickJudgement.cs b/osu.Game.Modes.Taiko/Judgements/TaikoDrumRollTickJudgement.cs index 7faf9db02f..b6a727aeb4 100644 --- a/osu.Game.Modes.Taiko/Judgements/TaikoDrumRollTickJudgement.cs +++ b/osu.Game.Modes.Taiko/Judgements/TaikoDrumRollTickJudgement.cs @@ -8,12 +8,12 @@ namespace osu.Game.Modes.Taiko.Judgements /// /// Drum roll ticks don't display judgement text. /// - public override string ScoreString => string.Empty; + public override string ResultString => string.Empty; /// /// Drum roll ticks don't display judgement text. /// - public override string MaxScoreString => string.Empty; + public override string MaxResultString => string.Empty; protected override int NumericResultForScore(TaikoHitResult result) { diff --git a/osu.Game.Modes.Taiko/Judgements/TaikoJudgement.cs b/osu.Game.Modes.Taiko/Judgements/TaikoJudgement.cs index d715fd0183..e50a685e24 100644 --- a/osu.Game.Modes.Taiko/Judgements/TaikoJudgement.cs +++ b/osu.Game.Modes.Taiko/Judgements/TaikoJudgement.cs @@ -9,38 +9,38 @@ namespace osu.Game.Modes.Taiko.Judgements public class TaikoJudgement : Judgement { /// - /// The maximum score value. + /// The maximum result. /// public const TaikoHitResult MAX_HIT_RESULT = TaikoHitResult.Great; /// - /// The score value. + /// The result. /// public TaikoHitResult TaikoResult; /// - /// The score value for the combo portion of the score. + /// The result value for the combo portion of the score. /// - public int ScoreValue => NumericResultForScore(TaikoResult); + public int ResultValueForScore => NumericResultForScore(TaikoResult); /// - /// The score value for the accuracy portion of the score. + /// The result value for the accuracy portion of the score. /// - public int AccuracyScoreValue => NumericResultForAccuracy(TaikoResult); + public int ResultValueForAccuracy => NumericResultForAccuracy(TaikoResult); /// - /// The maximum score value for the combo portion of the score. + /// The maximum result value for the combo portion of the score. /// - public int MaxScoreValue => NumericResultForScore(MAX_HIT_RESULT); + public int MaxResultValueForScore => NumericResultForScore(MAX_HIT_RESULT); /// - /// The maximum score value for the accuracy portion of the score. + /// The maximum result value for the accuracy portion of the score. /// - public int MaxAccuracyScoreValue => NumericResultForAccuracy(MAX_HIT_RESULT); + public int MaxResultValueForAccuracy => NumericResultForAccuracy(MAX_HIT_RESULT); - public override string ScoreString => TaikoResult.GetDescription(); + public override string ResultString => TaikoResult.GetDescription(); - public override string MaxScoreString => MAX_HIT_RESULT.GetDescription(); + public override string MaxResultString => MAX_HIT_RESULT.GetDescription(); /// /// Whether this Judgement has a secondary hit in the case of finishers. @@ -48,11 +48,11 @@ namespace osu.Game.Modes.Taiko.Judgements public bool SecondHit; /// - /// Computes the numeric score value for the combo portion of the score. + /// Computes the numeric result value for the combo portion of the score. /// For the accuracy portion of the score (including accuracy percentage), see . /// - /// The result to compute the score value for. - /// The numeric score value. + /// The result to compute the value for. + /// The numeric result value. protected virtual int NumericResultForScore(TaikoHitResult result) { switch (result) @@ -67,11 +67,11 @@ namespace osu.Game.Modes.Taiko.Judgements } /// - /// Computes the numeric score value for the accuracy portion of the score. + /// Computes the numeric result value for the accuracy portion of the score. /// For the combo portion of the score, see . /// - /// The result to compute the score value for. - /// The numeric score value. + /// The result to compute the value for. + /// The numeric result value. protected virtual int NumericResultForAccuracy(TaikoHitResult result) { switch (result) diff --git a/osu.Game.Modes.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Modes.Taiko/Objects/TaikoHitObject.cs index 4505065489..0ec1c2b93c 100644 --- a/osu.Game.Modes.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Modes.Taiko/Objects/TaikoHitObject.cs @@ -19,6 +19,12 @@ namespace osu.Game.Modes.Taiko.Objects /// public double PreEmpt; + /// + /// Whether this HitObject is accented. + /// Accented hit objects give more points for hitting the hit object with both keys. + /// + public bool Accented; + /// /// Whether this HitObject is in Kiai time. /// diff --git a/osu.Game.Modes.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Modes.Taiko/Scoring/TaikoScoreProcessor.cs new file mode 100644 index 0000000000..3007411230 --- /dev/null +++ b/osu.Game.Modes.Taiko/Scoring/TaikoScoreProcessor.cs @@ -0,0 +1,263 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// 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 + { + /// + /// The maximum score achievable. + /// Does _not_ include bonus score - for bonus score see . + /// + private const int max_score = 1000000; + + /// + /// The amount of the score attributed to combo. + /// + private const double combo_portion_max = max_score * 0.2; + + /// + /// The amount of the score attributed to accuracy. + /// + private const double accuracy_portion_max = max_score * 0.8; + + /// + /// The factor used to determine relevance of combos. + /// + private const double combo_base = 4; + + /// + /// The HP awarded by a hit. + /// + private const double hp_hit_great = 0.03; + + /// + /// The HP awarded for a hit. + /// + private const double hp_hit_good = 0.011; + + /// + /// The minimum HP deducted for a . + /// This occurs when HP Drain = 0. + /// + private const double hp_miss_min = -0.0018; + + /// + /// The median HP deducted for a . + /// This occurs when HP Drain = 5. + /// + private const double hp_miss_mid = -0.0075; + + /// + /// The maximum HP deducted for a . + /// This occurs when HP Drain = 10. + /// + private const double hp_miss_max = -0.12; + + /// + /// The HP awarded for a hit. + /// + /// 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. + /// + /// + private const double hp_hit_tick = 0.00000003; + + /// + /// Taiko fails at the end of the map if the player has not half-filled their HP bar. + /// + public override bool HasFailed => totalHits == maxTotalHits && Health.Value <= 0.5; + + /// + /// The cumulative combo portion of the score. + /// + private double comboScore => combo_portion_max * comboPortion / maxComboPortion; + + /// + /// The cumulative accuracy portion of the score. + /// + private double accuracyScore => accuracy_portion_max * Math.Pow(Accuracy, 3.6) * totalHits / maxTotalHits; + + /// + /// The cumulative bonus score. + /// This is added on top of , thus the total score can exceed . + /// + private double bonusScore; + + /// + /// The multiple of the original score added to the combo portion of the score + /// for correctly hitting an accented hit object with both keys. + /// + private double accentedHitScale; + + private double hpIncreaseTick; + private double hpIncreaseGreat; + private double hpIncreaseGood; + private double hpIncreaseMiss; + + private double maxComboPortion; + private double comboPortion; + private int maxTotalHits; + private int totalHits; + + public TaikoScoreProcessor() + { + } + + public TaikoScoreProcessor(HitRenderer hitRenderer) + : base(hitRenderer) + { + } + + protected override void ComputeTargets(Beatmap 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 + }); + } + } + + maxTotalHits = totalHits; + maxComboPortion = comboPortion; + } + + protected override void OnNewJugement(TaikoJudgement judgement) + { + bool isTick = judgement is TaikoDrumRollTickJudgement; + + // Don't consider ticks as a type of hit that counts towards map completion + if (!isTick) + totalHits++; + + // Apply score changes + if (judgement.Result == HitResult.Hit) + { + double baseValue = judgement.ResultValueForScore; + + // Add bonus points for hitting an accented hit object with the second key + if (judgement.SecondHit) + baseValue += baseValue * accentedHitScale; + + // Add score to portions + if (isTick) + bonusScore += baseValue; + else + { + 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 += baseValue * comboRelevance; + } + } + + // Apply HP changes + switch (judgement.Result) + { + case HitResult.Miss: + // Missing ticks shouldn't drop HP + if (!isTick) + Health.Value += hpIncreaseMiss; + break; + case HitResult.Hit: + switch (judgement.TaikoResult) + { + case TaikoHitResult.Good: + Health.Value += hpIncreaseGood; + break; + case TaikoHitResult.Great: + if (isTick) + Health.Value += hpIncreaseTick; + else + Health.Value += hpIncreaseGreat; + break; + } + break; + } + + // 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; + } + + protected override void Reset() + { + base.Reset(); + + Health.Value = 0; + + bonusScore = 0; + comboPortion = 0; + totalHits = 0; + } + } +} diff --git a/osu.Game.Modes.Taiko/TaikoRuleset.cs b/osu.Game.Modes.Taiko/TaikoRuleset.cs index ff481e9162..ce7e756e30 100644 --- a/osu.Game.Modes.Taiko/TaikoRuleset.cs +++ b/osu.Game.Modes.Taiko/TaikoRuleset.cs @@ -10,6 +10,8 @@ using osu.Game.Modes.Taiko.UI; using osu.Game.Modes.UI; using osu.Game.Screens.Play; using System.Collections.Generic; +using osu.Game.Modes.Scoring; +using osu.Game.Modes.Taiko.Scoring; namespace osu.Game.Modes.Taiko { diff --git a/osu.Game.Modes.Taiko/TaikoScoreProcessor.cs b/osu.Game.Modes.Taiko/TaikoScoreProcessor.cs deleted file mode 100644 index fc414c3382..0000000000 --- a/osu.Game.Modes.Taiko/TaikoScoreProcessor.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Game.Modes.Taiko.Judgements; -using osu.Game.Modes.Taiko.Objects; -using osu.Game.Modes.UI; - -namespace osu.Game.Modes.Taiko -{ - internal class TaikoScoreProcessor : ScoreProcessor - { - public TaikoScoreProcessor() - { - } - - public TaikoScoreProcessor(HitRenderer hitRenderer) - : base(hitRenderer) - { - } - - protected override void UpdateCalculations(TaikoJudgement newJudgement) - { - } - } -} diff --git a/osu.Game.Modes.Taiko/UI/TaikoHitRenderer.cs b/osu.Game.Modes.Taiko/UI/TaikoHitRenderer.cs index 21ee3434d3..3266b5c030 100644 --- a/osu.Game.Modes.Taiko/UI/TaikoHitRenderer.cs +++ b/osu.Game.Modes.Taiko/UI/TaikoHitRenderer.cs @@ -3,9 +3,11 @@ using osu.Game.Beatmaps; using osu.Game.Modes.Objects.Drawables; +using osu.Game.Modes.Scoring; using osu.Game.Modes.Taiko.Beatmaps; using osu.Game.Modes.Taiko.Judgements; using osu.Game.Modes.Taiko.Objects; +using osu.Game.Modes.Taiko.Scoring; using osu.Game.Modes.UI; namespace osu.Game.Modes.Taiko.UI diff --git a/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj b/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj index e92f152f5a..52d3f15df8 100644 --- a/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj +++ b/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj @@ -62,7 +62,7 @@ - + diff --git a/osu.Game/Database/ScoreDatabase.cs b/osu.Game/Database/ScoreDatabase.cs index cfa8e6ac7e..665ae8649e 100644 --- a/osu.Game/Database/ScoreDatabase.cs +++ b/osu.Game/Database/ScoreDatabase.cs @@ -8,6 +8,7 @@ using osu.Framework.Platform; using osu.Game.IO.Legacy; using osu.Game.IPC; using osu.Game.Modes; +using osu.Game.Modes.Scoring; using SharpCompress.Compressors.LZMA; namespace osu.Game.Database diff --git a/osu.Game/Graphics/Containers/ParallaxContainer.cs b/osu.Game/Graphics/Containers/ParallaxContainer.cs index 9dfc26dd3c..dc2f01a2bc 100644 --- a/osu.Game/Graphics/Containers/ParallaxContainer.cs +++ b/osu.Game/Graphics/Containers/ParallaxContainer.cs @@ -12,7 +12,7 @@ using osu.Framework.Configuration; namespace osu.Game.Graphics.Containers { - internal class ParallaxContainer : Container + internal class ParallaxContainer : Container, IRequireHighFrequencyMousePosition { public float ParallaxAmount = 0.02f; diff --git a/osu.Game/Modes/Judgements/DrawableJudgement.cs b/osu.Game/Modes/Judgements/DrawableJudgement.cs index ef67d07310..76c73335dd 100644 --- a/osu.Game/Modes/Judgements/DrawableJudgement.cs +++ b/osu.Game/Modes/Judgements/DrawableJudgement.cs @@ -35,7 +35,7 @@ namespace osu.Game.Modes.Judgements AutoSizeAxes = Axes.Both; - string scoreString = judgement.Result == HitResult.Hit ? judgement.ScoreString : judgement.Result.GetDescription(); + string resultString = judgement.Result == HitResult.Hit ? judgement.ResultString : judgement.Result.GetDescription(); Children = new[] { @@ -43,7 +43,7 @@ namespace osu.Game.Modes.Judgements { Origin = Anchor.Centre, Anchor = Anchor.Centre, - Text = scoreString.ToUpper(), + Text = resultString.ToUpper(), Font = @"Venera", TextSize = 16 } diff --git a/osu.Game/Modes/Judgements/Judgement.cs b/osu.Game/Modes/Judgements/Judgement.cs index 45f40647ca..d916fc15de 100644 --- a/osu.Game/Modes/Judgements/Judgement.cs +++ b/osu.Game/Modes/Judgements/Judgement.cs @@ -23,13 +23,13 @@ namespace osu.Game.Modes.Judgements public ulong? ComboAtHit; /// - /// The string representation for the score achieved. + /// The string representation for the result achieved. /// - public abstract string ScoreString { get; } + public abstract string ResultString { get; } /// - /// The string representation for the max score achievable. + /// The string representation for the max result achievable. /// - public abstract string MaxScoreString { get; } + public abstract string MaxResultString { get; } } } \ No newline at end of file diff --git a/osu.Game/Modes/Mods/Mod.cs b/osu.Game/Modes/Mods/Mod.cs index 6a720a3574..8416ae6211 100644 --- a/osu.Game/Modes/Mods/Mod.cs +++ b/osu.Game/Modes/Mods/Mod.cs @@ -6,6 +6,7 @@ using osu.Game.Graphics; using osu.Game.Modes.Objects; using osu.Game.Modes.UI; using System; +using osu.Game.Modes.Scoring; namespace osu.Game.Modes.Mods { diff --git a/osu.Game/Modes/Ruleset.cs b/osu.Game/Modes/Ruleset.cs index 886b49f26b..c97420fbe3 100644 --- a/osu.Game/Modes/Ruleset.cs +++ b/osu.Game/Modes/Ruleset.cs @@ -9,6 +9,7 @@ using osu.Game.Screens.Play; using System; using System.Collections.Concurrent; using System.Collections.Generic; +using osu.Game.Modes.Scoring; namespace osu.Game.Modes { diff --git a/osu.Game/Modes/Score.cs b/osu.Game/Modes/Scoring/Score.cs similarity index 94% rename from osu.Game/Modes/Score.cs rename to osu.Game/Modes/Scoring/Score.cs index 55bbed8e77..fa74fba279 100644 --- a/osu.Game/Modes/Score.cs +++ b/osu.Game/Modes/Scoring/Score.cs @@ -3,11 +3,11 @@ using System; using Newtonsoft.Json; -using osu.Game.Users; using osu.Game.Database; using osu.Game.Modes.Mods; +using osu.Game.Users; -namespace osu.Game.Modes +namespace osu.Game.Modes.Scoring { public class Score { diff --git a/osu.Game/Modes/ScoreProcessor.cs b/osu.Game/Modes/Scoring/ScoreProcessor.cs similarity index 89% rename from osu.Game/Modes/ScoreProcessor.cs rename to osu.Game/Modes/Scoring/ScoreProcessor.cs index 0886f34d55..f14b306e18 100644 --- a/osu.Game/Modes/ScoreProcessor.cs +++ b/osu.Game/Modes/Scoring/ScoreProcessor.cs @@ -1,15 +1,15 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Configuration; using System; using System.Collections.Generic; -using osu.Game.Modes.Judgements; -using osu.Game.Modes.UI; -using osu.Game.Modes.Objects; +using osu.Framework.Configuration; using osu.Game.Beatmaps; +using osu.Game.Modes.Judgements; +using osu.Game.Modes.Objects; +using osu.Game.Modes.UI; -namespace osu.Game.Modes +namespace osu.Game.Modes.Scoring { public abstract class ScoreProcessor { @@ -122,7 +122,7 @@ namespace osu.Game.Modes { Judgements.Capacity = hitRenderer.Beatmap.HitObjects.Count; - hitRenderer.OnJudgement += addJudgement; + hitRenderer.OnJudgement += AddJudgement; ComputeTargets(hitRenderer.Beatmap); @@ -139,11 +139,11 @@ namespace osu.Game.Modes /// Adds a judgement to this ScoreProcessor. /// /// The judgement to add. - private void addJudgement(TJudgement judgement) + protected void AddJudgement(TJudgement judgement) { Judgements.Add(judgement); - UpdateCalculations(judgement); + OnNewJugement(judgement); judgement.ComboAtHit = (ulong)Combo.Value; @@ -158,7 +158,7 @@ namespace osu.Game.Modes /// /// Update any values that potentially need post-processing on a judgement change. /// - /// A new Judgement that triggered this calculation. May be null. - protected abstract void UpdateCalculations(TJudgement newJudgement); + /// The judgement that triggered this calculation. + protected abstract void OnNewJugement(TJudgement judgement); } } \ No newline at end of file diff --git a/osu.Game/Modes/ScoreRank.cs b/osu.Game/Modes/Scoring/ScoreRank.cs similarity index 90% rename from osu.Game/Modes/ScoreRank.cs rename to osu.Game/Modes/Scoring/ScoreRank.cs index 433158f249..743f24ecd6 100644 --- a/osu.Game/Modes/ScoreRank.cs +++ b/osu.Game/Modes/Scoring/ScoreRank.cs @@ -3,7 +3,7 @@ using System.ComponentModel; -namespace osu.Game.Modes +namespace osu.Game.Modes.Scoring { public enum ScoreRank { diff --git a/osu.Game/Modes/UI/HitRenderer.cs b/osu.Game/Modes/UI/HitRenderer.cs index d53c65ec16..3d108b895d 100644 --- a/osu.Game/Modes/UI/HitRenderer.cs +++ b/osu.Game/Modes/UI/HitRenderer.cs @@ -14,6 +14,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using osu.Game.Modes.Scoring; namespace osu.Game.Modes.UI { diff --git a/osu.Game/Modes/UI/HudOverlay.cs b/osu.Game/Modes/UI/HudOverlay.cs index 4b454797ce..a6c54e7f3a 100644 --- a/osu.Game/Modes/UI/HudOverlay.cs +++ b/osu.Game/Modes/UI/HudOverlay.cs @@ -9,6 +9,7 @@ using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Play; using System; +using osu.Game.Modes.Scoring; namespace osu.Game.Modes.UI { diff --git a/osu.Game/Online/API/Requests/GetScoresRequest.cs b/osu.Game/Online/API/Requests/GetScoresRequest.cs index 8c0fc4383a..66c5e6c72d 100644 --- a/osu.Game/Online/API/Requests/GetScoresRequest.cs +++ b/osu.Game/Online/API/Requests/GetScoresRequest.cs @@ -5,7 +5,7 @@ using System.Collections.Generic; using Newtonsoft.Json; using osu.Framework.IO.Network; using osu.Game.Database; -using osu.Game.Modes; +using osu.Game.Modes.Scoring; namespace osu.Game.Online.API.Requests { diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 1fca318149..8ac809e591 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -26,6 +26,7 @@ using osu.Framework.Graphics.Primitives; using System.Threading.Tasks; using osu.Framework.Threading; using osu.Game.Graphics; +using osu.Game.Modes.Scoring; using osu.Game.Overlays.Notifications; using osu.Game.Screens.Play; diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index ddd963da00..702f907428 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -23,6 +23,7 @@ using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Ranking; using System; using System.Linq; +using osu.Game.Modes.Scoring; namespace osu.Game.Screens.Play { diff --git a/osu.Game/Screens/Ranking/Results.cs b/osu.Game/Screens/Ranking/Results.cs index 2df654b889..daf0866d10 100644 --- a/osu.Game/Screens/Ranking/Results.cs +++ b/osu.Game/Screens/Ranking/Results.cs @@ -6,7 +6,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Transforms; using osu.Game.Graphics.Sprites; -using osu.Game.Modes; +using osu.Game.Modes.Scoring; using osu.Game.Screens.Backgrounds; using OpenTK; using OpenTK.Graphics; diff --git a/osu.Game/Screens/Select/Footer.cs b/osu.Game/Screens/Select/Footer.cs index 36030f5de4..c86ea2f30e 100644 --- a/osu.Game/Screens/Select/Footer.cs +++ b/osu.Game/Screens/Select/Footer.cs @@ -22,6 +22,8 @@ namespace osu.Game.Screens.Select private const float play_song_select_button_width = 100; private const float play_song_select_button_height = 50; + public const float HEIGHT = 50; + public const int TRANSITION_LENGTH = 300; private const float padding = 80; @@ -69,10 +71,8 @@ namespace osu.Game.Screens.Select { AlwaysReceiveInput = true; - const float bottom_tool_height = 50; - RelativeSizeAxes = Axes.X; - Height = bottom_tool_height; + Height = HEIGHT; Anchor = Anchor.BottomCentre; Origin = Anchor.BottomCentre; Children = new Drawable[] diff --git a/osu.Game/Screens/Select/Leaderboards/DrawableRank.cs b/osu.Game/Screens/Select/Leaderboards/DrawableRank.cs index 9e73148ada..6b59af0bb4 100644 --- a/osu.Game/Screens/Select/Leaderboards/DrawableRank.cs +++ b/osu.Game/Screens/Select/Leaderboards/DrawableRank.cs @@ -6,8 +6,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; -using osu.Game.Modes; using osu.Framework.Extensions; +using osu.Game.Modes.Scoring; namespace osu.Game.Screens.Select.Leaderboards { diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 4ce40cb943..12ff096d16 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -9,8 +9,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; -using osu.Game.Modes; using System; +using osu.Game.Modes.Scoring; namespace osu.Game.Screens.Select.Leaderboards { diff --git a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs index d32f3a497a..1df6d2b55c 100644 --- a/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Screens/Select/Leaderboards/LeaderboardScore.cs @@ -10,11 +10,11 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Transforms; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; -using osu.Game.Modes; using osu.Framework.Extensions.Color4Extensions; using osu.Game.Modes.Mods; using osu.Game.Users; using osu.Framework; +using osu.Game.Modes.Scoring; namespace osu.Game.Screens.Select.Leaderboards { diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index e3a6371c27..fb7ed3809f 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -4,6 +4,7 @@ using OpenTK.Input; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Framework.Screens; using osu.Game.Beatmaps; @@ -22,12 +23,11 @@ namespace osu.Game.Screens.Select public PlaySongSelect() { - Add(modSelect = new ModSelectOverlay + FooterPanels.Add(modSelect = new ModSelectOverlay { RelativeSizeAxes = Axes.X, Origin = Anchor.BottomCentre, Anchor = Anchor.BottomCentre, - Margin = new MarginPadding { Bottom = 50 } }); LeftContent.Add(beatmapDetails = new BeatmapDetailArea @@ -66,6 +66,17 @@ namespace osu.Game.Screens.Select base.OnResuming(last); } + protected override bool OnExiting(Screen next) + { + if (modSelect.State == Visibility.Visible) + { + modSelect.Hide(); + return true; + } + + return base.OnExiting(next); + } + protected override void OnSelected() { if (player != null) return; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 1e7532b8a6..49762331c4 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -65,6 +65,12 @@ namespace osu.Game.Screens.Select /// protected readonly Footer Footer; + /// + /// Contains any panel which is triggered by a footer button. + /// Helps keep them located beneath the footer itself. + /// + protected readonly Container FooterPanels; + public readonly FilterControl FilterControl; protected SongSelect() @@ -131,11 +137,15 @@ namespace osu.Game.Screens.Select if (ShowFooter) { - Add(BeatmapOptions = new BeatmapOptionsOverlay + Add(FooterPanels = new Container { + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Margin = new MarginPadding { - Bottom = 50, + Bottom = Footer.HEIGHT, }, }); Add(Footer = new Footer @@ -143,6 +153,8 @@ namespace osu.Game.Screens.Select OnBack = Exit, OnStart = raiseSelect, }); + + FooterPanels.Add(BeatmapOptions = new BeatmapOptionsOverlay()); } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index bcae668e9f..989e605c16 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -124,8 +124,8 @@ - - + + @@ -359,7 +359,7 @@ - + diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index f266b81ec9..a92832ec8c 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -1,4 +1,9 @@  + True + True + True + True + ExplicitlyExcluded SOLUTION WARNING WARNING