mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2025-09-01 03:15:53 +00:00
Co-authored-by: Hundrec <hundrecard@gmail.com> Co-authored-by: Hyper_ <survivaltemer@gmail.com> Co-authored-by: lemz1 <ismael.amjad07@gmail.com> Co-authored-by: Hyper_ <40342021+NotHyper-474@users.noreply.github.com> Co-authored-by: Kade <26305836+Kade-github@users.noreply.github.com>
139 lines
4.2 KiB
Haxe
139 lines
4.2 KiB
Haxe
package funkin.data.song;
|
|
|
|
using SongData.SongNoteData;
|
|
|
|
/**
|
|
* Utility class for extra handling of song notes
|
|
*/
|
|
@:nullSafety
|
|
class SongNoteDataUtils
|
|
{
|
|
static final CHUNK_INTERVAL_MS:Float = 2500;
|
|
|
|
/**
|
|
* Retrieves all stacked notes. It does this by cycling through "chunks" of notes within a certain interval.
|
|
*
|
|
* @param notes Sorted notes by time.
|
|
* @param threshold The note stack threshold. Refer to `doNotesStack` for more details.
|
|
* @param includeOverlapped (Optional) If overlapped notes should be included.
|
|
* @param overlapped (Optional) An array that gets populated with overlapped notes.
|
|
* Note that it's only guaranteed to work properly if the provided notes are sorted.
|
|
* @return Stacked notes.
|
|
*/
|
|
public static function listStackedNotes(notes:Array<SongNoteData>, threshold:Float, includeOverlapped:Bool = true,
|
|
?overlapped:Array<SongNoteData>):Array<SongNoteData>
|
|
{
|
|
var stackedNotes:Array<SongNoteData> = [];
|
|
|
|
var chunkTime:Float = 0;
|
|
var chunks:Array<Array<SongNoteData>> = [[]];
|
|
|
|
for (note in notes)
|
|
{
|
|
if (note == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
while (note.time >= chunkTime + CHUNK_INTERVAL_MS)
|
|
{
|
|
chunkTime += CHUNK_INTERVAL_MS;
|
|
chunks.push([]);
|
|
}
|
|
|
|
chunks[chunks.length - 1].push(note);
|
|
}
|
|
|
|
for (chunk in chunks)
|
|
{
|
|
for (i in 0...(chunk.length - 1))
|
|
{
|
|
for (j in (i + 1)...chunk.length)
|
|
{
|
|
var noteI:SongNoteData = chunk[i];
|
|
var noteJ:SongNoteData = chunk[j];
|
|
|
|
if (doNotesStack(noteI, noteJ, threshold))
|
|
{
|
|
if (!stackedNotes.fastContains(noteI))
|
|
{
|
|
if (includeOverlapped) stackedNotes.push(noteI);
|
|
|
|
if (overlapped != null && !overlapped.contains(noteI)) overlapped.push(noteI);
|
|
}
|
|
|
|
if (!stackedNotes.fastContains(noteJ))
|
|
{
|
|
stackedNotes.push(noteJ);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return stackedNotes;
|
|
}
|
|
|
|
/**
|
|
* Concatenates two arrays of notes but overwrites notes in `lhs` that are overlapped by notes in `rhs`.
|
|
* Hold notes are only overwritten by longer hold notes.
|
|
* This operation only modifies the second array and `overwrittenNotes`.
|
|
*
|
|
* @param lhs An array of notes
|
|
* @param rhs An array of notes to concatenate into `lhs`
|
|
* @param overwrittenNotes An optional array that is modified in-place with the notes in `lhs` that were overwritten.
|
|
* @param threshold The note stack threshold. Refer to `doNotesStack` for more details.
|
|
* @return The unsorted resulting array.
|
|
*/
|
|
public static function concatOverwrite(lhs:Array<SongNoteData>, rhs:Array<SongNoteData>, ?overwrittenNotes:Array<SongNoteData>,
|
|
threshold:Float = 0):Array<SongNoteData>
|
|
{
|
|
if (lhs == null || rhs == null || rhs.length == 0) return lhs;
|
|
if (lhs.length == 0) return rhs;
|
|
|
|
var result = lhs.copy();
|
|
for (i in 0...rhs.length)
|
|
{
|
|
var noteB:SongNoteData = rhs[i];
|
|
var hasOverlap:Bool = false;
|
|
|
|
for (j in 0...lhs.length)
|
|
{
|
|
var noteA:SongNoteData = lhs[j];
|
|
if (doNotesStack(noteA, noteB, threshold))
|
|
{
|
|
// Long hold notes should have priority over shorter hold notes
|
|
if (noteA.length <= noteB.length)
|
|
{
|
|
overwrittenNotes?.push(result[j].clone());
|
|
result[j] = noteB;
|
|
}
|
|
hasOverlap = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!hasOverlap) result.push(noteB);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @param noteA First note.
|
|
* @param noteB Second note.
|
|
* @param threshold The note stack threshold, in steps.
|
|
* @return Returns `true` if both notes are on the same strumline, have the same direction
|
|
* and their time difference in steps is less than the step-based threshold.
|
|
* A threshold of 0 will return `true` if notes are nearly perfectly aligned.
|
|
*/
|
|
public static function doNotesStack(noteA:SongNoteData, noteB:SongNoteData, threshold:Float = 0):Bool
|
|
{
|
|
if (noteA.data != noteB.data) return false;
|
|
else if (threshold == 0) return Math.ffloor(Math.abs(noteA.time - noteB.time)) < 1;
|
|
|
|
final stepDiff:Float = Math.abs(noteA.getStepTime() - noteB.getStepTime());
|
|
return stepDiff <= threshold + 0.001;
|
|
}
|
|
}
|