From 38fe95d94aa1d27f12a12c5f46de7cfb72ccfcfe Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 16 Nov 2017 17:35:10 +0900 Subject: [PATCH 001/235] Add basic display for pp in TestCasePerformancePoints --- .../Tests/TestCasePerformancePoints.cs | 13 ++ .../osu.Game.Rulesets.Catch.csproj | 1 + .../Tests/TestCasePerformancePoints.cs | 13 ++ .../osu.Game.Rulesets.Mania.csproj | 1 + .../Tests/TestCasePerformancePoints.cs | 13 ++ .../osu.Game.Rulesets.Osu.csproj | 1 + .../Tests/TestCasePerformancePoints.cs | 13 ++ .../osu.Game.Rulesets.Taiko.csproj | 1 + .../Tests/Visual/TestCasePerformancePoints.cs | 214 ++++++++++++++++++ osu.Game/osu.Game.csproj | 1 + 10 files changed, 271 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Tests/TestCasePerformancePoints.cs create mode 100644 osu.Game.Rulesets.Mania/Tests/TestCasePerformancePoints.cs create mode 100644 osu.Game.Rulesets.Osu/Tests/TestCasePerformancePoints.cs create mode 100644 osu.Game.Rulesets.Taiko/Tests/TestCasePerformancePoints.cs create mode 100644 osu.Game/Tests/Visual/TestCasePerformancePoints.cs diff --git a/osu.Game.Rulesets.Catch/Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Catch/Tests/TestCasePerformancePoints.cs new file mode 100644 index 0000000000..071367b305 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Tests/TestCasePerformancePoints.cs @@ -0,0 +1,13 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Catch.Tests +{ + public class TestCasePerformancePoints : osu.Game.Tests.Visual.TestCasePerformancePoints + { + public TestCasePerformancePoints() + : base(new CatchRuleset(new RulesetInfo())) + { + } + } +} diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index a666984b95..6822643fb5 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -65,6 +65,7 @@ + diff --git a/osu.Game.Rulesets.Mania/Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Mania/Tests/TestCasePerformancePoints.cs new file mode 100644 index 0000000000..420465c045 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Tests/TestCasePerformancePoints.cs @@ -0,0 +1,13 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mania.Tests +{ + public class TestCasePerformancePoints : osu.Game.Tests.Visual.TestCasePerformancePoints + { + public TestCasePerformancePoints() + : base(new ManiaRuleset(new RulesetInfo())) + { + } + } +} diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 6f45a64d92..7c97ff05a7 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -82,6 +82,7 @@ + diff --git a/osu.Game.Rulesets.Osu/Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Osu/Tests/TestCasePerformancePoints.cs new file mode 100644 index 0000000000..b430803907 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Tests/TestCasePerformancePoints.cs @@ -0,0 +1,13 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Osu.Tests +{ + public class TestCasePerformancePoints : osu.Game.Tests.Visual.TestCasePerformancePoints + { + public TestCasePerformancePoints() + : base(new OsuRuleset(new RulesetInfo())) + { + } + } +} diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 3c90749777..97ddf7d46f 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -85,6 +85,7 @@ + diff --git a/osu.Game.Rulesets.Taiko/Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Taiko/Tests/TestCasePerformancePoints.cs new file mode 100644 index 0000000000..a60c79fc6a --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Tests/TestCasePerformancePoints.cs @@ -0,0 +1,13 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Taiko.Tests +{ + public class TestCasePerformancePoints : osu.Game.Tests.Visual.TestCasePerformancePoints + { + public TestCasePerformancePoints() + : base(new TaikoRuleset(new RulesetInfo())) + { + } + } +} diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index bf627d205a..0b4e6e43f2 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -83,6 +83,7 @@ + diff --git a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs new file mode 100644 index 0000000000..d3a6071fb8 --- /dev/null +++ b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs @@ -0,0 +1,214 @@ +// 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 System.Linq; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input; +using osu.Game.Beatmaps; +using osu.Game.Graphics.Sprites; +using osu.Game.Overlays; +using osu.Game.Overlays.Music; +using osu.Game.Rulesets; + +namespace osu.Game.Tests.Visual +{ + public abstract class TestCasePerformancePoints : OsuTestCase + { + public TestCasePerformancePoints(Ruleset ruleset) + { + Children = new Drawable[] + { + new Container + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + RelativeSizeAxes = Axes.Y, + Width = 300, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.5f, + }, + new ScrollContainer(Direction.Vertical) + { + RelativeSizeAxes = Axes.Both, + Child = new BeatmapList(ruleset) + } + } + }, + new PpDisplay(ruleset) + }; + } + + private class BeatmapList : CompositeDrawable + { + private readonly Container beatmapDisplays; + private readonly Ruleset ruleset; + + public BeatmapList(Ruleset ruleset) + { + this.ruleset = ruleset; + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + InternalChild = beatmapDisplays = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 4) + }; + } + + [BackgroundDependencyLoader] + private void load(OsuGameBase osuGame, BeatmapManager beatmaps) + { + var sets = beatmaps.GetAllUsableBeatmapSets(); + var allBeatmaps = sets.SelectMany(s => s.Beatmaps).Where(b => ruleset.LegacyID < 0 || b.RulesetID == ruleset.LegacyID); + + allBeatmaps.ForEach(b => beatmapDisplays.Add(new BeatmapDisplay(b))); + } + + private class BeatmapDisplay : CompositeDrawable + { + private readonly OsuSpriteText text; + private readonly BeatmapInfo beatmap; + + private BeatmapManager beatmaps; + private OsuGameBase osuGame; + + private bool isSelected; + + public BeatmapDisplay(BeatmapInfo beatmap) + { + this.beatmap = beatmap; + + AutoSizeAxes = Axes.Both; + InternalChild = text = new OsuSpriteText(); + } + + protected override bool OnClick(InputState state) + { + if (osuGame.Beatmap.Value.BeatmapInfo.ID == beatmap.ID) + return false; + + osuGame.Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap); + isSelected = true; + return true; + } + + protected override bool OnHover(InputState state) + { + if (isSelected) + return false; + this.FadeColour(Color4.Yellow, 100); + return true; + } + + protected override void OnHoverLost(InputState state) + { + if (isSelected) + return; + this.FadeColour(Color4.White, 100); + } + + [BackgroundDependencyLoader] + private void load(OsuGameBase osuGame, BeatmapManager beatmaps) + { + this.osuGame = osuGame; + this.beatmaps = beatmaps; + + var working = beatmaps.GetWorkingBeatmap(beatmap); + text.Text = $"{beatmap.Metadata.Artist} - {beatmap.Metadata.Title} ({beatmap.Metadata.AuthorString}) [{beatmap.Version}]"; + + osuGame.Beatmap.ValueChanged += beatmapChanged; + } + + private void beatmapChanged(WorkingBeatmap newBeatmap) + { + if (isSelected) + this.FadeColour(Color4.White, 100); + isSelected = false; + } + } + } + + private class PpDisplay : CompositeDrawable + { + private readonly Container strainsContainer; + private readonly OsuSpriteText totalPp; + + private readonly Ruleset ruleset; + + public PpDisplay(Ruleset ruleset) + { + this.ruleset = ruleset; + + RelativeSizeAxes = Axes.Y; + Width = 400; + + InternalChildren = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.2f + }, + totalPp = new OsuSpriteText { TextSize = 18 }, + new Container + { + AutoSizeAxes = Axes.Both, + Y = 26, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.2f, + }, + strainsContainer = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5) + } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuGameBase osuGame) + { + osuGame.Beatmap.ValueChanged += beatmapChanged; + } + + private void beatmapChanged(WorkingBeatmap beatmap) + { + var diffCalculator = ruleset.CreateDifficultyCalculator(beatmap.Beatmap); + + var strains = new Dictionary(); + double pp = diffCalculator.Calculate(strains); + + totalPp.Text = $"Total PP: {pp.ToString("n2")}"; + + strainsContainer.Clear(); + foreach (var kvp in strains) + strainsContainer.Add(new OsuSpriteText { Text = $"{kvp.Key} : {kvp.Value}" }); + } + } + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 7b479bdba2..8f0ce14305 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -775,6 +775,7 @@ + From 1e023f04195009f0551d19d43f0ccdb68a3350dc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Nov 2017 12:35:23 +0900 Subject: [PATCH 002/235] Implement PerformanceCalculator testcase --- osu.Game.Rulesets.Osu/OsuRuleset.cs | 3 + .../Scoring/OsuPerformanceCalculator.cs | 189 ++++++++++++++++++ .../osu.Game.Rulesets.Osu.csproj | 1 + osu.Game/Rulesets/Ruleset.cs | 2 + .../Rulesets/Scoring/PerformanceCalculator.cs | 35 ++++ .../Tests/Visual/TestCasePerformancePoints.cs | 172 +++++++++------- osu.Game/osu.Game.csproj | 1 + 7 files changed, 336 insertions(+), 67 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs create mode 100644 osu.Game/Rulesets/Scoring/PerformanceCalculator.cs diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 9c11474f97..4a0a5ce0c9 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -14,6 +14,7 @@ using System.Linq; using osu.Framework.Graphics; using osu.Game.Overlays.Settings; using osu.Framework.Input.Bindings; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu { @@ -114,6 +115,8 @@ namespace osu.Game.Rulesets.Osu public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new OsuDifficultyCalculator(beatmap, mods); + public override PerformanceCalculator CreatePerformanceCalculator(Beatmap beatmap, Score score) => new OsuPerformanceCalculator(this, beatmap, score); + public override string Description => "osu!"; public override SettingsSubsection CreateSettings() => new OsuSettings(); diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs new file mode 100644 index 0000000000..32469b1bf7 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs @@ -0,0 +1,189 @@ +// 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 System.Linq; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Beatmaps; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Osu.Scoring +{ + public class OsuPerformanceCalculator : PerformanceCalculator + { + private readonly int countHitCircles; + private readonly int beatmapMaxCombo; + + private Mod[] mods; + private double accuracy; + private int scoreMaxCombo; + private int count300; + private int count100; + private int count50; + private int countMiss; + + public OsuPerformanceCalculator(Ruleset ruleset, Beatmap beatmap, Score score) + : base(ruleset, beatmap, score) + { + countHitCircles = Beatmap.HitObjects.Count(h => h is HitCircle); + + beatmapMaxCombo = Beatmap.HitObjects.Count(); + beatmapMaxCombo += Beatmap.HitObjects.OfType().Sum(s => s.RepeatCount + s.Ticks.Count()); + } + + public override double Calculate(Dictionary categoryRatings = null) + { + mods = Score.Mods; + accuracy = Score.Accuracy; + scoreMaxCombo = Score.MaxCombo; + count300 = Convert.ToInt32(Score.Statistics["300"]); + count100 = Convert.ToInt32(Score.Statistics["100"]); + count50 = Convert.ToInt32(Score.Statistics["50"]); + countMiss = Convert.ToInt32(Score.Statistics["x"]); + + // Don't count scores made with supposedly unranked mods + if (mods.Any(m => m is OsuModRelax || m is OsuModAutopilot || m is OsuModAutoplay)) + return 0; + + // Custom multipliers for NoFail and SpunOut. + double multiplier = 1.12f; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things + + if (mods.Any(m => m is OsuModNoFail)) + multiplier *= 0.90f; + + if (mods.Any(m => m is OsuModSpunOut)) + multiplier *= 0.95f; + + double aimValue = computeAimValue(); + double speedValue = computeSpeedValue(); + double accuracyValue = computeAccuracyValue(); + double totalValue = + Math.Pow( + Math.Pow(aimValue, 1.1f) + + Math.Pow(speedValue, 1.1f) + + Math.Pow(accuracyValue, 1.1f), 1.0f / 1.1f + ) * multiplier; + + if (categoryRatings != null) + { + categoryRatings.Add("Aim", aimValue.ToString("0.00")); + categoryRatings.Add("Speed", speedValue.ToString("0.00")); + categoryRatings.Add("Accuracy", accuracyValue.ToString("0.00")); + } + + return totalValue; + } + + private double computeAimValue() + { + double aimValue = Math.Pow(5.0f * Math.Max(1.0f, double.Parse(Attributes["Aim"]) / 0.0675f) - 4.0f, 3.0f) / 100000.0f; + + // Longer maps are worth more + double LengthBonus = 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) + + (totalHits > 2000 ? Math.Log10(totalHits / 2000.0f) * 0.5f : 0.0f); + + aimValue *= LengthBonus; + + // Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available + aimValue *= Math.Pow(0.97f, countMiss); + + // Combo scaling + if (beatmapMaxCombo > 0) + aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f); + + double approachRate = Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate; + double approachRateFactor = 1.0f; + if (approachRate > 10.33f) + approachRateFactor += 0.45f * (approachRate - 10.33f); + else if (approachRate < 8.0f) + { + // HD is worth more with lower ar! + if (mods.Any(h => h is OsuModHidden)) + approachRateFactor += 0.02f * (8.0f - approachRate); + else + approachRateFactor += 0.01f * (8.0f - approachRate); + } + + aimValue *= approachRateFactor; + + if (mods.Any(h => h is OsuModHidden)) + aimValue *= 1.18f; + + if (mods.Any(h => h is OsuModFlashlight)) + { + // Apply length bonus again if flashlight is on simply because it becomes a lot harder on longer maps. + aimValue *= 1.45f * LengthBonus; + } + + // Scale the aim value with accuracy _slightly_ + aimValue *= 0.5f + accuracy / 2.0f; + // It is important to also consider accuracy difficulty when doing that + aimValue *= 0.98f + (Math.Pow(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 2) / 2500); + + return aimValue; + } + + private double computeSpeedValue() + { + double speedValue = Math.Pow(5.0f * Math.Max(1.0f, double.Parse(Attributes["Speed"]) / 0.0675f) - 4.0f, 3.0f) / 100000.0f; + + // Longer maps are worth more + speedValue *= 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) + + (totalHits > 2000 ? Math.Log10(totalHits / 2000.0f) * 0.5f : 0.0f); + + // Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available + speedValue *= Math.Pow(0.97f, countMiss); + + // Combo scaling + if (beatmapMaxCombo > 0) + speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f); + + // Scale the speed value with accuracy _slightly_ + speedValue *= 0.5f + accuracy / 2.0f; + // It is important to also consider accuracy difficulty when doing that + speedValue *= 0.98f + (Math.Pow(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 2) / 2500); + + return speedValue; + } + + private double computeAccuracyValue() + { + // This percentage only considers HitCircles of any value - in this part of the calculation we focus on hitting the timing hit window + double betterAccuracyPercentage = 0; + int amountHitObjectsWithAccuracy = countHitCircles; + + if (amountHitObjectsWithAccuracy > 0) + betterAccuracyPercentage = ((count300 - (totalHits - amountHitObjectsWithAccuracy)) * 6 + count100 * 2 + count50) / (amountHitObjectsWithAccuracy * 6); + else + betterAccuracyPercentage = 0; + + // It is possible to reach a negative accuracy with this formula. Cap it at zero - zero points + if (betterAccuracyPercentage < 0) + betterAccuracyPercentage = 0; + + // Lots of arbitrary values from testing. + // Considering to use derivation from perfect accuracy in a probabilistic manner - assume normal distribution + double accuracyValue = Math.Pow(1.52163f, Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty) * Math.Pow(betterAccuracyPercentage, 24) * 2.83f; + + // Bonus for many hitcircles - it's harder to keep good accuracy up for longer + accuracyValue *= Math.Min(1.15f, Math.Pow(amountHitObjectsWithAccuracy / 1000.0f, 0.3f)); + + if (mods.Any(m => m is OsuModHidden)) + accuracyValue *= 1.02f; + if (mods.Any(m => m is OsuModFlashlight)) + accuracyValue *= 1.02f; + + return accuracyValue; + } + + private double totalHits => count300 + count100 + count50 + countMiss; + private double totalSuccessfulHits => count300 + count100 + count50; + + protected override BeatmapConverter CreateBeatmapConverter() => new OsuBeatmapConverter(); + } +} diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 97ddf7d46f..2be057de40 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -89,6 +89,7 @@ + diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index ed2fdf4157..226a897126 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -49,6 +49,8 @@ namespace osu.Game.Rulesets public abstract DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null); + public virtual PerformanceCalculator CreatePerformanceCalculator(Beatmap beatmap, Score score) => null; + public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_question_circle }; public abstract string Description { get; } diff --git a/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs b/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs new file mode 100644 index 0000000000..49b7ac54c6 --- /dev/null +++ b/osu.Game/Rulesets/Scoring/PerformanceCalculator.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 System.Collections.Generic; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Rulesets.Scoring +{ + public abstract class PerformanceCalculator + { + public abstract double Calculate(Dictionary categoryDifficulty = null); + } + + public abstract class PerformanceCalculator : PerformanceCalculator + where TObject : HitObject + { + private readonly Dictionary attributes = new Dictionary(); + protected IDictionary Attributes => attributes; + + protected readonly Beatmap Beatmap; + protected readonly Score Score; + + public PerformanceCalculator(Ruleset ruleset, Beatmap beatmap, Score score) + { + Beatmap = CreateBeatmapConverter().Convert(beatmap); + Score = score; + + var diffCalc = ruleset.CreateDifficultyCalculator(beatmap); + diffCalc.Calculate(attributes); + } + + protected abstract BeatmapConverter CreateBeatmapConverter(); + } +} diff --git a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs index d3a6071fb8..7e8b9de954 100644 --- a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs +++ b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs @@ -14,9 +14,12 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; using osu.Game.Overlays; using osu.Game.Overlays.Music; using osu.Game.Rulesets; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Tests.Visual { @@ -24,30 +27,51 @@ namespace osu.Game.Tests.Visual { public TestCasePerformancePoints(Ruleset ruleset) { - Children = new Drawable[] + Child = new FillFlowContainer { - new Container + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new Drawable[] { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - RelativeSizeAxes = Axes.Y, - Width = 300, - Children = new Drawable[] + new Container { - new Box + RelativeSizeAxes = Axes.Both, + Width = 0.25f, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.5f, - }, - new ScrollContainer(Direction.Vertical) - { - RelativeSizeAxes = Axes.Both, - Child = new BeatmapList(ruleset) + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.5f, + }, + new ScrollContainer(Direction.Vertical) + { + RelativeSizeAxes = Axes.Both, + Child = new BeatmapList(ruleset) + } } - } - }, - new PpDisplay(ruleset) + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Width = 0.75f, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.5f, + }, + new ScrollContainer(Direction.Vertical) + { + RelativeSizeAxes = Axes.Both, + Child = new ScoreList { RelativeSizeAxes = Axes.Both } + } + } + }, + } }; } @@ -144,70 +168,84 @@ namespace osu.Game.Tests.Visual } } - private class PpDisplay : CompositeDrawable + private class ScoreList : CompositeDrawable { - private readonly Container strainsContainer; - private readonly OsuSpriteText totalPp; + private readonly FillFlowContainer scores; + private APIAccess api; - private readonly Ruleset ruleset; - - public PpDisplay(Ruleset ruleset) + public ScoreList() { - this.ruleset = ruleset; - - RelativeSizeAxes = Axes.Y; - Width = 400; - - InternalChildren = new Drawable[] + InternalChild = scores = new FillFlowContainer { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.2f - }, - totalPp = new OsuSpriteText { TextSize = 18 }, - new Container - { - AutoSizeAxes = Axes.Both, - Y = 26, - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.2f, - }, - strainsContainer = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 5) - } - } - } + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 4) }; } [BackgroundDependencyLoader] - private void load(OsuGameBase osuGame) + private void load(OsuGameBase osuGame, APIAccess api) { + this.api = api; osuGame.Beatmap.ValueChanged += beatmapChanged; } - private void beatmapChanged(WorkingBeatmap beatmap) + private GetScoresRequest lastRequest; + private void beatmapChanged(WorkingBeatmap newBeatmap) { - var diffCalculator = ruleset.CreateDifficultyCalculator(beatmap.Beatmap); + lastRequest?.Cancel(); + scores.Clear(); - var strains = new Dictionary(); - double pp = diffCalculator.Calculate(strains); + lastRequest = new GetScoresRequest(newBeatmap.BeatmapInfo); + lastRequest.Success += res => res.Scores.ForEach(s => scores.Add(new ScoreDisplay(s, newBeatmap.Beatmap))); + api.Queue(lastRequest); + } - totalPp.Text = $"Total PP: {pp.ToString("n2")}"; + private class ScoreDisplay : CompositeDrawable + { + private readonly OsuSpriteText playerName; + private readonly GridContainer attributeGrid; - strainsContainer.Clear(); - foreach (var kvp in strains) - strainsContainer.Add(new OsuSpriteText { Text = $"{kvp.Key} : {kvp.Value}" }); + private readonly Score score; + private readonly Beatmap beatmap; + + public ScoreDisplay(Score score, Beatmap beatmap) + { + this.score = score; + this.beatmap = beatmap; + + RelativeSizeAxes = Axes.X; + Height = 16; + InternalChild = new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] { playerName = new OsuSpriteText() }, + new Drawable[] { attributeGrid = new GridContainer { RelativeSizeAxes = Axes.Both } } + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.Relative, 0.75f), + new Dimension(GridSizeMode.Relative, 0.25f) + } + }; + } + + [BackgroundDependencyLoader] + private void load() + { + var ruleset = beatmap.BeatmapInfo.Ruleset.CreateInstance(); + var calculator = ruleset.CreatePerformanceCalculator(beatmap, score); + if (calculator == null) + return; + + var attributes = new Dictionary(); + double performance = calculator.Calculate(attributes); + + playerName.Text = $"{score.PP} | {performance.ToString("0.00")} | {score.PP / performance}"; + // var attributeRow = + } } } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8f0ce14305..a7f79b1ecd 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -620,6 +620,7 @@ + From 1ed2ce324fa5766d6c6c5b766902b7e09ebd5201 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Nov 2017 14:23:15 +0900 Subject: [PATCH 003/235] Further improvements to TestCasePerformancePoints --- .../Tests/Visual/TestCasePerformancePoints.cs | 255 +++++++++++++----- 1 file changed, 193 insertions(+), 62 deletions(-) diff --git a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs index 7e8b9de954..3a1fc20060 100644 --- a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs +++ b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs @@ -6,19 +6,23 @@ using System.Linq; using OpenTK; using OpenTK.Graphics; using osu.Framework.Allocation; +using osu.Framework.Caching; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Overlays; using osu.Game.Overlays.Music; using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; namespace osu.Game.Tests.Visual @@ -27,50 +31,77 @@ namespace osu.Game.Tests.Visual { public TestCasePerformancePoints(Ruleset ruleset) { - Child = new FillFlowContainer + Child = new GridContainer { RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Children = new Drawable[] + Content = new[] { - new Container + new Drawable[] { - RelativeSizeAxes = Axes.Both, - Width = 0.25f, - Children = new Drawable[] + new Container { - new Box + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.5f, - }, - new ScrollContainer(Direction.Vertical) - { - RelativeSizeAxes = Axes.Both, - Child = new BeatmapList(ruleset) + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.5f, + }, + new ScrollContainer(Direction.Vertical) + { + RelativeSizeAxes = Axes.Both, + Child = new BeatmapList(ruleset) + } } - } - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Width = 0.75f, - Children = new Drawable[] + }, + null, + new Container { - new Box + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black, - Alpha = 0.5f, - }, - new ScrollContainer(Direction.Vertical) - { - RelativeSizeAxes = Axes.Both, - Child = new ScoreList { RelativeSizeAxes = Axes.Both } + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.5f, + }, + new ScrollContainer(Direction.Vertical) + { + RelativeSizeAxes = Axes.Both, + Child = new StarRatingGrid() + } } - } - }, + }, + null, + new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black, + Alpha = 0.5f, + }, + new ScrollContainer(Direction.Vertical) + { + RelativeSizeAxes = Axes.Both, + Child = new PerformanceList() + } + } + }, + } + }, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension(GridSizeMode.Absolute, 20), + new Dimension(), + new Dimension(GridSizeMode.Absolute, 20) } }; } @@ -104,7 +135,7 @@ namespace osu.Game.Tests.Visual allBeatmaps.ForEach(b => beatmapDisplays.Add(new BeatmapDisplay(b))); } - private class BeatmapDisplay : CompositeDrawable + private class BeatmapDisplay : CompositeDrawable, IHasTooltip { private readonly OsuSpriteText text; private readonly BeatmapInfo beatmap; @@ -114,6 +145,8 @@ namespace osu.Game.Tests.Visual private bool isSelected; + public string TooltipText => text.Text; + public BeatmapDisplay(BeatmapInfo beatmap) { this.beatmap = beatmap; @@ -168,16 +201,19 @@ namespace osu.Game.Tests.Visual } } - private class ScoreList : CompositeDrawable + private class PerformanceList : CompositeDrawable { - private readonly FillFlowContainer scores; + private readonly FillFlowContainer scores; private APIAccess api; - public ScoreList() + public PerformanceList() { - InternalChild = scores = new FillFlowContainer + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + InternalChild = scores = new FillFlowContainer { - RelativeSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, Spacing = new Vector2(0, 4) }; @@ -197,39 +233,25 @@ namespace osu.Game.Tests.Visual scores.Clear(); lastRequest = new GetScoresRequest(newBeatmap.BeatmapInfo); - lastRequest.Success += res => res.Scores.ForEach(s => scores.Add(new ScoreDisplay(s, newBeatmap.Beatmap))); + lastRequest.Success += res => res.Scores.ForEach(s => scores.Add(new PerformanceDisplay(s, newBeatmap.Beatmap))); api.Queue(lastRequest); } - private class ScoreDisplay : CompositeDrawable + private class PerformanceDisplay : CompositeDrawable { - private readonly OsuSpriteText playerName; - private readonly GridContainer attributeGrid; + private readonly OsuSpriteText text; private readonly Score score; private readonly Beatmap beatmap; - public ScoreDisplay(Score score, Beatmap beatmap) + public PerformanceDisplay(Score score, Beatmap beatmap) { this.score = score; this.beatmap = beatmap; RelativeSizeAxes = Axes.X; - Height = 16; - InternalChild = new GridContainer - { - RelativeSizeAxes = Axes.Both, - Content = new[] - { - new Drawable[] { playerName = new OsuSpriteText() }, - new Drawable[] { attributeGrid = new GridContainer { RelativeSizeAxes = Axes.Both } } - }, - RowDimensions = new[] - { - new Dimension(GridSizeMode.Relative, 0.75f), - new Dimension(GridSizeMode.Relative, 0.25f) - } - }; + AutoSizeAxes = Axes.Y; + InternalChild = text = new OsuSpriteText(); } [BackgroundDependencyLoader] @@ -243,8 +265,117 @@ namespace osu.Game.Tests.Visual var attributes = new Dictionary(); double performance = calculator.Calculate(attributes); - playerName.Text = $"{score.PP} | {performance.ToString("0.00")} | {score.PP / performance}"; - // var attributeRow = + text.Text = $"{score.User.Username} -> online: {score.PP:n2}pp | local: {performance:n2}pp"; + } + } + } + + private class StarRatingGrid : CompositeDrawable + { + private readonly FillFlowContainer modFlow; + private readonly OsuSpriteText totalText; + private readonly FillFlowContainer categoryTexts; + + public StarRatingGrid() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + InternalChild = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + modFlow = new FillFlowContainer + { + Name = "Checkbox flow", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(4, 4) + }, + new FillFlowContainer + { + Name = "Information display", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(0, 4), + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + totalText = new OsuSpriteText { TextSize = 24 }, + categoryTexts = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical + } + } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load(OsuGameBase osuGame) + { + osuGame.Beatmap.ValueChanged += beatmapChanged; + } + + private Cached informationCache = new Cached(); + + private Ruleset ruleset; + private WorkingBeatmap beatmap; + + private void beatmapChanged(WorkingBeatmap newBeatmap) + { + beatmap = newBeatmap; + + modFlow.Clear(); + + ruleset = newBeatmap.BeatmapInfo.Ruleset.CreateInstance(); + foreach (var mod in ruleset.GetAllMods()) + { + var checkBox = new OsuCheckbox + { + RelativeSizeAxes = Axes.None, + Width = 50, + LabelText = mod.ShortenedName + }; + + checkBox.Current.ValueChanged += v => informationCache.Invalidate(); + modFlow.Add(checkBox); + } + + informationCache.Invalidate(); + } + + protected override void Update() + { + base.Update(); + + if (ruleset == null) + return; + + if (!informationCache.IsValid) + { + totalText.Text = string.Empty; + categoryTexts.Clear(); + + var allMods = ruleset.GetAllMods().ToList(); + Mod[] activeMods = modFlow.Where(c => c.Current.Value).Select(c => allMods.First(m => m.ShortenedName == c.LabelText)).ToArray(); + + var diffCalc = ruleset.CreateDifficultyCalculator(beatmap.Beatmap, activeMods); + if (diffCalc != null) + { + var categories = new Dictionary(); + double totalSr = diffCalc.Calculate(categories); + + totalText.Text = $"Star rating: {totalSr:n2}"; + foreach (var kvp in categories) + categoryTexts.Add(new OsuSpriteText { Text = $"{kvp.Key}: {kvp.Value:n2}" }); + } + + informationCache.Validate(); } } } From 825aa6570ee766028a50492575ba5e12212c7f31 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Nov 2017 14:29:19 +0900 Subject: [PATCH 004/235] Fix rebase issues --- osu.Game.Rulesets.Osu/OsuRuleset.cs | 1 + osu.Game/Rulesets/Ruleset.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 4a0a5ce0c9..de2e8233de 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -15,6 +15,7 @@ using osu.Framework.Graphics; using osu.Game.Overlays.Settings; using osu.Framework.Input.Bindings; using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Osu.Scoring; namespace osu.Game.Rulesets.Osu { diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 226a897126..d787da6a0a 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -10,6 +10,7 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets From bf44b3d0efd0ddc8da9d2016de75b756c5127484 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Nov 2017 16:54:50 +0900 Subject: [PATCH 005/235] Cleanups --- .../Tests/TestCasePerformancePoints.cs | 2 +- .../Tests/TestCasePerformancePoints.cs | 2 +- .../OsuDifficulty/OsuDifficultyCalculator.cs | 2 +- .../OsuDifficulty/Skills/Skill.cs | 4 ++-- .../Scoring/OsuPerformanceCalculator.cs | 15 +++++++-------- .../Tests/TestCasePerformancePoints.cs | 2 +- .../Tests/TestCasePerformancePoints.cs | 2 +- .../Rulesets/Scoring/PerformanceCalculator.cs | 2 +- .../Tests/Visual/TestCasePerformancePoints.cs | 16 ++++++---------- 9 files changed, 21 insertions(+), 26 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Catch/Tests/TestCasePerformancePoints.cs index 071367b305..6643316f15 100644 --- a/osu.Game.Rulesets.Catch/Tests/TestCasePerformancePoints.cs +++ b/osu.Game.Rulesets.Catch/Tests/TestCasePerformancePoints.cs @@ -3,7 +3,7 @@ namespace osu.Game.Rulesets.Catch.Tests { - public class TestCasePerformancePoints : osu.Game.Tests.Visual.TestCasePerformancePoints + public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints { public TestCasePerformancePoints() : base(new CatchRuleset(new RulesetInfo())) diff --git a/osu.Game.Rulesets.Mania/Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Mania/Tests/TestCasePerformancePoints.cs index 420465c045..e60808b1a6 100644 --- a/osu.Game.Rulesets.Mania/Tests/TestCasePerformancePoints.cs +++ b/osu.Game.Rulesets.Mania/Tests/TestCasePerformancePoints.cs @@ -3,7 +3,7 @@ namespace osu.Game.Rulesets.Mania.Tests { - public class TestCasePerformancePoints : osu.Game.Tests.Visual.TestCasePerformancePoints + public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints { public TestCasePerformancePoints() : base(new ManiaRuleset(new RulesetInfo())) diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs index 537874f643..3ab1a60443 100644 --- a/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty } foreach (Skill s in skills) - s.Process(h); + s.Process(h, TimeRate); } double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Skill.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Skill.cs index b9632e18e2..ae75a4449b 100644 --- a/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Skill.cs +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Skill.cs @@ -38,9 +38,9 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills /// /// Process an and update current strain values accordingly. /// - public void Process(OsuDifficultyHitObject current) + public void Process(OsuDifficultyHitObject current, double timeRate) { - currentStrain *= strainDecay(current.DeltaTime); + currentStrain *= strainDecay(current.DeltaTime / timeRate); if (!(current.BaseObject is Spinner)) currentStrain += StrainValueOf(current) * SkillMultiplier; diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs index 32469b1bf7..93003925ef 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Beatmaps; @@ -32,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Scoring { countHitCircles = Beatmap.HitObjects.Count(h => h is HitCircle); - beatmapMaxCombo = Beatmap.HitObjects.Count(); + beatmapMaxCombo = Beatmap.HitObjects.Count; beatmapMaxCombo += Beatmap.HitObjects.OfType().Sum(s => s.RepeatCount + s.Ticks.Count()); } @@ -84,10 +83,10 @@ namespace osu.Game.Rulesets.Osu.Scoring double aimValue = Math.Pow(5.0f * Math.Max(1.0f, double.Parse(Attributes["Aim"]) / 0.0675f) - 4.0f, 3.0f) / 100000.0f; // Longer maps are worth more - double LengthBonus = 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) + + double lengthBonus = 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) + (totalHits > 2000 ? Math.Log10(totalHits / 2000.0f) * 0.5f : 0.0f); - aimValue *= LengthBonus; + aimValue *= lengthBonus; // Penalize misses exponentially. This mainly fixes tag4 maps and the likes until a per-hitobject solution is available aimValue *= Math.Pow(0.97f, countMiss); @@ -117,13 +116,13 @@ namespace osu.Game.Rulesets.Osu.Scoring if (mods.Any(h => h is OsuModFlashlight)) { // Apply length bonus again if flashlight is on simply because it becomes a lot harder on longer maps. - aimValue *= 1.45f * LengthBonus; + aimValue *= 1.45f * lengthBonus; } // Scale the aim value with accuracy _slightly_ aimValue *= 0.5f + accuracy / 2.0f; // It is important to also consider accuracy difficulty when doing that - aimValue *= 0.98f + (Math.Pow(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 2) / 2500); + aimValue *= 0.98f + Math.Pow(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 2) / 2500; return aimValue; } @@ -146,7 +145,7 @@ namespace osu.Game.Rulesets.Osu.Scoring // Scale the speed value with accuracy _slightly_ speedValue *= 0.5f + accuracy / 2.0f; // It is important to also consider accuracy difficulty when doing that - speedValue *= 0.98f + (Math.Pow(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 2) / 2500); + speedValue *= 0.98f + Math.Pow(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty, 2) / 2500; return speedValue; } @@ -154,7 +153,7 @@ namespace osu.Game.Rulesets.Osu.Scoring private double computeAccuracyValue() { // This percentage only considers HitCircles of any value - in this part of the calculation we focus on hitting the timing hit window - double betterAccuracyPercentage = 0; + double betterAccuracyPercentage; int amountHitObjectsWithAccuracy = countHitCircles; if (amountHitObjectsWithAccuracy > 0) diff --git a/osu.Game.Rulesets.Osu/Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Osu/Tests/TestCasePerformancePoints.cs index b430803907..36590b484f 100644 --- a/osu.Game.Rulesets.Osu/Tests/TestCasePerformancePoints.cs +++ b/osu.Game.Rulesets.Osu/Tests/TestCasePerformancePoints.cs @@ -3,7 +3,7 @@ namespace osu.Game.Rulesets.Osu.Tests { - public class TestCasePerformancePoints : osu.Game.Tests.Visual.TestCasePerformancePoints + public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints { public TestCasePerformancePoints() : base(new OsuRuleset(new RulesetInfo())) diff --git a/osu.Game.Rulesets.Taiko/Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Taiko/Tests/TestCasePerformancePoints.cs index a60c79fc6a..269aca2cd9 100644 --- a/osu.Game.Rulesets.Taiko/Tests/TestCasePerformancePoints.cs +++ b/osu.Game.Rulesets.Taiko/Tests/TestCasePerformancePoints.cs @@ -3,7 +3,7 @@ namespace osu.Game.Rulesets.Taiko.Tests { - public class TestCasePerformancePoints : osu.Game.Tests.Visual.TestCasePerformancePoints + public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints { public TestCasePerformancePoints() : base(new TaikoRuleset(new RulesetInfo())) diff --git a/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs b/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs index 49b7ac54c6..359d6589ed 100644 --- a/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs +++ b/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Scoring protected readonly Beatmap Beatmap; protected readonly Score Score; - public PerformanceCalculator(Ruleset ruleset, Beatmap beatmap, Score score) + protected PerformanceCalculator(Ruleset ruleset, Beatmap beatmap, Score score) { Beatmap = CreateBeatmapConverter().Convert(beatmap); Score = score; diff --git a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs index 3a1fc20060..cd1005593e 100644 --- a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs +++ b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs @@ -12,15 +12,12 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.API.Requests; -using osu.Game.Overlays; -using osu.Game.Overlays.Music; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; @@ -29,7 +26,7 @@ namespace osu.Game.Tests.Visual { public abstract class TestCasePerformancePoints : OsuTestCase { - public TestCasePerformancePoints(Ruleset ruleset) + protected TestCasePerformancePoints(Ruleset ruleset) { Child = new GridContainer { @@ -49,7 +46,7 @@ namespace osu.Game.Tests.Visual Colour = Color4.Black, Alpha = 0.5f, }, - new ScrollContainer(Direction.Vertical) + new ScrollContainer { RelativeSizeAxes = Axes.Both, Child = new BeatmapList(ruleset) @@ -68,7 +65,7 @@ namespace osu.Game.Tests.Visual Colour = Color4.Black, Alpha = 0.5f, }, - new ScrollContainer(Direction.Vertical) + new ScrollContainer { RelativeSizeAxes = Axes.Both, Child = new StarRatingGrid() @@ -87,7 +84,7 @@ namespace osu.Game.Tests.Visual Colour = Color4.Black, Alpha = 0.5f, }, - new ScrollContainer(Direction.Vertical) + new ScrollContainer { RelativeSizeAxes = Axes.Both, Child = new PerformanceList() @@ -127,7 +124,7 @@ namespace osu.Game.Tests.Visual } [BackgroundDependencyLoader] - private void load(OsuGameBase osuGame, BeatmapManager beatmaps) + private void load(BeatmapManager beatmaps) { var sets = beatmaps.GetAllUsableBeatmapSets(); var allBeatmaps = sets.SelectMany(s => s.Beatmaps).Where(b => ruleset.LegacyID < 0 || b.RulesetID == ruleset.LegacyID); @@ -186,7 +183,6 @@ namespace osu.Game.Tests.Visual this.osuGame = osuGame; this.beatmaps = beatmaps; - var working = beatmaps.GetWorkingBeatmap(beatmap); text.Text = $"{beatmap.Metadata.Artist} - {beatmap.Metadata.Title} ({beatmap.Metadata.AuthorString}) [{beatmap.Version}]"; osuGame.Beatmap.ValueChanged += beatmapChanged; @@ -372,7 +368,7 @@ namespace osu.Game.Tests.Visual totalText.Text = $"Star rating: {totalSr:n2}"; foreach (var kvp in categories) - categoryTexts.Add(new OsuSpriteText { Text = $"{kvp.Key}: {kvp.Value:n2}" }); + categoryTexts.Add(new OsuSpriteText { Text = $"{kvp.Key}: {kvp.Value}" }); } informationCache.Validate(); From 5d753427f6edf05ee9a788c39d273d86146a403a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Nov 2017 17:28:22 +0900 Subject: [PATCH 006/235] Fix up DT not affecting hitobject densities --- .../OsuDifficulty/OsuDifficultyCalculator.cs | 4 ++-- .../OsuDifficulty/Preprocessing/OsuDifficultyBeatmap.cs | 8 ++++---- .../OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs | 8 ++++++-- osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Skill.cs | 4 ++-- osu.Game/Tests/Visual/TestCasePerformancePoints.cs | 3 ++- 5 files changed, 16 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs index 3ab1a60443..4f41d3b8e2 100644 --- a/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty public override double Calculate(Dictionary categoryDifficulty = null) { - OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap(Beatmap.HitObjects); + OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap(Beatmap.HitObjects, TimeRate); Skill[] skills = { new Aim(), @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty } foreach (Skill s in skills) - s.Process(h, TimeRate); + s.Process(h); } double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyBeatmap.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyBeatmap.cs index c6ecc3a506..f8e9423e29 100644 --- a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyBeatmap.cs +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyBeatmap.cs @@ -20,12 +20,12 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing /// Creates an enumerator, which preprocesses a list of s recieved as input, wrapping them as /// which contains extra data required for difficulty calculation. /// - public OsuDifficultyBeatmap(List objects) + public OsuDifficultyBeatmap(List objects, double timeRate) { // Sort OsuHitObjects by StartTime - they are not correctly ordered in some cases. // This should probably happen before the objects reach the difficulty calculator. objects.Sort((a, b) => a.StartTime.CompareTo(b.StartTime)); - difficultyObjects = createDifficultyObjectEnumerator(objects); + difficultyObjects = createDifficultyObjectEnumerator(objects, timeRate); } /// @@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - private IEnumerator createDifficultyObjectEnumerator(List objects) + private IEnumerator createDifficultyObjectEnumerator(List objects, double timeRate) { // We will process OsuHitObjects in groups of three to form a triangle, so we can calculate an angle for each object. OsuHitObject[] triangle = new OsuHitObject[3]; @@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing triangle[1] = triangle[0]; triangle[0] = objects[i]; - yield return new OsuDifficultyHitObject(triangle); + yield return new OsuDifficultyHitObject(triangle, timeRate); } } } diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs index bdeb62df3e..17d95990d5 100644 --- a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -33,13 +33,17 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing private const int normalized_radius = 52; + private readonly double timeRate; + private readonly OsuHitObject[] t; /// /// Initializes the object calculating extra data required for difficulty calculation. /// - public OsuDifficultyHitObject(OsuHitObject[] triangle) + public OsuDifficultyHitObject(OsuHitObject[] triangle, double timeRate) { + this.timeRate = timeRate; + t = triangle; BaseObject = t[0]; setDistances(); @@ -63,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing private void setTimingValues() { // Every timing inverval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure. - DeltaTime = Math.Max(40, t[0].StartTime - t[1].StartTime); + DeltaTime = Math.Max(40, t[0].StartTime - t[1].StartTime) / timeRate; TimeUntilHit = 450; // BaseObject.PreEmpt; } } diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Skill.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Skill.cs index ae75a4449b..b9632e18e2 100644 --- a/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Skill.cs +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Skills/Skill.cs @@ -38,9 +38,9 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Skills /// /// Process an and update current strain values accordingly. /// - public void Process(OsuDifficultyHitObject current, double timeRate) + public void Process(OsuDifficultyHitObject current) { - currentStrain *= strainDecay(current.DeltaTime / timeRate); + currentStrain *= strainDecay(current.DeltaTime); if (!(current.BaseObject is Spinner)) currentStrain += StrainValueOf(current) * SkillMultiplier; diff --git a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs index cd1005593e..6b2ab6433b 100644 --- a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs +++ b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs @@ -183,7 +183,8 @@ namespace osu.Game.Tests.Visual this.osuGame = osuGame; this.beatmaps = beatmaps; - text.Text = $"{beatmap.Metadata.Artist} - {beatmap.Metadata.Title} ({beatmap.Metadata.AuthorString}) [{beatmap.Version}]"; + var working = beatmaps.GetWorkingBeatmap(beatmap); + text.Text = $"{working.Metadata.Artist} - {working.Metadata.Title} ({working.Metadata.AuthorString}) [{working.BeatmapInfo.Version}]"; osuGame.Beatmap.ValueChanged += beatmapChanged; } From 433f4f03a108f80e12776ef24af3d198ba7e6838 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Nov 2017 20:19:49 +0900 Subject: [PATCH 007/235] Actually initialise DifficultyCalculator with mods --- osu.Game/Rulesets/Scoring/PerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs b/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs index 359d6589ed..000e279d11 100644 --- a/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs +++ b/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Scoring Beatmap = CreateBeatmapConverter().Convert(beatmap); Score = score; - var diffCalc = ruleset.CreateDifficultyCalculator(beatmap); + var diffCalc = ruleset.CreateDifficultyCalculator(beatmap, score.Mods); diffCalc.Calculate(attributes); } From c7ffe6fe5873c5d489e123b7b7e3d11162d9d074 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Nov 2017 20:27:44 +0900 Subject: [PATCH 008/235] Fix timeRate dividing incorrectly --- .../OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs index 17d95990d5..05738f1a7d 100644 --- a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing private void setTimingValues() { // Every timing inverval is hard capped at the equivalent of 375 BPM streaming speed as a safety measure. - DeltaTime = Math.Max(40, t[0].StartTime - t[1].StartTime) / timeRate; + DeltaTime = Math.Max(40, (t[0].StartTime - t[1].StartTime) / timeRate); TimeUntilHit = 450; // BaseObject.PreEmpt; } } From c221cfd30c0cb2d7ecca73461b028e5b2dee34b9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Nov 2017 20:28:41 +0900 Subject: [PATCH 009/235] Fix slider cursor positions not being taken into account --- osu.Game.Rulesets.Osu/Objects/Slider.cs | 6 ++++ .../Preprocessing/OsuDifficultyHitObject.cs | 36 ++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 112fcb1a30..2f6b5c7e68 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -45,6 +45,12 @@ namespace osu.Game.Rulesets.Osu.Objects set { Curve.Distance = value; } } + /// + /// The position of the cursor at the point of completion of this . + /// This is set and used by difficulty calculation. + /// + internal Vector2? CursorPosition; + public List RepeatSamples { get; set; } = new List(); public int RepeatCount { get; set; } = 1; diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs index 05738f1a7d..48f9bcce32 100644 --- a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -2,6 +2,8 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Linq; +using OpenTK; using osu.Game.Rulesets.Osu.Objects; namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing @@ -61,7 +63,39 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing scalingFactor *= 1 + smallCircleBonus; } - Distance = (t[0].StackedPosition - t[1].StackedPosition).Length * scalingFactor; + Vector2 lastCursorPosition = t[1].StackedPosition; + + var lastSlider = t[1] as Slider; + if (lastSlider != null) + { + if (lastSlider.CursorPosition == null) + { + float approxFollowCircleRadius = (float)(lastSlider.Radius * scalingFactor * 3); + + var computeVertex = new Action(t => + { + var diff = lastSlider.PositionAt(t) - lastCursorPosition; + float dist = diff.Length; + + if (dist > approxFollowCircleRadius) + { + // The cursor would be outside the follow circle, we need to move it + diff.Normalize(); // Obtain direction of diff + dist -= approxFollowCircleRadius; + lastCursorPosition += diff * dist; + } + }); + + var scoringTimes = lastSlider.Ticks.Select(t => t.StartTime).Concat(lastSlider.RepeatPoints.Select(r => r.StartTime)).OrderBy(t => t); + foreach (var time in scoringTimes) + computeVertex(time); + computeVertex(lastSlider.EndTime); + + lastSlider.CursorPosition = lastCursorPosition; + } + } + + Distance = (BaseObject.StackedPosition - lastCursorPosition).Length * scalingFactor; } private void setTimingValues() From 9260f5b64e85267cd1c2db349a86cf7e46f1116a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Nov 2017 20:57:45 +0900 Subject: [PATCH 010/235] Rework to avoid access to modified closures --- .../Preprocessing/OsuDifficultyHitObject.cs | 54 ++++++++++--------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs index 48f9bcce32..fd58ca9e6b 100644 --- a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -68,31 +68,8 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing var lastSlider = t[1] as Slider; if (lastSlider != null) { - if (lastSlider.CursorPosition == null) - { - float approxFollowCircleRadius = (float)(lastSlider.Radius * scalingFactor * 3); - - var computeVertex = new Action(t => - { - var diff = lastSlider.PositionAt(t) - lastCursorPosition; - float dist = diff.Length; - - if (dist > approxFollowCircleRadius) - { - // The cursor would be outside the follow circle, we need to move it - diff.Normalize(); // Obtain direction of diff - dist -= approxFollowCircleRadius; - lastCursorPosition += diff * dist; - } - }); - - var scoringTimes = lastSlider.Ticks.Select(t => t.StartTime).Concat(lastSlider.RepeatPoints.Select(r => r.StartTime)).OrderBy(t => t); - foreach (var time in scoringTimes) - computeVertex(time); - computeVertex(lastSlider.EndTime); - - lastSlider.CursorPosition = lastCursorPosition; - } + computeSliderCursorPosition(lastSlider); + lastCursorPosition = lastSlider.CursorPosition ?? lastCursorPosition; } Distance = (BaseObject.StackedPosition - lastCursorPosition).Length * scalingFactor; @@ -104,5 +81,32 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing DeltaTime = Math.Max(40, (t[0].StartTime - t[1].StartTime) / timeRate); TimeUntilHit = 450; // BaseObject.PreEmpt; } + + private void computeSliderCursorPosition(Slider slider) + { + if (slider.CursorPosition != null) + return; + slider.CursorPosition = slider.StackedPosition; + + float approxFollowCircleRadius = (float)(slider.Radius * 3); + var computeVertex = new Action(t => + { + var diff = slider.PositionAt(t) - slider.CursorPosition.Value; + float dist = diff.Length; + + if (dist > approxFollowCircleRadius) + { + // The cursor would be outside the follow circle, we need to move it + diff.Normalize(); // Obtain direction of diff + dist -= approxFollowCircleRadius; + slider.CursorPosition += diff * dist; + } + }); + + var scoringTimes = slider.Ticks.Select(t => t.StartTime).Concat(slider.RepeatPoints.Select(r => r.StartTime)).OrderBy(t => t); + foreach (var time in scoringTimes) + computeVertex(time); + computeVertex(slider.EndTime); + } } } From eb03b0db302236fbbbd18538ffafdf683620944c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 17 Nov 2017 21:28:59 +0900 Subject: [PATCH 011/235] Consider slider lengths as part of Distance --- osu.Game.Rulesets.Osu/Objects/Slider.cs | 12 +++++++++--- .../Preprocessing/OsuDifficultyHitObject.cs | 15 +++++++++------ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 2f6b5c7e68..39ec753fe1 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -46,10 +46,16 @@ namespace osu.Game.Rulesets.Osu.Objects } /// - /// The position of the cursor at the point of completion of this . - /// This is set and used by difficulty calculation. + /// The position of the cursor at the point of completion of this if it was hit + /// with as few movements as possible. This is set and used by difficulty calculation. /// - internal Vector2? CursorPosition; + internal Vector2? LazyEndPosition; + + /// + /// The distance travelled by the cursor upon completion of this if it was hit + /// with as few movements as possible. This is set and used by difficulty calculation. + /// + internal float LazyTravelDistance; public List RepeatSamples { get; set; } = new List(); public int RepeatCount { get; set; } = 1; diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs index fd58ca9e6b..972677a6f1 100644 --- a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -64,15 +64,17 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing } Vector2 lastCursorPosition = t[1].StackedPosition; + float lastTravelDistance = 0; var lastSlider = t[1] as Slider; if (lastSlider != null) { computeSliderCursorPosition(lastSlider); - lastCursorPosition = lastSlider.CursorPosition ?? lastCursorPosition; + lastCursorPosition = lastSlider.LazyEndPosition ?? lastCursorPosition; + lastTravelDistance = lastSlider.LazyTravelDistance; } - Distance = (BaseObject.StackedPosition - lastCursorPosition).Length * scalingFactor; + Distance = (lastTravelDistance + (BaseObject.StackedPosition - lastCursorPosition).Length) * scalingFactor; } private void setTimingValues() @@ -84,14 +86,14 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing private void computeSliderCursorPosition(Slider slider) { - if (slider.CursorPosition != null) + if (slider.LazyEndPosition != null) return; - slider.CursorPosition = slider.StackedPosition; + slider.LazyEndPosition = slider.StackedPosition; float approxFollowCircleRadius = (float)(slider.Radius * 3); var computeVertex = new Action(t => { - var diff = slider.PositionAt(t) - slider.CursorPosition.Value; + var diff = slider.PositionAt(t) - slider.LazyEndPosition.Value; float dist = diff.Length; if (dist > approxFollowCircleRadius) @@ -99,7 +101,8 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing // The cursor would be outside the follow circle, we need to move it diff.Normalize(); // Obtain direction of diff dist -= approxFollowCircleRadius; - slider.CursorPosition += diff * dist; + slider.LazyEndPosition += diff * dist; + slider.LazyTravelDistance += dist; } }); From fe7f9cccaa0520791ff08064fd5a851b0eaf423d Mon Sep 17 00:00:00 2001 From: jorolf Date: Sun, 19 Nov 2017 14:16:00 +0100 Subject: [PATCH 012/235] BeatmapSetCover can display other types of covers now --- .../Beatmaps/Drawables/BeatmapSetCover.cs | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/BeatmapSetCover.cs b/osu.Game/Beatmaps/Drawables/BeatmapSetCover.cs index 614ebc236b..ba79db3f48 100644 --- a/osu.Game/Beatmaps/Drawables/BeatmapSetCover.cs +++ b/osu.Game/Beatmaps/Drawables/BeatmapSetCover.cs @@ -11,21 +11,44 @@ namespace osu.Game.Beatmaps.Drawables public class BeatmapSetCover : Sprite { private readonly BeatmapSetInfo set; - public BeatmapSetCover(BeatmapSetInfo set) + private readonly BeatmapSetCoverType type; + + public BeatmapSetCover(BeatmapSetInfo set, BeatmapSetCoverType type = BeatmapSetCoverType.Cover) { if (set == null) throw new ArgumentNullException(nameof(set)); this.set = set; + this.type = type; } [BackgroundDependencyLoader] private void load(TextureStore textures) { - string resource = set.OnlineInfo.Covers.Cover; + string resource = null; + + switch (type) + { + case BeatmapSetCoverType.Cover: + resource = set.OnlineInfo.Covers.Cover; + break; + case BeatmapSetCoverType.Card: + resource = set.OnlineInfo.Covers.Card; + break; + case BeatmapSetCoverType.List: + resource = set.OnlineInfo.Covers.List; + break; + } if (resource != null) Texture = textures.Get(resource); } } + + public enum BeatmapSetCoverType + { + Cover, + Card, + List, + } } From 1f379cab8fcdaa8b1b5d1497e18b2bae313d2cc1 Mon Sep 17 00:00:00 2001 From: jorolf Date: Sun, 19 Nov 2017 14:17:14 +0100 Subject: [PATCH 013/235] move BeatmapMetadataContainer to a separate class --- .../Sections/BeatmapMetadataContainer.cs | 60 +++++++++++++++++++ .../Profile/Sections/Ranks/DrawableScore.cs | 44 +------------- osu.Game/osu.Game.csproj | 1 + 3 files changed, 62 insertions(+), 43 deletions(-) create mode 100644 osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs new file mode 100644 index 0000000000..5104f98f9d --- /dev/null +++ b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs @@ -0,0 +1,60 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Localisation; +using osu.Game.Beatmaps; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +namespace osu.Game.Overlays.Profile.Sections +{ + public class BeatmapMetadataContainer : OsuHoverContainer, IHasTooltip + { + private readonly BeatmapInfo beatmap; + + public BeatmapMetadataContainer(BeatmapInfo beatmap) + { + this.beatmap = beatmap; + AutoSizeAxes = Axes.Both; + TooltipText = $"{beatmap.Metadata.Artist} - {beatmap.Metadata.Title}"; + } + + public string TooltipText { get; } + + [BackgroundDependencyLoader(true)] + private void load(LocalisationEngine locale, BeatmapSetOverlay beatmapSetOverlay) + { + Action = () => + { + if (beatmap.OnlineBeatmapSetID.HasValue) beatmapSetOverlay?.ShowBeatmapSet(beatmap.OnlineBeatmapSetID.Value); + }; + + Child = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new OsuSpriteText + { + Current = locale.GetUnicodePreference( + $"{beatmap.Metadata.TitleUnicode ?? beatmap.Metadata.Title} [{beatmap.Version}] ", + $"{beatmap.Metadata.Title ?? beatmap.Metadata.TitleUnicode} [{beatmap.Version}] " + ), + TextSize = 15, + Font = "Exo2.0-SemiBoldItalic", + }, + new OsuSpriteText + { + Current = locale.GetUnicodePreference(beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist), + TextSize = 12, + Padding = new MarginPadding { Top = 3 }, + Font = "Exo2.0-RegularItalic", + }, + }, + }; + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs index 35f4778047..6c475cbb26 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs @@ -14,10 +14,8 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using OpenTK.Graphics; using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Cursor; using osu.Framework.Input; using osu.Framework.Extensions.Color4Extensions; -using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.Profile.Sections.Ranks { @@ -130,37 +128,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks Depth = -1, }); - metadata.Add(new MetadataContainer(Score.Beatmap.Metadata.Title, Score.Beatmap.Metadata.Artist) - { - AutoSizeAxes = Axes.Both, - Action = () => - { - if (Score.Beatmap.OnlineBeatmapSetID.HasValue) beatmapSetOverlay?.ShowBeatmapSet(Score.Beatmap.OnlineBeatmapSetID.Value); - }, - Child = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new OsuSpriteText - { - Current = locale.GetUnicodePreference( - $"{Score.Beatmap.Metadata.TitleUnicode ?? Score.Beatmap.Metadata.Title} [{Score.Beatmap.Version}] ", - $"{Score.Beatmap.Metadata.Title ?? Score.Beatmap.Metadata.TitleUnicode} [{Score.Beatmap.Version}] " - ), - TextSize = 15, - Font = "Exo2.0-SemiBoldItalic", - }, - new OsuSpriteText - { - Current = locale.GetUnicodePreference(Score.Beatmap.Metadata.ArtistUnicode, Score.Beatmap.Metadata.Artist), - TextSize = 12, - Padding = new MarginPadding { Top = 3 }, - Font = "Exo2.0-RegularItalic", - }, - }, - }, - }); + metadata.Add(new BeatmapMetadataContainer(Score.Beatmap)); foreach (Mod mod in Score.Mods) modsContainer.Add(new ModIcon(mod) { Scale = new Vector2(0.5f) }); @@ -181,15 +149,5 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks underscoreLine.FadeIn(fade_duration, Easing.OutQuint); base.OnHoverLost(state); } - - private class MetadataContainer : OsuHoverContainer, IHasTooltip - { - public string TooltipText { get; set; } - - public MetadataContainer(string title, string artist) - { - TooltipText = $"{artist} - {title}"; - } - } } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 7b479bdba2..c5493ea59f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -287,6 +287,7 @@ + From 48b44e8e4eb0f29c2430ca286438dd8b69ee9cc2 Mon Sep 17 00:00:00 2001 From: jorolf Date: Sun, 19 Nov 2017 14:18:14 +0100 Subject: [PATCH 014/235] add a user most played beatmaps request/response --- .../API/Requests/GetBeatmapSetsResponse.cs | 2 +- .../API/Requests/GetUserBeatmapsRequest.cs | 15 ++++- .../GetUserMostPlayedBeatmapsRequest.cs | 67 +++++++++++++++++++ osu.Game/osu.Game.csproj | 1 + 4 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs diff --git a/osu.Game/Online/API/Requests/GetBeatmapSetsResponse.cs b/osu.Game/Online/API/Requests/GetBeatmapSetsResponse.cs index 9e412a9b8b..d187cabf31 100644 --- a/osu.Game/Online/API/Requests/GetBeatmapSetsResponse.cs +++ b/osu.Game/Online/API/Requests/GetBeatmapSetsResponse.cs @@ -65,7 +65,7 @@ namespace osu.Game.Online.API.Requests Ranked = ranked, LastUpdated = lastUpdated, }, - Beatmaps = beatmaps.Select(b => b.ToBeatmap(rulesets)).ToList(), + Beatmaps = beatmaps?.Select(b => b.ToBeatmap(rulesets)).ToList(), }; } diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs index a66799f404..9c3a32ec9e 100644 --- a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs @@ -1,18 +1,19 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using Humanizer; using System.Collections.Generic; namespace osu.Game.Online.API.Requests { - public class GetUserBeatmapsRequest : APIRequest> + public class GetUserBeatmapsRequest : APIRequest> { private readonly long userId; private readonly int offset; private readonly BeatmapSetType type; - public GetUserBeatmapsRequest(long userId, BeatmapSetType type, int offset = 0) + protected GetUserBeatmapsRequest(long userId, BeatmapSetType type, int offset = 0) { this.userId = userId; this.offset = offset; @@ -22,6 +23,16 @@ namespace osu.Game.Online.API.Requests protected override string Target => $@"users/{userId}/beatmapsets/{type.ToString().Underscore()}?offset={offset}"; } + public class GetUserBeatmapsRequest : GetUserBeatmapsRequest + { + public GetUserBeatmapsRequest(long userID, BeatmapSetType type, int offset = 0) + : base(userID, type, offset) + { + if(type == BeatmapSetType.MostPlayed) + throw new ArgumentException("Please use " + nameof(GetUserMostPlayedBeatmapsRequest) + " instead"); + } + } + public enum BeatmapSetType { MostPlayed, diff --git a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs new file mode 100644 index 0000000000..33b755450b --- /dev/null +++ b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs @@ -0,0 +1,67 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using Newtonsoft.Json; +using osu.Framework.Extensions; +using osu.Game.Beatmaps; +using osu.Game.Rulesets; + +namespace osu.Game.Online.API.Requests +{ + public class GetUserMostPlayedBeatmapsRequest : GetUserBeatmapsRequest + { + public GetUserMostPlayedBeatmapsRequest(long userID, BeatmapSetType type, int offset = 0) + : base(userID, type, offset) + { + if (type != BeatmapSetType.MostPlayed) + throw new ArgumentException("Please use " + nameof(GetUserBeatmapsRequest) + " instead"); + } + } + + public class UserMostPlayedBeatmapsResponse + { + [JsonProperty("beatmap_id")] + public int BeatmapID; + + [JsonProperty("count")] + public int PlayCount; + + [JsonProperty] + private BeatmapResponse beatmap; + + [JsonProperty] + private GetBeatmapSetsResponse beatmapSet; + + public BeatmapInfo GetBeatmapInfo(RulesetStore rulesets) + { + BeatmapSetInfo setInfo = beatmapSet.ToBeatmapSet(rulesets); + return new BeatmapInfo + { + OnlineBeatmapID = beatmap.Id, + OnlineBeatmapSetID = setInfo.OnlineBeatmapSetID, + Ruleset = rulesets.AvailableRulesets.FirstOrDefault(ruleset => ruleset.Name.Equals(beatmap.Mode)), + StarDifficulty = beatmap.DifficultyRating, + Version = beatmap.Version, + Metadata = setInfo.Metadata, + BeatmapSet = setInfo, + }; + } + + private class BeatmapResponse + { + [JsonProperty] + public int Id; + + [JsonProperty] + public string Mode; + + [JsonProperty("difficulty_rating")] + public double DifficultyRating; + + [JsonProperty] + public string Version; + } + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index c5493ea59f..9fc1674c45 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -282,6 +282,7 @@ + From 4281d76bcf8d8e0c7f56424f8faa863c7aa02dfd Mon Sep 17 00:00:00 2001 From: jorolf Date: Sun, 19 Nov 2017 14:19:05 +0100 Subject: [PATCH 015/235] historical section now shows the most played beatmaps --- .../Historical/MostPlayedBeatmapDrawable.cs | 160 ++++++++++++++++++ .../PaginatedMostPlayedBeatmapContainer.cs | 52 ++++++ .../Profile/Sections/HistoricalSection.cs | 8 +- osu.Game/osu.Game.csproj | 2 + 4 files changed, 221 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Overlays/Profile/Sections/Historical/MostPlayedBeatmapDrawable.cs create mode 100644 osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs diff --git a/osu.Game/Overlays/Profile/Sections/Historical/MostPlayedBeatmapDrawable.cs b/osu.Game/Overlays/Profile/Sections/Historical/MostPlayedBeatmapDrawable.cs new file mode 100644 index 0000000000..0f419ad44e --- /dev/null +++ b/osu.Game/Overlays/Profile/Sections/Historical/MostPlayedBeatmapDrawable.cs @@ -0,0 +1,160 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.Profile.Sections.Historical +{ + public class MostPlayedBeatmapDrawable : Container + { + private readonly BeatmapInfo beatmap; + private readonly OsuHoverContainer mapperContainer; + + private readonly EdgeEffectParameters edgeEffectNormal = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Offset = new Vector2(0, 1f), + Radius = 2f, + Colour = Color4.Black.Opacity(0.25f), + }; + + private readonly EdgeEffectParameters edgeEffectHovered = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Offset = new Vector2(0, 5f), + Radius = 10f, + Colour = Color4.Black.Opacity(0.25f), + }; + + public MostPlayedBeatmapDrawable(BeatmapInfo beatmap, int playCount) + { + this.beatmap = beatmap; + RelativeSizeAxes = Axes.X; + Height = 50; + Margin = new MarginPadding { Bottom = 10 }; + Masking = true; + EdgeEffect = edgeEffectNormal; + + Children = new Drawable[] + { + new Box //Background for this container, otherwise the shadow would be visible + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(0.2f), + }, + new Box //Image Background while loading + { + Size = new Vector2(80, 50), + Colour = Color4.Black, + }, + new DelayedLoadWrapper(new BeatmapSetCover(beatmap.BeatmapSet, BeatmapSetCoverType.List) + { + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + }), + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(10) { Left = 90 }, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new BeatmapMetadataContainer(beatmap), + new FillFlowContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new [] + { + new OsuSpriteText + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Text = playCount.ToString(), + TextSize = 18, + Font = @"Exo2.0-SemiBoldItalic" + }, + new OsuSpriteText + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Text = @"times played ", + TextSize = 12, + Font = @"Exo2.0-RegularItalic" + }, + } + } + }, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = @"mapped by ", + TextSize = 12, + }, + mapperContainer = new OsuHoverContainer + { + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = beatmap.Metadata.AuthorString, + TextSize = 12, + Font = @"Exo2.0-MediumItalic" + } + } + }, + } + }, + }, + } + }; + } + + [BackgroundDependencyLoader(true)] + private void load(UserProfileOverlay profileOverlay) + { + if(profileOverlay != null) + mapperContainer.Action = () => profileOverlay.ShowUser(beatmap.BeatmapSet.Metadata.Author); + } + + protected override bool OnHover(InputState state) + { + TweenEdgeEffectTo(edgeEffectHovered, 120, Easing.OutQuint); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + TweenEdgeEffectTo(edgeEffectNormal, 120, Easing.OutQuint); + base.OnHoverLost(state); + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs new file mode 100644 index 0000000000..d42e00b1a5 --- /dev/null +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -0,0 +1,52 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Online.API.Requests; +using osu.Game.Users; + +namespace osu.Game.Overlays.Profile.Sections.Historical +{ + public class PaginatedMostPlayedBeatmapContainer : PaginatedContainer + { + public PaginatedMostPlayedBeatmapContainer(Bindable user) + :base(user, "Most Played Beatmaps", "No performance records. :(") + { + ItemsPerPage = 5; + + ItemsContainer.Direction = FillDirection.Vertical; + } + + protected override void ShowMore() + { + base.ShowMore(); + + var req = new GetUserMostPlayedBeatmapsRequest(User.Value.Id, BeatmapSetType.MostPlayed, VisiblePages++ * ItemsPerPage); + + req.Success += beatmaps => + { + ShowMoreButton.FadeTo(beatmaps.Count == ItemsPerPage ? 1 : 0); + ShowMoreLoading.Hide(); + + if (!beatmaps.Any() && VisiblePages == 1) + { + MissingText.Show(); + return; + } + + MissingText.Hide(); + + foreach (var beatmap in beatmaps) + { + ItemsContainer.Add(new MostPlayedBeatmapDrawable(beatmap.GetBeatmapInfo(Rulesets), beatmap.PlayCount)); + } + }; + + Api.Queue(req); + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs b/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs index a4d043d20a..ab99abdccd 100644 --- a/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs +++ b/osu.Game/Overlays/Profile/Sections/HistoricalSection.cs @@ -1,7 +1,9 @@ // 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.Game.Online.API.Requests; +using osu.Game.Overlays.Profile.Sections.Historical; using osu.Game.Overlays.Profile.Sections.Ranks; namespace osu.Game.Overlays.Profile.Sections @@ -14,7 +16,11 @@ namespace osu.Game.Overlays.Profile.Sections public HistoricalSection() { - Child = new PaginatedScoreContainer(ScoreType.Recent, User, "Recent Plays (24h)", "No performance records. :("); + Children = new Drawable[] + { + new PaginatedMostPlayedBeatmapContainer(User), + new PaginatedScoreContainer(ScoreType.Recent, User, "Recent Plays (24h)", "No performance records. :("), + }; } } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9fc1674c45..fd8e03f623 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -290,6 +290,8 @@ + + From 66c51c7b44a1d3f99187decbb465a884606bc102 Mon Sep 17 00:00:00 2001 From: jorolf Date: Sun, 19 Nov 2017 14:33:50 +0100 Subject: [PATCH 016/235] cleanup --- osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs | 2 +- .../Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs | 1 - .../Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs | 1 - osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs | 3 +-- 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs index 9c3a32ec9e..dca0395e3a 100644 --- a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; namespace osu.Game.Online.API.Requests { - public class GetUserBeatmapsRequest : APIRequest> + public abstract class GetUserBeatmapsRequest : APIRequest> { private readonly long userId; private readonly int offset; diff --git a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs index 33b755450b..cdc156be05 100644 --- a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs @@ -4,7 +4,6 @@ using System; using System.Linq; using Newtonsoft.Json; -using osu.Framework.Extensions; using osu.Game.Beatmaps; using osu.Game.Rulesets; diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index d42e00b1a5..916f1f437a 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -5,7 +5,6 @@ using System.Linq; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; using osu.Game.Online.API.Requests; using osu.Game.Users; diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs index 6c475cbb26..4d210f399e 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs @@ -9,7 +9,6 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Select.Leaderboards; -using osu.Framework.Localisation; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using OpenTK.Graphics; @@ -113,7 +112,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks } [BackgroundDependencyLoader(true)] - private void load(OsuColour colour, LocalisationEngine locale, BeatmapSetOverlay beatmapSetOverlay) + private void load(OsuColour colour) { coloredBackground.Colour = underscoreLine.Colour = colour.Gray4; From decee415dd3b9961912a18dd0aa3ac73d57c26fe Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Nov 2017 19:54:08 +0900 Subject: [PATCH 017/235] Calculate real AR based on PreEmpt time --- .../Scoring/OsuPerformanceCalculator.cs | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs index 93003925ef..0b6bb1c4af 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs @@ -49,6 +49,17 @@ namespace osu.Game.Rulesets.Osu.Scoring if (mods.Any(m => m is OsuModRelax || m is OsuModAutopilot || m is OsuModAutoplay)) return 0; + // Todo: In the future we should apply changes to PreEmpt/AR at an OsuHitObject/BaseDifficulty level, but this is done + // locally for now as doing so would modify animations and other things unexpectedly + // DO NOT MODIFY THIS + double ar = Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate; + if (mods.Any(m => m is OsuModHardRock)) + ar = Math.Min(10, ar * 1.4); + if (mods.Any(m => m is OsuModEasy)) + ar = Math.Max(0, ar / 2); + double preEmpt = BeatmapDifficulty.DifficultyRange(ar, 1800, 1200, 450); + realApproachRate = preEmpt > 1200 ? (1800 - preEmpt) / 120 : (1200 - preEmpt) / 150 + 5; + // Custom multipliers for NoFail and SpunOut. double multiplier = 1.12f; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things @@ -95,17 +106,16 @@ namespace osu.Game.Rulesets.Osu.Scoring if (beatmapMaxCombo > 0) aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f); - double approachRate = Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate; double approachRateFactor = 1.0f; - if (approachRate > 10.33f) - approachRateFactor += 0.45f * (approachRate - 10.33f); - else if (approachRate < 8.0f) + if (realApproachRate > 10.33f) + approachRateFactor += 0.45f * (realApproachRate - 10.33f); + else if (realApproachRate < 8.0f) { // HD is worth more with lower ar! if (mods.Any(h => h is OsuModHidden)) - approachRateFactor += 0.02f * (8.0f - approachRate); + approachRateFactor += 0.02f * (8.0f - realApproachRate); else - approachRateFactor += 0.01f * (8.0f - approachRate); + approachRateFactor += 0.01f * (8.0f - realApproachRate); } aimValue *= approachRateFactor; From f9ad4b6acbc122bf505e55a9f351ed24f9df586a Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 21 Nov 2017 19:54:53 +0900 Subject: [PATCH 018/235] Make categoryDifficulties return doubles to improve decimal accuracy --- .../CatchDifficultyCalculator.cs | 2 +- .../ManiaDifficultyCalculator.cs | 2 +- .../OsuDifficulty/OsuDifficultyCalculator.cs | 6 +++--- .../Scoring/OsuPerformanceCalculator.cs | 13 +++++++------ .../TaikoDifficultyCalculator.cs | 6 +++--- osu.Game/Beatmaps/DifficultyCalculator.cs | 2 +- osu.Game/Rulesets/Scoring/PerformanceCalculator.cs | 6 +++--- osu.Game/Tests/Visual/TestCasePerformancePoints.cs | 6 +++--- 8 files changed, 22 insertions(+), 21 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/CatchDifficultyCalculator.cs index b77be9d1f0..369290fcbd 100644 --- a/osu.Game.Rulesets.Catch/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/CatchDifficultyCalculator.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Catch { } - public override double Calculate(Dictionary categoryDifficulty = null) => 0; + public override double Calculate(Dictionary categoryDifficulty = null) => 0; protected override BeatmapConverter CreateBeatmapConverter(Beatmap beatmap) => new CatchBeatmapConverter(); } diff --git a/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs b/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs index 67bc347535..e0763284a6 100644 --- a/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Mania/ManiaDifficultyCalculator.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania { } - public override double Calculate(Dictionary categoryDifficulty = null) => 0; + public override double Calculate(Dictionary categoryDifficulty = null) => 0; protected override BeatmapConverter CreateBeatmapConverter(Beatmap beatmap) => new ManiaBeatmapConverter(true, (int)Math.Max(1, Math.Round(beatmap.BeatmapInfo.BaseDifficulty.CircleSize))); } diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs index 4f41d3b8e2..3d185ab694 100644 --- a/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/OsuDifficultyCalculator.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty (h as Slider)?.Curve?.Calculate(); } - public override double Calculate(Dictionary categoryDifficulty = null) + public override double Calculate(Dictionary categoryDifficulty = null) { OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap(Beatmap.HitObjects, TimeRate); Skill[] skills = @@ -67,8 +67,8 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty if (categoryDifficulty != null) { - categoryDifficulty.Add("Aim", aimRating.ToString("0.00")); - categoryDifficulty.Add("Speed", speedRating.ToString("0.00")); + categoryDifficulty.Add("Aim", aimRating); + categoryDifficulty.Add("Speed", speedRating); } return starRating; diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs index 0b6bb1c4af..cc5abb36b5 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs @@ -19,6 +19,7 @@ namespace osu.Game.Rulesets.Osu.Scoring private readonly int beatmapMaxCombo; private Mod[] mods; + private double realApproachRate; private double accuracy; private int scoreMaxCombo; private int count300; @@ -35,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Scoring beatmapMaxCombo += Beatmap.HitObjects.OfType().Sum(s => s.RepeatCount + s.Ticks.Count()); } - public override double Calculate(Dictionary categoryRatings = null) + public override double Calculate(Dictionary categoryRatings = null) { mods = Score.Mods; accuracy = Score.Accuracy; @@ -81,9 +82,9 @@ namespace osu.Game.Rulesets.Osu.Scoring if (categoryRatings != null) { - categoryRatings.Add("Aim", aimValue.ToString("0.00")); - categoryRatings.Add("Speed", speedValue.ToString("0.00")); - categoryRatings.Add("Accuracy", accuracyValue.ToString("0.00")); + categoryRatings.Add("Aim", aimValue); + categoryRatings.Add("Speed", speedValue); + categoryRatings.Add("Accuracy", accuracyValue); } return totalValue; @@ -91,7 +92,7 @@ namespace osu.Game.Rulesets.Osu.Scoring private double computeAimValue() { - double aimValue = Math.Pow(5.0f * Math.Max(1.0f, double.Parse(Attributes["Aim"]) / 0.0675f) - 4.0f, 3.0f) / 100000.0f; + double aimValue = Math.Pow(5.0f * Math.Max(1.0f, Attributes["Aim"] / 0.0675f) - 4.0f, 3.0f) / 100000.0f; // Longer maps are worth more double lengthBonus = 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) + @@ -139,7 +140,7 @@ namespace osu.Game.Rulesets.Osu.Scoring private double computeSpeedValue() { - double speedValue = Math.Pow(5.0f * Math.Max(1.0f, double.Parse(Attributes["Speed"]) / 0.0675f) - 4.0f, 3.0f) / 100000.0f; + double speedValue = Math.Pow(5.0f * Math.Max(1.0f, Attributes["Speed"] / 0.0675f) - 4.0f, 3.0f) / 100000.0f; // Longer maps are worth more speedValue *= 0.95f + 0.4f * Math.Min(1.0f, totalHits / 2000.0f) + diff --git a/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs index e881942fbf..394036df39 100644 --- a/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs @@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Taiko { } - public override double Calculate(Dictionary categoryDifficulty = null) + public override double Calculate(Dictionary categoryDifficulty = null) { // Fill our custom DifficultyHitObject class, that carries additional information difficultyHitObjects.Clear(); @@ -53,8 +53,8 @@ namespace osu.Game.Rulesets.Taiko if (categoryDifficulty != null) { - categoryDifficulty.Add("Strain", starRating.ToString("0.00", CultureInfo.InvariantCulture)); - categoryDifficulty.Add("Hit window 300", (35 /*HitObjectManager.HitWindow300*/ / TimeRate).ToString("0.00", CultureInfo.InvariantCulture)); + categoryDifficulty.Add("Strain", starRating); + categoryDifficulty.Add("Hit window 300", (35 /*HitObjectManager.HitWindow300*/ / TimeRate)); } return starRating; diff --git a/osu.Game/Beatmaps/DifficultyCalculator.cs b/osu.Game/Beatmaps/DifficultyCalculator.cs index f58f433cb2..687e1b2177 100644 --- a/osu.Game/Beatmaps/DifficultyCalculator.cs +++ b/osu.Game/Beatmaps/DifficultyCalculator.cs @@ -14,7 +14,7 @@ namespace osu.Game.Beatmaps { protected double TimeRate = 1; - public abstract double Calculate(Dictionary categoryDifficulty = null); + public abstract double Calculate(Dictionary categoryDifficulty = null); } public abstract class DifficultyCalculator : DifficultyCalculator where T : HitObject diff --git a/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs b/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs index 000e279d11..4f603049db 100644 --- a/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs +++ b/osu.Game/Rulesets/Scoring/PerformanceCalculator.cs @@ -9,14 +9,14 @@ namespace osu.Game.Rulesets.Scoring { public abstract class PerformanceCalculator { - public abstract double Calculate(Dictionary categoryDifficulty = null); + public abstract double Calculate(Dictionary categoryDifficulty = null); } public abstract class PerformanceCalculator : PerformanceCalculator where TObject : HitObject { - private readonly Dictionary attributes = new Dictionary(); - protected IDictionary Attributes => attributes; + private readonly Dictionary attributes = new Dictionary(); + protected IDictionary Attributes => attributes; protected readonly Beatmap Beatmap; protected readonly Score Score; diff --git a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs index 6b2ab6433b..80167e5e1f 100644 --- a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs +++ b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs @@ -259,7 +259,7 @@ namespace osu.Game.Tests.Visual if (calculator == null) return; - var attributes = new Dictionary(); + var attributes = new Dictionary(); double performance = calculator.Calculate(attributes); text.Text = $"{score.User.Username} -> online: {score.PP:n2}pp | local: {performance:n2}pp"; @@ -364,12 +364,12 @@ namespace osu.Game.Tests.Visual var diffCalc = ruleset.CreateDifficultyCalculator(beatmap.Beatmap, activeMods); if (diffCalc != null) { - var categories = new Dictionary(); + var categories = new Dictionary(); double totalSr = diffCalc.Calculate(categories); totalText.Text = $"Star rating: {totalSr:n2}"; foreach (var kvp in categories) - categoryTexts.Add(new OsuSpriteText { Text = $"{kvp.Key}: {kvp.Value}" }); + categoryTexts.Add(new OsuSpriteText { Text = $"{kvp.Key}: {kvp.Value:n2}" }); } informationCache.Validate(); From 0df5432f5ebe25ea320f181c3183d3edb483b526 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Wed, 22 Nov 2017 21:45:18 +0100 Subject: [PATCH 019/235] removed line that set metadata per beatmap to null --- osu.Game/Beatmaps/BeatmapManager.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index f461317ce1..eb10b23c6f 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -515,9 +515,6 @@ namespace osu.Game.Beatmaps if (existing == null) { - // TODO: Diff beatmap metadata with set metadata and leave it here if necessary - beatmap.BeatmapInfo.Metadata = null; - RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID); // TODO: this should be done in a better place once we actually need to dynamically update it. From 57f2d8556bd9caacf2bc6616da73a64bf485ff64 Mon Sep 17 00:00:00 2001 From: jorolf Date: Wed, 22 Nov 2017 22:00:17 +0100 Subject: [PATCH 020/235] add a visual test --- .../Visual/TestCaseHistoricalSection.cs | 43 +++++++++++++++++++ osu.Game.Tests/osu.Game.Tests.csproj | 1 + 2 files changed, 44 insertions(+) create mode 100644 osu.Game.Tests/Visual/TestCaseHistoricalSection.cs diff --git a/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs b/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs new file mode 100644 index 0000000000..e67f389969 --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs @@ -0,0 +1,43 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Overlays.Profile.Sections; +using osu.Game.Overlays.Profile.Sections.Historical; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual +{ + internal class TestCaseHistoricalSection : OsuTestCase + { + public override string Description => "User's History"; + + public override IReadOnlyList RequiredTypes => new [] { typeof(HistoricalSection), typeof(MostPlayedBeatmapDrawable)}; + + + public TestCaseHistoricalSection() + { + HistoricalSection section; + + Add(new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(0.2f) + }); + + Add(new ScrollContainer + { + RelativeSizeAxes = Axes.Both, + Child = section = new HistoricalSection(), + }); + + AddStep("Show peppy", () => section.User.Value = new User { Id = 2 }); + AddStep("Show WubWoofWolf", () => section.User.Value = new User { Id = 39828 }); + } + } +} diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 9bba09b1a7..b093989b45 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -110,6 +110,7 @@ + From a60e53c3828c6077e0803b93d0223ccda39e21ad Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Thu, 23 Nov 2017 12:31:18 +0100 Subject: [PATCH 021/235] Changed the text updates into a scheduled (async) operation, and implemented this in the updateStatistics and clearStats methods --- osu.Game/Screens/Select/BeatmapDetails.cs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index d7c509d979..6eaed67534 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.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; using OpenTK; using OpenTK.Graphics; using osu.Framework.Allocation; @@ -187,9 +188,7 @@ namespace osu.Game.Screens.Select ratingsContainer.FadeIn(transition_duration); advanced.Beatmap = Beatmap; - description.Text = Beatmap.Version; - source.Text = Beatmap.Metadata.Source; - tags.Text = Beatmap.Metadata.Tags; + loadDetailsAsync(Beatmap); var requestedBeatmap = Beatmap; if (requestedBeatmap.Metrics == null) @@ -213,6 +212,19 @@ namespace osu.Game.Screens.Select displayMetrics(requestedBeatmap.Metrics, false); } + private void loadDetailsAsync(BeatmapInfo beatmap) + { + if (description == null || source == null || tags == null) + throw new InvalidOperationException($@"Requires all {nameof(MetadataSection)} elements to be non-null."); + + Schedule(() => + { + description.Text = Beatmap?.Version; + source.Text = Beatmap?.Metadata?.Source; + tags.Text = Beatmap?.Metadata?.Tags; + }); + } + private void displayMetrics(BeatmapMetrics metrics, bool failOnMissing = true) { var hasRatings = metrics?.Ratings?.Any() ?? false; @@ -258,9 +270,7 @@ namespace osu.Game.Screens.Select private void clearStats() { - description.Text = null; - source.Text = null; - tags.Text = null; + loadDetailsAsync(null); advanced.Beatmap = new BeatmapInfo { StarDifficulty = 0, From e3a230320ae1aa3b1f2279b539d0e97b416d48fc Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Thu, 23 Nov 2017 19:46:58 +0100 Subject: [PATCH 022/235] compare metdata and remove duplicate from beatmap to prevent redundant storage --- osu.Game/Beatmaps/BeatmapManager.cs | 4 ++++ osu.Game/Beatmaps/BeatmapMetadata.cs | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index eb10b23c6f..ca715b8d1e 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -515,6 +515,10 @@ namespace osu.Game.Beatmaps if (existing == null) { + // Exclude beatmap-metadata if it's equal to beatmapset-metadata + if (metadata.Equals(beatmap.Metadata)) + beatmap.BeatmapInfo.Metadata = null; + RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID); // TODO: this should be done in a better place once we actually need to dynamically update it. diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs index 89f9ebf47a..2cd8e2669f 100644 --- a/osu.Game/Beatmaps/BeatmapMetadata.cs +++ b/osu.Game/Beatmaps/BeatmapMetadata.cs @@ -66,5 +66,23 @@ namespace osu.Game.Beatmaps Source, Tags }.Where(s => !string.IsNullOrEmpty(s)).ToArray(); + + public override bool Equals(object other) + { + var otherMetadata = other as BeatmapMetadata; + if (otherMetadata == null) return false; + + return (onlineBeatmapSetID?.Equals(otherMetadata.onlineBeatmapSetID) ?? false) + && (Title?.Equals(otherMetadata.Title) ?? false) + && (TitleUnicode?.Equals(otherMetadata.TitleUnicode) ?? false) + && (Artist?.Equals(otherMetadata.Artist) ?? false) + && (ArtistUnicode?.Equals(otherMetadata.ArtistUnicode) ?? false) + && (AuthorString?.Equals(otherMetadata.AuthorString) ?? false) + && (Source?.Equals(otherMetadata.Source) ?? false) + && (Tags?.Equals(otherMetadata.Tags) ?? false) + && PreviewTime.Equals(otherMetadata.PreviewTime) + && (AudioFile?.Equals(otherMetadata.AudioFile) ?? false) + && (BackgroundFile?.Equals(otherMetadata.BackgroundFile) ?? false); + } } } From 7ff39d62a1e52170c43026bd98cc94554878493d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Nov 2017 12:46:15 +0900 Subject: [PATCH 023/235] Fix SpriteIcon potentially not updating texture during a load race condition Better fix for #1577. --- osu.Game/Graphics/SpriteIcon.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/SpriteIcon.cs b/osu.Game/Graphics/SpriteIcon.cs index ca108bfa7a..e752b1d91a 100644 --- a/osu.Game/Graphics/SpriteIcon.cs +++ b/osu.Game/Graphics/SpriteIcon.cs @@ -57,19 +57,31 @@ namespace osu.Game.Graphics private void load(FontStore store) { this.store = store; - updateTexture(); } + protected override void LoadComplete() + { + base.LoadComplete(); + updateTexture(); + } + + private FontAwesome loadedIcon; private void updateTexture() { - var texture = store?.Get(((char)icon).ToString()); + var loadableIcon = icon; + + if (loadableIcon == loadedIcon) return; + + var texture = store?.Get(((char)loadableIcon).ToString()); spriteMain.Texture = texture; spriteShadow.Texture = texture; if (Size == Vector2.Zero) Size = new Vector2(texture?.DisplayWidth ?? 0, texture?.DisplayHeight ?? 0); + + loadedIcon = loadableIcon; } public override bool Invalidate(Invalidation invalidation = Invalidation.All, Drawable source = null, bool shallPropagate = true) From a4b67b2559741f61b57859304ffff02372843c66 Mon Sep 17 00:00:00 2001 From: Dan Balasescu <1329837+smoogipoo@users.noreply.github.com> Date: Fri, 24 Nov 2017 12:56:52 +0900 Subject: [PATCH 024/235] Fix CI --- osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs b/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs index 394036df39..e74c12fa5d 100644 --- a/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Taiko/TaikoDifficultyCalculator.cs @@ -5,7 +5,6 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Taiko.Beatmaps; using osu.Game.Rulesets.Taiko.Objects; using System.Collections.Generic; -using System.Globalization; using System; namespace osu.Game.Rulesets.Taiko @@ -54,7 +53,7 @@ namespace osu.Game.Rulesets.Taiko if (categoryDifficulty != null) { categoryDifficulty.Add("Strain", starRating); - categoryDifficulty.Add("Hit window 300", (35 /*HitObjectManager.HitWindow300*/ / TimeRate)); + categoryDifficulty.Add("Hit window 300", 35 /*HitObjectManager.HitWindow300*/ / TimeRate); } return starRating; From 09facdc83810ecd90275a8a46b6582576b2e7b7e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Nov 2017 18:34:20 +0900 Subject: [PATCH 025/235] Add a setting to toggle showing converted beatmaps --- osu.Game/Configuration/OsuConfigManager.cs | 4 +++- .../Sections/Gameplay/SongSelectSettings.cs | 7 ++++++- osu.Game/Screens/Select/FilterControl.cs | 15 ++++++++++++--- osu.Game/Screens/Select/FilterCriteria.cs | 3 ++- 4 files changed, 23 insertions(+), 6 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index b3e99fce06..c087a5afb7 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -16,6 +16,7 @@ namespace osu.Game.Configuration Set(OsuSetting.Ruleset, 0, 0, int.MaxValue); Set(OsuSetting.BeatmapDetailTab, BeatmapDetailTab.Details); + Set(OsuSetting.ShowConvertedBeatmaps, true); Set(OsuSetting.DisplayStarsMinimum, 0.0, 0, 10, 0.1); Set(OsuSetting.DisplayStarsMaximum, 10.0, 0, 10, 0.1); @@ -112,6 +113,7 @@ namespace osu.Game.Configuration SnakingOutSliders, ShowFpsDisplay, ChatDisplayHeight, - Version + Version, + ShowConvertedBeatmaps } } diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs index 07a8e7464a..9875ee8004 100644 --- a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs @@ -17,6 +17,11 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay { Children = new Drawable[] { + new SettingsCheckbox + { + LabelText = "Show converted beatmaps", + Bindable = config.GetBindable(OsuSetting.ShowConvertedBeatmaps), + }, new SettingsSlider { LabelText = "Display beatmaps from", @@ -33,7 +38,7 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay { LabelText = "Random beatmap selection", Bindable = config.GetBindable(OsuSetting.SelectionRandomType), - }, + } }; } diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index e83613125b..1b86cec613 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -15,6 +15,7 @@ using osu.Game.Screens.Select.Filter; using Container = osu.Framework.Graphics.Containers.Container; using osu.Framework.Input; using osu.Framework.Graphics.Shapes; +using osu.Game.Configuration; using osu.Game.Rulesets; namespace osu.Game.Screens.Select @@ -60,6 +61,7 @@ namespace osu.Game.Screens.Select Group = group, Sort = sort, SearchText = searchTextBox.Text, + AllowConvertedBeatmaps = showConverted, Ruleset = ruleset }; @@ -163,17 +165,24 @@ namespace osu.Game.Screens.Select private readonly Bindable ruleset = new Bindable(); + private Bindable showConverted; + [BackgroundDependencyLoader(permitNulls: true)] - private void load(OsuColour colours, OsuGame osu) + private void load(OsuColour colours, OsuGame osu, OsuConfigManager config) { sortTabs.AccentColour = colours.GreenLight; + showConverted = config.GetBindable(OsuSetting.ShowConvertedBeatmaps); + showConverted.ValueChanged += val => updateCriteria(); + if (osu != null) ruleset.BindTo(osu.Ruleset); - ruleset.ValueChanged += val => FilterChanged?.Invoke(CreateCriteria()); + ruleset.ValueChanged += val => updateCriteria(); ruleset.TriggerChange(); } + private void updateCriteria() => FilterChanged?.Invoke(CreateCriteria()); + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) => true; protected override bool OnMouseMove(InputState state) => true; @@ -182,4 +191,4 @@ namespace osu.Game.Screens.Select protected override bool OnDragStart(InputState state) => true; } -} \ No newline at end of file +} diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index 6c1fb1703d..c1355bfa63 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -16,6 +16,7 @@ namespace osu.Game.Screens.Select public SortMode Sort; public string SearchText; public RulesetInfo Ruleset; + public bool AllowConvertedBeatmaps; public void Filter(List groups) { @@ -23,7 +24,7 @@ namespace osu.Game.Screens.Select { var set = g.BeatmapSet; - bool hasCurrentMode = set.Beatmaps.Any(bm => bm.RulesetID == (Ruleset?.ID ?? 0)); + bool hasCurrentMode = AllowConvertedBeatmaps || set.Beatmaps.Any(bm => bm.RulesetID == (Ruleset?.ID ?? 0)); bool match = hasCurrentMode; From 21d5d107381f28a8efb2642ba0cd88bf05459ce3 Mon Sep 17 00:00:00 2001 From: jorolf Date: Fri, 24 Nov 2017 22:48:56 +0100 Subject: [PATCH 026/235] replace BeatmapResponse with BeatmapInfo --- .../Visual/TestCaseHistoricalSection.cs | 2 +- osu.Game/Beatmaps/BeatmapInfo.cs | 1 + .../GetUserMostPlayedBeatmapsRequest.cs | 32 +++---------------- 3 files changed, 7 insertions(+), 28 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs b/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs index e67f389969..d75f9116ce 100644 --- a/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs +++ b/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs @@ -17,7 +17,7 @@ namespace osu.Game.Tests.Visual { public override string Description => "User's History"; - public override IReadOnlyList RequiredTypes => new [] { typeof(HistoricalSection), typeof(MostPlayedBeatmapDrawable)}; + public override IReadOnlyList RequiredTypes => new [] { typeof(HistoricalSection), typeof(PaginatedMostPlayedBeatmapContainer), typeof(MostPlayedBeatmapDrawable) }; public TestCaseHistoricalSection() diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 022d64db03..f3a9694982 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -115,6 +115,7 @@ namespace osu.Game.Beatmaps // Metadata public string Version { get; set; } + [JsonProperty("difficulty_rating")] public double StarDifficulty { get; set; } public bool Equals(BeatmapInfo other) diff --git a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs index cdc156be05..2d08f09d20 100644 --- a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using System.Linq; using Newtonsoft.Json; using osu.Game.Beatmaps; using osu.Game.Rulesets; @@ -28,7 +27,7 @@ namespace osu.Game.Online.API.Requests public int PlayCount; [JsonProperty] - private BeatmapResponse beatmap; + private BeatmapInfo beatmap; [JsonProperty] private GetBeatmapSetsResponse beatmapSet; @@ -36,31 +35,10 @@ namespace osu.Game.Online.API.Requests public BeatmapInfo GetBeatmapInfo(RulesetStore rulesets) { BeatmapSetInfo setInfo = beatmapSet.ToBeatmapSet(rulesets); - return new BeatmapInfo - { - OnlineBeatmapID = beatmap.Id, - OnlineBeatmapSetID = setInfo.OnlineBeatmapSetID, - Ruleset = rulesets.AvailableRulesets.FirstOrDefault(ruleset => ruleset.Name.Equals(beatmap.Mode)), - StarDifficulty = beatmap.DifficultyRating, - Version = beatmap.Version, - Metadata = setInfo.Metadata, - BeatmapSet = setInfo, - }; - } - - private class BeatmapResponse - { - [JsonProperty] - public int Id; - - [JsonProperty] - public string Mode; - - [JsonProperty("difficulty_rating")] - public double DifficultyRating; - - [JsonProperty] - public string Version; + beatmap.BeatmapSet = setInfo; + beatmap.OnlineBeatmapSetID = setInfo.OnlineBeatmapSetID; + beatmap.Metadata = setInfo.Metadata; + return beatmap; } } } From 6b3347d6acfabc2df1dd0444ca97a31637c8d214 Mon Sep 17 00:00:00 2001 From: jorolf Date: Fri, 24 Nov 2017 22:59:21 +0100 Subject: [PATCH 027/235] remove description --- osu.Game.Tests/Visual/TestCaseHistoricalSection.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs b/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs index d75f9116ce..37bb935787 100644 --- a/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs +++ b/osu.Game.Tests/Visual/TestCaseHistoricalSection.cs @@ -15,8 +15,6 @@ namespace osu.Game.Tests.Visual { internal class TestCaseHistoricalSection : OsuTestCase { - public override string Description => "User's History"; - public override IReadOnlyList RequiredTypes => new [] { typeof(HistoricalSection), typeof(PaginatedMostPlayedBeatmapContainer), typeof(MostPlayedBeatmapDrawable) }; From d955229ee5cff6ce146e1ce2184e90ea429fb602 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sat, 25 Nov 2017 21:29:23 +0800 Subject: [PATCH 028/235] Handle user rating metrics when no maps by using more linq. --- osu.Game/Screens/Select/Details/UserRatings.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/Details/UserRatings.cs b/osu.Game/Screens/Select/Details/UserRatings.cs index 997e0baec3..19bcad367e 100644 --- a/osu.Game/Screens/Select/Details/UserRatings.cs +++ b/osu.Game/Screens/Select/Details/UserRatings.cs @@ -31,15 +31,15 @@ namespace osu.Game.Screens.Select.Details const int rating_range = 10; - var ratings = Metrics.Ratings.ToList().GetRange(1, rating_range); // adjust for API returning weird empty data at 0. + var ratings = Metrics.Ratings.Skip(1).Take(rating_range); // adjust for API returning weird empty data at 0. - var negativeCount = ratings.GetRange(0, rating_range / 2).Sum(); + var negativeCount = ratings.Take(rating_range / 2).Sum(); var totalCount = ratings.Sum(); negativeRatings.Text = negativeCount.ToString(); positiveRatings.Text = (totalCount - negativeCount).ToString(); ratingsBar.Length = totalCount == 0 ? 0 : (float)negativeCount / totalCount; - graph.Values = ratings.GetRange(0, rating_range).Select(r => (float)r); + graph.Values = ratings.Take(rating_range).Select(r => (float)r); } } From f6a33b3ea2c4a78c899303ef51911bf507937090 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 25 Nov 2017 14:45:57 +0100 Subject: [PATCH 029/235] fix osu!direct download reloading all panels This fixes the recreation of all panels when a download completes. Also fixes NullReference when you download without ever opening the details of one Set. --- osu.Game/Overlays/BeatmapSet/Header.cs | 2 +- osu.Game/Overlays/DirectOverlay.cs | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs index af59b21713..4135aef268 100644 --- a/osu.Game/Overlays/BeatmapSet/Header.cs +++ b/osu.Game/Overlays/BeatmapSet/Header.cs @@ -234,7 +234,7 @@ namespace osu.Game.Overlays.BeatmapSet private void handleBeatmapAdd(BeatmapSetInfo beatmap) { - if (beatmap.OnlineBeatmapSetID == BeatmapSet.OnlineBeatmapSetID) + if (beatmap.OnlineBeatmapSetID == BeatmapSet?.OnlineBeatmapSetID) downloadButtonsContainer.FadeOut(transition_duration); } diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index 6f7fabb910..11dd9dd976 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -65,8 +65,6 @@ namespace osu.Game.Overlays } ResultAmounts = new ResultCounts(distinctCount(artists), distinctCount(songs), distinctCount(tags)); - - recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value); } } @@ -282,7 +280,11 @@ namespace osu.Game.Overlays var sets = response.Select(r => r.ToBeatmapSet(rulesets)).Where(b => !presentOnlineIds.Contains(b.OnlineBeatmapSetID)).ToList(); // may not need scheduling; loads async internally. - Schedule(() => BeatmapSets = sets); + Schedule(() => + { + BeatmapSets = sets; + recreatePanels(Filter.DisplayStyleControl.DisplayStyle.Value); + }); }); }; From 5da1466e284b584c636a5cf6f2c00c8280d04e3b Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 25 Nov 2017 15:05:59 +0100 Subject: [PATCH 030/235] requested changes use IEquatable instead of overriding Equals and `==` operator for primitive types. --- osu.Game/Beatmaps/BeatmapMetadata.cs | 31 ++++++++++++++-------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs index 2cd8e2669f..22a64820bc 100644 --- a/osu.Game/Beatmaps/BeatmapMetadata.cs +++ b/osu.Game/Beatmaps/BeatmapMetadata.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; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; @@ -9,7 +10,7 @@ using osu.Game.Users; namespace osu.Game.Beatmaps { - public class BeatmapMetadata + public class BeatmapMetadata : IEquatable { [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int ID { get; set; } @@ -67,22 +68,22 @@ namespace osu.Game.Beatmaps Tags }.Where(s => !string.IsNullOrEmpty(s)).ToArray(); - public override bool Equals(object other) + public bool Equals(BeatmapMetadata other) { - var otherMetadata = other as BeatmapMetadata; - if (otherMetadata == null) return false; + if (other == null) + return false; - return (onlineBeatmapSetID?.Equals(otherMetadata.onlineBeatmapSetID) ?? false) - && (Title?.Equals(otherMetadata.Title) ?? false) - && (TitleUnicode?.Equals(otherMetadata.TitleUnicode) ?? false) - && (Artist?.Equals(otherMetadata.Artist) ?? false) - && (ArtistUnicode?.Equals(otherMetadata.ArtistUnicode) ?? false) - && (AuthorString?.Equals(otherMetadata.AuthorString) ?? false) - && (Source?.Equals(otherMetadata.Source) ?? false) - && (Tags?.Equals(otherMetadata.Tags) ?? false) - && PreviewTime.Equals(otherMetadata.PreviewTime) - && (AudioFile?.Equals(otherMetadata.AudioFile) ?? false) - && (BackgroundFile?.Equals(otherMetadata.BackgroundFile) ?? false); + return onlineBeatmapSetID == other.onlineBeatmapSetID + && (Title?.Equals(other.Title) ?? false) + && (TitleUnicode?.Equals(other.TitleUnicode) ?? false) + && (Artist?.Equals(other.Artist) ?? false) + && (ArtistUnicode?.Equals(other.ArtistUnicode) ?? false) + && (AuthorString?.Equals(other.AuthorString) ?? false) + && (Source?.Equals(other.Source) ?? false) + && (Tags?.Equals(other.Tags) ?? false) + && PreviewTime == other.PreviewTime + && (AudioFile?.Equals(other.AudioFile) ?? false) + && (BackgroundFile?.Equals(other.BackgroundFile) ?? false); } } } From 7be55df7904df2c8d75367577afa6e34f7cf76d9 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 25 Nov 2017 19:09:05 +0100 Subject: [PATCH 031/235] fix being stuck in gameplay if exit is pressed too fast The reliance on `pauseOverlay.Alpha == 1` created a race condition that, when you pressed Exit while the pauseoverlay is still fading in, could get you stuck in gameplay. The game wants to show the pause overlay but also thinks it's already paused and returns early. --- osu.Game/Screens/Play/PauseContainer.cs | 2 -- osu.Game/Screens/Play/Player.cs | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/PauseContainer.cs b/osu.Game/Screens/Play/PauseContainer.cs index eed5cd1c20..5f5eeb63a0 100644 --- a/osu.Game/Screens/Play/PauseContainer.cs +++ b/osu.Game/Screens/Play/PauseContainer.cs @@ -22,8 +22,6 @@ namespace osu.Game.Screens.Play { public bool IsPaused { get; private set; } - public bool AllowExit => IsPaused && pauseOverlay.Alpha == 1; - public Func CheckCanPause; private const double pause_cooldown = 1000; diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a19305778c..55eee5ce7d 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -357,7 +357,7 @@ namespace osu.Game.Screens.Play protected override bool OnExiting(Screen next) { - if (!AllowPause || HasFailed || !ValidForResume || pauseContainer?.AllowExit != false || RulesetContainer?.HasReplayLoaded != false) + if (!AllowPause || HasFailed || !ValidForResume || pauseContainer?.IsPaused != false || RulesetContainer?.HasReplayLoaded != false) { // In the case of replays, we may have changed the playback rate. applyRateFromMods(); From 69e388dd52a5ab0d25ce62b7397caf14811c51bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Nov 2017 05:08:20 +0900 Subject: [PATCH 032/235] Adjust background blur rate --- osu.Game/Screens/Play/Player.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a19305778c..e27b76e458 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -310,7 +310,7 @@ namespace osu.Game.Screens.Play if (!loadedSuccessfully) return; - (Background as BackgroundScreenBeatmap)?.BlurTo(Vector2.Zero, 1500, Easing.OutQuint); + (Background as BackgroundScreenBeatmap)?.BlurTo(Vector2.Zero, 1000, Easing.OutQuint); dimLevel.ValueChanged += dimLevel_ValueChanged; showStoryboard.ValueChanged += showStoryboard_ValueChanged; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 24a7b3db90..68437180ac 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -413,7 +413,7 @@ namespace osu.Game.Screens.Select if (backgroundModeBeatmap != null) { backgroundModeBeatmap.Beatmap = beatmap; - backgroundModeBeatmap.BlurTo(background_blur, 1000); + backgroundModeBeatmap.BlurTo(background_blur, 750, Easing.OutQuint); backgroundModeBeatmap.FadeTo(1, 250); } From df16a019ed13e07e1de9ed0c480be55a90717e8b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Nov 2017 17:07:35 +0900 Subject: [PATCH 033/235] Add a global reduction of audio track volume Music is overpowering compared to our current game samples. We will need to do further adjustments on this, but for now let's reduce the track volume globally. --- osu.Game/OsuGameBase.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 50639e3427..8eaa20f781 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.Reflection; using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Configuration; using osu.Framework.Development; using osu.Framework.Graphics; @@ -137,6 +138,10 @@ namespace osu.Game Beatmap = new NonNullableBindable(defaultBeatmap); BeatmapManager.DefaultBeatmap = defaultBeatmap; + // tracks play so loud our samples can't keep up. + // this adds a global reduction of track volume for the time being. + Audio.Track.AddAdjustment(AdjustableProperty.Volume, new BindableDouble(0.8)); + Beatmap.ValueChanged += b => { var trackLoaded = lastBeatmap?.TrackLoaded ?? false; From 7cdb8305304969e7d1ae741400428e71c24a5b8f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 25 Nov 2017 23:05:28 +0900 Subject: [PATCH 034/235] Fix broken app.config default --- osu.Desktop.Deploy/App.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Desktop.Deploy/App.config b/osu.Desktop.Deploy/App.config index 2fae7a5e1c..2fbea810f6 100644 --- a/osu.Desktop.Deploy/App.config +++ b/osu.Desktop.Deploy/App.config @@ -13,7 +13,7 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste - + From 22075ac7c9cf2b1f46f133a983eaf5a4bde06d72 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 25 Nov 2017 23:05:46 +0900 Subject: [PATCH 035/235] Ensure deploy script can work without a github token --- osu.Desktop.Deploy/Program.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Desktop.Deploy/Program.cs b/osu.Desktop.Deploy/Program.cs index 54fb50d0f8..e90fb1e567 100644 --- a/osu.Desktop.Deploy/Program.cs +++ b/osu.Desktop.Deploy/Program.cs @@ -145,6 +145,8 @@ namespace osu.Desktop.Deploy /// private static void checkReleaseFiles() { + if (!canGitHub) return; + var releaseLines = getReleaseLines(); //ensure we have all files necessary @@ -157,6 +159,8 @@ namespace osu.Desktop.Deploy private static void pruneReleases() { + if (!canGitHub) return; + write("Pruning RELEASES..."); var releaseLines = getReleaseLines().ToList(); @@ -190,7 +194,7 @@ namespace osu.Desktop.Deploy private static void uploadBuild(string version) { - if (string.IsNullOrEmpty(GitHubAccessToken) || string.IsNullOrEmpty(codeSigningCertPath)) + if (!canGitHub || string.IsNullOrEmpty(CodeSigningCertificate)) return; write("Publishing to GitHub..."); @@ -228,8 +232,12 @@ namespace osu.Desktop.Deploy private static void openGitHubReleasePage() => Process.Start(GitHubReleasePage); + private static bool canGitHub => !string.IsNullOrEmpty(GitHubAccessToken); + private static void checkGitHubReleases() { + if (!canGitHub) return; + write("Checking GitHub releases..."); var req = new JsonWebRequest>($"{GitHubApiEndpoint}"); req.AuthenticatedBlockingPerform(); From 4067b6129b8e8b557e478a8a20f280f63299e1c8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Nov 2017 04:16:36 +0900 Subject: [PATCH 036/235] Add visual highlighting on song select panel hover Prerequisite for adding hover sound effects. Didn't feel right without this change. --- osu.Game/Beatmaps/Drawables/Panel.cs | 52 ++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/Panel.cs b/osu.Game/Beatmaps/Drawables/Panel.cs index d6ed306b39..5fbc858b00 100644 --- a/osu.Game/Beatmaps/Drawables/Panel.cs +++ b/osu.Game/Beatmaps/Drawables/Panel.cs @@ -3,12 +3,15 @@ using System; using osu.Framework; +using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; using OpenTK; using OpenTK.Graphics; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; namespace osu.Game.Beatmaps.Drawables { @@ -22,6 +25,10 @@ namespace osu.Game.Beatmaps.Drawables private readonly Container nestedContainer; + private readonly Container borderContainer; + + private readonly Box hoverLayer; + protected override Container Content => nestedContainer; protected Panel() @@ -29,20 +36,53 @@ namespace osu.Game.Beatmaps.Drawables Height = MAX_HEIGHT; RelativeSizeAxes = Axes.X; - AddInternal(nestedContainer = new Container + AddInternal(borderContainer = new Container { RelativeSizeAxes = Axes.Both, Masking = true, CornerRadius = 10, BorderColour = new Color4(221, 255, 255, 255), + Children = new Drawable[] + { + nestedContainer = new Container + { + RelativeSizeAxes = Axes.Both, + }, + hoverLayer = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + Blending = new BlendingParameters { Mode = BlendingMode.Additive }, + }, + } }); Alpha = 0; } + + protected override bool OnHover(InputState state) + { + hoverLayer.FadeIn(100, Easing.OutQuint); + + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + hoverLayer.FadeOut(1000, Easing.OutQuint); + base.OnHoverLost(state); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + hoverLayer.Colour = colours.Blue.Opacity(0.1f); + } + public void SetMultiplicativeAlpha(float alpha) { - nestedContainer.Alpha = alpha; + borderContainer.Alpha = alpha; } protected override void LoadComplete() @@ -94,8 +134,8 @@ namespace osu.Game.Beatmaps.Drawables protected virtual void Selected() { - nestedContainer.BorderThickness = 2.5f; - nestedContainer.EdgeEffect = new EdgeEffectParameters + borderContainer.BorderThickness = 2.5f; + borderContainer.EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow, Colour = new Color4(130, 204, 255, 150), @@ -106,8 +146,8 @@ namespace osu.Game.Beatmaps.Drawables protected virtual void Deselected() { - nestedContainer.BorderThickness = 0; - nestedContainer.EdgeEffect = new EdgeEffectParameters + borderContainer.BorderThickness = 0; + borderContainer.EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, Offset = new Vector2(1), From 07d6a700286e489685cbe389993d237309dfbfe1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 25 Nov 2017 23:11:18 +0900 Subject: [PATCH 037/235] New sound effects with better names --- .../Graphics/Containers/OsuClickableContainer.cs | 2 +- .../Containers/OsuFocusedOverlayContainer.cs | 4 ++-- osu.Game/Graphics/UserInterface/OsuButton.cs | 2 +- osu.Game/Graphics/UserInterface/OsuMenu.cs | 2 +- osu.Game/Screens/Menu/Button.cs | 2 +- osu.Game/Screens/Menu/ButtonSystem.cs | 14 +++++++------- osu.Game/Screens/Menu/OsuLogo.cs | 4 ++-- osu.Game/Screens/OsuScreen.cs | 2 +- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuClickableContainer.cs b/osu.Game/Graphics/Containers/OsuClickableContainer.cs index 11c049ed3e..ce3780cf78 100644 --- a/osu.Game/Graphics/Containers/OsuClickableContainer.cs +++ b/osu.Game/Graphics/Containers/OsuClickableContainer.cs @@ -17,7 +17,7 @@ namespace osu.Game.Graphics.Containers private void load(AudioManager audio) { SampleHover = audio.Sample.Get(@"UI/generic-hover"); - SampleClick = audio.Sample.Get(@"UI/generic-click"); + SampleClick = audio.Sample.Get(@"UI/generic-select"); } protected override bool OnHover(InputState state) diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index 4ea4f4cdc3..c788df3066 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -16,8 +16,8 @@ namespace osu.Game.Graphics.Containers [BackgroundDependencyLoader] private void load(AudioManager audio) { - samplePopIn = audio.Sample.Get(@"UI/melodic-5"); - samplePopOut = audio.Sample.Get(@"UI/melodic-4"); + samplePopIn = audio.Sample.Get(@"UI/overlay-pop-in"); + samplePopOut = audio.Sample.Get(@"UI/overlay-pop-out"); StateChanged += onStateChanged; } diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs index ccc23e3ff6..5bbfab97e5 100644 --- a/osu.Game/Graphics/UserInterface/OsuButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuButton.cs @@ -70,7 +70,7 @@ namespace osu.Game.Graphics.UserInterface }, }); - sampleClick = audio.Sample.Get(@"UI/generic-click"); + sampleClick = audio.Sample.Get(@"UI/generic-select"); sampleHover = audio.Sample.Get(@"UI/generic-hover"); Enabled.ValueChanged += enabled_ValueChanged; diff --git a/osu.Game/Graphics/UserInterface/OsuMenu.cs b/osu.Game/Graphics/UserInterface/OsuMenu.cs index 3fd5481152..7f16d73613 100644 --- a/osu.Game/Graphics/UserInterface/OsuMenu.cs +++ b/osu.Game/Graphics/UserInterface/OsuMenu.cs @@ -72,7 +72,7 @@ namespace osu.Game.Graphics.UserInterface private void load(AudioManager audio) { sampleHover = audio.Sample.Get(@"UI/generic-hover"); - sampleClick = audio.Sample.Get(@"UI/generic-click"); + sampleClick = audio.Sample.Get(@"UI/generic-select"); BackgroundColour = Color4.Transparent; BackgroundColourHover = OsuColour.FromHex(@"172023"); diff --git a/osu.Game/Screens/Menu/Button.cs b/osu.Game/Screens/Menu/Button.cs index ccd61643ce..5e55166c19 100644 --- a/osu.Game/Screens/Menu/Button.cs +++ b/osu.Game/Screens/Menu/Button.cs @@ -174,7 +174,7 @@ namespace osu.Game.Screens.Menu [BackgroundDependencyLoader] private void load(AudioManager audio) { - sampleHover = audio.Sample.Get(@"Menu/hover"); + sampleHover = audio.Sample.Get(@"Menu/button-hover"); if (!string.IsNullOrEmpty(sampleName)) sampleClick = audio.Sample.Get($@"Menu/{sampleName}"); } diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 5a4a5f07b5..af5df2f8ec 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -117,13 +117,13 @@ namespace osu.Game.Screens.Menu }, }; - buttonsPlay.Add(new Button(@"solo", @"select-6", FontAwesome.fa_user, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P)); - buttonsPlay.Add(new Button(@"multi", @"select-5", FontAwesome.fa_users, new Color4(94, 63, 186, 255), () => OnMulti?.Invoke(), 0, Key.M)); - buttonsPlay.Add(new Button(@"chart", @"select-5", FontAwesome.fa_osu_charts, new Color4(80, 53, 160, 255), () => OnChart?.Invoke())); + buttonsPlay.Add(new Button(@"solo", @"button-solo-select", FontAwesome.fa_user, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P)); + buttonsPlay.Add(new Button(@"multi", @"button-generic-select", FontAwesome.fa_users, new Color4(94, 63, 186, 255), () => OnMulti?.Invoke(), 0, Key.M)); + buttonsPlay.Add(new Button(@"chart", @"button-generic-select", FontAwesome.fa_osu_charts, new Color4(80, 53, 160, 255), () => OnChart?.Invoke())); - buttonsTopLevel.Add(new Button(@"play", @"select-1", FontAwesome.fa_osu_logo, new Color4(102, 68, 204, 255), onPlay, WEDGE_WIDTH, Key.P)); - buttonsTopLevel.Add(new Button(@"osu!editor", @"select-5", FontAwesome.fa_osu_edit_o, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E)); - buttonsTopLevel.Add(new Button(@"osu!direct", string.Empty, FontAwesome.fa_osu_chevron_down_o, new Color4(165, 204, 0, 255), () => OnDirect?.Invoke(), 0, Key.D)); + buttonsTopLevel.Add(new Button(@"play", @"button-play-select", FontAwesome.fa_osu_logo, new Color4(102, 68, 204, 255), onPlay, WEDGE_WIDTH, Key.P)); + buttonsTopLevel.Add(new Button(@"osu!editor", @"button-generic-select", FontAwesome.fa_osu_edit_o, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E)); + buttonsTopLevel.Add(new Button(@"osu!direct", @"button-direct-select", FontAwesome.fa_osu_chevron_down_o, new Color4(165, 204, 0, 255), () => OnDirect?.Invoke(), 0, Key.D)); buttonsTopLevel.Add(new Button(@"exit", string.Empty, FontAwesome.fa_osu_cross_o, new Color4(238, 51, 153, 255), onExit, 0, Key.Q)); buttonFlow.AddRange(buttonsPlay); @@ -134,7 +134,7 @@ namespace osu.Game.Screens.Menu private void load(AudioManager audio, OsuGame game = null) { toolbar = game?.Toolbar; - sampleBack = audio.Sample.Get(@"Menu/select-4"); + sampleBack = audio.Sample.Get(@"Menu/button-back-select"); } protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index 252f2d37b5..2679368099 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -248,8 +248,8 @@ namespace osu.Game.Screens.Menu [BackgroundDependencyLoader] private void load(TextureStore textures, AudioManager audio) { - sampleClick = audio.Sample.Get(@"Menu/select-2"); - sampleBeat = audio.Sample.Get(@"Menu/heartbeat"); + sampleClick = audio.Sample.Get(@"Menu/osu-logo-select"); + sampleBeat = audio.Sample.Get(@"Menu/osu-logo-heartbeat"); logo.Texture = textures.Get(@"Menu/logo"); ripple.Texture = textures.Get(@"Menu/logo"); diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index f5ff9ea036..76ee4a607e 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -70,7 +70,7 @@ namespace osu.Game.Screens if (osuGame != null) Ruleset.BindTo(osuGame.Ruleset); - sampleExit = audio.Sample.Get(@"UI/melodic-1"); + sampleExit = audio.Sample.Get(@"UI/screen-back"); } protected override void OnResuming(Screen last) From ae48b858276d75c0f8b69e784505a0a85598a649 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 25 Nov 2017 23:29:08 +0900 Subject: [PATCH 038/235] Don't play logo select sample when actions are playing their own --- osu.Game/Screens/Menu/ButtonSystem.cs | 10 ++++++---- osu.Game/Screens/Menu/OsuLogo.cs | 10 ++++++---- osu.Game/Screens/Select/SongSelect.cs | 6 +++++- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index af5df2f8ec..ce7856c5a9 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -180,19 +180,21 @@ namespace osu.Game.Screens.Menu State = MenuState.TopLevel; } - private void onOsuLogo() + private bool onOsuLogo() { switch (state) { + default: + return true; case MenuState.Initial: State = MenuState.TopLevel; - return; + return true; case MenuState.TopLevel: buttonsTopLevel.First().TriggerOnClick(); - return; + return false; case MenuState.Play: buttonsPlay.First().TriggerOnClick(); - return; + return false; } } diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index 2679368099..9ca12702e5 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -48,7 +48,10 @@ namespace osu.Game.Screens.Menu private readonly Triangles triangles; - public Action Action; + /// + /// Return value decides whether the logo should play its own sample for the click action. + /// + public Func Action; public float SizeForFlow => logo == null ? 0 : logo.DrawSize.X * logo.Scale.X * logoBounceContainer.Scale.X * logoHoverContainer.Scale.X * 0.74f; @@ -354,13 +357,12 @@ namespace osu.Game.Screens.Menu { if (!interactive) return false; - sampleClick.Play(); + if (Action?.Invoke() ?? true) + sampleClick.Play(); flashLayer.ClearTransforms(); flashLayer.Alpha = 0.4f; flashLayer.FadeOut(1500, Easing.OutExpo); - - Action?.Invoke(); return true; } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 24a7b3db90..fcf459182f 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -332,7 +332,11 @@ namespace osu.Game.Screens.Select logo.FadeIn(logo_transition, Easing.OutQuint); logo.ScaleTo(0.4f, logo_transition, Easing.OutQuint); - logo.Action = () => carouselRaisedStart(); + logo.Action = () => + { + carouselRaisedStart(); + return true; + }; } protected override void LogoExiting(OsuLogo logo) From af499df6ddfdb7fdee0fa836a00614d37104f080 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Nov 2017 01:52:52 +0900 Subject: [PATCH 039/235] Make a base OsuButton class to handle default samples --- .../Visual/TestCaseReplaySettingsOverlay.cs | 2 +- osu.Game/Graphics/UserInterface/OsuButton.cs | 103 ++-------------- .../Graphics/UserInterface/TriangleButton.cs | 111 ++++++++++++++++++ .../KeyBinding/KeyBindingsSubsection.cs | 2 +- .../Sections/Maintenance/GeneralSettings.cs | 6 +- osu.Game/Overlays/Settings/SettingsButton.cs | 2 +- osu.Game/Screens/Tournament/Drawings.cs | 8 +- osu.Game/osu.Game.csproj | 3 +- 8 files changed, 131 insertions(+), 106 deletions(-) create mode 100644 osu.Game/Graphics/UserInterface/TriangleButton.cs diff --git a/osu.Game.Tests/Visual/TestCaseReplaySettingsOverlay.cs b/osu.Game.Tests/Visual/TestCaseReplaySettingsOverlay.cs index 22a2d717e4..badb98e6b7 100644 --- a/osu.Game.Tests/Visual/TestCaseReplaySettingsOverlay.cs +++ b/osu.Game.Tests/Visual/TestCaseReplaySettingsOverlay.cs @@ -22,7 +22,7 @@ namespace osu.Game.Tests.Visual Add(container = new ExampleContainer()); - AddStep(@"Add button", () => container.Add(new OsuButton + AddStep(@"Add button", () => container.Add(new TriangleButton { RelativeSizeAxes = Axes.X, Text = @"Button", diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs index 5bbfab97e5..884a3037a5 100644 --- a/osu.Game/Graphics/UserInterface/OsuButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuButton.cs @@ -1,87 +1,22 @@ // 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 OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; -using osu.Framework.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input; -using osu.Game.Graphics.Backgrounds; -using osu.Game.Graphics.Sprites; namespace osu.Game.Graphics.UserInterface { - public class OsuButton : Button, IFilterable + /// + /// A button with added default sound effects. + /// + public class OsuButton : Button { - private Box hover; - private SampleChannel sampleClick; private SampleChannel sampleHover; - protected Triangles Triangles; - - public OsuButton() - { - Height = 40; - } - - protected override SpriteText CreateText() => new OsuSpriteText - { - Depth = -1, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Font = @"Exo2.0-Bold", - }; - - public override bool HandleInput => Action != null; - - [BackgroundDependencyLoader] - private void load(OsuColour colours, AudioManager audio) - { - if (Action == null) - Colour = OsuColour.Gray(0.5f); - - BackgroundColour = colours.BlueDark; - - Content.Masking = true; - Content.CornerRadius = 5; - - AddRange(new Drawable[] - { - Triangles = new Triangles - { - RelativeSizeAxes = Axes.Both, - ColourDark = colours.BlueDarker, - ColourLight = colours.Blue, - }, - hover = new Box - { - RelativeSizeAxes = Axes.Both, - Blending = BlendingMode.Additive, - Colour = Color4.White.Opacity(0.1f), - Alpha = 0, - }, - }); - - sampleClick = audio.Sample.Get(@"UI/generic-select"); - sampleHover = audio.Sample.Get(@"UI/generic-hover"); - - Enabled.ValueChanged += enabled_ValueChanged; - Enabled.TriggerChange(); - } - - private void enabled_ValueChanged(bool enabled) - { - this.FadeColour(enabled ? Color4.White : Color4.Gray, 200, Easing.OutQuint); - } - protected override bool OnClick(InputState state) { sampleClick?.Play(); @@ -91,36 +26,14 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnHover(InputState state) { sampleHover?.Play(); - hover.FadeIn(200); return base.OnHover(state); } - protected override void OnHoverLost(InputState state) + [BackgroundDependencyLoader] + private void load(OsuColour colours, AudioManager audio) { - hover.FadeOut(200); - base.OnHoverLost(state); - } - - protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) - { - Content.ScaleTo(0.9f, 4000, Easing.OutQuint); - return base.OnMouseDown(state, args); - } - - protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) - { - Content.ScaleTo(1, 1000, Easing.OutElastic); - return base.OnMouseUp(state, args); - } - - public IEnumerable FilterTerms => new[] { Text }; - - public bool MatchingFilter - { - set - { - this.FadeTo(value ? 1 : 0); - } + sampleClick = audio.Sample.Get(@"UI/generic-select"); + sampleHover = audio.Sample.Get(@"UI/generic-hover"); } } } diff --git a/osu.Game/Graphics/UserInterface/TriangleButton.cs b/osu.Game/Graphics/UserInterface/TriangleButton.cs new file mode 100644 index 0000000000..33a8a8f99b --- /dev/null +++ b/osu.Game/Graphics/UserInterface/TriangleButton.cs @@ -0,0 +1,111 @@ +// 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 OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input; +using osu.Game.Graphics.Backgrounds; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Graphics.UserInterface +{ + public class TriangleButton : OsuButton, IFilterable + { + private Box hover; + + protected Triangles Triangles; + + public TriangleButton() + { + Height = 40; + } + + protected override SpriteText CreateText() => new OsuSpriteText + { + Depth = -1, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Font = @"Exo2.0-Bold", + }; + + public override bool HandleInput => Action != null; + + [BackgroundDependencyLoader] + private void load(OsuColour colours, AudioManager audio) + { + if (Action == null) + Colour = OsuColour.Gray(0.5f); + + BackgroundColour = colours.BlueDark; + + Content.Masking = true; + Content.CornerRadius = 5; + + AddRange(new Drawable[] + { + Triangles = new Triangles + { + RelativeSizeAxes = Axes.Both, + ColourDark = colours.BlueDarker, + ColourLight = colours.Blue, + }, + hover = new Box + { + RelativeSizeAxes = Axes.Both, + Blending = BlendingMode.Additive, + Colour = Color4.White.Opacity(0.1f), + Alpha = 0, + }, + }); + + Enabled.ValueChanged += enabled_ValueChanged; + Enabled.TriggerChange(); + } + + private void enabled_ValueChanged(bool enabled) + { + this.FadeColour(enabled ? Color4.White : Color4.Gray, 200, Easing.OutQuint); + } + + protected override bool OnHover(InputState state) + { + hover.FadeIn(200); + return base.OnHover(state); + } + + protected override void OnHoverLost(InputState state) + { + hover.FadeOut(200); + base.OnHoverLost(state); + } + + protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) + { + Content.ScaleTo(0.9f, 4000, Easing.OutQuint); + return base.OnMouseDown(state, args); + } + + protected override bool OnMouseUp(InputState state, MouseUpEventArgs args) + { + Content.ScaleTo(1, 1000, Easing.OutElastic); + return base.OnMouseUp(state, args); + } + + public IEnumerable FilterTerms => new[] { Text }; + + public bool MatchingFilter + { + set + { + this.FadeTo(value ? 1 : 0); + } + } + } +} diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs index 30ff0ab026..c670cc0153 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingsSubsection.cs @@ -55,7 +55,7 @@ namespace osu.Game.Overlays.KeyBinding } } - public class ResetButton : OsuButton + public class ResetButton : TriangleButton { [BackgroundDependencyLoader] private void load(OsuColour colours) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs index 4c82a9ae4b..4f4f381ae1 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/GeneralSettings.cs @@ -12,9 +12,9 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { public class GeneralSettings : SettingsSubsection { - private OsuButton importButton; - private OsuButton deleteButton; - private OsuButton restoreButton; + private TriangleButton importButton; + private TriangleButton deleteButton; + private TriangleButton restoreButton; protected override string Header => "General"; diff --git a/osu.Game/Overlays/Settings/SettingsButton.cs b/osu.Game/Overlays/Settings/SettingsButton.cs index 5320cef850..19493f6c70 100644 --- a/osu.Game/Overlays/Settings/SettingsButton.cs +++ b/osu.Game/Overlays/Settings/SettingsButton.cs @@ -6,7 +6,7 @@ using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Settings { - public class SettingsButton : OsuButton + public class SettingsButton : TriangleButton { public SettingsButton() { diff --git a/osu.Game/Screens/Tournament/Drawings.cs b/osu.Game/Screens/Tournament/Drawings.cs index e540782fc1..3e7ab56c99 100644 --- a/osu.Game/Screens/Tournament/Drawings.cs +++ b/osu.Game/Screens/Tournament/Drawings.cs @@ -193,21 +193,21 @@ namespace osu.Game.Screens.Tournament Children = new Drawable[] { - new OsuButton + new TriangleButton { RelativeSizeAxes = Axes.X, Text = "Begin random", Action = teamsContainer.StartScrolling, }, - new OsuButton + new TriangleButton { RelativeSizeAxes = Axes.X, Text = "Stop random", Action = teamsContainer.StopScrolling, }, - new OsuButton + new TriangleButton { RelativeSizeAxes = Axes.X, @@ -232,7 +232,7 @@ namespace osu.Game.Screens.Tournament Children = new Drawable[] { - new OsuButton + new TriangleButton { RelativeSizeAxes = Axes.X, diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 2aefde2916..a26ad963cf 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -271,6 +271,7 @@ + 20171019041408_InitialCreate.cs @@ -366,7 +367,7 @@ - + From 8d7c891882532151187f8b600d9281736cc5bb08 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Nov 2017 01:53:03 +0900 Subject: [PATCH 040/235] Add hover and click sound effects to settings sidebar buttons --- osu.Game/Overlays/Settings/SidebarButton.cs | 28 +++++++++------------ 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/osu.Game/Overlays/Settings/SidebarButton.cs b/osu.Game/Overlays/Settings/SidebarButton.cs index b39c8ab7cf..4b8366f0fc 100644 --- a/osu.Game/Overlays/Settings/SidebarButton.cs +++ b/osu.Game/Overlays/Settings/SidebarButton.cs @@ -12,14 +12,14 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Input; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Settings { - public class SidebarButton : Container + public class SidebarButton : OsuButton { private readonly SpriteIcon drawableIcon; private readonly SpriteText headerText; - private readonly Box backgroundBox; private readonly Box selectionIndicator; private readonly Container text; public Action Action; @@ -61,17 +61,14 @@ namespace osu.Game.Overlays.Settings public SidebarButton() { + BackgroundColour = OsuColour.Gray(60); + Background.Alpha = 0; + Height = Sidebar.DEFAULT_WIDTH; RelativeSizeAxes = Axes.X; - Children = new Drawable[] + + AddRange(new Drawable[] { - backgroundBox = new Box - { - RelativeSizeAxes = Axes.Both, - Blending = BlendingMode.Additive, - Colour = OsuColour.Gray(60), - Alpha = 0, - }, text = new Container { Width = Sidebar.DEFAULT_WIDTH, @@ -101,7 +98,7 @@ namespace osu.Game.Overlays.Settings Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, } - }; + }); } [BackgroundDependencyLoader] @@ -113,20 +110,19 @@ namespace osu.Game.Overlays.Settings protected override bool OnClick(InputState state) { Action?.Invoke(section); - backgroundBox.FlashColour(Color4.White, 400); - return true; + return base.OnClick(state); } protected override bool OnHover(InputState state) { - backgroundBox.FadeTo(0.4f, 200); + Background.FadeTo(0.4f, 200); return base.OnHover(state); } protected override void OnHoverLost(InputState state) { - backgroundBox.FadeTo(0, 200); + Background.FadeTo(0, 200); base.OnHoverLost(state); } } -} \ No newline at end of file +} From 8f57bf2498f628b0b751390714e4554faeb7617b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Nov 2017 02:10:25 +0900 Subject: [PATCH 041/235] Add choices of hover sample sets --- osu.Game/Graphics/UserInterface/OsuButton.cs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs index 884a3037a5..3b2377c06b 100644 --- a/osu.Game/Graphics/UserInterface/OsuButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuButton.cs @@ -1,9 +1,11 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System.ComponentModel; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; +using osu.Framework.Extensions; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input; @@ -17,6 +19,8 @@ namespace osu.Game.Graphics.UserInterface private SampleChannel sampleClick; private SampleChannel sampleHover; + protected HoverSampleSet SampleSet = HoverSampleSet.Normal; + protected override bool OnClick(InputState state) { sampleClick?.Play(); @@ -30,10 +34,20 @@ namespace osu.Game.Graphics.UserInterface } [BackgroundDependencyLoader] - private void load(OsuColour colours, AudioManager audio) + private void load(AudioManager audio) { - sampleClick = audio.Sample.Get(@"UI/generic-select"); - sampleHover = audio.Sample.Get(@"UI/generic-hover"); + sampleClick = audio.Sample.Get($@"UI/generic-select{SampleSet.GetDescription()}"); + sampleHover = audio.Sample.Get($@"UI/generic-hover{SampleSet.GetDescription()}"); + } + + public enum HoverSampleSet + { + [Description("")] + Normal, + [Description("-soft")] + Soft, + [Description("-softer")] + Softer } } } From ac7e373f40252ef301b624418e44f36892197bb0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Nov 2017 02:41:18 +0900 Subject: [PATCH 042/235] Add hover/click sound effects to more stuff everywhere --- .../Containers/OsuClickableContainer.cs | 28 +++------ .../UserInterface/HoverClickSounds.cs | 62 +++++++++++++++++++ osu.Game/Graphics/UserInterface/OsuButton.cs | 39 +----------- .../Graphics/UserInterface/OsuCheckbox.cs | 3 +- .../Graphics/UserInterface/OsuDropdown.cs | 9 ++- .../Graphics/UserInterface/OsuSliderBar.cs | 3 +- .../Graphics/UserInterface/OsuTabControl.cs | 3 +- .../Graphics/UserInterface/PageTabControl.cs | 1 + osu.Game/Overlays/Chat/ChatLine.cs | 2 +- osu.Game/Overlays/Chat/ChatTabControl.cs | 3 +- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 5 +- osu.Game/Users/UserPanel.cs | 3 +- osu.Game/osu.Game.csproj | 1 + 13 files changed, 95 insertions(+), 67 deletions(-) create mode 100644 osu.Game/Graphics/UserInterface/HoverClickSounds.cs diff --git a/osu.Game/Graphics/Containers/OsuClickableContainer.cs b/osu.Game/Graphics/Containers/OsuClickableContainer.cs index ce3780cf78..c9117ed159 100644 --- a/osu.Game/Graphics/Containers/OsuClickableContainer.cs +++ b/osu.Game/Graphics/Containers/OsuClickableContainer.cs @@ -2,34 +2,24 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; using osu.Framework.Graphics.Containers; -using osu.Framework.Input; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Graphics.Containers { public class OsuClickableContainer : ClickableContainer { - protected SampleChannel SampleClick, SampleHover; + private readonly HoverSampleSet sampleSet; + + public OsuClickableContainer(HoverSampleSet sampleSet = HoverSampleSet.Normal) + { + this.sampleSet = sampleSet; + } [BackgroundDependencyLoader] - private void load(AudioManager audio) + private void load() { - SampleHover = audio.Sample.Get(@"UI/generic-hover"); - SampleClick = audio.Sample.Get(@"UI/generic-select"); - } - - protected override bool OnHover(InputState state) - { - SampleHover?.Play(); - return base.OnHover(state); - } - - protected override bool OnClick(InputState state) - { - SampleClick?.Play(); - return base.OnClick(state); + AddInternal(new HoverClickSounds(sampleSet)); } } } diff --git a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs new file mode 100644 index 0000000000..eace307610 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs @@ -0,0 +1,62 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// Adds hover and click sounds to a drawable. + /// Does not draw anything. + /// + public class HoverClickSounds : CompositeDrawable + { + private SampleChannel sampleClick; + private SampleChannel sampleHover; + + protected readonly HoverSampleSet SampleSet; + + public HoverClickSounds(HoverSampleSet sampleSet = HoverSampleSet.Normal) + { + SampleSet = sampleSet; + RelativeSizeAxes = Axes.Both; + AlwaysPresent = true; + } + + protected override bool OnClick(InputState state) + { + sampleClick?.Play(); + return base.OnClick(state); + } + + protected override bool OnHover(InputState state) + { + sampleHover?.Play(); + return base.OnHover(state); + } + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + sampleClick = audio.Sample.Get($@"UI/generic-select{SampleSet.GetDescription()}"); + sampleHover = audio.Sample.Get($@"UI/generic-hover{SampleSet.GetDescription()}"); + } + } + + public enum HoverSampleSet + { + [Description("")] + Loud, + [Description("-soft")] + Normal, + [Description("-softer")] + Soft + } +} diff --git a/osu.Game/Graphics/UserInterface/OsuButton.cs b/osu.Game/Graphics/UserInterface/OsuButton.cs index 3b2377c06b..081e59d3a7 100644 --- a/osu.Game/Graphics/UserInterface/OsuButton.cs +++ b/osu.Game/Graphics/UserInterface/OsuButton.cs @@ -1,13 +1,7 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.ComponentModel; -using osu.Framework.Allocation; -using osu.Framework.Audio; -using osu.Framework.Audio.Sample; -using osu.Framework.Extensions; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input; namespace osu.Game.Graphics.UserInterface { @@ -16,38 +10,9 @@ namespace osu.Game.Graphics.UserInterface /// public class OsuButton : Button { - private SampleChannel sampleClick; - private SampleChannel sampleHover; - - protected HoverSampleSet SampleSet = HoverSampleSet.Normal; - - protected override bool OnClick(InputState state) + public OsuButton() { - sampleClick?.Play(); - return base.OnClick(state); - } - - protected override bool OnHover(InputState state) - { - sampleHover?.Play(); - return base.OnHover(state); - } - - [BackgroundDependencyLoader] - private void load(AudioManager audio) - { - sampleClick = audio.Sample.Get($@"UI/generic-select{SampleSet.GetDescription()}"); - sampleHover = audio.Sample.Get($@"UI/generic-hover{SampleSet.GetDescription()}"); - } - - public enum HoverSampleSet - { - [Description("")] - Normal, - [Description("-soft")] - Soft, - [Description("-softer")] - Softer + Add(new HoverClickSounds(HoverSampleSet.Loud)); } } } diff --git a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs index 68ff99e593..40ff1243dc 100644 --- a/osu.Game/Graphics/UserInterface/OsuCheckbox.cs +++ b/osu.Game/Graphics/UserInterface/OsuCheckbox.cs @@ -70,7 +70,8 @@ namespace osu.Game.Graphics.UserInterface Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, Margin = new MarginPadding { Right = 5 }, - } + }, + new HoverClickSounds() }; Nub.Current.BindTo(Current); diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index f605804aaa..4401b509fd 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -33,7 +33,6 @@ namespace osu.Game.Graphics.UserInterface if (accentColour == default(Color4)) accentColour = colours.PinkDarker; updateAccentColour(); - } private void updateAccentColour() @@ -137,6 +136,8 @@ namespace osu.Game.Graphics.UserInterface nonAccentHoverColour = colours.PinkDarker; nonAccentSelectedColour = Color4.Black.Opacity(0.5f); updateColours(); + + AddInternal(new HoverClickSounds(HoverSampleSet.Soft)); } protected override void UpdateForegroundColour() @@ -183,7 +184,7 @@ namespace osu.Game.Graphics.UserInterface { Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, - } + }, }; } } @@ -237,8 +238,10 @@ namespace osu.Game.Graphics.UserInterface Origin = Anchor.CentreRight, Margin = new MarginPadding { Right = 4 }, Size = new Vector2(20), - } + }, }; + + AddInternal(new HoverClickSounds()); } [BackgroundDependencyLoader] diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index 3dd3596c30..fd75269610 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -88,7 +88,8 @@ namespace osu.Game.Graphics.UserInterface { Origin = Anchor.TopCentre, Expanded = true, - } + }, + new HoverClickSounds() }; Current.DisabledChanged += disabled => diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs index b053195030..decbf57ad1 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs @@ -131,7 +131,8 @@ namespace osu.Game.Graphics.UserInterface Colour = Color4.White, Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, - } + }, + new HoverClickSounds() }; } diff --git a/osu.Game/Graphics/UserInterface/PageTabControl.cs b/osu.Game/Graphics/UserInterface/PageTabControl.cs index 6b97e54ecd..c69857a5c4 100644 --- a/osu.Game/Graphics/UserInterface/PageTabControl.cs +++ b/osu.Game/Graphics/UserInterface/PageTabControl.cs @@ -57,6 +57,7 @@ namespace osu.Game.Graphics.UserInterface Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, }, + new HoverClickSounds() }; } diff --git a/osu.Game/Overlays/Chat/ChatLine.cs b/osu.Game/Overlays/Chat/ChatLine.cs index 4db6bdf5e4..32f933ff42 100644 --- a/osu.Game/Overlays/Chat/ChatLine.cs +++ b/osu.Game/Overlays/Chat/ChatLine.cs @@ -222,7 +222,7 @@ namespace osu.Game.Overlays.Chat } - private class MessageSender : ClickableContainer, IHasContextMenu + private class MessageSender : OsuClickableContainer, IHasContextMenu { private readonly User sender; diff --git a/osu.Game/Overlays/Chat/ChatTabControl.cs b/osu.Game/Overlays/Chat/ChatTabControl.cs index 9f1028c168..f58ee8f819 100644 --- a/osu.Game/Overlays/Chat/ChatTabControl.cs +++ b/osu.Game/Overlays/Chat/ChatTabControl.cs @@ -17,6 +17,7 @@ using OpenTK; using OpenTK.Graphics; using osu.Framework.Configuration; using System; +using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.Chat { @@ -259,7 +260,7 @@ namespace osu.Game.Overlays.Chat }; } - public class CloseButton : ClickableContainer + public class CloseButton : OsuClickableContainer { private readonly SpriteIcon icon; diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index cb17216679..c039f9d311 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -13,6 +13,7 @@ using OpenTK; using OpenTK.Graphics; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Toolbar { @@ -74,7 +75,7 @@ namespace osu.Game.Overlays.Toolbar private readonly SpriteText tooltip2; protected FillFlowContainer Flow; - public ToolbarButton() + public ToolbarButton() : base(HoverSampleSet.Loud) { Width = WIDTH; RelativeSizeAxes = Axes.Y; @@ -195,4 +196,4 @@ namespace osu.Game.Overlays.Toolbar }; } } -} \ No newline at end of file +} diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index 1f235e3893..d056afcf54 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -17,10 +17,11 @@ using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.UserInterface; using osu.Framework.Graphics.Cursor; using osu.Game.Graphics.Backgrounds; +using osu.Game.Graphics.Containers; namespace osu.Game.Users { - public class UserPanel : ClickableContainer, IHasContextMenu + public class UserPanel : OsuClickableContainer, IHasContextMenu { private readonly User user; private const float height = 100; diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index a26ad963cf..415286f9a8 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -271,6 +271,7 @@ + From 0c6a1257777136f23696e270d7a65ebc7c64814b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Nov 2017 02:52:55 +0900 Subject: [PATCH 043/235] Remove custom colour handling --- osu.Game/Graphics/UserInterface/TriangleButton.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/TriangleButton.cs b/osu.Game/Graphics/UserInterface/TriangleButton.cs index 33a8a8f99b..9fbdf6832f 100644 --- a/osu.Game/Graphics/UserInterface/TriangleButton.cs +++ b/osu.Game/Graphics/UserInterface/TriangleButton.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using OpenTK.Graphics; using osu.Framework.Allocation; -using osu.Framework.Audio; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -38,11 +37,8 @@ namespace osu.Game.Graphics.UserInterface public override bool HandleInput => Action != null; [BackgroundDependencyLoader] - private void load(OsuColour colours, AudioManager audio) + private void load(OsuColour colours) { - if (Action == null) - Colour = OsuColour.Gray(0.5f); - BackgroundColour = colours.BlueDark; Content.Masking = true; From 880418fd0d83c5005145b68e3c7d8ca5e4aaa2db Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Nov 2017 03:27:16 +0900 Subject: [PATCH 044/235] Split out click and hover sound layers --- .../UserInterface/HoverClickSounds.cs | 30 +---------- .../Graphics/UserInterface/HoverSounds.cs | 54 +++++++++++++++++++ osu.Game/osu.Game.csproj | 1 + 3 files changed, 57 insertions(+), 28 deletions(-) create mode 100644 osu.Game/Graphics/UserInterface/HoverSounds.cs diff --git a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs index eace307610..0fac1c8092 100644 --- a/osu.Game/Graphics/UserInterface/HoverClickSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverClickSounds.cs @@ -1,13 +1,10 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.ComponentModel; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Input; namespace osu.Game.Graphics.UserInterface @@ -16,18 +13,12 @@ namespace osu.Game.Graphics.UserInterface /// Adds hover and click sounds to a drawable. /// Does not draw anything. /// - public class HoverClickSounds : CompositeDrawable + public class HoverClickSounds : HoverSounds { private SampleChannel sampleClick; - private SampleChannel sampleHover; - protected readonly HoverSampleSet SampleSet; - - public HoverClickSounds(HoverSampleSet sampleSet = HoverSampleSet.Normal) + public HoverClickSounds(HoverSampleSet sampleSet = HoverSampleSet.Normal) : base(sampleSet) { - SampleSet = sampleSet; - RelativeSizeAxes = Axes.Both; - AlwaysPresent = true; } protected override bool OnClick(InputState state) @@ -36,27 +27,10 @@ namespace osu.Game.Graphics.UserInterface return base.OnClick(state); } - protected override bool OnHover(InputState state) - { - sampleHover?.Play(); - return base.OnHover(state); - } - [BackgroundDependencyLoader] private void load(AudioManager audio) { sampleClick = audio.Sample.Get($@"UI/generic-select{SampleSet.GetDescription()}"); - sampleHover = audio.Sample.Get($@"UI/generic-hover{SampleSet.GetDescription()}"); } } - - public enum HoverSampleSet - { - [Description("")] - Loud, - [Description("-soft")] - Normal, - [Description("-softer")] - Soft - } } diff --git a/osu.Game/Graphics/UserInterface/HoverSounds.cs b/osu.Game/Graphics/UserInterface/HoverSounds.cs new file mode 100644 index 0000000000..d26ad35c49 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/HoverSounds.cs @@ -0,0 +1,54 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.ComponentModel; +using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using osu.Framework.Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input; + +namespace osu.Game.Graphics.UserInterface +{ + /// + /// Adds hover sounds to a drawable. + /// Does not draw anything. + /// + public class HoverSounds : CompositeDrawable + { + private SampleChannel sampleHover; + + protected readonly HoverSampleSet SampleSet; + + public HoverSounds(HoverSampleSet sampleSet = HoverSampleSet.Normal) + { + SampleSet = sampleSet; + RelativeSizeAxes = Axes.Both; + AlwaysPresent = true; + } + + protected override bool OnHover(InputState state) + { + sampleHover?.Play(); + return base.OnHover(state); + } + + [BackgroundDependencyLoader] + private void load(AudioManager audio) + { + sampleHover = audio.Sample.Get($@"UI/generic-hover{SampleSet.GetDescription()}"); + } + } + + public enum HoverSampleSet + { + [Description("")] + Loud, + [Description("-soft")] + Normal, + [Description("-softer")] + Soft + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 415286f9a8..ce9e8eff9c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -272,6 +272,7 @@ + From c5aacb75c63f43c6c2a185640f0ddba26c2dd237 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Nov 2017 03:28:11 +0900 Subject: [PATCH 045/235] Add hover sounds to mod buttons --- osu.Game/Overlays/Mods/ModButton.cs | 6 ++++-- osu.Game/Overlays/Mods/ModButtonEmpty.cs | 3 --- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index 0ead4ea019..f2a3da4145 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -17,6 +17,7 @@ using osu.Game.Rulesets.UI; using System; using System.Linq; using osu.Framework.Graphics.Cursor; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Mods { @@ -31,7 +32,7 @@ namespace osu.Game.Overlays.Mods private readonly Container iconsContainer; private SampleChannel sampleOn, sampleOff; - public Action Action; // Passed the selected mod or null if none + public new Action Action; // Passed the selected mod or null if none public string TooltipText => (SelectedMod?.Description ?? Mods.FirstOrDefault()?.Description) ?? string.Empty; @@ -148,7 +149,7 @@ namespace osu.Game.Overlays.Mods // the mods from Mod, only multiple if Mod is a MultiMod - public override Mod SelectedMod => Mods.ElementAtOrDefault(SelectedIndex); + public virtual Mod SelectedMod => Mods.ElementAtOrDefault(SelectedIndex); [BackgroundDependencyLoader] private void load(AudioManager audio) @@ -253,6 +254,7 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.TopCentre, TextSize = 18, }, + new HoverClickSounds() }; Mod = mod; diff --git a/osu.Game/Overlays/Mods/ModButtonEmpty.cs b/osu.Game/Overlays/Mods/ModButtonEmpty.cs index 638c2a0e47..f776c174d2 100644 --- a/osu.Game/Overlays/Mods/ModButtonEmpty.cs +++ b/osu.Game/Overlays/Mods/ModButtonEmpty.cs @@ -3,7 +3,6 @@ using OpenTK; using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Mods; namespace osu.Game.Overlays.Mods { @@ -12,8 +11,6 @@ namespace osu.Game.Overlays.Mods /// public class ModButtonEmpty : Container { - public virtual Mod SelectedMod => null; - public ModButtonEmpty() { Size = new Vector2(100f); From 5aa6615107756963ac9c5b03dceb3acf53c311f4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Nov 2017 03:30:05 +0900 Subject: [PATCH 046/235] Add confirm-selection sound in song select --- osu.Game/Screens/Select/PlaySongSelect.cs | 10 +++++++++- osu.Game/Screens/Select/SongSelect.cs | 2 +- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index e0a3693371..992bfa52ee 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -4,6 +4,8 @@ using System.Linq; using OpenTK.Input; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; @@ -42,9 +44,13 @@ namespace osu.Game.Screens.Select beatmapDetails.Leaderboard.ScoreSelected += s => Push(new Results(s)); } + private SampleChannel sampleConfirm; + [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, AudioManager audio) { + sampleConfirm = audio.Sample.Get($@"SongSelect/confirm-selection"); + Footer.AddButton(@"mods", colours.Yellow, modSelect, Key.F1, float.MaxValue); BeatmapOptions.AddButton(@"Remove", @"from unplayed", FontAwesome.fa_times_circle_o, colours.Purple, null, Key.Number1); @@ -128,6 +134,8 @@ namespace osu.Game.Screens.Select Beatmap.Value.Track.Looping = false; Beatmap.Disabled = true; + sampleConfirm?.Play(); + LoadComponentAsync(player = new PlayerLoader(new Player()), l => Push(player)); } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index fcf459182f..b232f2edad 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -335,7 +335,7 @@ namespace osu.Game.Screens.Select logo.Action = () => { carouselRaisedStart(); - return true; + return false; }; } From 9c90d9ca457c57e1ef0fb4d63021ad2b5d6510a0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Nov 2017 04:16:36 +0900 Subject: [PATCH 047/235] Add panel hover effects --- osu.Game/Beatmaps/Drawables/Panel.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/osu.Game/Beatmaps/Drawables/Panel.cs b/osu.Game/Beatmaps/Drawables/Panel.cs index d6ed306b39..32c1a31de6 100644 --- a/osu.Game/Beatmaps/Drawables/Panel.cs +++ b/osu.Game/Beatmaps/Drawables/Panel.cs @@ -3,12 +3,15 @@ using System; using osu.Framework; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; using OpenTK; using OpenTK.Graphics; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.MathUtils; namespace osu.Game.Beatmaps.Drawables { @@ -40,6 +43,20 @@ namespace osu.Game.Beatmaps.Drawables Alpha = 0; } + private SampleChannel sampleHover; + + protected override bool OnHover(InputState state) + { + sampleHover?.Play(); + return base.OnHover(state); + } + + [BackgroundDependencyLoader] + private void load(AudioManager audio, OsuColour colours) + { + sampleHover = audio.Sample.Get($@"SongSelect/song-ping-variation-{RNG.Next(1, 5)}"); + } + public void SetMultiplicativeAlpha(float alpha) { nestedContainer.Alpha = alpha; From 671b3d01ff83d9550fcf1b65cfcccdf5d352181d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 26 Nov 2017 04:59:55 +0900 Subject: [PATCH 048/235] Fix OsuClickableContainer's local content geting overwritten --- .../Containers/OsuClickableContainer.cs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/OsuClickableContainer.cs b/osu.Game/Graphics/Containers/OsuClickableContainer.cs index c9117ed159..8df533ad6e 100644 --- a/osu.Game/Graphics/Containers/OsuClickableContainer.cs +++ b/osu.Game/Graphics/Containers/OsuClickableContainer.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; @@ -11,6 +12,10 @@ namespace osu.Game.Graphics.Containers { private readonly HoverSampleSet sampleSet; + private readonly Container content = new Container { RelativeSizeAxes = Axes.Both }; + + protected override Container Content => content; + public OsuClickableContainer(HoverSampleSet sampleSet = HoverSampleSet.Normal) { this.sampleSet = sampleSet; @@ -19,7 +24,17 @@ namespace osu.Game.Graphics.Containers [BackgroundDependencyLoader] private void load() { - AddInternal(new HoverClickSounds(sampleSet)); + if (AutoSizeAxes != Axes.None) + { + content.RelativeSizeAxes = RelativeSizeAxes; + content.AutoSizeAxes = AutoSizeAxes; + } + + InternalChildren = new Drawable[] + { + content, + new HoverClickSounds(sampleSet) + }; } } } From ba0b16dc0b49a2f852f46eadbf321b1509356126 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Sun, 26 Nov 2017 19:00:30 +0100 Subject: [PATCH 049/235] setting BeatmapSets as list to prevent endless LINQ query chain --- osu.Game/Overlays/DirectOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index 11dd9dd976..b49ac269a9 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -50,7 +50,7 @@ namespace osu.Game.Overlays { if (beatmapSets?.Equals(value) ?? false) return; - beatmapSets = value; + beatmapSets = value?.ToList(); if (beatmapSets == null) return; From ae55d392de9a05f9d878d79a319e4a39070a1bd3 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 25 Nov 2017 15:28:39 +0100 Subject: [PATCH 050/235] only use `==` for comparion on primitive types --- osu.Game/Beatmaps/BeatmapMetadata.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapMetadata.cs b/osu.Game/Beatmaps/BeatmapMetadata.cs index 22a64820bc..a78ef25166 100644 --- a/osu.Game/Beatmaps/BeatmapMetadata.cs +++ b/osu.Game/Beatmaps/BeatmapMetadata.cs @@ -74,16 +74,16 @@ namespace osu.Game.Beatmaps return false; return onlineBeatmapSetID == other.onlineBeatmapSetID - && (Title?.Equals(other.Title) ?? false) - && (TitleUnicode?.Equals(other.TitleUnicode) ?? false) - && (Artist?.Equals(other.Artist) ?? false) - && (ArtistUnicode?.Equals(other.ArtistUnicode) ?? false) - && (AuthorString?.Equals(other.AuthorString) ?? false) - && (Source?.Equals(other.Source) ?? false) - && (Tags?.Equals(other.Tags) ?? false) + && Title == other.Title + && TitleUnicode == other.TitleUnicode + && Artist == other.Artist + && ArtistUnicode == other.ArtistUnicode + && AuthorString == other.AuthorString + && Source == other.Source + && Tags == other.Tags && PreviewTime == other.PreviewTime - && (AudioFile?.Equals(other.AudioFile) ?? false) - && (BackgroundFile?.Equals(other.BackgroundFile) ?? false); + && AudioFile == other.AudioFile + && BackgroundFile == other.BackgroundFile; } } } From 02fa1f9dd6c5418f68bccfd12d4015479855766b Mon Sep 17 00:00:00 2001 From: jorolf Date: Sun, 26 Nov 2017 21:52:35 +0100 Subject: [PATCH 051/235] move shared stuff between MostPlayedBeatmapDrawable and DrawableScore to DrawableBeatmapRow --- .../Profile/Sections/DrawableBeatmapRow.cs | 118 +++++++++++ .../Historical/MostPlayedBeatmapDrawable.cs | 188 +++++++----------- .../Ranks/DrawablePerformanceScore.cs | 4 +- .../Profile/Sections/Ranks/DrawableScore.cs | 119 ++--------- .../Sections/Ranks/DrawableTotalScore.cs | 2 +- osu.Game/osu.Game.csproj | 1 + 6 files changed, 213 insertions(+), 219 deletions(-) create mode 100644 osu.Game/Overlays/Profile/Sections/DrawableBeatmapRow.cs diff --git a/osu.Game/Overlays/Profile/Sections/DrawableBeatmapRow.cs b/osu.Game/Overlays/Profile/Sections/DrawableBeatmapRow.cs new file mode 100644 index 0000000000..f6800a5f32 --- /dev/null +++ b/osu.Game/Overlays/Profile/Sections/DrawableBeatmapRow.cs @@ -0,0 +1,118 @@ +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input; +using osu.Game.Graphics; +using OpenTK; +using OpenTK.Graphics; + +namespace osu.Game.Overlays.Profile.Sections +{ + public abstract class DrawableBeatmapRow : Container + { + private const int fade_duration = 200; + + private Box underscoreLine; + private readonly Box coloredBackground; + private readonly Container background; + + protected abstract Drawable CreatePicture(); + + protected FillFlowContainer LeftFlowContainer { get; private set; } + protected FillFlowContainer RightFlowContainer { get; private set; } + + protected override Container Content { get; } + + protected DrawableBeatmapRow() + { + RelativeSizeAxes = Axes.X; + Height = 60; + InternalChildren = new Drawable[] + { + background = new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = 3, + Alpha = 0, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Offset = new Vector2(0f, 1f), + Radius = 1f, + Colour = Color4.Black.Opacity(0.2f), + }, + Child = coloredBackground = new Box { RelativeSizeAxes = Axes.Both } + }, + Content = new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Width = 0.97f, + }, + }; + } + + [BackgroundDependencyLoader(true)] + private void load(OsuColour colour) + { + AddRange(new Drawable[] + { + underscoreLine = new Box + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + RelativeSizeAxes = Axes.X, + Height = 1, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new[] + { + CreatePicture(), + LeftFlowContainer = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Margin = new MarginPadding { Left = 10 }, + Direction = FillDirection.Vertical, + }, + } + }, + RightFlowContainer = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Direction = FillDirection.Vertical, + }, + }); + + coloredBackground.Colour = underscoreLine.Colour = colour.Gray4; + } + + protected override bool OnClick(InputState state) => true; + + protected override bool OnHover(InputState state) + { + background.FadeIn(fade_duration, Easing.OutQuint); + underscoreLine.FadeOut(fade_duration, Easing.OutQuint); + return true; + } + + protected override void OnHoverLost(InputState state) + { + background.FadeOut(fade_duration, Easing.OutQuint); + underscoreLine.FadeIn(fade_duration, Easing.OutQuint); + base.OnHoverLost(state); + } + } +} diff --git a/osu.Game/Overlays/Profile/Sections/Historical/MostPlayedBeatmapDrawable.cs b/osu.Game/Overlays/Profile/Sections/Historical/MostPlayedBeatmapDrawable.cs index 0f419ad44e..446af667f2 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/MostPlayedBeatmapDrawable.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/MostPlayedBeatmapDrawable.cs @@ -2,14 +2,11 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; -using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using OpenTK; @@ -17,144 +14,99 @@ using OpenTK.Graphics; namespace osu.Game.Overlays.Profile.Sections.Historical { - public class MostPlayedBeatmapDrawable : Container + public class MostPlayedBeatmapDrawable : DrawableBeatmapRow { private readonly BeatmapInfo beatmap; - private readonly OsuHoverContainer mapperContainer; - - private readonly EdgeEffectParameters edgeEffectNormal = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Offset = new Vector2(0, 1f), - Radius = 2f, - Colour = Color4.Black.Opacity(0.25f), - }; - - private readonly EdgeEffectParameters edgeEffectHovered = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Offset = new Vector2(0, 5f), - Radius = 10f, - Colour = Color4.Black.Opacity(0.25f), - }; + private readonly int playCount; + private OsuHoverContainer mapperContainer; public MostPlayedBeatmapDrawable(BeatmapInfo beatmap, int playCount) { this.beatmap = beatmap; - RelativeSizeAxes = Axes.X; - Height = 50; - Margin = new MarginPadding { Bottom = 10 }; - Masking = true; - EdgeEffect = edgeEffectNormal; + this.playCount = playCount; + } + protected override Drawable CreatePicture() => new Container + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, Children = new Drawable[] { - new Box //Background for this container, otherwise the shadow would be visible - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(0.2f), - }, new Box //Image Background while loading { - Size = new Vector2(80, 50), - Colour = Color4.Black, + Size = new Vector2(80, 50), + Colour = Color4.Black, }, new DelayedLoadWrapper(new BeatmapSetCover(beatmap.BeatmapSet, BeatmapSetCoverType.List) { RelativeSizeAxes = Axes.Both, FillMode = FillMode.Fit, }), - new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding(10) { Left = 90 }, - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new Drawable[] - { - new BeatmapMetadataContainer(beatmap), - new FillFlowContainer - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Children = new [] - { - new OsuSpriteText - { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - Text = playCount.ToString(), - TextSize = 18, - Font = @"Exo2.0-SemiBoldItalic" - }, - new OsuSpriteText - { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - Text = @"times played ", - TextSize = 12, - Font = @"Exo2.0-RegularItalic" - }, - } - } - }, - }, - new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Horizontal, - Children = new Drawable[] - { - new OsuSpriteText - { - Text = @"mapped by ", - TextSize = 12, - }, - mapperContainer = new OsuHoverContainer - { - AutoSizeAxes = Axes.Both, - Children = new Drawable[] - { - new OsuSpriteText - { - Text = beatmap.Metadata.AuthorString, - TextSize = 12, - Font = @"Exo2.0-MediumItalic" - } - } - }, - } - }, - }, - } - }; - } + }, + }; [BackgroundDependencyLoader(true)] private void load(UserProfileOverlay profileOverlay) { + LeftFlowContainer.Add(new BeatmapMetadataContainer(beatmap)); + LeftFlowContainer.Add(new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = @"mapped by ", + TextSize = 12, + }, + mapperContainer = new OsuHoverContainer + { + AutoSizeAxes = Axes.Both, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = beatmap.Metadata.AuthorString, + TextSize = 12, + Font = @"Exo2.0-MediumItalic" + } + } + }, + } + }); + + RightFlowContainer.Add(new FillFlowContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Children = new[] + { + new OsuSpriteText + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Text = playCount.ToString(), + TextSize = 18, + Font = @"Exo2.0-SemiBoldItalic" + }, + new OsuSpriteText + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Text = @"times played ", + TextSize = 12, + Font = @"Exo2.0-RegularItalic" + }, + } + }); + if(profileOverlay != null) mapperContainer.Action = () => profileOverlay.ShowUser(beatmap.BeatmapSet.Metadata.Author); } - - protected override bool OnHover(InputState state) - { - TweenEdgeEffectTo(edgeEffectHovered, 120, Easing.OutQuint); - return base.OnHover(state); - } - - protected override void OnHoverLost(InputState state) - { - TweenEdgeEffectTo(edgeEffectNormal, 120, Easing.OutQuint); - base.OnHoverLost(state); - } } } diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawablePerformanceScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawablePerformanceScore.cs index e6ba5b26ac..cd13d14575 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawablePerformanceScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawablePerformanceScore.cs @@ -23,7 +23,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks private void load(OsuColour colour) { double pp = Score.PP ?? 0; - Stats.Add(new OsuSpriteText + RightFlowContainer.Add(new OsuSpriteText { Text = $"{pp:0}pp", Anchor = Anchor.TopRight, @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks if (weight.HasValue) { - Stats.Add(new OsuSpriteText + RightFlowContainer.Add(new OsuSpriteText { Text = $"weighted: {pp * weight:0}pp ({weight:P0})", Anchor = Anchor.TopRight, diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs index 4d210f399e..db7647ce0e 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableScore.cs @@ -11,24 +11,14 @@ using osu.Game.Rulesets.Mods; using osu.Game.Screens.Select.Leaderboards; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; -using OpenTK.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input; -using osu.Framework.Extensions.Color4Extensions; namespace osu.Game.Overlays.Profile.Sections.Ranks { - public abstract class DrawableScore : Container + public abstract class DrawableScore : DrawableBeatmapRow { - private const int fade_duration = 200; - - protected readonly FillFlowContainer Stats; private readonly FillFlowContainer metadata; private readonly ScoreModsContainer modsContainer; protected readonly Score Score; - private readonly Box underscoreLine; - private readonly Box coloredBackground; - private readonly Container background; protected DrawableScore(Score score) { @@ -38,85 +28,21 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks Height = 60; Children = new Drawable[] { - background = new Container + modsContainer = new ScoreModsContainer { - RelativeSizeAxes = Axes.Both, - Masking = true, - CornerRadius = 3, - Alpha = 0, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Offset = new Vector2(0f, 1f), - Radius = 1f, - Colour = Color4.Black.Opacity(0.2f), - }, - Child = coloredBackground = new Box { RelativeSizeAxes = Axes.Both } - }, - new Container - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Width = 0.97f, - Children = new Drawable[] - { - underscoreLine = new Box - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - RelativeSizeAxes = Axes.X, - Height = 1, - }, - new DrawableRank(score.Rank) - { - RelativeSizeAxes = Axes.Y, - Width = 60, - FillMode = FillMode.Fit, - }, - Stats = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Direction = FillDirection.Vertical, - }, - metadata = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Margin = new MarginPadding { Left = 70 }, - Direction = FillDirection.Vertical, - Child = new OsuSpriteText - { - Text = score.Date.LocalDateTime.ToShortDateString(), - TextSize = 11, - Colour = OsuColour.Gray(0xAA), - Depth = -1, - }, - }, - modsContainer = new ScoreModsContainer - { - AutoSizeAxes = Axes.Y, - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Width = 60, - Margin = new MarginPadding { Right = 160 } - } - } - }, + AutoSizeAxes = Axes.Y, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Width = 60, + Margin = new MarginPadding { Right = 160 } + } }; } [BackgroundDependencyLoader(true)] private void load(OsuColour colour) { - coloredBackground.Colour = underscoreLine.Colour = colour.Gray4; - - Stats.Add(new OsuSpriteText + RightFlowContainer.Add(new OsuSpriteText { Text = $"accuracy: {Score.Accuracy:P2}", Anchor = Anchor.TopRight, @@ -127,26 +53,23 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks Depth = -1, }); - metadata.Add(new BeatmapMetadataContainer(Score.Beatmap)); + LeftFlowContainer.Add(new BeatmapMetadataContainer(Score.Beatmap)); + LeftFlowContainer.Add(new OsuSpriteText + { + Text = Score.Date.LocalDateTime.ToShortDateString(), + TextSize = 11, + Colour = OsuColour.Gray(0xAA), + }); foreach (Mod mod in Score.Mods) modsContainer.Add(new ModIcon(mod) { Scale = new Vector2(0.5f) }); } - protected override bool OnClick(InputState state) => true; - - protected override bool OnHover(InputState state) + protected override Drawable CreatePicture() => new DrawableRank(Score.Rank) { - background.FadeIn(fade_duration, Easing.OutQuint); - underscoreLine.FadeOut(fade_duration, Easing.OutQuint); - return true; - } - - protected override void OnHoverLost(InputState state) - { - background.FadeOut(fade_duration, Easing.OutQuint); - underscoreLine.FadeIn(fade_duration, Easing.OutQuint); - base.OnHoverLost(state); - } + RelativeSizeAxes = Axes.Y, + Width = 60, + FillMode = FillMode.Fit, + }; } } diff --git a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableTotalScore.cs b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableTotalScore.cs index 537b208b39..1539142f1d 100644 --- a/osu.Game/Overlays/Profile/Sections/Ranks/DrawableTotalScore.cs +++ b/osu.Game/Overlays/Profile/Sections/Ranks/DrawableTotalScore.cs @@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Profile.Sections.Ranks [BackgroundDependencyLoader] private void load() { - Stats.Add(new OsuSpriteText + RightFlowContainer.Add(new OsuSpriteText { Text = Score.TotalScore.ToString("#,###"), Anchor = Anchor.TopRight, diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 4921c4ce05..b774602b76 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -294,6 +294,7 @@ + From 4c68090e59d2139c6bc53033c423b138c7963f3e Mon Sep 17 00:00:00 2001 From: jorolf Date: Sun, 26 Nov 2017 22:06:03 +0100 Subject: [PATCH 052/235] separate GetUserBeatmapsRequest and GetUserMostPlayedBeatmapsRequest --- .../API/Requests/GetUserBeatmapsRequest.cs | 16 ++-------------- .../Requests/GetUserMostPlayedBeatmapsRequest.cs | 16 ++++++++++------ .../PaginatedMostPlayedBeatmapContainer.cs | 2 +- 3 files changed, 13 insertions(+), 21 deletions(-) diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs index dca0395e3a..691f8496d9 100644 --- a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs @@ -1,19 +1,18 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using Humanizer; using System.Collections.Generic; namespace osu.Game.Online.API.Requests { - public abstract class GetUserBeatmapsRequest : APIRequest> + public class GetUserBeatmapsRequest : APIRequest> { private readonly long userId; private readonly int offset; private readonly BeatmapSetType type; - protected GetUserBeatmapsRequest(long userId, BeatmapSetType type, int offset = 0) + public GetUserBeatmapsRequest(long userId, BeatmapSetType type, int offset = 0) { this.userId = userId; this.offset = offset; @@ -23,19 +22,8 @@ namespace osu.Game.Online.API.Requests protected override string Target => $@"users/{userId}/beatmapsets/{type.ToString().Underscore()}?offset={offset}"; } - public class GetUserBeatmapsRequest : GetUserBeatmapsRequest - { - public GetUserBeatmapsRequest(long userID, BeatmapSetType type, int offset = 0) - : base(userID, type, offset) - { - if(type == BeatmapSetType.MostPlayed) - throw new ArgumentException("Please use " + nameof(GetUserMostPlayedBeatmapsRequest) + " instead"); - } - } - public enum BeatmapSetType { - MostPlayed, Favourite, RankedAndApproved, Unranked, diff --git a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs index 2d08f09d20..c45ef734e6 100644 --- a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs @@ -1,21 +1,25 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using Newtonsoft.Json; using osu.Game.Beatmaps; using osu.Game.Rulesets; +using System.Collections.Generic; namespace osu.Game.Online.API.Requests { - public class GetUserMostPlayedBeatmapsRequest : GetUserBeatmapsRequest + public class GetUserMostPlayedBeatmapsRequest : APIRequest> { - public GetUserMostPlayedBeatmapsRequest(long userID, BeatmapSetType type, int offset = 0) - : base(userID, type, offset) + private readonly long userId; + private readonly int offset; + + public GetUserMostPlayedBeatmapsRequest(long userId, int offset = 0) { - if (type != BeatmapSetType.MostPlayed) - throw new ArgumentException("Please use " + nameof(GetUserBeatmapsRequest) + " instead"); + this.userId = userId; + this.offset = offset; } + + protected override string Target => $@"users/{userId}/beatmapsets/most_played?offset={offset}"; } public class UserMostPlayedBeatmapsResponse diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index 916f1f437a..b7df60a7a2 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical { base.ShowMore(); - var req = new GetUserMostPlayedBeatmapsRequest(User.Value.Id, BeatmapSetType.MostPlayed, VisiblePages++ * ItemsPerPage); + var req = new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++ * ItemsPerPage); req.Success += beatmaps => { From 70b6071898afa38590f00ca43accedb6b808e754 Mon Sep 17 00:00:00 2001 From: jorolf Date: Sun, 26 Nov 2017 22:13:52 +0100 Subject: [PATCH 053/235] add license header --- osu.Game/Overlays/Profile/Sections/DrawableBeatmapRow.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/DrawableBeatmapRow.cs b/osu.Game/Overlays/Profile/Sections/DrawableBeatmapRow.cs index f6800a5f32..4d2affd77c 100644 --- a/osu.Game/Overlays/Profile/Sections/DrawableBeatmapRow.cs +++ b/osu.Game/Overlays/Profile/Sections/DrawableBeatmapRow.cs @@ -1,4 +1,7 @@ -using osu.Framework.Allocation; +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; From f189de437a38450c4a1d568a51c3e013d54ced8b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Nov 2017 10:01:44 +0900 Subject: [PATCH 054/235] Simplify blending assignment --- osu.Game/Beatmaps/Drawables/Panel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Drawables/Panel.cs b/osu.Game/Beatmaps/Drawables/Panel.cs index 5fbc858b00..e4b7e55012 100644 --- a/osu.Game/Beatmaps/Drawables/Panel.cs +++ b/osu.Game/Beatmaps/Drawables/Panel.cs @@ -52,7 +52,7 @@ namespace osu.Game.Beatmaps.Drawables { RelativeSizeAxes = Axes.Both, Alpha = 0, - Blending = new BlendingParameters { Mode = BlendingMode.Additive }, + Blending = BlendingMode.Additive, }, } }); From b34e724b8d17a1fda502683659e2d5b3e4c44d7b Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Mon, 27 Nov 2017 08:18:09 +0100 Subject: [PATCH 055/235] Changed MetadataSection so that the Text setter loads the new text in async before displaying it. --- osu.Game/Screens/Select/BeatmapDetails.cs | 24 ++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 6eaed67534..1b2108a6c8 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -316,7 +316,7 @@ namespace osu.Game.Screens.Select private class MetadataSection : Container { - private readonly TextFlowContainer textFlow; + private TextFlowContainer textFlow; public string Text { @@ -329,11 +329,29 @@ namespace osu.Game.Screens.Select } this.FadeIn(transition_duration); - textFlow.Clear(); - textFlow.AddText(value, s => s.TextSize = 14); + addTextAsync(value); } } + private void addTextAsync(string text) + { + var newTextFlow = new TextFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Colour = textFlow.Colour, + }; + + newTextFlow.AddText(text, s => s.TextSize = 14); + + LoadComponentAsync(newTextFlow, d => + { + var textContainer = (InternalChild as FillFlowContainer); + textContainer.Remove(textFlow); + textContainer.Add(textFlow = d); + }); + } + public Color4 TextColour { get { return textFlow.Colour; } From d49ee295d99e0438fd016c7e00d2bd13255f0d41 Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Mon, 27 Nov 2017 08:47:36 +0100 Subject: [PATCH 056/235] Removed unnecessary schedule and null checks --- osu.Game/Screens/Select/BeatmapDetails.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 1b2108a6c8..f203f5cdc4 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -217,12 +217,9 @@ namespace osu.Game.Screens.Select if (description == null || source == null || tags == null) throw new InvalidOperationException($@"Requires all {nameof(MetadataSection)} elements to be non-null."); - Schedule(() => - { - description.Text = Beatmap?.Version; - source.Text = Beatmap?.Metadata?.Source; - tags.Text = Beatmap?.Metadata?.Tags; - }); + description.Text = beatmap.Version; + source.Text = beatmap.Metadata?.Source; + tags.Text = beatmap.Metadata?.Tags; } private void displayMetrics(BeatmapMetrics metrics, bool failOnMissing = true) From ac0942df8681714a92cddbfb1a146637c02d8b11 Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Mon, 27 Nov 2017 08:48:12 +0100 Subject: [PATCH 057/235] Removed unnecessary private method to update the metadata --- osu.Game/Screens/Select/BeatmapDetails.cs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index f203f5cdc4..892156cc22 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -188,7 +188,9 @@ namespace osu.Game.Screens.Select ratingsContainer.FadeIn(transition_duration); advanced.Beatmap = Beatmap; - loadDetailsAsync(Beatmap); + description.Text = Beatmap.Version; + source.Text = Beatmap.Metadata.Source; + tags.Text = Beatmap.Metadata.Tags; var requestedBeatmap = Beatmap; if (requestedBeatmap.Metrics == null) @@ -212,16 +214,6 @@ namespace osu.Game.Screens.Select displayMetrics(requestedBeatmap.Metrics, false); } - private void loadDetailsAsync(BeatmapInfo beatmap) - { - if (description == null || source == null || tags == null) - throw new InvalidOperationException($@"Requires all {nameof(MetadataSection)} elements to be non-null."); - - description.Text = beatmap.Version; - source.Text = beatmap.Metadata?.Source; - tags.Text = beatmap.Metadata?.Tags; - } - private void displayMetrics(BeatmapMetrics metrics, bool failOnMissing = true) { var hasRatings = metrics?.Ratings?.Any() ?? false; @@ -267,7 +259,10 @@ namespace osu.Game.Screens.Select private void clearStats() { - loadDetailsAsync(null); + description.Text = null; + source.Text = null; + tags.Text = null; + advanced.Beatmap = new BeatmapInfo { StarDifficulty = 0, From 507da0dfb74cf97ae08199e9238a2b078508dd44 Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Mon, 27 Nov 2017 08:48:47 +0100 Subject: [PATCH 058/235] Renamed addTextAsync to setTextAsync --- osu.Game/Screens/Select/BeatmapDetails.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 892156cc22..8bb7b45d7d 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -321,11 +321,11 @@ namespace osu.Game.Screens.Select } this.FadeIn(transition_duration); - addTextAsync(value); + setTextAsync(value); } } - private void addTextAsync(string text) + private void setTextAsync(string text) { var newTextFlow = new TextFlowContainer { From b4513497d68ec54e447e18c1ebe4b8ec6c264a3c Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Mon, 27 Nov 2017 08:52:26 +0100 Subject: [PATCH 059/235] Added a textContainer reference to MetadataSection so casting is no longer required for setting new text --- osu.Game/Screens/Select/BeatmapDetails.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 8bb7b45d7d..2c188319e5 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -308,6 +308,7 @@ namespace osu.Game.Screens.Select private class MetadataSection : Container { + private readonly FillFlowContainer textContainer; private TextFlowContainer textFlow; public string Text @@ -338,7 +339,6 @@ namespace osu.Game.Screens.Select LoadComponentAsync(newTextFlow, d => { - var textContainer = (InternalChild as FillFlowContainer); textContainer.Remove(textFlow); textContainer.Add(textFlow = d); }); @@ -355,7 +355,7 @@ namespace osu.Game.Screens.Select RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - InternalChild = new FillFlowContainer + InternalChild = textContainer = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, From 82a9b57277e72fa83a354de26c493af4d44c6b6d Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Mon, 27 Nov 2017 08:58:09 +0100 Subject: [PATCH 060/235] Removed unnecessary "using" statement --- osu.Game/Screens/Select/BeatmapDetails.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 2c188319e5..3cd6c3b107 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using OpenTK; using OpenTK.Graphics; using osu.Framework.Allocation; From cbd7e1ca0de651a7067aad53053e0d01125d0012 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Nov 2017 18:01:00 +0900 Subject: [PATCH 061/235] Update submodules --- osu-framework | 2 +- osu-resources | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu-framework b/osu-framework index fe49ccb3c8..d92cec7645 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit fe49ccb3c8f8661d653752d225ae1dc183944bb4 +Subproject commit d92cec764538da2e7ed95bfb566f6bc81a9667c8 diff --git a/osu-resources b/osu-resources index 1750ab8f67..4287ee8043 160000 --- a/osu-resources +++ b/osu-resources @@ -1 +1 @@ -Subproject commit 1750ab8f6761ab35592fd46da71fbe0c141bfd93 +Subproject commit 4287ee8043fb1419017359bc3a5db5dc06bc643f From 760f7d02d9f757fb47002a4ea1c2b34699ccca84 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Nov 2017 18:12:52 +0900 Subject: [PATCH 062/235] Remove AlwaysPresent (not actually required) --- osu.Game/Graphics/UserInterface/HoverSounds.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/HoverSounds.cs b/osu.Game/Graphics/UserInterface/HoverSounds.cs index d26ad35c49..24dbe37567 100644 --- a/osu.Game/Graphics/UserInterface/HoverSounds.cs +++ b/osu.Game/Graphics/UserInterface/HoverSounds.cs @@ -26,7 +26,6 @@ namespace osu.Game.Graphics.UserInterface { SampleSet = sampleSet; RelativeSizeAxes = Axes.Both; - AlwaysPresent = true; } protected override bool OnHover(InputState state) From 51372d7cdaf209a868aa92f5847308a77d4f20da Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Nov 2017 18:19:06 +0900 Subject: [PATCH 063/235] Remove HandleInput override from TriangleButton --- osu.Game/Graphics/UserInterface/TriangleButton.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/TriangleButton.cs b/osu.Game/Graphics/UserInterface/TriangleButton.cs index 9fbdf6832f..675e03aef8 100644 --- a/osu.Game/Graphics/UserInterface/TriangleButton.cs +++ b/osu.Game/Graphics/UserInterface/TriangleButton.cs @@ -34,8 +34,6 @@ namespace osu.Game.Graphics.UserInterface Font = @"Exo2.0-Bold", }; - public override bool HandleInput => Action != null; - [BackgroundDependencyLoader] private void load(OsuColour colours) { From 96d42b3e5b6a42ed5947d4485116e2d16f718902 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Nov 2017 18:20:13 +0900 Subject: [PATCH 064/235] Fix redundant string interpolation --- osu.Game/Screens/Select/PlaySongSelect.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 992bfa52ee..bba6ddf577 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -49,7 +49,7 @@ namespace osu.Game.Screens.Select [BackgroundDependencyLoader] private void load(OsuColour colours, AudioManager audio) { - sampleConfirm = audio.Sample.Get($@"SongSelect/confirm-selection"); + sampleConfirm = audio.Sample.Get(@"SongSelect/confirm-selection"); Footer.AddButton(@"mods", colours.Yellow, modSelect, Key.F1, float.MaxValue); From bf5ea027efd9813dbcaf78ba54268ae3da665b8d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Nov 2017 18:26:20 +0900 Subject: [PATCH 065/235] Add xmldoc to TriangleButton --- osu.Game/Graphics/UserInterface/TriangleButton.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Graphics/UserInterface/TriangleButton.cs b/osu.Game/Graphics/UserInterface/TriangleButton.cs index 675e03aef8..61e9705064 100644 --- a/osu.Game/Graphics/UserInterface/TriangleButton.cs +++ b/osu.Game/Graphics/UserInterface/TriangleButton.cs @@ -15,6 +15,9 @@ using osu.Game.Graphics.Sprites; namespace osu.Game.Graphics.UserInterface { + /// + /// A button with moving triangles in the background. + /// public class TriangleButton : OsuButton, IFilterable { private Box hover; From 2c53be7853145c2f699104026572eb3e36449100 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 27 Nov 2017 18:39:13 +0900 Subject: [PATCH 066/235] Fix possible invalid cast when generating mania patterns --- osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index f6d30ad3fa..d5a799b4ed 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -138,8 +138,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps Pattern newPattern = conversion.Generate(); lastPattern = newPattern; - var stairPatternGenerator = (HitObjectPatternGenerator)conversion; - lastStair = stairPatternGenerator.StairType; + var stairPatternGenerator = conversion as HitObjectPatternGenerator; + lastStair = stairPatternGenerator?.StairType ?? lastStair; return newPattern.HitObjects; } From 3a01bfc1ef88b89ef8e214fbd981c716f69dd1a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Nov 2017 18:53:19 +0900 Subject: [PATCH 067/235] Remove unnecessary new prefix --- osu.Game/Overlays/Mods/ModButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModButton.cs b/osu.Game/Overlays/Mods/ModButton.cs index f2a3da4145..77b7c3add2 100644 --- a/osu.Game/Overlays/Mods/ModButton.cs +++ b/osu.Game/Overlays/Mods/ModButton.cs @@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Mods private readonly Container iconsContainer; private SampleChannel sampleOn, sampleOff; - public new Action Action; // Passed the selected mod or null if none + public Action Action; // Passed the selected mod or null if none public string TooltipText => (SelectedMod?.Description ?? Mods.FirstOrDefault()?.Description) ?? string.Empty; From bf8d15108e0085204ff7143cf6c208b04bb35620 Mon Sep 17 00:00:00 2001 From: Santeri Nogelainen Date: Mon, 27 Nov 2017 13:39:01 +0200 Subject: [PATCH 068/235] headerbutton now derives from osubutton --- .../Overlays/BeatmapSet/DownloadButton.cs | 2 +- .../Overlays/BeatmapSet/FavouriteButton.cs | 3 +- osu.Game/Overlays/BeatmapSet/HeaderButton.cs | 42 +++++++++---------- 3 files changed, 24 insertions(+), 23 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/DownloadButton.cs b/osu.Game/Overlays/BeatmapSet/DownloadButton.cs index 18a0cfd968..b256f82a22 100644 --- a/osu.Game/Overlays/BeatmapSet/DownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/DownloadButton.cs @@ -14,10 +14,10 @@ namespace osu.Game.Overlays.BeatmapSet public DownloadButton(string title, string subtitle) { Width = 120; - RelativeSizeAxes = Axes.Y; Child = new Container { + Depth = -1, RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Horizontal = 10 }, Children = new Drawable[] diff --git a/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs b/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs index 9fd4ac177c..af4c56c614 100644 --- a/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs +++ b/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs @@ -17,7 +17,6 @@ namespace osu.Game.Overlays.BeatmapSet public FavouriteButton() { - RelativeSizeAxes = Axes.Y; Container pink; SpriteIcon icon; @@ -25,6 +24,7 @@ namespace osu.Game.Overlays.BeatmapSet { pink = new Container { + Depth = -1, RelativeSizeAxes = Axes.Both, Alpha = 0f, Children = new Drawable[] @@ -45,6 +45,7 @@ namespace osu.Game.Overlays.BeatmapSet }, icon = new SpriteIcon { + Depth = -1, Anchor = Anchor.Centre, Origin = Anchor.Centre, Icon = FontAwesome.fa_heart_o, diff --git a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs index 3075020fe6..4616128f1a 100644 --- a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs +++ b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs @@ -5,12 +5,15 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; +using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Containers; +using osu.Framework.Audio; +using osu.Framework.Allocation; namespace osu.Game.Overlays.BeatmapSet { - public class HeaderButton : OsuClickableContainer + public class HeaderButton : OsuButton { private readonly Container content; @@ -18,28 +21,25 @@ namespace osu.Game.Overlays.BeatmapSet public HeaderButton() { - CornerRadius = 3; - Masking = true; + Height = 0; + RelativeSizeAxes = Axes.Y; - InternalChildren = new Drawable[] + AddInternal(content = new Container { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.FromHex(@"094c5f"), - }, - new Triangles - { - RelativeSizeAxes = Axes.Both, - ColourLight = OsuColour.FromHex(@"0f7c9b"), - ColourDark = OsuColour.FromHex(@"094c5f"), - TriangleScale = 1.5f, - }, - content = new Container - { - RelativeSizeAxes = Axes.Both, - }, - }; + RelativeSizeAxes = Axes.Both + }); } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, AudioManager audio) + { + Masking = true; + CornerRadius = 3; + BackgroundColour = OsuColour.FromHex(@"094c5f"); + Triangles.ColourLight = OsuColour.FromHex(@"0f7c9b"); + Triangles.ColourDark = OsuColour.FromHex(@"094c5f"); + Triangles.TriangleScale = 1.5f; + } + } } From 96f782e75a65e61f6f54c8bf225cf3ffe8b5b816 Mon Sep 17 00:00:00 2001 From: Santeri Nogelainen Date: Mon, 27 Nov 2017 13:51:56 +0200 Subject: [PATCH 069/235] fix object reference issue maybe? --- osu.Game/Overlays/BeatmapSet/HeaderButton.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs index 4616128f1a..2296a4e8c0 100644 --- a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs +++ b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs @@ -36,9 +36,9 @@ namespace osu.Game.Overlays.BeatmapSet Masking = true; CornerRadius = 3; BackgroundColour = OsuColour.FromHex(@"094c5f"); - Triangles.ColourLight = OsuColour.FromHex(@"0f7c9b"); - Triangles.ColourDark = OsuColour.FromHex(@"094c5f"); - Triangles.TriangleScale = 1.5f; + this.Triangles.ColourLight = OsuColour.FromHex(@"0f7c9b"); + this.Triangles.ColourDark = OsuColour.FromHex(@"094c5f"); + this.Triangles.TriangleScale = 1.5f; } } From d87235a2891e64708a7e617af753e420eb4efa3e Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Mon, 27 Nov 2017 20:08:16 +0100 Subject: [PATCH 070/235] prevent inserting duplicate metadata --- osu.Game/Beatmaps/BeatmapStore.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index 3875202e32..ef658d2ff6 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.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 System.Linq; using Microsoft.EntityFrameworkCore; using osu.Game.Database; @@ -32,6 +33,15 @@ namespace osu.Game.Beatmaps { var context = GetContext(); + foreach (var beatmap in beatmapSet.Beatmaps.Where(b => b.Metadata != null)) + { + var contextMetadata = context.Set().Local.SingleOrDefault(e => e.Equals(beatmap.Metadata)); + if (contextMetadata != null) + beatmap.Metadata = contextMetadata; + else + context.BeatmapMetadata.Attach(beatmap.Metadata); + } + context.BeatmapSetInfo.Attach(beatmapSet); context.SaveChanges(); From c058065a3ab78258ec24ef90a33738a4a8b1af69 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Mon, 27 Nov 2017 20:24:01 +0100 Subject: [PATCH 071/235] remove unnecessary using --- osu.Game/Beatmaps/BeatmapStore.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index ef658d2ff6..352f793aac 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using System.Collections.Generic; using System.Linq; using Microsoft.EntityFrameworkCore; using osu.Game.Database; From 7f068c0c683c297d4d1257ebe58eca6db26be55c Mon Sep 17 00:00:00 2001 From: jorolf Date: Mon, 27 Nov 2017 21:13:01 +0100 Subject: [PATCH 072/235] correct string mistake --- .../Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index b7df60a7a2..cec0e9a775 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -13,7 +13,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical public class PaginatedMostPlayedBeatmapContainer : PaginatedContainer { public PaginatedMostPlayedBeatmapContainer(Bindable user) - :base(user, "Most Played Beatmaps", "No performance records. :(") + :base(user, "Most Played Beatmaps", "No records. :(") { ItemsPerPage = 5; From 86de6f8252d52a5f7d327fda28484dbe371813e6 Mon Sep 17 00:00:00 2001 From: Santeri Date: Tue, 28 Nov 2017 00:09:58 +0200 Subject: [PATCH 073/235] derives from trianglebutton rather than osubutton --- osu.Game/Overlays/BeatmapSet/DownloadButton.cs | 4 ++-- osu.Game/Overlays/BeatmapSet/FavouriteButton.cs | 4 ++-- osu.Game/Overlays/BeatmapSet/HeaderButton.cs | 16 ++++------------ 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/DownloadButton.cs b/osu.Game/Overlays/BeatmapSet/DownloadButton.cs index b256f82a22..47787d2ced 100644 --- a/osu.Game/Overlays/BeatmapSet/DownloadButton.cs +++ b/osu.Game/Overlays/BeatmapSet/DownloadButton.cs @@ -15,7 +15,7 @@ namespace osu.Game.Overlays.BeatmapSet { Width = 120; - Child = new Container + Add(new Container { Depth = -1, RelativeSizeAxes = Axes.Both, @@ -53,7 +53,7 @@ namespace osu.Game.Overlays.BeatmapSet Margin = new MarginPadding { Right = 5 }, }, }, - }; + }); } } } diff --git a/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs b/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs index af4c56c614..77be4da5f8 100644 --- a/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs +++ b/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs @@ -20,7 +20,7 @@ namespace osu.Game.Overlays.BeatmapSet Container pink; SpriteIcon icon; - Children = new Drawable[] + AddRange(new Drawable[] { pink = new Container { @@ -52,7 +52,7 @@ namespace osu.Game.Overlays.BeatmapSet Size = new Vector2(18), Shadow = false, }, - }; + }); Favourited.ValueChanged += value => { diff --git a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs index 2296a4e8c0..6c7edb252e 100644 --- a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs +++ b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs @@ -13,21 +13,13 @@ using osu.Framework.Allocation; namespace osu.Game.Overlays.BeatmapSet { - public class HeaderButton : OsuButton + public class HeaderButton : TriangleButton { - private readonly Container content; - - protected override Container Content => content; public HeaderButton() { Height = 0; RelativeSizeAxes = Axes.Y; - - AddInternal(content = new Container - { - RelativeSizeAxes = Axes.Both - }); } [BackgroundDependencyLoader] @@ -36,9 +28,9 @@ namespace osu.Game.Overlays.BeatmapSet Masking = true; CornerRadius = 3; BackgroundColour = OsuColour.FromHex(@"094c5f"); - this.Triangles.ColourLight = OsuColour.FromHex(@"0f7c9b"); - this.Triangles.ColourDark = OsuColour.FromHex(@"094c5f"); - this.Triangles.TriangleScale = 1.5f; + Triangles.ColourLight = OsuColour.FromHex(@"0f7c9b"); + Triangles.ColourDark = OsuColour.FromHex(@"094c5f"); + Triangles.TriangleScale = 1.5f; } } From 49949bf6984d712a0e3267f7f23350180983888d Mon Sep 17 00:00:00 2001 From: Santeri Date: Tue, 28 Nov 2017 00:20:44 +0200 Subject: [PATCH 074/235] fix minor param/directive errors --- osu.Game/Overlays/BeatmapSet/HeaderButton.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs index 6c7edb252e..cfcf26d0bd 100644 --- a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs +++ b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs @@ -2,12 +2,8 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; -using osu.Game.Graphics.Backgrounds; -using osu.Game.Graphics.Containers; using osu.Framework.Audio; using osu.Framework.Allocation; @@ -23,7 +19,7 @@ namespace osu.Game.Overlays.BeatmapSet } [BackgroundDependencyLoader] - private void load(OsuColour colours, AudioManager audio) + private void load() { Masking = true; CornerRadius = 3; From e9277d8cb1e1d1d0db6b9cfbc638f34a6c3fe346 Mon Sep 17 00:00:00 2001 From: Santeri Date: Tue, 28 Nov 2017 00:27:58 +0200 Subject: [PATCH 075/235] last directive error... --- osu.Game/Overlays/BeatmapSet/HeaderButton.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs index cfcf26d0bd..371e438a34 100644 --- a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs +++ b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs @@ -4,7 +4,6 @@ using osu.Framework.Graphics; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; -using osu.Framework.Audio; using osu.Framework.Allocation; namespace osu.Game.Overlays.BeatmapSet From 473eba9776e1d8cfcb54ca65f9a8f7619202c168 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Nov 2017 12:09:44 +0900 Subject: [PATCH 076/235] Remove precision limitation on chat height to allow pixel-perfect dragging --- osu.Game/Configuration/OsuConfigManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index c087a5afb7..1a7d29e907 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -22,7 +22,7 @@ namespace osu.Game.Configuration Set(OsuSetting.SelectionRandomType, SelectionRandomType.RandomPermutation); - Set(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2, 1, 0.01); + Set(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2, 1); // Online settings Set(OsuSetting.Username, string.Empty); From 84702211ecd93318003af357324386f0267a6d5f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Nov 2017 15:17:02 +0900 Subject: [PATCH 077/235] Rewrite mania auto generator to properly account for overlapping objects --- .../Replays/ManiaAutoGenerator.cs | 109 +++++------------- 1 file changed, 29 insertions(+), 80 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs index 64982532a7..35ca868d5a 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.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 System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Objects; @@ -13,7 +14,7 @@ namespace osu.Game.Rulesets.Mania.Replays { internal class ManiaAutoGenerator : AutoGenerator { - private const double release_delay = 20; + public const double RELEASE_DELAY = 20; private readonly int availableColumns; @@ -32,102 +33,50 @@ namespace osu.Game.Rulesets.Mania.Replays // Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled Replay.Frames.Add(new ReplayFrame(-100000, null, null, ReplayButtonState.None)); - double[] holdEndTimes = new double[availableColumns]; - for (int i = 0; i < availableColumns; i++) - holdEndTimes[i] = double.NegativeInfinity; + var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time); - // Notes are handled row-by-row - foreach (var objGroup in Beatmap.HitObjects.GroupBy(h => h.StartTime)) + int activeColumns = 0; + foreach (var group in pointGroups) { - double groupTime = objGroup.Key; - - int activeColumns = 0; - - // Get the previously held-down active columns - for (int i = 0; i < availableColumns; i++) + foreach (var point in group) { - if (holdEndTimes[i] > groupTime) - activeColumns |= 1 << i; + if (point is HitPoint) + activeColumns |= 1 << point.Column; + if (point is ReleasePoint) + activeColumns ^= 1 << point.Column; } - // Add on the group columns, keeping track of the held notes for the next rows - foreach (var obj in objGroup) - { - var holdNote = obj as HoldNote; - if (holdNote != null) - holdEndTimes[obj.Column] = Math.Max(holdEndTimes[obj.Column], holdNote.EndTime); - - activeColumns |= 1 << obj.Column; - } - - Replay.Frames.Add(new ReplayFrame(groupTime, activeColumns, null, ReplayButtonState.None)); - - // Add the release frames. We can't do this with the loop above because we need activeColumns to be fully populated - foreach (var obj in objGroup.GroupBy(h => (h as IHasEndTime)?.EndTime ?? h.StartTime + release_delay).OrderBy(h => h.Key)) - { - var groupEndTime = obj.Key; - - int activeColumnsAtEnd = 0; - for (int i = 0; i < availableColumns; i++) - { - if (holdEndTimes[i] > groupEndTime) - activeColumnsAtEnd |= 1 << i; - } - - Replay.Frames.Add(new ReplayFrame(groupEndTime, activeColumnsAtEnd, 0, ReplayButtonState.None)); - } + Replay.Frames.Add(new ReplayFrame(group.First().Time, activeColumns, 0, ReplayButtonState.None)); } - Replay.Frames = Replay.Frames - // Pick the maximum activeColumns for all frames at the same time - .GroupBy(f => f.Time) - .Select(g => new ReplayFrame(g.First().Time, maxMouseX(g), 0, ReplayButtonState.None)) - // The addition of release frames above maybe result in unordered frames, but we need them ordered - .OrderBy(f => f.Time) - .ToList(); - return Replay; } - /// - /// Finds the maximum by count of bits from a grouping of s. - /// - /// The grouping to search. - /// The maximum by count of bits. - private float maxMouseX(IGrouping group) + private IEnumerable generateActionPoints() { - int currentCount = -1; - int currentMax = 0; - - foreach (var val in group) + foreach (var obj in Beatmap.HitObjects) { - int newCount = countBits((int)(val.MouseX ?? 0)); - if (newCount > currentCount) - { - currentCount = newCount; - currentMax = (int)(val.MouseX ?? 0); - } + yield return new HitPoint { Time = obj.StartTime, Column = obj.Column }; + yield return new ReleasePoint { Time = ((obj as IHasEndTime)?.EndTime ?? obj.StartTime) + RELEASE_DELAY, Column = obj.Column }; } - - return currentMax; } - /// - /// Counts the number of bits set in a value. - /// - /// The value to count. - /// The number of set bits. - private int countBits(int value) + private interface IActionPoint { - int count = 0; - while (value > 0) - { - if ((value & 1) > 0) - count++; - value >>= 1; - } + double Time { get; set; } + int Column { get; set; } + } - return count; + private struct HitPoint : IActionPoint + { + public double Time { get; set; } + public int Column { get; set; } + } + + private struct ReleasePoint : IActionPoint + { + public double Time { get; set; } + public int Column { get; set; } } } } From b97cab4f29166a03a46ae9e415a60432b79d34fa Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Nov 2017 15:27:20 +0900 Subject: [PATCH 078/235] Make ManiaFramedReplayInputHandler properly account for special keys These are interleaved into the playfield, so we have to use the playfield's columns' actions. --- .../Replays/ManiaFramedReplayInputHandler.cs | 16 +++++++++++++--- .../UI/ManiaRulesetContainer.cs | 2 +- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs b/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs index e352997f2c..8440e4347d 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs @@ -2,29 +2,39 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using System.Linq; using osu.Framework.Input; +using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Replays; namespace osu.Game.Rulesets.Mania.Replays { internal class ManiaFramedReplayInputHandler : FramedReplayInputHandler { - public ManiaFramedReplayInputHandler(Replay replay) + private readonly ManiaRulesetContainer container; + + public ManiaFramedReplayInputHandler(Replay replay, ManiaRulesetContainer container) : base(replay) { + this.container = container; } + protected override bool AtImportantFrame => CurrentFrame.MouseX != PreviousFrame.MouseX; + + private ManiaPlayfield playfield; public override List GetPendingStates() { var actions = new List(); - int activeColumns = (int)(CurrentFrame.MouseX ?? 0); + if (playfield == null) + playfield = (ManiaPlayfield)container.Playfield; + int activeColumns = (int)(CurrentFrame.MouseX ?? 0); int counter = 0; while (activeColumns > 0) { if ((activeColumns & 1) > 0) - actions.Add(ManiaAction.Key1 + counter); + actions.Add(playfield.Columns.ElementAt(counter).Action); counter++; activeColumns >>= 1; } diff --git a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs index 08acd46c57..cbbcb84b31 100644 --- a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs +++ b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs @@ -124,6 +124,6 @@ namespace osu.Game.Rulesets.Mania.UI protected override SpeedAdjustmentContainer CreateSpeedAdjustmentContainer(MultiplierControlPoint controlPoint) => new ManiaSpeedAdjustmentContainer(controlPoint, ScrollingAlgorithm.Basic); - protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay); + protected override FramedReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay, this); } } From bd88df2722a589e4d7e7454a9c390937cb3d3193 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Nov 2017 14:39:39 +0900 Subject: [PATCH 079/235] Add note about sequential execution of ConvertHitObject --- osu.Game/Beatmaps/BeatmapConverter.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs index 962c790fb2..6132322f1e 100644 --- a/osu.Game/Beatmaps/BeatmapConverter.cs +++ b/osu.Game/Beatmaps/BeatmapConverter.cs @@ -80,6 +80,7 @@ namespace osu.Game.Beatmaps /// /// Performs the conversion of a hit object. + /// This method generally executed sequentially for all objects in a beatmap. /// /// The hit object to convert. /// The un-converted Beatmap. From 2deb33ac4190a0e2e43975199e5b4039a95606de Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Nov 2017 14:49:38 +0900 Subject: [PATCH 080/235] Add basic fruit scaling support --- osu.Game.Rulesets.Catch/Objects/CatchBaseHit.cs | 11 +++++++++++ .../Objects/Drawable/DrawableCatchHitObject.cs | 3 +++ 2 files changed, 14 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Objects/CatchBaseHit.cs b/osu.Game.Rulesets.Catch/Objects/CatchBaseHit.cs index 2f33cf1093..aac25a1fa7 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchBaseHit.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchBaseHit.cs @@ -1,6 +1,8 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using OpenTK.Graphics; @@ -20,5 +22,14 @@ namespace osu.Game.Rulesets.Catch.Objects /// The next fruit starts a new combo. Used for explodey. /// public virtual bool LastInCombo { get; set; } + + public float Scale { get; set; } = 1; + + public override void ApplyDefaults(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) + { + base.ApplyDefaults(controlPointInfo, difficulty); + + 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 e057bf3d8e..02efd81da3 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Graphics; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Drawables; +using OpenTK; namespace osu.Game.Rulesets.Catch.Objects.Drawable { @@ -17,6 +18,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable : base(hitObject) { HitObject = hitObject; + + Scale = new Vector2(HitObject.Scale); } } From 8f3fd7092e3692e31cb9e13d03fe3fe0306fd8b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Nov 2017 18:37:41 +0900 Subject: [PATCH 081/235] CatchBaseHit -> CatchHitObject Also moves default scale to CatchHitObject. --- osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs | 4 ++-- osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs | 6 +++--- osu.Game.Rulesets.Catch/CatchDifficultyCalculator.cs | 4 ++-- .../Objects/{CatchBaseHit.cs => CatchHitObject.cs} | 4 +++- .../Objects/Drawable/DrawableCatchHitObject.cs | 8 ++++---- .../Objects/Drawable/DrawableJuiceStream.cs | 4 ++-- osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs | 2 +- osu.Game.Rulesets.Catch/Objects/Droplet.cs | 2 +- osu.Game.Rulesets.Catch/Objects/Fruit.cs | 2 +- osu.Game.Rulesets.Catch/Objects/JuiceStream.cs | 6 +++--- osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs | 6 +++--- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 2 +- osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs | 8 ++++---- osu.Game.Rulesets.Catch/UI/Catcher.cs | 2 +- osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj | 2 +- 15 files changed, 32 insertions(+), 30 deletions(-) rename osu.Game.Rulesets.Catch/Objects/{CatchBaseHit.cs => CatchHitObject.cs} (86%) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs index 7126b6586d..6b9ec8b9a4 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs @@ -11,11 +11,11 @@ using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Catch.Beatmaps { - internal class CatchBeatmapConverter : BeatmapConverter + internal class CatchBeatmapConverter : BeatmapConverter { protected override IEnumerable ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) }; - protected override IEnumerable ConvertHitObject(HitObject obj, Beatmap beatmap) + protected override IEnumerable ConvertHitObject(HitObject obj, Beatmap beatmap) { var curveData = obj as IHasCurve; var positionData = obj as IHasXPosition; diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 7fac19d135..b2f7fdabfc 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -6,9 +6,9 @@ using osu.Game.Rulesets.Catch.Objects; namespace osu.Game.Rulesets.Catch.Beatmaps { - internal class CatchBeatmapProcessor : BeatmapProcessor + internal class CatchBeatmapProcessor : BeatmapProcessor { - public override void PostProcess(Beatmap beatmap) + public override void PostProcess(Beatmap beatmap) { if (beatmap.ComboColors.Count == 0) return; @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps int comboIndex = 0; int colourIndex = 0; - CatchBaseHit lastObj = null; + CatchHitObject lastObj = null; foreach (var obj in beatmap.HitObjects) { diff --git a/osu.Game.Rulesets.Catch/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/CatchDifficultyCalculator.cs index b77be9d1f0..4740458dfb 100644 --- a/osu.Game.Rulesets.Catch/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/CatchDifficultyCalculator.cs @@ -8,7 +8,7 @@ using System.Collections.Generic; namespace osu.Game.Rulesets.Catch { - public class CatchDifficultyCalculator : DifficultyCalculator + public class CatchDifficultyCalculator : DifficultyCalculator { public CatchDifficultyCalculator(Beatmap beatmap) : base(beatmap) { @@ -16,6 +16,6 @@ namespace osu.Game.Rulesets.Catch public override double Calculate(Dictionary categoryDifficulty = null) => 0; - protected override BeatmapConverter CreateBeatmapConverter(Beatmap beatmap) => new CatchBeatmapConverter(); + protected override BeatmapConverter CreateBeatmapConverter(Beatmap beatmap) => new CatchBeatmapConverter(); } } diff --git a/osu.Game.Rulesets.Catch/Objects/CatchBaseHit.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs similarity index 86% rename from osu.Game.Rulesets.Catch/Objects/CatchBaseHit.cs rename to osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index aac25a1fa7..cb4e6453ce 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchBaseHit.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -9,8 +9,10 @@ using OpenTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects { - public abstract class CatchBaseHit : HitObject, IHasXPosition, IHasCombo + public abstract class CatchHitObject : HitObject, IHasXPosition, IHasCombo { + public const double OBJECT_RADIUS = 44; + public float X { get; set; } public Color4 ComboColour { get; set; } = Color4.Gray; diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs index 02efd81da3..b90a06b94e 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs @@ -10,7 +10,7 @@ using OpenTK; namespace osu.Game.Rulesets.Catch.Objects.Drawable { public abstract class DrawableCatchHitObject : DrawableCatchHitObject - where TObject : CatchBaseHit + where TObject : CatchHitObject { public new TObject HitObject; @@ -23,9 +23,9 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable } } - public abstract class DrawableCatchHitObject : DrawableScrollingHitObject + public abstract class DrawableCatchHitObject : DrawableScrollingHitObject { - protected DrawableCatchHitObject(CatchBaseHit hitObject) + protected DrawableCatchHitObject(CatchHitObject hitObject) : base(hitObject) { RelativePositionAxes = Axes.Both; @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable Y = (float)HitObject.StartTime; } - public Func CheckPosition; + public Func CheckPosition; protected override void CheckForJudgements(bool userTriggered, double timeOffset) { diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs index afda91d0b4..bfb674d1b4 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable RelativeChildSize = new Vector2(1, (float)HitObject.Duration) }; - foreach (CatchBaseHit tick in s.Ticks) + foreach (CatchHitObject tick in s.Ticks) { TinyDroplet tiny = tick as TinyDroplet; if (tiny != null) @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable } } - protected override void AddNested(DrawableHitObject h) + protected override void AddNested(DrawableHitObject h) { ((DrawableCatchHitObject)h).CheckPosition = o => CheckPosition?.Invoke(o) ?? false; dropletContainer.Add(h); diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs index 00ddd365e3..2de266b3f0 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/Pieces/Pulp.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable.Pieces { public class Pulp : Circle, IHasAccentColour { - public const float PULP_SIZE = 20; + public const float PULP_SIZE = (float)CatchHitObject.OBJECT_RADIUS / 2.2f; public Pulp() { diff --git a/osu.Game.Rulesets.Catch/Objects/Droplet.cs b/osu.Game.Rulesets.Catch/Objects/Droplet.cs index b1206e0d75..a2bdf830e5 100644 --- a/osu.Game.Rulesets.Catch/Objects/Droplet.cs +++ b/osu.Game.Rulesets.Catch/Objects/Droplet.cs @@ -3,7 +3,7 @@ namespace osu.Game.Rulesets.Catch.Objects { - public class Droplet : CatchBaseHit + public class Droplet : CatchHitObject { } } diff --git a/osu.Game.Rulesets.Catch/Objects/Fruit.cs b/osu.Game.Rulesets.Catch/Objects/Fruit.cs index fc55f83969..5f1060fb51 100644 --- a/osu.Game.Rulesets.Catch/Objects/Fruit.cs +++ b/osu.Game.Rulesets.Catch/Objects/Fruit.cs @@ -3,7 +3,7 @@ namespace osu.Game.Rulesets.Catch.Objects { - public class Fruit : CatchBaseHit + public class Fruit : CatchHitObject { } } diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index 6462f6f6a8..bf9f0bd44b 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -15,7 +15,7 @@ using osu.Framework.Lists; namespace osu.Game.Rulesets.Catch.Objects { - public class JuiceStream : CatchBaseHit, IHasCurve + public class JuiceStream : CatchHitObject, IHasCurve { /// /// Positional distance that results in a duration of one second, before any speed adjustments. @@ -42,11 +42,11 @@ namespace osu.Game.Rulesets.Catch.Objects TickDistance = scoringDistance / difficulty.SliderTickRate; } - public IEnumerable Ticks + public IEnumerable Ticks { get { - SortedList ticks = new SortedList((a, b) => a.StartTime.CompareTo(b.StartTime)); + SortedList ticks = new SortedList((a, b) => a.StartTime.CompareTo(b.StartTime)); if (TickDistance == 0) return ticks; diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs index 66a5636b74..0806c4b29d 100644 --- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs @@ -10,14 +10,14 @@ using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Catch.Scoring { - internal class CatchScoreProcessor : ScoreProcessor + internal class CatchScoreProcessor : ScoreProcessor { - public CatchScoreProcessor(RulesetContainer rulesetContainer) + public CatchScoreProcessor(RulesetContainer rulesetContainer) : base(rulesetContainer) { } - protected override void SimulateAutoplay(Beatmap beatmap) + protected override void SimulateAutoplay(Beatmap beatmap) { foreach (var obj in beatmap.HitObjects) { diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 987eef5e45..33903f649e 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.Catch.UI catcher.Size = new Vector2(catcherContainer.DrawSize.Y); } - public bool CheckIfWeCanCatch(CatchBaseHit obj) => Math.Abs(catcher.Position.X - obj.X) < catcher.DrawSize.X / DrawSize.X / 2; + public bool CheckIfWeCanCatch(CatchHitObject obj) => Math.Abs(catcher.Position.X - obj.X) < catcher.DrawSize.X / DrawSize.X / 2; public override void Add(DrawableHitObject h) { diff --git a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs index 92912eb177..fda63fa977 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs @@ -13,7 +13,7 @@ using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Catch.UI { - public class CatchRulesetContainer : ScrollingRulesetContainer + public class CatchRulesetContainer : ScrollingRulesetContainer { public CatchRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) : base(ruleset, beatmap, isForCurrentRuleset) @@ -22,15 +22,15 @@ namespace osu.Game.Rulesets.Catch.UI public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this); - protected override BeatmapProcessor CreateBeatmapProcessor() => new CatchBeatmapProcessor(); + protected override BeatmapProcessor CreateBeatmapProcessor() => new CatchBeatmapProcessor(); - protected override BeatmapConverter CreateBeatmapConverter() => new CatchBeatmapConverter(); + protected override BeatmapConverter CreateBeatmapConverter() => new CatchBeatmapConverter(); protected override Playfield CreatePlayfield() => new CatchPlayfield(); public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo); - protected override DrawableHitObject GetVisualRepresentation(CatchBaseHit h) + protected override DrawableHitObject GetVisualRepresentation(CatchHitObject h) { var fruit = h as Fruit; if (fruit != null) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 87fe95ed2f..3a32b84639 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -157,7 +157,7 @@ namespace osu.Game.Rulesets.Catch.UI caughtFruit.Add(fruit); - if (((CatchBaseHit)fruit.HitObject).LastInCombo) + if (((CatchHitObject)fruit.HitObject).LastInCombo) explode(); } diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index a666984b95..e8763ec5d3 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -57,7 +57,7 @@ - + From b517523f4a602161708b0148319f489009d99fac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Nov 2017 18:39:45 +0900 Subject: [PATCH 082/235] Add back CatcherArea to simplify layout --- ...tCaseCatcher.cs => TestCaseCatcherArea.cs} | 12 +- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 24 +- .../UI/CatchRulesetContainer.cs | 2 +- osu.Game.Rulesets.Catch/UI/Catcher.cs | 193 -------------- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 238 ++++++++++++++++++ .../osu.Game.Rulesets.Catch.csproj | 4 +- 6 files changed, 252 insertions(+), 221 deletions(-) rename osu.Game.Rulesets.Catch/Tests/{TestCaseCatcher.cs => TestCaseCatcherArea.cs} (71%) delete mode 100644 osu.Game.Rulesets.Catch/UI/Catcher.cs create mode 100644 osu.Game.Rulesets.Catch/UI/CatcherArea.cs diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseCatcher.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs similarity index 71% rename from osu.Game.Rulesets.Catch/Tests/TestCaseCatcher.cs rename to osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs index 341612b760..21c24aed93 100644 --- a/osu.Game.Rulesets.Catch/Tests/TestCaseCatcher.cs +++ b/osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs @@ -8,17 +8,16 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Rulesets.Catch.UI; using osu.Game.Tests.Visual; -using OpenTK; namespace osu.Game.Rulesets.Catch.Tests { [TestFixture] [Ignore("getting CI working")] - internal class TestCaseCatcher : OsuTestCase + internal class TestCaseCatcherArea : OsuTestCase { public override IReadOnlyList RequiredTypes => new[] { - typeof(Catcher), + typeof(CatcherArea), }; [BackgroundDependencyLoader] @@ -29,13 +28,10 @@ namespace osu.Game.Rulesets.Catch.Tests new CatchInputManager(rulesets.GetRuleset(2)) { RelativeSizeAxes = Axes.Both, - Child = new Catcher + Child = new CatcherArea() { - RelativePositionAxes = Axes.Both, - RelativeSizeAxes = Axes.Both, Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Size = new Vector2(1, 0.2f), + Origin = Anchor.BottomLeft } }, }; diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs index 33903f649e..f59ba13edd 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -1,11 +1,11 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using osu.Framework.Graphics; using osu.Game.Rulesets.UI; using OpenTK; using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawable; using osu.Game.Rulesets.Judgements; @@ -20,10 +20,9 @@ namespace osu.Game.Rulesets.Catch.UI protected override Container Content => content; private readonly Container content; - private readonly Container catcherContainer; - private readonly Catcher catcher; + private readonly CatcherArea catcherArea; - public CatchPlayfield() + public CatchPlayfield(BeatmapDifficulty difficulty) : base(Axes.Y) { Container explodingFruitContainer; @@ -43,19 +42,11 @@ namespace osu.Game.Rulesets.Catch.UI { RelativeSizeAxes = Axes.Both, }, - catcherContainer = new Container + catcherArea = new CatcherArea(difficulty) { - RelativeSizeAxes = Axes.X, + ExplodingFruitTarget = explodingFruitContainer, Anchor = Anchor.BottomLeft, Origin = Anchor.TopLeft, - Height = 180, - Child = catcher = new Catcher - { - ExplodingFruitTarget = explodingFruitContainer, - RelativePositionAxes = Axes.Both, - Origin = Anchor.TopCentre, - X = 0.5f, - } } }; } @@ -63,10 +54,9 @@ namespace osu.Game.Rulesets.Catch.UI protected override void Update() { base.Update(); - catcher.Size = new Vector2(catcherContainer.DrawSize.Y); } - public bool CheckIfWeCanCatch(CatchHitObject obj) => Math.Abs(catcher.Position.X - obj.X) < catcher.DrawSize.X / DrawSize.X / 2; + public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.CanCatch(obj); public override void Add(DrawableHitObject h) { @@ -88,7 +78,7 @@ namespace osu.Game.Rulesets.Catch.UI (judgedObject.Parent as Container)?.Remove(judgedObject); (judgedObject.Parent as Container)?.Remove(judgedObject); - catcher.Add(judgedObject, screenPosition); + catcherArea.Add(judgedObject, screenPosition); } } } diff --git a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs index fda63fa977..3ed9090098 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Catch.UI protected override BeatmapConverter CreateBeatmapConverter() => new CatchBeatmapConverter(); - protected override Playfield CreatePlayfield() => new CatchPlayfield(); + protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty); public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo); diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs deleted file mode 100644 index 3a32b84639..0000000000 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ /dev/null @@ -1,193 +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.Linq; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; -using osu.Framework.Input.Bindings; -using osu.Framework.MathUtils; -using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Objects.Drawables; -using OpenTK; - -namespace osu.Game.Rulesets.Catch.UI -{ - public class Catcher : Container, IKeyBindingHandler - { - private Texture texture; - - private Container caughtFruit; - - public Container ExplodingFruitTarget; - - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - texture = textures.Get(@"Play/Catch/fruit-catcher-idle"); - - Children = new Drawable[] - { - createCatcherSprite(), - caughtFruit = new Container - { - Anchor = Anchor.TopCentre, - Origin = Anchor.BottomCentre, - } - }; - } - - private int currentDirection; - - private bool dashing; - - protected bool Dashing - { - get { return dashing; } - set - { - if (value == dashing) return; - - dashing = value; - - if (dashing) - Schedule(addAdditiveSprite); - } - } - - private void addAdditiveSprite() - { - if (!dashing) return; - - var additive = createCatcherSprite(); - - additive.RelativePositionAxes = Axes.Both; - additive.Blending = BlendingMode.Additive; - additive.Position = Position; - additive.Scale = Scale; - - ((Container)Parent).Add(additive); - - additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint).Expire(); - - Scheduler.AddDelayed(addAdditiveSprite, 50); - } - - private Sprite createCatcherSprite() => new Sprite - { - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit, - Texture = texture, - OriginPosition = new Vector2(DrawWidth / 2, 10) //temporary until the sprite is aligned correctly. - }; - - public bool OnPressed(CatchAction action) - { - switch (action) - { - case CatchAction.MoveLeft: - currentDirection--; - return true; - case CatchAction.MoveRight: - currentDirection++; - return true; - case CatchAction.Dash: - Dashing = true; - return true; - } - - return false; - } - - public bool OnReleased(CatchAction action) - { - switch (action) - { - case CatchAction.MoveLeft: - currentDirection++; - return true; - case CatchAction.MoveRight: - currentDirection--; - return true; - case CatchAction.Dash: - Dashing = false; - return true; - } - - return false; - } - - /// - /// The relative space to cover in 1 millisecond. based on 1 game pixel per millisecond as in osu-stable. - /// - private const double base_speed = 1.0 / 512; - - protected override void Update() - { - base.Update(); - - if (currentDirection == 0) return; - - double dashModifier = Dashing ? 1 : 0.5; - - Scale = new Vector2(Math.Sign(currentDirection), 1); - X = (float)MathHelper.Clamp(X + Math.Sign(currentDirection) * Clock.ElapsedFrameTime * base_speed * dashModifier, 0, 1); - } - - public void Add(DrawableHitObject fruit, Vector2 absolutePosition) - { - fruit.RelativePositionAxes = Axes.None; - fruit.Position = new Vector2(ToLocalSpace(absolutePosition).X - DrawSize.X / 2, 0); - - fruit.Anchor = Anchor.TopCentre; - fruit.Origin = Anchor.BottomCentre; - fruit.Scale *= 0.7f; - fruit.LifetimeEnd = double.MaxValue; - - float distance = fruit.DrawSize.X / 2 * fruit.Scale.X; - - while (caughtFruit.Any(f => f.LifetimeEnd == double.MaxValue && Vector2Extensions.DistanceSquared(f.Position, fruit.Position) < distance * distance)) - { - fruit.X += RNG.Next(-5, 5); - fruit.Y -= RNG.Next(0, 5); - } - - caughtFruit.Add(fruit); - - if (((CatchHitObject)fruit.HitObject).LastInCombo) - explode(); - } - - private void explode() - { - var fruit = caughtFruit.ToArray(); - - foreach (var f in fruit) - { - var originalX = f.X * Scale.X; - - if (ExplodingFruitTarget != null) - { - f.Anchor = Anchor.TopLeft; - f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget); - - caughtFruit.Remove(f); - - ExplodingFruitTarget.Add(f); - } - - f.MoveToY(f.Y - 50, 250, Easing.OutSine) - .Then() - .MoveToY(f.Y + 50, 500, Easing.InSine); - - f.MoveToX(f.X + originalX * 6, 1000); - f.FadeOut(750); - - f.Expire(); - } - } - } -} diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs new file mode 100644 index 0000000000..1cc810201f --- /dev/null +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -0,0 +1,238 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Input.Bindings; +using osu.Framework.MathUtils; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Objects.Drawables; +using OpenTK; + +namespace osu.Game.Rulesets.Catch.UI +{ + public class CatcherArea : Container + { + public const float CATCHER_SIZE = 172; + + private readonly Catcher catcher; + + public Container ExplodingFruitTarget + { + set { catcher.ExplodingFruitTarget = value; } + } + + public CatcherArea(BeatmapDifficulty difficulty = null) + { + RelativeSizeAxes = Axes.X; + Height = CATCHER_SIZE; + Child = catcher = new Catcher(difficulty) + { + AdditiveTarget = this, + }; + } + + 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.Anchor = Anchor.TopCentre; + fruit.Origin = Anchor.BottomCentre; + fruit.Scale *= 0.7f; + fruit.LifetimeEnd = double.MaxValue; + + catcher.Add(fruit); + } + + public bool CanCatch(CatchHitObject obj) => Math.Abs(catcher.Position.X - obj.X) < catcher.DrawSize.X / DrawSize.X / 2; + + public class Catcher : Container, IKeyBindingHandler + { + private Texture texture; + + private Container caughtFruit; + + public Container ExplodingFruitTarget; + + public Container AdditiveTarget; + + public Catcher(BeatmapDifficulty difficulty = null) + { + RelativePositionAxes = Axes.X; + X = 0.5f; + + Origin = Anchor.BottomCentre; + Anchor = Anchor.BottomLeft; + + Size = new Vector2(CATCHER_SIZE); + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + texture = textures.Get(@"Play/Catch/fruit-catcher-idle"); + + Children = new Drawable[] + { + createCatcherSprite(), + caughtFruit = new Container + { + Anchor = Anchor.TopCentre, + Origin = Anchor.BottomCentre, + } + }; + } + + private int currentDirection; + + private bool dashing; + + protected bool Dashing + { + get { return dashing; } + set + { + if (value == dashing) return; + + dashing = value; + + if (dashing) + Schedule(addAdditiveSprite); + } + } + + private void addAdditiveSprite() + { + if (!dashing || AdditiveTarget == null) return; + + var additive = createCatcherSprite(); + + additive.Anchor = Anchor; + additive.OriginPosition = additive.OriginPosition + new Vector2(DrawWidth / 2, DrawHeight); // also temporary to align sprite correctly. + additive.Position = Position; + additive.Scale = Scale; + additive.RelativePositionAxes = RelativePositionAxes; + additive.Blending = BlendingMode.Additive; + + AdditiveTarget.Add(additive); + + additive.FadeTo(0.4f).FadeOut(800, Easing.OutQuint).Expire(); + + Scheduler.AddDelayed(addAdditiveSprite, 50); + } + + private Sprite createCatcherSprite() => new Sprite + { + Size = new Vector2(CATCHER_SIZE), + FillMode = FillMode.Fill, + Texture = texture, + OriginPosition = new Vector2(-3, 10) // temporary until the sprite is aligned correctly. + }; + + public void Add(DrawableHitObject fruit) + { + float distance = fruit.DrawSize.X / 2 * fruit.Scale.X; + + while (caughtFruit.Any(f => f.LifetimeEnd == double.MaxValue && Vector2Extensions.DistanceSquared(f.Position, fruit.Position) < distance * distance)) + { + fruit.X += RNG.Next(-5, 5); + fruit.Y -= RNG.Next(0, 5); + } + + caughtFruit.Add(fruit); + + if (((CatchHitObject)fruit.HitObject).LastInCombo) + explode(); + } + + public bool OnPressed(CatchAction action) + { + switch (action) + { + case CatchAction.MoveLeft: + currentDirection--; + return true; + case CatchAction.MoveRight: + currentDirection++; + return true; + case CatchAction.Dash: + Dashing = true; + return true; + } + + return false; + } + + public bool OnReleased(CatchAction action) + { + switch (action) + { + case CatchAction.MoveLeft: + currentDirection++; + return true; + case CatchAction.MoveRight: + currentDirection--; + return true; + case CatchAction.Dash: + Dashing = false; + return true; + } + + return false; + } + + /// + /// The relative space to cover in 1 millisecond. based on 1 game pixel per millisecond as in osu-stable. + /// + public const double BASE_SPEED = 1.0 / 512; + + protected override void Update() + { + base.Update(); + + if (currentDirection == 0) return; + + double dashModifier = Dashing ? 1 : 0.5; + + Scale = new Vector2(Math.Sign(currentDirection), 1); + X = (float)MathHelper.Clamp(X + Math.Sign(currentDirection) * Clock.ElapsedFrameTime * BASE_SPEED * dashModifier, 0, 1); + } + + private void explode() + { + var fruit = caughtFruit.ToArray(); + + foreach (var f in fruit) + { + var originalX = f.X * Scale.X; + + if (ExplodingFruitTarget != null) + { + f.Anchor = Anchor.TopLeft; + f.Position = caughtFruit.ToSpaceOfOtherDrawable(f.DrawPosition, ExplodingFruitTarget); + + caughtFruit.Remove(f); + + ExplodingFruitTarget.Add(f); + } + + f.MoveToY(f.Y - 50, 250, Easing.OutSine) + .Then() + .MoveToY(f.Y + 50, 500, Easing.InSine); + + f.MoveToX(f.X + originalX * 6, 1000); + f.FadeOut(750); + + f.Expire(); + } + } + } + } +} diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index e8763ec5d3..bf60bc01bb 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -63,10 +63,10 @@ - + - + From 5ae9b4c79152e0e9489cf5e5c3a8e4267a6a92c4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Nov 2017 18:18:39 +0900 Subject: [PATCH 083/235] Make CatchStacker testcase more useful --- osu.Game.Rulesets.Catch/Tests/TestCaseCatchStacker.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseCatchStacker.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseCatchStacker.cs index a890a8a386..4672ab5746 100644 --- a/osu.Game.Rulesets.Catch/Tests/TestCaseCatchStacker.cs +++ b/osu.Game.Rulesets.Catch/Tests/TestCaseCatchStacker.cs @@ -19,8 +19,8 @@ namespace osu.Game.Rulesets.Catch.Tests { var beatmap = new Beatmap(); - for (int i = 0; i < 256; i++) - beatmap.HitObjects.Add(new Fruit { X = 0.5f, StartTime = i * 100, NewCombo = i % 8 == 0 }); + for (int i = 0; i < 512; i++) + beatmap.HitObjects.Add(new Fruit { X = 0.5f + (i / 2048f * ((i % 10) - 5)), StartTime = i * 100, NewCombo = i % 8 == 0 }); return beatmap; } From b11de50df28577b7a6408219b34fbbdadfa362ff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Nov 2017 18:42:50 +0900 Subject: [PATCH 084/235] Cleanups --- osu.Game.Rulesets.Catch/Tests/TestCaseCatchStacker.cs | 2 +- osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseCatchStacker.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseCatchStacker.cs index 4672ab5746..1c5f34f2e5 100644 --- a/osu.Game.Rulesets.Catch/Tests/TestCaseCatchStacker.cs +++ b/osu.Game.Rulesets.Catch/Tests/TestCaseCatchStacker.cs @@ -20,7 +20,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 / 2048f * ((i % 10) - 5)), StartTime = i * 100, NewCombo = i % 8 == 0 }); + beatmap.HitObjects.Add(new Fruit { X = 0.5f + i / 2048f * (i % 10 - 5), 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 f59ba13edd..6fd0793500 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs @@ -51,11 +51,6 @@ namespace osu.Game.Rulesets.Catch.UI }; } - protected override void Update() - { - base.Update(); - } - public bool CheckIfWeCanCatch(CatchHitObject obj) => catcherArea.CanCatch(obj); public override void Add(DrawableHitObject h) From f8c296877b96378506f3dab6d7698986740da380 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Nov 2017 18:57:28 +0900 Subject: [PATCH 085/235] Add testcase for mania beatmap conversion (currently ignored) --- .../Tests/TestCaseBeatmapConversion.cs | 173 ++++++++++++++++++ .../osu.Game.Rulesets.Mania.csproj | 1 + 2 files changed, 174 insertions(+) create mode 100644 osu.Game.Rulesets.Mania/Tests/TestCaseBeatmapConversion.cs diff --git a/osu.Game.Rulesets.Mania/Tests/TestCaseBeatmapConversion.cs b/osu.Game.Rulesets.Mania/Tests/TestCaseBeatmapConversion.cs new file mode 100644 index 0000000000..a8a3e9527b --- /dev/null +++ b/osu.Game.Rulesets.Mania/Tests/TestCaseBeatmapConversion.cs @@ -0,0 +1,173 @@ +// 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.Mania.Objects; +using osu.Game.Rulesets.Mania.Replays; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Mania.Tests +{ + [Ignore("getting CI working")] + public class TestCaseAutoGeneration : OsuTestCase + { + [Test] + public void TestSingleNote() + { + // | | + // | - | + // | | + + var beatmap = new Beatmap(); + beatmap.HitObjects.Add(new Note { StartTime = 1000 }); + + var generated = new ManiaAutoGenerator(beatmap, 1).Generate(); + + Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); + Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); + Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time"); + Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 0 has not been pressed"); + Assert.AreEqual(0, generated.Frames[2].MouseX, "Key 0 has not been released"); + } + + [Test] + public void TestSingleHoldNote() + { + // | | + // | * | + // | * | + // | * | + // | | + + var beatmap = new Beatmap(); + beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 }); + + var generated = new ManiaAutoGenerator(beatmap, 1).Generate(); + + Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); + Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); + Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time"); + Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 0 has not been pressed"); + Assert.AreEqual(0, generated.Frames[2].MouseX, "Key 0 has not been released"); + } + + [Test] + public void TestSingleNoteChord() + { + // | | | + // | - | - | + // | | | + + var beatmap = new Beatmap(); + beatmap.HitObjects.Add(new Note { StartTime = 1000 }); + beatmap.HitObjects.Add(new Note { StartTime = 1000, Column = 1 }); + + var generated = new ManiaAutoGenerator(beatmap, 2).Generate(); + + Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); + Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); + Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time"); + Assert.AreEqual(3, generated.Frames[1].MouseX, "Keys 1 and 2 have not been pressed"); + Assert.AreEqual(0, generated.Frames[2].MouseX, "Keys 1 and 2 have not been released"); + } + + [Test] + public void TestHoldNoteChord() + { + // | | | + // | * | * | + // | * | * | + // | * | * | + // | | | + + var beatmap = new Beatmap(); + beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 }); + beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000, Column = 1 }); + + var generated = new ManiaAutoGenerator(beatmap, 2).Generate(); + + Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); + Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); + Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect release time"); + Assert.AreEqual(3, generated.Frames[1].MouseX, "Keys 1 and 2 have not been pressed"); + Assert.AreEqual(0, generated.Frames[2].MouseX, "Keys 1 and 2 have not been released"); + } + + [Test] + public void TestSingleNoteStair() + { + // | | | + // | | - | + // | - | | + // | | | + + var beatmap = new Beatmap(); + beatmap.HitObjects.Add(new Note { StartTime = 1000 }); + beatmap.HitObjects.Add(new Note { StartTime = 2000, Column = 1 }); + + var generated = new ManiaAutoGenerator(beatmap, 2).Generate(); + + Assert.IsTrue(generated.Frames.Count == 5, "Replay must have 5 frames"); + Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time"); + Assert.AreEqual(1000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[2].Time, "Incorrect first note release time"); + Assert.AreEqual(2000, generated.Frames[3].Time, "Incorrect second note hit time"); + Assert.AreEqual(2000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time"); + Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 1 has not been pressed"); + Assert.AreEqual(0, generated.Frames[2].MouseX, "Key 1 has not been released"); + Assert.AreEqual(2, generated.Frames[3].MouseX, "Key 2 has not been pressed"); + Assert.AreEqual(0, generated.Frames[4].MouseX, "Key 2 has not been released"); + } + + [Test] + public void TestHoldNoteStair() + { + // | | | + // | | * | + // | * | * | + // | * | * | + // | * | | + // | | | + + var beatmap = new Beatmap(); + beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 }); + beatmap.HitObjects.Add(new HoldNote { StartTime = 2000, Duration = 2000, Column = 1 }); + + var generated = new ManiaAutoGenerator(beatmap, 2).Generate(); + + Assert.IsTrue(generated.Frames.Count == 5, "Replay must have 5 frames"); + Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time"); + Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect first note release time"); + Assert.AreEqual(2000, generated.Frames[2].Time, "Incorrect second note hit time"); + Assert.AreEqual(4000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[4].Time, "Incorrect second note release time"); + Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 1 has not been pressed"); + Assert.AreEqual(3, generated.Frames[2].MouseX, "Keys 1 and 2 have not been pressed"); + Assert.AreEqual(2, generated.Frames[3].MouseX, "Key 1 has not been released"); + Assert.AreEqual(0, generated.Frames[4].MouseX, "Key 2 has not been released"); + } + + [Test] + public void TestHoldNoteWithReleasePress() + { + // | | | + // | * | - | + // | * | | + // | * | | + // | | | + + var beatmap = new Beatmap(); + beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 - ManiaAutoGenerator.RELEASE_DELAY }); + beatmap.HitObjects.Add(new Note { StartTime = 3000, Column = 1 }); + + var generated = new ManiaAutoGenerator(beatmap, 2).Generate(); + + Assert.IsTrue(generated.Frames.Count == 4, "Replay must have 4 frames"); + Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time"); + Assert.AreEqual(3000, generated.Frames[2].Time, "Incorrect second note press time + first note release time"); + Assert.AreEqual(3000 + ManiaAutoGenerator.RELEASE_DELAY, generated.Frames[3].Time, "Incorrect second note release time"); + Assert.AreEqual(1, generated.Frames[1].MouseX, "Key 1 has not been pressed"); + Assert.AreEqual(2, generated.Frames[2].MouseX, "Key 1 has not been released or key 2 has not been pressed"); + Assert.AreEqual(0, generated.Frames[3].MouseX, "Keys 1 and 2 have not been released"); + } + } +} diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 6f45a64d92..3bd195e2f0 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -80,6 +80,7 @@ + From 567e378bbbbfb25d9919637683789e7f9fc73367 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Nov 2017 19:02:39 +0900 Subject: [PATCH 086/235] CI fixes --- osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs index 35ca868d5a..2f336d834f 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; From eaf2b1d94df6eb7f02621243e81bb858b5953987 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Nov 2017 19:06:46 +0900 Subject: [PATCH 087/235] Remove line that shouldn't have been added yet --- .../Replays/ManiaFramedReplayInputHandler.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs b/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs index 8440e4347d..12534d6eb4 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaFramedReplayInputHandler.cs @@ -19,8 +19,6 @@ namespace osu.Game.Rulesets.Mania.Replays this.container = container; } - protected override bool AtImportantFrame => CurrentFrame.MouseX != PreviousFrame.MouseX; - private ManiaPlayfield playfield; public override List GetPendingStates() { From cc9e06e16196710572154c111c8ee51bb9417bbd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Nov 2017 19:07:00 +0900 Subject: [PATCH 088/235] Remove unused elements --- osu.Game.Rulesets.Mania/Mods/ManiaMod.cs | 14 +------------- .../Replays/ManiaAutoGenerator.cs | 6 +----- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs b/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs index 164309c227..dfc9993bde 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaMod.cs @@ -176,22 +176,10 @@ namespace osu.Game.Rulesets.Mania.Mods public class ManiaModAutoplay : ModAutoplay { - private int availableColumns; - - public override void ApplyToRulesetContainer(RulesetContainer rulesetContainer) - { - // Todo: This shouldn't be done, we should be getting a ManiaBeatmap which should store AvailableColumns - // But this is dependent on a _lot_ of refactoring - var maniaRulesetContainer = (ManiaRulesetContainer)rulesetContainer; - availableColumns = maniaRulesetContainer.AvailableColumns; - - base.ApplyToRulesetContainer(rulesetContainer); - } - protected override Score CreateReplayScore(Beatmap beatmap) => new Score { User = new User { Username = "osu!topus!" }, - Replay = new ManiaAutoGenerator(beatmap, availableColumns).Generate(), + Replay = new ManiaAutoGenerator(beatmap).Generate(), }; } } diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs index 2f336d834f..85d840ed70 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs @@ -15,13 +15,9 @@ namespace osu.Game.Rulesets.Mania.Replays { public const double RELEASE_DELAY = 20; - private readonly int availableColumns; - - public ManiaAutoGenerator(Beatmap beatmap, int availableColumns) + public ManiaAutoGenerator(Beatmap beatmap) : base(beatmap) { - this.availableColumns = availableColumns; - Replay = new Replay { User = new User { Username = @"Autoplay" } }; } From 375f2710d417eb182362ce3007224eb053c2e611 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Nov 2017 19:09:17 +0900 Subject: [PATCH 089/235] Rename file --- .../{TestCaseBeatmapConversion.cs => TestCaseAutoGeneration.cs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename osu.Game.Rulesets.Mania/Tests/{TestCaseBeatmapConversion.cs => TestCaseAutoGeneration.cs} (100%) diff --git a/osu.Game.Rulesets.Mania/Tests/TestCaseBeatmapConversion.cs b/osu.Game.Rulesets.Mania/Tests/TestCaseAutoGeneration.cs similarity index 100% rename from osu.Game.Rulesets.Mania/Tests/TestCaseBeatmapConversion.cs rename to osu.Game.Rulesets.Mania/Tests/TestCaseAutoGeneration.cs From 00f9f97850cedb827118e998e85dfbf82b707af1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Nov 2017 19:13:10 +0900 Subject: [PATCH 090/235] Fix compile errors (rider didn't warn me about these) --- .../Tests/TestCaseAutoGeneration.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Tests/TestCaseAutoGeneration.cs b/osu.Game.Rulesets.Mania/Tests/TestCaseAutoGeneration.cs index a8a3e9527b..805553eafc 100644 --- a/osu.Game.Rulesets.Mania/Tests/TestCaseAutoGeneration.cs +++ b/osu.Game.Rulesets.Mania/Tests/TestCaseAutoGeneration.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Mania.Tests var beatmap = new Beatmap(); beatmap.HitObjects.Add(new Note { StartTime = 1000 }); - var generated = new ManiaAutoGenerator(beatmap, 1).Generate(); + var generated = new ManiaAutoGenerator(beatmap).Generate(); Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Mania.Tests var beatmap = new Beatmap(); beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 }); - var generated = new ManiaAutoGenerator(beatmap, 1).Generate(); + var generated = new ManiaAutoGenerator(beatmap).Generate(); Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Mania.Tests beatmap.HitObjects.Add(new Note { StartTime = 1000 }); beatmap.HitObjects.Add(new Note { StartTime = 1000, Column = 1 }); - var generated = new ManiaAutoGenerator(beatmap, 2).Generate(); + var generated = new ManiaAutoGenerator(beatmap).Generate(); Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); @@ -85,7 +85,7 @@ namespace osu.Game.Rulesets.Mania.Tests beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 }); beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000, Column = 1 }); - var generated = new ManiaAutoGenerator(beatmap, 2).Generate(); + var generated = new ManiaAutoGenerator(beatmap).Generate(); Assert.IsTrue(generated.Frames.Count == 3, "Replay must have 3 frames"); Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect hit time"); @@ -106,7 +106,7 @@ namespace osu.Game.Rulesets.Mania.Tests beatmap.HitObjects.Add(new Note { StartTime = 1000 }); beatmap.HitObjects.Add(new Note { StartTime = 2000, Column = 1 }); - var generated = new ManiaAutoGenerator(beatmap, 2).Generate(); + var generated = new ManiaAutoGenerator(beatmap).Generate(); Assert.IsTrue(generated.Frames.Count == 5, "Replay must have 5 frames"); Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time"); @@ -133,7 +133,7 @@ namespace osu.Game.Rulesets.Mania.Tests beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 }); beatmap.HitObjects.Add(new HoldNote { StartTime = 2000, Duration = 2000, Column = 1 }); - var generated = new ManiaAutoGenerator(beatmap, 2).Generate(); + var generated = new ManiaAutoGenerator(beatmap).Generate(); Assert.IsTrue(generated.Frames.Count == 5, "Replay must have 5 frames"); Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time"); @@ -159,7 +159,7 @@ namespace osu.Game.Rulesets.Mania.Tests beatmap.HitObjects.Add(new HoldNote { StartTime = 1000, Duration = 2000 - ManiaAutoGenerator.RELEASE_DELAY }); beatmap.HitObjects.Add(new Note { StartTime = 3000, Column = 1 }); - var generated = new ManiaAutoGenerator(beatmap, 2).Generate(); + var generated = new ManiaAutoGenerator(beatmap).Generate(); Assert.IsTrue(generated.Frames.Count == 4, "Replay must have 4 frames"); Assert.AreEqual(1000, generated.Frames[1].Time, "Incorrect first note hit time"); From 2bb61e51ac6f95065ab28c6f9b9febc15650180b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Nov 2017 19:43:26 +0900 Subject: [PATCH 091/235] Add catcher scale based on CircleSize --- .../Tests/TestCaseCatchStacker.cs | 14 +++++++-- .../Tests/TestCaseCatcherArea.cs | 30 ++++++++++++------- osu.Game.Rulesets.Catch/UI/CatcherArea.cs | 12 ++++---- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseCatchStacker.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseCatchStacker.cs index 1c5f34f2e5..586de17f15 100644 --- a/osu.Game.Rulesets.Catch/Tests/TestCaseCatchStacker.cs +++ b/osu.Game.Rulesets.Catch/Tests/TestCaseCatchStacker.cs @@ -11,13 +11,23 @@ namespace osu.Game.Rulesets.Catch.Tests [Ignore("getting CI working")] public class TestCaseCatchStacker : Game.Tests.Visual.TestCasePlayer { - public TestCaseCatchStacker() : base(typeof(CatchRuleset)) + public TestCaseCatchStacker() + : base(typeof(CatchRuleset)) { } protected override Beatmap CreateBeatmap() { - var beatmap = new Beatmap(); + var beatmap = new Beatmap + { + BeatmapInfo = new BeatmapInfo + { + BaseDifficulty = new BeatmapDifficulty + { + CircleSize = 6, + } + } + }; for (int i = 0; i < 512; i++) beatmap.HitObjects.Add(new Fruit { X = 0.5f + i / 2048f * (i % 10 - 5), StartTime = i * 100, NewCombo = i % 8 == 0 }); diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs index 21c24aed93..84ca08f97e 100644 --- a/osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs +++ b/osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.UI; using osu.Game.Tests.Visual; @@ -15,26 +16,35 @@ namespace osu.Game.Rulesets.Catch.Tests [Ignore("getting CI working")] internal class TestCaseCatcherArea : OsuTestCase { + private RulesetInfo catchRuleset; + public override IReadOnlyList RequiredTypes => new[] { typeof(CatcherArea), }; - [BackgroundDependencyLoader] - private void load(RulesetStore rulesets) + public TestCaseCatcherArea() { - Children = new Drawable[] + AddSliderStep("CircleSize", 0, 8, 5, craeteCatcher); + } + + private void craeteCatcher(float size) + { + Child = new CatchInputManager(catchRuleset) { - new CatchInputManager(rulesets.GetRuleset(2)) + RelativeSizeAxes = Axes.Both, + Child = new CatcherArea(new BeatmapDifficulty { CircleSize = size }) { - RelativeSizeAxes = Axes.Both, - Child = new CatcherArea() - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft - } + Anchor = Anchor.CentreLeft, + Origin = Anchor.BottomLeft }, }; } + + [BackgroundDependencyLoader] + private void load(RulesetStore rulesets) + { + catchRuleset = rulesets.GetRuleset(2); + } } } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index 1cc810201f..203db1bb8c 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -51,7 +51,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 / DrawSize.X / 2; + public bool CanCatch(CatchHitObject obj) => Math.Abs(catcher.Position.X - obj.X) < catcher.DrawSize.X * Math.Abs(catcher.Scale.X) / DrawSize.X / 2; public class Catcher : Container, IKeyBindingHandler { @@ -68,10 +68,12 @@ namespace osu.Game.Rulesets.Catch.UI RelativePositionAxes = Axes.X; X = 0.5f; - Origin = Anchor.BottomCentre; - Anchor = Anchor.BottomLeft; + Origin = Anchor.TopCentre; + Anchor = Anchor.TopLeft; Size = new Vector2(CATCHER_SIZE); + if (difficulty != null) + Scale = new Vector2(1.0f - 0.7f * (difficulty.CircleSize - 5) / 5); } [BackgroundDependencyLoader] @@ -115,7 +117,7 @@ namespace osu.Game.Rulesets.Catch.UI var additive = createCatcherSprite(); additive.Anchor = Anchor; - additive.OriginPosition = additive.OriginPosition + new Vector2(DrawWidth / 2, DrawHeight); // also temporary to align sprite correctly. + additive.OriginPosition = additive.OriginPosition + new Vector2(DrawWidth / 2, 0); // also temporary to align sprite correctly. additive.Position = Position; additive.Scale = Scale; additive.RelativePositionAxes = RelativePositionAxes; @@ -201,7 +203,7 @@ namespace osu.Game.Rulesets.Catch.UI double dashModifier = Dashing ? 1 : 0.5; - Scale = new Vector2(Math.Sign(currentDirection), 1); + 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); } From 23b4d2163ca2fc5f8128fbc814aef37b7e544292 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Nov 2017 21:30:03 +0900 Subject: [PATCH 092/235] Fix spelling and grammar --- osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs | 4 ++-- osu.Game/Beatmaps/BeatmapConverter.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs b/osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs index 84ca08f97e..538f6930ed 100644 --- a/osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs +++ b/osu.Game.Rulesets.Catch/Tests/TestCaseCatcherArea.cs @@ -25,10 +25,10 @@ namespace osu.Game.Rulesets.Catch.Tests public TestCaseCatcherArea() { - AddSliderStep("CircleSize", 0, 8, 5, craeteCatcher); + AddSliderStep("CircleSize", 0, 8, 5, createCatcher); } - private void craeteCatcher(float size) + private void createCatcher(float size) { Child = new CatchInputManager(catchRuleset) { diff --git a/osu.Game/Beatmaps/BeatmapConverter.cs b/osu.Game/Beatmaps/BeatmapConverter.cs index 6132322f1e..e087eebbfe 100644 --- a/osu.Game/Beatmaps/BeatmapConverter.cs +++ b/osu.Game/Beatmaps/BeatmapConverter.cs @@ -80,7 +80,7 @@ namespace osu.Game.Beatmaps /// /// Performs the conversion of a hit object. - /// This method generally executed sequentially for all objects in a beatmap. + /// This method is generally executed sequentially for all objects in a beatmap. /// /// The hit object to convert. /// The un-converted Beatmap. From 7db7fb91ddc35c5408ac47273e2e0a3b3674526d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Nov 2017 21:45:24 +0900 Subject: [PATCH 093/235] Ignore ruleset testcases from CI --- osu.Game.Rulesets.Catch/Tests/TestCasePerformancePoints.cs | 3 +++ osu.Game.Rulesets.Mania/Tests/TestCasePerformancePoints.cs | 3 +++ osu.Game.Rulesets.Osu/Tests/TestCasePerformancePoints.cs | 3 +++ osu.Game.Rulesets.Taiko/Tests/TestCasePerformancePoints.cs | 3 +++ 4 files changed, 12 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Catch/Tests/TestCasePerformancePoints.cs index 6643316f15..0d2dc14160 100644 --- a/osu.Game.Rulesets.Catch/Tests/TestCasePerformancePoints.cs +++ b/osu.Game.Rulesets.Catch/Tests/TestCasePerformancePoints.cs @@ -1,8 +1,11 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; + namespace osu.Game.Rulesets.Catch.Tests { + [Ignore("getting CI working")] public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints { public TestCasePerformancePoints() diff --git a/osu.Game.Rulesets.Mania/Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Mania/Tests/TestCasePerformancePoints.cs index e60808b1a6..8aa8c6b799 100644 --- a/osu.Game.Rulesets.Mania/Tests/TestCasePerformancePoints.cs +++ b/osu.Game.Rulesets.Mania/Tests/TestCasePerformancePoints.cs @@ -1,8 +1,11 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; + namespace osu.Game.Rulesets.Mania.Tests { + [Ignore("getting CI working")] public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints { public TestCasePerformancePoints() diff --git a/osu.Game.Rulesets.Osu/Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Osu/Tests/TestCasePerformancePoints.cs index 36590b484f..25a6110459 100644 --- a/osu.Game.Rulesets.Osu/Tests/TestCasePerformancePoints.cs +++ b/osu.Game.Rulesets.Osu/Tests/TestCasePerformancePoints.cs @@ -1,8 +1,11 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; + namespace osu.Game.Rulesets.Osu.Tests { + [Ignore("getting CI working")] public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints { public TestCasePerformancePoints() diff --git a/osu.Game.Rulesets.Taiko/Tests/TestCasePerformancePoints.cs b/osu.Game.Rulesets.Taiko/Tests/TestCasePerformancePoints.cs index 269aca2cd9..96d5b20b6e 100644 --- a/osu.Game.Rulesets.Taiko/Tests/TestCasePerformancePoints.cs +++ b/osu.Game.Rulesets.Taiko/Tests/TestCasePerformancePoints.cs @@ -1,8 +1,11 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using NUnit.Framework; + namespace osu.Game.Rulesets.Taiko.Tests { + [Ignore("getting CI working")] public class TestCasePerformancePoints : Game.Tests.Visual.TestCasePerformancePoints { public TestCasePerformancePoints() From f807d26caecf0ce8e08c6a97f54a4b73851610a3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Nov 2017 21:46:13 +0900 Subject: [PATCH 094/235] Use ranked property of mods rather than checking for individual mods --- osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs index cc5abb36b5..cd6b6c5e27 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuPerformanceCalculator.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Scoring countMiss = Convert.ToInt32(Score.Statistics["x"]); // Don't count scores made with supposedly unranked mods - if (mods.Any(m => m is OsuModRelax || m is OsuModAutopilot || m is OsuModAutoplay)) + if (mods.Any(m => !m.Ranked)) return 0; // Todo: In the future we should apply changes to PreEmpt/AR at an OsuHitObject/BaseDifficulty level, but this is done From 66fa10869623e096d82bcd0142b09341c84c1171 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Nov 2017 22:24:56 +0900 Subject: [PATCH 095/235] Vertically centre the panels in osu!direct --- osu.Game/Overlays/DirectOverlay.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index b49ac269a9..0b7a30797d 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -220,7 +220,11 @@ namespace osu.Game.Overlays switch (displayStyle) { case PanelDisplayStyle.Grid: - return new DirectGridPanel(b); + return new DirectGridPanel(b) + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }; default: return new DirectListPanel(b); } From 898c49c19db9ab54cd5eaf2bd91156aaac5b573d Mon Sep 17 00:00:00 2001 From: Santeri Date: Tue, 28 Nov 2017 16:14:32 +0200 Subject: [PATCH 096/235] remove unnecessary assignments --- osu.Game/Overlays/BeatmapSet/HeaderButton.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs index 371e438a34..cddc05c554 100644 --- a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs +++ b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs @@ -20,8 +20,6 @@ namespace osu.Game.Overlays.BeatmapSet [BackgroundDependencyLoader] private void load() { - Masking = true; - CornerRadius = 3; BackgroundColour = OsuColour.FromHex(@"094c5f"); Triangles.ColourLight = OsuColour.FromHex(@"0f7c9b"); Triangles.ColourDark = OsuColour.FromHex(@"094c5f"); From 1aa79a2d884a6daf4cba7837fe0d1f1eca8ab4ce Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Nov 2017 01:26:21 +0900 Subject: [PATCH 097/235] Hide inspectcode output from appveyor console --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 9048428590..78e0e6da4e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,5 +21,5 @@ build: parallel: true verbosity: minimal after_build: - - cmd: inspectcode /o="inspectcodereport.xml" /caches-home="inspectcode" osu.sln + - cmd: inspectcode /o="inspectcodereport.xml" /caches-home="inspectcode" osu.sln > NUL - cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors \ No newline at end of file From 6f5803b5eb39a2103096f83e3731c9763ff08900 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Nov 2017 01:51:22 +0900 Subject: [PATCH 098/235] Shallow clone submodules --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 78e0e6da4e..ea9a1fa3bf 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,7 +9,7 @@ cache: - inspectcode -> appveyor.yml - packages -> **\packages.config install: - - cmd: git submodule update --init --recursive + - cmd: git submodule update --init --recursive --depth=5 - cmd: choco install resharper-clt -y - cmd: choco install nvika -y - cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.3/CodeFileSanity.exe From 6a4cc933603cf102108f1e354af006b05b871940 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Tue, 28 Nov 2017 21:26:13 +0100 Subject: [PATCH 099/235] fixes crash if all beatmaps of a set are hidden --- osu.Game/Screens/Select/BeatmapCarousel.cs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index b0a636dfb3..e0f3137cec 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -45,7 +45,7 @@ namespace osu.Game.Screens.Select Task.Run(() => { - newGroups = value.Select(createGroup).ToList(); + newGroups = value.Select(createGroup).Where(g => g != null).ToList(); criteria.Filter(newGroups); }).ContinueWith(t => { @@ -124,16 +124,24 @@ namespace osu.Game.Screens.Select // todo: this method should be smarter as to not recreate panels that haven't changed, etc. var group = groups.Find(b => b.BeatmapSet.ID == set.ID); + BeatmapGroup newGroup; if (group == null) - return; + { + newGroup = createGroup(set); - int i = groups.IndexOf(group); - groups.RemoveAt(i); + if (newGroup != null) + groups.Add(newGroup); + } + else + { + int i = groups.IndexOf(group); + groups.RemoveAt(i); - var newGroup = createGroup(set); + newGroup = createGroup(set); - if (newGroup != null) - groups.Insert(i, newGroup); + if (newGroup != null) + groups.Insert(i, newGroup); + } bool hadSelection = selectedGroup == group; From 3bdf82d8df16ed910920c56938c3a74811a49602 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Tue, 28 Nov 2017 21:38:11 +0100 Subject: [PATCH 100/235] refactor newly added code to be less redundant --- osu.Game/Screens/Select/BeatmapCarousel.cs | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index e0f3137cec..3f42ae11ac 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -124,23 +124,18 @@ namespace osu.Game.Screens.Select // todo: this method should be smarter as to not recreate panels that haven't changed, etc. var group = groups.Find(b => b.BeatmapSet.ID == set.ID); - BeatmapGroup newGroup; - if (group == null) - { - newGroup = createGroup(set); - - if (newGroup != null) - groups.Add(newGroup); - } - else - { - int i = groups.IndexOf(group); + int i = groups.IndexOf(group); + if (i >= 0) groups.RemoveAt(i); - newGroup = createGroup(set); + var newGroup = createGroup(set); - if (newGroup != null) + if (newGroup != null) + { + if (i >= 0) groups.Insert(i, newGroup); + else + groups.Add(newGroup); } bool hadSelection = selectedGroup == group; From 8370d7694638d2de0f495e673b73b5687a654ff1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Nov 2017 12:27:18 +0900 Subject: [PATCH 101/235] Quieten nuget --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index ea9a1fa3bf..9cf68803a2 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -15,11 +15,11 @@ install: - cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.3/CodeFileSanity.exe before_build: - cmd: CodeFileSanity.exe - - cmd: nuget restore + - cmd: nuget restore -verbosity quiet build: project: osu.sln parallel: true verbosity: minimal after_build: - - cmd: inspectcode /o="inspectcodereport.xml" /caches-home="inspectcode" osu.sln > NUL + - cmd: inspectcode --o="inspectcodereport.xml" --projects:osu.Game* --caches-home="inspectcode" osu.sln > NUL - cmd: NVika parsereport "inspectcodereport.xml" --treatwarningsaserrors \ No newline at end of file From d1afbf80557773f00936865a860a8714e4a7b645 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Nov 2017 13:10:13 +0900 Subject: [PATCH 102/235] Load Player-based TestCases asynchronously --- osu.Game/Tests/Visual/TestCasePlayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs index f3a6d1efc3..64da838494 100644 --- a/osu.Game/Tests/Visual/TestCasePlayer.cs +++ b/osu.Game/Tests/Visual/TestCasePlayer.cs @@ -78,7 +78,7 @@ namespace osu.Game.Tests.Visual if (Player != null) Remove(Player); - LoadScreen(CreatePlayer(working, instance)); + LoadComponentAsync(CreatePlayer(working, instance), LoadScreen); } protected virtual Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset) => new Player From 1136db15562454e6c44d23b7d23030d9e1d501d9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Nov 2017 14:20:15 +0900 Subject: [PATCH 103/235] Fix mania replays not defining important frames Resolves https://github.com/ppy/osu/issues/1495 . --- .../Replays/ManiaAutoGenerator.cs | 9 +++++---- .../Replays/ManiaReplayFrame.cs | 17 +++++++++++++++++ .../osu.Game.Rulesets.Mania.csproj | 1 + osu.Game/Rulesets/Replays/ReplayFrame.cs | 4 ++-- 4 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs index 64982532a7..58fb43aa83 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mania.Replays public override Replay Generate() { // Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled - Replay.Frames.Add(new ReplayFrame(-100000, null, null, ReplayButtonState.None)); + Replay.Frames.Add(new ManiaReplayFrame(-100000, null, null, ReplayButtonState.None)); double[] holdEndTimes = new double[availableColumns]; for (int i = 0; i < availableColumns; i++) @@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Mania.Replays activeColumns |= 1 << obj.Column; } - Replay.Frames.Add(new ReplayFrame(groupTime, activeColumns, null, ReplayButtonState.None)); + Replay.Frames.Add(new ManiaReplayFrame(groupTime, activeColumns, null, ReplayButtonState.None)); // Add the release frames. We can't do this with the loop above because we need activeColumns to be fully populated foreach (var obj in objGroup.GroupBy(h => (h as IHasEndTime)?.EndTime ?? h.StartTime + release_delay).OrderBy(h => h.Key)) @@ -74,14 +74,15 @@ namespace osu.Game.Rulesets.Mania.Replays activeColumnsAtEnd |= 1 << i; } - Replay.Frames.Add(new ReplayFrame(groupEndTime, activeColumnsAtEnd, 0, ReplayButtonState.None)); + Replay.Frames.Add(new ManiaReplayFrame(groupEndTime, activeColumnsAtEnd, 0, ReplayButtonState.None)); } } Replay.Frames = Replay.Frames // Pick the maximum activeColumns for all frames at the same time .GroupBy(f => f.Time) - .Select(g => new ReplayFrame(g.First().Time, maxMouseX(g), 0, ReplayButtonState.None)) + .Select(g => new ManiaReplayFrame(g.First().Time, maxMouseX(g), 0, ReplayButtonState.None)) + .Cast() // The addition of release frames above maybe result in unordered frames, but we need them ordered .OrderBy(f => f.Time) .ToList(); diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs new file mode 100644 index 0000000000..c27ffa1875 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs @@ -0,0 +1,17 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Replays; + +namespace osu.Game.Rulesets.Mania.Replays +{ + public class ManiaReplayFrame : ReplayFrame + { + public override bool IsImportant => MouseX > 0; + + public ManiaReplayFrame(double time, float? mouseX, float? mouseY, ReplayButtonState buttonState) + : base(time, mouseX, mouseY, buttonState) + { + } + } +} diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 6f45a64d92..22f858c2ea 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -72,6 +72,7 @@ + diff --git a/osu.Game/Rulesets/Replays/ReplayFrame.cs b/osu.Game/Rulesets/Replays/ReplayFrame.cs index b0f62e5271..02c969f648 100644 --- a/osu.Game/Rulesets/Replays/ReplayFrame.cs +++ b/osu.Game/Rulesets/Replays/ReplayFrame.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Replays { public Vector2 Position => new Vector2(MouseX ?? 0, MouseY ?? 0); - public bool IsImportant => MouseX.HasValue && MouseY.HasValue && (MouseLeft || MouseRight); + public virtual bool IsImportant => MouseX.HasValue && MouseY.HasValue && (MouseLeft || MouseRight); public float? MouseX; public float? MouseY; @@ -68,4 +68,4 @@ namespace osu.Game.Rulesets.Replays 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 +} From 6fd550dc91de4557157a317de1897fd29f901184 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Nov 2017 15:47:10 +0900 Subject: [PATCH 104/235] Simplify/sanitize construction of ManiaReplayFrame --- osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs | 10 +++++----- osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs index 58fb43aa83..ca80bf8413 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mania.Replays public override Replay Generate() { // Todo: Realistically this shouldn't be needed, but the first frame is skipped with the way replays are currently handled - Replay.Frames.Add(new ManiaReplayFrame(-100000, null, null, ReplayButtonState.None)); + Replay.Frames.Add(new ManiaReplayFrame(-100000, 0)); double[] holdEndTimes = new double[availableColumns]; for (int i = 0; i < availableColumns; i++) @@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Mania.Replays activeColumns |= 1 << obj.Column; } - Replay.Frames.Add(new ManiaReplayFrame(groupTime, activeColumns, null, ReplayButtonState.None)); + Replay.Frames.Add(new ManiaReplayFrame(groupTime, activeColumns)); // Add the release frames. We can't do this with the loop above because we need activeColumns to be fully populated foreach (var obj in objGroup.GroupBy(h => (h as IHasEndTime)?.EndTime ?? h.StartTime + release_delay).OrderBy(h => h.Key)) @@ -74,14 +74,14 @@ namespace osu.Game.Rulesets.Mania.Replays activeColumnsAtEnd |= 1 << i; } - Replay.Frames.Add(new ManiaReplayFrame(groupEndTime, activeColumnsAtEnd, 0, ReplayButtonState.None)); + Replay.Frames.Add(new ManiaReplayFrame(groupEndTime, activeColumnsAtEnd)); } } Replay.Frames = Replay.Frames // Pick the maximum activeColumns for all frames at the same time .GroupBy(f => f.Time) - .Select(g => new ManiaReplayFrame(g.First().Time, maxMouseX(g), 0, ReplayButtonState.None)) + .Select(g => new ManiaReplayFrame(g.First().Time, maxMouseX(g))) .Cast() // The addition of release frames above maybe result in unordered frames, but we need them ordered .OrderBy(f => f.Time) @@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Mania.Replays /// /// The grouping to search. /// The maximum by count of bits. - private float maxMouseX(IGrouping group) + private int maxMouseX(IGrouping group) { int currentCount = -1; int currentMax = 0; diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs index c27ffa1875..8473d23b89 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs @@ -9,8 +9,8 @@ namespace osu.Game.Rulesets.Mania.Replays { public override bool IsImportant => MouseX > 0; - public ManiaReplayFrame(double time, float? mouseX, float? mouseY, ReplayButtonState buttonState) - : base(time, mouseX, mouseY, buttonState) + public ManiaReplayFrame(double time, int activeColumns) + : base(time, (float)activeColumns, null, ReplayButtonState.None) { } } From 8c30fd490c373492e219bead97d5ae9fc7e57bf9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Nov 2017 16:22:11 +0900 Subject: [PATCH 105/235] Add HitObjectComposer class --- osu.Game.Rulesets.Catch/Edit/HitObjectComposer.cs | 10 ++++++++++ osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj | 1 + osu.Game.Rulesets.Mania/Edit/HitObjectComposer.cs | 10 ++++++++++ osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj | 1 + osu.Game.Rulesets.Osu/Edit/HitObjectComposer.cs | 10 ++++++++++ osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj | 1 + osu.Game.Rulesets.Taiko/Edit/HitObjectComposer.cs | 10 ++++++++++ osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj | 1 + osu.Game/Rulesets/Edit/HitObjectComposer.cs | 10 ++++++++++ osu.Game/osu.Game.csproj | 1 + 10 files changed, 55 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Edit/HitObjectComposer.cs create mode 100644 osu.Game.Rulesets.Mania/Edit/HitObjectComposer.cs create mode 100644 osu.Game.Rulesets.Osu/Edit/HitObjectComposer.cs create mode 100644 osu.Game.Rulesets.Taiko/Edit/HitObjectComposer.cs create mode 100644 osu.Game/Rulesets/Edit/HitObjectComposer.cs diff --git a/osu.Game.Rulesets.Catch/Edit/HitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/HitObjectComposer.cs new file mode 100644 index 0000000000..01351fd00c --- /dev/null +++ b/osu.Game.Rulesets.Catch/Edit/HitObjectComposer.cs @@ -0,0 +1,10 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Catch.Edit +{ + public class HitObjectComposer : Rulesets.Edit.HitObjectComposer + { + + } +} diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index bf60bc01bb..30c088ac85 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -48,6 +48,7 @@ + diff --git a/osu.Game.Rulesets.Mania/Edit/HitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/HitObjectComposer.cs new file mode 100644 index 0000000000..aa0d43dc75 --- /dev/null +++ b/osu.Game.Rulesets.Mania/Edit/HitObjectComposer.cs @@ -0,0 +1,10 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Mania.Edit +{ + public class HitObjectComposer : Rulesets.Edit.HitObjectComposer + { + + } +} diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 6f45a64d92..1470ee57b1 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -53,6 +53,7 @@ + diff --git a/osu.Game.Rulesets.Osu/Edit/HitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/HitObjectComposer.cs new file mode 100644 index 0000000000..921fb72fdf --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/HitObjectComposer.cs @@ -0,0 +1,10 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Osu.Edit +{ + public class HitObjectComposer : Rulesets.Edit.HitObjectComposer + { + + } +} diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 3c90749777..32a0a34ff2 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -49,6 +49,7 @@ + diff --git a/osu.Game.Rulesets.Taiko/Edit/HitObjectComposer.cs b/osu.Game.Rulesets.Taiko/Edit/HitObjectComposer.cs new file mode 100644 index 0000000000..e524b4cdc7 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Edit/HitObjectComposer.cs @@ -0,0 +1,10 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Taiko.Edit +{ + public class HitObjectComposer : Rulesets.Edit.HitObjectComposer + { + + } +} diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index bf627d205a..41881fc250 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -46,6 +46,7 @@ + diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs new file mode 100644 index 0000000000..2724334ed4 --- /dev/null +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -0,0 +1,10 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Edit +{ + public class HitObjectComposer + { + + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ce9e8eff9c..b707a3382e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -559,6 +559,7 @@ + From 2ec24f58c8e5c30e9c3cd681f55869f971b4f80e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Nov 2017 16:30:59 +0900 Subject: [PATCH 106/235] Add testcase for editor compose --- .../Visual/TestCaseEditorCompose.cs | 47 +++++++++++++++++++ osu.Game.Tests/osu.Game.Tests.csproj | 1 + 2 files changed, 48 insertions(+) create mode 100644 osu.Game.Tests/Visual/TestCaseEditorCompose.cs diff --git a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs new file mode 100644 index 0000000000..9f060a9ec4 --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs @@ -0,0 +1,47 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Framework.Allocation; +using osu.Game.Beatmaps; +using osu.Game.Screens.Edit.Screens.Compose; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseEditorCompose : OsuTestCase + { + private readonly Random random; + private readonly Compose compose; + + public TestCaseEditorCompose() + { + random = new Random(1337); + + Add(compose = new Compose()); + AddStep("Next beatmap", nextBeatmap); + } + + private OsuGameBase osuGame; + private BeatmapManager beatmaps; + + [BackgroundDependencyLoader] + private void load(OsuGameBase osuGame, BeatmapManager beatmaps) + { + this.osuGame = osuGame; + this.beatmaps = beatmaps; + + compose.Beatmap.BindTo(osuGame.Beatmap); + nextBeatmap(); + } + + private void nextBeatmap() + { + var sets = beatmaps.GetAllUsableBeatmapSets(); + if (sets.Count == 0) + return; + + var b = sets[random.Next(0, sets.Count)].Beatmaps[0]; + osuGame.Beatmap.Value = beatmaps.GetWorkingBeatmap(b); + } + } +} diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 312a564f71..34f79987c5 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -105,6 +105,7 @@ + From f586cbac3285e2b2a8e8586276bd660e9736ddb0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Nov 2017 16:47:36 +0900 Subject: [PATCH 107/235] Restructure Compose to use grids and eventually support HitObjectContainer --- .../Screens/Edit/Screens/Compose/Compose.cs | 66 ++++++++++++------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs index 2349c261cf..67e66eecdf 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs @@ -12,40 +12,62 @@ namespace osu.Game.Screens.Edit.Screens.Compose { public class Compose : EditorScreen { + private const float vertical_margins = 10; + private const float horizontal_margins = 20; + public Compose() { ScrollableTimeline timeline; - Children = new[] + Children = new Drawable[] { - new Container + new GridContainer { - Name = "Timeline", - RelativeSizeAxes = Axes.X, - Height = 110, - Children = new Drawable[] + RelativeSizeAxes = Axes.Both, + Content = new[] { - new Box + new Drawable[] { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0.5f) - }, - new Container - { - Name = "Content", - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 17, Vertical = 10 }, - Children = new Drawable[] + new Container { - new Container + Name = "Timeline", + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] { - RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Right = 115 }, - Child = timeline = new ScrollableTimeline { RelativeSizeAxes = Axes.Both } + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4.Black.Opacity(0.5f) + }, + new Container + { + Name = "Timeline content", + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = horizontal_margins, Vertical = vertical_margins }, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Right = 115 }, + Child = timeline = new ScrollableTimeline { RelativeSizeAxes = Axes.Both } + } + } + } } } + }, + new Drawable[] + { + new Container + { + Name = "Composer content", + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = horizontal_margins, Vertical = vertical_margins }, + } } - } - } + }, + RowDimensions = new[] { new Dimension(GridSizeMode.Absolute, 110) } + }, }; timeline.Beatmap.BindTo(Beatmap); From 1ea089eb743c0f7b4eca26f5fc70e943a89d341f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Nov 2017 15:53:41 +0900 Subject: [PATCH 108/235] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index d92cec7645..4fc866eee3 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit d92cec764538da2e7ed95bfb566f6bc81a9667c8 +Subproject commit 4fc866eee3803f88b155150e32e021b9c21e647f From bad970d1d1f0b0feee09c54ab3b5ee7918458d90 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Nov 2017 17:25:55 +0900 Subject: [PATCH 109/235] Add wait steps to ensure the player is completely loaded before continuing --- osu.Game/Tests/Visual/TestCasePlayer.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs index 64da838494..d9951e002b 100644 --- a/osu.Game/Tests/Visual/TestCasePlayer.cs +++ b/osu.Game/Tests/Visual/TestCasePlayer.cs @@ -50,7 +50,11 @@ namespace osu.Game.Tests.Visual string instantiation = ruleset?.AssemblyQualifiedName; foreach (var r in rulesets.AvailableRulesets.Where(rs => instantiation == null || rs.InstantiationInfo == instantiation)) - AddStep(r.Name, () => loadPlayerFor(r)); + { + Player p = null; + AddStep(r.Name, () => p = loadPlayerFor(r)); + AddUntilStep(() => p.IsLoaded); + } } protected virtual Beatmap CreateBeatmap() @@ -64,7 +68,7 @@ namespace osu.Game.Tests.Visual return beatmap; } - private void loadPlayerFor(RulesetInfo r) + private Player loadPlayerFor(RulesetInfo r) { var beatmap = CreateBeatmap(); @@ -78,7 +82,11 @@ namespace osu.Game.Tests.Visual if (Player != null) Remove(Player); - LoadComponentAsync(CreatePlayer(working, instance), LoadScreen); + var player = CreatePlayer(working, instance); + + LoadComponentAsync(player, LoadScreen); + + return player; } protected virtual Player CreatePlayer(WorkingBeatmap beatmap, Ruleset ruleset) => new Player From 309eb4edd752b95d108770f2df55f6efdcd6ac2c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Nov 2017 17:46:12 +0900 Subject: [PATCH 110/235] Integrate HitObjectComposer into Compose Also removes the other rulesets' HitObjectComposers for now. --- .../Edit/HitObjectComposer.cs | 10 -------- .../osu.Game.Rulesets.Catch.csproj | 1 - .../Edit/HitObjectComposer.cs | 10 -------- .../osu.Game.Rulesets.Mania.csproj | 1 - ...ectComposer.cs => OsuHitObjectComposer.cs} | 7 ++++-- osu.Game.Rulesets.Osu/OsuRuleset.cs | 4 ++++ .../osu.Game.Rulesets.Osu.csproj | 2 +- .../Edit/HitObjectComposer.cs | 10 -------- .../osu.Game.Rulesets.Taiko.csproj | 1 - osu.Game/Rulesets/Edit/HitObjectComposer.cs | 23 +++++++++++++++++- osu.Game/Rulesets/Ruleset.cs | 3 +++ .../Screens/Edit/Screens/Compose/Compose.cs | 24 ++++++++++++++++++- 12 files changed, 58 insertions(+), 38 deletions(-) delete mode 100644 osu.Game.Rulesets.Catch/Edit/HitObjectComposer.cs delete mode 100644 osu.Game.Rulesets.Mania/Edit/HitObjectComposer.cs rename osu.Game.Rulesets.Osu/Edit/{HitObjectComposer.cs => OsuHitObjectComposer.cs} (52%) delete mode 100644 osu.Game.Rulesets.Taiko/Edit/HitObjectComposer.cs diff --git a/osu.Game.Rulesets.Catch/Edit/HitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/HitObjectComposer.cs deleted file mode 100644 index 01351fd00c..0000000000 --- a/osu.Game.Rulesets.Catch/Edit/HitObjectComposer.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Catch.Edit -{ - public class HitObjectComposer : Rulesets.Edit.HitObjectComposer - { - - } -} diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index 30c088ac85..bf60bc01bb 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -48,7 +48,6 @@ - diff --git a/osu.Game.Rulesets.Mania/Edit/HitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/HitObjectComposer.cs deleted file mode 100644 index aa0d43dc75..0000000000 --- a/osu.Game.Rulesets.Mania/Edit/HitObjectComposer.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Mania.Edit -{ - public class HitObjectComposer : Rulesets.Edit.HitObjectComposer - { - - } -} diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 1470ee57b1..6f45a64d92 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -53,7 +53,6 @@ - diff --git a/osu.Game.Rulesets.Osu/Edit/HitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs similarity index 52% rename from osu.Game.Rulesets.Osu/Edit/HitObjectComposer.cs rename to osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 921fb72fdf..18dfbb6711 100644 --- a/osu.Game.Rulesets.Osu/Edit/HitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -3,8 +3,11 @@ namespace osu.Game.Rulesets.Osu.Edit { - public class HitObjectComposer : Rulesets.Edit.HitObjectComposer + public class OsuHitObjectComposer : Rulesets.Edit.HitObjectComposer { - + public OsuHitObjectComposer(Ruleset ruleset) + : base(ruleset) + { + } } } diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index c87328d87c..64e76d6022 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -14,6 +14,8 @@ using System.Linq; using osu.Framework.Graphics; using osu.Game.Overlays.Settings; using osu.Framework.Input.Bindings; +using osu.Game.Rulesets.Osu.Edit; +using osu.Game.Rulesets.Edit; namespace osu.Game.Rulesets.Osu { @@ -114,6 +116,8 @@ namespace osu.Game.Rulesets.Osu public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new OsuDifficultyCalculator(beatmap, mods); + public override HitObjectComposer CreateHitObjectComposer() => new OsuHitObjectComposer(this); + public override string Description => "osu!"; public override SettingsSubsection CreateSettings() => new OsuSettings(); diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 32a0a34ff2..34f9bdf972 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -49,7 +49,7 @@ - + diff --git a/osu.Game.Rulesets.Taiko/Edit/HitObjectComposer.cs b/osu.Game.Rulesets.Taiko/Edit/HitObjectComposer.cs deleted file mode 100644 index e524b4cdc7..0000000000 --- a/osu.Game.Rulesets.Taiko/Edit/HitObjectComposer.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Taiko.Edit -{ - public class HitObjectComposer : Rulesets.Edit.HitObjectComposer - { - - } -} diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index 41881fc250..bf627d205a 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -46,7 +46,6 @@ - diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 2724334ed4..da31f6ba66 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -1,10 +1,31 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + namespace osu.Game.Rulesets.Edit { - public class HitObjectComposer + public abstract class HitObjectComposer : CompositeDrawable { + private readonly Ruleset ruleset; + public HitObjectComposer(Ruleset ruleset) + { + this.ruleset = ruleset; + + RelativeSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load(OsuGameBase osuGame) + { + try + { + InternalChild = ruleset.CreateRulesetContainerWith(osuGame.Beatmap.Value, true); + } + catch { } + } } } diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index ed2fdf4157..dfa9ea5125 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -9,6 +9,7 @@ using osu.Framework.Input.Bindings; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Overlays.Settings; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; @@ -49,6 +50,8 @@ namespace osu.Game.Rulesets public abstract DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null); + public virtual HitObjectComposer CreateHitObjectComposer() => null; + public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_question_circle }; public abstract string Description { get; } diff --git a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs index 67e66eecdf..8cfb04da06 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs @@ -1,11 +1,14 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using OpenTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Timing; +using osu.Game.Beatmaps; using osu.Game.Screens.Edit.Screens.Compose.Timeline; namespace osu.Game.Screens.Edit.Screens.Compose @@ -15,6 +18,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose private const float vertical_margins = 10; private const float horizontal_margins = 20; + private readonly Container composerContainer; + public Compose() { ScrollableTimeline timeline; @@ -58,7 +63,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose }, new Drawable[] { - new Container + composerContainer = new Container { Name = "Composer content", RelativeSizeAxes = Axes.Both, @@ -71,6 +76,23 @@ namespace osu.Game.Screens.Edit.Screens.Compose }; timeline.Beatmap.BindTo(Beatmap); + + Beatmap.ValueChanged += beatmapChanged; + } + + private void beatmapChanged(WorkingBeatmap newBeatmap) + { + var ruleset = newBeatmap.BeatmapInfo.Ruleset.CreateInstance(); + var composer = ruleset.CreateHitObjectComposer(); + if (composer == null) + { + // Todo: Handle this + //throw new InvalidOperationException($"Ruleset {ruleset.Description} doesn't support hitobject composition."); + return; + } + + composerContainer.Add(composer); + composerContainer.Clock = new InterpolatingFramedClock((IAdjustableClock)newBeatmap.Track ?? new StopwatchClock()); } } } From 7cbca37e2df471b03f20232cc1eb58a074b383b4 Mon Sep 17 00:00:00 2001 From: Dan Balasescu <1329837+smoogipoo@users.noreply.github.com> Date: Wed, 29 Nov 2017 18:18:36 +0900 Subject: [PATCH 111/235] Remove redundant cast --- osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs index 8473d23b89..d1bc7da911 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaReplayFrame.cs @@ -10,7 +10,7 @@ namespace osu.Game.Rulesets.Mania.Replays public override bool IsImportant => MouseX > 0; public ManiaReplayFrame(double time, int activeColumns) - : base(time, (float)activeColumns, null, ReplayButtonState.None) + : base(time, activeColumns, null, ReplayButtonState.None) { } } From ee35422efc0396ec7800933ae4e2e51abfb45140 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Nov 2017 18:38:12 +0900 Subject: [PATCH 112/235] Handle rulesets that don't support composition a bit better --- osu.Game/Screens/Edit/Editor.cs | 1 + osu.Game/Screens/Edit/Screens/Compose/Compose.cs | 10 +++++----- osu.Game/Screens/Edit/Screens/EditorScreen.cs | 3 +++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 607ff792d8..bc86c683c7 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -154,6 +154,7 @@ namespace osu.Game.Screens.Edit } currentScreen.Beatmap.BindTo(Beatmap); + currentScreen.ExitRequested = Exit; screenContainer.Add(currentScreen); } diff --git a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs index 8cfb04da06..6954995340 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs @@ -7,6 +7,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Logging; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Screens.Edit.Screens.Compose.Timeline; @@ -76,18 +77,17 @@ namespace osu.Game.Screens.Edit.Screens.Compose }; timeline.Beatmap.BindTo(Beatmap); - Beatmap.ValueChanged += beatmapChanged; } private void beatmapChanged(WorkingBeatmap newBeatmap) { - var ruleset = newBeatmap.BeatmapInfo.Ruleset.CreateInstance(); - var composer = ruleset.CreateHitObjectComposer(); + var ruleset = newBeatmap.BeatmapInfo.Ruleset?.CreateInstance(); + var composer = ruleset?.CreateHitObjectComposer(); if (composer == null) { - // Todo: Handle this - //throw new InvalidOperationException($"Ruleset {ruleset.Description} doesn't support hitobject composition."); + Logger.Log($"Ruleset {ruleset.Description} doesn't support hitobject composition."); + ExitRequested?.Invoke(); return; } diff --git a/osu.Game/Screens/Edit/Screens/EditorScreen.cs b/osu.Game/Screens/Edit/Screens/EditorScreen.cs index ac248930d8..9a158d20f1 100644 --- a/osu.Game/Screens/Edit/Screens/EditorScreen.cs +++ b/osu.Game/Screens/Edit/Screens/EditorScreen.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; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -10,6 +11,8 @@ namespace osu.Game.Screens.Edit.Screens { public class EditorScreen : Container { + public Action ExitRequested; + public readonly Bindable Beatmap = new Bindable(); protected override Container Content => content; From d69fa0966f83887c5d26cb4f0dcb2fe17621f8d8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Nov 2017 18:42:54 +0900 Subject: [PATCH 113/235] Generate ManiaReplayFrame instead of ReplayFrame --- osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs index 44297c1fb1..153fee3ab6 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Mania.Replays activeColumns ^= 1 << point.Column; } - Replay.Frames.Add(new ReplayFrame(group.First().Time, activeColumns, 0, ReplayButtonState.None)); + Replay.Frames.Add(new ManiaReplayFrame(group.First().Time, activeColumns)); } return Replay; From 801d81ecfcae0cfc2c912c233c07ce2eb98470af Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Nov 2017 19:00:08 +0900 Subject: [PATCH 114/235] Add a notice when not logged in --- .../Tests/Visual/TestCasePerformancePoints.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs index 80167e5e1f..6da14e9b12 100644 --- a/osu.Game/Tests/Visual/TestCasePerformancePoints.cs +++ b/osu.Game/Tests/Visual/TestCasePerformancePoints.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; using osu.Framework.Input; using osu.Game.Beatmaps; using osu.Game.Graphics.Sprites; @@ -220,6 +221,17 @@ namespace osu.Game.Tests.Visual private void load(OsuGameBase osuGame, APIAccess api) { this.api = api; + + if (!api.IsLoggedIn) + { + InternalChild = new SpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Text = "Please login to see online scores", + }; + } + osuGame.Beatmap.ValueChanged += beatmapChanged; } @@ -229,6 +241,9 @@ namespace osu.Game.Tests.Visual lastRequest?.Cancel(); scores.Clear(); + if (!api.IsLoggedIn) + return; + lastRequest = new GetScoresRequest(newBeatmap.BeatmapInfo); lastRequest.Success += res => res.Scores.ForEach(s => scores.Add(new PerformanceDisplay(s, newBeatmap.Beatmap))); api.Queue(lastRequest); From 6dd6f08bd05a3f14d8c262e99e17978512309c37 Mon Sep 17 00:00:00 2001 From: mattiapette Date: Wed, 29 Nov 2017 11:51:56 +0100 Subject: [PATCH 115/235] Random song selection when entering the select menu with default osu theme or invalid beatmap --- osu.Game/Screens/Select/SongSelect.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index a0b788d777..6fcaff7976 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -211,7 +211,7 @@ namespace osu.Game.Screens.Select if (Beatmap.Value.BeatmapSetInfo?.DeletePending == false) carousel.SelectBeatmap(Beatmap.Value.BeatmapInfo, false); else - carousel.SelectNext(); + carousel.SelectNextRandom(); } private void carouselRaisedStart(InputState state = null) From 2b7bf285e4bc860074058bf163aac32e9aaf78a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Nov 2017 20:07:00 +0900 Subject: [PATCH 116/235] Improve code quality --- osu.Game/Screens/Select/BeatmapDetails.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 3cd6c3b107..eb313319bd 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -320,26 +320,25 @@ namespace osu.Game.Screens.Select return; } - this.FadeIn(transition_duration); setTextAsync(value); } } private void setTextAsync(string text) { - var newTextFlow = new TextFlowContainer + LoadComponentAsync(new TextFlowContainer(s => s.TextSize = 14) { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Colour = textFlow.Colour, - }; - - newTextFlow.AddText(text, s => s.TextSize = 14); - - LoadComponentAsync(newTextFlow, d => + Text = text + }, loaded => { - textContainer.Remove(textFlow); - textContainer.Add(textFlow = d); + textFlow?.Expire(); + textContainer.Add(textFlow = loaded); + + // fade in if we haven't yet. + this.FadeIn(transition_duration); }); } From b2fc50247c053b1563f69a744664b4b407501e70 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Nov 2017 20:13:00 +0900 Subject: [PATCH 117/235] Hide metadata by default to avoid initial jump when no data present --- osu.Game/Screens/Select/BeatmapDetails.cs | 65 ++++++++++++----------- 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index eb313319bd..a9a778fe17 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -310,6 +310,39 @@ namespace osu.Game.Screens.Select private readonly FillFlowContainer textContainer; private TextFlowContainer textFlow; + public MetadataSection(string title) + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Alpha = 0; + + InternalChild = textContainer = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(spacing / 2), + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Child = new OsuSpriteText + { + Text = title, + Font = @"Exo2.0-Bold", + TextSize = 14, + }, + }, + textFlow = new TextFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }, + }, + }; + } + public string Text { set @@ -347,38 +380,6 @@ namespace osu.Game.Screens.Select get { return textFlow.Colour; } set { textFlow.Colour = value; } } - - public MetadataSection(string title) - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - - InternalChild = textContainer = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Spacing = new Vector2(spacing / 2), - Children = new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Child = new OsuSpriteText - { - Text = title, - Font = @"Exo2.0-Bold", - TextSize = 14, - }, - }, - textFlow = new TextFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - }, - }, - }; - } } private class DimmedLoadingAnimation : VisibilityContainer From a0c33499352682b894fc8f84321badc3ca722107 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 00:45:37 +0900 Subject: [PATCH 118/235] Add LangVer.props and osu.Game.props for common csproj properties --- LangVer.props | 6 ++++++ osu.Desktop.Deploy/osu.Desktop.Deploy.csproj | 5 +---- osu.Desktop/osu.Desktop.csproj | 6 +----- osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj | 5 +---- osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj | 5 +---- osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj | 5 +---- osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj | 5 +---- osu.Game.Tests/osu.Game.Tests.csproj | 5 +---- osu.Game.props | 9 +++++++++ osu.Game/osu.Game.csproj | 5 +---- 10 files changed, 23 insertions(+), 33 deletions(-) create mode 100644 LangVer.props create mode 100644 osu.Game.props diff --git a/LangVer.props b/LangVer.props new file mode 100644 index 0000000000..2ff5e95378 --- /dev/null +++ b/LangVer.props @@ -0,0 +1,6 @@ + + + + 6 + + \ No newline at end of file diff --git a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj index 6727a86a91..316d85db47 100644 --- a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj +++ b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj @@ -1,5 +1,6 @@  + Debug @@ -22,7 +23,6 @@ DEBUG;TRACE prompt 4 - 6 AnyCPU @@ -102,9 +102,6 @@ - - osu.licenseheader - PreserveNewest diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index 91c0da6f65..bf259c48ba 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -1,5 +1,6 @@  + {419659FD-72EA-4678-9EB8-B22A746CED70} Debug @@ -62,7 +63,6 @@ false - 6 none @@ -98,7 +98,6 @@ full AnyCPU false - 6 prompt --tests @@ -174,9 +173,6 @@ - - osu.licenseheader - diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index b7916f674e..b855e6178b 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -1,5 +1,6 @@  + Debug @@ -21,7 +22,6 @@ prompt 4 false - 6 pdbonly @@ -74,9 +74,6 @@ - - osu.licenseheader - diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index 19832d733e..b91f38cd3d 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -1,5 +1,6 @@  + Debug @@ -21,7 +22,6 @@ prompt 4 false - 6 pdbonly @@ -99,9 +99,6 @@ - - osu.licenseheader - diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 2be057de40..05fb71afdf 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -1,5 +1,6 @@  + Debug @@ -22,7 +23,6 @@ prompt 4 false - 6 pdbonly @@ -105,9 +105,6 @@ - - osu.licenseheader - diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index 0b4e6e43f2..7b86fd921f 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -1,5 +1,6 @@  + Debug @@ -21,7 +22,6 @@ prompt 4 false - 6 pdbonly @@ -96,9 +96,6 @@ - - osu.licenseheader - diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 312a564f71..8e7bfaa166 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -1,5 +1,6 @@  + Debug AnyCPU @@ -19,7 +20,6 @@ 4 false false - 6 true @@ -45,9 +45,6 @@ - - osu.licenseheader - diff --git a/osu.Game.props b/osu.Game.props new file mode 100644 index 0000000000..61d5bea511 --- /dev/null +++ b/osu.Game.props @@ -0,0 +1,9 @@ + + + + + + osu.licenseheader + + + \ No newline at end of file diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ccd1bd03dc..aebd250489 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -1,5 +1,6 @@  + {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D} Debug @@ -61,7 +62,6 @@ false - 6 none @@ -195,9 +195,6 @@ - - osu.licenseheader - From 006d67993905fac5c22f786cabc6f61134d58326 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 00:53:07 +0900 Subject: [PATCH 119/235] Migrate to C#7 --- LangVer.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LangVer.props b/LangVer.props index 2ff5e95378..e6a8b4b38c 100644 --- a/LangVer.props +++ b/LangVer.props @@ -1,6 +1,6 @@ - 6 + 7 \ No newline at end of file From 36be171c298d5c9aa43257d0b5b855d07b06bac9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 02:06:03 +0900 Subject: [PATCH 120/235] Fix up/clean up csprojs for C#7 support --- LangVer.props | 4 +++- osu.Desktop.Deploy/osu.Desktop.Deploy.csproj | 3 +-- osu.Desktop/osu.Desktop.csproj | 2 +- osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj | 1 - osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj | 1 - osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj | 1 - osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj | 1 - osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.Game.props | 2 +- osu.Game/osu.Game.csproj | 2 +- 10 files changed, 8 insertions(+), 11 deletions(-) diff --git a/LangVer.props b/LangVer.props index e6a8b4b38c..c3aee0505a 100644 --- a/LangVer.props +++ b/LangVer.props @@ -1,5 +1,7 @@ - + + + 7 diff --git a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj index 316d85db47..3bec56d322 100644 --- a/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj +++ b/osu.Desktop.Deploy/osu.Desktop.Deploy.csproj @@ -1,7 +1,6 @@  - - + Debug AnyCPU diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index bf259c48ba..e4e9807754 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -1,5 +1,5 @@  - + {419659FD-72EA-4678-9EB8-B22A746CED70} diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index b855e6178b..4605a1f50e 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -1,7 +1,6 @@  - Debug AnyCPU diff --git a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj index b91f38cd3d..ec6f59b5be 100644 --- a/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj +++ b/osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj @@ -1,7 +1,6 @@  - Debug AnyCPU diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index 05fb71afdf..5a0581f48b 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -1,7 +1,6 @@  - Debug AnyCPU diff --git a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj index 7b86fd921f..72e9e6a061 100644 --- a/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj +++ b/osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj @@ -1,7 +1,6 @@  - Debug AnyCPU diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 8e7bfaa166..b4242052d5 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -1,5 +1,5 @@  - + Debug diff --git a/osu.Game.props b/osu.Game.props index 61d5bea511..4173adb509 100644 --- a/osu.Game.props +++ b/osu.Game.props @@ -1,5 +1,5 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index aebd250489..f814cbb3d3 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -1,5 +1,5 @@  - + {2A66DD92-ADB1-4994-89E2-C94E04ACDA0D} From 7451bdaa0ee56b82f5fd22af1016b203adc5b29d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 02:06:24 +0900 Subject: [PATCH 121/235] Update DotSettings --- osu.sln.DotSettings | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index fdfbf25144..92ef2d1021 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -34,6 +34,7 @@ HINT WARNING WARNING + HINT WARNING WARNING DO_NOT_SHOW @@ -44,13 +45,16 @@ WARNING ERROR HINT + HINT HINT WARNING WARNING + DO_NOT_SHOW DO_NOT_SHOW HINT HINT HINT + HINT WARNING WARNING WARNING @@ -149,6 +153,7 @@ WARNING WARNING WARNING + HINT WARNING WARNING HINT From d402222f1778fa5e9d43f91354a7d8a6238670fa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Nov 2017 05:05:07 +0900 Subject: [PATCH 122/235] Fix DisposeTrack and improve AsyncLazy to support disposal --- osu.Game/Beatmaps/WorkingBeatmap.cs | 63 +++++++++++++++++++++++++---- osu.Game/OsuGameBase.cs | 2 +- 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 2a8178882e..93ba51367a 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -31,7 +31,7 @@ namespace osu.Game.Beatmaps Mods.ValueChanged += mods => applyRateAdjustments(); beatmap = new AsyncLazy(populateBeatmap); - background = new AsyncLazy(populateBackground); + background = new AsyncLazy(populateBackground, b => b == null || !b.IsDisposed); track = new AsyncLazy(populateTrack); waveform = new AsyncLazy(populateWaveform); } @@ -99,10 +99,11 @@ namespace osu.Game.Beatmaps if (WaveformLoaded) Waveform?.Dispose(); } - public void DisposeTrack() - { - if (TrackLoaded) Track?.Dispose(); - } + /// + /// Eagerly dispose of the audio track associated with this (if any). + /// Accessing track again will load a fresh instance. + /// + public void RecycleTrack() => track.Recycle(); private void applyRateAdjustments(Track t = null) { @@ -114,11 +115,57 @@ namespace osu.Game.Beatmaps mod.ApplyToClock(t); } - public class AsyncLazy : Lazy> + public class AsyncLazy { - public AsyncLazy(Func valueFactory) - : base(() => Task.Run(valueFactory)) + private Lazy> lazy; + private readonly Func valueFactory; + private readonly Func stillValidFunction; + + public AsyncLazy(Func valueFactory, Func stillValidFunction = null) { + this.valueFactory = valueFactory; + this.stillValidFunction = stillValidFunction; + + init(); + } + + public void Recycle() + { + if (!IsValueCreated) return; + + (lazy.Value.Result as IDisposable)?.Dispose(); + + init(); + } + + public bool IsValueCreated + { + get + { + ensureValid(); + return lazy.IsValueCreated; + } + } + + public Task Value + { + get + { + ensureValid(); + return lazy.Value; + } + } + + private void ensureValid() + { + if (!lazy.IsValueCreated || (stillValidFunction?.Invoke(lazy.Value.Result) ?? true)) return; + + init(); + } + + private void init() + { + lazy = new Lazy>(() => Task.Run(valueFactory)); } } } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 8eaa20f781..0ddff5e5aa 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -154,7 +154,7 @@ namespace osu.Game Debug.Assert(lastBeatmap != null); Debug.Assert(lastBeatmap.Track != null); - lastBeatmap.DisposeTrack(); + lastBeatmap.RecycleTrack(); } Audio.Track.AddItem(b.Track); From acb2cafa581da379df2ec85e6a69969f67694d13 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Wed, 29 Nov 2017 21:09:08 +0100 Subject: [PATCH 123/235] fix wedge not appearing --- osu.Game/Screens/Select/SongSelect.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index a0b788d777..b07e68f50e 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -263,10 +263,7 @@ namespace osu.Game.Screens.Select beatmapNoDebounce = beatmap; if (beatmap == null) - { - if (!Beatmap.IsDefault) - performLoad(); - } + performLoad(); else { if (beatmap.BeatmapSetInfoID == beatmapNoDebounce?.BeatmapSetInfoID) From cd653c1cbc5a5f11652ddfda35e4690502b2f4c0 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Wed, 29 Nov 2017 21:28:02 +0100 Subject: [PATCH 124/235] split storyboard loading into `GetStoryboard()` --- osu.Game/Beatmaps/BeatmapManager.cs | 32 ++++++++++++++++++++++------- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 ++ 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 0641cabcd8..a5578bcfde 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -25,6 +25,7 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; +using osu.Game.Storyboards; namespace osu.Game.Beatmaps { @@ -577,13 +578,6 @@ namespace osu.Game.Beatmaps beatmap = decoder.Decode(stream); } - if (beatmap == null || BeatmapSetInfo.StoryboardFile == null) - return beatmap; - - using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile)))) - decoder.Decode(stream, beatmap); - - return beatmap; } catch @@ -623,6 +617,30 @@ namespace osu.Game.Beatmaps } protected override Waveform GetWaveform() => new Waveform(store.GetStream(getPathForFile(Metadata.AudioFile))); + + protected override Storyboard GetStoryboard() + { + try + { + Beatmap beatmap = Beatmap; + + if (beatmap == null || BeatmapSetInfo.StoryboardFile == null) + return new Storyboard(); + + BeatmapDecoder decoder; + using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) + decoder = BeatmapDecoder.GetDecoder(stream); + + using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile)))) + decoder.Decode(stream, beatmap); + + return beatmap.Storyboard; + } + catch + { + return new Storyboard(); + } + } } /// diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 2a8178882e..456bf29387 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using osu.Game.Storyboards; namespace osu.Game.Beatmaps { @@ -40,6 +41,7 @@ namespace osu.Game.Beatmaps protected abstract Texture GetBackground(); protected abstract Track GetTrack(); protected virtual Waveform GetWaveform() => new Waveform(); + protected virtual Storyboard GetStoryboard() => new Storyboard(); public bool BeatmapLoaded => beatmap.IsValueCreated; public Beatmap Beatmap => beatmap.Value.Result; From 96f5bd33237440dcb6d327f18ec2562945d5e0b4 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Wed, 29 Nov 2017 21:54:04 +0100 Subject: [PATCH 125/235] remove Storyboard from Beatmap, add it to WorkingBeatmap --- osu.Game/Beatmaps/Beatmap.cs | 6 ------ osu.Game/Beatmaps/WorkingBeatmap.cs | 9 +++++++++ osu.Game/Storyboards/Storyboard.cs | 27 ++++++++++++++++++++++++++- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 35b6cc2b02..2c437f8a18 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -41,11 +41,6 @@ namespace osu.Game.Beatmaps /// public double TotalBreakTime => Breaks.Sum(b => b.Duration); - /// - /// The Beatmap's Storyboard. - /// - public Storyboard Storyboard = new Storyboard(); - /// /// Constructs a new beatmap. /// @@ -57,7 +52,6 @@ namespace osu.Game.Beatmaps Breaks = original?.Breaks ?? Breaks; ComboColors = original?.ComboColors ?? ComboColors; HitObjects = original?.HitObjects ?? HitObjects; - Storyboard = original?.Storyboard ?? Storyboard; if (original == null && Metadata == null) { diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 456bf29387..cd6e1bb8c2 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -35,6 +35,7 @@ namespace osu.Game.Beatmaps background = new AsyncLazy(populateBackground); track = new AsyncLazy(populateTrack); waveform = new AsyncLazy(populateWaveform); + storyboard = new AsyncLazy(populateStoryboard); } protected abstract Beatmap GetBeatmap(); @@ -86,6 +87,13 @@ namespace osu.Game.Beatmaps private Waveform populateWaveform() => GetWaveform(); + public bool StoryboardLoaded => storyboard.IsValueCreated; + public Storyboard Storyboard => storyboard.Value.Result; + public async Task GetStoryboardAsync() => await storyboard.Value; + private readonly AsyncLazy storyboard; + + private Storyboard populateStoryboard() => GetStoryboard(); + public void TransferTo(WorkingBeatmap other) { if (track.IsValueCreated && Track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo)) @@ -99,6 +107,7 @@ namespace osu.Game.Beatmaps { if (BackgroundLoaded) Background?.Dispose(); if (WaveformLoaded) Waveform?.Dispose(); + if (StoryboardLoaded) Storyboard?.Dispose(); } public void DisposeTrack() diff --git a/osu.Game/Storyboards/Storyboard.cs b/osu.Game/Storyboards/Storyboard.cs index 59cbe74650..4eca910c1e 100644 --- a/osu.Game/Storyboards/Storyboard.cs +++ b/osu.Game/Storyboards/Storyboard.cs @@ -5,10 +5,11 @@ using osu.Game.Beatmaps; using osu.Game.Storyboards.Drawables; using System.Collections.Generic; using System.Linq; +using System; namespace osu.Game.Storyboards { - public class Storyboard + public class Storyboard : IDisposable { private readonly Dictionary layers = new Dictionary(); public IEnumerable Layers => layers.Values; @@ -59,5 +60,29 @@ namespace osu.Game.Storyboards } return drawable; } + + #region Disposal + + ~Storyboard() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + private bool isDisposed; + + protected virtual void Dispose(bool isDisposing) + { + if (isDisposed) + return; + isDisposed = true; + } + + #endregion } } From 14fdf98abc0aa6981ddf5d56c263c3b3a0446a7b Mon Sep 17 00:00:00 2001 From: jorolf Date: Wed, 29 Nov 2017 23:08:46 +0100 Subject: [PATCH 126/235] rename GetBeatmapSetsResponse --- .../{GetBeatmapSetsResponse.cs => APIResponseBeatmapSet.cs} | 6 +++--- osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs | 2 +- osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs | 2 +- .../Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs | 2 +- osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs | 2 +- osu.Game/osu.Game.csproj | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) rename osu.Game/Online/API/Requests/{GetBeatmapSetsResponse.cs => APIResponseBeatmapSet.cs} (91%) diff --git a/osu.Game/Online/API/Requests/GetBeatmapSetsResponse.cs b/osu.Game/Online/API/Requests/APIResponseBeatmapSet.cs similarity index 91% rename from osu.Game/Online/API/Requests/GetBeatmapSetsResponse.cs rename to osu.Game/Online/API/Requests/APIResponseBeatmapSet.cs index d187cabf31..90d99446c7 100644 --- a/osu.Game/Online/API/Requests/GetBeatmapSetsResponse.cs +++ b/osu.Game/Online/API/Requests/APIResponseBeatmapSet.cs @@ -10,7 +10,7 @@ using System; namespace osu.Game.Online.API.Requests { - public class GetBeatmapSetsResponse : BeatmapMetadata // todo: this is a bit wrong... + public class APIResponseBeatmapSet : BeatmapMetadata // todo: this is a bit wrong... { [JsonProperty(@"covers")] private BeatmapSetOnlineCovers covers { get; set; } @@ -45,7 +45,7 @@ namespace osu.Game.Online.API.Requests } [JsonProperty(@"beatmaps")] - private IEnumerable beatmaps { get; set; } + private IEnumerable beatmaps { get; set; } public BeatmapSetInfo ToBeatmapSet(RulesetStore rulesets) { @@ -69,7 +69,7 @@ namespace osu.Game.Online.API.Requests }; } - private class GetBeatmapSetsBeatmapResponse : BeatmapMetadata + private class APIResponseSetsBeatmap : BeatmapMetadata { [JsonProperty(@"id")] private int onlineBeatmapID { get; set; } diff --git a/osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs b/osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs index e0fdc9adf2..1e6ceaafc6 100644 --- a/osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs +++ b/osu.Game/Online/API/Requests/GetBeatmapSetRequest.cs @@ -3,7 +3,7 @@ namespace osu.Game.Online.API.Requests { - public class GetBeatmapSetRequest : APIRequest + public class GetBeatmapSetRequest : APIRequest { private readonly int beatmapSetId; diff --git a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs index 691f8496d9..173562e04d 100644 --- a/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserBeatmapsRequest.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; namespace osu.Game.Online.API.Requests { - public class GetUserBeatmapsRequest : APIRequest> + public class GetUserBeatmapsRequest : APIRequest> { private readonly long userId; private readonly int offset; diff --git a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs index c45ef734e6..80409fc3b9 100644 --- a/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs +++ b/osu.Game/Online/API/Requests/GetUserMostPlayedBeatmapsRequest.cs @@ -34,7 +34,7 @@ namespace osu.Game.Online.API.Requests private BeatmapInfo beatmap; [JsonProperty] - private GetBeatmapSetsResponse beatmapSet; + private APIResponseBeatmapSet beatmapSet; public BeatmapInfo GetBeatmapInfo(RulesetStore rulesets) { diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs index 56858b3d56..4e6c70124f 100644 --- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs +++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs @@ -9,7 +9,7 @@ using osu.Game.Rulesets; namespace osu.Game.Online.API.Requests { - public class SearchBeatmapSetsRequest : APIRequest> + public class SearchBeatmapSetsRequest : APIRequest> { private readonly string query; private readonly RulesetInfo ruleset; diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index b774602b76..947300cba0 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -285,7 +285,7 @@ - + From a7a9569aee3c4f8e9adfdfb754d39f2a47aa656b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 13:53:57 +0900 Subject: [PATCH 127/235] Don't exit when we don't have composer for now --- osu.Game/Screens/Edit/Screens/Compose/Compose.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs index 6954995340..fea439bfb8 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs @@ -87,7 +87,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose if (composer == null) { Logger.Log($"Ruleset {ruleset.Description} doesn't support hitobject composition."); - ExitRequested?.Invoke(); + // ExitRequested?.Invoke(); return; } From abd61256911a9ff7e52cc5390895ec3c9e226fd1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 14:00:17 +0900 Subject: [PATCH 128/235] Make sure that composerContainer is cleared for testing purposes --- osu.Game/Screens/Edit/Screens/Compose/Compose.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs index fea439bfb8..7e4ce74c5c 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs @@ -82,6 +82,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose private void beatmapChanged(WorkingBeatmap newBeatmap) { + composerContainer.Clear(); + var ruleset = newBeatmap.BeatmapInfo.Ruleset?.CreateInstance(); var composer = ruleset?.CreateHitObjectComposer(); if (composer == null) @@ -91,7 +93,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose return; } - composerContainer.Add(composer); + composerContainer.Child = composer; composerContainer.Clock = new InterpolatingFramedClock((IAdjustableClock)newBeatmap.Track ?? new StopwatchClock()); } } From ead745697875d10bb96fd786c72cbf2e074bac41 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 15:43:19 +0900 Subject: [PATCH 129/235] Add compose radio buttons + testcase --- .../TestCaseEditorComposeRadioButtons.cs | 34 ++++++ osu.Game.Tests/osu.Game.Tests.csproj | 1 + .../RadioButtons/DrawableRadioButton.cs | 107 ++++++++++++++++++ .../Compose/RadioButtons/RadioButton.cs | 20 ++++ .../RadioButtons/RadioButtonCollection.cs | 48 ++++++++ osu.Game/osu.Game.csproj | 5 +- 6 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs create mode 100644 osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs create mode 100644 osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs create mode 100644 osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs diff --git a/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs b/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs new file mode 100644 index 0000000000..55ceac6fd8 --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs @@ -0,0 +1,34 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using System.Collections.Generic; +using osu.Framework.Graphics; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Screens.Edit.Screens.Compose.RadioButtons; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseEditorComposeRadioButtons : OsuTestCase + { + public override IReadOnlyList RequiredTypes => new[] { typeof(DrawableRadioButton) }; + + public TestCaseEditorComposeRadioButtons() + { + Add(new RadioButtonCollection + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Width = 150, + Items = new[] + { + new RadioButton { Text = "Item 1", Action = () => { } }, + new RadioButton { Text = "Item 2", Action = () => { } }, + new RadioButton { Text = "Item 3", Action = () => { } }, + new RadioButton { Text = "Item 4", Action = () => { } }, + new RadioButton { Text = "Item 5", Action = () => { } } + } + }); + } + } +} diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 34f79987c5..ae88fb004f 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -106,6 +106,7 @@ + diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs new file mode 100644 index 0000000000..4ac36cc07e --- /dev/null +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs @@ -0,0 +1,107 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons +{ + public class DrawableRadioButton : TriangleButton + { + private static readonly Color4 DefaultBackgroundColour = OsuColour.FromHex("333"); + private static readonly Color4 DefaultBubbleColour = DefaultBackgroundColour.Darken(0.5f); + private static readonly Color4 SelectedBackgroundColour = OsuColour.FromHex("1188aa"); + private static readonly Color4 SelectedBubbleColour = SelectedBackgroundColour.Lighten(0.5f); + + /// + /// Invoked when this has been selected. + /// + public Action Selected; + + private Drawable bubble; + + public DrawableRadioButton(RadioButton button) + { + Text = button.Text; + Action = button.Action; + + RelativeSizeAxes = Axes.X; + } + + [BackgroundDependencyLoader] + private void load() + { + Triangles.Alpha = 0; + BackgroundColour = DefaultBackgroundColour; + + Add(bubble = new CircularContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + Scale = new Vector2(0.5f), + X = 10, + Masking = true, + Colour = DefaultBubbleColour, + Blending = BlendingMode.Additive, + Child = new Box { RelativeSizeAxes = Axes.Both } + }); + } + + private bool isSelected; + + public void Deselect() + { + if (!isSelected) + return; + isSelected = false; + + BackgroundColour = DefaultBackgroundColour; + bubble.Colour = DefaultBubbleColour; + } + + public void Select() + { + if (isSelected) + return; + isSelected = true; + Selected?.Invoke(this); + + BackgroundColour = SelectedBackgroundColour; + bubble.Colour = SelectedBubbleColour; + } + + protected override bool OnClick(InputState state) + { + if (isSelected) + return true; + + if (!Enabled) + return true; + + Select(); + + return base.OnClick(state); + } + + protected override SpriteText CreateText() => new OsuSpriteText + { + Depth = -1, + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + X = 40f + }; + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs new file mode 100644 index 0000000000..3ef89d2a2b --- /dev/null +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs @@ -0,0 +1,20 @@ +// 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.Screens.Edit.Screens.Compose.RadioButtons +{ + public class RadioButton + { + /// + /// The text that should be displayed in this button. + /// + public string Text; + + /// + /// The that should be invoked when this button is selected. + /// + public Action Action; + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs new file mode 100644 index 0000000000..557e8e1ee8 --- /dev/null +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs @@ -0,0 +1,48 @@ +// 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 System.Linq; +using OpenTK; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons +{ + public class RadioButtonCollection : CompositeDrawable + { + public IReadOnlyList Items + { + set + { + buttonContainer.Clear(); + value.ForEach(addButton); + } + } + + private readonly FlowContainer buttonContainer; + + public RadioButtonCollection() + { + AutoSizeAxes = Axes.Y; + + InternalChild = buttonContainer = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5) + }; + } + + private void addButton(RadioButton button) => buttonContainer.Add(new DrawableRadioButton(button) { Selected = buttonSelected }); + + private DrawableRadioButton currentlySelected; + private void buttonSelected(DrawableRadioButton drawableButton) + { + currentlySelected?.Deselect(); + currentlySelected = drawableButton; + } + } +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index b4ab8a4cf2..1064665a0e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -306,7 +306,6 @@ - @@ -316,6 +315,10 @@ + + + + From 36cfa552f43e6ec9f1e3da59022601a524bb0f0d Mon Sep 17 00:00:00 2001 From: Nicolas Brassard Date: Wed, 29 Nov 2017 23:03:26 -0800 Subject: [PATCH 130/235] Fix SensitivitySlider keyboard control --- osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index 5ebac37cc8..53704ec72d 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -80,6 +80,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input set { + KeyboardStep = 0.01f; + BindableDouble doubleValue = (BindableDouble)value; // create a second layer of bindable so we can only handle state changes when not being dragged. @@ -105,8 +107,6 @@ namespace osu.Game.Overlays.Settings.Sections.Input public SensitivitySlider() { - KeyboardStep = 0.01f; - Current.ValueChanged += newValue => { if (!isDragging && Sensitivity != null) @@ -133,4 +133,4 @@ namespace osu.Game.Overlays.Settings.Sections.Input public override string TooltipText => Current.Disabled ? "Enable raw input to adjust sensitivity" : Current.Value.ToString(@"0.##x"); } } -} \ No newline at end of file +} From 73e41f9dded5128bb4df51efc5ffc74e107828fe Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 16:57:30 +0900 Subject: [PATCH 131/235] Add constructors to RadioButton --- .../Visual/TestCaseEditorComposeRadioButtons.cs | 10 +++++----- .../Screens/Compose/RadioButtons/RadioButton.cs | 14 +++++++++++++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs b/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs index 55ceac6fd8..c259ca666e 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs @@ -22,11 +22,11 @@ namespace osu.Game.Tests.Visual Width = 150, Items = new[] { - new RadioButton { Text = "Item 1", Action = () => { } }, - new RadioButton { Text = "Item 2", Action = () => { } }, - new RadioButton { Text = "Item 3", Action = () => { } }, - new RadioButton { Text = "Item 4", Action = () => { } }, - new RadioButton { Text = "Item 5", Action = () => { } } + new RadioButton("Item 1", () => { }), + new RadioButton("Item 2", () => { }), + new RadioButton("Item 3", () => { }), + new RadioButton("Item 4", () => { }), + new RadioButton("Item 5", () => { }) } }); } diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs index 3ef89d2a2b..bec2d1903d 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs @@ -5,7 +5,7 @@ using System; namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons { - public class RadioButton + public struct RadioButton { /// /// The text that should be displayed in this button. @@ -16,5 +16,17 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons /// The that should be invoked when this button is selected. /// public Action Action; + + public RadioButton(string text) + { + Text = text; + Action = null; + } + + public RadioButton(string text, Action action) + { + Text = text; + Action = action; + } } } From 456bbe25f38e34253cf77b9057e20022add7e9e5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 16:58:14 +0900 Subject: [PATCH 132/235] Implement toolbox into HitObjectComposer --- .../Edit/OsuHitObjectComposer.cs | 11 +++ osu.Game/Rulesets/Edit/HitObjectComposer.cs | 67 ++++++++++++++++++- osu.Game/Rulesets/Edit/ToolboxGroup.cs | 19 ++++++ .../Edit/Tools/HitObjectCompositionTool.cs | 10 +++ .../Rulesets/Edit/Tools/ICompositionTool.cs | 10 +++ osu.Game/Rulesets/Edit/Tools/SelectionTool.cs | 7 ++ .../Screens/Edit/Screens/Compose/Compose.cs | 1 - osu.Game/osu.Game.csproj | 4 ++ 8 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 osu.Game/Rulesets/Edit/ToolboxGroup.cs create mode 100644 osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs create mode 100644 osu.Game/Rulesets/Edit/Tools/ICompositionTool.cs create mode 100644 osu.Game/Rulesets/Edit/Tools/SelectionTool.cs diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 18dfbb6711..851e572163 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -1,6 +1,10 @@ // 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.Rulesets.Edit.Tools; +using osu.Game.Rulesets.Osu.Objects; + namespace osu.Game.Rulesets.Osu.Edit { public class OsuHitObjectComposer : Rulesets.Edit.HitObjectComposer @@ -9,5 +13,12 @@ namespace osu.Game.Rulesets.Osu.Edit : base(ruleset) { } + + protected override IReadOnlyList CompositionTools => new ICompositionTool[] + { + new HitObjectCompositionTool(), + new HitObjectCompositionTool(), + new HitObjectCompositionTool() + }; } } diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index da31f6ba66..8c4969cec5 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -1,9 +1,20 @@ // 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 System.Linq; using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Logging; +using osu.Framework.Timing; +using osu.Game.Rulesets.Edit.Tools; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.UI; +using osu.Game.Screens.Edit.Screens.Compose.RadioButtons; +using osu.Game.Screens.Play.ReplaySettings; namespace osu.Game.Rulesets.Edit { @@ -21,11 +32,63 @@ namespace osu.Game.Rulesets.Edit [BackgroundDependencyLoader] private void load(OsuGameBase osuGame) { + RulesetContainer rulesetContainer; try { - InternalChild = ruleset.CreateRulesetContainerWith(osuGame.Beatmap.Value, true); + rulesetContainer = ruleset.CreateRulesetContainerWith(osuGame.Beatmap.Value, true); } - catch { } + catch (Exception e) + { + Logger.Log($"Could not load this beatmap sucessfully ({e})!", LoggingTarget.Runtime, LogLevel.Error); + return; + } + + RadioButtonCollection toolboxCollection; + InternalChild = new GridContainer + { + RelativeSizeAxes = Axes.Both, + Content = new[] + { + new Drawable[] + { + new FillFlowContainer + { + Name = "Sidebar", + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Right = 10 }, + Children = new Drawable[] + { + new ToolboxGroup { Child = toolboxCollection = new RadioButtonCollection { RelativeSizeAxes = Axes.X } } + } + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + Child = rulesetContainer + } + }, + }, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.Absolute, 200), + } + }; + + rulesetContainer.Clock = new InterpolatingFramedClock((IAdjustableClock)osuGame.Beatmap.Value.Track ?? new StopwatchClock()); + + toolboxCollection.Items = + new[] { new RadioButton("Select", () => setCompositionTool(new SelectionTool())) } + .Concat( + CompositionTools.Select(t => new RadioButton(t.Name, () => setCompositionTool(t))) + ) + .ToList(); } + + private void setCompositionTool(ICompositionTool tool) + { + } + + protected abstract IReadOnlyList CompositionTools { get; } } } diff --git a/osu.Game/Rulesets/Edit/ToolboxGroup.cs b/osu.Game/Rulesets/Edit/ToolboxGroup.cs new file mode 100644 index 0000000000..70e4d3a0c5 --- /dev/null +++ b/osu.Game/Rulesets/Edit/ToolboxGroup.cs @@ -0,0 +1,19 @@ +// 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.Game.Screens.Play.ReplaySettings; + +namespace osu.Game.Rulesets.Edit +{ + public class ToolboxGroup : ReplayGroup + { + protected override string Title => "toolbox"; + + public ToolboxGroup() + { + RelativeSizeAxes = Axes.X; + Width = 1; + } + } +} diff --git a/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs b/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs new file mode 100644 index 0000000000..914cbd11ca --- /dev/null +++ b/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs @@ -0,0 +1,10 @@ +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Rulesets.Edit.Tools +{ + public class HitObjectCompositionTool : ICompositionTool + where T : HitObject + { + public string Name => typeof(T).Name; + } +} diff --git a/osu.Game/Rulesets/Edit/Tools/ICompositionTool.cs b/osu.Game/Rulesets/Edit/Tools/ICompositionTool.cs new file mode 100644 index 0000000000..eba873f0cf --- /dev/null +++ b/osu.Game/Rulesets/Edit/Tools/ICompositionTool.cs @@ -0,0 +1,10 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Edit.Tools +{ + public interface ICompositionTool + { + string Name { get; } + } +} diff --git a/osu.Game/Rulesets/Edit/Tools/SelectionTool.cs b/osu.Game/Rulesets/Edit/Tools/SelectionTool.cs new file mode 100644 index 0000000000..9f3ef78a02 --- /dev/null +++ b/osu.Game/Rulesets/Edit/Tools/SelectionTool.cs @@ -0,0 +1,7 @@ +namespace osu.Game.Rulesets.Edit.Tools +{ + public class SelectionTool : ICompositionTool + { + public string Name => "Select"; + } +} diff --git a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs index 7e4ce74c5c..5eeaf3205d 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs @@ -94,7 +94,6 @@ namespace osu.Game.Screens.Edit.Screens.Compose } composerContainer.Child = composer; - composerContainer.Clock = new InterpolatingFramedClock((IAdjustableClock)newBeatmap.Track ?? new StopwatchClock()); } } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 1064665a0e..886d3313c6 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -562,7 +562,11 @@ + + + + From e5353bb53e2a898a523c542854b6911ed30687ea Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 17:38:55 +0900 Subject: [PATCH 133/235] Add border to playfield, add shadow to toolbox buttons --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 15 ++++++++++++++- .../Compose/RadioButtons/DrawableRadioButton.cs | 8 ++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 8c4969cec5..ed057dbb03 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -4,10 +4,12 @@ using System; using System.Collections.Generic; using System.Linq; +using OpenTK.Graphics; using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Logging; using osu.Framework.Timing; using osu.Game.Rulesets.Edit.Tools; @@ -65,7 +67,18 @@ namespace osu.Game.Rulesets.Edit { RelativeSizeAxes = Axes.Both, Masking = true, - Child = rulesetContainer + BorderColour = Color4.White, + BorderThickness = 2, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + AlwaysPresent = true, + }, + rulesetContainer + } } }, }, diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs index 4ac36cc07e..690a50bb11 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs @@ -45,6 +45,14 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons Triangles.Alpha = 0; BackgroundColour = DefaultBackgroundColour; + Content.EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Shadow, + Radius = 2, + Offset = new Vector2(0, 1), + Colour = Color4.Black.Opacity(0.5f) + }; + Add(bubble = new CircularContainer { Anchor = Anchor.CentreLeft, From 33adf569a4d825ac3ffd90d251785ada52ec4b5c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 17:39:06 +0900 Subject: [PATCH 134/235] Don't select a new beatmap in load() --- osu.Game.Tests/Visual/TestCaseEditorCompose.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs index 9f060a9ec4..d52f27f4ab 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorCompose.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorCompose.cs @@ -31,7 +31,6 @@ namespace osu.Game.Tests.Visual this.beatmaps = beatmaps; compose.Beatmap.BindTo(osuGame.Beatmap); - nextBeatmap(); } private void nextBeatmap() From efa39f38ca1fbd03ad880f010670111f6a7f52f2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 18:48:00 +0900 Subject: [PATCH 135/235] CI fixes --- .../Edit/OsuHitObjectComposer.cs | 3 ++- .../TestCaseEditorComposeRadioButtons.cs | 1 - osu.Game/Rulesets/Edit/HitObjectComposer.cs | 5 +---- .../Screens/Edit/Screens/Compose/Compose.cs | 11 +++++++--- .../RadioButtons/DrawableRadioButton.cs | 20 +++++++++---------- .../RadioButtons/RadioButtonCollection.cs | 1 - 6 files changed, 21 insertions(+), 20 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 851e572163..8a919e0178 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -2,12 +2,13 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Osu.Objects; namespace osu.Game.Rulesets.Osu.Edit { - public class OsuHitObjectComposer : Rulesets.Edit.HitObjectComposer + public class OsuHitObjectComposer : HitObjectComposer { public OsuHitObjectComposer(Ruleset ruleset) : base(ruleset) diff --git a/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs b/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs index c259ca666e..8c2a07b536 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using osu.Framework.Graphics; -using osu.Framework.Graphics.UserInterface; using osu.Game.Screens.Edit.Screens.Compose.RadioButtons; namespace osu.Game.Tests.Visual diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index ed057dbb03..9297c82ba4 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -6,17 +6,14 @@ using System.Collections.Generic; using System.Linq; using OpenTK.Graphics; using osu.Framework.Allocation; -using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Logging; using osu.Framework.Timing; using osu.Game.Rulesets.Edit.Tools; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI; using osu.Game.Screens.Edit.Screens.Compose.RadioButtons; -using osu.Game.Screens.Play.ReplaySettings; namespace osu.Game.Rulesets.Edit { @@ -24,7 +21,7 @@ namespace osu.Game.Rulesets.Edit { private readonly Ruleset ruleset; - public HitObjectComposer(Ruleset ruleset) + protected HitObjectComposer(Ruleset ruleset) { this.ruleset = ruleset; diff --git a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs index 5eeaf3205d..6bc7356f26 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Compose.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Compose.cs @@ -1,14 +1,12 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using OpenTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Logging; -using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Screens.Edit.Screens.Compose.Timeline; @@ -85,7 +83,14 @@ namespace osu.Game.Screens.Edit.Screens.Compose composerContainer.Clear(); var ruleset = newBeatmap.BeatmapInfo.Ruleset?.CreateInstance(); - var composer = ruleset?.CreateHitObjectComposer(); + if (ruleset == null) + { + Logger.Log("Beatmap doesn't have a ruleset assigned."); + // ExitRequested?.Invoke(); + return; + } + + var composer = ruleset.CreateHitObjectComposer(); if (composer == null) { Logger.Log($"Ruleset {ruleset.Description} doesn't support hitobject composition."); diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs index 690a50bb11..b3c9983db9 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs @@ -19,10 +19,10 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons { public class DrawableRadioButton : TriangleButton { - private static readonly Color4 DefaultBackgroundColour = OsuColour.FromHex("333"); - private static readonly Color4 DefaultBubbleColour = DefaultBackgroundColour.Darken(0.5f); - private static readonly Color4 SelectedBackgroundColour = OsuColour.FromHex("1188aa"); - private static readonly Color4 SelectedBubbleColour = SelectedBackgroundColour.Lighten(0.5f); + private static readonly Color4 default_background_colour = OsuColour.FromHex("333"); + private static readonly Color4 default_bubble_colour = default_background_colour.Darken(0.5f); + private static readonly Color4 selected_background_colour = OsuColour.FromHex("1188aa"); + private static readonly Color4 selected_bubble_colour = selected_background_colour.Lighten(0.5f); /// /// Invoked when this has been selected. @@ -43,7 +43,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons private void load() { Triangles.Alpha = 0; - BackgroundColour = DefaultBackgroundColour; + BackgroundColour = default_background_colour; Content.EdgeEffect = new EdgeEffectParameters { @@ -62,7 +62,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons Scale = new Vector2(0.5f), X = 10, Masking = true, - Colour = DefaultBubbleColour, + Colour = default_bubble_colour, Blending = BlendingMode.Additive, Child = new Box { RelativeSizeAxes = Axes.Both } }); @@ -76,8 +76,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons return; isSelected = false; - BackgroundColour = DefaultBackgroundColour; - bubble.Colour = DefaultBubbleColour; + BackgroundColour = default_background_colour; + bubble.Colour = default_bubble_colour; } public void Select() @@ -87,8 +87,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons isSelected = true; Selected?.Invoke(this); - BackgroundColour = SelectedBackgroundColour; - bubble.Colour = SelectedBubbleColour; + BackgroundColour = selected_background_colour; + bubble.Colour = selected_bubble_colour; } protected override bool OnClick(InputState state) diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs index 557e8e1ee8..8a3194b72e 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Collections.Generic; -using System.Linq; using OpenTK; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; From 677f3653eb04ed29b7c99f7c21079691ccc6ce67 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 19:19:34 +0900 Subject: [PATCH 136/235] Hide osu! playfield cursor --- .../Edit/OsuEditPlayfield.cs | 13 +++++++++++++ .../Edit/OsuEditRulesetContainer.cs | 19 +++++++++++++++++++ .../Edit/OsuHitObjectComposer.cs | 4 ++++ osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 8 +++++++- .../osu.Game.Rulesets.Osu.csproj | 2 ++ osu.Game/Rulesets/Edit/HitObjectComposer.cs | 5 ++++- 6 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs create mode 100644 osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs diff --git a/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs b/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs new file mode 100644 index 0000000000..d5fc1b606b --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/OsuEditPlayfield.cs @@ -0,0 +1,13 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics.Cursor; +using osu.Game.Rulesets.Osu.UI; + +namespace osu.Game.Rulesets.Osu.Edit +{ + public class OsuEditPlayfield : OsuPlayfield + { + protected override CursorContainer CreateCursor() => null; + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs b/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs new file mode 100644 index 0000000000..1e9e4b4686 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/OsuEditRulesetContainer.cs @@ -0,0 +1,19 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Osu.Edit +{ + public class OsuEditRulesetContainer : OsuRulesetContainer + { + public OsuEditRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) + : base(ruleset, beatmap, isForCurrentRuleset) + { + } + + protected override Playfield CreatePlayfield() => new OsuEditPlayfield(); + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 8a919e0178..ec3aa4661c 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -2,9 +2,11 @@ // 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.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Osu.Edit { @@ -15,6 +17,8 @@ namespace osu.Game.Rulesets.Osu.Edit { } + protected override RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => new OsuEditRulesetContainer(ruleset, beatmap, true); + protected override IReadOnlyList CompositionTools => new ICompositionTool[] { new HitObjectCompositionTool(), diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 89f6a4e255..387a098a5a 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -13,6 +13,7 @@ using System.Linq; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.UI.Cursor; +using osu.Framework.Graphics.Cursor; namespace osu.Game.Rulesets.Osu.UI { @@ -65,7 +66,10 @@ namespace osu.Game.Rulesets.Osu.UI protected override void LoadComplete() { base.LoadComplete(); - AddInternal(new GameplayCursor()); + + var cursor = CreateCursor(); + if (cursor != null) + AddInternal(cursor); } public override void Add(DrawableHitObject h) @@ -102,5 +106,7 @@ namespace osu.Game.Rulesets.Osu.UI judgementLayer.Add(explosion); } + + protected virtual CursorContainer CreateCursor() => new GameplayCursor(); } } diff --git a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj index c527265c40..bc343ece05 100644 --- a/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj +++ b/osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj @@ -49,6 +49,8 @@ + + diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 9297c82ba4..1e47ea4e63 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Logging; using osu.Framework.Timing; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.UI; using osu.Game.Screens.Edit.Screens.Compose.RadioButtons; @@ -34,7 +35,7 @@ namespace osu.Game.Rulesets.Edit RulesetContainer rulesetContainer; try { - rulesetContainer = ruleset.CreateRulesetContainerWith(osuGame.Beatmap.Value, true); + rulesetContainer = CreateRulesetContainer(ruleset, osuGame.Beatmap.Value); } catch (Exception e) { @@ -99,6 +100,8 @@ namespace osu.Game.Rulesets.Edit { } + protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap, true); + protected abstract IReadOnlyList CompositionTools { get; } } } From e9cbef88f17021032a5cce26d6a616b4648c5a6b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 19:49:55 +0900 Subject: [PATCH 137/235] Improve selection/deselection behaviour of RadioButtonCollections --- .../TestCaseEditorComposeRadioButtons.cs | 10 ++- .../RadioButtons/DrawableRadioButton.cs | 72 ++++++++++--------- .../Compose/RadioButtons/RadioButton.cs | 31 ++++++-- .../RadioButtons/RadioButtonCollection.cs | 26 +++++-- 4 files changed, 93 insertions(+), 46 deletions(-) diff --git a/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs b/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs index 8c2a07b536..f8669cde4b 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorComposeRadioButtons.cs @@ -14,7 +14,8 @@ namespace osu.Game.Tests.Visual public TestCaseEditorComposeRadioButtons() { - Add(new RadioButtonCollection + RadioButtonCollection collection; + Add(collection = new RadioButtonCollection { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -28,6 +29,13 @@ namespace osu.Game.Tests.Visual new RadioButton("Item 5", () => { }) } }); + + for (int i = 0; i < collection.Items.Count; i++) + { + int l = i; + AddStep($"Select item {l + 1}", () => collection.Items[l].Select()); + AddStep($"Deselect item {l + 1}", () => collection.Items[l].Deselect()); + } } } } diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs index b3c9983db9..a6c0f48f1f 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs @@ -27,16 +27,34 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons /// /// Invoked when this has been selected. /// - public Action Selected; + public Action Selected; private Drawable bubble; + private readonly RadioButton button; + public DrawableRadioButton(RadioButton button) { + this.button = button; + Text = button.Text; Action = button.Action; RelativeSizeAxes = Axes.X; + + bubble = new CircularContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit, + Scale = new Vector2(0.5f), + X = 10, + Masking = true, + Colour = default_bubble_colour, + Blending = BlendingMode.Additive, + Child = new Box { RelativeSizeAxes = Axes.Both } + }; } [BackgroundDependencyLoader] @@ -53,53 +71,41 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons Colour = Color4.Black.Opacity(0.5f) }; - Add(bubble = new CircularContainer + Add(bubble); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + button.Selected.ValueChanged += v => { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fit, - Scale = new Vector2(0.5f), - X = 10, - Masking = true, - Colour = default_bubble_colour, - Blending = BlendingMode.Additive, - Child = new Box { RelativeSizeAxes = Axes.Both } - }); + updateSelectionState(); + if (v) + Selected?.Invoke(button); + }; + + updateSelectionState(); } - private bool isSelected; - - public void Deselect() + private void updateSelectionState() { - if (!isSelected) + if (!IsLoaded) return; - isSelected = false; - BackgroundColour = default_background_colour; - bubble.Colour = default_bubble_colour; - } - - public void Select() - { - if (isSelected) - return; - isSelected = true; - Selected?.Invoke(this); - - BackgroundColour = selected_background_colour; - bubble.Colour = selected_bubble_colour; + BackgroundColour = button.Selected ? selected_background_colour : default_background_colour; + bubble.Colour = button.Selected ? selected_bubble_colour : default_bubble_colour; } protected override bool OnClick(InputState state) { - if (isSelected) + if (button.Selected) return true; if (!Enabled) return true; - Select(); + button.Selected.Value = true; return base.OnClick(state); } diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs index bec2d1903d..055362d9e1 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButton.cs @@ -2,11 +2,18 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using osu.Framework.Configuration; namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons { - public struct RadioButton + public class RadioButton { + /// + /// Whether this is selected. + /// + /// + public readonly BindableBool Selected; + /// /// The text that should be displayed in this button. /// @@ -17,16 +24,28 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons /// public Action Action; + public RadioButton(string text, Action action) + { + Text = text; + Action = action; + Selected = new BindableBool(); + } + public RadioButton(string text) + : this(text, null) { Text = text; Action = null; } - public RadioButton(string text, Action action) - { - Text = text; - Action = action; - } + /// + /// Selects this . + /// + public void Select() => Selected.Value = true; + + /// + /// Deselects this . + /// + public void Deselect() => Selected.Value = false; } } diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs index 8a3194b72e..ad24a1da52 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs @@ -11,10 +11,16 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons { public class RadioButtonCollection : CompositeDrawable { + private IReadOnlyList items; public IReadOnlyList Items { + get { return items; } set { + if (items == value) + return; + items = value; + buttonContainer.Clear(); value.ForEach(addButton); } @@ -35,13 +41,21 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons }; } - private void addButton(RadioButton button) => buttonContainer.Add(new DrawableRadioButton(button) { Selected = buttonSelected }); - - private DrawableRadioButton currentlySelected; - private void buttonSelected(DrawableRadioButton drawableButton) + private RadioButton currentlySelected; + private void addButton(RadioButton button) { - currentlySelected?.Deselect(); - currentlySelected = drawableButton; + button.Selected.ValueChanged += v => + { + if (v) + { + currentlySelected?.Deselect(); + currentlySelected = button; + } + else + currentlySelected = null; + }; + + buttonContainer.Add(new DrawableRadioButton(button)); } } } From 7e34b0f08dcc504b81833487530111117eada6c9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 19:53:52 +0900 Subject: [PATCH 138/235] Remove SelectionTool, make Select the default tool --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 4 +++- osu.Game/Rulesets/Edit/Tools/SelectionTool.cs | 7 ------- 2 files changed, 3 insertions(+), 8 deletions(-) delete mode 100644 osu.Game/Rulesets/Edit/Tools/SelectionTool.cs diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 1e47ea4e63..b06af84ec8 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -89,11 +89,13 @@ namespace osu.Game.Rulesets.Edit rulesetContainer.Clock = new InterpolatingFramedClock((IAdjustableClock)osuGame.Beatmap.Value.Track ?? new StopwatchClock()); toolboxCollection.Items = - new[] { new RadioButton("Select", () => setCompositionTool(new SelectionTool())) } + new[] { new RadioButton("Select", () => setCompositionTool(null)) } .Concat( CompositionTools.Select(t => new RadioButton(t.Name, () => setCompositionTool(t))) ) .ToList(); + + toolboxCollection.Items[0].Select(); } private void setCompositionTool(ICompositionTool tool) diff --git a/osu.Game/Rulesets/Edit/Tools/SelectionTool.cs b/osu.Game/Rulesets/Edit/Tools/SelectionTool.cs deleted file mode 100644 index 9f3ef78a02..0000000000 --- a/osu.Game/Rulesets/Edit/Tools/SelectionTool.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace osu.Game.Rulesets.Edit.Tools -{ - public class SelectionTool : ICompositionTool - { - public string Name => "Select"; - } -} From 89772f4efd889836f60480ba3de93cd73598caf9 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 19:54:58 +0900 Subject: [PATCH 139/235] A few resharper fixes --- .../Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs | 3 +-- .../Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs | 2 +- osu.Game/osu.Game.csproj | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs index a6c0f48f1f..48cc7f3379 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs @@ -29,8 +29,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons /// public Action Selected; - private Drawable bubble; - + private readonly Drawable bubble; private readonly RadioButton button; public DrawableRadioButton(RadioButton button) diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs index ad24a1da52..08473eaeba 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs @@ -17,7 +17,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons get { return items; } set { - if (items == value) + if (ReferenceEquals(items, value)) return; items = value; diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 886d3313c6..e1425ff8b8 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -564,7 +564,6 @@ - From e8cbde3ae18746e0b96f17e6ddea0114590085b0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 21:56:12 +0900 Subject: [PATCH 140/235] Add overlay/underlay --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 14 ++++++++---- osu.Game/Rulesets/Edit/PlayfieldOverlay.cs | 24 +++++++++++++++++++++ osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs | 21 ++++++++++++++++++ osu.Game/Rulesets/UI/RulesetContainer.cs | 10 ++++----- osu.Game/osu.Game.csproj | 2 ++ 5 files changed, 62 insertions(+), 9 deletions(-) create mode 100644 osu.Game/Rulesets/Edit/PlayfieldOverlay.cs create mode 100644 osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index b06af84ec8..3aa633115b 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -22,6 +22,8 @@ namespace osu.Game.Rulesets.Edit { private readonly Ruleset ruleset; + protected ICompositionTool CurrentTool { get; private set; } + protected HitObjectComposer(Ruleset ruleset) { this.ruleset = ruleset; @@ -75,7 +77,9 @@ namespace osu.Game.Rulesets.Edit Alpha = 0, AlwaysPresent = true, }, - rulesetContainer + CreateUnderlay(rulesetContainer.Playfield), + rulesetContainer, + CreateOverlay(rulesetContainer.Playfield) } } }, @@ -98,12 +102,14 @@ namespace osu.Game.Rulesets.Edit toolboxCollection.Items[0].Select(); } - private void setCompositionTool(ICompositionTool tool) - { - } + private void setCompositionTool(ICompositionTool tool) => CurrentTool = tool; protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap, true); + protected virtual PlayfieldUnderlay CreateUnderlay(Playfield playfield) => new PlayfieldUnderlay(playfield); + + protected virtual PlayfieldOverlay CreateOverlay(Playfield playfield) => new PlayfieldOverlay(playfield); + protected abstract IReadOnlyList CompositionTools { get; } } } diff --git a/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs b/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs new file mode 100644 index 0000000000..2138079784 --- /dev/null +++ b/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs @@ -0,0 +1,24 @@ +// 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.UI; + +namespace osu.Game.Rulesets.Edit +{ + public class PlayfieldOverlay : CompositeDrawable + { + + private Playfield playfield; + + public PlayfieldOverlay(Playfield playfield) + { + this.playfield = playfield; + + RelativeSizeAxes = Axes.Both; + + } + + } +} diff --git a/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs b/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs new file mode 100644 index 0000000000..7a18c59613 --- /dev/null +++ b/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs @@ -0,0 +1,21 @@ +// 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.UI; + +namespace osu.Game.Rulesets.Edit +{ + public class PlayfieldUnderlay : CompositeDrawable + { + private readonly Playfield playfield; + + public PlayfieldUnderlay(Playfield playfield) + { + this.playfield = playfield; + + RelativeSizeAxes = Axes.Both; + } + } +} diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs index ec26f6f310..69bf6bba29 100644 --- a/osu.Game/Rulesets/UI/RulesetContainer.cs +++ b/osu.Game/Rulesets/UI/RulesetContainer.cs @@ -55,6 +55,11 @@ namespace osu.Game.Rulesets.UI public abstract IEnumerable Objects { get; } + /// + /// The playfield. + /// + public Playfield Playfield { get; protected set; } + protected readonly Ruleset Ruleset; /// @@ -135,11 +140,6 @@ namespace osu.Game.Rulesets.UI public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor(this); - /// - /// The playfield. - /// - public Playfield Playfield { get; private set; } - protected override Container Content => content; private Container content; diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index e1425ff8b8..ad1370890f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -302,6 +302,8 @@ + + From c0c051aa32166fc378e7d13051ecfd932cc04cc7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 21:58:41 +0900 Subject: [PATCH 141/235] Remove unused parameter for now --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 4 ++-- osu.Game/Rulesets/Edit/PlayfieldOverlay.cs | 9 ++------- osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs | 7 +------ 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 3aa633115b..f55b7b0531 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -106,9 +106,9 @@ namespace osu.Game.Rulesets.Edit protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap, true); - protected virtual PlayfieldUnderlay CreateUnderlay(Playfield playfield) => new PlayfieldUnderlay(playfield); + protected virtual PlayfieldUnderlay CreateUnderlay(Playfield playfield) => new PlayfieldUnderlay(); - protected virtual PlayfieldOverlay CreateOverlay(Playfield playfield) => new PlayfieldOverlay(playfield); + protected virtual PlayfieldOverlay CreateOverlay(Playfield playfield) => new PlayfieldOverlay(); protected abstract IReadOnlyList CompositionTools { get; } } diff --git a/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs b/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs index 2138079784..4f5658e39c 100644 --- a/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs +++ b/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs @@ -3,22 +3,17 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Edit { public class PlayfieldOverlay : CompositeDrawable { - - private Playfield playfield; - - public PlayfieldOverlay(Playfield playfield) + public PlayfieldOverlay() { - this.playfield = playfield; - RelativeSizeAxes = Axes.Both; } + } } diff --git a/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs b/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs index 7a18c59613..e9198c71c6 100644 --- a/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs +++ b/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs @@ -3,18 +3,13 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Edit { public class PlayfieldUnderlay : CompositeDrawable { - private readonly Playfield playfield; - - public PlayfieldUnderlay(Playfield playfield) + public PlayfieldUnderlay() { - this.playfield = playfield; - RelativeSizeAxes = Axes.Both; } } From 52ba68e25dd1fb873608d548507b7f4cd7945445 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Nov 2017 22:21:02 +0900 Subject: [PATCH 142/235] Add/fix up license headers --- osu.Game/Rulesets/Edit/PlayfieldOverlay.cs | 2 +- osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs | 2 +- osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs b/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs index 4f5658e39c..58b15e3de2 100644 --- a/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs +++ b/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs @@ -1,5 +1,5 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; diff --git a/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs b/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs index e9198c71c6..bace5258f8 100644 --- a/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs +++ b/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs @@ -1,5 +1,5 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu-framework/master/LICENCE +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; diff --git a/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs b/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs index 914cbd11ca..dd182dcbdb 100644 --- a/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs +++ b/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs @@ -1,3 +1,6 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Edit.Tools From 016057ab014954b09c5510ed9ae2142cf2f1d6f3 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Thu, 30 Nov 2017 16:49:53 +0100 Subject: [PATCH 143/235] readd storyboard to beatmap + minor cleanup --- osu.Game/Beatmaps/Beatmap.cs | 6 ++++++ osu.Game/Beatmaps/BeatmapManager.cs | 17 +++++------------ osu.Game/Screens/Play/Player.cs | 4 ++-- 3 files changed, 13 insertions(+), 14 deletions(-) diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 2c437f8a18..35b6cc2b02 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -41,6 +41,11 @@ namespace osu.Game.Beatmaps /// public double TotalBreakTime => Breaks.Sum(b => b.Duration); + /// + /// The Beatmap's Storyboard. + /// + public Storyboard Storyboard = new Storyboard(); + /// /// Constructs a new beatmap. /// @@ -52,6 +57,7 @@ namespace osu.Game.Beatmaps Breaks = original?.Breaks ?? Breaks; ComboColors = original?.ComboColors ?? ComboColors; HitObjects = original?.HitObjects ?? HitObjects; + Storyboard = original?.Storyboard ?? Storyboard; if (original == null && Metadata == null) { diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index a5578bcfde..4f7c0051c1 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -569,16 +569,11 @@ namespace osu.Game.Beatmaps { try { - Beatmap beatmap; - - BeatmapDecoder decoder; using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) { - decoder = BeatmapDecoder.GetDecoder(stream); - beatmap = decoder.Decode(stream); + BeatmapDecoder decoder = BeatmapDecoder.GetDecoder(stream); + return decoder.Decode(stream); } - - return beatmap; } catch { @@ -622,9 +617,7 @@ namespace osu.Game.Beatmaps { try { - Beatmap beatmap = Beatmap; - - if (beatmap == null || BeatmapSetInfo.StoryboardFile == null) + if (Beatmap == null || BeatmapSetInfo.StoryboardFile == null) return new Storyboard(); BeatmapDecoder decoder; @@ -632,9 +625,9 @@ namespace osu.Game.Beatmaps decoder = BeatmapDecoder.GetDecoder(stream); using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile)))) - decoder.Decode(stream, beatmap); + decoder.Decode(stream, Beatmap); - return beatmap.Storyboard; + return Beatmap.Storyboard; } catch { diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index dc746b305c..b5b09504da 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -245,7 +245,7 @@ namespace osu.Game.Screens.Play private void initializeStoryboard(bool asyncLoad) { - var beatmap = Beatmap.Value.Beatmap; + var beatmap = Beatmap.Value; storyboard = beatmap.Storyboard.CreateDrawable(Beatmap.Value); storyboard.Masking = true; @@ -388,7 +388,7 @@ namespace osu.Game.Screens.Play initializeStoryboard(true); var beatmap = Beatmap.Value; - var storyboardVisible = showStoryboard && beatmap.Beatmap.Storyboard.HasDrawable; + var storyboardVisible = showStoryboard && beatmap.Storyboard.HasDrawable; storyboardContainer.FadeColour(new Color4(opacity, opacity, opacity, 1), 800); storyboardContainer.FadeTo(storyboardVisible && opacity > 0 ? 1 : 0); From c16925059c992cc428d8cf71862d74ca9a063d1a Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Thu, 30 Nov 2017 19:16:13 +0100 Subject: [PATCH 144/235] split parsing a beatmap and parsing a storyboard --- .../Beatmaps/Formats/OsuLegacyDecoderTest.cs | 12 +- .../Beatmaps/IO/OszArchiveReaderTest.cs | 2 +- osu.Game.Tests/Visual/TestCaseStoryboard.cs | 2 +- osu.Game/Beatmaps/Beatmap.cs | 6 - osu.Game/Beatmaps/BeatmapManager.cs | 16 +- osu.Game/Beatmaps/Formats/BeatmapDecoder.cs | 25 +- osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs | 400 ++++++++++-------- osu.Game/Tests/Visual/TestCasePlayer.cs | 2 +- 8 files changed, 262 insertions(+), 203 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs index 95b691e07f..175e07f99c 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs @@ -22,7 +22,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new OsuLegacyDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { - var beatmap = decoder.Decode(new StreamReader(stream)); + var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)); var meta = beatmap.BeatmapInfo.Metadata; Assert.AreEqual(241526, meta.OnlineBeatmapSetID); Assert.AreEqual("Soleily", meta.Artist); @@ -44,7 +44,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new OsuLegacyDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { - var beatmapInfo = decoder.Decode(new StreamReader(stream)).BeatmapInfo; + var beatmapInfo = decoder.DecodeBeatmap(new StreamReader(stream)).BeatmapInfo; Assert.AreEqual(0, beatmapInfo.AudioLeadIn); Assert.AreEqual(false, beatmapInfo.Countdown); Assert.AreEqual(0.7f, beatmapInfo.StackLeniency); @@ -61,7 +61,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new OsuLegacyDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { - var beatmap = decoder.Decode(new StreamReader(stream)).BeatmapInfo; + var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)).BeatmapInfo; int[] expectedBookmarks = { 11505, 22054, 32604, 43153, 53703, 64252, 74802, 85351, @@ -84,7 +84,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new OsuLegacyDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { - var beatmap = decoder.Decode(new StreamReader(stream)); + var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)); var difficulty = beatmap.BeatmapInfo.BaseDifficulty; Assert.AreEqual(6.5f, difficulty.DrainRate); Assert.AreEqual(4, difficulty.CircleSize); @@ -101,7 +101,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new OsuLegacyDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { - var beatmap = decoder.Decode(new StreamReader(stream)); + var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)); Color4[] expected = { new Color4(142, 199, 255, 255), @@ -123,7 +123,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var decoder = new OsuLegacyDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { - var beatmap = decoder.Decode(new StreamReader(stream)); + var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)); var curveData = beatmap.HitObjects[0] as IHasCurve; var positionData = beatmap.HitObjects[0] as IHasPosition; diff --git a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs index 12bbde5b57..7b30a4f1fe 100644 --- a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs @@ -50,7 +50,7 @@ namespace osu.Game.Tests.Beatmaps.IO BeatmapMetadata meta; using (var stream = new StreamReader(reader.GetStream("Soleily - Renatus (Deif) [Platter].osu"))) - meta = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata; + meta = BeatmapDecoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata; Assert.AreEqual(241526, meta.OnlineBeatmapSetID); Assert.AreEqual("Soleily", meta.Artist); diff --git a/osu.Game.Tests/Visual/TestCaseStoryboard.cs b/osu.Game.Tests/Visual/TestCaseStoryboard.cs index 1dad106cbe..0a158f5662 100644 --- a/osu.Game.Tests/Visual/TestCaseStoryboard.cs +++ b/osu.Game.Tests/Visual/TestCaseStoryboard.cs @@ -79,7 +79,7 @@ namespace osu.Game.Tests.Visual var decoupledClock = new DecoupleableInterpolatingFramedClock { IsCoupled = true }; storyboardContainer.Clock = decoupledClock; - storyboard = working.Beatmap.Storyboard.CreateDrawable(beatmapBacking); + storyboard = working.Storyboard.CreateDrawable(beatmapBacking); storyboard.Passing = false; storyboardContainer.Add(storyboard); diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 35b6cc2b02..2c437f8a18 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -41,11 +41,6 @@ namespace osu.Game.Beatmaps /// public double TotalBreakTime => Breaks.Sum(b => b.Duration); - /// - /// The Beatmap's Storyboard. - /// - public Storyboard Storyboard = new Storyboard(); - /// /// Constructs a new beatmap. /// @@ -57,7 +52,6 @@ namespace osu.Game.Beatmaps Breaks = original?.Breaks ?? Breaks; ComboColors = original?.ComboColors ?? ComboColors; HitObjects = original?.HitObjects ?? HitObjects; - Storyboard = original?.Storyboard ?? Storyboard; if (original == null && Metadata == null) { diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 4f7c0051c1..e5f2064ee3 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -495,7 +495,7 @@ namespace osu.Game.Beatmaps BeatmapMetadata metadata; using (var stream = new StreamReader(reader.GetStream(mapName))) - metadata = BeatmapDecoder.GetDecoder(stream).Decode(stream).Metadata; + metadata = BeatmapDecoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata; // check if a set already exists with the same online id. beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.OnlineBeatmapSetID == metadata.OnlineBeatmapSetID) ?? new BeatmapSetInfo @@ -519,7 +519,7 @@ namespace osu.Game.Beatmaps ms.Position = 0; var decoder = BeatmapDecoder.GetDecoder(sr); - Beatmap beatmap = decoder.Decode(sr); + Beatmap beatmap = decoder.DecodeBeatmap(sr); beatmap.BeatmapInfo.Path = name; beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash(); @@ -572,7 +572,7 @@ namespace osu.Game.Beatmaps using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) { BeatmapDecoder decoder = BeatmapDecoder.GetDecoder(stream); - return decoder.Decode(stream); + return decoder.DecodeBeatmap(stream); } } catch @@ -615,19 +615,17 @@ namespace osu.Game.Beatmaps protected override Storyboard GetStoryboard() { + if (BeatmapSetInfo?.StoryboardFile == null) + return new Storyboard(); + try { - if (Beatmap == null || BeatmapSetInfo.StoryboardFile == null) - return new Storyboard(); - BeatmapDecoder decoder; using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) decoder = BeatmapDecoder.GetDecoder(stream); using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile)))) - decoder.Decode(stream, Beatmap); - - return Beatmap.Storyboard; + return decoder.DecodeStoryboard(stream); } catch { diff --git a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs index 7e1a87085c..d785618780 100644 --- a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using osu.Game.Storyboards; namespace osu.Game.Beatmaps.Formats { @@ -35,17 +36,12 @@ namespace osu.Game.Beatmaps.Formats decoders[magic] = typeof(T); } - public virtual Beatmap Decode(StreamReader stream) + public virtual Beatmap DecodeBeatmap(StreamReader stream) { - return ParseFile(stream); + return ParseBeatmap(stream); } - public virtual void Decode(StreamReader stream, Beatmap beatmap) - { - ParseFile(stream, beatmap); - } - - protected virtual Beatmap ParseFile(StreamReader stream) + protected virtual Beatmap ParseBeatmap(StreamReader stream) { var beatmap = new Beatmap { @@ -56,10 +52,19 @@ namespace osu.Game.Beatmaps.Formats }, }; - ParseFile(stream, beatmap); + ParseBeatmap(stream, beatmap); return beatmap; } - protected abstract void ParseFile(StreamReader stream, Beatmap beatmap); + protected abstract void ParseBeatmap(StreamReader stream, Beatmap beatmap); + + public virtual Storyboard DecodeStoryboard(StreamReader stream) + { + var storyboard = new Storyboard(); + ParseStoryboard(stream, storyboard); + return storyboard; + } + + protected abstract void ParseStoryboard(StreamReader stream, Storyboard storyboard); } } diff --git a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs index 11631e9447..e0af018059 100644 --- a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs @@ -54,20 +54,135 @@ namespace osu.Game.Beatmaps.Formats beatmapVersion = int.Parse(header.Substring(17)); } - private enum Section + // + + protected override Beatmap ParseBeatmap(StreamReader stream) { - None, - General, - Editor, - Metadata, - Difficulty, - Events, - TimingPoints, - Colours, - HitObjects, - Variables, + return new LegacyBeatmap(base.ParseBeatmap(stream)); } + public override Beatmap DecodeBeatmap(StreamReader stream) + { + return new LegacyBeatmap(base.DecodeBeatmap(stream)); + } + + protected override void ParseBeatmap(StreamReader stream, Beatmap beatmap) + { + if (beatmap == null) + throw new ArgumentNullException(nameof(beatmap)); + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + beatmap.BeatmapInfo.BeatmapVersion = beatmapVersion; + + Section section = Section.None; + bool hasCustomColours = false; + + string line; + while ((line = stream.ReadLine()) != null) + { + if (string.IsNullOrWhiteSpace(line)) + continue; + + if (line.StartsWith("//")) + continue; + + if (line.StartsWith(@"osu file format v")) + { + beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17)); + continue; + } + + if (line.StartsWith(@"[") && line.EndsWith(@"]")) + { + if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section)) + throw new InvalidDataException($@"Unknown osu section {line}"); + continue; + } + + switch (section) + { + case Section.General: + handleGeneral(beatmap, line); + break; + case Section.Editor: + handleEditor(beatmap, line); + break; + case Section.Metadata: + handleMetadata(beatmap, line); + break; + case Section.Difficulty: + handleDifficulty(beatmap, line); + break; + case Section.Events: + handleEvents(beatmap, line); + break; + case Section.TimingPoints: + handleTimingPoints(beatmap, line); + break; + case Section.Colours: + handleColours(beatmap, line, ref hasCustomColours); + break; + case Section.HitObjects: + + // If the ruleset wasn't specified, assume the osu!standard ruleset. + if (parser == null) + parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(); + + var obj = parser.Parse(line); + + if (obj != null) + beatmap.HitObjects.Add(obj); + + break; + case Section.Variables: + handleVariables(line); + break; + } + } + + foreach (var hitObject in beatmap.HitObjects) + hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty); + } + + protected override void ParseStoryboard(StreamReader stream, Storyboard storyboard) + { + if (storyboard == null) + throw new ArgumentNullException(nameof(storyboard)); + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + Section section = Section.None; + StoryboardSprite storyboardSprite = null; + CommandTimelineGroup timelineGroup = null; + + string line; + while ((line = stream.ReadLine()) != null) + { + if (string.IsNullOrWhiteSpace(line)) + continue; + + if (line.StartsWith("//")) + continue; + + if (line.StartsWith(@"[") && line.EndsWith(@"]")) + { + if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section)) + throw new InvalidDataException($@"Unknown osu section {line}"); + continue; + } + + switch (section) + { + case Section.Events: + handleEvents(storyboard, line, ref storyboardSprite, ref timelineGroup); + break; + } + } + } + + // + private void handleGeneral(Beatmap beatmap, string line) { if (beatmap == null) @@ -240,38 +355,49 @@ namespace osu.Game.Beatmaps.Formats } } - /// - /// Decodes any beatmap variables present in a line into their real values. - /// - /// The line which may contains variables. - private void decodeVariables(ref string line) - { - if (line == null) - throw new ArgumentNullException(nameof(line)); - - while (line.IndexOf('$') >= 0) - { - string origLine = line; - string[] split = line.Split(','); - for (int i = 0; i < split.Length; i++) - { - var item = split[i]; - if (item.StartsWith("$") && variables.ContainsKey(item)) - split[i] = variables[item]; - } - - line = string.Join(",", split); - if (line == origLine) break; - } - } - - private void handleEvents(Beatmap beatmap, string line, ref StoryboardSprite storyboardSprite, ref CommandTimelineGroup timelineGroup) + private void handleEvents(Beatmap beatmap, string line) { if (line == null) throw new ArgumentNullException(nameof(line)); if (beatmap == null) throw new ArgumentNullException(nameof(beatmap)); + decodeVariables(ref line); + + string[] split = line.Split(','); + + EventType type; + if (!Enum.TryParse(split[0], out type)) + throw new InvalidDataException($@"Unknown event type {split[0]}"); + + switch (type) + { + case EventType.Background: + string filename = split[2].Trim('"'); + beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; + break; + case EventType.Break: + var breakEvent = new BreakPeriod + { + StartTime = double.Parse(split[1], NumberFormatInfo.InvariantInfo), + EndTime = double.Parse(split[2], NumberFormatInfo.InvariantInfo) + }; + + if (!breakEvent.HasEffect) + return; + + beatmap.Breaks.Add(breakEvent); + break; + } + } + + private void handleEvents(Storyboard storyboard, string line, ref StoryboardSprite storyboardSprite, ref CommandTimelineGroup timelineGroup) + { + if (line == null) + throw new ArgumentNullException(nameof(line)); + if (storyboard == null) + throw new ArgumentNullException(nameof(storyboard)); + var depth = 0; while (line.StartsWith(" ") || line.StartsWith("_")) { @@ -293,26 +419,6 @@ namespace osu.Game.Beatmaps.Formats switch (type) { - case EventType.Video: - case EventType.Background: - string filename = split[2].Trim('"'); - - if (type == EventType.Background) - beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; - - break; - case EventType.Break: - var breakEvent = new BreakPeriod - { - StartTime = double.Parse(split[1], NumberFormatInfo.InvariantInfo), - EndTime = double.Parse(split[2], NumberFormatInfo.InvariantInfo) - }; - - if (!breakEvent.HasEffect) - return; - - beatmap.Breaks.Add(breakEvent); - break; case EventType.Sprite: { var layer = parseLayer(split[1]); @@ -321,7 +427,7 @@ namespace osu.Game.Beatmaps.Formats var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo); var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo); storyboardSprite = new StoryboardSprite(path, origin, new Vector2(x, y)); - beatmap.Storyboard.GetLayer(layer).Add(storyboardSprite); + storyboard.GetLayer(layer).Add(storyboardSprite); } break; case EventType.Animation: @@ -335,7 +441,7 @@ namespace osu.Game.Beatmaps.Formats var frameDelay = double.Parse(split[7], NumberFormatInfo.InvariantInfo); var loopType = split.Length > 8 ? (AnimationLoopType)Enum.Parse(typeof(AnimationLoopType), split[8]) : AnimationLoopType.LoopForever; storyboardSprite = new StoryboardAnimation(path, origin, new Vector2(x, y), frameCount, frameDelay, loopType); - beatmap.Storyboard.GetLayer(layer).Add(storyboardSprite); + storyboard.GetLayer(layer).Add(storyboardSprite); } break; case EventType.Sample: @@ -344,7 +450,7 @@ namespace osu.Game.Beatmaps.Formats var layer = parseLayer(split[2]); var path = cleanFilename(split[3]); var volume = split.Length > 4 ? float.Parse(split[4], CultureInfo.InvariantCulture) : 100; - beatmap.Storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume)); + storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume)); } break; } @@ -456,9 +562,15 @@ namespace osu.Game.Beatmaps.Formats var type = split[4]; switch (type) { - case "A": timelineGroup?.BlendingMode.Add(easing, startTime, endTime, BlendingMode.Additive, startTime == endTime ? BlendingMode.Additive : BlendingMode.Inherit); break; - case "H": timelineGroup?.FlipH.Add(easing, startTime, endTime, true, startTime == endTime); break; - case "V": timelineGroup?.FlipV.Add(easing, startTime, endTime, true, startTime == endTime); break; + case "A": + timelineGroup?.BlendingMode.Add(easing, startTime, endTime, BlendingMode.Additive, startTime == endTime ? BlendingMode.Additive : BlendingMode.Inherit); + break; + case "H": + timelineGroup?.FlipH.Add(easing, startTime, endTime, true, startTime == endTime); + break; + case "V": + timelineGroup?.FlipV.Add(easing, startTime, endTime, true, startTime == endTime); + break; } } break; @@ -471,30 +583,6 @@ namespace osu.Game.Beatmaps.Formats } } - private static string cleanFilename(string path) - => FileSafety.PathStandardise(path.Trim('\"')); - - private static Anchor parseOrigin(string value) - { - var origin = (LegacyOrigins)Enum.Parse(typeof(LegacyOrigins), value); - switch (origin) - { - case LegacyOrigins.TopLeft: return Anchor.TopLeft; - case LegacyOrigins.TopCentre: return Anchor.TopCentre; - case LegacyOrigins.TopRight: return Anchor.TopRight; - case LegacyOrigins.CentreLeft: return Anchor.CentreLeft; - case LegacyOrigins.Centre: return Anchor.Centre; - case LegacyOrigins.CentreRight: return Anchor.CentreRight; - case LegacyOrigins.BottomLeft: return Anchor.BottomLeft; - case LegacyOrigins.BottomCentre: return Anchor.BottomCentre; - case LegacyOrigins.BottomRight: return Anchor.BottomRight; - } - throw new InvalidDataException($@"Unknown origin: {value}"); - } - - private static string parseLayer(string value) - => Enum.Parse(typeof(StoryLayer), value).ToString(); - private void handleTimingPoints(Beatmap beatmap, string line) { if (beatmap == null) @@ -632,97 +720,57 @@ namespace osu.Game.Beatmaps.Formats variables[pair.Key] = pair.Value; } - protected override Beatmap ParseFile(StreamReader stream) + // + + /// + /// Decodes any beatmap variables present in a line into their real values. + /// + /// The line which may contains variables. + private void decodeVariables(ref string line) { - return new LegacyBeatmap(base.ParseFile(stream)); - } + if (line == null) + throw new ArgumentNullException(nameof(line)); - public override Beatmap Decode(StreamReader stream) - { - return new LegacyBeatmap(base.Decode(stream)); - } - - protected override void ParseFile(StreamReader stream, Beatmap beatmap) - { - if (beatmap == null) - throw new ArgumentNullException(nameof(beatmap)); - if (stream == null) - throw new ArgumentNullException(nameof(stream)); - - beatmap.BeatmapInfo.BeatmapVersion = beatmapVersion; - - Section section = Section.None; - bool hasCustomColours = false; - StoryboardSprite storyboardSprite = null; - CommandTimelineGroup timelineGroup = null; - - string line; - while ((line = stream.ReadLine()) != null) + while (line.IndexOf('$') >= 0) { - if (string.IsNullOrWhiteSpace(line)) - continue; - - if (line.StartsWith("//")) - continue; - - if (line.StartsWith(@"osu file format v")) + string origLine = line; + string[] split = line.Split(','); + for (int i = 0; i < split.Length; i++) { - beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17)); - continue; + var item = split[i]; + if (item.StartsWith("$") && variables.ContainsKey(item)) + split[i] = variables[item]; } - if (line.StartsWith(@"[") && line.EndsWith(@"]")) - { - if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section)) - throw new InvalidDataException($@"Unknown osu section {line}"); - continue; - } - - switch (section) - { - case Section.General: - handleGeneral(beatmap, line); - break; - case Section.Editor: - handleEditor(beatmap, line); - break; - case Section.Metadata: - handleMetadata(beatmap, line); - break; - case Section.Difficulty: - handleDifficulty(beatmap, line); - break; - case Section.Events: - handleEvents(beatmap, line, ref storyboardSprite, ref timelineGroup); - break; - case Section.TimingPoints: - handleTimingPoints(beatmap, line); - break; - case Section.Colours: - handleColours(beatmap, line, ref hasCustomColours); - break; - case Section.HitObjects: - - // If the ruleset wasn't specified, assume the osu!standard ruleset. - if (parser == null) - parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(); - - var obj = parser.Parse(line); - - if (obj != null) - beatmap.HitObjects.Add(obj); - - break; - case Section.Variables: - handleVariables(line); - break; - } + line = string.Join(",", split); + if (line == origLine) break; } - - foreach (var hitObject in beatmap.HitObjects) - hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty); } + private static string cleanFilename(string path) + => FileSafety.PathStandardise(path.Trim('\"')); + + private static Anchor parseOrigin(string value) + { + var origin = (LegacyOrigins)Enum.Parse(typeof(LegacyOrigins), value); + switch (origin) + { + case LegacyOrigins.TopLeft: return Anchor.TopLeft; + case LegacyOrigins.TopCentre: return Anchor.TopCentre; + case LegacyOrigins.TopRight: return Anchor.TopRight; + case LegacyOrigins.CentreLeft: return Anchor.CentreLeft; + case LegacyOrigins.Centre: return Anchor.Centre; + case LegacyOrigins.CentreRight: return Anchor.CentreRight; + case LegacyOrigins.BottomLeft: return Anchor.BottomLeft; + case LegacyOrigins.BottomCentre: return Anchor.BottomCentre; + case LegacyOrigins.BottomRight: return Anchor.BottomRight; + } + throw new InvalidDataException($@"Unknown origin: {value}"); + } + + private static string parseLayer(string value) + => Enum.Parse(typeof(StoryLayer), value).ToString(); + private KeyValuePair splitKeyVal(string line, char separator) { if (line == null) @@ -737,6 +785,20 @@ namespace osu.Game.Beatmaps.Formats ); } + private enum Section + { + None, + General, + Editor, + Metadata, + Difficulty, + Events, + TimingPoints, + Colours, + HitObjects, + Variables, + } + internal enum LegacySampleBank { None = 0, diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs index d9951e002b..d17f20ff2f 100644 --- a/osu.Game/Tests/Visual/TestCasePlayer.cs +++ b/osu.Game/Tests/Visual/TestCasePlayer.cs @@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(test_beatmap_data))) using (var reader = new StreamReader(stream)) - beatmap = BeatmapDecoder.GetDecoder(reader).Decode(reader); + beatmap = BeatmapDecoder.GetDecoder(reader).DecodeBeatmap(reader); return beatmap; } From be018a63c6fa0edfc20d9dc94b505f7b060192f1 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Thu, 30 Nov 2017 19:17:11 +0100 Subject: [PATCH 145/235] remove unnecessary lines --- osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs index e0af018059..47e35f231f 100644 --- a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs @@ -54,8 +54,6 @@ namespace osu.Game.Beatmaps.Formats beatmapVersion = int.Parse(header.Substring(17)); } - // - protected override Beatmap ParseBeatmap(StreamReader stream) { return new LegacyBeatmap(base.ParseBeatmap(stream)); @@ -181,8 +179,6 @@ namespace osu.Game.Beatmaps.Formats } } - // - private void handleGeneral(Beatmap beatmap, string line) { if (beatmap == null) @@ -720,8 +716,6 @@ namespace osu.Game.Beatmaps.Formats variables[pair.Key] = pair.Value; } - // - /// /// Decodes any beatmap variables present in a line into their real values. /// From 7080711cb24184c5561d25a782c113210f797749 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Thu, 30 Nov 2017 20:13:10 +0100 Subject: [PATCH 146/235] remove unnecessary `using` --- osu.Game/Beatmaps/Beatmap.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 2c437f8a18..c8390310d4 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -8,7 +8,6 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps.ControlPoints; using osu.Game.IO.Serialization; -using osu.Game.Storyboards; namespace osu.Game.Beatmaps { From 76a1c7db3b68318229daca40cd06385088e6ca62 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Nov 2017 21:22:57 +0900 Subject: [PATCH 147/235] 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 edb9b1907905ac6afefd62169cdbb8634df3669b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 1 Dec 2017 13:53:18 +0900 Subject: [PATCH 148/235] Make JoinNullCheckWithAssignment a hint --- osu.sln.DotSettings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 92ef2d1021..76929dcbb3 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -49,7 +49,7 @@ HINT WARNING WARNING - DO_NOT_SHOW + HINT DO_NOT_SHOW HINT HINT From a73dfd692bd433f47f2847bbc385a46d62f30979 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 1 Dec 2017 13:53:32 +0900 Subject: [PATCH 149/235] Merge LangVer.props and osu.Game.props --- LangVer.props | 8 -------- osu.Game.props | 6 +++++- 2 files changed, 5 insertions(+), 9 deletions(-) delete mode 100644 LangVer.props diff --git a/LangVer.props b/LangVer.props deleted file mode 100644 index c3aee0505a..0000000000 --- a/LangVer.props +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - 7 - - \ No newline at end of file diff --git a/osu.Game.props b/osu.Game.props index 4173adb509..60a5e97944 100644 --- a/osu.Game.props +++ b/osu.Game.props @@ -1,6 +1,10 @@ - + + + + 7 + osu.licenseheader From 19051dd52962642c0323ce8d0355bcb529cc7845 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 16:44:49 +0900 Subject: [PATCH 150/235] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 4fc866eee3..87d68cda00 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 4fc866eee3803f88b155150e32e021b9c21e647f +Subproject commit 87d68cda0015d51dc3da56d2322fa10d399fc4ed From 881745d7561bb25a67c03978a33ee214b09fbc20 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 15:08:12 +0900 Subject: [PATCH 151/235] 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 0b95e36675b2131ae7eb0ca5185678f8d432a627 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 1 Dec 2017 18:00:20 +0900 Subject: [PATCH 152/235] Fix RelativeChildSize error temporarily --- .../Edit/Components/Timelines/Summary/Parts/TimelinePart.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs index 229d06ef09..8a19784be9 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs @@ -40,7 +40,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts return; } - timeline.RelativeChildSize = new Vector2((float)Math.Max(1, Beatmap.Value.Track.Length), 1); + timeline.RelativeChildSize = Beatmap.Value.Track.Length == double.PositiveInfinity ? Vector2.One : new Vector2((float)Math.Max(1, Beatmap.Value.Track.Length), 1); } protected void Add(Drawable visualisation) => timeline.Add(visualisation); From 5be00cb0ecd9990a6c277a268725655d001775dd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 1 Dec 2017 18:40:55 +0900 Subject: [PATCH 153/235] Add todo --- .../Edit/Components/Timelines/Summary/Parts/TimelinePart.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs index 8a19784be9..df95a5c384 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs @@ -40,6 +40,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts return; } + // Todo: This should be handled more gracefully timeline.RelativeChildSize = Beatmap.Value.Track.Length == double.PositiveInfinity ? Vector2.One : new Vector2((float)Math.Max(1, Beatmap.Value.Track.Length), 1); } From 51cae24a26933ce468b858ae91dd13382039b837 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 19:24:48 +0900 Subject: [PATCH 154/235] 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 155/235] 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 156/235] 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 157/235] 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 158/235] 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 159/235] 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 160/235] 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 161/235] 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 162/235] 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 163/235] 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; From 75327959356327ec3599a4348c0b3099b9e625ae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 22:43:49 +0900 Subject: [PATCH 164/235] Lock during validity checks --- osu.Game/Beatmaps/WorkingBeatmap.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 93ba51367a..8c96074352 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -121,6 +121,8 @@ namespace osu.Game.Beatmaps private readonly Func valueFactory; private readonly Func stillValidFunction; + private readonly object initLock = new object(); + public AsyncLazy(Func valueFactory, Func stillValidFunction = null) { this.valueFactory = valueFactory; @@ -134,7 +136,6 @@ namespace osu.Game.Beatmaps if (!IsValueCreated) return; (lazy.Value.Result as IDisposable)?.Dispose(); - init(); } @@ -158,9 +159,11 @@ namespace osu.Game.Beatmaps private void ensureValid() { - if (!lazy.IsValueCreated || (stillValidFunction?.Invoke(lazy.Value.Result) ?? true)) return; - - init(); + lock (initLock) + { + if (!lazy.IsValueCreated || (stillValidFunction?.Invoke(lazy.Value.Result) ?? true)) return; + init(); + } } private void init() From ee75f90ab33545e51ab8a919b88a3a000eb60ae5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 1 Dec 2017 22:44:25 +0900 Subject: [PATCH 165/235] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 87d68cda00..cc013fc406 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 87d68cda0015d51dc3da56d2322fa10d399fc4ed +Subproject commit cc013fc4063dda0843f38c1c73568a413abcf229 From 806c0e3b2629f708c80fe8f5d63e2b5fa0f62156 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Fri, 1 Dec 2017 17:43:33 +0100 Subject: [PATCH 166/235] restructured OsuLegacyDecoder into LegacyDecoder Beatmap works, Storyboard not... --- .../Beatmaps/Formats/OsuLegacyDecoderTest.cs | 12 +- osu.Game/Beatmaps/Formats/BeatmapDecoder.cs | 7 +- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 401 +++++++++ osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 182 ++++ .../Formats/LegacyStoryboardDecoder.cs | 261 ++++++ osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs | 837 ------------------ .../Objects/Legacy/ConvertHitObjectParser.cs | 4 +- osu.Game/osu.Game.csproj | 4 +- 8 files changed, 857 insertions(+), 851 deletions(-) create mode 100644 osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs create mode 100644 osu.Game/Beatmaps/Formats/LegacyDecoder.cs create mode 100644 osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs delete mode 100644 osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs index 175e07f99c..2ef1b796d1 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs @@ -19,7 +19,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeMetadata() { - var decoder = new OsuLegacyDecoder(); + var decoder = new LegacyBeatmapDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)); @@ -41,7 +41,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeGeneral() { - var decoder = new OsuLegacyDecoder(); + var decoder = new LegacyBeatmapDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { var beatmapInfo = decoder.DecodeBeatmap(new StreamReader(stream)).BeatmapInfo; @@ -58,7 +58,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeEditor() { - var decoder = new OsuLegacyDecoder(); + var decoder = new LegacyBeatmapDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)).BeatmapInfo; @@ -81,7 +81,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeDifficulty() { - var decoder = new OsuLegacyDecoder(); + var decoder = new LegacyBeatmapDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)); @@ -98,7 +98,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeColors() { - var decoder = new OsuLegacyDecoder(); + var decoder = new LegacyBeatmapDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)); @@ -120,7 +120,7 @@ namespace osu.Game.Tests.Beatmaps.Formats [Test] public void TestDecodeHitObjects() { - var decoder = new OsuLegacyDecoder(); + var decoder = new LegacyBeatmapDecoder(); using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) { var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)); diff --git a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs index d785618780..1ecc6c0ee0 100644 --- a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs @@ -14,7 +14,7 @@ namespace osu.Game.Beatmaps.Formats static BeatmapDecoder() { - OsuLegacyDecoder.Register(); + LegacyDecoder.Register(); } public static BeatmapDecoder GetDecoder(StreamReader stream) @@ -36,10 +36,7 @@ namespace osu.Game.Beatmaps.Formats decoders[magic] = typeof(T); } - public virtual Beatmap DecodeBeatmap(StreamReader stream) - { - return ParseBeatmap(stream); - } + public virtual Beatmap DecodeBeatmap(StreamReader stream) => ParseBeatmap(stream); protected virtual Beatmap ParseBeatmap(StreamReader stream) { diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs new file mode 100644 index 0000000000..c8e6d6cfea --- /dev/null +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -0,0 +1,401 @@ +// 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 System.Globalization; +using System.IO; +using OpenTK.Graphics; +using osu.Game.Beatmaps.Timing; +using osu.Game.Beatmaps.Legacy; +using osu.Game.Rulesets.Objects.Legacy; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Storyboards; +using OpenTK; +using osu.Framework.Graphics; +using osu.Framework.IO.File; + +namespace osu.Game.Beatmaps.Formats +{ + public class LegacyBeatmapDecoder : LegacyDecoder + { + private ConvertHitObjectParser parser; + private bool hasCustomColours = false; + + private LegacySampleBank defaultSampleBank; + private int defaultSampleVolume = 100; + + public LegacyBeatmapDecoder() + { + } + + public LegacyBeatmapDecoder(string header) + { + beatmapVersion = int.Parse(header.Substring(17)); + } + + protected override void processSection(Section section, string line) + { + switch (section) + { + case Section.General: + handleGeneral(line); + break; + case Section.Editor: + handleEditor(line); + break; + case Section.Metadata: + handleMetadata(line); + break; + case Section.Difficulty: + handleDifficulty(line); + break; + case Section.Events: + handleEvents(line); + break; + case Section.TimingPoints: + handleTimingPoints(line); + break; + case Section.Colours: + handleColours(line); + break; + case Section.HitObjects: + handleHitObjects(line); + break; + case Section.Variables: + handleVariables(line); + break; + } + } + + private void handleGeneral(string line) + { + var pair = splitKeyVal(line, ':'); + + var metadata = beatmap.BeatmapInfo.Metadata; + switch (pair.Key) + { + case @"AudioFilename": + metadata.AudioFile = pair.Value; + break; + case @"AudioLeadIn": + beatmap.BeatmapInfo.AudioLeadIn = int.Parse(pair.Value); + break; + case @"PreviewTime": + metadata.PreviewTime = int.Parse(pair.Value); + break; + case @"Countdown": + beatmap.BeatmapInfo.Countdown = int.Parse(pair.Value) == 1; + break; + case @"SampleSet": + defaultSampleBank = (LegacySampleBank)Enum.Parse(typeof(LegacySampleBank), pair.Value); + break; + case @"SampleVolume": + defaultSampleVolume = int.Parse(pair.Value); + break; + case @"StackLeniency": + beatmap.BeatmapInfo.StackLeniency = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + case @"Mode": + beatmap.BeatmapInfo.RulesetID = int.Parse(pair.Value); + + switch (beatmap.BeatmapInfo.RulesetID) + { + case 0: + parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(); + break; + case 1: + parser = new Rulesets.Objects.Legacy.Taiko.ConvertHitObjectParser(); + break; + case 2: + parser = new Rulesets.Objects.Legacy.Catch.ConvertHitObjectParser(); + break; + case 3: + parser = new Rulesets.Objects.Legacy.Mania.ConvertHitObjectParser(); + break; + } + break; + case @"LetterboxInBreaks": + beatmap.BeatmapInfo.LetterboxInBreaks = int.Parse(pair.Value) == 1; + break; + case @"SpecialStyle": + beatmap.BeatmapInfo.SpecialStyle = int.Parse(pair.Value) == 1; + break; + case @"WidescreenStoryboard": + beatmap.BeatmapInfo.WidescreenStoryboard = int.Parse(pair.Value) == 1; + break; + } + } + + private void handleEditor(string line) + { + var pair = splitKeyVal(line, ':'); + + switch (pair.Key) + { + case @"Bookmarks": + beatmap.BeatmapInfo.StoredBookmarks = pair.Value; + break; + case @"DistanceSpacing": + beatmap.BeatmapInfo.DistanceSpacing = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + case @"BeatDivisor": + beatmap.BeatmapInfo.BeatDivisor = int.Parse(pair.Value); + break; + case @"GridSize": + beatmap.BeatmapInfo.GridSize = int.Parse(pair.Value); + break; + case @"TimelineZoom": + beatmap.BeatmapInfo.TimelineZoom = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + } + } + + private void handleMetadata(string line) + { + var pair = splitKeyVal(line, ':'); + + var metadata = beatmap.BeatmapInfo.Metadata; + switch (pair.Key) + { + case @"Title": + metadata.Title = pair.Value; + break; + case @"TitleUnicode": + metadata.TitleUnicode = pair.Value; + break; + case @"Artist": + metadata.Artist = pair.Value; + break; + case @"ArtistUnicode": + metadata.ArtistUnicode = pair.Value; + break; + case @"Creator": + metadata.AuthorString = pair.Value; + break; + case @"Version": + beatmap.BeatmapInfo.Version = pair.Value; + break; + case @"Source": + beatmap.BeatmapInfo.Metadata.Source = pair.Value; + break; + case @"Tags": + beatmap.BeatmapInfo.Metadata.Tags = pair.Value; + break; + case @"BeatmapID": + beatmap.BeatmapInfo.OnlineBeatmapID = int.Parse(pair.Value); + break; + case @"BeatmapSetID": + beatmap.BeatmapInfo.OnlineBeatmapSetID = int.Parse(pair.Value); + metadata.OnlineBeatmapSetID = int.Parse(pair.Value); + break; + } + } + + private void handleDifficulty(string line) + { + var pair = splitKeyVal(line, ':'); + + var difficulty = beatmap.BeatmapInfo.BaseDifficulty; + switch (pair.Key) + { + case @"HPDrainRate": + difficulty.DrainRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + case @"CircleSize": + difficulty.CircleSize = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + case @"OverallDifficulty": + difficulty.OverallDifficulty = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + case @"ApproachRate": + difficulty.ApproachRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + case @"SliderMultiplier": + difficulty.SliderMultiplier = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + case @"SliderTickRate": + difficulty.SliderTickRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + break; + } + } + + private void handleEvents(string line) + { + decodeVariables(ref line); + + string[] split = line.Split(','); + + EventType type; + if (!Enum.TryParse(split[0], out type)) + throw new InvalidDataException($@"Unknown event type {split[0]}"); + + switch (type) + { + case EventType.Background: + string filename = split[2].Trim('"'); + beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; + break; + case EventType.Break: + var breakEvent = new BreakPeriod + { + StartTime = double.Parse(split[1], NumberFormatInfo.InvariantInfo), + EndTime = double.Parse(split[2], NumberFormatInfo.InvariantInfo) + }; + + if (!breakEvent.HasEffect) + return; + + beatmap.Breaks.Add(breakEvent); + break; + } + } + + private void handleTimingPoints(string line) + { + string[] split = line.Split(','); + + double time = double.Parse(split[0].Trim(), NumberFormatInfo.InvariantInfo); + double beatLength = double.Parse(split[1].Trim(), NumberFormatInfo.InvariantInfo); + double speedMultiplier = beatLength < 0 ? 100.0 / -beatLength : 1; + + TimeSignatures timeSignature = TimeSignatures.SimpleQuadruple; + if (split.Length >= 3) + timeSignature = split[2][0] == '0' ? TimeSignatures.SimpleQuadruple : (TimeSignatures)int.Parse(split[2]); + + LegacySampleBank sampleSet = defaultSampleBank; + if (split.Length >= 4) + sampleSet = (LegacySampleBank)int.Parse(split[3]); + + //SampleBank sampleBank = SampleBank.Default; + //if (split.Length >= 5) + // sampleBank = (SampleBank)int.Parse(split[4]); + + int sampleVolume = defaultSampleVolume; + if (split.Length >= 6) + sampleVolume = int.Parse(split[5]); + + bool timingChange = true; + if (split.Length >= 7) + timingChange = split[6][0] == '1'; + + bool kiaiMode = false; + bool omitFirstBarSignature = false; + if (split.Length >= 8) + { + int effectFlags = int.Parse(split[7]); + kiaiMode = (effectFlags & 1) > 0; + omitFirstBarSignature = (effectFlags & 8) > 0; + } + + string stringSampleSet = sampleSet.ToString().ToLower(); + if (stringSampleSet == @"none") + stringSampleSet = @"normal"; + + DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(time); + SoundControlPoint soundPoint = beatmap.ControlPointInfo.SoundPointAt(time); + EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(time); + + if (timingChange) + { + beatmap.ControlPointInfo.TimingPoints.Add(new TimingControlPoint + { + Time = time, + BeatLength = beatLength, + TimeSignature = timeSignature + }); + } + + if (speedMultiplier != difficultyPoint.SpeedMultiplier) + { + beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == time); + beatmap.ControlPointInfo.DifficultyPoints.Add(new DifficultyControlPoint + { + Time = time, + SpeedMultiplier = speedMultiplier + }); + } + + if (stringSampleSet != soundPoint.SampleBank || sampleVolume != soundPoint.SampleVolume) + { + beatmap.ControlPointInfo.SoundPoints.Add(new SoundControlPoint + { + Time = time, + SampleBank = stringSampleSet, + SampleVolume = sampleVolume + }); + } + + if (kiaiMode != effectPoint.KiaiMode || omitFirstBarSignature != effectPoint.OmitFirstBarLine) + { + beatmap.ControlPointInfo.EffectPoints.Add(new EffectControlPoint + { + Time = time, + KiaiMode = kiaiMode, + OmitFirstBarLine = omitFirstBarSignature + }); + } + } + + private void handleColours(string line) + { + var pair = splitKeyVal(line, ':'); + + string[] split = pair.Value.Split(','); + + if (split.Length != 3) + throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {pair.Value}"); + + byte r, g, b; + if (!byte.TryParse(split[0], out r) || !byte.TryParse(split[1], out g) || !byte.TryParse(split[2], out b)) + throw new InvalidOperationException(@"Color must be specified with 8-bit integer components"); + + if (!hasCustomColours) + { + beatmap.ComboColors.Clear(); + hasCustomColours = true; + } + + // Note: the combo index specified in the beatmap is discarded + if (pair.Key.StartsWith(@"Combo")) + { + beatmap.ComboColors.Add(new Color4 + { + R = r / 255f, + G = g / 255f, + B = b / 255f, + A = 1f, + }); + } + } + + private void handleHitObjects(string line) + { + // If the ruleset wasn't specified, assume the osu!standard ruleset. + if (parser == null) + parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(); + + var obj = parser.Parse(line); + + if (obj != null) + beatmap.HitObjects.Add(obj); + } + + private void handleVariables(string line) + { + var pair = splitKeyVal(line, '='); + variables[pair.Key] = pair.Value; + } + + private KeyValuePair splitKeyVal(string line, char separator) + { + var split = line.Trim().Split(new[] { separator }, 2); + + return new KeyValuePair + ( + split[0].Trim(), + split.Length > 1 ? split[1].Trim() : string.Empty + ); + } + } +} diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs new file mode 100644 index 0000000000..00b06d28d2 --- /dev/null +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -0,0 +1,182 @@ +// 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 System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using osu.Game.Beatmaps.Legacy; +using osu.Game.Storyboards; + +namespace osu.Game.Beatmaps.Formats +{ + public abstract class LegacyDecoder : BeatmapDecoder + { + public static void Register() + { + AddDecoder(@"osu file format v14"); + AddDecoder(@"osu file format v13"); + AddDecoder(@"osu file format v12"); + AddDecoder(@"osu file format v11"); + AddDecoder(@"osu file format v10"); + AddDecoder(@"osu file format v9"); + AddDecoder(@"osu file format v8"); + AddDecoder(@"osu file format v7"); + AddDecoder(@"osu file format v6"); + AddDecoder(@"osu file format v5"); + AddDecoder(@"osu file format v4"); + AddDecoder(@"osu file format v3"); + // TODO: differences between versions + } + + protected Beatmap beatmap; + protected Storyboard storyboard; + + protected int beatmapVersion; + protected readonly Dictionary variables = new Dictionary(); + + public override Beatmap DecodeBeatmap(StreamReader stream) => new LegacyBeatmap(base.DecodeBeatmap(stream)); + + protected override Beatmap ParseBeatmap(StreamReader stream) => new LegacyBeatmap(base.ParseBeatmap(stream)); + + protected override void ParseBeatmap(StreamReader stream, Beatmap beatmap) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + if (beatmap == null) + throw new ArgumentNullException(nameof(beatmap)); + + this.beatmap = beatmap; + this.beatmap.BeatmapInfo.BeatmapVersion = beatmapVersion; + + ParseContent(stream); + + foreach (var hitObject in this.beatmap.HitObjects) + hitObject.ApplyDefaults(this.beatmap.ControlPointInfo, this.beatmap.BeatmapInfo.BaseDifficulty); + } + + protected override void ParseStoryboard(StreamReader stream, Storyboard storyboard) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + if (storyboard == null) + throw new ArgumentNullException(nameof(storyboard)); + + this.storyboard = storyboard; + + ParseContent(stream); + } + + protected void ParseContent(StreamReader stream) + { + Section section = Section.None; + + string line; + while ((line = stream.ReadLine()) != null) + { + if (string.IsNullOrWhiteSpace(line)) + continue; + + if (line.StartsWith("//")) + continue; + + if (line.StartsWith(@"osu file format v")) + { + beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17)); + continue; + } + + if (line.StartsWith(@"[") && line.EndsWith(@"]")) + { + if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section)) + throw new InvalidDataException($@"Unknown osu section {line}"); + continue; + } + + processSection(section, line); + } + } + + protected abstract void processSection(Section section, string line); + + /// + /// Decodes any beatmap variables present in a line into their real values. + /// + /// The line which may contains variables. + protected void decodeVariables(ref string line) + { + while (line.IndexOf('$') >= 0) + { + string origLine = line; + string[] split = line.Split(','); + for (int i = 0; i < split.Length; i++) + { + var item = split[i]; + if (item.StartsWith("$") && variables.ContainsKey(item)) + split[i] = variables[item]; + } + + line = string.Join(",", split); + if (line == origLine) + break; + } + } + + protected enum Section + { + None, + General, + Editor, + Metadata, + Difficulty, + Events, + TimingPoints, + Colours, + HitObjects, + Variables, + } + + internal enum LegacySampleBank + { + None = 0, + Normal = 1, + Soft = 2, + Drum = 3 + } + + internal enum EventType + { + Background = 0, + Video = 1, + Break = 2, + Colour = 3, + Sprite = 4, + Sample = 5, + Animation = 6 + } + + internal enum LegacyOrigins + { + TopLeft, + Centre, + CentreLeft, + TopRight, + BottomCentre, + TopCentre, + Custom, + CentreRight, + BottomLeft, + BottomRight + }; + + internal enum StoryLayer + { + Background = 0, + Fail = 1, + Pass = 2, + Foreground = 3 + } + } +} diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs new file mode 100644 index 0000000000..9362676245 --- /dev/null +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -0,0 +1,261 @@ +// 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 System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.IO.File; +using osu.Game.Storyboards; + +namespace osu.Game.Beatmaps.Formats +{ + public class LegacyStoryboardDecoder : LegacyDecoder + { + private StoryboardSprite storyboardSprite = null; + private CommandTimelineGroup timelineGroup = null; + + public LegacyStoryboardDecoder() + { + } + + public LegacyStoryboardDecoder(string header) + { + beatmapVersion = int.Parse(header.Substring(17)); + } + + protected override void processSection(Section section, string line) + { + switch (section) + { + case Section.Events: + handleEvents(line); + break; + } + } + + private void handleEvents(string line) + { + var depth = 0; + while (line.StartsWith(" ") || line.StartsWith("_")) + { + ++depth; + line = line.Substring(1); + } + + decodeVariables(ref line); + + string[] split = line.Split(','); + + if (depth == 0) + { + storyboardSprite = null; + + EventType type; + if (!Enum.TryParse(split[0], out type)) + throw new InvalidDataException($@"Unknown event type {split[0]}"); + + switch (type) + { + case EventType.Sprite: + { + var layer = parseLayer(split[1]); + var origin = parseOrigin(split[2]); + var path = cleanFilename(split[3]); + var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo); + var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo); + storyboardSprite = new StoryboardSprite(path, origin, new Vector2(x, y)); + storyboard.GetLayer(layer).Add(storyboardSprite); + } + break; + case EventType.Animation: + { + var layer = parseLayer(split[1]); + var origin = parseOrigin(split[2]); + var path = cleanFilename(split[3]); + var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo); + var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo); + var frameCount = int.Parse(split[6]); + var frameDelay = double.Parse(split[7], NumberFormatInfo.InvariantInfo); + var loopType = split.Length > 8 ? (AnimationLoopType)Enum.Parse(typeof(AnimationLoopType), split[8]) : AnimationLoopType.LoopForever; + storyboardSprite = new StoryboardAnimation(path, origin, new Vector2(x, y), frameCount, frameDelay, loopType); + storyboard.GetLayer(layer).Add(storyboardSprite); + } + break; + case EventType.Sample: + { + var time = double.Parse(split[1], CultureInfo.InvariantCulture); + var layer = parseLayer(split[2]); + var path = cleanFilename(split[3]); + var volume = split.Length > 4 ? float.Parse(split[4], CultureInfo.InvariantCulture) : 100; + storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume)); + } + break; + } + } + else + { + if (depth < 2) + timelineGroup = storyboardSprite?.TimelineGroup; + + var commandType = split[0]; + switch (commandType) + { + case "T": + { + var triggerName = split[1]; + var startTime = split.Length > 2 ? double.Parse(split[2], CultureInfo.InvariantCulture) : double.MinValue; + var endTime = split.Length > 3 ? double.Parse(split[3], CultureInfo.InvariantCulture) : double.MaxValue; + var groupNumber = split.Length > 4 ? int.Parse(split[4]) : 0; + timelineGroup = storyboardSprite?.AddTrigger(triggerName, startTime, endTime, groupNumber); + } + break; + case "L": + { + var startTime = double.Parse(split[1], CultureInfo.InvariantCulture); + var loopCount = int.Parse(split[2]); + timelineGroup = storyboardSprite?.AddLoop(startTime, loopCount); + } + break; + default: + { + if (string.IsNullOrEmpty(split[3])) + split[3] = split[2]; + + var easing = (Easing)int.Parse(split[1]); + var startTime = double.Parse(split[2], CultureInfo.InvariantCulture); + var endTime = double.Parse(split[3], CultureInfo.InvariantCulture); + + switch (commandType) + { + case "F": + { + var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); + var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; + timelineGroup?.Alpha.Add(easing, startTime, endTime, startValue, endValue); + } + break; + case "S": + { + var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); + var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; + timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startValue), new Vector2(endValue)); + } + break; + case "V": + { + var startX = float.Parse(split[4], CultureInfo.InvariantCulture); + var startY = float.Parse(split[5], CultureInfo.InvariantCulture); + var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX; + var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY; + timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY)); + } + break; + case "R": + { + var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); + var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; + timelineGroup?.Rotation.Add(easing, startTime, endTime, MathHelper.RadiansToDegrees(startValue), MathHelper.RadiansToDegrees(endValue)); + } + break; + case "M": + { + var startX = float.Parse(split[4], CultureInfo.InvariantCulture); + var startY = float.Parse(split[5], CultureInfo.InvariantCulture); + var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX; + var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY; + timelineGroup?.X.Add(easing, startTime, endTime, startX, endX); + timelineGroup?.Y.Add(easing, startTime, endTime, startY, endY); + } + break; + case "MX": + { + var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); + var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; + timelineGroup?.X.Add(easing, startTime, endTime, startValue, endValue); + } + break; + case "MY": + { + var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); + var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; + timelineGroup?.Y.Add(easing, startTime, endTime, startValue, endValue); + } + break; + case "C": + { + var startRed = float.Parse(split[4], CultureInfo.InvariantCulture); + var startGreen = float.Parse(split[5], CultureInfo.InvariantCulture); + var startBlue = float.Parse(split[6], CultureInfo.InvariantCulture); + var endRed = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startRed; + var endGreen = split.Length > 8 ? float.Parse(split[8], CultureInfo.InvariantCulture) : startGreen; + var endBlue = split.Length > 9 ? float.Parse(split[9], CultureInfo.InvariantCulture) : startBlue; + timelineGroup?.Colour.Add(easing, startTime, endTime, + new Color4(startRed / 255f, startGreen / 255f, startBlue / 255f, 1), + new Color4(endRed / 255f, endGreen / 255f, endBlue / 255f, 1)); + } + break; + case "P": + { + var type = split[4]; + switch (type) + { + case "A": + timelineGroup?.BlendingMode.Add(easing, startTime, endTime, BlendingMode.Additive, startTime == endTime ? BlendingMode.Additive : BlendingMode.Inherit); + break; + case "H": + timelineGroup?.FlipH.Add(easing, startTime, endTime, true, startTime == endTime); + break; + case "V": + timelineGroup?.FlipV.Add(easing, startTime, endTime, true, startTime == endTime); + break; + } + } + break; + default: + throw new InvalidDataException($@"Unknown command type: {commandType}"); + } + } + break; + } + } + } + + private static string parseLayer(string value) => Enum.Parse(typeof(StoryLayer), value).ToString(); + + private static Anchor parseOrigin(string value) + { + var origin = (LegacyOrigins)Enum.Parse(typeof(LegacyOrigins), value); + switch (origin) + { + case LegacyOrigins.TopLeft: + return Anchor.TopLeft; + case LegacyOrigins.TopCentre: + return Anchor.TopCentre; + case LegacyOrigins.TopRight: + return Anchor.TopRight; + case LegacyOrigins.CentreLeft: + return Anchor.CentreLeft; + case LegacyOrigins.Centre: + return Anchor.Centre; + case LegacyOrigins.CentreRight: + return Anchor.CentreRight; + case LegacyOrigins.BottomLeft: + return Anchor.BottomLeft; + case LegacyOrigins.BottomCentre: + return Anchor.BottomCentre; + case LegacyOrigins.BottomRight: + return Anchor.BottomRight; + } + throw new InvalidDataException($@"Unknown origin: {value}"); + } + + private static string cleanFilename(string path) => FileSafety.PathStandardise(path.Trim('\"')); + } +} diff --git a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs b/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs deleted file mode 100644 index 47e35f231f..0000000000 --- a/osu.Game/Beatmaps/Formats/OsuLegacyDecoder.cs +++ /dev/null @@ -1,837 +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.Collections.Generic; -using System.Globalization; -using System.IO; -using OpenTK.Graphics; -using osu.Game.Beatmaps.Timing; -using osu.Game.Beatmaps.Legacy; -using osu.Game.Rulesets.Objects.Legacy; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Storyboards; -using OpenTK; -using osu.Framework.Graphics; -using osu.Framework.IO.File; - -namespace osu.Game.Beatmaps.Formats -{ - public class OsuLegacyDecoder : BeatmapDecoder - { - public static void Register() - { - AddDecoder(@"osu file format v14"); - AddDecoder(@"osu file format v13"); - AddDecoder(@"osu file format v12"); - AddDecoder(@"osu file format v11"); - AddDecoder(@"osu file format v10"); - AddDecoder(@"osu file format v9"); - AddDecoder(@"osu file format v8"); - AddDecoder(@"osu file format v7"); - AddDecoder(@"osu file format v6"); - AddDecoder(@"osu file format v5"); - AddDecoder(@"osu file format v4"); - AddDecoder(@"osu file format v3"); - // TODO: differences between versions - } - - private ConvertHitObjectParser parser; - - private readonly Dictionary variables = new Dictionary(); - - private LegacySampleBank defaultSampleBank; - private int defaultSampleVolume = 100; - - private readonly int beatmapVersion; - - public OsuLegacyDecoder() - { - } - - public OsuLegacyDecoder(string header) - { - beatmapVersion = int.Parse(header.Substring(17)); - } - - protected override Beatmap ParseBeatmap(StreamReader stream) - { - return new LegacyBeatmap(base.ParseBeatmap(stream)); - } - - public override Beatmap DecodeBeatmap(StreamReader stream) - { - return new LegacyBeatmap(base.DecodeBeatmap(stream)); - } - - protected override void ParseBeatmap(StreamReader stream, Beatmap beatmap) - { - if (beatmap == null) - throw new ArgumentNullException(nameof(beatmap)); - if (stream == null) - throw new ArgumentNullException(nameof(stream)); - - beatmap.BeatmapInfo.BeatmapVersion = beatmapVersion; - - Section section = Section.None; - bool hasCustomColours = false; - - string line; - while ((line = stream.ReadLine()) != null) - { - if (string.IsNullOrWhiteSpace(line)) - continue; - - if (line.StartsWith("//")) - continue; - - if (line.StartsWith(@"osu file format v")) - { - beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17)); - continue; - } - - if (line.StartsWith(@"[") && line.EndsWith(@"]")) - { - if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section)) - throw new InvalidDataException($@"Unknown osu section {line}"); - continue; - } - - switch (section) - { - case Section.General: - handleGeneral(beatmap, line); - break; - case Section.Editor: - handleEditor(beatmap, line); - break; - case Section.Metadata: - handleMetadata(beatmap, line); - break; - case Section.Difficulty: - handleDifficulty(beatmap, line); - break; - case Section.Events: - handleEvents(beatmap, line); - break; - case Section.TimingPoints: - handleTimingPoints(beatmap, line); - break; - case Section.Colours: - handleColours(beatmap, line, ref hasCustomColours); - break; - case Section.HitObjects: - - // If the ruleset wasn't specified, assume the osu!standard ruleset. - if (parser == null) - parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(); - - var obj = parser.Parse(line); - - if (obj != null) - beatmap.HitObjects.Add(obj); - - break; - case Section.Variables: - handleVariables(line); - break; - } - } - - foreach (var hitObject in beatmap.HitObjects) - hitObject.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty); - } - - protected override void ParseStoryboard(StreamReader stream, Storyboard storyboard) - { - if (storyboard == null) - throw new ArgumentNullException(nameof(storyboard)); - if (stream == null) - throw new ArgumentNullException(nameof(stream)); - - Section section = Section.None; - StoryboardSprite storyboardSprite = null; - CommandTimelineGroup timelineGroup = null; - - string line; - while ((line = stream.ReadLine()) != null) - { - if (string.IsNullOrWhiteSpace(line)) - continue; - - if (line.StartsWith("//")) - continue; - - if (line.StartsWith(@"[") && line.EndsWith(@"]")) - { - if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section)) - throw new InvalidDataException($@"Unknown osu section {line}"); - continue; - } - - switch (section) - { - case Section.Events: - handleEvents(storyboard, line, ref storyboardSprite, ref timelineGroup); - break; - } - } - } - - private void handleGeneral(Beatmap beatmap, string line) - { - if (beatmap == null) - throw new ArgumentNullException(nameof(beatmap)); - if (line == null) - throw new ArgumentNullException(nameof(line)); - - var pair = splitKeyVal(line, ':'); - - var metadata = beatmap.BeatmapInfo.Metadata; - switch (pair.Key) - { - case @"AudioFilename": - metadata.AudioFile = pair.Value; - break; - case @"AudioLeadIn": - beatmap.BeatmapInfo.AudioLeadIn = int.Parse(pair.Value); - break; - case @"PreviewTime": - metadata.PreviewTime = int.Parse(pair.Value); - break; - case @"Countdown": - beatmap.BeatmapInfo.Countdown = int.Parse(pair.Value) == 1; - break; - case @"SampleSet": - defaultSampleBank = (LegacySampleBank)Enum.Parse(typeof(LegacySampleBank), pair.Value); - break; - case @"SampleVolume": - defaultSampleVolume = int.Parse(pair.Value); - break; - case @"StackLeniency": - beatmap.BeatmapInfo.StackLeniency = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - case @"Mode": - beatmap.BeatmapInfo.RulesetID = int.Parse(pair.Value); - - switch (beatmap.BeatmapInfo.RulesetID) - { - case 0: - parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(); - break; - case 1: - parser = new Rulesets.Objects.Legacy.Taiko.ConvertHitObjectParser(); - break; - case 2: - parser = new Rulesets.Objects.Legacy.Catch.ConvertHitObjectParser(); - break; - case 3: - parser = new Rulesets.Objects.Legacy.Mania.ConvertHitObjectParser(); - break; - } - break; - case @"LetterboxInBreaks": - beatmap.BeatmapInfo.LetterboxInBreaks = int.Parse(pair.Value) == 1; - break; - case @"SpecialStyle": - beatmap.BeatmapInfo.SpecialStyle = int.Parse(pair.Value) == 1; - break; - case @"WidescreenStoryboard": - beatmap.BeatmapInfo.WidescreenStoryboard = int.Parse(pair.Value) == 1; - break; - } - } - - private void handleEditor(Beatmap beatmap, string line) - { - if (beatmap == null) - throw new ArgumentNullException(nameof(beatmap)); - if (line == null) - throw new ArgumentNullException(nameof(line)); - - var pair = splitKeyVal(line, ':'); - - switch (pair.Key) - { - case @"Bookmarks": - beatmap.BeatmapInfo.StoredBookmarks = pair.Value; - break; - case @"DistanceSpacing": - beatmap.BeatmapInfo.DistanceSpacing = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - case @"BeatDivisor": - beatmap.BeatmapInfo.BeatDivisor = int.Parse(pair.Value); - break; - case @"GridSize": - beatmap.BeatmapInfo.GridSize = int.Parse(pair.Value); - break; - case @"TimelineZoom": - beatmap.BeatmapInfo.TimelineZoom = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - } - } - - private void handleMetadata(Beatmap beatmap, string line) - { - if (beatmap == null) - throw new ArgumentNullException(nameof(beatmap)); - if (line == null) - throw new ArgumentNullException(nameof(line)); - - var pair = splitKeyVal(line, ':'); - - var metadata = beatmap.BeatmapInfo.Metadata; - switch (pair.Key) - { - case @"Title": - metadata.Title = pair.Value; - break; - case @"TitleUnicode": - metadata.TitleUnicode = pair.Value; - break; - case @"Artist": - metadata.Artist = pair.Value; - break; - case @"ArtistUnicode": - metadata.ArtistUnicode = pair.Value; - break; - case @"Creator": - metadata.AuthorString = pair.Value; - break; - case @"Version": - beatmap.BeatmapInfo.Version = pair.Value; - break; - case @"Source": - beatmap.BeatmapInfo.Metadata.Source = pair.Value; - break; - case @"Tags": - beatmap.BeatmapInfo.Metadata.Tags = pair.Value; - break; - case @"BeatmapID": - beatmap.BeatmapInfo.OnlineBeatmapID = int.Parse(pair.Value); - break; - case @"BeatmapSetID": - beatmap.BeatmapInfo.OnlineBeatmapSetID = int.Parse(pair.Value); - metadata.OnlineBeatmapSetID = int.Parse(pair.Value); - break; - } - } - - private void handleDifficulty(Beatmap beatmap, string line) - { - if (beatmap == null) - throw new ArgumentNullException(nameof(beatmap)); - if (line == null) - throw new ArgumentNullException(nameof(line)); - - var pair = splitKeyVal(line, ':'); - - var difficulty = beatmap.BeatmapInfo.BaseDifficulty; - switch (pair.Key) - { - case @"HPDrainRate": - difficulty.DrainRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - case @"CircleSize": - difficulty.CircleSize = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - case @"OverallDifficulty": - difficulty.OverallDifficulty = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - case @"ApproachRate": - difficulty.ApproachRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - case @"SliderMultiplier": - difficulty.SliderMultiplier = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - case @"SliderTickRate": - difficulty.SliderTickRate = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); - break; - } - } - - private void handleEvents(Beatmap beatmap, string line) - { - if (line == null) - throw new ArgumentNullException(nameof(line)); - if (beatmap == null) - throw new ArgumentNullException(nameof(beatmap)); - - decodeVariables(ref line); - - string[] split = line.Split(','); - - EventType type; - if (!Enum.TryParse(split[0], out type)) - throw new InvalidDataException($@"Unknown event type {split[0]}"); - - switch (type) - { - case EventType.Background: - string filename = split[2].Trim('"'); - beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; - break; - case EventType.Break: - var breakEvent = new BreakPeriod - { - StartTime = double.Parse(split[1], NumberFormatInfo.InvariantInfo), - EndTime = double.Parse(split[2], NumberFormatInfo.InvariantInfo) - }; - - if (!breakEvent.HasEffect) - return; - - beatmap.Breaks.Add(breakEvent); - break; - } - } - - private void handleEvents(Storyboard storyboard, string line, ref StoryboardSprite storyboardSprite, ref CommandTimelineGroup timelineGroup) - { - if (line == null) - throw new ArgumentNullException(nameof(line)); - if (storyboard == null) - throw new ArgumentNullException(nameof(storyboard)); - - var depth = 0; - while (line.StartsWith(" ") || line.StartsWith("_")) - { - ++depth; - line = line.Substring(1); - } - - decodeVariables(ref line); - - string[] split = line.Split(','); - - if (depth == 0) - { - storyboardSprite = null; - - EventType type; - if (!Enum.TryParse(split[0], out type)) - throw new InvalidDataException($@"Unknown event type {split[0]}"); - - switch (type) - { - case EventType.Sprite: - { - var layer = parseLayer(split[1]); - var origin = parseOrigin(split[2]); - var path = cleanFilename(split[3]); - var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo); - var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo); - storyboardSprite = new StoryboardSprite(path, origin, new Vector2(x, y)); - storyboard.GetLayer(layer).Add(storyboardSprite); - } - break; - case EventType.Animation: - { - var layer = parseLayer(split[1]); - var origin = parseOrigin(split[2]); - var path = cleanFilename(split[3]); - var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo); - var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo); - var frameCount = int.Parse(split[6]); - var frameDelay = double.Parse(split[7], NumberFormatInfo.InvariantInfo); - var loopType = split.Length > 8 ? (AnimationLoopType)Enum.Parse(typeof(AnimationLoopType), split[8]) : AnimationLoopType.LoopForever; - storyboardSprite = new StoryboardAnimation(path, origin, new Vector2(x, y), frameCount, frameDelay, loopType); - storyboard.GetLayer(layer).Add(storyboardSprite); - } - break; - case EventType.Sample: - { - var time = double.Parse(split[1], CultureInfo.InvariantCulture); - var layer = parseLayer(split[2]); - var path = cleanFilename(split[3]); - var volume = split.Length > 4 ? float.Parse(split[4], CultureInfo.InvariantCulture) : 100; - storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume)); - } - break; - } - } - else - { - if (depth < 2) - timelineGroup = storyboardSprite?.TimelineGroup; - - var commandType = split[0]; - switch (commandType) - { - case "T": - { - var triggerName = split[1]; - var startTime = split.Length > 2 ? double.Parse(split[2], CultureInfo.InvariantCulture) : double.MinValue; - var endTime = split.Length > 3 ? double.Parse(split[3], CultureInfo.InvariantCulture) : double.MaxValue; - var groupNumber = split.Length > 4 ? int.Parse(split[4]) : 0; - timelineGroup = storyboardSprite?.AddTrigger(triggerName, startTime, endTime, groupNumber); - } - break; - case "L": - { - var startTime = double.Parse(split[1], CultureInfo.InvariantCulture); - var loopCount = int.Parse(split[2]); - timelineGroup = storyboardSprite?.AddLoop(startTime, loopCount); - } - break; - default: - { - if (string.IsNullOrEmpty(split[3])) - split[3] = split[2]; - - var easing = (Easing)int.Parse(split[1]); - var startTime = double.Parse(split[2], CultureInfo.InvariantCulture); - var endTime = double.Parse(split[3], CultureInfo.InvariantCulture); - - switch (commandType) - { - case "F": - { - var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); - var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; - timelineGroup?.Alpha.Add(easing, startTime, endTime, startValue, endValue); - } - break; - case "S": - { - var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); - var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; - timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startValue), new Vector2(endValue)); - } - break; - case "V": - { - var startX = float.Parse(split[4], CultureInfo.InvariantCulture); - var startY = float.Parse(split[5], CultureInfo.InvariantCulture); - var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX; - var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY; - timelineGroup?.Scale.Add(easing, startTime, endTime, new Vector2(startX, startY), new Vector2(endX, endY)); - } - break; - case "R": - { - var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); - var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; - timelineGroup?.Rotation.Add(easing, startTime, endTime, MathHelper.RadiansToDegrees(startValue), MathHelper.RadiansToDegrees(endValue)); - } - break; - case "M": - { - var startX = float.Parse(split[4], CultureInfo.InvariantCulture); - var startY = float.Parse(split[5], CultureInfo.InvariantCulture); - var endX = split.Length > 6 ? float.Parse(split[6], CultureInfo.InvariantCulture) : startX; - var endY = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startY; - timelineGroup?.X.Add(easing, startTime, endTime, startX, endX); - timelineGroup?.Y.Add(easing, startTime, endTime, startY, endY); - } - break; - case "MX": - { - var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); - var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; - timelineGroup?.X.Add(easing, startTime, endTime, startValue, endValue); - } - break; - case "MY": - { - var startValue = float.Parse(split[4], CultureInfo.InvariantCulture); - var endValue = split.Length > 5 ? float.Parse(split[5], CultureInfo.InvariantCulture) : startValue; - timelineGroup?.Y.Add(easing, startTime, endTime, startValue, endValue); - } - break; - case "C": - { - var startRed = float.Parse(split[4], CultureInfo.InvariantCulture); - var startGreen = float.Parse(split[5], CultureInfo.InvariantCulture); - var startBlue = float.Parse(split[6], CultureInfo.InvariantCulture); - var endRed = split.Length > 7 ? float.Parse(split[7], CultureInfo.InvariantCulture) : startRed; - var endGreen = split.Length > 8 ? float.Parse(split[8], CultureInfo.InvariantCulture) : startGreen; - var endBlue = split.Length > 9 ? float.Parse(split[9], CultureInfo.InvariantCulture) : startBlue; - timelineGroup?.Colour.Add(easing, startTime, endTime, - new Color4(startRed / 255f, startGreen / 255f, startBlue / 255f, 1), - new Color4(endRed / 255f, endGreen / 255f, endBlue / 255f, 1)); - } - break; - case "P": - { - var type = split[4]; - switch (type) - { - case "A": - timelineGroup?.BlendingMode.Add(easing, startTime, endTime, BlendingMode.Additive, startTime == endTime ? BlendingMode.Additive : BlendingMode.Inherit); - break; - case "H": - timelineGroup?.FlipH.Add(easing, startTime, endTime, true, startTime == endTime); - break; - case "V": - timelineGroup?.FlipV.Add(easing, startTime, endTime, true, startTime == endTime); - break; - } - } - break; - default: - throw new InvalidDataException($@"Unknown command type: {commandType}"); - } - } - break; - } - } - } - - private void handleTimingPoints(Beatmap beatmap, string line) - { - if (beatmap == null) - throw new ArgumentNullException(nameof(beatmap)); - if (line == null) - throw new ArgumentNullException(nameof(line)); - - string[] split = line.Split(','); - - double time = double.Parse(split[0].Trim(), NumberFormatInfo.InvariantInfo); - double beatLength = double.Parse(split[1].Trim(), NumberFormatInfo.InvariantInfo); - double speedMultiplier = beatLength < 0 ? 100.0 / -beatLength : 1; - - TimeSignatures timeSignature = TimeSignatures.SimpleQuadruple; - if (split.Length >= 3) - timeSignature = split[2][0] == '0' ? TimeSignatures.SimpleQuadruple : (TimeSignatures)int.Parse(split[2]); - - LegacySampleBank sampleSet = defaultSampleBank; - if (split.Length >= 4) - sampleSet = (LegacySampleBank)int.Parse(split[3]); - - //SampleBank sampleBank = SampleBank.Default; - //if (split.Length >= 5) - // sampleBank = (SampleBank)int.Parse(split[4]); - - int sampleVolume = defaultSampleVolume; - if (split.Length >= 6) - sampleVolume = int.Parse(split[5]); - - bool timingChange = true; - if (split.Length >= 7) - timingChange = split[6][0] == '1'; - - bool kiaiMode = false; - bool omitFirstBarSignature = false; - if (split.Length >= 8) - { - int effectFlags = int.Parse(split[7]); - kiaiMode = (effectFlags & 1) > 0; - omitFirstBarSignature = (effectFlags & 8) > 0; - } - - string stringSampleSet = sampleSet.ToString().ToLower(); - if (stringSampleSet == @"none") - stringSampleSet = @"normal"; - - DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(time); - SoundControlPoint soundPoint = beatmap.ControlPointInfo.SoundPointAt(time); - EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(time); - - if (timingChange) - { - beatmap.ControlPointInfo.TimingPoints.Add(new TimingControlPoint - { - Time = time, - BeatLength = beatLength, - TimeSignature = timeSignature - }); - } - - if (speedMultiplier != difficultyPoint.SpeedMultiplier) - { - beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == time); - beatmap.ControlPointInfo.DifficultyPoints.Add(new DifficultyControlPoint - { - Time = time, - SpeedMultiplier = speedMultiplier - }); - } - - if (stringSampleSet != soundPoint.SampleBank || sampleVolume != soundPoint.SampleVolume) - { - beatmap.ControlPointInfo.SoundPoints.Add(new SoundControlPoint - { - Time = time, - SampleBank = stringSampleSet, - SampleVolume = sampleVolume - }); - } - - if (kiaiMode != effectPoint.KiaiMode || omitFirstBarSignature != effectPoint.OmitFirstBarLine) - { - beatmap.ControlPointInfo.EffectPoints.Add(new EffectControlPoint - { - Time = time, - KiaiMode = kiaiMode, - OmitFirstBarLine = omitFirstBarSignature - }); - } - } - - private void handleColours(Beatmap beatmap, string line, ref bool hasCustomColours) - { - if (beatmap == null) - throw new ArgumentNullException(nameof(beatmap)); - if (line == null) - throw new ArgumentNullException(nameof(line)); - - var pair = splitKeyVal(line, ':'); - - string[] split = pair.Value.Split(','); - - if (split.Length != 3) - throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {pair.Value}"); - - byte r, g, b; - if (!byte.TryParse(split[0], out r) || !byte.TryParse(split[1], out g) || !byte.TryParse(split[2], out b)) - throw new InvalidOperationException(@"Color must be specified with 8-bit integer components"); - - if (!hasCustomColours) - { - beatmap.ComboColors.Clear(); - hasCustomColours = true; - } - - // Note: the combo index specified in the beatmap is discarded - if (pair.Key.StartsWith(@"Combo")) - { - beatmap.ComboColors.Add(new Color4 - { - R = r / 255f, - G = g / 255f, - B = b / 255f, - A = 1f, - }); - } - } - - private void handleVariables(string line) - { - if (line == null) - throw new ArgumentNullException(nameof(line)); - - var pair = splitKeyVal(line, '='); - variables[pair.Key] = pair.Value; - } - - /// - /// Decodes any beatmap variables present in a line into their real values. - /// - /// The line which may contains variables. - private void decodeVariables(ref string line) - { - if (line == null) - throw new ArgumentNullException(nameof(line)); - - while (line.IndexOf('$') >= 0) - { - string origLine = line; - string[] split = line.Split(','); - for (int i = 0; i < split.Length; i++) - { - var item = split[i]; - if (item.StartsWith("$") && variables.ContainsKey(item)) - split[i] = variables[item]; - } - - line = string.Join(",", split); - if (line == origLine) break; - } - } - - private static string cleanFilename(string path) - => FileSafety.PathStandardise(path.Trim('\"')); - - private static Anchor parseOrigin(string value) - { - var origin = (LegacyOrigins)Enum.Parse(typeof(LegacyOrigins), value); - switch (origin) - { - case LegacyOrigins.TopLeft: return Anchor.TopLeft; - case LegacyOrigins.TopCentre: return Anchor.TopCentre; - case LegacyOrigins.TopRight: return Anchor.TopRight; - case LegacyOrigins.CentreLeft: return Anchor.CentreLeft; - case LegacyOrigins.Centre: return Anchor.Centre; - case LegacyOrigins.CentreRight: return Anchor.CentreRight; - case LegacyOrigins.BottomLeft: return Anchor.BottomLeft; - case LegacyOrigins.BottomCentre: return Anchor.BottomCentre; - case LegacyOrigins.BottomRight: return Anchor.BottomRight; - } - throw new InvalidDataException($@"Unknown origin: {value}"); - } - - private static string parseLayer(string value) - => Enum.Parse(typeof(StoryLayer), value).ToString(); - - private KeyValuePair splitKeyVal(string line, char separator) - { - if (line == null) - throw new ArgumentNullException(nameof(line)); - - var split = line.Trim().Split(new[] { separator }, 2); - - return new KeyValuePair - ( - split[0].Trim(), - split.Length > 1 ? split[1].Trim() : string.Empty - ); - } - - private enum Section - { - None, - General, - Editor, - Metadata, - Difficulty, - Events, - TimingPoints, - Colours, - HitObjects, - Variables, - } - - internal enum LegacySampleBank - { - None = 0, - Normal = 1, - Soft = 2, - Drum = 3 - } - - internal enum EventType - { - Background = 0, - Video = 1, - Break = 2, - Colour = 3, - Sprite = 4, - Sample = 5, - Animation = 6 - } - - internal enum LegacyOrigins - { - TopLeft, - Centre, - CentreLeft, - TopRight, - BottomCentre, - TopCentre, - Custom, - CentreRight, - BottomLeft, - BottomRight - }; - - internal enum StoryLayer - { - Background = 0, - Fail = 1, - Pass = 2, - Foreground = 3 - } - } -} diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index d4f9c7191a..4300f76e9d 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -177,8 +177,8 @@ namespace osu.Game.Rulesets.Objects.Legacy string[] split = str.Split(':'); - var bank = (OsuLegacyDecoder.LegacySampleBank)Convert.ToInt32(split[0]); - var addbank = (OsuLegacyDecoder.LegacySampleBank)Convert.ToInt32(split[1]); + var bank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[0]); + var addbank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[1]); // Let's not implement this for now, because this doesn't fit nicely into the bank structure //string sampleFile = split2.Length > 4 ? split2[4] : string.Empty; diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ccd1bd03dc..4c33b2d266 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -269,6 +269,8 @@ + + @@ -312,7 +314,7 @@ - + From db50ad794e716f0221eca657c59e9d4616cabd65 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Fri, 1 Dec 2017 17:58:11 +0100 Subject: [PATCH 167/235] CI adjustments - removing unnecessary `using`s - name Fields/Methods according to rules - removing unnecessary initializations --- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 90 ++++++++----------- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 47 ++++++---- .../Formats/LegacyStoryboardDecoder.cs | 17 ++-- .../Objects/Legacy/ConvertHitObjectParser.cs | 4 +- 4 files changed, 76 insertions(+), 82 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index c8e6d6cfea..61e9bbc11d 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -7,34 +7,20 @@ using System.Globalization; using System.IO; using OpenTK.Graphics; using osu.Game.Beatmaps.Timing; -using osu.Game.Beatmaps.Legacy; using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Storyboards; -using OpenTK; -using osu.Framework.Graphics; -using osu.Framework.IO.File; namespace osu.Game.Beatmaps.Formats { public class LegacyBeatmapDecoder : LegacyDecoder { + private bool hasCustomColours; private ConvertHitObjectParser parser; - private bool hasCustomColours = false; private LegacySampleBank defaultSampleBank; private int defaultSampleVolume = 100; - public LegacyBeatmapDecoder() - { - } - - public LegacyBeatmapDecoder(string header) - { - beatmapVersion = int.Parse(header.Substring(17)); - } - - protected override void processSection(Section section, string line) + protected override void ProcessSection(Section section, string line) { switch (section) { @@ -72,20 +58,20 @@ namespace osu.Game.Beatmaps.Formats { var pair = splitKeyVal(line, ':'); - var metadata = beatmap.BeatmapInfo.Metadata; + var metadata = Beatmap.BeatmapInfo.Metadata; switch (pair.Key) { case @"AudioFilename": metadata.AudioFile = pair.Value; break; case @"AudioLeadIn": - beatmap.BeatmapInfo.AudioLeadIn = int.Parse(pair.Value); + Beatmap.BeatmapInfo.AudioLeadIn = int.Parse(pair.Value); break; case @"PreviewTime": metadata.PreviewTime = int.Parse(pair.Value); break; case @"Countdown": - beatmap.BeatmapInfo.Countdown = int.Parse(pair.Value) == 1; + Beatmap.BeatmapInfo.Countdown = int.Parse(pair.Value) == 1; break; case @"SampleSet": defaultSampleBank = (LegacySampleBank)Enum.Parse(typeof(LegacySampleBank), pair.Value); @@ -94,12 +80,12 @@ namespace osu.Game.Beatmaps.Formats defaultSampleVolume = int.Parse(pair.Value); break; case @"StackLeniency": - beatmap.BeatmapInfo.StackLeniency = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + Beatmap.BeatmapInfo.StackLeniency = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); break; case @"Mode": - beatmap.BeatmapInfo.RulesetID = int.Parse(pair.Value); + Beatmap.BeatmapInfo.RulesetID = int.Parse(pair.Value); - switch (beatmap.BeatmapInfo.RulesetID) + switch (Beatmap.BeatmapInfo.RulesetID) { case 0: parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(); @@ -116,13 +102,13 @@ namespace osu.Game.Beatmaps.Formats } break; case @"LetterboxInBreaks": - beatmap.BeatmapInfo.LetterboxInBreaks = int.Parse(pair.Value) == 1; + Beatmap.BeatmapInfo.LetterboxInBreaks = int.Parse(pair.Value) == 1; break; case @"SpecialStyle": - beatmap.BeatmapInfo.SpecialStyle = int.Parse(pair.Value) == 1; + Beatmap.BeatmapInfo.SpecialStyle = int.Parse(pair.Value) == 1; break; case @"WidescreenStoryboard": - beatmap.BeatmapInfo.WidescreenStoryboard = int.Parse(pair.Value) == 1; + Beatmap.BeatmapInfo.WidescreenStoryboard = int.Parse(pair.Value) == 1; break; } } @@ -134,19 +120,19 @@ namespace osu.Game.Beatmaps.Formats switch (pair.Key) { case @"Bookmarks": - beatmap.BeatmapInfo.StoredBookmarks = pair.Value; + Beatmap.BeatmapInfo.StoredBookmarks = pair.Value; break; case @"DistanceSpacing": - beatmap.BeatmapInfo.DistanceSpacing = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + Beatmap.BeatmapInfo.DistanceSpacing = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); break; case @"BeatDivisor": - beatmap.BeatmapInfo.BeatDivisor = int.Parse(pair.Value); + Beatmap.BeatmapInfo.BeatDivisor = int.Parse(pair.Value); break; case @"GridSize": - beatmap.BeatmapInfo.GridSize = int.Parse(pair.Value); + Beatmap.BeatmapInfo.GridSize = int.Parse(pair.Value); break; case @"TimelineZoom": - beatmap.BeatmapInfo.TimelineZoom = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + Beatmap.BeatmapInfo.TimelineZoom = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); break; } } @@ -155,7 +141,7 @@ namespace osu.Game.Beatmaps.Formats { var pair = splitKeyVal(line, ':'); - var metadata = beatmap.BeatmapInfo.Metadata; + var metadata = Beatmap.BeatmapInfo.Metadata; switch (pair.Key) { case @"Title": @@ -174,19 +160,19 @@ namespace osu.Game.Beatmaps.Formats metadata.AuthorString = pair.Value; break; case @"Version": - beatmap.BeatmapInfo.Version = pair.Value; + Beatmap.BeatmapInfo.Version = pair.Value; break; case @"Source": - beatmap.BeatmapInfo.Metadata.Source = pair.Value; + Beatmap.BeatmapInfo.Metadata.Source = pair.Value; break; case @"Tags": - beatmap.BeatmapInfo.Metadata.Tags = pair.Value; + Beatmap.BeatmapInfo.Metadata.Tags = pair.Value; break; case @"BeatmapID": - beatmap.BeatmapInfo.OnlineBeatmapID = int.Parse(pair.Value); + Beatmap.BeatmapInfo.OnlineBeatmapID = int.Parse(pair.Value); break; case @"BeatmapSetID": - beatmap.BeatmapInfo.OnlineBeatmapSetID = int.Parse(pair.Value); + Beatmap.BeatmapInfo.OnlineBeatmapSetID = int.Parse(pair.Value); metadata.OnlineBeatmapSetID = int.Parse(pair.Value); break; } @@ -196,7 +182,7 @@ namespace osu.Game.Beatmaps.Formats { var pair = splitKeyVal(line, ':'); - var difficulty = beatmap.BeatmapInfo.BaseDifficulty; + var difficulty = Beatmap.BeatmapInfo.BaseDifficulty; switch (pair.Key) { case @"HPDrainRate": @@ -222,7 +208,7 @@ namespace osu.Game.Beatmaps.Formats private void handleEvents(string line) { - decodeVariables(ref line); + DecodeVariables(ref line); string[] split = line.Split(','); @@ -234,7 +220,7 @@ namespace osu.Game.Beatmaps.Formats { case EventType.Background: string filename = split[2].Trim('"'); - beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; + Beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; break; case EventType.Break: var breakEvent = new BreakPeriod @@ -246,7 +232,7 @@ namespace osu.Game.Beatmaps.Formats if (!breakEvent.HasEffect) return; - beatmap.Breaks.Add(breakEvent); + Beatmap.Breaks.Add(breakEvent); break; } } @@ -292,13 +278,13 @@ namespace osu.Game.Beatmaps.Formats if (stringSampleSet == @"none") stringSampleSet = @"normal"; - DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(time); - SoundControlPoint soundPoint = beatmap.ControlPointInfo.SoundPointAt(time); - EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(time); + DifficultyControlPoint difficultyPoint = Beatmap.ControlPointInfo.DifficultyPointAt(time); + SoundControlPoint soundPoint = Beatmap.ControlPointInfo.SoundPointAt(time); + EffectControlPoint effectPoint = Beatmap.ControlPointInfo.EffectPointAt(time); if (timingChange) { - beatmap.ControlPointInfo.TimingPoints.Add(new TimingControlPoint + Beatmap.ControlPointInfo.TimingPoints.Add(new TimingControlPoint { Time = time, BeatLength = beatLength, @@ -308,8 +294,8 @@ namespace osu.Game.Beatmaps.Formats if (speedMultiplier != difficultyPoint.SpeedMultiplier) { - beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == time); - beatmap.ControlPointInfo.DifficultyPoints.Add(new DifficultyControlPoint + Beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == time); + Beatmap.ControlPointInfo.DifficultyPoints.Add(new DifficultyControlPoint { Time = time, SpeedMultiplier = speedMultiplier @@ -318,7 +304,7 @@ namespace osu.Game.Beatmaps.Formats if (stringSampleSet != soundPoint.SampleBank || sampleVolume != soundPoint.SampleVolume) { - beatmap.ControlPointInfo.SoundPoints.Add(new SoundControlPoint + Beatmap.ControlPointInfo.SoundPoints.Add(new SoundControlPoint { Time = time, SampleBank = stringSampleSet, @@ -328,7 +314,7 @@ namespace osu.Game.Beatmaps.Formats if (kiaiMode != effectPoint.KiaiMode || omitFirstBarSignature != effectPoint.OmitFirstBarLine) { - beatmap.ControlPointInfo.EffectPoints.Add(new EffectControlPoint + Beatmap.ControlPointInfo.EffectPoints.Add(new EffectControlPoint { Time = time, KiaiMode = kiaiMode, @@ -352,14 +338,14 @@ namespace osu.Game.Beatmaps.Formats if (!hasCustomColours) { - beatmap.ComboColors.Clear(); + Beatmap.ComboColors.Clear(); hasCustomColours = true; } // Note: the combo index specified in the beatmap is discarded if (pair.Key.StartsWith(@"Combo")) { - beatmap.ComboColors.Add(new Color4 + Beatmap.ComboColors.Add(new Color4 { R = r / 255f, G = g / 255f, @@ -378,13 +364,13 @@ namespace osu.Game.Beatmaps.Formats var obj = parser.Parse(line); if (obj != null) - beatmap.HitObjects.Add(obj); + Beatmap.HitObjects.Add(obj); } private void handleVariables(string line) { var pair = splitKeyVal(line, '='); - variables[pair.Key] = pair.Value; + Variables[pair.Key] = pair.Value; } private KeyValuePair splitKeyVal(string line, char separator) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 00b06d28d2..249b106962 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -4,15 +4,12 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using osu.Game.Beatmaps.Legacy; using osu.Game.Storyboards; namespace osu.Game.Beatmaps.Formats { - public abstract class LegacyDecoder : BeatmapDecoder + public class LegacyDecoder : BeatmapDecoder { public static void Register() { @@ -31,11 +28,20 @@ namespace osu.Game.Beatmaps.Formats // TODO: differences between versions } - protected Beatmap beatmap; - protected Storyboard storyboard; + public LegacyDecoder() + { + } - protected int beatmapVersion; - protected readonly Dictionary variables = new Dictionary(); + public LegacyDecoder(string header) + { + BeatmapVersion = int.Parse(header.Substring(17)); + } + + protected Beatmap Beatmap; + protected Storyboard Storyboard; + + protected int BeatmapVersion; + protected readonly Dictionary Variables = new Dictionary(); public override Beatmap DecodeBeatmap(StreamReader stream) => new LegacyBeatmap(base.DecodeBeatmap(stream)); @@ -48,13 +54,13 @@ namespace osu.Game.Beatmaps.Formats if (beatmap == null) throw new ArgumentNullException(nameof(beatmap)); - this.beatmap = beatmap; - this.beatmap.BeatmapInfo.BeatmapVersion = beatmapVersion; + Beatmap = beatmap; + Beatmap.BeatmapInfo.BeatmapVersion = BeatmapVersion; ParseContent(stream); - foreach (var hitObject in this.beatmap.HitObjects) - hitObject.ApplyDefaults(this.beatmap.ControlPointInfo, this.beatmap.BeatmapInfo.BaseDifficulty); + foreach (var hitObject in Beatmap.HitObjects) + hitObject.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty); } protected override void ParseStoryboard(StreamReader stream, Storyboard storyboard) @@ -64,7 +70,7 @@ namespace osu.Game.Beatmaps.Formats if (storyboard == null) throw new ArgumentNullException(nameof(storyboard)); - this.storyboard = storyboard; + Storyboard = storyboard; ParseContent(stream); } @@ -84,7 +90,7 @@ namespace osu.Game.Beatmaps.Formats if (line.StartsWith(@"osu file format v")) { - beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17)); + Beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17)); continue; } @@ -95,17 +101,20 @@ namespace osu.Game.Beatmaps.Formats continue; } - processSection(section, line); + ProcessSection(section, line); } } - protected abstract void processSection(Section section, string line); + protected virtual void ProcessSection(Section section, string line) + { + + } /// /// Decodes any beatmap variables present in a line into their real values. /// /// The line which may contains variables. - protected void decodeVariables(ref string line) + protected void DecodeVariables(ref string line) { while (line.IndexOf('$') >= 0) { @@ -114,8 +123,8 @@ namespace osu.Game.Beatmaps.Formats for (int i = 0; i < split.Length; i++) { var item = split[i]; - if (item.StartsWith("$") && variables.ContainsKey(item)) - split[i] = variables[item]; + if (item.StartsWith("$") && Variables.ContainsKey(item)) + split[i] = Variables[item]; } line = string.Join(",", split); diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 9362676245..576eaead11 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; @@ -18,8 +17,8 @@ namespace osu.Game.Beatmaps.Formats { public class LegacyStoryboardDecoder : LegacyDecoder { - private StoryboardSprite storyboardSprite = null; - private CommandTimelineGroup timelineGroup = null; + private StoryboardSprite storyboardSprite; + private CommandTimelineGroup timelineGroup; public LegacyStoryboardDecoder() { @@ -27,10 +26,10 @@ namespace osu.Game.Beatmaps.Formats public LegacyStoryboardDecoder(string header) { - beatmapVersion = int.Parse(header.Substring(17)); + BeatmapVersion = int.Parse(header.Substring(17)); } - protected override void processSection(Section section, string line) + protected override void ProcessSection(Section section, string line) { switch (section) { @@ -49,7 +48,7 @@ namespace osu.Game.Beatmaps.Formats line = line.Substring(1); } - decodeVariables(ref line); + DecodeVariables(ref line); string[] split = line.Split(','); @@ -71,7 +70,7 @@ namespace osu.Game.Beatmaps.Formats var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo); var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo); storyboardSprite = new StoryboardSprite(path, origin, new Vector2(x, y)); - storyboard.GetLayer(layer).Add(storyboardSprite); + Storyboard.GetLayer(layer).Add(storyboardSprite); } break; case EventType.Animation: @@ -85,7 +84,7 @@ namespace osu.Game.Beatmaps.Formats var frameDelay = double.Parse(split[7], NumberFormatInfo.InvariantInfo); var loopType = split.Length > 8 ? (AnimationLoopType)Enum.Parse(typeof(AnimationLoopType), split[8]) : AnimationLoopType.LoopForever; storyboardSprite = new StoryboardAnimation(path, origin, new Vector2(x, y), frameCount, frameDelay, loopType); - storyboard.GetLayer(layer).Add(storyboardSprite); + Storyboard.GetLayer(layer).Add(storyboardSprite); } break; case EventType.Sample: @@ -94,7 +93,7 @@ namespace osu.Game.Beatmaps.Formats var layer = parseLayer(split[2]); var path = cleanFilename(split[3]); var volume = split.Length > 4 ? float.Parse(split[4], CultureInfo.InvariantCulture) : 100; - storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume)); + Storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume)); } break; } diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 4300f76e9d..0d7d617405 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -177,8 +177,8 @@ namespace osu.Game.Rulesets.Objects.Legacy string[] split = str.Split(':'); - var bank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[0]); - var addbank = (LegacyBeatmapDecoder.LegacySampleBank)Convert.ToInt32(split[1]); + var bank = (LegacyDecoder.LegacySampleBank)Convert.ToInt32(split[0]); + var addbank = (LegacyDecoder.LegacySampleBank)Convert.ToInt32(split[1]); // Let's not implement this for now, because this doesn't fit nicely into the bank structure //string sampleFile = split2.Length > 4 ? split2[4] : string.Empty; From de94082b1e142174f8d2275c3369b990df0f3ca3 Mon Sep 17 00:00:00 2001 From: gtensha Date: Fri, 1 Dec 2017 18:10:39 +0100 Subject: [PATCH 168/235] Fix filename casing to compile on Linux --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index f814cbb3d3..8b6bdefc6c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -280,7 +280,7 @@ 20171025071459_AddMissingIndexRules.cs - + 20171119065731_AddBeatmapOnlineIDUniqueConstraint.cs From a49f3479a2dcc111a821a6891814553443526ec7 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Fri, 1 Dec 2017 19:11:52 +0100 Subject: [PATCH 169/235] Split retrieving of beatmap and storyboard decoder Storyboard works again. Not satisfied with the solution though. --- .../Beatmaps/IO/OszArchiveReaderTest.cs | 2 +- osu.Game/Beatmaps/BeatmapManager.cs | 10 +- osu.Game/Beatmaps/Formats/BeatmapDecoder.cs | 67 ---------- osu.Game/Beatmaps/Formats/Decoder.cs | 120 ++++++++++++++++++ .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 9 ++ osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 40 ++---- .../Formats/LegacyStoryboardDecoder.cs | 3 - osu.Game/Tests/Visual/TestCasePlayer.cs | 2 +- osu.Game/osu.Game.csproj | 2 +- 9 files changed, 151 insertions(+), 104 deletions(-) delete mode 100644 osu.Game/Beatmaps/Formats/BeatmapDecoder.cs create mode 100644 osu.Game/Beatmaps/Formats/Decoder.cs diff --git a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs index 7b30a4f1fe..22b1d16f7d 100644 --- a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs @@ -50,7 +50,7 @@ namespace osu.Game.Tests.Beatmaps.IO BeatmapMetadata meta; using (var stream = new StreamReader(reader.GetStream("Soleily - Renatus (Deif) [Platter].osu"))) - meta = BeatmapDecoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata; + meta = Decoder.GetBeatmapDecoder(stream).DecodeBeatmap(stream).Metadata; Assert.AreEqual(241526, meta.OnlineBeatmapSetID); Assert.AreEqual("Soleily", meta.Artist); diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index e5f2064ee3..e0d6ac214e 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -495,7 +495,7 @@ namespace osu.Game.Beatmaps BeatmapMetadata metadata; using (var stream = new StreamReader(reader.GetStream(mapName))) - metadata = BeatmapDecoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata; + metadata = Decoder.GetBeatmapDecoder(stream).DecodeBeatmap(stream).Metadata; // check if a set already exists with the same online id. beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.OnlineBeatmapSetID == metadata.OnlineBeatmapSetID) ?? new BeatmapSetInfo @@ -518,7 +518,7 @@ namespace osu.Game.Beatmaps raw.CopyTo(ms); ms.Position = 0; - var decoder = BeatmapDecoder.GetDecoder(sr); + var decoder = Decoder.GetBeatmapDecoder(sr); Beatmap beatmap = decoder.DecodeBeatmap(sr); beatmap.BeatmapInfo.Path = name; @@ -571,7 +571,7 @@ namespace osu.Game.Beatmaps { using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) { - BeatmapDecoder decoder = BeatmapDecoder.GetDecoder(stream); + Decoder decoder = Decoder.GetBeatmapDecoder(stream); return decoder.DecodeBeatmap(stream); } } @@ -620,9 +620,9 @@ namespace osu.Game.Beatmaps try { - BeatmapDecoder decoder; + Decoder decoder; using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) - decoder = BeatmapDecoder.GetDecoder(stream); + decoder = Decoder.GetStoryboardDecoder(stream); using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile)))) return decoder.DecodeStoryboard(stream); diff --git a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs deleted file mode 100644 index 1ecc6c0ee0..0000000000 --- a/osu.Game/Beatmaps/Formats/BeatmapDecoder.cs +++ /dev/null @@ -1,67 +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.Collections.Generic; -using System.IO; -using osu.Game.Storyboards; - -namespace osu.Game.Beatmaps.Formats -{ - public abstract class BeatmapDecoder - { - private static readonly Dictionary decoders = new Dictionary(); - - static BeatmapDecoder() - { - LegacyDecoder.Register(); - } - - public static BeatmapDecoder GetDecoder(StreamReader stream) - { - if (stream == null) - throw new ArgumentNullException(nameof(stream)); - - string line; - do { line = stream.ReadLine()?.Trim(); } - while (line != null && line.Length == 0); - - if (line == null || !decoders.ContainsKey(line)) - throw new IOException(@"Unknown file format"); - return (BeatmapDecoder)Activator.CreateInstance(decoders[line], line); - } - - protected static void AddDecoder(string magic) where T : BeatmapDecoder - { - decoders[magic] = typeof(T); - } - - public virtual Beatmap DecodeBeatmap(StreamReader stream) => ParseBeatmap(stream); - - protected virtual Beatmap ParseBeatmap(StreamReader stream) - { - var beatmap = new Beatmap - { - BeatmapInfo = new BeatmapInfo - { - Metadata = new BeatmapMetadata(), - BaseDifficulty = new BeatmapDifficulty(), - }, - }; - - ParseBeatmap(stream, beatmap); - return beatmap; - } - - protected abstract void ParseBeatmap(StreamReader stream, Beatmap beatmap); - - public virtual Storyboard DecodeStoryboard(StreamReader stream) - { - var storyboard = new Storyboard(); - ParseStoryboard(stream, storyboard); - return storyboard; - } - - protected abstract void ParseStoryboard(StreamReader stream, Storyboard storyboard); - } -} diff --git a/osu.Game/Beatmaps/Formats/Decoder.cs b/osu.Game/Beatmaps/Formats/Decoder.cs new file mode 100644 index 0000000000..75e660bc8d --- /dev/null +++ b/osu.Game/Beatmaps/Formats/Decoder.cs @@ -0,0 +1,120 @@ +// 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 System.IO; +using osu.Game.Storyboards; + +namespace osu.Game.Beatmaps.Formats +{ + public abstract class Decoder + { + private static readonly Dictionary beatmapDecoders = new Dictionary(); + private static readonly Dictionary storyboardDecoders = new Dictionary(); + + static Decoder() + { + LegacyDecoder.Register(); + } + + /// + /// Retrieves a to parse s. + /// + /// A stream pointing to the to retrieve the version from. + public static Decoder GetBeatmapDecoder(StreamReader stream) + { + string line = readFirstLine(stream); + + if (line == null || !beatmapDecoders.ContainsKey(line)) + throw new IOException(@"Unknown file format"); + return (Decoder)Activator.CreateInstance(beatmapDecoders[line], line); + } + + /// + /// Retrieves a to parse s. + /// + /// A stream pointing to the to retrieve the version from. + public static Decoder GetStoryboardDecoder(StreamReader stream) + { + string line = readFirstLine(stream); + + if (line == null || !storyboardDecoders.ContainsKey(line)) + throw new IOException(@"Unknown file format"); + return (Decoder)Activator.CreateInstance(storyboardDecoders[line], line); + } + + private static string readFirstLine(StreamReader stream) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + string line; + do + { line = stream.ReadLine()?.Trim(); } + while (line != null && line.Length == 0); + + return line; + } + + /// + /// Adds the to the list of and decoder. + /// + /// Type to decode a with. + /// /// Type to decode a with. + /// A string representation of the version. + protected static void AddDecoder(string version) where A : Decoder where B : Decoder + { + beatmapDecoders[version] = typeof(A); + storyboardDecoders[version] = typeof(B); + } + + /// + /// Adds the to the list of decoder. + /// + /// Type to decode a with. + /// A string representation of the version. + protected static void AddBeatmapDecoder(string version) where T : Decoder + { + beatmapDecoders[version] = typeof(T); + } + + /// + /// Adds the to the list of decoder. + /// + /// Type to decode a with. + /// A string representation of the version. + protected static void AddStoryboardDecoder(string version) where T : Decoder + { + storyboardDecoders[version] = typeof(T); + } + + public virtual Beatmap DecodeBeatmap(StreamReader stream) => ParseBeatmap(stream); + + protected virtual Beatmap ParseBeatmap(StreamReader stream) + { + var beatmap = new Beatmap + { + BeatmapInfo = new BeatmapInfo + { + Metadata = new BeatmapMetadata(), + BaseDifficulty = new BeatmapDifficulty(), + }, + }; + + ParseBeatmap(stream, beatmap); + return beatmap; + } + + protected abstract void ParseBeatmap(StreamReader stream, Beatmap beatmap); + + public virtual Storyboard DecodeStoryboard(StreamReader stream) + { + var storyboard = new Storyboard(); + ParseStoryboard(stream, storyboard); + return storyboard; + } + + protected abstract void ParseStoryboard(StreamReader stream, Storyboard storyboard); + } +} diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 61e9bbc11d..17215a935c 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -20,6 +20,15 @@ namespace osu.Game.Beatmaps.Formats private LegacySampleBank defaultSampleBank; private int defaultSampleVolume = 100; + public LegacyBeatmapDecoder() + { + } + + public LegacyBeatmapDecoder(string header) + { + BeatmapVersion = int.Parse(header.Substring(17)); + } + protected override void ProcessSection(Section section, string line) { switch (section) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 249b106962..b060de5120 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -9,34 +9,25 @@ using osu.Game.Storyboards; namespace osu.Game.Beatmaps.Formats { - public class LegacyDecoder : BeatmapDecoder + public abstract class LegacyDecoder : Decoder { public static void Register() { - AddDecoder(@"osu file format v14"); - AddDecoder(@"osu file format v13"); - AddDecoder(@"osu file format v12"); - AddDecoder(@"osu file format v11"); - AddDecoder(@"osu file format v10"); - AddDecoder(@"osu file format v9"); - AddDecoder(@"osu file format v8"); - AddDecoder(@"osu file format v7"); - AddDecoder(@"osu file format v6"); - AddDecoder(@"osu file format v5"); - AddDecoder(@"osu file format v4"); - AddDecoder(@"osu file format v3"); + AddDecoder(@"osu file format v14"); + AddDecoder(@"osu file format v13"); + AddDecoder(@"osu file format v12"); + AddDecoder(@"osu file format v11"); + AddDecoder(@"osu file format v10"); + AddDecoder(@"osu file format v9"); + AddDecoder(@"osu file format v8"); + AddDecoder(@"osu file format v7"); + AddDecoder(@"osu file format v6"); + AddDecoder(@"osu file format v5"); + AddDecoder(@"osu file format v4"); + AddDecoder(@"osu file format v3"); // TODO: differences between versions } - public LegacyDecoder() - { - } - - public LegacyDecoder(string header) - { - BeatmapVersion = int.Parse(header.Substring(17)); - } - protected Beatmap Beatmap; protected Storyboard Storyboard; @@ -105,10 +96,7 @@ namespace osu.Game.Beatmaps.Formats } } - protected virtual void ProcessSection(Section section, string line) - { - - } + protected abstract void ProcessSection(Section section, string line); /// /// Decodes any beatmap variables present in a line into their real values. diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 576eaead11..317f99b9d5 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -4,9 +4,6 @@ using System; using System.Globalization; using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using OpenTK; using OpenTK.Graphics; using osu.Framework.Graphics; diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs index d17f20ff2f..bca2cc02d0 100644 --- a/osu.Game/Tests/Visual/TestCasePlayer.cs +++ b/osu.Game/Tests/Visual/TestCasePlayer.cs @@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(test_beatmap_data))) using (var reader = new StreamReader(stream)) - beatmap = BeatmapDecoder.GetDecoder(reader).DecodeBeatmap(reader); + beatmap = Game.Beatmaps.Formats.Decoder.GetBeatmapDecoder(reader).DecodeBeatmap(reader); return beatmap; } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 4c33b2d266..1b1576b4c3 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -313,7 +313,7 @@ - + From c466296b14479ac0e8e6d290fd5db77a3a07ba86 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Fri, 1 Dec 2017 22:05:01 +0100 Subject: [PATCH 170/235] reverted split at Decoder, moved logic down I'm done experimenting, sorry - `Decoder` only returns a "Beatmap"`Decoder` now - "Storyboard"`Decoder` is retrieved from a "Beatmap"`Decoder` - moved parse methods down in the hierarchy where I forgot to do that - renamed `OsuLegacyDecoderTest` to `LegacyDecoderTest` --- ...acyDecoderTest.cs => LegacyDecoderTest.cs} | 2 +- .../Beatmaps/IO/OszArchiveReaderTest.cs | 2 +- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- osu.Game/Beatmaps/BeatmapManager.cs | 10 +-- osu.Game/Beatmaps/Formats/Decoder.cs | 64 +++----------- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 88 +++++++++++-------- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 64 +++++--------- .../Formats/LegacyStoryboardDecoder.cs | 24 +++-- osu.Game/Tests/Visual/TestCasePlayer.cs | 2 +- 9 files changed, 115 insertions(+), 143 deletions(-) rename osu.Game.Tests/Beatmaps/Formats/{OsuLegacyDecoderTest.cs => LegacyDecoderTest.cs} (97%) diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyDecoderTest.cs similarity index 97% rename from osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs rename to osu.Game.Tests/Beatmaps/Formats/LegacyDecoderTest.cs index 2ef1b796d1..4266afa526 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuLegacyDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyDecoderTest.cs @@ -14,7 +14,7 @@ using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Tests.Beatmaps.Formats { [TestFixture] - public class OsuLegacyDecoderTest + public class LegacyDecoderTest { [Test] public void TestDecodeMetadata() diff --git a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs index 22b1d16f7d..ffe735c89f 100644 --- a/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/OszArchiveReaderTest.cs @@ -50,7 +50,7 @@ namespace osu.Game.Tests.Beatmaps.IO BeatmapMetadata meta; using (var stream = new StreamReader(reader.GetStream("Soleily - Renatus (Deif) [Platter].osu"))) - meta = Decoder.GetBeatmapDecoder(stream).DecodeBeatmap(stream).Metadata; + meta = Decoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata; Assert.AreEqual(241526, meta.OnlineBeatmapSetID); Assert.AreEqual("Soleily", meta.Artist); diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 312a564f71..7d7adf5983 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -89,7 +89,7 @@ - + diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index e0d6ac214e..00182d5d85 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -495,7 +495,7 @@ namespace osu.Game.Beatmaps BeatmapMetadata metadata; using (var stream = new StreamReader(reader.GetStream(mapName))) - metadata = Decoder.GetBeatmapDecoder(stream).DecodeBeatmap(stream).Metadata; + metadata = Decoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata; // check if a set already exists with the same online id. beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.OnlineBeatmapSetID == metadata.OnlineBeatmapSetID) ?? new BeatmapSetInfo @@ -518,7 +518,7 @@ namespace osu.Game.Beatmaps raw.CopyTo(ms); ms.Position = 0; - var decoder = Decoder.GetBeatmapDecoder(sr); + var decoder = Decoder.GetDecoder(sr); Beatmap beatmap = decoder.DecodeBeatmap(sr); beatmap.BeatmapInfo.Path = name; @@ -571,7 +571,7 @@ namespace osu.Game.Beatmaps { using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) { - Decoder decoder = Decoder.GetBeatmapDecoder(stream); + Decoder decoder = Decoder.GetDecoder(stream); return decoder.DecodeBeatmap(stream); } } @@ -622,10 +622,10 @@ namespace osu.Game.Beatmaps { Decoder decoder; using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) - decoder = Decoder.GetStoryboardDecoder(stream); + decoder = Decoder.GetDecoder(stream); using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile)))) - return decoder.DecodeStoryboard(stream); + return decoder.GetStoryboardDecoder().DecodeStoryboard(stream); } catch { diff --git a/osu.Game/Beatmaps/Formats/Decoder.cs b/osu.Game/Beatmaps/Formats/Decoder.cs index 75e660bc8d..e157150651 100644 --- a/osu.Game/Beatmaps/Formats/Decoder.cs +++ b/osu.Game/Beatmaps/Formats/Decoder.cs @@ -10,8 +10,7 @@ namespace osu.Game.Beatmaps.Formats { public abstract class Decoder { - private static readonly Dictionary beatmapDecoders = new Dictionary(); - private static readonly Dictionary storyboardDecoders = new Dictionary(); + private static readonly Dictionary decoders = new Dictionary(); static Decoder() { @@ -19,32 +18,10 @@ namespace osu.Game.Beatmaps.Formats } /// - /// Retrieves a to parse s. + /// Retrieves a to parse a . /// - /// A stream pointing to the to retrieve the version from. - public static Decoder GetBeatmapDecoder(StreamReader stream) - { - string line = readFirstLine(stream); - - if (line == null || !beatmapDecoders.ContainsKey(line)) - throw new IOException(@"Unknown file format"); - return (Decoder)Activator.CreateInstance(beatmapDecoders[line], line); - } - - /// - /// Retrieves a to parse s. - /// - /// A stream pointing to the to retrieve the version from. - public static Decoder GetStoryboardDecoder(StreamReader stream) - { - string line = readFirstLine(stream); - - if (line == null || !storyboardDecoders.ContainsKey(line)) - throw new IOException(@"Unknown file format"); - return (Decoder)Activator.CreateInstance(storyboardDecoders[line], line); - } - - private static string readFirstLine(StreamReader stream) + /// A stream pointing to the . + public static Decoder GetDecoder(StreamReader stream) { if (stream == null) throw new ArgumentNullException(nameof(stream)); @@ -54,44 +31,27 @@ namespace osu.Game.Beatmaps.Formats { line = stream.ReadLine()?.Trim(); } while (line != null && line.Length == 0); - return line; + if (line == null || !decoders.ContainsKey(line)) + throw new IOException(@"Unknown file format"); + return (Decoder)Activator.CreateInstance(decoders[line], line); } /// /// Adds the to the list of and decoder. /// - /// Type to decode a with. - /// /// Type to decode a with. - /// A string representation of the version. - protected static void AddDecoder(string version) where A : Decoder where B : Decoder - { - beatmapDecoders[version] = typeof(A); - storyboardDecoders[version] = typeof(B); - } - - /// - /// Adds the to the list of decoder. - /// /// Type to decode a with. /// A string representation of the version. - protected static void AddBeatmapDecoder(string version) where T : Decoder + protected static void AddDecoder(string version) where T : Decoder { - beatmapDecoders[version] = typeof(T); + decoders[version] = typeof(T); } /// - /// Adds the to the list of decoder. + /// Retrieves a to parse a /// - /// Type to decode a with. - /// A string representation of the version. - protected static void AddStoryboardDecoder(string version) where T : Decoder - { - storyboardDecoders[version] = typeof(T); - } + public abstract Decoder GetStoryboardDecoder(); - public virtual Beatmap DecodeBeatmap(StreamReader stream) => ParseBeatmap(stream); - - protected virtual Beatmap ParseBeatmap(StreamReader stream) + public virtual Beatmap DecodeBeatmap(StreamReader stream) { var beatmap = new Beatmap { diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 17215a935c..29cf7dd913 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -2,18 +2,20 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using System.Collections.Generic; using System.Globalization; using System.IO; using OpenTK.Graphics; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Beatmaps.ControlPoints; +using System.Collections.Generic; namespace osu.Game.Beatmaps.Formats { public class LegacyBeatmapDecoder : LegacyDecoder { + private Beatmap beatmap; + private bool hasCustomColours; private ConvertHitObjectParser parser; @@ -29,6 +31,22 @@ namespace osu.Game.Beatmaps.Formats BeatmapVersion = int.Parse(header.Substring(17)); } + protected override void ParseBeatmap(StreamReader stream, Beatmap beatmap) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + if (beatmap == null) + throw new ArgumentNullException(nameof(beatmap)); + + this.beatmap = beatmap; + this.beatmap.BeatmapInfo.BeatmapVersion = BeatmapVersion; + + ParseContent(stream); + + foreach (var hitObject in this.beatmap.HitObjects) + hitObject.ApplyDefaults(this.beatmap.ControlPointInfo, this.beatmap.BeatmapInfo.BaseDifficulty); + } + protected override void ProcessSection(Section section, string line) { switch (section) @@ -67,20 +85,20 @@ namespace osu.Game.Beatmaps.Formats { var pair = splitKeyVal(line, ':'); - var metadata = Beatmap.BeatmapInfo.Metadata; + var metadata = beatmap.BeatmapInfo.Metadata; switch (pair.Key) { case @"AudioFilename": metadata.AudioFile = pair.Value; break; case @"AudioLeadIn": - Beatmap.BeatmapInfo.AudioLeadIn = int.Parse(pair.Value); + beatmap.BeatmapInfo.AudioLeadIn = int.Parse(pair.Value); break; case @"PreviewTime": metadata.PreviewTime = int.Parse(pair.Value); break; case @"Countdown": - Beatmap.BeatmapInfo.Countdown = int.Parse(pair.Value) == 1; + beatmap.BeatmapInfo.Countdown = int.Parse(pair.Value) == 1; break; case @"SampleSet": defaultSampleBank = (LegacySampleBank)Enum.Parse(typeof(LegacySampleBank), pair.Value); @@ -89,12 +107,12 @@ namespace osu.Game.Beatmaps.Formats defaultSampleVolume = int.Parse(pair.Value); break; case @"StackLeniency": - Beatmap.BeatmapInfo.StackLeniency = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + beatmap.BeatmapInfo.StackLeniency = float.Parse(pair.Value, NumberFormatInfo.InvariantInfo); break; case @"Mode": - Beatmap.BeatmapInfo.RulesetID = int.Parse(pair.Value); + beatmap.BeatmapInfo.RulesetID = int.Parse(pair.Value); - switch (Beatmap.BeatmapInfo.RulesetID) + switch (beatmap.BeatmapInfo.RulesetID) { case 0: parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(); @@ -111,13 +129,13 @@ namespace osu.Game.Beatmaps.Formats } break; case @"LetterboxInBreaks": - Beatmap.BeatmapInfo.LetterboxInBreaks = int.Parse(pair.Value) == 1; + beatmap.BeatmapInfo.LetterboxInBreaks = int.Parse(pair.Value) == 1; break; case @"SpecialStyle": - Beatmap.BeatmapInfo.SpecialStyle = int.Parse(pair.Value) == 1; + beatmap.BeatmapInfo.SpecialStyle = int.Parse(pair.Value) == 1; break; case @"WidescreenStoryboard": - Beatmap.BeatmapInfo.WidescreenStoryboard = int.Parse(pair.Value) == 1; + beatmap.BeatmapInfo.WidescreenStoryboard = int.Parse(pair.Value) == 1; break; } } @@ -129,19 +147,19 @@ namespace osu.Game.Beatmaps.Formats switch (pair.Key) { case @"Bookmarks": - Beatmap.BeatmapInfo.StoredBookmarks = pair.Value; + beatmap.BeatmapInfo.StoredBookmarks = pair.Value; break; case @"DistanceSpacing": - Beatmap.BeatmapInfo.DistanceSpacing = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + beatmap.BeatmapInfo.DistanceSpacing = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); break; case @"BeatDivisor": - Beatmap.BeatmapInfo.BeatDivisor = int.Parse(pair.Value); + beatmap.BeatmapInfo.BeatDivisor = int.Parse(pair.Value); break; case @"GridSize": - Beatmap.BeatmapInfo.GridSize = int.Parse(pair.Value); + beatmap.BeatmapInfo.GridSize = int.Parse(pair.Value); break; case @"TimelineZoom": - Beatmap.BeatmapInfo.TimelineZoom = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); + beatmap.BeatmapInfo.TimelineZoom = double.Parse(pair.Value, NumberFormatInfo.InvariantInfo); break; } } @@ -150,7 +168,7 @@ namespace osu.Game.Beatmaps.Formats { var pair = splitKeyVal(line, ':'); - var metadata = Beatmap.BeatmapInfo.Metadata; + var metadata = beatmap.BeatmapInfo.Metadata; switch (pair.Key) { case @"Title": @@ -169,19 +187,19 @@ namespace osu.Game.Beatmaps.Formats metadata.AuthorString = pair.Value; break; case @"Version": - Beatmap.BeatmapInfo.Version = pair.Value; + beatmap.BeatmapInfo.Version = pair.Value; break; case @"Source": - Beatmap.BeatmapInfo.Metadata.Source = pair.Value; + beatmap.BeatmapInfo.Metadata.Source = pair.Value; break; case @"Tags": - Beatmap.BeatmapInfo.Metadata.Tags = pair.Value; + beatmap.BeatmapInfo.Metadata.Tags = pair.Value; break; case @"BeatmapID": - Beatmap.BeatmapInfo.OnlineBeatmapID = int.Parse(pair.Value); + beatmap.BeatmapInfo.OnlineBeatmapID = int.Parse(pair.Value); break; case @"BeatmapSetID": - Beatmap.BeatmapInfo.OnlineBeatmapSetID = int.Parse(pair.Value); + beatmap.BeatmapInfo.OnlineBeatmapSetID = int.Parse(pair.Value); metadata.OnlineBeatmapSetID = int.Parse(pair.Value); break; } @@ -191,7 +209,7 @@ namespace osu.Game.Beatmaps.Formats { var pair = splitKeyVal(line, ':'); - var difficulty = Beatmap.BeatmapInfo.BaseDifficulty; + var difficulty = beatmap.BeatmapInfo.BaseDifficulty; switch (pair.Key) { case @"HPDrainRate": @@ -229,7 +247,7 @@ namespace osu.Game.Beatmaps.Formats { case EventType.Background: string filename = split[2].Trim('"'); - Beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; + beatmap.BeatmapInfo.Metadata.BackgroundFile = filename; break; case EventType.Break: var breakEvent = new BreakPeriod @@ -241,7 +259,7 @@ namespace osu.Game.Beatmaps.Formats if (!breakEvent.HasEffect) return; - Beatmap.Breaks.Add(breakEvent); + beatmap.Breaks.Add(breakEvent); break; } } @@ -287,13 +305,13 @@ namespace osu.Game.Beatmaps.Formats if (stringSampleSet == @"none") stringSampleSet = @"normal"; - DifficultyControlPoint difficultyPoint = Beatmap.ControlPointInfo.DifficultyPointAt(time); - SoundControlPoint soundPoint = Beatmap.ControlPointInfo.SoundPointAt(time); - EffectControlPoint effectPoint = Beatmap.ControlPointInfo.EffectPointAt(time); + DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(time); + SoundControlPoint soundPoint = beatmap.ControlPointInfo.SoundPointAt(time); + EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(time); if (timingChange) { - Beatmap.ControlPointInfo.TimingPoints.Add(new TimingControlPoint + beatmap.ControlPointInfo.TimingPoints.Add(new TimingControlPoint { Time = time, BeatLength = beatLength, @@ -303,8 +321,8 @@ namespace osu.Game.Beatmaps.Formats if (speedMultiplier != difficultyPoint.SpeedMultiplier) { - Beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == time); - Beatmap.ControlPointInfo.DifficultyPoints.Add(new DifficultyControlPoint + beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == time); + beatmap.ControlPointInfo.DifficultyPoints.Add(new DifficultyControlPoint { Time = time, SpeedMultiplier = speedMultiplier @@ -313,7 +331,7 @@ namespace osu.Game.Beatmaps.Formats if (stringSampleSet != soundPoint.SampleBank || sampleVolume != soundPoint.SampleVolume) { - Beatmap.ControlPointInfo.SoundPoints.Add(new SoundControlPoint + beatmap.ControlPointInfo.SoundPoints.Add(new SoundControlPoint { Time = time, SampleBank = stringSampleSet, @@ -323,7 +341,7 @@ namespace osu.Game.Beatmaps.Formats if (kiaiMode != effectPoint.KiaiMode || omitFirstBarSignature != effectPoint.OmitFirstBarLine) { - Beatmap.ControlPointInfo.EffectPoints.Add(new EffectControlPoint + beatmap.ControlPointInfo.EffectPoints.Add(new EffectControlPoint { Time = time, KiaiMode = kiaiMode, @@ -347,14 +365,14 @@ namespace osu.Game.Beatmaps.Formats if (!hasCustomColours) { - Beatmap.ComboColors.Clear(); + beatmap.ComboColors.Clear(); hasCustomColours = true; } // Note: the combo index specified in the beatmap is discarded if (pair.Key.StartsWith(@"Combo")) { - Beatmap.ComboColors.Add(new Color4 + beatmap.ComboColors.Add(new Color4 { R = r / 255f, G = g / 255f, @@ -373,7 +391,7 @@ namespace osu.Game.Beatmaps.Formats var obj = parser.Parse(line); if (obj != null) - Beatmap.HitObjects.Add(obj); + beatmap.HitObjects.Add(obj); } private void handleVariables(string line) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index b060de5120..85d77d93bc 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -13,57 +13,36 @@ namespace osu.Game.Beatmaps.Formats { public static void Register() { - AddDecoder(@"osu file format v14"); - AddDecoder(@"osu file format v13"); - AddDecoder(@"osu file format v12"); - AddDecoder(@"osu file format v11"); - AddDecoder(@"osu file format v10"); - AddDecoder(@"osu file format v9"); - AddDecoder(@"osu file format v8"); - AddDecoder(@"osu file format v7"); - AddDecoder(@"osu file format v6"); - AddDecoder(@"osu file format v5"); - AddDecoder(@"osu file format v4"); - AddDecoder(@"osu file format v3"); + AddDecoder(@"osu file format v14"); + AddDecoder(@"osu file format v13"); + AddDecoder(@"osu file format v12"); + AddDecoder(@"osu file format v11"); + AddDecoder(@"osu file format v10"); + AddDecoder(@"osu file format v9"); + AddDecoder(@"osu file format v8"); + AddDecoder(@"osu file format v7"); + AddDecoder(@"osu file format v6"); + AddDecoder(@"osu file format v5"); + AddDecoder(@"osu file format v4"); + AddDecoder(@"osu file format v3"); // TODO: differences between versions } - protected Beatmap Beatmap; - protected Storyboard Storyboard; - protected int BeatmapVersion; protected readonly Dictionary Variables = new Dictionary(); - public override Beatmap DecodeBeatmap(StreamReader stream) => new LegacyBeatmap(base.DecodeBeatmap(stream)); + public override Decoder GetStoryboardDecoder() => new LegacyStoryboardDecoder(BeatmapVersion); - protected override Beatmap ParseBeatmap(StreamReader stream) => new LegacyBeatmap(base.ParseBeatmap(stream)); + public override Beatmap DecodeBeatmap(StreamReader stream) => new LegacyBeatmap(base.DecodeBeatmap(stream)); protected override void ParseBeatmap(StreamReader stream, Beatmap beatmap) { - if (stream == null) - throw new ArgumentNullException(nameof(stream)); - if (beatmap == null) - throw new ArgumentNullException(nameof(beatmap)); - - Beatmap = beatmap; - Beatmap.BeatmapInfo.BeatmapVersion = BeatmapVersion; - - ParseContent(stream); - - foreach (var hitObject in Beatmap.HitObjects) - hitObject.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty); + throw new NotImplementedException(); } protected override void ParseStoryboard(StreamReader stream, Storyboard storyboard) { - if (stream == null) - throw new ArgumentNullException(nameof(stream)); - if (storyboard == null) - throw new ArgumentNullException(nameof(storyboard)); - - Storyboard = storyboard; - - ParseContent(stream); + throw new NotImplementedException(); } protected void ParseContent(StreamReader stream) @@ -79,11 +58,12 @@ namespace osu.Game.Beatmaps.Formats if (line.StartsWith("//")) continue; - if (line.StartsWith(@"osu file format v")) - { - Beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17)); - continue; - } + // It's already set in ParseBeatmap... why do it again? + //if (line.StartsWith(@"osu file format v")) + //{ + // Beatmap.BeatmapInfo.BeatmapVersion = int.Parse(line.Substring(17)); + // continue; + //} if (line.StartsWith(@"[") && line.EndsWith(@"]")) { diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 317f99b9d5..aca92f3969 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -14,6 +14,8 @@ namespace osu.Game.Beatmaps.Formats { public class LegacyStoryboardDecoder : LegacyDecoder { + private Storyboard storyboard; + private StoryboardSprite storyboardSprite; private CommandTimelineGroup timelineGroup; @@ -21,9 +23,21 @@ namespace osu.Game.Beatmaps.Formats { } - public LegacyStoryboardDecoder(string header) + public LegacyStoryboardDecoder(int beatmapVersion) { - BeatmapVersion = int.Parse(header.Substring(17)); + BeatmapVersion = beatmapVersion; + } + + protected override void ParseStoryboard(StreamReader stream, Storyboard storyboard) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + if (storyboard == null) + throw new ArgumentNullException(nameof(storyboard)); + + this.storyboard = storyboard; + + ParseContent(stream); } protected override void ProcessSection(Section section, string line) @@ -67,7 +81,7 @@ namespace osu.Game.Beatmaps.Formats var x = float.Parse(split[4], NumberFormatInfo.InvariantInfo); var y = float.Parse(split[5], NumberFormatInfo.InvariantInfo); storyboardSprite = new StoryboardSprite(path, origin, new Vector2(x, y)); - Storyboard.GetLayer(layer).Add(storyboardSprite); + storyboard.GetLayer(layer).Add(storyboardSprite); } break; case EventType.Animation: @@ -81,7 +95,7 @@ namespace osu.Game.Beatmaps.Formats var frameDelay = double.Parse(split[7], NumberFormatInfo.InvariantInfo); var loopType = split.Length > 8 ? (AnimationLoopType)Enum.Parse(typeof(AnimationLoopType), split[8]) : AnimationLoopType.LoopForever; storyboardSprite = new StoryboardAnimation(path, origin, new Vector2(x, y), frameCount, frameDelay, loopType); - Storyboard.GetLayer(layer).Add(storyboardSprite); + storyboard.GetLayer(layer).Add(storyboardSprite); } break; case EventType.Sample: @@ -90,7 +104,7 @@ namespace osu.Game.Beatmaps.Formats var layer = parseLayer(split[2]); var path = cleanFilename(split[3]); var volume = split.Length > 4 ? float.Parse(split[4], CultureInfo.InvariantCulture) : 100; - Storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume)); + storyboard.GetLayer(layer).Add(new StoryboardSample(path, time, volume)); } break; } diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs index bca2cc02d0..35d16d14a5 100644 --- a/osu.Game/Tests/Visual/TestCasePlayer.cs +++ b/osu.Game/Tests/Visual/TestCasePlayer.cs @@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual using (var stream = new MemoryStream(Encoding.UTF8.GetBytes(test_beatmap_data))) using (var reader = new StreamReader(stream)) - beatmap = Game.Beatmaps.Formats.Decoder.GetBeatmapDecoder(reader).DecodeBeatmap(reader); + beatmap = Game.Beatmaps.Formats.Decoder.GetDecoder(reader).DecodeBeatmap(reader); return beatmap; } From e07b85311b29cff6c5b057cb2eb574be5948cb36 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Fri, 1 Dec 2017 22:15:10 +0100 Subject: [PATCH 171/235] removed unnecessary using --- osu.Game/Tests/Visual/TestCasePlayer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Tests/Visual/TestCasePlayer.cs b/osu.Game/Tests/Visual/TestCasePlayer.cs index 35d16d14a5..106f0fa8f3 100644 --- a/osu.Game/Tests/Visual/TestCasePlayer.cs +++ b/osu.Game/Tests/Visual/TestCasePlayer.cs @@ -8,7 +8,6 @@ using System.Text; using osu.Framework.Allocation; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Formats; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens.Play; From 5a7c4772bd57453466f9cc2f3599f54b7882ce22 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 2 Dec 2017 19:53:11 +0900 Subject: [PATCH 172/235] Remove PlayfieldUnderlay for now --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 3 --- osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs | 16 ---------------- osu.Game/osu.Game.csproj | 1 - 3 files changed, 20 deletions(-) delete mode 100644 osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index f55b7b0531..767e8ade46 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -77,7 +77,6 @@ namespace osu.Game.Rulesets.Edit Alpha = 0, AlwaysPresent = true, }, - CreateUnderlay(rulesetContainer.Playfield), rulesetContainer, CreateOverlay(rulesetContainer.Playfield) } @@ -106,8 +105,6 @@ namespace osu.Game.Rulesets.Edit protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap, true); - protected virtual PlayfieldUnderlay CreateUnderlay(Playfield playfield) => new PlayfieldUnderlay(); - protected virtual PlayfieldOverlay CreateOverlay(Playfield playfield) => new PlayfieldOverlay(); protected abstract IReadOnlyList CompositionTools { get; } diff --git a/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs b/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs deleted file mode 100644 index bace5258f8..0000000000 --- a/osu.Game/Rulesets/Edit/PlayfieldUnderlay.cs +++ /dev/null @@ -1,16 +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.Graphics.Containers; - -namespace osu.Game.Rulesets.Edit -{ - public class PlayfieldUnderlay : CompositeDrawable - { - public PlayfieldUnderlay() - { - RelativeSizeAxes = Axes.Both; - } - } -} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ad1370890f..ee5834890f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -303,7 +303,6 @@ - From a80059032cdc94a37a83b78fd86b4466be095c61 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 2 Dec 2017 20:24:10 +0900 Subject: [PATCH 173/235] Remove PlayfieldOverlay --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 5 +---- osu.Game/Rulesets/Edit/PlayfieldOverlay.cs | 19 ------------------- osu.Game/osu.Game.csproj | 1 - 3 files changed, 1 insertion(+), 24 deletions(-) delete mode 100644 osu.Game/Rulesets/Edit/PlayfieldOverlay.cs diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 767e8ade46..3184b84e98 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -77,8 +77,7 @@ namespace osu.Game.Rulesets.Edit Alpha = 0, AlwaysPresent = true, }, - rulesetContainer, - CreateOverlay(rulesetContainer.Playfield) + rulesetContainer } } }, @@ -105,8 +104,6 @@ namespace osu.Game.Rulesets.Edit protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap, true); - protected virtual PlayfieldOverlay CreateOverlay(Playfield playfield) => new PlayfieldOverlay(); - protected abstract IReadOnlyList CompositionTools { get; } } } diff --git a/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs b/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs deleted file mode 100644 index 58b15e3de2..0000000000 --- a/osu.Game/Rulesets/Edit/PlayfieldOverlay.cs +++ /dev/null @@ -1,19 +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.Graphics.Containers; - -namespace osu.Game.Rulesets.Edit -{ - public class PlayfieldOverlay : CompositeDrawable - { - public PlayfieldOverlay() - { - RelativeSizeAxes = Axes.Both; - - } - - - } -} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index ee5834890f..e1425ff8b8 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -302,7 +302,6 @@ - From c14d68e685143e00f8d37a2c1bc3d4f9d883378e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 2 Dec 2017 20:30:18 +0900 Subject: [PATCH 174/235] Use OsuColour for radio buttons --- .../RadioButtons/DrawableRadioButton.cs | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs index 48cc7f3379..10b6c07f3d 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/DrawableRadioButton.cs @@ -19,16 +19,16 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons { public class DrawableRadioButton : TriangleButton { - private static readonly Color4 default_background_colour = OsuColour.FromHex("333"); - private static readonly Color4 default_bubble_colour = default_background_colour.Darken(0.5f); - private static readonly Color4 selected_background_colour = OsuColour.FromHex("1188aa"); - private static readonly Color4 selected_bubble_colour = selected_background_colour.Lighten(0.5f); - /// /// Invoked when this has been selected. /// public Action Selected; + private Color4 defaultBackgroundColour; + private Color4 defaultBubbleColour; + private Color4 selectedBackgroundColour; + private Color4 selectedBubbleColour; + private readonly Drawable bubble; private readonly RadioButton button; @@ -50,17 +50,20 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons Scale = new Vector2(0.5f), X = 10, Masking = true, - Colour = default_bubble_colour, Blending = BlendingMode.Additive, Child = new Box { RelativeSizeAxes = Axes.Both } }; } [BackgroundDependencyLoader] - private void load() + private void load(OsuColour colours) { + defaultBackgroundColour = colours.Gray3; + defaultBubbleColour = defaultBackgroundColour.Darken(0.5f); + selectedBackgroundColour = colours.BlueDark; + selectedBubbleColour = selectedBackgroundColour.Lighten(0.5f); + Triangles.Alpha = 0; - BackgroundColour = default_background_colour; Content.EdgeEffect = new EdgeEffectParameters { @@ -92,8 +95,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons if (!IsLoaded) return; - BackgroundColour = button.Selected ? selected_background_colour : default_background_colour; - bubble.Colour = button.Selected ? selected_bubble_colour : default_bubble_colour; + BackgroundColour = button.Selected ? selectedBackgroundColour : defaultBackgroundColour; + bubble.Colour = button.Selected ? selectedBubbleColour : defaultBubbleColour; } protected override bool OnClick(InputState state) From cd20d6df160b1b4617b4f8035dc31ee52063588f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 2 Dec 2017 20:31:10 +0900 Subject: [PATCH 175/235] Apply suggested changes --- osu.Game/Screens/Edit/Editor.cs | 1 - .../Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs | 2 +- osu.Game/Screens/Edit/Screens/EditorScreen.cs | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index bc86c683c7..607ff792d8 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -154,7 +154,6 @@ namespace osu.Game.Screens.Edit } currentScreen.Beatmap.BindTo(Beatmap); - currentScreen.ExitRequested = Exit; screenContainer.Add(currentScreen); } diff --git a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs index 08473eaeba..5f1def4a2e 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/RadioButtons/RadioButtonCollection.cs @@ -22,7 +22,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.RadioButtons items = value; buttonContainer.Clear(); - value.ForEach(addButton); + items.ForEach(addButton); } } diff --git a/osu.Game/Screens/Edit/Screens/EditorScreen.cs b/osu.Game/Screens/Edit/Screens/EditorScreen.cs index 9a158d20f1..773975d493 100644 --- a/osu.Game/Screens/Edit/Screens/EditorScreen.cs +++ b/osu.Game/Screens/Edit/Screens/EditorScreen.cs @@ -11,8 +11,6 @@ namespace osu.Game.Screens.Edit.Screens { public class EditorScreen : Container { - public Action ExitRequested; - public readonly Bindable Beatmap = new Bindable(); protected override Container Content => content; From 2852337b04c0160a6b52e5376acdf73e09af4fd8 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Sat, 2 Dec 2017 20:35:47 +0900 Subject: [PATCH 176/235] Remove unused using --- osu.Game/Screens/Edit/Screens/EditorScreen.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Screens/EditorScreen.cs b/osu.Game/Screens/Edit/Screens/EditorScreen.cs index 773975d493..ac248930d8 100644 --- a/osu.Game/Screens/Edit/Screens/EditorScreen.cs +++ b/osu.Game/Screens/Edit/Screens/EditorScreen.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; From 2bbfe0dda1bd886791dc628594822060820ec67b Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Sat, 2 Dec 2017 15:28:00 +0100 Subject: [PATCH 177/235] expanded BeatmapDecoder tests, added StoryboardDecoder tests --- .../Formats/LegacyBeatmapDecoderTest.cs | 214 ++ .../Beatmaps/Formats/LegacyDecoderTest.cs | 146 -- .../Formats/LegacyStoryboardDecoderTest.cs | 89 + ...ni Yoroshiku (RLC) [Winber1's Extreme].osu | 1997 +++++++++++++++++ osu.Game.Tests/osu.Game.Tests.csproj | 4 +- 5 files changed, 2303 insertions(+), 147 deletions(-) create mode 100644 osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs delete mode 100644 osu.Game.Tests/Beatmaps/Formats/LegacyDecoderTest.cs create mode 100644 osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs create mode 100644 osu.Game.Tests/Resources/Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs new file mode 100644 index 0000000000..86413af4b6 --- /dev/null +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -0,0 +1,214 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.IO; +using NUnit.Framework; +using OpenTK; +using OpenTK.Graphics; +using osu.Game.Tests.Resources; +using System.Linq; +using osu.Game.Audio; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Beatmaps.Formats; +using osu.Game.Beatmaps.Timing; + +namespace osu.Game.Tests.Beatmaps.Formats +{ + [TestFixture] + public class LegacyBeatmapDecoderTest + { + [Test] + public void TestDecodeBeatmapGeneral() + { + var decoder = new LegacyBeatmapDecoder(); + using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var stream = new StreamReader(resStream)) + { + var beatmap = decoder.DecodeBeatmap(stream); + var beatmapInfo = beatmap.BeatmapInfo; + var metadata = beatmap.Metadata; + + Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", metadata.AudioFile); + Assert.AreEqual(0, beatmapInfo.AudioLeadIn); + Assert.AreEqual(164471, metadata.PreviewTime); + Assert.IsFalse(beatmapInfo.Countdown); + Assert.AreEqual(0.7f, beatmapInfo.StackLeniency); + Assert.IsTrue(beatmapInfo.RulesetID == 0); + Assert.IsFalse(beatmapInfo.LetterboxInBreaks); + Assert.IsFalse(beatmapInfo.SpecialStyle); + Assert.IsFalse(beatmapInfo.WidescreenStoryboard); + } + } + + [Test] + public void TestDecodeBeatmapEditor() + { + var decoder = new LegacyBeatmapDecoder(); + using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var stream = new StreamReader(resStream)) + { + var beatmapInfo = decoder.DecodeBeatmap(stream).BeatmapInfo; + + int[] expectedBookmarks = + { + 11505, 22054, 32604, 43153, 53703, 64252, 74802, 85351, + 95901, 106450, 116999, 119637, 130186, 140735, 151285, + 161834, 164471, 175020, 185570, 196119, 206669, 209306 + }; + Assert.AreEqual(expectedBookmarks.Length, beatmapInfo.Bookmarks.Length); + for (int i = 0; i < expectedBookmarks.Length; i++) + Assert.AreEqual(expectedBookmarks[i], beatmapInfo.Bookmarks[i]); + Assert.AreEqual(1.8, beatmapInfo.DistanceSpacing); + Assert.AreEqual(4, beatmapInfo.BeatDivisor); + Assert.AreEqual(4, beatmapInfo.GridSize); + Assert.AreEqual(2, beatmapInfo.TimelineZoom); + } + } + + [Test] + public void TestDecodeBeatmapMetadata() + { + var decoder = new LegacyBeatmapDecoder(); + using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var stream = new StreamReader(resStream)) + { + var beatmap = decoder.DecodeBeatmap(stream); + var beatmapInfo = beatmap.BeatmapInfo; + var metadata = beatmap.Metadata; + + Assert.AreEqual("Renatus", metadata.Title); + Assert.AreEqual("Renatus", metadata.TitleUnicode); + Assert.AreEqual("Soleily", metadata.Artist); + Assert.AreEqual("Soleily", metadata.ArtistUnicode); + Assert.AreEqual("Gamu", metadata.AuthorString); + Assert.AreEqual("Insane", beatmapInfo.Version); + Assert.AreEqual(string.Empty, metadata.Source); + Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", metadata.Tags); + Assert.AreEqual(557821, beatmapInfo.OnlineBeatmapID); + Assert.AreEqual(241526, metadata.OnlineBeatmapSetID); + } + } + + [Test] + public void TestDecodeBeatmapDifficulty() + { + var decoder = new LegacyBeatmapDecoder(); + using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var stream = new StreamReader(resStream)) + { + var difficulty = decoder.DecodeBeatmap(stream).BeatmapInfo.BaseDifficulty; + + Assert.AreEqual(6.5f, difficulty.DrainRate); + Assert.AreEqual(4, difficulty.CircleSize); + Assert.AreEqual(8, difficulty.OverallDifficulty); + Assert.AreEqual(9, difficulty.ApproachRate); + Assert.AreEqual(1.8f, difficulty.SliderMultiplier); + Assert.AreEqual(2, difficulty.SliderTickRate); + } + } + + [Test] + public void TestDecodeBeatmapEvents() + { + var decoder = new LegacyBeatmapDecoder(); + using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var stream = new StreamReader(resStream)) + { + var beatmap = decoder.DecodeBeatmap(stream); + var metadata = beatmap.Metadata; + var breakPoint = beatmap.Breaks[0]; + + Assert.AreEqual("machinetop_background.jpg", metadata.BackgroundFile); + Assert.AreEqual(122474, breakPoint.StartTime); + Assert.AreEqual(140135, breakPoint.EndTime); + Assert.IsTrue(breakPoint.HasEffect); + } + } + + [Test] + public void TestDecodeBeatmapTimingPoints() + { + var decoder = new LegacyBeatmapDecoder(); + using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var stream = new StreamReader(resStream)) + { + var beatmap = decoder.DecodeBeatmap(stream); + var controlPoints = beatmap.ControlPointInfo; + + Assert.AreEqual(4, controlPoints.TimingPoints.Count); + var timingPoint = controlPoints.TimingPoints[0]; + Assert.AreEqual(956, timingPoint.Time); + Assert.AreEqual(329.67032967033d, timingPoint.BeatLength); + Assert.AreEqual(TimeSignatures.SimpleQuadruple, timingPoint.TimeSignature); + + Assert.AreEqual(5, controlPoints.DifficultyPoints.Count); + var difficultyPoint = controlPoints.DifficultyPoints[0]; + Assert.AreEqual(116999, difficultyPoint.Time); + Assert.AreEqual(0.75000000000000189d, difficultyPoint.SpeedMultiplier); + + Assert.AreEqual(34, controlPoints.SoundPoints.Count); + var soundPoint = controlPoints.SoundPoints[0]; + Assert.AreEqual(956, soundPoint.Time); + Assert.AreEqual("soft", soundPoint.SampleBank); + Assert.AreEqual(60, soundPoint.SampleVolume); + + Assert.AreEqual(8, controlPoints.EffectPoints.Count); + var effectPoint = controlPoints.EffectPoints[0]; + Assert.AreEqual(53703, effectPoint.Time); + Assert.IsTrue(effectPoint.KiaiMode); + Assert.IsFalse(effectPoint.OmitFirstBarLine); + } + } + + [Test] + public void TestDecodeBeatmapColors() + { + var decoder = new LegacyBeatmapDecoder(); + using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var stream = new StreamReader(resStream)) + { + var comboColors = decoder.DecodeBeatmap(stream).ComboColors; + + Color4[] expectedColors = + { + new Color4(142, 199, 255, 255), + new Color4(255, 128, 128, 255), + new Color4(128, 255, 255, 255), + new Color4(128, 255, 128, 255), + new Color4(255, 187, 255, 255), + new Color4(255, 177, 140, 255), + }; + Assert.AreEqual(expectedColors.Length, comboColors.Count); + for (int i = 0; i < expectedColors.Length; i++) + Assert.AreEqual(expectedColors[i], comboColors[i]); + } + } + + [Test] + public void TestDecodeBeatmapHitObjects() + { + var decoder = new LegacyBeatmapDecoder(); + using (var resStream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) + using (var stream = new StreamReader(resStream)) + { + var hitObjects = decoder.DecodeBeatmap(stream).HitObjects; + + var curveData = hitObjects[0] as IHasCurve; + var positionData = hitObjects[0] as IHasPosition; + + Assert.IsNotNull(positionData); + Assert.IsNotNull(curveData); + Assert.AreEqual(new Vector2(192, 168), positionData.Position); + Assert.AreEqual(956, hitObjects[0].StartTime); + Assert.IsTrue(hitObjects[0].Samples.Any(s => s.Name == SampleInfo.HIT_NORMAL)); + + positionData = hitObjects[1] as IHasPosition; + + Assert.IsNotNull(positionData); + Assert.AreEqual(new Vector2(304, 56), positionData.Position); + Assert.AreEqual(1285, hitObjects[1].StartTime); + Assert.IsTrue(hitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP)); + } + } + } +} diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyDecoderTest.cs deleted file mode 100644 index 4266afa526..0000000000 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyDecoderTest.cs +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -using System.IO; -using NUnit.Framework; -using OpenTK; -using OpenTK.Graphics; -using osu.Game.Beatmaps.Formats; -using osu.Game.Tests.Resources; -using System.Linq; -using osu.Game.Audio; -using osu.Game.Rulesets.Objects.Types; - -namespace osu.Game.Tests.Beatmaps.Formats -{ - [TestFixture] - public class LegacyDecoderTest - { - [Test] - public void TestDecodeMetadata() - { - var decoder = new LegacyBeatmapDecoder(); - using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - { - var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)); - var meta = beatmap.BeatmapInfo.Metadata; - Assert.AreEqual(241526, meta.OnlineBeatmapSetID); - Assert.AreEqual("Soleily", meta.Artist); - Assert.AreEqual("Soleily", meta.ArtistUnicode); - Assert.AreEqual("03. Renatus - Soleily 192kbps.mp3", meta.AudioFile); - Assert.AreEqual("Gamu", meta.AuthorString); - Assert.AreEqual("machinetop_background.jpg", meta.BackgroundFile); - Assert.AreEqual(164471, meta.PreviewTime); - Assert.AreEqual(string.Empty, meta.Source); - Assert.AreEqual("MBC7 Unisphere 地球ヤバイEP Chikyu Yabai", meta.Tags); - Assert.AreEqual("Renatus", meta.Title); - Assert.AreEqual("Renatus", meta.TitleUnicode); - } - } - - [Test] - public void TestDecodeGeneral() - { - var decoder = new LegacyBeatmapDecoder(); - using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - { - var beatmapInfo = decoder.DecodeBeatmap(new StreamReader(stream)).BeatmapInfo; - Assert.AreEqual(0, beatmapInfo.AudioLeadIn); - Assert.AreEqual(false, beatmapInfo.Countdown); - Assert.AreEqual(0.7f, beatmapInfo.StackLeniency); - Assert.AreEqual(false, beatmapInfo.SpecialStyle); - Assert.IsTrue(beatmapInfo.RulesetID == 0); - Assert.AreEqual(false, beatmapInfo.LetterboxInBreaks); - Assert.AreEqual(false, beatmapInfo.WidescreenStoryboard); - } - } - - [Test] - public void TestDecodeEditor() - { - var decoder = new LegacyBeatmapDecoder(); - using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - { - var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)).BeatmapInfo; - int[] expectedBookmarks = - { - 11505, 22054, 32604, 43153, 53703, 64252, 74802, 85351, - 95901, 106450, 116999, 119637, 130186, 140735, 151285, - 161834, 164471, 175020, 185570, 196119, 206669, 209306 - }; - Assert.AreEqual(expectedBookmarks.Length, beatmap.Bookmarks.Length); - for (int i = 0; i < expectedBookmarks.Length; i++) - Assert.AreEqual(expectedBookmarks[i], beatmap.Bookmarks[i]); - Assert.AreEqual(1.8, beatmap.DistanceSpacing); - Assert.AreEqual(4, beatmap.BeatDivisor); - Assert.AreEqual(4, beatmap.GridSize); - Assert.AreEqual(2, beatmap.TimelineZoom); - } - } - - [Test] - public void TestDecodeDifficulty() - { - var decoder = new LegacyBeatmapDecoder(); - using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - { - var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)); - var difficulty = beatmap.BeatmapInfo.BaseDifficulty; - Assert.AreEqual(6.5f, difficulty.DrainRate); - Assert.AreEqual(4, difficulty.CircleSize); - Assert.AreEqual(8, difficulty.OverallDifficulty); - Assert.AreEqual(9, difficulty.ApproachRate); - Assert.AreEqual(1.8f, difficulty.SliderMultiplier); - Assert.AreEqual(2, difficulty.SliderTickRate); - } - } - - [Test] - public void TestDecodeColors() - { - var decoder = new LegacyBeatmapDecoder(); - using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - { - var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)); - Color4[] expected = - { - new Color4(142, 199, 255, 255), - new Color4(255, 128, 128, 255), - new Color4(128, 255, 255, 255), - new Color4(128, 255, 128, 255), - new Color4(255, 187, 255, 255), - new Color4(255, 177, 140, 255), - }; - Assert.AreEqual(expected.Length, beatmap.ComboColors.Count); - for (int i = 0; i < expected.Length; i++) - Assert.AreEqual(expected[i], beatmap.ComboColors[i]); - } - } - - [Test] - public void TestDecodeHitObjects() - { - var decoder = new LegacyBeatmapDecoder(); - using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) - { - var beatmap = decoder.DecodeBeatmap(new StreamReader(stream)); - - var curveData = beatmap.HitObjects[0] as IHasCurve; - var positionData = beatmap.HitObjects[0] as IHasPosition; - - Assert.IsNotNull(positionData); - Assert.IsNotNull(curveData); - Assert.AreEqual(new Vector2(192, 168), positionData.Position); - Assert.AreEqual(956, beatmap.HitObjects[0].StartTime); - Assert.IsTrue(beatmap.HitObjects[0].Samples.Any(s => s.Name == SampleInfo.HIT_NORMAL)); - - positionData = beatmap.HitObjects[1] as IHasPosition; - - Assert.IsNotNull(positionData); - Assert.AreEqual(new Vector2(304, 56), positionData.Position); - Assert.AreEqual(1285, beatmap.HitObjects[1].StartTime); - Assert.IsTrue(beatmap.HitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP)); - } - } - } -} diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs new file mode 100644 index 0000000000..a42318883c --- /dev/null +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs @@ -0,0 +1,89 @@ +// 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 System.IO; +using System.Linq; +using NUnit.Framework; +using OpenTK; +using osu.Framework.Graphics; +using osu.Game.Beatmaps.Formats; +using osu.Game.Storyboards; +using osu.Game.Tests.Resources; + +namespace osu.Game.Tests.Beatmaps.Formats +{ + [TestFixture] + public class LegacyStoryboardDecoderTest + { + [Test] + public void TestDecodeStoryboardEvents() + { + var decoder = new LegacyBeatmapDecoder(); + using (var resStream = Resource.OpenResource("Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu")) + using (var stream = new StreamReader(resStream)) + { + var storyboard = decoder.GetStoryboardDecoder().DecodeStoryboard(stream); + + Assert.IsTrue(storyboard.HasDrawable); + Assert.AreEqual(4, storyboard.Layers.Count()); + + StoryboardLayer background = storyboard.Layers.FirstOrDefault(l => l.Depth == 3); + Assert.IsNotNull(background); + Assert.AreEqual(16, background.Elements.Count()); + Assert.IsTrue(background.EnabledWhenFailing); + Assert.IsTrue(background.EnabledWhenPassing); + Assert.AreEqual("Background", background.Name); + + StoryboardLayer fail = storyboard.Layers.FirstOrDefault(l => l.Depth == 2); + Assert.IsNotNull(fail); + Assert.AreEqual(0, fail.Elements.Count()); + Assert.IsTrue(fail.EnabledWhenFailing); + Assert.IsFalse(fail.EnabledWhenPassing); + Assert.AreEqual("Fail", fail.Name); + + StoryboardLayer pass = storyboard.Layers.FirstOrDefault(l => l.Depth == 1); + Assert.IsNotNull(pass); + Assert.AreEqual(0, pass.Elements.Count()); + Assert.IsFalse(pass.EnabledWhenFailing); + Assert.IsTrue(pass.EnabledWhenPassing); + Assert.AreEqual("Pass", pass.Name); + + StoryboardLayer foreground = storyboard.Layers.FirstOrDefault(l => l.Depth == 0); + Assert.IsNotNull(foreground); + Assert.AreEqual(151, foreground.Elements.Count()); + Assert.IsTrue(foreground.EnabledWhenFailing); + Assert.IsTrue(foreground.EnabledWhenPassing); + Assert.AreEqual("Foreground", foreground.Name); + + int spriteCount = background.Elements.Where(x => x.GetType() == typeof(StoryboardSprite)).Count(); + int animationCount = background.Elements.Where(x => x.GetType() == typeof(StoryboardAnimation)).Count(); + int sampleCount = background.Elements.Where(x => x.GetType() == typeof(StoryboardSample)).Count(); + + Assert.AreEqual(15, spriteCount); + Assert.AreEqual(1, animationCount); + Assert.AreEqual(0, sampleCount); + Assert.AreEqual(background.Elements.Count(), spriteCount + animationCount + sampleCount); + + var sprite = background.Elements.ElementAt(0) as StoryboardSprite; + Assert.IsTrue(sprite.HasCommands); + Assert.AreEqual(new Vector2(320, 240), sprite.InitialPosition); + Assert.IsTrue(sprite.IsDrawable); + Assert.AreEqual(Anchor.Centre, sprite.Origin); + Assert.AreEqual("SB/lyric/ja-21.png", sprite.Path); + + var animation = background.Elements.ElementAt(12) as StoryboardAnimation; + Assert.AreEqual(141175, animation.EndTime); + Assert.AreEqual(10, animation.FrameCount); + Assert.AreEqual(30, animation.FrameDelay); + Assert.IsTrue(animation.HasCommands); + Assert.AreEqual(new Vector2(320, 240), animation.InitialPosition); + Assert.IsTrue(animation.IsDrawable); + Assert.AreEqual(AnimationLoopType.LoopForever, animation.LoopType); + Assert.AreEqual(Anchor.Centre, animation.Origin); + Assert.AreEqual("SB/red jitter/red_0000.jpg", animation.Path); + Assert.AreEqual(78993, animation.StartTime); + } + } + } +} diff --git a/osu.Game.Tests/Resources/Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu b/osu.Game.Tests/Resources/Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu new file mode 100644 index 0000000000..f7b33fa6a8 --- /dev/null +++ b/osu.Game.Tests/Resources/Himeringo - Yotsuya-san ni Yoroshiku (RLC) [Winber1's Extreme].osu @@ -0,0 +1,1997 @@ +osu file format v13 + +[General] +AudioFilename: yotsuya192.mp3 +AudioLeadIn: 0 +PreviewTime: 71220 +Countdown: 0 +SampleSet: Soft +StackLeniency: 0.7 +Mode: 0 +LetterboxInBreaks: 0 +WidescreenStoryboard: 1 + +[Editor] +Bookmarks: 24456,34275,43002,53911,71366,88820,99729,117184,143366,152093,160820,169547 +DistanceSpacing: 1.4 +BeatDivisor: 4 +GridSize: 4 +TimelineZoom: 1 + +[Metadata] +Title:Yotsuya-san ni Yoroshiku +TitleUnicode:四ツ谷さんによろしく +Artist:Himeringo +ArtistUnicode:ひめりんご +Creator:RLC +Version:Winber1's Extreme +Source: +Tags:flask nyquill winber1 skystar amamiya yuko cheesiest onosakihito utaite leave it to eight hatsune miku vocaloid +BeatmapID:378781 +BeatmapSetID:100049 + +[Difficulty] +HPDrainRate:6 +CircleSize:4 +OverallDifficulty:8.5 +ApproachRate:9.5 +SliderMultiplier:1.9 +SliderTickRate:1 + +[Events] +//Background and Video events +0,0,"primary.jpg",0,0 +//Break Periods +//Storyboard Layer 0 (Background) +Sprite,Background,Centre,"SB\lyric\ja-21.png",320,240 + S,0,117175,,0.8 + F,0,117175,117584,0,1 + M,0,117175,118675,192,393,-12,393 + F,0,117584,118402,1 + F,0,118402,118675,1,0 +Sprite,Background,Centre,"SB\lyric\ja-22.png",320,240 + S,0,118675,,0.8 + F,0,118675,119084,0,1 + M,0,118675,119629,150,393,23,393 + F,0,119084,119357,1 + F,0,119357,119629,1,0 +Sprite,Background,Centre,"SB\lyric\ja-22-repeat.png",320,240 + S,0,119357,,0.8 + M,0,119629,,196,393 + F,0,119629,119902,0,1 + M,0,119629,121947,196,393,-58,393 + F,0,119902,121675,1 + F,0,121675,121947,1,0 +Sprite,Background,Centre,"SB\lyric\ja-23.png",320,240 + S,0,121947,,0.8 + F,0,121947,122357,0,1 + M,0,121947,123038,158,393,23,393 + F,0,122357,122766,1 + F,0,122766,123038,1,0 +Sprite,Background,Centre,"SB\lyric\ja-24.png",320,240 + S,0,123038,,0.8 + F,0,123038,123447,0,1 + M,0,123038,123720,249,393,187,393 + F,0,123447,123720,1 +Sprite,Background,Centre,"SB\lyric\en-21.png",320,240 + S,0,117175,,0.6 + F,0,117175,117584,0,1 + M,0,117175,117925,641,425,551,425 + F,0,117584,118402,1 + M,0,117925,118675,551,425,461,425 + F,0,118402,118675,1,0 +Sprite,Background,Centre,"SB\lyric\en-22.png",320,240 + S,0,118675,,0.6 + F,0,118675,119084,0,1 + M,0,118675,119152,616,425,554,425 + F,0,119084,119357,1 + M,0,119152,119629,554,425,489,425 + F,0,119357,119629,1,0 +Sprite,Background,Centre,"SB\lyric\en-22-repeat.png",320,240 + S,0,119629,,0.6 + F,0,119629,120038,0,1 + M,0,119629,121947,683,425,417,425 + F,0,120038,121675,1 + F,0,121675,121947,1,0 +Sprite,Background,Centre,"SB\lyric\en-23.png",320,240 + S,0,121947,,0.6 + F,0,121947,122357,0,1 + M,0,121947,123038,617,425,487,425 + F,0,122357,122766,1 + F,0,122766,123038,1,0 +Sprite,Background,Centre,"SB\lyric\en-24.png",320,240 + S,0,123038,,0.6 + F,0,123038,123447,0,1 + M,0,123038,123720,515,425,446,425 +Sprite,Background,Centre,"SB\lower mask.png",320,240 + S,0,117175,,0.625 + R,0,117175,,0.0001 + M,0,117175,123720,320,330 +Sprite,Background,Centre,"SB\black.jpg",320,240 + F,0,-97,,1 + F,0,20084,23357,1,0 + F,0,23357,24447,0 + F,0,24447,25470,0,1 + F,0,25538,26629,0,1 + F,0,27720,28811,0.5,1 + F,0,29902,30993,0,1 + F,0,32084,33175,0.5,1 + F,0,34266,,0 + F,0,70266,,1 + S,0,70266,,1.267815 + R,0,70266,,0.0001 + F,0,71357,,0 + F,0,71357,,0 + F,0,86629,,1 + F,0,87720,88538,1,0 + F,0,88538,,0 + M,0,108877,116493,314,289,312,322 + F,0,116084,,1 + F,0,117175,117720,0,0.5 + F,0,117720,118266,0.5,0 + F,0,118266,118811,0,0.5 + F,0,118811,119357,0.5,0 + F,0,119357,119902,0,0.5 + F,0,119902,120447,0.5,0 + F,0,120447,120993,0,0.5 + F,0,120993,121538,0.5,0 + F,0,121538,122084,0,0.5 + F,0,122084,122629,0.5,0 + F,0,122629,123175,0,0.5 + F,0,123175,123686,0.5,0 + F,0,123720,,1 + F,0,126175,,1,0.07840011 + F,0,141175,,1 + F,0,143357,144447,0,1 + F,0,145538,146629,0.5,1 + F,0,147720,148811,0,1 + F,0,149902,150993,0.5,1 + F,0,150993,,1 + F,0,152084,,0 + F,0,171447,,1 + F,0,171447,177720,1 +Animation,Background,Centre,"SB\red jitter\red_0000.jpg",320,240,10,30,LoopForever + S,0,78993,,1.001 + R,0,78993,,0.0001 + F,0,78993,80084,0,1 + F,0,80084,,1 + F,0,86629,,0 + F,0,133538,134629,0.9180929,1 + F,0,141175,,1 +Sprite,Background,Centre,"SB\brown.jpg",320,240 + M,0,71357,,320,240 + S,0,71357,,1.001 + R,0,71357,,0.0001 + F,0,71357,80084,1 + F,0,80084,81175,1,0 +Sprite,Background,Centre,"SB\blue.jpg",320,240 + S,0,126175,,1.001 + R,0,126175,,0.0001 + F,0,126175,134629,1 + F,0,134629,135720,1,0 +Sprite,Background,Centre,"SB\cloud2.png",320,240 + S,0,126175,,4.659974 + R,0,126175,,0.0001 + M,0,126175,135175,72,29,474,29 + F,0,133538,135175,0.4690581,0 +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +Sprite,Foreground,Centre,"SB\lyric\ja-1.png",320,240 + S,0,53902,,0.8 + F,0,53902,54993,0,1 + M,0,53902,58266,232,393,263,393 + F,0,54993,57175,1 + F,0,57175,58266,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-1.png",320,240 + S,0,53902,,0.6 + F,0,53902,54993,0,1 + M,0,53902,58266,404,425,373,425 + F,0,54993,57175,1 + F,0,57175,58266,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-2.png",320,240 + S,0,58266,,0.8 + F,0,58266,59357,0,1 + M,0,58266,62629,232,393,263,393 + F,0,59357,61538,1 + F,0,61538,,1 + F,0,61538,,1 + F,0,61538,62357,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-3.png",320,240 + S,0,62629,,0.8 + F,0,62629,63720,0,1 + M,0,62629,66447,232,393,263,393 + F,0,63720,65357,1 + F,0,65357,66447,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-4.png",320,240 + S,0,66447,,0.8 + F,0,66447,67538,0,1 + M,0,66447,70266,232,393,263,393 + F,0,67538,69175,1 + F,0,69175,70266,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-5.png",320,240 + S,0,70266,,0.5 + M,0,70266,70402,179,335 +Sprite,Foreground,Centre,"SB\lyric\ja-5.png",320,240 + S,0,70402,,0.8 + M,0,70402,70538,469,297 +Sprite,Foreground,Centre,"SB\lyric\ja-5.png",320,240 + S,0,70538,,1.1 + M,0,70538,70675,255,158 +Sprite,Foreground,Centre,"SB\lyric\ja-5RED.png",320,240 + S,0,70675,,1.3 + F,0,70675,70947,1 + M,0,70675,71220,320,240 + F,0,70947,71357,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-6-1.png",320,240 + S,0,71357,,0.8 + M,0,71357,71902,320,393 +Sprite,Foreground,Centre,"SB\lyric\ja-6-2.png",320,240 + S,0,71902,,0.8 + M,0,71902,72447,320,393 +Sprite,Foreground,Centre,"SB\lyric\ja-6-3.png",320,240 + S,0,72447,,0.8 + M,0,72447,72857,320,393 +Sprite,Foreground,Centre,"SB\lyric\ja-7.png",320,240 + S,0,72857,,0.8 + M,0,72857,73947,320,393 +Sprite,Foreground,Centre,"SB\lyric\ja-8.png",320,240 + S,0,73947,,0.8 + M,0,73947,76129,320,393 +Sprite,Foreground,Centre,"SB\lyric\ja-9.png",320,240 + S,0,76129,,0.8 + M,0,76129,77220,320,393 +Sprite,Foreground,Centre,"SB\lyric\ja-10-2.png",320,240 + S,0,78311,,0.8 + M,0,78311,80084,376,393 +Sprite,Foreground,Centre,"SB\lyric\ja-10-1.png",320,240 + S,0,77220,,0.8 + M,0,77220,80084,264,393 +Animation,Foreground,Centre,"SB\lyric\11-1\ja-11-1_0000.png",320,240,10,30,LoopForever + S,0,80084,,0.8 + M,0,80084,81584,230,326 +Animation,Foreground,Centre,"SB\lyric\11-2\ja-11-2_0000.png",320,240,10,30,LoopForever + S,0,80629,,1.1 + M,0,80629,81584,325,305 +Animation,Foreground,Centre,"SB\lyric\11-3\ja-11-3_0000.png",320,240,10,30,LoopForever + S,0,81175,,1.3 + M,0,81175,81584,425,284 +Animation,Foreground,Centre,"SB\lyric\12\ja-12_0000.png",320,240,10,30,LoopForever + S,0,81584,,1.1 + M,0,81584,82675,306,214 +Animation,Foreground,Centre,"SB\lyric\13\ja-13_0000.png",320,240,10,30,LoopForever + S,0,82675,,0.8 + M,0,82675,84857,315,294 +Animation,Foreground,Centre,"SB\lyric\14\ja-14_0000.png",320,240,10,30,LoopForever + S,0,84857,,1.1 + M,0,84857,85947,320,222 +Animation,Foreground,Centre,"SB\lyric\15-1\ja-15-1_0000.png",320,240,10,30,LoopForever + S,0,85947,,0.8 + M,0,85947,86629,269,393 +Sprite,Foreground,Centre,"SB\lyric\ja-15-1.png",320,240 + S,0,86629,,0.8 + M,0,86629,87720,269,393 + F,0,87720,88538,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-15-2.png",320,240 + S,0,87175,,0.8 + M,0,87175,87720,373,393 + F,0,87720,88538,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-16.png",320,240 + S,0,99720,,0.8 + F,0,99720,100811,0,1 + M,0,99720,104084,232,393,263,393 + F,0,100811,102993,1 + F,0,102993,104084,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-17.png",320,240 + S,0,104084,,0.8 + F,0,104084,105175,0,1 + M,0,104084,108447,232,393,263,393 + F,0,105175,107357,1 + F,0,107357,108175,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-18.png",320,240 + S,0,108447,,0.8 + F,0,108447,109538,0,1 + M,0,108447,112266,232,393,263,393 + F,0,109538,111175,1 + F,0,111175,112266,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-19.png",320,240 + S,0,112266,,0.8 + F,0,112266,113357,0,1 + M,0,112266,116084,232,393,263,393 + F,0,113357,114993,1 + F,0,114993,116084,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-20_1.png",320,240 + S,0,116084,,0.5 + M,0,116084,116220,478,319 +Sprite,Foreground,Centre,"SB\lyric\ja-20_1.png",320,240 + S,0,116220,,0.8 + M,0,116220,116357,160,257 +Sprite,Foreground,Centre,"SB\lyric\ja-20_1.png",320,240 + S,0,116357,,1.1 + M,0,116357,116493,393,161 +Sprite,Foreground,Centre,"SB\lyric\ja-20RED.png",320,240 + M,0,116493,,320,240 + S,0,116493,,1.3 + F,0,116493,116766,1 + F,0,116766,117175,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-24RED.png",320,240 + M,0,123720,,187,393 + S,0,123720,,0.8 + F,0,123720,124811,1 + F,0,124811,125902,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-25-1.png",320,240 + S,0,125766,,0.8 + M,0,125766,126175,156,312 +Sprite,Foreground,Centre,"SB\lyric\ja-25-2.png",320,240 + S,0,126175,,0.8 + M,0,126175,126993,156,312 + F,0,127538,127811,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-25-3.png",320,240 + S,0,126447,,1.1 + R,0,126447,,-0.189046 + M,0,126447,126993,306,288 + F,0,127538,,1 + F,0,127538,127811,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-25-4.png",320,240 + M,0,126993,,388,400 + S,0,126993,,1.3 + R,0,126993,,0.1785437 + F,0,127538,127811,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-26.png",320,240 + S,0,127402,,0.8 + F,0,127402,127538,0,1 + M,0,127402,128084,315,201 + R,0,127402,128084,0.1050253 + F,0,127538,128084,1 + M,0,128084,128493,315,201,315,236 + F,0,128084,128493,1,0 + R,0,128084,128493,0.1050253,0.3360815 +Sprite,Foreground,Centre,"SB\lyric\ja-27.png",320,240 + S,0,128493,,0.9 + F,0,128493,128629,0,1 + M,0,128493,130266,191,426 + R,0,128493,130266,0.06301453 + F,0,128629,130266,1 + M,0,130266,130675,191,426,191,458 + F,0,130266,130675,1,0 + R,0,130266,130675,0.06301453,0.2835687 +Sprite,Foreground,Centre,"SB\lyric\ja-28.png",320,240 + S,0,130675,,0.8 + R,0,130675,,-0.1155283 + F,0,130675,130811,0,1 + M,0,130675,131357,320,240 + F,0,130811,131357,1 + F,0,131357,131766,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-29.png",320,240 + S,0,131766,,0.8 + F,0,131766,131902,0,1 + M,0,131766,133538,230,154 + R,0,131766,133538,0.1260309 + F,0,131902,133538,1 + M,0,133538,134629,230,154,230,206 + F,0,133538,134629,1,0 + R,0,133538,134629,0.1260309,0.2835693 +Animation,Foreground,Centre,"SB\lyric\12\ja-12_0000.png",320,240,10,30,LoopForever + S,0,136129,,1.1 + M,0,136129,137220,306,214 +Animation,Foreground,Centre,"SB\lyric\13\ja-13_0000.png",320,240,10,30,LoopForever + S,0,137220,,0.8 + M,0,137220,139402,315,294 +Animation,Foreground,Centre,"SB\lyric\14\ja-14_0000.png",320,240,10,30,LoopForever + S,0,139402,,1.1 + M,0,139402,140493,320,222 +Animation,Foreground,Centre,"SB\lyric\30-1\ja-30-1_0000.png",320,240,10,30,LoopForever + S,0,134629,,0.8 + M,0,134629,136129,153,292 +Animation,Foreground,Centre,"SB\lyric\30-2\ja-30-2_0000.png",320,240,10,30,LoopForever + S,0,135175,,1.1 + M,0,135175,136129,333,216 +Animation,Foreground,Centre,"SB\lyric\30-3\ja-30-3_0000.png",320,240,10,30,LoopForever + S,0,135720,,1.3 + M,0,135720,136129,485,133 +Animation,Foreground,Centre,"SB\lyric\34-1\ja-34-1_0000.png",320,240,10,30,LoopForever + S,0,140493,,0.8 + M,0,140493,141175,269,393 +Sprite,Foreground,Centre,"SB\lyric\ja-34-1.png",320,240 + S,0,141175,,0.8 + M,0,141175,143357,269,393 + F,0,142266,143357,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-34-2.png",320,240 + S,0,141720,,0.8 + M,0,141720,143357,391,393 + F,0,142266,143357,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-2.png",320,240 + S,0,58266,,0.6 + F,0,58266,59357,0,1 + M,0,58266,62629,404,425,373,425 + F,0,59357,61538,1 + F,0,61538,62357,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-3.png",320,240 + S,0,62629,,0.6 + F,0,62629,63720,0,1 + M,0,62629,66447,404,425,373,425 + F,0,63720,65357,1 + F,0,65357,66447,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-4.png",320,240 + S,0,66447,,0.6 + F,0,66447,67538,0,1 + M,0,66447,70266,404,425,373,425 + F,0,67538,69175,1 + F,0,69175,70266,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-5.png",320,240 + F,0,70266,,1 + S,0,70266,,0.4 + M,0,70266,70402,436,192 +Sprite,Foreground,Centre,"SB\lyric\en-5.png",320,240 + S,0,70402,,0.6 + M,0,70402,70538,181,97 +Sprite,Foreground,Centre,"SB\lyric\en-5.png",320,240 + S,0,70538,,0.8 + M,0,70538,70675,391,392 +Sprite,Foreground,Centre,"SB\lyric\en-5RED.png",320,240 + M,0,70675,,320,294 + F,0,70675,70947,1 + F,0,70947,71357,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-6-1.png",320,240 + S,0,71357,,0.7885935 + M,0,71357,71902,320,425 +Sprite,Foreground,Centre,"SB\lyric\en-6-2.png",320,240 + S,0,71902,,0.7357419 + M,0,71902,72447,320,425 +Sprite,Foreground,Centre,"SB\lyric\en-6-3.png",320,240 + S,0,72447,,0.8084131 + M,0,72447,72857,320,425 +Sprite,Foreground,Centre,"SB\lyric\en-7.png",320,240 + S,0,72857,,0.8018063 + M,0,72857,73947,320,425 +Sprite,Foreground,Centre,"SB\lyric\en-8.png",320,240 + S,0,73947,,0.6961035 + M,0,73947,76129,320,425 +Sprite,Foreground,Centre,"SB\lyric\en-9.png",320,240 + S,0,76129,,0.762168 + M,0,76129,77220,320,425 +Sprite,Foreground,Centre,"SB\lyric\en-10-1.png",320,240 + S,0,77220,,0.6564645 + M,0,77220,80084,262,425 +Sprite,Foreground,Centre,"SB\lyric\en-10-2.png",320,240 + S,0,78311,,0.5837936 + M,0,78311,80084,411,425 +Sprite,Foreground,Centre,"SB\lyric\en-16.png",320,240 + S,0,99720,,0.6 + F,0,99720,100811,0,1 + M,0,99720,104084,404,425,373,425 + F,0,100811,102993,1 + F,0,102993,104084,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-17-1.png",320,240 + S,0,104084,,0.6 + F,0,104084,105175,0,1 + M,0,104084,108447,404,425,373,425 + F,0,105175,107357,1 + F,0,107357,108175,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-17-2.png",320,240 + S,0,104084,,0.6 + F,0,104084,105175,0,1 + M,0,104084,108175,232,457,263,457 + F,0,105175,107357,1 + F,0,107357,108175,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-18.png",320,240 + S,0,108447,,0.6 + F,0,108447,109538,0,1 + M,0,108447,112266,404,425,373,425 + F,0,109538,111175,1 + F,0,111175,112266,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-19.png",320,240 + S,0,112266,,0.6 + F,0,112266,113357,0,1 + M,0,112266,116084,404,425,373,425 + F,0,113357,114993,1 + F,0,114993,116084,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-20-1.png",320,240 + S,0,116084,,0.4 + M,0,116084,116220,205,167 +Sprite,Foreground,Centre,"SB\lyric\en-20-1.png",320,240 + S,0,116220,,0.6 + M,0,116220,116357,389,252 +Sprite,Foreground,Centre,"SB\lyric\en-20-1.png",320,240 + S,0,116357,,0.8 + M,0,116357,116493,220,395 +Sprite,Foreground,Centre,"SB\lyric\en-20RED.png",320,240 + M,0,116493,,320,293 + F,0,116493,116766,1 + F,0,116766,117175,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-24RED.png",320,240 + M,0,123720,,446,425 + S,0,123720,,0.6 + F,0,123720,124811,1 + F,0,124811,125902,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-25-1.png",320,240 + S,0,125766,,0.6 + M,0,125766,126175,408,240 +Sprite,Foreground,Centre,"SB\lyric\en-25-2.png",320,240 + S,0,126175,,0.6 + M,0,126175,126993,408,240 + F,0,127538,127811,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-25-3.png",320,240 + M,0,126447,,525,277 + S,0,126447,,0.8 + R,0,126447,,0.189046 + F,0,127538,127811,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-25-4.png",320,240 + M,0,126993,,219,400 + S,0,126993,,1 + R,0,126993,,-0.1260309 + F,0,126993,127402,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-26.png",320,240 + S,0,127402,,0.7 + F,0,127402,127538,0,1 + M,0,127402,128084,416,325 + R,0,127402,128084,-0.1680408 + F,0,127538,128084,1 + M,0,128084,128493,416,325,416,391 + F,0,128084,128493,1,0 + R,0,128084,128493,-0.1680408,-0.3990967 +Sprite,Foreground,Centre,"SB\lyric\en-27.png",320,240 + S,0,128493,,0.6 + F,0,128493,128629,0,1 + M,0,128493,130266,443,226 + R,0,128493,130266,-0.08402039 + F,0,128629,130266,1 + M,0,130266,130675,443,226,443,252 + F,0,130266,130675,1,0 + R,0,130266,130675,-0.08402039,-0.2310565 +Sprite,Foreground,Centre,"SB\lyric\en-28.png",320,240 + S,0,130675,,0.7 + R,0,130675,,0.273067 + F,0,130675,130811,0,1 + M,0,130675,131357,140,303 + F,0,130811,131357,1 + F,0,131357,131766,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-29.png",320,240 + S,0,131766,,0.7 + F,0,131766,131902,0,1 + M,0,131766,133538,438,257 + R,0,131766,133538,-0.1890472 + F,0,131902,133538,1 + M,0,133538,134629,438,257,438,314 + F,0,133538,134629,1,0 + R,0,133538,134629,-0.1890472,-0.4306067 +Animation,Foreground,Centre,"SB\lyric\11-1\en-11-1_0000.png",320,240,10,30,LoopForever + S,0,80357,,0.6 + M,0,80357,81584,336,384 +Animation,Foreground,Centre,"SB\lyric\11-2\en-11-2_0000.png",320,240,10,30,LoopForever + S,0,80902,,0.8 + M,0,80902,81993,142,245 +Animation,Foreground,Centre,"SB\lyric\11-3\en-11-3_0000.png",320,240,10,30,LoopForever + S,0,81447,,1 + M,0,81447,81993,413,164 +Animation,Foreground,Centre,"SB\lyric\12\en-12_0000.png",320,240,10,30,LoopForever + S,0,81993,,0.8 + M,0,81993,83357,255,360 +Animation,Foreground,Centre,"SB\lyric\13\en-13_0000.png",320,240,10,30,LoopForever + S,0,83357,,0.6 + M,0,83357,85538,222,140 +Animation,Foreground,Centre,"SB\lyric\14\en-14_0000.png",320,240,10,30,LoopForever + S,0,85538,,1 + M,0,85538,86357,437,300 +Animation,Foreground,Centre,"SB\lyric\15-1\en-15-1_0000.png",320,240,10,30,LoopForever + F,0,86357,,1 + S,0,86357,,0.6564642 + M,0,86357,86629,237,430 +Sprite,Foreground,Centre,"SB\lyric\en-15-1.png",320,240 + S,0,86629,,0.6630707 + M,0,86629,87720,234,430 + F,0,86629,87720,1 + F,0,87720,88538,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-15-2.png",320,240 + S,0,87175,,0.7423482 + M,0,87175,87720,417,430 + F,0,87175,87720,1 + F,0,87720,88538,1,0 +Animation,Foreground,Centre,"SB\lyric\30-1\en-30-1_0000.png",320,240,10,30,LoopForever + F,0,134902,,1 + S,0,134902,,0.6 + M,0,134902,136129,419,374 +Animation,Foreground,Centre,"SB\lyric\30-2\en-30-2_0000.png",320,240,10,30,LoopForever + S,0,135447,,0.6 + M,0,135447,136811,156,164 +Animation,Foreground,Centre,"SB\lyric\30-3\en-30-3_0000.png",320,240,10,30,LoopForever + S,0,135993,,0.6 + M,0,135993,136811,128,404 +Animation,Foreground,Centre,"SB\lyric\12\en-12_0000.png",320,240,10,30,LoopForever + S,0,136811,,0.8 + M,0,136811,137902,439,391 +Animation,Foreground,Centre,"SB\lyric\13\en-13_0000.png",320,240,10,30,LoopForever + S,0,137902,,0.6 + M,0,137902,140084,450,142 +Animation,Foreground,Centre,"SB\lyric\14\en-14_0000.png",320,240,10,30,LoopForever + S,0,140084,,1 + M,0,140084,140902,191,294 +Animation,Foreground,Centre,"SB\lyric\34-1\en-34-1_0000.png",320,240,10,30,LoopForever + S,0,140902,,0.6 + M,0,140902,141175,286,425 +Sprite,Foreground,Centre,"SB\lyric\en-34-1-1.png",320,240 + S,0,141175,,0.6 + M,0,141175,142266,286,425 + F,0,142266,143357,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-34-1-2.png",320,240 + S,0,141175,,0.6 + M,0,141175,142266,286,425 + F,0,142266,143357,1,0 +Sprite,Foreground,Centre,"SB\lyric\en-34-2.png",320,240 + S,0,141720,,0.6 + M,0,141720,142266,451,425 + F,0,142266,143357,1,0 +Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-1.png",320,240 + S,0,25538,,0.6 + M,0,25538,27720,50,240 + M,0,27720,27857,50,240,50,247 + F,0,27720,28538,1,0 + R,0,27720,28538,0,0.3567487 + M,0,27857,27993,50,247,50,263 + M,0,27993,28129,50,263,50,290 + M,0,28129,28266,50,290,50,321 + M,0,28266,28402,50,321,50,360 + M,0,28402,28538,50,360,50,408 +Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-2.png",320,240 + S,0,25538,,0.6 + M,0,25538,28947,104,240 + M,0,28947,29084,104,240,104,248 + F,0,28947,29766,1,0 + R,0,28947,29766,0,-0.4095996 + M,0,29084,29220,104,248,104,263 + M,0,29220,29357,104,263,104,287 + M,0,29357,29493,104,287,104,320 + M,0,29493,29629,104,320,104,360 + M,0,29629,29766,104,360,104,409 +Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-3.png",320,240 + S,0,25538,,0.6 + M,0,25538,28129,158,240 + M,0,28129,28266,158,240,158,249 + F,0,28129,28947,1,0 + R,0,28129,28947,0,-0.2114062 + M,0,28266,28402,158,249,158,263 + M,0,28402,28538,158,263,158,288 + M,0,28538,28675,158,288,158,321 + M,0,28675,28811,158,321,158,360 + M,0,28811,28947,158,360,158,409 +Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-4.png",320,240 + S,0,25538,,0.6 + M,0,25538,28811,212,240 + M,0,28811,28947,212,240,212,248 + F,0,28811,29629,1,0 + R,0,28811,29629,0,0.4624512 + M,0,28947,29084,212,248,212,263 + M,0,29084,29220,212,263,212,288 + M,0,29220,29357,212,288,212,320 + M,0,29357,29493,212,320,212,360 + M,0,29493,29629,212,360,212,407 +Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-5.png",320,240 + S,0,25538,,0.6 + M,0,25538,28538,266,240 + M,0,28538,28675,266,240,266,248 + F,0,28538,29357,1,0 + R,0,28538,29357,0,0.2906836 + M,0,28675,28811,266,248,266,264 + M,0,28811,28947,266,264,266,288 + M,0,28947,29084,266,288,266,321 + M,0,29084,29220,266,321,266,358 + M,0,29220,29357,266,358,266,407 +Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-6.png",320,240 + S,0,25538,,0.6 + M,0,25538,27993,320,240 + M,0,27993,28129,320,240,320,247 + F,0,27993,28811,1,0 + R,0,27993,28811,0,0.2906836 + M,0,28129,28266,320,247,320,263 + M,0,28266,28402,320,263,320,288 + M,0,28402,28538,320,288,320,321 + M,0,28538,28675,320,321,320,360 + M,0,28675,28811,320,360,320,408 +Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-7.png",320,240 + S,0,25538,,0.6 + M,0,25538,28266,374,240 + M,0,28266,28402,374,240,374,248 + F,0,28266,29084,1,0 + R,0,28266,29084,0,0.1717682 + M,0,28402,28538,374,248,374,264 + M,0,28538,28675,374,264,374,288 + M,0,28675,28811,374,288,374,320 + M,0,28811,28947,374,320,374,359 + M,0,28947,29084,374,359,374,409 +Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-8.png",320,240 + S,0,25538,,0.6 + M,0,25538,27857,428,240 + M,0,27857,27993,428,240,428,249 + F,0,27857,28675,1,0 + R,0,27857,28675,0,-0.237832 + M,0,27993,28129,428,249,428,263 + M,0,28129,28266,428,263,428,288 + M,0,28266,28402,428,288,428,320 + M,0,28402,28538,428,320,428,358 + M,0,28538,28675,428,358,428,408 +Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-9.png",320,240 + S,0,25538,,0.6 + M,0,25538,28675,482,240 + M,0,28675,28811,482,240,482,247 + F,0,28675,29493,1,0 + R,0,28675,29493,0,-0.5020905 + M,0,28811,28947,482,247,482,263 + M,0,28947,29084,482,263,482,287 + M,0,29084,29220,482,287,482,320 + M,0,29220,29357,482,320,482,360 + M,0,29357,29493,482,360,482,407 +Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-10-1.png",320,240 + S,0,25538,,0.6 + M,0,25538,28402,536,240 + M,0,28402,28538,536,240,536,247 + F,0,28402,29220,1,0 + R,0,28402,29220,0,-0.3038965 + M,0,28538,28675,536,247,536,263 + M,0,28675,28811,536,263,536,289 + M,0,28811,28947,536,289,536,321 + M,0,28947,29084,536,321,536,360 + M,0,29084,29220,536,360,536,407 +Sprite,Foreground,Centre,"SB\lyric\y1\intro-ja-11.png",320,240 + S,0,25538,,0.6 + M,0,25538,29084,590,240 + F,0,28947,29902,1,0 + R,0,28947,29902,0,0.6474323 + M,0,29084,29220,590,240,590,247 + M,0,29220,29357,590,247,590,263 + M,0,29357,29493,590,263,590,288 + M,0,29493,29629,590,288,590,320 + M,0,29629,29766,590,320,590,361 + M,0,29766,29902,590,361,590,408 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-1.png",320,240 + S,0,29902,,0.6 + M,0,29902,32084,50,240 + M,0,32084,32221,50,240,50,247 + F,0,32084,32902,1,0 + R,0,32084,32902,0,0.4 + M,0,32221,32357,50,247,50,263 + M,0,32357,32493,50,263,50,290 + M,0,32493,32630,50,290,50,321 + M,0,32630,32766,50,321,50,360 + M,0,32766,32902,50,360,50,408 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-2.png",320,240 + S,0,29902,,0.6 + M,0,29902,33311,104,240 + M,0,33311,33448,104,240,104,248 + F,0,33311,34130,1,0 + R,0,33311,34130,0,-0.4 + M,0,33448,33584,104,248,104,263 + M,0,33584,33721,104,263,104,287 + M,0,33721,33857,104,287,104,320 + M,0,33857,33993,104,320,104,360 + M,0,33993,34130,104,360,104,409 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-3.png",320,240 + S,0,29902,,0.6 + M,0,29902,32493,158,240 + M,0,32493,32630,158,240,158,249 + F,0,32493,33311,1,0 + R,0,32493,33311,0,-0.4 + M,0,32630,32766,158,249,158,263 + M,0,32766,32902,158,263,158,288 + M,0,32902,33039,158,288,158,321 + M,0,33039,33175,158,321,158,360 + M,0,33175,33311,158,360,158,409 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-4.png",320,240 + S,0,29902,,0.6 + M,0,29902,33175,212,240 + M,0,33175,33311,212,240,212,248 + F,0,33175,33993,1,0 + R,0,33175,33993,0,0.4 + M,0,33311,33448,212,248,212,263 + M,0,33448,33584,212,263,212,288 + M,0,33584,33721,212,288,212,320 + M,0,33721,33857,212,320,212,360 + M,0,33857,33993,212,360,212,407 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-5.png",320,240 + S,0,29902,,0.6 + M,0,29902,32902,266,240 + M,0,32902,33039,266,240,266,248 + F,0,32902,33721,1,0 + R,0,32902,33721,0,0.4 + M,0,33039,33175,266,248,266,264 + M,0,33175,33311,266,264,266,288 + M,0,33311,33448,266,288,266,321 + M,0,33448,33584,266,321,266,358 + M,0,33584,33721,266,358,266,407 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-6.png",320,240 + S,0,29902,,0.6 + M,0,29902,32357,320,240 + M,0,32357,32493,320,240,320,247 + F,0,32357,33175,1,0 + R,0,32357,33175,0,0.4 + M,0,32493,32630,320,247,320,263 + M,0,32630,32766,320,263,320,288 + M,0,32766,32902,320,288,320,321 + M,0,32902,33039,320,321,320,360 + M,0,33039,33175,320,360,320,408 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-7.png",320,240 + S,0,29902,,0.6 + M,0,29902,32630,374,240 + M,0,32630,32766,374,240,374,248 + F,0,32630,33448,1,0 + R,0,32630,33448,0,0.4 + M,0,32766,32902,374,248,374,264 + M,0,32902,33039,374,264,374,288 + M,0,33039,33175,374,288,374,320 + M,0,33175,33311,374,320,374,359 + M,0,33311,33448,374,359,374,409 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-8.png",320,240 + S,0,29902,,0.6 + M,0,29902,32221,428,240 + M,0,32221,32357,428,240,428,249 + F,0,32221,33039,1,0 + R,0,32221,33039,0,-0.4 + M,0,32357,32493,428,249,428,263 + M,0,32493,32630,428,263,428,288 + M,0,32630,32766,428,288,428,320 + M,0,32766,32902,428,320,428,358 + M,0,32902,33039,428,358,428,408 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-9.png",320,240 + S,0,29902,,0.6 + M,0,29902,33039,482,240 + M,0,33039,33175,482,240,482,247 + F,0,33039,33857,1,0 + R,0,33039,33857,0,-0.4 + M,0,33175,33311,482,247,482,263 + M,0,33311,33448,482,263,482,287 + M,0,33448,33584,482,287,482,320 + M,0,33584,33721,482,320,482,360 + M,0,33721,33857,482,360,482,407 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-10-1.png",320,240 + S,0,29902,,0.6 + M,0,29902,32766,536,240 + M,0,32766,32902,536,240,536,247 + F,0,32766,33584,1,0 + R,0,32766,33584,0,-0.4 + M,0,32902,33039,536,247,536,263 + M,0,33039,33175,536,263,536,289 + M,0,33175,33311,536,289,536,321 + M,0,33311,33448,536,321,536,360 + M,0,33448,33584,536,360,536,407 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-11.png",320,240 + S,0,29902,,0.6 + M,0,29902,33448,590,240 + F,0,33311,34266,1,0 + R,0,33311,34266,0,0.4 + M,0,33448,33584,590,240,590,247 + M,0,33584,33721,590,247,590,263 + M,0,33721,33857,590,263,590,288 + M,0,33857,33993,590,288,590,320 + M,0,33993,34130,590,320,590,361 + M,0,34130,34266,590,361,590,408 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-1.png",320,240 + S,0,143357,,0.6 + M,0,143357,145539,50,240 + M,0,145539,145676,50,240,50,247 + F,0,145539,146357,1,0 + R,0,145539,146357,0,0.4 + M,0,145676,145812,50,247,50,263 + M,0,145812,145948,50,263,50,290 + M,0,145948,146085,50,290,50,321 + M,0,146085,146221,50,321,50,360 + M,0,146221,146357,50,360,50,408 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-2.png",320,240 + S,0,143357,,0.6 + M,0,143357,146766,104,240 + M,0,146766,146903,104,240,104,248 + F,0,146766,147585,1,0 + R,0,146766,147585,0,-0.4 + M,0,146903,147039,104,248,104,263 + M,0,147039,147176,104,263,104,287 + M,0,147176,147312,104,287,104,320 + M,0,147312,147448,104,320,104,360 + M,0,147448,147585,104,360,104,409 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-3.png",320,240 + S,0,143357,,0.6 + M,0,143357,145948,158,240 + M,0,145948,146085,158,240,158,249 + F,0,145948,146766,1,0 + R,0,145948,146766,0,-0.4 + M,0,146085,146221,158,249,158,263 + M,0,146221,146357,158,263,158,288 + M,0,146357,146494,158,288,158,321 + M,0,146494,146630,158,321,158,360 + M,0,146630,146766,158,360,158,409 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-4.png",320,240 + S,0,143357,,0.6 + M,0,143357,146630,212,240 + M,0,146630,146766,212,240,212,248 + F,0,146630,147448,1,0 + R,0,146630,147448,0,0.4 + M,0,146766,146903,212,248,212,263 + M,0,146903,147039,212,263,212,288 + M,0,147039,147176,212,288,212,320 + M,0,147176,147312,212,320,212,360 + M,0,147312,147448,212,360,212,407 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-5.png",320,240 + S,0,143357,,0.6 + M,0,143357,146357,266,240 + M,0,146357,146494,266,240,266,248 + F,0,146357,147176,1,0 + R,0,146357,147176,0,0.4 + M,0,146494,146630,266,248,266,264 + M,0,146630,146766,266,264,266,288 + M,0,146766,146903,266,288,266,321 + M,0,146903,147039,266,321,266,358 + M,0,147039,147176,266,358,266,407 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-6.png",320,240 + S,0,143357,,0.6 + M,0,143357,145812,320,240 + M,0,145812,145948,320,240,320,247 + F,0,145812,146630,1,0 + R,0,145812,146630,0,0.4 + M,0,145948,146085,320,247,320,263 + M,0,146085,146221,320,263,320,288 + M,0,146221,146357,320,288,320,321 + M,0,146357,146494,320,321,320,360 + M,0,146494,146630,320,360,320,408 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-7.png",320,240 + S,0,143357,,0.6 + M,0,143357,146085,374,240 + M,0,146085,146221,374,240,374,248 + F,0,146085,146903,1,0 + R,0,146085,146903,0,0.4 + M,0,146221,146357,374,248,374,264 + M,0,146357,146494,374,264,374,288 + M,0,146494,146630,374,288,374,320 + M,0,146630,146766,374,320,374,359 + M,0,146766,146903,374,359,374,409 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-8.png",320,240 + S,0,143357,,0.6 + M,0,143357,145676,428,240 + M,0,145676,145812,428,240,428,249 + F,0,145676,146494,1,0 + R,0,145676,146494,0,-0.4 + M,0,145812,145948,428,249,428,263 + M,0,145948,146085,428,263,428,288 + M,0,146085,146221,428,288,428,320 + M,0,146221,146357,428,320,428,358 + M,0,146357,146494,428,358,428,408 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-9.png",320,240 + S,0,143357,,0.6 + M,0,143357,146494,482,240 + M,0,146494,146630,482,240,482,247 + F,0,146494,147312,1,0 + R,0,146494,147312,0,-0.4 + M,0,146630,146766,482,247,482,263 + M,0,146766,146903,482,263,482,287 + M,0,146903,147039,482,287,482,320 + M,0,147039,147176,482,320,482,360 + M,0,147176,147312,482,360,482,407 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-10-1.png",320,240 + S,0,143357,,0.6 + M,0,143357,146221,536,240 + M,0,146221,146357,536,240,536,247 + F,0,146221,147039,1,0 + R,0,146221,147039,0,-0.4 + M,0,146357,146494,536,247,536,263 + M,0,146494,146630,536,263,536,289 + M,0,146630,146766,536,289,536,321 + M,0,146766,146903,536,321,536,360 + M,0,146903,147039,536,360,536,407 +Sprite,Foreground,Centre,"SB\lyric\y2\introSECOND-ja-11.png",320,240 + S,0,143357,,0.6 + M,0,143357,146903,590,240 + F,0,146766,147721,1,0 + R,0,146766,147721,0,0.4 + M,0,146903,147039,590,240,590,247 + M,0,147039,147176,590,247,590,263 + M,0,147176,147312,590,263,590,288 + M,0,147312,147448,590,288,590,320 + M,0,147448,147585,590,320,590,361 + M,0,147585,147721,590,361,590,408 +Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-1.png",320,240 + S,0,147720,,0.6 + M,0,147720,150993,104,240 + M,0,150993,151130,104,240,104,248 + F,0,150993,151812,1,0 + R,0,150993,151812,0,-0.4 + M,0,151130,151266,104,248,104,263 + M,0,151266,151403,104,263,104,287 + M,0,151403,151539,104,287,104,320 + M,0,151539,151675,104,320,104,360 + M,0,151675,151812,104,360,104,409 +Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-2.png",320,240 + S,0,147720,,0.6 + M,0,147720,150175,158,240 + M,0,150175,150312,158,240,158,249 + F,0,150175,150993,1,0 + R,0,150175,150993,0,-0.4 + M,0,150312,150448,158,249,158,263 + M,0,150448,150584,158,263,158,288 + M,0,150584,150721,158,288,158,321 + M,0,150721,150857,158,321,158,360 + M,0,150857,150993,158,360,158,409 +Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-3.png",320,240 + S,0,147720,,0.6 + M,0,147720,150857,212,240 + M,0,150857,150993,212,240,212,248 + F,0,150857,151675,1,0 + R,0,150857,151675,0,0.4 + M,0,150993,151130,212,248,212,263 + M,0,151130,151266,212,263,212,288 + M,0,151266,151403,212,288,212,320 + M,0,151403,151539,212,320,212,360 + M,0,151539,151675,212,360,212,407 +Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-4.png",320,240 + S,0,147720,,0.6 + M,0,147720,150584,266,240 + M,0,150584,150721,266,240,266,248 + F,0,150584,151403,1,0 + R,0,150584,151403,0,0.4 + M,0,150721,150857,266,248,266,264 + M,0,150857,150993,266,264,266,288 + M,0,150993,151130,266,288,266,321 + M,0,151130,151266,266,321,266,358 + M,0,151266,151403,266,358,266,407 +Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-5.png",320,240 + S,0,147720,,0.6 + M,0,147720,150039,320,240 + M,0,150039,150175,320,240,320,247 + F,0,150039,150857,1,0 + R,0,150039,150857,0,0.4 + M,0,150175,150312,320,247,320,263 + M,0,150312,150448,320,263,320,288 + M,0,150448,150584,320,288,320,321 + M,0,150584,150721,320,321,320,360 + M,0,150721,150857,320,360,320,408 +Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-6.png",320,240 + S,0,147720,,0.6 + M,0,147720,150312,374,240 + M,0,150312,150448,374,240,374,248 + F,0,150312,151130,1,0 + R,0,150312,151130,0,0.4 + M,0,150448,150584,374,248,374,264 + M,0,150584,150721,374,264,374,288 + M,0,150721,150857,374,288,374,320 + M,0,150857,150993,374,320,374,359 + M,0,150993,151130,374,359,374,409 +Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-7.png",320,240 + S,0,147720,,0.6 + M,0,147720,149903,428,240 + M,0,149903,150039,428,240,428,249 + F,0,149903,150721,1,0 + R,0,149903,150721,0,-0.4 + M,0,150039,150175,428,249,428,263 + M,0,150175,150312,428,263,428,288 + M,0,150312,150448,428,288,428,320 + M,0,150448,150584,428,320,428,358 + M,0,150584,150721,428,358,428,408 +Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-8.png",320,240 + S,0,147720,,0.6 + M,0,147720,150721,482,240 + M,0,150721,150857,482,240,482,247 + F,0,150721,151539,1,0 + R,0,150721,151539,0,-0.4 + M,0,150857,150993,482,247,482,263 + M,0,150993,151130,482,263,482,287 + M,0,151130,151266,482,287,482,320 + M,0,151266,151403,482,320,482,360 + M,0,151403,151539,482,360,482,407 +Sprite,Foreground,Centre,"SB\lyric\z\outroSECOND-ja-9.png",320,240 + S,0,147720,,0.6 + M,0,147720,150448,536,240 + M,0,150448,150584,536,240,536,247 + F,0,150448,151266,1,0 + R,0,150448,151266,0,-0.4 + M,0,150584,150721,536,247,536,263 + M,0,150721,150857,536,263,536,289 + M,0,150857,150993,536,289,536,321 + M,0,150993,151130,536,321,536,360 + M,0,151130,151266,536,360,536,407 +Sprite,Foreground,Centre,"SB\lyric\y1\intro-en1.png",320,240 + S,0,25538,,0.8 + M,0,25538,27720,320,280 + F,0,27720,27993,1,0 +Sprite,Foreground,Centre,"SB\lyric\y2\intro-en2.png",320,240 + M,0,29902,,320,280 + S,0,29902,,0.8 + F,0,32084,32357,1,0 +Sprite,Foreground,Centre,"SB\lyric\z\outro-en1.png",320,240 + M,0,143357,,320,280 + S,0,143357,,0.8 + F,0,145538,145811,1,0 +Sprite,Foreground,Centre,"SB\lyric\z\outro-en2.png",320,240 + M,0,147720,,320,280 + S,0,147720,,0.8 + F,0,149902,150175,1,0 +Sprite,Foreground,Centre,"SB\lyric\ja-34-1-1.png",320,240 + S,0,141174,,0.8 + M,0,141174,143356,343,393 + F,0,142265,143356,1,0 +//Storyboard Sound Samples +//Background Colour Transformations +3,100,163,162,255 + +[TimingPoints] +2629,272.727272727273,4,2,1,30,1,0 +20083,291.26213592233,4,2,1,30,1,0 +21248,288.461538461538,4,2,1,30,1,0 +22329,-181.818181818182,4,2,1,30,0,0 +22473,-181.818181818182,4,2,1,5,0,0 +23356,272.727272727273,4,2,1,5,1,0 +23492,-100,4,2,1,10,0,0 +23628,-100,4,2,1,15,0,0 +23765,-100,4,2,1,20,0,0 +23901,-100,4,2,1,25,0,0 +24037,-100,4,2,1,30,0,0 +24174,-100,4,2,1,35,0,0 +24310,-100,4,2,1,40,0,0 +24446,-117.647058823529,4,1,1,50,0,0 +25333,-100,4,1,1,5,0,0 +25537,-100,4,1,1,50,0,0 +25810,-117.647058823529,4,2,1,50,0,0 +26492,-117.647058823529,4,1,1,50,0,0 +26765,-117.647058823529,4,2,1,50,0,0 +27719,-117.647058823529,4,1,1,50,0,0 +27992,-117.647058823529,4,2,1,50,0,0 +28674,-117.647058823529,4,1,1,50,0,0 +28878,-117.647058823529,4,2,1,5,0,0 +28946,-117.647058823529,4,2,1,50,0,0 +29901,-100,4,1,1,50,0,0 +30174,-117.647058823529,4,2,1,50,0,0 +30856,-117.647058823529,4,1,1,50,0,0 +31128,-117.647058823529,4,2,1,50,0,0 +32083,-117.647058823529,4,1,1,50,0,0 +32356,-117.647058823529,4,2,1,50,0,0 +33242,-117.647058823529,4,1,1,5,0,0 +33310,-117.647058823529,4,1,1,30,0,0 +34265,-100,4,1,1,60,0,0 +42583,-117.647058823529,4,1,1,40,0,0 +42992,-100,4,1,1,60,0,0 +51719,-133.333333333333,4,2,1,60,0,0 +53492,-117.647058823529,4,2,1,80,0,0 +53628,-200,4,2,1,60,0,0 +56015,-200,4,2,1,5,0,0 +56083,-200,4,2,1,60,0,0 +60992,-200,4,2,1,60,0,0 +61060,-200,4,1,1,20,0,0 +61128,-200,4,1,1,60,0,0 +61196,-200,4,1,1,20,0,0 +61265,-200,4,1,1,60,0,0 +61401,-200,4,1,1,20,0,0 +61537,-200,4,1,1,60,0,0 +61605,-200,4,1,1,20,0,0 +61946,-200,4,1,1,60,0,0 +62015,-200,4,1,1,20,0,0 +62355,-200,4,1,1,60,0,0 +62628,-200,4,2,1,60,0,0 +70265,-100,4,1,1,60,0,0 +71356,-90.9090909090909,4,1,1,60,0,1 +78992,-90.9090909090909,4,1,1,40,0,0 +80083,-90.9090909090909,4,1,1,60,0,1 +86628,-117.647058823529,4,1,1,60,0,0 +87719,-117.647058823529,4,1,1,60,0,0 +88810,-100,4,1,1,60,0,0 +97537,-133.333333333333,4,2,1,60,0,0 +99310,-100,4,2,1,80,0,0 +99446,-200,4,2,1,60,0,0 +101833,-200,4,2,1,5,0,0 +101901,-200,4,2,1,60,0,0 +106810,-200,4,2,1,60,0,0 +106878,-200,4,2,1,20,0,0 +106878,-200,4,2,1,60,0,0 +106946,-200,4,1,1,60,0,0 +107015,-200,4,1,1,20,0,0 +107083,-200,4,1,1,60,0,0 +107219,-200,4,1,1,20,0,0 +107355,-200,4,1,1,60,0,0 +107424,-200,4,1,1,20,0,0 +107765,-200,4,1,1,60,0,0 +107833,-200,4,1,1,20,0,0 +108174,-200,4,1,1,60,0,0 +108446,-200,4,2,1,60,0,0 +116083,-200,4,1,1,60,0,0 +117174,-133.333333333333,4,1,1,60,0,1 +123719,-200,4,1,1,20,0,0 +124674,-200,4,1,1,60,0,0 +126174,-90.9090909090909,4,1,1,60,0,1 +127810,-200,4,1,1,60,0,0 +128356,-100,4,1,1,60,0,1 +129992,-200,4,2,1,60,0,0 +130265,-100,4,1,1,60,0,1 +133537,-100,4,1,1,60,0,0 +134628,-100,4,1,1,60,0,1 +141174,-100,4,1,1,60,0,0 +142265,-117.647058823529,4,1,1,60,0,1 +143287,-117.647058823529,4,1,1,5,0,1 +143355,-117.647058823529,4,1,1,5,0,0 +143356,-133.333333333333,4,2,1,60,0,0 +147651,-133.333333333333,4,2,1,5,0,0 +147719,-133.333333333333,4,2,1,60,0,0 +152083,-100,4,1,1,60,0,0 +160810,-83.3333333333333,4,1,1,60,0,1 +162992,-76.9230769230769,4,1,1,60,0,1 +165174,-71.4285714285714,4,1,1,60,0,1 +167356,-117.647058823529,4,1,1,60,0,0 +168446,-117.647058823529,4,1,1,60,0,1 +169537,-117.647058823529,4,2,1,60,0,0 +171310,-117.647058823529,4,2,1,80,0,0 +171446,-117.647058823529,4,2,1,60,0,0 + + +[Colours] +Combo1 : 255,255,255 +Combo2 : 72,255,72 +Combo3 : 255,128,0 +Combo4 : 255,32,32 +Combo5 : 0,128,255 +Combo6 : 255,85,255 +Combo7 : 0,183,0 +Combo8 : 176,112,77 + +[HitObjects] +154,247,22401,6,0,P|56:170|169:171,1,313.500009567261 +169,170,23356,53,0,1:0:0:0: +192,92,23492,1,0,1:0:0:0: +252,40,23629,1,0,1:0:0:0: +332,28,23765,1,0,1:0:0:0: +404,60,23901,1,0,1:0:0:0: +452,124,24038,1,0,1:0:0:0: +456,204,24174,1,0,1:0:0:0: +424,276,24310,1,0,1:0:0:0: +356,316,24447,54,0,P|316:312|272:312,1,80.750001540184,0|0,0:0|2:0,0:0:0:0: +275,311,24651,1,0,2:0:0:0: +275,311,24719,1,2,2:0:0:0: +168,344,24856,1,4,3:0:0:0: +92,264,24992,1,0,0:0:0:0: +96,249,25060,1,0,2:0:0:0: +99,235,25129,1,0,2:0:0:0: +103,221,25197,1,0,2:0:0:0: +106,207,25265,54,0,P|72:144|92:104,1,121.125002310276 +108,208,25538,54,0,P|153:206|208:204,1,95 +260,88,25810,2,0,L|176:84,1,80.750001540184 +304,220,26083,1,2,0:0:0:0: +400,212,26219,1,0,0:0:0:0: +344,132,26356,1,0,0:0:0:0: +356,300,26492,54,0,P|300:360|212:348,1,161.500003080368,0|0,3:0|0:0,0:0:0:0: +200,216,26901,1,0,0:0:0:0: +200,216,27038,1,0,0:0:0:0: +108,328,27174,1,2,0:0:0:0: +108,328,27310,1,0,0:0:0:0: +28,208,27447,1,2,0:0:0:0: +112,244,27583,1,4,3:0:0:0: +112,244,27651,1,0,0:0:0:0: +112,244,27719,54,0,B|104:176|88:116|88:116|104:124|120:128,1,161.500003080368 +208,164,28129,2,0,P|248:159|284:136,1,80.750001540184,0|2,0:0|0:0,0:0:0:0: +212,72,28401,1,0,0:0:0:0: +280,240,28538,1,0,0:0:0:0: +388,140,28674,54,0,P|392:200|372:264,1,121.125002310276,0|0,3:0|0:0,0:0:0:0: +375,257,28947,2,0,P|336:252|288:252,1,80.750001540184,0|2,0:0|0:0,0:0:0:0: +456,308,29219,1,0,0:0:0:0: +376,372,29356,1,2,0:0:0:0: +376,372,29492,1,0,0:0:0:0: +376,372,29629,1,2,0:0:0:0: +376,372,29765,1,4,3:0:0:0: +376,372,29901,54,0,P|331:371|280:368,1,95 +224,248,30174,2,0,P|264:247|312:244,1,80.750001540184 +164,352,30447,1,2,0:0:0:0: +68,360,30583,1,0,0:0:0:0: +108,272,30719,1,0,0:0:0:0: +40,156,30856,54,0,P|44:80|120:36,1,161.500003080368,0|0,3:0|0:0,0:0:0:0: +136,132,31265,1,0,0:0:0:0: +136,132,31401,1,0,0:0:0:0: +256,48,31538,1,2,0:0:0:0: +256,48,31674,1,0,0:0:0:0: +376,132,31810,1,2,0:0:0:0: +312,224,31947,1,4,3:0:0:0: +312,224,32015,1,0,0:0:0:0: +312,224,32083,54,0,B|272:288|312:352|312:352|280:344,1,161.500003080368 +196,324,32492,1,0,0:0:0:0: +312,352,32629,2,0,P|348:340|396:340,1,80.750001540184,2|0,0:0|0:0,0:0:0:0: +484,368,32901,1,0,0:0:0:0: +448,229,33038,54,0,B|430:168|430:168|448:149|456:113,1,121.125002310276,2|0,0:0|0:0,0:0:0:0: +455,116,33310,2,0,L|375:132,1,80.750001540184 +208,44,33583,2,0,L|290:58,1,80.750001540184,2|0,2:0|0:0,0:0:0:0: +295,65,34265,54,0,L|199:47,1,95,4|0,0:0|0:0,0:0:0:0: +76,88,34538,1,8,0:0:0:0: +116,216,34674,1,2,2:0:0:0: +116,216,34742,1,2,2:0:0:0: +116,216,34810,2,0,P|156:244|208:240,1,95,2|0,2:0|0:0,0:0:0:0: +360,152,35083,2,0,P|311:147|268:177,1,95,8|0,0:0|0:0,0:0:0:0: +336,244,35356,54,0,L|384:60,1,190,2|8,2:0|0:0,0:0:0:0: +436,228,35765,1,2,2:0:0:0: +287,76,35901,1,2,2:0:0:0: +462,128,36038,1,2,2:0:0:0: +260,176,36174,1,8,0:0:0:0: +384,60,36310,1,2,2:0:0:0: +360,152,36447,54,0,B|312:224|348:312|348:312|320:300,1,190,4|8,0:0|0:0,0:0:0:0: +204,252,36856,1,2,2:0:0:0: +204,252,36924,1,2,2:0:0:0: +204,252,36992,2,0,P|160:236|104:244,1,95 +232,356,37265,1,8,0:0:0:0: +176,152,37401,1,0,0:0:0:0: +48,332,37538,54,0,L|144:324,1,95,8|2,0:0|2:0,0:0:0:0: +300,244,37810,2,0,L|204:252,1,95,8|2,0:0|2:0,0:0:0:0: +40,320,38083,2,0,L|136:312,1,95,0|2,0:0|2:0,0:0:0:0: +312,252,38356,2,0,P|340:184|308:124,1,142.5,8|0,0:0|0:0,0:0:0:0: +296,120,38629,54,0,P|340:132|390:129,1,95,4|0,0:0|0:0,0:0:0:0: +264,44,38901,2,0,P|224:72|212:116,1,95,8|0,0:0|0:0,0:0:0:0: +252,192,39174,1,2,2:0:0:0: +96,120,39310,1,0,0:0:0:0: +144,284,39447,1,8,0:0:0:0: +256,304,39583,1,2,2:0:0:0: +256,304,39651,1,2,2:0:0:0: +256,304,39719,54,0,B|352:296|420:292|420:292|404:260,1,190,0|8,0:0|0:0,0:0:0:0: +360,196,40129,1,0,0:0:0:0: +320,376,40265,2,0,L|424:371,1,95,2|2,2:0|2:0,0:0:0:0: +456,192,40538,2,0,L|352:197,1,95,8|0,0:0|0:0,0:0:0:0: +416,272,40810,54,0,P|360:192|400:116,1,190,4|8,0:0|0:0,0:0:0:0: +500,84,41219,1,0,0:0:0:0: +380,28,41356,2,0,P|332:20|284:40,1,95,2|0,2:0|0:0,0:0:0:0: +248,164,41629,1,8,0:0:0:0: +120,184,41765,1,2,2:0:0:0: +164,60,41901,54,0,L|68:56,1,95,8|2,0:0|2:0,0:0:0:0: +25,180,42174,2,0,L|121:184,1,95,8|2,0:0|2:0,0:0:0:0: +164,308,42447,1,8,0:0:0:0: +419,236,42583,1,0,0:0:0:0: +398,233,42651,1,0,0:0:0:0: +377,231,42719,1,8,0:0:0:0: +356,230,42788,1,0,0:0:0:0: +335,231,42856,1,8,0:0:0:0: +314,233,42924,1,8,0:0:0:0: +294,237,42992,54,0,P|257:209|249:161,1,95,4|0,0:0|0:0,0:0:0:0: +296,40,43265,1,8,0:0:0:0: +328,168,43401,1,2,2:0:0:0: +328,168,43469,1,2,2:0:0:0: +328,168,43538,2,0,P|376:176|424:152,1,95,2|0,2:0|0:0,0:0:0:0: +480,64,43810,1,8,0:0:0:0: +492,180,43947,1,0,0:0:0:0: +344,320,44083,54,0,P|256:328|197:233,1,190,2|8,2:0|0:0,0:0:0:0: +294,236,44492,1,0,0:0:0:0: +232,160,44629,53,2,2:0:0:0: +244,28,44765,1,2,2:0:0:0: +112,16,44901,1,8,0:0:0:0: +96,148,45038,1,2,2:0:0:0: +96,148,45106,1,0,0:0:0:0: +96,148,45174,54,0,B|72:216|112:268|112:268|88:264,1,142.5,4|0,0:0|0:0,0:0:0:0: +84,264,45447,2,0,P|36:268|0:304,1,95,8|0,0:0|0:0,0:0:0:0: +72,344,45719,2,0,L|272:360,1,190,2|8,2:0|0:0,0:0:0:0: +261,359,46129,1,0,0:0:0:0: +444,296,46265,54,0,P|472:260|472:200,1,95,8|2,0:0|2:0,0:0:0:0: +408,48,46538,2,0,P|397:92|423:146,1,95,8|2,0:0|2:0,0:0:0:0: +384,224,46810,1,2,2:0:0:0: +384,224,46947,2,0,L|288:216,1,95,8|8,0:0|0:0,0:0:0:0: +320,80,47219,1,2,2:0:0:0: +320,80,47288,1,2,2:0:0:0: +320,80,47356,54,0,B|248:68|184:100|184:100|161:85|120:80,1,190,4|8,0:0|0:0,0:0:0:0: +16,144,47765,1,0,0:0:0:0: +4,284,47901,2,0,P|32:323|84:344,1,95,2|0,2:0|0:0,0:0:0:0: +160,368,48174,1,8,0:0:0:0: +268,348,48310,1,0,0:0:0:0: +160,368,48447,54,0,P|206:365|256:364,1,95,2|0,2:0|0:0,0:0:0:0: +440,308,48719,2,0,P|393:305|344:304,1,95,8|0,0:0|0:0,0:0:0:0: +209,229,48992,1,2,2:0:0:0: +232,224,49060,1,0,2:0:0:0: +253,213,49129,1,2,2:0:0:0: +270,196,49197,1,0,2:0:0:0: +282,175,49265,1,8,0:0:0:0: +287,151,49333,1,0,2:0:0:0: +286,127,49401,1,2,2:0:0:0: +279,104,49469,1,2,2:0:0:0: +266,83,49538,54,0,P|312:97|364:86,1,95,4|0,0:0|0:0,0:0:0:0: +195,142,49810,2,0,P|146:137|99:161,1,95,8|0,0:0|0:0,0:0:0:0: +84,288,50083,2,0,P|188:304|264:256,1,190,2|8,2:0|0:0,0:0:0:0: +160,224,50492,1,0,0:0:0:0: +195,142,50629,53,8,0:0:0:0: +172,60,50765,1,2,2:0:0:0: +272,120,50901,53,8,0:0:0:0: +293,37,51038,1,2,2:0:0:0: +348,132,51174,53,8,0:0:0:0: +407,70,51310,1,2,2:0:0:0: +414,174,51447,53,8,0:0:0:0: +416,179,51515,1,8,0:0:0:0: +419,184,51583,1,8,0:0:0:0: +421,189,51651,1,8,0:0:0:0: +424,195,51719,38,0,P|432:231|432:267,1,71.2500027179719,0|0,1:0|0:0,0:0:0:0: +412,356,51992,2,0,P|378:353|336:352,1,71.2500027179719,0|0,1:0|0:0,0:0:0:0: +180,296,52265,1,0,1:0:0:0: +180,296,52401,1,0,1:0:0:0: +180,296,52538,2,0,P|217:294|252:293,1,71.2500027179719,0|0,1:0|0:0,0:0:0:0: +316,216,52810,101,4,3:0:0:0: +280,124,52947,1,0,0:0:0:0: +280,124,53015,1,0,0:0:0:0: +280,124,53083,1,2,0:0:0:0: +212,196,53219,5,4,3:0:0:0: +176,104,53356,1,0,0:0:0:0: +176,104,53424,1,0,0:0:0:0: +176,104,53492,1,2,0:0:0:0: +116,176,53629,1,0,1:0:0:0: +168,308,53765,5,2,0:0:0:0: +168,308,53901,2,0,P|232:300|312:300,1,142.5,4|0,3:0|0:0,0:0:0:0: +368,348,54447,2,0,P|384:312|372:256,1,95,0|0,1:0|1:0,0:0:0:0: +320,204,54856,1,0,0:0:0:0: +448,240,54992,54,0,L|464:184,1,47.5,4|4,3:0|3:0,3:0:0:0: +392,152,55265,2,0,L|388:100,1,47.5,0|4,1:0|3:0,0:0:0:0: +312,80,55538,2,0,L|284:32,1,47.5,0|4,0:0|3:0,0:0:0:0: +228,116,55810,2,0,P|188:112|136:112,1,71.25,0|0,1:0|0:0,0:0:0:0: +156,111,56083,54,0,P|152:152|100:192,1,95,4|0,3:0|0:0,0:0:0:0: +36,184,56492,1,0,0:0:0:0: +72,252,56629,1,0,1:0:0:0: +80,120,56765,1,4,3:0:0:0: +52,48,56901,53,0,1:0:0:0: +114,188,57038,2,0,P|184:184|256:184,1,142.5,0|2,0:0|0:0,0:0:0:0: +352,284,57583,1,0,0:0:0:0: +352,284,57719,1,2,0:0:0:0: +352,284,57856,1,0,0:0:0:0: +352,284,57992,2,0,L|336:344,1,47.5,2|2,0:0|0:0,0:0:0:0: +352,284,58265,54,0,P|288:277|208:276,1,142.5,4|4,3:0|3:0,0:0:0:0: +209,275,58810,2,0,P|180:240|180:184,1,95,0|0,1:0|1:0,0:0:0:0: +204,120,59219,1,0,1:0:0:0: +252,212,59356,54,0,L|304:196,1,47.5,4|0,3:0|0:0,0:0:0:0: +456,76,59629,2,0,L|404:92,1,47.5,0|4,1:0|3:0,0:0:0:0: +260,220,59901,2,0,L|312:204,1,47.5,0|4,0:0|3:0,0:0:0:0: +464,84,60174,2,0,L|412:100,1,47.5,0|0,1:0|0:0,0:0:0:0: +364,152,60447,54,0,P|320:128|268:136,1,95,4|2,3:0|0:0,0:0:0:0: +304,208,60856,1,0,0:0:0:0: +432,289,60992,1,0,1:0:0:0: +415,287,61060,1,0,0:0:0:0: +398,286,61129,1,4,3:0:0:0: +381,285,61197,1,0,0:0:0:0: +364,286,61265,1,0,1:0:0:0: +263,356,61401,1,0,0:0:0:0: +263,356,61469,1,0,0:0:0:0: +263,356,61538,53,0,0:0:0:0: +246,352,61606,1,0,0:0:0:0: +229,349,61674,1,0,0:0:0:0: +212,348,61742,1,0,0:0:0:0: +195,348,61810,1,0,0:0:0:0: +60,362,61947,1,0,0:0:0:0: +48,343,62015,1,0,0:0:0:0: +39,323,62083,1,0,0:0:0:0: +34,302,62151,1,0,0:0:0:0: +34,280,62219,1,0,0:0:0:0: +38,259,62288,1,0,0:0:0:0: +46,239,62356,1,0,0:0:0:0: +46,239,62629,54,0,B|97:232|157:220|157:220|183:242,1,142.5,4|4,3:0|3:0,0:0:0:0: +92,156,63174,2,0,L|189:138,1,95,0|0,1:0|1:0,0:0:0:0: +256,100,63583,1,0,1:0:0:0: +256,176,63719,53,4,3:0:0:0: +256,176,63856,1,4,3:0:0:0: +256,24,63992,1,0,1:0:0:0: +326,138,64129,2,0,L|420:156,1,95,4|4,3:0|3:0,0:0:0:0: +476,208,64538,2,0,P|492:256|476:304,1,95,0|0,1:0|1:0,0:0:0:0: +480,297,65083,1,0,0:0:0:0: +328,224,65219,1,0,0:0:0:0: +340,304,65356,53,0,1:0:0:0: +208,324,65492,1,4,3:0:0:0: +340,304,65629,2,0,P|288:296|260:264,1,95,0|4,1:0|3:0,1:0:0:0: +260,188,66038,1,0,0:0:0:0: +192,224,66174,2,0,L|136:216,1,47.5,0|4,1:0|3:0,0:0:0:0: +24,196,66447,53,2,0:0:0:0: +24,196,66583,1,4,3:0:0:0: +24,196,66719,1,0,1:0:0:0: +24,196,66856,2,0,P|0:152|8:104,1,95,4|0,3:0|0:0,0:0:0:0: +76,140,67265,2,0,P|122:137|172:132,1,95,2|0,0:0|1:0,0:0:0:0: +332,172,67674,1,4,3:0:0:0: +332,172,67810,2,0,L|284:176|236:180,1,95,0|0,1:0|0:0,0:0:0:0: +128,240,68219,1,0,0:0:0:0: +232,276,68356,1,0,1:0:0:0: +120,332,68492,1,4,3:0:0:0: +224,372,68629,54,0,P|268:376|320:356,1,95,4|0,3:0|1:0,0:0:0:0: +360,300,69038,1,4,3:0:0:0: +432,276,69174,2,0,P|452:232|440:180,1,95,2|0,0:0|1:0,0:0:0:0: +344,44,69583,2,0,P|340:92|372:140,1,95,4|0,3:0|0:0,0:0:0:0: +448,88,69992,1,0,1:0:0:0: +448,88,70265,21,0,0:0:0:0: +419,356,70401,1,0,0:0:0:0: +151,324,70538,1,0,0:0:0:0: +180,56,70674,1,0,0:0:0:0: +180,56,71219,37,0,0:0:0:0: +256,300,71356,2,0,L|224:192,1,104.500003189087,4|0,0:0|0:0,0:0:0:0: +202,130,71629,1,8,0:0:0:0: +252,24,71765,53,0,0:0:0:0: +151,324,71901,2,0,L|192:208,1,104.500003189087 +220,124,72174,1,8,0:0:0:0: +352,56,72310,53,0,0:0:0:0: +20,216,72447,2,0,L|124:172,1,104.500003189087 +236,116,72719,1,8,0:0:0:0: +350,191,72856,53,0,0:0:0:0: +468,120,72992,1,0,0:0:0:0: +472,256,73129,1,0,0:0:0:0: +388,120,73265,1,8,0:0:0:0: +511,187,73401,1,0,0:0:0:0: +392,256,73538,54,0,P|284:228|288:144,1,209.000006378174,4|8,0:0|0:0,0:0:0:0: +168,264,73947,1,0,0:0:0:0: +350,191,74083,2,0,P|297:189|236:188,1,104.500003189087 +468,120,74356,2,0,P|415:118|354:117,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: +172,168,74629,54,0,P|184:236|264:260,1,156.750004783631 +172,168,74901,2,0,P|119:164|60:164,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: +20,236,75174,53,0,0:0:0:0: +80,56,75310,1,0,0:0:0:0: +248,132,75447,2,0,P|298:125|352:124,1,104.500003189087,8|8,0:0|0:0,0:0:0:0: +351,123,75651,1,8,0:0:0:0: +351,123,75719,54,0,B|348:186|340:240|340:240|352:251|364:276,1,156.750004783631,12|0,0:0|0:0,0:0:0:0: +372,288,75993,2,0,L|384:180,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: +280,316,76265,2,0,P|232:340|168:328,1,104.500003189087 +220,260,76538,1,8,0:0:0:0: +344,368,76674,53,0,0:0:0:0: +396,176,76810,2,0,L|384:284,1,104.500003189087 +456,348,77083,1,8,0:0:0:0: +308,200,77219,53,0,0:0:0:0: +472,252,77356,1,0,0:0:0:0: +452,116,77492,1,0,0:0:0:0: +352,24,77629,1,8,0:0:0:0: +216,28,77765,1,0,0:0:0:0: +112,116,77901,54,0,B|76:216|144:288|144:288|148:256,1,209.000006378174,4|8,0:0|0:0,0:0:0:0: +196,80,78310,1,0,0:0:0:0: +372,200,78447,2,0,B|332:164|284:148|284:148|224:156|180:184,1,209.000006378174,0|0,0:0|0:0,0:0:0:0: +36,268,78856,1,0,0:0:0:0: +92,368,78992,53,8,0:0:0:0: +110,362,79060,1,0,0:0:0:0: +128,357,79129,1,0,0:0:0:0: +146,353,79197,1,0,0:0:0:0: +165,350,79265,1,0,0:0:0:0: +183,348,79333,1,0,0:0:0:0: +201,346,79401,1,8,0:0:0:0: +219,345,79469,1,0,0:0:0:0: +237,345,79538,1,0,0:0:0:0: +208,236,79674,1,0,0:0:0:0: +370,255,79810,1,8,0:0:0:0: +386,262,79879,1,0,0:0:0:0: +402,268,79947,1,0,0:0:0:0: +417,278,80015,1,0,0:0:0:0: +431,287,80083,6,0,P|408:243|424:184,1,104.500003189087,4|0,0:0|0:0,0:0:0:0: +486,234,80356,1,8,0:0:0:0: +374,350,80492,1,0,0:0:0:0: +286,215,80629,54,0,P|288:159|324:117,1,104.500003189087 +368,192,80901,1,8,0:0:0:0: +182,243,81038,1,0,0:0:0:0: +152,76,81174,54,0,P|168:28|221:-2,1,104.500003189087 +262,103,81447,1,8,0:0:0:0: +105,201,81583,1,0,0:0:0:0: +56,116,81719,53,0,0:0:0:0: +20,248,81856,1,0,0:0:0:0: +152,280,81992,1,8,0:0:0:0: +192,152,82129,1,0,0:0:0:0: +20,248,82265,54,0,P|8:320|64:380,1,156.750004783631 +60,378,82538,2,0,P|114:374|168:372,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: +260,336,82810,2,0,P|280:284|268:232,1,104.500003189087 +260,336,83083,1,8,0:0:0:0: +192,152,83219,1,0,0:0:0:0: +105,201,83356,54,0,P|54:203|-7:209,1,104.500003189087 +56,52,83629,2,0,P|107:54|168:60,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: +312,216,83901,2,0,P|368:184|376:96,1,156.750004783631 +360,88,84174,2,0,P|416:92|472:76,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: +380,284,84447,54,0,P|312:216|200:216,1,209.000006378174,4|8,0:0|0:0,0:0:0:0: +256,340,84856,1,0,0:0:0:0: +164,364,84992,2,0,P|100:356|64:328,1,104.500003189087 +16,208,85265,1,8,0:0:0:0: +140,260,85401,53,0,0:0:0:0: +92,132,85538,2,0,P|120:88|172:64,1,104.500003189087 +192,168,85810,1,8,0:0:0:0: +192,168,85947,1,0,0:0:0:0: +280,64,86083,53,0,0:0:0:0: +404,120,86219,1,0,0:0:0:0: +392,256,86356,1,8,0:0:0:0: +260,284,86492,1,0,0:0:0:0: +304,176,86629,53,0,0:0:0:0: +288,172,87174,53,0,0:0:0:0: +272,168,87719,69,8,0:0:0:0: +257,174,87788,1,0,0:0:0:0: +241,178,87856,2,0,P|202:170|160:168,1,80.750001540184 +136,284,88129,1,8,0:0:0:0: +151,290,88197,1,0,0:0:0:0: +167,294,88265,2,0,P|206:286|248:284,1,80.750001540184 +57,253,88538,1,8,0:0:0:0: +57,253,88810,6,0,P|80:300|72:348,1,95,4|0,0:0|0:0,0:0:0:0: +0,312,89083,1,8,0:0:0:0: +148,344,89219,1,2,2:0:0:0: +96,176,89356,2,0,P|48:168|0:192,1,95,2|0,2:0|0:0,0:0:0:0: +160,232,89629,1,8,0:0:0:0: +148,344,89765,1,0,0:0:0:0: +176,144,89901,54,0,P|264:136|323:231,1,190,2|8,2:0|0:0,0:0:0:0: +336,292,90310,1,0,0:0:0:0: +252,260,90447,53,2,2:0:0:0: +408,236,90583,1,2,2:0:0:0: +244,272,90719,1,8,0:0:0:0: +420,224,90856,1,2,2:0:0:0: +420,224,90924,1,0,0:0:0:0: +420,224,90992,2,0,B|452:152|400:88,1,142.5,4|0,0:0|0:0,0:0:0:0: +401,90,91265,2,0,P|359:95|296:100,1,95,8|0,0:0|0:0,0:0:0:0: +504,64,91538,2,0,L|304:20,1,190,2|8,2:0|0:0,0:0:0:0: +220,80,91947,1,0,0:0:0:0: +60,48,92083,54,0,P|32:84|32:144,1,95,8|2,0:0|2:0,0:0:0:0: +112,292,92356,2,0,P|123:248|97:194,1,95,8|2,0:0|2:0,0:0:0:0: +32,256,92629,1,8,0:0:0:0: +112,116,92765,1,2,2:0:0:0: +208,236,92901,1,8,0:0:0:0: +192,332,93038,1,2,2:0:0:0: +192,332,93106,1,2,2:0:0:0: +192,332,93174,54,0,B|264:344|328:312|328:312|351:327|392:332,1,190,12|8,0:0|0:0,0:0:0:0: +508,268,93583,1,0,0:0:0:0: +384,184,93719,2,0,P|348:152|336:104,1,95,2|0,2:0|0:0,0:0:0:0: +352,16,93992,1,8,0:0:0:0: +244,36,94129,1,0,0:0:0:0: +352,16,94265,54,0,P|306:19|256:20,1,95,2|0,2:0|0:0,0:0:0:0: +228,184,94538,2,0,P|275:187|324:188,1,95,8|0,0:0|0:0,0:0:0:0: +292,104,94810,53,2,2:0:0:0: +144,116,94947,1,2,2:0:0:0: +217,80,95083,53,8,0:0:0:0: +120,192,95219,1,2,2:0:0:0: +156,264,95356,54,0,P|110:250|58:261,1,95,4|0,0:0|0:0,0:0:0:0: +124,352,95629,2,0,P|173:357|220:333,1,95,8|0,0:0|0:0,0:0:0:0: +412,228,95901,2,0,P|308:212|232:260,1,190,2|8,2:0|0:0,0:0:0:0: +340,308,96310,1,0,0:0:0:0: +389,146,96447,37,8,0:0:0:0: +412,228,96583,1,2,2:0:0:0: +300,124,96719,101,8,0:0:0:0: +323,206,96856,1,2,2:0:0:0: +212,104,96992,37,8,0:0:0:0: +235,186,97129,1,2,2:0:0:0: +148,304,97265,53,8,0:0:0:0: +148,304,97333,1,8,0:0:0:0: +148,304,97401,1,8,0:0:0:0: +148,304,97469,1,8,0:0:0:0: +148,304,97538,86,0,P|112:296|72:288,1,71.2500027179719,0|0,1:0|0:0,0:0:0:0: +28,184,97810,2,0,P|65:177|107:173,1,71.2500027179719,0|0,1:0|0:0,1:0:0:0: +232,152,98083,1,0,1:0:0:0: +232,152,98219,1,0,1:0:0:0: +232,152,98356,2,0,P|222:185|217:223,1,71.2500027179719,0|0,1:0|0:0,0:0:0:0: +248,352,98629,101,4,3:0:0:0: +328,324,98765,1,0,0:0:0:0: +328,324,98833,1,0,0:0:0:0: +328,324,98901,1,2,0:0:0:0: +476,288,99038,5,4,3:0:0:0: +392,268,99174,1,0,0:0:0:0: +392,268,99242,1,0,0:0:0:0: +392,268,99310,1,2,0:0:0:0: +448,212,99447,1,0,1:0:0:0: +264,272,99583,5,2,0:0:0:0: +264,272,99719,2,0,P|256:208|312:160,1,142.5,4|0,3:0|0:0,0:0:0:0: +392,156,100265,2,0,P|381:118|335:82,1,95,0|0,1:0|1:0,0:0:0:0: +264,96,100674,1,0,0:0:0:0: +192,72,100810,54,0,L|136:56,1,47.5,4|0,3:0|0:0,0:0:0:0: +32,96,101083,2,0,L|88:112,1,47.5,0|4,1:0|3:0,0:0:0:0: +168,192,101356,2,0,L|112:176,1,47.5,0|4,0:0|3:0,0:0:0:0: +60,228,101629,2,0,P|56:268|56:320,1,71.25,0|0,1:0|0:0,0:0:0:0: +55,299,101901,54,0,P|100:280|144:288,1,95,4|2,3:0|0:0,0:0:0:0: +108,356,102310,1,0,0:0:0:0: +184,356,102447,1,0,1:0:0:0: +188,224,102583,1,4,3:0:0:0: +224,288,102719,53,0,1:0:0:0: +117,193,102856,2,0,P|173:149|245:173,1,142.5,4|8,3:0|1:0,0:0:0:0: +308,304,103401,1,4,3:0:0:0: +308,304,103538,1,0,1:0:0:0: +308,304,103674,1,4,3:0:0:0: +308,304,103810,2,0,L|324:244,1,47.5,0|0,1:0|0:0,0:0:0:0: +308,304,104083,54,0,P|340:352|424:336,1,142.5,4|4,3:0|3:0,0:0:0:0: +456,280,104629,2,0,P|452:231|448:180,1,95,0|0,1:0|1:0,0:0:0:0: +440,108,105038,1,2,0:0:0:0: +340,44,105174,54,0,L|288:60,1,47.5,4|0,3:0|0:0,0:0:0:0: +172,32,105447,2,0,L|188:84,1,47.5,0|4,1:0|3:0,0:0:0:0: +164,192,105719,2,0,L|216:176,1,47.5,0|4,0:0|3:0,0:0:0:0: +324,208,105992,2,0,L|308:156,1,47.5,0|0,1:0|0:0,0:0:0:0: +252,112,106265,54,0,P|208:88|156:96,1,95,4|2,3:0|0:0,0:0:0:0: +192,164,106674,1,0,0:0:0:0: +48,240,106810,1,0,1:0:0:0: +65,242,106879,1,0,0:0:0:0: +82,243,106947,1,4,3:0:0:0: +99,244,107015,1,0,0:0:0:0: +116,243,107083,1,0,1:0:0:0: +180,324,107219,1,0,0:0:0:0: +180,324,107288,1,0,0:0:0:0: +180,324,107356,53,0,0:0:0:0: +197,320,107424,1,0,0:0:0:0: +214,317,107492,1,0,0:0:0:0: +231,316,107560,1,0,0:0:0:0: +248,316,107629,1,0,0:0:0:0: +364,367,107765,1,0,0:0:0:0: +372,347,107833,1,0,0:0:0:0: +376,326,107901,1,0,0:0:0:0: +376,304,107969,1,0,0:0:0:0: +371,283,108038,1,0,0:0:0:0: +362,263,108106,1,0,0:0:0:0: +350,244,108174,1,0,0:0:0:0: +350,244,108447,54,0,B|361:178|372:120|372:120|344:132,1,142.5,4|4,3:0|3:0,0:0:0:0: +268,256,108992,2,0,L|286:157,1,95,0|0,1:0|1:0,0:0:0:0: +256,92,109401,1,4,3:0:0:0: +208,152,109538,53,4,3:0:0:0: +208,152,109674,1,0,0:0:0:0: +88,76,109810,1,0,1:0:0:0: +84,216,109947,2,0,P|36:212|-4:180,1,95,4|4,3:0|3:0,0:0:0:0: +64,144,110356,1,0,1:0:0:0: +24,320,110629,54,0,P|72:321|124:324,1,95,4|2,3:0|0:0,0:0:0:0: +196,348,111038,1,0,0:0:0:0: +184,272,111174,53,0,1:0:0:0: +316,252,111310,1,4,3:0:0:0: +184,272,111447,2,0,P|236:280|264:312,1,95,0|4,1:0|3:0,0:0:0:0: +292,380,111856,1,0,0:0:0:0: +344,324,111992,2,0,L|400:332,1,47.5,0|4,1:0|3:0,0:0:0:0: +488,248,112265,53,2,0:0:0:0: +488,248,112401,1,4,3:0:0:0: +488,248,112538,1,0,1:0:0:0: +488,248,112674,2,0,P|512:204|504:156,1,95,4|0,3:0|0:0,0:0:0:0: +456,104,113083,2,0,P|410:107|360:112,1,95,2|0,0:0|1:0,0:0:0:0: +308,232,113492,1,4,3:0:0:0: +308,232,113629,2,0,P|268:204|212:212,1,95,0|4,1:0|3:0,0:0:0:0: +248,280,114038,1,0,0:0:0:0: +128,348,114174,1,0,1:0:0:0: +148,220,114310,1,4,3:0:0:0: +76,184,114447,54,0,P|108:150|163:141,1,95,4|0,3:0|1:0,0:0:0:0: +228,164,114856,1,0,0:0:0:0: +288,116,114992,2,0,P|336:104|384:112,1,95,4|0,3:0|1:0,0:0:0:0: +468,244,115401,2,0,P|468:196|440:148,1,95,4|0,3:0|0:0,0:0:0:0: +376,188,115810,1,0,1:0:0:0: +376,188,116083,85,0,0:0:0:0: +248,324,116219,1,0,0:0:0:0: +164,156,116356,1,0,0:0:0:0: +288,16,116492,1,0,0:0:0:0: +288,16,117038,53,0,0:0:0:0: +196,56,117174,2,0,P|124:48|48:48,1,142.500005435944,4|0,0:0|0:0,0:0:0:0: +16,140,117583,1,0,0:0:0:0: +72,232,117719,2,0,P|116:256|180:248,1,106.875004076958,8|0,0:0|0:0,0:0:0:0: +184,248,117992,2,0,P|221:249|256:264,1,71.2500027179719,0|0,0:0|0:0,0:0:0:0: +300,340,118265,6,0,P|372:356|440:320,1,142.500005435944 +448,240,118674,1,0,0:0:0:0: +408,160,118810,101,8,0:0:0:0: +464,92,118947,1,0,0:0:0:0: +324,144,119083,5,0,0:0:0:0: +408,160,119219,1,0,0:0:0:0: +256,192,119356,102,0,P|188:196|112:200,1,142.500005435944 +56,120,119765,1,0,0:0:0:0: +72,288,119901,2,0,P|126:282|184:300,1,106.875004076958,8|0,0:0|0:0,0:0:0:0: +192,304,120174,2,0,P|230:306|264:296,1,71.2500027179719 +352,240,120447,6,0,P|416:208|488:228,1,142.500005435944 +428,308,120856,1,0,0:0:0:0: +460,128,120992,101,8,0:0:0:0: +424,36,121129,1,0,0:0:0:0: +360,112,121265,1,0,0:0:0:0: +296,72,121401,1,0,0:0:0:0: +296,72,121469,1,0,0:0:0:0: +296,72,121538,6,0,P|248:96|180:88,1,106.875004076958,4|0,0:0|0:0,0:0:0:0: +194,19,121810,2,0,P|228:5|268:6,1,71.2500027179719 +136,148,122083,102,0,P|106:192|43:219,1,106.875004076958,8|0,0:0|0:0,0:0:0:0: +23,153,122356,2,0,P|45:123|76:103,1,71.2500027179719 +184,256,122629,6,0,P|236:254|284:278,1,106.875004076958 +253,347,122901,2,0,P|218:343|184:329,1,71.2500027179719 +240,164,123174,101,8,0:0:0:0: +204,88,123310,1,0,0:0:0:0: +324,180,123447,5,0,0:0:0:0: +240,164,123583,1,0,0:0:0:0: +292,100,123719,6,0,P|392:132|328:264,1,285,4|0,3:0|0:0,0:0:0:0: +324,180,124674,101,0,0:0:0:0: +324,180,124810,1,0,0:0:0:0: +248,264,124947,1,0,0:0:0:0: +216,152,125083,1,0,0:0:0:0: +136,232,125219,1,0,0:0:0:0: +104,124,125356,1,0,0:0:0:0: +24,200,125492,1,0,0:0:0:0: +72,352,125629,37,0,0:0:0:0: +72,352,125765,1,0,0:0:0:0: +72,352,125901,2,0,P|108:346|152:344,1,71.25,2|0,2:0|0:0,0:0:0:0: +156,324,126174,6,0,P|108:340|48:332,1,104.500003189087 +248,264,126447,2,0,P|300:258|348:274,1,104.500003189087,0|0,0:0|0:0,0:0:0:0: +432,368,126719,1,8,0:0:0:0: +464,248,126856,53,0,0:0:0:0: +292,340,126992,2,0,L|308:184,1,156.750004783631,0|0,0:0|0:0,0:0:0:0: +312,164,127265,2,0,P|300:108|264:76,1,104.500003189087,8|0,0:0|0:0,0:0:0:0: +220,156,127538,1,0,0:0:0:0: +368,76,127674,1,0,0:0:0:0: +200,152,127810,1,8,0:0:0:0: +388,80,127947,53,8,0:0:0:0: +400,96,128015,1,8,0:0:0:0: +412,112,128083,1,8,0:0:0:0: +412,112,128356,54,0,P|348:144|288:96,1,142.5,12|0,0:0|0:0,0:0:0:0: +284,92,128629,2,0,P|238:93|188:88,1,95 +24,176,128901,2,0,P|73:173|120:172,1,95,8|0,0:0|0:0,0:0:0:0: +200,244,129174,53,0,0:0:0:0: +152,384,129310,1,0,0:0:0:0: +304,356,129447,1,8,0:0:0:0: +220,324,129583,1,0,0:0:0:0: +268,188,129719,1,8,0:0:0:0: +288,184,129788,1,0,0:0:0:0: +308,184,129856,1,0,0:0:0:0: +325,188,129924,1,0,0:0:0:0: +342,195,129992,1,2,0:0:0:0: +342,195,130265,54,0,P|320:152|324:100,1,95,4|0,0:0|0:0,0:0:0:0: +428,268,130538,1,8,0:0:0:0: +312,304,130674,1,0,0:0:0:0: +408,92,130810,54,0,P|368:32|284:28,1,142.5,0|0,0:0|0:0,0:0:0:0: +280,32,131083,2,0,P|233:27|180:32,1,95,8|0,0:0|0:0,0:0:0:0: +164,42,131288,1,0,0:0:0:0: +141,54,131356,54,0,B|150:142|150:142|132:163|124:192,1,142.5 +120,204,131629,2,0,P|168:180|208:180,1,95,8|0,0:0|0:0,0:0:0:0: +308,236,131901,53,0,0:0:0:0: +136,320,132038,1,0,0:0:0:0: +228,152,132174,1,8,0:0:0:0: +256,344,132310,1,2,2:0:0:0: +256,344,132379,1,2,2:0:0:0: +256,344,132447,54,0,B|305:335|352:332|352:332|372:280|424:260,1,190,4|8,0:0|0:0,0:0:0:0: +300,256,132856,2,0,P|255:261|204:268,1,95 +120,204,133129,1,0,0:0:0:0: +212,164,133265,53,8,0:0:0:0: +164,20,133401,1,0,0:0:0:0: +312,48,133538,54,0,L|344:88,1,47.5,8|0,0:0|0:0,0:0:0:0: +504,120,133674,2,0,L|453:127,1,47.5 +344,252,133810,2,0,L|362:204,1,47.5 +428,32,133947,2,0,L|409:79,1,47.5,8|0,0:0|0:0,0:0:0:0: +461,234,134083,2,0,L|429:194,1,47.5 +267,161,134219,2,0,L|317:153,1,47.5 +388,140,134356,1,8,0:0:0:0: +394,142,134424,1,0,0:0:0:0: +399,145,134492,1,0,0:0:0:0: +405,148,134560,1,0,0:0:0:0: +411,152,134629,102,0,P|439:208|407:280,1,142.5,4|0,0:0|0:0,0:0:0:0: +372,340,134901,2,0,P|324:341|276:344,1,95,8|0,0:0|0:0,0:0:0:0: +56,308,135174,53,0,0:0:0:0: +200,208,135310,1,8,0:0:0:0: +204,384,135447,1,0,0:0:0:0: +276,244,135583,53,0,0:0:0:0: +128,348,135719,1,0,0:0:0:0: +128,168,135856,2,0,P|168:128|212:124,1,95,0|8,0:0|0:0,0:0:0:0: +20,228,136129,53,0,0:0:0:0: +200,208,136265,1,0,0:0:0:0: +8,128,136401,1,8,0:0:0:0: +186,108,136538,1,0,0:0:0:0: +116,274,136674,1,0,0:0:0:0: +92,64,136810,54,0,B|99:117|112:160|112:160|104:180|96:208,1,142.5,4|0,0:0|0:0,0:0:0:0: +116,274,137083,2,0,P|166:268|212:268,1,95,8|0,0:0|0:0,0:0:0:0: +372,352,137356,1,0,0:0:0:0: +210,267,137492,1,0,0:0:0:0: +372,352,137629,2,0,P|420:340|452:304,1,95,8|0,0:0|0:0,0:0:0:0: +424,216,137901,53,0,0:0:0:0: +448,56,138038,1,0,0:0:0:0: +352,160,138174,5,8,0:0:0:0: +280,16,138310,1,0,0:0:0:0: +264,152,138447,5,0,0:0:0:0: +120,76,138583,1,0,0:0:0:0: +180,188,138719,5,8,0:0:0:0: +24,224,138856,1,0,0:0:0:0: +136,268,138992,70,0,P|184:268|232:272,1,95,4|0,0:0|0:0,0:0:0:0: +332,284,139265,1,8,0:0:0:0: +272,364,139401,53,0,0:0:0:0: +292,188,139538,2,0,P|332:124|408:120,1,142.5 +376,200,139810,2,0,P|425:195|480:188,1,95,8|0,0:0|0:0,0:0:0:0: +368,372,140083,54,0,P|320:368|268:364,1,95 +168,352,140356,1,8,0:0:0:0: +232,276,140492,53,0,0:0:0:0: +220,152,140629,1,0,0:0:0:0: +104,108,140765,1,0,0:0:0:0: +8,188,140901,1,8,0:0:0:0: +4,212,140969,1,0,0:0:0:0: +8,236,141038,1,0,0:0:0:0: +20,256,141106,1,0,0:0:0:0: +38,272,141174,53,0,0:0:0:0: +77,284,141719,53,0,0:0:0:0: +116,292,142265,54,0,B|104:196|8:232|-44:140|4:44|60:20|136:16|172:76|172:76|152:96|152:96|163:120|163:120|144:141|144:141|155:165|155:165|136:188|136:188|156:204|172:236|172:236,1,605.62501155138 +168,224,143356,6,0,P|228:188|304:196,1,142.500005435944,0|2,1:0|0:0,0:0:0:0: +298,193,143697,1,0,0:0:0:0: +298,193,143765,1,0,0:0:0:0: +352,264,143901,1,0,0:0:0:0: +424,212,144038,2,0,P|472:152|464:88,2,142.500005435944,2|2|0,0:0|0:0|0:0,0:0:0:0: +388,132,144719,54,0,L|364:48,1,71.2500027179719,0|0,0:0|0:0,0:0:0:0: +296,112,144992,2,0,P|264:96|224:96,2,71.2500027179719,2|0|0,0:0|0:0|0:0,0:0:0:0: +240,176,145401,53,0,0:0:0:0: +344,308,145538,2,0,P|288:348|216:344,1,142.500005435944,2|0,0:0|0:0,0:0:0:0: +260,260,145947,1,2,0:0:0:0: +172,240,146083,53,0,0:0:0:0: +172,240,146219,1,0,0:0:0:0: +172,240,146356,1,0,0:0:0:0: +172,240,146492,2,0,B|150:244|128:252|128:252|86:238|32:260,1,142.500005435944,2|0,0:0|0:0,0:0:0:0: +124,168,146901,2,0,B|146:164|168:156|168:156|210:170|264:148,1,142.500005435944,2|2,0:0|0:0,0:0:0:0: +276,140,147242,1,0,0:0:0:0: +288,132,147310,1,0,0:0:0:0: +448,180,147447,2,0,B|403:182|360:176|360:176|372:192,1,106.875004076958,2|0,0:0|0:0,0:0:0:0: +380,204,147719,54,0,P|420:264|384:336,1,142.500005435944,4|2,0:0|0:0,0:0:0:0: +397,325,148129,1,0,0:0:0:0: +332,268,148265,1,0,0:0:0:0: +276,332,148401,2,0,P|203:327|128:328,2,142.500005435944,2|2|0,0:0|0:0|0:0,0:0:0:0: +48,264,149083,1,0,0:0:0:0: +48,264,149219,2,0,P|28:196|60:136,2,142.500005435944,2|2|0,0:0|0:0|0:0,0:0:0:0: +112,208,149901,53,2,0:0:0:0: +296,256,150174,1,2,0:0:0:0: +296,256,150242,1,0,0:0:0:0: +296,256,150310,2,0,P|336:252|368:232,1,71.2500027179719,0|0,0:0|0:0,0:0:0:0: +388,72,150583,2,0,P|348:76|316:96,1,71.2500027179719,2|0,0:0|0:0,0:0:0:0: +308,172,150856,2,0,L|460:208,1,142.500005435944,2|0,0:0|0:0,0:0:0:0: +446,204,151265,37,2,0:0:0:0: +446,204,151401,1,0,0:0:0:0: +446,204,151538,5,2,0:0:0:0: +446,204,152083,86,0,P|472:248|464:304,1,95,4|0,0:0|0:0,0:0:0:0: +400,384,152356,1,8,0:0:0:0: +392,264,152492,1,2,2:0:0:0: +392,264,152560,1,2,2:0:0:0: +392,264,152629,2,0,P|348:240|300:240,1,95,2|0,2:0|0:0,0:0:0:0: +328,320,152901,1,8,0:0:0:0: +280,160,153038,1,0,0:0:0:0: +328,320,153174,54,0,L|140:344,1,190,2|8,2:0|0:0,0:0:0:0: +64,296,153583,1,0,0:0:0:0: +176,236,153719,2,2,L|224:244,2,47.5,2|2|2,2:0|2:0|2:0,2:0:0:0: +4,184,153992,1,8,0:0:0:0: +116,124,154129,1,2,2:0:0:0: +116,124,154197,1,2,2:0:0:0: +116,124,154265,54,0,P|136:68|128:32,1,95,4|0,0:0|0:0,0:0:0:0: +16,96,154538,2,0,P|-4:152|4:188,1,95,8|0,0:0|0:0,0:0:0:0: +76,288,154810,2,0,P|124:284|176:284,1,95,2|0,2:0|0:0,0:0:0:0: +292,340,155083,1,8,0:0:0:0: +276,208,155219,1,0,0:0:0:0: +380,312,155356,54,0,P|430:304|488:300,1,95,8|2,0:0|2:0,0:0:0:0: +384,232,155629,2,0,P|434:224|492:220,1,95,8|2,0:0|2:0,0:0:0:0: +312,128,155901,2,0,P|262:120|204:116,1,95,8|2,0:0|2:0,0:0:0:0: +308,48,156174,2,0,P|258:40|200:36,1,95,0|2,0:0|2:0,0:0:0:0: +213,36,156379,1,2,2:0:0:0: +213,36,156447,54,0,P|308:35|408:32,1,190,4|8,0:0|0:0,0:0:0:0: +480,88,156856,1,0,0:0:0:0: +384,232,156992,2,0,P|288:236|236:164,1,190,2|8,2:0|0:0,0:0:0:0: +328,152,157401,1,0,0:0:0:0: +264,324,157538,2,0,P|168:324|64:332,1,190,2|8,2:0|0:0,0:0:0:0: +112,248,157947,1,2,2:0:0:0: +112,248,158015,1,2,2:0:0:0: +112,248,158083,53,2,2:0:0:0: +28,144,158219,1,2,2:0:0:0: +100,32,158356,1,8,0:0:0:0: +228,72,158492,1,2,2:0:0:0: +232,204,158629,54,0,P|264:244|320:256,1,95,4|0,0:0|0:0,0:0:0:0: +484,208,158901,2,0,P|436:189|381:206,1,95,8|0,0:0|0:0,0:0:0:0: +160,328,159174,2,0,P|164:256|216:208,1,142.5,2|2,2:0|2:0,0:0:0:0: +232,204,159447,2,0,P|284:208|320:236,1,95,8|0,0:0|0:0,0:0:0:0: +251,295,159719,54,0,L|215:103,1,190,8|8,0:0|0:0,0:0:0:0: +296,64,160129,1,2,2:0:0:0: +128,152,160265,1,2,2:0:0:0: +105,144,160333,1,2,2:0:0:0: +84,131,160401,1,2,2:0:0:0: +67,114,160469,1,0,2:0:0:0: +54,94,160538,1,8,0:0:0:0: +52,117,160606,1,0,2:0:0:0: +48,140,160674,1,2,2:0:0:0: +37,161,160742,1,2,2:0:0:0: +23,179,160810,6,0,P|5:234|16:292,1,113.999996520996,4|8,0:0|0:0,0:0:0:0: +92,240,161083,2,0,P|103:296|142:341,1,113.999996520996,0|8,0:0|0:0,0:0:0:0: +184,264,161356,2,0,P|222:307|278:326,1,113.999996520996,0|8,0:0|0:0,0:0:0:0: +276,240,161629,2,0,P|331:258|388:247,1,113.999996520996,0|8,0:0|0:0,0:0:0:0: +500,184,161901,54,0,P|518:129|507:71,1,113.999996520996,0|8,0:0|0:0,0:0:0:0: +403,107,162174,2,0,P|391:50|352:5,1,113.999996520996,0|8,0:0|0:0,0:0:0:0: +275,87,162447,2,0,P|236:43|180:24,1,113.999996520996,8|0,0:0|0:0,0:0:0:0: +151,127,162719,2,0,P|96:109|38:120,1,113.999996520996,8|0,0:0|0:0,0:0:0:0: +84,292,162992,53,4,0:0:0:0: +188,228,163129,1,8,0:0:0:0: +200,352,163265,2,0,P|260:344|332:344,1,123.499994346619,0|8,0:0|0:0,0:0:0:0: +512,200,163538,53,0,0:0:0:0: +408,136,163674,1,8,0:0:0:0: +396,260,163810,2,0,P|336:252|264:252,1,123.499994346619,0|8,0:0|0:0,0:0:0:0: +240,80,164083,54,0,P|192:32|128:32,1,123.499994346619,0|8,0:0|0:0,0:0:0:0: +64,176,164356,2,0,P|112:224|176:224,1,123.499994346619,0|8,0:0|0:0,0:0:0:0: +244,72,164629,54,0,P|196:24|132:24,1,123.499994346619,0|8,0:0|0:0,0:0:0:0: +56,184,164901,2,8,P|104:232|168:232,1,123.499994346619,8|8,0:0|0:0,0:0:0:0: +328,128,165174,54,0,P|392:120|448:76,1,132.999995941162,4|8,0:0|0:0,0:0:0:0: +360,212,165447,2,0,B|394:206|416:200|416:200|456:216|500:208,1,132.999995941162,0|8,0:0|0:0,0:0:0:0: +328,300,165719,2,0,P|392:320|452:352,1,132.999995941162,0|8,0:0|0:0,0:0:0:0: +184,300,165992,2,0,P|120:320|60:352,1,132.999995941162,0|8,0:0|0:0,0:0:0:0: +152,212,166265,2,0,B|117:206|96:200|96:200|56:216|12:208,1,132.999995941162,0|8,0:0|0:0,0:0:0:0: +184,128,166538,2,0,P|120:120|64:76,1,132.999995941162,0|8,0:0|0:0,0:0:0:0: +256,80,166810,53,8,0:0:0:0: +256,100,166879,1,0,2:0:0:0: +256,120,166947,1,2,2:0:0:0: +256,140,167015,1,2,2:0:0:0: +256,160,167083,1,2,2:0:0:0: +256,256,167219,1,2,2:0:0:0: +256,256,167288,1,0,2:0:0:0: +256,256,167356,53,4,3:0:0:0: +256,276,167901,53,0,0:0:0:0: +256,296,168447,53,0,0:0:0:0: +256,84,168583,1,8,0:0:0:0: +181,265,168719,1,0,0:0:0:0: +330,115,168856,1,8,0:0:0:0: +150,190,168992,1,0,0:0:0:0: +361,190,169129,1,8,0:0:0:0: +181,115,169265,1,0,0:0:0:0: +330,265,169401,1,8,0:0:0:0: +256,192,169538,38,0,L|344:184,1,80.750001540184,4|0,3:0|0:0,0:0:0:0: +256,192,169810,2,0,L|168:200,1,80.750001540184,4|0,3:0|0:0,0:0:0:0: +256,36,170083,1,4,3:0:0:0: +256,36,170219,1,2,0:0:0:0: +256,36,170356,2,0,L|256:116,1,80.750001540184 +208,260,170629,69,2,0:0:0:0: +191,249,170697,1,0,0:0:0:0: +176,240,170765,1,0,0:0:0:0: +256,192,170901,1,0,0:0:0:0: +304,260,171038,37,2,0:0:0:0: +321,249,171106,1,0,0:0:0:0: +336,240,171174,1,0,0:0:0:0: +256,192,171310,1,0,0:0:0:0: +256,332,171447,101,4,3:0:0:0: diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index 7d7adf5983..45de8e2411 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -86,10 +86,11 @@ + - + @@ -147,6 +148,7 @@ + \ No newline at end of file From cc76c58f5f250151bb85ad5efa3f6ce008f0cbb0 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Sat, 2 Dec 2017 16:05:39 +0100 Subject: [PATCH 178/235] fall back to .osu file for storyboard if no .osb file is present + CI fixes --- .../Beatmaps/Formats/LegacyStoryboardDecoderTest.cs | 9 +++++---- osu.Game/Beatmaps/BeatmapManager.cs | 6 ++++-- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 7 +++++++ osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 12 ++++++++---- osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs | 6 +++--- 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs index a42318883c..839932c640 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; using System.IO; using System.Linq; using NUnit.Framework; @@ -56,9 +55,9 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.IsTrue(foreground.EnabledWhenPassing); Assert.AreEqual("Foreground", foreground.Name); - int spriteCount = background.Elements.Where(x => x.GetType() == typeof(StoryboardSprite)).Count(); - int animationCount = background.Elements.Where(x => x.GetType() == typeof(StoryboardAnimation)).Count(); - int sampleCount = background.Elements.Where(x => x.GetType() == typeof(StoryboardSample)).Count(); + int spriteCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardSprite)); + int animationCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardAnimation)); + int sampleCount = background.Elements.Count(x => x.GetType() == typeof(StoryboardSample)); Assert.AreEqual(15, spriteCount); Assert.AreEqual(1, animationCount); @@ -66,6 +65,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(background.Elements.Count(), spriteCount + animationCount + sampleCount); var sprite = background.Elements.ElementAt(0) as StoryboardSprite; + Assert.NotNull(sprite); Assert.IsTrue(sprite.HasCommands); Assert.AreEqual(new Vector2(320, 240), sprite.InitialPosition); Assert.IsTrue(sprite.IsDrawable); @@ -73,6 +73,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual("SB/lyric/ja-21.png", sprite.Path); var animation = background.Elements.ElementAt(12) as StoryboardAnimation; + Assert.NotNull(animation); Assert.AreEqual(141175, animation.EndTime); Assert.AreEqual(10, animation.FrameCount); Assert.AreEqual(30, animation.FrameDelay); diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 00182d5d85..d97cc6a7bf 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -615,7 +615,7 @@ namespace osu.Game.Beatmaps protected override Storyboard GetStoryboard() { - if (BeatmapSetInfo?.StoryboardFile == null) + if (BeatmapInfo?.Path == null && BeatmapSetInfo?.StoryboardFile == null) return new Storyboard(); try @@ -624,7 +624,9 @@ namespace osu.Game.Beatmaps using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) decoder = Decoder.GetDecoder(stream); - using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapSetInfo.StoryboardFile)))) + // try for .osb first and fall back to .osu + string storyboardFile = BeatmapSetInfo.StoryboardFile ?? BeatmapInfo.Path; + using (var stream = new StreamReader(store.GetStream(getPathForFile(storyboardFile)))) return decoder.GetStoryboardDecoder().DecodeStoryboard(stream); } catch diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 29cf7dd913..b7004dd3eb 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -47,6 +47,13 @@ namespace osu.Game.Beatmaps.Formats hitObject.ApplyDefaults(this.beatmap.ControlPointInfo, this.beatmap.BeatmapInfo.BaseDifficulty); } + protected override bool ShouldSkipLine(string line) + { + if (base.ShouldSkipLine(line) || line.StartsWith(" ") || line.StartsWith("_")) + return true; + return false; + } + protected override void ProcessSection(Section section, string line) { switch (section) diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 85d77d93bc..96747a870d 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -52,10 +52,7 @@ namespace osu.Game.Beatmaps.Formats string line; while ((line = stream.ReadLine()) != null) { - if (string.IsNullOrWhiteSpace(line)) - continue; - - if (line.StartsWith("//")) + if (ShouldSkipLine(line)) continue; // It's already set in ParseBeatmap... why do it again? @@ -76,6 +73,13 @@ namespace osu.Game.Beatmaps.Formats } } + protected virtual bool ShouldSkipLine(string line) + { + if (string.IsNullOrWhiteSpace(line) || line.StartsWith("//")) + return true; + return false; + } + protected abstract void ProcessSection(Section section, string line); /// diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index aca92f3969..8da6a0cefb 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -237,9 +237,9 @@ namespace osu.Game.Beatmaps.Formats } } - private static string parseLayer(string value) => Enum.Parse(typeof(StoryLayer), value).ToString(); + private string parseLayer(string value) => Enum.Parse(typeof(StoryLayer), value).ToString(); - private static Anchor parseOrigin(string value) + private Anchor parseOrigin(string value) { var origin = (LegacyOrigins)Enum.Parse(typeof(LegacyOrigins), value); switch (origin) @@ -266,6 +266,6 @@ namespace osu.Game.Beatmaps.Formats throw new InvalidDataException($@"Unknown origin: {value}"); } - private static string cleanFilename(string path) => FileSafety.PathStandardise(path.Trim('\"')); + private string cleanFilename(string path) => FileSafety.PathStandardise(path.Trim('\"')); } } From ad8cd7eb5de9c97fdafd9e4df3af0a3a83163ada Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Sat, 2 Dec 2017 17:04:42 +0100 Subject: [PATCH 179/235] fix possible NullReference? AppVeyor pls? --- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index d97cc6a7bf..edbda1a685 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -621,7 +621,7 @@ namespace osu.Game.Beatmaps try { Decoder decoder; - using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo.Path)))) + using (var stream = new StreamReader(store.GetStream(getPathForFile(BeatmapInfo?.Path)))) decoder = Decoder.GetDecoder(stream); // try for .osb first and fall back to .osu From a1dbd7916b8a890e8a241b0d43f55aed39a1cd2a Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Sun, 3 Dec 2017 14:03:43 +0100 Subject: [PATCH 180/235] fixes MusicController constantly trying to start a track In Detail: It tried to start a track if the current one ended even if no BeatmapSets were present. Also if only one BeatmapSet is present if will loop by itself now. --- osu.Game/Overlays/Music/PlaylistOverlay.cs | 6 ++++++ osu.Game/Overlays/MusicController.cs | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index d05ad85726..eeb1982d91 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -149,6 +149,12 @@ namespace osu.Game.Overlays.Music private void playSpecified(BeatmapInfo info) { beatmapBacking.Value = beatmaps.GetWorkingBeatmap(info, beatmapBacking); + + if (BeatmapSets.Count() == 1) + beatmapBacking.Value.Track.Looping = true; + else + beatmapBacking.Value.Track.Looping = false; + beatmapBacking.Value.Track.Start(); } } diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 4f57ea1bcd..b30ee8f6fc 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -251,7 +251,7 @@ namespace osu.Game.Overlays playButton.Icon = track.IsRunning ? FontAwesome.fa_pause_circle_o : FontAwesome.fa_play_circle_o; - if (track.HasCompleted && !track.Looping && !beatmapBacking.Disabled) + if (track.HasCompleted && !beatmapBacking.Disabled && playlist.BeatmapSets.Any()) next(); } else From 0c9ebcd58c2393910b9a07b407a5c45e31ccccda Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Sun, 3 Dec 2017 14:15:08 +0100 Subject: [PATCH 181/235] fix possible NullRef --- osu.Game/Overlays/Music/PlaylistOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index eeb1982d91..4bac91edb8 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -150,7 +150,7 @@ namespace osu.Game.Overlays.Music { beatmapBacking.Value = beatmaps.GetWorkingBeatmap(info, beatmapBacking); - if (BeatmapSets.Count() == 1) + if (BeatmapSets?.Count() == 1) beatmapBacking.Value.Track.Looping = true; else beatmapBacking.Value.Track.Looping = false; From dfa74487162de873d71da25c5698561f1677a120 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Sun, 3 Dec 2017 14:25:12 +0100 Subject: [PATCH 182/235] use ?: expression --- osu.Game/Overlays/Music/PlaylistOverlay.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index 4bac91edb8..1874b7ead8 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -149,12 +149,7 @@ namespace osu.Game.Overlays.Music private void playSpecified(BeatmapInfo info) { beatmapBacking.Value = beatmaps.GetWorkingBeatmap(info, beatmapBacking); - - if (BeatmapSets?.Count() == 1) - beatmapBacking.Value.Track.Looping = true; - else - beatmapBacking.Value.Track.Looping = false; - + beatmapBacking.Value.Track.Looping = BeatmapSets?.Count() == 1 ? true : false; beatmapBacking.Value.Track.Start(); } } From 9d13bf36025a8c355a4f5891007fffe62ab8a99a Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Sun, 3 Dec 2017 14:35:23 +0100 Subject: [PATCH 183/235] remove redundant expression --- osu.Game/Overlays/Music/PlaylistOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index 1874b7ead8..75798bab2d 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -149,7 +149,7 @@ namespace osu.Game.Overlays.Music private void playSpecified(BeatmapInfo info) { beatmapBacking.Value = beatmaps.GetWorkingBeatmap(info, beatmapBacking); - beatmapBacking.Value.Track.Looping = BeatmapSets?.Count() == 1 ? true : false; + beatmapBacking.Value.Track.Looping = BeatmapSets?.Count() == 1; beatmapBacking.Value.Track.Start(); } } From 14096c90cc73a03811c58d305ab8db0f6b22c42a Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Sun, 3 Dec 2017 16:41:21 +0100 Subject: [PATCH 184/235] removed looping if only one song is in the list (temporarily) It had one problem in relation to SongSelect disabling it when left and in general that topic belongs to another PR. --- osu.Game/Overlays/Music/PlaylistList.cs | 7 ++++++- osu.Game/Overlays/Music/PlaylistOverlay.cs | 16 +++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs index af01cdc451..8db4af6fb2 100644 --- a/osu.Game/Overlays/Music/PlaylistList.cs +++ b/osu.Game/Overlays/Music/PlaylistList.cs @@ -35,7 +35,11 @@ namespace osu.Game.Overlays.Music set { base.Padding = value; } } - public IEnumerable BeatmapSets { set { items.Sets = value; } } + public IEnumerable BeatmapSets + { + get { return items.Sets; } + set { items.Sets = value; } + } public BeatmapSetInfo FirstVisibleSet => items.FirstVisibleSet; public BeatmapSetInfo NextSet => items.NextSet; @@ -81,6 +85,7 @@ namespace osu.Game.Overlays.Music public IEnumerable Sets { + get { return items.Select(x => x.BeatmapSetInfo).ToList(); } set { items.Clear(); diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index 75798bab2d..54b34aaead 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -29,7 +29,7 @@ namespace osu.Game.Overlays.Music private readonly Bindable beatmapBacking = new Bindable(); - public IEnumerable BeatmapSets; + public IEnumerable BeatmapSets => list.BeatmapSets; [BackgroundDependencyLoader] private void load(OsuGameBase game, BeatmapManager beatmaps, OsuColour colours) @@ -74,11 +74,10 @@ namespace osu.Game.Overlays.Music }, }; - beatmaps.BeatmapSetAdded += s => Schedule(() => list.AddBeatmapSet(s)); - beatmaps.BeatmapSetRemoved += s => Schedule(() => list.RemoveBeatmapSet(s)); - - list.BeatmapSets = BeatmapSets = beatmaps.GetAllUsableBeatmapSets(); + beatmaps.BeatmapSetAdded += delegate (BeatmapSetInfo set) { list.AddBeatmapSet(set); }; + beatmaps.BeatmapSetRemoved += delegate (BeatmapSetInfo set) { list.RemoveBeatmapSet(set); }; + list.BeatmapSets = beatmaps.GetAllUsableBeatmapSets(); beatmapBacking.BindTo(game.Beatmap); @@ -121,7 +120,7 @@ namespace osu.Game.Overlays.Music return; } - playSpecified(set.Beatmaps[0]); + playSpecified(set.Beatmaps.First()); } public void PlayPrevious() @@ -130,7 +129,7 @@ namespace osu.Game.Overlays.Music if (playable != null) { - playSpecified(playable.Beatmaps[0]); + playSpecified(playable.Beatmaps.First()); list.SelectedSet = playable; } } @@ -141,7 +140,7 @@ namespace osu.Game.Overlays.Music if (playable != null) { - playSpecified(playable.Beatmaps[0]); + playSpecified(playable.Beatmaps.First()); list.SelectedSet = playable; } } @@ -149,7 +148,6 @@ namespace osu.Game.Overlays.Music private void playSpecified(BeatmapInfo info) { beatmapBacking.Value = beatmaps.GetWorkingBeatmap(info, beatmapBacking); - beatmapBacking.Value.Track.Looping = BeatmapSets?.Count() == 1; beatmapBacking.Value.Track.Start(); } } From 5f9de399e40d6748a14657fa97da77aa3f916323 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 4 Dec 2017 11:30:25 +0900 Subject: [PATCH 185/235] Add keydown override --- osu.Game/Screens/OsuScreen.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 76ee4a607e..4a27c7f1ea 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -13,6 +13,8 @@ using osu.Framework.Audio; using osu.Framework.Graphics; using osu.Game.Rulesets; using osu.Game.Screens.Menu; +using osu.Framework.Input; +using OpenTK.Input; namespace osu.Game.Screens { @@ -73,6 +75,20 @@ namespace osu.Game.Screens sampleExit = audio.Sample.Get(@"UI/screen-back"); } + protected override bool OnKeyDown(InputState state, KeyDownEventArgs args) + { + if (args.Repeat || !IsCurrentScreen) return false; + + switch (args.Key) + { + case Key.Escape: + Exit(); + return true; + } + + return base.OnKeyDown(state, args); + } + protected override void OnResuming(Screen last) { base.OnResuming(last); From 7cfe694a3fd63ac1dd54caddcb87bce80f09e8cb Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 4 Dec 2017 11:37:19 +0900 Subject: [PATCH 186/235] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index cc013fc406..8c16e6244d 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit cc013fc4063dda0843f38c1c73568a413abcf229 +Subproject commit 8c16e6244d8ca884a743a0d5a1f1ee485f18655c From 011223048b129b213003083279a9f6793e930fb5 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Mon, 4 Dec 2017 11:47:27 +0100 Subject: [PATCH 187/235] fix crash if any amount of maps were restored from main menu also fixes preview not playing if an entire set is restored --- osu.Game/Screens/Select/BeatmapCarousel.cs | 30 ++++++++++++++-------- osu.Game/Screens/Select/SongSelect.cs | 4 +-- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 3f42ae11ac..47bfe6095f 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -34,7 +34,6 @@ namespace osu.Game.Screens.Select public IEnumerable Beatmaps { get { return groups.Select(g => g.BeatmapSet); } - set { scrollableContent.Clear(false); @@ -44,15 +43,14 @@ namespace osu.Game.Screens.Select List newGroups = null; Task.Run(() => - { - newGroups = value.Select(createGroup).Where(g => g != null).ToList(); - criteria.Filter(newGroups); - }).ContinueWith(t => { Schedule(() => { + newGroups = value.Select(createGroup).Where(g => g != null).ToList(); + criteria.Filter(newGroups); + foreach (var g in newGroups) - if (g != null) addGroup(g); + addGroup(g); computeYPositions(); BeatmapsChanged?.Invoke(); @@ -135,7 +133,7 @@ namespace osu.Game.Screens.Select if (i >= 0) groups.Insert(i, newGroup); else - groups.Add(newGroup); + addGroup(newGroup); } bool hadSelection = selectedGroup == group; @@ -149,8 +147,10 @@ namespace osu.Game.Screens.Select if (hadSelection && newGroup != null) { var newSelection = - newGroup.BeatmapPanels.Find(p => p.Beatmap.ID == selectedPanel?.Beatmap.ID) ?? - newGroup.BeatmapPanels[Math.Min(newGroup.BeatmapPanels.Count - 1, group.BeatmapPanels.IndexOf(selectedPanel))]; + newGroup.BeatmapPanels.Find(p => p.Beatmap.ID == selectedPanel?.Beatmap.ID); + + if(newSelection == null && group != null && selectedPanel != null) + newSelection = newGroup.BeatmapPanels[Math.Min(newGroup.BeatmapPanels.Count - 1, group.BeatmapPanels.IndexOf(selectedPanel))]; selectGroup(newGroup, newSelection); } @@ -350,6 +350,8 @@ namespace osu.Game.Screens.Select private BeatmapGroup createGroup(BeatmapSetInfo beatmapSet) { + beatmapSet = manager.Refresh(beatmapSet); + if (beatmapSet.Beatmaps.All(b => b.Hidden)) return null; @@ -381,6 +383,10 @@ namespace osu.Game.Screens.Select private void addGroup(BeatmapGroup group) { + // prevent duplicates by concurrent independent actions trying to add a group + if (groups.Any(g => g.BeatmapSet.ID == group.BeatmapSet.ID)) + return; + groups.Add(group); panels.Add(group.Header); panels.AddRange(group.BeatmapPanels); @@ -478,7 +484,8 @@ namespace osu.Game.Screens.Select if (panel == null) panel = group.BeatmapPanels.First(); - if (selectedPanel == panel) return; + if (selectedPanel == panel) + return; Trace.Assert(group.BeatmapPanels.Contains(panel), @"Selected panel must be in provided group"); @@ -490,7 +497,8 @@ namespace osu.Game.Screens.Select panel.State = PanelSelectedState.Selected; - if (selectedPanel == panel) return; + if (selectedPanel == panel) + return; selectedPanel = panel; selectedGroup = group; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 46284226d7..0f72e3fd95 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -255,11 +255,11 @@ namespace osu.Game.Screens.Select UpdateBeatmap(Beatmap.Value); }; - selectionChangedDebounce?.Cancel(); - if (beatmap?.Equals(beatmapNoDebounce) == true) return; + selectionChangedDebounce?.Cancel(); + beatmapNoDebounce = beatmap; if (beatmap == null) From 12665fb8cf83ca2648da32eb72f2f303a3a372d3 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Mon, 4 Dec 2017 12:11:28 +0100 Subject: [PATCH 188/235] remove unnecessary declaration + revert accidental formatting --- osu.Game/Screens/Select/BeatmapCarousel.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 47bfe6095f..fe981f1f2a 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -40,13 +40,11 @@ namespace osu.Game.Screens.Select panels.Clear(); groups.Clear(); - List newGroups = null; - Task.Run(() => { Schedule(() => { - newGroups = value.Select(createGroup).Where(g => g != null).ToList(); + var newGroups = value.Select(createGroup).Where(g => g != null).ToList(); criteria.Filter(newGroups); foreach (var g in newGroups) @@ -484,8 +482,7 @@ namespace osu.Game.Screens.Select if (panel == null) panel = group.BeatmapPanels.First(); - if (selectedPanel == panel) - return; + if (selectedPanel == panel) return; Trace.Assert(group.BeatmapPanels.Contains(panel), @"Selected panel must be in provided group"); @@ -497,8 +494,7 @@ namespace osu.Game.Screens.Select panel.State = PanelSelectedState.Selected; - if (selectedPanel == panel) - return; + if (selectedPanel == panel) return; selectedPanel = panel; selectedGroup = group; From a83add8540d59d40ac19003de9ca1b29a2011512 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Dec 2017 20:30:56 +0900 Subject: [PATCH 189/235] Tidy up events --- osu.Game/Overlays/Music/PlaylistList.cs | 10 +++++----- osu.Game/Overlays/Music/PlaylistOverlay.cs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Music/PlaylistList.cs b/osu.Game/Overlays/Music/PlaylistList.cs index 8db4af6fb2..245b2d36ce 100644 --- a/osu.Game/Overlays/Music/PlaylistList.cs +++ b/osu.Game/Overlays/Music/PlaylistList.cs @@ -52,7 +52,7 @@ namespace osu.Game.Overlays.Music } public void AddBeatmapSet(BeatmapSetInfo beatmapSet) => items.AddBeatmapSet(beatmapSet); - public bool RemoveBeatmapSet(BeatmapSetInfo beatmapSet) => items.RemoveBeatmapSet(beatmapSet); + public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) => items.RemoveBeatmapSet(beatmapSet); public void Filter(string searchTerm) => items.SearchTerm = searchTerm; @@ -108,12 +108,11 @@ namespace osu.Game.Overlays.Music }); } - public bool RemoveBeatmapSet(BeatmapSetInfo beatmapSet) + public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) { var itemToRemove = items.FirstOrDefault(i => i.BeatmapSetInfo.ID == beatmapSet.ID); - if (itemToRemove == null) - return false; - return items.Remove(itemToRemove); + if (itemToRemove != null) + items.Remove(itemToRemove); } public BeatmapSetInfo SelectedSet @@ -235,6 +234,7 @@ namespace osu.Game.Overlays.Music private class ItemSearchContainer : FillFlowContainer, IHasFilterableChildren { public IEnumerable FilterTerms => new string[] { }; + public bool MatchingFilter { set diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index 54b34aaead..2c7c097be2 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -74,8 +74,8 @@ namespace osu.Game.Overlays.Music }, }; - beatmaps.BeatmapSetAdded += delegate (BeatmapSetInfo set) { list.AddBeatmapSet(set); }; - beatmaps.BeatmapSetRemoved += delegate (BeatmapSetInfo set) { list.RemoveBeatmapSet(set); }; + beatmaps.BeatmapSetAdded += list.AddBeatmapSet; + beatmaps.BeatmapSetRemoved += list.RemoveBeatmapSet; list.BeatmapSets = beatmaps.GetAllUsableBeatmapSets(); From 7649a3115704dfae6c3947917f294ad97177b181 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Dec 2017 20:32:50 +0900 Subject: [PATCH 190/235] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 8c16e6244d..e21fc5e170 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 8c16e6244d8ca884a743a0d5a1f1ee485f18655c +Subproject commit e21fc5e1705593151a323007fd66e90044332398 From caee6c1cf1163407734edf6fd68226849aafba66 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Dec 2017 21:40:26 +0900 Subject: [PATCH 191/235] Use Restart instead of Start --- osu.Game/Overlays/Music/PlaylistOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index 2c7c097be2..cc55a72f24 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -148,7 +148,7 @@ namespace osu.Game.Overlays.Music private void playSpecified(BeatmapInfo info) { beatmapBacking.Value = beatmaps.GetWorkingBeatmap(info, beatmapBacking); - beatmapBacking.Value.Track.Start(); + beatmapBacking.Value.Track.Restart(); } } From b4641142a8e6205a25b175f3c4bae90b40d17e9d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Dec 2017 01:00:06 +0900 Subject: [PATCH 192/235] Update framework once more --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index e21fc5e170..d2c6d11f3b 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit e21fc5e1705593151a323007fd66e90044332398 +Subproject commit d2c6d11f3bcc54a5aeb5a16d6563036c7e1f4759 From c1b607fed926eeb2051b8d0ebb6e3869efb9dcc9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Dec 2017 09:56:24 +0900 Subject: [PATCH 193/235] Wait until track has restarted before continuing operation I removed this from my previous PR thinking it was not required, but it turned out to be required after all. Just isn't so noticeable when it fails. --- osu.Game/Overlays/Music/PlaylistOverlay.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Music/PlaylistOverlay.cs b/osu.Game/Overlays/Music/PlaylistOverlay.cs index cc55a72f24..23bec53014 100644 --- a/osu.Game/Overlays/Music/PlaylistOverlay.cs +++ b/osu.Game/Overlays/Music/PlaylistOverlay.cs @@ -13,6 +13,7 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using OpenTK; using OpenTK.Graphics; +using System.Threading; namespace osu.Game.Overlays.Music { @@ -148,7 +149,15 @@ namespace osu.Game.Overlays.Music private void playSpecified(BeatmapInfo info) { beatmapBacking.Value = beatmaps.GetWorkingBeatmap(info, beatmapBacking); - beatmapBacking.Value.Track.Restart(); + + var track = beatmapBacking.Value.Track; + + track.Restart(); + + // this is temporary until we have blocking (async.Wait()) audio component methods. + // then we can call RestartAsync().Wait() or the blocking version above. + while (!track.IsRunning) + Thread.Sleep(1); } } From fb6408257a5fa980d0abd728c16cdbc1d42ea22f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Dec 2017 15:54:15 +0900 Subject: [PATCH 194/235] Add shader precompile step Resolves https://github.com/ppy/osu-framework/issues/1180 in a way. --- osu.Game/Screens/Loader.cs | 70 +++++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs index ca541ea552..ec2e8e0cb1 100644 --- a/osu.Game/Screens/Loader.cs +++ b/osu.Game/Screens/Loader.cs @@ -1,8 +1,12 @@ // 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 System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Shaders; using osu.Game.Screens.Menu; using OpenTK; using osu.Framework.Screens; @@ -33,14 +37,28 @@ namespace osu.Game.Screens logo.FadeInFromZero(5000, Easing.OutQuint); } + private OsuScreen loadScreen; + private ShaderPrecompiler precompiler; + protected override void OnEntering(Screen last) { base.OnEntering(last); - if (showDisclaimer) - LoadComponentAsync(new Disclaimer(), d => Push(d)); - else - LoadComponentAsync(new Intro(), d => Push(d)); + LoadComponentAsync(precompiler = new ShaderPrecompiler(loadIfReady), Add); + LoadComponentAsync(loadScreen = showDisclaimer ? (OsuScreen)new Disclaimer() : new Intro(), s => loadIfReady()); + } + + private void loadIfReady() + { + if (ChildScreen == loadScreen) return; + + if (loadScreen.LoadState != LoadState.Ready) + return; + + if (!precompiler.FinishedCompiling) + return; + + Push(loadScreen); } protected override void LogoSuspending(OsuLogo logo) @@ -54,5 +72,49 @@ namespace osu.Game.Screens { showDisclaimer = game.IsDeployedBuild; } + + /// + /// Compiles a set of shaders before continuing. Attempts to draw some frames between compilation by limiting to one compile per draw frame. + /// + public class ShaderPrecompiler : Drawable + { + private readonly Action onLoaded; + private readonly List loadTargets = new List(); + + public bool FinishedCompiling { get; private set; } + + public ShaderPrecompiler(Action onLoaded) + { + this.onLoaded = onLoaded; + } + + [BackgroundDependencyLoader] + private void load(ShaderManager manager) + { + loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE_ROUNDED)); + loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.BLUR)); + loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE)); + + loadTargets.Add(manager.Load(@"CursorTrail", FragmentShaderDescriptor.TEXTURE)); + + loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_3, FragmentShaderDescriptor.TEXTURE_ROUNDED)); + loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_3, FragmentShaderDescriptor.TEXTURE)); + } + + private Shader currentLoadTarget; + + protected override void Update() + { + base.Update(); + + // if our target is null we are done. + if (loadTargets.All(s => s.Loaded)) + { + FinishedCompiling = true; + Expire(); + onLoaded?.Invoke(); + } + } + } } } From 15ed3b4aac3c87ae277c1c53696f02b3c1b1097e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2017 09:49:12 +0900 Subject: [PATCH 195/235] Fix IsValueCreated method not cecking whether the async task was completed Caused potential stutters for components that relied on this check. --- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 8c96074352..a23d74f288 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -144,7 +144,7 @@ namespace osu.Game.Beatmaps get { ensureValid(); - return lazy.IsValueCreated; + return lazy.Value.IsCompleted; } } From 988cc27f0e7fc461d0d8c66aa3e0375eb24ffa28 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2017 11:59:53 +0900 Subject: [PATCH 196/235] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index d2c6d11f3b..9cd6968a8c 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit d2c6d11f3bcc54a5aeb5a16d6563036c7e1f4759 +Subproject commit 9cd6968a8c4d27415808f5e770d24fec5a44485f From 04ae64e9fd7bb412cb20cb3424d2325afd94ced0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2017 16:20:49 +0900 Subject: [PATCH 197/235] Add missing IsCompleted check to ensureValid --- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index a23d74f288..6378bb7568 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -161,7 +161,7 @@ namespace osu.Game.Beatmaps { lock (initLock) { - if (!lazy.IsValueCreated || (stillValidFunction?.Invoke(lazy.Value.Result) ?? true)) return; + if (!lazy.IsValueCreated || !lazy.Value.IsCompleted || (stillValidFunction?.Invoke(lazy.Value.Result) ?? true)) return; init(); } } From 34596b3368591d2b9eba717a5139854dd1ce4ba1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2017 21:47:48 +0900 Subject: [PATCH 198/235] Rename and comment for clarification --- osu.Game/Beatmaps/WorkingBeatmap.cs | 44 ++++++++++++++++------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 6378bb7568..bc826ea140 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -41,7 +41,7 @@ namespace osu.Game.Beatmaps protected abstract Track GetTrack(); protected virtual Waveform GetWaveform() => new Waveform(); - public bool BeatmapLoaded => beatmap.IsValueCreated; + public bool BeatmapLoaded => beatmap.IsResultAvailable; public Beatmap Beatmap => beatmap.Value.Result; public async Task GetBeatmapAsync() => await beatmap.Value; @@ -57,14 +57,14 @@ namespace osu.Game.Beatmaps return b; } - public bool BackgroundLoaded => background.IsValueCreated; + public bool BackgroundLoaded => background.IsResultAvailable; public Texture Background => background.Value.Result; public async Task GetBackgroundAsync() => await background.Value; private AsyncLazy background; private Texture populateBackground() => GetBackground(); - public bool TrackLoaded => track.IsValueCreated; + public bool TrackLoaded => track.IsResultAvailable; public Track Track => track.Value.Result; public async Task GetTrackAsync() => await track.Value; private AsyncLazy track; @@ -77,7 +77,7 @@ namespace osu.Game.Beatmaps return t; } - public bool WaveformLoaded => waveform.IsValueCreated; + public bool WaveformLoaded => waveform.IsResultAvailable; public Waveform Waveform => waveform.Value.Result; public async Task GetWaveformAsync() => await waveform.Value; private readonly AsyncLazy waveform; @@ -86,10 +86,10 @@ namespace osu.Game.Beatmaps public void TransferTo(WorkingBeatmap other) { - if (track.IsValueCreated && Track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo)) + if (track.IsResultAvailable && Track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo)) other.track = track; - if (background.IsValueCreated && Background != null && BeatmapInfo.BackgroundEquals(other.BeatmapInfo)) + if (background.IsResultAvailable && Background != null && BeatmapInfo.BackgroundEquals(other.BeatmapInfo)) other.background = background; } @@ -107,7 +107,7 @@ namespace osu.Game.Beatmaps private void applyRateAdjustments(Track t = null) { - if (t == null && track.IsValueCreated) t = Track; + if (t == null && track.IsResultAvailable) t = Track; if (t == null) return; t.ResetSpeedAdjustments(); @@ -128,22 +128,22 @@ namespace osu.Game.Beatmaps this.valueFactory = valueFactory; this.stillValidFunction = stillValidFunction; - init(); + recreate(); } public void Recycle() { - if (!IsValueCreated) return; + if (!IsResultAvailable) return; (lazy.Value.Result as IDisposable)?.Dispose(); - init(); + recreate(); } - public bool IsValueCreated + public bool IsResultAvailable { get { - ensureValid(); + recreateIfInvalid(); return lazy.Value.IsCompleted; } } @@ -152,24 +152,28 @@ namespace osu.Game.Beatmaps { get { - ensureValid(); + recreateIfInvalid(); return lazy.Value; } } - private void ensureValid() + private void recreateIfInvalid() { lock (initLock) { - if (!lazy.IsValueCreated || !lazy.Value.IsCompleted || (stillValidFunction?.Invoke(lazy.Value.Result) ?? true)) return; - init(); + if (!lazy.IsValueCreated || !lazy.Value.IsCompleted) + // we have not yet been initialised or haven't run the task. + return; + + if (stillValidFunction?.Invoke(lazy.Value.Result) ?? true) + // we are still in a valid state. + return; + + recreate(); } } - private void init() - { - lazy = new Lazy>(() => Task.Run(valueFactory)); - } + private void recreate() => lazy = new Lazy>(() => Task.Run(valueFactory)); } } } From a78441bc5a511d5f780cd949d2d21cc2218c8be9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2017 23:03:31 +0900 Subject: [PATCH 199/235] Apply changes in line with framework input adjustments --- osu.Game.Rulesets.Osu/OsuInputManager.cs | 3 +++ .../DatabasedKeyBindingInputManager.cs | 4 +-- osu.Game/Input/KeyBindingStore.cs | 2 +- .../KeyBinding/GlobalKeyBindingsSection.cs | 4 +-- osu.Game/Rulesets/UI/RulesetInputManager.cs | 25 +++++++++++++++---- 5 files changed, 28 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/OsuInputManager.cs b/osu.Game.Rulesets.Osu/OsuInputManager.cs index a65d28cec0..56990d1351 100644 --- a/osu.Game.Rulesets.Osu/OsuInputManager.cs +++ b/osu.Game.Rulesets.Osu/OsuInputManager.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 System.ComponentModel; using osu.Framework.Input.Bindings; using osu.Game.Rulesets.UI; @@ -9,6 +10,8 @@ namespace osu.Game.Rulesets.Osu { public class OsuInputManager : RulesetInputManager { + public IEnumerable PressedActions => KeyBindingContainer.PressedActions; + public OsuInputManager(RulesetInfo ruleset) : base(ruleset, 0, SimultaneousBindingMode.Unique) { } diff --git a/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs b/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs index bae14fc1dc..784e6462f2 100644 --- a/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs +++ b/osu.Game/Input/Bindings/DatabasedKeyBindingInputManager.cs @@ -14,7 +14,7 @@ namespace osu.Game.Input.Bindings /// A KeyBindingInputManager with a database backing for custom overrides. /// /// The type of the custom action. - public abstract class DatabasedKeyBindingInputManager : KeyBindingInputManager + public class DatabasedKeyBindingInputManager : KeyBindingContainer where T : struct { private readonly RulesetInfo ruleset; @@ -31,7 +31,7 @@ namespace osu.Game.Input.Bindings /// A reference to identify the current . Used to lookup mappings. Null for global mappings. /// An optional variant for the specified . Used when a ruleset has more than one possible keyboard layouts. /// Specify how to deal with multiple matches of s and s. - protected DatabasedKeyBindingInputManager(RulesetInfo ruleset = null, int? variant = null, SimultaneousBindingMode simultaneousMode = SimultaneousBindingMode.None) + public DatabasedKeyBindingInputManager(RulesetInfo ruleset = null, int? variant = null, SimultaneousBindingMode simultaneousMode = SimultaneousBindingMode.None) : base(simultaneousMode) { this.ruleset = ruleset; diff --git a/osu.Game/Input/KeyBindingStore.cs b/osu.Game/Input/KeyBindingStore.cs index 95791391f0..9e2988417a 100644 --- a/osu.Game/Input/KeyBindingStore.cs +++ b/osu.Game/Input/KeyBindingStore.cs @@ -27,7 +27,7 @@ namespace osu.Game.Input } } - public void Register(KeyBindingInputManager manager) => insertDefaults(manager.DefaultKeyBindings); + public void Register(KeyBindingContainer manager) => insertDefaults(manager.DefaultKeyBindings); private void insertDefaults(IEnumerable defaults, int? rulesetId = null, int? variant = null) { diff --git a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs index 8ebd4ac545..4a7e4f4e6e 100644 --- a/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs +++ b/osu.Game/Overlays/KeyBinding/GlobalKeyBindingsSection.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.KeyBinding public override FontAwesome Icon => FontAwesome.fa_osu_hot; public override string Header => "Global"; - public GlobalKeyBindingsSection(KeyBindingInputManager manager) + public GlobalKeyBindingsSection(KeyBindingContainer manager) { Add(new DefaultBindingsSubsection(manager)); } @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.KeyBinding { protected override string Header => string.Empty; - public DefaultBindingsSubsection(KeyBindingInputManager manager) + public DefaultBindingsSubsection(KeyBindingContainer manager) : base(null) { Defaults = manager.DefaultKeyBindings; diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 8c4d6de1fe..5cd79cff29 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -5,6 +5,8 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Configuration; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Timing; @@ -16,11 +18,24 @@ using OpenTK.Input; namespace osu.Game.Rulesets.UI { - public abstract class RulesetInputManager : DatabasedKeyBindingInputManager, ICanAttachKeyCounter, IHasReplayHandler + public abstract class RulesetInputManager : PassThroughInputManager, ICanAttachKeyCounter, IHasReplayHandler where T : struct { - protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) : base(ruleset, variant, unique) + public class RulesetKeyBindingContainer : DatabasedKeyBindingInputManager { + public RulesetKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) + : base(ruleset, variant, unique) + { + } + } + + protected readonly KeyBindingContainer KeyBindingContainer; + + protected override Container Content => KeyBindingContainer; + + protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) + { + InternalChild = KeyBindingContainer = new RulesetKeyBindingContainer(ruleset, variant, unique); } #region Action mapping (for replays) @@ -41,10 +56,10 @@ namespace osu.Game.Rulesets.UI List newActions = replayState.PressedActions; foreach (var released in lastPressedActions.Except(newActions)) - PropagateReleased(KeyBindingInputQueue, released); + KeyBindingContainer.TriggerReleased(released); foreach (var pressed in newActions.Except(lastPressedActions)) - PropagatePressed(KeyBindingInputQueue, pressed); + KeyBindingContainer.TriggerPressed(pressed); lastPressedActions = newActions; } @@ -203,7 +218,7 @@ namespace osu.Game.Rulesets.UI Add(receptor); keyCounter.SetReceptor(receptor); - keyCounter.AddRange(DefaultKeyBindings.Select(b => b.GetAction()).Distinct().Select(b => new KeyCounterAction(b))); + keyCounter.AddRange(KeyBindingContainer.DefaultKeyBindings.Select(b => b.GetAction()).Distinct().Select(b => new KeyCounterAction(b))); } public class ActionReceptor : KeyCounterCollection.Receptor, IKeyBindingHandler From 2a1a9b9f1f8454a595daeb89b012410541b9661c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Dec 2017 17:26:39 +0900 Subject: [PATCH 200/235] Fix post-merge issue --- osu.Game/Beatmaps/WorkingBeatmap.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index c451460a78..736cc2a0b0 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -87,7 +87,7 @@ namespace osu.Game.Beatmaps private Waveform populateWaveform() => GetWaveform(); - public bool StoryboardLoaded => storyboard.IsValueCreated; + public bool StoryboardLoaded => storyboard.IsResultAvailable; public Storyboard Storyboard => storyboard.Value.Result; public async Task GetStoryboardAsync() => await storyboard.Value; private readonly AsyncLazy storyboard; From 13d0524542bed68cc93a559b64d45103c29bb6ac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Dec 2017 18:36:49 +0900 Subject: [PATCH 201/235] Update framework --- osu-framework | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu-framework b/osu-framework index 9cd6968a8c..797a351db2 160000 --- a/osu-framework +++ b/osu-framework @@ -1 +1 @@ -Subproject commit 9cd6968a8c4d27415808f5e770d24fec5a44485f +Subproject commit 797a351db2e852fef5296453641ffbf6b2f6dc11 From 8c89354b36638a36def5a25ea70e6f96ac738c96 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Dec 2017 20:44:38 +0900 Subject: [PATCH 202/235] Remove extra whitespace --- osu.Game/Overlays/BeatmapSet/HeaderButton.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs index cddc05c554..ac5683de00 100644 --- a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs +++ b/osu.Game/Overlays/BeatmapSet/HeaderButton.cs @@ -10,7 +10,6 @@ namespace osu.Game.Overlays.BeatmapSet { public class HeaderButton : TriangleButton { - public HeaderButton() { Height = 0; @@ -25,6 +24,5 @@ namespace osu.Game.Overlays.BeatmapSet Triangles.ColourDark = OsuColour.FromHex(@"094c5f"); Triangles.TriangleScale = 1.5f; } - } } From 24b3b10942bca6ef24e531e4e1a12adbc57be3c9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Dec 2017 20:44:55 +0900 Subject: [PATCH 203/235] Move drawable assignment to load, remove depth overrides --- osu.Game/Overlays/BeatmapSet/FavouriteButton.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs b/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs index 77be4da5f8..1b22853656 100644 --- a/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs +++ b/osu.Game/Overlays/BeatmapSet/FavouriteButton.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 osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -15,16 +16,15 @@ namespace osu.Game.Overlays.BeatmapSet { public readonly Bindable Favourited = new Bindable(); - public FavouriteButton() + [BackgroundDependencyLoader] + private void load() { - Container pink; SpriteIcon icon; AddRange(new Drawable[] { pink = new Container { - Depth = -1, RelativeSizeAxes = Axes.Both, Alpha = 0f, Children = new Drawable[] @@ -45,7 +45,6 @@ namespace osu.Game.Overlays.BeatmapSet }, icon = new SpriteIcon { - Depth = -1, Anchor = Anchor.Centre, Origin = Anchor.Centre, Icon = FontAwesome.fa_heart_o, From d52b84df46ee3e5662a058cc8de2a8d4414ff554 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 7 Dec 2017 20:53:28 +0900 Subject: [PATCH 204/235] Move KeyboardStep assignment to ctor --- osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index 53704ec72d..392bc6f1bd 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -80,8 +80,6 @@ namespace osu.Game.Overlays.Settings.Sections.Input set { - KeyboardStep = 0.01f; - BindableDouble doubleValue = (BindableDouble)value; // create a second layer of bindable so we can only handle state changes when not being dragged. @@ -99,6 +97,11 @@ namespace osu.Game.Overlays.Settings.Sections.Input doubleValue.ValueChanged += newValue => base.Bindable.Value = newValue; } } + + public SensitivitySetting() + { + KeyboardStep = 0.01f; + } } private class SensitivitySlider : OsuSliderBar From 76c09ae59ef513cf899f0ca0310252d3d0e2e8b5 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Thu, 7 Dec 2017 13:44:47 +0100 Subject: [PATCH 205/235] added comments for local context checking --- osu.Game/Beatmaps/BeatmapStore.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index 352f793aac..b23f093786 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -34,11 +34,12 @@ namespace osu.Game.Beatmaps foreach (var beatmap in beatmapSet.Beatmaps.Where(b => b.Metadata != null)) { + // check local context for metadata so we can reuse duplicates from the same set var contextMetadata = context.Set().Local.SingleOrDefault(e => e.Equals(beatmap.Metadata)); if (contextMetadata != null) - beatmap.Metadata = contextMetadata; + beatmap.Metadata = contextMetadata; // reuse existing else - context.BeatmapMetadata.Attach(beatmap.Metadata); + context.BeatmapMetadata.Attach(beatmap.Metadata); // adding new to context } context.BeatmapSetInfo.Attach(beatmapSet); From 1dcbfab18e97a3c39a01ceeeb42bb1096519269e Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Thu, 7 Dec 2017 13:56:37 +0100 Subject: [PATCH 206/235] removed redundant comment --- osu.Game/Beatmaps/BeatmapStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index b23f093786..ac40b3378a 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -37,7 +37,7 @@ namespace osu.Game.Beatmaps // check local context for metadata so we can reuse duplicates from the same set var contextMetadata = context.Set().Local.SingleOrDefault(e => e.Equals(beatmap.Metadata)); if (contextMetadata != null) - beatmap.Metadata = contextMetadata; // reuse existing + beatmap.Metadata = contextMetadata; else context.BeatmapMetadata.Attach(beatmap.Metadata); // adding new to context } From 95955d68eff35ed128d90aab1ec1ca532bb4e3a1 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Thu, 7 Dec 2017 14:14:50 +0100 Subject: [PATCH 207/235] rephrased description of local context checking --- osu.Game/Beatmaps/BeatmapStore.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index ac40b3378a..fb45c17454 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -34,12 +34,14 @@ namespace osu.Game.Beatmaps foreach (var beatmap in beatmapSet.Beatmaps.Where(b => b.Metadata != null)) { - // check local context for metadata so we can reuse duplicates from the same set + // If we detect a new metadata object it'll be attached to the current context so it can be reused + // to prevent duplicate entries when persisting. To accomplish this we look in the cache (.Local) + // of the corresponding table (.Set()) for matching entries to our criteria. var contextMetadata = context.Set().Local.SingleOrDefault(e => e.Equals(beatmap.Metadata)); if (contextMetadata != null) beatmap.Metadata = contextMetadata; else - context.BeatmapMetadata.Attach(beatmap.Metadata); // adding new to context + context.BeatmapMetadata.Attach(beatmap.Metadata); } context.BeatmapSetInfo.Attach(beatmapSet); From 105d01d85b9616f9d8be3ea9ea69893df8f882a1 Mon Sep 17 00:00:00 2001 From: Aergwyn Date: Thu, 7 Dec 2017 22:02:53 +0100 Subject: [PATCH 208/235] fix crash when restoring from any non-SongSelect-screen removed unwanted refresh --- osu.Game/Screens/Select/BeatmapCarousel.cs | 29 ++++++++-------------- osu.Game/Screens/Select/SongSelect.cs | 13 ++++++++-- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index fe981f1f2a..cbeefcdda5 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -112,29 +112,26 @@ namespace osu.Game.Screens.Select Schedule(() => removeGroup(groups.Find(b => b.BeatmapSet.ID == beatmapSet.ID))); } - public void UpdateBeatmap(BeatmapInfo beatmap) + public void UpdateBeatmapSet(BeatmapSetInfo beatmapSet) { - // todo: this method should not run more than once for the same BeatmapSetInfo. - var set = manager.QueryBeatmapSet(s => s.ID == beatmap.BeatmapSetInfoID); - // todo: this method should be smarter as to not recreate panels that haven't changed, etc. - var group = groups.Find(b => b.BeatmapSet.ID == set.ID); + var oldGroup = groups.Find(b => b.BeatmapSet.ID == beatmapSet.ID); - int i = groups.IndexOf(group); - if (i >= 0) - groups.RemoveAt(i); + var newGroup = createGroup(beatmapSet); - var newGroup = createGroup(set); + int index = groups.IndexOf(oldGroup); + if (index >= 0) + groups.RemoveAt(index); if (newGroup != null) { - if (i >= 0) - groups.Insert(i, newGroup); + if (index >= 0) + groups.Insert(index, newGroup); else addGroup(newGroup); } - bool hadSelection = selectedGroup == group; + bool hadSelection = selectedGroup == oldGroup; if (hadSelection && newGroup == null) selectedGroup = null; @@ -147,8 +144,8 @@ namespace osu.Game.Screens.Select var newSelection = newGroup.BeatmapPanels.Find(p => p.Beatmap.ID == selectedPanel?.Beatmap.ID); - if(newSelection == null && group != null && selectedPanel != null) - newSelection = newGroup.BeatmapPanels[Math.Min(newGroup.BeatmapPanels.Count - 1, group.BeatmapPanels.IndexOf(selectedPanel))]; + if(newSelection == null && oldGroup != null && selectedPanel != null) + newSelection = newGroup.BeatmapPanels[Math.Min(newGroup.BeatmapPanels.Count - 1, oldGroup.BeatmapPanels.IndexOf(selectedPanel))]; selectGroup(newGroup, newSelection); } @@ -306,8 +303,6 @@ namespace osu.Game.Screens.Select if (newCriteria != null) criteria = newCriteria; - if (!IsLoaded) return; - Action perform = delegate { filterTask = null; @@ -348,8 +343,6 @@ namespace osu.Game.Screens.Select private BeatmapGroup createGroup(BeatmapSetInfo beatmapSet) { - beatmapSet = manager.Refresh(beatmapSet); - if (beatmapSet.Beatmaps.All(b => b.Hidden)) return null; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 0f72e3fd95..ee231ff5f4 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -203,8 +203,17 @@ namespace osu.Game.Screens.Select Push(new Editor()); } - private void onBeatmapRestored(BeatmapInfo b) => Schedule(() => carousel.UpdateBeatmap(b)); - private void onBeatmapHidden(BeatmapInfo b) => Schedule(() => carousel.UpdateBeatmap(b)); + private void onBeatmapRestored(BeatmapInfo beatmap) + { + var beatmapSet = beatmaps.QueryBeatmapSet(s => s.ID == beatmap.BeatmapSetInfoID); + Schedule(() => carousel.UpdateBeatmapSet(beatmapSet)); + } + + private void onBeatmapHidden(BeatmapInfo beatmap) + { + var beatmapSet = beatmaps.QueryBeatmapSet(s => s.ID == beatmap.BeatmapSetInfoID); + Schedule(() => carousel.UpdateBeatmapSet(beatmapSet)); + } private void carouselBeatmapsLoaded() { From 2e1dfa16a28c3bbcae530d8308aec726374cd052 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Dec 2017 14:59:32 +0900 Subject: [PATCH 209/235] Fix checks disallowing import of older beatmaps with no embedded online IDs --- osu.Game/Beatmaps/BeatmapManager.cs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index edbda1a685..6d6b1cb3f4 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -497,15 +497,21 @@ namespace osu.Game.Beatmaps using (var stream = new StreamReader(reader.GetStream(mapName))) metadata = Decoder.GetDecoder(stream).DecodeBeatmap(stream).Metadata; + // check if a set already exists with the same online id. - beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.OnlineBeatmapSetID == metadata.OnlineBeatmapSetID) ?? new BeatmapSetInfo - { - OnlineBeatmapSetID = metadata.OnlineBeatmapSetID, - Beatmaps = new List(), - Hash = hash, - Files = fileInfos, - Metadata = metadata - }; + if (metadata.OnlineBeatmapSetID != null) + beatmapSet = beatmaps.BeatmapSets.FirstOrDefault(b => b.OnlineBeatmapSetID == metadata.OnlineBeatmapSetID); + + if (beatmapSet == null) + beatmapSet = new BeatmapSetInfo + { + OnlineBeatmapSetID = metadata.OnlineBeatmapSetID, + Beatmaps = new List(), + Hash = hash, + Files = fileInfos, + Metadata = metadata + }; + var mapNames = reader.Filenames.Where(f => f.EndsWith(".osu")); @@ -525,7 +531,7 @@ namespace osu.Game.Beatmaps beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash(); beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash(); - var existing = beatmaps.Beatmaps.FirstOrDefault(b => b.Hash == beatmap.BeatmapInfo.Hash || b.OnlineBeatmapID == beatmap.BeatmapInfo.OnlineBeatmapID); + var existing = beatmaps.Beatmaps.FirstOrDefault(b => b.Hash == beatmap.BeatmapInfo.Hash || (beatmap.BeatmapInfo.OnlineBeatmapID != null && b.OnlineBeatmapID == beatmap.BeatmapInfo.OnlineBeatmapID)); if (existing == null) { From 31884a951aada0914546c5bd4505038da70d640c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Dec 2017 15:53:59 +0900 Subject: [PATCH 210/235] Remove "redundant" parenthesis --- osu.Game/Beatmaps/BeatmapManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 6d6b1cb3f4..f599e8a108 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -531,7 +531,7 @@ namespace osu.Game.Beatmaps beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash(); beatmap.BeatmapInfo.MD5Hash = ms.ComputeMD5Hash(); - var existing = beatmaps.Beatmaps.FirstOrDefault(b => b.Hash == beatmap.BeatmapInfo.Hash || (beatmap.BeatmapInfo.OnlineBeatmapID != null && b.OnlineBeatmapID == beatmap.BeatmapInfo.OnlineBeatmapID)); + var existing = beatmaps.Beatmaps.FirstOrDefault(b => b.Hash == beatmap.BeatmapInfo.Hash || beatmap.BeatmapInfo.OnlineBeatmapID != null && b.OnlineBeatmapID == beatmap.BeatmapInfo.OnlineBeatmapID); if (existing == null) { From 51dc66df12ee244544757498c8a144f4d53352a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Dec 2017 18:45:06 +0900 Subject: [PATCH 211/235] Add support for relevance based search results in osu!direct --- osu.Game/Overlays/Direct/FilterControl.cs | 1 + osu.Game/Overlays/DirectOverlay.cs | 26 +++++++++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Direct/FilterControl.cs b/osu.Game/Overlays/Direct/FilterControl.cs index 9b52cfd367..46ba000a28 100644 --- a/osu.Game/Overlays/Direct/FilterControl.cs +++ b/osu.Game/Overlays/Direct/FilterControl.cs @@ -105,6 +105,7 @@ namespace osu.Game.Overlays.Direct public enum DirectSortCriteria { + Relevance, Title, Artist, Creator, diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs index 0b7a30797d..7994483043 100644 --- a/osu.Game/Overlays/DirectOverlay.cs +++ b/osu.Game/Overlays/DirectOverlay.cs @@ -43,6 +43,7 @@ namespace osu.Game.Overlays protected override SearchableListFilterControl CreateFilterControl() => new FilterControl(); private IEnumerable beatmapSets; + public IEnumerable BeatmapSets { get { return beatmapSets; } @@ -69,6 +70,7 @@ namespace osu.Game.Overlays } private ResultCounts resultAmounts; + public ResultCounts ResultAmounts { get { return resultAmounts; } @@ -115,7 +117,23 @@ namespace osu.Game.Overlays }, }; - Filter.Search.Current.ValueChanged += text => { if (text != string.Empty) Header.Tabs.Current.Value = DirectTab.Search; }; + Filter.Search.Current.ValueChanged += text => + { + if (text != string.Empty) + { + Header.Tabs.Current.Value = DirectTab.Search; + + if (Filter.Tabs.Current.Value == DirectSortCriteria.Ranked) + Filter.Tabs.Current.Value = DirectSortCriteria.Relevance; + } + else + { + Header.Tabs.Current.Value = DirectTab.NewestMaps; + + if (Filter.Tabs.Current.Value == DirectSortCriteria.Relevance) + Filter.Tabs.Current.Value = DirectSortCriteria.Ranked; + } + }; ((FilterControl)Filter).Ruleset.ValueChanged += ruleset => Scheduler.AddOnce(updateSearch); Filter.DisplayStyleControl.DisplayStyle.ValueChanged += recreatePanels; Filter.DisplayStyleControl.Dropdown.Current.ValueChanged += rankStatus => Scheduler.AddOnce(updateSearch); @@ -271,9 +289,9 @@ namespace osu.Game.Overlays if (Header.Tabs.Current.Value == DirectTab.Search && (Filter.Search.Text == string.Empty || currentQuery == string.Empty)) return; getSetsRequest = new SearchBeatmapSetsRequest(currentQuery.Value ?? string.Empty, - ((FilterControl)Filter).Ruleset.Value, - Filter.DisplayStyleControl.Dropdown.Current.Value, - Filter.Tabs.Current.Value); //todo: sort direction (?) + ((FilterControl)Filter).Ruleset.Value, + Filter.DisplayStyleControl.Dropdown.Current.Value, + Filter.Tabs.Current.Value); //todo: sort direction (?) getSetsRequest.Success += response => { From 679134c0306c5c74d7f1776d335ca075eecd836a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 8 Dec 2017 18:55:25 +0900 Subject: [PATCH 212/235] Add ShortName to rulesets --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 2 ++ osu.Game.Rulesets.Mania/ManiaRuleset.cs | 2 ++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 ++ osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 2 ++ osu.Game/Beatmaps/DummyWorkingBeatmap.cs | 2 ++ osu.Game/Rulesets/Ruleset.cs | 19 ++++++++++++------- 6 files changed, 22 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 1d5fc0545e..cb46c75583 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -93,6 +93,8 @@ namespace osu.Game.Rulesets.Catch public override string Description => "osu!catch"; + public override string ShortName => "fruits"; + public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_fruits_o }; public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new CatchDifficultyCalculator(beatmap); diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 4eea884891..070c7b09d1 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -105,6 +105,8 @@ namespace osu.Game.Rulesets.Mania public override string Description => "osu!mania"; + public override string ShortName => "mania"; + public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o }; public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new ManiaDifficultyCalculator(beatmap); diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 0d2343a33e..bb5700976d 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -124,6 +124,8 @@ namespace osu.Game.Rulesets.Osu public override string Description => "osu!"; + public override string ShortName => "osu"; + public override SettingsSubsection CreateSettings() => new OsuSettings(); public override int LegacyID => 0; diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 99ae36967a..4de9ba0ce7 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -95,6 +95,8 @@ namespace osu.Game.Rulesets.Taiko public override string Description => "osu!taiko"; + public override string ShortName => "taiko"; + public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_taiko_o }; public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new TaikoDifficultyCalculator(beatmap); diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index c187aa592a..1434943da0 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -63,6 +63,8 @@ namespace osu.Game.Beatmaps public override string Description => "dummy"; + public override string ShortName => "dummy"; + public DummyRuleset(RulesetInfo rulesetInfo) : base(rulesetInfo) { diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index df5e12a24f..642647ec69 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -23,13 +23,13 @@ namespace osu.Game.Rulesets public virtual IEnumerable GetBeatmapStatistics(WorkingBeatmap beatmap) => new BeatmapStatistic[] { }; public IEnumerable GetAllMods() => Enum.GetValues(typeof(ModType)).Cast() - // Get all mod types as an IEnumerable - .SelectMany(GetModsFor) - // Confine all mods of each mod type into a single IEnumerable - .Where(mod => mod != null) - // Filter out all null mods - .SelectMany(mod => (mod as MultiMod)?.Mods ?? new[] { mod }); - // Resolve MultiMods as their .Mods property + // Get all mod types as an IEnumerable + .SelectMany(GetModsFor) + // Confine all mods of each mod type into a single IEnumerable + .Where(mod => mod != null) + // Filter out all null mods + .SelectMany(mod => (mod as MultiMod)?.Mods ?? new[] { mod }); + // Resolve MultiMods as their .Mods property public abstract IEnumerable GetModsFor(ModType type); @@ -66,6 +66,11 @@ namespace osu.Game.Rulesets /// public virtual int LegacyID => -1; + /// + /// A unique short name to reference this ruleset in online requests. + /// + public abstract string ShortName { get; } + /// /// A list of available variant ids. /// From 4a723f738290cea4cf593cd30762ebeec1dc3976 Mon Sep 17 00:00:00 2001 From: FreezyLemon Date: Fri, 8 Dec 2017 11:05:00 +0100 Subject: [PATCH 213/235] Added the "Service Include" tag that is used by Visual Studio's test explorer to the .csproj files --- osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj | 3 +++ osu.Game.Rulesets.Mania/osu.Game.Rulesets.Mania.csproj | 3 +++ osu.Game.Rulesets.Osu/osu.Game.Rulesets.Osu.csproj | 4 +++- osu.Game.Rulesets.Taiko/osu.Game.Rulesets.Taiko.csproj | 3 +++ osu.Game/osu.Game.csproj | 3 +++ 5 files changed, 15 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj index 969ee702e3..b03c8d2eea 100644 --- a/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj +++ b/osu.Game.Rulesets.Catch/osu.Game.Rulesets.Catch.csproj @@ -90,6 +90,9 @@ True + + +