From f219b7f9fb8fa980c2556245b59781a1eb64abc4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Nov 2017 19:31:30 +0900 Subject: [PATCH 01/27] Fix bonusScore being stored locally instead of incrementally changing --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 5a54c679dd..ec5d47c7c7 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -174,6 +174,7 @@ public class ScoreProcessor : ScoreProcessor private double maxBaseScore; private double rollingMaxBaseScore; private double baseScore; + private double bonusScore; protected ScoreProcessor() { @@ -219,7 +220,6 @@ protected void AddJudgement(Judgement judgement) protected virtual void OnNewJudgement(Judgement judgement) { - double bonusScore = 0; if (judgement.AffectsCombo) { @@ -271,6 +271,7 @@ protected override void Reset(bool storeResults) Hits = 0; baseScore = 0; rollingMaxBaseScore = 0; + bonusScore = 0; } } From 6883b3742ff1cc3eb4427194da84c4924829c47d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Nov 2017 20:23:28 +0900 Subject: [PATCH 02/27] Make initial DrawableOsuHitObject initial states not use transforms --- .../Objects/Drawables/DrawableHitCircle.cs | 20 +++++++++++-------- .../Objects/Drawables/DrawableOsuHitObject.cs | 5 +++-- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 3184b83202..ed0578d3a4 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -86,15 +86,19 @@ protected override void UpdateInitialState() { base.UpdateInitialState(); - // sane defaults - ring.Show(); - circle.Show(); - number.Show(); - glow.Show(); + // Hide() cannot be used here, because when rewinding, we need these to be the final values - ApproachCircle.Hide(); - ApproachCircle.ScaleTo(new Vector2(4)); - explode.Hide(); + ring.Alpha = 1; + circle.Alpha = 1; + number.Alpha = 1; + glow.Alpha = 1; + + ApproachCircle.Alpha = 0; + ApproachCircle.Scale = new Vector2(4); + explode.Alpha = 0; + flash.Alpha = 0; + + Scale = new Vector2(HitObject.Scale); } protected override void UpdatePreemptState() diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 9205f43a6d..7429f084c3 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -23,7 +23,7 @@ protected DrawableOsuHitObject(OsuHitObject hitObject) protected sealed override void UpdateState(ArmedState state) { - FinishTransforms(); + ClearTransforms(true); using (BeginAbsoluteSequence(HitObject.StartTime - TIME_PREEMPT, true)) { @@ -38,7 +38,8 @@ protected sealed override void UpdateState(ArmedState state) protected virtual void UpdateInitialState() { - Hide(); + // Hide() cannot be used here, because when rewinding, we need these to be the final values + Alpha = 0; } protected virtual void UpdatePreemptState() From fe00ac7e4136d125c97e99ce3c17a128a7c57d47 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Nov 2017 21:21:07 +0900 Subject: [PATCH 03/27] Make DrawableHitObject/ScoreProcessor support rewinding --- .../Objects/Drawables/DrawableHoldNoteTick.cs | 2 +- .../Tests/TestCaseHitObjects.cs | 2 +- .../Objects/Drawables/DrawableHit.cs | 2 +- .../Rulesets/Judgements/DrawableJudgement.cs | 2 +- osu.Game/Rulesets/Judgements/Judgement.cs | 13 +++ .../Objects/Drawables/DrawableHitObject.cs | 87 +++++++++++-------- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 39 ++++++++- osu.Game/Rulesets/UI/RulesetContainer.cs | 3 + 8 files changed, 107 insertions(+), 43 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs index 324f4e4e99..557fbf6ea8 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs @@ -94,7 +94,7 @@ protected override void CheckForJudgements(bool userTriggered, double timeOffset protected override void UpdateState(ArmedState state) { - switch (State) + switch (State.Value) { case ArmedState.Hit: AccentColour = Color4.Green; diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseHitObjects.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseHitObjects.cs index 2ac15c55a7..99526b64ee 100644 --- a/osu.Game.Rulesets.Osu/Tests/TestCaseHitObjects.cs +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseHitObjects.cs @@ -111,7 +111,7 @@ private void add(DrawableOsuHitObject h) h.Depth = depth++; if (auto) - h.State = ArmedState.Hit; + h.State.Value = ArmedState.Hit; playfieldContainer.Add(h); var proxyable = h as IDrawableHitObjectWithProxiedApproach; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 55eaa8dbb8..6c14a71a4c 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -72,7 +72,7 @@ protected override void UpdateState(ArmedState state) var offset = !AllJudged ? 0 : Time.Current - HitObject.StartTime; using (BeginDelayedSequence(HitObject.StartTime - Time.Current + offset, true)) { - switch (State) + switch (State.Value) { case ArmedState.Idle: this.Delay(HitObject.HitWindowMiss).Expire(); diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index 12edfd802a..5ab4b7636b 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -83,7 +83,7 @@ protected override void LoadComplete() break; } - Expire(); + Expire(true); } } } diff --git a/osu.Game/Rulesets/Judgements/Judgement.cs b/osu.Game/Rulesets/Judgements/Judgement.cs index 0ae33272a7..a1920097d3 100644 --- a/osu.Game/Rulesets/Judgements/Judgement.cs +++ b/osu.Game/Rulesets/Judgements/Judgement.cs @@ -17,6 +17,19 @@ public class Judgement /// public virtual HitResult MaxResult => HitResult.Perfect; + /// + /// The combo prior to this judgement occurring. + /// + internal int ComboAtJudgement { get; set; } + + /// + /// The highest combo achieved prior to this judgement occurring. + /// + internal int HighestComboAtJudgement { get; set; } + + /// + /// Whether a successful hit occurred. + /// public bool IsHit => Result > HitResult.Miss; /// diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index bcd6734af6..9b4f7e7fc7 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -13,6 +13,7 @@ using osu.Game.Audio; using System.Linq; using osu.Game.Graphics; +using osu.Framework.Configuration; namespace osu.Game.Rulesets.Objects.Drawables { @@ -30,6 +31,9 @@ public abstract class DrawableHitObject : Container, IHasAccentColour /// public virtual bool DisplayJudgement => true; + public override bool RemoveCompletedTransforms => false; + public override bool RemoveWhenNotAlive => false; + protected DrawableHitObject(HitObject hitObject) { HitObject = hitObject; @@ -40,6 +44,7 @@ public abstract class DrawableHitObject : DrawableHitObject where TObject : HitObject { public event Action OnJudgement; + public event Action OnJudgementRemoved; public new readonly TObject HitObject; @@ -56,31 +61,42 @@ public abstract class DrawableHitObject : DrawableHitObject protected List Samples = new List(); + public readonly Bindable State = new Bindable(); + protected DrawableHitObject(TObject hitObject) : base(hitObject) { HitObject = hitObject; } - private ArmedState state; - public ArmedState State + [BackgroundDependencyLoader] + private void load(AudioManager audio) { - get { return state; } - - set + foreach (SampleInfo sample in HitObject.Samples) { - if (state == value) - return; - state = value; + SampleChannel channel = audio.Sample.Get($@"Gameplay/{sample.Bank}-{sample.Name}"); - if (!IsLoaded) - return; + if (channel == null) + continue; + channel.Volume.Value = sample.Volume; + Samples.Add(channel); + } + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + State.ValueChanged += state => + { UpdateState(state); if (State == ArmedState.Hit) PlaySamples(); - } + }; + + State.TriggerChange(); } protected void PlaySamples() @@ -88,16 +104,8 @@ protected void PlaySamples() Samples.ForEach(s => s?.Play()); } - protected override void LoadComplete() - { - base.LoadComplete(); - - //force application of the state that was set before we loaded. - UpdateState(State); - } - - private bool hasJudgementResult; private bool judgementOccurred; + private bool hasJudgementResult => Judgements.LastOrDefault()?.Result >= HitResult.Miss; /// /// Whether this and all of its nested s have been judged. @@ -110,7 +118,6 @@ protected override void LoadComplete() /// The . protected void AddJudgement(Judgement judgement) { - hasJudgementResult = judgement.Result >= HitResult.Miss; judgementOccurred = true; // Ensure that the judgement is given a valid time offset, because this may not get set by the caller @@ -124,10 +131,10 @@ protected void AddJudgement(Judgement judgement) case HitResult.None: break; case HitResult.Miss: - State = ArmedState.Miss; + State.Value = ArmedState.Miss; break; default: - State = ArmedState.Hit; + State.Value = ArmedState.Hit; break; } @@ -170,6 +177,25 @@ protected bool UpdateJudgement(bool userTriggered) /// implies that this check occurred after the end time of . protected virtual void CheckForJudgements(bool userTriggered, double timeOffset) { } + protected override void Update() + { + base.Update(); + + var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; + + while (judgements.Count > 0) + { + var lastJudgement = judgements[judgements.Count - 1]; + if (lastJudgement.TimeOffset + endTime <= Time.Current) + break; + + judgements.RemoveAt(judgements.Count - 1); + State.Value = ArmedState.Idle; + + OnJudgementRemoved?.Invoke(this, lastJudgement); + } + } + protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); @@ -177,21 +203,6 @@ protected override void UpdateAfterChildren() UpdateJudgement(false); } - [BackgroundDependencyLoader] - private void load(AudioManager audio) - { - foreach (SampleInfo sample in HitObject.Samples) - { - SampleChannel channel = audio.Sample.Get($@"Gameplay/{sample.Bank}-{sample.Name}"); - - if (channel == null) - continue; - - channel.Volume.Value = sample.Volume; - Samples.Add(channel); - } - } - private List> nestedHitObjects; protected IEnumerable> NestedHitObjects => nestedHitObjects; diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index ec5d47c7c7..4dd88600b2 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -185,6 +185,7 @@ public ScoreProcessor(RulesetContainer rulesetContainer) Debug.Assert(base_portion + combo_portion == 1.0); rulesetContainer.OnJudgement += AddJudgement; + rulesetContainer.OnJudgementRemoved += RemoveJudgement; SimulateAutoplay(rulesetContainer.Beatmap); Reset(true); @@ -213,13 +214,26 @@ protected virtual void SimulateAutoplay(Beatmap beatmap) { } protected void AddJudgement(Judgement judgement) { OnNewJudgement(judgement); - NotifyNewJudgement(judgement); + updateScore(); + NotifyNewJudgement(judgement); UpdateFailed(); } + protected void RemoveJudgement(Judgement judgement) + { + OnJudgementRemoved(judgement); + updateScore(); + } + + /// + /// Applies a judgement. + /// + /// The judgement to apply/ protected virtual void OnNewJudgement(Judgement judgement) { + judgement.ComboAtJudgement = Combo; + judgement.HighestComboAtJudgement = HighestCombo; if (judgement.AffectsCombo) { @@ -242,7 +256,30 @@ protected virtual void OnNewJudgement(Judgement judgement) } else if (judgement.IsHit) bonusScore += judgement.NumericResult; + } + /// + /// Removes a judgement. This should reverse everything in . + /// + /// The judgement to remove. + protected virtual void OnJudgementRemoved(Judgement judgement) + { + Combo.Value = judgement.ComboAtJudgement; + HighestCombo.Value = judgement.HighestComboAtJudgement; + + if (judgement.AffectsCombo) + { + baseScore -= judgement.NumericResult; + rollingMaxBaseScore -= judgement.MaxNumericResult; + + Hits--; + } + else if (judgement.IsHit) + bonusScore -= judgement.NumericResult; + } + + private void updateScore() + { if (rollingMaxBaseScore != 0) Accuracy.Value = baseScore / rollingMaxBaseScore; diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index 6f53b76031..36dce7218d 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -104,6 +104,7 @@ public abstract class RulesetContainer : RulesetContainer where TObject : HitObject { public event Action OnJudgement; + public event Action OnJudgementRemoved; /// /// The Beatmap @@ -241,6 +242,8 @@ private void loadObjects() OnJudgement?.Invoke(j); }; + drawableObject.OnJudgementRemoved += (d, j) => { OnJudgementRemoved?.Invoke(j); }; + Playfield.Add(drawableObject); } From 8ee13ef0aea5c09b22ece39ba0e439048a8ce148 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Nov 2017 21:33:31 +0900 Subject: [PATCH 04/27] Properties are unnecessary --- osu.Game/Rulesets/Judgements/Judgement.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Judgements/Judgement.cs b/osu.Game/Rulesets/Judgements/Judgement.cs index a1920097d3..684ee0b403 100644 --- a/osu.Game/Rulesets/Judgements/Judgement.cs +++ b/osu.Game/Rulesets/Judgements/Judgement.cs @@ -20,12 +20,12 @@ public class Judgement /// /// The combo prior to this judgement occurring. /// - internal int ComboAtJudgement { get; set; } + internal int ComboAtJudgement; /// /// The highest combo achieved prior to this judgement occurring. /// - internal int HighestComboAtJudgement { get; set; } + internal int HighestComboAtJudgement; /// /// Whether a successful hit occurred. From 326891f51c3f7e0a58bb6b62863a1b20d0b1b92c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Nov 2017 21:54:28 +0900 Subject: [PATCH 05/27] Add "Final" to better determine when to stop processing the hitobject --- osu.Game/Rulesets/Judgements/Judgement.cs | 5 +++++ osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Judgements/Judgement.cs b/osu.Game/Rulesets/Judgements/Judgement.cs index 684ee0b403..2b5c4aae95 100644 --- a/osu.Game/Rulesets/Judgements/Judgement.cs +++ b/osu.Game/Rulesets/Judgements/Judgement.cs @@ -32,6 +32,11 @@ public class Judgement /// public bool IsHit => Result > HitResult.Miss; + /// + /// Whether this judgement is the final judgement for the hit object. + /// + public bool Final = true; + /// /// The offset from a perfect hit at which this judgement occurred. /// Populated when added via . diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 9b4f7e7fc7..19bddd05e0 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -105,12 +105,12 @@ protected void PlaySamples() } private bool judgementOccurred; - private bool hasJudgementResult => Judgements.LastOrDefault()?.Result >= HitResult.Miss; + private bool judgementFinalized => judgements.LastOrDefault()?.Final == true; /// /// Whether this and all of its nested s have been judged. /// - public virtual bool AllJudged => (!ProvidesJudgement || hasJudgementResult) && (NestedHitObjects?.All(h => h.AllJudged) ?? true); + public virtual bool AllJudged => (!ProvidesJudgement || judgementFinalized) && (NestedHitObjects?.All(h => h.AllJudged) ?? true); /// /// Notifies that a new judgement has occurred for this . @@ -159,7 +159,7 @@ protected bool UpdateJudgement(bool userTriggered) judgementOccurred |= d.UpdateJudgement(userTriggered); } - if (!ProvidesJudgement || hasJudgementResult || judgementOccurred) + if (!ProvidesJudgement || judgementFinalized || judgementOccurred) return judgementOccurred; var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime; From e2b6003f9864ff412df4d3ebe842f1b13ecc9f17 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Nov 2017 21:55:50 +0900 Subject: [PATCH 06/27] Make taiko use the new "Final" field Ensures that the first hit on HitStrongs is _always_ non-final unless it was a miss. The second hit is always final. --- .../Judgements/TaikoStrongHitJudgement.cs | 4 +--- .../Objects/Drawables/DrawableHit.cs | 15 +++++++++++- .../Objects/Drawables/DrawableHitStrong.cs | 24 +++++++++---------- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs index f0b57e5c09..07c499b56c 100644 --- a/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs +++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs @@ -11,9 +11,7 @@ public class TaikoStrongHitJudgement : TaikoJudgement public TaikoStrongHitJudgement() { - base.Result = HitResult.Perfect; + Final = true; } - - public new HitResult Result => base.Result; } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 6c14a71a4c..abb4c7770e 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -17,6 +17,11 @@ public abstract class DrawableHit : DrawableTaikoHitObject /// protected abstract TaikoAction[] HitActions { get; } + /// + /// Whether a second hit is allowed to be processed. + /// + protected bool SecondHitAllowed { get; private set; } + /// /// Whether the last key pressed is a valid hit key. /// @@ -45,7 +50,15 @@ protected override void CheckForJudgements(bool userTriggered, double timeOffset if (!validKeyPressed) AddJudgement(new TaikoJudgement { Result = HitResult.Miss }); else if (hitOffset < HitObject.HitWindowGood) - AddJudgement(new TaikoJudgement { Result = hitOffset < HitObject.HitWindowGreat ? HitResult.Great : HitResult.Good }); + { + AddJudgement(new TaikoJudgement + { + Result = hitOffset < HitObject.HitWindowGreat ? HitResult.Great : HitResult.Good, + Final = !HitObject.IsStrong + }); + + SecondHitAllowed = true; + } else AddJudgement(new TaikoJudgement { Result = HitResult.Miss }); } diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs index 48812093c4..c07eaf4d8b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Taiko.Judgements; namespace osu.Game.Rulesets.Taiko.Objects.Drawables @@ -24,27 +25,25 @@ protected DrawableHitStrong(Hit hit) { } - private bool processedSecondHit; - public override bool AllJudged => processedSecondHit && base.AllJudged; - protected override void CheckForJudgements(bool userTriggered, double timeOffset) { - if (!base.AllJudged) + if (!SecondHitAllowed) { base.CheckForJudgements(userTriggered, timeOffset); return; } if (!userTriggered) + { + if (timeOffset > second_hit_window) + AddJudgement(new TaikoStrongHitJudgement { Result = HitResult.Miss }); return; + } // 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) - { - AddJudgement(new TaikoStrongHitJudgement()); - processedSecondHit = true; - } + AddJudgement(new TaikoStrongHitJudgement { Result = HitResult.Great }); } public override bool OnReleased(TaikoAction action) @@ -56,8 +55,11 @@ public override bool OnReleased(TaikoAction action) public override bool OnPressed(TaikoAction action) { + if (AllJudged) + return false; + // Check if we've handled the first key - if (!base.AllJudged) + if (!SecondHitAllowed) { // First key hasn't been handled yet, attempt to handle it bool handled = base.OnPressed(action); @@ -72,10 +74,6 @@ public override bool OnPressed(TaikoAction action) return handled; } - // If we've already hit the second key, don't handle this object any further - if (processedSecondHit) - return false; - // Don't handle represses of the first key if (firstHitAction == action) return false; From 0620d0bd7a2e21034d7a96e10acc556c958b79b6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Nov 2017 21:56:09 +0900 Subject: [PATCH 07/27] AllJudged does not need to be virtual anymore --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 19bddd05e0..091af04106 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -110,7 +110,7 @@ protected void PlaySamples() /// /// Whether this and all of its nested s have been judged. /// - public virtual bool AllJudged => (!ProvidesJudgement || judgementFinalized) && (NestedHitObjects?.All(h => h.AllJudged) ?? true); + public bool AllJudged => (!ProvidesJudgement || judgementFinalized) && (NestedHitObjects?.All(h => h.AllJudged) ?? true); /// /// Notifies that a new judgement has occurred for this . From 240997e4fbbd95617e4cd02af875ad413e4b13cf Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Nov 2017 21:56:18 +0900 Subject: [PATCH 08/27] Remove duplicate property --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index 8a96640b1e..7199691ae6 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -20,8 +20,6 @@ public class DrawableSliderTick : DrawableOsuHitObject public bool Tracking; - public override bool RemoveWhenNotAlive => false; - public override bool DisplayJudgement => false; public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick) From 5c2b1d4be2941166b6e7c48c80bf44026375d8c9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Nov 2017 21:58:29 +0900 Subject: [PATCH 09/27] Update xmldoc --- osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index abb4c7770e..489eacf386 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -18,7 +18,7 @@ public abstract class DrawableHit : DrawableTaikoHitObject protected abstract TaikoAction[] HitActions { get; } /// - /// Whether a second hit is allowed to be processed. + /// Whether a second hit is allowed to be processed. This occurs once this hit object has been hit successfully. /// protected bool SecondHitAllowed { get; private set; } From f7540e28baf59f8c063a1abd11b39d5db550f1e6 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Nov 2017 22:22:53 +0900 Subject: [PATCH 10/27] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index ef10edfc75..c8222d1dc9 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit ef10edfc750b39258edbff46019f1d10700548c2 +Subproject commit c8222d1dc932aafe17ec42bfbe6cbec81851f55d From 3f20caa543897e87b93f2a2da3aeda86b1f004a0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 2 Nov 2017 23:31:50 +0900 Subject: [PATCH 11/27] Make taiko stop crashing for now --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 136da8a532..ac3796f5b8 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -244,7 +244,12 @@ public override void OnJudgement(DrawableHitObject judgedObject, Judgement judge if (judgedObject.X >= -0.05f && judgedObject is DrawableHit) { // If we're far enough away from the left stage, we should bring outselves in front of it - topLevelHitContainer.Add(judgedObject.CreateProxy()); + // Todo: The following try-catch is temporary for replay rewinding support + try + { + topLevelHitContainer.Add(judgedObject.CreateProxy()); + } + catch { } } hitExplosionContainer.Add(new HitExplosion(judgedObject, isRim)); From 70ea3e50253210f52b72d8ae7aff00b6d4c19ff1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 3 Nov 2017 15:29:16 +0900 Subject: [PATCH 12/27] Fix up initial scale of DrawableRepeatPoint --- .../Objects/Drawables/DrawableRepeatPoint.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index bb200c9ecd..235a646ac8 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -47,16 +47,20 @@ protected override void CheckForJudgements(bool userTriggered, double timeOffset AddJudgement(new OsuJudgement { Result = drawableSlider.Tracking ? HitResult.Great : HitResult.Miss }); } + protected override void UpdateInitialState() + { + base.UpdateInitialState(); + + Scale = new Vector2(0.5f); + } + protected override void UpdatePreemptState() { var animIn = Math.Min(150, repeatPoint.StartTime - FadeInTime); - this.Animate( - d => d.FadeIn(animIn), - d => d.ScaleTo(0.5f).ScaleTo(1.2f, animIn) - ).Then( - d => d.ScaleTo(1, 150, Easing.Out) - ); + this.FadeIn(animIn).ScaleTo(1.2f, animIn) + .Then() + .ScaleTo(1, 150, Easing.Out); } protected override void UpdateCurrentState(ArmedState state) From 06a62edeb635602211eba9e1f7ab83acb0faa6d1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 3 Nov 2017 15:30:46 +0900 Subject: [PATCH 13/27] Make DrawableRepeatPoints show up when replayed Fixes #1458 --- .../Objects/Drawables/DrawableRepeatPoint.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index 235a646ac8..200c697a0f 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -18,13 +18,16 @@ public class DrawableRepeatPoint : DrawableOsuHitObject public double FadeInTime; public double FadeOutTime; - public override bool RemoveWhenNotAlive => false; - - public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider) : base(repeatPoint) + public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider) + : base(repeatPoint) { this.repeatPoint = repeatPoint; this.drawableSlider = drawableSlider; + // The containing DrawableSlider is updated before us and clears our transforms, so we need to be + // present to get updated and have UpdateState correctly called when rewinding. + AlwaysPresent = true; + AutoSizeAxes = Axes.Both; Blending = BlendingMode.Additive; Origin = Anchor.Centre; From 60048e6cd1f65d74a0b947e66f8c106c4cccd0ab Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 3 Nov 2017 15:33:24 +0900 Subject: [PATCH 14/27] Fix slider ticks not showing up again once replayed Fixes #1456 --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index 7199691ae6..9fe475f4aa 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -28,6 +28,10 @@ public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick) Size = new Vector2(16) * sliderTick.Scale; + // The containing DrawableSlider is updated before us and clears our transforms, so we need to be + // present to get updated and have UpdateState correctly called when rewinding. + AlwaysPresent = true; + Masking = true; CornerRadius = Size.X / 2; From 5fd311514239d795cefbf272fe806f063c32b5b5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 3 Nov 2017 15:58:12 +0900 Subject: [PATCH 15/27] Fix slider ball not animating fade/scale after rewinding Fixes #1455 --- .../Objects/Drawables/Pieces/SliderBall.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index 1986b1431b..2068ad9205 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -101,14 +101,21 @@ protected override bool OnMouseMove(InputState state) // If the current time is between the start and end of the slider, we should track mouse input regardless of the cursor position. public override bool ReceiveMouseInputAt(Vector2 screenSpacePos) => canCurrentlyTrack || base.ReceiveMouseInputAt(screenSpacePos); + public override void ClearTransforms(bool propagateChildren = false, string targetMember = null) + { + // Consider the case of rewinding - children's transforms are handled internally, so propagating down + // any further will cause weirdness with the Tracking bool below. Let's not propagate further at this point. + base.ClearTransforms(false, targetMember); + } + private bool tracking; public bool Tracking { get { return tracking; } private set { - if (value == tracking) return; - + if (value == tracking) + return; tracking = value; follow.ScaleTo(tracking ? 2.8f : 1, 300, Easing.OutQuint); @@ -123,8 +130,10 @@ protected override void Update() base.Update(); // Make sure to use the base version of ReceiveMouseInputAt so that we correctly check the position. - if (Time.Current < slider.EndTime) - Tracking = canCurrentlyTrack && lastState != null && base.ReceiveMouseInputAt(lastState.Mouse.NativeState.Position) && ((Parent as DrawableSlider)?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false); + Tracking = canCurrentlyTrack + && lastState != null + && base.ReceiveMouseInputAt(lastState.Mouse.NativeState.Position) + && ((Parent as DrawableSlider)?.OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false); } public void UpdateProgress(double progress, int repeat) From 3b189c1ffed02f8e758efaaf2cce8c4e789636ca Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 3 Nov 2017 21:20:36 +0900 Subject: [PATCH 16/27] Fix BreakOverlay not properly working with rewinding In various ways: * It wouldn't show up if rewound after the break was complete. * The time would increase backwards if rewind happened during a break. * Etc. * Basically the fix is to use transformations everywhere. BreakOverlay could be refactored further, but this is enough to make it work for now. --- .../Play/BreaksOverlay/BreakOverlay.cs | 37 +++++++++------- .../BreaksOverlay/RemainingTimeCounter.cs | 44 +++++++------------ 2 files changed, 36 insertions(+), 45 deletions(-) diff --git a/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs b/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs index f5062aa40f..b3d08c0c82 100644 --- a/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs @@ -30,6 +30,8 @@ public List Breaks } } + public override bool RemoveCompletedTransforms => false; + private readonly bool letterboxing; private readonly LetterboxOverlay letterboxOverlay; private readonly Container remainingTimeAdjustmentBox; @@ -101,38 +103,41 @@ private void initializeBreaks() if (!b.HasEffect) continue; + using (BeginAbsoluteSequence(b.StartTime, true)) + { + remainingTimeAdjustmentBox + .ResizeWidthTo(remaining_time_container_max_size, fade_duration, Easing.OutQuint) + .Delay(b.Duration - fade_duration) + .ResizeWidthTo(0); + + remainingTimeBox + .ResizeWidthTo(0, b.Duration - fade_duration) + .Then() + .ResizeWidthTo(1); + + remainingTimeCounter.CountTo(b.Duration); + } + using (BeginAbsoluteSequence(b.StartTime)) { - Schedule(() => onBreakIn(b)); + Schedule(() => showBreak(b)); using (BeginDelayedSequence(b.Duration - fade_duration)) - Schedule(onBreakOut); + Schedule(hideBreak); } } } - private void onBreakIn(BreakPeriod b) + private void showBreak(BreakPeriod b) { if (letterboxing) letterboxOverlay.Show(); - remainingTimeAdjustmentBox - .ResizeWidthTo(remaining_time_container_max_size, fade_duration, Easing.OutQuint) - .Delay(b.Duration - fade_duration) - .ResizeWidthTo(0); - - remainingTimeBox - .ResizeWidthTo(0, b.Duration - fade_duration) - .Then() - .ResizeWidthTo(1); - - remainingTimeCounter.StartCounting(b.EndTime); - remainingTimeCounter.Show(); info.Show(); arrowsOverlay.Show(); } - private void onBreakOut() + private void hideBreak() { if (letterboxing) letterboxOverlay.Hide(); diff --git a/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs b/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs index b5d77d0d02..e144ac25da 100644 --- a/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs +++ b/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs @@ -9,18 +9,12 @@ namespace osu.Game.Screens.Play.BreaksOverlay { - public class RemainingTimeCounter : VisibilityContainer + public class RemainingTimeCounter : Container { private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2; private readonly OsuSpriteText counter; - private int? previousSecond; - - private double endTime; - - private bool isCounting; - public RemainingTimeCounter() { AutoSizeAxes = Axes.Both; @@ -31,35 +25,27 @@ public RemainingTimeCounter() TextSize = 33, Font = "Venera", }; + + Alpha = 0; } - public void StartCounting(double endTime) + public void CountTo(double duration) { - this.endTime = endTime; - isCounting = true; - } + double offset = 0; - protected override void Update() - { - base.Update(); - - if (isCounting) + while (duration > 0) { - var currentTime = Clock.CurrentTime; - if (currentTime < endTime) - { - int currentSecond = (int)Math.Ceiling((endTime - Clock.CurrentTime) / 1000.0); - if (currentSecond != previousSecond) - { - counter.Text = currentSecond.ToString(); - previousSecond = currentSecond; - } - } - else isCounting = false; + int seconds = (int)Math.Ceiling(duration / 1000); + counter.Delay(offset).TransformTextTo(seconds.ToString()); + + double localOffset = duration - (seconds - 1) * 1000 + 1; // +1 because we want the duration to be the next second when ceiled + + offset += localOffset; + duration -= localOffset; } } - protected override void PopIn() => this.FadeIn(fade_duration); - protected override void PopOut() => this.FadeOut(fade_duration); + public override void Show() => this.FadeIn(fade_duration); + public override void Hide() => this.FadeOut(fade_duration); } } From edd0d166b1c774cf102cc83208c52c56d6ab8df4 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 4 Nov 2017 00:42:36 +0900 Subject: [PATCH 17/27] Add text transforms to OsuSpriteText --- osu.Game/Graphics/Sprites/OsuSpriteText.cs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/osu.Game/Graphics/Sprites/OsuSpriteText.cs b/osu.Game/Graphics/Sprites/OsuSpriteText.cs index f5749846be..cbd9d9582d 100644 --- a/osu.Game/Graphics/Sprites/OsuSpriteText.cs +++ b/osu.Game/Graphics/Sprites/OsuSpriteText.cs @@ -6,6 +6,7 @@ using osu.Framework.MathUtils; using OpenTK; using OpenTK.Graphics; +using osu.Framework.Graphics.Transforms; namespace osu.Game.Graphics.Sprites { @@ -40,4 +41,23 @@ protected override Drawable CreateFallbackCharacterDrawable() return base.CreateFallbackCharacterDrawable(); } } + + public static class OsuSpriteTextTransformExtensions + { + /// + /// Sets to a new value after a duration. + /// + /// A to which further transforms can be added. + public static TransformSequence TransformTextTo(this T spriteText, string newText, double duration = 0, Easing easing = Easing.None) + where T : OsuSpriteText + => spriteText.TransformTo(nameof(OsuSpriteText.Text), newText, duration, easing); + + /// + /// Sets to a new value after a duration. + /// + /// A to which further transforms can be added. + public static TransformSequence TransformTextTo(this TransformSequence t, string newText, double duration = 0, Easing easing = Easing.None) + where T : OsuSpriteText + => t.Append(o => o.TransformTextTo(newText, duration, easing)); + } } From 15f69dff813f5ff46e7c31d1208627d12b2fcb6e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 4 Nov 2017 00:57:10 +0900 Subject: [PATCH 18/27] Make mania hit explosions not stick around when rewinding Fixes #1461. --- osu.Game.Rulesets.Mania/UI/HitExplosion.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/UI/HitExplosion.cs b/osu.Game.Rulesets.Mania/UI/HitExplosion.cs index 8164adcebd..433c518929 100644 --- a/osu.Game.Rulesets.Mania/UI/HitExplosion.cs +++ b/osu.Game.Rulesets.Mania/UI/HitExplosion.cs @@ -57,8 +57,10 @@ protected override void LoadComplete() { base.LoadComplete(); - this.ScaleTo(2f, 600, Easing.OutQuint).FadeOut(500).Expire(); + this.ScaleTo(2f, 600, Easing.OutQuint).FadeOut(500); inner.FadeOut(250); + + Expire(true); } } } From 4854302aaa9065d62aeebc298596e5c4ddaf5414 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 4 Nov 2017 01:02:33 +0900 Subject: [PATCH 19/27] Fix follow points not showing up again after rewinding Fixes #1463. --- .../Objects/Drawables/Connections/FollowPoint.cs | 2 ++ .../Objects/Drawables/Connections/FollowPointRenderer.cs | 3 +++ 2 files changed, 5 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs index dbf5c6c541..ee0b5e6c50 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPoint.cs @@ -14,6 +14,8 @@ public class FollowPoint : Container { private const float width = 8; + public override bool RemoveWhenNotAlive => false; + public FollowPoint() { Origin = Anchor.Centre; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs index 2396e5d129..fca9187047 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs @@ -52,9 +52,12 @@ public override IEnumerable HitObjects } } + public override bool RemoveCompletedTransforms => false; + private void update() { Clear(); + if (hitObjects == null) return; From 2fbd49062690c2bc4dd2402552a1291425abcde3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 6 Nov 2017 14:58:05 +0900 Subject: [PATCH 20/27] Make RemainingTimeCounter into a Counter --- .../Play/BreaksOverlay/BreakOverlay.cs | 3 ++- .../BreaksOverlay/RemainingTimeCounter.cs | 21 ++++--------------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs b/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs index b3d08c0c82..6128a8f3d7 100644 --- a/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs @@ -7,6 +7,7 @@ using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Scoring; using System.Collections.Generic; +using osu.Framework.Graphics.UserInterface; namespace osu.Game.Screens.Play.BreaksOverlay { @@ -115,7 +116,7 @@ private void initializeBreaks() .Then() .ResizeWidthTo(1); - remainingTimeCounter.CountTo(b.Duration); + remainingTimeCounter.CountTo(b.Duration).CountTo(0, b.Duration); } using (BeginAbsoluteSequence(b.StartTime)) diff --git a/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs b/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs index e144ac25da..9b043a6e90 100644 --- a/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs +++ b/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs @@ -6,10 +6,11 @@ using osu.Framework.Graphics; using System; using osu.Game.Beatmaps.Timing; +using osu.Framework.Graphics.UserInterface; namespace osu.Game.Screens.Play.BreaksOverlay { - public class RemainingTimeCounter : Container + public class RemainingTimeCounter : Counter { private const double fade_duration = BreakPeriod.MIN_BREAK_DURATION / 2; @@ -18,7 +19,7 @@ public class RemainingTimeCounter : Container public RemainingTimeCounter() { AutoSizeAxes = Axes.Both; - Child = counter = new OsuSpriteText + InternalChild = counter = new OsuSpriteText { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -29,21 +30,7 @@ public RemainingTimeCounter() Alpha = 0; } - public void CountTo(double duration) - { - double offset = 0; - - while (duration > 0) - { - int seconds = (int)Math.Ceiling(duration / 1000); - counter.Delay(offset).TransformTextTo(seconds.ToString()); - - double localOffset = duration - (seconds - 1) * 1000 + 1; // +1 because we want the duration to be the next second when ceiled - - offset += localOffset; - duration -= localOffset; - } - } + protected override void OnCountChanged(double count) => counter.Text = ((int)Math.Ceiling(count / 1000)).ToString(); public override void Show() => this.FadeIn(fade_duration); public override void Hide() => this.FadeOut(fade_duration); From c7426ebed80d6187b65e1efca94877f48d99a498 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 6 Nov 2017 17:22:22 +0900 Subject: [PATCH 21/27] Fix spinners showing very weird numbers after rewinding Fixes #1462 --- .../Objects/Drawables/Pieces/SpinnerSpmCounter.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs index ebe978f659..774511313a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerSpmCounter.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -61,6 +62,10 @@ private struct RotationRecord public void SetRotation(float currentRotation) { + // If we've gone back in time, it's fine to work with a fresh set of records for now + if (records.Count > 0 && Time.Current < records.Last().Time) + records.Clear(); + if (records.Count > 0) { var record = records.Peek(); From 348083f589e0faff13d33878fd71ed3e6dac94d0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 9 Nov 2017 14:04:59 +0900 Subject: [PATCH 22/27] Update with framework state transformation Removes explicit initial state setting in DrawableOsuHitObjects. --- osu-framework | 2 +- .../Objects/Drawables/DrawableHitCircle.cs | 21 ++----------------- .../Objects/Drawables/DrawableOsuHitObject.cs | 15 +++++-------- .../Objects/Drawables/DrawableRepeatPoint.cs | 12 +---------- .../Objects/Drawables/DrawableSlider.cs | 14 +++---------- .../Objects/Drawables/DrawableSliderTick.cs | 4 ---- .../Objects/Drawables/DrawableHitObject.cs | 3 +++ 7 files changed, 15 insertions(+), 56 deletions(-) diff --git a/osu-framework b/osu-framework index c8222d1dc9..b70ca3de9e 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit c8222d1dc932aafe17ec42bfbe6cbec81851f55d +Subproject commit b70ca3de9ec1a8eb7fb73fcfd169ff38d00b07cd diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index ed0578d3a4..a973d2b580 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -58,6 +58,8 @@ public DrawableHitCircle(OsuHitObject h) : base(h) }, ApproachCircle = new ApproachCircle { + Alpha = 0, + Scale = new Vector2(4), Colour = AccentColour, } }; @@ -82,25 +84,6 @@ protected override void CheckForJudgements(bool userTriggered, double timeOffset }); } - protected override void UpdateInitialState() - { - base.UpdateInitialState(); - - // Hide() cannot be used here, because when rewinding, we need these to be the final values - - ring.Alpha = 1; - circle.Alpha = 1; - number.Alpha = 1; - glow.Alpha = 1; - - ApproachCircle.Alpha = 0; - ApproachCircle.Scale = new Vector2(4); - explode.Alpha = 0; - flash.Alpha = 0; - - Scale = new Vector2(HitObject.Scale); - } - protected override void UpdatePreemptState() { base.UpdatePreemptState(); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index 7429f084c3..e968e5b9df 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -23,12 +23,13 @@ protected DrawableOsuHitObject(OsuHitObject hitObject) protected sealed override void UpdateState(ArmedState state) { - ClearTransforms(true); + double transformTime = HitObject.StartTime - TIME_PREEMPT; - using (BeginAbsoluteSequence(HitObject.StartTime - TIME_PREEMPT, true)) + TransformStateTo(transformTime, true); + ClearTransformsAfter(transformTime, true); + + using (BeginAbsoluteSequence(transformTime, true)) { - UpdateInitialState(); - UpdatePreemptState(); using (BeginDelayedSequence(TIME_PREEMPT + (Judgements.FirstOrDefault()?.TimeOffset ?? 0), true)) @@ -36,12 +37,6 @@ protected sealed override void UpdateState(ArmedState state) } } - protected virtual void UpdateInitialState() - { - // Hide() cannot be used here, because when rewinding, we need these to be the final values - Alpha = 0; - } - protected virtual void UpdatePreemptState() { this.FadeIn(TIME_FADEIN); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs index 200c697a0f..a9b63ea642 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs @@ -24,13 +24,10 @@ public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlide this.repeatPoint = repeatPoint; this.drawableSlider = drawableSlider; - // The containing DrawableSlider is updated before us and clears our transforms, so we need to be - // present to get updated and have UpdateState correctly called when rewinding. - AlwaysPresent = true; - AutoSizeAxes = Axes.Both; Blending = BlendingMode.Additive; Origin = Anchor.Centre; + Scale = new Vector2(0.5f); Children = new Drawable[] { @@ -50,13 +47,6 @@ protected override void CheckForJudgements(bool userTriggered, double timeOffset AddJudgement(new OsuJudgement { Result = drawableSlider.Tracking ? HitResult.Great : HitResult.Miss }); } - protected override void UpdateInitialState() - { - base.UpdateInitialState(); - - Scale = new Vector2(0.5f); - } - protected override void UpdatePreemptState() { var animIn = Math.Min(150, repeatPoint.StartTime - FadeInTime); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 2e6e82ce0c..74454ca555 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -43,7 +43,9 @@ public DrawableSlider(Slider s) : base(s) ball = new SliderBall(s) { Scale = new Vector2(s.Scale), - AccentColour = AccentColour + AccentColour = AccentColour, + AlwaysPresent = true, + Alpha = 0 }, initialCircle = new DrawableHitCircle(new HitCircle { @@ -148,16 +150,6 @@ protected override void CheckForJudgements(bool userTriggered, double timeOffset } } - protected override void UpdateInitialState() - { - base.UpdateInitialState(); - body.Alpha = 1; - - //we need to be present to handle input events. note that we still don't get enough events (we don't get a position if the mouse hasn't moved since the slider appeared). - ball.AlwaysPresent = true; - ball.Alpha = 0; - } - protected override void UpdateCurrentState(ArmedState state) { ball.FadeIn(); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index 9fe475f4aa..7199691ae6 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -28,10 +28,6 @@ public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick) Size = new Vector2(16) * sliderTick.Scale; - // The containing DrawableSlider is updated before us and clears our transforms, so we need to be - // present to get updated and have UpdateState correctly called when rewinding. - AlwaysPresent = true; - Masking = true; CornerRadius = Size.X / 2; diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 091af04106..99b62f265d 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -215,6 +215,9 @@ protected virtual void AddNested(DrawableHitObject h) nestedHitObjects.Add(h); } + protected override bool AllowStateTransformByParent => false; + protected override bool AllowTransformClearByParent => false; + protected abstract void UpdateState(ArmedState state); } } From 66ee9d163188908c7084bc77635222ec214f8847 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 9 Nov 2017 17:04:04 +0900 Subject: [PATCH 23/27] Update in-line with framework changes --- osu-framework | 2 +- .../Objects/Drawables/DrawableOsuHitObject.cs | 9 +++++++-- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 3 --- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/osu-framework b/osu-framework index b70ca3de9e..ff1e04024d 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit b70ca3de9ec1a8eb7fb73fcfd169ff38d00b07cd +Subproject commit ff1e04024d16c0a6cabec7792573b0d019bd1bba diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index e968e5b9df..3e0c23a1ab 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -25,8 +25,8 @@ protected sealed override void UpdateState(ArmedState state) { double transformTime = HitObject.StartTime - TIME_PREEMPT; - TransformStateTo(transformTime, true); - ClearTransformsAfter(transformTime, true); + base.ApplyTransformsAt(transformTime, true); + base.ClearTransformsAfter(transformTime, true); using (BeginAbsoluteSequence(transformTime, true)) { @@ -46,6 +46,11 @@ protected virtual void UpdateCurrentState(ArmedState state) { } + // Todo: At some point we need to move these to DrawableHitObject after ensuring that all other Rulesets apply + // transforms in the same way and don't rely on them not being cleared + public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null) { } + public override void ApplyTransformsAt(double time, bool propagateChildren = false) { } + private OsuInputManager osuActionInputManager; internal OsuInputManager OsuActionInputManager => osuActionInputManager ?? (osuActionInputManager = GetContainingInputManager() as OsuInputManager); } diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 99b62f265d..091af04106 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -215,9 +215,6 @@ protected virtual void AddNested(DrawableHitObject h) nestedHitObjects.Add(h); } - protected override bool AllowStateTransformByParent => false; - protected override bool AllowTransformClearByParent => false; - protected abstract void UpdateState(ArmedState state); } } From bd2de899183ebc28262a4466e35c3f2dc09991f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2017 19:26:25 +0900 Subject: [PATCH 24/27] Why weren't these fixed previously --- osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs | 2 -- osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs | 1 - 2 files changed, 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs index 07c499b56c..e1fe19212b 100644 --- a/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs +++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs @@ -1,8 +1,6 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Rulesets.Objects.Drawables; - namespace osu.Game.Rulesets.Taiko.Judgements { public class TaikoStrongHitJudgement : TaikoJudgement diff --git a/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs b/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs index 9b043a6e90..0df7bb97e0 100644 --- a/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs +++ b/osu.Game/Screens/Play/BreaksOverlay/RemainingTimeCounter.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Framework.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Framework.Graphics; using System; From 49731f4c050ed31ab155f4c3e8502801faf7c1ab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 10 Nov 2017 19:32:09 +0900 Subject: [PATCH 25/27] Remove unused parmeter --- osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs b/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs index 6128a8f3d7..7ef1ef8d8a 100644 --- a/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreaksOverlay/BreakOverlay.cs @@ -121,14 +121,14 @@ private void initializeBreaks() using (BeginAbsoluteSequence(b.StartTime)) { - Schedule(() => showBreak(b)); + Schedule(showBreak); using (BeginDelayedSequence(b.Duration - fade_duration)) Schedule(hideBreak); } } } - private void showBreak(BreakPeriod b) + private void showBreak() { if (letterboxing) letterboxOverlay.Show(); From 5277c3c1647c86b33c04ee98ea35d1c54fe52aca Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 10 Nov 2017 22:11:25 +0900 Subject: [PATCH 26/27] Set the frame time appropriately to reverse judgements a little better --- osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs index f0d68c0467..06f7259f78 100644 --- a/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Rulesets/Replays/FramedReplayInputHandler.cs @@ -104,7 +104,13 @@ protected Vector2? Position { //if we changed frames, we want to execute once *exactly* on the frame's time. if (currentDirection == time.CompareTo(NextFrame.Time) && advanceFrame()) + { + // If going backwards, we need to execute once _before_ the frame time to reverse any judgements + // that would occur as a result of this frame in forward playback + if (currentDirection == -1) + return currentTime = CurrentFrame.Time - 1; return currentTime = CurrentFrame.Time; + } //if we didn't change frames, we need to ensure we are allowed to run frames in between, else return null. if (inImportantSection) From e742c07f7d3edaf3e703397493ad07f1268e2bae Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 10 Nov 2017 22:17:41 +0900 Subject: [PATCH 27/27] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 68e0072674..1a563d7ce0 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 68e0072674c4bbc4e608120f51cd1d7e91e9500c +Subproject commit 1a563d7ce0834cede2ef587762f98fe580badf3c