diff --git a/osu.Desktop.Tests/Visual/TestCaseScrollingPlayfield.cs b/osu.Desktop.Tests/Visual/TestCaseScrollingPlayfield.cs index f04c237629..6565ca98f9 100644 --- a/osu.Desktop.Tests/Visual/TestCaseScrollingPlayfield.cs +++ b/osu.Desktop.Tests/Visual/TestCaseScrollingPlayfield.cs @@ -154,7 +154,7 @@ namespace osu.Desktop.Tests.Visual private class TestScoreProcessor : ScoreProcessor { - protected override void OnNewJudgement(TestJudgement judgement) + protected override void OnNewJudgement(Judgement judgement) { } } diff --git a/osu.Desktop.Tests/Visual/TestCaseTaikoPlayfield.cs b/osu.Desktop.Tests/Visual/TestCaseTaikoPlayfield.cs index e40016b858..0bfa21a45e 100644 --- a/osu.Desktop.Tests/Visual/TestCaseTaikoPlayfield.cs +++ b/osu.Desktop.Tests/Visual/TestCaseTaikoPlayfield.cs @@ -139,35 +139,20 @@ namespace osu.Desktop.Tests.Visual Hit hit = new Hit(); hit.ApplyDefaults(cpi, new BeatmapDifficulty()); - var h = new DrawableTestHit(hit) - { - X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f), - Judgement = new TaikoJudgement - { - Result = hitResult, - TimeOffset = 0 - } - }; + var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) }; - rulesetContainer.Playfield.OnJudgement(h); + rulesetContainer.Playfield.OnJudgement(h, new TaikoJudgement { Result = hitResult }); if (RNG.Next(10) == 0) { - h.Judgement.SecondHit = true; - rulesetContainer.Playfield.OnJudgement(h); + rulesetContainer.Playfield.OnJudgement(h, new TaikoJudgement { Result = hitResult }); + rulesetContainer.Playfield.OnJudgement(h, new TaikoStrongHitJudgement()); } } private void addMissJudgement() { - rulesetContainer.Playfield.OnJudgement(new DrawableTestHit(new Hit()) - { - Judgement = new TaikoJudgement - { - Result = HitResult.Miss, - TimeOffset = 0 - } - }); + rulesetContainer.Playfield.OnJudgement(new DrawableTestHit(new Hit()), new TaikoJudgement { Result = HitResult.Miss }); } private void addBarLine(bool major, double delay = scroll_time) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs index c6c716a29d..0cbece5544 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.MathUtils; using osu.Game.Graphics; using osu.Game.Rulesets.Catch.Judgements; +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Drawables; using OpenTK; using OpenTK.Graphics; @@ -100,10 +101,10 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable private const float preempt = 1000; - protected override void CheckJudgement(bool userTriggered) + protected override void CheckForJudgements(bool userTriggered, double timeOffset) { - if (Judgement.TimeOffset > 0) - Judgement.Result = CheckPosition?.Invoke(HitObject) ?? false ? HitResult.Perfect : HitResult.Miss; + if (timeOffset > 0) + AddJudgement(new Judgement { Result = CheckPosition?.Invoke(HitObject) ?? false ? HitResult.Perfect : HitResult.Miss }); } protected override void UpdateState(ArmedState state) diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs index 33c1355823..8a9e17de6e 100644 --- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs @@ -3,6 +3,7 @@ using osu.Game.Rulesets.Catch.Judgements; using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; @@ -27,7 +28,7 @@ namespace osu.Game.Rulesets.Catch.Scoring Accuracy.Value = 1; } - protected override void OnNewJudgement(CatchJudgement judgement) + protected override void OnNewJudgement(Judgement judgement) { } } diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 292ddaca12..cf00f80e5e 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -8,6 +8,7 @@ using OpenTK; using osu.Game.Rulesets.Catch.Judgements; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Catch.Objects.Drawable; +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Catch.UI @@ -53,13 +54,13 @@ namespace osu.Game.Rulesets.Catch.UI fruit.OnJudgement += Fruit_OnJudgement; } - private void Fruit_OnJudgement(DrawableHitObject obj) + private void Fruit_OnJudgement(DrawableHitObject judgedObject, Judgement judgement) { - if (obj.Judgement.Result > HitResult.Miss) + if (judgement.Result > HitResult.Miss) { - Vector2 screenPosition = obj.ScreenSpaceDrawQuad.Centre; - Remove(obj); - catcherArea.Add(obj, screenPosition); + Vector2 screenPosition = judgedObject.ScreenSpaceDrawQuad.Centre; + Remove(judgedObject); + catcherArea.Add(judgedObject, screenPosition); } } } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index f416f6acfb..f79733e9c0 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Input.Bindings; using osu.Framework.MathUtils; -using osu.Game.Rulesets.Catch.Judgements; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawable; using osu.Game.Rulesets.Objects.Drawables; @@ -22,7 +21,7 @@ namespace osu.Game.Rulesets.Catch.UI { private Catcher catcher; - public void Add(DrawableHitObject fruit, Vector2 screenPosition) => catcher.AddToStack(fruit, screenPosition); + public void Add(DrawableHitObject fruit, Vector2 screenPosition) => catcher.AddToStack(fruit, screenPosition); public bool CheckIfWeCanCatch(CatchBaseHit obj) => Math.Abs(catcher.Position.X - obj.Position) < catcher.DrawSize.X / DrawSize.X / 2; @@ -152,7 +151,7 @@ namespace osu.Game.Rulesets.Catch.UI X = (float)MathHelper.Clamp(X + Math.Sign(currentDirection) * Clock.ElapsedFrameTime / 1800 * speed, 0, 1); } - public void AddToStack(DrawableHitObject fruit, Vector2 absolutePosition) + public void AddToStack(DrawableHitObject fruit, Vector2 absolutePosition) { fruit.RelativePositionAxes = Axes.None; fruit.Position = new Vector2(ToLocalSpace(absolutePosition).X - DrawSize.X / 2, 0); diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 4ce73e388b..6b3c6cbc20 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -1,6 +1,8 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; +using System.Linq; using osu.Game.Rulesets.Objects.Drawables; using osu.Framework.Graphics; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; @@ -133,7 +135,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables holdStartTime = null; // If the key has been released too early, the user should not receive full score for the release - if (!tail.Judged) + if (!tail.AllJudged) hasBroken = true; return true; @@ -160,12 +162,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables if (!base.OnPressed(action)) return false; - // We only want to trigger a holding state from the head if the head has received a judgement - if (!Judged) - return false; - // If the key has been released too early, the user should not receive full score for the release - if (Judgement.Result == HitResult.Miss) + if (Judgements.Any(j => j.Result == HitResult.Miss)) holdNote.hasBroken = true; // The head note also handles early hits before the body, but we want accurate early hits to count as the body being held @@ -192,17 +190,32 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables Y = 0; } - protected ManiaJudgement CreateJudgement() => new HoldNoteTailJudgement(); - - protected override void CheckJudgement(bool userTriggered) + protected override void CheckForJudgements(bool userTriggered, double timeOffset) { - base.CheckJudgement(userTriggered); + if (!userTriggered) + { + if (timeOffset > HitObject.HitWindows.Bad / 2) + { + AddJudgement(new HoldNoteTailJudgement + { + Result = HitResult.Miss, + HasBroken = holdNote.hasBroken + }); + } - var tailJudgement = Judgement as HoldNoteTailJudgement; - if (tailJudgement == null) + return; + } + + double offset = Math.Abs(timeOffset); + + if (offset > HitObject.HitWindows.Miss / 2) return; - tailJudgement.HasBroken = holdNote.hasBroken; + AddJudgement(new HoldNoteTailJudgement + { + Result = HitObject.HitWindows.ResultFor(offset) ?? HitResult.Miss, + HasBroken = holdNote.hasBroken + }); } public override bool OnPressed(ManiaAction action) => false; // Tail doesn't handle key down @@ -213,9 +226,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables if (!holdNote.holdStartTime.HasValue) return false; - if (Judgement.Result != HitResult.None) - return false; - if (action != Action) return false; diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs index 4bc925b99a..1715bb06d7 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs @@ -82,7 +82,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables protected ManiaJudgement CreateJudgement() => new HoldNoteTickJudgement(); - protected override void CheckJudgement(bool userTriggered) + protected override void CheckForJudgements(bool userTriggered, double timeOffset) { if (!userTriggered) return; @@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables if (HoldStartTime?.Invoke() > HitObject.StartTime) return; - Judgement.Result = HitResult.Perfect; + AddJudgement(new ManiaJudgement { Result = HitResult.Perfect }); } protected override void UpdateState(ArmedState state) @@ -108,9 +108,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables protected override void Update() { - if (Judgement.Result != HitResult.None) - return; - if (IsHolding?.Invoke() != true) return; diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index cfe3372c71..0d6647e0fb 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -5,6 +5,7 @@ using System; using OpenTK.Graphics; using osu.Framework.Graphics; using osu.Framework.Input.Bindings; +using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Objects.Drawables; @@ -43,21 +44,21 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables } } - protected override void CheckJudgement(bool userTriggered) + protected override void CheckForJudgements(bool userTriggered, double timeOffset) { if (!userTriggered) { - if (Judgement.TimeOffset > HitObject.HitWindows.Bad / 2) - Judgement.Result = HitResult.Miss; + if (timeOffset > HitObject.HitWindows.Bad / 2) + AddJudgement(new ManiaJudgement { Result = HitResult.Miss }); return; } - double offset = Math.Abs(Judgement.TimeOffset); + double offset = Math.Abs(timeOffset); if (offset > HitObject.HitWindows.Miss / 2) return; - Judgement.Result = HitObject.HitWindows.ResultFor(offset) ?? HitResult.Miss; + AddJudgement(new ManiaJudgement { Result = HitObject.HitWindows.ResultFor(offset) ?? HitResult.Miss }); } protected override void UpdateState(ArmedState state) diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs index 4a71b8a77d..c30c20402a 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Objects.Drawables; @@ -203,7 +204,7 @@ namespace osu.Game.Rulesets.Mania.Scoring maxComboPortion = comboPortion; } - protected override void OnNewJudgement(ManiaJudgement judgement) + protected override void OnNewJudgement(Judgement judgement) { bool isTick = judgement is HoldNoteTickJudgement; @@ -254,8 +255,10 @@ namespace osu.Game.Rulesets.Mania.Scoring foreach (var j in Judgements) { - scoreForAccuracy += j.NumericAccuracyResult; - maxScoreForAccuracy += j.MaxNumericAccuracyResult; + var maniaJudgement = (ManiaJudgement)j; + + scoreForAccuracy += maniaJudgement.NumericAccuracyResult; + maxScoreForAccuracy += maniaJudgement.MaxNumericAccuracyResult; } Accuracy.Value = (double)scoreForAccuracy / maxScoreForAccuracy; diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 8644ad45ae..5c39139956 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -99,10 +99,6 @@ {C92A607B-1FDD-4954-9F92-03FF547D9080} osu.Game.Rulesets.Osu - - {F167E17A-7DE6-4AF5-B920-A5112296C695} - osu.Game.Rulesets.Taiko - {0D3FBF8A-7464-4CF7-8C90-3E7886DF2D4D} osu.Game diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 43d7fb3016..3184b83202 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -7,6 +7,7 @@ using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using OpenTK; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu.Judgements; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -38,9 +39,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Colour = AccentColour, Hit = () => { - if (Judgement.Result != HitResult.None) return false; + if (AllJudged) + return false; - Judgement.PositionOffset = Vector2.Zero; //todo: set to correct value UpdateJudgement(true); return true; }, @@ -65,16 +66,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Size = circle.DrawSize; } - protected override void CheckJudgement(bool userTriggered) + protected override void CheckForJudgements(bool userTriggered, double timeOffset) { if (!userTriggered) { - if (Judgement.TimeOffset > HitObject.HitWindowFor(HitResult.Meh)) - Judgement.Result = HitResult.Miss; + if (timeOffset > HitObject.HitWindowFor(HitResult.Meh)) + AddJudgement(new OsuJudgement { Result = HitResult.Miss }); return; } - Judgement.Result = HitObject.ScoreResultForOffset(Math.Abs(Judgement.TimeOffset)); + AddJudgement(new OsuJudgement + { + Result = HitObject.ScoreResultForOffset(Math.Abs(timeOffset)), + PositionOffset = Vector2.Zero //todo: set to correct value + }); } protected override void UpdateInitialState() diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index e18bcc856d..6870916b5b 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -5,6 +5,7 @@ using System.ComponentModel; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Judgements; using osu.Framework.Graphics; +using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -31,7 +32,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables UpdatePreemptState(); - using (BeginDelayedSequence(TIME_PREEMPT + Judgement.TimeOffset, true)) + var offset = Time.Current - ((HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime); + using (BeginDelayedSequence(TIME_PREEMPT + offset, true)) UpdateCurrentState(state); } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs index 892d106b02..0cd30e34ac 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs @@ -11,7 +11,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { public class DrawableOsuJudgement : DrawableJudgement { - public DrawableOsuJudgement(OsuJudgement judgement) : base(judgement) + public DrawableOsuJudgement(OsuJudgement judgement) + : base(judgement) { } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 6f3cea7ac1..7cb06df679 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -8,6 +8,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using System.Collections.Generic; using System.Linq; using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Osu.Judgements; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -114,31 +115,31 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables bouncer2.Position = slider.Curve.PositionAt(body.SnakedEnd ?? 0); //todo: we probably want to reconsider this before adding scoring, but it looks and feels nice. - if (initialCircle.Judgement?.Result <= HitResult.Miss) + if (!initialCircle.Judgements.Any(j => j.IsHit)) initialCircle.Position = slider.Curve.PositionAt(progress); foreach (var c in components) c.UpdateProgress(progress, repeat); foreach (var t in ticks.Children) t.Tracking = ball.Tracking; } - protected override void CheckJudgement(bool userTriggered) + protected override void CheckForJudgements(bool userTriggered, double timeOffset) { if (!userTriggered && Time.Current >= slider.EndTime) { var ticksCount = ticks.Children.Count + 1; - var ticksHit = ticks.Children.Count(t => t.Judgement.Result > HitResult.Miss); - if (initialCircle.Judgement.Result > HitResult.Miss) + var ticksHit = ticks.Children.Count(t => t.Judgements.Any(j => j.IsHit)); + if (initialCircle.Judgements.Any(j => j.IsHit)) ticksHit++; var hitFraction = (double)ticksHit / ticksCount; - if (hitFraction == 1 && initialCircle.Judgement.Result == HitResult.Great) - Judgement.Result = HitResult.Great; - else if (hitFraction >= 0.5 && initialCircle.Judgement.Result >= HitResult.Good) - Judgement.Result = HitResult.Good; + if (hitFraction == 1 && initialCircle.Judgements.Any(j => j.Result == HitResult.Great)) + AddJudgement(new OsuJudgement { Result = HitResult.Great }); + else if (hitFraction >= 0.5 && initialCircle.Judgements.Any(j => j.Result >= HitResult.Good)) + AddJudgement(new OsuJudgement { Result = HitResult.Good }); else if (hitFraction > 0) - Judgement.Result = HitResult.Meh; + AddJudgement(new OsuJudgement { Result = HitResult.Meh }); else - Judgement.Result = HitResult.Miss; + AddJudgement(new OsuJudgement { Result = HitResult.Miss }); } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index 89d7cd1658..0938f78843 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -7,6 +7,7 @@ using osu.Game.Rulesets.Objects.Drawables; using OpenTK; using OpenTK.Graphics; using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Osu.Judgements; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -46,10 +47,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables }; } - protected override void CheckJudgement(bool userTriggered) + protected override void CheckForJudgements(bool userTriggered, double timeOffset) { - if (Judgement.TimeOffset >= 0) - Judgement.Result = Tracking ? HitResult.Perfect : HitResult.Miss; + if (timeOffset >= 0) + AddJudgement(new SliderTickJudgement { Result = Tracking ? HitResult.Perfect : HitResult.Miss }); } protected override void UpdatePreemptState() diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 923f659d27..98dd40b0e6 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -11,6 +11,7 @@ using OpenTK.Graphics; using osu.Game.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Allocation; +using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Screens.Ranking; namespace osu.Game.Rulesets.Osu.Objects.Drawables @@ -107,7 +108,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public float Progress => MathHelper.Clamp(disc.RotationAbsolute / 360 / spinner.SpinsRequired, 0, 1); - protected override void CheckJudgement(bool userTriggered) + protected override void CheckForJudgements(bool userTriggered, double timeOffset) { if (Time.Current < HitObject.StartTime) return; @@ -129,13 +130,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (!userTriggered && Time.Current >= spinner.EndTime) { if (Progress >= 1) - Judgement.Result = HitResult.Great; + AddJudgement(new OsuJudgement { Result = HitResult.Great }); else if (Progress > .9) - Judgement.Result = HitResult.Good; + AddJudgement(new OsuJudgement { Result = HitResult.Good }); else if (Progress > .75) - Judgement.Result = HitResult.Meh; + AddJudgement(new OsuJudgement { Result = HitResult.Meh }); else if (Time.Current >= spinner.EndTime) - Judgement.Result = HitResult.Miss; + AddJudgement(new OsuJudgement { Result = HitResult.Miss }); } } diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs index a2e9fa2624..b6922cbf3b 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using osu.Framework.Configuration; using osu.Framework.Extensions; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Objects; @@ -42,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Scoring hpDrainRate = beatmap.BeatmapInfo.Difficulty.DrainRate; totalAccurateJudgements = beatmap.HitObjects.Count; - foreach (var h in beatmap.HitObjects) + foreach (var unused in beatmap.HitObjects) { // TODO: add support for other object types. AddJudgement(new OsuJudgement { Result = HitResult.Great }); @@ -70,41 +71,38 @@ namespace osu.Game.Rulesets.Osu.Scoring score.Statistics[@"x"] = scoreResultCounts.GetOrDefault(HitResult.Miss); } - protected override void OnNewJudgement(OsuJudgement judgement) + protected override void OnNewJudgement(Judgement judgement) { - if (judgement != null) + var osuJudgement = (OsuJudgement)judgement; + + if (judgement.Result != HitResult.None) { - if (judgement.Result != HitResult.None) - { - scoreResultCounts[judgement.Result] = scoreResultCounts.GetOrDefault(judgement.Result) + 1; - comboResultCounts[judgement.Combo] = comboResultCounts.GetOrDefault(judgement.Combo) + 1; - } - - switch (judgement.Result) - { - case HitResult.Great: - Health.Value += (10.2 - hpDrainRate) * 0.02; - break; - - case HitResult.Good: - Health.Value += (8 - hpDrainRate) * 0.02; - break; - - case HitResult.Meh: - Health.Value += (4 - hpDrainRate) * 0.02; - break; - - /*case HitResult.SliderTick: - Health.Value += Math.Max(7 - hpDrainRate, 0) * 0.01; - break;*/ - - case HitResult.Miss: - Health.Value -= hpDrainRate * 0.04; - break; - } + scoreResultCounts[judgement.Result] = scoreResultCounts.GetOrDefault(judgement.Result) + 1; + comboResultCounts[osuJudgement.Combo] = comboResultCounts.GetOrDefault(osuJudgement.Combo) + 1; } - calculateScore(); + switch (judgement.Result) + { + case HitResult.Great: + Health.Value += (10.2 - hpDrainRate) * 0.02; + break; + + case HitResult.Good: + Health.Value += (8 - hpDrainRate) * 0.02; + break; + + case HitResult.Meh: + Health.Value += (4 - hpDrainRate) * 0.02; + break; + + /*case HitResult.SliderTick: + Health.Value += Math.Max(7 - hpDrainRate, 0) * 0.01; + break;*/ + + case HitResult.Miss: + Health.Value -= hpDrainRate * 0.04; + break; + } calculateScore(); } diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 9b88c9d1b3..6b367b8be2 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -10,6 +10,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables.Connections; using osu.Game.Rulesets.UI; using System.Linq; +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.UI.Cursor; @@ -85,12 +86,15 @@ namespace osu.Game.Rulesets.Osu.UI .OrderBy(h => h.StartTime).OfType(); } - public override void OnJudgement(DrawableHitObject judgedObject) + public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) { - DrawableOsuJudgement explosion = new DrawableOsuJudgement(judgedObject.Judgement) + var osuJudgement = (OsuJudgement)judgement; + var osuObject = (OsuHitObject)judgedObject.HitObject; + + DrawableOsuJudgement explosion = new DrawableOsuJudgement(osuJudgement) { Origin = Anchor.Centre, - Position = judgedObject.HitObject.StackedEndPosition + judgedObject.Judgement.PositionOffset + Position = osuObject.StackedEndPosition + osuJudgement.PositionOffset }; judgementLayer.Add(explosion); diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs index 3e48453460..9d6b5ca535 100644 --- a/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs +++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoJudgement.cs @@ -18,11 +18,6 @@ namespace osu.Game.Rulesets.Taiko.Judgements /// public int MaxResultValueForAccuracy => NumericResultForAccuracy(HitResult.Great); - /// - /// Whether this Judgement has a secondary hit in the case of strong hits. - /// - public virtual bool SecondHit { get; set; } - /// /// Computes the numeric result value for the combo portion of the score. /// For the accuracy portion of the score (including accuracy percentage), see . diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs index 4996cac39e..bb67784ab1 100644 --- a/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs +++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs @@ -1,25 +1,17 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Taiko.Judgements { - public class TaikoStrongHitJudgement : TaikoJudgement, IPartialJudgement + public class TaikoStrongHitJudgement : TaikoJudgement { - public bool Changed { get; set; } - - public override bool SecondHit + public TaikoStrongHitJudgement() { - get { return base.SecondHit; } - set - { - if (base.SecondHit == value) - return; - base.SecondHit = value; - - Changed = true; - } + base.Result = HitResult.Perfect; } + + public new HitResult Result => base.Result; } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs index c044ebeeb1..2396f3bf91 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs @@ -12,6 +12,7 @@ using OpenTK.Graphics; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Judgements; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { @@ -52,8 +53,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } } - protected TaikoJudgement CreateJudgement() => new TaikoJudgement { SecondHit = HitObject.IsStrong }; - protected override TaikoPiece CreateMainPiece() => new ElongatedCirclePiece(); public override bool OnPressed(TaikoAction action) => false; @@ -65,9 +64,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables accentDarkColour = colours.YellowDarker; } - private void onTickJudgement(DrawableHitObject obj) + private void onTickJudgement(DrawableHitObject obj, Judgement judgement) { - if (obj.Judgement.Result > HitResult.Miss) + if (judgement.Result > HitResult.Miss) rollingHits++; else rollingHits--; @@ -78,22 +77,24 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables MainPiece.FadeAccent(newAccent, 100); } - protected override void CheckJudgement(bool userTriggered) + protected override void CheckForJudgements(bool userTriggered, double timeOffset) { if (userTriggered) return; - if (Judgement.TimeOffset < 0) + if (timeOffset < 0) return; - int countHit = NestedHitObjects.Count(o => o.Judgement.Result > HitResult.Miss); + int countHit = NestedHitObjects.Count(o => o.AllJudged); if (countHit > HitObject.RequiredGoodHits) { - Judgement.Result = countHit >= HitObject.RequiredGreatHits ? HitResult.Great : HitResult.Good; + AddJudgement(new TaikoJudgement { Result = countHit >= HitObject.RequiredGreatHits ? HitResult.Great : HitResult.Good }); + if (HitObject.IsStrong) + AddJudgement(new TaikoStrongHitJudgement()); } else - Judgement.Result = HitResult.Miss; + AddJudgement(new TaikoJudgement { Result = HitResult.Miss }); } protected override void UpdateState(ArmedState state) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index 97c0f6bf63..8ac67ba0a6 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -34,15 +34,17 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables Filled = HitObject.FirstTick }; - protected TaikoJudgement CreateJudgement() => new TaikoDrumRollTickJudgement { SecondHit = HitObject.IsStrong }; - - protected override void CheckJudgement(bool userTriggered) + protected override void CheckForJudgements(bool userTriggered, double timeOffset) { if (!userTriggered) return; - if (Math.Abs(Judgement.TimeOffset) < HitObject.HitWindow) - Judgement.Result = HitResult.Great; + if (!(Math.Abs(timeOffset) < HitObject.HitWindow)) + return; + + AddJudgement(new TaikoDrumRollTickJudgement { Result = HitResult.Great }); + if (HitObject.IsStrong) + AddJudgement(new TaikoStrongHitJudgement()); } protected override void UpdateState(ArmedState state) @@ -55,9 +57,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } } - public override bool OnPressed(TaikoAction action) - { - return Judgement.Result == HitResult.None && UpdateJudgement(true); - } + public override bool OnPressed(TaikoAction action) => UpdateJudgement(true); } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 42195e31f3..675fa7be33 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -5,6 +5,7 @@ using System; using System.Linq; using osu.Framework.Graphics; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; namespace osu.Game.Rulesets.Taiko.Objects.Drawables @@ -27,33 +28,30 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables FillMode = FillMode.Fit; } - protected override void CheckJudgement(bool userTriggered) + protected override void CheckForJudgements(bool userTriggered, double timeOffset) { if (!userTriggered) { - if (Judgement.TimeOffset > HitObject.HitWindowGood) - Judgement.Result = HitResult.Miss; + if (timeOffset > HitObject.HitWindowGood) + AddJudgement(new TaikoJudgement { Result = HitResult.Miss }); return; } - double hitOffset = Math.Abs(Judgement.TimeOffset); + double hitOffset = Math.Abs(timeOffset); if (hitOffset > HitObject.HitWindowMiss) return; if (!validKeyPressed) - Judgement.Result = HitResult.Miss; + AddJudgement(new TaikoJudgement { Result = HitResult.Miss }); else if (hitOffset < HitObject.HitWindowGood) - Judgement.Result = hitOffset < HitObject.HitWindowGreat ? HitResult.Great : HitResult.Good; + AddJudgement(new TaikoJudgement { Result = hitOffset < HitObject.HitWindowGreat ? HitResult.Great : HitResult.Good }); else - Judgement.Result = HitResult.Miss; + AddJudgement(new TaikoJudgement { Result = HitResult.Miss }); } public override bool OnPressed(TaikoAction action) { - if (Judgement.Result != HitResult.None) - return false; - validKeyPressed = HitActions.Contains(action); return UpdateJudgement(true); @@ -71,7 +69,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables var circlePiece = MainPiece as CirclePiece; circlePiece?.FlashBox.FinishTransforms(); - using (BeginDelayedSequence(HitObject.StartTime - Time.Current + Judgement.TimeOffset, true)) + var offset = Time.Current - HitObject.StartTime; + using (BeginDelayedSequence(HitObject.StartTime - Time.Current + offset, true)) { switch (State) { diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs index 855aa6fb0b..48812093c4 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs @@ -3,7 +3,6 @@ using System; using System.Linq; -using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects.Drawables @@ -25,13 +24,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { } - protected TaikoJudgement CreateJudgement() => new TaikoStrongHitJudgement(); + private bool processedSecondHit; + public override bool AllJudged => processedSecondHit && base.AllJudged; - protected override void CheckJudgement(bool userTriggered) + protected override void CheckForJudgements(bool userTriggered, double timeOffset) { - if (Judgement.Result == HitResult.None) + if (!base.AllJudged) { - base.CheckJudgement(userTriggered); + base.CheckForJudgements(userTriggered, timeOffset); return; } @@ -41,7 +41,10 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables // If we get here, we're assured that the key pressed is the correct secondary key if (Math.Abs(firstHitTime - Time.Current) < second_hit_window) - Judgement.SecondHit = true; + { + AddJudgement(new TaikoStrongHitJudgement()); + processedSecondHit = true; + } } public override bool OnReleased(TaikoAction action) @@ -54,7 +57,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public override bool OnPressed(TaikoAction action) { // Check if we've handled the first key - if (Judgement.Result == HitResult.None) + if (!base.AllJudged) { // First key hasn't been handled yet, attempt to handle it bool handled = base.OnPressed(action); @@ -70,7 +73,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables } // If we've already hit the second key, don't handle this object any further - if (Judgement.SecondHit) + if (processedSecondHit) return false; // Don't handle represses of the first key diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs index a4fdf19c59..9c6fb1ec3d 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs @@ -13,6 +13,7 @@ using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces; using OpenTK; using OpenTK.Graphics; using osu.Framework.Graphics.Shapes; +using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects.Drawables { @@ -134,7 +135,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables Width *= Parent.RelativeChildSize.X; } - protected override void CheckJudgement(bool userTriggered) + protected override void CheckForJudgements(bool userTriggered, double timeOffset) { if (userTriggered) { @@ -152,18 +153,17 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, Easing.OutQuint); if (userHits == HitObject.RequiredHits) - Judgement.Result = HitResult.Great; + AddJudgement(new TaikoJudgement { Result = HitResult.Great }); } else { - if (Judgement.TimeOffset < 0) + if (timeOffset < 0) return; //TODO: THIS IS SHIT AND CAN'T EXIST POST-TAIKO WORLD CUP - if (userHits > HitObject.RequiredHits / 2) - Judgement.Result = HitResult.Good; - else - Judgement.Result = HitResult.Miss; + AddJudgement(userHits > HitObject.RequiredHits / 2 + ? new TaikoJudgement { Result = HitResult.Good } + : new TaikoJudgement { Result = HitResult.Miss }); } } @@ -172,8 +172,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables const float preempt = 100; const float out_transition_time = 300; + var offset = Time.Current - HitObject.EndTime; double untilStartTime = HitObject.StartTime - Time.Current; - double untilJudgement = untilStartTime + Judgement.TimeOffset + HitObject.Duration; + double untilJudgement = untilStartTime + offset + HitObject.Duration; targetRing.Delay(untilStartTime - preempt).ScaleTo(target_ring_scale, preempt * 4, Easing.OutQuint); this.Delay(untilJudgement).FadeOut(out_transition_time, Easing.Out); @@ -207,9 +208,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables public override bool OnPressed(TaikoAction action) { - if (Judgement.Result != HitResult.None) - return false; - // Don't handle keys before the swell starts if (Time.Current < HitObject.StartTime) return false; diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs index 83f3745763..ca3dcc8984 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs @@ -3,6 +3,7 @@ using System; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Judgements; @@ -138,28 +139,24 @@ namespace osu.Game.Rulesets.Taiko.Scoring { if (obj is Hit) { - AddJudgement(new TaikoJudgement - { - Result = HitResult.Great, - SecondHit = obj.IsStrong - }); + AddJudgement(new TaikoJudgement { Result = HitResult.Great }); + if (obj.IsStrong) + AddJudgement(new TaikoStrongHitJudgement()); } else if (obj is DrumRoll) { for (int i = 0; i < ((DrumRoll)obj).TotalTicks; i++) { - AddJudgement(new TaikoDrumRollTickJudgement - { - Result = HitResult.Great, - SecondHit = obj.IsStrong - }); + AddJudgement(new TaikoDrumRollTickJudgement { Result = HitResult.Great }); + + if (obj.IsStrong) + AddJudgement(new TaikoStrongHitJudgement()); } - AddJudgement(new TaikoJudgement - { - Result = HitResult.Great, - SecondHit = obj.IsStrong - }); + AddJudgement(new TaikoJudgement { Result = HitResult.Great }); + + if (obj.IsStrong) + AddJudgement(new TaikoStrongHitJudgement()); } else if (obj is Swell) { @@ -171,16 +168,40 @@ namespace osu.Game.Rulesets.Taiko.Scoring maxComboPortion = comboPortion; } - protected override void OnNewJudgement(TaikoJudgement judgement) + protected override void OnNewJudgement(Judgement judgement) { + bool isStrong = judgement is TaikoStrongHitJudgement; bool isTick = judgement is TaikoDrumRollTickJudgement; - // Don't consider ticks as a type of hit that counts towards map completion - if (!isTick) + // Don't consider ticks and strong hits as a type of hit that counts towards map completion + if (!isTick && !isStrong) totalHits++; // Apply score changes - addHitScore(judgement); + if (judgement.IsHit) + { + double baseValue = judgement.NumericResult; + + if (isStrong) + { + // Add increased score for the previous judgement by hitting a strong hit object with the second key + var prevJudgement = Judgements[Judgements.Count - 1]; + baseValue = prevJudgement.NumericResult * strongHitScale; + + } + + // Add score to portions + if (judgement is TaikoDrumRollTickJudgement) + bonusScore += baseValue; + else + { + // 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) @@ -201,50 +222,15 @@ namespace osu.Game.Rulesets.Taiko.Scoring break; } - calculateScore(); - } - - protected override void OnJudgementChanged(TaikoJudgement judgement) - { - // Apply score changes - addHitScore(judgement); - - calculateScore(); - } - - private void addHitScore(TaikoJudgement judgement) - { - if (!judgement.IsHit) - return; - - double baseValue = judgement.NumericResult; - - // Add increased score for hitting a strong hit object with the second key - if (judgement.SecondHit) - baseValue *= strongHitScale; - - // Add score to portions - if (judgement is TaikoDrumRollTickJudgement) - bonusScore += baseValue; - else - { - // 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; - } - } - - private void calculateScore() - { int scoreForAccuracy = 0; int maxScoreForAccuracy = 0; foreach (var j in Judgements) { - scoreForAccuracy += j.ResultNumericForAccuracy; - maxScoreForAccuracy += j.MaxResultValueForAccuracy; + var taikoJudgement = (TaikoJudgement)j; + + scoreForAccuracy += taikoJudgement.ResultNumericForAccuracy; + maxScoreForAccuracy += taikoJudgement.MaxResultValueForAccuracy; } Accuracy.Value = (double)scoreForAccuracy / maxScoreForAccuracy; diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs index d0ad343264..ea9c59c2a5 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs @@ -15,13 +15,16 @@ namespace osu.Game.Rulesets.Taiko.UI /// public class DrawableTaikoJudgement : DrawableJudgement { + public readonly DrawableHitObject JudgedObject; + /// /// Creates a new judgement text. /// /// The judgement to visualise. - public DrawableTaikoJudgement(TaikoJudgement judgement) + public DrawableTaikoJudgement(DrawableHitObject judgedObject, Judgement judgement) : base(judgement) { + JudgedObject = judgedObject; } [BackgroundDependencyLoader] diff --git a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs index cb849a11c7..830a01bfbc 100644 --- a/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/HitExplosion.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; -using osu.Game.Rulesets.Taiko.Judgements; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.UI @@ -18,17 +18,17 @@ namespace osu.Game.Rulesets.Taiko.UI /// internal class HitExplosion : CircularContainer { - public readonly TaikoJudgement Judgement; + public readonly DrawableHitObject JudgedObject; private readonly Box innerFill; private readonly bool isRim; - public HitExplosion(TaikoJudgement judgement, bool isRim) + public HitExplosion(DrawableHitObject judgedObject, bool isRim) { this.isRim = isRim; - Judgement = judgement; + JudgedObject = judgedObject; Anchor = Anchor.CentreLeft; Origin = Anchor.Centre; diff --git a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs index bac956a25b..e3e1d485d8 100644 --- a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs @@ -7,22 +7,22 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; -using osu.Game.Rulesets.Taiko.Judgements; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.UI { public class KiaiHitExplosion : CircularContainer { - public readonly TaikoJudgement Judgement; + public readonly DrawableHitObject JudgedObject; private readonly bool isRim; - public KiaiHitExplosion(TaikoJudgement judgement, bool isRim) + public KiaiHitExplosion(DrawableHitObject judgedObject, bool isRim) { this.isRim = isRim; - Judgement = judgement; + JudgedObject = judgedObject; Anchor = Anchor.CentreLeft; Origin = Anchor.Centre; diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 4da00a58c6..1fce2b3a48 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -14,6 +14,7 @@ using osu.Game.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Extensions.Color4Extensions; using System.Linq; +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Taiko.Objects.Drawables; namespace osu.Game.Rulesets.Taiko.UI @@ -218,18 +219,22 @@ namespace osu.Game.Rulesets.Taiko.UI swell.OnStart += () => topLevelHitContainer.Add(swell.CreateProxy()); } - public override void OnJudgement(DrawableHitObject judgedObject) + public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) { - bool wasHit = judgedObject.Judgement.Result > HitResult.Miss; - bool secondHit = judgedObject.Judgement.SecondHit; + bool wasHit = judgement.Result > HitResult.Miss; + bool secondHit = judgement is TaikoStrongHitJudgement; + var taikoObject = (TaikoHitObject)judgedObject.HitObject; - judgementContainer.Add(new DrawableTaikoJudgement(judgedObject.Judgement) + if (judgementContainer.FirstOrDefault(j => j.JudgedObject == judgedObject) == null) { - Anchor = wasHit ? Anchor.TopLeft : Anchor.CentreLeft, - Origin = wasHit ? Anchor.BottomCentre : Anchor.Centre, - RelativePositionAxes = Axes.X, - X = wasHit ? judgedObject.Position.X : 0, - }); + judgementContainer.Add(new DrawableTaikoJudgement(judgedObject, judgement) + { + Anchor = wasHit ? Anchor.TopLeft : Anchor.CentreLeft, + Origin = wasHit ? Anchor.BottomCentre : Anchor.Centre, + RelativePositionAxes = Axes.X, + X = wasHit ? judgedObject.Position.X : 0, + }); + } if (!wasHit) return; @@ -244,13 +249,13 @@ namespace osu.Game.Rulesets.Taiko.UI topLevelHitContainer.Add(judgedObject.CreateProxy()); } - hitExplosionContainer.Add(new HitExplosion(judgedObject.Judgement, isRim)); + hitExplosionContainer.Add(new HitExplosion(judgedObject, isRim)); - if (judgedObject.HitObject.Kiai) - kiaiExplosionContainer.Add(new KiaiHitExplosion(judgedObject.Judgement, isRim)); + if (taikoObject.Kiai) + kiaiExplosionContainer.Add(new KiaiHitExplosion(judgedObject, isRim)); } else - hitExplosionContainer.Children.FirstOrDefault(e => e.Judgement == judgedObject.Judgement)?.VisualiseSecondHit(); + hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == judgedObject)?.VisualiseSecondHit(); } } } diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index 80311654be..795acdc0cf 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Judgements public class DrawableJudgement : Container where TJudgement : Judgement { - protected readonly TJudgement Judgement; + protected readonly Judgement Judgement; protected readonly SpriteText JudgementText; @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Judgements /// Creates a drawable which visualises a . /// /// The judgement to visualise. - public DrawableJudgement(TJudgement judgement) + public DrawableJudgement(Judgement judgement) { Judgement = judgement; diff --git a/osu.Game/Rulesets/Judgements/Judgement.cs b/osu.Game/Rulesets/Judgements/Judgement.cs index 88b9600fac..cbdea5caf1 100644 --- a/osu.Game/Rulesets/Judgements/Judgement.cs +++ b/osu.Game/Rulesets/Judgements/Judgement.cs @@ -43,4 +43,4 @@ namespace osu.Game.Rulesets.Judgements /// The number. protected virtual int NumericResultFor(HitResult result) => result > HitResult.Miss ? 1 : 0; } -} \ No newline at end of file +} diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 9d39efb8fe..940e7a4839 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -46,13 +46,19 @@ namespace osu.Game.Rulesets.Objects.Drawables where TObject : HitObject where TJudgement : Judgement { - public event Action> OnJudgement; + public event Action OnJudgement; public override bool HandleInput => Interactive; public bool Interactive = true; - public TJudgement Judgement; + /// + /// Whether this can be judged. + /// + protected virtual bool ProvidesJudgement => true; + + private readonly List judgements = new List(); + public IReadOnlyList Judgements => judgements; protected List Samples = new List(); @@ -95,69 +101,73 @@ namespace osu.Game.Rulesets.Objects.Drawables UpdateState(State); } - /// - /// Whether this hit object and all of its nested hit objects have been judged. - /// - public bool Judged => (Judgement?.Result ?? HitResult.None) != HitResult.None && (NestedHitObjects?.All(h => h.Judged) ?? true); + private bool hasJudgementResult; + private bool judgementOccurred; /// - /// Process a hit of this hitobject. Carries out judgement. + /// Whether this and all of its nested s have been judged. /// - /// Whether a hit was processed. - protected bool UpdateJudgement(bool userTriggered) + public virtual bool AllJudged => (!ProvidesJudgement || hasJudgementResult) && (NestedHitObjects?.All(h => h.AllJudged) ?? true); + + /// + /// Notifies that a new judgement has occurred for this . + /// + /// The . + protected void AddJudgement(Judgement judgement) { - if (Judgement == null) - return false; + hasJudgementResult = judgement.Result >= HitResult.Miss; + judgementOccurred = true; - var partial = Judgement as IPartialJudgement; + // Ensure that the judgement is given a valid time offset, because this may not get set by the caller + var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; + judgement.TimeOffset = Time.Current - endTime; - // Never re-process non-partial hits - if (Judgement.Result != HitResult.None && partial == null) - return false; + judgements.Add(judgement); - // Update the judgement state - double endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; - Judgement.TimeOffset = Time.Current - endTime; - - // Update the judgement state - bool hadResult = Judgement.Result != HitResult.None; - CheckJudgement(userTriggered); - - // Don't process judgements with no result - if (Judgement.Result == HitResult.None) - return false; - - // Don't process judgements that previously had results but the results were unchanged - if (hadResult && partial?.Changed != true) - return false; - - switch (Judgement.Result) - { - default: - State = ArmedState.Hit; - break; - case HitResult.Miss: - State = ArmedState.Miss; - break; - } - - OnJudgement?.Invoke(this); - - if (partial != null) - partial.Changed = false; - - return true; + OnJudgement?.Invoke(this, judgement); } - protected virtual void CheckJudgement(bool userTriggered) + /// + /// Processes this , checking if any judgements have occurred. + /// + /// Whether the user triggered this process. + /// Whether a judgement has occurred from this or any nested s. + protected bool UpdateJudgement(bool userTriggered) { + judgementOccurred = false; + + if (AllJudged) + return false; + if (NestedHitObjects != null) { foreach (var d in NestedHitObjects) - d.CheckJudgement(userTriggered); + { + if (d.AllJudged) + continue; + + d.UpdateJudgement(userTriggered); + + if (d.AllJudged) + return true; + } } + + var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; + CheckForJudgements(userTriggered, Time.Current - endTime); + + return judgementOccurred; } + /// + /// Checks if any judgements have occurred for this . This method must construct + /// all s and notify of them through . + /// + /// Whether the user triggered this check. + /// The offset from the end time at which this check occurred. A > 0 + /// implies that this check occurred after the end time of . + protected virtual void CheckForJudgements(bool userTriggered, double timeOffset) { } + protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); @@ -188,7 +198,7 @@ namespace osu.Game.Rulesets.Objects.Drawables if (nestedHitObjects == null) nestedHitObjects = new List>(); - h.OnJudgement += d => OnJudgement?.Invoke(d); + h.OnJudgement += (d, j) => OnJudgement?.Invoke(d, j); nestedHitObjects.Add(h); } diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 42dabfbdff..aea7e40df4 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -142,7 +142,7 @@ namespace osu.Game.Rulesets.Scoring /// /// All judgements held by this ScoreProcessor. /// - protected readonly List Judgements = new List(); + protected readonly List Judgements = new List(); public override bool HasFailed => Health.Value == Health.MinValue; @@ -171,7 +171,7 @@ namespace osu.Game.Rulesets.Scoring /// Adds a judgement to this ScoreProcessor. /// /// The judgement to add. - protected void AddJudgement(TJudgement judgement) + protected void AddJudgement(Judgement judgement) { bool exists = Judgements.Contains(judgement); @@ -197,8 +197,6 @@ namespace osu.Game.Rulesets.Scoring NotifyNewJudgement(judgement); } - else - OnJudgementChanged(judgement); UpdateFailed(); } @@ -212,20 +210,8 @@ namespace osu.Game.Rulesets.Scoring /// /// Updates any values that need post-processing. Invoked when a new judgement has occurred. - /// - /// This is not triggered when existing judgements are changed - for that see . - /// /// /// The judgement that triggered this calculation. - protected abstract void OnNewJudgement(TJudgement judgement); - - /// - /// Updates any values that need post-processing. Invoked when an existing judgement has changed. - /// - /// This is not triggered when a new judgement has occurred - for that see . - /// - /// - /// The judgement that triggered this calculation. - protected virtual void OnJudgementChanged(TJudgement judgement) { } + protected abstract void OnNewJudgement(Judgement judgement); } } diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index cc16eff6d6..ba526e2178 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.UI /// Triggered when an object's Judgement is updated. /// /// The object that Judgement has been updated for. - public virtual void OnJudgement(DrawableHitObject judgedObject) { } + public virtual void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) { } public class HitObjectContainer : CompositeDrawable { diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index a7472f4dbc..d87e47794f 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -230,7 +230,7 @@ namespace osu.Game.Rulesets.UI where TObject : HitObject where TJudgement : Judgement { - public event Action OnJudgement; + public event Action OnJudgement; public sealed override bool ProvidingUserCursor => !HasReplayLoaded && Playfield.ProvidingUserCursor; @@ -239,7 +239,7 @@ namespace osu.Game.Rulesets.UI /// public new IEnumerable Objects => Beatmap.HitObjects; - protected override bool AllObjectsJudged => drawableObjects.All(h => h.Judged); + protected override bool AllObjectsJudged => drawableObjects.All(h => h.AllJudged); /// /// The playfield. @@ -298,7 +298,12 @@ namespace osu.Game.Rulesets.UI if (drawableObject == null) continue; - drawableObject.OnJudgement += onJudgement; + drawableObject.OnJudgement += (d, j) => + { + Playfield.OnJudgement(d, j); + OnJudgement?.Invoke(j); + CheckAllJudged(); + }; drawableObjects.Add(drawableObject); Playfield.Add(drawableObject); @@ -320,19 +325,6 @@ namespace osu.Game.Rulesets.UI /// protected virtual Vector2 GetPlayfieldAspectAdjust() => new Vector2(0.75f); //a sane default - /// - /// Triggered when an object's Judgement is updated. - /// - /// The object that Judgement has been updated for. - private void onJudgement(DrawableHitObject judgedObject) - { - Playfield.OnJudgement(judgedObject); - - OnJudgement?.Invoke(judgedObject.Judgement); - - CheckAllJudged(); - } - /// /// Creates a DrawableHitObject from a HitObject. ///