From 76a1c7db3b68318229daca40cd06385088e6ca62 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Nov 2017 21:22:57 +0900 Subject: [PATCH 01/12] Hyperdash preparation --- .../Beatmaps/CatchBeatmapProcessor.cs | 8 +++++ .../Tests/TestCaseFruit.cs | 36 +++++++++++++++++++ .../Tests/TestCaseHyperdash.cs | 26 ++++++++++++++ .../osu.Game.Rulesets.Catch.csproj | 2 ++ 4 files changed, 72 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Tests/TestCaseFruit.cs create mode 100644 osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index b2f7fdabfc..8a19afcd39 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -1,6 +1,7 @@ // 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.Beatmaps; using osu.Game.Rulesets.Catch.Objects; @@ -18,6 +19,8 @@ namespace osu.Game.Rulesets.Catch.Beatmaps CatchHitObject lastObj = null; + convertHyperDash(beatmap.HitObjects); + foreach (var obj in beatmap.HitObjects) { if (obj.NewCombo) @@ -34,5 +37,10 @@ namespace osu.Game.Rulesets.Catch.Beatmaps lastObj = obj; } } + + private void convertHyperDash(List objects) + { + + } } } diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseFruit.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseFruit.cs new file mode 100644 index 0000000000..0ced95edf9 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Tests/TestCaseFruit.cs @@ -0,0 +1,36 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.Objects.Drawable; +using osu.Game.Tests.Visual; +using OpenTK; + +namespace osu.Game.Rulesets.Catch.Tests +{ + public class TestCaseFruit : OsuTestCase + { + public TestCaseFruit() + { + Add(new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + new DrawableFruit(new Fruit()) { Position = new Vector2(0.5f) }, + new DrawableFruit(new Fruit()) { Position = new Vector2(0.5f) }, + }, + new Drawable[] + { + new DrawableFruit(new Fruit()) { Position = new Vector2(0.5f) }, + new DrawableFruit(new Fruit()) { Position = new Vector2(0.5f) }, + } + } + }); + } + } +} diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs new file mode 100644 index 0000000000..822743debc --- /dev/null +++ b/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs @@ -0,0 +1,26 @@ +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Objects; + +namespace osu.Game.Rulesets.Catch.Tests +{ + [TestFixture] + [Ignore("getting CI working")] + public class TestCaseHyperdash : Game.Tests.Visual.TestCasePlayer + { + public TestCaseHyperdash() + : base(typeof(CatchRuleset)) + { + } + + protected override Beatmap CreateBeatmap() + { + var beatmap = new Beatmap(); + + for (int i = 0; i < 512; i++) + beatmap.HitObjects.Add(new Fruit { X = 0.5f + (i % 2 == 0 ? -0.4f : 0.4f), StartTime = i * 100, NewCombo = i % 8 == 0 }); + + return beatmap; + } + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index b7916f674e..4e819fb58b 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -67,6 +67,8 @@ + + From 881745d7561bb25a67c03978a33ee214b09fbc20 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 15:08:12 +0900 Subject: [PATCH 02/12] Initial implementation of hyperdash calculation --- .../Beatmaps/CatchBeatmapProcessor.cs | 43 +++++++++++++++ .../Objects/CatchHitObject.cs | 2 + .../Objects/Drawable/DrawableFruit.cs | 15 ++++++ .../Tests/TestCaseHyperdash.cs | 52 +++++++++---------- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 2 +- 5 files changed, 87 insertions(+), 27 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 8a19afcd39..d225cdca55 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -1,9 +1,13 @@ // 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.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Objects.Types; +using OpenTK; namespace osu.Game.Rulesets.Catch.Beatmaps { @@ -40,7 +44,46 @@ namespace osu.Game.Rulesets.Catch.Beatmaps private void convertHyperDash(List objects) { + // todo: add difficulty adjust. + const double catcher_width = CatcherArea.CATCHER_SIZE / CatchPlayfield.BASE_WIDTH; + const double catcher_width_half = catcher_width / 2; + int lastDirection = 0; + double lastExcess = catcher_width_half; + + int objCount = objects.Count; + + for (int i = 0; i < objCount - 1; i++) + { + CatchHitObject currentObject = objects[i]; + + // not needed? + if (currentObject is TinyDroplet) continue; + + CatchHitObject nextObject = objects[i + 1]; + while (nextObject is TinyDroplet) + { + if (++i == objCount - 1) break; + nextObject = objects[i + 1]; + } + + int thisDirection = nextObject.X > currentObject.X ? 1 : -1; + double timeToNext = nextObject.StartTime - ((currentObject as IHasEndTime)?.EndTime ?? currentObject.StartTime) - 4; + double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : catcher_width_half); + + if (timeToNext * CatcherArea.Catcher.BASE_SPEED < distanceToNext) + { + currentObject.HyperDash = true; + lastExcess = catcher_width_half; + } + else + { + //currentObject.DistanceToHyperDash = timeToNext - distanceToNext; + lastExcess = MathHelper.Clamp(timeToNext - distanceToNext, 0, catcher_width_half); + } + + lastDirection = thisDirection; + } } } } diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index cb4e6453ce..bae2edd055 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -27,6 +27,8 @@ namespace osu.Game.Rulesets.Catch.Objects public float Scale { get; set; } = 1; + public bool HyperDash { get; set; } + public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) { base.ApplyDefaults(controlPointInfo, difficulty); diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs index 4c28a9d021..9f46bbd3a4 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableFruit.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.MathUtils; using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces; using OpenTK; +using OpenTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawable { @@ -70,6 +71,20 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable } } }; + + if (HitObject.HyperDash) + { + Add(new Pulp + { + RelativePositionAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AccentColour = Color4.Red, + Blending = BlendingMode.Additive, + Alpha = 0.5f, + Scale = new Vector2(2) + }); + } } } } diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs index 822743debc..5b0e1fb93c 100644 --- a/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs +++ b/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs @@ -1,26 +1,26 @@ -using NUnit.Framework; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Catch.Objects; - -namespace osu.Game.Rulesets.Catch.Tests -{ - [TestFixture] - [Ignore("getting CI working")] - public class TestCaseHyperdash : Game.Tests.Visual.TestCasePlayer - { - public TestCaseHyperdash() - : base(typeof(CatchRuleset)) - { - } - - protected override Beatmap CreateBeatmap() - { - var beatmap = new Beatmap(); - - for (int i = 0; i < 512; i++) - beatmap.HitObjects.Add(new Fruit { X = 0.5f + (i % 2 == 0 ? -0.4f : 0.4f), StartTime = i * 100, NewCombo = i % 8 == 0 }); - - return beatmap; - } - } -} \ No newline at end of file +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Objects; + +namespace osu.Game.Rulesets.Catch.Tests +{ + [TestFixture] + [Ignore("getting CI working")] + public class TestCaseHyperdash : Game.Tests.Visual.TestCasePlayer + { + public TestCaseHyperdash() + : base(typeof(CatchRuleset)) + { + } + + protected override Beatmap CreateBeatmap() + { + var beatmap = new Beatmap(); + + for (int i = 0; i < 512; i++) + beatmap.HitObjects.Add(new Fruit { X = 0.5f + (i % 8 < 4 ? -0.4f : 0.4f), StartTime = i * 100, NewCombo = i % 8 == 0 }); + + return beatmap; + } + } +} diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 6fd0793500..3cfdcafc48 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Catch.UI { public class CatchPlayfield : ScrollingPlayfield { - public static readonly float BASE_WIDTH = 512; + public const float BASE_WIDTH = 512; protected override Container Content => content; private readonly Container content; From 51cae24a26933ce468b858ae91dd13382039b837 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 19:24:48 +0900 Subject: [PATCH 03/12] Add basic hyperdash movement Doesn't restrict direction yet. Also improves readability of fruit catch detection. --- .../Beatmaps/CatchBeatmapProcessor.cs | 2 +- .../Objects/CatchHitObject.cs | 10 +- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 2 +- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 104 ++++++++++++++++-- 4 files changed, 108 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index d225cdca55..e12d7f3fd3 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps if (timeToNext * CatcherArea.Catcher.BASE_SPEED < distanceToNext) { - currentObject.HyperDash = true; + currentObject.HyperDashTarget = nextObject; lastExcess = catcher_width_half; } else diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index bae2edd055..38757d4928 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -27,7 +27,15 @@ namespace osu.Game.Rulesets.Catch.Objects public float Scale { get; set; } = 1; - public bool HyperDash { get; set; } + /// + /// Whether this fruit can initiate a hyperdash. + /// + public bool HyperDash => HyperDashTarget != null; + + /// + /// The target fruit if we are to initiate a hyperdash. + /// + public CatchHitObject HyperDashTarget; public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) { diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 3cfdcafc48..76dbfa77c6 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Catch.UI }; } - public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.CanCatch(obj); + public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.AttemptCatch(obj); public override void Add(DrawableHitObject h) { diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 203db1bb8c..2785647cbd 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -14,6 +14,7 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Objects.Drawables; using OpenTK; +using OpenTK.Graphics; namespace osu.Game.Rulesets.Catch.UI { @@ -51,7 +52,7 @@ namespace osu.Game.Rulesets.Catch.UI catcher.Add(fruit); } - public bool CanCatch(CatchHitObject obj) => Math.Abs(catcher.Position.X - obj.X) < catcher.DrawSize.X * Math.Abs(catcher.Scale.X) / DrawSize.X / 2; + public bool AttemptCatch(CatchHitObject obj) => catcher.AttemptCatch(obj); public class Catcher : Container, IKeyBindingHandler { @@ -105,14 +106,35 @@ namespace osu.Game.Rulesets.Catch.UI dashing = value; - if (dashing) - Schedule(addAdditiveSprite); + Trail |= dashing; } } - private void addAdditiveSprite() + private bool trail; + + /// + /// Activate or deactive the trail. Will be automatically deactivated when conditions to keep the trail displayed are no longer met. + /// + protected bool Trail { - if (!dashing || AdditiveTarget == null) return; + get { return trail; } + set + { + if (value == trail) return; + + trail = value; + + if (Trail) + beginTrail(); + } + } + + private void beginTrail() + { + Trail &= dashing || HyperDashing; + Trail &= AdditiveTarget != null; + + if (!Trail) return; var additive = createCatcherSprite(); @@ -120,6 +142,7 @@ namespace osu.Game.Rulesets.Catch.UI additive.OriginPosition = additive.OriginPosition + new Vector2(DrawWidth / 2, 0); // also temporary to align sprite correctly. additive.Position = Position; additive.Scale = Scale; + additive.Colour = HyperDashing ? Color4.Red : Color4.White; additive.RelativePositionAxes = RelativePositionAxes; additive.Blending = BlendingMode.Additive; @@ -127,7 +150,7 @@ namespace osu.Game.Rulesets.Catch.UI additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint).Expire(); - Scheduler.AddDelayed(addAdditiveSprite, 50); + Scheduler.AddDelayed(beginTrail, HyperDashing ? 25 : 50); } private Sprite createCatcherSprite() => new Sprite @@ -138,6 +161,10 @@ namespace osu.Game.Rulesets.Catch.UI OriginPosition = new Vector2(-3, 10) // temporary until the sprite is aligned correctly. }; + /// + /// Add a caught fruit to the catcher's stack. + /// + /// The fruit that was caught. public void Add(DrawableHitObject fruit) { float distance = fruit.DrawSize.X / 2 * fruit.Scale.X; @@ -150,8 +177,35 @@ namespace osu.Game.Rulesets.Catch.UI caughtFruit.Add(fruit); - if (((CatchHitObject)fruit.HitObject).LastInCombo) + var catchObject = (CatchHitObject)fruit.HitObject; + + if (catchObject.LastInCombo) explode(); + + updateHyperDashState(catchObject, true); + } + + /// + /// Let the catcher attempt to catch a fruit. + /// + /// The fruit to catch. + /// Whether the catch is possible. + public bool AttemptCatch(CatchHitObject fruit) + { + const double relative_catcher_width = CATCHER_SIZE / 2; + + // this stuff wil disappear once we move fruit to non-relative coordinate space in the future. + var catchObjectPosition = fruit.X * CatchPlayfield.BASE_WIDTH; + var catcherPosition = Position.X * CatchPlayfield.BASE_WIDTH; + + var validCatch = + catchObjectPosition >= catcherPosition - relative_catcher_width / 2 && + catchObjectPosition <= catcherPosition + relative_catcher_width / 2; + + // if we are hypderdashing in teh next hit is not, let's change our state here (it's our only opportunity to handle missed fruit currently). + updateHyperDashState(fruit, false); + + return validCatch; } public bool OnPressed(CatchAction action) @@ -203,10 +257,46 @@ namespace osu.Game.Rulesets.Catch.UI double dashModifier = Dashing ? 1 : 0.5; + if (hyperDashModifier != 1) + dashModifier = hyperDashModifier; + Scale = new Vector2(Math.Abs(Scale.X) * Math.Sign(currentDirection), Scale.Y); X = (float)MathHelper.Clamp(X + Math.Sign(currentDirection) * Clock.ElapsedFrameTime * BASE_SPEED * dashModifier, 0, 1); } + /// + /// Whether we are hypderdashing or not. + /// + protected bool HyperDashing => hyperDashModifier != 1; + + private double hyperDashModifier = 1; + + /// + /// Update whether we are hyper or not. + /// + /// The fruit to use as a condition for deciding our new state. + /// Whether to allow entering hyperdash or not. If false, we will only exit if required, but never enter. + private void updateHyperDashState(CatchHitObject fruit, bool allowBegin) + { + const float transition_length = 180; + + if (!fruit.HyperDash) + { + hyperDashModifier = 1; + this.FadeColour(Color4.White, transition_length, Easing.OutQuint); + this.FadeTo(1, transition_length, Easing.OutQuint); + return; + } + + if (allowBegin) + { + hyperDashModifier = Math.Abs(fruit.HyperDashTarget.X - fruit.X) / Math.Abs(fruit.HyperDashTarget.StartTime - fruit.StartTime) / BASE_SPEED; + this.FadeColour(Color4.AliceBlue, transition_length, Easing.OutQuint); + this.FadeTo(0.5f, transition_length, Easing.OutQuint); + Trail = true; + } + } + private void explode() { var fruit = caughtFruit.ToArray(); From 07081f400cfcc68846d64f268880d5994b1763cb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 19:33:20 +0900 Subject: [PATCH 04/12] Make hyperdash testcase easier to win --- osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs index 5b0e1fb93c..a39c727a61 100644 --- a/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs +++ b/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Catch.Tests var beatmap = new Beatmap(); for (int i = 0; i < 512; i++) - beatmap.HitObjects.Add(new Fruit { X = 0.5f + (i % 8 < 4 ? -0.4f : 0.4f), StartTime = i * 100, NewCombo = i % 8 == 0 }); + beatmap.HitObjects.Add(new Fruit { X = i % 8 < 4 ? 0.02f : 0.98f, StartTime = i * 100, NewCombo = i % 8 == 0 }); return beatmap; } From 445bb70ef5b2d70a9c1552d72d4b5439209a3d4d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 19:58:00 +0900 Subject: [PATCH 05/12] Add hyperdash visual testing to TestCaseCatcherArea Also tidies up hyperdash state logic --- .../Tests/TestCaseCatcherArea.cs | 14 +++- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 84 +++++++++---------- 2 files changed, 54 insertions(+), 44 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs index 538f6930ed..daa3e12800 100644 --- a/osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs +++ b/osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs @@ -17,6 +17,7 @@ namespace osu.Game.Rulesets.Catch.Tests internal class TestCaseCatcherArea : OsuTestCase { private RulesetInfo catchRuleset; + private TestCatcherArea catcherArea; public override IReadOnlyList RequiredTypes => new[] { @@ -26,6 +27,7 @@ namespace osu.Game.Rulesets.Catch.Tests public TestCaseCatcherArea() { AddSliderStep("CircleSize", 0, 8, 5, createCatcher); + AddToggleStep("Hyperdash", t => catcherArea.ToggleHyperDash(t)); } private void createCatcher(float size) @@ -33,7 +35,7 @@ namespace osu.Game.Rulesets.Catch.Tests Child = new CatchInputManager(catchRuleset) { RelativeSizeAxes = Axes.Both, - Child = new CatcherArea(new BeatmapDifficulty { CircleSize = size }) + Child = catcherArea = new TestCatcherArea(new BeatmapDifficulty { CircleSize = size }) { Anchor = Anchor.CentreLeft, Origin = Anchor.BottomLeft @@ -46,5 +48,15 @@ namespace osu.Game.Rulesets.Catch.Tests { catchRuleset = rulesets.GetRuleset(2); } + + private class TestCatcherArea : CatcherArea + { + public TestCatcherArea(BeatmapDifficulty beatmapDifficulty) + : base(beatmapDifficulty) + { + } + + public void ToggleHyperDash(bool status) => MovableCatcher.HyperDashModifier = status ? 2 : 1; + } } } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 2785647cbd..52f06d4396 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -22,18 +22,18 @@ namespace osu.Game.Rulesets.Catch.UI { public const float CATCHER_SIZE = 172; - private readonly Catcher catcher; + protected readonly Catcher MovableCatcher; public Container ExplodingFruitTarget { - set { catcher.ExplodingFruitTarget = value; } + set { MovableCatcher.ExplodingFruitTarget = value; } } public CatcherArea(BeatmapDifficulty difficulty = null) { RelativeSizeAxes = Axes.X; Height = CATCHER_SIZE; - Child = catcher = new Catcher(difficulty) + Child = MovableCatcher = new Catcher(difficulty) { AdditiveTarget = this, }; @@ -42,17 +42,17 @@ namespace osu.Game.Rulesets.Catch.UI public void Add(DrawableHitObject fruit, Vector2 absolutePosition) { fruit.RelativePositionAxes = Axes.None; - fruit.Position = new Vector2(catcher.ToLocalSpace(absolutePosition).X - catcher.DrawSize.X / 2, 0); + fruit.Position = new Vector2(MovableCatcher.ToLocalSpace(absolutePosition).X - MovableCatcher.DrawSize.X / 2, 0); fruit.Anchor = Anchor.TopCentre; fruit.Origin = Anchor.BottomCentre; fruit.Scale *= 0.7f; fruit.LifetimeEnd = double.MaxValue; - catcher.Add(fruit); + MovableCatcher.Add(fruit); } - public bool AttemptCatch(CatchHitObject obj) => catcher.AttemptCatch(obj); + public bool AttemptCatch(CatchHitObject obj) => MovableCatcher.AttemptCatch(obj); public class Catcher : Container, IKeyBindingHandler { @@ -181,8 +181,6 @@ namespace osu.Game.Rulesets.Catch.UI if (catchObject.LastInCombo) explode(); - - updateHyperDashState(catchObject, true); } /// @@ -202,12 +200,45 @@ namespace osu.Game.Rulesets.Catch.UI catchObjectPosition >= catcherPosition - relative_catcher_width / 2 && catchObjectPosition <= catcherPosition + relative_catcher_width / 2; - // if we are hypderdashing in teh next hit is not, let's change our state here (it's our only opportunity to handle missed fruit currently). - updateHyperDashState(fruit, false); + if (validCatch && fruit.HyperDash) + HyperDashModifier = Math.Abs(fruit.HyperDashTarget.X - fruit.X) / Math.Abs(fruit.HyperDashTarget.StartTime - fruit.StartTime) / BASE_SPEED; + else + HyperDashModifier = 1; return validCatch; } + /// + /// Whether we are hypderdashing or not. + /// + public bool HyperDashing => hyperDashModifier != 1; + + private double hyperDashModifier = 1; + + public double HyperDashModifier + { + get { return hyperDashModifier; } + set + { + if (value == hyperDashModifier) return; + hyperDashModifier = value; + + const float transition_length = 180; + + if (HyperDashing) + { + this.FadeColour(Color4.Yellow, transition_length, Easing.OutQuint); + this.FadeTo(0.2f, transition_length, Easing.OutQuint); + Trail = true; + } + else + { + this.FadeColour(Color4.White, transition_length, Easing.OutQuint); + this.FadeTo(1, transition_length, Easing.OutQuint); + } + } + } + public bool OnPressed(CatchAction action) { switch (action) @@ -264,39 +295,6 @@ namespace osu.Game.Rulesets.Catch.UI X = (float)MathHelper.Clamp(X + Math.Sign(currentDirection) * Clock.ElapsedFrameTime * BASE_SPEED * dashModifier, 0, 1); } - /// - /// Whether we are hypderdashing or not. - /// - protected bool HyperDashing => hyperDashModifier != 1; - - private double hyperDashModifier = 1; - - /// - /// Update whether we are hyper or not. - /// - /// The fruit to use as a condition for deciding our new state. - /// Whether to allow entering hyperdash or not. If false, we will only exit if required, but never enter. - private void updateHyperDashState(CatchHitObject fruit, bool allowBegin) - { - const float transition_length = 180; - - if (!fruit.HyperDash) - { - hyperDashModifier = 1; - this.FadeColour(Color4.White, transition_length, Easing.OutQuint); - this.FadeTo(1, transition_length, Easing.OutQuint); - return; - } - - if (allowBegin) - { - hyperDashModifier = Math.Abs(fruit.HyperDashTarget.X - fruit.X) / Math.Abs(fruit.HyperDashTarget.StartTime - fruit.StartTime) / BASE_SPEED; - this.FadeColour(Color4.AliceBlue, transition_length, Easing.OutQuint); - this.FadeTo(0.5f, transition_length, Easing.OutQuint); - Trail = true; - } - } - private void explode() { var fruit = caughtFruit.ToArray(); From 25207c51b5ed04bf50c38f94527d4448d41506aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 20:07:28 +0900 Subject: [PATCH 06/12] Add directionality --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 52f06d4396..265f19c744 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -201,7 +201,10 @@ namespace osu.Game.Rulesets.Catch.UI catchObjectPosition <= catcherPosition + relative_catcher_width / 2; if (validCatch && fruit.HyperDash) + { HyperDashModifier = Math.Abs(fruit.HyperDashTarget.X - fruit.X) / Math.Abs(fruit.HyperDashTarget.StartTime - fruit.StartTime) / BASE_SPEED; + HyperDashDirection = fruit.HyperDashTarget.X - fruit.X; + } else HyperDashModifier = 1; @@ -215,6 +218,8 @@ namespace osu.Game.Rulesets.Catch.UI private double hyperDashModifier = 1; + public double HyperDashDirection; + public double HyperDashModifier { get { return hyperDashModifier; } @@ -233,6 +238,7 @@ namespace osu.Game.Rulesets.Catch.UI } else { + HyperDashDirection = 0; this.FadeColour(Color4.White, transition_length, Easing.OutQuint); this.FadeTo(1, transition_length, Easing.OutQuint); } @@ -286,13 +292,15 @@ namespace osu.Game.Rulesets.Catch.UI if (currentDirection == 0) return; + var direction = Math.Sign(currentDirection); + double dashModifier = Dashing ? 1 : 0.5; - if (hyperDashModifier != 1) + if (hyperDashModifier != 1 && (HyperDashDirection == 0 || direction == Math.Sign(HyperDashDirection))) dashModifier = hyperDashModifier; - Scale = new Vector2(Math.Abs(Scale.X) * Math.Sign(currentDirection), Scale.Y); - X = (float)MathHelper.Clamp(X + Math.Sign(currentDirection) * Clock.ElapsedFrameTime * BASE_SPEED * dashModifier, 0, 1); + Scale = new Vector2(Math.Abs(Scale.X) * direction, Scale.Y); + X = (float)MathHelper.Clamp(X + direction * Clock.ElapsedFrameTime * BASE_SPEED * dashModifier, 0, 1); } private void explode() From 273793f1852d5ee0792c39bcbb1414f5806c428e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 20:08:49 +0900 Subject: [PATCH 07/12] Add comments --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 265f19c744..984aaf98ee 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -218,8 +218,14 @@ namespace osu.Game.Rulesets.Catch.UI private double hyperDashModifier = 1; + /// + /// The direction in which hyperdash is allowed. 0 allows both directions. + /// public double HyperDashDirection; + /// + /// The speed modifier resultant from hyperdash. Will trigger hyperdash when not equal to 1. + /// public double HyperDashModifier { get { return hyperDashModifier; } From bf606522c1c3cd33ec90d70d1290fbfd5114d3aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 20:13:32 +0900 Subject: [PATCH 08/12] Make hyperdash testcase easier to win again --- osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs index a39c727a61..a0eb5f0054 100644 --- a/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs +++ b/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs @@ -18,7 +18,8 @@ namespace osu.Game.Rulesets.Catch.Tests var beatmap = new Beatmap(); for (int i = 0; i < 512; i++) - beatmap.HitObjects.Add(new Fruit { X = i % 8 < 4 ? 0.02f : 0.98f, StartTime = i * 100, NewCombo = i % 8 == 0 }); + if (i % 5 < 3) + beatmap.HitObjects.Add(new Fruit { X = i % 10 < 5 ? 0.02f : 0.98f, StartTime = i * 100, NewCombo = i % 8 == 0 }); return beatmap; } From e75d73ac1cb8b86ee16d679a59b80e8b98999a2d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 20:13:46 +0900 Subject: [PATCH 09/12] Change hyperdash colour again --- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 984aaf98ee..2bb0f3cd18 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -238,7 +238,7 @@ namespace osu.Game.Rulesets.Catch.UI if (HyperDashing) { - this.FadeColour(Color4.Yellow, transition_length, Easing.OutQuint); + this.FadeColour(Color4.OrangeRed, transition_length, Easing.OutQuint); this.FadeTo(0.2f, transition_length, Easing.OutQuint); Trail = true; } From 79e1bf3394f696df2c88742bb0943d3b01051125 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 20:14:42 +0900 Subject: [PATCH 10/12] Remove unused testcase --- .../Tests/TestCaseFruit.cs | 36 ------------------- .../osu.Game.Rulesets.Catch.csproj | 1 - 2 files changed, 37 deletions(-) delete mode 100644 osu.Game.Rulesets.Catch/Tests/TestCaseFruit.cs diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseFruit.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseFruit.cs deleted file mode 100644 index 0ced95edf9..0000000000 --- a/osu.Game.Rulesets.Catch/Tests/TestCaseFruit.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Catch.Objects.Drawable; -using osu.Game.Tests.Visual; -using OpenTK; - -namespace osu.Game.Rulesets.Catch.Tests -{ - public class TestCaseFruit : OsuTestCase - { - public TestCaseFruit() - { - Add(new GridContainer - { - RelativeSizeAxes = Axes.Both, - Content = new[] - { - new Drawable[] - { - new DrawableFruit(new Fruit()) { Position = new Vector2(0.5f) }, - new DrawableFruit(new Fruit()) { Position = new Vector2(0.5f) }, - }, - new Drawable[] - { - new DrawableFruit(new Fruit()) { Position = new Vector2(0.5f) }, - new DrawableFruit(new Fruit()) { Position = new Vector2(0.5f) }, - } - } - }); - } - } -} diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index 4e819fb58b..264f5c41be 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -67,7 +67,6 @@ - From 997cdfaee42ebb67e15bc0bcbe1162c89b0bbe6c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 20:31:54 +0900 Subject: [PATCH 11/12] Add missing licence header --- osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs index a0eb5f0054..ce3f79bae2 100644 --- a/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs +++ b/osu.Game.Rulesets.Catch/Tests/TestCaseHyperdash.cs @@ -1,4 +1,7 @@ -using NUnit.Framework; +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using NUnit.Framework; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; From 8c3ae9430b53273b95f258279560b6396d9a7b41 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 20:39:58 +0900 Subject: [PATCH 12/12] Add difficulty scaling considerations to hyperdash initialisation --- .../Beatmaps/CatchBeatmapProcessor.cs | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index e12d7f3fd3..9901dbde18 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.UI; @@ -23,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps CatchHitObject lastObj = null; - convertHyperDash(beatmap.HitObjects); + initialiseHyperDash(beatmap.HitObjects); foreach (var obj in beatmap.HitObjects) { @@ -42,14 +43,13 @@ namespace osu.Game.Rulesets.Catch.Beatmaps } } - private void convertHyperDash(List objects) + private void initialiseHyperDash(List objects) { // todo: add difficulty adjust. - const double catcher_width = CatcherArea.CATCHER_SIZE / CatchPlayfield.BASE_WIDTH; - const double catcher_width_half = catcher_width / 2; + double halfCatcherWidth = CatcherArea.CATCHER_SIZE * (objects.FirstOrDefault()?.Scale ?? 1) / CatchPlayfield.BASE_WIDTH / 2; int lastDirection = 0; - double lastExcess = catcher_width_half; + double lastExcess = halfCatcherWidth; int objCount = objects.Count; @@ -58,28 +58,29 @@ namespace osu.Game.Rulesets.Catch.Beatmaps CatchHitObject currentObject = objects[i]; // not needed? - if (currentObject is TinyDroplet) continue; + // if (currentObject is TinyDroplet) continue; CatchHitObject nextObject = objects[i + 1]; - while (nextObject is TinyDroplet) - { - if (++i == objCount - 1) break; - nextObject = objects[i + 1]; - } + + // while (nextObject is TinyDroplet) + // { + // if (++i == objCount - 1) break; + // nextObject = objects[i + 1]; + // } int thisDirection = nextObject.X > currentObject.X ? 1 : -1; double timeToNext = nextObject.StartTime - ((currentObject as IHasEndTime)?.EndTime ?? currentObject.StartTime) - 4; - double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : catcher_width_half); + double distanceToNext = Math.Abs(nextObject.X - currentObject.X) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth); if (timeToNext * CatcherArea.Catcher.BASE_SPEED < distanceToNext) { currentObject.HyperDashTarget = nextObject; - lastExcess = catcher_width_half; + lastExcess = halfCatcherWidth; } else { //currentObject.DistanceToHyperDash = timeToNext - distanceToNext; - lastExcess = MathHelper.Clamp(timeToNext - distanceToNext, 0, catcher_width_half); + lastExcess = MathHelper.Clamp(timeToNext - distanceToNext, 0, halfCatcherWidth); } lastDirection = thisDirection;