Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lanes core #222

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
aaf8194
Initial lanes implementation 9/22/24
Purplo-cf Oct 2, 2024
7e1335b
Allow non-tremolo notes in tremolo phrases
Purplo-cf Sep 26, 2024
f0573ef
New lane engine logic based on hit window size
Purplo-cf Oct 1, 2024
c1c3c81
Stricter overstrum forgiveness while lane is active
Purplo-cf Oct 1, 2024
72876bb
Guitar chord tremolo support
Purplo-cf Oct 1, 2024
135dc33
Added engine parameter for lane forgiveness window
Purplo-cf Oct 2, 2024
a752e78
Fix rebase
Purplo-cf Oct 2, 2024
9436615
Fixed log position
Purplo-cf Oct 2, 2024
2748c9c
Calculane lane forgiveness based on max hit window
Purplo-cf Oct 2, 2024
868c297
Modify precision engine windows for lanes
Purplo-cf Oct 2, 2024
f94427c
Updated drum tremolo parsing logic
Purplo-cf Oct 5, 2024
fd9ead0
Parsing for intersecting tremolo and trill phrases
Purplo-cf Oct 5, 2024
ad4b41e
Adjustments to engine preset setting
Purplo-cf Oct 9, 2024
e9e66ce
Drum fix for lane note misses while lane is active
Purplo-cf Oct 9, 2024
31a28cb
Allow non-laned notes in drum trill phrases
Purplo-cf Oct 9, 2024
16fcb36
GuitarEngine override for ActiveLaneIncludesNote
Purplo-cf Oct 9, 2024
5fb0fac
Changed default lane forgiveness parameters
Purplo-cf Oct 11, 2024
319bfbc
Clarify comment
Purplo-cf Oct 11, 2024
516734b
Fix for open chord tremolo
Purplo-cf Oct 27, 2024
4653539
Overstrum leniency window after lane end
Purplo-cf Oct 27, 2024
a504047
Modify presets for new engine behavior
Purplo-cf Oct 27, 2024
cc32730
Update deprecated comments
Purplo-cf Oct 27, 2024
23d22b1
Remove deprecated comment for TremoloFrontEndPercent
Purplo-cf Oct 27, 2024
e5f5346
Simplify HitNoteFromLane logic
Purplo-cf Oct 27, 2024
211b2e6
Fix for forgiven lane notes still missing when CurrentTime crosses La…
Purplo-cf Oct 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions YARG.Core/Chart/Loaders/MoonSong/MoonSongLoader.Drums.cs
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Guitar and Keys, every note inside of a trill and tremolo phrase is assumed to be part of the lane. There are special considerations for drum rolls and cymbal swells where that is not always the case. Only the notes that appear the most in the phrase will get the appropriate lane flag.

