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/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/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/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/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 2ebe5efd0f..698f9de787 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -26,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 @@ -147,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/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/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/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/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs index d6b6595b69..323046f758 100644 --- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs +++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs @@ -18,6 +18,8 @@ namespace osu.Game.Graphics.Containers private SampleChannel samplePopIn; private SampleChannel samplePopOut; + protected virtual bool PlaySamplesOnStateChange => true; + private PreviewTrackManager previewTrackManager; protected readonly Bindable OverlayActivationMode = new Bindable(OverlayActivation.All); @@ -69,12 +71,14 @@ namespace osu.Game.Graphics.Containers { case Visibility.Visible: if (OverlayActivationMode != OverlayActivation.Disabled) - samplePopIn?.Play(); + { + if (PlaySamplesOnStateChange) samplePopIn?.Play(); + } else State = Visibility.Hidden; break; case Visibility.Hidden: - samplePopOut?.Play(); + if (PlaySamplesOnStateChange) samplePopOut?.Play(); break; } } 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/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/OsuGame.cs b/osu.Game/OsuGame.cs index ba8685b5b2..501d8544ff 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -85,7 +85,7 @@ namespace osu.Game private OnScreenDisplay onscreenDisplay; private Bindable configRuleset; - public Bindable Ruleset = new Bindable(); + private readonly Bindable ruleset = new Bindable(); private Bindable configSkin; @@ -147,10 +147,13 @@ namespace osu.Game dependencies.CacheAs(this); + dependencies.CacheAs(ruleset); + dependencies.CacheAs>(ruleset); + // bind config int to database RulesetInfo configRuleset = LocalConfig.GetBindable(OsuSetting.Ruleset); - Ruleset.Value = RulesetStore.GetRuleset(configRuleset.Value) ?? RulesetStore.AvailableRulesets.First(); - Ruleset.ValueChanged += r => configRuleset.Value = r.ID ?? 0; + ruleset.Value = RulesetStore.GetRuleset(configRuleset.Value) ?? RulesetStore.AvailableRulesets.First(); + ruleset.ValueChanged += r => configRuleset.Value = r.ID ?? 0; // bind config int to database SkinInfo configSkin = LocalConfig.GetBindable(OsuSetting.Skin); @@ -216,7 +219,7 @@ namespace osu.Game return; } - Ruleset.Value = s.Ruleset; + ruleset.Value = s.Ruleset; Beatmap.Value = BeatmapManager.GetWorkingBeatmap(s.Beatmap); Beatmap.Value.Mods.Value = s.Mods; @@ -550,7 +553,7 @@ namespace osu.Game // the use case for not applying is in visual/unit tests. bool applyRestrictions = !currentScreen?.AllowBeatmapRulesetChange ?? false; - Ruleset.Disabled = applyRestrictions; + ruleset.Disabled = applyRestrictions; Beatmap.Disabled = applyRestrictions; mainContent.Padding = new MarginPadding { Top = ToolbarOffset }; diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index 7caab0dd6c..e26a3cba2f 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -30,6 +30,8 @@ namespace osu.Game.Overlays State = Visibility.Visible; } + protected override bool PlaySamplesOnStateChange => false; + private void onDialogOnStateChanged(VisibilityContainer dialog, Visibility v) { if (v != Visibility.Hidden) return; diff --git a/osu.Game/Overlays/Direct/FilterControl.cs b/osu.Game/Overlays/Direct/FilterControl.cs index 4f4348c131..df98cc3c32 100644 --- a/osu.Game/Overlays/Direct/FilterControl.cs +++ b/osu.Game/Overlays/Direct/FilterControl.cs @@ -35,15 +35,13 @@ namespace osu.Game.Overlays.Direct } [BackgroundDependencyLoader(true)] - private void load(OsuGame game, RulesetStore rulesets, OsuColour colours) + private void load(RulesetStore rulesets, OsuColour colours, Bindable ruleset) { DisplayStyleControl.Dropdown.AccentColour = colours.BlueDark; - Ruleset.Value = game?.Ruleset.Value ?? rulesets.GetRuleset(0); + Ruleset.Value = ruleset ?? rulesets.GetRuleset(0); foreach (var r in rulesets.AvailableRulesets) - { modeButtons.Add(new RulesetToggleButton(Ruleset, r)); - } } private class RulesetToggleButton : OsuClickableContainer diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index f1624721da..48b7907572 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -52,7 +52,7 @@ namespace osu.Game.Overlays.Mods } [BackgroundDependencyLoader(permitNulls: true)] - private void load(OsuColour colours, OsuGame osu, RulesetStore rulesets, AudioManager audio) + private void load(OsuColour colours, Bindable ruleset, RulesetStore rulesets, AudioManager audio) { SelectedMods.ValueChanged += selectedModsChanged; @@ -60,8 +60,8 @@ namespace osu.Game.Overlays.Mods HighMultiplierColour = colours.Green; UnrankedLabel.Colour = colours.Blue; - if (osu != null) - Ruleset.BindTo(osu.Ruleset); + if (ruleset != null) + Ruleset.BindTo(ruleset); else Ruleset.Value = rulesets.AvailableRulesets.First(); 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/Toolbar/ToolbarModeSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs index 3078c44844..6926502d9b 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarModeSelector.cs @@ -68,7 +68,7 @@ namespace osu.Game.Overlays.Toolbar } [BackgroundDependencyLoader(true)] - private void load(RulesetStore rulesets, OsuGame game) + private void load(RulesetStore rulesets, Bindable parentRuleset) { this.rulesets = rulesets; foreach (var r in rulesets.AvailableRulesets) @@ -83,8 +83,8 @@ namespace osu.Game.Overlays.Toolbar ruleset.ValueChanged += rulesetChanged; ruleset.DisabledChanged += disabledChanged; - if (game != null) - ruleset.BindTo(game.Ruleset); + if (parentRuleset != null) + ruleset.BindTo(parentRuleset); else ruleset.Value = rulesets.AvailableRulesets.FirstOrDefault(); } 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/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/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index a7a9fea5f2..513173ef2f 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -84,7 +84,13 @@ namespace osu.Game.Rulesets { try { - var instanceInfo = ((Ruleset)Activator.CreateInstance(Type.GetType(r.InstantiationInfo), (RulesetInfo)null)).RulesetInfo; + var instanceInfo = ((Ruleset)Activator.CreateInstance(Type.GetType(r.InstantiationInfo, asm => + { + // for the time being, let's ignore the version being loaded. + // this allows for debug builds to successfully load rulesets (even though debug rulesets have a 0.0.0 version). + asm.Version = null; + return Assembly.Load(asm); + }, null), (RulesetInfo)null)).RulesetInfo; r.Name = instanceInfo.Name; r.ShortName = instanceInfo.ShortName; 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/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 61018f9e08..bf650dd514 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -82,22 +82,24 @@ namespace osu.Game.Screens private SampleChannel sampleExit; [BackgroundDependencyLoader(true)] - private void load(BindableBeatmap beatmap, OsuGame osuGame, AudioManager audio) + private void load(BindableBeatmap beatmap, OsuGame osu, AudioManager audio, Bindable ruleset) { if (beatmap != null) Beatmap.BindTo(beatmap); - if (osuGame != null) + if (ruleset != null) + Ruleset.BindTo(ruleset); + + if (osu != null) { - Ruleset.BindTo(osuGame.Ruleset); - OverlayActivationMode.BindTo(osuGame.OverlayActivationMode); + OverlayActivationMode.BindTo(osu.OverlayActivationMode); updateOverlayStates = () => { if (HideOverlaysOnEnter) - osuGame.CloseAllOverlays(); + osu.CloseAllOverlays(); else - osuGame.Toolbar.State = Visibility.Visible; + osu.Toolbar.State = Visibility.Visible; }; } 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/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 7950018554..27089311f2 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -53,10 +53,10 @@ namespace osu.Game.Screens.Select } [BackgroundDependencyLoader(true)] - private void load([CanBeNull] OsuGame osuGame) + private void load([CanBeNull] Bindable parentRuleset) { - if (osuGame != null) - ruleset.BindTo(osuGame.Ruleset); + if (parentRuleset != null) + ruleset.BindTo(parentRuleset); ruleset.ValueChanged += _ => updateDisplay(); } diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index f9f3db3827..39f1a523ea 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -62,7 +62,7 @@ namespace osu.Game.Screens.Select Sort = sort, SearchText = searchTextBox.Text, AllowConvertedBeatmaps = showConverted, - Ruleset = ruleset + Ruleset = ruleset.Value }; public Action Exit; @@ -163,24 +163,23 @@ namespace osu.Game.Screens.Select searchTextBox.HoldFocus = true; } - private readonly Bindable ruleset = new Bindable(); + private readonly IBindable ruleset = new Bindable(); private Bindable showConverted; public readonly Box Background; [BackgroundDependencyLoader(permitNulls: true)] - private void load(OsuColour colours, OsuGame osu, OsuConfigManager config) + private void load(OsuColour colours, IBindable parentRuleset, 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 => updateCriteria(); - ruleset.TriggerChange(); + if (parentRuleset != null) + ruleset.BindTo(parentRuleset); + ruleset.BindValueChanged(val => updateCriteria(), true); } private void updateCriteria() => FilterChanged?.Invoke(CreateCriteria()); diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs index 9eca3802ae..ac6154369d 100644 --- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs +++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs @@ -19,7 +19,6 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using System.Linq; using osu.Framework.Configuration; -using osu.Framework.Logging; using osu.Game.Rulesets; namespace osu.Game.Screens.Select.Leaderboards @@ -33,7 +32,7 @@ namespace osu.Game.Screens.Select.Leaderboards private FillFlowContainer scrollFlow; - private readonly Bindable ruleset = new Bindable(); + private readonly IBindable ruleset = new Bindable(); public Action ScoreSelected; @@ -174,9 +173,8 @@ namespace osu.Game.Screens.Select.Leaderboards private APIAccess api; private BeatmapInfo beatmap; - private OsuGame osuGame; - private ScheduledDelegate pendingBeatmapSwitch; + private ScheduledDelegate pendingUpdateScores; public BeatmapInfo Beatmap { @@ -189,21 +187,19 @@ namespace osu.Game.Screens.Select.Leaderboards beatmap = value; Scores = null; - pendingBeatmapSwitch?.Cancel(); - pendingBeatmapSwitch = Schedule(updateScores); + updateScores(); } } [BackgroundDependencyLoader(permitNulls: true)] - private void load(APIAccess api, OsuGame osuGame) + private void load(APIAccess api, IBindable parentRuleset) { this.api = api; - this.osuGame = osuGame; - if (osuGame != null) - ruleset.BindTo(osuGame.Ruleset); + if (parentRuleset != null) + ruleset.BindTo(parentRuleset); - ruleset.ValueChanged += r => updateScores(); + ruleset.ValueChanged += _ => updateScores(); if (api != null) api.OnStateChange += handleApiStateChange; @@ -231,51 +227,57 @@ namespace osu.Game.Screens.Select.Leaderboards private void updateScores() { - if (Scope == LeaderboardScope.Local) - { - // TODO: get local scores from wherever here. - PlaceholderState = PlaceholderState.NoScores; - return; - } + getScoresRequest?.Cancel(); + getScoresRequest = null; - if (Beatmap?.OnlineBeatmapID == null) + pendingUpdateScores?.Cancel(); + pendingUpdateScores = Schedule(() => { - PlaceholderState = PlaceholderState.Unavailable; - return; - } - - if (api?.IsLoggedIn != true) - { - PlaceholderState = PlaceholderState.NotLoggedIn; - return; - } - - if (Scope != LeaderboardScope.Global && !api.LocalUser.Value.IsSupporter) - { - PlaceholderState = PlaceholderState.NotSupporter; - return; - } - - PlaceholderState = PlaceholderState.Retrieving; - loading.Show(); - - getScoresRequest = new GetScoresRequest(Beatmap, osuGame?.Ruleset.Value ?? Beatmap.Ruleset, Scope); - getScoresRequest.Success += r => Schedule(() => - { - Scores = r.Scores; - PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores; - }); - - getScoresRequest.Failure += e => Schedule(() => - { - if (e is OperationCanceledException) + if (Scope == LeaderboardScope.Local) + { + // TODO: get local scores from wherever here. + PlaceholderState = PlaceholderState.NoScores; return; + } - PlaceholderState = PlaceholderState.NetworkFailure; - Logger.Error(e, @"Couldn't fetch beatmap scores!"); + if (Beatmap?.OnlineBeatmapID == null) + { + PlaceholderState = PlaceholderState.Unavailable; + return; + } + + if (api?.IsLoggedIn != true) + { + PlaceholderState = PlaceholderState.NotLoggedIn; + return; + } + + if (Scope != LeaderboardScope.Global && !api.LocalUser.Value.IsSupporter) + { + PlaceholderState = PlaceholderState.NotSupporter; + return; + } + + PlaceholderState = PlaceholderState.Retrieving; + loading.Show(); + + getScoresRequest = new GetScoresRequest(Beatmap, ruleset.Value ?? Beatmap.Ruleset, Scope); + getScoresRequest.Success += r => Schedule(() => + { + Scores = r.Scores; + PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores; + }); + + getScoresRequest.Failure += e => Schedule(() => + { + if (e is OperationCanceledException) + return; + + PlaceholderState = PlaceholderState.NetworkFailure; + }); + + api.Queue(getScoresRequest); }); - - api.Queue(getScoresRequest); } private Placeholder currentPlaceholder; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 70b473bcd9..94c16f1797 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -8,6 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Audio.Track; +using osu.Framework.Configuration; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input; @@ -17,6 +18,7 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Overlays; +using osu.Game.Rulesets; using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Edit; using osu.Game.Screens.Menu; @@ -62,6 +64,8 @@ namespace osu.Game.Screens.Select private SampleChannel sampleChangeDifficulty; private SampleChannel sampleChangeBeatmap; + protected new readonly Bindable Ruleset = new Bindable(); + private DependencyContainer dependencies; protected override IReadOnlyDependencyContainer CreateLocalDependencies(IReadOnlyDependencyContainer parent) => dependencies = new DependencyContainer(base.CreateLocalDependencies(parent)); @@ -123,7 +127,7 @@ namespace osu.Game.Screens.Select Size = new Vector2(carousel_width, 1), Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, - SelectionChanged = carouselSelectionChanged, + SelectionChanged = updateSelectedBeatmap, BeatmapSetsChanged = carouselBeatmapsLoaded, }, FilterControl = new FilterControl @@ -177,9 +181,13 @@ namespace osu.Game.Screens.Select } [BackgroundDependencyLoader(true)] - private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuGame osu, OsuColour colours) + private void load(BeatmapManager beatmaps, AudioManager audio, DialogOverlay dialog, OsuColour colours) { dependencies.CacheAs(this); + dependencies.CacheAs(Ruleset); + dependencies.CacheAs>(Ruleset); + + base.Ruleset.ValueChanged += r => updateSelectedBeatmap(beatmapNoDebounce); if (Footer != null) { @@ -192,9 +200,6 @@ namespace osu.Game.Screens.Select if (this.beatmaps == null) this.beatmaps = beatmaps; - if (osu != null) - Ruleset.BindTo(osu.Ruleset); - this.beatmaps.ItemAdded += onBeatmapSetAdded; this.beatmaps.ItemRemoved += onBeatmapSetRemoved; this.beatmaps.BeatmapHidden += onBeatmapHidden; @@ -250,9 +255,6 @@ namespace osu.Game.Screens.Select private ScheduledDelegate selectionChangedDebounce; - // We need to keep track of the last selected beatmap ignoring debounce to play the correct selection sounds. - private BeatmapInfo beatmapNoDebounce; - private void workingBeatmapChanged(WorkingBeatmap beatmap) { if (beatmap is DummyWorkingBeatmap) return; @@ -266,11 +268,17 @@ namespace osu.Game.Screens.Select } } + // We need to keep track of the last selected beatmap ignoring debounce to play the correct selection sounds. + private BeatmapInfo beatmapNoDebounce; + private RulesetInfo rulesetNoDebounce; + /// - /// selection has been changed as the result of interaction with the carousel. + /// selection has been changed as the result of a user interaction. /// - private void carouselSelectionChanged(BeatmapInfo beatmap) + private void updateSelectedBeatmap(BeatmapInfo beatmap) { + var ruleset = base.Ruleset.Value; + void performLoad() { // We may be arriving here due to another component changing the bindable Beatmap. @@ -283,15 +291,18 @@ namespace osu.Game.Screens.Select ensurePlayingSelected(preview); } + Ruleset.Value = ruleset; + UpdateBeatmap(Beatmap.Value); } - if (beatmap?.Equals(beatmapNoDebounce) == true) + if (beatmap?.Equals(beatmapNoDebounce) == true && ruleset?.Equals(rulesetNoDebounce) == true) return; selectionChangedDebounce?.Cancel(); beatmapNoDebounce = beatmap; + rulesetNoDebounce = ruleset; if (beatmap == null) performLoad(); @@ -460,7 +471,7 @@ namespace osu.Game.Screens.Select { // in the case random selection failed, we want to trigger selectionChanged // to show the dummy beatmap (we have nothing else to display). - carouselSelectionChanged(null); + updateSelectedBeatmap(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 @@ - +