Skip to content

Commit

Permalink
Merge pull request #170 from WilliamQiufeng/scroll-speed-factor
Browse files Browse the repository at this point in the history
Add scroll speed factor info for changing scroll speed multiplier in map
  • Loading branch information
Swan authored Nov 5, 2024
2 parents a7180cf + 9c168ba commit 6f69d51
Show file tree
Hide file tree
Showing 3 changed files with 149 additions and 0 deletions.
17 changes: 17 additions & 0 deletions Quaver.API/Helpers/MathHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,22 @@ public static T Clamp<T>(this T val, T min, T max) where T : IComparable<T>
if (val.CompareTo(min) < 0) return min;
return val.CompareTo(max) > 0 ? max : val;
}

/// <summary>
/// Mirrors the first <see cref="bits"/> bits of <see cref="x"/>.
/// https://stackoverflow.com/a/4245986/23723435
/// </summary>
/// <param name="x"></param>
/// <param name="bits"></param>
/// <returns></returns>
public static uint Reverse(uint x, int bits)
{
x = ((x & 0x55555555) << 1) | ((x & 0xAAAAAAAA) >> 1); // Swap _<>_
x = ((x & 0x33333333) << 2) | ((x & 0xCCCCCCCC) >> 2); // Swap __<>__
x = ((x & 0x0F0F0F0F) << 4) | ((x & 0xF0F0F0F0) >> 4); // Swap ____<>____
x = ((x & 0x00FF00FF) << 8) | ((x & 0xFF00FF00) >> 8); // Swap ...
x = ((x & 0x0000FFFF) << 16) | ((x & 0xFFFF0000) >> 16); // Swap ...
return x >> (32 - bits);
}
}
}
18 changes: 18 additions & 0 deletions Quaver.API/Maps/Qua.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,11 @@ public List<SliderVelocityInfo> SliderVelocities
private set => DefaultScrollGroup.ScrollVelocities = value;
}

/// <summary>
/// Scroll Speed Factor .qua data
/// </summary>
public List<ScrollSpeedFactorInfo> ScrollSpeedFactors { get; private set; } = new List<ScrollSpeedFactorInfo>();

/// <summary>
/// HitObject .qua data
/// </summary>
Expand Down Expand Up @@ -258,6 +263,7 @@ public bool EqualByValue(Qua other) =>
Genre == other.Genre &&
TimingPoints.SequenceEqual(other.TimingPoints, TimingPointInfo.ByValueComparer) &&
SliderVelocities.SequenceEqual(other.SliderVelocities, SliderVelocityInfo.ByValueComparer) &&
ScrollSpeedFactors.SequenceEqual(other.ScrollSpeedFactors, ScrollSpeedFactorInfo.ByValueComparer) &&
InitialScrollVelocity == other.InitialScrollVelocity &&
CompareTimingGroups(other.TimingGroups) &&
BPMDoesNotAffectScrollVelocity == other.BPMDoesNotAffectScrollVelocity &&
Expand Down Expand Up @@ -489,6 +495,7 @@ public void Sort()
SortSoundEffects();
SortTimingPoints();
SortSliderVelocities();
SortScrollSpeedFactors();
}

/// <summary>
Expand Down Expand Up @@ -630,6 +637,13 @@ public TimingPointInfo GetTimingPointAt(double time)
public SliderVelocityInfo GetScrollVelocityAt(double time, string timingGroupId = DefaultScrollGroupId) =>
((ScrollGroup)TimingGroups[timingGroupId]).GetScrollVelocityAt(time);

/// <summary>
/// Gets a scroll velocity at a particular time in the map
/// </summary>
/// <param name="time"></param>
/// <returns></returns>
public ScrollSpeedFactorInfo GetScrollSpeedFactorAt(double time) => ScrollSpeedFactors.AtTime((float)time);

/// <summary>
/// Finds the length of a timing point.
/// </summary>
Expand Down Expand Up @@ -1019,6 +1033,10 @@ public void MirrorHitObjects()
/// </summary>
public void SortBookmarks() => Bookmarks.HybridSort();

/// <summary>
/// </summary>
public void SortScrollSpeedFactors() => ScrollSpeedFactors.HybridSort();

