Skip to content

Commit

Permalink
Rework solo button detection
Browse files Browse the repository at this point in the history
  • Loading branch information
wyrdough committed Jan 19, 2025
1 parent 40758c3 commit f5f8903
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 55 deletions.
47 changes: 24 additions & 23 deletions YARG.Core/Engine/Guitar/Engines/YargFiveFretEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ protected override void UpdateBot(double time)
return;
}

LastButtonMask = ButtonMask;
ButtonMask = (byte) note.NoteMask;
LastButtonMask = EffectiveButtonMask;
EffectiveButtonMask = (byte) note.NoteMask;

YargLogger.LogFormatTrace("[Bot] Set button mask to: {0}", ButtonMask);
YargLogger.LogFormatTrace("[Bot] Set button mask to: {0}", EffectiveButtonMask);

HasTapped = ButtonMask != LastButtonMask;
HasTapped = EffectiveButtonMask != LastButtonMask;
IsFretPress = true;
HasStrummed = false;
StrumLeniencyTimer.Start(time);
Expand All @@ -48,15 +48,15 @@ protected override void UpdateBot(double time)

if (sustainNote.IsDisjoint)
{
ButtonMask |= (byte) sustainNote.DisjointMask;
EffectiveButtonMask |= (byte) sustainNote.DisjointMask;

YargLogger.LogFormatTrace("[Bot] Added Disjoint Sustain Mask {0} to button mask. {1}", sustainNote.DisjointMask, ButtonMask);
YargLogger.LogFormatTrace("[Bot] Added Disjoint Sustain Mask {0} to button mask. {1}", sustainNote.DisjointMask, EffectiveButtonMask);
}
else
{
ButtonMask |= (byte) sustainNote.NoteMask;
EffectiveButtonMask |= (byte) sustainNote.NoteMask;

YargLogger.LogFormatTrace("[Bot] Added Sustain Mask {0} to button mask. {1}", sustainNote.NoteMask, ButtonMask);
YargLogger.LogFormatTrace("[Bot] Added Sustain Mask {0} to button mask. {1}", sustainNote.NoteMask, EffectiveButtonMask);
}
}
}
Expand All @@ -81,26 +81,26 @@ protected override void MutateStateWithInput(GameInput gameInput)
}
else if (IsFretInput(gameInput))
{
LastButtonMask = ButtonMask;
LastButtonMask = EffectiveButtonMask;
HasFretted = true;
IsFretPress = gameInput.Button;

ToggleFret(gameInput.Action, gameInput.Button);

// No other frets are held, enable the "open fret"
if ((ButtonMask & ~OPEN_MASK) == 0)
if ((EffectiveButtonMask & ~OPEN_MASK) == 0)
{
ButtonMask |= OPEN_MASK;
EffectiveButtonMask |= OPEN_MASK;
}
else
{
// Some frets are held, disable the "open fret"
ButtonMask &= unchecked((byte) ~OPEN_MASK);
EffectiveButtonMask &= unchecked((byte) ~OPEN_MASK);
}
}

YargLogger.LogFormatTrace("Mutated input state: Button Mask: {0}, HasFretted: {1}, HasStrummed: {2}",
ButtonMask, HasFretted, HasStrummed);
EffectiveButtonMask, HasFretted, HasStrummed);
}

