diff --git a/assets b/assets index 9b9baa6f2..d094640f7 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 9b9baa6f2b38dc9bd3354b350084f548e1e16c0f +Subproject commit d094640f727a670a348b3579d11af5ff6a2ada3a diff --git a/source/funkin/Highscore.hx b/source/funkin/Highscore.hx index 2c18ffa2d..24b65832b 100644 --- a/source/funkin/Highscore.hx +++ b/source/funkin/Highscore.hx @@ -21,7 +21,6 @@ abstract Tallies(RawTallies) bad: 0, good: 0, sick: 0, - killer: 0, totalNotes: 0, totalNotesHit: 0, maxCombo: 0, @@ -43,7 +42,6 @@ typedef RawTallies = var bad:Int; var good:Int; var sick:Int; - var killer:Int; var maxCombo:Int; var isNewHighscore:Bool; diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx index c244fd60f..afb5400a3 100644 --- a/source/funkin/play/PlayState.hx +++ b/source/funkin/play/PlayState.hx @@ -1865,17 +1865,30 @@ class PlayState extends MusicBeatSubState } } - // TODO: Potential penalty for dropping a hold note? if (holdNote.missedNote && !holdNote.handledMiss) { + // When the opponent drops a hold note. holdNote.handledMiss = true; + + // We dropped a hold note. + // Mute vocals and play miss animation, but don't penalize. + vocals.opponentVolume = 0; + currentStage.getOpponent().playSingAnimation(holdNote.noteData.getDirection(), true); } } // Process notes on the player's side. for (note in playerStrumline.notes.members) { - if (note == null || note.hasBeenHit) continue; + if (note == null) continue; + + if (note.hasBeenHit) + { + note.tooEarly = false; + note.mayHit = false; + note.hasMissed = false; + continue; + } var hitWindowStart = note.strumTime - Constants.HIT_WINDOW_MS; var hitWindowCenter = note.strumTime; @@ -1938,8 +1951,15 @@ class PlayState extends MusicBeatSubState songScore += Std.int(Constants.SCORE_HOLD_BONUS_PER_SECOND * elapsed); } - // TODO: Potential penalty for dropping a hold note? - // if (holdNote.missedNote && !holdNote.handledMiss) { holdNote.handledMiss = true; } + if (holdNote.missedNote && !holdNote.handledMiss) + { + // The player dropped a hold note. + holdNote.handledMiss = true; + + // Mute vocals and play miss animation, but don't penalize. + vocals.playerVolume = 0; + currentStage.getBoyfriend().playSingAnimation(holdNote.noteData.getDirection(), true); + } } } @@ -2031,8 +2051,6 @@ class PlayState extends MusicBeatSubState trace('Hit note! ${targetNote.noteData}'); goodNoteHit(targetNote, input); - targetNote.visible = false; - targetNote.kill(); notesInDirection.remove(targetNote); // Play the strumline animation. @@ -2064,15 +2082,8 @@ class PlayState extends MusicBeatSubState // Calling event.cancelEvent() skips all the other logic! Neat! if (event.eventCanceled) return; - Highscore.tallies.combo++; - Highscore.tallies.totalNotesHit++; - - if (Highscore.tallies.combo > Highscore.tallies.maxCombo) Highscore.tallies.maxCombo = Highscore.tallies.combo; - popUpScore(note, input); - playerStrumline.hitNote(note); - if (note.isHoldNote && note.holdNoteSprite != null) { playerStrumline.playNoteHoldCover(note.holdNoteSprite); @@ -2088,8 +2099,6 @@ class PlayState extends MusicBeatSubState function onNoteMiss(note:NoteSprite):Void { // a MISS is when you let a note scroll past you!! - Highscore.tallies.missed++; - var event:NoteScriptEvent = new NoteScriptEvent(NOTE_MISS, note, Highscore.tallies.combo, true); dispatchEvent(event); // Calling event.cancelEvent() skips all the other logic! Neat! @@ -2137,8 +2146,11 @@ class PlayState extends MusicBeatSubState } vocals.playerVolume = 0; + Highscore.tallies.missed++; + if (Highscore.tallies.combo != 0) { + // Break the combo. Highscore.tallies.combo = comboPopUps.displayCombo(0); } @@ -2286,29 +2298,54 @@ class PlayState extends MusicBeatSubState var score = Scoring.scoreNote(noteDiff, PBOT1); var daRating = Scoring.judgeNote(noteDiff, PBOT1); + if (daRating == 'miss') + { + // If daRating is 'miss', that means we made a mistake and should not continue. + trace('[WARNING] popUpScore judged a note as a miss!'); + // TODO: Remove this. + comboPopUps.displayRating('miss'); + return; + } + + // TODO: DEBUG OVERRIDE DON'T BE A DUMBASS + daRating = 'bad'; + + var isComboBreak = false; switch (daRating) { - case 'killer': - Highscore.tallies.killer += 1; - health += Constants.HEALTH_KILLER_BONUS; case 'sick': Highscore.tallies.sick += 1; health += Constants.HEALTH_SICK_BONUS; + isComboBreak = Constants.JUDGEMENT_SICK_COMBO_BREAK; case 'good': Highscore.tallies.good += 1; health += Constants.HEALTH_GOOD_BONUS; + isComboBreak = Constants.JUDGEMENT_GOOD_COMBO_BREAK; case 'bad': Highscore.tallies.bad += 1; health += Constants.HEALTH_BAD_BONUS; + isComboBreak = Constants.JUDGEMENT_BAD_COMBO_BREAK; case 'shit': Highscore.tallies.shit += 1; health += Constants.HEALTH_SHIT_BONUS; - case 'miss': - Highscore.tallies.missed += 1; - health -= Constants.HEALTH_MISS_PENALTY; + isComboBreak = Constants.JUDGEMENT_SHIT_COMBO_BREAK; } - if (daRating == "sick" || daRating == "killer") + if (isComboBreak) + { + // Break the combo, but don't increment tallies.misses. + Highscore.tallies.combo = comboPopUps.displayCombo(0); + } + else + { + Highscore.tallies.combo++; + Highscore.tallies.totalNotesHit++; + if (Highscore.tallies.combo > Highscore.tallies.maxCombo) Highscore.tallies.maxCombo = Highscore.tallies.combo; + } + + playerStrumline.hitNote(daNote, !isComboBreak); + + if (daRating == "sick") { playerStrumline.playNoteSplash(daNote.noteData.getDirection()); } @@ -2444,7 +2481,6 @@ class PlayState extends MusicBeatSubState score: songScore, tallies: { - killer: Highscore.tallies.killer, sick: Highscore.tallies.sick, good: Highscore.tallies.good, bad: Highscore.tallies.bad, @@ -2495,7 +2531,6 @@ class PlayState extends MusicBeatSubState tallies: { // TODO: Sum up the values for the whole level! - killer: 0, sick: 0, good: 0, bad: 0, diff --git a/source/funkin/play/notes/NoteSprite.hx b/source/funkin/play/notes/NoteSprite.hx index e5c4786d3..0368b18e9 100644 --- a/source/funkin/play/notes/NoteSprite.hx +++ b/source/funkin/play/notes/NoteSprite.hx @@ -4,6 +4,7 @@ import funkin.data.song.SongData.SongNoteData; import funkin.play.notes.notestyle.NoteStyle; import flixel.graphics.frames.FlxAtlasFrames; import flixel.FlxSprite; +import funkin.graphics.shaders.HSVShader; class NoteSprite extends FlxSprite { @@ -11,6 +12,8 @@ class NoteSprite extends FlxSprite public var holdNoteSprite:SustainTrail; + var hsvShader:HSVShader; + /** * The time at which the note should be hit, in milliseconds. */ @@ -102,6 +105,8 @@ class NoteSprite extends FlxSprite this.strumTime = strumTime; this.direction = direction; + this.hsvShader = new HSVShader(); + if (this.strumTime < 0) this.strumTime = 0; setupNoteGraphic(noteStyle); @@ -116,16 +121,57 @@ class NoteSprite extends FlxSprite setGraphicSize(Strumline.STRUMLINE_SIZE); updateHitbox(); + + this.shader = hsvShader; + } + + #if FLX_DEBUG + /** + * Call this to override how debug bounding boxes are drawn for this sprite. + */ + public override function drawDebugOnCamera(camera:flixel.FlxCamera):Void + { + if (!camera.visible || !camera.exists || !isOnScreen(camera)) return; + + var gfx = beginDrawDebug(camera); + + var rect = getBoundingBox(camera); + trace('note sprite bounding box: ' + rect.x + ', ' + rect.y + ', ' + rect.width + ', ' + rect.height); + + gfx.lineStyle(2, 0xFFFF66FF, 0.5); // thickness, color, alpha + gfx.drawRect(rect.x, rect.y, rect.width, rect.height); + + gfx.lineStyle(2, 0xFFFFFF66, 0.5); // thickness, color, alpha + gfx.drawRect(rect.x, rect.y + rect.height / 2, rect.width, 1); + + endDrawDebug(camera); + } + #end + + public function desaturate():Void + { + this.hsvShader.saturation = 0.2; + } + + public function setHue(hue:Float):Void + { + this.hsvShader.hue = hue; } public override function revive():Void { super.revive(); + this.visible = true; + this.alpha = 1.0; this.active = false; this.tooEarly = false; this.hasBeenHit = false; this.mayHit = false; this.hasMissed = false; + + this.hsvShader.hue = 1.0; + this.hsvShader.saturation = 1.0; + this.hsvShader.value = 1.0; } public override function kill():Void diff --git a/source/funkin/play/notes/Strumline.hx b/source/funkin/play/notes/Strumline.hx index d56bb33ca..5fdd3945f 100644 --- a/source/funkin/play/notes/Strumline.hx +++ b/source/funkin/play/notes/Strumline.hx @@ -316,7 +316,7 @@ class Strumline extends FlxSpriteGroup // Update rendering of notes. for (note in notes.members) { - if (note == null || !note.alive || note.hasBeenHit) continue; + if (note == null || !note.alive) continue; var vwoosh:Bool = note.holdNoteSprite == null; // Set the note's position. @@ -384,10 +384,6 @@ class Strumline extends FlxSpriteGroup var yOffset:Float = (holdNote.fullSustainLength - holdNote.sustainLength) * Constants.PIXELS_PER_MS; - trace('yOffset: ' + yOffset); - trace('holdNote.fullSustainLength: ' + holdNote.fullSustainLength); - trace('holdNote.sustainLength: ' + holdNote.sustainLength); - var vwoosh:Bool = false; if (Preferences.downscroll) @@ -531,11 +527,24 @@ class Strumline extends FlxSpriteGroup this.noteData.insertionSort(compareNoteData.bind(FlxSort.ASCENDING)); } - public function hitNote(note:NoteSprite):Void + /** + * @param note The note to hit. + * @param removeNote True to remove the note immediately, false to make it transparent and let it move offscreen. + */ + public function hitNote(note:NoteSprite, removeNote:Bool = true):Void { playConfirm(note.direction); note.hasBeenHit = true; - killNote(note); + + if (removeNote) + { + killNote(note); + } + else + { + note.alpha = 0.5; + note.desaturate(); + } if (note.holdNoteSprite != null) { diff --git a/source/funkin/play/notes/SustainTrail.hx b/source/funkin/play/notes/SustainTrail.hx index f5d444f61..056a6a5a9 100644 --- a/source/funkin/play/notes/SustainTrail.hx +++ b/source/funkin/play/notes/SustainTrail.hx @@ -326,6 +326,7 @@ class SustainTrail extends FlxSprite hitNote = false; missedNote = false; + handledMiss = false; } override public function destroy():Void diff --git a/source/funkin/play/scoring/Scoring.hx b/source/funkin/play/scoring/Scoring.hx index 75d002cb5..edfb2cae7 100644 --- a/source/funkin/play/scoring/Scoring.hx +++ b/source/funkin/play/scoring/Scoring.hx @@ -158,8 +158,8 @@ class Scoring return switch (absTiming) { - case(_ < PBOT1_KILLER_THRESHOLD) => true: - 'killer'; + // case(_ < PBOT1_KILLER_THRESHOLD) => true: + // 'killer'; case(_ < PBOT1_SICK_THRESHOLD) => true: 'sick'; case(_ < PBOT1_GOOD_THRESHOLD) => true: diff --git a/source/funkin/save/Save.hx b/source/funkin/save/Save.hx index 1fbcc6c20..c68caad5f 100644 --- a/source/funkin/save/Save.hx +++ b/source/funkin/save/Save.hx @@ -792,7 +792,6 @@ typedef SaveScoreData = typedef SaveScoreTallyData = { - var killer:Int; var sick:Int; var good:Int; var bad:Int; diff --git a/source/funkin/save/migrator/SaveDataMigrator.hx b/source/funkin/save/migrator/SaveDataMigrator.hx index d5b23cfd9..92bee4ceb 100644 --- a/source/funkin/save/migrator/SaveDataMigrator.hx +++ b/source/funkin/save/migrator/SaveDataMigrator.hx @@ -120,7 +120,6 @@ class SaveDataMigrator accuracy: inputSaveData.songCompletion.get('${levelId}-easy') ?? 0.0, tallies: { - killer: 0, sick: 0, good: 0, bad: 0, @@ -140,7 +139,6 @@ class SaveDataMigrator accuracy: inputSaveData.songCompletion.get('${levelId}') ?? 0.0, tallies: { - killer: 0, sick: 0, good: 0, bad: 0, @@ -160,7 +158,6 @@ class SaveDataMigrator accuracy: inputSaveData.songCompletion.get('${levelId}-hard') ?? 0.0, tallies: { - killer: 0, sick: 0, good: 0, bad: 0, @@ -183,7 +180,6 @@ class SaveDataMigrator accuracy: 0, tallies: { - killer: 0, sick: 0, good: 0, bad: 0, @@ -209,7 +205,6 @@ class SaveDataMigrator accuracy: 0, tallies: { - killer: 0, sick: 0, good: 0, bad: 0, @@ -235,7 +230,6 @@ class SaveDataMigrator accuracy: 0, tallies: { - killer: 0, sick: 0, good: 0, bad: 0, diff --git a/source/funkin/util/Constants.hx b/source/funkin/util/Constants.hx index 197fa28e8..1005b312e 100644 --- a/source/funkin/util/Constants.hx +++ b/source/funkin/util/Constants.hx @@ -363,6 +363,12 @@ class Constants */ public static final SCORE_HOLD_BONUS_PER_SECOND:Float = 250.0; + public static final JUDGEMENT_KILLER_COMBO_BREAK:Bool = false; + public static final JUDGEMENT_SICK_COMBO_BREAK:Bool = false; + public static final JUDGEMENT_GOOD_COMBO_BREAK:Bool = false; + public static final JUDGEMENT_BAD_COMBO_BREAK:Bool = true; + public static final JUDGEMENT_SHIT_COMBO_BREAK:Bool = true; + /** * FILE EXTENSIONS */