/// <summary>
/// </summary>
public void SortHitObjects() => HitObjects.HybridSort();
Expand Down
114 changes: 114 additions & 0 deletions Quaver.API/Maps/Structures/ScrollSpeedFactorInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
using System;
using System.Collections.Generic;
using System.Text;
using MoonSharp.Interpreter;

namespace Quaver.API.Maps.Structures
{
/// <summary>
/// ScrollSpeedFactors section of the .qua
/// </summary>
[Serializable]
[MoonSharpUserData]
public class ScrollSpeedFactorInfo : IStartTime
{
/// <summary>
/// The time in milliseconds at which the scroll speed factor will be exactly <see cref="Factor"/>
/// </summary>
public float StartTime { get; [MoonSharpHidden] set; }

/// <summary>
/// The multiplier given to the scroll speed.
/// It will be lerped to the next scroll speed factor like keyframes, unless this is the last factor change.
/// </summary>
public float Factor { get; [MoonSharpHidden] set; }

/// <summary>
/// Bit flag: i-th bit for if this factor applies to the i-th lane. For i >= KeyCount, i-th bit is 1.
/// So the value would be -1 to apply to all lanes, even after we change the key count in editor.
/// </summary>
public int LaneMask { get; [MoonSharpHidden] set; } = -1;

public IEnumerable<int> GetLaneMaskLanes(int keyCount) => GetLaneMaskLanes(LaneMask, keyCount);

public string MaskRepresentation(int keyCount) => MaskRepresentation(LaneMask, keyCount);

public static IEnumerable<int> GetLaneMaskLanes(int laneMask, int keyCount)
{
for (var i = 0; i < keyCount; i++)
{
if ((laneMask & (1 << i)) != 0)
yield return i;
}
}

/// <summary>
/// Mask in human-readable form should be in reverse order and binary, so that the left-most character
/// represents the left-most lane (lane 1)
/// </summary>
/// <param name="laneMask"></param>
/// <param name="keyCount"></param>
/// <returns></returns>
public static string MaskRepresentation(int laneMask, int keyCount)
{
var sb = new StringBuilder();
for (var i = 0; i < keyCount; i++)
{
sb.Append((laneMask & (1 << i)) == 0 ? '0' : '1');
}

return sb.ToString();
}

/// <summary>
/// Turns representation of lane mask back into integer
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static int ParseMask(string input)
{
var result = -1;
for (var i = 0; i < input.Length; i++)
{
if (input[i] == '0') result &= ~(1 << i);
}

return result;
}

private sealed class StartTimeRelationalComparer : IComparer<ScrollSpeedFactorInfo>
{
public int Compare(ScrollSpeedFactorInfo x, ScrollSpeedFactorInfo y)
{
if (ReferenceEquals(x, y)) return 0;
if (ReferenceEquals(null, y)) return 1;
if (ReferenceEquals(null, x)) return -1;
return x.StartTime.CompareTo(y.StartTime);
}
}

public static IComparer<ScrollSpeedFactorInfo> StartTimeComparer { get; } = new StartTimeRelationalComparer();

/// <summary>
/// By-value comparer, auto-generated by Rider.
/// </summary>
private sealed class ByValueEqualityComparer : IEqualityComparer<ScrollSpeedFactorInfo>
{
public bool Equals(ScrollSpeedFactorInfo x, ScrollSpeedFactorInfo y)
{
if (ReferenceEquals(x, y)) return true;
if (ReferenceEquals(x, null)) return false;
if (ReferenceEquals(y, null)) return false;
if (x.GetType() != y.GetType()) return false;
return x.StartTime.Equals(y.StartTime) && x.Factor.Equals(y.Factor) && x.LaneMask == y.LaneMask;
}

public int GetHashCode(ScrollSpeedFactorInfo obj)
{
return HashCode.Combine(obj.StartTime, obj.Factor, obj.LaneMask);
}
}

public static IEqualityComparer<ScrollSpeedFactorInfo> ByValueComparer { get; } = new ByValueEqualityComparer();
}
}

0 comments on commit 6f69d51

Please sign in to comment.