diff --git a/osu.Android.props b/osu.Android.props
index 97812402a3..f56baf4e5f 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -52,6 +52,6 @@
-
+
diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs
index ee88edbea1..4b008d2734 100644
--- a/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs
+++ b/osu.Game.Rulesets.Catch/Mods/CatchModHidden.cs
@@ -17,9 +17,11 @@ namespace osu.Game.Rulesets.Catch.Mods
private const double fade_out_offset_multiplier = 0.6;
private const double fade_out_duration_multiplier = 0.44;
- protected override void ApplyHiddenState(DrawableHitObject drawable, ArmedState state)
+ protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state)
{
- if (!(drawable is DrawableCatchHitObject catchDrawable))
+ base.ApplyNormalVisibilityState(hitObject, state);
+
+ if (!(hitObject is DrawableCatchHitObject catchDrawable))
return;
if (catchDrawable.NestedHitObjects.Any())
diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs
index ac1f11e09f..7922510a49 100644
--- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs
+++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableCatchHitObject.cs
@@ -6,9 +6,8 @@ using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Game.Rulesets.Objects;
-using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Catch.UI;
+using osu.Game.Rulesets.Objects.Drawables;
using osuTK;
using osuTK.Graphics;
@@ -90,22 +89,17 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables
ApplyResult(r => r.Type = CheckPosition.Invoke(HitObject) ? r.Judgement.MaxResult : r.Judgement.MinResult);
}
- protected override void UpdateStateTransforms(ArmedState state)
+ protected override void UpdateHitStateTransforms(ArmedState state)
{
- var endTime = HitObject.GetEndTime();
-
- using (BeginAbsoluteSequence(endTime, true))
+ switch (state)
{
- switch (state)
- {
- case ArmedState.Miss:
- this.FadeOut(250).RotateTo(Rotation * 2, 250, Easing.Out);
- break;
+ case ArmedState.Miss:
+ this.FadeOut(250).RotateTo(Rotation * 2, 250, Easing.Out);
+ break;
- case ArmedState.Hit:
- this.FadeOut();
- break;
- }
+ case ArmedState.Hit:
+ this.FadeOut();
+ break;
}
}
}
diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs
index 2028cae9a5..afc08dcc96 100644
--- a/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs
+++ b/osu.Game.Rulesets.Mania/Edit/ManiaBeatSnapGrid.cs
@@ -204,10 +204,6 @@ namespace osu.Game.Rulesets.Mania.Edit
protected override void UpdateInitialTransforms()
{
// don't perform any fading – we are handling that ourselves.
- }
-
- protected override void UpdateStateTransforms(ArmedState state)
- {
LifetimeEnd = HitObject.StartTime + visible_range;
}
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs
index 08b5b75f9c..074cbf6bd6 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableBarLine.cs
@@ -1,10 +1,9 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using osuTK;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
-using osu.Game.Rulesets.Objects.Drawables;
+using osuTK;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Mania.Objects.Drawables
@@ -71,6 +70,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
}
- protected override void UpdateStateTransforms(ArmedState state) => this.FadeOut(150);
+ protected override void UpdateStartTimeStateTransforms() => this.FadeOut(150);
}
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
index f6d539c91b..d9d740c145 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs
@@ -229,12 +229,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
}
}
- protected override void UpdateStateTransforms(ArmedState state)
- {
- using (BeginDelayedSequence(HitObject.Duration, true))
- base.UpdateStateTransforms(state);
- }
-
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
if (Tail.AllJudged)
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs
index cd56b81e10..75dcf0e55e 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteHead.cs
@@ -1,8 +1,6 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using osu.Game.Rulesets.Objects.Drawables;
-
namespace osu.Game.Rulesets.Mania.Objects.Drawables
{
///
@@ -19,8 +17,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
public void UpdateResult() => base.UpdateResult(true);
- protected override void UpdateStateTransforms(ArmedState state)
+ protected override void UpdateInitialTransforms()
{
+ base.UpdateInitialTransforms();
+
// This hitobject should never expire, so this is just a safe maximum.
LifetimeEnd = LifetimeStart + 30000;
}
diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs
index 27960b3f3a..1550faee50 100644
--- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs
+++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableManiaHitObject.cs
@@ -118,7 +118,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables
Anchor = Origin = e.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre;
}
- protected override void UpdateStateTransforms(ArmedState state)
+ protected override void UpdateHitStateTransforms(ArmedState state)
{
switch (state)
{
diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs
index 49d7d9249c..a452f93676 100644
--- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs
+++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs
@@ -295,7 +295,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor
private void assertControlPointPosition(int index, Vector2 position) =>
AddAssert($"control point {index} at {position}", () => Precision.AlmostEquals(position, getSlider().Path.ControlPoints[index].Position.Value, 1));
- private Slider getSlider() => HitObjectContainer.Count > 0 ? (Slider)((DrawableSlider)HitObjectContainer[0]).HitObject : null;
+ private Slider getSlider() => HitObjectContainer.Count > 0 ? ((DrawableSlider)HitObjectContainer[0]).HitObject : null;
protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableSlider((Slider)hitObject);
protected override PlacementBlueprint CreateBlueprint() => new SliderPlacementBlueprint();
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneFollowPoints.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneFollowPoints.cs
index 87da7ef417..6c077eb214 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneFollowPoints.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneFollowPoints.cs
@@ -171,7 +171,7 @@ namespace osu.Game.Rulesets.Osu.Tests
}
hitObjectContainer.Add(drawableObject);
- followPointRenderer.AddFollowPoints(drawableObject);
+ followPointRenderer.AddFollowPoints(objects[i]);
}
});
}
@@ -180,10 +180,10 @@ namespace osu.Game.Rulesets.Osu.Tests
{
AddStep("remove hitobject", () =>
{
- var drawableObject = getFunc?.Invoke();
+ var drawableObject = getFunc.Invoke();
hitObjectContainer.Remove(drawableObject);
- followPointRenderer.RemoveFollowPoints(drawableObject);
+ followPointRenderer.RemoveFollowPoints(drawableObject.HitObject);
});
}
@@ -215,10 +215,10 @@ namespace osu.Game.Rulesets.Osu.Tests
DrawableOsuHitObject expectedStart = getObject(i);
DrawableOsuHitObject expectedEnd = i < hitObjectContainer.Count - 1 ? getObject(i + 1) : null;
- if (getGroup(i).Start != expectedStart)
+ if (getGroup(i).Start != expectedStart.HitObject)
throw new AssertionException($"Object {i} expected to be the start of group {i}.");
- if (getGroup(i).End != expectedEnd)
+ if (getGroup(i).End != expectedEnd?.HitObject)
throw new AssertionException($"Object {(expectedEnd == null ? "null" : i.ToString())} expected to be the end of group {i}.");
}
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
index c9e112f76d..c400e2f2ea 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs
@@ -112,10 +112,10 @@ namespace osu.Game.Rulesets.Osu.Tests
new HitSampleInfo { Name = HitSampleInfo.HIT_WHISTLE },
});
- AddAssert("head samples updated", () => assertSamples(((Slider)slider.HitObject).HeadCircle));
- AddAssert("tick samples not updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType().All(assertTickSamples));
- AddAssert("repeat samples updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType().All(assertSamples));
- AddAssert("tail has no samples", () => ((Slider)slider.HitObject).TailCircle.Samples.Count == 0);
+ AddAssert("head samples updated", () => assertSamples(slider.HitObject.HeadCircle));
+ AddAssert("tick samples not updated", () => slider.HitObject.NestedHitObjects.OfType().All(assertTickSamples));
+ AddAssert("repeat samples updated", () => slider.HitObject.NestedHitObjects.OfType().All(assertSamples));
+ AddAssert("tail has no samples", () => slider.HitObject.TailCircle.Samples.Count == 0);
static bool assertTickSamples(SliderTick tick) => tick.Samples.Single().Name == "slidertick";
@@ -136,7 +136,7 @@ namespace osu.Game.Rulesets.Osu.Tests
slider = (DrawableSlider)createSlider(repeats: 1);
for (int i = 0; i < 2; i++)
- ((Slider)slider.HitObject).NodeSamples.Add(new List { new HitSampleInfo { Name = HitSampleInfo.HIT_FINISH } });
+ slider.HitObject.NodeSamples.Add(new List { new HitSampleInfo { Name = HitSampleInfo.HIT_FINISH } });
Add(slider);
});
@@ -147,10 +147,10 @@ namespace osu.Game.Rulesets.Osu.Tests
new HitSampleInfo { Name = HitSampleInfo.HIT_WHISTLE },
});
- AddAssert("head samples not updated", () => assertSamples(((Slider)slider.HitObject).HeadCircle));
- AddAssert("tick samples not updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType().All(assertTickSamples));
- AddAssert("repeat samples not updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType().All(assertSamples));
- AddAssert("tail has no samples", () => ((Slider)slider.HitObject).TailCircle.Samples.Count == 0);
+ AddAssert("head samples not updated", () => assertSamples(slider.HitObject.HeadCircle));
+ AddAssert("tick samples not updated", () => slider.HitObject.NestedHitObjects.OfType().All(assertTickSamples));
+ AddAssert("repeat samples not updated", () => slider.HitObject.NestedHitObjects.OfType().All(assertSamples));
+ AddAssert("tail has no samples", () => slider.HitObject.TailCircle.Samples.Count == 0);
static bool assertTickSamples(SliderTick tick) => tick.Samples.Single().Name == "slidertick";
diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs
index 94d1cb8864..496b1b3559 100644
--- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs
+++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs
@@ -20,8 +20,8 @@ namespace osu.Game.Rulesets.Osu.Tests
private TestDrawableSpinner drawableSpinner;
- [TestCase(false)]
[TestCase(true)]
+ [TestCase(false)]
public void TestVariousSpinners(bool autoplay)
{
string term = autoplay ? "Hit" : "Miss";
diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs
index 1ad2eb83bf..9b758ec898 100644
--- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs
+++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs
@@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
protected override void OnSelected()
{
- AddInternal(ControlPointVisualiser = new PathControlPointVisualiser((Slider)slider.HitObject, true)
+ AddInternal(ControlPointVisualiser = new PathControlPointVisualiser(slider.HitObject, true)
{
RemoveControlPointsRequested = removeControlPoints
});
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs
index f69cacd432..025e202666 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs
@@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
@@ -23,28 +22,38 @@ namespace osu.Game.Rulesets.Osu.Mods
private const double fade_in_duration_multiplier = 0.4;
private const double fade_out_duration_multiplier = 0.3;
- protected override bool IsFirstHideableObject(DrawableHitObject hitObject) => !(hitObject is DrawableSpinner);
+ protected override bool IsFirstAdjustableObject(HitObject hitObject) => !(hitObject is Spinner);
public override void ApplyToDrawableHitObjects(IEnumerable drawables)
{
- static void adjustFadeIn(OsuHitObject h) => h.TimeFadeIn = h.TimePreempt * fade_in_duration_multiplier;
-
- foreach (var d in drawables.OfType())
- {
- adjustFadeIn(d.HitObject);
- foreach (var h in d.HitObject.NestedHitObjects.OfType())
- adjustFadeIn(h);
- }
+ foreach (var d in drawables)
+ d.ApplyCustomUpdateState += applyFadeInAdjustment;
base.ApplyToDrawableHitObjects(drawables);
}
+ private void applyFadeInAdjustment(DrawableHitObject hitObject, ArmedState state)
+ {
+ if (!(hitObject is DrawableOsuHitObject d))
+ return;
+
+ d.HitObject.TimeFadeIn = d.HitObject.TimePreempt * fade_in_duration_multiplier;
+ }
+
private double lastSliderHeadFadeOutStartTime;
private double lastSliderHeadFadeOutDuration;
- protected override void ApplyFirstObjectIncreaseVisibilityState(DrawableHitObject drawable, ArmedState state) => applyState(drawable, true);
+ protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
+ {
+ base.ApplyIncreasedVisibilityState(hitObject, state);
+ applyState(hitObject, true);
+ }
- protected override void ApplyHiddenState(DrawableHitObject drawable, ArmedState state) => applyState(drawable, false);
+ protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state)
+ {
+ base.ApplyNormalVisibilityState(hitObject, state);
+ applyState(hitObject, false);
+ }
private void applyState(DrawableHitObject drawable, bool increaseVisibility)
{
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs b/osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs
index 06ba4cde4a..d1be162f73 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs
@@ -2,11 +2,8 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using System.Collections.Generic;
-using System.Linq;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
-using osu.Game.Configuration;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
@@ -17,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Mods
///
/// Adjusts the size of hit objects during their fade in animation.
///
- public abstract class OsuModObjectScaleTween : Mod, IReadFromConfig, IApplicableToDrawableHitObjects
+ public abstract class OsuModObjectScaleTween : ModWithVisibilityAdjustment
{
public override ModType Type => ModType.Fun;
@@ -27,33 +24,19 @@ namespace osu.Game.Rulesets.Osu.Mods
protected virtual float EndScale => 1;
- private Bindable increaseFirstObjectVisibility = new Bindable();
-
public override Type[] IncompatibleMods => new[] { typeof(OsuModSpinIn), typeof(OsuModTraceable) };
- public void ReadFromConfig(OsuConfigManager config)
+ protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
{
- increaseFirstObjectVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility);
}
- public void ApplyToDrawableHitObjects(IEnumerable drawables)
- {
- foreach (var drawable in drawables.Skip(increaseFirstObjectVisibility.Value ? 1 : 0))
- {
- switch (drawable)
- {
- case DrawableSpinner _:
- continue;
+ protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyCustomState(hitObject, state);
- default:
- drawable.ApplyCustomUpdateState += ApplyCustomState;
- break;
- }
- }
- }
-
- protected virtual void ApplyCustomState(DrawableHitObject drawable, ArmedState state)
+ private void applyCustomState(DrawableHitObject drawable, ArmedState state)
{
+ if (drawable is DrawableSpinner)
+ return;
+
var h = (OsuHitObject)drawable.HitObject;
// apply grow effect
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs
index 940c888f3a..96ba58da23 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs
@@ -2,12 +2,8 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using System.Collections.Generic;
-using System.Linq;
-using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
-using osu.Game.Configuration;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
@@ -16,7 +12,7 @@ using osuTK;
namespace osu.Game.Rulesets.Osu.Mods
{
- public class OsuModSpinIn : Mod, IApplicableToDrawableHitObjects, IReadFromConfig
+ public class OsuModSpinIn : ModWithVisibilityAdjustment
{
public override string Name => "Spin In";
public override string Acronym => "SI";
@@ -31,31 +27,17 @@ namespace osu.Game.Rulesets.Osu.Mods
private const int rotate_offset = 360;
private const float rotate_starting_width = 2;
- private Bindable increaseFirstObjectVisibility = new Bindable();
-
- public void ReadFromConfig(OsuConfigManager config)
+ protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
{
- increaseFirstObjectVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility);
}
- public void ApplyToDrawableHitObjects(IEnumerable drawables)
- {
- foreach (var drawable in drawables.Skip(increaseFirstObjectVisibility.Value ? 1 : 0))
- {
- switch (drawable)
- {
- case DrawableSpinner _:
- continue;
-
- default:
- drawable.ApplyCustomUpdateState += applyZoomState;
- break;
- }
- }
- }
+ protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyZoomState(hitObject, state);
private void applyZoomState(DrawableHitObject drawable, ArmedState state)
{
+ if (drawable is DrawableSpinner)
+ return;
+
var h = (OsuHitObject)drawable.HitObject;
switch (drawable)
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs
index bb2213aa31..b7e60295cb 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs
@@ -2,12 +2,8 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using System.Linq;
-using osu.Framework.Bindables;
-using System.Collections.Generic;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
-using osu.Game.Configuration;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables;
@@ -15,7 +11,7 @@ using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
namespace osu.Game.Rulesets.Osu.Mods
{
- internal class OsuModTraceable : Mod, IReadFromConfig, IApplicableToDrawableHitObjects
+ internal class OsuModTraceable : ModWithVisibilityAdjustment
{
public override string Name => "Traceable";
public override string Acronym => "TC";
@@ -24,20 +20,14 @@ namespace osu.Game.Rulesets.Osu.Mods
public override double ScoreMultiplier => 1;
public override Type[] IncompatibleMods => new[] { typeof(OsuModHidden), typeof(OsuModSpinIn), typeof(OsuModObjectScaleTween) };
- private Bindable increaseFirstObjectVisibility = new Bindable();
- public void ReadFromConfig(OsuConfigManager config)
+ protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
{
- increaseFirstObjectVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility);
}
- public void ApplyToDrawableHitObjects(IEnumerable drawables)
- {
- foreach (var drawable in drawables.Skip(increaseFirstObjectVisibility.Value ? 1 : 0))
- drawable.ApplyCustomUpdateState += ApplyTraceableState;
- }
+ protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyTraceableState(hitObject, state);
- protected void ApplyTraceableState(DrawableHitObject drawable, ArmedState state)
+ private void applyTraceableState(DrawableHitObject drawable, ArmedState state)
{
if (!(drawable is DrawableOsuHitObject))
return;
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs
index 5e80d08667..b5905d7015 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Game.Rulesets.Mods;
@@ -13,7 +12,7 @@ using osuTK;
namespace osu.Game.Rulesets.Osu.Mods
{
- internal class OsuModTransform : Mod, IApplicableToDrawableHitObjects
+ internal class OsuModTransform : ModWithVisibilityAdjustment
{
public override string Name => "Transform";
public override string Acronym => "TR";
@@ -25,11 +24,9 @@ namespace osu.Game.Rulesets.Osu.Mods
private float theta;
- public void ApplyToDrawableHitObjects(IEnumerable drawables)
- {
- foreach (var drawable in drawables)
- drawable.ApplyCustomUpdateState += applyTransform;
- }
+ protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyTransform(hitObject, state);
+
+ protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyTransform(hitObject, state);
private void applyTransform(DrawableHitObject drawable, ArmedState state)
{
diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs
index 3cad52faeb..9c5e41f245 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Game.Rulesets.Mods;
@@ -13,7 +12,7 @@ using osuTK;
namespace osu.Game.Rulesets.Osu.Mods
{
- internal class OsuModWiggle : Mod, IApplicableToDrawableHitObjects
+ internal class OsuModWiggle : ModWithVisibilityAdjustment
{
public override string Name => "Wiggle";
public override string Acronym => "WG";
@@ -26,11 +25,9 @@ namespace osu.Game.Rulesets.Osu.Mods
private const int wiggle_duration = 90; // (ms) Higher = fewer wiggles
private const int wiggle_strength = 10; // Higher = stronger wiggles
- public void ApplyToDrawableHitObjects(IEnumerable drawables)
- {
- foreach (var drawable in drawables)
- drawable.ApplyCustomUpdateState += drawableOnApplyCustomUpdateState;
- }
+ protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) => drawableOnApplyCustomUpdateState(hitObject, state);
+
+ protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) => drawableOnApplyCustomUpdateState(hitObject, state);
private void drawableOnApplyCustomUpdateState(DrawableHitObject drawable, ArmedState state)
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs
index 2c41e6b0e9..3a9e19b361 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointConnection.cs
@@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using System.Diagnostics;
using JetBrains.Annotations;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
@@ -31,19 +32,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
/// The which s will exit from.
///
[NotNull]
- public readonly DrawableOsuHitObject Start;
+ public readonly OsuHitObject Start;
///
/// Creates a new .
///
/// The which s will exit from.
- public FollowPointConnection([NotNull] DrawableOsuHitObject start)
+ public FollowPointConnection([NotNull] OsuHitObject start)
{
Start = start;
RelativeSizeAxes = Axes.Both;
- StartTime.BindTo(Start.HitObject.StartTimeBindable);
+ StartTime.BindTo(start.StartTimeBindable);
}
protected override void LoadComplete()
@@ -52,13 +53,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
bindEvents(Start);
}
- private DrawableOsuHitObject end;
+ private OsuHitObject end;
///
/// The which s will enter.
///
[CanBeNull]
- public DrawableOsuHitObject End
+ public OsuHitObject End
{
get => end;
set
@@ -75,10 +76,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
}
}
- private void bindEvents(DrawableOsuHitObject drawableObject)
+ private void bindEvents(OsuHitObject obj)
{
- drawableObject.HitObject.PositionBindable.BindValueChanged(_ => scheduleRefresh());
- drawableObject.HitObject.DefaultsApplied += _ => scheduleRefresh();
+ obj.PositionBindable.BindValueChanged(_ => scheduleRefresh());
+ obj.DefaultsApplied += _ => scheduleRefresh();
}
private void scheduleRefresh()
@@ -88,23 +89,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
private void refresh()
{
- OsuHitObject osuStart = Start.HitObject;
- double startTime = osuStart.GetEndTime();
+ double startTime = Start.GetEndTime();
LifetimeStart = startTime;
- OsuHitObject osuEnd = End?.HitObject;
-
- if (osuEnd == null || osuEnd.NewCombo || osuStart is Spinner || osuEnd is Spinner)
+ if (End == null || End.NewCombo || Start is Spinner || End is Spinner)
{
// ensure we always set a lifetime for full LifetimeManagementContainer benefits
LifetimeEnd = LifetimeStart;
return;
}
- Vector2 startPosition = osuStart.StackedEndPosition;
- Vector2 endPosition = osuEnd.StackedPosition;
- double endTime = osuEnd.StartTime;
+ Vector2 startPosition = Start.StackedEndPosition;
+ Vector2 endPosition = End.StackedPosition;
+ double endTime = End.StartTime;
Vector2 distanceVector = endPosition - startPosition;
int distance = (int)distanceVector.Length;
@@ -130,10 +128,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
AddInternal(fp = new FollowPoint());
+ Debug.Assert(End != null);
+
fp.Position = pointStartPosition;
fp.Rotation = rotation;
fp.Alpha = 0;
- fp.Scale = new Vector2(1.5f * osuEnd.Scale);
+ fp.Scale = new Vector2(1.5f * End.Scale);
firstTransformStartTime ??= fadeInTime;
@@ -141,12 +141,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
using (fp.BeginAbsoluteSequence(fadeInTime))
{
- fp.FadeIn(osuEnd.TimeFadeIn);
- fp.ScaleTo(osuEnd.Scale, osuEnd.TimeFadeIn, Easing.Out);
- fp.MoveTo(pointEndPosition, osuEnd.TimeFadeIn, Easing.Out);
- fp.Delay(fadeOutTime - fadeInTime).FadeOut(osuEnd.TimeFadeIn);
+ fp.FadeIn(End.TimeFadeIn);
+ fp.ScaleTo(End.Scale, End.TimeFadeIn, Easing.Out);
+ fp.MoveTo(pointEndPosition, End.TimeFadeIn, Easing.Out);
+ fp.Delay(fadeOutTime - fadeInTime).FadeOut(End.TimeFadeIn);
- finalTransformEndTime = fadeOutTime + osuEnd.TimeFadeIn;
+ finalTransformEndTime = fadeOutTime + End.TimeFadeIn;
}
point++;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs
index 11571ea761..be1392d7c3 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Connections/FollowPointRenderer.cs
@@ -24,19 +24,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
public override bool RemoveCompletedTransforms => false;
///
- /// Adds the s around a .
+ /// Adds the s around an .
/// This includes s leading into , and s exiting .
///
- /// The to add s for.
- public void AddFollowPoints(DrawableOsuHitObject hitObject)
+ /// The to add s for.
+ public void AddFollowPoints(OsuHitObject hitObject)
=> addConnection(new FollowPointConnection(hitObject).With(g => g.StartTime.BindValueChanged(_ => onStartTimeChanged(g))));
///
- /// Removes the s around a .
+ /// Removes the s around an .
/// This includes s leading into , and s exiting .
///
- /// The to remove s for.
- public void RemoveFollowPoints(DrawableOsuHitObject hitObject) => removeGroup(connections.Single(g => g.Start == hitObject));
+ /// The to remove s for.
+ public void RemoveFollowPoints(OsuHitObject hitObject) => removeGroup(connections.Single(g => g.Start == hitObject));
///
/// Adds a to this .
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
index b5ac26c824..b0c4e3758d 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs
@@ -4,7 +4,6 @@
using System;
using System.Diagnostics;
using osu.Framework.Allocation;
-using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Input;
@@ -21,28 +20,25 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public class DrawableHitCircle : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach
{
- public ApproachCircle ApproachCircle { get; }
-
- private readonly IBindable positionBindable = new Bindable();
- private readonly IBindable stackHeightBindable = new Bindable();
- private readonly IBindable scaleBindable = new BindableFloat();
-
public OsuAction? HitAction => HitArea.HitAction;
-
- public readonly HitReceptor HitArea;
- public readonly SkinnableDrawable CirclePiece;
- private readonly Container scaleContainer;
-
protected virtual OsuSkinComponents CirclePieceComponent => OsuSkinComponents.HitCircle;
+ public ApproachCircle ApproachCircle { get; private set; }
+ public HitReceptor HitArea { get; private set; }
+ public SkinnableDrawable CirclePiece { get; private set; }
+
+ private Container scaleContainer;
private InputManager inputManager;
public DrawableHitCircle(HitCircle h)
: base(h)
{
- Origin = Anchor.Centre;
+ }
- Position = HitObject.StackedPosition;
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Origin = Anchor.Centre;
InternalChildren = new Drawable[]
{
@@ -75,19 +71,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
};
Size = HitArea.DrawSize;
- }
-
- [BackgroundDependencyLoader]
- private void load()
- {
- positionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
- stackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
- scaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true);
-
- positionBindable.BindTo(HitObject.PositionBindable);
- stackHeightBindable.BindTo(HitObject.StackHeightBindable);
- scaleBindable.BindTo(HitObject.ScaleBindable);
+ PositionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition, true);
+ StackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition, true);
+ ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true);
AccentColour.BindValueChanged(accent => ApproachCircle.Colour = accent.NewValue, true);
}
@@ -164,19 +151,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
ApproachCircle.Expire(true);
}
- protected override void UpdateStateTransforms(ArmedState state)
+ protected override void UpdateHitStateTransforms(ArmedState state)
{
- base.UpdateStateTransforms(state);
-
Debug.Assert(HitObject.HitWindows != null);
switch (state)
{
case ArmedState.Idle:
this.Delay(HitObject.TimePreempt).FadeOut(500);
-
- Expire(true);
-
HitArea.HitAction = null;
break;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
index 45c664ba3b..208f79f165 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs
@@ -2,18 +2,24 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Graphics.Containers;
using osu.Game.Rulesets.Osu.UI;
+using osuTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public class DrawableOsuHitObject : DrawableHitObject
{
- private readonly ShakeContainer shakeContainer;
+ public readonly IBindable PositionBindable = new Bindable();
+ public readonly IBindable StackHeightBindable = new Bindable();
+ public readonly IBindable ScaleBindable = new BindableFloat();
+ public readonly IBindable IndexInCurrentComboBindable = new Bindable();
// Must be set to update IsHovered as it's used in relax mdo to detect osu hit objects.
public override bool HandlePositionalInput => true;
@@ -26,16 +32,28 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
///
public Func CheckHittable;
+ private ShakeContainer shakeContainer;
+
protected DrawableOsuHitObject(OsuHitObject hitObject)
: base(hitObject)
{
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Alpha = 0;
+
base.AddInternal(shakeContainer = new ShakeContainer
{
ShakeDuration = 30,
RelativeSizeAxes = Axes.Both
});
- Alpha = 0;
+ IndexInCurrentComboBindable.BindTo(HitObject.IndexInCurrentComboBindable);
+ PositionBindable.BindTo(HitObject.PositionBindable);
+ StackHeightBindable.BindTo(HitObject.StackHeightBindable);
+ ScaleBindable.BindTo(HitObject.ScaleBindable);
}
// Forward all internal management to shakeContainer.
@@ -51,17 +69,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected virtual void Shake(double maximumLength) => shakeContainer.Shake(maximumLength);
- protected override void UpdateStateTransforms(ArmedState state)
+ protected override void UpdateInitialTransforms()
{
- base.UpdateStateTransforms(state);
+ base.UpdateInitialTransforms();
- switch (state)
- {
- case ArmedState.Idle:
- // Manually set to reduce the number of future alive objects to a bare minimum.
- LifetimeStart = HitObject.StartTime - HitObject.TimePreempt;
- break;
- }
+ // Manually set to reduce the number of future alive objects to a bare minimum.
+ LifetimeStart = HitObject.StartTime - HitObject.TimePreempt;
}
///
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs
index 49535e7fff..98898ce1b4 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuJudgement.cs
@@ -2,22 +2,17 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Configuration;
using osuTK;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects.Drawables;
-using osu.Game.Skinning;
-using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public class DrawableOsuJudgement : DrawableJudgement
{
- protected SkinnableSprite Lighting;
-
- private Bindable lightingColour;
+ protected SkinnableLighting Lighting { get; private set; }
[Resolved]
private OsuConfigManager config { get; set; }
@@ -34,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
[BackgroundDependencyLoader]
private void load()
{
- AddInternal(Lighting = new SkinnableSprite("lighting")
+ AddInternal(Lighting = new SkinnableLighting
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -59,19 +54,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
base.PrepareForUse();
- lightingColour?.UnbindAll();
-
Lighting.ResetAnimation();
-
- if (JudgedObject != null)
- {
- lightingColour = JudgedObject.AccentColour.GetBoundCopy();
- lightingColour.BindValueChanged(colour => Lighting.Colour = Result.IsHit ? colour.NewValue : Color4.Transparent, true);
- }
- else
- {
- Lighting.Colour = Color4.White;
- }
+ Lighting.SetColourFrom(JudgedObject, Result);
}
private double fadeOutDelay;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
index b00d12983d..d8dd0d7471 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs
@@ -20,62 +20,54 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public class DrawableSlider : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach
{
+ public new Slider HitObject => (Slider)base.HitObject;
+
public DrawableSliderHead HeadCircle => headContainer.Child;
public DrawableSliderTail TailCircle => tailContainer.Child;
- public readonly SliderBall Ball;
- public readonly SkinnableDrawable Body;
+ public SliderBall Ball { get; private set; }
+ public SkinnableDrawable Body { get; private set; }
public override bool DisplayResult => false;
private PlaySliderBody sliderBody => Body.Drawable as PlaySliderBody;
- private readonly Container headContainer;
- private readonly Container tailContainer;
- private readonly Container tickContainer;
- private readonly Container repeatContainer;
+ public readonly IBindable PathVersion = new Bindable();
- private readonly Slider slider;
-
- private readonly IBindable positionBindable = new Bindable();
- private readonly IBindable stackHeightBindable = new Bindable();
- private readonly IBindable scaleBindable = new BindableFloat();
+ private Container headContainer;
+ private Container tailContainer;
+ private Container tickContainer;
+ private Container repeatContainer;
public DrawableSlider(Slider s)
: base(s)
{
- slider = s;
-
- Position = s.StackedPosition;
+ }
+ [BackgroundDependencyLoader]
+ private void load()
+ {
InternalChildren = new Drawable[]
{
Body = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderBody), _ => new DefaultSliderBody(), confineMode: ConfineMode.NoScaling),
tailContainer = new Container { RelativeSizeAxes = Axes.Both },
tickContainer = new Container { RelativeSizeAxes = Axes.Both },
repeatContainer = new Container { RelativeSizeAxes = Axes.Both },
- Ball = new SliderBall(s, this)
+ Ball = new SliderBall(this)
{
GetInitialHitAction = () => HeadCircle.HitAction,
BypassAutoSizeAxes = Axes.Both,
- Scale = new Vector2(s.Scale),
AlwaysPresent = true,
Alpha = 0
},
headContainer = new Container { RelativeSizeAxes = Axes.Both },
};
- }
- [BackgroundDependencyLoader]
- private void load()
- {
- positionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
- stackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition);
- scaleBindable.BindValueChanged(scale => Ball.Scale = new Vector2(scale.NewValue));
+ PathVersion.BindTo(HitObject.Path.Version);
- positionBindable.BindTo(HitObject.PositionBindable);
- stackHeightBindable.BindTo(HitObject.StackHeightBindable);
- scaleBindable.BindTo(HitObject.ScaleBindable);
+ PositionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition, true);
+ StackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition, true);
+ ScaleBindable.BindValueChanged(scale => Ball.Scale = new Vector2(scale.NewValue), true);
AccentColour.BindValueChanged(colour =>
{
@@ -162,20 +154,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
switch (hitObject)
{
case SliderTailCircle tail:
- return new DrawableSliderTail(slider, tail);
+ return new DrawableSliderTail(tail);
case SliderHeadCircle head:
- return new DrawableSliderHead(slider, head)
+ return new DrawableSliderHead(HitObject, head)
{
OnShake = Shake,
CheckHittable = (d, t) => CheckHittable?.Invoke(d, t) ?? true
};
case SliderTick tick:
- return new DrawableSliderTick(tick) { Position = tick.Position - slider.Position };
+ return new DrawableSliderTick(tick) { Position = tick.Position - HitObject.Position };
case SliderRepeat repeat:
- return new DrawableSliderRepeat(repeat, this) { Position = repeat.Position - slider.Position };
+ return new DrawableSliderRepeat(repeat, this) { Position = repeat.Position - HitObject.Position };
}
return base.CreateNestedHitObject(hitObject);
@@ -200,14 +192,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
// keep the sliding sample playing at the current tracking position
slidingSample.Balance.Value = CalculateSamplePlaybackBalance(Ball.X / OsuPlayfield.BASE_SIZE.X);
- double completionProgress = Math.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1);
+ double completionProgress = Math.Clamp((Time.Current - HitObject.StartTime) / HitObject.Duration, 0, 1);
Ball.UpdateProgress(completionProgress);
sliderBody?.UpdateProgress(completionProgress);
foreach (DrawableHitObject hitObject in NestedHitObjects)
{
- if (hitObject is ITrackSnaking s) s.UpdateSnakingPosition(slider.Path.PositionAt(sliderBody?.SnakedStart ?? 0), slider.Path.PositionAt(sliderBody?.SnakedEnd ?? 0));
+ if (hitObject is ITrackSnaking s) s.UpdateSnakingPosition(HitObject.Path.PositionAt(sliderBody?.SnakedStart ?? 0), HitObject.Path.PositionAt(sliderBody?.SnakedEnd ?? 0));
if (hitObject is IRequireTracking t) t.Tracking = Ball.Tracking;
}
@@ -239,7 +231,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void CheckForResult(bool userTriggered, double timeOffset)
{
- if (userTriggered || Time.Current < slider.EndTime)
+ if (userTriggered || Time.Current < HitObject.EndTime)
return;
ApplyResult(r => r.Type = r.Judgement.MaxResult);
@@ -253,29 +245,31 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
base.PlaySamples();
}
- protected override void UpdateStateTransforms(ArmedState state)
+ protected override void UpdateStartTimeStateTransforms()
{
- base.UpdateStateTransforms(state);
+ base.UpdateStartTimeStateTransforms();
Ball.FadeIn();
Ball.ScaleTo(HitObject.Scale);
+ }
- using (BeginDelayedSequence(slider.Duration, true))
+ protected override void UpdateHitStateTransforms(ArmedState state)
+ {
+ base.UpdateHitStateTransforms(state);
+
+ const float fade_out_time = 450;
+
+ // intentionally pile on an extra FadeOut to make it happen much faster.
+ Ball.FadeOut(fade_out_time / 4, Easing.Out);
+
+ switch (state)
{
- const float fade_out_time = 450;
-
- // intentionally pile on an extra FadeOut to make it happen much faster.
- Ball.FadeOut(fade_out_time / 4, Easing.Out);
-
- switch (state)
- {
- case ArmedState.Hit:
- Ball.ScaleTo(HitObject.Scale * 1.4f, fade_out_time, Easing.Out);
- break;
- }
-
- this.FadeOut(fade_out_time, Easing.OutQuint);
+ case ArmedState.Hit:
+ Ball.ScaleTo(HitObject.Scale * 1.4f, fade_out_time, Easing.Out);
+ break;
}
+
+ this.FadeOut(fade_out_time, Easing.OutQuint);
}
public Drawable ProxiedLayer => HeadCircle.ProxiedLayer;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs
index 04f563eeec..49ed9f12e3 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs
@@ -5,13 +5,11 @@ using System;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Game.Rulesets.Objects.Types;
-using osuTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public class DrawableSliderHead : DrawableHitCircle
{
- private readonly IBindable positionBindable = new Bindable();
private readonly IBindable pathVersion = new Bindable();
protected override OsuSkinComponents CirclePieceComponent => OsuSkinComponents.SliderHeadHitCircle;
@@ -27,10 +25,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
[BackgroundDependencyLoader]
private void load()
{
- positionBindable.BindTo(HitObject.PositionBindable);
pathVersion.BindTo(slider.Path.Version);
- positionBindable.BindValueChanged(_ => updatePosition());
+ PositionBindable.BindValueChanged(_ => updatePosition());
pathVersion.BindValueChanged(_ => updatePosition(), true);
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs
index 2a88f11f69..9c382bd0a7 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs
@@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using osu.Framework.Allocation;
-using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Utils;
@@ -22,9 +21,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private double animDuration;
- private readonly Drawable scaleContainer;
-
- public readonly Drawable CirclePiece;
+ public Drawable CirclePiece { get; private set; }
+ private Drawable scaleContainer;
+ private ReverseArrowPiece arrow;
public override bool DisplayResult => false;
@@ -33,10 +32,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
this.sliderRepeat = sliderRepeat;
this.drawableSlider = drawableSlider;
+ }
- Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
-
+ [BackgroundDependencyLoader]
+ private void load()
+ {
Origin = Anchor.Centre;
+ Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
InternalChild = scaleContainer = new Container
{
@@ -50,15 +52,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
arrow = new ReverseArrowPiece(),
}
};
- }
- private readonly IBindable scaleBindable = new BindableFloat();
-
- [BackgroundDependencyLoader]
- private void load()
- {
- scaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true);
- scaleBindable.BindTo(HitObject.ScaleBindable);
+ ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true);
}
protected override void CheckForResult(bool userTriggered, double timeOffset)
@@ -77,9 +72,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
);
}
- protected override void UpdateStateTransforms(ArmedState state)
+ protected override void UpdateHitStateTransforms(ArmedState state)
{
- base.UpdateStateTransforms(state);
+ base.UpdateHitStateTransforms(state);
switch (state)
{
@@ -100,8 +95,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
private bool hasRotation;
- private readonly ReverseArrowPiece arrow;
-
public void UpdateSnakingPosition(Vector2 start, Vector2 end)
{
// When the repeat is hit, the arrow should fade out on spot rather than following the slider
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs
index f5bcecccdf..3be5983c57 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs
@@ -3,7 +3,6 @@
using System.Diagnostics;
using osu.Framework.Allocation;
-using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects.Drawables;
@@ -23,18 +22,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public bool Tracking { get; set; }
- private readonly IBindable scaleBindable = new BindableFloat();
+ private SkinnableDrawable circlePiece;
+ private Container scaleContainer;
- private readonly SkinnableDrawable circlePiece;
-
- private readonly Container scaleContainer;
-
- public DrawableSliderTail(Slider slider, SliderTailCircle tailCircle)
+ public DrawableSliderTail(SliderTailCircle tailCircle)
: base(tailCircle)
{
this.tailCircle = tailCircle;
- Origin = Anchor.Centre;
+ }
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ Origin = Anchor.Centre;
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
InternalChildren = new Drawable[]
@@ -51,13 +51,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
},
};
- }
- [BackgroundDependencyLoader]
- private void load()
- {
- scaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true);
- scaleBindable.BindTo(HitObject.ScaleBindable);
+ ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true);
}
protected override void UpdateInitialTransforms()
@@ -67,9 +62,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
circlePiece.FadeInFromZero(HitObject.TimeFadeIn);
}
- protected override void UpdateStateTransforms(ArmedState state)
+ protected override void UpdateHitStateTransforms(ArmedState state)
{
- base.UpdateStateTransforms(state);
+ base.UpdateHitStateTransforms(state);
Debug.Assert(HitObject.HitWindows != null);
@@ -77,8 +72,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
case ArmedState.Idle:
this.Delay(HitObject.TimePreempt).FadeOut(500);
-
- Expire(true);
break;
case ArmedState.Miss:
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
index 9b68b446a4..2af51ea486 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
-using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using osuTK;
@@ -23,10 +22,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public override bool DisplayResult => false;
- private readonly SkinnableDrawable scaleContainer;
+ private SkinnableDrawable scaleContainer;
public DrawableSliderTick(SliderTick sliderTick)
: base(sliderTick)
+ {
+ }
+
+ [BackgroundDependencyLoader]
+ private void load()
{
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
Origin = Anchor.Centre;
@@ -49,15 +53,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
};
- }
- private readonly IBindable scaleBindable = new BindableFloat();
-
- [BackgroundDependencyLoader]
- private void load()
- {
- scaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true);
- scaleBindable.BindTo(HitObject.ScaleBindable);
+ ScaleBindable.BindValueChanged(scale => scaleContainer.Scale = new Vector2(scale.NewValue), true);
}
protected override void CheckForResult(bool userTriggered, double timeOffset)
@@ -72,9 +69,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
this.ScaleTo(0.5f).ScaleTo(1f, ANIM_DURATION * 4, Easing.OutElasticHalf);
}
- protected override void UpdateStateTransforms(ArmedState state)
+ protected override void UpdateHitStateTransforms(ArmedState state)
{
- base.UpdateStateTransforms(state);
+ base.UpdateHitStateTransforms(state);
switch (state)
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
index 936bfaeb86..77fbff9c51 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs
@@ -16,34 +16,33 @@ using osu.Game.Rulesets.Osu.Skinning;
using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Ranking;
using osu.Game.Skinning;
-using osuTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public class DrawableSpinner : DrawableOsuHitObject
{
- protected readonly Spinner Spinner;
+ public new Spinner HitObject => (Spinner)base.HitObject;
- private readonly Container ticks;
+ public SpinnerRotationTracker RotationTracker { get; private set; }
+ public SpinnerSpmCounter SpmCounter { get; private set; }
- public readonly SpinnerRotationTracker RotationTracker;
- public readonly SpinnerSpmCounter SpmCounter;
- private readonly SpinnerBonusDisplay bonusDisplay;
-
- private readonly IBindable positionBindable = new Bindable();
+ private Container ticks;
+ private SpinnerBonusDisplay bonusDisplay;
+ private Bindable isSpinning;
private bool spinnerFrequencyModulate;
public DrawableSpinner(Spinner s)
: base(s)
+ {
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
{
Origin = Anchor.Centre;
- Position = s.Position;
-
RelativeSizeAxes = Axes.Both;
- Spinner = s;
-
InternalChildren = new Drawable[]
{
ticks = new Container(),
@@ -55,7 +54,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Children = new Drawable[]
{
new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SpinnerBody), _ => new DefaultSpinnerDisc()),
- RotationTracker = new SpinnerRotationTracker(Spinner)
+ RotationTracker = new SpinnerRotationTracker(this)
}
},
SpmCounter = new SpinnerSpmCounter
@@ -72,9 +71,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
Y = -120,
}
};
- }
- private Bindable isSpinning;
+ PositionBindable.BindValueChanged(pos => Position = pos.NewValue, true);
+ }
protected override void LoadComplete()
{
@@ -142,12 +141,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
}
- protected override void UpdateStateTransforms(ArmedState state)
+ protected override void UpdateHitStateTransforms(ArmedState state)
{
- base.UpdateStateTransforms(state);
+ base.UpdateHitStateTransforms(state);
- using (BeginDelayedSequence(Spinner.Duration, true))
- this.FadeOut(160);
+ this.FadeOut(160);
// skin change does a rewind of transforms, which will stop the spinning sound from playing if it's currently in playback.
isSpinning?.TriggerChange();
@@ -173,13 +171,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
return base.CreateNestedHitObject(hitObject);
}
- [BackgroundDependencyLoader]
- private void load(OsuColour colours)
- {
- positionBindable.BindValueChanged(pos => Position = pos.NewValue);
- positionBindable.BindTo(HitObject.PositionBindable);
- }
-
protected override void ApplySkin(ISkinSource skin, bool allowFallback)
{
base.ApplySkin(skin, allowFallback);
@@ -193,12 +184,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
get
{
- if (Spinner.SpinsRequired == 0)
+ if (HitObject.SpinsRequired == 0)
// some spinners are so short they can't require an integer spin count.
// these become implicitly hit.
return 1;
- return Math.Clamp(RotationTracker.RateAdjustedRotation / 360 / Spinner.SpinsRequired, 0, 1);
+ return Math.Clamp(RotationTracker.RateAdjustedRotation / 360 / HitObject.SpinsRequired, 0, 1);
}
}
@@ -208,7 +199,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
RotationTracker.Complete.Value = Progress >= 1;
- if (userTriggered || Time.Current < Spinner.EndTime)
+ if (userTriggered || Time.Current < HitObject.EndTime)
return;
// Trigger a miss result for remaining ticks to avoid infinite gameplay.
@@ -223,7 +214,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
r.Type = HitResult.Ok;
else if (Progress > .75)
r.Type = HitResult.Meh;
- else if (Time.Current >= Spinner.EndTime)
+ else if (Time.Current >= HitObject.EndTime)
r.Type = r.Judgement.MinResult;
});
}
@@ -275,7 +266,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
tick.TriggerResult(true);
if (tick is DrawableSpinnerBonusTick)
- bonusDisplay.SetBonusCount(spins - Spinner.SpinsRequired);
+ bonusDisplay.SetBonusCount(spins - HitObject.SpinsRequired);
}
wholeSpins++;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs
index e95cdc7ee3..c455c66e8d 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/CirclePiece.cs
@@ -36,7 +36,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Origin = Anchor.Centre,
Texture = textures.Get(@"Gameplay/osu/disc"),
},
- new TrianglesPiece((int)drawableHitObject.HitObject.StartTime)
+ new TrianglesPiece(drawableHitObject.GetHashCode())
{
RelativeSizeAxes = Axes.Both,
Blending = BlendingParameters.Additive,
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs
index e855317544..731852c221 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs
@@ -18,8 +18,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
private DrawableSpinner drawableSpinner;
- private Spinner spinner;
-
private const float initial_scale = 1.3f;
private const float idle_alpha = 0.2f;
private const float tracking_alpha = 0.4f;
@@ -52,7 +50,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
private void load(OsuColour colours, DrawableHitObject drawableHitObject)
{
drawableSpinner = (DrawableSpinner)drawableHitObject;
- spinner = (Spinner)drawableSpinner.HitObject;
normalColour = colours.BlueDark;
completeColour = colours.YellowLight;
@@ -130,6 +127,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
if (!(drawableHitObject is DrawableSpinner))
return;
+ Spinner spinner = drawableSpinner.HitObject;
+
using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true))
{
this.ScaleTo(initial_scale);
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs
index cb3787a493..98432eb4fe 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/MainCirclePiece.cs
@@ -42,10 +42,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
private readonly IBindable accentColour = new Bindable();
private readonly IBindable indexInCurrentCombo = new Bindable();
+ [Resolved]
+ private DrawableHitObject drawableObject { get; set; }
+
[BackgroundDependencyLoader]
- private void load(DrawableHitObject drawableObject)
+ private void load()
{
- OsuHitObject osuObject = (OsuHitObject)drawableObject.HitObject;
+ var drawableOsuObject = (DrawableOsuHitObject)drawableObject;
state.BindTo(drawableObject.State);
state.BindValueChanged(updateState, true);
@@ -58,38 +61,41 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
circle.Colour = colour.NewValue;
}, true);
- indexInCurrentCombo.BindTo(osuObject.IndexInCurrentComboBindable);
+ indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
indexInCurrentCombo.BindValueChanged(index => number.Text = (index.NewValue + 1).ToString(), true);
}
private void updateState(ValueChangedEvent state)
{
- glow.FadeOut(400);
-
- switch (state.NewValue)
+ using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime, true))
{
- case ArmedState.Hit:
- const double flash_in = 40;
- const double flash_out = 100;
+ glow.FadeOut(400);
- flash.FadeTo(0.8f, flash_in)
- .Then()
- .FadeOut(flash_out);
+ switch (state.NewValue)
+ {
+ case ArmedState.Hit:
+ const double flash_in = 40;
+ const double flash_out = 100;
- explode.FadeIn(flash_in);
- this.ScaleTo(1.5f, 400, Easing.OutQuad);
+ flash.FadeTo(0.8f, flash_in)
+ .Then()
+ .FadeOut(flash_out);
- using (BeginDelayedSequence(flash_in, true))
- {
- // after the flash, we can hide some elements that were behind it
- ring.FadeOut();
- circle.FadeOut();
- number.FadeOut();
+ explode.FadeIn(flash_in);
+ this.ScaleTo(1.5f, 400, Easing.OutQuad);
- this.FadeOut(800);
- }
+ using (BeginDelayedSequence(flash_in, true))
+ {
+ // after the flash, we can hide some elements that were behind it
+ ring.FadeOut();
+ circle.FadeOut();
+ number.FadeOut();
- break;
+ this.FadeOut(800);
+ }
+
+ break;
+ }
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/PlaySliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/PlaySliderBody.cs
index cedf2f6e09..29dff53f54 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/PlaySliderBody.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/PlaySliderBody.cs
@@ -17,23 +17,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
private IBindable pathVersion;
private IBindable accentColour;
- [Resolved]
- private DrawableHitObject drawableObject { get; set; }
-
[Resolved(CanBeNull = true)]
private OsuRulesetConfigManager config { get; set; }
- private Slider slider;
-
[BackgroundDependencyLoader]
- private void load(ISkinSource skin)
+ private void load(ISkinSource skin, DrawableHitObject drawableObject)
{
- slider = (Slider)drawableObject.HitObject;
+ var drawableSlider = (DrawableSlider)drawableObject;
- scaleBindable = slider.ScaleBindable.GetBoundCopy();
+ scaleBindable = drawableSlider.ScaleBindable.GetBoundCopy();
scaleBindable.BindValueChanged(scale => PathRadius = OsuHitObject.OBJECT_RADIUS * scale.NewValue, true);
- pathVersion = slider.Path.Version.GetBoundCopy();
+ pathVersion = drawableSlider.PathVersion.GetBoundCopy();
pathVersion.BindValueChanged(_ => Refresh());
accentColour = drawableObject.AccentColour.GetBoundCopy();
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
index 07dc6021c9..c5bf790377 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs
@@ -30,15 +30,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
set => ball.Colour = value;
}
- private readonly Slider slider;
private readonly Drawable followCircle;
private readonly DrawableSlider drawableSlider;
private readonly Drawable ball;
- public SliderBall(Slider slider, DrawableSlider drawableSlider = null)
+ public SliderBall(DrawableSlider drawableSlider)
{
this.drawableSlider = drawableSlider;
- this.slider = slider;
Origin = Anchor.Centre;
@@ -133,7 +131,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
if (headCircleHitAction == null)
timeToAcceptAnyKeyAfter = null;
- var actions = drawableSlider?.OsuActionInputManager?.PressedActions;
+ var actions = drawableSlider.OsuActionInputManager?.PressedActions;
// if the head circle was hit with a specific key, tracking should only occur while that key is pressed.
if (headCircleHitAction != null && timeToAcceptAnyKeyAfter == null)
@@ -147,7 +145,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
Tracking =
// in valid time range
- Time.Current >= slider.StartTime && Time.Current < slider.EndTime &&
+ Time.Current >= drawableSlider.HitObject.StartTime && Time.Current < drawableSlider.HitObject.EndTime &&
// in valid position range
lastScreenSpaceMousePosition.HasValue && followCircle.ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) &&
// valid action
@@ -172,9 +170,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public void UpdateProgress(double completionProgress)
{
- var newPos = slider.CurvePositionAt(completionProgress);
+ var newPos = drawableSlider.HitObject.CurvePositionAt(completionProgress);
- var diff = lastPosition.HasValue ? lastPosition.Value - newPos : newPos - slider.CurvePositionAt(completionProgress + 0.01f);
+ var diff = lastPosition.HasValue ? lastPosition.Value - newPos : newPos - drawableSlider.HitObject.CurvePositionAt(completionProgress + 0.01f);
if (diff == Vector2.Zero)
return;
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs
index e24fa865ad..e63f25b7bc 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SnakingSliderBody.cs
@@ -51,18 +51,23 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
///
private Vector2 snakedPathOffset;
- private Slider slider;
+ private DrawableSlider drawableSlider;
[BackgroundDependencyLoader]
private void load(DrawableHitObject drawableObject)
{
- slider = (Slider)drawableObject.HitObject;
+ drawableSlider = (DrawableSlider)drawableObject;
Refresh();
}
public void UpdateProgress(double completionProgress)
{
+ if (drawableSlider == null)
+ return;
+
+ Slider slider = drawableSlider.HitObject;
+
var span = slider.SpanAt(completionProgress);
var spanProgress = slider.ProgressAt(completionProgress);
@@ -87,8 +92,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
public void Refresh()
{
+ if (drawableSlider == null)
+ return;
+
// Generate the entire curve
- slider.Path.GetPathToProgress(CurrentCurve, 0, 1);
+ drawableSlider.HitObject.Path.GetPathToProgress(CurrentCurve, 0, 1);
SetVertices(CurrentCurve);
// Force the body to be the final path size to avoid excessive autosize computations
@@ -132,7 +140,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
SnakedStart = p0;
SnakedEnd = p1;
- slider.Path.GetPathToProgress(CurrentCurve, p0, p1);
+ drawableSlider.HitObject.Path.GetPathToProgress(CurrentCurve, p0, p1);
SetVertices(CurrentCurve);
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs
index 05ed38d241..910899c307 100644
--- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs
@@ -15,13 +15,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
{
public class SpinnerRotationTracker : CircularContainer
{
- private readonly Spinner spinner;
-
public override bool IsPresent => true; // handle input when hidden
- public SpinnerRotationTracker(Spinner s)
+ private readonly DrawableSpinner drawableSpinner;
+
+ public SpinnerRotationTracker(DrawableSpinner drawableSpinner)
{
- spinner = s;
+ this.drawableSpinner = drawableSpinner;
RelativeSizeAxes = Axes.Both;
}
@@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
///
/// Whether currently in the correct time range to allow spinning.
///
- private bool isSpinnableTime => spinner.StartTime <= Time.Current && spinner.EndTime > Time.Current;
+ private bool isSpinnableTime => drawableSpinner.HitObject.StartTime <= Time.Current && drawableSpinner.HitObject.EndTime > Time.Current;
protected override bool OnMouseMove(MouseMoveEvent e)
{
diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/SkinnableLighting.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/SkinnableLighting.cs
new file mode 100644
index 0000000000..02dc770285
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Objects/Drawables/SkinnableLighting.cs
@@ -0,0 +1,48 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using osu.Game.Rulesets.Judgements;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Skinning;
+using osuTK.Graphics;
+
+namespace osu.Game.Rulesets.Osu.Objects.Drawables
+{
+ public class SkinnableLighting : SkinnableSprite
+ {
+ private DrawableHitObject targetObject;
+ private JudgementResult targetResult;
+
+ public SkinnableLighting()
+ : base("lighting")
+ {
+ }
+
+ protected override void SkinChanged(ISkinSource skin, bool allowFallback)
+ {
+ base.SkinChanged(skin, allowFallback);
+ updateColour();
+ }
+
+ ///
+ /// Updates the lighting colour from a given hitobject and result.
+ ///
+ /// The that's been judged.
+ /// The that was judged with.
+ public void SetColourFrom(DrawableHitObject targetObject, JudgementResult targetResult)
+ {
+ this.targetObject = targetObject;
+ this.targetResult = targetResult;
+
+ updateColour();
+ }
+
+ private void updateColour()
+ {
+ if (targetObject == null || targetResult == null)
+ Colour = Color4.White;
+ else
+ Colour = targetResult.IsHit ? targetObject.AccentColour.Value : Color4.Transparent;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs
index 382d6e53cc..1551d1c149 100644
--- a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs
@@ -11,6 +11,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Skinning;
using osuTK;
using osuTK.Graphics;
@@ -41,13 +42,16 @@ namespace osu.Game.Rulesets.Osu.Skinning
private readonly Bindable accentColour = new Bindable();
private readonly IBindable indexInCurrentCombo = new Bindable();
+ [Resolved]
+ private DrawableHitObject drawableObject { get; set; }
+
[Resolved]
private ISkinSource skin { get; set; }
[BackgroundDependencyLoader]
- private void load(DrawableHitObject drawableObject)
+ private void load()
{
- OsuHitObject osuObject = (OsuHitObject)drawableObject.HitObject;
+ var drawableOsuObject = (DrawableOsuHitObject)drawableObject;
bool allowFallback = false;
@@ -111,7 +115,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
state.BindTo(drawableObject.State);
accentColour.BindTo(drawableObject.AccentColour);
- indexInCurrentCombo.BindTo(osuObject.IndexInCurrentComboBindable);
+ indexInCurrentCombo.BindTo(drawableOsuObject.IndexInCurrentComboBindable);
Texture getTextureWithFallback(string name)
{
@@ -143,28 +147,31 @@ namespace osu.Game.Rulesets.Osu.Skinning
{
const double legacy_fade_duration = 240;
- switch (state.NewValue)
+ using (BeginAbsoluteSequence(drawableObject.HitStateUpdateTime, true))
{
- case ArmedState.Hit:
- circleSprites.FadeOut(legacy_fade_duration, Easing.Out);
- circleSprites.ScaleTo(1.4f, legacy_fade_duration, Easing.Out);
+ switch (state.NewValue)
+ {
+ case ArmedState.Hit:
+ circleSprites.FadeOut(legacy_fade_duration, Easing.Out);
+ circleSprites.ScaleTo(1.4f, legacy_fade_duration, Easing.Out);
- if (hasNumber)
- {
- var legacyVersion = skin.GetConfig(LegacySetting.Version)?.Value;
-
- if (legacyVersion >= 2.0m)
- // legacy skins of version 2.0 and newer only apply very short fade out to the number piece.
- hitCircleText.FadeOut(legacy_fade_duration / 4, Easing.Out);
- else
+ if (hasNumber)
{
- // old skins scale and fade it normally along other pieces.
- hitCircleText.FadeOut(legacy_fade_duration, Easing.Out);
- hitCircleText.ScaleTo(1.4f, legacy_fade_duration, Easing.Out);
- }
- }
+ var legacyVersion = skin.GetConfig(LegacySetting.Version)?.Value;
- break;
+ if (legacyVersion >= 2.0m)
+ // legacy skins of version 2.0 and newer only apply very short fade out to the number piece.
+ hitCircleText.FadeOut(legacy_fade_duration / 4, Easing.Out);
+ else
+ {
+ // old skins scale and fade it normally along other pieces.
+ hitCircleText.FadeOut(legacy_fade_duration, Easing.Out);
+ hitCircleText.ScaleTo(1.4f, legacy_fade_duration, Easing.Out);
+ }
+ }
+
+ break;
+ }
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs
index 56b5571ce1..05f4c8e307 100644
--- a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs
@@ -19,98 +19,113 @@ namespace osu.Game.Rulesets.Osu.Skinning
/// Legacy skinned spinner with two main spinning layers, one fixed overlay and one final spinning overlay.
/// No background layer.
///
- public class LegacyNewStyleSpinner : CompositeDrawable
+ public class LegacyNewStyleSpinner : LegacySpinner
{
+ private Sprite glow;
private Sprite discBottom;
private Sprite discTop;
private Sprite spinningMiddle;
private Sprite fixedMiddle;
- private DrawableSpinner drawableSpinner;
+ private readonly Color4 glowColour = new Color4(3, 151, 255, 255);
- private const float final_scale = 0.625f;
+ private Container scaleContainer;
[BackgroundDependencyLoader]
- private void load(ISkinSource source, DrawableHitObject drawableObject)
+ private void load(ISkinSource source)
{
- drawableSpinner = (DrawableSpinner)drawableObject;
-
- Scale = new Vector2(final_scale);
-
- InternalChildren = new Drawable[]
+ AddInternal(scaleContainer = new Container
{
- discBottom = new Sprite
+ Scale = new Vector2(SPRITE_SCALE),
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Children = new Drawable[]
{
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Texture = source.GetTexture("spinner-bottom")
- },
- discTop = new Sprite
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Texture = source.GetTexture("spinner-top")
- },
- fixedMiddle = new Sprite
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Texture = source.GetTexture("spinner-middle")
- },
- spinningMiddle = new Sprite
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Texture = source.GetTexture("spinner-middle2")
+ glow = new Sprite
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Texture = source.GetTexture("spinner-glow"),
+ Blending = BlendingParameters.Additive,
+ Colour = glowColour,
+ },
+ discBottom = new Sprite
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Texture = source.GetTexture("spinner-bottom")
+ },
+ discTop = new Sprite
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Texture = source.GetTexture("spinner-top")
+ },
+ fixedMiddle = new Sprite
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Texture = source.GetTexture("spinner-middle")
+ },
+ spinningMiddle = new Sprite
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Texture = source.GetTexture("spinner-middle2")
+ }
}
- };
+ });
}
- protected override void LoadComplete()
+ protected override void UpdateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
{
- base.LoadComplete();
+ base.UpdateStateTransforms(drawableHitObject, state);
- drawableSpinner.ApplyCustomUpdateState += updateStateTransforms;
- updateStateTransforms(drawableSpinner, drawableSpinner.State.Value);
- }
-
- private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
- {
- if (!(drawableHitObject is DrawableSpinner))
- return;
-
- var spinner = (Spinner)drawableSpinner.HitObject;
-
- using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true))
- this.FadeOut();
-
- using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2, true))
- this.FadeInFromZero(spinner.TimeFadeIn / 2);
-
- using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true))
+ switch (drawableHitObject)
{
- fixedMiddle.FadeColour(Color4.White);
+ case DrawableSpinner d:
+ Spinner spinner = d.HitObject;
- using (BeginDelayedSequence(spinner.TimePreempt, true))
- fixedMiddle.FadeColour(Color4.Red, spinner.Duration);
+ using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true))
+ this.FadeOut();
+
+ using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimeFadeIn / 2, true))
+ this.FadeInFromZero(spinner.TimeFadeIn / 2);
+
+ using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true))
+ {
+ fixedMiddle.FadeColour(Color4.White);
+
+ using (BeginDelayedSequence(spinner.TimePreempt, true))
+ fixedMiddle.FadeColour(Color4.Red, spinner.Duration);
+ }
+
+ if (state == ArmedState.Hit)
+ {
+ using (BeginAbsoluteSequence(d.HitStateUpdateTime))
+ glow.FadeOut(300);
+ }
+
+ break;
+
+ case DrawableSpinnerBonusTick _:
+ if (state == ArmedState.Hit)
+ glow.FlashColour(Color4.White, 200);
+
+ break;
}
}
protected override void Update()
{
base.Update();
- spinningMiddle.Rotation = discTop.Rotation = drawableSpinner.RotationTracker.Rotation;
+ spinningMiddle.Rotation = discTop.Rotation = DrawableSpinner.RotationTracker.Rotation;
discBottom.Rotation = discTop.Rotation / 3;
- Scale = new Vector2(final_scale * (0.8f + (float)Interpolation.ApplyEasing(Easing.Out, drawableSpinner.Progress) * 0.2f));
- }
+ glow.Alpha = DrawableSpinner.Progress;
- protected override void Dispose(bool isDisposing)
- {
- base.Dispose(isDisposing);
-
- if (drawableSpinner != null)
- drawableSpinner.ApplyCustomUpdateState -= updateStateTransforms;
+ scaleContainer.Scale = new Vector2(SPRITE_SCALE * (0.8f + (float)Interpolation.ApplyEasing(Easing.Out, DrawableSpinner.Progress) * 0.2f));
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs
index 7b0d7acbbc..fba802f085 100644
--- a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs
@@ -8,6 +8,7 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Utils;
using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Skinning;
using osuTK;
@@ -17,28 +18,22 @@ namespace osu.Game.Rulesets.Osu.Skinning
///
/// Legacy skinned spinner with one main spinning layer and a background layer.
///
- public class LegacyOldStyleSpinner : CompositeDrawable
+ public class LegacyOldStyleSpinner : LegacySpinner
{
- private DrawableSpinner drawableSpinner;
private Sprite disc;
private Sprite metreSprite;
private Container metre;
private bool spinnerBlink;
- private const float sprite_scale = 1 / 1.6f;
- private const float final_metre_height = 692 * sprite_scale;
+ private const float final_metre_height = 692 * SPRITE_SCALE;
[BackgroundDependencyLoader]
- private void load(ISkinSource source, DrawableHitObject drawableObject)
+ private void load(ISkinSource source)
{
spinnerBlink = source.GetConfig(OsuSkinConfiguration.SpinnerNoBlink)?.Value != true;
- drawableSpinner = (DrawableSpinner)drawableObject;
-
- RelativeSizeAxes = Axes.Both;
-
- InternalChild = new Container
+ AddInternal(new Container
{
// the old-style spinner relied heavily on absolute screen-space coordinate values.
// wrap everything in a container simulating absolute coords to preserve alignment
@@ -54,14 +49,14 @@ namespace osu.Game.Rulesets.Osu.Skinning
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Texture = source.GetTexture("spinner-background"),
- Scale = new Vector2(sprite_scale)
+ Scale = new Vector2(SPRITE_SCALE)
},
disc = new Sprite
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Texture = source.GetTexture("spinner-circle"),
- Scale = new Vector2(sprite_scale)
+ Scale = new Vector2(SPRITE_SCALE)
},
metre = new Container
{
@@ -77,27 +72,21 @@ namespace osu.Game.Rulesets.Osu.Skinning
Texture = source.GetTexture("spinner-metre"),
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
- Scale = new Vector2(0.625f)
+ Scale = new Vector2(SPRITE_SCALE)
}
}
}
- };
+ });
}
- protected override void LoadComplete()
+ protected override void UpdateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
{
- base.LoadComplete();
+ base.UpdateStateTransforms(drawableHitObject, state);
- drawableSpinner.ApplyCustomUpdateState += updateStateTransforms;
- updateStateTransforms(drawableSpinner, drawableSpinner.State.Value);
- }
-
- private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
- {
- if (!(drawableHitObject is DrawableSpinner))
+ if (!(drawableHitObject is DrawableSpinner d))
return;
- var spinner = drawableSpinner.HitObject;
+ Spinner spinner = d.HitObject;
using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt, true))
this.FadeOut();
@@ -109,11 +98,11 @@ namespace osu.Game.Rulesets.Osu.Skinning
protected override void Update()
{
base.Update();
- disc.Rotation = drawableSpinner.RotationTracker.Rotation;
+ disc.Rotation = DrawableSpinner.RotationTracker.Rotation;
// careful: need to call this exactly once for all calculations in a frame
// as the function has a random factor in it
- var metreHeight = getMetreHeight(drawableSpinner.Progress);
+ var metreHeight = getMetreHeight(DrawableSpinner.Progress);
// hack to make the metre blink up from below than down from above.
// move down the container to be able to apply masking for the metre,
@@ -139,13 +128,5 @@ namespace osu.Game.Rulesets.Osu.Skinning
return (float)barCount / total_bars * final_metre_height;
}
-
- protected override void Dispose(bool isDisposing)
- {
- base.Dispose(isDisposing);
-
- if (drawableSpinner != null)
- drawableSpinner.ApplyCustomUpdateState -= updateStateTransforms;
- }
}
}
diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs b/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs
index 25ab96445a..836069013d 100644
--- a/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs
+++ b/osu.Game.Rulesets.Osu/Skinning/LegacySliderBall.cs
@@ -5,7 +5,6 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites;
-using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Skinning;
using osuTK.Graphics;
@@ -26,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Skinning
}
[BackgroundDependencyLoader]
- private void load(ISkinSource skin, DrawableHitObject drawableObject)
+ private void load(ISkinSource skin)
{
var ballColour = skin.GetConfig(OsuSkinColour.SliderBall)?.Value ?? Color4.White;
diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs
new file mode 100644
index 0000000000..eb9fa85fde
--- /dev/null
+++ b/osu.Game.Rulesets.Osu/Skinning/LegacySpinner.cs
@@ -0,0 +1,126 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Sprites;
+using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Objects.Drawables;
+using osu.Game.Skinning;
+using osuTK;
+
+namespace osu.Game.Rulesets.Osu.Skinning
+{
+ public abstract class LegacySpinner : CompositeDrawable
+ {
+ protected const float SPRITE_SCALE = 0.625f;
+
+ protected DrawableSpinner DrawableSpinner { get; private set; }
+
+ private Sprite spin;
+ private Sprite clear;
+
+ [BackgroundDependencyLoader]
+ private void load(DrawableHitObject drawableHitObject, ISkinSource source)
+ {
+ RelativeSizeAxes = Axes.Both;
+
+ DrawableSpinner = (DrawableSpinner)drawableHitObject;
+
+ AddRangeInternal(new[]
+ {
+ spin = new Sprite
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Depth = float.MinValue,
+ Texture = source.GetTexture("spinner-spin"),
+ Scale = new Vector2(SPRITE_SCALE),
+ Y = 120 - 45 // offset temporarily to avoid overlapping default spin counter
+ },
+ clear = new Sprite
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Depth = float.MinValue,
+ Alpha = 0,
+ Texture = source.GetTexture("spinner-clear"),
+ Scale = new Vector2(SPRITE_SCALE),
+ Y = -60
+ },
+ });
+ }
+
+ private readonly Bindable completed = new Bindable();
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ completed.BindTo(DrawableSpinner.RotationTracker.Complete);
+ completed.BindValueChanged(onCompletedChanged, true);
+
+ DrawableSpinner.ApplyCustomUpdateState += UpdateStateTransforms;
+ UpdateStateTransforms(DrawableSpinner, DrawableSpinner.State.Value);
+ }
+
+ private void onCompletedChanged(ValueChangedEvent completed)
+ {
+ if (completed.NewValue)
+ {
+ double startTime = Math.Min(Time.Current, DrawableSpinner.HitStateUpdateTime - 400);
+
+ using (BeginAbsoluteSequence(startTime, true))
+ {
+ clear.FadeInFromZero(400, Easing.Out);
+
+ clear.ScaleTo(SPRITE_SCALE * 2)
+ .Then().ScaleTo(SPRITE_SCALE * 0.8f, 240, Easing.Out)
+ .Then().ScaleTo(SPRITE_SCALE, 160);
+ }
+
+ const double fade_out_duration = 50;
+ using (BeginAbsoluteSequence(DrawableSpinner.HitStateUpdateTime - fade_out_duration, true))
+ clear.FadeOut(fade_out_duration);
+ }
+ else
+ {
+ clear.ClearTransforms();
+ clear.Alpha = 0;
+ }
+ }
+
+ protected virtual void UpdateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
+ {
+ switch (drawableHitObject)
+ {
+ case DrawableSpinner d:
+ double fadeOutLength = Math.Min(400, d.HitObject.Duration);
+
+ using (BeginAbsoluteSequence(drawableHitObject.HitStateUpdateTime - fadeOutLength, true))
+ spin.FadeOutFromOne(fadeOutLength);
+ break;
+
+ case DrawableSpinnerTick d:
+ if (state == ArmedState.Hit)
+ {
+ using (BeginAbsoluteSequence(d.HitStateUpdateTime, true))
+ spin.FadeOut(300);
+ }
+
+ break;
+ }
+ }
+
+ protected override void Dispose(bool isDisposing)
+ {
+ base.Dispose(isDisposing);
+
+ if (DrawableSpinner != null)
+ DrawableSpinner.ApplyCustomUpdateState -= UpdateStateTransforms;
+ }
+ }
+}
diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
index 50727d590a..321eeeab65 100644
--- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
+++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs
@@ -4,12 +4,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Pooling;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Drawables;
+using osu.Game.Rulesets.Osu.Configuration;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Connections;
using osu.Game.Rulesets.Osu.Scoring;
@@ -17,9 +20,6 @@ using osu.Game.Rulesets.Osu.UI.Cursor;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Skinning;
-using osu.Framework.Allocation;
-using osu.Framework.Bindables;
-using osu.Game.Rulesets.Osu.Configuration;
using osuTK;
namespace osu.Game.Rulesets.Osu.UI
@@ -95,6 +95,8 @@ namespace osu.Game.Rulesets.Osu.UI
public override void Add(DrawableHitObject h)
{
+ DrawableOsuHitObject osuHitObject = (DrawableOsuHitObject)h;
+
h.OnNewResult += onNewResult;
h.OnLoadComplete += d =>
{
@@ -107,18 +109,19 @@ namespace osu.Game.Rulesets.Osu.UI
base.Add(h);
- DrawableOsuHitObject osuHitObject = (DrawableOsuHitObject)h;
osuHitObject.CheckHittable = hitPolicy.IsHittable;
- followPoints.AddFollowPoints(osuHitObject);
+ followPoints.AddFollowPoints(osuHitObject.HitObject);
}
public override bool Remove(DrawableHitObject h)
{
+ DrawableOsuHitObject osuHitObject = (DrawableOsuHitObject)h;
+
bool result = base.Remove(h);
if (result)
- followPoints.RemoveFollowPoints((DrawableOsuHitObject)h);
+ followPoints.RemoveFollowPoints(osuHitObject.HitObject);
return result;
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs
index 1e08e921a6..aadcc420df 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableBarLine.cs
@@ -58,6 +58,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
});
}
- protected override void UpdateStateTransforms(ArmedState state) => this.FadeOut(150);
+ protected override void UpdateHitStateTransforms(ArmedState state) => this.FadeOut(150);
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
index 8f268dc1c7..c596fa2c7c 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRoll.cs
@@ -135,13 +135,13 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
ApplyResult(r => r.Type = r.Judgement.MinResult);
}
- protected override void UpdateStateTransforms(ArmedState state)
+ protected override void UpdateHitStateTransforms(ArmedState state)
{
switch (state)
{
case ArmedState.Hit:
case ArmedState.Miss:
- this.Delay(HitObject.Duration).FadeOut(100);
+ this.FadeOut(100);
break;
}
}
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs
index 9d7dcc7218..bf44a80037 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableDrumRollTick.cs
@@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
ApplyResult(r => r.Type = r.Judgement.MaxResult);
}
- protected override void UpdateStateTransforms(ArmedState state)
+ protected override void UpdateHitStateTransforms(ArmedState state)
{
switch (state)
{
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
index bb42240f25..4a3759794b 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs
@@ -193,7 +193,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
pressHandledThisFrame = false;
}
- protected override void UpdateStateTransforms(ArmedState state)
+ protected override void UpdateHitStateTransforms(ArmedState state)
{
Debug.Assert(HitObject.HitWindows != null);
diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
index 8ee4a5db71..ff0a27023d 100644
--- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
+++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableSwell.cs
@@ -215,15 +215,15 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
}
}
- protected override void UpdateInitialTransforms()
+ protected override void UpdateStartTimeStateTransforms()
{
- base.UpdateInitialTransforms();
+ base.UpdateStartTimeStateTransforms();
- using (BeginAbsoluteSequence(HitObject.StartTime - ring_appear_offset, true))
+ using (BeginDelayedSequence(-ring_appear_offset, true))
targetRing.ScaleTo(target_ring_scale, 400, Easing.OutQuint);
}
- protected override void UpdateStateTransforms(ArmedState state)
+ protected override void UpdateHitStateTransforms(ArmedState state)
{
const double transition_duration = 300;
@@ -235,12 +235,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
case ArmedState.Miss:
case ArmedState.Hit:
- using (BeginDelayedSequence(HitObject.Duration, true))
- {
- this.FadeOut(transition_duration, Easing.Out);
- bodyContainer.ScaleTo(1.4f, transition_duration);
- }
-
+ this.FadeOut(transition_duration, Easing.Out);
+ bodyContainer.ScaleTo(1.4f, transition_duration);
break;
}
}
diff --git a/osu.Game.Tests/Beatmaps/BeatmapDifficultyManagerTest.cs b/osu.Game.Tests/Beatmaps/BeatmapDifficultyManagerTest.cs
index 7c1ddd757f..ec77f48063 100644
--- a/osu.Game.Tests/Beatmaps/BeatmapDifficultyManagerTest.cs
+++ b/osu.Game.Tests/Beatmaps/BeatmapDifficultyManagerTest.cs
@@ -14,8 +14,8 @@ namespace osu.Game.Tests.Beatmaps
[Test]
public void TestKeyEqualsWithDifferentModInstances()
{
- var key1 = new BeatmapDifficultyManager.DifficultyCacheLookup(1234, 0, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
- var key2 = new BeatmapDifficultyManager.DifficultyCacheLookup(1234, 0, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
+ var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(1234, 0, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
+ var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(1234, 0, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
Assert.That(key1, Is.EqualTo(key2));
}
@@ -23,8 +23,8 @@ namespace osu.Game.Tests.Beatmaps
[Test]
public void TestKeyEqualsWithDifferentModOrder()
{
- var key1 = new BeatmapDifficultyManager.DifficultyCacheLookup(1234, 0, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
- var key2 = new BeatmapDifficultyManager.DifficultyCacheLookup(1234, 0, new Mod[] { new OsuModHidden(), new OsuModHardRock() });
+ var key1 = new BeatmapDifficultyCache.DifficultyCacheLookup(1234, 0, new Mod[] { new OsuModHardRock(), new OsuModHidden() });
+ var key2 = new BeatmapDifficultyCache.DifficultyCacheLookup(1234, 0, new Mod[] { new OsuModHidden(), new OsuModHardRock() });
Assert.That(key1, Is.EqualTo(key2));
}
@@ -47,7 +47,7 @@ namespace osu.Game.Tests.Beatmaps
[TestCase(8.3, DifficultyRating.ExpertPlus)]
public void TestDifficultyRatingMapping(double starRating, DifficultyRating expectedBracket)
{
- var actualBracket = BeatmapDifficultyManager.GetDifficultyRating(starRating);
+ var actualBracket = BeatmapDifficultyCache.GetDifficultyRating(starRating);
Assert.AreEqual(expectedBracket, actualBracket);
}
diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs
index df4b85b37a..72c6fd8d44 100644
--- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs
+++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs
@@ -250,7 +250,7 @@ namespace osu.Game.Tests.Visual.Gameplay
public void EndPlay(int beatmapId)
{
- ((ISpectatorClient)this).UserFinishedPlaying((int)StreamingUser.Id, new SpectatorState
+ ((ISpectatorClient)this).UserFinishedPlaying(StreamingUser.Id, new SpectatorState
{
BeatmapID = beatmapId,
RulesetID = 0,
@@ -273,7 +273,7 @@ namespace osu.Game.Tests.Visual.Gameplay
}
var bundle = new FrameDataBundle(frames);
- ((ISpectatorClient)this).UserSentFrames((int)StreamingUser.Id, bundle);
+ ((ISpectatorClient)this).UserSentFrames(StreamingUser.Id, bundle);
if (!sentState)
sendState(beatmapId);
@@ -293,7 +293,7 @@ namespace osu.Game.Tests.Visual.Gameplay
private void sendState(int beatmapId)
{
sentState = true;
- ((ISpectatorClient)this).UserBeganPlaying((int)StreamingUser.Id, new SpectatorState
+ ((ISpectatorClient)this).UserBeganPlaying(StreamingUser.Id, new SpectatorState
{
BeatmapID = beatmapId,
RulesetID = 0,
diff --git a/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs
index 7196f47bd6..582f72429b 100644
--- a/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs
+++ b/osu.Game.Tournament/Screens/Editors/TeamEditorScreen.cs
@@ -277,7 +277,7 @@ namespace osu.Game.Tournament.Screens.Editors
userId.Value = user.Id.ToString();
userId.BindValueChanged(idString =>
{
- long.TryParse(idString.NewValue, out var parsed);
+ int.TryParse(idString.NewValue, out var parsed);
user.Id = parsed;
diff --git a/osu.Game/Beatmaps/BeatmapDifficultyManager.cs b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs
similarity index 87%
rename from osu.Game/Beatmaps/BeatmapDifficultyManager.cs
rename to osu.Game/Beatmaps/BeatmapDifficultyCache.cs
index 9e83738e70..af1b1de0c1 100644
--- a/osu.Game/Beatmaps/BeatmapDifficultyManager.cs
+++ b/osu.Game/Beatmaps/BeatmapDifficultyCache.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
@@ -11,25 +10,25 @@ using System.Threading.Tasks;
using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
-using osu.Framework.Graphics.Containers;
using osu.Framework.Lists;
using osu.Framework.Logging;
using osu.Framework.Threading;
using osu.Framework.Utils;
+using osu.Game.Database;
using osu.Game.Rulesets;
-using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
namespace osu.Game.Beatmaps
{
- public class BeatmapDifficultyManager : CompositeDrawable
+ ///
+ /// A component which performs and acts as a central cache for difficulty calculations of beatmap/ruleset/mod combinations.
+ /// Currently not persisted between game sessions.
+ ///
+ public class BeatmapDifficultyCache : MemoryCachingComponent
{
// Too many simultaneous updates can lead to stutters. One thread seems to work fine for song select display purposes.
- private readonly ThreadedTaskScheduler updateScheduler = new ThreadedTaskScheduler(1, nameof(BeatmapDifficultyManager));
-
- // A permanent cache to prevent re-computations.
- private readonly ConcurrentDictionary difficultyCache = new ConcurrentDictionary();
+ private readonly ThreadedTaskScheduler updateScheduler = new ThreadedTaskScheduler(1, nameof(BeatmapDifficultyCache));
// All bindables that should be updated along with the current ruleset + mods.
private readonly LockedWeakList trackedBindables = new LockedWeakList();
@@ -239,7 +238,7 @@ namespace osu.Game.Beatmaps
var calculator = ruleset.CreateDifficultyCalculator(beatmapManager.GetWorkingBeatmap(beatmapInfo));
var attributes = calculator.Calculate(key.Mods);
- return difficultyCache[key] = new StarDifficulty(attributes);
+ return Cache[key] = new StarDifficulty(attributes);
}
catch (BeatmapInvalidForRulesetException e)
{
@@ -250,7 +249,7 @@ namespace osu.Game.Beatmaps
if (rulesetInfo.Equals(beatmapInfo.Ruleset))
{
Logger.Error(e, $"Failed to convert {beatmapInfo.OnlineBeatmapID} to the beatmap's default ruleset ({beatmapInfo.Ruleset}).");
- return difficultyCache[key] = new StarDifficulty();
+ return Cache[key] = new StarDifficulty();
}
// Check the cache first because this is now a different ruleset than the one previously guarded against.
@@ -261,7 +260,7 @@ namespace osu.Game.Beatmaps
}
catch
{
- return difficultyCache[key] = new StarDifficulty();
+ return Cache[key] = new StarDifficulty();
}
}
@@ -290,7 +289,7 @@ namespace osu.Game.Beatmaps
}
key = new DifficultyCacheLookup(beatmapInfo.ID, rulesetInfo.ID.Value, mods);
- return difficultyCache.TryGetValue(key, out existingDifficulty);
+ return Cache.TryGetValue(key, out existingDifficulty);
}
protected override void Dispose(bool isDisposing)
@@ -344,49 +343,4 @@ namespace osu.Game.Beatmaps
}
}
}
-
- public readonly struct StarDifficulty
- {
- ///
- /// The star difficulty rating for the given beatmap.
- ///
- public readonly double Stars;
-
- ///
- /// The maximum combo achievable on the given beatmap.
- ///
- public readonly int MaxCombo;
-
- ///
- /// The difficulty attributes computed for the given beatmap.
- /// Might not be available if the star difficulty is associated with a beatmap that's not locally available.
- ///
- [CanBeNull]
- public readonly DifficultyAttributes Attributes;
-
- ///
- /// Creates a structure based on computed
- /// by a .
- ///
- public StarDifficulty([NotNull] DifficultyAttributes attributes)
- {
- Stars = attributes.StarRating;
- MaxCombo = attributes.MaxCombo;
- Attributes = attributes;
- // Todo: Add more members (BeatmapInfo.DifficultyRating? Attributes? Etc...)
- }
-
- ///
- /// Creates a structure with a pre-populated star difficulty and max combo
- /// in scenarios where computing is not feasible (i.e. when working with online sources).
- ///
- public StarDifficulty(double starDifficulty, int maxCombo)
- {
- Stars = starDifficulty;
- MaxCombo = maxCombo;
- Attributes = null;
- }
-
- public DifficultyRating DifficultyRating => BeatmapDifficultyManager.GetDifficultyRating(Stars);
- }
}
diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs
index ffd8d14048..a898e10e4f 100644
--- a/osu.Game/Beatmaps/BeatmapInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapInfo.cs
@@ -136,7 +136,7 @@ namespace osu.Game.Beatmaps
public List Scores { get; set; }
[JsonIgnore]
- public DifficultyRating DifficultyRating => BeatmapDifficultyManager.GetDifficultyRating(StarDifficulty);
+ public DifficultyRating DifficultyRating => BeatmapDifficultyCache.GetDifficultyRating(StarDifficulty);
public string[] SearchableTerms => new[]
{
diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs
index a1d5e33d1e..96e18f120a 100644
--- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs
+++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs
@@ -142,7 +142,7 @@ namespace osu.Game.Beatmaps.Drawables
private CancellationTokenSource difficultyCancellation;
[Resolved]
- private BeatmapDifficultyManager difficultyManager { get; set; }
+ private BeatmapDifficultyCache difficultyCache { get; set; }
public DifficultyRetriever(BeatmapInfo beatmap, RulesetInfo ruleset, IReadOnlyList mods)
{
@@ -158,8 +158,8 @@ namespace osu.Game.Beatmaps.Drawables
{
difficultyCancellation = new CancellationTokenSource();
localStarDifficulty = ruleset != null
- ? difficultyManager.GetBindableDifficulty(beatmap, ruleset, mods, difficultyCancellation.Token)
- : difficultyManager.GetBindableDifficulty(beatmap, difficultyCancellation.Token);
+ ? difficultyCache.GetBindableDifficulty(beatmap, ruleset, mods, difficultyCancellation.Token)
+ : difficultyCache.GetBindableDifficulty(beatmap, difficultyCancellation.Token);
localStarDifficulty.BindValueChanged(difficulty => StarDifficulty.Value = difficulty.NewValue);
}
diff --git a/osu.Game/Beatmaps/StarDifficulty.cs b/osu.Game/Beatmaps/StarDifficulty.cs
new file mode 100644
index 0000000000..f438b6f0bc
--- /dev/null
+++ b/osu.Game/Beatmaps/StarDifficulty.cs
@@ -0,0 +1,53 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using JetBrains.Annotations;
+using osu.Game.Rulesets.Difficulty;
+
+namespace osu.Game.Beatmaps
+{
+ public readonly struct StarDifficulty
+ {
+ ///
+ /// The star difficulty rating for the given beatmap.
+ ///
+ public readonly double Stars;
+
+ ///
+ /// The maximum combo achievable on the given beatmap.
+ ///
+ public readonly int MaxCombo;
+
+ ///
+ /// The difficulty attributes computed for the given beatmap.
+ /// Might not be available if the star difficulty is associated with a beatmap that's not locally available.
+ ///
+ [CanBeNull]
+ public readonly DifficultyAttributes Attributes;
+
+ ///
+ /// Creates a structure based on computed
+ /// by a .
+ ///
+ public StarDifficulty([NotNull] DifficultyAttributes attributes)
+ {
+ Stars = attributes.StarRating;
+ MaxCombo = attributes.MaxCombo;
+ Attributes = attributes;
+ // Todo: Add more members (BeatmapInfo.DifficultyRating? Attributes? Etc...)
+ }
+
+ ///
+ /// Creates a structure with a pre-populated star difficulty and max combo
+ /// in scenarios where computing is not feasible (i.e. when working with online sources).
+ ///
+ public StarDifficulty(double starDifficulty, int maxCombo)
+ {
+ Stars = starDifficulty;
+ MaxCombo = maxCombo;
+ Attributes = null;
+ }
+
+ public DifficultyRating DifficultyRating => BeatmapDifficultyCache.GetDifficultyRating(Stars);
+ }
+}
diff --git a/osu.Game/Database/MemoryCachingComponent.cs b/osu.Game/Database/MemoryCachingComponent.cs
new file mode 100644
index 0000000000..85cf3b8af1
--- /dev/null
+++ b/osu.Game/Database/MemoryCachingComponent.cs
@@ -0,0 +1,17 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Concurrent;
+using osu.Framework.Graphics;
+
+namespace osu.Game.Database
+{
+ ///
+ /// A component which performs lookups (or calculations) and caches the results.
+ /// Currently not persisted between game sessions.
+ ///
+ public abstract class MemoryCachingComponent : Component
+ {
+ protected readonly ConcurrentDictionary Cache = new ConcurrentDictionary();
+ }
+}
diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs
index 1ca14256e5..6d0160fbc4 100644
--- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs
+++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs
@@ -61,7 +61,7 @@ namespace osu.Game.Online.API.Requests.Responses
private int[] ratings { get; set; }
[JsonProperty(@"user_id")]
- private long creatorId
+ private int creatorId
{
set => Author.Id = value;
}
diff --git a/osu.Game/Online/API/Requests/Responses/APIChangelogUser.cs b/osu.Game/Online/API/Requests/Responses/APIChangelogUser.cs
index 5891391e83..024e1ce048 100644
--- a/osu.Game/Online/API/Requests/Responses/APIChangelogUser.cs
+++ b/osu.Game/Online/API/Requests/Responses/APIChangelogUser.cs
@@ -20,7 +20,7 @@ namespace osu.Game.Online.API.Requests.Responses
public string OsuUsername { get; set; }
[JsonProperty("user_id")]
- public long? UserId { get; set; }
+ public int? UserId { get; set; }
[JsonProperty("user_url")]
public string UserUrl { get; set; }
diff --git a/osu.Game/Online/Chat/Channel.cs b/osu.Game/Online/Chat/Channel.cs
index 8c1e1ad128..187a3e5dfc 100644
--- a/osu.Game/Online/Chat/Channel.cs
+++ b/osu.Game/Online/Chat/Channel.cs
@@ -22,7 +22,7 @@ namespace osu.Game.Online.Chat
public readonly ObservableCollection Users = new ObservableCollection();
[JsonProperty(@"users")]
- private long[] userIds
+ private int[] userIds
{
set
{
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index a0ddab702e..64f8d4415b 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -278,7 +278,7 @@ namespace osu.Game
break;
case LinkAction.OpenUserProfile:
- if (long.TryParse(link.Argument, out long userId))
+ if (int.TryParse(link.Argument, out int userId))
ShowUser(userId);
break;
@@ -321,7 +321,7 @@ namespace osu.Game
/// Show a user's profile as an overlay.
///
/// The user to display.
- public void ShowUser(long userId) => waitForReady(() => userProfile, _ => userProfile.ShowUser(userId));
+ public void ShowUser(int userId) => waitForReady(() => userProfile, _ => userProfile.ShowUser(userId));
///
/// Show a beatmap's set as an overlay, displaying the given beatmap.
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index 4bc54e7e83..3da692249d 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -59,7 +59,7 @@ namespace osu.Game
protected ScoreManager ScoreManager;
- protected BeatmapDifficultyManager DifficultyManager;
+ protected BeatmapDifficultyCache DifficultyCache;
protected SkinManager SkinManager;
@@ -202,7 +202,7 @@ namespace osu.Game
dependencies.Cache(FileStore = new FileStore(contextFactory, Storage));
// ordering is important here to ensure foreign keys rules are not broken in ModelStore.Cleanup()
- dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, contextFactory, Host, () => DifficultyManager, LocalConfig));
+ dependencies.Cache(ScoreManager = new ScoreManager(RulesetStore, () => BeatmapManager, Storage, API, contextFactory, Host, () => DifficultyCache, LocalConfig));
dependencies.Cache(BeatmapManager = new BeatmapManager(Storage, contextFactory, RulesetStore, API, Audio, Host, defaultBeatmap, true));
// this should likely be moved to ArchiveModelManager when another case appers where it is necessary
@@ -226,10 +226,10 @@ namespace osu.Game
ScoreManager.Undelete(getBeatmapScores(item), true);
});
- dependencies.Cache(DifficultyManager = new BeatmapDifficultyManager());
- AddInternal(DifficultyManager);
+ dependencies.Cache(DifficultyCache = new BeatmapDifficultyCache());
+ AddInternal(DifficultyCache);
- var scorePerformanceManager = new ScorePerformanceManager();
+ var scorePerformanceManager = new ScorePerformanceCache();
dependencies.Cache(scorePerformanceManager);
AddInternal(scorePerformanceManager);
diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs
index 697ceacf0a..dae27f35ae 100644
--- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs
+++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs
@@ -59,7 +59,7 @@ namespace osu.Game.Overlays.Dashboard
var request = new GetUserRequest(u);
request.Success += user => Schedule(() =>
{
- if (playingUsers.Contains((int)user.Id))
+ if (playingUsers.Contains(user.Id))
userFlow.Add(createUserPanel(user));
});
api.Queue(request);
diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs
index 55adf02a45..9beb859f28 100644
--- a/osu.Game/Overlays/NowPlayingOverlay.cs
+++ b/osu.Game/Overlays/NowPlayingOverlay.cs
@@ -308,7 +308,6 @@ namespace osu.Game.Overlays
if (disabled)
playlist.Hide();
- playButton.Enabled.Value = !disabled;
prevButton.Enabled.Value = !disabled;
nextButton.Enabled.Value = !disabled;
playlistButton.Enabled.Value = !disabled;
diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs
index d52ad84592..81027667fa 100644
--- a/osu.Game/Overlays/UserProfileOverlay.cs
+++ b/osu.Game/Overlays/UserProfileOverlay.cs
@@ -33,7 +33,7 @@ namespace osu.Game.Overlays
{
}
- public void ShowUser(long userId) => ShowUser(new User { Id = userId });
+ public void ShowUser(int userId) => ShowUser(new User { Id = userId });
public void ShowUser(User user, bool fetchOnline = true)
{
diff --git a/osu.Game/Rulesets/Judgements/JudgementResult.cs b/osu.Game/Rulesets/Judgements/JudgementResult.cs
index 3a35fd4433..e3b2501cdc 100644
--- a/osu.Game/Rulesets/Judgements/JudgementResult.cs
+++ b/osu.Game/Rulesets/Judgements/JudgementResult.cs
@@ -36,6 +36,12 @@ namespace osu.Game.Rulesets.Judgements
///
public double TimeOffset { get; internal set; }
+ ///
+ /// The absolute time at which this occurred.
+ /// Equal to the (end) time of the + .
+ ///
+ public double TimeAbsolute => HitObject.GetEndTime() + TimeOffset;
+
///
/// The combo prior to this occurring.
///
diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs
index ad01bf036c..df421adbe5 100644
--- a/osu.Game/Rulesets/Mods/ModHidden.cs
+++ b/osu.Game/Rulesets/Mods/ModHidden.cs
@@ -1,19 +1,16 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
-using osu.Game.Configuration;
+using System;
using osu.Game.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
-using System.Collections.Generic;
-using System.Linq;
-using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
namespace osu.Game.Rulesets.Mods
{
- public abstract class ModHidden : Mod, IReadFromConfig, IApplicableToDrawableHitObjects, IApplicableToScoreProcessor
+ public abstract class ModHidden : ModWithVisibilityAdjustment, IApplicableToScoreProcessor
{
public override string Name => "Hidden";
public override string Acronym => "HD";
@@ -21,37 +18,14 @@ namespace osu.Game.Rulesets.Mods
public override ModType Type => ModType.DifficultyIncrease;
public override bool Ranked => true;
- protected Bindable IncreaseFirstObjectVisibility = new Bindable();
-
///
/// Check whether the provided hitobject should be considered the "first" hideable object.
/// Can be used to skip spinners, for instance.
///
/// The hitobject to check.
+ [Obsolete("Use IsFirstAdjustableObject() instead.")] // Can be removed 20210506
protected virtual bool IsFirstHideableObject(DrawableHitObject hitObject) => true;
- public void ReadFromConfig(OsuConfigManager config)
- {
- IncreaseFirstObjectVisibility = config.GetBindable(OsuSetting.IncreaseFirstObjectVisibility);
- }
-
- public virtual void ApplyToDrawableHitObjects(IEnumerable drawables)
- {
- if (IncreaseFirstObjectVisibility.Value)
- {
- drawables = drawables.SkipWhile(h => !IsFirstHideableObject(h));
-
- var firstObject = drawables.FirstOrDefault();
- if (firstObject != null)
- firstObject.ApplyCustomUpdateState += ApplyFirstObjectIncreaseVisibilityState;
-
- drawables = drawables.Skip(1);
- }
-
- foreach (var dho in drawables)
- dho.ApplyCustomUpdateState += ApplyHiddenState;
- }
-
public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor)
{
// Default value of ScoreProcessor's Rank in Hidden Mod should be SS+
@@ -73,11 +47,26 @@ namespace osu.Game.Rulesets.Mods
}
}
+ protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state)
+ {
+#pragma warning disable 618
+ ApplyFirstObjectIncreaseVisibilityState(hitObject, state);
+#pragma warning restore 618
+ }
+
+ protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state)
+ {
+#pragma warning disable 618
+ ApplyHiddenState(hitObject, state);
+#pragma warning restore 618
+ }
+
///
/// Apply a special visibility state to the first object in a beatmap, if the user chooses to turn on the "increase first object visibility" setting.
///
/// The hit object to apply the state change to.
/// The state of the hit object.
+ [Obsolete("Use ApplyIncreasedVisibilityState() instead.")] // Can be removed 20210506
protected virtual void ApplyFirstObjectIncreaseVisibilityState(DrawableHitObject hitObject, ArmedState state)
{
}
@@ -87,6 +76,7 @@ namespace osu.Game.Rulesets.Mods
///
/// The hit object to apply the state change to.
/// The state of the hit object.
+ [Obsolete("Use ApplyNormalVisibilityState() instead.")] // Can be removed 20210506
protected virtual void ApplyHiddenState(DrawableHitObject hitObject, ArmedState state)
{
}
diff --git a/osu.Game/Rulesets/Mods/ModWithVisibilityAdjustment.cs b/osu.Game/Rulesets/Mods/ModWithVisibilityAdjustment.cs
new file mode 100644
index 0000000000..5b119b5e46
--- /dev/null
+++ b/osu.Game/Rulesets/Mods/ModWithVisibilityAdjustment.cs
@@ -0,0 +1,114 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Collections.Generic;
+using osu.Framework.Bindables;
+using osu.Game.Beatmaps;
+using osu.Game.Configuration;
+using osu.Game.Rulesets.Objects;
+using osu.Game.Rulesets.Objects.Drawables;
+
+namespace osu.Game.Rulesets.Mods
+{
+ ///
+ /// A which applies visibility adjustments to s
+ /// with an optional increased visibility adjustment depending on the user's "increase first object visibility" setting.
+ ///
+ public abstract class ModWithVisibilityAdjustment : Mod, IReadFromConfig, IApplicableToBeatmap, IApplicableToDrawableHitObjects
+ {
+ ///
+ /// The first adjustable object.
+ ///
+ protected HitObject FirstObject { get; private set; }
+
+ ///
+ /// Whether the visibility of should be increased.
+ ///
+ protected readonly Bindable IncreaseFirstObjectVisibility = new Bindable();
+
+ ///
+ /// Check whether the provided hitobject should be considered the "first" adjustable object.
+ /// Can be used to skip spinners, for instance.
+ ///
+ /// The hitobject to check.
+ protected virtual bool IsFirstAdjustableObject(HitObject hitObject) => true;
+
+ ///
+ /// Apply a special increased-visibility state to the first adjustable object.
+ /// Only applicable if the user chooses to turn on the "increase first object visibility" setting.
+ ///
+ /// The hit object to apply the state change to.
+ /// The state of the hitobject.
+ protected abstract void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state);
+
+ ///
+ /// Apply a normal visibility state adjustment to an object.
+ ///
+ /// The hit object to apply the state change to.
+ /// The state of the hitobject.
+ protected abstract void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state);
+
+ public virtual void ReadFromConfig(OsuConfigManager config)
+ {
+ config.BindWith(OsuSetting.IncreaseFirstObjectVisibility, IncreaseFirstObjectVisibility);
+ }
+
+ public virtual void ApplyToBeatmap(IBeatmap beatmap)
+ {
+ FirstObject = getFirstAdjustableObjectRecursive(beatmap.HitObjects);
+
+ HitObject getFirstAdjustableObjectRecursive(IReadOnlyList hitObjects)
+ {
+ foreach (var h in hitObjects)
+ {
+ if (IsFirstAdjustableObject(h))
+ return h;
+
+ var nestedResult = getFirstAdjustableObjectRecursive(h.NestedHitObjects);
+ if (nestedResult != null)
+ return nestedResult;
+ }
+
+ return null;
+ }
+ }
+
+ public virtual void ApplyToDrawableHitObjects(IEnumerable drawables)
+ {
+ foreach (var dho in drawables)
+ {
+ dho.ApplyCustomUpdateState += (o, state) =>
+ {
+ // Increased visibility is applied to the entire first object, including all of its nested hitobjects.
+ if (IncreaseFirstObjectVisibility.Value && isObjectEqualToOrNestedIn(o.HitObject, FirstObject))
+ ApplyIncreasedVisibilityState(o, state);
+ else
+ ApplyNormalVisibilityState(o, state);
+ };
+ }
+ }
+
+ ///
+ /// Checks whether a given object is nested within a target.
+ ///
+ /// The to check.
+ /// The which may be equal to or contain as a nested object.
+ /// Whether is equal to or nested within .
+ private bool isObjectEqualToOrNestedIn(HitObject toCheck, HitObject target)
+ {
+ if (target == null)
+ return false;
+
+ if (toCheck == target)
+ return true;
+
+ foreach (var h in target.NestedHitObjects)
+ {
+ if (isObjectEqualToOrNestedIn(toCheck, h))
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
index 1ef6c8c207..5c3f57c2d0 100644
--- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
+++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs
@@ -125,14 +125,14 @@ namespace osu.Game.Rulesets.Objects.Drawables
Result = CreateResult(judgement);
if (Result == null)
throw new InvalidOperationException($"{GetType().ReadableName()} must provide a {nameof(JudgementResult)} through {nameof(CreateResult)}.");
-
- LoadSamples();
}
protected override void LoadAsyncComplete()
{
base.LoadAsyncComplete();
+ LoadSamples();
+
HitObject.DefaultsApplied += onDefaultsApplied;
startTimeBindable = HitObject.StartTimeBindable.GetBoundCopy();
@@ -255,17 +255,20 @@ namespace osu.Game.Rulesets.Objects.Drawables
base.ClearTransformsAfter(double.MinValue, true);
using (BeginAbsoluteSequence(transformTime, true))
- {
UpdateInitialTransforms();
- var judgementOffset = Result?.TimeOffset ?? 0;
+ using (BeginAbsoluteSequence(StateUpdateTime, true))
+ UpdateStartTimeStateTransforms();
- using (BeginDelayedSequence(InitialLifetimeOffset + judgementOffset, true))
- {
- UpdateStateTransforms(newState);
- state.Value = newState;
- }
- }
+#pragma warning disable 618
+ using (BeginAbsoluteSequence(StateUpdateTime + (Result?.TimeOffset ?? 0), true))
+ UpdateStateTransforms(newState);
+#pragma warning restore 618
+
+ using (BeginAbsoluteSequence(HitStateUpdateTime, true))
+ UpdateHitStateTransforms(newState);
+
+ state.Value = newState;
if (LifetimeEnd == double.MaxValue && (state.Value != ArmedState.Idle || HitObject.HitWindows == null))
Expire();
@@ -297,10 +300,30 @@ namespace osu.Game.Rulesets.Objects.Drawables
/// In the case of a non-idle , and if was not set during this call, will be invoked.
///
/// The new armed state.
+ [Obsolete("Use UpdateStartTimeStateTransforms and UpdateHitStateTransforms instead")] // Can be removed 20210504
protected virtual void UpdateStateTransforms(ArmedState state)
{
}
+ ///
+ /// Apply passive transforms at the 's StartTime.
+ /// This is called each time changes.
+ /// Previous states are automatically cleared.
+ ///
+ protected virtual void UpdateStartTimeStateTransforms()
+ {
+ }
+
+ ///
+ /// Apply transforms based on the current . This call is offset by (HitObject.EndTime + Result.Offset), equivalent to when the user hit the object.
+ /// If was not set during this call, will be invoked.
+ /// Previous states are automatically cleared.
+ ///
+ /// The new armed state.
+ protected virtual void UpdateHitStateTransforms(ArmedState state)
+ {
+ }
+
public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null)
{
// Parent calls to this should be blocked for safety, as we are manually handling this in updateState.
@@ -454,6 +477,18 @@ namespace osu.Game.Rulesets.Objects.Drawables
///
protected virtual double InitialLifetimeOffset => 10000;
+ ///
+ /// The time at which state transforms should be applied that line up to 's StartTime.
+ /// This is used to offset calls to .
+ ///
+ public double StateUpdateTime => HitObject.StartTime;
+
+ ///
+ /// The time at which judgement dependent state transforms should be applied. This is equivalent of the (end) time of the object, in addition to any judgement offset.
+ /// This is used to offset calls to .
+ ///
+ public double HitStateUpdateTime => Result?.TimeAbsolute ?? HitObject.GetEndTime();
+
///
/// Will be called at least once after this has become not alive.
///
@@ -511,7 +546,11 @@ namespace osu.Game.Rulesets.Objects.Drawables
// Ensure that the judgement is given a valid time offset, because this may not get set by the caller
var endTime = HitObject.GetEndTime();
- Result.TimeOffset = Math.Min(HitObject.HitWindows.WindowFor(HitResult.Miss), Time.Current - endTime);
+ Result.TimeOffset = Time.Current - endTime;
+
+ double missWindow = HitObject.HitWindows.WindowFor(HitResult.Miss);
+ if (missWindow > 0)
+ Result.TimeOffset = Math.Min(Result.TimeOffset, missWindow);
if (Result.HasResult)
updateState(Result.IsHit ? ArmedState.Hit : ArmedState.Miss);
diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs
index 596e98a6bd..f5192f3a40 100644
--- a/osu.Game/Scoring/ScoreInfo.cs
+++ b/osu.Game/Scoring/ScoreInfo.cs
@@ -123,7 +123,7 @@ namespace osu.Game.Scoring
[JsonIgnore]
[Column("UserID")]
- public long? UserID
+ public int? UserID
{
get => User?.Id ?? 1;
set
diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs
index cce6153953..cf1d123c06 100644
--- a/osu.Game/Scoring/ScoreManager.cs
+++ b/osu.Game/Scoring/ScoreManager.cs
@@ -37,13 +37,13 @@ namespace osu.Game.Scoring
private readonly Func beatmaps;
[CanBeNull]
- private readonly Func difficulties;
+ private readonly Func difficulties;
[CanBeNull]
private readonly OsuConfigManager configManager;
public ScoreManager(RulesetStore rulesets, Func beatmaps, Storage storage, IAPIProvider api, IDatabaseContextFactory contextFactory, IIpcHost importHost = null,
- Func difficulties = null, OsuConfigManager configManager = null)
+ Func difficulties = null, OsuConfigManager configManager = null)
: base(storage, contextFactory, api, new ScoreStore(contextFactory, storage), importHost)
{
this.rulesets = rulesets;
@@ -121,14 +121,14 @@ namespace osu.Game.Scoring
public readonly Bindable ScoringMode = new Bindable();
private readonly ScoreInfo score;
- private readonly Func difficulties;
+ private readonly Func difficulties;
///
/// Creates a new .
///
/// The to provide the total score of.
- /// A function to retrieve the .
- public TotalScoreBindable(ScoreInfo score, Func difficulties)
+ /// A function to retrieve the .
+ public TotalScoreBindable(ScoreInfo score, Func difficulties)
{
this.score = score;
this.difficulties = difficulties;
diff --git a/osu.Game/Scoring/ScorePerformanceManager.cs b/osu.Game/Scoring/ScorePerformanceCache.cs
similarity index 71%
rename from osu.Game/Scoring/ScorePerformanceManager.cs
rename to osu.Game/Scoring/ScorePerformanceCache.cs
index ddda1b99af..435b93d7af 100644
--- a/osu.Game/Scoring/ScorePerformanceManager.cs
+++ b/osu.Game/Scoring/ScorePerformanceCache.cs
@@ -2,27 +2,23 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
using osu.Framework.Allocation;
-using osu.Framework.Graphics;
using osu.Game.Beatmaps;
+using osu.Game.Database;
namespace osu.Game.Scoring
{
///
- /// A global component which calculates and caches results of performance calculations for locally databased scores.
+ /// A component which performs and acts as a central cache for performance calculations of locally databased scores.
+ /// Currently not persisted between game sessions.
///
- public class ScorePerformanceManager : Component
+ public class ScorePerformanceCache : MemoryCachingComponent
{
- // this cache will grow indefinitely per session and should be considered temporary.
- // this whole component should likely be replaced with database persistence.
- private readonly ConcurrentDictionary performanceCache = new ConcurrentDictionary();
-
[Resolved]
- private BeatmapDifficultyManager difficultyManager { get; set; }
+ private BeatmapDifficultyCache difficultyCache { get; set; }
///
/// Calculates performance for the given .
@@ -33,7 +29,7 @@ namespace osu.Game.Scoring
{
var lookupKey = new PerformanceCacheLookup(score);
- if (performanceCache.TryGetValue(lookupKey, out double performance))
+ if (Cache.TryGetValue(lookupKey, out double performance))
return Task.FromResult((double?)performance);
return computePerformanceAsync(score, lookupKey, token);
@@ -41,7 +37,7 @@ namespace osu.Game.Scoring
private async Task computePerformanceAsync(ScoreInfo score, PerformanceCacheLookup lookupKey, CancellationToken token = default)
{
- var attributes = await difficultyManager.GetDifficultyAsync(score.Beatmap, score.Ruleset, score.Mods, token);
+ var attributes = await difficultyCache.GetDifficultyAsync(score.Beatmap, score.Ruleset, score.Mods, token);
// Performance calculation requires the beatmap and ruleset to be locally available. If not, return a default value.
if (attributes.Attributes == null)
@@ -53,7 +49,7 @@ namespace osu.Game.Scoring
var total = calculator?.Calculate();
if (total.HasValue)
- performanceCache[lookupKey] = total.Value;
+ Cache[lookupKey] = total.Value;
return total;
}
diff --git a/osu.Game/Screens/Play/Spectator.cs b/osu.Game/Screens/Play/Spectator.cs
index 9ed911efd5..0f593db277 100644
--- a/osu.Game/Screens/Play/Spectator.cs
+++ b/osu.Game/Screens/Play/Spectator.cs
@@ -182,7 +182,7 @@ namespace osu.Game.Screens.Play
spectatorStreaming.OnUserFinishedPlaying += userFinishedPlaying;
spectatorStreaming.OnNewFrames += userSentFrames;
- spectatorStreaming.WatchUser((int)targetUser.Id);
+ spectatorStreaming.WatchUser(targetUser.Id);
managerUpdated = beatmaps.ItemUpdated.GetBoundCopy();
managerUpdated.BindValueChanged(beatmapUpdated);
@@ -353,7 +353,7 @@ namespace osu.Game.Screens.Play
spectatorStreaming.OnUserFinishedPlaying -= userFinishedPlaying;
spectatorStreaming.OnNewFrames -= userSentFrames;
- spectatorStreaming.StopWatchingUser((int)targetUser.Id);
+ spectatorStreaming.StopWatchingUser(targetUser.Id);
}
managerUpdated?.UnbindAll();
diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs
index f9b7625913..33ee5d2ee4 100644
--- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs
+++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs
@@ -56,7 +56,7 @@ namespace osu.Game.Screens.Ranking.Expanded
}
[BackgroundDependencyLoader]
- private void load(BeatmapDifficultyManager beatmapDifficultyManager)
+ private void load(BeatmapDifficultyCache beatmapDifficultyCache)
{
var beatmap = score.Beatmap;
var metadata = beatmap.BeatmapSet?.Metadata ?? beatmap.Metadata;
@@ -143,7 +143,7 @@ namespace osu.Game.Screens.Ranking.Expanded
Spacing = new Vector2(5, 0),
Children = new Drawable[]
{
- new StarRatingDisplay(beatmapDifficultyManager.GetDifficulty(beatmap, score.Ruleset, score.Mods))
+ new StarRatingDisplay(beatmapDifficultyCache.GetDifficulty(beatmap, score.Ruleset, score.Mods))
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft
diff --git a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs
index ffb12d474b..f7e50fdc8a 100644
--- a/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs
+++ b/osu.Game/Screens/Ranking/Expanded/StarRatingDisplay.cs
@@ -34,7 +34,7 @@ namespace osu.Game.Screens.Ranking.Expanded
}
[BackgroundDependencyLoader]
- private void load(OsuColour colours, BeatmapDifficultyManager difficultyManager)
+ private void load(OsuColour colours, BeatmapDifficultyCache difficultyCache)
{
AutoSizeAxes = Axes.Both;
diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs
index cd9d8005c6..68da4ec724 100644
--- a/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs
+++ b/osu.Game/Screens/Ranking/Expanded/Statistics/PerformanceStatistic.cs
@@ -28,7 +28,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics
}
[BackgroundDependencyLoader]
- private void load(ScorePerformanceManager performanceManager)
+ private void load(ScorePerformanceCache performanceCache)
{
if (score.PP.HasValue)
{
@@ -36,8 +36,8 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics
}
else
{
- performanceManager.CalculatePerformanceAsync(score, cancellationTokenSource.Token)
- .ContinueWith(t => Schedule(() => setPerformanceValue(t.Result)), cancellationTokenSource.Token);
+ performanceCache.CalculatePerformanceAsync(score, cancellationTokenSource.Token)
+ .ContinueWith(t => Schedule(() => setPerformanceValue(t.Result)), cancellationTokenSource.Token);
}
}
diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs
index 2634f117de..04c1f6efe4 100644
--- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs
+++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs
@@ -41,7 +41,7 @@ namespace osu.Game.Screens.Select
private readonly IBindable ruleset = new Bindable();
[Resolved]
- private BeatmapDifficultyManager difficultyManager { get; set; }
+ private BeatmapDifficultyCache difficultyCache { get; set; }
private IBindable beatmapDifficulty;
@@ -100,7 +100,7 @@ namespace osu.Game.Screens.Select
cancellationSource = new CancellationTokenSource();
beatmapDifficulty?.UnbindAll();
- beatmapDifficulty = difficultyManager.GetBindableDifficulty(beatmap.BeatmapInfo, cancellationSource.Token);
+ beatmapDifficulty = difficultyCache.GetBindableDifficulty(beatmap.BeatmapInfo, cancellationSource.Token);
beatmapDifficulty.BindValueChanged(_ => updateDisplay());
updateDisplay();
diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs
index 49a370724e..e66469ff8d 100644
--- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs
+++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs
@@ -55,7 +55,7 @@ namespace osu.Game.Screens.Select.Carousel
private BeatmapSetOverlay beatmapOverlay { get; set; }
[Resolved]
- private BeatmapDifficultyManager difficultyManager { get; set; }
+ private BeatmapDifficultyCache difficultyCache { get; set; }
[Resolved(CanBeNull = true)]
private CollectionManager collectionManager { get; set; }
@@ -216,7 +216,7 @@ namespace osu.Game.Screens.Select.Carousel
if (Item.State.Value != CarouselItemState.Collapsed)
{
// We've potentially cancelled the computation above so a new bindable is required.
- starDifficultyBindable = difficultyManager.GetBindableDifficulty(beatmap, (starDifficultyCancellationSource = new CancellationTokenSource()).Token);
+ starDifficultyBindable = difficultyCache.GetBindableDifficulty(beatmap, (starDifficultyCancellationSource = new CancellationTokenSource()).Token);
starDifficultyBindable.BindValueChanged(d => starCounter.Current = (float)d.NewValue.Stars, true);
}
diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs
index 44c328187f..44d908fc46 100644
--- a/osu.Game/Screens/Select/Details/AdvancedStats.cs
+++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs
@@ -32,7 +32,7 @@ namespace osu.Game.Screens.Select.Details
private IBindable ruleset { get; set; }
[Resolved]
- private BeatmapDifficultyManager difficultyManager { get; set; }
+ private BeatmapDifficultyCache difficultyCache { get; set; }
protected readonly StatisticRow FirstValue, HpDrain, Accuracy, ApproachRate;
private readonly StatisticRow starDifficulty;
@@ -161,8 +161,8 @@ namespace osu.Game.Screens.Select.Details
starDifficultyCancellationSource = new CancellationTokenSource();
- normalStarDifficulty = difficultyManager.GetBindableDifficulty(Beatmap, ruleset.Value, null, starDifficultyCancellationSource.Token);
- moddedStarDifficulty = difficultyManager.GetBindableDifficulty(Beatmap, ruleset.Value, mods.Value, starDifficultyCancellationSource.Token);
+ normalStarDifficulty = difficultyCache.GetBindableDifficulty(Beatmap, ruleset.Value, null, starDifficultyCancellationSource.Token);
+ moddedStarDifficulty = difficultyCache.GetBindableDifficulty(Beatmap, ruleset.Value, mods.Value, starDifficultyCancellationSource.Token);
normalStarDifficulty.BindValueChanged(_ => updateDisplay());
moddedStarDifficulty.BindValueChanged(_ => updateDisplay(), true);
diff --git a/osu.Game/Skinning/PausableSkinnableSound.cs b/osu.Game/Skinning/PausableSkinnableSound.cs
index d340f67575..4f09aec0b6 100644
--- a/osu.Game/Skinning/PausableSkinnableSound.cs
+++ b/osu.Game/Skinning/PausableSkinnableSound.cs
@@ -4,6 +4,7 @@
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
+using osu.Framework.Threading;
using osu.Game.Audio;
using osu.Game.Screens.Play;
@@ -25,6 +26,8 @@ namespace osu.Game.Skinning
private readonly IBindable samplePlaybackDisabled = new Bindable();
+ private ScheduledDelegate scheduledStart;
+
[BackgroundDependencyLoader(true)]
private void load(ISamplePlaybackDisabler samplePlaybackDisabler)
{
@@ -39,12 +42,14 @@ namespace osu.Game.Skinning
// let non-looping samples that have already been started play out to completion (sounds better than abruptly cutting off).
if (!Looping) return;
+ cancelPendingStart();
+
if (disabled.NewValue)
base.Stop();
else
{
// schedule so we don't start playing a sample which is no longer alive.
- Schedule(() =>
+ scheduledStart = Schedule(() =>
{
if (RequestedPlaying)
base.Play();
@@ -56,6 +61,7 @@ namespace osu.Game.Skinning
public override void Play()
{
+ cancelPendingStart();
RequestedPlaying = true;
if (samplePlaybackDisabled.Value)
@@ -66,8 +72,15 @@ namespace osu.Game.Skinning
public override void Stop()
{
+ cancelPendingStart();
RequestedPlaying = false;
base.Stop();
}
+
+ private void cancelPendingStart()
+ {
+ scheduledStart?.Cancel();
+ scheduledStart = null;
+ }
}
}
diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs
index fe4f735325..68098f9d3b 100644
--- a/osu.Game/Tests/Visual/SkinnableTestScene.cs
+++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs
@@ -65,17 +65,15 @@ namespace osu.Game.Tests.Visual
private Drawable createProvider(Skin skin, Func creationFunction, IBeatmap beatmap)
{
var created = creationFunction();
+
createdDrawables.Add(created);
- var autoSize = created.RelativeSizeAxes == Axes.None;
+ SkinProvidingContainer mainProvider;
+ Container childContainer;
+ OutlineBox outlineBox;
+ SkinProvidingContainer skinProvider;
- var mainProvider = new SkinProvidingContainer(skin)
- {
- RelativeSizeAxes = !autoSize ? Axes.Both : Axes.None,
- AutoSizeAxes = autoSize ? Axes.Both : Axes.None,
- };
-
- return new Container
+ var children = new Container
{
RelativeSizeAxes = Axes.Both,
BorderColour = Color4.White,
@@ -96,27 +94,47 @@ namespace osu.Game.Tests.Visual
Scale = new Vector2(1.5f),
Padding = new MarginPadding(5),
},
- new Container
+ childContainer = new Container
{
- RelativeSizeAxes = !autoSize ? Axes.Both : Axes.None,
- AutoSizeAxes = autoSize ? Axes.Both : Axes.None,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
- new OutlineBox { Alpha = autoSize ? 1 : 0 },
- mainProvider.WithChild(
- new SkinProvidingContainer(Ruleset.Value.CreateInstance().CreateLegacySkinProvider(mainProvider, beatmap))
+ outlineBox = new OutlineBox(),
+ (mainProvider = new SkinProvidingContainer(skin)).WithChild(
+ skinProvider = new SkinProvidingContainer(Ruleset.Value.CreateInstance().CreateLegacySkinProvider(mainProvider, beatmap))
{
Child = created,
- RelativeSizeAxes = !autoSize ? Axes.Both : Axes.None,
- AutoSizeAxes = autoSize ? Axes.Both : Axes.None,
}
)
}
},
}
};
+
+ // run this once initially to bring things into a sane state as early as possible.
+ updateSizing();
+
+ // run this once after construction to handle the case the changes are made in a BDL/LoadComplete call.
+ Schedule(updateSizing);
+
+ return children;
+
+ void updateSizing()
+ {
+ var autoSize = created.RelativeSizeAxes == Axes.None;
+
+ foreach (var c in new[] { mainProvider, childContainer, skinProvider })
+ {
+ c.RelativeSizeAxes = Axes.None;
+ c.AutoSizeAxes = Axes.None;
+
+ c.RelativeSizeAxes = !autoSize ? Axes.Both : Axes.None;
+ c.AutoSizeAxes = autoSize ? Axes.Both : Axes.None;
+ }
+
+ outlineBox.Alpha = autoSize ? 1 : 0;
+ }
}
///
diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs
index 89786e3bd8..1fbc3d06f4 100644
--- a/osu.Game/Users/User.cs
+++ b/osu.Game/Users/User.cs
@@ -12,7 +12,7 @@ namespace osu.Game.Users
public class User : IEquatable
{
[JsonProperty(@"id")]
- public long Id = 1;
+ public int Id = 1;
[JsonProperty(@"join_date")]
public DateTimeOffset JoinDate;
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index e57807e989..3783ae7d5c 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -26,7 +26,7 @@
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 40ecfffcca..ed3ec9e48b 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -70,7 +70,7 @@
-
+
@@ -88,7 +88,7 @@
-
+