From 01508e68134c45a6380d6a892694957afbc11b73 Mon Sep 17 00:00:00 2001 From: Unknown Date: Fri, 28 Jun 2019 10:34:04 +0200 Subject: [PATCH] implement HD for CtB --- .../TestSceneDrawableHitObjects.cs | 160 ++++++++++++++++++ .../TestSceneDrawableHitObjectsHidden.cs | 20 +++ .../Mods/CatchModHidden.cs | 35 ++++ .../Objects/CatchHitObject.cs | 4 + .../Drawable/DrawableCatchHitObject.cs | 4 +- 5 files changed, 220 insertions(+), 3 deletions(-) create mode 100644 osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs create mode 100644 osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjectsHidden.cs diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs new file mode 100644 index 0000000000..7a9b61c60c --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs @@ -0,0 +1,160 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.Objects.Drawable; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Tests.Visual; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Tests +{ + public class TestSceneDrawableHitObjects : OsuTestScene + { + public override IReadOnlyList RequiredTypes => new[] + { + typeof(CatcherArea.Catcher), + typeof(DrawableCatchRuleset), + typeof(DrawableFruit), + typeof(DrawableJuiceStream), + typeof(DrawableBanana) + }; + + private DrawableCatchRuleset drawableRuleset; + private double playfieldTime => drawableRuleset.Playfield.Time.Current; + + [BackgroundDependencyLoader] + private void load() + { + var controlPointInfo = new ControlPointInfo(); + controlPointInfo.TimingPoints.Add(new TimingControlPoint()); + + WorkingBeatmap beatmap = CreateWorkingBeatmap(new Beatmap + { + HitObjects = new List { new Fruit() }, + BeatmapInfo = new BeatmapInfo + { + BaseDifficulty = new BeatmapDifficulty(), + Metadata = new BeatmapMetadata + { + Artist = @"Unknown", + Title = @"You're breathtaking", + AuthorString = @"Everyone", + }, + Ruleset = new CatchRuleset().RulesetInfo + }, + ControlPointInfo = controlPointInfo + }); + + Add(new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Children = new[] + { + drawableRuleset = new DrawableCatchRuleset(new CatchRuleset(), beatmap, Array.Empty()) + } + }); + + AddStep("miss fruits", () => spawnFruits()); + AddStep("hit fruits", () => spawnFruits(true)); + AddStep("miss juicestream", () => spawnJuiceStream()); + AddStep("hit juicestream", () => spawnJuiceStream(true)); + AddStep("miss bananas", () => spawnBananas()); + AddStep("hit bananas", () => spawnBananas(true)); + } + + private void spawnFruits(bool hit = false) + { + for (int i = 1; i <= 4; i++) + { + var fruit = new Fruit + { + X = getXCoords(hit), + LastInCombo = i % 4 == 0, + StartTime = playfieldTime + 800 + (200 * i) + }; + + fruit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + + addToPlayfield(new DrawableFruit(fruit)); + } + } + + private void spawnJuiceStream(bool hit = false) + { + var xCoords = getXCoords(hit); + + var juice = new JuiceStream + { + X = xCoords, + StartTime = playfieldTime + 1000, + Path = new SliderPath(PathType.Linear, new[] + { + Vector2.Zero, + new Vector2(0, 200) + }) + }; + + juice.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + + if (juice.NestedHitObjects.Last() is CatchHitObject tail) + tail.LastInCombo = true; // usually the (Catch)BeatmapProcessor would do this for us when necessary + + addToPlayfield(new DrawableJuiceStream(juice, drawableRuleset.CreateDrawableRepresentation)); + } + + private void spawnBananas(bool hit = false) + { + for (int i = 1; i <= 4; i++) + { + var banana = new Banana + { + X = getXCoords(hit), + LastInCombo = i % 4 == 0, + StartTime = playfieldTime + 800 + (200 * i) + }; + + banana.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + + addToPlayfield(new DrawableBanana(banana)); + } + } + + private float getXCoords(bool hit) + { + const float x_offset = 0.2f; + float xCoords = drawableRuleset.Playfield.Width / 2; + + if (drawableRuleset.Playfield is CatchPlayfield catchPlayfield) + catchPlayfield.CatcherArea.MovableCatcher.X = xCoords - x_offset; + + if (hit) + xCoords -= x_offset; + else + xCoords += x_offset; + + return xCoords; + } + + private void addToPlayfield(DrawableCatchHitObject drawable) + { + foreach (var mod in Mods.Value.OfType()) + mod.ApplyToDrawableHitObjects(new[] { drawable }); + + drawableRuleset.Playfield.Add(drawable); + } + } +} diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjectsHidden.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjectsHidden.cs new file mode 100644 index 0000000000..f6d26addaa --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjectsHidden.cs @@ -0,0 +1,20 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Game.Rulesets.Catch.Mods; + +namespace osu.Game.Rulesets.Catch.Tests +{ + public class TestSceneDrawableHitObjectsHidden : TestSceneDrawableHitObjects + { + public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(CatchModHidden) }).ToList(); + + public TestSceneDrawableHitObjectsHidden() + { + Mods.Value = new[] { new CatchModHidden() }; + } + } +} diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs index 9990b01427..606a935229 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs @@ -1,7 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; +using osu.Framework.Graphics; +using osu.Game.Rulesets.Catch.Objects.Drawable; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects.Drawables; namespace osu.Game.Rulesets.Catch.Mods { @@ -9,5 +13,36 @@ namespace osu.Game.Rulesets.Catch.Mods { public override string Description => @"Play with fading fruits."; public override double ScoreMultiplier => 1.06; + + private const double fade_out_offset_multiplier = 0.6; + private const double fade_out_duration_multiplier = 0.44; + + protected override void ApplyHiddenState(DrawableHitObject drawable, ArmedState state) + { + if (!(drawable is DrawableCatchHitObject catchDrawable)) + return; + + if (catchDrawable.NestedHitObjects.Any()) + { + foreach (var nestedDrawable in catchDrawable.NestedHitObjects) + { + if (nestedDrawable is DrawableCatchHitObject nestedCatchDrawable) + fadeOutHitObject(nestedCatchDrawable); + } + } + else + fadeOutHitObject(catchDrawable); + } + + private void fadeOutHitObject(DrawableCatchHitObject drawable) + { + var hitObject = drawable.HitObject; + + var offset = hitObject.TimePreempt * fade_out_offset_multiplier; + var duration = offset - hitObject.TimePreempt * fade_out_duration_multiplier; + + using (drawable.BeginAbsoluteSequence(hitObject.StartTime - offset, true)) + drawable.FadeOut(duration); + } } } diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index 2153b8dc85..be76edc01b 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -14,6 +14,8 @@ namespace osu.Game.Rulesets.Catch.Objects public float X { get; set; } + public double TimePreempt = 1000; + public int IndexInBeatmap { get; set; } public virtual FruitVisualRepresentation VisualRepresentation => (FruitVisualRepresentation)(ComboIndex % 4); @@ -54,6 +56,8 @@ namespace osu.Game.Rulesets.Catch.Objects { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); + TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450); + Scale = 1.0f - 0.7f * (difficulty.CircleSize - 5) / 5; } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs index 2f8ccec48b..5785d9a9ca 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs @@ -68,11 +68,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable AccentColour = skin.GetValue(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; } - private const float preempt = 1000; - protected override void UpdateState(ArmedState state) { - using (BeginAbsoluteSequence(HitObject.StartTime - preempt)) + using (BeginAbsoluteSequence(HitObject.StartTime - HitObject.TimePreempt)) this.FadeIn(200); var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;