From 382af3b4851c63839bca5eed672bf0c2d5a2ed1a Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 28 Aug 2024 05:42:14 -0400 Subject: [PATCH] Reimplement ghost tapping (disabled via compile define) --- source/funkin/play/PlayState.hx | 16 +++---- source/funkin/play/notes/Strumline.hx | 65 ++++++++++++++++++++++++--- source/funkin/util/Constants.hx | 10 +++-- 3 files changed, 71 insertions(+), 20 deletions(-) diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index fa059f73a..b3beba4e2 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -2323,7 +2323,11 @@ class PlayState extends MusicBeatSubState var notesInDirection:Array = notesByDirection[input.noteDirection]; - if (!Constants.GHOST_TAPPING && notesInDirection.length == 0) + #if FEATURE_GHOST_TAPPING + if ((!playerStrumline.mayGhostTap()) && notesInDirection.length == 0) + #else + if (notesInDirection.length == 0) + #end { // Pressed a wrong key with no notes nearby. // Perform a ghost miss (anti-spam). @@ -2333,16 +2337,6 @@ class PlayState extends MusicBeatSubState playerStrumline.playPress(input.noteDirection); trace('PENALTY Score: ${songScore}'); } - else if (Constants.GHOST_TAPPING && (!playerStrumline.mayGhostTap()) && notesInDirection.length == 0) - { - // Pressed a wrong key with notes visible on-screen. - // Perform a ghost miss (anti-spam). - ghostNoteMiss(input.noteDirection, notesInRange.length > 0); - - // Play the strumline animation. - playerStrumline.playPress(input.noteDirection); - trace('PENALTY Score: ${songScore}'); - } else if (notesInDirection.length == 0) { // Press a key with no penalty. diff --git a/source/funkin/play/notes/Strumline.hx b/source/funkin/play/notes/Strumline.hx index 1e5782ad2..e894f9c62 100644 --- a/source/funkin/play/notes/Strumline.hx +++ b/source/funkin/play/notes/Strumline.hx @@ -94,6 +94,10 @@ class Strumline extends FlxSpriteGroup final noteStyle:NoteStyle; + #if FEATURE_GHOST_TAPPING + var ghostTapTimer:Float = 0.0; + #end + /** * The note data for the song. Should NOT be altered after the song starts, * so we can easily rewind. @@ -179,21 +183,36 @@ class Strumline extends FlxSpriteGroup super.update(elapsed); updateNotes(); + + #if FEATURE_GHOST_TAPPING + updateGhostTapTimer(elapsed); + #end } + #if FEATURE_GHOST_TAPPING /** * Returns `true` if no notes are in range of the strumline and the player can spam without penalty. */ public function mayGhostTap():Bool { - // TODO: Refine this. Only querying "can be hit" is too tight but "is being rendered" is too loose. - // Also, if you just hit a note, there should be a (short) period where this is off so you can't spam. + // Any notes in range of the strumline. + if (getNotesMayHit().length > 0) + { + return false; + } + // Any hold notes in range of the strumline. + if (getHoldNotesHitOrMissed().length > 0) + { + return false; + } - // If there are any notes on screen, we can't ghost tap. - return notes.members.filter(function(note:NoteSprite) { - return note != null && note.alive && !note.hasBeenHit; - }).length == 0; + // Note has been hit recently. + if (ghostTapTimer > 0.0) return false; + + // **yippee** + return true; } + #end /** * Return notes that are within `Constants.HIT_WINDOW` ms of the strumline. @@ -492,6 +511,32 @@ class Strumline extends FlxSpriteGroup } } + /** + * Return notes that are within, or way after, `Constants.HIT_WINDOW` ms of the strumline. + * @return An array of `NoteSprite` objects. + */ + public function getNotesOnScreen():Array + { + return notes.members.filter(function(note:NoteSprite) { + return note != null && note.alive && !note.hasBeenHit; + }); + } + + #if FEATURE_GHOST_TAPPING + function updateGhostTapTimer(elapsed:Float):Void + { + // If it's still our turn, don't update the ghost tap timer. + if (getNotesOnScreen().length > 0) return; + + ghostTapTimer -= elapsed; + + if (ghostTapTimer <= 0) + { + ghostTapTimer = 0; + } + } + #end + /** * Called when the PlayState skips a large amount of time forward or backward. */ @@ -563,6 +608,10 @@ class Strumline extends FlxSpriteGroup playStatic(dir); } resetScrollSpeed(); + + #if FEATURE_GHOST_TAPPING + ghostTapTimer = 0; + #end } public function applyNoteData(data:Array):Void @@ -602,6 +651,10 @@ class Strumline extends FlxSpriteGroup note.holdNoteSprite.sustainLength = (note.holdNoteSprite.strumTime + note.holdNoteSprite.fullSustainLength) - conductorInUse.songPosition; } + + #if FEATURE_GHOST_TAPPING + ghostTapTimer = Constants.GHOST_TAP_DELAY; + #end } public function killNote(note:NoteSprite):Void diff --git a/source/funkin/util/Constants.hx b/source/funkin/util/Constants.hx index 80318f1a4..fa03b229d 100644 --- a/source/funkin/util/Constants.hx +++ b/source/funkin/util/Constants.hx @@ -524,12 +524,16 @@ class Constants * OTHER */ // ============================== + #if FEATURE_GHOST_TAPPING + // Hey there, Eric here. + // This feature is currently still in development. You can test it out by creating a special debug build! + // lime build windows -DFEATURE_GHOST_TAPPING /** - * If true, the player will not receive the ghost miss penalty if there are no notes within the hit window. - * This is the thing people have been begging for forever lolol. + * Duration, in seconds, after the player's section ends before the player can spam without penalty. */ - public static final GHOST_TAPPING:Bool = false; + public static final GHOST_TAP_DELAY:Float = 3 / 8; + #end /** * The maximum number of previous file paths for the Chart Editor to remember.