Chord tremolos are still possible here if more than one note is tied for the most appearances, but kick notes will not be included in the case of a tie. Kick tremolos are only possible when kick notes are the undisputed winner.

Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ internal partial class MoonSongLoader : ISongLoader
{
private bool _discoFlip = false;

private uint _lastLanePhraseTick;
private List<int>? _validLaneNotes = null;

public InstrumentTrack<DrumNote> LoadDrumsTrack(Instrument instrument)
{
_discoFlip = false;
Expand Down Expand Up @@ -37,7 +40,10 @@ private DrumNote CreateFourLaneDrumNote(MoonNote moonNote, Dictionary<MoonPhrase
{
var pad = GetFourLaneDrumPad(moonNote);
var noteType = GetDrumNoteType(moonNote);

var generalFlags = GetGeneralFlags(moonNote, currentPhrases);
generalFlags = ModifyDrumLaneFlags(moonNote, currentPhrases, generalFlags);

var drumFlags = GetDrumNoteFlags(moonNote, currentPhrases);

double time = _moonSong.TickToTime(moonNote.tick);
Expand All @@ -48,7 +54,10 @@ private DrumNote CreateFiveLaneDrumNote(MoonNote moonNote, Dictionary<MoonPhrase
{
var pad = GetFiveLaneDrumPad(moonNote);
var noteType = GetDrumNoteType(moonNote);

var generalFlags = GetGeneralFlags(moonNote, currentPhrases);
generalFlags = ModifyDrumLaneFlags(moonNote, currentPhrases, generalFlags);

var drumFlags = GetDrumNoteFlags(moonNote, currentPhrases);

double time = _moonSong.TickToTime(moonNote.tick);
Expand Down Expand Up @@ -297,5 +306,118 @@ private DrumNoteFlags GetDrumNoteFlags(MoonNote moonNote, Dictionary<MoonPhrase.

return flags;
}

private NoteFlags ModifyDrumLaneFlags(MoonNote moonNote, Dictionary<MoonPhrase.Type, MoonPhrase> currentPhrases, NoteFlags flags)
{
MoonPhrase? lanePhrase = null;
bool isTrill = false;

if ((flags & NoteFlags.Tremolo) != 0)
{
currentPhrases.TryGetValue(MoonPhrase.Type.TremoloLane, out lanePhrase);
}
else if ((flags & NoteFlags.Trill) != 0)
{
currentPhrases.TryGetValue(MoonPhrase.Type.TrillLane, out lanePhrase);
isTrill = true;
}

if (lanePhrase == null)
{
return flags;
}

if (_validLaneNotes == null || lanePhrase.tick != _lastLanePhraseTick)
{
_lastLanePhraseTick = lanePhrase.tick;
_validLaneNotes = GetValidLaneNotes(moonNote, lanePhrase, isTrill);
}

if (!_validLaneNotes.Contains(moonNote.rawNote))
{
flags &= ~NoteFlags.Tremolo;
flags &= ~NoteFlags.Trill;
flags &= ~NoteFlags.LaneStart;
flags &= ~NoteFlags.LaneEnd;
}

return flags;

static List<int> GetValidLaneNotes(MoonNote moonNote, MoonPhrase lanePhrase, bool isTrill)
{
// Iterate forward every note in this phrase to find the notes that appear the most
// Assumes that this will only run when the first note in a phrase is provided
Dictionary<int,int> noteTotals = new();

// Stop searching if the current note value has this much of a lead over the others
const int CLINCH_THRESHOLD = 5;
int highestTotal = 0;

for (var noteRef = moonNote; noteRef != null && IsEventInPhrase(noteRef, lanePhrase); noteRef = noteRef.next)
{
if (noteRef.isChord && noteRef.drumPad == MoonNote.DrumPad.Kick)
{
// Kick tremolos are only possible with a winning total on non-chorded kicks, no ties with other notes
continue;
}

int thisNote = noteRef.rawNote;

int thisTotal;
if (noteTotals.ContainsKey(thisNote))
{
thisTotal = ++noteTotals[thisNote];
}
else
{
thisTotal = noteTotals[thisNote] = 1;
}

if (thisTotal <= highestTotal)
{
continue;
}

highestTotal = thisTotal;

if (thisTotal >= CLINCH_THRESHOLD)
{
bool stopSearching = true;
foreach(var (otherNote, otherTotal) in noteTotals)
{
if (otherNote == thisNote)
{
continue;
}

if (thisTotal - otherTotal < CLINCH_THRESHOLD)
{
stopSearching = false;
break;
}
}

if (stopSearching)
{
// Safe to say this is the only laned note a tremolo phrase
break;
}
}
}

int validNoteTotal = isTrill ? highestTotal - 2 : highestTotal;
List<int> validTremoloNotes = new();

foreach (var (note, total) in noteTotals)
{
if (total >= validNoteTotal)
{
validTremoloNotes.Add(note);
}
}

return validTremoloNotes;
}
}
}
}
44 changes: 44 additions & 0 deletions YARG.Core/Chart/Loaders/MoonSong/MoonSongLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,9 @@ private List<TNote> GetNotes<TNote>(MoonChart moonChart, Difficulty difficulty,

// Skip Expert+ notes if not on Expert+
if (difficulty != Difficulty.ExpertPlus && (moonNote.flags & MoonNote.Flags.InstrumentPlus) != 0)
{
continue;
}

var newNote = createNote(moonNote, currentPhrases);
AddNoteToList(notes, newNote);
Expand Down Expand Up @@ -196,7 +198,9 @@ private List<Phrase> GetPhrases(MoonChart moonChart)
};

if (!phraseType.HasValue)
{
continue;
}

double time = _moonSong.TickToTime(moonPhrase.tick);
var newPhrase = new Phrase(phraseType.Value, time, GetLengthInTime(moonPhrase), moonPhrase.tick, moonPhrase.length);
Expand Down Expand Up @@ -232,10 +236,14 @@ private NoteFlags GetGeneralFlags(MoonNote moonNote, CurrentPhrases currentPhras
flags |= NoteFlags.StarPower;

if (previous == null || !IsEventInPhrase(previous, starPower))
{
flags |= NoteFlags.StarPowerStart;
}

if (next == null || !IsEventInPhrase(next, starPower))
{
flags |= NoteFlags.StarPowerEnd;
}
}

// Solos
Expand All @@ -244,10 +252,46 @@ private NoteFlags GetGeneralFlags(MoonNote moonNote, CurrentPhrases currentPhras
flags |= NoteFlags.Solo;

if (previous == null || !IsEventInPhrase(previous, solo))
{
flags |= NoteFlags.SoloStart;
}

if (next == null || !IsEventInPhrase(next, solo))
{
flags |= NoteFlags.SoloEnd;
}
}

// Trill
if (currentPhrases.TryGetValue(MoonPhrase.Type.TrillLane, out var trill) && IsEventInPhrase(moonNote, trill))
{
flags |= NoteFlags.Trill;

if (previous == null || !IsEventInPhrase(previous, trill))
{
flags |= NoteFlags.LaneStart;
}

if (next == null || !IsEventInPhrase(next, trill))
{
flags |= NoteFlags.LaneEnd;
}
}

// Tremolo
if (currentPhrases.TryGetValue(MoonPhrase.Type.TremoloLane, out var tremolo) && IsEventInPhrase(moonNote, tremolo))
{
flags |= NoteFlags.Tremolo;

if (previous == null || !IsEventInPhrase(previous, tremolo))
{
flags |= NoteFlags.LaneStart;
}

if (next == null || !IsEventInPhrase(next, tremolo))
{
flags |= NoteFlags.LaneEnd;
}
}

return flags;
Expand Down
2 changes: 2 additions & 0 deletions YARG.Core/Chart/Notes/DrumNote.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ public class DrumNote : Note<DrumNote>

public bool IsStarPowerActivator => (DrumFlags & DrumNoteFlags.StarPowerActivator) != 0;

public override int LaneNote => Pad;

public DrumNote(FourLaneDrumPad pad, DrumNoteType noteType, DrumNoteFlags drumFlags,
NoteFlags flags, double time, uint tick)
: this((int)pad, noteType, drumFlags, flags, time, tick)
Expand Down
2 changes: 2 additions & 0 deletions YARG.Core/Chart/Notes/GuitarNote.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ public class GuitarNote : Note<GuitarNote>
public bool IsExtendedSustain => (GuitarFlags & GuitarNoteFlags.ExtendedSustain) != 0;
public bool IsDisjoint => (GuitarFlags & GuitarNoteFlags.Disjoint) != 0;

public override int LaneNote => NoteMask;

public GuitarNote(FiveFretGuitarFret fret, GuitarNoteType noteType, GuitarNoteFlags guitarFlags,
NoteFlags flags, double time, double timeLength, uint tick, uint tickLength)
: this((int) fret, noteType, guitarFlags, flags, time, timeLength, tick, tickLength)
Expand Down
28 changes: 26 additions & 2 deletions YARG.Core/Chart/Notes/Note.cs
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note.LaneNote is read during generic BaseEngine and TrackPlayer logic to get around instrument-specific property names. GuitarNote returns NoteMask to allow for tremolo chords.

Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ public enum NoteFlags
Solo = 1 << 3,
SoloStart = 1 << 4,
SoloEnd = 1 << 5,

Tremolo = 1 << 6,
Trill = 1 << 7,
LaneStart = 1 << 8,
LaneEnd = 1 << 9,
}

public abstract class Note<TNote> : ChartEvent, ICloneable<TNote>
Expand Down Expand Up @@ -86,6 +91,14 @@ public AllNotesEnumerator GetEnumerator()
public bool IsSoloStart => (Flags & NoteFlags.SoloStart) != 0;
public bool IsSoloEnd => (Flags & NoteFlags.SoloEnd) != 0;

public bool IsTremolo => (Flags & NoteFlags.Tremolo) != 0;
public bool IsTrill => (Flags & NoteFlags.Trill) != 0;
public bool IsLane => IsTremolo || IsTrill;
public bool IsLaneStart => (Flags & NoteFlags.LaneStart) != 0;
public bool IsLaneEnd => (Flags & NoteFlags.LaneEnd) != 0;

public virtual int LaneNote => -1;

/// <summary>
/// Returns an enumerator that contains all child notes and the parent note itself (allocation free).
/// </summary>
Expand All @@ -110,9 +123,14 @@ protected Note(Note<TNote> other) : base(other)
public virtual void AddChildNote(TNote note)
{
if (note.Tick != Tick)
{
throw new InvalidOperationException("Child note being added is not on the same tick!");
}

if (note.ChildNotes.Count > 0)
{
throw new InvalidOperationException("Child note being added has its own children!");
}

note.Parent = (TNote) this;
_childNotes.Add(note);
Expand All @@ -121,7 +139,10 @@ public virtual void AddChildNote(TNote note)
public void SetHitState(bool hit, bool includeChildren)
{
WasHit = hit;
if (!includeChildren) return;
if (!includeChildren)
{
return;
}

foreach (var childNote in _childNotes)
{
Expand All @@ -132,7 +153,10 @@ public void SetHitState(bool hit, bool includeChildren)
public void SetMissState(bool miss, bool includeChildren)
{
WasMissed = miss;
if (!includeChildren) return;
if (!includeChildren)
{
return;
}

foreach (var childNote in _childNotes)
{
Expand Down
2 changes: 2 additions & 0 deletions YARG.Core/Chart/Notes/ProKeysNote.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public class ProKeysNote : Note<ProKeysNote>

public bool IsSustain => TickLength > 0;

public override int LaneNote => Key;

public ProKeysNote(int key, ProKeysNoteFlags proKeysFlags, NoteFlags flags,
double time, double timeLength, uint tick, uint tickLength)
: base(flags, time, timeLength, tick, tickLength)
Expand Down
Loading