Example #1
0
        /// <summary>
        /// Returns the final position of the hit object
        /// </summary>
        /// <returns>Final position of the hit object</returns>
        private void applyRandomisation(float rateOfChangeMultiplier, RandomObjectInfo previous, RandomObjectInfo current)
        {
            if (previous == null)
            {
                var playfieldSize = OsuPlayfield.BASE_SIZE;

                current.AngleRad           = (float)(rng.NextDouble() * 2 * Math.PI - Math.PI);
                current.PositionRandomised = new Vector2((float)rng.NextDouble() * playfieldSize.X, (float)rng.NextDouble() * playfieldSize.Y);

                return;
            }

            float distanceToPrev = Vector2.Distance(previous.EndPositionOriginal, current.PositionOriginal);

            // The max. angle (relative to the angle of the vector pointing from the 2nd last to the last hit object)
            // is proportional to the distance between the last and the current hit object
            // to allow jumps and prevent too sharp turns during streams.
            var randomAngleRad = rateOfChangeMultiplier * 2 * Math.PI * distanceToPrev / playfield_diagonal;

            current.AngleRad = (float)randomAngleRad + previous.AngleRad;
            if (current.AngleRad < 0)
            {
                current.AngleRad += 2 * (float)Math.PI;
            }

            var posRelativeToPrev = new Vector2(
                distanceToPrev * (float)Math.Cos(current.AngleRad),
                distanceToPrev * (float)Math.Sin(current.AngleRad)
                );

            posRelativeToPrev = OsuHitObjectGenerationUtils.RotateAwayFromEdge(previous.EndPositionRandomised, posRelativeToPrev);

            current.AngleRad = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X);

            var position = previous.EndPositionRandomised + posRelativeToPrev;

            // Move hit objects back into the playfield if they are outside of it,
            // which would sometimes happen during big jumps otherwise.
            position.X = MathHelper.Clamp(position.X, 0, OsuPlayfield.BASE_SIZE.X);
            position.Y = MathHelper.Clamp(position.Y, 0, OsuPlayfield.BASE_SIZE.Y);

            current.PositionRandomised = position;
        }
Example #2
0
        public void ApplyToHitObject(HitObject hitObject)
        {
            var osuObject = (OsuHitObject)hitObject;

            switch (Reflection.Value)
            {
            case MirrorType.Horizontal:
                OsuHitObjectGenerationUtils.ReflectHorizontally(osuObject);
                break;

            case MirrorType.Vertical:
                OsuHitObjectGenerationUtils.ReflectVertically(osuObject);
                break;

            case MirrorType.Both:
                OsuHitObjectGenerationUtils.ReflectHorizontally(osuObject);
                OsuHitObjectGenerationUtils.ReflectVertically(osuObject);
                break;
            }
        }
Example #3
0
        /// <summary>
        /// Returns the final position of the hit object
        /// </summary>
        /// <returns>Final position of the hit object</returns>
        private void applyRandomisation(float rateOfChangeMultiplier, RandomObjectInfo previous, RandomObjectInfo current)
        {
            if (previous == null)
            {
                var playfieldSize = OsuPlayfield.BASE_SIZE;

                current.AngleRad           = (float)(rng.NextDouble() * 2 * Math.PI - Math.PI);
                current.PositionRandomised = new Vector2((float)rng.NextDouble() * playfieldSize.X, (float)rng.NextDouble() * playfieldSize.Y);

                return;
            }

            float distanceToPrev = Vector2.Distance(previous.EndPositionOriginal, current.PositionOriginal);

            // The max. angle (relative to the angle of the vector pointing from the 2nd last to the last hit object)
            // is proportional to the distance between the last and the current hit object
            // to allow jumps and prevent too sharp turns during streams.

            // Allow maximum jump angle when jump distance is more than half of playfield diagonal length
            var randomAngleRad = rateOfChangeMultiplier * 2 * Math.PI * Math.Min(1f, distanceToPrev / (playfield_diagonal * 0.5f));

            current.AngleRad = (float)randomAngleRad + previous.AngleRad;
            if (current.AngleRad < 0)
            {
                current.AngleRad += 2 * (float)Math.PI;
            }

            var posRelativeToPrev = new Vector2(
                distanceToPrev * (float)Math.Cos(current.AngleRad),
                distanceToPrev * (float)Math.Sin(current.AngleRad)
                );

            posRelativeToPrev = OsuHitObjectGenerationUtils.RotateAwayFromEdge(previous.EndPositionRandomised, posRelativeToPrev);

            current.AngleRad = (float)Math.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X);

            current.PositionRandomised = previous.EndPositionRandomised + posRelativeToPrev;
        }
