diff --git a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs index 89e8361a72..4b95a6754e 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs @@ -13,11 +13,12 @@ using osu.Game.Tests.Beatmaps; namespace osu.Game.Rulesets.Catch.Tests { - internal class CatchBeatmapConversionTest : BeatmapConversionTest + [TestFixture] + public class CatchBeatmapConversionTest : BeatmapConversionTest { protected override string ResourceAssembly => "osu.Game.Rulesets.Catch"; - [TestCase("basic"), Ignore("See: https://github.com/ppy/osu/issues/2232")] + [TestCase("basic")] [TestCase("spinner")] [TestCase("spinner-and-circles")] public new void Test(string name) @@ -44,7 +45,7 @@ namespace osu.Game.Rulesets.Catch.Tests protected override Ruleset CreateRuleset() => new CatchRuleset(); } - internal struct ConvertValue : IEquatable + public struct ConvertValue : IEquatable { /// /// A sane value to account for osu!stable using ints everwhere. diff --git a/osu.Game.Rulesets.Catch.Tests/TestCaseFruitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestCaseFruitObjects.cs index e77dd76353..5c41e4136c 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestCaseFruitObjects.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestCaseFruitObjects.cs @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Catch.Tests private DrawableFruit createDrawable(int index) { Fruit fruit = index == 5 - ? new BananaShower.Banana + ? new Banana { StartTime = 1000000000000, IndexInBeatmap = index, diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs index 46fe8454dc..68a8dfb7d3 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs @@ -26,6 +26,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps var positionData = obj as IHasXPosition; var comboData = obj as IHasCombo; var endTime = obj as IHasEndTime; + var legacyOffset = obj as IHasLegacyLastTickOffset; if (curveData != null) { @@ -39,7 +40,8 @@ namespace osu.Game.Rulesets.Catch.Beatmaps RepeatSamples = curveData.RepeatSamples, RepeatCount = curveData.RepeatCount, X = (positionData?.X ?? 0) / CatchPlayfield.BASE_WIDTH, - NewCombo = comboData?.NewCombo ?? false + NewCombo = comboData?.NewCombo ?? false, + LegacyLastTickOffset = legacyOffset?.LegacyLastTickOffset ?? 0 }; } else if (endTime != null) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 8473f5a36c..33fc838d35 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -49,9 +49,9 @@ namespace osu.Game.Rulesets.Catch.Beatmaps switch (obj) { case BananaShower bananaShower: - foreach (var nested in bananaShower.NestedHitObjects) + foreach (var banana in bananaShower.NestedHitObjects.OfType()) { - ((BananaShower.Banana)nested).X = (float)rng.NextDouble(); + banana.X = (float)rng.NextDouble(); rng.Next(); // osu!stable retrieved a random banana type rng.Next(); // osu!stable retrieved a random banana rotation rng.Next(); // osu!stable retrieved a random banana colour diff --git a/osu.Game.Rulesets.Catch/Judgements/CatchBananaJudgement.cs b/osu.Game.Rulesets.Catch/Judgements/CatchBananaJudgement.cs new file mode 100644 index 0000000000..c39e663d75 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Judgements/CatchBananaJudgement.cs @@ -0,0 +1,36 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Catch.Judgements +{ + public class CatchBananaJudgement : CatchJudgement + { + public override bool AffectsCombo => false; + + public override bool ShouldExplode => true; + + protected override int NumericResultFor(HitResult result) + { + switch (result) + { + default: + return 0; + case HitResult.Perfect: + return 1100; + } + } + + protected override float HealthIncreaseFor(HitResult result) + { + switch (result) + { + default: + return 0; + case HitResult.Perfect: + return 8; + } + } + } +} diff --git a/osu.Game.Rulesets.Catch/Judgements/CatchDropletJudgement.cs b/osu.Game.Rulesets.Catch/Judgements/CatchDropletJudgement.cs new file mode 100644 index 0000000000..0df2305339 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Judgements/CatchDropletJudgement.cs @@ -0,0 +1,32 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Catch.Judgements +{ + public class CatchDropletJudgement : CatchJudgement + { + protected override int NumericResultFor(HitResult result) + { + switch (result) + { + default: + return 0; + case HitResult.Perfect: + return 30; + } + } + + protected override float HealthIncreaseFor(HitResult result) + { + switch (result) + { + default: + return 0; + case HitResult.Perfect: + return 7; + } + } + } +} diff --git a/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs b/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs index bb2786f14f..51d7d3b5cd 100644 --- a/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs +++ b/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs @@ -2,11 +2,51 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Catch.Judgements { public class CatchJudgement : Judgement { - // todo: wangs + public override HitResult MaxResult => HitResult.Perfect; + + protected override int NumericResultFor(HitResult result) + { + switch (result) + { + default: + return 0; + case HitResult.Perfect: + return 300; + } + } + + /// + /// The base health increase for the result achieved. + /// + public float HealthIncrease => HealthIncreaseFor(Result); + + /// + /// Whether fruit on the platter should explode or drop. + /// Note that this is only checked if the owning object is also + /// + public virtual bool ShouldExplode => IsHit; + + /// + /// Convert a to a base health increase. + /// + /// The value to convert. + /// The base health increase. + protected virtual float HealthIncreaseFor(HitResult result) + { + switch (result) + { + default: + return 0; + case HitResult.Perfect: + return 10.2f; + } + } } } diff --git a/osu.Game.Rulesets.Catch/Judgements/CatchTinyDropletJudgement.cs b/osu.Game.Rulesets.Catch/Judgements/CatchTinyDropletJudgement.cs new file mode 100644 index 0000000000..8b77351027 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Judgements/CatchTinyDropletJudgement.cs @@ -0,0 +1,34 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Catch.Judgements +{ + public class CatchTinyDropletJudgement : CatchJudgement + { + public override bool AffectsCombo => false; + + protected override int NumericResultFor(HitResult result) + { + switch (result) + { + default: + return 0; + case HitResult.Perfect: + return 10; + } + } + + protected override float HealthIncreaseFor(HitResult result) + { + switch (result) + { + default: + return 0; + case HitResult.Perfect: + return 4; + } + } + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/Banana.cs b/osu.Game.Rulesets.Catch/Objects/Banana.cs new file mode 100644 index 0000000000..f7c60a7a47 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Objects/Banana.cs @@ -0,0 +1,10 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Catch.Objects +{ + public class Banana : Fruit + { + public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana; + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs b/osu.Game.Rulesets.Catch/Objects/BananaShower.cs index 4dd491966c..25af7e4bdf 100644 --- a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs +++ b/osu.Game.Rulesets.Catch/Objects/BananaShower.cs @@ -37,10 +37,5 @@ namespace osu.Game.Rulesets.Catch.Objects public double EndTime => StartTime + Duration; public double Duration { get; set; } - - public class Banana : Fruit - { - public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana; - } } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBanana.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBanana.cs new file mode 100644 index 0000000000..dd027abbe0 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBanana.cs @@ -0,0 +1,17 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Rulesets.Catch.Judgements; + +namespace osu.Game.Rulesets.Catch.Objects.Drawable +{ + public class DrawableBanana : DrawableFruit + { + public DrawableBanana(Banana h) + : base(h) + { + } + + protected override CatchJudgement CreateJudgement() => new CatchBananaJudgement(); + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBananaShower.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBananaShower.cs index 739cc6a59b..f039504600 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBananaShower.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBananaShower.cs @@ -5,9 +5,7 @@ using System; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Catch.Objects.Drawable { @@ -24,15 +22,11 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable InternalChild = bananaContainer = new Container { RelativeSizeAxes = Axes.Both }; - foreach (var b in s.NestedHitObjects.Cast()) + foreach (var b in s.NestedHitObjects.Cast()) AddNested(getVisualRepresentation?.Invoke(b)); } - protected override void CheckForJudgements(bool userTriggered, double timeOffset) - { - if (timeOffset >= 0) - AddJudgement(new Judgement { Result = NestedHitObjects.Cast().Any(n => n.Judgements.Any(j => j.IsHit)) ? HitResult.Perfect : HitResult.Miss }); - } + protected override bool ProvidesJudgement => false; protected override void AddNested(DrawableHitObject h) { diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs index e3564b5967..6ce2e6a2ae 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs @@ -2,14 +2,14 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using OpenTK; +using OpenTK.Graphics; using osu.Framework.Graphics; -using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Catch.Judgements; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; -using OpenTK; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; -using OpenTK.Graphics; namespace osu.Game.Rulesets.Catch.Objects.Drawable { @@ -58,9 +58,15 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable if (CheckPosition == null) return; if (timeOffset >= 0) - AddJudgement(new Judgement { Result = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss }); + { + var judgement = CreateJudgement(); + judgement.Result = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss; + AddJudgement(judgement); + } } + protected virtual CatchJudgement CreateJudgement() => new CatchJudgement(); + protected override void SkinChanged(ISkinSource skin, bool allowFallback) { base.SkinChanged(skin, allowFallback); diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs index 5c8a7c4a7c..11d5ed1f92 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics; using osu.Game.Rulesets.Catch.Objects.Drawable.Pieces; using OpenTK; using OpenTK.Graphics; +using osu.Game.Rulesets.Catch.Judgements; namespace osu.Game.Rulesets.Catch.Objects.Drawable { @@ -23,6 +24,8 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable Masking = false; } + protected override CatchJudgement CreateJudgement() => new CatchDropletJudgement(); + [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableTinyDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableTinyDroplet.cs new file mode 100644 index 0000000000..2232bb81a7 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableTinyDroplet.cs @@ -0,0 +1,19 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using osu.Game.Rulesets.Catch.Judgements; + +namespace osu.Game.Rulesets.Catch.Objects.Drawable +{ + public class DrawableTinyDroplet : DrawableDroplet + { + public DrawableTinyDroplet(Droplet h) + : base(h) + { + Size = new Vector2((float)CatchHitObject.OBJECT_RADIUS) / 8; + } + + protected override CatchJudgement CreateJudgement() => new CatchTinyDropletJudgement(); + } +} diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index 6fe9692c26..0344189af5 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -77,6 +77,13 @@ namespace osu.Game.Rulesets.Catch.Objects double time = spanStartTime + timeProgress * spanDuration; + if (LegacyLastTickOffset != null) + { + // If we're the last tick, apply the legacy offset + if (span == this.SpanCount() - 1 && d + tickDistance > length) + time = Math.Max(StartTime + Duration / 2, time - LegacyLastTickOffset.Value); + } + double tinyTickInterval = time - lastDropletTime; while (tinyTickInterval > 100) tinyTickInterval /= 2; @@ -152,5 +159,7 @@ namespace osu.Game.Rulesets.Catch.Objects get { return Curve.CurveType; } set { Curve.CurveType = value; } } + + public double? LegacyLastTickOffset { get; set; } } } diff --git a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs index 936ab6a9d3..23b620248f 100644 --- a/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs +++ b/osu.Game.Rulesets.Catch/Replays/CatchAutoGenerator.cs @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Catch.Replays return; } - if (h is BananaShower.Banana) + if (h is Banana) { // auto bananas unrealistically warp to catch 100% combo. Replay.Frames.Add(new CatchReplayFrame(h.StartTime, h.X)); @@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.Catch.Replays { switch (nestedObj) { - case BananaShower.Banana _: + case Banana _: case TinyDroplet _: case Droplet _: case Fruit _: diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs index ce1aee5c34..5b69d836a3 100644 --- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs @@ -1,10 +1,12 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Judgements; using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; @@ -17,28 +19,57 @@ namespace osu.Game.Rulesets.Catch.Scoring { } + private float hpDrainRate; + protected override void SimulateAutoplay(Beatmap beatmap) { + hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate; + foreach (var obj in beatmap.HitObjects) { switch (obj) { case JuiceStream stream: - foreach (var _ in stream.NestedHitObjects.Cast()) - AddJudgement(new CatchJudgement { Result = HitResult.Perfect }); + foreach (var nestedObject in stream.NestedHitObjects) + switch (nestedObject) + { + case TinyDroplet _: + AddJudgement(new CatchTinyDropletJudgement { Result = HitResult.Perfect }); + break; + case Droplet _: + AddJudgement(new CatchDropletJudgement { Result = HitResult.Perfect }); + break; + case Fruit _: + AddJudgement(new CatchJudgement { Result = HitResult.Perfect }); + break; + } break; case BananaShower shower: foreach (var _ in shower.NestedHitObjects.Cast()) - AddJudgement(new CatchJudgement { Result = HitResult.Perfect }); - AddJudgement(new CatchJudgement { Result = HitResult.Perfect }); + AddJudgement(new CatchBananaJudgement { Result = HitResult.Perfect }); break; case Fruit _: AddJudgement(new CatchJudgement { Result = HitResult.Perfect }); break; } } + } - base.SimulateAutoplay(beatmap); + private const double harshness = 0.01; + + protected override void OnNewJudgement(Judgement judgement) + { + base.OnNewJudgement(judgement); + + if (judgement.Result == HitResult.Miss) + { + if (!judgement.IsBonus) + Health.Value -= hpDrainRate * (harshness * 2); + return; + } + + if (judgement is CatchJudgement catchJudgement) + Health.Value += Math.Max(catchJudgement.HealthIncrease - hpDrainRate, 0) * harshness; } } } diff --git a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs index 52763e09af..1ac052de4d 100644 --- a/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs +++ b/osu.Game.Rulesets.Catch/UI/CatchRulesetContainer.cs @@ -38,14 +38,16 @@ namespace osu.Game.Rulesets.Catch.UI { switch (h) { + case Banana banana: + return new DrawableBanana(banana); case Fruit fruit: return new DrawableFruit(fruit); case JuiceStream stream: return new DrawableJuiceStream(stream, GetVisualRepresentation); - case BananaShower banana: - return new DrawableBananaShower(banana, GetVisualRepresentation); + case BananaShower shower: + return new DrawableBananaShower(shower, GetVisualRepresentation); case TinyDroplet tiny: - return new DrawableDroplet(tiny) { Scale = new Vector2(0.5f) }; + return new DrawableTinyDroplet(tiny); case Droplet droplet: return new DrawableDroplet(droplet); } diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs index ceb05d349f..9c376f340a 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Textures; using osu.Framework.Input.Bindings; using osu.Framework.MathUtils; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Judgements; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawable; using osu.Game.Rulesets.Catch.Replays; @@ -78,12 +79,11 @@ namespace osu.Game.Rulesets.Catch.UI if (!fruit.StaysOnPlate) runAfterLoaded(() => MovableCatcher.Explode(caughtFruit)); - } if (fruit.HitObject.LastInCombo) { - if (judgement.IsHit) + if (((CatchJudgement)judgement).ShouldExplode) runAfterLoaded(() => MovableCatcher.Explode()); else MovableCatcher.Drop(); diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs index 5ae899f6d6..73555dcecb 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs @@ -12,7 +12,8 @@ using osu.Game.Tests.Beatmaps; namespace osu.Game.Rulesets.Mania.Tests { - internal class ManiaBeatmapConversionTest : BeatmapConversionTest + [TestFixture] + public class ManiaBeatmapConversionTest : BeatmapConversionTest { protected override string ResourceAssembly => "osu.Game.Rulesets.Mania"; @@ -36,7 +37,7 @@ namespace osu.Game.Rulesets.Mania.Tests protected override Ruleset CreateRuleset() => new ManiaRuleset(); } - internal struct ConvertValue : IEquatable + public struct ConvertValue : IEquatable { /// /// A sane value to account for osu!stable using ints everwhere. diff --git a/osu.Game.Rulesets.Mania/Judgements/HoldNoteJudgement.cs b/osu.Game.Rulesets.Mania/Judgements/HoldNoteJudgement.cs index 9630ba9273..9055e48a4c 100644 --- a/osu.Game.Rulesets.Mania/Judgements/HoldNoteJudgement.cs +++ b/osu.Game.Rulesets.Mania/Judgements/HoldNoteJudgement.cs @@ -8,6 +8,7 @@ namespace osu.Game.Rulesets.Mania.Judgements public class HoldNoteJudgement : ManiaJudgement { public override bool AffectsCombo => false; + protected override int NumericResultFor(HitResult result) => 0; } } diff --git a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs index 386ae5eb05..3fa039d946 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs @@ -8,17 +8,19 @@ using osu.Framework.MathUtils; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.UI; using osu.Game.Tests.Beatmaps; -using OpenTK; namespace osu.Game.Rulesets.Osu.Tests { - internal class OsuBeatmapConversionTest : BeatmapConversionTest + [TestFixture] + public class OsuBeatmapConversionTest : BeatmapConversionTest { protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; [TestCase("basic")] [TestCase("colinear-perfect-curve")] + [TestCase("slider-ticks")] public new void Test(string name) { base.Test(name); @@ -26,24 +28,30 @@ namespace osu.Game.Rulesets.Osu.Tests protected override IEnumerable CreateConvertValue(HitObject hitObject) { - var startPosition = (hitObject as IHasPosition)?.Position ?? new Vector2(256, 192); - var endPosition = (hitObject as Slider)?.EndPosition ?? startPosition; - - yield return new ConvertValue + switch (hitObject) { - StartTime = hitObject.StartTime, - EndTime = (hitObject as IHasEndTime)?.EndTime ?? hitObject.StartTime, - StartX = startPosition.X, - StartY = startPosition.Y, - EndX = endPosition.X, - EndY = endPosition.Y + case Slider slider: + foreach (var nested in slider.NestedHitObjects) + yield return createConvertValue(nested); + break; + default: + yield return createConvertValue(hitObject); + break; + } + + ConvertValue createConvertValue(HitObject obj) => new ConvertValue + { + StartTime = obj.StartTime, + EndTime = (obj as IHasEndTime)?.EndTime ?? obj.StartTime, + X = (obj as IHasPosition)?.X ?? OsuPlayfield.BASE_SIZE.X / 2, + Y = (obj as IHasPosition)?.Y ?? OsuPlayfield.BASE_SIZE.Y / 2, }; } protected override Ruleset CreateRuleset() => new OsuRuleset(); } - internal struct ConvertValue : IEquatable + public struct ConvertValue : IEquatable { /// /// A sane value to account for osu!stable using ints everwhere. @@ -52,17 +60,13 @@ namespace osu.Game.Rulesets.Osu.Tests public double StartTime; public double EndTime; - public float StartX; - public float StartY; - public float EndX; - public float EndY; + public float X; + public float Y; public bool Equals(ConvertValue other) - => Precision.AlmostEquals(StartTime, other.StartTime) + => Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience) && Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience) - && Precision.AlmostEquals(StartX, other.StartX) - && Precision.AlmostEquals(StartY, other.StartY, conversion_lenience) - && Precision.AlmostEquals(EndX, other.EndX, conversion_lenience) - && Precision.AlmostEquals(EndY, other.EndY, conversion_lenience); + && Precision.AlmostEquals(X, other.X, conversion_lenience) + && Precision.AlmostEquals(Y, other.Y, conversion_lenience); } } diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs index 80eb808f6e..405493cde4 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs @@ -27,6 +27,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps var endTimeData = original as IHasEndTime; var positionData = original as IHasPosition; var comboData = original as IHasCombo; + var legacyOffset = original as IHasLegacyLastTickOffset; if (curveData != null) { @@ -40,7 +41,8 @@ namespace osu.Game.Rulesets.Osu.Beatmaps RepeatSamples = curveData.RepeatSamples, RepeatCount = curveData.RepeatCount, Position = positionData?.Position ?? Vector2.Zero, - NewCombo = comboData?.NewCombo ?? false + NewCombo = comboData?.NewCombo ?? false, + LegacyLastTickOffset = legacyOffset?.LegacyLastTickOffset }; } else if (endTimeData != null) diff --git a/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs b/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs index b7c4470592..26becfdec9 100644 --- a/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs +++ b/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using OpenTK; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Scoring; @@ -12,11 +11,6 @@ namespace osu.Game.Rulesets.Osu.Judgements { public override HitResult MaxResult => HitResult.Great; - /// - /// The positional hit offset. - /// - public Vector2 PositionOffset; - protected override int NumericResultFor(HitResult result) { switch (result) diff --git a/osu.Game.Rulesets.Osu/Judgements/OsuSliderTailJudgement.cs b/osu.Game.Rulesets.Osu/Judgements/OsuSliderTailJudgement.cs index c4e265aac9..d52de9f971 100644 --- a/osu.Game.Rulesets.Osu/Judgements/OsuSliderTailJudgement.cs +++ b/osu.Game.Rulesets.Osu/Judgements/OsuSliderTailJudgement.cs @@ -8,6 +8,7 @@ namespace osu.Game.Rulesets.Osu.Judgements public class OsuSliderTailJudgement : OsuJudgement { public override bool AffectsCombo => false; + protected override int NumericResultFor(HitResult result) => 0; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 9fe6dcd46c..421c93d485 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -93,7 +93,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables AddJudgement(new OsuJudgement { Result = result, - PositionOffset = Vector2.Zero //todo: set to correct value }); } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index dfab123038..c8544d9cc1 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -93,6 +93,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables base.AccentColour = value; Body.AccentColour = AccentColour; Ball.AccentColour = AccentColour; + if (HasNestedHitObjects) + foreach (var drawableHitObject in NestedHitObjects) + drawableHitObject.AccentColour = AccentColour; } } diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index 54126b934f..befbc01f3c 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -54,9 +54,9 @@ namespace osu.Game.Rulesets.Osu.Objects public virtual bool NewCombo { get; set; } - public int IndexInCurrentCombo { get; set; } + public virtual int IndexInCurrentCombo { get; set; } - public int ComboIndex { get; set; } + public virtual int ComboIndex { get; set; } public bool LastInCombo { get; set; } diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 7f4407370f..698f9de787 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -1,6 +1,7 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; using OpenTK; using osu.Game.Rulesets.Objects.Types; using System.Collections.Generic; @@ -25,6 +26,28 @@ namespace osu.Game.Rulesets.Osu.Objects public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t); public override Vector2 EndPosition => Position + this.CurvePositionAt(1); + public override int ComboIndex + { + get => base.ComboIndex; + set + { + base.ComboIndex = value; + foreach (var n in NestedHitObjects.OfType()) + n.ComboIndex = value; + } + } + + public override int IndexInCurrentCombo + { + get => base.IndexInCurrentCombo; + set + { + base.IndexInCurrentCombo = value; + foreach (var n in NestedHitObjects.OfType()) + n.IndexInCurrentCombo = value; + } + } + public SliderCurve Curve { get; } = new SliderCurve(); public List ControlPoints @@ -45,6 +68,8 @@ namespace osu.Game.Rulesets.Osu.Objects set { Curve.Distance = value; } } + public double? LegacyLastTickOffset { get; set; } + /// /// 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. @@ -91,6 +116,9 @@ namespace osu.Game.Rulesets.Osu.Objects createSliderEnds(); createTicks(); createRepeatPoints(); + + if (LegacyLastTickOffset != null) + TailCircle.StartTime = Math.Max(StartTime + Duration / 2, TailCircle.StartTime - LegacyLastTickOffset.Value); } private void createSliderEnds() @@ -141,7 +169,8 @@ namespace osu.Game.Rulesets.Osu.Objects var distanceProgress = d / length; var timeProgress = reversed ? 1 - distanceProgress : distanceProgress; - var firstSample = Samples.FirstOrDefault(s => s.Name == SampleInfo.HIT_NORMAL) ?? Samples.FirstOrDefault(); // TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933) + var firstSample = Samples.FirstOrDefault(s => s.Name == SampleInfo.HIT_NORMAL) + ?? Samples.FirstOrDefault(); // TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933) var sampleList = new List(); if (firstSample != null) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs index 324d0502c1..bb0bd891b3 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGeneratorBase.cs @@ -6,6 +6,7 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Osu.Objects; using System; using System.Collections.Generic; +using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Replays; using osu.Game.Users; @@ -18,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Replays /// /// Constants (for spinners). /// - protected static readonly Vector2 SPINNER_CENTRE = new Vector2(256, 192); + protected static readonly Vector2 SPINNER_CENTRE = OsuPlayfield.BASE_SIZE / 2; protected const float SPIN_RADIUS = 50; /// diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/basic-expected-conversion.json b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/basic-expected-conversion.json index b82fddbe79..491adc2b50 100644 --- a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/basic-expected-conversion.json +++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/basic-expected-conversion.json @@ -1,124 +1,256 @@ { "Mappings": [{ - "StartTime": 500, - "Objects": [{ - "StartTime": 500, - "EndTime": 2500, - "StartX": 96, - "StartY": 192, - "EndX": 96, - "EndY": 192 - }] - }, - { - "StartTime": 3000, - "Objects": [{ - "StartTime": 3000, - "EndTime": 4000, - "StartX": 256, - "StartY": 192, - "EndX": 256, - "EndY": 192 - }] - }, - { - "StartTime": 4500, - "Objects": [{ - "StartTime": 4500, - "EndTime": 5500, - "StartX": 256, - "StartY": 192, - "EndX": 256, - "EndY": 192 - }] - }, - { - "StartTime": 6000, - "Objects": [{ - "StartTime": 6000, - "EndTime": 6500, - "StartX": 256, - "StartY": 192, - "EndX": 256, - "EndY": 192 - }] - }, - { - "StartTime": 7000, - "Objects": [{ - "StartTime": 7000, - "EndTime": 8000, - "StartX": 256, - "StartY": 128, - "EndX": 256, - "EndY": 128 - }] - }, - { - "StartTime": 8500, - "Objects": [{ - "StartTime": 8500, - "EndTime": 10999, - "StartX": 32, - "StartY": 192, - "EndX": 508.166229, - "EndY": 153.299271 - }] - }, - { - "StartTime": 11500, - "Objects": [{ - "StartTime": 11500, - "EndTime": 12000, - "StartX": 256, - "StartY": 192, - "EndX": 256, - "EndY": 192 - }] - }, - { - "StartTime": 12500, - "Objects": [{ - "StartTime": 12500, - "EndTime": 16500, - "StartX": 512, - "StartY": 320, - "EndX": 291.1977, - "EndY": 40.799427 - }] - }, - { - "StartTime": 17000, - "Objects": [{ - "StartTime": 17000, - "EndTime": 18000, - "StartX": 256, - "StartY": 256, - "EndX": 256, - "EndY": 256 - }] - }, - { - "StartTime": 18500, - "Objects": [{ - "StartTime": 18500, - "EndTime": 19450, - "StartX": 256, - "StartY": 192, - "EndX": 256, - "EndY": 192 - }] - }, - { - "StartTime": 19875, - "Objects": [{ - "StartTime": 19875, - "EndTime": 23874, - "StartX": 216, - "StartY": 231, - "EndX": 408.720825, - "EndY": 339.810455 - }] - } - ] + "StartTime": 500.0, + "Objects": [{ + "StartTime": 500.0, + "EndTime": 500.0, + "X": 96.0, + "Y": 192.0 + }, { + "StartTime": 1000.0, + "EndTime": 1000.0, + "X": 256.0, + "Y": 192.0 + }, { + "StartTime": 1500.0, + "EndTime": 1500.0, + "X": 416.0, + "Y": 192.0 + }, { + "StartTime": 2000.0, + "EndTime": 2000.0, + "X": 256.0, + "Y": 192.0 + }, { + "StartTime": 2464.0, + "EndTime": 2464.0, + "X": 96.0, + "Y": 192.0 + }] + }, { + "StartTime": 3000.0, + "Objects": [{ + "StartTime": 3000.0, + "EndTime": 4000.0, + "X": 256.0, + "Y": 192.0 + }] + }, { + "StartTime": 4500.0, + "Objects": [{ + "StartTime": 4500.0, + "EndTime": 5500.0, + "X": 256.0, + "Y": 192.0 + }] + }, { + "StartTime": 6000.0, + "Objects": [{ + "StartTime": 6000.0, + "EndTime": 6500.0, + "X": 256.0, + "Y": 192.0 + }] + }, { + "StartTime": 7000.0, + "Objects": [{ + "StartTime": 7000.0, + "EndTime": 7000.0, + "X": 256.0, + "Y": 128.0 + }, { + "StartTime": 7250.0, + "EndTime": 7250.0, + "X": 336.0, + "Y": 128.0 + }, { + "StartTime": 7500.0, + "EndTime": 7500.0, + "X": 256.0, + "Y": 128.0 + }, { + "StartTime": 7750.0, + "EndTime": 7750.0, + "X": 336.0, + "Y": 128.0 + }, { + "StartTime": 7964.0, + "EndTime": 7964.0, + "X": 256.0, + "Y": 128.0 + }] + }, { + "StartTime": 8500.0, + "Objects": [{ + "StartTime": 8500.0, + "EndTime": 8500.0, + "X": 32.0, + "Y": 192.0 + }, { + "StartTime": 9000.0, + "EndTime": 9000.0, + "X": 101.81015, + "Y": 326.4915 + }, { + "StartTime": 9500.0, + "EndTime": 9500.0, + "X": 237.2304, + "Y": 276.282928 + }, { + "StartTime": 10000.0, + "EndTime": 10000.0, + "X": 270.339874, + "Y": 121.1423 + }, { + "StartTime": 10500.0, + "EndTime": 10500.0, + "X": 401.0588, + "Y": 49.1515045 + }, { + "StartTime": 10964.0, + "EndTime": 10964.0, + "X": 508.166229, + "Y": 153.299271 + }] + }, { + "StartTime": 11500.0, + "Objects": [{ + "StartTime": 11500.0, + "EndTime": 12000.0, + "X": 256.0, + "Y": 192.0 + }] + }, { + "StartTime": 12500.0, + "Objects": [{ + "StartTime": 12500.0, + "EndTime": 12500.0, + "X": 512.0, + "Y": 320.0 + }, { + "StartTime": 13000.0, + "EndTime": 13000.0, + "X": 353.235535, + "Y": 300.154449 + }, { + "StartTime": 13500.0, + "EndTime": 13500.0, + "X": 194.471069, + "Y": 280.3089 + }, { + "StartTime": 14000.0, + "EndTime": 14000.0, + "X": 35.7066345, + "Y": 260.463318 + }, { + "StartTime": 14500.0, + "EndTime": 14500.0, + "X": 118.370323, + "Y": 219.009277 + }, { + "StartTime": 15000.0, + "EndTime": 15000.0, + "X": 271.087128, + "Y": 171.285278 + }, { + "StartTime": 15500.0, + "EndTime": 15500.0, + "X": 423.803925, + "Y": 123.561279 + }, { + "StartTime": 16000.0, + "EndTime": 16000.0, + "X": 446.420532, + "Y": 79.60513 + }, { + "StartTime": 16464.0, + "EndTime": 16464.0, + "X": 291.1977, + "Y": 40.799427 + }] + }, { + "StartTime": 17000.0, + "Objects": [{ + "StartTime": 17000.0, + "EndTime": 17000.0, + "X": 256.0, + "Y": 256.0 + }, { + "StartTime": 17250.0, + "EndTime": 17250.0, + "X": 176.0, + "Y": 256.0 + }, { + "StartTime": 17500.0, + "EndTime": 17500.0, + "X": 256.0, + "Y": 256.0 + }, { + "StartTime": 17750.0, + "EndTime": 17750.0, + "X": 176.0, + "Y": 256.0 + }, { + "StartTime": 17964.0, + "EndTime": 17964.0, + "X": 256.0, + "Y": 256.0 + }] + }, { + "StartTime": 18500.0, + "Objects": [{ + "StartTime": 18500.0, + "EndTime": 19450.0, + "X": 256.0, + "Y": 192.0 + }] + }, { + "StartTime": 19875.0, + "Objects": [{ + "StartTime": 19875.0, + "EndTime": 19875.0, + "X": 216.0, + "Y": 231.0 + }, { + "StartTime": 20375.0, + "EndTime": 20375.0, + "X": 317.446747, + "Y": 171.345245 + }, { + "StartTime": 20875.0, + "EndTime": 20875.0, + "X": 270.3294, + "Y": 310.4395 + }, { + "StartTime": 21375.0, + "EndTime": 21375.0, + "X": 119.121056, + "Y": 322.8657 + }, { + "StartTime": 21875.0, + "EndTime": 21875.0, + "X": 124.28746, + "Y": 165.224731 + }, { + "StartTime": 22375.0, + "EndTime": 22375.0, + "X": 240.4715, + "Y": 62.65587 + }, { + "StartTime": 22875.0, + "EndTime": 22875.0, + "X": 398.054047, + "Y": 39.064167 + }, { + "StartTime": 23375.0, + "EndTime": 23375.0, + "X": 439.749878, + "Y": 183.668091 + }, { + "StartTime": 23839.0, + "EndTime": 23839.0, + "X": 408.720825, + "Y": 339.810455 + }] + }] } \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve-expected-conversion.json b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve-expected-conversion.json index 7fe038658c..96e4bf1637 100644 --- a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve-expected-conversion.json +++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve-expected-conversion.json @@ -1,13 +1,16 @@ { - "Mappings": [{ - "StartTime": 118858, - "Objects": [{ - "StartTime": 118858, - "EndTime": 119088, - "StartX": 219, - "StartY": 215, - "EndX": 239.6507, - "EndY": 29.1437378 + "Mappings": [{ + "StartTime": 118858.0, + "Objects": [{ + "StartTime": 118858.0, + "EndTime": 118858.0, + "X": 219.0, + "Y": 215.0 + }, { + "StartTime": 119052.0, + "EndTime": 119052.0, + "X": 239.6507, + "Y": 29.1437378 + }] }] - }] } \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/slider-ticks-expected-conversion.json b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/slider-ticks-expected-conversion.json new file mode 100644 index 0000000000..9c049c7cd6 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/slider-ticks-expected-conversion.json @@ -0,0 +1,331 @@ +{ + "Mappings": [{ + "StartTime": 500.0, + "Objects": [{ + "StartTime": 500.0, + "EndTime": 500.0, + "X": 96.0, + "Y": 192.0 + }, { + "StartTime": 624.0, + "EndTime": 624.0, + "X": 105.921242, + "Y": 192.0 + }, { + "StartTime": 749.0, + "EndTime": 749.0, + "X": 115.922493, + "Y": 192.0 + }, { + "StartTime": 874.0, + "EndTime": 874.0, + "X": 125.923737, + "Y": 192.0 + }, { + "StartTime": 999.0, + "EndTime": 999.0, + "X": 135.924988, + "Y": 192.0 + }, { + "StartTime": 1124.0, + "EndTime": 1124.0, + "X": 145.926239, + "Y": 192.0 + }, { + "StartTime": 1249.0, + "EndTime": 1249.0, + "X": 155.92749, + "Y": 192.0 + }, { + "StartTime": 1374.0, + "EndTime": 1374.0, + "X": 165.928741, + "Y": 192.0 + }, { + "StartTime": 1499.0, + "EndTime": 1499.0, + "X": 175.93, + "Y": 192.0 + }, { + "StartTime": 1624.0, + "EndTime": 1624.0, + "X": 185.931244, + "Y": 192.0 + }, { + "StartTime": 1749.0, + "EndTime": 1749.0, + "X": 195.9325, + "Y": 192.0 + }, { + "StartTime": 1874.0, + "EndTime": 1874.0, + "X": 205.933746, + "Y": 192.0 + }, { + "StartTime": 1999.0, + "EndTime": 1999.0, + "X": 215.935, + "Y": 192.0 + }, { + "StartTime": 2124.0, + "EndTime": 2124.0, + "X": 225.936234, + "Y": 192.0 + }, { + "StartTime": 2249.0, + "EndTime": 2249.0, + "X": 235.9375, + "Y": 192.0 + }, { + "StartTime": 2374.0, + "EndTime": 2374.0, + "X": 245.938751, + "Y": 192.0 + }, { + "StartTime": 2499.0, + "EndTime": 2499.0, + "X": 255.94, + "Y": 192.0 + }, { + "StartTime": 2624.0, + "EndTime": 2624.0, + "X": 265.941223, + "Y": 192.0 + }, { + "StartTime": 2749.0, + "EndTime": 2749.0, + "X": 275.9425, + "Y": 192.0 + }, { + "StartTime": 2874.0, + "EndTime": 2874.0, + "X": 285.943756, + "Y": 192.0 + }, { + "StartTime": 2999.0, + "EndTime": 2999.0, + "X": 295.945, + "Y": 192.0 + }, { + "StartTime": 3124.0, + "EndTime": 3124.0, + "X": 305.946259, + "Y": 192.0 + }, { + "StartTime": 3249.0, + "EndTime": 3249.0, + "X": 315.9475, + "Y": 192.0 + }, { + "StartTime": 3374.0, + "EndTime": 3374.0, + "X": 325.94873, + "Y": 192.0 + }, { + "StartTime": 3499.0, + "EndTime": 3499.0, + "X": 335.949982, + "Y": 192.0 + }, { + "StartTime": 3624.0, + "EndTime": 3624.0, + "X": 345.951233, + "Y": 192.0 + }, { + "StartTime": 3749.0, + "EndTime": 3749.0, + "X": 355.952484, + "Y": 192.0 + }, { + "StartTime": 3874.0, + "EndTime": 3874.0, + "X": 365.953766, + "Y": 192.0 + }, { + "StartTime": 3999.0, + "EndTime": 3999.0, + "X": 375.955, + "Y": 192.0 + }, { + "StartTime": 4124.0, + "EndTime": 4124.0, + "X": 385.956238, + "Y": 192.0 + }, { + "StartTime": 4249.0, + "EndTime": 4249.0, + "X": 395.9575, + "Y": 192.0 + }, { + "StartTime": 4374.0, + "EndTime": 4374.0, + "X": 405.95874, + "Y": 192.0 + }, { + "StartTime": 4499.0, + "EndTime": 4499.0, + "X": 415.960022, + "Y": 192.0 + }, { + "StartTime": 4624.0, + "EndTime": 4624.0, + "X": 406.038757, + "Y": 192.0 + }, { + "StartTime": 4749.0, + "EndTime": 4749.0, + "X": 396.0375, + "Y": 192.0 + }, { + "StartTime": 4874.0, + "EndTime": 4874.0, + "X": 386.036255, + "Y": 192.0 + }, { + "StartTime": 4999.0, + "EndTime": 4999.0, + "X": 376.035034, + "Y": 192.0 + }, { + "StartTime": 5124.0, + "EndTime": 5124.0, + "X": 366.033752, + "Y": 192.0 + }, { + "StartTime": 5249.0, + "EndTime": 5249.0, + "X": 356.0325, + "Y": 192.0 + }, { + "StartTime": 5374.0, + "EndTime": 5374.0, + "X": 346.03125, + "Y": 192.0 + }, { + "StartTime": 5499.0, + "EndTime": 5499.0, + "X": 336.030029, + "Y": 192.0 + }, { + "StartTime": 5624.0, + "EndTime": 5624.0, + "X": 326.028748, + "Y": 192.0 + }, { + "StartTime": 5749.0, + "EndTime": 5749.0, + "X": 316.0275, + "Y": 192.0 + }, { + "StartTime": 5874.0, + "EndTime": 5874.0, + "X": 306.026245, + "Y": 192.0 + }, { + "StartTime": 5999.0, + "EndTime": 5999.0, + "X": 296.025, + "Y": 192.0 + }, { + "StartTime": 6124.0, + "EndTime": 6124.0, + "X": 286.023773, + "Y": 192.0 + }, { + "StartTime": 6249.0, + "EndTime": 6249.0, + "X": 276.022522, + "Y": 192.0 + }, { + "StartTime": 6374.0, + "EndTime": 6374.0, + "X": 266.02124, + "Y": 192.0 + }, { + "StartTime": 6499.0, + "EndTime": 6499.0, + "X": 256.02, + "Y": 192.0 + }, { + "StartTime": 6624.0, + "EndTime": 6624.0, + "X": 246.018768, + "Y": 192.0 + }, { + "StartTime": 6749.0, + "EndTime": 6749.0, + "X": 236.017517, + "Y": 192.0 + }, { + "StartTime": 6874.0, + "EndTime": 6874.0, + "X": 226.016251, + "Y": 192.0 + }, { + "StartTime": 6999.0, + "EndTime": 6999.0, + "X": 216.014984, + "Y": 192.0 + }, { + "StartTime": 7124.0, + "EndTime": 7124.0, + "X": 206.013733, + "Y": 192.0 + }, { + "StartTime": 7249.0, + "EndTime": 7249.0, + "X": 196.012512, + "Y": 192.0 + }, { + "StartTime": 7374.0, + "EndTime": 7374.0, + "X": 186.011261, + "Y": 192.0 + }, { + "StartTime": 7499.0, + "EndTime": 7499.0, + "X": 176.01, + "Y": 192.0 + }, { + "StartTime": 7624.0, + "EndTime": 7624.0, + "X": 166.008728, + "Y": 192.0 + }, { + "StartTime": 7749.0, + "EndTime": 7749.0, + "X": 156.0075, + "Y": 192.0 + }, { + "StartTime": 7874.0, + "EndTime": 7874.0, + "X": 146.006256, + "Y": 192.0 + }, { + "StartTime": 7999.0, + "EndTime": 7999.0, + "X": 136.005, + "Y": 192.0 + }, { + "StartTime": 8124.0, + "EndTime": 8124.0, + "X": 126.003738, + "Y": 192.0 + }, { + "StartTime": 8249.0, + "EndTime": 8249.0, + "X": 116.002518, + "Y": 192.0 + }, { + "StartTime": 8374.0, + "EndTime": 8374.0, + "X": 106.001259, + "Y": 192.0 + }, { + "StartTime": 8463.0, + "EndTime": 8463.0, + "X": 96.0, + "Y": 192.0 + }] + }] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/slider-ticks.osu b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/slider-ticks.osu new file mode 100644 index 0000000000..fd835efbc8 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/slider-ticks.osu @@ -0,0 +1,20 @@ +osu file format v14 + +[General] +StackLeniency: 0.7 + +[Difficulty] +HPDrainRate:6 +CircleSize:4 +OverallDifficulty:7 +ApproachRate:8.3 +SliderMultiplier:0.400000005960464 +SliderTickRate:4 + +[TimingPoints] +500,500,4,2,1,50,1,0 +13426,-100,4,3,1,45,0,0 +14884,-100,4,2,1,50,0,0 + +[HitObjects] +96,192,500,6,0,L|416:192,2,320.000004768372 diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index f2d5631e93..04724931ae 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -11,7 +11,6 @@ using osu.Game.Rulesets.Osu.Objects.Drawables.Connections; using osu.Game.Rulesets.UI; using System.Linq; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Osu.Judgements; namespace osu.Game.Rulesets.Osu.UI { @@ -75,7 +74,7 @@ namespace osu.Game.Rulesets.Osu.UI DrawableOsuJudgement explosion = new DrawableOsuJudgement(judgement, judgedObject) { Origin = Anchor.Centre, - Position = ((OsuHitObject)judgedObject.HitObject).StackedEndPosition + ((OsuJudgement)judgement).PositionOffset + Position = ((OsuHitObject)judgedObject.HitObject).StackedEndPosition }; judgementLayer.Add(explosion); diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs index 11586e340b..db8480c742 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoBeatmapConversionTest.cs @@ -12,7 +12,8 @@ using osu.Game.Tests.Beatmaps; namespace osu.Game.Rulesets.Taiko.Tests { - internal class TaikoBeatmapConversionTest : BeatmapConversionTest + [TestFixture] + public class TaikoBeatmapConversionTest : BeatmapConversionTest { protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko"; @@ -41,7 +42,7 @@ namespace osu.Game.Rulesets.Taiko.Tests protected override Ruleset CreateRuleset() => new TaikoRuleset(); } - internal struct ConvertValue : IEquatable + public struct ConvertValue : IEquatable { /// /// A sane value to account for osu!stable using ints everwhere. diff --git a/osu.Game.Tests/Visual/TestCaseLeaderboard.cs b/osu.Game.Tests/Visual/TestCaseLeaderboard.cs index 7f5bce3b84..e8ac19e7fc 100644 --- a/osu.Game.Tests/Visual/TestCaseLeaderboard.cs +++ b/osu.Game.Tests/Visual/TestCaseLeaderboard.cs @@ -1,6 +1,8 @@ // Copyright (c) 2007-2018 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; using osu.Framework.Graphics; using osu.Game.Rulesets.Scoring; @@ -17,6 +19,12 @@ namespace osu.Game.Tests.Visual [Description("PlaySongSelect leaderboard")] public class TestCaseLeaderboard : OsuTestCase { + public override IReadOnlyList RequiredTypes => new[] { + typeof(Placeholder), + typeof(MessagePlaceholder), + typeof(RetrievalFailurePlaceholder), + }; + private RulesetStore rulesets; private readonly FailableLeaderboard leaderboard; diff --git a/osu.Game.Tests/Visual/TestCaseLoadingAnimation.cs b/osu.Game.Tests/Visual/TestCaseLoadingAnimation.cs new file mode 100644 index 0000000000..ebbc673d36 --- /dev/null +++ b/osu.Game.Tests/Visual/TestCaseLoadingAnimation.cs @@ -0,0 +1,63 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Testing; +using osu.Game.Graphics.UserInterface; +using OpenTK.Graphics; + +namespace osu.Game.Tests.Visual +{ + public class TestCaseLoadingAnimation : GridTestCase + { + public TestCaseLoadingAnimation() + : base(2, 2) + { + LoadingAnimation loading; + + Cell(0).AddRange(new Drawable[] + { + new Box + { + Colour = Color4.Black, + RelativeSizeAxes = Axes.Both + }, + loading = new LoadingAnimation() + }); + + loading.Show(); + + Cell(1).AddRange(new Drawable[] + { + new Box + { + Colour = Color4.White, + RelativeSizeAxes = Axes.Both + }, + loading = new LoadingAnimation() + }); + + loading.Show(); + + Cell(2).AddRange(new Drawable[] + { + new Box + { + Colour = Color4.Gray, + RelativeSizeAxes = Axes.Both + }, + loading = new LoadingAnimation() + }); + + loading.Show(); + + Cell(3).AddRange(new Drawable[] + { + loading = new LoadingAnimation() + }); + + Scheduler.AddDelayed(() => loading.ToggleVisibility(), 200, true); + } + } +} diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 3afc3c4d32..303a19aab3 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -66,8 +66,8 @@ namespace osu.Game.Beatmaps // General public int AudioLeadIn { get; set; } - public bool Countdown { get; set; } - public float StackLeniency { get; set; } + public bool Countdown { get; set; } = true; + public float StackLeniency { get; set; } = 0.7f; public bool SpecialStyle { get; set; } public int RulesetID { get; set; } diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 50428cc5e6..e488dacf80 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -138,7 +138,7 @@ namespace osu.Game.Beatmaps PostNotification?.Invoke(new SimpleNotification { Icon = FontAwesome.fa_superpowers, - Text = "You gotta be a supporter to download for now 'yo" + Text = "You gotta be an osu!supporter to download for now 'yo" }); return; } diff --git a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs index 71406c6034..43ae30f780 100644 --- a/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/BeatmapManager_WorkingBeatmap.cs @@ -69,11 +69,22 @@ namespace osu.Game.Beatmaps } catch { - return new TrackVirtual(); + return null; } } - protected override Waveform GetWaveform() => new Waveform(store.GetStream(getPathForFile(Metadata.AudioFile))); + protected override Waveform GetWaveform() + { + try + { + var trackData = store.GetStream(getPathForFile(Metadata.AudioFile)); + return trackData == null ? null : new Waveform(trackData); + } + catch + { + return null; + } + } protected override Storyboard GetStoryboard() { diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index ed8fbdbb26..ebebe42097 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -13,7 +13,13 @@ namespace osu.Game.Beatmaps [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int ID { get; set; } - public int? OnlineBeatmapSetID { get; set; } + private int? onlineBeatmapSetID; + + public int? OnlineBeatmapSetID + { + get { return onlineBeatmapSetID; } + set { onlineBeatmapSetID = value > 0 ? value : null; } + } public BeatmapMetadata Metadata { get; set; } diff --git a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs index 265c6832b2..25a76b52a7 100644 --- a/osu.Game/Beatmaps/DummyWorkingBeatmap.cs +++ b/osu.Game/Beatmaps/DummyWorkingBeatmap.cs @@ -45,7 +45,7 @@ namespace osu.Game.Beatmaps protected override Texture GetBackground() => game?.Textures.Get(@"Backgrounds/bg4"); - protected override Track GetTrack() => new TrackVirtual(); + protected override Track GetTrack() => new TrackVirtual { Length = 1000 }; private class DummyRulesetInfo : RulesetInfo { diff --git a/osu.Game/Beatmaps/WorkingBeatmap.cs b/osu.Game/Beatmaps/WorkingBeatmap.cs index 9b50aed077..1a65611a3d 100644 --- a/osu.Game/Beatmaps/WorkingBeatmap.cs +++ b/osu.Game/Beatmaps/WorkingBeatmap.cs @@ -19,7 +19,7 @@ using osu.Game.Skinning; namespace osu.Game.Beatmaps { - public abstract class WorkingBeatmap : IDisposable + public abstract partial class WorkingBeatmap : IDisposable { public readonly BeatmapInfo BeatmapInfo; @@ -145,7 +145,7 @@ namespace osu.Game.Beatmaps private Track populateTrack() { // we want to ensure that we always have a track, even if it's a fake one. - var t = GetTrack() ?? new TrackVirtual(); + var t = GetTrack() ?? new VirtualBeatmapTrack(Beatmap); applyRateAdjustments(t); return t; } diff --git a/osu.Game/Beatmaps/WorkingBeatmap_VirtualBeatmapTrack.cs b/osu.Game/Beatmaps/WorkingBeatmap_VirtualBeatmapTrack.cs new file mode 100644 index 0000000000..0e0a9a3bb9 --- /dev/null +++ b/osu.Game/Beatmaps/WorkingBeatmap_VirtualBeatmapTrack.cs @@ -0,0 +1,38 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System.Linq; +using osu.Framework.Audio.Track; +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Beatmaps +{ + public partial class WorkingBeatmap + { + /// + /// A type of which provides a valid length based on the s of an . + /// + protected class VirtualBeatmapTrack : TrackVirtual + { + private const double excess_length = 1000; + + public VirtualBeatmapTrack(IBeatmap beatmap) + { + var lastObject = beatmap.HitObjects.LastOrDefault(); + + switch (lastObject) + { + case null: + Length = excess_length; + break; + case IHasEndTime endTime: + Length = endTime.EndTime + excess_length; + break; + default: + Length = lastObject.StartTime + excess_length; + break; + } + } + } + } +} diff --git a/osu.Game/Graphics/Containers/OsuTextFlowContainer.cs b/osu.Game/Graphics/Containers/OsuTextFlowContainer.cs index 119af4d762..e77e075fe2 100644 --- a/osu.Game/Graphics/Containers/OsuTextFlowContainer.cs +++ b/osu.Game/Graphics/Containers/OsuTextFlowContainer.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics.Sprites; @@ -16,6 +17,8 @@ namespace osu.Game.Graphics.Containers protected override SpriteText CreateSpriteText() => new OsuSpriteText(); + public void AddArbitraryDrawable(Drawable drawable) => AddInternal(drawable); + public void AddIcon(FontAwesome icon, Action creationParameters = null) => AddText(((char)icon).ToString(), creationParameters); } } diff --git a/osu.Game/Graphics/DrawableDate.cs b/osu.Game/Graphics/DrawableDate.cs index 763e57e397..406fc2ffd2 100644 --- a/osu.Game/Graphics/DrawableDate.cs +++ b/osu.Game/Graphics/DrawableDate.cs @@ -12,14 +12,14 @@ namespace osu.Game.Graphics { public class DrawableDate : OsuSpriteText, IHasTooltip { - private readonly DateTimeOffset date; + protected readonly DateTimeOffset Date; public DrawableDate(DateTimeOffset date) { AutoSizeAxes = Axes.Both; Font = "Exo2.0-RegularItalic"; - this.date = date.ToLocalTime(); + Date = date.ToLocalTime(); } [BackgroundDependencyLoader] @@ -38,7 +38,7 @@ namespace osu.Game.Graphics { updateTime(); - var diffToNow = DateTimeOffset.Now.Subtract(date); + var diffToNow = DateTimeOffset.Now.Subtract(Date); double timeUntilNextUpdate = 1000; if (diffToNow.TotalSeconds > 60) @@ -58,8 +58,10 @@ namespace osu.Game.Graphics public override bool HandleMouseInput => true; - private void updateTime() => Text = date.Humanize(); + protected virtual string Format() => Date.Humanize(); - public string TooltipText => date.ToString(); + private void updateTime() => Text = Format(); + + public virtual string TooltipText => string.Format($"{Date:MMMM d, yyyy h:mm tt \"UTC\"z}"); } } diff --git a/osu.Game/Graphics/UserInterface/LoadingAnimation.cs b/osu.Game/Graphics/UserInterface/LoadingAnimation.cs index 5ea6bce432..8d45b03a43 100644 --- a/osu.Game/Graphics/UserInterface/LoadingAnimation.cs +++ b/osu.Game/Graphics/UserInterface/LoadingAnimation.cs @@ -4,12 +4,17 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using OpenTK; +using OpenTK.Graphics; namespace osu.Game.Graphics.UserInterface { public class LoadingAnimation : VisibilityContainer { private readonly SpriteIcon spinner; + private readonly SpriteIcon spinnerShadow; + + private const float spin_duration = 600; + private const float transition_duration = 200; public LoadingAnimation() { @@ -20,12 +25,22 @@ namespace osu.Game.Graphics.UserInterface Children = new Drawable[] { - spinner = new SpriteIcon + spinnerShadow = new SpriteIcon { - Size = new Vector2(20), Anchor = Anchor.Centre, Origin = Anchor.Centre, - Icon = FontAwesome.fa_spinner + RelativeSizeAxes = Axes.Both, + Position = new Vector2(1, 1), + Colour = Color4.Black, + Alpha = 0.4f, + Icon = FontAwesome.fa_circle_o_notch + }, + spinner = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Icon = FontAwesome.fa_circle_o_notch } }; } @@ -34,12 +49,11 @@ namespace osu.Game.Graphics.UserInterface { base.LoadComplete(); - spinner.Spin(2000, RotationDirection.Clockwise); + spinner.Spin(spin_duration, RotationDirection.Clockwise); + spinnerShadow.Spin(spin_duration, RotationDirection.Clockwise); } - private const float transition_duration = 500; - - protected override void PopIn() => this.FadeIn(transition_duration * 5, Easing.OutQuint); + protected override void PopIn() => this.FadeIn(transition_duration * 2, Easing.OutQuint); protected override void PopOut() => this.FadeOut(transition_duration, Easing.OutQuint); } diff --git a/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.Designer.cs b/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.Designer.cs new file mode 100644 index 0000000000..7eeacd56d7 --- /dev/null +++ b/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.Designer.cs @@ -0,0 +1,376 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using osu.Game.Database; + +namespace osu.Game.Migrations +{ + [DbContext(typeof(OsuDbContext))] + [Migration("20180628011956_RemoveNegativeSetIDs")] + partial class RemoveNegativeSetIDs + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "2.1.1-rtm-30846"); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("ApproachRate"); + + b.Property("CircleSize"); + + b.Property("DrainRate"); + + b.Property("OverallDifficulty"); + + b.Property("SliderMultiplier"); + + b.Property("SliderTickRate"); + + b.HasKey("ID"); + + b.ToTable("BeatmapDifficulty"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("AudioLeadIn"); + + b.Property("BaseDifficultyID"); + + b.Property("BeatDivisor"); + + b.Property("BeatmapSetInfoID"); + + b.Property("Countdown"); + + b.Property("DistanceSpacing"); + + b.Property("GridSize"); + + b.Property("Hash"); + + b.Property("Hidden"); + + b.Property("LetterboxInBreaks"); + + b.Property("MD5Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapID"); + + b.Property("Path"); + + b.Property("RulesetID"); + + b.Property("SpecialStyle"); + + b.Property("StackLeniency"); + + b.Property("StarDifficulty"); + + b.Property("StoredBookmarks"); + + b.Property("TimelineZoom"); + + b.Property("Version"); + + b.Property("WidescreenStoryboard"); + + b.HasKey("ID"); + + b.HasIndex("BaseDifficultyID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("Hash"); + + b.HasIndex("MD5Hash"); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapID") + .IsUnique(); + + b.HasIndex("RulesetID"); + + b.ToTable("BeatmapInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapMetadata", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Artist"); + + b.Property("ArtistUnicode"); + + b.Property("AudioFile"); + + b.Property("AuthorString") + .HasColumnName("Author"); + + b.Property("BackgroundFile"); + + b.Property("PreviewTime"); + + b.Property("Source"); + + b.Property("Tags"); + + b.Property("Title"); + + b.Property("TitleUnicode"); + + b.HasKey("ID"); + + b.ToTable("BeatmapMetadata"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("BeatmapSetInfoID"); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.HasKey("ID"); + + b.HasIndex("BeatmapSetInfoID"); + + b.HasIndex("FileInfoID"); + + b.ToTable("BeatmapSetFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("DeletePending"); + + b.Property("Hash"); + + b.Property("MetadataID"); + + b.Property("OnlineBeatmapSetID"); + + b.Property("Protected"); + + b.HasKey("ID"); + + b.HasIndex("DeletePending"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("MetadataID"); + + b.HasIndex("OnlineBeatmapSetID") + .IsUnique(); + + b.ToTable("BeatmapSetInfo"); + }); + + modelBuilder.Entity("osu.Game.Configuration.DatabasedSetting", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntKey") + .HasColumnName("Key"); + + b.Property("RulesetID"); + + b.Property("StringValue") + .HasColumnName("Value"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("Settings"); + }); + + modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("IntAction") + .HasColumnName("Action"); + + b.Property("KeysString") + .HasColumnName("Keys"); + + b.Property("RulesetID"); + + b.Property("Variant"); + + b.HasKey("ID"); + + b.HasIndex("IntAction"); + + b.HasIndex("RulesetID", "Variant"); + + b.ToTable("KeyBinding"); + }); + + modelBuilder.Entity("osu.Game.IO.FileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Hash"); + + b.Property("ReferenceCount"); + + b.HasKey("ID"); + + b.HasIndex("Hash") + .IsUnique(); + + b.HasIndex("ReferenceCount"); + + b.ToTable("FileInfo"); + }); + + modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Available"); + + b.Property("InstantiationInfo"); + + b.Property("Name"); + + b.Property("ShortName"); + + b.HasKey("ID"); + + b.HasIndex("Available"); + + b.HasIndex("ShortName") + .IsUnique(); + + b.ToTable("RulesetInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("FileInfoID"); + + b.Property("Filename") + .IsRequired(); + + b.Property("SkinInfoID"); + + b.HasKey("ID"); + + b.HasIndex("FileInfoID"); + + b.HasIndex("SkinInfoID"); + + b.ToTable("SkinFileInfo"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinInfo", b => + { + b.Property("ID") + .ValueGeneratedOnAdd(); + + b.Property("Creator"); + + b.Property("DeletePending"); + + b.Property("Name"); + + b.HasKey("ID"); + + b.ToTable("SkinInfo"); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapDifficulty", "BaseDifficulty") + .WithMany() + .HasForeignKey("BaseDifficultyID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo", "BeatmapSet") + .WithMany("Beatmaps") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("Beatmaps") + .HasForeignKey("MetadataID"); + + b.HasOne("osu.Game.Rulesets.RulesetInfo", "Ruleset") + .WithMany() + .HasForeignKey("RulesetID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetFileInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapSetInfo") + .WithMany("Files") + .HasForeignKey("BeatmapSetInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); + + modelBuilder.Entity("osu.Game.Beatmaps.BeatmapSetInfo", b => + { + b.HasOne("osu.Game.Beatmaps.BeatmapMetadata", "Metadata") + .WithMany("BeatmapSets") + .HasForeignKey("MetadataID"); + }); + + modelBuilder.Entity("osu.Game.Skinning.SkinFileInfo", b => + { + b.HasOne("osu.Game.IO.FileInfo", "FileInfo") + .WithMany() + .HasForeignKey("FileInfoID") + .OnDelete(DeleteBehavior.Cascade); + + b.HasOne("osu.Game.Skinning.SkinInfo") + .WithMany("Files") + .HasForeignKey("SkinInfoID") + .OnDelete(DeleteBehavior.Cascade); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.cs b/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.cs new file mode 100644 index 0000000000..c38cd19b42 --- /dev/null +++ b/osu.Game/Migrations/20180628011956_RemoveNegativeSetIDs.cs @@ -0,0 +1,19 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace osu.Game.Migrations +{ + public partial class RemoveNegativeSetIDs : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + // There was a change that baetmaps were being loaded with "-1" online IDs, which is completely incorrect. + // This ensures there will not be unique key conflicts as a result of these incorrectly imported beatmaps. + migrationBuilder.Sql("UPDATE BeatmapSetInfo SET OnlineBeatmapSetID = null WHERE OnlineBeatmapSetID <= 0"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs index 4b91a3d700..28cc484109 100644 --- a/osu.Game/Overlays/Direct/PlayButton.cs +++ b/osu.Game/Overlays/Direct/PlayButton.cs @@ -10,6 +10,7 @@ using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; +using OpenTK; using OpenTK.Graphics; namespace osu.Game.Overlays.Direct @@ -48,9 +49,15 @@ namespace osu.Game.Overlays.Direct set { if (value) + { + icon.FadeTo(0.5f, transition_duration, Easing.OutQuint); loadingAnimation.Show(); + } else + { + icon.FadeTo(1, transition_duration, Easing.OutQuint); loadingAnimation.Hide(); + } } } @@ -67,7 +74,10 @@ namespace osu.Game.Overlays.Direct RelativeSizeAxes = Axes.Both, Icon = FontAwesome.fa_play, }, - loadingAnimation = new LoadingAnimation(), + loadingAnimation = new LoadingAnimation + { + Size = new Vector2(15), + }, }); Playing.ValueChanged += playingStateChanged; diff --git a/osu.Game/Overlays/Profile/Components/DrawableJoinDate.cs b/osu.Game/Overlays/Profile/Components/DrawableJoinDate.cs new file mode 100644 index 0000000000..11ee329f33 --- /dev/null +++ b/osu.Game/Overlays/Profile/Components/DrawableJoinDate.cs @@ -0,0 +1,20 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using System; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.Profile.Components +{ + public class DrawableJoinDate : DrawableDate + { + public DrawableJoinDate(DateTimeOffset date) + : base(date) + { + } + + protected override string Format() => Text = Date.ToUniversalTime().Year < 2008 ? "Here since the beginning" : $"{Date:MMMM yyyy}"; + + public override string TooltipText => $"{Date:MMMM d, yyyy}"; + } +} diff --git a/osu.Game/Overlays/Profile/Components/GradeBadge.cs b/osu.Game/Overlays/Profile/Components/GradeBadge.cs new file mode 100644 index 0000000000..14a47e8d03 --- /dev/null +++ b/osu.Game/Overlays/Profile/Components/GradeBadge.cs @@ -0,0 +1,50 @@ +// Copyright (c) 2007-2018 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.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Graphics.Sprites; + +namespace osu.Game.Overlays.Profile.Components +{ + public class GradeBadge : Container + { + private const float width = 50; + private readonly string grade; + private readonly Sprite badge; + private readonly SpriteText numberText; + + public int DisplayCount + { + set => numberText.Text = value.ToString(@"#,0"); + } + + public GradeBadge(string grade) + { + this.grade = grade; + Width = width; + Height = 41; + Add(badge = new Sprite + { + Width = width, + Height = 26 + }); + Add(numberText = new OsuSpriteText + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + TextSize = 14, + Font = @"Exo2.0-Bold" + }); + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + badge.Texture = textures.Get($"Grades/{grade}"); + } + } +} diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 067144c26e..c72ff6131b 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -16,6 +16,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Overlays.Profile.Components; using osu.Game.Overlays.Profile.Header; using osu.Game.Users; @@ -375,12 +376,12 @@ namespace osu.Game.Overlays.Profile if (user.JoinDate.ToUniversalTime().Year < 2008) { - infoTextLeft.AddText("Here since the beginning", boldItalic); + infoTextLeft.AddText(new DrawableJoinDate(user.JoinDate), lightText); } else { infoTextLeft.AddText("Joined ", lightText); - infoTextLeft.AddText(new DrawableDate(user.JoinDate), boldItalic); + infoTextLeft.AddText(new DrawableJoinDate(user.JoinDate), boldItalic); } infoTextLeft.NewLine(); @@ -470,43 +471,5 @@ namespace osu.Game.Overlays.Profile infoTextRight.NewLine(); } - - private class GradeBadge : Container - { - private const float width = 50; - private readonly string grade; - private readonly Sprite badge; - private readonly SpriteText numberText; - - public int DisplayCount - { - set { numberText.Text = value.ToString(@"#,0"); } - } - - public GradeBadge(string grade) - { - this.grade = grade; - Width = width; - Height = 41; - Add(badge = new Sprite - { - Width = width, - Height = 26 - }); - Add(numberText = new OsuSpriteText - { - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - TextSize = 14, - Font = @"Exo2.0-Bold" - }); - } - - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - badge.Texture = textures.Get($"Grades/{grade}"); - } - } } } diff --git a/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs b/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs index 046c1b1a33..fefb289d17 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs @@ -141,11 +141,11 @@ namespace osu.Game.Overlays.Profile.Sections.Recent break; case RecentActivityType.UserSupportFirst: - message = $"{userLinkTemplate()} has become an osu! supporter - thanks for your generosity!"; + message = $"{userLinkTemplate()} has become an osu!supporter - thanks for your generosity!"; break; case RecentActivityType.UserSupportGift: - message = $"{userLinkTemplate()} has received the gift of osu! supporter!"; + message = $"{userLinkTemplate()} has received the gift of osu!supporter!"; break; case RecentActivityType.UsernameChange: diff --git a/osu.Game/Rulesets/Judgements/Judgement.cs b/osu.Game/Rulesets/Judgements/Judgement.cs index 587f2c8d15..129dd07c3e 100644 --- a/osu.Game/Rulesets/Judgements/Judgement.cs +++ b/osu.Game/Rulesets/Judgements/Judgement.cs @@ -45,11 +45,15 @@ namespace osu.Game.Rulesets.Judgements public double TimeOffset { get; set; } /// - /// Whether the should affect the combo portion of the score. - /// If false, the will be considered for the bonus portion of the score. + /// Whether the should affect the current combo. /// public virtual bool AffectsCombo => true; + /// + /// Whether the should be counted as base (combo) or bonus score. + /// + public virtual bool IsBonus => !AffectsCombo; + /// /// The numeric representation for the result achieved. /// diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs index 17848d1e6e..ef1eecec3d 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs @@ -10,7 +10,7 @@ using osu.Game.Beatmaps.ControlPoints; namespace osu.Game.Rulesets.Objects.Legacy { - internal abstract class ConvertSlider : HitObject, IHasCurve + internal abstract class ConvertSlider : HitObject, IHasCurve, IHasLegacyLastTickOffset { /// /// Scoring distance with a speed-adjusted beat length of 1 second. @@ -45,5 +45,7 @@ namespace osu.Game.Rulesets.Objects.Legacy Velocity = scoringDistance / timingPoint.BeatLength; } + + public double LegacyLastTickOffset => 36; } } diff --git a/osu.Game/Rulesets/Objects/Types/IHasComboIndex.cs b/osu.Game/Rulesets/Objects/Types/IHasComboIndex.cs deleted file mode 100644 index c5d0152ae7..0000000000 --- a/osu.Game/Rulesets/Objects/Types/IHasComboIndex.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2007-2018 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Rulesets.Objects.Types -{ - /// - /// A HitObject that is part of a combo and has extended information about its position relative to other combo objects. - /// - public interface IHasComboIndex : IHasCombo - { - /// - /// The offset of this hitobject in the current combo. - /// - int IndexInCurrentCombo { get; set; } - - /// - /// The offset of this hitobject in the current combo. - /// - int ComboIndex { get; set; } - - /// - /// Whether this is the last object in the current combo. - /// - bool LastInCombo { get; set; } - } -} diff --git a/osu.Game/Rulesets/Objects/Types/IHasLegacyLastTickOffset.cs b/osu.Game/Rulesets/Objects/Types/IHasLegacyLastTickOffset.cs new file mode 100644 index 0000000000..ab2573e933 --- /dev/null +++ b/osu.Game/Rulesets/Objects/Types/IHasLegacyLastTickOffset.cs @@ -0,0 +1,14 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Rulesets.Objects.Types +{ + /// + /// A type of which may require the last tick to be offset. + /// This is specific to osu!stable conversion, and should not be used elsewhere. + /// + public interface IHasLegacyLastTickOffset + { + double LegacyLastTickOffset { get; } + } +} diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 8d267f48e9..a7a9fea5f2 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -84,10 +84,11 @@ namespace osu.Game.Rulesets { try { - var instance = r.CreateInstance(); + var instanceInfo = ((Ruleset)Activator.CreateInstance(Type.GetType(r.InstantiationInfo), (RulesetInfo)null)).RulesetInfo; - r.Name = instance.Description; - r.ShortName = instance.ShortName; + r.Name = instanceInfo.Name; + r.ShortName = instanceInfo.ShortName; + r.InstantiationInfo = instanceInfo.InstantiationInfo; r.Available = true; } diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index dd4120f2fb..9e8ea0f7c2 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -261,13 +261,19 @@ namespace osu.Game.Rulesets.Scoring break; } - baseScore += judgement.NumericResult; - rollingMaxBaseScore += judgement.MaxNumericResult; - JudgedHits++; } - else if (judgement.IsHit) - bonusScore += judgement.NumericResult; + + if (judgement.IsBonus) + { + if (judgement.IsHit) + bonusScore += judgement.NumericResult; + } + else + { + baseScore += judgement.NumericResult; + rollingMaxBaseScore += judgement.MaxNumericResult; + } } /// @@ -280,14 +286,18 @@ namespace osu.Game.Rulesets.Scoring HighestCombo.Value = judgement.HighestComboAtJudgement; if (judgement.AffectsCombo) + JudgedHits--; + + if (judgement.IsBonus) + { + if (judgement.IsHit) + bonusScore -= judgement.NumericResult; + } + else { baseScore -= judgement.NumericResult; rollingMaxBaseScore -= judgement.MaxNumericResult; - - JudgedHits--; } - else if (judgement.IsHit) - bonusScore -= judgement.NumericResult; } private void updateScore() diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs index d1fc8be005..0dc110951b 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/MarkerPart.cs @@ -52,8 +52,6 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts if (Beatmap.Value == null) return; - if (Beatmap.Value.Track.Length == double.PositiveInfinity) return; - float markerPos = MathHelper.Clamp(ToLocalSpace(screenPosition).X, 0, DrawWidth); adjustableClock.Seek(markerPos / DrawWidth * Beatmap.Value.Track.Length); } 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 07d9398d38..5628630d0e 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/TimelinePart.cs @@ -47,8 +47,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); + timeline.RelativeChildSize = new Vector2((float)Math.Max(1, Beatmap.Value.Track.Length), 1); } protected void Add(Drawable visualisation) => timeline.Add(visualisation); diff --git a/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs index e6a04bf1e7..8cb0fdd908 100644 --- a/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Screens/Compose/Timeline/Timeline.cs @@ -52,13 +52,11 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline WaveformVisible.ValueChanged += visible => waveform.FadeTo(visible ? 1 : 0, 200, Easing.OutQuint); Beatmap.BindTo(beatmap); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - Beatmap.BindValueChanged(b => waveform.Waveform = b.Waveform); - waveform.Waveform = Beatmap.Value.Waveform; + Beatmap.BindValueChanged(b => + { + waveform.Waveform = b.Waveform; + track = b.Track; + }, true); } /// @@ -81,6 +79,8 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline /// private bool trackWasPlaying; + private Track track; + protected override void Update() { base.Update(); @@ -118,21 +118,18 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Timeline private void seekTrackToCurrent() { - var track = Beatmap.Value.Track; - if (track is TrackVirtual || !track.IsLoaded) + if (!track.IsLoaded) return; - if (!(Beatmap.Value.Track is TrackVirtual)) - adjustableClock.Seek(Current / Content.DrawWidth * Beatmap.Value.Track.Length); + adjustableClock.Seek(Current / Content.DrawWidth * track.Length); } private void scrollToTrackTime() { - var track = Beatmap.Value.Track; - if (track is TrackVirtual || !track.IsLoaded) + if (!track.IsLoaded) return; - ScrollTo((float)(adjustableClock.CurrentTime / Beatmap.Value.Track.Length) * Content.DrawWidth, false); + ScrollTo((float)(adjustableClock.CurrentTime / track.Length) * Content.DrawWidth, false); } protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a2ed01f5a7..a993b61e7b 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -158,7 +158,8 @@ namespace osu.Game.Screens.Play userAudioOffset.TriggerChange(); ScoreProcessor = RulesetContainer.CreateScoreProcessor(); - config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode); + if (!ScoreProcessor.Mode.Disabled) + config.BindWith(OsuSetting.ScoreDisplayMode, ScoreProcessor.Mode); Children = new Drawable[] { diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 9dae8fb273..9eca3802ae 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -146,7 +146,7 @@ namespace osu.Game.Screens.Select.Leaderboards replacePlaceholder(new MessagePlaceholder(@"Please sign in to view online leaderboards!")); break; case PlaceholderState.NotSupporter: - replacePlaceholder(new MessagePlaceholder(@"Please invest in a supporter tag to view this leaderboard!")); + replacePlaceholder(new MessagePlaceholder(@"Please invest in an osu!supporter tag to view this leaderboard!")); break; default: replacePlaceholder(null); @@ -331,23 +331,4 @@ namespace osu.Game.Screens.Select.Leaderboards } } } - - public enum LeaderboardScope - { - Local, - Country, - Global, - Friend, - } - - public enum PlaceholderState - { - Successful, - Retrieving, - NetworkFailure, - Unavailable, - NoScores, - NotLoggedIn, - NotSupporter, - } } diff --git a/osu.Game/Screens/Select/Leaderboards/LeaderboardScope.cs b/osu.Game/Screens/Select/Leaderboards/LeaderboardScope.cs new file mode 100644 index 0000000000..761f53a5e8 --- /dev/null +++ b/osu.Game/Screens/Select/Leaderboards/LeaderboardScope.cs @@ -0,0 +1,13 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Screens.Select.Leaderboards +{ + public enum LeaderboardScope + { + Local, + Country, + Global, + Friend, + } +} diff --git a/osu.Game/Screens/Select/Leaderboards/MessagePlaceholder.cs b/osu.Game/Screens/Select/Leaderboards/MessagePlaceholder.cs index ac2ac818d8..f01a55b662 100644 --- a/osu.Game/Screens/Select/Leaderboards/MessagePlaceholder.cs +++ b/osu.Game/Screens/Select/Leaderboards/MessagePlaceholder.cs @@ -2,10 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using OpenTK; namespace osu.Game.Screens.Select.Leaderboards { @@ -15,22 +12,13 @@ namespace osu.Game.Screens.Select.Leaderboards public MessagePlaceholder(string message) { - Direction = FillDirection.Horizontal; - AutoSizeAxes = Axes.Both; - Children = new Drawable[] + AddIcon(FontAwesome.fa_exclamation_circle, cp => { - new SpriteIcon - { - Icon = FontAwesome.fa_exclamation_circle, - Size = new Vector2(26), - Margin = new MarginPadding { Right = 10 }, - }, - new OsuSpriteText - { - Text = this.message = message, - TextSize = 22, - }, - }; + cp.TextSize = TEXT_SIZE; + cp.Padding = new MarginPadding { Right = 10 }; + }); + + AddText(this.message = message); } public override bool Equals(Placeholder other) => (other as MessagePlaceholder)?.message == message; diff --git a/osu.Game/Screens/Select/Leaderboards/Placeholder.cs b/osu.Game/Screens/Select/Leaderboards/Placeholder.cs index c56d279e6c..307986a299 100644 --- a/osu.Game/Screens/Select/Leaderboards/Placeholder.cs +++ b/osu.Game/Screens/Select/Leaderboards/Placeholder.cs @@ -3,16 +3,27 @@ using System; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.Containers; namespace osu.Game.Screens.Select.Leaderboards { - public abstract class Placeholder : FillFlowContainer, IEquatable + public abstract class Placeholder : OsuTextFlowContainer, IEquatable { + protected const float TEXT_SIZE = 22; + + public override bool HandleMouseInput => true; + protected Placeholder() + : base(cp => cp.TextSize = TEXT_SIZE) { Anchor = Anchor.Centre; Origin = Anchor.Centre; + TextAnchor = Anchor.TopCentre; + + Padding = new MarginPadding(20); + + AutoSizeAxes = Axes.Y; + RelativeSizeAxes = Axes.X; } public virtual bool Equals(Placeholder other) => GetType() == other?.GetType(); diff --git a/osu.Game/Screens/Select/Leaderboards/PlaceholderState.cs b/osu.Game/Screens/Select/Leaderboards/PlaceholderState.cs new file mode 100644 index 0000000000..33a56540f3 --- /dev/null +++ b/osu.Game/Screens/Select/Leaderboards/PlaceholderState.cs @@ -0,0 +1,16 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.Screens.Select.Leaderboards +{ + public enum PlaceholderState + { + Successful, + Retrieving, + NetworkFailure, + Unavailable, + NoScores, + NotLoggedIn, + NotSupporter, + } +} diff --git a/osu.Game/Screens/Select/Leaderboards/RetrievalFailurePlaceholder.cs b/osu.Game/Screens/Select/Leaderboards/RetrievalFailurePlaceholder.cs index 174fbac120..99b0c53835 100644 --- a/osu.Game/Screens/Select/Leaderboards/RetrievalFailurePlaceholder.cs +++ b/osu.Game/Screens/Select/Leaderboards/RetrievalFailurePlaceholder.cs @@ -3,11 +3,9 @@ using System; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Framework.Input; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; using OpenTK; namespace osu.Game.Screens.Select.Leaderboards @@ -18,22 +16,13 @@ namespace osu.Game.Screens.Select.Leaderboards public RetrievalFailurePlaceholder() { - Direction = FillDirection.Horizontal; - AutoSizeAxes = Axes.Both; - Children = new Drawable[] + AddArbitraryDrawable(new RetryButton { - new RetryButton - { - Action = () => OnRetry?.Invoke(), - Margin = new MarginPadding { Right = 10 }, - }, - new OsuSpriteText - { - Anchor = Anchor.TopLeft, - Text = @"Couldn't retrieve scores!", - TextSize = 22, - }, - }; + Action = () => OnRetry?.Invoke(), + Padding = new MarginPadding { Right = 10 } + }); + + AddText(@"Couldn't retrieve scores!"); } public class RetryButton : OsuHoverContainer @@ -44,18 +33,16 @@ namespace osu.Game.Screens.Select.Leaderboards public RetryButton() { - Height = 26; - Width = 26; + AutoSizeAxes = Axes.Both; + Child = new OsuClickableContainer { AutoSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, Action = () => Action?.Invoke(), Child = icon = new SpriteIcon { Icon = FontAwesome.fa_refresh, - Size = new Vector2(26), + Size = new Vector2(TEXT_SIZE), Shadow = true, }, }; diff --git a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs index 37693c99e8..2f5c887726 100644 --- a/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs +++ b/osu.Game/Tests/Beatmaps/TestWorkingBeatmap.cs @@ -1,12 +1,10 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Linq; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; using osu.Game.Rulesets; -using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Tests.Beatmaps { @@ -26,21 +24,6 @@ namespace osu.Game.Tests.Beatmaps private readonly IBeatmap beatmap; protected override IBeatmap GetBeatmap() => beatmap; protected override Texture GetBackground() => null; - - protected override Track GetTrack() - { - var lastObject = beatmap.HitObjects.LastOrDefault(); - if (lastObject != null) - return new TestTrack(((lastObject as IHasEndTime)?.EndTime ?? lastObject.StartTime) + 1000); - return new TrackVirtual(); - } - - private class TestTrack : TrackVirtual - { - public TestTrack(double length) - { - Length = length; - } - } + protected override Track GetTrack() => null; } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9668d40fd5..b4fccf0898 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -19,7 +19,7 @@ - +