diff --git a/osu.Game.Rulesets.Mania/Testing/TestCaseManiaHitObjects.cs b/osu.Game.Rulesets.Mania/Tests/TestCaseManiaHitObjects.cs similarity index 96% rename from osu.Game.Rulesets.Mania/Testing/TestCaseManiaHitObjects.cs rename to osu.Game.Rulesets.Mania/Tests/TestCaseManiaHitObjects.cs index a5568b7f6d..4230171288 100644 --- a/osu.Game.Rulesets.Mania/Testing/TestCaseManiaHitObjects.cs +++ b/osu.Game.Rulesets.Mania/Tests/TestCaseManiaHitObjects.cs @@ -10,7 +10,7 @@ using osu.Game.Tests.Visual; using OpenTK; using OpenTK.Graphics; -namespace osu.Game.Rulesets.Mania.Testing +namespace osu.Game.Rulesets.Mania.Tests { [TestFixture] internal class TestCaseManiaHitObjects : OsuTestCase diff --git a/osu.Game.Rulesets.Mania/Testing/TestCaseManiaPlayfield.cs b/osu.Game.Rulesets.Mania/Tests/TestCaseManiaPlayfield.cs similarity index 96% rename from osu.Game.Rulesets.Mania/Testing/TestCaseManiaPlayfield.cs rename to osu.Game.Rulesets.Mania/Tests/TestCaseManiaPlayfield.cs index 4b68334efb..c1de273a1b 100644 --- a/osu.Game.Rulesets.Mania/Testing/TestCaseManiaPlayfield.cs +++ b/osu.Game.Rulesets.Mania/Tests/TestCaseManiaPlayfield.cs @@ -17,7 +17,7 @@ using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Timing; using osu.Game.Tests.Visual; -namespace osu.Game.Rulesets.Mania.Testing +namespace osu.Game.Rulesets.Mania.Tests { [TestFixture] internal class TestCaseManiaPlayfield : OsuTestCase diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index fa8b9d35aa..967f23bfd3 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -80,8 +80,8 @@ - - + + diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index 53b3427fc4..8a96640b1e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public override bool RemoveWhenNotAlive => false; + public override bool DisplayJudgement => false; + public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick) { this.sliderTick = sliderTick; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 98dd40b0e6..054a2067ec 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -22,6 +22,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private readonly SpinnerDisc disc; private readonly SpinnerTicks ticks; + private readonly SpinnerSpmCounter spmCounter; private readonly Container mainContainer; @@ -103,6 +104,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables }, } }, + spmCounter = new SpinnerSpmCounter + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Y = 120, + Alpha = 0 + } }; } @@ -157,6 +165,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void Update() { disc.Tracking = OsuActionInputManager.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton); + if (!spmCounter.IsPresent && disc.Tracking) + spmCounter.FadeIn(TIME_FADEIN); base.Update(); } @@ -167,6 +177,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables circle.Rotation = disc.Rotation; ticks.Rotation = disc.Rotation; + spmCounter.SetRotation(disc.RotationAbsolute); float relativeCircleScale = spinner.Scale * circle.DrawHeight / mainContainer.DrawHeight; disc.ScaleTo(relativeCircleScale + (1 - relativeCircleScale) * Progress, 200, Easing.OutQuint); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs index 6577c7fd50..ca75a61738 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs @@ -77,7 +77,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private float lastAngle; private float currentRotation; public float RotationAbsolute; - private int completeTick; private bool updateCompleteTick() => completeTick != (completeTick = (int)(RotationAbsolute / 360)); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs new file mode 100644 index 0000000000..c079d3343b --- /dev/null +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs @@ -0,0 +1,75 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces +{ + public class SpinnerSpmCounter : Container + { + private readonly OsuSpriteText spmText; + + public SpinnerSpmCounter() + { + Children = new Drawable[] + { + spmText = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = @"0", + Font = @"Venera", + TextSize = 24 + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = @"SPINS PER MINUTE", + Font = @"Venera", + TextSize = 12, + Y = 30 + } + }; + } + + private double spm; + + public double SpinsPerMinute + { + get { return spm; } + private set + { + if (value == spm) return; + spm = value; + spmText.Text = Math.Truncate(value).ToString(@"#0"); + } + } + + private struct RotationRecord + { + public float Rotation; + public double Time; + } + + private readonly Queue records = new Queue(); + private const double spm_count_duration = 595; // not using hundreds to avoid frame rounding issues + + public void SetRotation(float currentRotation) + { + if (records.Count > 0) + { + var record = records.Peek(); + while (Time.Current - records.Peek().Time > spm_count_duration) + record = records.Dequeue(); + SpinsPerMinute = (currentRotation - record.Rotation) / (Time.Current - record.Time) * 1000 * 60 / 360; + } + + records.Enqueue(new RotationRecord { Rotation = currentRotation, Time = Time.Current }); + } + } +} diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 1bb4e8493b..89f6a4e255 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -91,6 +91,9 @@ namespace osu.Game.Rulesets.Osu.UI var osuJudgement = (OsuJudgement)judgement; var osuObject = (OsuHitObject)judgedObject.HitObject; + if (!judgedObject.DisplayJudgement) + return; + DrawableOsuJudgement explosion = new DrawableOsuJudgement(osuJudgement) { Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 300000754c..6bad45b8ca 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -68,6 +68,7 @@ + diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs index 8ac67ba0a6..e662f61bbe 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs @@ -21,6 +21,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables FillMode = FillMode.Fit; } + public override bool DisplayJudgement => false; + protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index d9a216bbfc..136da8a532 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -221,7 +221,7 @@ namespace osu.Game.Rulesets.Taiko.UI public override void OnJudgement(DrawableHitObject judgedObject, Judgement judgement) { - if (judgementContainer.FirstOrDefault(j => j.JudgedObject == judgedObject) == null) + if (judgedObject.DisplayJudgement && judgementContainer.FirstOrDefault(j => j.JudgedObject == judgedObject) == null) { judgementContainer.Add(new DrawableTaikoJudgement(judgedObject, judgement) { diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 7a26a53c2a..bcd6734af6 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -25,6 +25,11 @@ namespace osu.Game.Rulesets.Objects.Drawables /// public virtual Color4 AccentColour { get; set; } = Color4.Gray; + /// + /// Whether a visible judgement should be displayed when this representation is hit. + /// + public virtual bool DisplayJudgement => true; + protected DrawableHitObject(HitObject hitObject) { HitObject = hitObject;