diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj
index 6ee9c3155e..180abd7fec 100644
--- a/osu.Desktop/osu.Desktop.csproj
+++ b/osu.Desktop/osu.Desktop.csproj
@@ -27,7 +27,7 @@
-
+
diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs
index 68a8dfb7d3..15d4edc411 100644
--- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs
@@ -41,6 +41,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
RepeatCount = curveData.RepeatCount,
X = (positionData?.X ?? 0) / CatchPlayfield.BASE_WIDTH,
NewCombo = comboData?.NewCombo ?? false,
+ ComboOffset = comboData?.ComboOffset ?? 0,
LegacyLastTickOffset = legacyOffset?.LegacyLastTickOffset ?? 0
};
}
@@ -51,7 +52,8 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
StartTime = obj.StartTime,
Samples = obj.Samples,
Duration = endTime.Duration,
- NewCombo = comboData?.NewCombo ?? false
+ NewCombo = comboData?.NewCombo ?? false,
+ ComboOffset = comboData?.ComboOffset ?? 0,
};
}
else
@@ -61,6 +63,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
StartTime = obj.StartTime,
Samples = obj.Samples,
NewCombo = comboData?.NewCombo ?? false,
+ ComboOffset = comboData?.ComboOffset ?? 0,
X = (positionData?.X ?? 0) / CatchPlayfield.BASE_WIDTH
};
}
diff --git a/osu.Game.Rulesets.Catch/Judgements/CatchBananaJudgement.cs b/osu.Game.Rulesets.Catch/Judgements/CatchBananaJudgement.cs
index c39e663d75..f38009263f 100644
--- a/osu.Game.Rulesets.Catch/Judgements/CatchBananaJudgement.cs
+++ b/osu.Game.Rulesets.Catch/Judgements/CatchBananaJudgement.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 osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Catch.Judgements
@@ -9,8 +10,6 @@ namespace osu.Game.Rulesets.Catch.Judgements
{
public override bool AffectsCombo => false;
- public override bool ShouldExplode => true;
-
protected override int NumericResultFor(HitResult result)
{
switch (result)
@@ -32,5 +31,7 @@ namespace osu.Game.Rulesets.Catch.Judgements
return 8;
}
}
+
+ public override bool ShouldExplodeFor(JudgementResult result) => true;
}
}
diff --git a/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs b/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs
index 51d7d3b5cd..8a51867899 100644
--- a/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs
+++ b/osu.Game.Rulesets.Catch/Judgements/CatchJudgement.cs
@@ -23,21 +23,10 @@ namespace osu.Game.Rulesets.Catch.Judgements
}
///
- /// The base health increase for the result achieved.
+ /// Retrieves the numeric health increase of a .
///
- 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.
+ /// The to find the numeric health increase for.
+ /// The numeric health increase of .
protected virtual float HealthIncreaseFor(HitResult result)
{
switch (result)
@@ -48,5 +37,18 @@ namespace osu.Game.Rulesets.Catch.Judgements
return 10.2f;
}
}
+
+ ///
+ /// Retrieves the numeric health increase of a .
+ ///
+ /// The to find the numeric health increase for.
+ /// The numeric health increase of .
+ public float HealthIncreaseFor(JudgementResult result) => HealthIncreaseFor(result.Type);
+
+ ///
+ /// Whether fruit on the platter should explode or drop.
+ /// Note that this is only checked if the owning object is also
+ ///
+ public virtual bool ShouldExplodeFor(JudgementResult result) => result.IsHit;
}
}
diff --git a/osu.Game.Rulesets.Catch/Objects/Banana.cs b/osu.Game.Rulesets.Catch/Objects/Banana.cs
index f7c60a7a47..e1af4c1075 100644
--- a/osu.Game.Rulesets.Catch/Objects/Banana.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Banana.cs
@@ -1,10 +1,15 @@
// 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;
+using osu.Game.Rulesets.Judgements;
+
namespace osu.Game.Rulesets.Catch.Objects
{
public class Banana : Fruit
{
public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana;
+
+ public override Judgement CreateJudgement() => new CatchBananaJudgement();
}
}
diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
index d55cdac115..621fc100c2 100644
--- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs
@@ -20,6 +20,8 @@ namespace osu.Game.Rulesets.Catch.Objects
public virtual bool NewCombo { get; set; }
+ public int ComboOffset { get; set; }
+
public int IndexInCurrentCombo { get; set; }
public int ComboIndex { get; set; }
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBanana.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBanana.cs
index dd027abbe0..8756a5178f 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBanana.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBanana.cs
@@ -1,8 +1,6 @@
// 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
@@ -11,7 +9,5 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
: 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 f039504600..697fab85c9 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBananaShower.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableBananaShower.cs
@@ -26,8 +26,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
AddNested(getVisualRepresentation?.Invoke(b));
}
- protected override bool ProvidesJudgement => false;
-
protected override void AddNested(DrawableHitObject h)
{
((DrawableCatchHitObject)h).CheckPosition = o => CheckPosition?.Invoke(o) ?? false;
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs
index 6ce2e6a2ae..9e840301fd 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableCatchHitObject.cs
@@ -5,7 +5,6 @@ using System;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics;
-using osu.Game.Rulesets.Catch.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
@@ -53,20 +52,14 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
public Func CheckPosition;
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (CheckPosition == null) return;
- if (timeOffset >= 0)
- {
- var judgement = CreateJudgement();
- judgement.Result = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss;
- AddJudgement(judgement);
- }
+ if (timeOffset >= 0 && Result != null)
+ ApplyResult(r => r.Type = CheckPosition.Invoke(HitObject) ? HitResult.Perfect : HitResult.Miss);
}
- 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 11d5ed1f92..5c8a7c4a7c 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableDroplet.cs
@@ -6,7 +6,6 @@ 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
{
@@ -24,8 +23,6 @@ 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/DrawableJuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs
index 854b63edeb..e66852c5c2 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableJuiceStream.cs
@@ -26,8 +26,6 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
AddNested(getVisualRepresentation?.Invoke(o));
}
- protected override bool ProvidesJudgement => false;
-
protected override void AddNested(DrawableHitObject h)
{
var catchObject = (DrawableCatchHitObject)h;
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableTinyDroplet.cs b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableTinyDroplet.cs
index 2232bb81a7..e0f02454c4 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableTinyDroplet.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawable/DrawableTinyDroplet.cs
@@ -2,18 +2,15 @@
// 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)
+ public DrawableTinyDroplet(TinyDroplet 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/Droplet.cs b/osu.Game.Rulesets.Catch/Objects/Droplet.cs
index f91a70c506..8b54922959 100644
--- a/osu.Game.Rulesets.Catch/Objects/Droplet.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Droplet.cs
@@ -1,9 +1,13 @@
// 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;
+using osu.Game.Rulesets.Judgements;
+
namespace osu.Game.Rulesets.Catch.Objects
{
public class Droplet : CatchHitObject
{
+ public override Judgement CreateJudgement() => new CatchDropletJudgement();
}
}
diff --git a/osu.Game.Rulesets.Catch/Objects/Fruit.cs b/osu.Game.Rulesets.Catch/Objects/Fruit.cs
index fcbb339ffd..2c2cd013c3 100644
--- a/osu.Game.Rulesets.Catch/Objects/Fruit.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Fruit.cs
@@ -1,9 +1,13 @@
// 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;
+using osu.Game.Rulesets.Judgements;
+
namespace osu.Game.Rulesets.Catch.Objects
{
public class Fruit : CatchHitObject
{
+ public override Judgement CreateJudgement() => new CatchJudgement();
}
}
diff --git a/osu.Game.Rulesets.Catch/Objects/TinyDroplet.cs b/osu.Game.Rulesets.Catch/Objects/TinyDroplet.cs
index 76cc8d9808..39f1cadad5 100644
--- a/osu.Game.Rulesets.Catch/Objects/TinyDroplet.cs
+++ b/osu.Game.Rulesets.Catch/Objects/TinyDroplet.cs
@@ -1,9 +1,13 @@
// 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;
+using osu.Game.Rulesets.Judgements;
+
namespace osu.Game.Rulesets.Catch.Objects
{
public class TinyDroplet : Droplet
{
+ public override Judgement CreateJudgement() => new CatchTinyDropletJudgement();
}
}
diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
index 5b69d836a3..403cedde8c 100644
--- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
+++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs
@@ -2,7 +2,6 @@
// 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;
@@ -21,55 +20,28 @@ namespace osu.Game.Rulesets.Catch.Scoring
private float hpDrainRate;
- protected override void SimulateAutoplay(Beatmap beatmap)
+ protected override void ApplyBeatmap(Beatmap beatmap)
{
- hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate;
+ base.ApplyBeatmap(beatmap);
- foreach (var obj in beatmap.HitObjects)
- {
- switch (obj)
- {
- case JuiceStream stream:
- 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 CatchBananaJudgement { Result = HitResult.Perfect });
- break;
- case Fruit _:
- AddJudgement(new CatchJudgement { Result = HitResult.Perfect });
- break;
- }
- }
+ hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate;
}
private const double harshness = 0.01;
- protected override void OnNewJudgement(Judgement judgement)
+ protected override void ApplyResult(JudgementResult result)
{
- base.OnNewJudgement(judgement);
+ base.ApplyResult(result);
- if (judgement.Result == HitResult.Miss)
+ if (result.Type == HitResult.Miss)
{
- if (!judgement.IsBonus)
+ if (!result.Judgement.IsBonus)
Health.Value -= hpDrainRate * (harshness * 2);
return;
}
- if (judgement is CatchJudgement catchJudgement)
- Health.Value += Math.Max(catchJudgement.HealthIncrease - hpDrainRate, 0) * harshness;
+ if (result.Judgement is CatchJudgement catchJudgement)
+ Health.Value += Math.Max(catchJudgement.HealthIncreaseFor(result) - hpDrainRate, 0) * harshness;
}
}
}
diff --git a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
index ea3b6fb0e0..d49be69856 100644
--- a/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatchPlayfield.cs
@@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Catch.UI
public override void Add(DrawableHitObject h)
{
- h.OnJudgement += onJudgement;
+ h.OnNewResult += onNewResult;
base.Add(h);
@@ -67,6 +67,7 @@ namespace osu.Game.Rulesets.Catch.UI
fruit.CheckPosition = CheckIfWeCanCatch;
}
- private void onJudgement(DrawableHitObject judgedObject, Judgement judgement) => catcherArea.OnJudgement((DrawableCatchHitObject)judgedObject, judgement);
+ private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
+ => catcherArea.OnResult((DrawableCatchHitObject)judgedObject, result);
}
}
diff --git a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
index 7b06426b07..4327abb96f 100644
--- a/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
+++ b/osu.Game.Rulesets.Catch/UI/CatcherArea.cs
@@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Catch.UI
private DrawableCatchHitObject lastPlateableFruit;
- public void OnJudgement(DrawableCatchHitObject fruit, Judgement judgement)
+ public void OnResult(DrawableCatchHitObject fruit, JudgementResult result)
{
void runAfterLoaded(Action action)
{
@@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Catch.UI
lastPlateableFruit.OnLoadComplete = _ => action();
}
- if (judgement.IsHit && fruit.CanBePlated)
+ if (result.IsHit && fruit.CanBePlated)
{
var caughtFruit = (DrawableCatchHitObject)GetVisualRepresentation?.Invoke(fruit.HitObject);
@@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Catch.UI
if (fruit.HitObject.LastInCombo)
{
- if (((CatchJudgement)judgement).ShouldExplode)
+ if (((CatchJudgement)result.Judgement).ShouldExplodeFor(result))
runAfterLoaded(() => MovableCatcher.Explode());
else
MovableCatcher.Drop();
diff --git a/osu.Game.Rulesets.Mania.Tests/ScrollingTestContainer.cs b/osu.Game.Rulesets.Mania.Tests/ScrollingTestContainer.cs
index 5a93efb0dc..29663c2093 100644
--- a/osu.Game.Rulesets.Mania.Tests/ScrollingTestContainer.cs
+++ b/osu.Game.Rulesets.Mania.Tests/ScrollingTestContainer.cs
@@ -14,24 +14,20 @@ namespace osu.Game.Rulesets.Mania.Tests
///
public class ScrollingTestContainer : Container
{
- private readonly ScrollingDirection direction;
+ [Cached(Type = typeof(IScrollingInfo))]
+ private readonly TestScrollingInfo scrollingInfo = new TestScrollingInfo();
public ScrollingTestContainer(ScrollingDirection direction)
{
- this.direction = direction;
+ scrollingInfo.Direction.Value = direction;
}
- protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
- {
- var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
- dependencies.CacheAs(new ScrollingInfo { Direction = { Value = direction }});
- return dependencies;
- }
+ public void Flip() => scrollingInfo.Direction.Value = scrollingInfo.Direction.Value == ScrollingDirection.Up ? ScrollingDirection.Down : ScrollingDirection.Up;
+ }
- private class ScrollingInfo : IScrollingInfo
- {
- public readonly Bindable Direction = new Bindable();
- IBindable IScrollingInfo.Direction => Direction;
- }
+ public class TestScrollingInfo : IScrollingInfo
+ {
+ public readonly Bindable Direction = new Bindable();
+ IBindable IScrollingInfo.Direction => Direction;
}
}
diff --git a/osu.Game.Rulesets.Mania.Tests/TestCaseEditor.cs b/osu.Game.Rulesets.Mania.Tests/TestCaseEditor.cs
new file mode 100644
index 0000000000..6c0f931cda
--- /dev/null
+++ b/osu.Game.Rulesets.Mania.Tests/TestCaseEditor.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 NUnit.Framework;
+using osu.Framework.Allocation;
+using osu.Framework.Configuration;
+using osu.Game.Rulesets.Mania.Configuration;
+using osu.Game.Rulesets.Mania.UI;
+using osu.Game.Tests.Visual;
+
+namespace osu.Game.Rulesets.Mania.Tests
+{
+ [TestFixture]
+ public class TestCaseEditor : EditorTestCase
+ {
+ private readonly Bindable direction = new Bindable();
+
+ public TestCaseEditor()
+ : base(new ManiaRuleset())
+ {
+ AddStep("upwards scroll", () => direction.Value = ManiaScrollingDirection.Up);
+ AddStep("downwards scroll", () => direction.Value = ManiaScrollingDirection.Down);
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(RulesetConfigCache configCache)
+ {
+ var config = (ManiaConfigManager)configCache.GetConfigFor(Ruleset.Value.CreateInstance());
+ config.BindWith(ManiaSetting.ScrollDirection, direction);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania.Tests/TestCaseNotes.cs b/osu.Game.Rulesets.Mania.Tests/TestCaseNotes.cs
index fdc8f362f7..a8b2b20fda 100644
--- a/osu.Game.Rulesets.Mania.Tests/TestCaseNotes.cs
+++ b/osu.Game.Rulesets.Mania.Tests/TestCaseNotes.cs
@@ -46,15 +46,20 @@ namespace osu.Game.Rulesets.Mania.Tests
Spacing = new Vector2(20),
Children = new[]
{
- createNoteDisplay(ScrollingDirection.Down),
- createNoteDisplay(ScrollingDirection.Up),
- createHoldNoteDisplay(ScrollingDirection.Down),
- createHoldNoteDisplay(ScrollingDirection.Up),
+ createNoteDisplay(ScrollingDirection.Down, 1, out var note1),
+ createNoteDisplay(ScrollingDirection.Up, 2, out var note2),
+ createHoldNoteDisplay(ScrollingDirection.Down, 1, out var holdNote1),
+ createHoldNoteDisplay(ScrollingDirection.Up, 2, out var holdNote2),
}
};
+
+ AddAssert("note 1 facing downwards", () => verifyAnchors(note1, Anchor.y2));
+ AddAssert("note 2 facing upwards", () => verifyAnchors(note2, Anchor.y0));
+ AddAssert("hold note 1 facing downwards", () => verifyAnchors(holdNote1, Anchor.y2));
+ AddAssert("hold note 2 facing upwards", () => verifyAnchors(holdNote2, Anchor.y0));
}
- private Drawable createNoteDisplay(ScrollingDirection direction)
+ private Drawable createNoteDisplay(ScrollingDirection direction, int identifier, out DrawableNote hitObject)
{
var note = new Note { StartTime = 999999999 };
note.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
@@ -62,24 +67,24 @@ namespace osu.Game.Rulesets.Mania.Tests
return new ScrollingTestContainer(direction)
{
AutoSizeAxes = Axes.Both,
- Child = new NoteContainer(direction, $"note, scrolling {direction.ToString().ToLowerInvariant()}")
+ Child = new NoteContainer(direction, $"note {identifier}, scrolling {direction.ToString().ToLowerInvariant()}")
{
- Child = new DrawableNote(note) { AccentColour = Color4.OrangeRed }
+ Child = hitObject = new DrawableNote(note) { AccentColour = Color4.OrangeRed }
}
};
}
- private Drawable createHoldNoteDisplay(ScrollingDirection direction)
+ private Drawable createHoldNoteDisplay(ScrollingDirection direction, int identifier, out DrawableHoldNote hitObject)
{
- var note = new HoldNote { StartTime = 999999999, Duration = 1000 };
+ var note = new HoldNote { StartTime = 999999999, Duration = 5000 };
note.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
return new ScrollingTestContainer(direction)
{
AutoSizeAxes = Axes.Both,
- Child = new NoteContainer(direction, $"hold note, scrolling {direction.ToString().ToLowerInvariant()}")
+ Child = new NoteContainer(direction, $"hold note {identifier}, scrolling {direction.ToString().ToLowerInvariant()}")
{
- Child = new DrawableHoldNote(note)
+ Child = hitObject = new DrawableHoldNote(note)
{
RelativeSizeAxes = Axes.Both,
AccentColour = Color4.OrangeRed,
@@ -88,6 +93,12 @@ namespace osu.Game.Rulesets.Mania.Tests
};
}
+ private bool verifyAnchors(DrawableHitObject hitObject, Anchor expectedAnchor)
+ => hitObject.Anchor.HasFlag(expectedAnchor) && hitObject.Origin.HasFlag(expectedAnchor);
+
+ private bool verifyAnchors(DrawableHoldNote holdNote, Anchor expectedAnchor)
+ => verifyAnchors((DrawableHitObject)holdNote, expectedAnchor) && holdNote.NestedHitObjects.All(n => verifyAnchors(n, expectedAnchor));
+
private class NoteContainer : Container
{
private readonly Container content;
diff --git a/osu.Game.Rulesets.Mania.Tests/TestCaseStage.cs b/osu.Game.Rulesets.Mania.Tests/TestCaseStage.cs
index b1bb7f5187..5c5d955168 100644
--- a/osu.Game.Rulesets.Mania.Tests/TestCaseStage.cs
+++ b/osu.Game.Rulesets.Mania.Tests/TestCaseStage.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
+using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
@@ -24,6 +25,8 @@ namespace osu.Game.Rulesets.Mania.Tests
private readonly List stages = new List();
+ private FillFlowContainer fill;
+
public TestCaseStage()
: base(columns)
{
@@ -32,7 +35,7 @@ namespace osu.Game.Rulesets.Mania.Tests
[BackgroundDependencyLoader]
private void load()
{
- Child = new FillFlowContainer
+ Child = fill = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
@@ -54,8 +57,22 @@ namespace osu.Game.Rulesets.Mania.Tests
AddStep("hold note", createHoldNote);
AddStep("minor bar line", () => createBarLine(false));
AddStep("major bar line", () => createBarLine(true));
+
+ AddAssert("check note anchors", () => notesInStageAreAnchored(stages[0], Anchor.TopCentre));
+ AddAssert("check note anchors", () => notesInStageAreAnchored(stages[1], Anchor.BottomCentre));
+
+ AddStep("flip direction", () =>
+ {
+ foreach (var c in fill.Children)
+ c.Flip();
+ });
+
+ AddAssert("check note anchors", () => notesInStageAreAnchored(stages[0], Anchor.BottomCentre));
+ AddAssert("check note anchors", () => notesInStageAreAnchored(stages[1], Anchor.TopCentre));
}
+ private bool notesInStageAreAnchored(ManiaStage stage, Anchor anchor) => stage.Columns.SelectMany(c => c.AllHitObjects).All(o => o.Anchor == anchor);
+
private void createNote()
{
foreach (var stage in stages)
@@ -101,7 +118,7 @@ namespace osu.Game.Rulesets.Mania.Tests
}
}
- private Drawable createStage(ScrollingDirection direction, ManiaAction action)
+ private ScrollingTestContainer createStage(ScrollingDirection direction, ManiaAction action)
{
var specialAction = ManiaAction.Special1;
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
index 8d0d78120a..010cf962cc 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
@@ -176,16 +176,23 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int nextColumn = Random.Next(RandomStart, TotalColumns);
for (int i = 0; i < Math.Min(usableColumns, noteCount); i++)
{
- while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn)) //find available column
+ // Find available column
+ RunWhile(() => pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn), () =>
+ {
nextColumn = Random.Next(RandomStart, TotalColumns);
+ });
+
addToPattern(pattern, nextColumn, startTime, EndTime);
}
// This is can't be combined with the above loop due to RNG
for (int i = 0; i < noteCount - usableColumns; i++)
{
- while (pattern.ColumnHasObject(nextColumn))
+ RunWhile(() => pattern.ColumnHasObject(nextColumn), () =>
+ {
nextColumn = Random.Next(RandomStart, TotalColumns);
+ });
+
addToPattern(pattern, nextColumn, startTime, EndTime);
}
@@ -211,16 +218,21 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
{
- while (PreviousPattern.ColumnHasObject(nextColumn))
+ RunWhile(() => PreviousPattern.ColumnHasObject(nextColumn), () =>
+ {
nextColumn = Random.Next(RandomStart, TotalColumns);
+ });
}
int lastColumn = nextColumn;
for (int i = 0; i < noteCount; i++)
{
addToPattern(pattern, nextColumn, startTime, startTime);
- while (nextColumn == lastColumn)
+
+ RunWhile(() => nextColumn == lastColumn, () =>
+ {
nextColumn = Random.Next(RandomStart, TotalColumns);
+ });
lastColumn = nextColumn;
startTime += SegmentDuration;
@@ -393,14 +405,18 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
{
- while (PreviousPattern.ColumnHasObject(nextColumn))
+ RunWhile(() => PreviousPattern.ColumnHasObject(nextColumn), () =>
+ {
nextColumn = Random.Next(RandomStart, TotalColumns);
+ });
}
for (int i = 0; i < columnRepeat; i++)
{
- while (pattern.ColumnHasObject(nextColumn))
+ RunWhile(() => pattern.ColumnHasObject(nextColumn), () =>
+ {
nextColumn = Random.Next(RandomStart, TotalColumns);
+ });
addToPattern(pattern, nextColumn, startTime, EndTime);
startTime += SegmentDuration;
@@ -427,8 +443,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int holdColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
if (convertType.HasFlag(PatternType.ForceNotStack) && PreviousPattern.ColumnWithObjects < TotalColumns)
{
- while (PreviousPattern.ColumnHasObject(holdColumn))
+ RunWhile(() => PreviousPattern.ColumnHasObject(holdColumn), () =>
+ {
holdColumn = Random.Next(RandomStart, TotalColumns);
+ });
}
// Create the hold note
@@ -455,8 +473,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
for (int j = 0; j < noteCount; j++)
{
- while (rowPattern.ColumnHasObject(nextColumn) || nextColumn == holdColumn)
+ RunWhile(() => rowPattern.ColumnHasObject(nextColumn) || nextColumn == holdColumn, () =>
+ {
nextColumn = Random.Next(RandomStart, TotalColumns);
+ });
+
addToPattern(rowPattern, nextColumn, startTime, startTime);
}
}
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
index 06b4b8a27e..eae9a0fc3b 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs
@@ -59,8 +59,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{
int nextColumn = Random.Next(start, TotalColumns);
- while (PreviousPattern.ColumnHasObject(nextColumn))
+ RunWhile(() => PreviousPattern.ColumnHasObject(nextColumn), () =>
+ {
nextColumn = Random.Next(start, TotalColumns);
+ });
return nextColumn;
}
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
index 84ebfdb839..930ca26660 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
@@ -234,7 +234,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
for (int i = 0; i < noteCount; i++)
{
- while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn) && !allowStacking)
+ RunWhile(() => pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn) && !allowStacking, () =>
{
if (convertType.HasFlag(PatternType.Gathered))
{
@@ -244,7 +244,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
}
else
nextColumn = Random.Next(RandomStart, TotalColumns);
- }
+ });
addToPattern(pattern, nextColumn);
}
@@ -295,8 +295,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
int nextColumn = Random.Next(RandomStart, columnLimit);
for (int i = 0; i < noteCount; i++)
{
- while (pattern.ColumnHasObject(nextColumn))
+ RunWhile(() => pattern.ColumnHasObject(nextColumn), () =>
+ {
nextColumn = Random.Next(RandomStart, columnLimit);
+ });
// Add normal note
addToPattern(pattern, nextColumn);
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs
index a42d57cdd1..3434c9f01e 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/PatternGenerator.cs
@@ -3,6 +3,9 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
+using JetBrains.Annotations;
+using osu.Framework.Logging;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
@@ -12,6 +15,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
///
internal abstract class PatternGenerator
{
+ ///
+ /// An arbitrary maximum amount of iterations to perform in .
+ /// The specific value is not super important - enough such that no false-positives occur.
+ ///
+ private const int max_rng_iterations = 20;
+
///
/// The last pattern.
///
@@ -42,6 +51,21 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
TotalColumns = Beatmap.TotalColumns;
}
+ protected void RunWhile([InstantHandle] Func condition, Action action)
+ {
+ int iterations = 0;
+
+ while (condition() && iterations++ < max_rng_iterations)
+ action();
+
+ if (iterations < max_rng_iterations)
+ return;
+
+ // Generate + log an error/stacktrace
+
+ Logger.Log($"Allowable iterations ({max_rng_iterations}) exceeded:\n{new StackTrace(0)}", level: LogLevel.Error);
+ }
+
///
/// Generates the patterns for , each filled with hit objects.
///
diff --git a/osu.Game.Rulesets.Mania/Edit/Layers/Selection/Overlays/HoldNoteMask.cs b/osu.Game.Rulesets.Mania/Edit/Layers/Selection/Overlays/HoldNoteMask.cs
new file mode 100644
index 0000000000..bfa6bc0a17
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Edit/Layers/Selection/Overlays/HoldNoteMask.cs
@@ -0,0 +1,84 @@
+// 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.Configuration;
+using osu.Framework.Graphics;
+using osu.Game.Graphics;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Mania.Objects.Drawables;
+using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
+using osu.Game.Rulesets.Mania.UI;
+using osu.Game.Rulesets.UI.Scrolling;
+using OpenTK;
+using OpenTK.Graphics;
+
+namespace osu.Game.Rulesets.Mania.Edit.Layers.Selection.Overlays
+{
+ public class HoldNoteMask : HitObjectMask
+ {
+ public new DrawableHoldNote HitObject => (DrawableHoldNote)base.HitObject;
+
+ private readonly IBindable direction = new Bindable();
+
+ private readonly BodyPiece body;
+
+ public HoldNoteMask(DrawableHoldNote hold)
+ : base(hold)
+ {
+ InternalChildren = new Drawable[]
+ {
+ new HoldNoteNoteMask(hold.Head),
+ new HoldNoteNoteMask(hold.Tail),
+ body = new BodyPiece
+ {
+ AccentColour = Color4.Transparent
+ },
+ };
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours, IScrollingInfo scrollingInfo)
+ {
+ body.BorderColour = colours.Yellow;
+
+ direction.BindTo(scrollingInfo.Direction);
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ Size = HitObject.DrawSize + new Vector2(0, HitObject.Tail.DrawHeight);
+ Position = Parent.ToLocalSpace(HitObject.ScreenSpaceDrawQuad.TopLeft);
+
+ // This is a side-effect of not matching the hitobject's anchors/origins, which is kinda hard to do
+ // When scrolling upwards our origin is already at the top of the head note (which is the intended location),
+ // but when scrolling downwards our origin is at the _bottom_ of the tail note (where we need to be at the _top_ of the tail note)
+ if (direction.Value == ScrollingDirection.Down)
+ Y -= HitObject.Tail.DrawHeight;
+ }
+
+ private class HoldNoteNoteMask : NoteMask
+ {
+ public HoldNoteNoteMask(DrawableNote note)
+ : base(note)
+ {
+ Select();
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ Anchor = HitObject.Anchor;
+ Origin = HitObject.Origin;
+
+ Position = HitObject.DrawPosition;
+ }
+
+ // Todo: This is temporary, since the note masks don't do anything special yet. In the future they will handle input.
+ public override bool HandleMouseInput => false;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Edit/Layers/Selection/Overlays/NoteMask.cs b/osu.Game.Rulesets.Mania/Edit/Layers/Selection/Overlays/NoteMask.cs
new file mode 100644
index 0000000000..78f876cb14
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Edit/Layers/Selection/Overlays/NoteMask.cs
@@ -0,0 +1,39 @@
+// 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.Game.Graphics;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Mania.Objects.Drawables;
+using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
+
+namespace osu.Game.Rulesets.Mania.Edit.Layers.Selection.Overlays
+{
+ public class NoteMask : HitObjectMask
+ {
+ public NoteMask(DrawableNote note)
+ : base(note)
+ {
+ Scale = note.Scale;
+
+ CornerRadius = 5;
+ Masking = true;
+
+ AddInternal(new NotePiece());
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ Colour = colours.Yellow;
+ }
+
+ protected override void Update()
+ {
+ base.Update();
+
+ Size = HitObject.DrawSize;
+ Position = Parent.ToLocalSpace(HitObject.ScreenSpaceDrawQuad.TopLeft);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaEditPlayfield.cs b/osu.Game.Rulesets.Mania/Edit/ManiaEditPlayfield.cs
new file mode 100644
index 0000000000..e7bc526471
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Edit/ManiaEditPlayfield.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.Mania.Beatmaps;
+using osu.Game.Rulesets.Mania.UI;
+using System.Collections.Generic;
+
+namespace osu.Game.Rulesets.Mania.Edit
+{
+ public class ManiaEditPlayfield : ManiaPlayfield
+ {
+ public ManiaEditPlayfield(List stages)
+ : base(stages)
+ {
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaEditRulesetContainer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaEditRulesetContainer.cs
new file mode 100644
index 0000000000..a01947a60b
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Edit/ManiaEditRulesetContainer.cs
@@ -0,0 +1,27 @@
+// 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 OpenTK;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Mania.UI;
+using osu.Game.Rulesets.UI;
+
+namespace osu.Game.Rulesets.Mania.Edit
+{
+ public class ManiaEditRulesetContainer : ManiaRulesetContainer
+ {
+ public ManiaEditRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
+ : base(ruleset, beatmap)
+ {
+ }
+
+ protected override Playfield CreatePlayfield() => new ManiaEditPlayfield(Beatmap.Stages)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ };
+
+ protected override Vector2 PlayfieldArea => Vector2.One;
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
new file mode 100644
index 0000000000..f37d8134ce
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs
@@ -0,0 +1,56 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Edit;
+using osu.Game.Rulesets.Edit.Tools;
+using osu.Game.Rulesets.Mania.Edit.Layers.Selection.Overlays;
+using osu.Game.Rulesets.Mania.Objects;
+using osu.Game.Rulesets.Mania.Objects.Drawables;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.UI;
+using System.Collections.Generic;
+using osu.Framework.Allocation;
+using osu.Game.Rulesets.Mania.Configuration;
+using osu.Game.Rulesets.Mania.UI;
+
+namespace osu.Game.Rulesets.Mania.Edit
+{
+ public class ManiaHitObjectComposer : HitObjectComposer
+ {
+ protected new ManiaConfigManager Config => (ManiaConfigManager)base.Config;
+
+ public ManiaHitObjectComposer(Ruleset ruleset)
+ : base(ruleset)
+ {
+ }
+
+ protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
+ {
+ var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
+ dependencies.CacheAs(new ManiaScrollingInfo(Config));
+ return dependencies;
+ }
+
+ protected override RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => new ManiaEditRulesetContainer(ruleset, beatmap);
+
+ protected override IReadOnlyList CompositionTools => new ICompositionTool[]
+ {
+ new HitObjectCompositionTool("Note"),
+ new HitObjectCompositionTool("Hold"),
+ };
+
+ public override HitObjectMask CreateMaskFor(DrawableHitObject hitObject)
+ {
+ switch (hitObject)
+ {
+ case DrawableNote note:
+ return new NoteMask(note);
+ case DrawableHoldNote holdNote:
+ return new HoldNoteMask(holdNote);
+ }
+
+ return base.CreateMaskFor(hitObject);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs b/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs
deleted file mode 100644
index 3a4beda77d..0000000000
--- a/osu.Game.Rulesets.Mania/Judgements/HoldNoteTailJudgement.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-// 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.Mania.Judgements
-{
- public class HoldNoteTailJudgement : ManiaJudgement
- {
- ///
- /// Whether the hold note has been released too early and shouldn't give full score for the release.
- ///
- public bool HasBroken;
-
- protected override int NumericResultFor(HitResult result)
- {
- switch (result)
- {
- default:
- return base.NumericResultFor(result);
- case HitResult.Great:
- case HitResult.Perfect:
- return base.NumericResultFor(HasBroken ? HitResult.Good : result);
- }
- }
- }
-}
diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
index 1cd1714705..19e89c8ec5 100644
--- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
@@ -19,9 +19,11 @@ using osu.Game.Configuration;
using osu.Game.Overlays.Settings;
using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Difficulty;
+using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Configuration;
using osu.Game.Rulesets.Mania.Difficulty;
+using osu.Game.Rulesets.Mania.Edit;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Mania
@@ -32,6 +34,8 @@ namespace osu.Game.Rulesets.Mania
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap);
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, Score score) => new ManiaPerformanceCalculator(this, beatmap, score);
+ public override HitObjectComposer CreateHitObjectComposer() => new ManiaHitObjectComposer(this);
+
public override IEnumerable ConvertLegacyMods(LegacyMods mods)
{
if (mods.HasFlag(LegacyMods.Nightcore))
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
index 597450f223..af2a889f03 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
@@ -7,7 +7,6 @@ using osu.Framework.Graphics;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using OpenTK.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Game.Rulesets.Mania.Judgements;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI.Scrolling;
@@ -19,10 +18,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
///
public class DrawableHoldNote : DrawableManiaHitObject, IKeyBindingHandler
{
- public override bool DisplayJudgement => false;
+ public override bool DisplayResult => false;
- private readonly DrawableNote head;
- private readonly DrawableNote tail;
+ public readonly DrawableNote Head;
+ public readonly DrawableNote Tail;
private readonly BodyPiece bodyPiece;
@@ -57,12 +56,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
HoldStartTime = () => holdStartTime
})
},
- head = new DrawableHeadNote(this)
+ Head = new DrawableHeadNote(this)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
},
- tail = new DrawableTailNote(this)
+ Tail = new DrawableTailNote(this)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre
@@ -72,8 +71,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
foreach (var tick in tickContainer)
AddNested(tick);
- AddNested(head);
- AddNested(tail);
+ AddNested(Head);
+ AddNested(Tail);
}
protected override void OnDirectionChanged(ScrollingDirection direction)
@@ -91,16 +90,16 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
base.AccentColour = value;
bodyPiece.AccentColour = value;
- head.AccentColour = value;
- tail.AccentColour = value;
+ Head.AccentColour = value;
+ Tail.AccentColour = value;
tickContainer.ForEach(t => t.AccentColour = value);
}
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
- if (tail.AllJudged)
- AddJudgement(new HoldNoteJudgement { Result = HitResult.Perfect });
+ if (Tail.AllJudged)
+ ApplyResult(r => r.Type = HitResult.Perfect);
}
protected override void Update()
@@ -108,8 +107,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
base.Update();
// Make the body piece not lie under the head note
- bodyPiece.Y = (Direction.Value == ScrollingDirection.Up ? 1 : -1) * head.Height / 2;
- bodyPiece.Height = DrawHeight - head.Height / 2 + tail.Height / 2;
+ bodyPiece.Y = (Direction.Value == ScrollingDirection.Up ? 1 : -1) * Head.Height / 2;
+ bodyPiece.Height = DrawHeight - Head.Height / 2 + Tail.Height / 2;
}
public bool OnPressed(ManiaAction action)
@@ -141,7 +140,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
holdStartTime = null;
// If the key has been released too early, the user should not receive full score for the release
- if (!tail.IsHit)
+ if (!Tail.IsHit)
hasBroken = true;
return true;
@@ -166,7 +165,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
return false;
// If the key has been released too early, the user should not receive full score for the release
- if (Judgements.Any(j => j.Result == HitResult.Miss))
+ if (Result.Type == HitResult.Miss)
holdNote.hasBroken = true;
// The head note also handles early hits before the body, but we want accurate early hits to count as the body being held
@@ -197,7 +196,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
this.holdNote = holdNote;
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
// Factor in the release lenience
timeOffset /= release_window_lenience;
@@ -205,13 +204,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
- {
- AddJudgement(new HoldNoteTailJudgement
- {
- Result = HitResult.Miss,
- HasBroken = holdNote.hasBroken
- });
- }
+ ApplyResult(r => r.Type = HitResult.Miss);
return;
}
@@ -220,10 +213,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (result == HitResult.None)
return;
- AddJudgement(new HoldNoteTailJudgement
+ ApplyResult(r =>
{
- Result = result,
- HasBroken = holdNote.hasBroken
+ if (holdNote.hasBroken && (result == HitResult.Perfect || result == HitResult.Perfect))
+ result = HitResult.Good;
+
+ r.Type = result;
});
}
@@ -238,7 +233,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (action != Action.Value)
return false;
- UpdateJudgement(true);
+ UpdateResult(true);
// Handled by the hold note, which will set holding = false
return false;
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs
index 5df6079efa..01d5bc6fd4 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTick.cs
@@ -7,7 +7,6 @@ using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Game.Rulesets.Mania.Judgements;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Scoring;
@@ -72,29 +71,17 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
}
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
- if (!userTriggered)
- return;
-
if (Time.Current < HitObject.StartTime)
return;
- if (HoldStartTime?.Invoke() > HitObject.StartTime)
- return;
+ var startTime = HoldStartTime?.Invoke();
- AddJudgement(new HoldNoteTickJudgement { Result = HitResult.Perfect });
- }
-
- protected override void Update()
- {
- if (AllJudged)
- return;
-
- if (HoldStartTime?.Invoke() == null)
- return;
-
- UpdateJudgement(true);
+ if (startTime == null || startTime > HitObject.StartTime)
+ ApplyResult(r => r.Type = HitResult.Miss);
+ else
+ ApplyResult(r => r.Type = HitResult.Perfect);
}
}
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
index 18084c4c08..7567f40b2f 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs
@@ -6,7 +6,6 @@ using OpenTK.Graphics;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings;
-using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI.Scrolling;
@@ -56,12 +55,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
}
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
- AddJudgement(new ManiaJudgement { Result = HitResult.Miss });
+ ApplyResult(r => r.Type = HitResult.Miss);
return;
}
@@ -69,7 +68,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (result == HitResult.None)
return;
- AddJudgement(new ManiaJudgement { Result = result });
+ ApplyResult(r => r.Type = result);
}
public virtual bool OnPressed(ManiaAction action)
@@ -77,7 +76,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
if (action != Action.Value)
return false;
- return UpdateJudgement(true);
+ return UpdateResult(true);
}
public virtual bool OnReleased(ManiaAction action) => false;
diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
index 22fa93a308..77562bb4c2 100644
--- a/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/HoldNote.cs
@@ -3,6 +3,8 @@
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Mania.Objects
@@ -55,7 +57,7 @@ namespace osu.Game.Rulesets.Mania.Objects
///
/// The tail note of the hold.
///
- public readonly Note Tail = new Note();
+ public readonly TailNote Tail = new TailNote();
///
/// The time between ticks of this hold.
@@ -68,9 +70,6 @@ namespace osu.Game.Rulesets.Mania.Objects
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
tickSpacing = timingPoint.BeatLength / difficulty.SliderTickRate;
-
- Head.ApplyDefaults(controlPointInfo, difficulty);
- Tail.ApplyDefaults(controlPointInfo, difficulty);
}
protected override void CreateNestedHitObjects()
@@ -78,6 +77,9 @@ namespace osu.Game.Rulesets.Mania.Objects
base.CreateNestedHitObjects();
createTicks();
+
+ AddNested(Head);
+ AddNested(Tail);
}
private void createTicks()
@@ -94,5 +96,7 @@ namespace osu.Game.Rulesets.Mania.Objects
});
}
}
+
+ public override Judgement CreateJudgement() => new HoldNoteJudgement();
}
}
diff --git a/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs b/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs
index d078c15c92..05959a31c0 100644
--- a/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs
+++ b/osu.Game.Rulesets.Mania/Objects/HoldNoteTick.cs
@@ -1,6 +1,9 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Mania.Judgements;
+
namespace osu.Game.Rulesets.Mania.Objects
{
///
@@ -8,5 +11,6 @@ namespace osu.Game.Rulesets.Mania.Objects
///
public class HoldNoteTick : ManiaHitObject
{
+ public override Judgement CreateJudgement() => new HoldNoteTickJudgement();
}
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Note.cs b/osu.Game.Rulesets.Mania/Objects/Note.cs
index 975188e550..42877649d2 100644
--- a/osu.Game.Rulesets.Mania/Objects/Note.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Note.cs
@@ -1,6 +1,9 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Mania.Judgements;
+
namespace osu.Game.Rulesets.Mania.Objects
{
///
@@ -8,5 +11,6 @@ namespace osu.Game.Rulesets.Mania.Objects
///
public class Note : ManiaHitObject
{
+ public override Judgement CreateJudgement() => new ManiaJudgement();
}
}
diff --git a/osu.Game.Rulesets.Mania/Objects/TailNote.cs b/osu.Game.Rulesets.Mania/Objects/TailNote.cs
new file mode 100644
index 0000000000..9de542bcd3
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/Objects/TailNote.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
+
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Mania.Judgements;
+
+namespace osu.Game.Rulesets.Mania.Objects
+{
+ public class TailNote : Note
+ {
+ public override Judgement CreateJudgement() => new ManiaJudgement();
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
index 4c955a680e..12b32c46ee 100644
--- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs
+++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.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 System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements;
@@ -97,31 +96,20 @@ namespace osu.Game.Rulesets.Mania.Scoring
{
}
- protected override void SimulateAutoplay(Beatmap beatmap)
+ protected override void ApplyBeatmap(Beatmap beatmap)
{
+ base.ApplyBeatmap(beatmap);
+
BeatmapDifficulty difficulty = beatmap.BeatmapInfo.BaseDifficulty;
hpMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_min, hp_multiplier_mid, hp_multiplier_max);
hpMissMultiplier = BeatmapDifficulty.DifficultyRange(difficulty.DrainRate, hp_multiplier_miss_min, hp_multiplier_miss_mid, hp_multiplier_miss_max);
+ }
+ protected override void SimulateAutoplay(Beatmap beatmap)
+ {
while (true)
{
- foreach (var obj in beatmap.HitObjects)
- {
- var holdNote = obj as HoldNote;
-
- if (holdNote != null)
- {
- // Head
- AddJudgement(new ManiaJudgement { Result = HitResult.Perfect });
-
- // Ticks
- int tickCount = holdNote.NestedHitObjects.OfType().Count();
- for (int i = 0; i < tickCount; i++)
- AddJudgement(new HoldNoteTickJudgement { Result = HitResult.Perfect });
- }
-
- AddJudgement(new ManiaJudgement { Result = HitResult.Perfect });
- }
+ base.SimulateAutoplay(beatmap);
if (!HasFailed)
break;
@@ -133,20 +121,20 @@ namespace osu.Game.Rulesets.Mania.Scoring
}
}
- protected override void OnNewJudgement(Judgement judgement)
+ protected override void ApplyResult(JudgementResult result)
{
- base.OnNewJudgement(judgement);
+ base.ApplyResult(result);
- bool isTick = judgement is HoldNoteTickJudgement;
+ bool isTick = result.Judgement is HoldNoteTickJudgement;
if (isTick)
{
- if (judgement.IsHit)
+ if (result.IsHit)
Health.Value += hpMultiplier * hp_increase_tick;
}
else
{
- switch (judgement.Result)
+ switch (result.Type)
{
case HitResult.Miss:
Health.Value += hpMissMultiplier * hp_increase_miss;
diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs
index 877189dd61..d489d48fc3 100644
--- a/osu.Game.Rulesets.Mania/UI/Column.cs
+++ b/osu.Game.Rulesets.Mania/UI/Column.cs
@@ -131,14 +131,14 @@ namespace osu.Game.Rulesets.Mania.UI
public override void Add(DrawableHitObject hitObject)
{
hitObject.AccentColour = AccentColour;
- hitObject.OnJudgement += OnJudgement;
+ hitObject.OnNewResult += OnNewResult;
HitObjects.Add(hitObject);
}
- internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
+ internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result)
{
- if (!judgement.IsHit || !judgedObject.DisplayJudgement || !DisplayJudgements)
+ if (!result.IsHit || !judgedObject.DisplayResult || !DisplayJudgements)
return;
explosionContainer.Add(new HitExplosion(judgedObject)
diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs
index 6566d44ef5..dc66249cd9 100644
--- a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs
+++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs
@@ -8,10 +8,10 @@ using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.UI
{
- internal class DrawableManiaJudgement : DrawableJudgement
+ public class DrawableManiaJudgement : DrawableJudgement
{
- public DrawableManiaJudgement(Judgement judgement, DrawableHitObject judgedObject)
- : base(judgement, judgedObject)
+ public DrawableManiaJudgement(JudgementResult result, DrawableHitObject judgedObject)
+ : base(result, judgedObject)
{
}
@@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.UI
this.FadeInFromZero(50, Easing.OutQuint);
- if (Judgement.IsHit)
+ if (Result.IsHit)
{
this.ScaleTo(0.8f);
this.ScaleTo(1, 250, Easing.OutElastic);
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
index f88169726e..6bf63443b5 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaPlayfield.cs
@@ -1,22 +1,20 @@
// 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.Game.Rulesets.Mania.Objects;
using osu.Framework.Graphics.Containers;
using System;
using System.Collections.Generic;
-using System.Linq;
-using osu.Framework.Allocation;
using osu.Game.Rulesets.Mania.Beatmaps;
-using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Mania.Configuration;
+using osu.Game.Rulesets.Mania.Objects;
+using osu.Game.Rulesets.Objects.Drawables;
namespace osu.Game.Rulesets.Mania.UI
{
public class ManiaPlayfield : ManiaScrollingPlayfield
{
- public List Columns => stages.SelectMany(x => x.Columns).ToList();
private readonly List stages = new List();
public ManiaPlayfield(List stageDefinitions)
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs
index aaa4505b5e..09ebde2799 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaRulesetContainer.cs
@@ -4,7 +4,6 @@
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
-using osu.Framework.Configuration;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Input;
@@ -35,8 +34,7 @@ namespace osu.Game.Rulesets.Mania.UI
public IEnumerable BarLines;
- private readonly Bindable configDirection = new Bindable();
- private ScrollingInfo scrollingInfo;
+ protected new ManiaConfigManager Config => (ManiaConfigManager)base.Config;
public ManiaRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
@@ -73,9 +71,6 @@ namespace osu.Game.Rulesets.Mania.UI
private void load()
{
BarLines.ForEach(Playfield.Add);
-
- ((ManiaConfigManager)Config).BindWith(ManiaSetting.ScrollDirection, configDirection);
- configDirection.BindValueChanged(d => scrollingInfo.Direction.Value = (ScrollingDirection)d, true);
}
private DependencyContainer dependencies;
@@ -83,11 +78,14 @@ namespace osu.Game.Rulesets.Mania.UI
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
{
dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
- dependencies.CacheAs(scrollingInfo = new ScrollingInfo());
+
+ if (dependencies.Get() == null)
+ dependencies.CacheAs(new ManiaScrollingInfo(Config));
+
return dependencies;
}
- protected sealed override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages)
+ protected override Playfield CreatePlayfield() => new ManiaPlayfield(Beatmap.Stages)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -115,11 +113,5 @@ namespace osu.Game.Rulesets.Mania.UI
protected override Vector2 PlayfieldArea => new Vector2(1, 0.8f);
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new ManiaFramedReplayInputHandler(replay);
-
- private class ScrollingInfo : IScrollingInfo
- {
- public readonly Bindable Direction = new Bindable();
- IBindable IScrollingInfo.Direction => Direction;
- }
}
}
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaScrollingInfo.cs b/osu.Game.Rulesets.Mania/UI/ManiaScrollingInfo.cs
new file mode 100644
index 0000000000..624ea13e1b
--- /dev/null
+++ b/osu.Game.Rulesets.Mania/UI/ManiaScrollingInfo.cs
@@ -0,0 +1,23 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Framework.Configuration;
+using osu.Game.Rulesets.Mania.Configuration;
+using osu.Game.Rulesets.UI.Scrolling;
+
+namespace osu.Game.Rulesets.Mania.UI
+{
+ public class ManiaScrollingInfo : IScrollingInfo
+ {
+ private readonly Bindable configDirection = new Bindable();
+
+ public readonly Bindable Direction = new Bindable();
+ IBindable IScrollingInfo.Direction => Direction;
+
+ public ManiaScrollingInfo(ManiaConfigManager config)
+ {
+ config.BindWith(ManiaSetting.ScrollDirection, configDirection);
+ configDirection.BindValueChanged(v => Direction.Value = (ScrollingDirection)v, true);
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs
index f386cf15a2..f292d5ff16 100644
--- a/osu.Game.Rulesets.Mania/UI/ManiaStage.cs
+++ b/osu.Game.Rulesets.Mania/UI/ManiaStage.cs
@@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mania.UI
///
/// A collection of s.
///
- internal class ManiaStage : ManiaScrollingPlayfield
+ public class ManiaStage : ManiaScrollingPlayfield
{
public const float HIT_TARGET_POSITION = 50;
@@ -156,18 +156,18 @@ namespace osu.Game.Rulesets.Mania.UI
var maniaObject = (ManiaHitObject)h.HitObject;
int columnIndex = maniaObject.Column - firstColumnIndex;
Columns.ElementAt(columnIndex).Add(h);
- h.OnJudgement += OnJudgement;
+ h.OnNewResult += OnNewResult;
}
public void Add(BarLine barline) => base.Add(new DrawableBarLine(barline));
- internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
+ internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result)
{
- if (!judgedObject.DisplayJudgement || !DisplayJudgements)
+ if (!judgedObject.DisplayResult || !DisplayJudgements)
return;
judgements.Clear();
- judgements.Add(new DrawableManiaJudgement(judgement, judgedObject)
+ judgements.Add(new DrawableManiaJudgement(result, judgedObject)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs
index 7af7140fd8..c2d3aab2ab 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestCaseHitCircle.cs
@@ -5,12 +5,10 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
-using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Tests.Visual;
using OpenTK;
-using osu.Game.Rulesets.Osu.Judgements;
using System.Collections.Generic;
using System;
using osu.Game.Rulesets.Mods;
@@ -96,19 +94,15 @@ namespace osu.Game.Rulesets.Osu.Tests
this.auto = auto;
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (auto && !userTriggered && timeOffset > 0)
{
// force success
- AddJudgement(new OsuJudgement
- {
- Result = HitResult.Great
- });
- State.Value = ArmedState.Hit;
+ ApplyResult(r => r.Type = HitResult.Great);
}
else
- base.CheckForJudgements(userTriggered, timeOffset);
+ base.CheckForResult(userTriggered, timeOffset);
}
}
}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs
index cb1ea5cc5f..3f9464a98f 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSlider.cs
@@ -304,13 +304,13 @@ namespace osu.Game.Rulesets.Osu.Tests
foreach (var mod in Mods.OfType())
mod.ApplyToDrawableHitObjects(new[] { drawable });
- drawable.OnJudgement += onJudgement;
+ drawable.OnNewResult += onNewResult;
Add(drawable);
}
private float judgementOffsetDirection = 1;
- private void onJudgement(DrawableHitObject judgedObject, Judgement judgement)
+ private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
{
var osuObject = judgedObject as DrawableOsuHitObject;
if (osuObject == null)
@@ -321,8 +321,8 @@ namespace osu.Game.Rulesets.Osu.Tests
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
- Text = judgement.IsHit ? "Hit!" : "Miss!",
- Colour = judgement.IsHit ? Color4.Green : Color4.Red,
+ Text = result.IsHit ? "Hit!" : "Miss!",
+ Colour = result.IsHit ? Color4.Green : Color4.Red,
TextSize = 30,
Position = osuObject.HitObject.StackedEndPosition + judgementOffsetDirection * new Vector2(0, 45)
});
diff --git a/osu.Game.Rulesets.Osu.Tests/TestCaseSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestCaseSpinner.cs
index b05a763e88..3b91ea93b8 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestCaseSpinner.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestCaseSpinner.cs
@@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.Tests
this.auto = auto;
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (auto && !userTriggered && Time.Current > Spinner.StartTime + Spinner.Duration / 2 && Progress < 1)
{
@@ -81,7 +81,7 @@ namespace osu.Game.Rulesets.Osu.Tests
auto = false;
}
- base.CheckForJudgements(userTriggered, timeOffset);
+ base.CheckForResult(userTriggered, timeOffset);
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs
index 405493cde4..93f3f06dc2 100644
--- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs
@@ -42,6 +42,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
RepeatCount = curveData.RepeatCount,
Position = positionData?.Position ?? Vector2.Zero,
NewCombo = comboData?.NewCombo ?? false,
+ ComboOffset = comboData?.ComboOffset ?? 0,
LegacyLastTickOffset = legacyOffset?.LegacyLastTickOffset
};
}
@@ -52,7 +53,9 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
StartTime = original.StartTime,
Samples = original.Samples,
EndTime = endTimeData.EndTime,
- Position = positionData?.Position ?? OsuPlayfield.BASE_SIZE / 2
+ Position = positionData?.Position ?? OsuPlayfield.BASE_SIZE / 2,
+ NewCombo = comboData?.NewCombo ?? false,
+ ComboOffset = comboData?.ComboOffset ?? 0,
};
}
else
@@ -62,7 +65,8 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
StartTime = original.StartTime,
Samples = original.Samples,
Position = positionData?.Position ?? Vector2.Zero,
- NewCombo = comboData?.NewCombo ?? false
+ NewCombo = comboData?.NewCombo ?? false,
+ ComboOffset = comboData?.ComboOffset ?? 0,
};
}
}
diff --git a/osu.Game.Rulesets.Osu/Judgements/ComboResult.cs b/osu.Game.Rulesets.Osu/Judgements/ComboResult.cs
new file mode 100644
index 0000000000..3000031c78
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Judgements/ComboResult.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 System.ComponentModel;
+
+namespace osu.Game.Rulesets.Osu.Judgements
+{
+ public enum ComboResult
+ {
+ [Description(@"")]
+ None,
+ [Description(@"Good")]
+ Good,
+ [Description(@"Amazing")]
+ Perfect
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs b/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs
index 26becfdec9..b1c9760866 100644
--- a/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs
+++ b/osu.Game.Rulesets.Osu/Judgements/OsuJudgement.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Judgements;
-using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Judgements
@@ -25,7 +24,5 @@ namespace osu.Game.Rulesets.Osu.Judgements
return 300;
}
}
-
- public ComboResult Combo;
}
}
diff --git a/osu.Game.Rulesets.Osu/Judgements/OsuJudgementResult.cs b/osu.Game.Rulesets.Osu/Judgements/OsuJudgementResult.cs
new file mode 100644
index 0000000000..17b8b4399f
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Judgements/OsuJudgementResult.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.Judgements;
+
+namespace osu.Game.Rulesets.Osu.Judgements
+{
+ public class OsuJudgementResult : JudgementResult
+ {
+ public ComboResult ComboType;
+
+ public OsuJudgementResult(Judgement judgement)
+ : base(judgement)
+ {
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
index c525b4bd97..6344fbb770 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
@@ -6,7 +6,6 @@ using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using OpenTK;
-using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
using OpenTK.Graphics;
@@ -40,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (AllJudged)
return false;
- UpdateJudgement(true);
+ UpdateResult(true);
return true;
},
},
@@ -77,12 +76,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
- AddJudgement(new OsuJudgement { Result = HitResult.Miss });
+ ApplyResult(r => r.Type = HitResult.Miss);
+
return;
}
@@ -90,10 +90,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (result == HitResult.None)
return;
- AddJudgement(new OsuJudgement
- {
- Result = result,
- });
+ ApplyResult(r => r.Type = result);
}
protected override void UpdatePreemptState()
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
index 5dc141bed0..0501f8b7a0 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
@@ -2,11 +2,11 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
-using System.ComponentModel;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Graphics;
-using System.Linq;
+using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using OpenTK.Graphics;
@@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
UpdatePreemptState();
- var judgementOffset = Math.Min(HitObject.HitWindows.HalfWindowFor(HitResult.Miss), Judgements.FirstOrDefault()?.TimeOffset ?? 0);
+ var judgementOffset = Math.Min(HitObject.HitWindows.HalfWindowFor(HitResult.Miss), Result?.TimeOffset ?? 0);
using (BeginDelayedSequence(HitObject.TimePreempt + judgementOffset, true))
UpdateCurrentState(state);
@@ -57,20 +57,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
// Todo: At some point we need to move these to DrawableHitObject after ensuring that all other Rulesets apply
// transforms in the same way and don't rely on them not being cleared
- public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null) { }
- public override void ApplyTransformsAt(double time, bool propagateChildren = false) { }
+ public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null)
+ {
+ }
+
+ public override void ApplyTransformsAt(double time, bool propagateChildren = false)
+ {
+ }
private OsuInputManager osuActionInputManager;
internal OsuInputManager OsuActionInputManager => osuActionInputManager ?? (osuActionInputManager = GetContainingInputManager() as OsuInputManager);
- }
- public enum ComboResult
- {
- [Description(@"")]
- None,
- [Description(@"Good")]
- Good,
- [Description(@"Amazing")]
- Perfect
+ protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(judgement);
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs
index e8743281da..04ec3f13c7 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs
@@ -11,14 +11,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public class DrawableOsuJudgement : DrawableJudgement
{
- public DrawableOsuJudgement(Judgement judgement, DrawableHitObject judgedObject)
- : base(judgement, judgedObject)
+ public DrawableOsuJudgement(JudgementResult result, DrawableHitObject judgedObject)
+ : base(result, judgedObject)
{
}
protected override void LoadComplete()
{
- if (Judgement.Result != HitResult.Miss)
+ if (Result.Type != HitResult.Miss)
JudgementText?.TransformSpacingTo(new Vector2(14, 0), 1800, Easing.OutQuint);
base.LoadComplete();
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
index 6bff1380d6..dfe7937e81 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableRepeatPoint.cs
@@ -8,7 +8,6 @@ using osu.Framework.MathUtils;
using osu.Game.Rulesets.Objects.Drawables;
using OpenTK;
using osu.Game.Graphics;
-using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
@@ -42,10 +41,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
};
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (repeatPoint.StartTime <= Time.Current)
- AddJudgement(new OsuJudgement { Result = drawableSlider.Tracking ? HitResult.Great : HitResult.Miss });
+ ApplyResult(r => r.Type = drawableSlider.Tracking ? HitResult.Great : HitResult.Miss);
}
protected override void UpdatePreemptState()
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index f1907a92a8..f48f03f197 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -9,7 +9,6 @@ using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics.Containers;
-using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Configuration;
using osu.Game.Rulesets.Scoring;
using OpenTK.Graphics;
@@ -132,23 +131,27 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
- if (!userTriggered && Time.Current >= slider.EndTime)
+ if (userTriggered || Time.Current < slider.EndTime)
+ return;
+
+ ApplyResult(r =>
{
var judgementsCount = NestedHitObjects.Count();
var judgementsHit = NestedHitObjects.Count(h => h.IsHit);
var hitFraction = (double)judgementsHit / judgementsCount;
- if (hitFraction == 1 && HeadCircle.Judgements.Any(j => j.Result == HitResult.Great))
- AddJudgement(new OsuJudgement { Result = HitResult.Great });
- else if (hitFraction >= 0.5 && HeadCircle.Judgements.Any(j => j.Result >= HitResult.Good))
- AddJudgement(new OsuJudgement { Result = HitResult.Good });
+
+ if (hitFraction == 1 && HeadCircle.Result.Type == HitResult.Great)
+ r.Type = HitResult.Great;
+ else if (hitFraction >= 0.5 && HeadCircle.Result.Type >= HitResult.Good)
+ r.Type = HitResult.Good;
else if (hitFraction > 0)
- AddJudgement(new OsuJudgement { Result = HitResult.Meh });
+ r.Type = HitResult.Meh;
else
- AddJudgement(new OsuJudgement { Result = HitResult.Miss });
- }
+ r.Type = HitResult.Miss;
+ });
}
protected override void UpdateCurrentState(ArmedState state)
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs
index fee663963e..45c925b87a 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
-using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
@@ -12,11 +11,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
///
/// The judgement text is provided by the .
///
- public override bool DisplayJudgement => false;
+ public override bool DisplayResult => false;
public bool Tracking { get; set; }
- public DrawableSliderTail(Slider slider, HitCircle hitCircle)
+ public DrawableSliderTail(Slider slider, SliderTailCircle hitCircle)
: base(hitCircle)
{
Origin = Anchor.Centre;
@@ -29,10 +28,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Position = HitObject.Position - slider.Position;
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (!userTriggered && timeOffset >= 0)
- AddJudgement(new OsuSliderTailJudgement { Result = Tracking ? HitResult.Great : HitResult.Miss });
+ ApplyResult(r => r.Type = Tracking ? HitResult.Great : HitResult.Miss);
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
index a5ecb63d12..964c75131a 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
@@ -6,7 +6,6 @@ using osu.Game.Rulesets.Objects.Drawables;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
-using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning;
using osu.Framework.Graphics.Containers;
@@ -19,7 +18,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public bool Tracking { get; set; }
- public override bool DisplayJudgement => false;
+ public override bool DisplayResult => false;
public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick)
{
@@ -48,10 +47,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
};
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (timeOffset >= 0)
- AddJudgement(new OsuJudgement { Result = Tracking ? HitResult.Great : HitResult.Miss });
+ ApplyResult(r => r.Type = Tracking ? HitResult.Great : HitResult.Miss);
}
protected override void UpdatePreemptState()
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
index 1d3df69fb8..51b1990a21 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
@@ -11,7 +11,6 @@ using OpenTK.Graphics;
using osu.Game.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Allocation;
-using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Screens.Ranking;
using osu.Game.Rulesets.Scoring;
@@ -117,7 +116,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public float Progress => MathHelper.Clamp(Disc.RotationAbsolute / 360 / Spinner.SpinsRequired, 0, 1);
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (Time.Current < HitObject.StartTime) return;
@@ -136,17 +135,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
glow.FadeColour(completeColour, duration);
}
- if (!userTriggered && Time.Current >= Spinner.EndTime)
+ if (userTriggered || Time.Current < Spinner.EndTime)
+ return;
+
+ ApplyResult(r =>
{
if (Progress >= 1)
- AddJudgement(new OsuJudgement { Result = HitResult.Great });
+ r.Type = HitResult.Great;
else if (Progress > .9)
- AddJudgement(new OsuJudgement { Result = HitResult.Good });
+ r.Type = HitResult.Good;
else if (Progress > .75)
- AddJudgement(new OsuJudgement { Result = HitResult.Meh });
+ r.Type = HitResult.Meh;
else if (Time.Current >= Spinner.EndTime)
- AddJudgement(new OsuJudgement { Result = HitResult.Miss });
- }
+ r.Type = HitResult.Miss;
+ });
}
[BackgroundDependencyLoader]
diff --git a/osu.Game.Rulesets.Osu/Objects/HitCircle.cs b/osu.Game.Rulesets.Osu/Objects/HitCircle.cs
index 9e309a376d..d1656a9672 100644
--- a/osu.Game.Rulesets.Osu/Objects/HitCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/HitCircle.cs
@@ -1,9 +1,13 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Osu.Judgements;
+
namespace osu.Game.Rulesets.Osu.Objects
{
public class HitCircle : OsuHitObject
{
+ public override Judgement CreateJudgement() => new OsuJudgement();
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
index 48a6365c00..fdf5aaffa8 100644
--- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs
@@ -54,6 +54,8 @@ namespace osu.Game.Rulesets.Osu.Objects
public virtual bool NewCombo { get; set; }
+ public int ComboOffset { get; set; }
+
public virtual int IndexInCurrentCombo { get; set; }
public virtual int ComboIndex { get; set; }
diff --git a/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs b/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs
index 3495bc1b4b..c8621cdbcf 100644
--- a/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs
+++ b/osu.Game.Rulesets.Osu/Objects/RepeatPoint.cs
@@ -4,6 +4,8 @@
using System;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects
{
@@ -24,5 +26,7 @@ namespace osu.Game.Rulesets.Osu.Objects
if (RepeatIndex > 0)
TimePreempt = Math.Min(SpanDuration * 2, TimePreempt);
}
+
+ public override Judgement CreateJudgement() => new OsuJudgement();
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs
index 698f9de787..7a0dcc77a6 100644
--- a/osu.Game.Rulesets.Osu/Objects/Slider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs
@@ -10,6 +10,8 @@ using System.Linq;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects
{
@@ -94,7 +96,7 @@ namespace osu.Game.Rulesets.Osu.Objects
public double TickDistance;
public HitCircle HeadCircle;
- public HitCircle TailCircle;
+ public SliderTailCircle TailCircle;
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
{
@@ -133,7 +135,7 @@ namespace osu.Game.Rulesets.Osu.Objects
ComboIndex = ComboIndex,
};
- TailCircle = new SliderCircle(this)
+ TailCircle = new SliderTailCircle(this)
{
StartTime = EndTime,
Position = EndPosition,
@@ -211,5 +213,7 @@ namespace osu.Game.Rulesets.Osu.Objects
});
}
}
+
+ public override Judgement CreateJudgement() => new OsuJudgement();
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs
new file mode 100644
index 0000000000..23616ea005
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs
@@ -0,0 +1,18 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Osu.Judgements;
+
+namespace osu.Game.Rulesets.Osu.Objects
+{
+ public class SliderTailCircle : SliderCircle
+ {
+ public SliderTailCircle(Slider slider)
+ : base(slider)
+ {
+ }
+
+ public override Judgement CreateJudgement() => new OsuSliderTailJudgement();
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTick.cs b/osu.Game.Rulesets.Osu/Objects/SliderTick.cs
index 54337a12be..906f0a0182 100644
--- a/osu.Game.Rulesets.Osu/Objects/SliderTick.cs
+++ b/osu.Game.Rulesets.Osu/Objects/SliderTick.cs
@@ -3,6 +3,8 @@
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects
{
@@ -26,5 +28,7 @@ namespace osu.Game.Rulesets.Osu.Objects
TimePreempt = (StartTime - SpanStartTime) / 2 + offset;
}
+
+ public override Judgement CreateJudgement() => new OsuJudgement();
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs
index 503ad85674..e1a7a7c6df 100644
--- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs
@@ -5,6 +5,8 @@ using System;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Osu.Judgements;
namespace osu.Game.Rulesets.Osu.Objects
{
@@ -29,5 +31,7 @@ namespace osu.Game.Rulesets.Osu.Objects
// spinning doesn't match 1:1 with stable, so let's fudge them easier for the time being.
SpinsRequired = (int)Math.Max(1, SpinsRequired * 0.6);
}
+
+ public override Judgement CreateJudgement() => new OsuJudgement();
}
}
diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
index 01b92255ae..a9d39e88b4 100644
--- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
+++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs
@@ -2,13 +2,11 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
-using System.Linq;
using osu.Framework.Extensions;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.Objects;
-using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
@@ -26,28 +24,11 @@ namespace osu.Game.Rulesets.Osu.Scoring
private readonly Dictionary scoreResultCounts = new Dictionary();
private readonly Dictionary comboResultCounts = new Dictionary();
- protected override void SimulateAutoplay(Beatmap beatmap)
+ protected override void ApplyBeatmap(Beatmap beatmap)
{
+ base.ApplyBeatmap(beatmap);
+
hpDrainRate = beatmap.BeatmapInfo.BaseDifficulty.DrainRate;
-
- foreach (var obj in beatmap.HitObjects)
- {
- if (obj is Slider slider)
- {
- // Head
- AddJudgement(new OsuJudgement { Result = HitResult.Great });
-
- // Ticks
- foreach (var unused in slider.NestedHitObjects.OfType())
- AddJudgement(new OsuJudgement { Result = HitResult.Great });
-
- //Repeats
- foreach (var unused in slider.NestedHitObjects.OfType())
- AddJudgement(new OsuJudgement { Result = HitResult.Great });
- }
-
- AddJudgement(new OsuJudgement { Result = HitResult.Great });
- }
}
protected override void Reset(bool storeResults)
@@ -70,19 +51,19 @@ namespace osu.Game.Rulesets.Osu.Scoring
private const double harshness = 0.01;
- protected override void OnNewJudgement(Judgement judgement)
+ protected override void ApplyResult(JudgementResult result)
{
- base.OnNewJudgement(judgement);
+ base.ApplyResult(result);
- var osuJudgement = (OsuJudgement)judgement;
+ var osuResult = (OsuJudgementResult)result;
- if (judgement.Result != HitResult.None)
+ if (result.Type != HitResult.None)
{
- scoreResultCounts[judgement.Result] = scoreResultCounts.GetOrDefault(judgement.Result) + 1;
- comboResultCounts[osuJudgement.Combo] = comboResultCounts.GetOrDefault(osuJudgement.Combo) + 1;
+ scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) + 1;
+ comboResultCounts[osuResult.ComboType] = comboResultCounts.GetOrDefault(osuResult.ComboType) + 1;
}
- switch (judgement.Result)
+ switch (result.Type)
{
case HitResult.Great:
Health.Value += (10.2 - hpDrainRate) * harshness;
@@ -105,5 +86,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
break;
}
}
+
+ protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(judgement);
}
}
diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
index b0ba9afee6..703d8764fc 100644
--- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
+++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
@@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.UI
public override void Add(DrawableHitObject h)
{
- h.OnJudgement += onJudgement;
+ h.OnNewResult += onNewResult;
var c = h as IDrawableHitObjectWithProxiedApproach;
if (c != null)
@@ -64,12 +64,12 @@ namespace osu.Game.Rulesets.Osu.UI
connectionLayer.HitObjects = HitObjects.Objects.Select(d => d.HitObject).OfType();
}
- private void onJudgement(DrawableHitObject judgedObject, Judgement judgement)
+ private void onNewResult(DrawableHitObject judgedObject, JudgementResult result)
{
- if (!judgedObject.DisplayJudgement || !DisplayJudgements)
+ if (!judgedObject.DisplayResult || !DisplayJudgements)
return;
- DrawableOsuJudgement explosion = new DrawableOsuJudgement(judgement, judgedObject)
+ DrawableOsuJudgement explosion = new DrawableOsuJudgement(result, judgedObject)
{
Origin = Anchor.Centre,
Position = ((OsuHitObject)judgedObject.HitObject).StackedEndPosition
diff --git a/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs b/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs
index 1bf24a46bc..fc103e4c72 100644
--- a/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs
+++ b/osu.Game.Rulesets.Taiko.Tests/TestCaseTaikoPlayfield.cs
@@ -8,9 +8,9 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.MathUtils;
-using osu.Framework.Timing;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Judgements;
@@ -39,8 +39,10 @@ namespace osu.Game.Rulesets.Taiko.Tests
[BackgroundDependencyLoader]
private void load()
{
- AddStep("Hit!", () => addHitJudgement(false));
+ AddStep("Hit", () => addHitJudgement(false));
+ AddStep("Strong hit", () => addStrongHitJudgement(false));
AddStep("Kiai hit", () => addHitJudgement(true));
+ AddStep("Strong kiai hit", () => addStrongHitJudgement(true));
AddStep("Miss :(", addMissJudgement);
AddStep("DrumRoll", () => addDrumRoll(false));
AddStep("Strong DrumRoll", () => addDrumRoll(true));
@@ -78,15 +80,12 @@ namespace osu.Game.Rulesets.Taiko.Tests
ControlPointInfo = controlPointInfo
});
- var rateAdjustClock = new StopwatchClock(true) { Rate = 1 };
-
Add(playfieldContainer = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.X,
Height = 768,
- Clock = new FramedClock(rateAdjustClock),
Children = new[] { rulesetContainer = new TaikoRulesetContainer(new TaikoRuleset(), beatmap) }
});
}
@@ -133,28 +132,35 @@ namespace osu.Game.Rulesets.Taiko.Tests
HitResult hitResult = RNG.Next(2) == 0 ? HitResult.Good : HitResult.Great;
var cpi = new ControlPointInfo();
- cpi.EffectPoints.Add(new EffectControlPoint
- {
- KiaiMode = kiai
- });
+ cpi.EffectPoints.Add(new EffectControlPoint { KiaiMode = kiai });
Hit hit = new Hit();
hit.ApplyDefaults(cpi, new BeatmapDifficulty());
var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) };
- ((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(h, new TaikoJudgement { Result = hitResult });
+ ((TaikoPlayfield)rulesetContainer.Playfield).OnNewResult(h, new JudgementResult(new TaikoJudgement()) { Type = hitResult });
+ }
- if (RNG.Next(10) == 0)
- {
- ((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(h, new TaikoJudgement { Result = hitResult });
- ((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(h, new TaikoStrongHitJudgement());
- }
+ private void addStrongHitJudgement(bool kiai)
+ {
+ HitResult hitResult = RNG.Next(2) == 0 ? HitResult.Good : HitResult.Great;
+
+ var cpi = new ControlPointInfo();
+ cpi.EffectPoints.Add(new EffectControlPoint { KiaiMode = kiai });
+
+ Hit hit = new Hit();
+ hit.ApplyDefaults(cpi, new BeatmapDifficulty());
+
+ var h = new DrawableTestHit(hit) { X = RNG.NextSingle(hitResult == HitResult.Good ? -0.1f : -0.05f, hitResult == HitResult.Good ? 0.1f : 0.05f) };
+
+ ((TaikoPlayfield)rulesetContainer.Playfield).OnNewResult(h, new JudgementResult(new TaikoJudgement()) { Type = hitResult });
+ ((TaikoPlayfield)rulesetContainer.Playfield).OnNewResult(new TestStrongNestedHit(h), new JudgementResult(new TaikoStrongJudgement()) { Type = HitResult.Great });
}
private void addMissJudgement()
{
- ((TaikoPlayfield)rulesetContainer.Playfield).OnJudgement(new DrawableTestHit(new Hit()), new TaikoJudgement { Result = HitResult.Miss });
+ ((TaikoPlayfield)rulesetContainer.Playfield).OnNewResult(new DrawableTestHit(new Hit()), new JudgementResult(new TaikoJudgement()) { Type = HitResult.Miss });
}
private void addBarLine(bool major, double delay = scroll_time)
@@ -204,10 +210,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
- if (strong)
- rulesetContainer.Playfield.Add(new DrawableCentreHitStrong(h));
- else
- rulesetContainer.Playfield.Add(new DrawableCentreHit(h));
+ rulesetContainer.Playfield.Add(new DrawableCentreHit(h));
}
private void addRimHit(bool strong)
@@ -220,10 +223,17 @@ namespace osu.Game.Rulesets.Taiko.Tests
h.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
- if (strong)
- rulesetContainer.Playfield.Add(new DrawableRimHitStrong(h));
- else
- rulesetContainer.Playfield.Add(new DrawableRimHit(h));
+ rulesetContainer.Playfield.Add(new DrawableRimHit(h));
+ }
+
+ private class TestStrongNestedHit : DrawableStrongNestedHit
+ {
+ public TestStrongNestedHit(DrawableHitObject mainObject)
+ : base(null, mainObject)
+ {
+ }
+
+ public override bool OnPressed(TaikoAction action) => false;
}
private class DrawableTestHit : DrawableHitObject
diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
index 41972b5d20..acd6d43284 100644
--- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
+++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs
@@ -168,7 +168,6 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
{
StartTime = obj.StartTime,
Samples = obj.Samples,
- IsStrong = strong,
Duration = endTimeData.Duration,
RequiredHits = (int)Math.Max(1, endTimeData.Duration / 1000 * hitMultiplier)
};
diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoIntermediateSwellJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoIntermediateSwellJudgement.cs
index 608f1f9be2..81a1bd1344 100644
--- a/osu.Game.Rulesets.Taiko/Judgements/TaikoIntermediateSwellJudgement.cs
+++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoIntermediateSwellJudgement.cs
@@ -7,15 +7,10 @@ namespace osu.Game.Rulesets.Taiko.Judgements
{
public class TaikoIntermediateSwellJudgement : TaikoJudgement
{
- public override HitResult MaxResult => HitResult.Perfect;
+ public override HitResult MaxResult => HitResult.Great;
public override bool AffectsCombo => false;
- public TaikoIntermediateSwellJudgement()
- {
- Final = false;
- }
-
///
/// Computes the numeric result value for the combo portion of the score.
///
diff --git a/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs b/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongJudgement.cs
similarity index 64%
rename from osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs
rename to osu.Game.Rulesets.Taiko/Judgements/TaikoStrongJudgement.cs
index 288ad236aa..ccfdeb5b0e 100644
--- a/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongHitJudgement.cs
+++ b/osu.Game.Rulesets.Taiko/Judgements/TaikoStrongJudgement.cs
@@ -3,13 +3,8 @@
namespace osu.Game.Rulesets.Taiko.Judgements
{
- public class TaikoStrongHitJudgement : TaikoJudgement
+ public class TaikoStrongJudgement : TaikoJudgement
{
public override bool AffectsCombo => false;
-
- public TaikoStrongHitJudgement()
- {
- Final = true;
- }
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs
index dda96c2caf..a6e9972dd3 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHit.cs
@@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
public class DrawableCentreHit : DrawableHit
{
- protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftCentre, TaikoAction.RightCentre };
+ public override TaikoAction[] HitActions { get; } = { TaikoAction.LeftCentre, TaikoAction.RightCentre };
public DrawableCentreHit(Hit hit)
: base(hit)
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHitStrong.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHitStrong.cs
deleted file mode 100644
index a2dabf2b18..0000000000
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableCentreHitStrong.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
-
-using osu.Framework.Allocation;
-using osu.Game.Graphics;
-using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
-
-namespace osu.Game.Rulesets.Taiko.Objects.Drawables
-{
- public class DrawableCentreHitStrong : DrawableHitStrong
- {
- protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftCentre, TaikoAction.RightCentre };
-
- public DrawableCentreHitStrong(Hit hit)
- : base(hit)
- {
- MainPiece.Add(new CentreHitSymbolPiece());
- }
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- MainPiece.AccentColour = colours.PinkDarker;
- }
- }
-}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
index 00eac4adca..5142f125ac 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
@@ -6,7 +6,6 @@ using osu.Framework.Allocation;
using osu.Framework.MathUtils;
using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Taiko.Judgements;
using OpenTK;
using OpenTK.Graphics;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
@@ -40,7 +39,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
foreach (var tick in drumRoll.NestedHitObjects.OfType())
{
var newTick = new DrawableDrumRollTick(tick);
- newTick.OnJudgement += onTickJudgement;
+ newTick.OnNewResult += onNewTickResult;
AddNested(newTick);
tickContainer.Add(newTick);
@@ -61,9 +60,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
colourEngaged = colours.YellowDarker;
}
- private void onTickJudgement(DrawableHitObject obj, Judgement judgement)
+ private void onNewTickResult(DrawableHitObject obj, JudgementResult result)
{
- if (judgement.Result > HitResult.Miss)
+ if (result.Type > HitResult.Miss)
rollingHits++;
else
rollingHits--;
@@ -74,7 +73,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
MainPiece.FadeAccent(newColour, 100);
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (userTriggered)
return;
@@ -84,13 +83,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
int countHit = NestedHitObjects.Count(o => o.IsHit);
if (countHit >= HitObject.RequiredGoodHits)
- {
- AddJudgement(new TaikoJudgement { Result = countHit >= HitObject.RequiredGreatHits ? HitResult.Great : HitResult.Good });
- if (HitObject.IsStrong)
- AddJudgement(new TaikoStrongHitJudgement());
- }
+ ApplyResult(r => r.Type = countHit >= HitObject.RequiredGreatHits ? HitResult.Great : HitResult.Good);
else
- AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
+ ApplyResult(r => r.Type = HitResult.Miss);
}
protected override void UpdateState(ArmedState state)
@@ -103,5 +98,25 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
break;
}
}
+
+ protected override DrawableStrongNestedHit CreateStrongHit(StrongHitObject hitObject) => new StrongNestedHit(hitObject, this);
+
+ private class StrongNestedHit : DrawableStrongNestedHit
+ {
+ public StrongNestedHit(StrongHitObject strong, DrawableDrumRoll drumRoll)
+ : base(strong, drumRoll)
+ {
+ }
+
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
+ {
+ if (!MainObject.Judged)
+ return;
+
+ ApplyResult(r => r.Type = MainObject.IsHit ? HitResult.Great : HitResult.Miss);
+ }
+
+ public override bool OnPressed(TaikoAction action) => false;
+ }
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs
index 7a57cf77b4..a70d7bde0e 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs
@@ -5,7 +5,6 @@ using System;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
-using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
@@ -18,24 +17,26 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
FillMode = FillMode.Fit;
}
- public override bool DisplayJudgement => false;
+ public override bool DisplayResult => false;
protected override TaikoPiece CreateMainPiece() => new TickPiece
{
Filled = HitObject.FirstTick
};
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (!userTriggered)
+ {
+ if (timeOffset > HitObject.HitWindow)
+ ApplyResult(r => r.Type = HitResult.Miss);
+ return;
+ }
+
+ if (Math.Abs(timeOffset) > HitObject.HitWindow)
return;
- if (!(Math.Abs(timeOffset) < HitObject.HitWindow))
- return;
-
- AddJudgement(new TaikoDrumRollTickJudgement { Result = HitResult.Great });
- if (HitObject.IsStrong)
- AddJudgement(new TaikoStrongHitJudgement());
+ ApplyResult(r => r.Type = HitResult.Great);
}
protected override void UpdateState(ArmedState state)
@@ -48,6 +49,26 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
}
}
- public override bool OnPressed(TaikoAction action) => UpdateJudgement(true);
+ public override bool OnPressed(TaikoAction action) => UpdateResult(true);
+
+ protected override DrawableStrongNestedHit CreateStrongHit(StrongHitObject hitObject) => new StrongNestedHit(hitObject, this);
+
+ private class StrongNestedHit : DrawableStrongNestedHit
+ {
+ public StrongNestedHit(StrongHitObject strong, DrawableDrumRollTick tick)
+ : base(strong, tick)
+ {
+ }
+
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
+ {
+ if (!MainObject.Judged)
+ return;
+
+ ApplyResult(r => r.Type = MainObject.IsHit ? HitResult.Great : HitResult.Miss);
+ }
+
+ public override bool OnPressed(TaikoAction action) => false;
+ }
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
index bb9cd02b14..f59dc8c1ee 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
@@ -1,11 +1,11 @@
// 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.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring;
-using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
@@ -15,17 +15,14 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
///
/// A list of keys which can result in hits for this HitObject.
///
- protected abstract TaikoAction[] HitActions { get; }
+ public abstract TaikoAction[] HitActions { get; }
///
- /// Whether a second hit is allowed to be processed. This occurs once this hit object has been hit successfully.
+ /// The action that caused this to be hit.
///
- protected bool SecondHitAllowed { get; private set; }
+ public TaikoAction? HitAction { get; private set; }
- ///
- /// Whether the last key pressed is a valid hit key.
- ///
- private bool validKeyPressed;
+ private bool validActionPressed;
protected DrawableHit(Hit hit)
: base(hit)
@@ -33,12 +30,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
FillMode = FillMode.Fit;
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (!userTriggered)
{
if (!HitObject.HitWindows.CanBeHit(timeOffset))
- AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
+ ApplyResult(r => r.Type = HitResult.Miss);
return;
}
@@ -46,26 +43,33 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
if (result == HitResult.None)
return;
- if (!validKeyPressed || result == HitResult.Miss)
- AddJudgement(new TaikoJudgement { Result = HitResult.Miss });
+ if (!validActionPressed)
+ ApplyResult(r => r.Type = HitResult.Miss);
else
- {
- AddJudgement(new TaikoJudgement
- {
- Result = result,
- Final = !HitObject.IsStrong
- });
-
- SecondHitAllowed = true;
- }
+ ApplyResult(r => r.Type = result);
}
public override bool OnPressed(TaikoAction action)
{
- validKeyPressed = HitActions.Contains(action);
+ if (Judged)
+ return false;
+
+ validActionPressed = HitActions.Contains(action);
// Only count this as handled if the new judgement is a hit
- return UpdateJudgement(true);
+ var result = UpdateResult(true);
+
+ if (IsHit)
+ HitAction = action;
+
+ return result;
+ }
+
+ public override bool OnReleased(TaikoAction action)
+ {
+ if (action == HitAction)
+ HitAction = null;
+ return base.OnReleased(action);
}
protected override void Update()
@@ -86,8 +90,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
switch (State.Value)
{
case ArmedState.Idle:
- SecondHitAllowed = false;
- validKeyPressed = false;
+ validActionPressed = false;
UnproxyContent();
this.Delay(HitObject.HitWindows.HalfWindowFor(HitResult.Miss)).Expire();
@@ -123,5 +126,65 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
}
}
}
+
+ protected override DrawableStrongNestedHit CreateStrongHit(StrongHitObject hitObject) => new StrongNestedHit(hitObject, this);
+
+ private class StrongNestedHit : DrawableStrongNestedHit
+ {
+ ///
+ /// The lenience for the second key press.
+ /// This does not adjust by map difficulty in ScoreV2 yet.
+ ///
+ private const double second_hit_window = 30;
+
+ public new DrawableHit MainObject => (DrawableHit)base.MainObject;
+
+ public StrongNestedHit(StrongHitObject strong, DrawableHit hit)
+ : base(strong, hit)
+ {
+ }
+
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
+ {
+ if (!MainObject.Result.HasResult)
+ {
+ base.CheckForResult(userTriggered, timeOffset);
+ return;
+ }
+
+ if (!MainObject.Result.IsHit)
+ {
+ ApplyResult(r => r.Type = HitResult.Miss);
+ return;
+ }
+
+ if (!userTriggered)
+ {
+ if (timeOffset > second_hit_window)
+ ApplyResult(r => r.Type = HitResult.Miss);
+ return;
+ }
+
+ if (Math.Abs(MainObject.Result.TimeOffset - timeOffset) < second_hit_window)
+ ApplyResult(r => r.Type = HitResult.Great);
+ }
+
+ public override bool OnPressed(TaikoAction action)
+ {
+ // Don't process actions until the main hitobject is hit
+ if (!MainObject.IsHit)
+ return false;
+
+ // Don't process actions if the pressed button was released
+ if (MainObject.HitAction == null)
+ return false;
+
+ // Don't handle invalid hit action presses
+ if (!MainObject.HitActions.Contains(action))
+ return false;
+
+ return UpdateResult(true);
+ }
+ }
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs
deleted file mode 100644
index b431d35e16..0000000000
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs
+++ /dev/null
@@ -1,103 +0,0 @@
-// 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.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.Scoring;
-using osu.Game.Rulesets.Taiko.Judgements;
-
-namespace osu.Game.Rulesets.Taiko.Objects.Drawables
-{
- public abstract class DrawableHitStrong : DrawableHit
- {
- ///
- /// The lenience for the second key press.
- /// This does not adjust by map difficulty in ScoreV2 yet.
- ///
- private const double second_hit_window = 30;
-
- private double firstHitTime;
- private bool firstKeyHeld;
- private TaikoAction firstHitAction;
-
- protected DrawableHitStrong(Hit hit)
- : base(hit)
- {
- }
-
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
- {
- if (!SecondHitAllowed)
- {
- base.CheckForJudgements(userTriggered, timeOffset);
- return;
- }
-
- if (!userTriggered)
- {
- if (timeOffset > second_hit_window)
- AddJudgement(new TaikoStrongHitJudgement { Result = HitResult.None });
- return;
- }
-
- // If we get here, we're assured that the key pressed is the correct secondary key
-
- if (Math.Abs(firstHitTime - Time.Current) < second_hit_window)
- AddJudgement(new TaikoStrongHitJudgement { Result = HitResult.Great });
- }
-
- protected override void UpdateState(ArmedState state)
- {
- base.UpdateState(state);
-
- switch (state)
- {
- case ArmedState.Idle:
- firstHitTime = 0;
- firstKeyHeld = false;
- break;
- }
- }
-
- public override bool OnReleased(TaikoAction action)
- {
- if (action == firstHitAction)
- firstKeyHeld = false;
- return base.OnReleased(action);
- }
-
- public override bool OnPressed(TaikoAction action)
- {
- if (AllJudged)
- return false;
-
- // Check if we've handled the first key
- if (!SecondHitAllowed)
- {
- // First key hasn't been handled yet, attempt to handle it
- bool handled = base.OnPressed(action);
-
- if (handled)
- {
- firstHitTime = Time.Current;
- firstHitAction = action;
- firstKeyHeld = true;
- }
-
- return handled;
- }
-
- // Don't handle represses of the first key
- if (firstHitAction == action)
- return false;
-
- // Don't handle invalid hit action presses
- if (!HitActions.Contains(action))
- return false;
-
- // Assume the intention was to hit the strong hit with both keys only if the first key is still being held down
- return firstKeyHeld && UpdateJudgement(true);
- }
- }
-}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs
index f2194c6d56..188cafe1db 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHit.cs
@@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
public class DrawableRimHit : DrawableHit
{
- protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftRim, TaikoAction.RightRim };
+ public override TaikoAction[] HitActions { get; } = { TaikoAction.LeftRim, TaikoAction.RightRim };
public DrawableRimHit(Hit hit)
: base(hit)
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHitStrong.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHitStrong.cs
deleted file mode 100644
index 728fe416f7..0000000000
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableRimHitStrong.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
-
-using osu.Framework.Allocation;
-using osu.Game.Graphics;
-using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
-
-namespace osu.Game.Rulesets.Taiko.Objects.Drawables
-{
- public class DrawableRimHitStrong : DrawableHitStrong
- {
- protected override TaikoAction[] HitActions { get; } = { TaikoAction.LeftRim, TaikoAction.RightRim };
-
- public DrawableRimHitStrong(Hit hit)
- : base(hit)
- {
- MainPiece.Add(new RimHitSymbolPiece());
- }
-
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- MainPiece.AccentColour = colours.BlueDarker;
- }
- }
-}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs
new file mode 100644
index 0000000000..b27de3832a
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableStrongNestedHit.cs
@@ -0,0 +1,26 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Taiko.Judgements;
+
+namespace osu.Game.Rulesets.Taiko.Objects.Drawables
+{
+ ///
+ /// Used as a nested hitobject to provide s for s.
+ ///
+ public abstract class DrawableStrongNestedHit : DrawableTaikoHitObject
+ {
+ public readonly DrawableHitObject MainObject;
+
+ protected DrawableStrongNestedHit(StrongHitObject strong, DrawableHitObject mainObject)
+ : base(strong)
+ {
+ MainObject = mainObject;
+ }
+
+ protected override void UpdateState(ArmedState state)
+ {
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
index 408b37e377..5059734663 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
@@ -2,6 +2,8 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
+using System.Collections.Generic;
+using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
@@ -12,23 +14,19 @@ using osu.Game.Rulesets.Taiko.Objects.Drawables.Pieces;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Graphics.Shapes;
-using osu.Game.Rulesets.Taiko.Judgements;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
public class DrawableSwell : DrawableTaikoHitObject
{
- ///
- /// A judgement is only displayed when the user has complete the swell (either a hit or miss).
- ///
- public override bool DisplayJudgement => AllJudged;
-
private const float target_ring_thick_border = 1.4f;
private const float target_ring_thin_border = 1f;
private const float target_ring_scale = 5f;
private const float inner_ring_alpha = 0.65f;
+ private readonly List ticks = new List();
+
private readonly Container bodyContainer;
private readonly CircularContainer targetRing;
private readonly CircularContainer expandingRing;
@@ -106,6 +104,15 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
});
MainPiece.Add(symbol = new SwellSymbolPiece());
+
+ foreach (var tick in HitObject.NestedHitObjects.OfType())
+ {
+ var vis = new DrawableSwellTick(tick);
+
+ ticks.Add(vis);
+ AddInternal(vis);
+ AddNested(vis);
+ }
}
[BackgroundDependencyLoader]
@@ -124,13 +131,17 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Width *= Parent.RelativeChildSize.X;
}
- protected override void CheckForJudgements(bool userTriggered, double timeOffset)
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (userTriggered)
{
- AddJudgement(new TaikoIntermediateSwellJudgement());
+ var nextTick = ticks.FirstOrDefault(j => !j.IsHit);
- var completion = (float)Judgements.Count / HitObject.RequiredHits;
+ nextTick?.TriggerResult(HitResult.Great);
+
+ var numHits = ticks.Count(r => r.IsHit);
+
+ var completion = (float)numHits / HitObject.RequiredHits;
expandingRing
.FadeTo(expandingRing.Alpha + MathHelper.Clamp(completion / 16, 0.1f, 0.6f), 50)
@@ -141,18 +152,30 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
expandingRing.ScaleTo(1f + Math.Min(target_ring_scale - 1f, (target_ring_scale - 1f) * completion * 1.3f), 260, Easing.OutQuint);
- if (Judgements.Count == HitObject.RequiredHits)
- AddJudgement(new TaikoJudgement { Result = HitResult.Great });
+ if (numHits == HitObject.RequiredHits)
+ ApplyResult(r => r.Type = HitResult.Great);
}
else
{
if (timeOffset < 0)
return;
- //TODO: THIS IS SHIT AND CAN'T EXIST POST-TAIKO WORLD CUP
- AddJudgement(Judgements.Count > HitObject.RequiredHits / 2
- ? new TaikoJudgement { Result = HitResult.Good }
- : new TaikoJudgement { Result = HitResult.Miss });
+ int numHits = 0;
+
+ foreach (var tick in ticks)
+ {
+ if (tick.IsHit)
+ {
+ numHits++;
+ continue;
+ }
+
+ tick.TriggerResult(HitResult.Miss);
+ }
+
+ var hitResult = numHits > HitObject.RequiredHits / 2 ? HitResult.Good : HitResult.Miss;
+
+ ApplyResult(r => r.Type = hitResult);
}
}
@@ -208,7 +231,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
return false;
lastWasCentre = isCentre;
- UpdateJudgement(true);
+ UpdateResult(true);
return true;
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs
new file mode 100644
index 0000000000..36c468c6d6
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwellTick.cs
@@ -0,0 +1,30 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Scoring;
+
+namespace osu.Game.Rulesets.Taiko.Objects.Drawables
+{
+ public class DrawableSwellTick : DrawableTaikoHitObject
+ {
+ public override bool DisplayResult => false;
+
+ public DrawableSwellTick(TaikoHitObject hitObject)
+ : base(hitObject)
+ {
+ }
+
+ public void TriggerResult(HitResult type) => ApplyResult(r => r.Type = type);
+
+ protected override void CheckForResult(bool userTriggered, double timeOffset)
+ {
+ }
+
+ protected override void UpdateState(ArmedState state)
+ {
+ }
+
+ public override bool OnPressed(TaikoAction action) => false;
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
index a6d61f1a5a..51e39dc648 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs
@@ -101,6 +101,15 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
Content.Add(MainPiece = CreateMainPiece());
MainPiece.KiaiMode = HitObject.Kiai;
+
+ var strongObject = HitObject.NestedHitObjects.OfType().FirstOrDefault();
+ if (strongObject != null)
+ {
+ var strongHit = CreateStrongHit(strongObject);
+
+ AddNested(strongHit);
+ AddInternal(strongHit);
+ }
}
// Normal and clap samples are handled by the drum
@@ -109,5 +118,13 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
protected override string SampleNamespace => "Taiko";
protected virtual TaikoPiece CreateMainPiece() => new CirclePiece();
+
+ ///
+ /// Creates the handler for this 's .
+ /// This is only invoked if is true for .
+ ///
+ /// The strong hitobject.
+ /// The strong hitobject handler.
+ protected virtual DrawableStrongNestedHit CreateStrongHit(StrongHitObject hitObject) => null;
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
index 4c9ec5473b..405ea85f0d 100644
--- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs
@@ -54,12 +54,12 @@ namespace osu.Game.Rulesets.Taiko.Objects
protected override void CreateNestedHitObjects()
{
- base.CreateNestedHitObjects();
-
createTicks();
RequiredGoodHits = NestedHitObjects.Count * Math.Min(0.15, 0.05 + 0.10 / 6 * overallDifficulty);
RequiredGreatHits = NestedHitObjects.Count * Math.Min(0.30, 0.10 + 0.20 / 6 * overallDifficulty);
+
+ base.CreateNestedHitObjects();
}
private void createTicks()
diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs
index e546d6427f..967d5acfd7 100644
--- a/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs
@@ -1,6 +1,9 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Taiko.Judgements;
+
namespace osu.Game.Rulesets.Taiko.Objects
{
public class DrumRollTick : TaikoHitObject
@@ -20,5 +23,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
/// The time allowed to hit this tick.
///
public double HitWindow => TickSpacing / 2;
+
+ public override Judgement CreateJudgement() => new TaikoDrumRollTickJudgement();
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/StrongHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/StrongHitObject.cs
new file mode 100644
index 0000000000..fac3705110
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/Objects/StrongHitObject.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
+
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Taiko.Judgements;
+
+namespace osu.Game.Rulesets.Taiko.Objects
+{
+ public class StrongHitObject : TaikoHitObject
+ {
+ public override Judgement CreateJudgement() => new TaikoStrongJudgement();
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Swell.cs b/osu.Game.Rulesets.Taiko/Objects/Swell.cs
index eb6f931af4..702bf63bf5 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Swell.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Swell.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 osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Taiko.Objects
@@ -15,5 +16,15 @@ namespace osu.Game.Rulesets.Taiko.Objects
/// The number of hits required to complete the swell successfully.
///
public int RequiredHits = 10;
+
+ public override bool IsStrong { set => throw new NotSupportedException($"{nameof(Swell)} cannot be a strong hitobject."); }
+
+ protected override void CreateNestedHitObjects()
+ {
+ base.CreateNestedHitObjects();
+
+ for (int i = 0; i < RequiredHits; i++)
+ AddNested(new SwellTick());
+ }
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/SwellTick.cs b/osu.Game.Rulesets.Taiko/Objects/SwellTick.cs
new file mode 100644
index 0000000000..49eb6d2a15
--- /dev/null
+++ b/osu.Game.Rulesets.Taiko/Objects/SwellTick.cs
@@ -0,0 +1,9 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+namespace osu.Game.Rulesets.Taiko.Objects
+{
+ public class SwellTick : TaikoHitObject
+ {
+ }
+}
diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs
index ffbbe28f2e..9c86b60688 100644
--- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs
@@ -1,7 +1,10 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Types;
+using osu.Game.Rulesets.Taiko.Judgements;
namespace osu.Game.Rulesets.Taiko.Objects
{
@@ -26,7 +29,17 @@ namespace osu.Game.Rulesets.Taiko.Objects
/// Whether this HitObject is a "strong" type.
/// Strong hit objects give more points for hitting the hit object with both keys.
///
- public bool IsStrong;
+ public virtual bool IsStrong { get; set; }
+
+ protected override void CreateNestedHitObjects()
+ {
+ base.CreateNestedHitObjects();
+
+ if (IsStrong)
+ AddNested(new StrongHitObject { StartTime = (this as IHasEndTime)?.EndTime ?? StartTime });
+ }
+
+ public override Judgement CreateJudgement() => new TaikoJudgement();
protected override HitWindows CreateHitWindows() => new TaikoHitWindows();
}
diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
index 7dd50ab8b8..cf33141027 100644
--- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs
+++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.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 System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Scoring;
@@ -60,63 +59,31 @@ namespace osu.Game.Rulesets.Taiko.Scoring
private double hpIncreaseGood;
private double hpIncreaseMiss;
- public TaikoScoreProcessor()
- {
- }
-
public TaikoScoreProcessor(RulesetContainer rulesetContainer)
: base(rulesetContainer)
{
}
- protected override void SimulateAutoplay(Beatmap beatmap)
+ protected override void ApplyBeatmap(Beatmap beatmap)
{
+ base.ApplyBeatmap(beatmap);
+
double hpMultiplierNormal = 1 / (hp_hit_great * beatmap.HitObjects.FindAll(o => o is Hit).Count * BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, 0.5, 0.75, 0.98));
hpIncreaseTick = hp_hit_tick;
hpIncreaseGreat = hpMultiplierNormal * hp_hit_great;
hpIncreaseGood = hpMultiplierNormal * hp_hit_good;
hpIncreaseMiss = BeatmapDifficulty.DifficultyRange(beatmap.BeatmapInfo.BaseDifficulty.DrainRate, hp_miss_min, hp_miss_mid, hp_miss_max);
-
- foreach (var obj in beatmap.HitObjects)
- {
- switch (obj)
- {
- case Hit _:
- AddJudgement(new TaikoJudgement { Result = HitResult.Great });
- if (obj.IsStrong)
- AddJudgement(new TaikoStrongHitJudgement());
- break;
- case DrumRoll drumRoll:
- var count = drumRoll.NestedHitObjects.OfType().Count();
- for (int i = 0; i < count; i++)
- {
- AddJudgement(new TaikoDrumRollTickJudgement { Result = HitResult.Great });
-
- if (obj.IsStrong)
- AddJudgement(new TaikoStrongHitJudgement());
- }
-
- AddJudgement(new TaikoJudgement { Result = HitResult.Great });
-
- if (obj.IsStrong)
- AddJudgement(new TaikoStrongHitJudgement());
- break;
- case Swell _:
- AddJudgement(new TaikoJudgement { Result = HitResult.Great });
- break;
- }
- }
}
- protected override void OnNewJudgement(Judgement judgement)
+ protected override void ApplyResult(JudgementResult result)
{
- base.OnNewJudgement(judgement);
+ base.ApplyResult(result);
- bool isTick = judgement is TaikoDrumRollTickJudgement;
+ bool isTick = result.Judgement is TaikoDrumRollTickJudgement;
// Apply HP changes
- switch (judgement.Result)
+ switch (result.Type)
{
case HitResult.Miss:
// Missing ticks shouldn't drop HP
diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs
index b07a3ce8df..4d660918b8 100644
--- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs
+++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoJudgement.cs
@@ -19,16 +19,16 @@ namespace osu.Game.Rulesets.Taiko.UI
/// Creates a new judgement text.
///
/// The object which is being judged.
- /// The judgement to visualise.
- public DrawableTaikoJudgement(Judgement judgement, DrawableHitObject judgedObject)
- : base(judgement, judgedObject)
+ /// The judgement to visualise.
+ public DrawableTaikoJudgement(JudgementResult result, DrawableHitObject judgedObject)
+ : base(result, judgedObject)
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
- switch (Judgement.Result)
+ switch (Result.Type)
{
case HitResult.Good:
Colour = colours.GreenLight;
@@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Taiko.UI
protected override void LoadComplete()
{
- if (Judgement.IsHit)
+ if (Result.IsHit)
this.MoveToY(-100, 500);
base.LoadComplete();
diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
index 4cb8dd48a7..325beb38a5 100644
--- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
+++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs
@@ -209,7 +209,7 @@ namespace osu.Game.Rulesets.Taiko.UI
public override void Add(DrawableHitObject h)
{
- h.OnJudgement += OnJudgement;
+ h.OnNewResult += OnNewResult;
base.Add(h);
@@ -224,35 +224,40 @@ namespace osu.Game.Rulesets.Taiko.UI
}
}
- internal void OnJudgement(DrawableHitObject judgedObject, Judgement judgement)
+ internal void OnNewResult(DrawableHitObject judgedObject, JudgementResult result)
{
if (!DisplayJudgements)
return;
- if (judgedObject.DisplayJudgement && judgementContainer.FirstOrDefault(j => j.JudgedObject == judgedObject) == null)
- {
- judgementContainer.Add(new DrawableTaikoJudgement(judgement, judgedObject)
- {
- Anchor = judgement.IsHit ? Anchor.TopLeft : Anchor.CentreLeft,
- Origin = judgement.IsHit ? Anchor.BottomCentre : Anchor.Centre,
- RelativePositionAxes = Axes.X,
- X = judgement.IsHit ? judgedObject.Position.X : 0,
- });
- }
-
- if (!judgement.IsHit)
+ if (!judgedObject.DisplayResult)
return;
- bool isRim = judgedObject.HitObject is RimHit;
-
- if (judgement is TaikoStrongHitJudgement)
- hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == judgedObject)?.VisualiseSecondHit();
- else
+ switch (result.Judgement)
{
- hitExplosionContainer.Add(new HitExplosion(judgedObject, isRim));
+ case TaikoStrongJudgement _:
+ if (result.IsHit)
+ hitExplosionContainer.Children.FirstOrDefault(e => e.JudgedObject == ((DrawableStrongNestedHit)judgedObject).MainObject)?.VisualiseSecondHit();
+ break;
+ default:
+ judgementContainer.Add(new DrawableTaikoJudgement(result, judgedObject)
+ {
+ Anchor = result.IsHit ? Anchor.TopLeft : Anchor.CentreLeft,
+ Origin = result.IsHit ? Anchor.BottomCentre : Anchor.Centre,
+ RelativePositionAxes = Axes.X,
+ X = result.IsHit ? judgedObject.Position.X : 0,
+ });
- if (judgedObject.HitObject.Kiai)
- kiaiExplosionContainer.Add(new KiaiHitExplosion(judgedObject, isRim));
+ if (!result.IsHit)
+ break;
+
+ bool isRim = judgedObject.HitObject is RimHit;
+
+ hitExplosionContainer.Add(new HitExplosion(judgedObject, isRim));
+
+ if (judgedObject.HitObject.Kiai)
+ kiaiExplosionContainer.Add(new KiaiHitExplosion(judgedObject, isRim));
+
+ break;
}
}
}
diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs
index 2fa4627bde..229ab69ceb 100644
--- a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs
+++ b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs
@@ -100,12 +100,8 @@ namespace osu.Game.Rulesets.Taiko.UI
{
switch (h)
{
- case CentreHit centreHit when h.IsStrong:
- return new DrawableCentreHitStrong(centreHit);
case CentreHit centreHit:
return new DrawableCentreHit(centreHit);
- case RimHit rimHit when h.IsStrong:
- return new DrawableRimHitStrong(rimHit);
case RimHit rimHit:
return new DrawableRimHit(rimHit);
case DrumRoll drumRoll:
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
index 400380b407..0a5df0e093 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs
@@ -186,6 +186,18 @@ namespace osu.Game.Tests.Beatmaps.Formats
}
}
+ [Test]
+ public void TestDecodeBeatmapComboOffsets()
+ {
+ var decoder = new LegacyBeatmapDecoder();
+ using (var resStream = Resource.OpenResource("hitobject-combo-offset.osu"))
+ using (var stream = new StreamReader(resStream))
+ {
+ var beatmap = decoder.Decode(stream);
+ Assert.AreEqual(3, ((IHasCombo)beatmap.HitObjects[0]).ComboOffset);
+ }
+ }
+
[Test]
public void TestDecodeBeatmapHitObjects()
{
diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs
index 3431be91f9..82adc88c6b 100644
--- a/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs
+++ b/osu.Game.Tests/Beatmaps/Formats/LegacyStoryboardDecoderTest.cs
@@ -86,5 +86,19 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(78993, animation.StartTime);
}
}
+
+ [Test]
+ public void TestDecodeVariableWithSuffix()
+ {
+ var decoder = new LegacyStoryboardDecoder();
+ using (var resStream = Resource.OpenResource("variable-with-suffix.osb"))
+ using (var stream = new StreamReader(resStream))
+ {
+ var storyboard = decoder.Decode(stream);
+
+ StoryboardLayer background = storyboard.Layers.Single(l => l.Depth == 3);
+ Assert.AreEqual(123456, ((StoryboardSprite)background.Elements.Single()).InitialPosition.X);
+ }
+ }
}
}
diff --git a/osu.Game.Tests/Resources/hitobject-combo-offset.osu b/osu.Game.Tests/Resources/hitobject-combo-offset.osu
new file mode 100644
index 0000000000..4a44d31e22
--- /dev/null
+++ b/osu.Game.Tests/Resources/hitobject-combo-offset.osu
@@ -0,0 +1,4 @@
+osu file format v14
+
+[HitObjects]
+255,193,2170,49,0,0:0:0:0:
\ No newline at end of file
diff --git a/osu.Game.Tests/Resources/variable-with-suffix.osb b/osu.Game.Tests/Resources/variable-with-suffix.osb
new file mode 100644
index 0000000000..5c9b46ca98
--- /dev/null
+++ b/osu.Game.Tests/Resources/variable-with-suffix.osb
@@ -0,0 +1,5 @@
+[Variables]
+$var=1234
+
+[Events]
+Sprite,Background,TopCentre,"img.jpg",$var56,240
diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs
index b232180eba..175db7d246 100644
--- a/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs
+++ b/osu.Game.Tests/Visual/TestCaseBeatmapInfoWedge.cs
@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Linq;
+using JetBrains.Annotations;
using NUnit.Framework;
using OpenTK;
using osu.Framework.Allocation;
@@ -116,7 +117,7 @@ namespace osu.Game.Tests.Visual
private void testNullBeatmap()
{
- selectNullBeatmap();
+ selectBeatmap(null);
AddAssert("check empty version", () => string.IsNullOrEmpty(infoWedge.Info.VersionLabel.Text));
AddAssert("check default title", () => infoWedge.Info.TitleLabel.Text == Beatmap.Default.BeatmapInfo.Metadata.Title);
AddAssert("check default artist", () => infoWedge.Info.ArtistLabel.Text == Beatmap.Default.BeatmapInfo.Metadata.Artist);
@@ -124,28 +125,19 @@ namespace osu.Game.Tests.Visual
AddAssert("check no info labels", () => !infoWedge.Info.InfoLabelContainer.Children.Any());
}
- private void selectBeatmap(IBeatmap b)
+ private void selectBeatmap([CanBeNull] IBeatmap b)
{
BeatmapInfoWedge.BufferedWedgeInfo infoBefore = null;
- AddStep($"select {b.Metadata.Title} beatmap", () =>
+ AddStep($"select {b?.Metadata.Title ?? "null"} beatmap", () =>
{
infoBefore = infoWedge.Info;
- infoWedge.Beatmap = Beatmap.Value = new TestWorkingBeatmap(b);
+ infoWedge.Beatmap = Beatmap.Value = b == null ? Beatmap.Default : new TestWorkingBeatmap(b);
});
AddUntilStep(() => infoWedge.Info != infoBefore, "wait for async load");
}
- private void selectNullBeatmap()
- {
- AddStep("select null beatmap", () =>
- {
- Beatmap.Value = Beatmap.Default;
- infoWedge.Beatmap = Beatmap;
- });
- }
-
private IBeatmap createTestBeatmap(RulesetInfo ruleset)
{
List objects = new List();
diff --git a/osu.Game.Tests/Visual/TestCaseMods.cs b/osu.Game.Tests/Visual/TestCaseMods.cs
index cc396a63e3..ab53dbd968 100644
--- a/osu.Game.Tests/Visual/TestCaseMods.cs
+++ b/osu.Game.Tests/Visual/TestCaseMods.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
-using System.ComponentModel;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Overlays.Mods;
@@ -13,11 +12,11 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods;
using System.Linq;
using System.Collections.Generic;
-using osu.Game.Rulesets.Osu;
+using NUnit.Framework;
+using osu.Framework.Configuration;
using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.Sprites;
using osu.Game.Overlays.Mods.Sections;
-using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Mania.Mods;
using osu.Game.Rulesets.UI;
using OpenTK.Graphics;
@@ -50,11 +49,6 @@ namespace osu.Game.Tests.Visual
private void load(RulesetStore rulesets)
{
this.rulesets = rulesets;
- }
-
- protected override void LoadComplete()
- {
- base.LoadComplete();
Add(modSelect = new TestModSelectOverlay
{
@@ -71,34 +65,25 @@ namespace osu.Game.Tests.Visual
Position = new Vector2(0, 25),
});
+ modDisplay.Current.UnbindBindings();
modDisplay.Current.BindTo(modSelect.SelectedMods);
- AddStep("Toggle", modSelect.ToggleVisibility);
- AddStep("Hide", modSelect.Hide);
AddStep("Show", modSelect.Show);
-
- foreach (var rulesetInfo in rulesets.AvailableRulesets)
- {
- Ruleset ruleset = rulesetInfo.CreateInstance();
- AddStep($"switch to {ruleset.Description}", () => Ruleset.Value = rulesetInfo);
-
- switch (ruleset)
- {
- case OsuRuleset or:
- testOsuMods(or);
- break;
- case ManiaRuleset mr:
- testManiaMods(mr);
- break;
- }
- }
+ AddStep("Toggle", modSelect.ToggleVisibility);
+ AddStep("Toggle", modSelect.ToggleVisibility);
}
- private void testOsuMods(OsuRuleset ruleset)
+ [Test]
+ public void TestOsuMods()
{
- var easierMods = ruleset.GetModsFor(ModType.DifficultyReduction);
- var harderMods = ruleset.GetModsFor(ModType.DifficultyIncrease);
- var assistMods = ruleset.GetModsFor(ModType.Automation);
+ var ruleset = rulesets.AvailableRulesets.First(r => r.ID == 0);
+ AddStep("change ruleset", () => { Ruleset.Value = ruleset; });
+
+ var instance = ruleset.CreateInstance();
+
+ var easierMods = instance.GetModsFor(ModType.DifficultyReduction);
+ var harderMods = instance.GetModsFor(ModType.DifficultyIncrease);
+ var assistMods = instance.GetModsFor(ModType.Automation);
var noFailMod = easierMods.FirstOrDefault(m => m is OsuModNoFail);
var hiddenMod = harderMods.FirstOrDefault(m => m is OsuModHidden);
@@ -120,9 +105,40 @@ namespace osu.Game.Tests.Visual
testUnimplementedMod(autoPilotMod);
}
- private void testManiaMods(ManiaRuleset ruleset)
+ [Test]
+ public void TestManiaMods()
{
- testRankedText(ruleset.GetModsFor(ModType.Conversion).First(m => m is ManiaModRandom));
+ var ruleset = rulesets.AvailableRulesets.First(r => r.ID == 3);
+ AddStep("change ruleset", () => { Ruleset.Value = ruleset; });
+
+ testRankedText(ruleset.CreateInstance().GetModsFor(ModType.Conversion).First(m => m is ManiaModRandom));
+ }
+
+ [Test]
+ public void TestRulesetChanges()
+ {
+ var rulesetOsu = rulesets.AvailableRulesets.First(r => r.ID == 0);
+ var rulesetMania = rulesets.AvailableRulesets.First(r => r.ID == 3);
+
+ AddStep("change ruleset to null", () => { Ruleset.Value = null; });
+
+ var instance = rulesetOsu.CreateInstance();
+ var easierMods = instance.GetModsFor(ModType.DifficultyReduction);
+ var noFailMod = easierMods.FirstOrDefault(m => m is OsuModNoFail);
+
+ AddStep("set mods externally", () => { modDisplay.Current.Value = new[] { noFailMod }; });
+
+ AddStep("change ruleset to osu", () => { Ruleset.Value = rulesetOsu; });
+
+ AddAssert("ensure mods still selected", () => modDisplay.Current.Value.Single(m => m is OsuModNoFail) != null);
+
+ AddStep("change ruleset to mania", () => { Ruleset.Value = rulesetMania; });
+
+ AddAssert("ensure mods not selected", () => !modDisplay.Current.Value.Any(m => m is OsuModNoFail));
+
+ AddStep("change ruleset to osu", () => { Ruleset.Value = rulesetOsu; });
+
+ AddAssert("ensure mods not selected", () => !modDisplay.Current.Value.Any());
}
private void testSingleMod(Mod mod)
@@ -237,6 +253,8 @@ namespace osu.Game.Tests.Visual
private class TestModSelectOverlay : ModSelectOverlay
{
+ public new Bindable> SelectedMods => base.SelectedMods;
+
public ModButton GetModButton(Mod mod)
{
var section = ModSectionsContainer.Children.Single(s => s.ModType == mod.Type);
diff --git a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs
index b1ffe04b68..888bf6250f 100644
--- a/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs
+++ b/osu.Game.Tests/Visual/TestCasePlaySongSelect.cs
@@ -8,11 +8,15 @@ using System.Linq;
using System.Text;
using NUnit.Framework;
using osu.Framework.Allocation;
+using osu.Framework.Configuration;
using osu.Framework.Extensions;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Database;
using osu.Game.Rulesets;
+using osu.Game.Rulesets.Mods;
+using osu.Game.Rulesets.Osu.Mods;
+using osu.Game.Rulesets.Taiko;
using osu.Game.Screens.Select;
using osu.Game.Screens.Select.Carousel;
using osu.Game.Screens.Select.Filter;
@@ -29,6 +33,10 @@ namespace osu.Game.Tests.Visual
private WorkingBeatmap defaultBeatmap;
private DatabaseContextFactory factory;
+ [Cached]
+ [Cached(Type = typeof(IBindable>))]
+ private readonly Bindable> selectedMods = new Bindable>(new Mod[] { });
+
public override IReadOnlyList RequiredTypes => new[]
{
typeof(SongSelect),
@@ -49,6 +57,8 @@ namespace osu.Game.Tests.Visual
private class TestSongSelect : PlaySongSelect
{
+ public new Bindable Ruleset => base.Ruleset;
+
public WorkingBeatmap CurrentBeatmap => Beatmap.Value;
public WorkingBeatmap CurrentBeatmapDetailsBeatmap => BeatmapDetails.Beatmap;
public new BeatmapCarousel Carousel => base.Carousel;
@@ -121,7 +131,7 @@ namespace osu.Game.Tests.Visual
[Test]
[Ignore("needs fixing")]
- public void ImportUnderDifferentRuleset()
+ public void TestImportUnderDifferentRuleset()
{
changeRuleset(2);
importForRuleset(0);
@@ -129,7 +139,7 @@ namespace osu.Game.Tests.Visual
}
[Test]
- public void ImportUnderCurrentRuleset()
+ public void TestImportUnderCurrentRuleset()
{
changeRuleset(2);
importForRuleset(2);
@@ -143,11 +153,42 @@ namespace osu.Game.Tests.Visual
AddUntilStep(() => songSelect.Carousel.SelectedBeatmap == null, "no selection");
}
+ [Test]
+ public void TestRulesetChangeResetsMods()
+ {
+ changeRuleset(0);
+
+ changeMods(new OsuModHardRock());
+
+ int actionIndex = 0;
+ int modChangeIndex = 0;
+ int rulesetChangeIndex = 0;
+
+ AddStep("change ruleset", () =>
+ {
+ songSelect.CurrentBeatmap.Mods.ValueChanged += onModChange;
+ songSelect.Ruleset.ValueChanged += onRulesetChange;
+
+ Ruleset.Value = new TaikoRuleset().RulesetInfo;
+
+ songSelect.CurrentBeatmap.Mods.ValueChanged -= onModChange;
+ songSelect.Ruleset.ValueChanged -= onRulesetChange;
+ });
+
+ AddAssert("mods changed before ruleset", () => modChangeIndex < rulesetChangeIndex);
+ AddAssert("empty mods", () => !selectedMods.Value.Any());
+
+ void onModChange(IEnumerable mods) => modChangeIndex = actionIndex++;
+ void onRulesetChange(RulesetInfo ruleset) => rulesetChangeIndex = actionIndex--;
+ }
+
private void importForRuleset(int id) => AddStep($"import test map for ruleset {id}", () => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray())));
private static int importId;
private int getImportId() => ++importId;
+ private void changeMods(params Mod[] mods) => AddStep($"change mods to {string.Join(", ", mods.Select(m => m.ShortenedName))}", () => selectedMods.Value = mods);
+
private void changeRuleset(int id) => AddStep($"change ruleset to {id}", () => Ruleset.Value = rulesets.AvailableRulesets.First(r => r.ID == id));
private void addManyTestMaps()
diff --git a/osu.Game.Tests/Visual/TestCasePlayerLoader.cs b/osu.Game.Tests/Visual/TestCasePlayerLoader.cs
index 52a9db080d..de839a21af 100644
--- a/osu.Game.Tests/Visual/TestCasePlayerLoader.cs
+++ b/osu.Game.Tests/Visual/TestCasePlayerLoader.cs
@@ -1,25 +1,61 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using System.Threading;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual
{
- public class TestCasePlayerLoader : OsuTestCase
+ public class TestCasePlayerLoader : ManualInputManagerTestCase
{
+ private PlayerLoader loader;
+
[BackgroundDependencyLoader]
private void load(OsuGameBase game)
{
Beatmap.Value = new DummyWorkingBeatmap(game);
- AddStep("load dummy beatmap", () => Add(new PlayerLoader(new Player
+ AddStep("load dummy beatmap", () => Add(loader = new PlayerLoader(new Player
{
AllowPause = false,
AllowLeadIn = false,
AllowResults = false,
})));
+
+ AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre));
+
+ AddUntilStep(() => !loader.IsCurrentScreen, "wait for no longer current");
+
+ AddStep("load slow dummy beatmap", () =>
+ {
+ SlowLoadPlayer slow;
+
+ Add(loader = new PlayerLoader(slow = new SlowLoadPlayer
+ {
+ AllowPause = false,
+ AllowLeadIn = false,
+ AllowResults = false,
+ }));
+
+ Scheduler.AddDelayed(() => slow.Ready = true, 5000);
+ });
+
+ AddUntilStep(() => !loader.IsCurrentScreen, "wait for no longer current");
+ }
+
+ protected class SlowLoadPlayer : Player
+ {
+ public bool Ready;
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ while (!Ready)
+ Thread.Sleep(1);
+ }
}
}
+
}
diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs
index 67f02c8ac4..1c28b533d2 100644
--- a/osu.Game/Beatmaps/BeatmapManager.cs
+++ b/osu.Game/Beatmaps/BeatmapManager.cs
@@ -329,7 +329,7 @@ namespace osu.Game.Beatmaps
return;
}
- await Task.Factory.StartNew(() => Import(stable.GetDirectories("Songs")), TaskCreationOptions.LongRunning);
+ await Task.Factory.StartNew(() => Import(stable.GetDirectories("Songs").Select(f => stable.GetFullPath(f)).ToArray()), TaskCreationOptions.LongRunning);
}
///
diff --git a/osu.Game/Beatmaps/BeatmapProcessor.cs b/osu.Game/Beatmaps/BeatmapProcessor.cs
index 0173125e8b..9d7cd673dc 100644
--- a/osu.Game/Beatmaps/BeatmapProcessor.cs
+++ b/osu.Game/Beatmaps/BeatmapProcessor.cs
@@ -27,11 +27,10 @@ namespace osu.Game.Beatmaps
if (obj.NewCombo)
{
obj.IndexInCurrentCombo = 0;
+ obj.ComboIndex = (lastObj?.ComboIndex ?? 0) + obj.ComboOffset + 1;
+
if (lastObj != null)
- {
lastObj.LastInCombo = true;
- obj.ComboIndex = lastObj.ComboIndex + 1;
- }
}
else if (lastObj != null)
{
diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
index 26f28c86ca..31a7698f50 100644
--- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs
@@ -126,16 +126,16 @@ namespace osu.Game.Beatmaps.Formats
switch (beatmap.BeatmapInfo.RulesetID)
{
case 0:
- parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser();
+ parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(getOffsetTime(), FormatVersion);
break;
case 1:
- parser = new Rulesets.Objects.Legacy.Taiko.ConvertHitObjectParser();
+ parser = new Rulesets.Objects.Legacy.Taiko.ConvertHitObjectParser(getOffsetTime(), FormatVersion);
break;
case 2:
- parser = new Rulesets.Objects.Legacy.Catch.ConvertHitObjectParser();
+ parser = new Rulesets.Objects.Legacy.Catch.ConvertHitObjectParser(getOffsetTime(), FormatVersion);
break;
case 3:
- parser = new Rulesets.Objects.Legacy.Mania.ConvertHitObjectParser();
+ parser = new Rulesets.Objects.Legacy.Mania.ConvertHitObjectParser(getOffsetTime(), FormatVersion);
break;
}
@@ -354,6 +354,11 @@ namespace osu.Game.Beatmaps.Formats
private void handleTimingControlPoint(TimingControlPoint newPoint)
{
+ var existing = beatmap.ControlPointInfo.TimingPointAt(newPoint.Time);
+
+ if (existing.Time == newPoint.Time)
+ beatmap.ControlPointInfo.TimingPoints.Remove(existing);
+
beatmap.ControlPointInfo.TimingPoints.Add(newPoint);
}
@@ -364,7 +369,9 @@ namespace osu.Game.Beatmaps.Formats
if (newPoint.EquivalentTo(existing))
return;
- beatmap.ControlPointInfo.DifficultyPoints.RemoveAll(x => x.Time == newPoint.Time);
+ if (existing.Time == newPoint.Time)
+ beatmap.ControlPointInfo.DifficultyPoints.Remove(existing);
+
beatmap.ControlPointInfo.DifficultyPoints.Add(newPoint);
}
@@ -375,6 +382,9 @@ namespace osu.Game.Beatmaps.Formats
if (newPoint.EquivalentTo(existing))
return;
+ if (existing.Time == newPoint.Time)
+ beatmap.ControlPointInfo.EffectPoints.Remove(existing);
+
beatmap.ControlPointInfo.EffectPoints.Add(newPoint);
}
@@ -385,6 +395,9 @@ namespace osu.Game.Beatmaps.Formats
if (newPoint.EquivalentTo(existing))
return;
+ if (existing.Time == newPoint.Time)
+ beatmap.ControlPointInfo.SamplePoints.Remove(existing);
+
beatmap.ControlPointInfo.SamplePoints.Add(newPoint);
}
@@ -392,9 +405,9 @@ namespace osu.Game.Beatmaps.Formats
{
// If the ruleset wasn't specified, assume the osu!standard ruleset.
if (parser == null)
- parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser();
+ parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(getOffsetTime(), FormatVersion);
- var obj = parser.Parse(line, getOffsetTime());
+ var obj = parser.Parse(line);
if (obj != null)
{
diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
index b418cbd5ec..a8a62013b1 100644
--- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
+++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs
@@ -289,15 +289,10 @@ namespace osu.Game.Beatmaps.Formats
while (line.IndexOf('$') >= 0)
{
string origLine = line;
- string[] split = line.Split(',');
- for (int i = 0; i < split.Length; i++)
- {
- var item = split[i];
- if (item.StartsWith("$") && variables.ContainsKey(item))
- split[i] = variables[item];
- }
- line = string.Join(",", split);
+ foreach (var v in variables)
+ line = line.Replace(v.Key, v.Value);
+
if (line == origLine)
break;
}
diff --git a/osu.Game/Database/ArchiveModelManager.cs b/osu.Game/Database/ArchiveModelManager.cs
index 0465c0ad73..c00df59e3e 100644
--- a/osu.Game/Database/ArchiveModelManager.cs
+++ b/osu.Game/Database/ArchiveModelManager.cs
@@ -412,7 +412,7 @@ namespace osu.Game.Database
private ArchiveReader getReaderFrom(string path)
{
if (ZipUtils.IsZipArchive(path))
- return new ZipArchiveReader(Files.Storage.GetStream(path), Path.GetFileName(path));
+ return new ZipArchiveReader(File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read), Path.GetFileName(path));
if (Directory.Exists(path))
return new LegacyFilesystemReader(path);
throw new InvalidFormatException($"{path} is not a valid archive");
diff --git a/osu.Game/Online/Chat/InfoMessage.cs b/osu.Game/Online/Chat/InfoMessage.cs
index 2be025e403..2ff901deb1 100644
--- a/osu.Game/Online/Chat/InfoMessage.cs
+++ b/osu.Game/Online/Chat/InfoMessage.cs
@@ -6,7 +6,7 @@ using osu.Game.Users;
namespace osu.Game.Online.Chat
{
- public class InfoMessage : Message
+ public class InfoMessage : LocalMessage
{
private static int infoID = -1;
diff --git a/osu.Game/Online/Chat/LocalEchoMessage.cs b/osu.Game/Online/Chat/LocalEchoMessage.cs
index 2e90b9d3fd..7d678029aa 100644
--- a/osu.Game/Online/Chat/LocalEchoMessage.cs
+++ b/osu.Game/Online/Chat/LocalEchoMessage.cs
@@ -3,7 +3,7 @@
namespace osu.Game.Online.Chat
{
- public class LocalEchoMessage : Message
+ public class LocalEchoMessage : LocalMessage
{
public LocalEchoMessage() : base(null)
{
diff --git a/osu.Game/Online/Chat/LocalMessage.cs b/osu.Game/Online/Chat/LocalMessage.cs
new file mode 100644
index 0000000000..93f1e7f9ea
--- /dev/null
+++ b/osu.Game/Online/Chat/LocalMessage.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.Online.Chat
+{
+ ///
+ /// A message which is generated and displayed locally.
+ ///
+ public class LocalMessage : Message
+ {
+ protected LocalMessage(long? id)
+ : base(id)
+ {
+ }
+ }
+}
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index 025d5f50e3..a1e385921f 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -99,7 +99,9 @@ namespace osu.Game
private readonly List overlays = new List();
// todo: move this to SongSelect once Screen has the ability to unsuspend.
- public readonly Bindable> SelectedMods = new Bindable>(new List());
+ [Cached]
+ [Cached(Type = typeof(IBindable>))]
+ private readonly Bindable> selectedMods = new Bindable>(new Mod[] { });
public OsuGame(string[] args = null)
{
diff --git a/osu.Game/Overlays/Chat/DrawableChannel.cs b/osu.Game/Overlays/Chat/DrawableChannel.cs
index bcc8879902..c57e71b5ad 100644
--- a/osu.Game/Overlays/Chat/DrawableChannel.cs
+++ b/osu.Game/Overlays/Chat/DrawableChannel.cs
@@ -85,7 +85,7 @@ namespace osu.Game.Overlays.Chat
if (!IsLoaded) return;
- if (scroll.IsScrolledToEnd(10) || !flow.Children.Any())
+ if (scroll.IsScrolledToEnd(10) || !flow.Children.Any() || newMessages.Any(m => m is LocalMessage))
scrollToEnd();
var staleMessages = flow.Children.Where(c => c.LifetimeEnd == double.MaxValue).ToArray();
diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/Direct/DirectListPanel.cs
index 45e1164a57..850ead37f6 100644
--- a/osu.Game/Overlays/Direct/DirectListPanel.cs
+++ b/osu.Game/Overlays/Direct/DirectListPanel.cs
@@ -12,7 +12,6 @@ using osu.Game.Graphics.Sprites;
using osu.Framework.Allocation;
using osu.Framework.Localisation;
using osu.Framework.Graphics.Shapes;
-using osu.Framework.Input.States;
using osu.Game.Beatmaps;
namespace osu.Game.Overlays.Direct
@@ -26,12 +25,14 @@ namespace osu.Game.Overlays.Direct
private PlayButton playButton;
private Box progressBar;
- private Container downloadContainer;
+
+ protected override bool FadePlayButton => false;
protected override PlayButton PlayButton => playButton;
protected override Box PreviewBar => progressBar;
- public DirectListPanel(BeatmapSetInfo beatmap) : base(beatmap)
+ public DirectListPanel(BeatmapSetInfo beatmap)
+ : base(beatmap)
{
RelativeSizeAxes = Axes.X;
Height = height;
@@ -66,30 +67,45 @@ namespace osu.Game.Overlays.Direct
Spacing = new Vector2(10, 0),
Children = new Drawable[]
{
- playButton = new PlayButton(SetInfo)
- {
- Origin = Anchor.CentreLeft,
- Anchor = Anchor.CentreLeft,
- Size = new Vector2(height / 2),
- FillMode = FillMode.Fit,
- Alpha = 0,
- },
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
- new OsuSpriteText
+ new FillFlowContainer
{
- Current = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title),
- TextSize = 18,
- Font = @"Exo2.0-BoldItalic",
- },
- new OsuSpriteText
- {
- Current = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist),
- Font = @"Exo2.0-BoldItalic",
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Horizontal,
+ Children = new Drawable[]
+ {
+ playButton = new PlayButton(SetInfo)
+ {
+ Origin = Anchor.CentreLeft,
+ Anchor = Anchor.CentreLeft,
+ Size = new Vector2(height / 2),
+ FillMode = FillMode.Fit,
+ },
+ new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
+ {
+ new OsuSpriteText
+ {
+ Current = localisation.GetUnicodePreference(SetInfo.Metadata.TitleUnicode, SetInfo.Metadata.Title),
+ TextSize = 18,
+ Font = @"Exo2.0-BoldItalic",
+ },
+ new OsuSpriteText
+ {
+ Current = localisation.GetUnicodePreference(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist),
+ Font = @"Exo2.0-BoldItalic",
+ },
+ }
+ },
+ }
},
new FillFlowContainer
{
@@ -108,16 +124,13 @@ namespace osu.Game.Overlays.Direct
Origin = Anchor.TopRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
- LayoutEasing = Easing.OutQuint,
- LayoutDuration = transition_duration,
Children = new Drawable[]
{
- downloadContainer = new Container
+ new Container
{
- Anchor = Anchor.TopRight,
- Origin = Anchor.TopRight,
+ Anchor = Anchor.CentreRight,
+ Origin = Anchor.CentreRight,
AutoSizeAxes = Axes.Both,
- Alpha = 0,
Child = new DownloadButton(SetInfo)
{
Size = new Vector2(height - vertical_padding * 3),
@@ -184,17 +197,5 @@ namespace osu.Game.Overlays.Direct
},
});
}
-
- protected override bool OnHover(InputState state)
- {
- downloadContainer.FadeIn(transition_duration, Easing.InOutQuint);
- return base.OnHover(state);
- }
-
- protected override void OnHoverLost(InputState state)
- {
- downloadContainer.FadeOut(transition_duration, Easing.InOutQuint);
- base.OnHoverLost(state);
- }
}
}
diff --git a/osu.Game/Overlays/Direct/DirectPanel.cs b/osu.Game/Overlays/Direct/DirectPanel.cs
index 7d5c0c16cc..322db0b7d6 100644
--- a/osu.Game/Overlays/Direct/DirectPanel.cs
+++ b/osu.Game/Overlays/Direct/DirectPanel.cs
@@ -40,6 +40,8 @@ namespace osu.Game.Overlays.Direct
protected abstract PlayButton PlayButton { get; }
protected abstract Box PreviewBar { get; }
+ protected virtual bool FadePlayButton => true;
+
protected override Container Content => content;
protected DirectPanel(BeatmapSetInfo setInfo)
@@ -125,7 +127,8 @@ namespace osu.Game.Overlays.Direct
{
content.TweenEdgeEffectTo(edgeEffectHovered, hover_transition_time, Easing.OutQuint);
content.MoveToY(-4, hover_transition_time, Easing.OutQuint);
- PlayButton.FadeIn(120, Easing.InOutQuint);
+ if (FadePlayButton)
+ PlayButton.FadeIn(120, Easing.InOutQuint);
return base.OnHover(state);
}
@@ -134,7 +137,7 @@ namespace osu.Game.Overlays.Direct
{
content.TweenEdgeEffectTo(edgeEffectNormal, hover_transition_time, Easing.OutQuint);
content.MoveToY(0, hover_transition_time, Easing.OutQuint);
- if (!PreviewPlaying)
+ if (FadePlayButton && !PreviewPlaying)
PlayButton.FadeOut(120, Easing.InOutQuint);
base.OnHoverLost(state);
diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
index 4745eba68d..e83dedaf35 100644
--- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs
+++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs
@@ -27,6 +27,11 @@ namespace osu.Game.Overlays.Mods
{
public class ModSelectOverlay : WaveOverlayContainer
{
+ ///
+ /// How much this container should overflow the sides of the screen to account for parallax shifting.
+ ///
+ private const float overflow_padding = 50;
+
private const float content_width = 0.8f;
protected Color4 LowMultiplierColour, HighMultiplierColour;
@@ -39,9 +44,39 @@ namespace osu.Game.Overlays.Mods
protected readonly FillFlowContainer ModSectionsContainer;
- public readonly Bindable> SelectedMods = new Bindable>();
+ protected readonly Bindable> SelectedMods = new Bindable>(new Mod[] { });
- public readonly IBindable Ruleset = new Bindable();
+ protected readonly IBindable Ruleset = new Bindable();
+
+ [BackgroundDependencyLoader(true)]
+ private void load(OsuColour colours, IBindable ruleset, AudioManager audio, Bindable> selectedMods)
+ {
+ LowMultiplierColour = colours.Red;
+ HighMultiplierColour = colours.Green;
+ UnrankedLabel.Colour = colours.Blue;
+
+ Ruleset.BindTo(ruleset);
+ if (selectedMods != null) SelectedMods.BindTo(selectedMods);
+
+ sampleOn = audio.Sample.Get(@"UI/check-on");
+ sampleOff = audio.Sample.Get(@"UI/check-off");
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ Ruleset.BindValueChanged(rulesetChanged, true);
+ SelectedMods.BindValueChanged(selectedModsChanged, true);
+ }
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+
+ Ruleset.UnbindAll();
+ SelectedMods.UnbindAll();
+ }
private void rulesetChanged(RulesetInfo newRuleset)
{
@@ -51,33 +86,16 @@ namespace osu.Game.Overlays.Mods
foreach (ModSection section in ModSectionsContainer.Children)
section.Mods = instance.GetModsFor(section.ModType);
+
+ // attempt to re-select any already selected mods.
+ // this may be the first time we are receiving the ruleset, in which case they will still match.
+ selectedModsChanged(SelectedMods.Value);
+
+ // write the mods back to the SelectedMods bindable in the case a change was not applicable.
+ // this generally isn't required as the previous line will perform deselection; just here for safety.
refreshSelectedMods();
}
- [BackgroundDependencyLoader]
- private void load(OsuColour colours, IBindable ruleset, AudioManager audio)
- {
- SelectedMods.ValueChanged += selectedModsChanged;
-
- LowMultiplierColour = colours.Red;
- HighMultiplierColour = colours.Green;
- UnrankedLabel.Colour = colours.Blue;
-
- Ruleset.BindTo(ruleset);
- Ruleset.BindValueChanged(rulesetChanged, true);
-
- sampleOn = audio.Sample.Get(@"UI/check-on");
- sampleOff = audio.Sample.Get(@"UI/check-off");
- }
-
- protected override void Dispose(bool isDisposing)
- {
- base.Dispose(isDisposing);
-
- Ruleset.UnbindAll();
- SelectedMods.UnbindAll();
- }
-
private void selectedModsChanged(IEnumerable obj)
{
foreach (ModSection section in ModSectionsContainer.Children)
@@ -176,10 +194,7 @@ namespace osu.Game.Overlays.Mods
refreshSelectedMods();
}
- private void refreshSelectedMods()
- {
- SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray();
- }
+ private void refreshSelectedMods() => SelectedMods.Value = ModSectionsContainer.Children.SelectMany(s => s.SelectedMods).ToArray();
public ModSelectOverlay()
{
@@ -189,6 +204,11 @@ namespace osu.Game.Overlays.Mods
Waves.FourthWaveColour = OsuColour.FromHex(@"003a4e");
Height = 510;
+ Padding = new MarginPadding
+ {
+ Left = -overflow_padding,
+ Right = -overflow_padding
+ };
Children = new Drawable[]
{
@@ -248,6 +268,11 @@ namespace osu.Game.Overlays.Mods
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Width = content_width,
+ Padding = new MarginPadding
+ {
+ Left = overflow_padding,
+ Right = overflow_padding
+ },
Children = new Drawable[]
{
new OsuSpriteText
@@ -285,7 +310,12 @@ namespace osu.Game.Overlays.Mods
Origin = Anchor.TopCentre,
Anchor = Anchor.TopCentre,
RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding { Vertical = 10 },
+ Padding = new MarginPadding
+ {
+ Vertical = 10,
+ Left = overflow_padding,
+ Right = overflow_padding
+ },
Child = ModSectionsContainer = new FillFlowContainer
{
Origin = Anchor.TopCentre,
@@ -331,7 +361,9 @@ namespace osu.Game.Overlays.Mods
Direction = FillDirection.Horizontal,
Padding = new MarginPadding
{
- Vertical = 15
+ Vertical = 15,
+ Left = overflow_padding,
+ Right = overflow_padding
},
Children = new Drawable[]
{
diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
index b2f6e909d2..a3253250f2 100644
--- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs
+++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs
@@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Logging;
using osu.Framework.Timing;
using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Edit.Tools;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.UI;
@@ -23,12 +24,15 @@ namespace osu.Game.Rulesets.Edit
{
private readonly Ruleset ruleset;
+ public IEnumerable HitObjects => rulesetContainer.Playfield.AllHitObjects;
+
protected ICompositionTool CurrentTool { get; private set; }
+ protected IRulesetConfigManager Config { get; private set; }
+
+ private readonly List layerContainers = new List();
+ private readonly IBindable beatmap = new Bindable();
private RulesetContainer rulesetContainer;
- private readonly List layerContainers = new List();
-
- private readonly IBindable beatmap = new Bindable();
protected HitObjectComposer(Ruleset ruleset)
{
@@ -60,7 +64,7 @@ namespace osu.Game.Rulesets.Edit
};
var layerAboveRuleset = CreateLayerContainer();
- layerAboveRuleset.Child = new HitObjectMaskLayer(rulesetContainer.Playfield, this);
+ layerAboveRuleset.Child = new HitObjectMaskLayer();
layerContainers.Add(layerBelowRuleset);
layerContainers.Add(layerAboveRuleset);
@@ -110,6 +114,16 @@ namespace osu.Game.Rulesets.Edit
toolboxCollection.Items[0].Select();
}
+ protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
+ {
+ var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
+
+ dependencies.CacheAs(this);
+ Config = dependencies.Get().GetConfigFor(ruleset);
+
+ return dependencies;
+ }
+
protected override void LoadComplete()
{
base.LoadComplete();
diff --git a/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs b/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs
index 2c3720fc8f..78ad236e74 100644
--- a/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs
+++ b/osu.Game/Rulesets/Edit/Tools/HitObjectCompositionTool.cs
@@ -8,6 +8,16 @@ namespace osu.Game.Rulesets.Edit.Tools
public class HitObjectCompositionTool : ICompositionTool
where T : HitObject
{
- public string Name => typeof(T).Name;
+ public string Name { get; }
+
+ public HitObjectCompositionTool()
+ : this(typeof(T).Name)
+ {
+ }
+
+ public HitObjectCompositionTool(string name)
+ {
+ Name = name;
+ }
}
}
diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs
index 5de14ae579..65b2ef75c4 100644
--- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs
+++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs
@@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Judgements
private OsuColour colours;
- protected readonly Judgement Judgement;
+ protected readonly JudgementResult Result;
public readonly DrawableHitObject JudgedObject;
@@ -34,11 +34,11 @@ namespace osu.Game.Rulesets.Judgements
///
/// Creates a drawable which visualises a .
///
- /// The judgement to visualise.
+ /// The judgement to visualise.
/// The object which was judged.
- public DrawableJudgement(Judgement judgement, DrawableHitObject judgedObject)
+ public DrawableJudgement(JudgementResult result, DrawableHitObject judgedObject)
{
- Judgement = judgement;
+ Result = result;
JudgedObject = judgedObject;
Size = new Vector2(judgement_size);
@@ -49,11 +49,11 @@ namespace osu.Game.Rulesets.Judgements
{
this.colours = colours;
- Child = new SkinnableDrawable($"Play/{Judgement.Result}", _ => JudgementText = new OsuSpriteText
+ Child = new SkinnableDrawable($"Play/{Result.Type}", _ => JudgementText = new OsuSpriteText
{
- Text = Judgement.Result.GetDescription().ToUpperInvariant(),
+ Text = Result.Type.GetDescription().ToUpperInvariant(),
Font = @"Venera",
- Colour = judgementColour(Judgement.Result),
+ Colour = judgementColour(Result.Type),
Scale = new Vector2(0.85f, 1),
TextSize = 12
}, restrictSize: false);
@@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Judgements
this.FadeInFromZero(100, Easing.OutQuint);
- switch (Judgement.Result)
+ switch (Result.Type)
{
case HitResult.None:
break;
diff --git a/osu.Game/Rulesets/Judgements/Judgement.cs b/osu.Game/Rulesets/Judgements/Judgement.cs
index 129dd07c3e..c679df5900 100644
--- a/osu.Game/Rulesets/Judgements/Judgement.cs
+++ b/osu.Game/Rulesets/Judgements/Judgement.cs
@@ -1,74 +1,48 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Judgements
{
+ ///
+ /// The scoring information provided by a .
+ ///
public class Judgement
{
- ///
- /// Whether this judgement is the result of a hit or a miss.
- ///
- public HitResult Result;
-
///
/// The maximum that can be achieved.
///
public virtual HitResult MaxResult => HitResult.Perfect;
///
- /// The combo prior to this judgement occurring.
- ///
- public int ComboAtJudgement;
-
- ///
- /// The highest combo achieved prior to this judgement occurring.
- ///
- public int HighestComboAtJudgement;
-
- ///
- /// Whether a successful hit occurred.
- ///
- public bool IsHit => Result > HitResult.Miss;
-
- ///
- /// Whether this judgement is the final judgement for the hit object.
- ///
- public bool Final = true;
-
- ///
- /// The offset from a perfect hit at which this judgement occurred.
- /// Populated when added via .
- ///
- public double TimeOffset { get; set; }
-
- ///
- /// Whether the should affect the current combo.
+ /// Whether this should affect the current combo.
///
public virtual bool AffectsCombo => true;
///
- /// Whether the should be counted as base (combo) or bonus score.
+ /// Whether this should be counted as base (combo) or bonus score.
///
public virtual bool IsBonus => !AffectsCombo;
///
- /// The numeric representation for the result achieved.
- ///
- public int NumericResult => NumericResultFor(Result);
-
- ///
- /// The numeric representation for the maximum achievable result.
+ /// The numeric score representation for the maximum achievable result.
///
public int MaxNumericResult => NumericResultFor(MaxResult);
///
- /// Convert a to a numeric score representation.
+ /// Retrieves the numeric score representation of a .
///
- /// The value to convert.
- /// The number.
+ /// The to find the numeric score representation for.
+ /// The numeric score representation of .
protected virtual int NumericResultFor(HitResult result) => result > HitResult.Miss ? 1 : 0;
+
+ ///
+ /// Retrieves the numeric score representation of a .
+ ///
+ /// The to find the numeric score representation for.
+ /// The numeric score representation of .
+ public int NumericResultFor(JudgementResult result) => NumericResultFor(result.Type);
}
}
diff --git a/osu.Game/Rulesets/Judgements/JudgementResult.cs b/osu.Game/Rulesets/Judgements/JudgementResult.cs
new file mode 100644
index 0000000000..5cadf7e2ee
--- /dev/null
+++ b/osu.Game/Rulesets/Judgements/JudgementResult.cs
@@ -0,0 +1,59 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Scoring;
+
+namespace osu.Game.Rulesets.Judgements
+{
+ ///
+ /// The scoring result of a .
+ ///
+ public class JudgementResult
+ {
+ ///
+ /// Whether this is the result of a hit or a miss.
+ ///
+ public HitResult Type;
+
+ ///
+ /// The which this applies for.
+ ///
+ public readonly Judgement Judgement;
+
+ ///
+ /// The offset from a perfect hit at which this occurred.
+ /// Populated when this is applied via .
+ ///
+ public double TimeOffset { get; internal set; }
+
+ ///
+ /// The combo prior to this occurring.
+ ///
+ public int ComboAtJudgement { get; internal set; }
+
+ ///
+ /// The highest combo achieved prior to this occurring.
+ ///
+ public int HighestComboAtJudgement { get; internal set; }
+
+ ///
+ /// Whether a miss or hit occurred.
+ ///
+ public bool HasResult => Type > HitResult.None;
+
+ ///
+ /// Whether a successful hit occurred.
+ ///
+ public bool IsHit => Type > HitResult.Miss;
+
+ ///
+ /// Creates a new .
+ ///
+ /// The to refer to for scoring information.
+ public JudgementResult(Judgement judgement)
+ {
+ Judgement = judgement;
+ }
+ }
+}
diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
index a22aaa784f..7e3e955740 100644
--- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
+++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
@@ -6,6 +6,7 @@ using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
+using osu.Framework.Extensions.TypeExtensions;
using osu.Game.Audio;
using osu.Game.Graphics;
using osu.Game.Rulesets.Judgements;
@@ -35,34 +36,44 @@ namespace osu.Game.Rulesets.Objects.Drawables
private readonly Lazy> nestedHitObjects = new Lazy>();
public IEnumerable NestedHitObjects => nestedHitObjects.IsValueCreated ? nestedHitObjects.Value : Enumerable.Empty();
- public event Action OnJudgement;
- public event Action OnJudgementRemoved;
-
- public IReadOnlyList Judgements => judgements;
- private readonly List judgements = new List();
+ ///
+ /// Invoked when a has been applied by this or a nested .
+ ///
+ public event Action OnNewResult;
///
- /// Whether a visible judgement should be displayed when this representation is hit.
+ /// Invoked when a is being reverted by this or a nested .
///
- public virtual bool DisplayJudgement => true;
+ public event Action OnRevertResult;
///
- /// Whether this and all of its nested s have been hit.
+ /// Whether a visual indicator should be displayed when a scoring result occurs.
///
- public bool IsHit => Judgements.Any(j => j.Final && j.IsHit) && NestedHitObjects.All(n => n.IsHit);
+ public virtual bool DisplayResult => true;
///
/// Whether this and all of its nested s have been judged.
///
- public bool AllJudged => (!ProvidesJudgement || judgementFinalized) && NestedHitObjects.All(h => h.AllJudged);
+ public bool AllJudged => Judged && NestedHitObjects.All(h => h.AllJudged);
///
- /// Whether this can be judged.
+ /// Whether this has been hit. This occurs if is .
+ /// Note: This does NOT include nested hitobjects.
///
- protected virtual bool ProvidesJudgement => true;
+ public bool IsHit => Result?.IsHit ?? false;
+
+ ///
+ /// Whether this has been judged.
+ /// Note: This does NOT include nested hitobjects.
+ ///
+ public bool Judged => Result?.HasResult ?? true;
+
+ ///
+ /// The scoring result of this .
+ ///
+ public JudgementResult Result { get; private set; }
private bool judgementOccurred;
- private bool judgementFinalized => judgements.LastOrDefault()?.Final == true;
public bool Interactive = true;
public override bool HandleKeyboardInput => Interactive;
@@ -82,6 +93,14 @@ namespace osu.Game.Rulesets.Objects.Drawables
[BackgroundDependencyLoader]
private void load()
{
+ var judgement = HitObject.CreateJudgement();
+ if (judgement != null)
+ {
+ Result = CreateResult(judgement);
+ if (Result == null)
+ throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}.");
+ }
+
var samples = GetSamples().ToArray();
if (samples.Any())
@@ -132,18 +151,17 @@ namespace osu.Game.Rulesets.Objects.Drawables
{
base.Update();
- var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
-
- while (judgements.Count > 0)
+ if (Result != null && Result.HasResult)
{
- var lastJudgement = judgements[judgements.Count - 1];
- if (lastJudgement.TimeOffset + endTime <= Time.Current)
- break;
+ var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
- judgements.RemoveAt(judgements.Count - 1);
- State.Value = ArmedState.Idle;
+ if (Result.TimeOffset + endTime > Time.Current)
+ {
+ OnRevertResult?.Invoke(this, Result);
- OnJudgementRemoved?.Invoke(this, lastJudgement);
+ Result.Type = HitResult.None;
+ State.Value = ArmedState.Idle;
+ }
}
}
@@ -151,33 +169,37 @@ namespace osu.Game.Rulesets.Objects.Drawables
{
base.UpdateAfterChildren();
- UpdateJudgement(false);
+ UpdateResult(false);
}
protected virtual void AddNested(DrawableHitObject h)
{
- h.OnJudgement += (d, j) => OnJudgement?.Invoke(d, j);
- h.OnJudgementRemoved += (d, j) => OnJudgementRemoved?.Invoke(d, j);
+ h.OnNewResult += (d, r) => OnNewResult?.Invoke(d, r);
+ h.OnRevertResult += (d, r) => OnRevertResult?.Invoke(d, r);
h.ApplyCustomUpdateState += (d, j) => ApplyCustomUpdateState?.Invoke(d, j);
nestedHitObjects.Value.Add(h);
}
///
- /// Notifies that a new judgement has occurred for this .
+ /// Applies the of this , notifying responders such as
+ /// the of the .
///
- /// The .
- protected void AddJudgement(Judgement judgement)
+ /// The callback that applies changes to the .
+ protected void ApplyResult(Action application)
{
+ application?.Invoke(Result);
+
+ if (!Result.HasResult)
+ throw new InvalidOperationException($"{GetType().ReadableName()} applied a {nameof(JudgementResult)} but did not update {nameof(JudgementResult.Type)}.");
+
judgementOccurred = true;
// Ensure that the judgement is given a valid time offset, because this may not get set by the caller
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
- judgement.TimeOffset = Time.Current - endTime;
+ Result.TimeOffset = Time.Current - endTime;
- judgements.Add(judgement);
-
- switch (judgement.Result)
+ switch (Result.Type)
{
case HitResult.None:
break;
@@ -189,15 +211,15 @@ namespace osu.Game.Rulesets.Objects.Drawables
break;
}
- OnJudgement?.Invoke(this, judgement);
+ OnNewResult?.Invoke(this, Result);
}
///
- /// Processes this , checking if any judgements have occurred.
+ /// Processes this , checking if a scoring result has occurred.
///
/// Whether the user triggered this process.
- /// Whether a judgement has occurred from this or any nested s.
- protected bool UpdateJudgement(bool userTriggered)
+ /// Whether a scoring result has occurred from this or any nested .
+ protected bool UpdateResult(bool userTriggered)
{
judgementOccurred = false;
@@ -205,27 +227,35 @@ namespace osu.Game.Rulesets.Objects.Drawables
return false;
foreach (var d in NestedHitObjects)
- judgementOccurred |= d.UpdateJudgement(userTriggered);
+ judgementOccurred |= d.UpdateResult(userTriggered);
- if (!ProvidesJudgement || judgementFinalized || judgementOccurred)
+ if (judgementOccurred || Judged)
return judgementOccurred;
var endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
- CheckForJudgements(userTriggered, Time.Current - endTime);
+ CheckForResult(userTriggered, Time.Current - endTime);
return judgementOccurred;
}
///
- /// Checks if any judgements have occurred for this . This method must construct
- /// all s and notify of them through .
+ /// Checks if a scoring result has occurred for this .
///
+ ///
+ /// If a scoring result has occurred, this method must invoke to update the result and notify responders.
+ ///
/// Whether the user triggered this check.
- /// The offset from the end time at which this check occurred. A > 0
- /// implies that this check occurred after the end time of .
- protected virtual void CheckForJudgements(bool userTriggered, double timeOffset)
+ /// The offset from the end time of the at which this check occurred.
+ /// A > 0 implies that this check occurred after the end time of the .
+ protected virtual void CheckForResult(bool userTriggered, double timeOffset)
{
}
+
+ ///
+ /// Creates the that represents the scoring result for this .
+ ///
+ /// The that provides the scoring information.
+ protected virtual JudgementResult CreateResult(Judgement judgement) => new JudgementResult(judgement);
}
public abstract class DrawableHitObject : DrawableHitObject
diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs
index 15c24e2975..beb9620f78 100644
--- a/osu.Game/Rulesets/Objects/HitObject.cs
+++ b/osu.Game/Rulesets/Objects/HitObject.cs
@@ -9,6 +9,7 @@ using osu.Framework.Lists;
using osu.Game.Audio;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
+using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Objects
@@ -105,6 +106,12 @@ namespace osu.Game.Rulesets.Objects
protected void AddNested(HitObject hitObject) => nestedHitObjects.Value.Add(hitObject);
+ ///
+ /// Creates the that represents the scoring information for this .
+ /// May be null.
+ ///
+ public virtual Judgement CreateJudgement() => null;
+
///
/// Creates the for this .
/// This can be null to indicate that the has no .
diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs
index 50035ea116..0573a08361 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs
@@ -13,5 +13,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
public float X { get; set; }
public bool NewCombo { get; set; }
+
+ public int ComboOffset { get; set; }
}
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs
index c7451dc978..fb4cde479b 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs
@@ -13,21 +13,28 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
///
public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
{
- protected override HitObject CreateHit(Vector2 position, bool newCombo)
+ public ConvertHitObjectParser(double offset, int formatVersion)
+ : base(offset, formatVersion)
+ {
+ }
+
+ protected override HitObject CreateHit(Vector2 position, bool newCombo, int comboOffset)
{
return new ConvertHit
{
X = position.X,
NewCombo = newCombo,
+ ComboOffset = comboOffset
};
}
- protected override HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples)
+ protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples)
{
return new ConvertSlider
{
X = position.X,
- NewCombo = newCombo,
+ NewCombo = FirstObject || newCombo,
+ ComboOffset = comboOffset,
ControlPoints = controlPoints,
Distance = length,
CurveType = curveType,
@@ -36,15 +43,17 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
};
}
- protected override HitObject CreateSpinner(Vector2 position, double endTime)
+ protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double endTime)
{
return new ConvertSpinner
{
- EndTime = endTime
+ EndTime = endTime,
+ NewCombo = FirstObject || newCombo,
+ ComboOffset = comboOffset
};
}
- protected override HitObject CreateHold(Vector2 position, bool newCombo, double endTime)
+ protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime)
{
return null;
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs
index 73e277a125..a187caaa26 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs
@@ -13,5 +13,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
public float X { get; set; }
public bool NewCombo { get; set; }
+
+ public int ComboOffset { get; set; }
}
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs
index 9dfe12f25e..db79ca60f1 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs
@@ -8,10 +8,14 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
///
/// Legacy osu!catch Spinner-type, used for parsing Beatmaps.
///
- internal sealed class ConvertSpinner : HitObject, IHasEndTime
+ internal sealed class ConvertSpinner : HitObject, IHasEndTime, IHasCombo
{
public double EndTime { get; set; }
public double Duration => EndTime - StartTime;
+
+ public bool NewCombo { get; set; }
+
+ public int ComboOffset { get; set; }
}
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
index c48060bfa9..8236333a3f 100644
--- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs
@@ -19,12 +19,25 @@ namespace osu.Game.Rulesets.Objects.Legacy
///
public abstract class ConvertHitObjectParser : HitObjectParser
{
- public override HitObject Parse(string text)
+ ///
+ /// The offset to apply to all time values.
+ ///
+ protected readonly double Offset;
+
+ ///
+ /// The beatmap version.
+ ///
+ protected readonly int FormatVersion;
+
+ protected bool FirstObject { get; private set; } = true;
+
+ protected ConvertHitObjectParser(double offset, int formatVersion)
{
- return Parse(text, 0);
+ Offset = offset;
+ FormatVersion = formatVersion;
}
- public HitObject Parse(string text, double offset)
+ public override HitObject Parse(string text)
{
try
{
@@ -32,7 +45,11 @@ namespace osu.Game.Rulesets.Objects.Legacy
Vector2 pos = new Vector2((int)Convert.ToSingle(split[0], CultureInfo.InvariantCulture), (int)Convert.ToSingle(split[1], CultureInfo.InvariantCulture));
- ConvertHitObjectType type = (ConvertHitObjectType)int.Parse(split[3]) & ~ConvertHitObjectType.ColourHax;
+ ConvertHitObjectType type = (ConvertHitObjectType)int.Parse(split[3]);
+
+ int comboOffset = (int)(type & ConvertHitObjectType.ComboOffset) >> 4;
+ type &= ~ConvertHitObjectType.ComboOffset;
+
bool combo = type.HasFlag(ConvertHitObjectType.NewCombo);
type &= ~ConvertHitObjectType.NewCombo;
@@ -43,7 +60,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
if (type.HasFlag(ConvertHitObjectType.Circle))
{
- result = CreateHit(pos, combo);
+ result = CreateHit(pos, combo, comboOffset);
if (split.Length > 5)
readCustomSampleBanks(split[5], bankInfo);
@@ -148,11 +165,11 @@ namespace osu.Game.Rulesets.Objects.Legacy
for (int i = 0; i < nodes; i++)
nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i]));
- result = CreateSlider(pos, combo, points, length, curveType, repeatCount, nodeSamples);
+ result = CreateSlider(pos, combo, comboOffset, points, length, curveType, repeatCount, nodeSamples);
}
else if (type.HasFlag(ConvertHitObjectType.Spinner))
{
- result = CreateSpinner(new Vector2(512, 384) / 2, Convert.ToDouble(split[5], CultureInfo.InvariantCulture) + offset);
+ result = CreateSpinner(new Vector2(512, 384) / 2, combo, comboOffset, Convert.ToDouble(split[5], CultureInfo.InvariantCulture) + Offset);
if (split.Length > 6)
readCustomSampleBanks(split[6], bankInfo);
@@ -170,15 +187,17 @@ namespace osu.Game.Rulesets.Objects.Legacy
readCustomSampleBanks(string.Join(":", ss.Skip(1)), bankInfo);
}
- result = CreateHold(pos, combo, endTime + offset);
+ result = CreateHold(pos, combo, comboOffset, endTime + Offset);
}
if (result == null)
throw new InvalidOperationException($@"Unknown hit object type {type}.");
- result.StartTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture) + offset;
+ result.StartTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture) + Offset;
result.Samples = convertSoundType(soundType, bankInfo);
+ FirstObject = false;
+
return result;
}
catch (FormatException)
@@ -221,37 +240,42 @@ namespace osu.Game.Rulesets.Objects.Legacy
///
/// The position of the hit object.
/// Whether the hit object creates a new combo.
+ /// When starting a new combo, the offset of the new combo relative to the current one.
/// The hit object.
- protected abstract HitObject CreateHit(Vector2 position, bool newCombo);
+ protected abstract HitObject CreateHit(Vector2 position, bool newCombo, int comboOffset);
///
/// Creats a legacy Slider-type hit object.
///
/// The position of the hit object.
/// Whether the hit object creates a new combo.
+ /// When starting a new combo, the offset of the new combo relative to the current one.
/// The slider control points.
/// The slider length.
/// The slider curve type.
/// The slider repeat count.
/// The samples to be played when the repeat nodes are hit. This includes the head and tail of the slider.
/// The hit object.
- protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples);
+ protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples);
///
/// Creates a legacy Spinner-type hit object.
///
/// The position of the hit object.
+ /// Whether the hit object creates a new combo.
+ /// When starting a new combo, the offset of the new combo relative to the current one.
/// The spinner end time.
/// The hit object.
- protected abstract HitObject CreateSpinner(Vector2 position, double endTime);
+ protected abstract HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double endTime);
///
/// Creates a legacy Hold-type hit object.
///
/// The position of the hit object.
/// Whether the hit object creates a new combo.
+ /// When starting a new combo, the offset of the new combo relative to the current one.
/// The hold end time.
- protected abstract HitObject CreateHold(Vector2 position, bool newCombo, double endTime);
+ protected abstract HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime);
private List convertSoundType(LegacySoundType type, SampleBankInfo bankInfo)
{
diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectType.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectType.cs
index c0626c3e56..fa47e56de7 100644
--- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectType.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectType.cs
@@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
Slider = 1 << 1,
NewCombo = 1 << 2,
Spinner = 1 << 3,
- ColourHax = 112,
+ ComboOffset = 1 << 4 | 1 << 5 | 1 << 6,
Hold = 1 << 7
}
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs
index ea4e7f6907..cbc8d2d4df 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHit.cs
@@ -8,12 +8,10 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
///
/// Legacy osu!mania Hit-type, used for parsing Beatmaps.
///
- internal sealed class ConvertHit : HitObject, IHasXPosition, IHasCombo
+ internal sealed class ConvertHit : HitObject, IHasXPosition
{
public float X { get; set; }
- public bool NewCombo { get; set; }
-
protected override HitWindows CreateHitWindows() => null;
}
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs
index 99ba1304e8..6f59965e18 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertHitObjectParser.cs
@@ -13,21 +13,24 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
///
public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
{
- protected override HitObject CreateHit(Vector2 position, bool newCombo)
+ public ConvertHitObjectParser(double offset, int formatVersion)
+ : base(offset, formatVersion)
+ {
+ }
+
+ protected override HitObject CreateHit(Vector2 position, bool newCombo, int comboOffset)
{
return new ConvertHit
{
- X = position.X,
- NewCombo = newCombo,
+ X = position.X
};
}
- protected override HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples)
+ protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples)
{
return new ConvertSlider
{
X = position.X,
- NewCombo = newCombo,
ControlPoints = controlPoints,
Distance = length,
CurveType = curveType,
@@ -36,7 +39,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
};
}
- protected override HitObject CreateSpinner(Vector2 position, double endTime)
+ protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double endTime)
{
return new ConvertSpinner
{
@@ -45,7 +48,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
};
}
- protected override HitObject CreateHold(Vector2 position, bool newCombo, double endTime)
+ protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime)
{
return new ConvertHold
{
diff --git a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs
index a8d7b23df1..e1572889a3 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Mania/ConvertSlider.cs
@@ -8,12 +8,10 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
///
/// Legacy osu!mania Slider-type, used for parsing Beatmaps.
///
- internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasXPosition, IHasCombo
+ internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasXPosition
{
public float X { get; set; }
- public bool NewCombo { get; set; }
-
protected override HitWindows CreateHitWindows() => null;
}
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs
index f015272b2c..0062e83a28 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs
@@ -19,6 +19,8 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
public bool NewCombo { get; set; }
+ public int ComboOffset { get; set; }
+
protected override HitWindows CreateHitWindows() => null;
}
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs
index 801e4ea449..0823653830 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs
@@ -14,21 +14,28 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
///
public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
{
- protected override HitObject CreateHit(Vector2 position, bool newCombo)
+ public ConvertHitObjectParser(double offset, int formatVersion)
+ : base(offset, formatVersion)
+ {
+ }
+
+ protected override HitObject CreateHit(Vector2 position, bool newCombo, int comboOffset)
{
return new ConvertHit
{
Position = position,
- NewCombo = newCombo,
+ NewCombo = FirstObject || newCombo,
+ ComboOffset = comboOffset
};
}
- protected override HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples)
+ protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples)
{
return new ConvertSlider
{
Position = position,
- NewCombo = newCombo,
+ NewCombo = FirstObject || newCombo,
+ ComboOffset = comboOffset,
ControlPoints = controlPoints,
Distance = Math.Max(0, length),
CurveType = curveType,
@@ -37,16 +44,18 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
};
}
- protected override HitObject CreateSpinner(Vector2 position, double endTime)
+ protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double endTime)
{
return new ConvertSpinner
{
Position = position,
- EndTime = endTime
+ EndTime = endTime,
+ NewCombo = FormatVersion <= 8 || FirstObject || newCombo,
+ ComboOffset = comboOffset
};
}
- protected override HitObject CreateHold(Vector2 position, bool newCombo, double endTime)
+ protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime)
{
return null;
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs
index ec5a002bbb..45f7bc9e67 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs
@@ -19,6 +19,8 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
public bool NewCombo { get; set; }
+ public int ComboOffset { get; set; }
+
protected override HitWindows CreateHitWindows() => null;
}
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs
index 0141785f31..3b349d9704 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs
@@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
///
/// Legacy osu! Spinner-type, used for parsing Beatmaps.
///
- internal sealed class ConvertSpinner : HitObject, IHasEndTime, IHasPosition
+ internal sealed class ConvertSpinner : HitObject, IHasEndTime, IHasPosition, IHasCombo
{
public double EndTime { get; set; }
@@ -22,5 +22,9 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
public float Y => Position.Y;
protected override HitWindows CreateHitWindows() => null;
+
+ public bool NewCombo { get; set; }
+
+ public int ComboOffset { get; set; }
}
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs
index 5e9786c84a..66e504bf22 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHit.cs
@@ -1,17 +1,13 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using osu.Game.Rulesets.Objects.Types;
-
namespace osu.Game.Rulesets.Objects.Legacy.Taiko
{
///
/// Legacy osu!taiko Hit-type, used for parsing Beatmaps.
///
- internal sealed class ConvertHit : HitObject, IHasCombo
+ internal sealed class ConvertHit : HitObject
{
- public bool NewCombo { get; set; }
-
protected override HitWindows CreateHitWindows() => null;
}
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs
index 03b1a3187a..e5904825c2 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertHitObjectParser.cs
@@ -13,19 +13,20 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
///
public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser
{
- protected override HitObject CreateHit(Vector2 position, bool newCombo)
+ public ConvertHitObjectParser(double offset, int formatVersion)
+ : base(offset, formatVersion)
{
- return new ConvertHit
- {
- NewCombo = newCombo,
- };
}
- protected override HitObject CreateSlider(Vector2 position, bool newCombo, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples)
+ protected override HitObject CreateHit(Vector2 position, bool newCombo, int comboOffset)
+ {
+ return new ConvertHit();
+ }
+
+ protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List controlPoints, double length, CurveType curveType, int repeatCount, List> repeatSamples)
{
return new ConvertSlider
{
- NewCombo = newCombo,
ControlPoints = controlPoints,
Distance = length,
CurveType = curveType,
@@ -34,7 +35,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
};
}
- protected override HitObject CreateSpinner(Vector2 position, double endTime)
+ protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double endTime)
{
return new ConvertSpinner
{
@@ -42,7 +43,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
};
}
- protected override HitObject CreateHold(Vector2 position, bool newCombo, double endTime)
+ protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double endTime)
{
return null;
}
diff --git a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs
index 8a9a0db0a7..11c0a2baae 100644
--- a/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs
+++ b/osu.Game/Rulesets/Objects/Legacy/Taiko/ConvertSlider.cs
@@ -1,17 +1,13 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using osu.Game.Rulesets.Objects.Types;
-
namespace osu.Game.Rulesets.Objects.Legacy.Taiko
{
///
/// Legacy osu!taiko Slider-type, used for parsing Beatmaps.
///
- internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasCombo
+ internal sealed class ConvertSlider : Legacy.ConvertSlider
{
- public bool NewCombo { get; set; }
-
protected override HitWindows CreateHitWindows() => null;
}
}
diff --git a/osu.Game/Rulesets/Objects/Types/IHasCombo.cs b/osu.Game/Rulesets/Objects/Types/IHasCombo.cs
index cb8b6f495a..95f1a1cb3d 100644
--- a/osu.Game/Rulesets/Objects/Types/IHasCombo.cs
+++ b/osu.Game/Rulesets/Objects/Types/IHasCombo.cs
@@ -12,5 +12,10 @@ namespace osu.Game.Rulesets.Objects.Types
/// Whether the HitObject starts a new combo.
///
bool NewCombo { get; }
+
+ ///
+ /// When starting a new combo, the offset of the new combo relative to the current one.
+ ///
+ int ComboOffset { get; }
}
}
diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
index 9e8ea0f7c2..b0cea7009e 100644
--- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
+++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
@@ -4,6 +4,7 @@
using System;
using System.Diagnostics;
using osu.Framework.Configuration;
+using osu.Framework.Extensions.TypeExtensions;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
@@ -28,7 +29,7 @@ namespace osu.Game.Rulesets.Scoring
///
/// Invoked when a new judgement has occurred. This occurs after the judgement has been processed by the .
///
- public event Action NewJudgement;
+ public event Action NewJudgement;
///
/// Additional conditions on top of that cause a failing state.
@@ -144,9 +145,10 @@ namespace osu.Game.Rulesets.Scoring
/// Notifies subscribers of that a new judgement has occurred.
///
/// The judgement to notify subscribers of.
- protected void NotifyNewJudgement(Judgement judgement)
+ /// The judgement scoring result to notify subscribers of.
+ protected void NotifyNewJudgement(JudgementResult result)
{
- NewJudgement?.Invoke(judgement);
+ NewJudgement?.Invoke(result);
if (HasCompleted)
AllJudged?.Invoke();
@@ -194,9 +196,10 @@ namespace osu.Game.Rulesets.Scoring
{
Debug.Assert(base_portion + combo_portion == 1.0);
- rulesetContainer.OnJudgement += AddJudgement;
- rulesetContainer.OnJudgementRemoved += RemoveJudgement;
+ rulesetContainer.OnNewResult += applyResult;
+ rulesetContainer.OnRevertResult += revertResult;
+ ApplyBeatmap(rulesetContainer.Beatmap);
SimulateAutoplay(rulesetContainer.Beatmap);
Reset(true);
@@ -210,46 +213,80 @@ namespace osu.Game.Rulesets.Scoring
}
///
- /// Simulates an autoplay of s that will be judged by this
- /// by adding s for each in the .
- ///
- /// This is required for to work, otherwise will be used.
- ///
+ /// Applies any properties of the which affect scoring to this .
///
- /// The containing the s that will be judged by this .
- protected virtual void SimulateAutoplay(Beatmap beatmap) { }
+ /// The to read properties from.
+ protected virtual void ApplyBeatmap(Beatmap beatmap)
+ {
+ }
///
- /// Adds a judgement to this ScoreProcessor.
+ /// Simulates an autoplay of the to determine scoring values.
///
- /// The judgement to add.
- protected void AddJudgement(Judgement judgement)
+ /// This provided temporarily. DO NOT USE.
+ /// The to simulate.
+ protected virtual void SimulateAutoplay(Beatmap beatmap)
{
- OnNewJudgement(judgement);
+ foreach (var obj in beatmap.HitObjects)
+ simulate(obj);
+
+ void simulate(HitObject obj)
+ {
+ foreach (var nested in obj.NestedHitObjects)
+ simulate(nested);
+
+ var judgement = obj.CreateJudgement();
+ if (judgement == null)
+ return;
+
+ var result = CreateResult(judgement);
+ if (result == null)
+ throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}.");
+
+ result.Type = judgement.MaxResult;
+
+ applyResult(result);
+ }
+ }
+
+ ///
+ /// Applies the score change of a to this .
+ ///
+ /// The to apply.
+ private void applyResult(JudgementResult result)
+ {
+ ApplyResult(result);
updateScore();
UpdateFailed();
- NotifyNewJudgement(judgement);
+ NotifyNewJudgement(result);
}
- protected void RemoveJudgement(Judgement judgement)
+ ///
+ /// Reverts the score change of a that was applied to this .
+ ///
+ /// The judgement to remove.
+ /// The judgement scoring result.
+ private void revertResult(JudgementResult result)
{
- OnJudgementRemoved(judgement);
+ RevertResult(result);
updateScore();
}
///
- /// Applies a judgement.
+ /// Applies the score change of a to this .
///
- /// The judgement to apply/
- protected virtual void OnNewJudgement(Judgement judgement)
+ /// The to apply.
+ protected virtual void ApplyResult(JudgementResult result)
{
- judgement.ComboAtJudgement = Combo;
- judgement.HighestComboAtJudgement = HighestCombo;
+ result.ComboAtJudgement = Combo;
+ result.HighestComboAtJudgement = HighestCombo;
- if (judgement.AffectsCombo)
+ JudgedHits++;
+
+ if (result.Judgement.AffectsCombo)
{
- switch (judgement.Result)
+ switch (result.Type)
{
case HitResult.None:
break;
@@ -260,43 +297,41 @@ namespace osu.Game.Rulesets.Scoring
Combo.Value++;
break;
}
-
- JudgedHits++;
}
- if (judgement.IsBonus)
+ if (result.Judgement.IsBonus)
{
- if (judgement.IsHit)
- bonusScore += judgement.NumericResult;
+ if (result.IsHit)
+ bonusScore += result.Judgement.NumericResultFor(result);
}
else
{
- baseScore += judgement.NumericResult;
- rollingMaxBaseScore += judgement.MaxNumericResult;
+ baseScore += result.Judgement.NumericResultFor(result);
+ rollingMaxBaseScore += result.Judgement.MaxNumericResult;
}
}
///
- /// Removes a judgement. This should reverse everything in .
+ /// Reverts the score change of a that was applied to this .
///
/// The judgement to remove.
- protected virtual void OnJudgementRemoved(Judgement judgement)
+ /// The judgement scoring result.
+ protected virtual void RevertResult(JudgementResult result)
{
- Combo.Value = judgement.ComboAtJudgement;
- HighestCombo.Value = judgement.HighestComboAtJudgement;
+ Combo.Value = result.ComboAtJudgement;
+ HighestCombo.Value = result.HighestComboAtJudgement;
- if (judgement.AffectsCombo)
- JudgedHits--;
+ JudgedHits--;
- if (judgement.IsBonus)
+ if (result.Judgement.IsBonus)
{
- if (judgement.IsHit)
- bonusScore -= judgement.NumericResult;
+ if (result.IsHit)
+ bonusScore -= result.Judgement.NumericResultFor(result);
}
else
{
- baseScore -= judgement.NumericResult;
- rollingMaxBaseScore -= judgement.MaxNumericResult;
+ baseScore -= result.Judgement.NumericResultFor(result);
+ rollingMaxBaseScore -= result.Judgement.MaxNumericResult;
}
}
@@ -333,6 +368,12 @@ namespace osu.Game.Rulesets.Scoring
rollingMaxBaseScore = 0;
bonusScore = 0;
}
+
+ ///
+ /// Creates the that represents the scoring result for a .
+ ///
+ /// The that provides the scoring information.
+ protected virtual JudgementResult CreateResult(Judgement judgement) => new JudgementResult(judgement);
}
public enum ScoringMode
diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs
index f7b3736e74..7a7ada3435 100644
--- a/osu.Game/Rulesets/UI/Playfield.cs
+++ b/osu.Game/Rulesets/UI/Playfield.cs
@@ -1,10 +1,13 @@
// 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.Linq;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Allocation;
+using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Configuration;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
@@ -14,15 +17,21 @@ namespace osu.Game.Rulesets.UI
public abstract class Playfield : ScalableContainer
{
///
- /// The HitObjects contained in this Playfield.
+ /// The contained in this Playfield.
///
public HitObjectContainer HitObjects { get; private set; }
///
- /// All the s nested inside this playfield.
+ /// All the s contained in this and all .
///
- public IReadOnlyList NestedPlayfields => nestedPlayfields;
- private List nestedPlayfields;
+ public IEnumerable AllHitObjects => HitObjects?.Objects.Concat(NestedPlayfields.SelectMany(p => p.AllHitObjects)) ?? Enumerable.Empty();
+
+ ///
+ /// All s nested inside this .
+ ///
+ public IEnumerable NestedPlayfields => nestedPlayfields.IsValueCreated ? nestedPlayfields.Value : Enumerable.Empty();
+
+ private readonly Lazy> nestedPlayfields = new Lazy>();
///
/// Whether judgements should be displayed by this and and all nested s.
@@ -59,7 +68,7 @@ namespace osu.Game.Rulesets.UI
///
/// Performs post-processing tasks (if any) after all DrawableHitObjects are loaded into this Playfield.
///
- public virtual void PostProcess() => nestedPlayfields?.ForEach(p => p.PostProcess());
+ public virtual void PostProcess() => NestedPlayfields.ForEach(p => p.PostProcess());
///
/// Adds a DrawableHitObject to this Playfield.
@@ -80,12 +89,8 @@ namespace osu.Game.Rulesets.UI
/// The to add.
protected void AddNested(Playfield otherPlayfield)
{
- if (nestedPlayfields == null)
- nestedPlayfields = new List();
-
- nestedPlayfields.Add(otherPlayfield);
-
otherPlayfield.DisplayJudgements.BindTo(DisplayJudgements);
+ nestedPlayfields.Value.Add(otherPlayfield);
}
///
diff --git a/osu.Game/Rulesets/UI/RulesetContainer.cs b/osu.Game/Rulesets/UI/RulesetContainer.cs
index ee34e2df04..64ee680d45 100644
--- a/osu.Game/Rulesets/UI/RulesetContainer.cs
+++ b/osu.Game/Rulesets/UI/RulesetContainer.cs
@@ -182,8 +182,15 @@ namespace osu.Game.Rulesets.UI
public abstract class RulesetContainer : RulesetContainer
where TObject : HitObject
{
- public event Action OnJudgement;
- public event Action OnJudgementRemoved;
+ ///
+ /// Invoked when a has been applied by a .
+ ///
+ public event Action OnNewResult;
+
+ ///
+ /// Invoked when a is being reverted by a .
+ ///
+ public event Action OnRevertResult;
///
/// The Beatmap
@@ -290,8 +297,8 @@ namespace osu.Game.Rulesets.UI
if (drawableObject == null)
continue;
- drawableObject.OnJudgement += (d, j) => OnJudgement?.Invoke(j);
- drawableObject.OnJudgementRemoved += (d, j) => OnJudgementRemoved?.Invoke(j);
+ drawableObject.OnNewResult += (_, r) => OnNewResult?.Invoke(r);
+ drawableObject.OnRevertResult += (_, r) => OnRevertResult?.Invoke(r);
Playfield.Add(drawableObject);
}
diff --git a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs
index 03ac8e91f0..d212bbe7dd 100644
--- a/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.cs
+++ b/osu.Game/Screens/Edit/Screens/Compose/Layers/HitObjectMaskLayer.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 System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -9,30 +8,24 @@ using osu.Framework.Input.EventArgs;
using osu.Framework.Input.States;
using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Rulesets.UI;
namespace osu.Game.Screens.Edit.Screens.Compose.Layers
{
public class HitObjectMaskLayer : CompositeDrawable
{
- private readonly Playfield playfield;
- private readonly HitObjectComposer composer;
-
private MaskContainer maskContainer;
+ private HitObjectComposer composer;
- public HitObjectMaskLayer(Playfield playfield, HitObjectComposer composer)
+ public HitObjectMaskLayer()
{
- // we need the playfield as HitObjects may not be initialised until its BDL.
- this.playfield = playfield;
-
- this.composer = composer;
-
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
- private void load()
+ private void load(HitObjectComposer composer)
{
+ this.composer = composer;
+
maskContainer = new MaskContainer();
var maskSelection = composer.CreateMaskSelection();
@@ -55,7 +48,7 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers
dragLayer.CreateProxy()
};
- foreach (var obj in playfield.HitObjects.Objects)
+ foreach (var obj in composer.HitObjects)
addMask(obj);
}
@@ -77,18 +70,5 @@ namespace osu.Game.Screens.Edit.Screens.Compose.Layers
maskContainer.Add(mask);
}
-
- ///
- /// Removes the mask for a .
- ///
- /// The to remove the mask for.
- private void removeMask(DrawableHitObject hitObject)
- {
- var mask = maskContainer.FirstOrDefault(h => h.HitObject == hitObject);
- if (mask == null)
- return;
-
- maskContainer.Remove(mask);
- }
}
}
diff --git a/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs b/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs
index ab446550a6..b551b1f7a6 100644
--- a/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs
+++ b/osu.Game/Screens/Play/HUD/StandardHealthDisplay.cs
@@ -92,9 +92,9 @@ namespace osu.Game.Screens.Play.HUD
};
}
- public void Flash(Judgement judgement)
+ public void Flash(JudgementResult result)
{
- if (judgement.Result == HitResult.Miss)
+ if (result.Type == HitResult.Miss)
return;
fill.FadeEdgeEffectTo(Math.Min(1, fill.EdgeEffect.Colour.Linear.A + (1f - base_glow_opacity) / glow_max_hits), 50, Easing.OutQuint)
diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs
index cc649960ea..2e23bb16f0 100644
--- a/osu.Game/Screens/Play/Player.cs
+++ b/osu.Game/Screens/Play/Player.cs
@@ -213,7 +213,7 @@ namespace osu.Game.Screens.Play
{
if (!IsCurrentScreen) return;
- pauseContainer.Hide();
+ fadeOut(true);
Restart();
},
}
@@ -364,16 +364,10 @@ namespace osu.Game.Screens.Play
return true;
}
- private void fadeOut()
+ private void fadeOut(bool instant = false)
{
- const float fade_out_duration = 250;
-
- RulesetContainer?.FadeOut(fade_out_duration);
- Content.FadeOut(fade_out_duration);
-
- hudOverlay?.ScaleTo(0.7f, fade_out_duration * 3, Easing.In);
-
- Background?.FadeTo(1f, fade_out_duration);
+ float fadeOutDuration = instant ? 0 : 250;
+ Content.FadeOut(fadeOutDuration);
}
protected override bool OnScroll(InputState state) => mouseWheelDisabled.Value && !pauseContainer.IsPaused;
diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs
index e9aa012cd7..fd4322c268 100644
--- a/osu.Game/Screens/Play/PlayerLoader.cs
+++ b/osu.Game/Screens/Play/PlayerLoader.cs
@@ -14,9 +14,11 @@ using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
+using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Menu;
using osu.Game.Screens.Play.PlayerSettings;
using OpenTK;
+using OpenTK.Graphics;
namespace osu.Game.Screens.Play
{
@@ -69,21 +71,25 @@ namespace osu.Game.Screens.Play
}
});
- loadTask = LoadComponentAsync(player);
+ loadTask = LoadComponentAsync(player, playerLoaded);
}
+ private void playerLoaded(Player player) => info.Loading = false;
+
protected override void OnResuming(Screen last)
{
base.OnResuming(last);
contentIn();
+ info.Loading = true;
+
//we will only be resumed if the player has requested a re-run (see ValidForResume setting above)
loadTask = LoadComponentAsync(player = new Player
{
RestartCount = player.RestartCount + 1,
RestartRequested = player.RestartRequested,
- });
+ }, playerLoaded);
this.Delay(400).Schedule(pushWhenLoaded);
}
@@ -258,6 +264,25 @@ namespace osu.Game.Screens.Play
}
private readonly WorkingBeatmap beatmap;
+ private LoadingAnimation loading;
+ private Sprite backgroundSprite;
+
+ public bool Loading
+ {
+ set
+ {
+ if (value)
+ {
+ loading.Show();
+ backgroundSprite.FadeColour(OsuColour.Gray(0.5f), 400, Easing.OutQuint);
+ }
+ else
+ {
+ loading.Hide();
+ backgroundSprite.FadeColour(Color4.White, 400, Easing.OutQuint);
+ }
+ }
+ }
public BeatmapMetadataDisplay(WorkingBeatmap beatmap)
{
@@ -304,9 +329,9 @@ namespace osu.Game.Screens.Play
Anchor = Anchor.TopCentre,
CornerRadius = 10,
Masking = true,
- Children = new[]
+ Children = new Drawable[]
{
- new Sprite
+ backgroundSprite = new Sprite
{
RelativeSizeAxes = Axes.Both,
Texture = beatmap?.Background,
@@ -314,6 +339,7 @@ namespace osu.Game.Screens.Play
Anchor = Anchor.Centre,
FillMode = FillMode.Fill,
},
+ loading = new LoadingAnimation { Scale = new Vector2(1.3f) }
}
},
new OsuSpriteText
@@ -341,6 +367,8 @@ namespace osu.Game.Screens.Play
},
}
};
+
+ Loading = true;
}
}
}
diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs
index a346911ca2..e914eb365e 100644
--- a/osu.Game/Screens/Select/PlaySongSelect.cs
+++ b/osu.Game/Screens/Select/PlaySongSelect.cs
@@ -50,13 +50,14 @@ namespace osu.Game.Screens.Select
private SampleChannel sampleConfirm;
- public readonly Bindable> SelectedMods = new Bindable>(new List());
+ [Cached]
+ [Cached(Type = typeof(IBindable>))]
+ private readonly Bindable> selectedMods = new Bindable>(new Mod[] { });
[BackgroundDependencyLoader(true)]
- private void load(OsuColour colours, AudioManager audio, BeatmapManager beatmaps, DialogOverlay dialogOverlay, OsuGame osu)
+ private void load(OsuColour colours, AudioManager audio, BeatmapManager beatmaps, DialogOverlay dialogOverlay, Bindable> selectedMods)
{
- if (osu != null) SelectedMods.BindTo(osu.SelectedMods);
- modSelect.SelectedMods.BindTo(SelectedMods);
+ if (selectedMods != null) this.selectedMods.BindTo(selectedMods);
sampleConfirm = audio.Sample.Get(@"SongSelect/confirm-selection");
@@ -84,7 +85,7 @@ namespace osu.Game.Screens.Select
protected override void UpdateBeatmap(WorkingBeatmap beatmap)
{
- beatmap.Mods.BindTo(SelectedMods);
+ beatmap.Mods.BindTo(selectedMods);
base.UpdateBeatmap(beatmap);
@@ -131,7 +132,7 @@ namespace osu.Game.Screens.Select
if (Beatmap.Value.Track != null)
Beatmap.Value.Track.Looping = false;
- SelectedMods.UnbindAll();
+ selectedMods.UnbindAll();
Beatmap.Value.Mods.Value = new Mod[] { };
return false;
@@ -147,10 +148,10 @@ namespace osu.Game.Screens.Select
var auto = Ruleset.Value.CreateInstance().GetAutoplayMod();
var autoType = auto.GetType();
- var mods = modSelect.SelectedMods.Value;
+ var mods = selectedMods.Value;
if (mods.All(m => m.GetType() != autoType))
{
- modSelect.SelectedMods.Value = mods.Append(auto);
+ selectedMods.Value = mods.Append(auto);
removeAutoModOnResume = true;
}
}
diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs
index 1bcd65e30b..54143bef8a 100644
--- a/osu.Game/Screens/Select/SongSelect.cs
+++ b/osu.Game/Screens/Select/SongSelect.cs
@@ -314,13 +314,13 @@ namespace osu.Game.Screens.Select
{
Logger.Log($"updating selection with beatmap:{beatmap?.ID.ToString() ?? "null"} ruleset:{ruleset?.ID.ToString() ?? "null"}");
- WorkingBeatmap working = Beatmap.Value;
-
bool preview = false;
if (ruleset?.Equals(Ruleset.Value) == false)
{
Logger.Log($"ruleset changed from \"{Ruleset.Value}\" to \"{ruleset}\"");
+
+ Beatmap.Value.Mods.Value = Enumerable.Empty();
Ruleset.Value = ruleset;
// force a filter before attempting to change the beatmap.
@@ -340,7 +340,7 @@ namespace osu.Game.Screens.Select
Logger.Log($"beatmap changed from \"{Beatmap.Value.BeatmapInfo}\" to \"{beatmap}\"");
preview = beatmap?.BeatmapSetInfoID != Beatmap.Value?.BeatmapInfo.BeatmapSetInfoID;
- working = beatmaps.GetWorkingBeatmap(beatmap, Beatmap.Value);
+ Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmap, Beatmap.Value);
if (beatmap != null)
{
@@ -351,9 +351,6 @@ namespace osu.Game.Screens.Select
}
}
- working.Mods.Value = Enumerable.Empty();
- Beatmap.Value = working;
-
ensurePlayingSelected(preview);
UpdateBeatmap(Beatmap.Value);
}
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 89e80bd06b..eef586fd4c 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -18,7 +18,7 @@
-
+