From 3f6b17c0bf463945337e13b630ecd6d0b09d5900 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Tue, 28 Mar 2017 15:05:45 +0900 Subject: [PATCH 01/22] Add drawable Swell. --- .../Tests/TestCaseTaikoHitObjects.cs | 26 +--- .../Tests/TestCaseTaikoPlayfield.cs | 12 ++ .../Objects/Drawable/DrawableSwell.cs | 115 ++++++++++++++++++ .../Drawable/Pieces/SwellCirclePiece.cs | 35 ++++++ osu.Game.Modes.Taiko/Objects/Swell.cs | 2 +- .../osu.Game.Modes.Taiko.csproj | 1 + 6 files changed, 166 insertions(+), 25 deletions(-) create mode 100644 osu.Game.Modes.Taiko/Objects/Drawable/Pieces/SwellCirclePiece.cs diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs b/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs index 0204058b8a..82fbb27d15 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs @@ -62,7 +62,7 @@ namespace osu.Desktop.VisualTests.Tests Position = new Vector2(350, 300) }); - Add(new SwellCircle(new CirclePiece() + Add(new SwellCirclePiece(new CirclePiece { KiaiMode = kiai }) @@ -70,7 +70,7 @@ namespace osu.Desktop.VisualTests.Tests Position = new Vector2(100, 500) }); - Add(new SwellCircle(new StrongCirclePiece() + Add(new SwellCirclePiece(new StrongCirclePiece { KiaiMode = kiai }) @@ -97,28 +97,6 @@ namespace osu.Desktop.VisualTests.Tests }); } - private class SwellCircle : BaseCircle - { - public SwellCircle(CirclePiece piece) - : base(piece) - { - Piece.Add(new TextAwesome - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - TextSize = SYMBOL_INNER_SIZE, - Icon = FontAwesome.fa_asterisk, - Shadow = false - }); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - Piece.AccentColour = colours.YellowDark; - } - } - private class DrumRollCircle : BaseCircle { public DrumRollCircle(CirclePiece piece) diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs b/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs index 395a0cab13..2cef59238d 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseTaikoPlayfield.cs @@ -6,6 +6,7 @@ using osu.Framework.Screens.Testing; using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Taiko.Judgements; using osu.Game.Modes.Taiko.Objects; +using osu.Game.Modes.Taiko.Objects.Drawable; using osu.Game.Modes.Taiko.UI; namespace osu.Desktop.VisualTests.Tests @@ -22,6 +23,7 @@ namespace osu.Desktop.VisualTests.Tests AddButton("Hit!", addHitJudgement); AddButton("Miss :(", addMissJudgement); + AddButton("Swell", addSwell); Add(playfield = new TaikoPlayfield { @@ -60,6 +62,16 @@ namespace osu.Desktop.VisualTests.Tests }); } + private void addSwell() + { + playfield.Add(new DrawableSwell(new Swell + { + StartTime = Time.Current + 1000, + EndTime = Time.Current + 5000, + PreEmpt = 1000 + })); + } + private class DrawableTestHit : DrawableHitObject { public DrawableTestHit(TaikoHitObject hitObject) diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs index 15584ac73f..405fc48f3e 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs @@ -1,15 +1,30 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using OpenTK; +using OpenTK.Graphics; using OpenTK.Input; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Transforms; +using osu.Game.Graphics; using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Taiko.Judgements; +using osu.Game.Modes.Taiko.Objects.Drawable.Pieces; using System; namespace osu.Game.Modes.Taiko.Objects.Drawable { public class DrawableSwell : DrawableTaikoHitObject { + private const float target_ring_thick_border = 4f; + private const float target_ring_thin_border = 1f; + private const float target_ring_scale = 5f; + private const float inner_ring_alpha = 0.35f; + /// /// The amount of times the user has hit this swell. /// @@ -17,10 +32,94 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable private readonly Swell swell; + private readonly Container bodyContainer; + private readonly CircularContainer targetRing; + private readonly CircularContainer innerRing; + public DrawableSwell(Swell swell) : base(swell) { this.swell = swell; + + Children = new Framework.Graphics.Drawable[] + { + bodyContainer = new Container + { + Children = new Framework.Graphics.Drawable[] + { + innerRing = new CircularContainer + { + Name = "Inner ring", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2), + Masking = true, + Children = new [] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = inner_ring_alpha, + } + } + }, + targetRing = new CircularContainer + { + Name = "Target ring (thick border)", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2), + Masking = true, + BorderThickness = target_ring_thick_border, + Children = new Framework.Graphics.Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + }, + new CircularContainer + { + Name = "Target ring (thin border)", + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Masking = true, + BorderThickness = target_ring_thin_border, + BorderColour = Color4.White, + Children = new[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true + } + } + } + } + }, + new SwellCirclePiece(new CirclePiece()) + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + innerRing.Colour = colours.YellowDark; + targetRing.BorderColour = colours.YellowDark.Opacity(0.25f); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + LifetimeEnd = swell.EndTime + HitObject.PreEmpt; + + targetRing.Delay(HitObject.StartTime - Time.Current).ScaleTo(target_ring_scale, 600, EasingTypes.OutQuint); } protected override void CheckJudgement(bool userTriggered) @@ -32,6 +131,10 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable userHits++; + innerRing.FadeTo(1); + innerRing.FadeTo(inner_ring_alpha, 500, EasingTypes.OutQuint); + innerRing.ScaleTo(1f + (target_ring_scale - 1) * userHits / swell.RequiredHits, 1200, EasingTypes.OutElastic); + if (userHits == swell.RequiredHits) { Judgement.Result = HitResult.Hit; @@ -55,6 +158,18 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable protected override void UpdateState(ArmedState state) { + switch (state) + { + case ArmedState.Idle: + break; + case ArmedState.Miss: + bodyContainer.FadeOut(100); + break; + case ArmedState.Hit: + bodyContainer.ScaleTo(1.2f, 400, EasingTypes.OutQuad); + bodyContainer.FadeOut(600, EasingTypes.OutQuint); + break; + } } protected override void UpdateScrollPosition(double time) diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/SwellCirclePiece.cs b/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/SwellCirclePiece.cs new file mode 100644 index 0000000000..8274a2bff0 --- /dev/null +++ b/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/SwellCirclePiece.cs @@ -0,0 +1,35 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; + +namespace osu.Game.Modes.Taiko.Objects.Drawable.Pieces +{ + public class SwellCirclePiece : Container + { + private readonly CirclePiece circle; + + public SwellCirclePiece(CirclePiece piece) + { + Add(circle = piece); + + circle.Add(new TextAwesome + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + TextSize = CirclePiece.SYMBOL_INNER_SIZE, + Icon = FontAwesome.fa_asterisk, + Shadow = false + }); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + circle.AccentColour = colours.YellowDark; + } + } +} diff --git a/osu.Game.Modes.Taiko/Objects/Swell.cs b/osu.Game.Modes.Taiko/Objects/Swell.cs index 20b9a6effb..4cbb5904c7 100644 --- a/osu.Game.Modes.Taiko/Objects/Swell.cs +++ b/osu.Game.Modes.Taiko/Objects/Swell.cs @@ -17,7 +17,7 @@ namespace osu.Game.Modes.Taiko.Objects /// /// The number of hits required to complete the swell successfully. /// - public int RequiredHits { get; protected set; } + public int RequiredHits { get; protected set; } = 10; public override void ApplyDefaults(TimingInfo timing, BeatmapDifficulty difficulty) { diff --git a/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj b/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj index fa09ae2c82..7cec1bfa7f 100644 --- a/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj +++ b/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj @@ -57,6 +57,7 @@ + From 45653d7b430fead6698063044d09e0b3b20b16ee Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 29 Mar 2017 08:32:34 +0900 Subject: [PATCH 02/22] Fix post-merge errors. --- osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs index 405fc48f3e..73e7a87f95 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs @@ -9,7 +9,6 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Transforms; using osu.Game.Graphics; using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Taiko.Judgements; From 6bee57c94ffec06b0ababb8c1d47d7e1a19f8a11 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 29 Mar 2017 08:41:50 +0900 Subject: [PATCH 03/22] Better life time ends. --- osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs index 73e7a87f95..a2bbedd9c6 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs @@ -116,7 +116,7 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable { base.LoadComplete(); - LifetimeEnd = swell.EndTime + HitObject.PreEmpt; + LifetimeEnd = double.MaxValue; targetRing.Delay(HitObject.StartTime - Time.Current).ScaleTo(target_ring_scale, 600, EasingTypes.OutQuint); } @@ -162,11 +162,14 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable case ArmedState.Idle: break; case ArmedState.Miss: - bodyContainer.FadeOut(100); + FadeOut(100); + Expire(); break; case ArmedState.Hit: bodyContainer.ScaleTo(1.2f, 400, EasingTypes.OutQuad); - bodyContainer.FadeOut(600, EasingTypes.OutQuint); + + FadeOut(600); + Expire(); break; } } From 7fc68864beab883202e40f5b051a275c210802ec Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 29 Mar 2017 09:01:40 +0900 Subject: [PATCH 04/22] Move swells on top of the playfield when they reach the hit target. --- .../Objects/Drawable/DrawableSwell.cs | 19 ++++++++++++++++++- osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs | 16 +++++++++++----- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs index a2bbedd9c6..862b81c5dc 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs @@ -19,6 +19,12 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable { public class DrawableSwell : DrawableTaikoHitObject { + /// + /// Invoked when the swell has reached the hit target, i.e. when CurrentTime >= StartTime. + /// This is only ever invoked once. + /// + public event Action OnStart; + private const float target_ring_thick_border = 4f; private const float target_ring_thin_border = 1f; private const float target_ring_scale = 5f; @@ -35,6 +41,8 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable private readonly CircularContainer targetRing; private readonly CircularContainer innerRing; + private bool hasStarted; + public DrawableSwell(Swell swell) : base(swell) { @@ -176,7 +184,16 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable protected override void UpdateScrollPosition(double time) { - base.UpdateScrollPosition(Math.Min(time, HitObject.StartTime)); + // Make the swell stop at the hit target + double t = Math.Min(HitObject.StartTime, time); + + if (t == HitObject.StartTime && !hasStarted) + { + OnStart?.Invoke(); + hasStarted = true; + } + + base.UpdateScrollPosition(t); } protected override bool HandleKeyPress(Key key) diff --git a/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs index 9bc75a55f5..41e22ea371 100644 --- a/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs @@ -14,6 +14,7 @@ using osu.Game.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics.Primitives; +using osu.Game.Modes.Taiko.Objects.Drawable; namespace osu.Game.Modes.Taiko.UI { @@ -52,7 +53,7 @@ namespace osu.Game.Modes.Taiko.UI private readonly Container judgementContainer; private readonly Container hitObjectContainer; - //private Container topLevelHitContainer; + private Container topLevelHitContainer; private readonly Container leftBackgroundContainer; private readonly Container rightBackgroundContainer; private readonly Box leftBackground; @@ -154,10 +155,10 @@ namespace osu.Game.Modes.Taiko.UI }, } }, - //topLevelHitContainer = new Container - //{ - // RelativeSizeAxes = Axes.Both, - //} + topLevelHitContainer = new Container + { + RelativeSizeAxes = Axes.Both, + } }); } @@ -177,6 +178,11 @@ namespace osu.Game.Modes.Taiko.UI h.Scale = new Vector2(PLAYFIELD_SCALE); base.Add(h); + + // Swells should be moved at the very top of the playfield when they reach the hit target + var swell = h as DrawableSwell; + if (swell != null) + swell.OnStart += () => topLevelHitContainer.Add(swell.CreateProxy()); } public override void OnJudgement(DrawableHitObject judgedObject) From 6d5b0a13e46bbdeb460b8d06d64a9ce5fc8c43f9 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 29 Mar 2017 09:02:49 +0900 Subject: [PATCH 05/22] Fix resharper warning. --- osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs index 41e22ea371..1dce4a5c7b 100644 --- a/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Modes.Taiko/UI/TaikoPlayfield.cs @@ -53,7 +53,7 @@ namespace osu.Game.Modes.Taiko.UI private readonly Container judgementContainer; private readonly Container hitObjectContainer; - private Container topLevelHitContainer; + private readonly Container topLevelHitContainer; private readonly Container leftBackgroundContainer; private readonly Container rightBackgroundContainer; private readonly Box leftBackground; From d74454141bc903aeb37de8404a076542cac8c16c Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 29 Mar 2017 09:12:21 +0900 Subject: [PATCH 06/22] Remove explicit life time end. --- osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs | 2 -- osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHitObject.cs | 1 - 2 files changed, 3 deletions(-) diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs index 862b81c5dc..b5246321da 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs @@ -124,8 +124,6 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable { base.LoadComplete(); - LifetimeEnd = double.MaxValue; - targetRing.Delay(HitObject.StartTime - Time.Current).ScaleTo(target_ring_scale, 600, EasingTypes.OutQuint); } diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHitObject.cs b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHitObject.cs index c14dc6d7b0..5d6d669dc1 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHitObject.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableTaikoHitObject.cs @@ -30,7 +30,6 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable protected override void LoadComplete() { LifetimeStart = HitObject.StartTime - HitObject.PreEmpt * 2; - LifetimeEnd = HitObject.StartTime + HitObject.PreEmpt; base.LoadComplete(); } From 400a57ace6ca32dc13fb422a579fbb88cd08ec37 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 29 Mar 2017 13:33:52 +0900 Subject: [PATCH 07/22] Fix delay + animations a bit (peppy). --- .../Objects/Drawable/DrawableSwell.cs | 30 +++++++++---------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs index b5246321da..242159446a 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs @@ -120,13 +120,6 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable targetRing.BorderColour = colours.YellowDark.Opacity(0.25f); } - protected override void LoadComplete() - { - base.LoadComplete(); - - targetRing.Delay(HitObject.StartTime - Time.Current).ScaleTo(target_ring_scale, 600, EasingTypes.OutQuint); - } - protected override void CheckJudgement(bool userTriggered) { if (userTriggered) @@ -151,6 +144,7 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable if (Judgement.TimeOffset < 0) return; + //TODO: THIS IS SHIT AND CAN'T EXIST POST-TAIKO WORLD CUP if (userHits > swell.RequiredHits / 2) { Judgement.Result = HitResult.Hit; @@ -163,21 +157,25 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable protected override void UpdateState(ArmedState state) { + const float preempt = 300; + + Delay(HitObject.StartTime - Time.Current - preempt, true); + + targetRing.ScaleTo(target_ring_scale, preempt, EasingTypes.Out); + Delay(preempt, true); + + Delay(Judgement.TimeOffset + swell.Duration, true); + switch (state) { - case ArmedState.Idle: - break; - case ArmedState.Miss: - FadeOut(100); - Expire(); - break; case ArmedState.Hit: bodyContainer.ScaleTo(1.2f, 400, EasingTypes.OutQuad); - - FadeOut(600); - Expire(); break; } + + FadeOut(600); + + Expire(); } protected override void UpdateScrollPosition(double time) From 37dcc8b71d31d7ab6c3801908dcc0a6970d7ce5d Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 29 Mar 2017 14:52:14 +0900 Subject: [PATCH 08/22] Rewrite how swell circle piece is instantiated. --- .../Tests/TestCaseTaikoHitObjects.cs | 22 +++++------- .../Objects/Drawable/DrawableSwell.cs | 11 +++++- .../Drawable/Pieces/SwellCirclePiece.cs | 35 ------------------- .../Drawable/Pieces/SwellSymbolPiece.cs | 23 ++++++++++++ .../osu.Game.Modes.Taiko.csproj | 4 +-- 5 files changed, 44 insertions(+), 51 deletions(-) delete mode 100644 osu.Game.Modes.Taiko/Objects/Drawable/Pieces/SwellCirclePiece.cs create mode 100644 osu.Game.Modes.Taiko/Objects/Drawable/Pieces/SwellSymbolPiece.cs diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs b/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs index dddc487bd6..02f98edc97 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs @@ -62,20 +62,16 @@ namespace osu.Desktop.VisualTests.Tests Position = new Vector2(350, 300) }); - Add(new SwellCirclePiece(new CirclePiece + Add(new CirclePiece { - KiaiMode = kiai - }) - { - Position = new Vector2(100, 500) - }); - - Add(new SwellCirclePiece(new StrongCirclePiece - { - KiaiMode = kiai - }) - { - Position = new Vector2(350, 500) + Position = new Vector2(100, 500), + Width = 0, + AccentColour = Color4.Orange, + KiaiMode = kiai, + Children = new[] + { + new SwellSymbolPiece() + } }); Add(new DrumRollCircle(new CirclePiece() diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs index 242159446a..291e1df837 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs @@ -41,6 +41,8 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable private readonly CircularContainer targetRing; private readonly CircularContainer innerRing; + private readonly CirclePiece circlePiece; + private bool hasStarted; public DrawableSwell(Swell swell) @@ -107,7 +109,13 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable } } }, - new SwellCirclePiece(new CirclePiece()) + circlePiece = new CirclePiece + { + Children = new [] + { + new SwellSymbolPiece() + } + } } } }; @@ -116,6 +124,7 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable [BackgroundDependencyLoader] private void load(OsuColour colours) { + circlePiece.AccentColour = colours.YellowDark; innerRing.Colour = colours.YellowDark; targetRing.BorderColour = colours.YellowDark.Opacity(0.25f); } diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/SwellCirclePiece.cs b/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/SwellCirclePiece.cs deleted file mode 100644 index 8274a2bff0..0000000000 --- a/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/SwellCirclePiece.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Allocation; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics; - -namespace osu.Game.Modes.Taiko.Objects.Drawable.Pieces -{ - public class SwellCirclePiece : Container - { - private readonly CirclePiece circle; - - public SwellCirclePiece(CirclePiece piece) - { - Add(circle = piece); - - circle.Add(new TextAwesome - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - TextSize = CirclePiece.SYMBOL_INNER_SIZE, - Icon = FontAwesome.fa_asterisk, - Shadow = false - }); - } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - circle.AccentColour = colours.YellowDark; - } - } -} diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/SwellSymbolPiece.cs b/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/SwellSymbolPiece.cs new file mode 100644 index 0000000000..ba7f5b8df9 --- /dev/null +++ b/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/SwellSymbolPiece.cs @@ -0,0 +1,23 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Graphics; +using osu.Framework.Graphics; + +namespace osu.Game.Modes.Taiko.Objects.Drawable.Pieces +{ + /// + /// The symbol used for swell pieces. + /// + public class SwellSymbolPiece : TextAwesome + { + public SwellSymbolPiece() + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + TextSize = CirclePiece.SYMBOL_INNER_SIZE; + Icon = FontAwesome.fa_asterisk; + Shadow = false; + } + } +} diff --git a/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj b/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj index 2e9034839f..d3d7143baa 100644 --- a/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj +++ b/osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj @@ -58,9 +58,9 @@ - + @@ -103,4 +103,4 @@ --> - + \ No newline at end of file From 982fcbbf5334583f4b94b8110af4ac5892f7d269 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 29 Mar 2017 15:20:20 +0900 Subject: [PATCH 09/22] Update framework. --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 4d131fd0d9..54830759f8 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 4d131fd0d997bee313de3fa33a45900637570ff0 +Subproject commit 54830759f8a9a248585684de6faf3d9f1976b335 From 3d2c8f19ae0a13c9f85c4994bf8095d52115ea3d Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 29 Mar 2017 15:59:12 +0900 Subject: [PATCH 10/22] Make Swells require alternating key hits. --- .../Objects/Drawable/DrawableSwell.cs | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs index 291e1df837..d1a9760691 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs @@ -14,6 +14,7 @@ using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Taiko.Judgements; using osu.Game.Modes.Taiko.Objects.Drawable.Pieces; using System; +using System.Linq; namespace osu.Game.Modes.Taiko.Objects.Drawable { @@ -30,11 +31,6 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable private const float target_ring_scale = 5f; private const float inner_ring_alpha = 0.35f; - /// - /// The amount of times the user has hit this swell. - /// - private int userHits; - private readonly Swell swell; private readonly Container bodyContainer; @@ -43,6 +39,15 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable private readonly CirclePiece circlePiece; + private readonly Key[] rimKeys = { Key.D, Key.K }; + private readonly Key[] centreKeys = { Key.F, Key.J }; + private Key[] lastKeySet; + + /// + /// The amount of times the user has hit this swell. + /// + private int userHits; + private bool hasStarted; public DrawableSwell(Swell swell) @@ -206,6 +211,14 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable if (Judgement.Result.HasValue) return false; + // Find the keyset which this key corresponds to + var keySet = rimKeys.Contains(key) ? rimKeys : centreKeys; + + // Ensure alternating keysets + if (keySet == lastKeySet) + return false; + lastKeySet = keySet; + UpdateJudgement(true); return true; From 1b3e908565161f814ba884d3c66bf0ac19848592 Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Wed, 29 Mar 2017 16:02:12 +0900 Subject: [PATCH 11/22] Fix notelocking by pressing a key before the swell's start time. --- osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs index d1a9760691..0462880f3e 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs @@ -138,9 +138,6 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable { if (userTriggered) { - if (Time.Current < HitObject.StartTime) - return; - userHits++; innerRing.FadeTo(1); @@ -211,6 +208,10 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable if (Judgement.Result.HasValue) return false; + // Don't handle keys before the swell starts + if (Time.Current < HitObject.StartTime) + return false; + // Find the keyset which this key corresponds to var keySet = rimKeys.Contains(key) ? rimKeys : centreKeys; From f3599b080ccbf60454ec73435fab938eb8e5014d Mon Sep 17 00:00:00 2001 From: smoogipooo Date: Thu, 30 Mar 2017 16:12:21 +0900 Subject: [PATCH 12/22] Fix using. --- osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs b/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs index 5bf12ce586..e8e32ebf3d 100644 --- a/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs +++ b/osu.Desktop.VisualTests/Tests/TestCaseTaikoHitObjects.cs @@ -4,7 +4,6 @@ using OpenTK; using OpenTK.Graphics; using osu.Framework.Allocation; -using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Graphics; From b48def16275378e131ce2ed9157796a7c687eaab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 Mar 2017 11:19:33 +0900 Subject: [PATCH 13/22] Align SwellSymbolPiece better for rotation. --- osu.Game.Modes.Taiko/Objects/Drawable/Pieces/SwellSymbolPiece.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/SwellSymbolPiece.cs b/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/SwellSymbolPiece.cs index ba7f5b8df9..2bd86406a7 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/SwellSymbolPiece.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawable/Pieces/SwellSymbolPiece.cs @@ -15,6 +15,7 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable.Pieces { Anchor = Anchor.Centre; Origin = Anchor.Centre; + UseFullGlyphHeight = true; TextSize = CirclePiece.SYMBOL_INNER_SIZE; Icon = FontAwesome.fa_asterisk; Shadow = false; From bcd7e41bf78956e2a3ed643cece478f0471834db Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 Mar 2017 11:20:04 +0900 Subject: [PATCH 14/22] Visual adjustments to DrawableSwell. --- .../Objects/Drawable/DrawableSwell.cs | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs index 0462880f3e..d789d12c90 100644 --- a/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs +++ b/osu.Game.Modes.Taiko/Objects/Drawable/DrawableSwell.cs @@ -26,16 +26,16 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable /// public event Action OnStart; - private const float target_ring_thick_border = 4f; + private const float target_ring_thick_border = 1.4f; private const float target_ring_thin_border = 1f; private const float target_ring_scale = 5f; - private const float inner_ring_alpha = 0.35f; + private const float inner_ring_alpha = 0.65f; private readonly Swell swell; private readonly Container bodyContainer; private readonly CircularContainer targetRing; - private readonly CircularContainer innerRing; + private readonly CircularContainer expandingRing; private readonly CirclePiece circlePiece; @@ -49,6 +49,7 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable private int userHits; private bool hasStarted; + private readonly SwellSymbolPiece symbol; public DrawableSwell(Swell swell) : base(swell) @@ -61,12 +62,14 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable { Children = new Framework.Graphics.Drawable[] { - innerRing = new CircularContainer + expandingRing = new CircularContainer { - Name = "Inner ring", + Name = "Expanding ring", Anchor = Anchor.Centre, Origin = Anchor.Centre, + Alpha = 0, Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2), + BlendingMode = BlendingMode.Additive, Masking = true, Children = new [] { @@ -85,6 +88,7 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable Size = new Vector2(TaikoHitObject.CIRCLE_RADIUS * 2), Masking = true, BorderThickness = target_ring_thick_border, + BlendingMode = BlendingMode.Additive, Children = new Framework.Graphics.Drawable[] { new Box @@ -118,7 +122,7 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable { Children = new [] { - new SwellSymbolPiece() + symbol = new SwellSymbolPiece() } } } @@ -130,7 +134,7 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable private void load(OsuColour colours) { circlePiece.AccentColour = colours.YellowDark; - innerRing.Colour = colours.YellowDark; + expandingRing.Colour = colours.YellowLight; targetRing.BorderColour = colours.YellowDark.Opacity(0.25f); } @@ -140,9 +144,16 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable { userHits++; - innerRing.FadeTo(1); - innerRing.FadeTo(inner_ring_alpha, 500, EasingTypes.OutQuint); - innerRing.ScaleTo(1f + (target_ring_scale - 1) * userHits / swell.RequiredHits, 1200, EasingTypes.OutElastic); + var completion = (float)userHits / swell.RequiredHits; + + expandingRing.FadeTo(expandingRing.Alpha + MathHelper.Clamp(completion / 16, 0.1f, 0.6f), 50); + expandingRing.Delay(50); + expandingRing.FadeTo(completion / 8, 2000, EasingTypes.OutQuint); + expandingRing.DelayReset(); + + symbol.RotateTo((float)(completion * swell.Duration / 8), 4000, EasingTypes.OutQuint); + + expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, EasingTypes.OutQuint); if (userHits == swell.RequiredHits) { @@ -168,23 +179,26 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable protected override void UpdateState(ArmedState state) { - const float preempt = 300; + const float preempt = 100; Delay(HitObject.StartTime - Time.Current - preempt, true); - targetRing.ScaleTo(target_ring_scale, preempt, EasingTypes.Out); + targetRing.ScaleTo(target_ring_scale, preempt * 4, EasingTypes.OutQuint); + Delay(preempt, true); Delay(Judgement.TimeOffset + swell.Duration, true); + const float out_transition_time = 300; + switch (state) { case ArmedState.Hit: - bodyContainer.ScaleTo(1.2f, 400, EasingTypes.OutQuad); + bodyContainer.ScaleTo(1.4f, out_transition_time); break; } - FadeOut(600); + FadeOut(out_transition_time, EasingTypes.Out); Expire(); } From e7ecc479c9bad2681f9b2c6211efd54c9f5bc2d1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 Mar 2017 12:43:42 +0900 Subject: [PATCH 15/22] Add FramedReplay and use where legacy is not needed. --- osu.Game.Modes.Osu/OsuAutoReplay.cs | 63 ++++---- .../FramedReplay.cs} | 134 ++---------------- osu.Game/Modes/Replays/LegacyFramedReplay.cs | 70 +++++++++ osu.Game/Modes/{ => Replays}/Replay.cs | 2 +- osu.Game/Modes/Replays/ReplayButtonState.cs | 18 +++ osu.Game/Modes/Replays/ReplayFrame.cs | 71 ++++++++++ osu.Game/Modes/Scoring/Score.cs | 1 + osu.Game/osu.Game.csproj | 7 +- 8 files changed, 209 insertions(+), 157 deletions(-) rename osu.Game/Modes/{LegacyReplay.cs => Replays/FramedReplay.cs} (53%) create mode 100644 osu.Game/Modes/Replays/LegacyFramedReplay.cs rename osu.Game/Modes/{ => Replays}/Replay.cs (87%) create mode 100644 osu.Game/Modes/Replays/ReplayButtonState.cs create mode 100644 osu.Game/Modes/Replays/ReplayFrame.cs diff --git a/osu.Game.Modes.Osu/OsuAutoReplay.cs b/osu.Game.Modes.Osu/OsuAutoReplay.cs index 86985c834a..2ac5f771c3 100644 --- a/osu.Game.Modes.Osu/OsuAutoReplay.cs +++ b/osu.Game.Modes.Osu/OsuAutoReplay.cs @@ -11,10 +11,11 @@ using System.Collections.Generic; using System.Diagnostics; using osu.Framework.Graphics; using osu.Game.Modes.Objects.Types; +using osu.Game.Modes.Replays; namespace osu.Game.Modes.Osu { - public class OsuAutoReplay : LegacyReplay + public class OsuAutoReplay : FramedReplay { private static readonly Vector2 spinner_centre = new Vector2(256, 192); @@ -29,17 +30,17 @@ namespace osu.Game.Modes.Osu createAutoReplay(); } - private class LegacyReplayFrameComparer : IComparer + private class ReplayFrameComparer : IComparer { - public int Compare(LegacyReplayFrame f1, LegacyReplayFrame f2) + public int Compare(ReplayFrame f1, ReplayFrame f2) { return f1.Time.CompareTo(f2.Time); } } - private static readonly IComparer replay_frame_comparer = new LegacyReplayFrameComparer(); + private static readonly IComparer replay_frame_comparer = new ReplayFrameComparer(); - private int findInsertionIndex(LegacyReplayFrame frame) + private int findInsertionIndex(ReplayFrame frame) { int index = Frames.BinarySearch(frame, replay_frame_comparer); @@ -59,7 +60,7 @@ namespace osu.Game.Modes.Osu return index; } - private void addFrameToReplay(LegacyReplayFrame frame) => Frames.Insert(findInsertionIndex(frame), frame); + private void addFrameToReplay(ReplayFrame frame) => Frames.Insert(findInsertionIndex(frame), frame); private static Vector2 circlePosition(double t, double radius) => new Vector2((float)(Math.Cos(t) * radius), (float)(Math.Sin(t) * radius)); @@ -74,9 +75,9 @@ namespace osu.Game.Modes.Osu EasingTypes preferredEasing = DelayedMovements ? EasingTypes.InOutCubic : EasingTypes.Out; - addFrameToReplay(new LegacyReplayFrame(-100000, 256, 500, LegacyButtonState.None)); - addFrameToReplay(new LegacyReplayFrame(beatmap.HitObjects[0].StartTime - 1500, 256, 500, LegacyButtonState.None)); - addFrameToReplay(new LegacyReplayFrame(beatmap.HitObjects[0].StartTime - 1000, 256, 192, LegacyButtonState.None)); + addFrameToReplay(new ReplayFrame(-100000, 256, 500, ReplayButtonState.None)); + addFrameToReplay(new ReplayFrame(beatmap.HitObjects[0].StartTime - 1500, 256, 500, ReplayButtonState.None)); + addFrameToReplay(new ReplayFrame(beatmap.HitObjects[0].StartTime - 1000, 256, 192, ReplayButtonState.None)); // We are using ApplyModsToRate and not ApplyModsToTime to counteract the speed up / slow down from HalfTime / DoubleTime so that we remain at a constant framerate of 60 fps. float frameDelay = (float)applyModsToRate(1000.0 / 60.0); @@ -106,18 +107,18 @@ namespace osu.Game.Modes.Osu //Make the cursor stay at a hitObject as long as possible (mainly for autopilot). if (h.StartTime - h.HitWindowFor(OsuScoreResult.Miss) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50) { - if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new LegacyReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None)); - if (!(h is Spinner)) addFrameToReplay(new LegacyReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Miss), h.Position.X, h.Position.Y, LegacyButtonState.None)); + if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), last.EndPosition.X, last.EndPosition.Y, ReplayButtonState.None)); + if (!(h is Spinner)) addFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Miss), h.Position.X, h.Position.Y, ReplayButtonState.None)); } else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50) { - if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new LegacyReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None)); - if (!(h is Spinner)) addFrameToReplay(new LegacyReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50), h.Position.X, h.Position.Y, LegacyButtonState.None)); + if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), last.EndPosition.X, last.EndPosition.Y, ReplayButtonState.None)); + if (!(h is Spinner)) addFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50), h.Position.X, h.Position.Y, ReplayButtonState.None)); } else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100) > endTime + h.HitWindowFor(OsuScoreResult.Hit100) + 50) { - if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new LegacyReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit100), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None)); - if (!(h is Spinner)) addFrameToReplay(new LegacyReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.Position.X, h.Position.Y, LegacyButtonState.None)); + if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new ReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit100), last.EndPosition.X, last.EndPosition.Y, ReplayButtonState.None)); + if (!(h is Spinner)) addFrameToReplay(new ReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.Position.X, h.Position.Y, ReplayButtonState.None)); } } @@ -173,13 +174,13 @@ namespace osu.Game.Modes.Osu // Do some nice easing for cursor movements if (Frames.Count > 0) { - LegacyReplayFrame lastFrame = Frames[Frames.Count - 1]; + ReplayFrame lastFrame = Frames[Frames.Count - 1]; // Wait until Auto could "see and react" to the next note. double waitTime = h.StartTime - Math.Max(0.0, DrawableOsuHitObject.TIME_PREEMPT - reactionTime); if (waitTime > lastFrame.Time) { - lastFrame = new LegacyReplayFrame(waitTime, lastFrame.MouseX, lastFrame.MouseY, lastFrame.ButtonState); + lastFrame = new ReplayFrame(waitTime, lastFrame.MouseX, lastFrame.MouseY, lastFrame.ButtonState); addFrameToReplay(lastFrame); } @@ -196,7 +197,7 @@ namespace osu.Game.Modes.Osu for (double time = lastFrame.Time + frameDelay; time < h.StartTime; time += frameDelay) { Vector2 currentPosition = Interpolation.ValueAt(time, lastPosition, targetPosition, lastFrame.Time, h.StartTime, easing); - addFrameToReplay(new LegacyReplayFrame((int)time, currentPosition.X, currentPosition.Y, lastFrame.ButtonState)); + addFrameToReplay(new ReplayFrame((int)time, currentPosition.X, currentPosition.Y, lastFrame.ButtonState)); } buttonIndex = 0; @@ -207,12 +208,12 @@ namespace osu.Game.Modes.Osu } } - LegacyButtonState button = buttonIndex % 2 == 0 ? LegacyButtonState.Left1 : LegacyButtonState.Right1; + ReplayButtonState button = buttonIndex % 2 == 0 ? ReplayButtonState.Left1 : ReplayButtonState.Right1; double hEndTime = (h as IHasEndTime)?.EndTime ?? h.StartTime; - LegacyReplayFrame newFrame = new LegacyReplayFrame(h.StartTime, targetPosition.X, targetPosition.Y, button); - LegacyReplayFrame endFrame = new LegacyReplayFrame(hEndTime + endDelay, h.EndPosition.X, h.EndPosition.Y, LegacyButtonState.None); + ReplayFrame newFrame = new ReplayFrame(h.StartTime, targetPosition.X, targetPosition.Y, button); + ReplayFrame endFrame = new ReplayFrame(hEndTime + endDelay, h.EndPosition.X, h.EndPosition.Y, ReplayButtonState.None); // Decrement because we want the previous frame, not the next one int index = findInsertionIndex(newFrame) - 1; @@ -220,19 +221,19 @@ namespace osu.Game.Modes.Osu // Do we have a previous frame? No need to check for < replay.Count since we decremented! if (index >= 0) { - LegacyReplayFrame previousFrame = Frames[index]; + ReplayFrame previousFrame = Frames[index]; var previousButton = previousFrame.ButtonState; // If a button is already held, then we simply alternate - if (previousButton != LegacyButtonState.None) + if (previousButton != ReplayButtonState.None) { - Debug.Assert(previousButton != (LegacyButtonState.Left1 | LegacyButtonState.Right1)); + Debug.Assert(previousButton != (ReplayButtonState.Left1 | ReplayButtonState.Right1)); // Force alternation if we have the same button. Otherwise we can just keep the naturally to us assigned button. if (previousButton == button) { - button = (LegacyButtonState.Left1 | LegacyButtonState.Right1) & ~button; - newFrame.SetButtonStates(button); + button = (ReplayButtonState.Left1 | ReplayButtonState.Right1) & ~button; + newFrame.ButtonState = button; } // We always follow the most recent slider / spinner, so remove any other frames that occur while it exists. @@ -246,7 +247,7 @@ namespace osu.Game.Modes.Osu { // Don't affect frames which stop pressing a button! if (j < Frames.Count - 1 || Frames[j].ButtonState == previousButton) - Frames[j].SetButtonStates(button); + Frames[j].ButtonState = button; } } } @@ -270,13 +271,13 @@ namespace osu.Game.Modes.Osu t = applyModsToTime(j - h.StartTime) * spinnerDirection; Vector2 pos = spinner_centre + circlePosition(t / 20 + angle, spin_radius); - addFrameToReplay(new LegacyReplayFrame((int)j, pos.X, pos.Y, button)); + addFrameToReplay(new ReplayFrame((int)j, pos.X, pos.Y, button)); } t = applyModsToTime(s.EndTime - h.StartTime) * spinnerDirection; Vector2 endPosition = spinner_centre + circlePosition(t / 20 + angle, spin_radius); - addFrameToReplay(new LegacyReplayFrame(s.EndTime, endPosition.X, endPosition.Y, button)); + addFrameToReplay(new ReplayFrame(s.EndTime, endPosition.X, endPosition.Y, button)); endFrame.MouseX = endPosition.X; endFrame.MouseY = endPosition.Y; @@ -288,10 +289,10 @@ namespace osu.Game.Modes.Osu for (double j = frameDelay; j < s.Duration; j += frameDelay) { Vector2 pos = s.PositionAt(j / s.Duration); - addFrameToReplay(new LegacyReplayFrame(h.StartTime + j, pos.X, pos.Y, button)); + addFrameToReplay(new ReplayFrame(h.StartTime + j, pos.X, pos.Y, button)); } - addFrameToReplay(new LegacyReplayFrame(s.EndTime, s.EndPosition.X, s.EndPosition.Y, button)); + addFrameToReplay(new ReplayFrame(s.EndTime, s.EndPosition.X, s.EndPosition.Y, button)); } // We only want to let go of our button if we are at the end of the current replay. Otherwise something is still going on after us so we need to keep the button pressed! diff --git a/osu.Game/Modes/LegacyReplay.cs b/osu.Game/Modes/Replays/FramedReplay.cs similarity index 53% rename from osu.Game/Modes/LegacyReplay.cs rename to osu.Game/Modes/Replays/FramedReplay.cs index d57d4a15d2..26144d1ed7 100644 --- a/osu.Game/Modes/LegacyReplay.cs +++ b/osu.Game/Modes/Replays/FramedReplay.cs @@ -3,67 +3,39 @@ using System; using System.Collections.Generic; -using System.IO; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Input; using osu.Framework.MathUtils; using osu.Game.Input.Handlers; -using osu.Game.IO.Legacy; using OpenTK; using OpenTK.Input; using KeyboardState = osu.Framework.Input.KeyboardState; using MouseState = osu.Framework.Input.MouseState; -namespace osu.Game.Modes +namespace osu.Game.Modes.Replays { - public class LegacyReplay : Replay + public abstract class FramedReplay : Replay { - protected List Frames = new List(); + protected List Frames = new List(); - protected LegacyReplay() - { - - } - - public LegacyReplay(StreamReader reader) - { - float lastTime = 0; - - foreach (var l in reader.ReadToEnd().Split(',')) - { - var split = l.Split('|'); - - if (split.Length < 4 || float.Parse(split[0]) < 0) continue; - - lastTime += float.Parse(split[0]); - - Frames.Add(new LegacyReplayFrame( - lastTime, - float.Parse(split[1]), - 384 - float.Parse(split[2]), - (LegacyButtonState)int.Parse(split[3]) - )); - } - } - - public override ReplayInputHandler CreateInputHandler() => new LegacyReplayInputHandler(Frames); + public override ReplayInputHandler CreateInputHandler() => new FramedReplayInputHandler(Frames); /// /// The ReplayHandler will take a replay and handle the propagation of updates to the input stack. /// It handles logic of any frames which *must* be executed. /// - protected class LegacyReplayInputHandler : ReplayInputHandler + public class FramedReplayInputHandler : ReplayInputHandler { - private readonly List replayContent; + private readonly List replayContent; - public LegacyReplayFrame CurrentFrame => !hasFrames ? null : replayContent[currentFrameIndex]; - public LegacyReplayFrame NextFrame => !hasFrames ? null : replayContent[nextFrameIndex]; + public ReplayFrame CurrentFrame => !hasFrames ? null : replayContent[currentFrameIndex]; + public ReplayFrame NextFrame => !hasFrames ? null : replayContent[nextFrameIndex]; private int currentFrameIndex; private int nextFrameIndex => MathHelper.Clamp(currentFrameIndex + (currentDirection > 0 ? 1 : -1), 0, replayContent.Count - 1); - public LegacyReplayInputHandler(List replayContent) + public FramedReplayInputHandler(List replayContent) { this.replayContent = replayContent; } @@ -133,7 +105,7 @@ namespace osu.Game.Modes private bool inImportantSection => FrameAccuratePlayback && //a button is in a pressed state - (currentDirection > 0 ? CurrentFrame : NextFrame)?.ButtonState > LegacyButtonState.None && + ((currentDirection > 0 ? CurrentFrame : NextFrame)?.IsImportant ?? false) && //the next frame is within an allowable time span Math.Abs(currentTime - NextFrame?.Time ?? 0) <= sixty_frame_time * 1.2; @@ -180,89 +152,5 @@ namespace osu.Game.Modes } } } - - [Flags] - protected enum LegacyButtonState - { - None = 0, - Left1 = 1, - Right1 = 2, - Left2 = 4, - Right2 = 8, - Smoke = 16 - } - - protected class LegacyReplayFrame - { - public Vector2 Position => new Vector2(MouseX, MouseY); - - public float MouseX; - public float MouseY; - public bool MouseLeft; - public bool MouseRight; - public bool MouseLeft1; - public bool MouseRight1; - public bool MouseLeft2; - public bool MouseRight2; - public LegacyButtonState ButtonState; - public double Time; - - public LegacyReplayFrame(double time, float posX, float posY, LegacyButtonState buttonState) - { - MouseX = posX; - MouseY = posY; - ButtonState = buttonState; - SetButtonStates(buttonState); - Time = time; - } - - public void SetButtonStates(LegacyButtonState buttonState) - { - ButtonState = buttonState; - MouseLeft = (buttonState & (LegacyButtonState.Left1 | LegacyButtonState.Left2)) > 0; - MouseLeft1 = (buttonState & LegacyButtonState.Left1) > 0; - MouseLeft2 = (buttonState & LegacyButtonState.Left2) > 0; - MouseRight = (buttonState & (LegacyButtonState.Right1 | LegacyButtonState.Right2)) > 0; - MouseRight1 = (buttonState & LegacyButtonState.Right1) > 0; - MouseRight2 = (buttonState & LegacyButtonState.Right2) > 0; - } - - public LegacyReplayFrame(Stream s) : this(new SerializationReader(s)) - { - } - - public LegacyReplayFrame(SerializationReader sr) - { - ButtonState = (LegacyButtonState)sr.ReadByte(); - SetButtonStates(ButtonState); - - byte bt = sr.ReadByte(); - if (bt > 0)//Handle Pre-Taiko compatible replays. - SetButtonStates(LegacyButtonState.Right1); - - MouseX = sr.ReadSingle(); - MouseY = sr.ReadSingle(); - Time = sr.ReadInt32(); - } - - public void ReadFromStream(SerializationReader sr) - { - throw new NotImplementedException(); - } - - public void WriteToStream(SerializationWriter sw) - { - sw.Write((byte)ButtonState); - sw.Write((byte)0); - sw.Write(MouseX); - sw.Write(MouseY); - sw.Write(Time); - } - - public override string ToString() - { - return $"{Time}\t({MouseX},{MouseY})\t{MouseLeft}\t{MouseRight}\t{MouseLeft1}\t{MouseRight1}\t{MouseLeft2}\t{MouseRight2}\t{ButtonState}"; - } - } } -} +} \ No newline at end of file diff --git a/osu.Game/Modes/Replays/LegacyFramedReplay.cs b/osu.Game/Modes/Replays/LegacyFramedReplay.cs new file mode 100644 index 0000000000..3e93751e09 --- /dev/null +++ b/osu.Game/Modes/Replays/LegacyFramedReplay.cs @@ -0,0 +1,70 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.IO; +using osu.Game.IO.Legacy; + +namespace osu.Game.Modes.Replays +{ + /// + /// Reads a replay from a legacy replay file (.osr v1) + /// + public class LegacyReplay : FramedReplay + { + public LegacyReplay(StreamReader reader) + { + float lastTime = 0; + + foreach (var l in reader.ReadToEnd().Split(',')) + { + var split = l.Split('|'); + + if (split.Length < 4 || float.Parse(split[0]) < 0) continue; + + lastTime += float.Parse(split[0]); + + Frames.Add(new ReplayFrame( + lastTime, + float.Parse(split[1]), + 384 - float.Parse(split[2]), + (ReplayButtonState)int.Parse(split[3]) + )); + } + } + + public class LegacyReplayFrame : ReplayFrame + { + public LegacyReplayFrame(Stream s) : this(new SerializationReader(s)) + { + } + + public LegacyReplayFrame(SerializationReader sr) + { + ButtonState = (ReplayButtonState)sr.ReadByte(); + + byte bt = sr.ReadByte(); + if (bt > 0)//Handle Pre-Taiko compatible replays. + ButtonState = ReplayButtonState.Right1; + + MouseX = sr.ReadSingle(); + MouseY = sr.ReadSingle(); + Time = sr.ReadInt32(); + } + + public void ReadFromStream(SerializationReader sr) + { + throw new NotImplementedException(); + } + + public void WriteToStream(SerializationWriter sw) + { + sw.Write((byte)ButtonState); + sw.Write((byte)0); + sw.Write(MouseX); + sw.Write(MouseY); + sw.Write(Time); + } + } + } +} diff --git a/osu.Game/Modes/Replay.cs b/osu.Game/Modes/Replays/Replay.cs similarity index 87% rename from osu.Game/Modes/Replay.cs rename to osu.Game/Modes/Replays/Replay.cs index 6d93afdeb9..c1ae952b77 100644 --- a/osu.Game/Modes/Replay.cs +++ b/osu.Game/Modes/Replays/Replay.cs @@ -3,7 +3,7 @@ using osu.Game.Input.Handlers; -namespace osu.Game.Modes +namespace osu.Game.Modes.Replays { public abstract class Replay { diff --git a/osu.Game/Modes/Replays/ReplayButtonState.cs b/osu.Game/Modes/Replays/ReplayButtonState.cs new file mode 100644 index 0000000000..d49139226c --- /dev/null +++ b/osu.Game/Modes/Replays/ReplayButtonState.cs @@ -0,0 +1,18 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; + +namespace osu.Game.Modes.Replays +{ + [Flags] + public enum ReplayButtonState + { + None = 0, + Left1 = 1, + Right1 = 2, + Left2 = 4, + Right2 = 8, + Smoke = 16 + } +} \ No newline at end of file diff --git a/osu.Game/Modes/Replays/ReplayFrame.cs b/osu.Game/Modes/Replays/ReplayFrame.cs new file mode 100644 index 0000000000..4cd681beaf --- /dev/null +++ b/osu.Game/Modes/Replays/ReplayFrame.cs @@ -0,0 +1,71 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; + +namespace osu.Game.Modes.Replays +{ + public class ReplayFrame + { + public Vector2 Position => new Vector2(MouseX, MouseY); + + public bool IsImportant => MouseLeft || MouseRight; + + public float MouseX; + public float MouseY; + + public bool MouseLeft => MouseLeft1 || MouseLeft2; + public bool MouseRight => MouseRight1 || MouseRight2; + + public bool MouseLeft1 + { + get { return (ButtonState & ReplayButtonState.Left1) > 0; } + set { setButtonState(ReplayButtonState.Left1, value); } + } + public bool MouseRight1 + { + get { return (ButtonState & ReplayButtonState.Right1) > 0; } + set { setButtonState(ReplayButtonState.Right1, value); } + } + public bool MouseLeft2 + { + get { return (ButtonState & ReplayButtonState.Left2) > 0; } + set { setButtonState(ReplayButtonState.Left2, value); } + } + public bool MouseRight2 + { + get { return (ButtonState & ReplayButtonState.Right2) > 0; } + set { setButtonState(ReplayButtonState.Right2, value); } + } + + private void setButtonState(ReplayButtonState singleButton, bool pressed) + { + if (pressed) + ButtonState |= singleButton; + else + ButtonState &= ~singleButton; + } + + public double Time; + + public ReplayButtonState ButtonState; + + protected ReplayFrame() + { + + } + + public ReplayFrame(double time, float posX, float posY, ReplayButtonState buttonState) + { + MouseX = posX; + MouseY = posY; + ButtonState = buttonState; + Time = time; + } + + public override string ToString() + { + return $"{Time}\t({MouseX},{MouseY})\t{MouseLeft}\t{MouseRight}\t{MouseLeft1}\t{MouseRight1}\t{MouseLeft2}\t{MouseRight2}\t{ButtonState}"; + } + } +} \ No newline at end of file diff --git a/osu.Game/Modes/Scoring/Score.cs b/osu.Game/Modes/Scoring/Score.cs index 75c243278d..ab376ebdd6 100644 --- a/osu.Game/Modes/Scoring/Score.cs +++ b/osu.Game/Modes/Scoring/Score.cs @@ -7,6 +7,7 @@ using osu.Game.Database; using osu.Game.Modes.Mods; using osu.Game.Users; using System.IO; +using osu.Game.Modes.Replays; namespace osu.Game.Modes.Scoring { diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index a5bdf1df69..0bcbe9032f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -96,8 +96,9 @@ + - + @@ -123,7 +124,9 @@ - + + + From 0bcec4e61a158f976d0af09f258dbcb08ed6991d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 Mar 2017 12:55:43 +0900 Subject: [PATCH 16/22] Add null-checks to comparers. --- osu.Game.Modes.Osu/OsuAutoReplay.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Modes.Osu/OsuAutoReplay.cs b/osu.Game.Modes.Osu/OsuAutoReplay.cs index 2ac5f771c3..cc5859602a 100644 --- a/osu.Game.Modes.Osu/OsuAutoReplay.cs +++ b/osu.Game.Modes.Osu/OsuAutoReplay.cs @@ -34,6 +34,9 @@ namespace osu.Game.Modes.Osu { public int Compare(ReplayFrame f1, ReplayFrame f2) { + if (f1 == null) throw new NullReferenceException($@"{nameof(f1)} cannot be null"); + if (f2 == null) throw new NullReferenceException($@"{nameof(f2)} cannot be null"); + return f1.Time.CompareTo(f2.Time); } } From 79031b9e749fe5e1ca2e81eb82d1acdc805e15f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 Mar 2017 12:58:54 +0900 Subject: [PATCH 17/22] Update framework. --- osu-framework | 2 +- osu.Game/Graphics/UserInterface/PercentageCounter.cs | 2 +- osu.Game/Graphics/UserInterface/ScoreCounter.cs | 2 +- osu.Game/Modes/UI/ComboCounter.cs | 2 +- osu.Game/Modes/UI/ComboResultCounter.cs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu-framework b/osu-framework index 269a1fd192..7ae3cf1a10 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 269a1fd192c573d558a5ab0ff990a8b462947287 +Subproject commit 7ae3cf1a10fa973a49995a71cbaf768702be1cce diff --git a/osu.Game/Graphics/UserInterface/PercentageCounter.cs b/osu.Game/Graphics/UserInterface/PercentageCounter.cs index 66c9e7d461..c32b654840 100644 --- a/osu.Game/Graphics/UserInterface/PercentageCounter.cs +++ b/osu.Game/Graphics/UserInterface/PercentageCounter.cs @@ -47,7 +47,7 @@ namespace osu.Game.Graphics.UserInterface protected class TransformAccuracy : Transform { - protected override double CurrentValue + public override double CurrentValue { get { diff --git a/osu.Game/Graphics/UserInterface/ScoreCounter.cs b/osu.Game/Graphics/UserInterface/ScoreCounter.cs index c9a1040185..c2b1b026b6 100644 --- a/osu.Game/Graphics/UserInterface/ScoreCounter.cs +++ b/osu.Game/Graphics/UserInterface/ScoreCounter.cs @@ -51,7 +51,7 @@ namespace osu.Game.Graphics.UserInterface protected class TransformScore : Transform { - protected override double CurrentValue + public override double CurrentValue { get { diff --git a/osu.Game/Modes/UI/ComboCounter.cs b/osu.Game/Modes/UI/ComboCounter.cs index f831677e44..3629634889 100644 --- a/osu.Game/Modes/UI/ComboCounter.cs +++ b/osu.Game/Modes/UI/ComboCounter.cs @@ -213,7 +213,7 @@ namespace osu.Game.Modes.UI protected class TransformComboRoll : Transform { - protected override int CurrentValue + public override int CurrentValue { get { diff --git a/osu.Game/Modes/UI/ComboResultCounter.cs b/osu.Game/Modes/UI/ComboResultCounter.cs index 957a720c94..63009c5351 100644 --- a/osu.Game/Modes/UI/ComboResultCounter.cs +++ b/osu.Game/Modes/UI/ComboResultCounter.cs @@ -36,7 +36,7 @@ namespace osu.Game.Modes.UI protected class TransformComboResult : Transform { - protected override ulong CurrentValue + public override ulong CurrentValue { get { From c531d774b7d77eda253cdbd1e459f27adec72060 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 Mar 2017 15:21:30 +0900 Subject: [PATCH 18/22] Fix file naming. --- osu.Game/Modes/Replays/LegacyFramedReplay.cs | 4 ++-- osu.Game/Modes/Scoring/Score.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Modes/Replays/LegacyFramedReplay.cs b/osu.Game/Modes/Replays/LegacyFramedReplay.cs index 3e93751e09..3b99614f5e 100644 --- a/osu.Game/Modes/Replays/LegacyFramedReplay.cs +++ b/osu.Game/Modes/Replays/LegacyFramedReplay.cs @@ -10,9 +10,9 @@ namespace osu.Game.Modes.Replays /// /// Reads a replay from a legacy replay file (.osr v1) /// - public class LegacyReplay : FramedReplay + public class LegacyFramedReplay : FramedReplay { - public LegacyReplay(StreamReader reader) + public LegacyFramedReplay(StreamReader reader) { float lastTime = 0; diff --git a/osu.Game/Modes/Scoring/Score.cs b/osu.Game/Modes/Scoring/Score.cs index ab376ebdd6..70777a77ef 100644 --- a/osu.Game/Modes/Scoring/Score.cs +++ b/osu.Game/Modes/Scoring/Score.cs @@ -50,7 +50,7 @@ namespace osu.Game.Modes.Scoring /// /// The stream reader. /// The replay. - public virtual Replay CreateLegacyReplayFrom(StreamReader reader) => new LegacyReplay(reader); + public virtual Replay CreateLegacyReplayFrom(StreamReader reader) => new LegacyFramedReplay(reader); // [JsonProperty(@"count50")] 0, //[JsonProperty(@"count100")] 0, From 0f4b98ce7320825aefa7440a4d5c483f68aebc41 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 Mar 2017 15:32:34 +0900 Subject: [PATCH 19/22] Move FramedReplayInputHandler to own file and un-nest. --- osu.Game/Modes/Replays/FramedReplay.cs | 141 ----------------- .../Modes/Replays/FramedReplayInputHandler.cs | 146 ++++++++++++++++++ osu.Game/osu.Game.csproj | 1 + 3 files changed, 147 insertions(+), 141 deletions(-) create mode 100644 osu.Game/Modes/Replays/FramedReplayInputHandler.cs diff --git a/osu.Game/Modes/Replays/FramedReplay.cs b/osu.Game/Modes/Replays/FramedReplay.cs index 26144d1ed7..53b258e28b 100644 --- a/osu.Game/Modes/Replays/FramedReplay.cs +++ b/osu.Game/Modes/Replays/FramedReplay.cs @@ -1,16 +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.Collections.Generic; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Input; -using osu.Framework.MathUtils; using osu.Game.Input.Handlers; -using OpenTK; -using OpenTK.Input; -using KeyboardState = osu.Framework.Input.KeyboardState; -using MouseState = osu.Framework.Input.MouseState; namespace osu.Game.Modes.Replays { @@ -19,138 +11,5 @@ namespace osu.Game.Modes.Replays protected List Frames = new List(); public override ReplayInputHandler CreateInputHandler() => new FramedReplayInputHandler(Frames); - - /// - /// The ReplayHandler will take a replay and handle the propagation of updates to the input stack. - /// It handles logic of any frames which *must* be executed. - /// - public class FramedReplayInputHandler : ReplayInputHandler - { - private readonly List replayContent; - - public ReplayFrame CurrentFrame => !hasFrames ? null : replayContent[currentFrameIndex]; - public ReplayFrame NextFrame => !hasFrames ? null : replayContent[nextFrameIndex]; - - private int currentFrameIndex; - - private int nextFrameIndex => MathHelper.Clamp(currentFrameIndex + (currentDirection > 0 ? 1 : -1), 0, replayContent.Count - 1); - - public FramedReplayInputHandler(List replayContent) - { - this.replayContent = replayContent; - } - - private bool advanceFrame() - { - int newFrame = nextFrameIndex; - - //ensure we aren't at an extent. - if (newFrame == currentFrameIndex) return false; - - currentFrameIndex = newFrame; - return true; - } - - public void SetPosition(Vector2 pos) - { - } - - private Vector2? position - { - get - { - if (!hasFrames) - return null; - - return Interpolation.ValueAt(currentTime, CurrentFrame.Position, NextFrame.Position, CurrentFrame.Time, NextFrame.Time); - } - } - - public override List GetPendingStates() - { - var buttons = new HashSet(); - if (CurrentFrame?.MouseLeft ?? false) - buttons.Add(MouseButton.Left); - if (CurrentFrame?.MouseRight ?? false) - buttons.Add(MouseButton.Right); - - return new List - { - new InputState - { - Mouse = new ReplayMouseState(ToScreenSpace(position ?? Vector2.Zero), buttons), - Keyboard = new ReplayKeyboardState(new List()) - } - }; - } - - public bool AtLastFrame => currentFrameIndex == replayContent.Count - 1; - public bool AtFirstFrame => currentFrameIndex == 0; - - public Vector2 Size => new Vector2(512, 384); - - private const double sixty_frame_time = 1000.0 / 60; - - private double currentTime; - private int currentDirection; - - /// - /// When set, we will ensure frames executed by nested drawables are frame-accurate to replay data. - /// Disabling this can make replay playback smoother (useful for autoplay, currently). - /// - public bool FrameAccuratePlayback = true; - - private bool hasFrames => replayContent.Count > 0; - - private bool inImportantSection => - FrameAccuratePlayback && - //a button is in a pressed state - ((currentDirection > 0 ? CurrentFrame : NextFrame)?.IsImportant ?? false) && - //the next frame is within an allowable time span - Math.Abs(currentTime - NextFrame?.Time ?? 0) <= sixty_frame_time * 1.2; - - /// - /// Update the current frame based on an incoming time value. - /// There are cases where we return a "must-use" time value that is different from the input. - /// This is to ensure accurate playback of replay data. - /// - /// The time which we should use for finding the current frame. - /// The usable time value. If null, we should not advance time as we do not have enough data. - public override double? SetFrameFromTime(double time) - { - currentDirection = time.CompareTo(currentTime); - if (currentDirection == 0) currentDirection = 1; - - if (hasFrames) - { - //if we changed frames, we want to execute once *exactly* on the frame's time. - if (currentDirection == time.CompareTo(NextFrame.Time) && advanceFrame()) - 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) - return null; - } - - return currentTime = time; - } - - protected class ReplayMouseState : MouseState - { - public ReplayMouseState(Vector2 position, IEnumerable list) - { - Position = position; - list.ForEach(b => PressedButtons.Add(b)); - } - } - - protected class ReplayKeyboardState : KeyboardState - { - public ReplayKeyboardState(List keys) - { - Keys = keys; - } - } - } } } \ No newline at end of file diff --git a/osu.Game/Modes/Replays/FramedReplayInputHandler.cs b/osu.Game/Modes/Replays/FramedReplayInputHandler.cs new file mode 100644 index 0000000000..d72c023539 --- /dev/null +++ b/osu.Game/Modes/Replays/FramedReplayInputHandler.cs @@ -0,0 +1,146 @@ +using System; +using System.Collections.Generic; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Input; +using osu.Framework.MathUtils; +using osu.Game.Input.Handlers; +using OpenTK; +using OpenTK.Input; +using KeyboardState = osu.Framework.Input.KeyboardState; +using MouseState = osu.Framework.Input.MouseState; + +namespace osu.Game.Modes.Replays +{ + /// + /// The ReplayHandler will take a replay and handle the propagation of updates to the input stack. + /// It handles logic of any frames which *must* be executed. + /// + public class FramedReplayInputHandler : ReplayInputHandler + { + private readonly List replayContent; + + public ReplayFrame CurrentFrame => !hasFrames ? null : replayContent[currentFrameIndex]; + public ReplayFrame NextFrame => !hasFrames ? null : replayContent[nextFrameIndex]; + + private int currentFrameIndex; + + private int nextFrameIndex => MathHelper.Clamp(currentFrameIndex + (currentDirection > 0 ? 1 : -1), 0, replayContent.Count - 1); + + public FramedReplayInputHandler(List replayContent) + { + this.replayContent = replayContent; + } + + private bool advanceFrame() + { + int newFrame = nextFrameIndex; + + //ensure we aren't at an extent. + if (newFrame == currentFrameIndex) return false; + + currentFrameIndex = newFrame; + return true; + } + + public void SetPosition(Vector2 pos) + { + } + + private Vector2? position + { + get + { + if (!hasFrames) + return null; + + return Interpolation.ValueAt(currentTime, CurrentFrame.Position, NextFrame.Position, CurrentFrame.Time, NextFrame.Time); + } + } + + public override List GetPendingStates() + { + var buttons = new HashSet(); + if (CurrentFrame?.MouseLeft ?? false) + buttons.Add(MouseButton.Left); + if (CurrentFrame?.MouseRight ?? false) + buttons.Add(MouseButton.Right); + + return new List + { + new InputState + { + Mouse = new ReplayMouseState(ToScreenSpace(position ?? Vector2.Zero), buttons), + Keyboard = new ReplayKeyboardState(new List()) + } + }; + } + + public bool AtLastFrame => currentFrameIndex == replayContent.Count - 1; + public bool AtFirstFrame => currentFrameIndex == 0; + + public Vector2 Size => new Vector2(512, 384); + + private const double sixty_frame_time = 1000.0 / 60; + + private double currentTime; + private int currentDirection; + + /// + /// When set, we will ensure frames executed by nested drawables are frame-accurate to replay data. + /// Disabling this can make replay playback smoother (useful for autoplay, currently). + /// + public bool FrameAccuratePlayback = true; + + private bool hasFrames => replayContent.Count > 0; + + private bool inImportantSection => + FrameAccuratePlayback && + //a button is in a pressed state + ((currentDirection > 0 ? CurrentFrame : NextFrame)?.IsImportant ?? false) && + //the next frame is within an allowable time span + Math.Abs(currentTime - NextFrame?.Time ?? 0) <= sixty_frame_time * 1.2; + + /// + /// Update the current frame based on an incoming time value. + /// There are cases where we return a "must-use" time value that is different from the input. + /// This is to ensure accurate playback of replay data. + /// + /// The time which we should use for finding the current frame. + /// The usable time value. If null, we should not advance time as we do not have enough data. + public override double? SetFrameFromTime(double time) + { + currentDirection = time.CompareTo(currentTime); + if (currentDirection == 0) currentDirection = 1; + + if (hasFrames) + { + //if we changed frames, we want to execute once *exactly* on the frame's time. + if (currentDirection == time.CompareTo(NextFrame.Time) && advanceFrame()) + 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) + return null; + } + + return currentTime = time; + } + + protected class ReplayMouseState : MouseState + { + public ReplayMouseState(Vector2 position, IEnumerable list) + { + Position = position; + list.ForEach(b => PressedButtons.Add(b)); + } + } + + protected class ReplayKeyboardState : KeyboardState + { + public ReplayKeyboardState(List keys) + { + Keys = keys; + } + } + } +} \ No newline at end of file diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 0bcbe9032f..a3ece6d5ca 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -98,6 +98,7 @@ + From fa7c72a0990c8b5f0ec19ec4c71bd1b821e790f7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 Mar 2017 15:59:53 +0900 Subject: [PATCH 20/22] Refactor ReplayInputHandler creation for more flexibility. --- osu.Game.Modes.Osu/OsuAutoReplay.cs | 2 +- osu.Game/Database/ScoreDatabase.cs | 2 +- osu.Game/Modes/Mods/Mod.cs | 2 +- osu.Game/Modes/Replays/FramedReplay.cs | 15 ---- .../Modes/Replays/FramedReplayInputHandler.cs | 23 +++--- osu.Game/Modes/Replays/LegacyFramedReplay.cs | 70 ------------------- osu.Game/Modes/Replays/Replay.cs | 6 +- osu.Game/Modes/Scoring/Score.cs | 28 +++++++- osu.Game/Modes/UI/HitRenderer.cs | 5 ++ osu.Game/OsuGame.cs | 2 +- osu.Game/Screens/Play/Player.cs | 20 ++---- osu.Game/Screens/Play/ReplayPlayer.cs | 23 ++++++ osu.Game/osu.Game.csproj | 5 +- 13 files changed, 84 insertions(+), 119 deletions(-) delete mode 100644 osu.Game/Modes/Replays/FramedReplay.cs delete mode 100644 osu.Game/Modes/Replays/LegacyFramedReplay.cs create mode 100644 osu.Game/Screens/Play/ReplayPlayer.cs diff --git a/osu.Game.Modes.Osu/OsuAutoReplay.cs b/osu.Game.Modes.Osu/OsuAutoReplay.cs index cc5859602a..ae85bd72d8 100644 --- a/osu.Game.Modes.Osu/OsuAutoReplay.cs +++ b/osu.Game.Modes.Osu/OsuAutoReplay.cs @@ -15,7 +15,7 @@ using osu.Game.Modes.Replays; namespace osu.Game.Modes.Osu { - public class OsuAutoReplay : FramedReplay + public class OsuAutoReplay : Replay { private static readonly Vector2 spinner_centre = new Vector2(256, 192); diff --git a/osu.Game/Database/ScoreDatabase.cs b/osu.Game/Database/ScoreDatabase.cs index 096c0dcc29..5ce3ff273e 100644 --- a/osu.Game/Database/ScoreDatabase.cs +++ b/osu.Game/Database/ScoreDatabase.cs @@ -101,7 +101,7 @@ namespace osu.Game.Database using (var lzma = new LzmaStream(properties, replayInStream, compressedSize, outSize)) using (var reader = new StreamReader(lzma)) - score.Replay = score.CreateLegacyReplayFrom(reader); + score.Replay = score.CreateReplay(reader); } } diff --git a/osu.Game/Modes/Mods/Mod.cs b/osu.Game/Modes/Mods/Mod.cs index c53c6faa21..b6f09b8506 100644 --- a/osu.Game/Modes/Mods/Mod.cs +++ b/osu.Game/Modes/Mods/Mod.cs @@ -157,7 +157,7 @@ namespace osu.Game.Modes.Mods public void Apply(HitRenderer hitRenderer) { - hitRenderer.InputManager.ReplayInputHandler = CreateReplayScore(hitRenderer.Beatmap)?.Replay?.CreateInputHandler(); + hitRenderer.SetReplay(CreateReplayScore(hitRenderer.Beatmap)?.Replay); } } diff --git a/osu.Game/Modes/Replays/FramedReplay.cs b/osu.Game/Modes/Replays/FramedReplay.cs deleted file mode 100644 index 53b258e28b..0000000000 --- a/osu.Game/Modes/Replays/FramedReplay.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.Collections.Generic; -using osu.Game.Input.Handlers; - -namespace osu.Game.Modes.Replays -{ - public abstract class FramedReplay : Replay - { - protected List Frames = new List(); - - public override ReplayInputHandler CreateInputHandler() => new FramedReplayInputHandler(Frames); - } -} \ No newline at end of file diff --git a/osu.Game/Modes/Replays/FramedReplayInputHandler.cs b/osu.Game/Modes/Replays/FramedReplayInputHandler.cs index d72c023539..ae20ece515 100644 --- a/osu.Game/Modes/Replays/FramedReplayInputHandler.cs +++ b/osu.Game/Modes/Replays/FramedReplayInputHandler.cs @@ -1,4 +1,7 @@ -using System; +// 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.Extensions.IEnumerableExtensions; using osu.Framework.Input; @@ -17,18 +20,20 @@ namespace osu.Game.Modes.Replays /// public class FramedReplayInputHandler : ReplayInputHandler { - private readonly List replayContent; + private readonly Replay replay; - public ReplayFrame CurrentFrame => !hasFrames ? null : replayContent[currentFrameIndex]; - public ReplayFrame NextFrame => !hasFrames ? null : replayContent[nextFrameIndex]; + protected List Frames => replay.Frames; + + public ReplayFrame CurrentFrame => !hasFrames ? null : Frames[currentFrameIndex]; + public ReplayFrame NextFrame => !hasFrames ? null : Frames[nextFrameIndex]; private int currentFrameIndex; - private int nextFrameIndex => MathHelper.Clamp(currentFrameIndex + (currentDirection > 0 ? 1 : -1), 0, replayContent.Count - 1); + private int nextFrameIndex => MathHelper.Clamp(currentFrameIndex + (currentDirection > 0 ? 1 : -1), 0, Frames.Count - 1); - public FramedReplayInputHandler(List replayContent) + public FramedReplayInputHandler(Replay replay) { - this.replayContent = replayContent; + this.replay = replay; } private bool advanceFrame() @@ -75,7 +80,7 @@ namespace osu.Game.Modes.Replays }; } - public bool AtLastFrame => currentFrameIndex == replayContent.Count - 1; + public bool AtLastFrame => currentFrameIndex == Frames.Count - 1; public bool AtFirstFrame => currentFrameIndex == 0; public Vector2 Size => new Vector2(512, 384); @@ -91,7 +96,7 @@ namespace osu.Game.Modes.Replays /// public bool FrameAccuratePlayback = true; - private bool hasFrames => replayContent.Count > 0; + private bool hasFrames => Frames.Count > 0; private bool inImportantSection => FrameAccuratePlayback && diff --git a/osu.Game/Modes/Replays/LegacyFramedReplay.cs b/osu.Game/Modes/Replays/LegacyFramedReplay.cs deleted file mode 100644 index 3b99614f5e..0000000000 --- a/osu.Game/Modes/Replays/LegacyFramedReplay.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System; -using System.IO; -using osu.Game.IO.Legacy; - -namespace osu.Game.Modes.Replays -{ - /// - /// Reads a replay from a legacy replay file (.osr v1) - /// - public class LegacyFramedReplay : FramedReplay - { - public LegacyFramedReplay(StreamReader reader) - { - float lastTime = 0; - - foreach (var l in reader.ReadToEnd().Split(',')) - { - var split = l.Split('|'); - - if (split.Length < 4 || float.Parse(split[0]) < 0) continue; - - lastTime += float.Parse(split[0]); - - Frames.Add(new ReplayFrame( - lastTime, - float.Parse(split[1]), - 384 - float.Parse(split[2]), - (ReplayButtonState)int.Parse(split[3]) - )); - } - } - - public class LegacyReplayFrame : ReplayFrame - { - public LegacyReplayFrame(Stream s) : this(new SerializationReader(s)) - { - } - - public LegacyReplayFrame(SerializationReader sr) - { - ButtonState = (ReplayButtonState)sr.ReadByte(); - - byte bt = sr.ReadByte(); - if (bt > 0)//Handle Pre-Taiko compatible replays. - ButtonState = ReplayButtonState.Right1; - - MouseX = sr.ReadSingle(); - MouseY = sr.ReadSingle(); - Time = sr.ReadInt32(); - } - - public void ReadFromStream(SerializationReader sr) - { - throw new NotImplementedException(); - } - - public void WriteToStream(SerializationWriter sw) - { - sw.Write((byte)ButtonState); - sw.Write((byte)0); - sw.Write(MouseX); - sw.Write(MouseY); - sw.Write(Time); - } - } - } -} diff --git a/osu.Game/Modes/Replays/Replay.cs b/osu.Game/Modes/Replays/Replay.cs index c1ae952b77..62f60358e0 100644 --- a/osu.Game/Modes/Replays/Replay.cs +++ b/osu.Game/Modes/Replays/Replay.cs @@ -1,12 +1,12 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using osu.Game.Input.Handlers; +using System.Collections.Generic; namespace osu.Game.Modes.Replays { - public abstract class Replay + public class Replay { - public virtual ReplayInputHandler CreateInputHandler() => null; + public List Frames = new List(); } } \ No newline at end of file diff --git a/osu.Game/Modes/Scoring/Score.cs b/osu.Game/Modes/Scoring/Score.cs index 70777a77ef..c998b11f77 100644 --- a/osu.Game/Modes/Scoring/Score.cs +++ b/osu.Game/Modes/Scoring/Score.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Collections.Generic; using Newtonsoft.Json; using osu.Game.Database; using osu.Game.Modes.Mods; @@ -46,11 +47,34 @@ namespace osu.Game.Modes.Scoring public DateTime Date; /// - /// Creates a legacy replay which is read from a stream. + /// Creates a replay which is read from a stream. /// /// The stream reader. /// The replay. - public virtual Replay CreateLegacyReplayFrom(StreamReader reader) => new LegacyFramedReplay(reader); + public virtual Replay CreateReplay(StreamReader reader) + { + var frames = new List(); + + float lastTime = 0; + + foreach (var l in reader.ReadToEnd().Split(',')) + { + var split = l.Split('|'); + + if (split.Length < 4 || float.Parse(split[0]) < 0) continue; + + lastTime += float.Parse(split[0]); + + frames.Add(new ReplayFrame( + lastTime, + float.Parse(split[1]), + 384 - float.Parse(split[2]), + (ReplayButtonState)int.Parse(split[3]) + )); + } + + return new Replay { Frames = frames }; + } // [JsonProperty(@"count50")] 0, //[JsonProperty(@"count100")] 0, diff --git a/osu.Game/Modes/UI/HitRenderer.cs b/osu.Game/Modes/UI/HitRenderer.cs index 3d108b895d..8581d12244 100644 --- a/osu.Game/Modes/UI/HitRenderer.cs +++ b/osu.Game/Modes/UI/HitRenderer.cs @@ -14,6 +14,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using osu.Game.Modes.Replays; using osu.Game.Modes.Scoring; namespace osu.Game.Modes.UI @@ -68,6 +69,10 @@ namespace osu.Game.Modes.UI /// /// The input manager. protected virtual KeyConversionInputManager CreateKeyConversionInputManager() => new KeyConversionInputManager(); + + protected virtual FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new FramedReplayInputHandler(replay); + + public void SetReplay(Replay replay) => InputManager.ReplayInputHandler = CreateReplayInputHandler(replay); } /// diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 66196670b8..8ac86c5c67 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -125,7 +125,7 @@ namespace osu.Game Beatmap.Value = BeatmapDatabase.GetWorkingBeatmap(s.Beatmap); - menu.Push(new PlayerLoader(new Player { ReplayInputHandler = s.Replay.CreateInputHandler() })); + menu.Push(new PlayerLoader(new ReplayPlayer(s.Replay))); } protected override void LoadComplete() diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 0554c0e77b..73d397b24b 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -15,7 +15,6 @@ using osu.Framework.Screens; using osu.Framework.Timing; using osu.Game.Configuration; using osu.Game.Database; -using osu.Game.Input.Handlers; using osu.Game.Modes; using osu.Game.Modes.UI; using osu.Game.Screens.Backgrounds; @@ -34,7 +33,7 @@ namespace osu.Game.Screens.Play internal override bool HasLocalCursorDisplayed => !hasReplayLoaded && !IsPaused; - private bool hasReplayLoaded => hitRenderer.InputManager.ReplayInputHandler != null; + private bool hasReplayLoaded => HitRenderer.InputManager.ReplayInputHandler != null; public BeatmapInfo BeatmapInfo; @@ -53,7 +52,7 @@ namespace osu.Game.Screens.Play private Ruleset ruleset; private ScoreProcessor scoreProcessor; - private HitRenderer hitRenderer; + protected HitRenderer HitRenderer; private Bindable dimLevel; private SkipButton skipButton; @@ -112,9 +111,9 @@ namespace osu.Game.Screens.Play }); ruleset = Ruleset.GetRuleset(Beatmap.PlayMode); - hitRenderer = ruleset.CreateHitRendererWith(Beatmap); + HitRenderer = ruleset.CreateHitRendererWith(Beatmap); - scoreProcessor = hitRenderer.CreateScoreProcessor(); + scoreProcessor = HitRenderer.CreateScoreProcessor(); hudOverlay = new StandardHudOverlay(); hudOverlay.KeyCounter.Add(ruleset.CreateGameplayKeys()); @@ -133,13 +132,10 @@ namespace osu.Game.Screens.Play }; - if (ReplayInputHandler != null) - hitRenderer.InputManager.ReplayInputHandler = ReplayInputHandler; - - hudOverlay.BindHitRenderer(hitRenderer); + hudOverlay.BindHitRenderer(HitRenderer); //bind HitRenderer to ScoreProcessor and ourselves (for a pass situation) - hitRenderer.OnAllJudged += onCompletion; + HitRenderer.OnAllJudged += onCompletion; //bind ScoreProcessor to ourselves (for a fail situation) scoreProcessor.Failed += onFail; @@ -152,7 +148,7 @@ namespace osu.Game.Screens.Play Clock = interpolatedSourceClock, Children = new Drawable[] { - hitRenderer, + HitRenderer, skipButton = new SkipButton { Alpha = 0 @@ -336,8 +332,6 @@ namespace osu.Game.Screens.Play private Bindable mouseWheelDisabled; - public ReplayInputHandler ReplayInputHandler; - protected override bool OnWheel(InputState state) => mouseWheelDisabled.Value && !IsPaused; } } \ No newline at end of file diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs new file mode 100644 index 0000000000..4593656a2e --- /dev/null +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -0,0 +1,23 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Modes.Replays; + +namespace osu.Game.Screens.Play +{ + public class ReplayPlayer : Player + { + public Replay Replay; + + public ReplayPlayer(Replay replay) + { + Replay = replay; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + HitRenderer.SetReplay(Replay); + } + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index a3ece6d5ca..0dc2da48d1 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -96,10 +96,9 @@ - + - @@ -125,7 +124,6 @@ - @@ -202,6 +200,7 @@ + From cf87330f802b20d5e838fd3bfc1f3fc881ef2a00 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 Mar 2017 16:00:40 +0900 Subject: [PATCH 21/22] Allow SetReplay to receive null. --- osu.Game/Modes/UI/HitRenderer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Modes/UI/HitRenderer.cs b/osu.Game/Modes/UI/HitRenderer.cs index 8581d12244..62e3babd77 100644 --- a/osu.Game/Modes/UI/HitRenderer.cs +++ b/osu.Game/Modes/UI/HitRenderer.cs @@ -72,7 +72,7 @@ namespace osu.Game.Modes.UI protected virtual FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new FramedReplayInputHandler(replay); - public void SetReplay(Replay replay) => InputManager.ReplayInputHandler = CreateReplayInputHandler(replay); + public void SetReplay(Replay replay) => InputManager.ReplayInputHandler = replay != null ? CreateReplayInputHandler(replay) : null; } /// From 3f080ab424d80df7c9baf9dda602c916475f6c0c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 Mar 2017 16:01:48 +0900 Subject: [PATCH 22/22] Add some commenting. --- osu.Game/Modes/UI/HitRenderer.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Modes/UI/HitRenderer.cs b/osu.Game/Modes/UI/HitRenderer.cs index 62e3babd77..8c1e495c7a 100644 --- a/osu.Game/Modes/UI/HitRenderer.cs +++ b/osu.Game/Modes/UI/HitRenderer.cs @@ -72,6 +72,10 @@ namespace osu.Game.Modes.UI protected virtual FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new FramedReplayInputHandler(replay); + /// + /// Sets a replay to be used, overriding local input. + /// + /// The replay, null for local input. public void SetReplay(Replay replay) => InputManager.ReplayInputHandler = replay != null ? CreateReplayInputHandler(replay) : null; }