diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs
index cdaa8fa3d5..3c2c5d7759 100644
--- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs
+++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs
@@ -35,18 +35,18 @@ namespace osu.Game.Rulesets.Osu.Mods
rng = new Random((int)Seed.Value);
- var positionModifier = new OsuHitObjectPositionModifier(osuBeatmap.HitObjects);
+ var positionInfos = OsuHitObjectGenerationUtils.GeneratePositionInfos(osuBeatmap.HitObjects);
float rateOfChangeMultiplier = 0;
- foreach (var positionInfo in positionModifier.ObjectPositionInfos)
+ foreach (var positionInfo in positionInfos)
{
// rateOfChangeMultiplier only changes every 5 iterations in a combo
// to prevent shaky-line-shaped streams
if (positionInfo.HitObject.IndexInCurrentCombo % 5 == 0)
rateOfChangeMultiplier = (float)rng.NextDouble() * 2 - 1;
- if (positionInfo == positionModifier.ObjectPositionInfos.First())
+ if (positionInfo == positionInfos.First())
{
positionInfo.DistanceFromPrevious = (float)rng.NextDouble() * OsuPlayfield.BASE_SIZE.Y / 2;
positionInfo.RelativeAngle = (float)(rng.NextDouble() * 2 * Math.PI - Math.PI);
@@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Mods
}
}
- positionModifier.ApplyModifications();
+ osuBeatmap.HitObjects = OsuHitObjectGenerationUtils.RepositionHitObjects(positionInfos);
}
}
}
diff --git a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs
index 97a4b14a62..da73c2addb 100644
--- a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs
+++ b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils.cs
@@ -11,7 +11,7 @@ using osuTK;
namespace osu.Game.Rulesets.Osu.Utils
{
- public static class OsuHitObjectGenerationUtils
+ public static partial class OsuHitObjectGenerationUtils
{
// The relative distance to the edge of the playfield before objects' positions should start to "turn around" and curve towards the middle.
// The closer the hit objects draw to the border, the sharper the turn
diff --git a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectPositionModifier.cs b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils_Reposition.cs
similarity index 84%
rename from osu.Game.Rulesets.Osu/Utils/OsuHitObjectPositionModifier.cs
rename to osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils_Reposition.cs
index 32f547dfe7..2a735c89d9 100644
--- a/osu.Game.Rulesets.Osu/Utils/OsuHitObjectPositionModifier.cs
+++ b/osu.Game.Rulesets.Osu/Utils/OsuHitObjectGenerationUtils_Reposition.cs
@@ -13,10 +13,7 @@ using osuTK;
namespace osu.Game.Rulesets.Osu.Utils
{
- ///
- /// Places hit objects according to information in while keeping objects inside the playfield.
- ///
- public class OsuHitObjectPositionModifier
+ public static partial class OsuHitObjectGenerationUtils
{
///
/// Number of previous hitobjects to be shifted together when an object is being moved.
@@ -25,24 +22,15 @@ namespace osu.Game.Rulesets.Osu.Utils
private static readonly Vector2 playfield_centre = OsuPlayfield.BASE_SIZE / 2;
- private readonly List hitObjects;
-
- private readonly List objectPositionInfos = new List();
-
///
- /// Contains information specifying how each hit object should be placed.
- /// The default values correspond to how objects are originally placed in the beatmap.
+ /// Generate a list of s containing information for how the given list of
+ /// s are positioned.
///
- public IReadOnlyList ObjectPositionInfos => objectPositionInfos;
-
- public OsuHitObjectPositionModifier(List hitObjects)
- {
- this.hitObjects = hitObjects;
- populateObjectPositionInfos();
- }
-
- private void populateObjectPositionInfos()
+ /// A list of s to process.
+ /// A list of s describing how each hit object is positioned relative to the previous one.
+ public static List GeneratePositionInfos(IEnumerable hitObjects)
{
+ var positionInfos = new List();
Vector2 previousPosition = playfield_centre;
float previousAngle = 0;
@@ -52,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Utils
float absoluteAngle = (float)Math.Atan2(relativePosition.Y, relativePosition.X);
float relativeAngle = absoluteAngle - previousAngle;
- objectPositionInfos.Add(new ObjectPositionInfo(hitObject)
+ positionInfos.Add(new ObjectPositionInfo(hitObject)
{
RelativeAngle = relativeAngle,
DistanceFromPrevious = relativePosition.Length
@@ -61,18 +49,23 @@ namespace osu.Game.Rulesets.Osu.Utils
previousPosition = hitObject.EndPosition;
previousAngle = absoluteAngle;
}
+
+ return positionInfos;
}
///
- /// Reposition the hit objects according to the information in .
+ /// Reposition the hit objects according to the information in .
///
- public void ApplyModifications()
+ ///
+ /// The repositioned hit objects.
+ public static List RepositionHitObjects(IEnumerable objectPositionInfos)
{
+ List positionInfos = objectPositionInfos.Cast().ToList();
ObjectPositionInfo? previous = null;
- for (int i = 0; i < objectPositionInfos.Count; i++)
+ for (int i = 0; i < positionInfos.Count; i++)
{
- var current = objectPositionInfos[i];
+ var current = positionInfos[i];
var hitObject = current.HitObject;
if (hitObject is Spinner)
@@ -81,7 +74,7 @@ namespace osu.Game.Rulesets.Osu.Utils
continue;
}
- computeModifiedPosition(current, previous, i > 1 ? objectPositionInfos[i - 2] : null);
+ computeModifiedPosition(current, previous, i > 1 ? positionInfos[i - 2] : null);
// Move hit objects back into the playfield if they are outside of it
Vector2 shift = Vector2.Zero;
@@ -104,9 +97,9 @@ namespace osu.Game.Rulesets.Osu.Utils
for (int j = i - 1; j >= i - preceding_hitobjects_to_shift && j >= 0; j--)
{
// only shift hit circles
- if (!(hitObjects[j] is HitCircle)) break;
+ if (!(positionInfos[j].HitObject is HitCircle)) break;
- toBeShifted.Add(hitObjects[j]);
+ toBeShifted.Add(positionInfos[j].HitObject);
}
if (toBeShifted.Count > 0)
@@ -115,6 +108,8 @@ namespace osu.Game.Rulesets.Osu.Utils
previous = current;
}
+
+ return positionInfos.Select(p => p.HitObject).ToList();
}
///
@@ -123,7 +118,7 @@ namespace osu.Game.Rulesets.Osu.Utils
/// The representing the hit object to have the modified position computed for.
/// The representing the hit object immediately preceding the current one.
/// The representing the hit object immediately preceding the one.
- private void computeModifiedPosition(ObjectPositionInfo current, ObjectPositionInfo? previous, ObjectPositionInfo? beforePrevious)
+ private static void computeModifiedPosition(ObjectPositionInfo current, ObjectPositionInfo? previous, ObjectPositionInfo? beforePrevious)
{
float previousAbsoluteAngle = 0f;
@@ -143,7 +138,7 @@ namespace osu.Game.Rulesets.Osu.Utils
Vector2 lastEndPosition = previous?.EndPositionModified ?? playfield_centre;
- posRelativeToPrev = OsuHitObjectGenerationUtils.RotateAwayFromEdge(lastEndPosition, posRelativeToPrev);
+ posRelativeToPrev = RotateAwayFromEdge(lastEndPosition, posRelativeToPrev);
current.PositionModified = lastEndPosition + posRelativeToPrev;
}
@@ -152,7 +147,7 @@ namespace osu.Game.Rulesets.Osu.Utils
/// Move the modified position of a hit circle so that it fits inside the playfield.
///
/// The deviation from the original modified position in order to fit within the playfield.
- private Vector2 clampHitCircleToPlayfield(HitCircle circle, ObjectPositionInfo objectPositionInfo)
+ private static Vector2 clampHitCircleToPlayfield(HitCircle circle, ObjectPositionInfo objectPositionInfo)
{
var previousPosition = objectPositionInfo.PositionModified;
objectPositionInfo.EndPositionModified = objectPositionInfo.PositionModified = clampToPlayfieldWithPadding(
@@ -169,7 +164,7 @@ namespace osu.Game.Rulesets.Osu.Utils
/// Moves the and all necessary nested s into the if they aren't already.
///
/// The deviation from the original modified position in order to fit within the playfield.
- private Vector2 clampSliderToPlayfield(Slider slider, ObjectPositionInfo objectPositionInfo)
+ private static Vector2 clampSliderToPlayfield(Slider slider, ObjectPositionInfo objectPositionInfo)
{
var possibleMovementBounds = calculatePossibleMovementBounds(slider);
@@ -199,7 +194,7 @@ namespace osu.Game.Rulesets.Osu.Utils
///
/// The list of hit objects to be shifted.
/// The amount to be shifted.
- private void applyDecreasingShift(IList hitObjects, Vector2 shift)
+ private static void applyDecreasingShift(IList hitObjects, Vector2 shift)
{
for (int i = 0; i < hitObjects.Count; i++)
{
@@ -219,7 +214,7 @@ namespace osu.Game.Rulesets.Osu.Utils
///
/// If the slider is larger than the playfield, the returned may have negative width/height.
///
- private RectangleF calculatePossibleMovementBounds(Slider slider)
+ private static RectangleF calculatePossibleMovementBounds(Slider slider)
{
var pathPositions = new List();
slider.Path.GetPathToProgress(pathPositions, 0, 1);
@@ -266,7 +261,7 @@ namespace osu.Game.Rulesets.Osu.Utils
///
/// whose nested s and s should be shifted
/// The the 's nested s and s should be shifted by
- private void shiftNestedObjects(Slider slider, Vector2 shift)
+ private static void shiftNestedObjects(Slider slider, Vector2 shift)
{
foreach (var hitObject in slider.NestedHitObjects.Where(o => o is SliderTick || o is SliderRepeat))
{
@@ -283,7 +278,7 @@ namespace osu.Game.Rulesets.Osu.Utils
/// The position to be clamped.
/// The minimum distance allowed from playfield edges.
/// The clamped position.
- private Vector2 clampToPlayfieldWithPadding(Vector2 position, float padding)
+ private static Vector2 clampToPlayfieldWithPadding(Vector2 position, float padding)
{
return new Vector2(
Math.Clamp(position.X, padding, OsuPlayfield.BASE_SIZE.X - padding),