Example #4
0
        public void ApplyToBeatmap(IBeatmap beatmap)
        {
            if (!(beatmap is OsuBeatmap osuBeatmap))
            {
                return;
            }

            Seed.Value ??= RNG.Next();

            rng = new Random((int)Seed.Value);

            var positionInfos = OsuHitObjectGenerationUtils.GeneratePositionInfos(osuBeatmap.HitObjects);

            float rateOfChangeMultiplier = 0;

            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 == positionInfos.First())
                {
                    positionInfo.DistanceFromPrevious = (float)(rng.NextDouble() * OsuPlayfield.BASE_SIZE.X / 2);
                    positionInfo.RelativeAngle        = (float)(rng.NextDouble() * 2 * Math.PI - Math.PI);
                }
                else
                {
                    positionInfo.RelativeAngle = rateOfChangeMultiplier * 2 * (float)Math.PI * Math.Min(1f, positionInfo.DistanceFromPrevious / (playfield_diagonal * 0.5f));
                }
            }

            osuBeatmap.HitObjects = OsuHitObjectGenerationUtils.RepositionHitObjects(positionInfos);
        }
Example #5
0
        private void computeRandomisedPosition(RandomObjectInfo current, RandomObjectInfo?previous, RandomObjectInfo?beforePrevious)
        {
            float previousAbsoluteAngle = 0f;

            if (previous != null)
            {
                Vector2 earliestPosition = beforePrevious?.HitObject.EndPosition ?? playfield_centre;
                Vector2 relativePosition = previous.HitObject.Position - earliestPosition;
                previousAbsoluteAngle = (float)Math.Atan2(relativePosition.Y, relativePosition.X);
            }

            float absoluteAngle = previousAbsoluteAngle + current.RelativeAngle;

            var posRelativeToPrev = new Vector2(
                current.DistanceFromPrevious * (float)Math.Cos(absoluteAngle),
                current.DistanceFromPrevious * (float)Math.Sin(absoluteAngle)
                );

            Vector2 lastEndPosition = previous?.EndPositionRandomised ?? playfield_centre;

            posRelativeToPrev = OsuHitObjectGenerationUtils.RotateAwayFromEdge(lastEndPosition, posRelativeToPrev);

            current.PositionRandomised = lastEndPosition + posRelativeToPrev;
        }
Example #6
0
        private void randomizeCirclePos(IReadOnlyList <OsuHitObject> hitObjects)
        {
            if (hitObjects.Count == 0)
            {
                return;
            }

            float nextSingle(float max = 1f) => (float)(rng.NextDouble() * max);

            const float two_pi = MathF.PI * 2;

            float direction     = two_pi * nextSingle();
            int   maxComboIndex = hitObjects.Last().ComboIndex;

            for (int i = 0; i < hitObjects.Count; i++)
            {
                var obj     = hitObjects[i];
                var lastPos = i == 0
                    ? Vector2.Divide(OsuPlayfield.BASE_SIZE, 2)
                    : hitObjects[i - 1].Position;

                float distance = maxComboIndex == 0
                    ? (float)obj.Radius
                    : mapRange(obj.ComboIndex, 0, maxComboIndex, (float)obj.Radius, max_base_distance);
                if (obj.NewCombo)
                {
                    distance *= 1.5f;
                }
                if (obj.Kiai)
                {
                    distance *= 1.2f;
                }
                distance = Math.Min(distance_cap, distance);

                // Attempt to place the circle at a place that does not overlap with previous ones

                int tryCount = 0;

                // for checking overlap
                var precedingObjects = hitObjects.SkipLast(hitObjects.Count - i).TakeLast(overlap_check_count).ToList();

                do
                {
                    if (tryCount > 0)
                    {
                        direction = two_pi * nextSingle();
                    }

                    var relativePos = new Vector2(
                        distance * MathF.Cos(direction),
                        distance * MathF.Sin(direction)
                        );
                    // Rotate the new circle away from playfield border
                    relativePos = OsuHitObjectGenerationUtils.RotateAwayFromEdge(lastPos, relativePos, edge_rotation_multiplier);
                    direction   = MathF.Atan2(relativePos.Y, relativePos.X);

                    var newPosition = Vector2.Add(lastPos, relativePos);

                    obj.Position = newPosition;

                    clampToPlayfield(obj);

                    tryCount++;
                    if (tryCount % 10 == 0)
                    {
                        distance *= 0.9f;
                    }
                } while (distance >= obj.Radius * 2 && checkForOverlap(precedingObjects, obj));

                if (obj.LastInCombo)
                {
                    direction = two_pi * nextSingle();
                }
                else
                {
                    direction += distance / distance_cap * (nextSingle() * two_pi - MathF.PI);
                }
            }
        }
Example #7
0
        public void ApplyToHitObject(HitObject hitObject)
        {
            var osuObject = (OsuHitObject)hitObject;

            OsuHitObjectGenerationUtils.ReflectVertically(osuObject);
        }