diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
index ae799875a9..b2d8e3f8a5 100644
--- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
+++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs
@@ -124,6 +124,9 @@ namespace osu.Game.Rulesets.Catch.Objects
X = X + Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
});
}
+
+ if (NestedHitObjects.LastOrDefault() is IHasComboInformation lastNested)
+ lastNested.LastInCombo = LastInCombo;
}
public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity;
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
index afa9bdbbd7..f60958d581 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs
@@ -58,6 +58,13 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
public override Pattern Generate()
{
+ if (TotalColumns == 1)
+ {
+ var pattern = new Pattern();
+ addToPattern(pattern, 0, HitObject.StartTime, endTime);
+ return pattern;
+ }
+
if (spanCount > 1)
{
if (segmentDuration <= 90)
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
index cec3e18ad6..b4160dc98b 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/HitObjectPatternGenerator.cs
@@ -77,10 +77,25 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
}
else
convertType |= PatternType.LowProbability;
+
+ if ((convertType & PatternType.KeepSingle) == 0)
+ {
+ if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && TotalColumns != 8)
+ convertType |= PatternType.Mirror;
+ else
+ convertType |= PatternType.Gathered;
+ }
}
public override Pattern Generate()
{
+ if (TotalColumns == 1)
+ {
+ var pattern = new Pattern();
+ addToPattern(pattern, 0);
+ return pattern;
+ }
+
int lastColumn = PreviousPattern.HitObjects.FirstOrDefault()?.Column ?? 0;
if ((convertType & PatternType.Reverse) > 0 && PreviousPattern.HitObjects.Any())
@@ -346,7 +361,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
addToCentre = false;
if ((convertType & PatternType.ForceNotStack) > 0)
- return getRandomNoteCount(p2 / 2, p2, (p2 + p3) / 2, p3);
+ return getRandomNoteCount(1 / 2f + p2 / 2, p2, (p2 + p3) / 2, p3);
switch (TotalColumns)
{
diff --git a/osu.Game.Tests/Visual/TestCaseVolumePieces.cs b/osu.Game.Tests/Visual/TestCaseVolumePieces.cs
index 449f48b7d7..3c5b91ccd2 100644
--- a/osu.Game.Tests/Visual/TestCaseVolumePieces.cs
+++ b/osu.Game.Tests/Visual/TestCaseVolumePieces.cs
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Game.Overlays.Volume;
+using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Tests.Visual
@@ -17,13 +18,21 @@ namespace osu.Game.Tests.Visual
{
VolumeMeter meter;
MuteButton mute;
- Add(meter = new VolumeMeter("MASTER", 125, Color4.Blue));
+ Add(meter = new VolumeMeter("MASTER", 125, Color4.Blue) { Position = new Vector2(10) });
+ AddSliderStep("master volume", 0, 10, 0, i => meter.Bindable.Value = i * 0.1);
+
+ Add(new VolumeMeter("BIG", 250, Color4.Red)
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Position = new Vector2(10),
+ });
+
Add(mute = new MuteButton
{
Margin = new MarginPadding { Top = 200 }
});
- AddSliderStep("master volume", 0, 10, 0, i => meter.Bindable.Value = i * 0.1);
AddToggleStep("mute", b => mute.Current.Value = b);
}
}
diff --git a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
index 0186a170c9..0528f7b3ae 100644
--- a/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
+++ b/osu.Game/Graphics/Containers/OsuFocusedOverlayContainer.cs
@@ -33,7 +33,7 @@ namespace osu.Game.Graphics.Containers
///
/// Whether mouse input should be blocked screen-wide while this overlay is visible.
- /// Performing mouse actions outside of the valid extents will hide the overlay but pass the events through.
+ /// Performing mouse actions outside of the valid extents will hide the overlay.
///
public virtual bool BlockScreenWideMouse => BlockPassThroughMouse;
diff --git a/osu.Game/Overlays/Direct/DirectGridPanel.cs b/osu.Game/Overlays/Direct/DirectGridPanel.cs
index 723e9e8b35..e286837746 100644
--- a/osu.Game/Overlays/Direct/DirectGridPanel.cs
+++ b/osu.Game/Overlays/Direct/DirectGridPanel.cs
@@ -149,7 +149,7 @@ namespace osu.Game.Overlays.Direct
{
new OsuSpriteText
{
- Text = $"{SetInfo.Metadata.Source}",
+ Text = SetInfo.Metadata.Source,
TextSize = 14,
Shadow = false,
Colour = colours.Gray5,
diff --git a/osu.Game/Overlays/Direct/DirectListPanel.cs b/osu.Game/Overlays/Direct/DirectListPanel.cs
index 6e3483604b..812a0e2073 100644
--- a/osu.Game/Overlays/Direct/DirectListPanel.cs
+++ b/osu.Game/Overlays/Direct/DirectListPanel.cs
@@ -160,7 +160,7 @@ namespace osu.Game.Overlays.Direct
},
new OsuSpriteText
{
- Text = $"from {SetInfo.Metadata.Source}",
+ Text = SetInfo.Metadata.Source,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
TextSize = 14,
diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs
index d96bb40165..a57d5fd183 100644
--- a/osu.Game/Overlays/MusicController.cs
+++ b/osu.Game/Overlays/MusicController.cs
@@ -66,34 +66,6 @@ namespace osu.Game.Overlays
AlwaysPresent = true;
}
- private Vector2 dragStart;
-
- protected override bool OnDragStart(InputState state)
- {
- base.OnDragStart(state);
- dragStart = state.Mouse.Position;
- return true;
- }
-
- protected override bool OnDrag(InputState state)
- {
- if (base.OnDrag(state)) return true;
-
- Vector2 change = state.Mouse.Position - dragStart;
-
- // Diminish the drag distance as we go further to simulate "rubber band" feeling.
- change *= change.Length <= 0 ? 0 : (float)Math.Pow(change.Length, 0.7f) / change.Length;
-
- dragContainer.MoveTo(change);
- return true;
- }
-
- protected override bool OnDragEnd(InputState state)
- {
- dragContainer.MoveTo(Vector2.Zero, 800, Easing.OutElastic);
- return base.OnDragEnd(state);
- }
-
[BackgroundDependencyLoader]
private void load(BindableBeatmap beatmap, BeatmapManager beatmaps, OsuColour colours, LocalisationEngine localisation)
{
@@ -103,7 +75,7 @@ namespace osu.Game.Overlays
Children = new Drawable[]
{
- dragContainer = new Container
+ dragContainer = new DragContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
@@ -470,5 +442,36 @@ namespace osu.Game.Overlays
sprite.Texture = beatmap?.Background ?? textures.Get(@"Backgrounds/bg4");
}
}
+
+ private class DragContainer : Container
+ {
+ private Vector2 dragStart;
+
+ protected override bool OnDragStart(InputState state)
+ {
+ base.OnDragStart(state);
+ dragStart = state.Mouse.Position;
+ return true;
+ }
+
+ protected override bool OnDrag(InputState state)
+ {
+ if (base.OnDrag(state)) return true;
+
+ Vector2 change = state.Mouse.Position - dragStart;
+
+ // Diminish the drag distance as we go further to simulate "rubber band" feeling.
+ change *= change.Length <= 0 ? 0 : (float)Math.Pow(change.Length, 0.7f) / change.Length;
+
+ this.MoveTo(change);
+ return true;
+ }
+
+ protected override bool OnDragEnd(InputState state)
+ {
+ this.MoveTo(Vector2.Zero, 800, Easing.OutElastic);
+ return base.OnDragEnd(state);
+ }
+ }
}
}
diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs
index b2cf43704b..0e43945f8c 100644
--- a/osu.Game/Overlays/Volume/VolumeMeter.cs
+++ b/osu.Game/Overlays/Volume/VolumeMeter.cs
@@ -24,6 +24,8 @@ namespace osu.Game.Overlays.Volume
public class VolumeMeter : Container, IKeyBindingHandler
{
private CircularProgress volumeCircle;
+ private CircularProgress volumeCircleGlow;
+
public BindableDouble Bindable { get; } = new BindableDouble { MinValue = 0, MaxValue = 1 };
private readonly float circleSize;
private readonly Color4 meterColour;
@@ -44,90 +46,143 @@ namespace osu.Game.Overlays.Volume
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
- Add(new Container
- {
- Size = new Vector2(120, 20),
- CornerRadius = 10,
- Masking = true,
- Margin = new MarginPadding { Left = circleSize + 10 },
- Origin = Anchor.CentreLeft,
- Anchor = Anchor.CentreLeft,
- Children = new Drawable[]
- {
- new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = colours.Gray1,
- Alpha = 0.9f,
- },
- new OsuSpriteText
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Font = "Exo2.0-Bold",
- Text = name
- }
- }
- });
+ Color4 backgroundColour = colours.Gray1;
CircularProgress bgProgress;
- Add(new CircularContainer
+ const float progress_start_radius = 0.75f;
+ const float progress_size = 0.03f;
+ const float progress_end_radius = progress_start_radius + progress_size;
+
+ const float blur_amount = 5;
+
+ Children = new Drawable[]
{
- Masking = true,
- Size = new Vector2(circleSize),
- Children = new Drawable[]
+ new Container
{
- new Box
+ Size = new Vector2(circleSize),
+ Children = new Drawable[]
{
- RelativeSizeAxes = Axes.Both,
- Colour = colours.Gray1,
- Alpha = 0.9f,
- },
- bgProgress = new CircularProgress
- {
- RelativeSizeAxes = Axes.Both,
- InnerRadius = 0.05f,
- Rotation = 180,
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Colour = colours.Gray2,
- Size = new Vector2(0.8f)
- },
- new Container
- {
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- RelativeSizeAxes = Axes.Both,
- Size = new Vector2(0.8f),
- Padding = new MarginPadding(-Blur.KernelSize(5)),
- Rotation = 180,
- Child = (volumeCircle = new CircularProgress
+ new BufferedContainer
{
+ Alpha = 0.9f,
RelativeSizeAxes = Axes.Both,
- InnerRadius = 0.05f,
+ Children = new Drawable[]
+ {
+ new Circle
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = backgroundColour,
+ },
+ new CircularContainer
+ {
+ Masking = true,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Size = new Vector2(progress_end_radius),
+ Children = new Drawable[]
+ {
+ bgProgress = new CircularProgress
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Rotation = 180,
+ Colour = backgroundColour,
+ },
+ new Container
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Name = "Progress under covers for smoothing",
+ RelativeSizeAxes = Axes.Both,
+ Rotation = 180,
+ Child = volumeCircle = new CircularProgress
+ {
+ RelativeSizeAxes = Axes.Both,
+ }
+ },
+ }
+ },
+ new Circle
+ {
+ Name = "Inner Cover",
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Colour = backgroundColour,
+ Size = new Vector2(progress_start_radius),
+ },
+ new Container
+ {
+ Name = "Progress overlay for glow",
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ RelativeSizeAxes = Axes.Both,
+ Size = new Vector2(progress_start_radius + progress_size / 1.5f),
+ Rotation = 180,
+ Padding = new MarginPadding(-Blur.KernelSize(blur_amount)),
+ Child = (volumeCircleGlow = new CircularProgress
+ {
+ RelativeSizeAxes = Axes.Both,
+ InnerRadius = progress_size * 0.8f,
+ }).WithEffect(new GlowEffect
+ {
+ Colour = meterColour,
+ BlurSigma = new Vector2(blur_amount),
+ Strength = 5,
+ PadExtent = true
+ }),
+ },
+ },
+ },
+ maxGlow = (text = new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Font = "Venera",
+ TextSize = 0.16f * circleSize
}).WithEffect(new GlowEffect
{
- Colour = meterColour,
- Strength = 2,
- PadExtent = true
- }),
- },
- maxGlow = (text = new OsuSpriteText
+ Colour = Color4.Transparent,
+ PadExtent = true,
+ })
+ }
+ },
+ new Container
+ {
+ Size = new Vector2(120, 20),
+ CornerRadius = 10,
+ Masking = true,
+ Margin = new MarginPadding { Left = circleSize + 10 },
+ Origin = Anchor.CentreLeft,
+ Anchor = Anchor.CentreLeft,
+ Children = new Drawable[]
{
- Anchor = Anchor.Centre,
- Origin = Anchor.Centre,
- Font = "Venera",
- TextSize = 0.16f * circleSize
- }).WithEffect(new GlowEffect
- {
- Colour = Color4.Transparent,
- PadExtent = true,
- })
+ new Box
+ {
+ Alpha = 0.9f,
+ RelativeSizeAxes = Axes.Both,
+ Colour = backgroundColour,
+ },
+ new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Font = "Exo2.0-Bold",
+ Text = name
+ }
+ }
}
- });
-
- Bindable.ValueChanged += newVolume => { this.TransformTo("DisplayVolume", newVolume, 400, Easing.OutQuint); };
+ };
+ Bindable.ValueChanged += newVolume =>
+ {
+ this.TransformTo("DisplayVolume",
+ newVolume,
+ 400,
+ Easing.OutQuint);
+ };
bgProgress.Current.Value = 0.75f;
}
@@ -158,6 +213,7 @@ namespace osu.Game.Overlays.Volume
}
volumeCircle.Current.Value = displayVolume * 0.75f;
+ volumeCircleGlow.Current.Value = displayVolume * 0.75f;
}
}
diff --git a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs
index 7470f6ebed..cf4dda52a8 100644
--- a/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs
+++ b/osu.Game/Tests/Beatmaps/BeatmapConversionTest.cs
@@ -16,7 +16,8 @@ using osu.Game.Rulesets.Objects;
namespace osu.Game.Tests.Beatmaps
{
[TestFixture]
- public abstract class BeatmapConversionTest
+ public abstract class BeatmapConversionTest
+ where TConvertMapping : ConvertMapping, IEquatable, new()
where TConvertValue : IEquatable
{
private const string resource_namespace = "Testing.Beatmaps";
@@ -59,9 +60,13 @@ namespace osu.Game.Tests.Beatmaps
else if (objectCounter >= expectedMapping.Objects.Count)
Assert.Fail($"The conversion generated a hitobject, but should not have, for hitobject at time: {ourMapping.StartTime}:\n"
+ $"Received: {JsonConvert.SerializeObject(ourMapping.Objects[objectCounter])}\n");
- else if (!EqualityComparer.Default.Equals(expectedMapping.Objects[objectCounter], ourMapping.Objects[objectCounter]))
+ else if (!expectedMapping.Equals(ourMapping))
+ Assert.Fail($"The conversion mapping differed for object at time {expectedMapping.StartTime}:\n"
+ + $"Expected {JsonConvert.SerializeObject(expectedMapping)}\n"
+ + $"Received: {JsonConvert.SerializeObject(ourMapping)}\n");
+ else if (!expectedMapping.Objects[objectCounter].Equals(ourMapping.Objects[objectCounter]))
{
- Assert.Fail($"The conversion generated differing hitobjects for object at time: {expectedMapping.StartTime}\n"
+ Assert.Fail($"The conversion generated differing hitobjects for object at time: {expectedMapping.StartTime}:\n"
+ $"Expected: {JsonConvert.SerializeObject(expectedMapping.Objects[objectCounter])}\n"
+ $"Received: {JsonConvert.SerializeObject(ourMapping.Objects[objectCounter])}\n");
}
@@ -84,19 +89,22 @@ namespace osu.Game.Tests.Beatmaps
beatmap.BeatmapInfo.Ruleset = beatmap.BeatmapInfo.RulesetID == rulesetInstance.RulesetInfo.ID ? rulesetInstance.RulesetInfo : new RulesetInfo();
var result = new ConvertResult();
-
var converter = rulesetInstance.CreateBeatmapConverter(beatmap);
+
converter.ObjectConverted += (orig, converted) =>
{
converted.ForEach(h => h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty));
- var mapping = new ConvertMapping { StartTime = orig.StartTime };
+ var mapping = CreateConvertMapping();
+ mapping.StartTime = orig.StartTime;
+
foreach (var obj in converted)
mapping.Objects.AddRange(CreateConvertValue(obj));
result.Mappings.Add(mapping);
};
- converter.Convert();
+ IBeatmap convertedBeatmap = converter.Convert();
+ rulesetInstance.CreateBeatmapProcessor(convertedBeatmap)?.PostProcess();
return result;
}
@@ -128,21 +136,54 @@ namespace osu.Game.Tests.Beatmaps
return Assembly.LoadFrom(Path.Combine(localPath, $"{ResourceAssembly}.dll")).GetManifestResourceStream($@"{ResourceAssembly}.Resources.{name}");
}
- protected abstract IEnumerable CreateConvertValue(HitObject hitObject);
- protected abstract Ruleset CreateRuleset();
+ ///
+ /// Creates the conversion mapping for a . A conversion mapping stores important information about the conversion process.
+ /// This is generated _after_ the has been converted.
+ ///
+ /// This should be used to validate the integrity of the conversion process after a conversion has occurred.
+ ///
+ ///
+ protected virtual TConvertMapping CreateConvertMapping() => new TConvertMapping();
- private class ConvertMapping
- {
- [JsonProperty]
- public double StartTime;
- [JsonProperty]
- public List Objects = new List();
- }
+ ///
+ /// Creates the conversion value for a . A conversion value stores information about the converted .
+ ///
+ /// This should be used to validate the integrity of the converted .
+ ///
+ ///
+ /// The converted .
+ protected abstract IEnumerable CreateConvertValue(HitObject hitObject);
+
+ ///
+ /// Creates the applicable to this .
+ ///
+ ///
+ protected abstract Ruleset CreateRuleset();
private class ConvertResult
{
[JsonProperty]
- public List Mappings = new List();
+ public List Mappings = new List();
}
}
+
+ public abstract class BeatmapConversionTest : BeatmapConversionTest, TConvertValue>
+ where TConvertValue : IEquatable
+ {
+ }
+
+ public class ConvertMapping : IEquatable>
+ where TConvertValue : IEquatable
+ {
+ [JsonProperty]
+ public double StartTime;
+
+ [JsonIgnore]
+ public List Objects = new List();
+
+ [JsonProperty("Objects")]
+ private List setObjects { set => Objects = value; }
+
+ public virtual bool Equals(ConvertMapping other) => StartTime.Equals(other?.StartTime);
+ }
}