protected override void UpdateHitLogic(double time)
Expand Down Expand Up @@ -222,7 +222,7 @@ protected override void CheckForNoteHit()
if (!CanNoteBeHit(note))
{
YargLogger.LogFormatTrace("Cant hit note (Index: {0}, Mask {1}) at {2}. Buttons: {3}", i,
note.NoteMask, CurrentTime, ButtonMask);
note.NoteMask, CurrentTime, EffectiveButtonMask);
// This does nothing special, it's just logging strum leniency
if (isFirstNoteInWindow && HasStrummed && StrumLeniencyTimer.IsActive)
{
Expand All @@ -243,7 +243,7 @@ protected override void CheckForNoteHit()
// Defines whether solo tapping is allowed
// Only if SoloTaps engine parameter is set, solo is active, and no non-solo buttons are pressed
// Also allow tap if the note is a solo start note, since IsSoloActive isn't set until after this point
bool SoloTapAllowed = EngineParameters.SoloTaps && (IsSoloActive || note.IsSoloStart) && !(StandardButtonCount > 0);
bool SoloTapAllowed = EngineParameters.SoloTaps && (IsSoloActive || note.IsSoloStart) && !StandardButtonHeld;

// Handles hitting a hopo notes
// If first note is a hopo then it can be hit without combo (for practice mode)
Expand Down Expand Up @@ -290,7 +290,7 @@ protected override void CheckForNoteHit()

protected override bool CanNoteBeHit(GuitarNote note)
{
byte buttonsMasked = ButtonMask;
ushort buttonsMasked = EffectiveButtonMask;
if (ActiveSustains.Count > 0)
{
foreach (var sustain in ActiveSustains)
Expand All @@ -315,7 +315,7 @@ protected override bool CanNoteBeHit(GuitarNote note)

// If the resulting masked buttons are 0, we need to apply the Open Mask so open notes can be hit
// Need to make a copy of the button mask to prevent modifying the original
byte buttonMaskCopy = ButtonMask;
ushort buttonMaskCopy = EffectiveButtonMask;
if (buttonsMasked == 0)
{
buttonsMasked |= OPEN_MASK;
Expand All @@ -330,9 +330,9 @@ protected override bool CanNoteBeHit(GuitarNote note)
}

// If masked/extended sustain logic didn't work, try original ButtonMask
return IsNoteHittable(note, ButtonMask);
return IsNoteHittable(note, EffectiveButtonMask);

static bool IsNoteHittable(GuitarNote note, byte buttonsMasked)
static bool IsNoteHittable(GuitarNote note, ushort buttonsMasked)
{
// Only used for sustain logic
bool useDisjointSustainMask = note is { IsDisjoint: true, WasHit: true };
Expand Down Expand Up @@ -423,8 +423,9 @@ static bool IsNoteHittable(GuitarNote note, byte buttonsMasked)

protected override void HitNote(GuitarNote note)
{
// Hopo leniency needs to activate on solo taps also
bool SoloTapAllowed = EngineParameters.SoloTaps && (IsSoloActive || note.IsSoloStart) && !(StandardButtonCount > 0);
// Defines whether solo tapping is allowed
// Only if SoloTaps engine parameter is set, solo is active, and no non-solo buttons are pressed
bool SoloTapAllowed = EngineParameters.SoloTaps && (IsSoloActive || note.IsSoloStart) && !StandardButtonHeld;

if (note.IsHopo || note.IsTap || SoloTapAllowed)
{
Expand Down Expand Up @@ -500,10 +501,10 @@ protected bool CheckForGhostInput(GuitarNote note)
}

// Input is a hammer-on if the highest fret held is higher than the highest fret of the previous mask
bool isHammerOn = GetMostSignificantBit(ButtonMask) > GetMostSignificantBit(LastButtonMask);
bool isHammerOn = GetMostSignificantBit(EffectiveButtonMask) > GetMostSignificantBit(LastButtonMask);

// Input is a hammer-on and the button pressed is not part of the note mask (incorrect fret)
if (isHammerOn && (ButtonMask & note.NoteMask) == 0)
if (isHammerOn && (EffectiveButtonMask & note.NoteMask) == 0)
{
return true;
}
Expand Down
51 changes: 19 additions & 32 deletions YARG.Core/Engine/Guitar/GuitarEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,18 @@ public abstract class GuitarEngine : BaseEngine<GuitarNote, GuitarEngineParamete
GuitarStats>
{
protected const byte OPEN_MASK = 64;
// Mask of all the solo buttons in bit math
protected const ushort SOLO_MASK = 31744;

public delegate void OverstrumEvent();

public OverstrumEvent? OnOverstrum;

public byte ButtonMask { get; protected set; } = OPEN_MASK;

public byte EffectiveButtonMask { get; protected set; } = OPEN_MASK;
public ushort InputButtonMask { get; protected set; }
public byte LastButtonMask { get; protected set; }

public byte SoloButtonMask { get; protected set; } = OPEN_MASK;

// Count of standard buttons currently pressed
public int StandardButtonCount { get; protected set; } = 0;
public bool StandardButtonHeld { get; private set; }

protected bool HasFretted;
protected bool HasStrummed;
Expand Down Expand Up @@ -88,9 +87,9 @@ protected override void GenerateQueuedUpdates(double nextTime)

public override void Reset(bool keepCurrentButtons = false)
{
byte buttons = ButtonMask;
byte buttons = EffectiveButtonMask;

ButtonMask = OPEN_MASK;
EffectiveButtonMask = OPEN_MASK;

HasFretted = false;
HasStrummed = false;
Expand All @@ -109,7 +108,7 @@ public override void Reset(bool keepCurrentButtons = false)

if (keepCurrentButtons)
{
ButtonMask = buttons;
EffectiveButtonMask = buttons;
}
}

Expand Down Expand Up @@ -170,7 +169,7 @@ protected override bool CanSustainHold(GuitarNote note)
{
var mask = note.IsDisjoint ? note.DisjointMask : note.NoteMask;

var buttonsMasked = ButtonMask;
var buttonsMasked = EffectiveButtonMask;
if ((mask & OPEN_MASK) != 0)
{
buttonsMasked |= OPEN_MASK;
Expand Down Expand Up @@ -363,32 +362,20 @@ protected sealed override int CalculateBaseScore()

protected void ToggleFret(int fret, bool active)
{
// FIXME: This is a terrible hack
if((int) fret >= 10) {
fret -= 10;
} else {
if (active)
{
// Add one to count of standard buttons pressed, avoiding overflow
StandardButtonCount = (StandardButtonCount < int.MaxValue) ? StandardButtonCount + 1 : StandardButtonCount;
}
else
{
// Subtract one from count, avoiding underflow
StandardButtonCount = (StandardButtonCount > 0) ? StandardButtonCount - 1 : 0;
}
}
ButtonMask = (byte) (active ? ButtonMask | (1 << fret) : ButtonMask & ~(1 << fret));
InputButtonMask = (ushort) (active ? InputButtonMask | (1 << fret) : InputButtonMask & ~(1 << fret));

// What we're doing here is transposing bits 10-14 of the input mask down to 0-4 of the effective mask
// used elsewhere so that solo buttons are treated as if they were regular buttons
byte soloButtonMask = (byte) (InputButtonMask >> 10);

// EffectiveButtonMask is the bitwise or of the low 8 bits of InputButtonMask and soloButtonMask
EffectiveButtonMask = (byte) (InputButtonMask | soloButtonMask);
StandardButtonHeld = (InputButtonMask & ~OPEN_MASK & ~SOLO_MASK) > 0;
}

public bool IsFretHeld(GuitarAction fret)
{
// FIXME: This is a terrible hack
if((int) fret >= 10)
{
fret -= 10;
}
return (ButtonMask & (1 << (int) fret)) != 0;
return (EffectiveButtonMask & (1 << (int) fret)) != 0;
}

protected static bool IsFretInput(GameInput input)
Expand Down

0 comments on commit f5f8903

Please sign in to comment.