From 4e273048bc7af39336040170e6f2a983de7d1c12 Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Wed, 26 Nov 2025 16:05:14 +0800 Subject: [PATCH 01/14] Input --- source/funkin/game/PlayState.hx | 8 ++- source/funkin/game/StrumLine.hx | 86 ++++++++++++++++++++++++--------- 2 files changed, 70 insertions(+), 24 deletions(-) diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index cac4e5c8f..309aee198 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -1837,7 +1837,13 @@ class PlayState extends MusicBeatState var directionID:Null = note == null ? direction : note.strumID; if (playerID == null || directionID == null || playerID == -1) return; - var event:NoteMissEvent = gameAndCharsEvent("onPlayerMiss", EventManager.get(NoteMissEvent).recycle(note, -10, 1, muteVocalsOnMiss, note != null ? -0.0475 : -0.04, Paths.sound(FlxG.random.getObject(Flags.DEFAULT_MISS_SOUNDS)), FlxG.random.float(0.1, 0.2), note == null, combo > 5, "sad", true, true, "miss", strumLines.members[playerID].characters, playerID, note != null ? note.noteType : null, directionID, 0)); + var sustain:Bool = note != null ? (note.isSustainNote && note.prevNote != null && note.prevNote.isSustainNote && !note.prevNote.wasGoodHit) : false; + if (sustain) { + strumLine.deleteNote(note); + return; + } + + var event:NoteMissEvent = gameAndCharsEvent("onPlayerMiss", EventManager.get(NoteMissEvent).recycle(note, -10, 1, muteVocalsOnMiss, note != null ? (note.isSustainNote ? -0.2375 : -0.0475) : -0.04, Paths.sound(FlxG.random.getObject(Flags.DEFAULT_MISS_SOUNDS)), FlxG.random.float(0.1, 0.2), note == null, combo > 5, "sad", true, true, "miss", strumLines.members[playerID].characters, playerID, note != null ? note.noteType : null, directionID, 0)); strumLine.onMiss.dispatch(event); if (event.cancelled) { gameAndCharsEvent("onPostPlayerMiss", event); diff --git a/source/funkin/game/StrumLine.hx b/source/funkin/game/StrumLine.hx index c998666e8..710a0e16a 100644 --- a/source/funkin/game/StrumLine.hx +++ b/source/funkin/game/StrumLine.hx @@ -265,8 +265,9 @@ class StrumLine extends FlxTypedGroup { var __notePerStrum:Array = []; function __inputProcessPressed(note:Note) { - if (__pressed[note.strumID] && note.isSustainNote && note.sustainParent != null && note.sustainParent.wasGoodHit && note.strumTime < __updateNote_songPos && !note.wasGoodHit) { + if (__pressed[note.strumID] && note.isSustainNote && note.sustainParent != null && note.prevNote != null && note.prevNote.wasGoodHit && note.strumTime < __updateNote_songPos && !note.wasGoodHit) { note.tripTimer = 1; + note.canBeHit = true; PlayState.instance.goodNoteHit(this, note); } } @@ -287,61 +288,100 @@ class StrumLine extends FlxTypedGroup { __notePerStrum[note.strumID] = note; } } + function __processImmediateSustains(parentNote:Note) + { + if (parentNote == null || parentNote.isSustainNote) + return; + + var currentSustain:Note = parentNote.nextSustain; + while (currentSustain != null && currentSustain.isSustainNote && currentSustain.sustainParent == parentNote) + { + trace("Immediate sustain hit for note at " + currentSustain.strumTime); + if (currentSustain.strumTime <= __updateNote_songPos && !currentSustain.wasGoodHit && currentSustain.canBeHit) + { + currentSustain.tripTimer = 1; + PlayState.instance.goodNoteHit(this, currentSustain); + } + currentSustain = currentSustain.nextSustain; + } + } /** * Updates the input for the strumline, and handles the input. * @param id The ID of the strum **/ - public function updateInput(id:Int = 0) { + public function updateInput(id:Int = 0) + { updateNotes(); - if (cpu) return; + if (cpu) + return; __funcsToExec.clear(); __pressed.resize(members.length); __justPressed.resize(members.length); __justReleased.resize(members.length); - for(i in 0...members.length) { + for (i in 0...members.length) + { __pressed[i] = members[i].__getPressed(this); __justPressed[i] = members[i].__getJustPressed(this); __justReleased[i] = members[i].__getJustReleased(this); } var event = EventManager.get(InputSystemEvent).recycle(__pressed, __justPressed, __justReleased, this, id); - if (PlayState.instance != null) PlayState.instance.gameAndCharsEvent("onInputUpdate", event); - if (event.cancelled) return; + if (PlayState.instance != null) + PlayState.instance.gameAndCharsEvent("onInputUpdate", event); + if (event.cancelled) + return; __pressed = CoolUtil.getDefault(event.pressed, []); __justPressed = CoolUtil.getDefault(event.justPressed, []); __justReleased = CoolUtil.getDefault(event.justReleased, []); - __notePerStrum = cast new haxe.ds.Vector(members.length);//[for(_ in 0...members.length) null]; + __notePerStrum = cast new haxe.ds.Vector(members.length); - - if (__pressed.contains(true)) { - for(c in characters) - if (c.lastAnimContext != DANCE) - c.__lockAnimThisFrame = true; - - __funcsToExec.push(__inputProcessPressed); - } if (__justPressed.contains(true)) __funcsToExec.push(__inputProcessJustPressed); - if (__funcsToExec.length > 0) { - notes.forEachAlive(function(note:Note) { - for(e in __funcsToExec) if (e != null) e(note); + if (__funcsToExec.length > 0) + { + notes.forEachAlive(function(note:Note) + { + for (e in __funcsToExec) + if (e != null) + e(note); }); } - if (!ghostTapping) for(k=>pr in __justPressed) if (pr && __notePerStrum[k] == null) { - // FUCK YOU - PlayState.instance.noteMiss(this, null, k, ID); + for (e in __notePerStrum) + if (e != null) + { + PlayState.instance.goodNoteHit(this, e); + if (!e.isSustainNote) + { + __processImmediateSustains(e); + } + } + + if (__pressed.contains(true)) + { + for (c in characters) + if (c.lastAnimContext != DANCE) + c.__lockAnimThisFrame = true; + + notes.forEachAlive(__inputProcessPressed); } - for(e in __notePerStrum) if (e != null) PlayState.instance.goodNoteHit(this, e); - forEach(function(str:Strum) { + if (!ghostTapping) + for (k => pr in __justPressed) + if (pr && __notePerStrum[k] == null) + { + PlayState.instance.noteMiss(this, null, k, ID); + } + + forEach(function(str:Strum) + { str.updatePlayerInput(str.__getPressed(this), str.__getJustPressed(this), str.__getJustReleased(this)); }); PlayState.instance.gameAndCharsCall("onPostInputUpdate"); From 6a3d1dab19e0d949592607a8218cf84cb6e1dcee Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Wed, 26 Nov 2025 16:16:01 +0800 Subject: [PATCH 02/14] Health --- source/funkin/game/PlayState.hx | 2 +- source/funkin/game/StrumLine.hx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 309aee198..3f1bd0069 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -1843,7 +1843,7 @@ class PlayState extends MusicBeatState return; } - var event:NoteMissEvent = gameAndCharsEvent("onPlayerMiss", EventManager.get(NoteMissEvent).recycle(note, -10, 1, muteVocalsOnMiss, note != null ? (note.isSustainNote ? -0.2375 : -0.0475) : -0.04, Paths.sound(FlxG.random.getObject(Flags.DEFAULT_MISS_SOUNDS)), FlxG.random.float(0.1, 0.2), note == null, combo > 5, "sad", true, true, "miss", strumLines.members[playerID].characters, playerID, note != null ? note.noteType : null, directionID, 0)); + var event:NoteMissEvent = gameAndCharsEvent("onPlayerMiss", EventManager.get(NoteMissEvent).recycle(note, -10, 1, muteVocalsOnMiss, note != null ? (note.isSustainNote ? -0.19 : -0.0475) : -0.04, Paths.sound(FlxG.random.getObject(Flags.DEFAULT_MISS_SOUNDS)), FlxG.random.float(0.1, 0.2), note == null, combo > 5, "sad", true, true, "miss", strumLines.members[playerID].characters, playerID, note != null ? note.noteType : null, directionID, 0)); strumLine.onMiss.dispatch(event); if (event.cancelled) { gameAndCharsEvent("onPostPlayerMiss", event); diff --git a/source/funkin/game/StrumLine.hx b/source/funkin/game/StrumLine.hx index 710a0e16a..3aa35012f 100644 --- a/source/funkin/game/StrumLine.hx +++ b/source/funkin/game/StrumLine.hx @@ -296,7 +296,6 @@ class StrumLine extends FlxTypedGroup { var currentSustain:Note = parentNote.nextSustain; while (currentSustain != null && currentSustain.isSustainNote && currentSustain.sustainParent == parentNote) { - trace("Immediate sustain hit for note at " + currentSustain.strumTime); if (currentSustain.strumTime <= __updateNote_songPos && !currentSustain.wasGoodHit && currentSustain.canBeHit) { currentSustain.tripTimer = 1; From ca4611fe2da6c8778907d5831c6be48a0c99a4fc Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Thu, 27 Nov 2025 21:38:50 +0800 Subject: [PATCH 03/14] optimize --- source/funkin/game/StrumLine.hx | 60 +++++++++------------------------ 1 file changed, 15 insertions(+), 45 deletions(-) diff --git a/source/funkin/game/StrumLine.hx b/source/funkin/game/StrumLine.hx index 3aa35012f..51b218dd3 100644 --- a/source/funkin/game/StrumLine.hx +++ b/source/funkin/game/StrumLine.hx @@ -267,7 +267,6 @@ class StrumLine extends FlxTypedGroup { function __inputProcessPressed(note:Note) { if (__pressed[note.strumID] && note.isSustainNote && note.sustainParent != null && note.prevNote != null && note.prevNote.wasGoodHit && note.strumTime < __updateNote_songPos && !note.wasGoodHit) { note.tripTimer = 1; - note.canBeHit = true; PlayState.instance.goodNoteHit(this, note); } } @@ -288,51 +287,30 @@ class StrumLine extends FlxTypedGroup { __notePerStrum[note.strumID] = note; } } - function __processImmediateSustains(parentNote:Note) - { - if (parentNote == null || parentNote.isSustainNote) - return; - - var currentSustain:Note = parentNote.nextSustain; - while (currentSustain != null && currentSustain.isSustainNote && currentSustain.sustainParent == parentNote) - { - if (currentSustain.strumTime <= __updateNote_songPos && !currentSustain.wasGoodHit && currentSustain.canBeHit) - { - currentSustain.tripTimer = 1; - PlayState.instance.goodNoteHit(this, currentSustain); - } - currentSustain = currentSustain.nextSustain; - } - } /** * Updates the input for the strumline, and handles the input. * @param id The ID of the strum **/ - public function updateInput(id:Int = 0) - { + public function updateInput(id:Int = 0) { updateNotes(); - if (cpu) - return; + if (cpu) return; __funcsToExec.clear(); __pressed.resize(members.length); __justPressed.resize(members.length); __justReleased.resize(members.length); - for (i in 0...members.length) - { + for (i in 0...members.length) { __pressed[i] = members[i].__getPressed(this); __justPressed[i] = members[i].__getJustPressed(this); __justReleased[i] = members[i].__getJustReleased(this); } var event = EventManager.get(InputSystemEvent).recycle(__pressed, __justPressed, __justReleased, this, id); - if (PlayState.instance != null) - PlayState.instance.gameAndCharsEvent("onInputUpdate", event); - if (event.cancelled) - return; + if (PlayState.instance != null) PlayState.instance.gameAndCharsEvent("onInputUpdate", event); + if (event.cancelled) return; __pressed = CoolUtil.getDefault(event.pressed, []); __justPressed = CoolUtil.getDefault(event.justPressed, []); @@ -340,11 +318,11 @@ class StrumLine extends FlxTypedGroup { __notePerStrum = cast new haxe.ds.Vector(members.length); - if (__justPressed.contains(true)) + if (__justPressed.contains(true)) { __funcsToExec.push(__inputProcessJustPressed); + } - if (__funcsToExec.length > 0) - { + if (__funcsToExec.length > 0) { notes.forEachAlive(function(note:Note) { for (e in __funcsToExec) @@ -353,18 +331,11 @@ class StrumLine extends FlxTypedGroup { }); } - for (e in __notePerStrum) - if (e != null) - { - PlayState.instance.goodNoteHit(this, e); - if (!e.isSustainNote) - { - __processImmediateSustains(e); - } - } + if (__pressed.contains(true)) { + for (e in __notePerStrum) + if (e != null) + PlayState.instance.goodNoteHit(this, e); - if (__pressed.contains(true)) - { for (c in characters) if (c.lastAnimContext != DANCE) c.__lockAnimThisFrame = true; @@ -374,15 +345,14 @@ class StrumLine extends FlxTypedGroup { if (!ghostTapping) for (k => pr in __justPressed) - if (pr && __notePerStrum[k] == null) - { + if (pr && __notePerStrum[k] == null) { PlayState.instance.noteMiss(this, null, k, ID); } - forEach(function(str:Strum) - { + forEach(function(str:Strum) { str.updatePlayerInput(str.__getPressed(this), str.__getJustPressed(this), str.__getJustReleased(this)); }); + PlayState.instance.gameAndCharsCall("onPostInputUpdate"); } From 5bc6acb953b61dddbb07f3b21507246ab549f574 Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Thu, 27 Nov 2025 21:43:36 +0800 Subject: [PATCH 04/14] Update StrumLine.hx --- source/funkin/game/StrumLine.hx | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/source/funkin/game/StrumLine.hx b/source/funkin/game/StrumLine.hx index 51b218dd3..e7b81cdfb 100644 --- a/source/funkin/game/StrumLine.hx +++ b/source/funkin/game/StrumLine.hx @@ -318,16 +318,12 @@ class StrumLine extends FlxTypedGroup { __notePerStrum = cast new haxe.ds.Vector(members.length); - if (__justPressed.contains(true)) { + if (__justPressed.contains(true)) __funcsToExec.push(__inputProcessJustPressed); - } if (__funcsToExec.length > 0) { - notes.forEachAlive(function(note:Note) - { - for (e in __funcsToExec) - if (e != null) - e(note); + notes.forEachAlive(function(note:Note) { + for (e in __funcsToExec) if (e != null) e(note); }); } @@ -343,11 +339,10 @@ class StrumLine extends FlxTypedGroup { notes.forEachAlive(__inputProcessPressed); } - if (!ghostTapping) - for (k => pr in __justPressed) - if (pr && __notePerStrum[k] == null) { - PlayState.instance.noteMiss(this, null, k, ID); - } + if (!ghostTapping) for (k => pr in __justPressed) if (pr && __notePerStrum[k] == null) { + // FUCK YOU + PlayState.instance.noteMiss(this, null, k, ID); + } forEach(function(str:Strum) { str.updatePlayerInput(str.__getPressed(this), str.__getJustPressed(this), str.__getJustReleased(this)); From c20b115ac5b84a218a20edc129428669d42c3751 Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Thu, 27 Nov 2025 22:02:42 +0800 Subject: [PATCH 05/14] Update StrumLine.hx --- source/funkin/game/StrumLine.hx | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/source/funkin/game/StrumLine.hx b/source/funkin/game/StrumLine.hx index e7b81cdfb..53c1b3788 100644 --- a/source/funkin/game/StrumLine.hx +++ b/source/funkin/game/StrumLine.hx @@ -258,7 +258,7 @@ class StrumLine extends FlxTypedGroup { } } - var __funcsToExec:ArrayVoid> = []; + // var __funcsToExec:ArrayVoid> = []; var __pressed:Array = []; var __justPressed:Array = []; var __justReleased:Array = []; @@ -297,7 +297,7 @@ class StrumLine extends FlxTypedGroup { if (cpu) return; - __funcsToExec.clear(); + // __funcsToExec.clear(); __pressed.resize(members.length); __justPressed.resize(members.length); __justReleased.resize(members.length); @@ -319,13 +319,7 @@ class StrumLine extends FlxTypedGroup { __notePerStrum = cast new haxe.ds.Vector(members.length); if (__justPressed.contains(true)) - __funcsToExec.push(__inputProcessJustPressed); - - if (__funcsToExec.length > 0) { - notes.forEachAlive(function(note:Note) { - for (e in __funcsToExec) if (e != null) e(note); - }); - } + notes.forEachAlive(__inputProcessJustPressed); if (__pressed.contains(true)) { for (e in __notePerStrum) From 46c9ef87b4384f32abf359021c53f0a468f0e348 Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Thu, 27 Nov 2025 22:04:02 +0800 Subject: [PATCH 06/14] Delete directly? --- source/funkin/game/StrumLine.hx | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/funkin/game/StrumLine.hx b/source/funkin/game/StrumLine.hx index 53c1b3788..c0d901be7 100644 --- a/source/funkin/game/StrumLine.hx +++ b/source/funkin/game/StrumLine.hx @@ -258,7 +258,6 @@ class StrumLine extends FlxTypedGroup { } } - // var __funcsToExec:ArrayVoid> = []; var __pressed:Array = []; var __justPressed:Array = []; var __justReleased:Array = []; @@ -297,7 +296,6 @@ class StrumLine extends FlxTypedGroup { if (cpu) return; - // __funcsToExec.clear(); __pressed.resize(members.length); __justPressed.resize(members.length); __justReleased.resize(members.length); From a5049f0bc4a837438c5e420071e7e768a635064f Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Thu, 27 Nov 2025 22:09:37 +0800 Subject: [PATCH 07/14] Update StrumLine.hx --- source/funkin/game/StrumLine.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/game/StrumLine.hx b/source/funkin/game/StrumLine.hx index c0d901be7..ace81945d 100644 --- a/source/funkin/game/StrumLine.hx +++ b/source/funkin/game/StrumLine.hx @@ -314,7 +314,7 @@ class StrumLine extends FlxTypedGroup { __justPressed = CoolUtil.getDefault(event.justPressed, []); __justReleased = CoolUtil.getDefault(event.justReleased, []); - __notePerStrum = cast new haxe.ds.Vector(members.length); + __notePerStrum = cast new haxe.ds.Vector(members.length); // [for(_ in 0...members.length) null]; if (__justPressed.contains(true)) notes.forEachAlive(__inputProcessJustPressed); From f7700230ad6a404c6da5bfc021f50e46e1e17a34 Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Sun, 30 Nov 2025 17:57:50 +0800 Subject: [PATCH 08/14] optimize --- source/funkin/game/StrumLine.hx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/source/funkin/game/StrumLine.hx b/source/funkin/game/StrumLine.hx index ace81945d..2934076c6 100644 --- a/source/funkin/game/StrumLine.hx +++ b/source/funkin/game/StrumLine.hx @@ -316,9 +316,15 @@ class StrumLine extends FlxTypedGroup { __notePerStrum = cast new haxe.ds.Vector(members.length); // [for(_ in 0...members.length) null]; - if (__justPressed.contains(true)) + if (__justPressed.contains(true)) { notes.forEachAlive(__inputProcessJustPressed); + if (!ghostTapping) for (k => pr in __justPressed) if (pr && __notePerStrum[k] == null) { + // FUCK YOU + PlayState.instance.noteMiss(this, null, k, ID); + } + } + if (__pressed.contains(true)) { for (e in __notePerStrum) if (e != null) @@ -331,11 +337,6 @@ class StrumLine extends FlxTypedGroup { notes.forEachAlive(__inputProcessPressed); } - if (!ghostTapping) for (k => pr in __justPressed) if (pr && __notePerStrum[k] == null) { - // FUCK YOU - PlayState.instance.noteMiss(this, null, k, ID); - } - forEach(function(str:Strum) { str.updatePlayerInput(str.__getPressed(this), str.__getJustPressed(this), str.__getJustReleased(this)); }); From 0c8c90838854e709a68b7a373b3b248e4a03a16c Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Sun, 30 Nov 2025 17:59:20 +0800 Subject: [PATCH 09/14] Update StrumLine.hx --- source/funkin/game/StrumLine.hx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/source/funkin/game/StrumLine.hx b/source/funkin/game/StrumLine.hx index 2934076c6..68aebc744 100644 --- a/source/funkin/game/StrumLine.hx +++ b/source/funkin/game/StrumLine.hx @@ -319,10 +319,8 @@ class StrumLine extends FlxTypedGroup { if (__justPressed.contains(true)) { notes.forEachAlive(__inputProcessJustPressed); - if (!ghostTapping) for (k => pr in __justPressed) if (pr && __notePerStrum[k] == null) { - // FUCK YOU - PlayState.instance.noteMiss(this, null, k, ID); - } + if (!ghostTapping) for (k => pr in __justPressed) if (pr && __notePerStrum[k] == null) + PlayState.instance.noteMiss(this, null, k, ID); // FUCK YOU } if (__pressed.contains(true)) { From 343063a8eeee0a8a7d7df090ac8e3f70cdd1f323 Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Sat, 13 Dec 2025 14:17:33 +0800 Subject: [PATCH 10/14] Options.sustainsAsOneNote --- assets/languages/en/Options.xml | 3 +++ source/funkin/game/PlayState.hx | 11 ++++++----- source/funkin/game/StrumLine.hx | 11 ++++++++++- source/funkin/options/Options.hx | 1 + source/funkin/options/categories/GameplayOptions.hx | 1 + 5 files changed, 21 insertions(+), 6 deletions(-) diff --git a/assets/languages/en/Options.xml b/assets/languages/en/Options.xml index d1958961f..20d0d5c6a 100644 --- a/assets/languages/en/Options.xml +++ b/assets/languages/en/Options.xml @@ -105,6 +105,9 @@ Ghost Tapping If unchecked, trying to hit any strum that have no note that can be hit will cause a miss. + Sustains as One Note + If checked,Hold Notes can't be pressedif you miss, and count as a sin9le Hit/Miss. Uncheck this if you prefer the old Input System. + Naughtyness If unchecked, will censor the Week 7 cutscenes or more. diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 3f1bd0069..188c51cb8 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -1837,13 +1837,14 @@ class PlayState extends MusicBeatState var directionID:Null = note == null ? direction : note.strumID; if (playerID == null || directionID == null || playerID == -1) return; - var sustain:Bool = note != null ? (note.isSustainNote && note.prevNote != null && note.prevNote.isSustainNote && !note.prevNote.wasGoodHit) : false; - if (sustain) { - strumLine.deleteNote(note); - return; + if (Options.sustainsAsOneNote) { + if (note != null ? (note.isSustainNote && note.prevNote != null && note.prevNote.isSustainNote && !note.prevNote.wasGoodHit) : false) { + strumLine.deleteNote(note); + return; + } } - var event:NoteMissEvent = gameAndCharsEvent("onPlayerMiss", EventManager.get(NoteMissEvent).recycle(note, -10, 1, muteVocalsOnMiss, note != null ? (note.isSustainNote ? -0.19 : -0.0475) : -0.04, Paths.sound(FlxG.random.getObject(Flags.DEFAULT_MISS_SOUNDS)), FlxG.random.float(0.1, 0.2), note == null, combo > 5, "sad", true, true, "miss", strumLines.members[playerID].characters, playerID, note != null ? note.noteType : null, directionID, 0)); + var event:NoteMissEvent = gameAndCharsEvent("onPlayerMiss", EventManager.get(NoteMissEvent).recycle(note, -10, 1, muteVocalsOnMiss, note != null ? ((note.isSustainNote && Options.sustainsAsOneNote) ? -0.19 : -0.0475) : -0.04, Paths.sound(FlxG.random.getObject(Flags.DEFAULT_MISS_SOUNDS)), FlxG.random.float(0.1, 0.2), note == null, combo > 5, "sad", true, true, "miss", strumLines.members[playerID].characters, playerID, note != null ? note.noteType : null, directionID, 0)); strumLine.onMiss.dispatch(event); if (event.cancelled) { gameAndCharsEvent("onPostPlayerMiss", event); diff --git a/source/funkin/game/StrumLine.hx b/source/funkin/game/StrumLine.hx index 68aebc744..55c068b6c 100644 --- a/source/funkin/game/StrumLine.hx +++ b/source/funkin/game/StrumLine.hx @@ -264,6 +264,12 @@ class StrumLine extends FlxTypedGroup { var __notePerStrum:Array = []; function __inputProcessPressed(note:Note) { + if (__pressed[note.strumID] && note.isSustainNote && note.strumTime < __updateNote_songPos && !note.wasGoodHit) { + note.tripTimer = 1; + PlayState.instance.goodNoteHit(this, note); + } + } + function __inputProcessPressedOne(note:Note) { if (__pressed[note.strumID] && note.isSustainNote && note.sustainParent != null && note.prevNote != null && note.prevNote.wasGoodHit && note.strumTime < __updateNote_songPos && !note.wasGoodHit) { note.tripTimer = 1; PlayState.instance.goodNoteHit(this, note); @@ -332,7 +338,10 @@ class StrumLine extends FlxTypedGroup { if (c.lastAnimContext != DANCE) c.__lockAnimThisFrame = true; - notes.forEachAlive(__inputProcessPressed); + if (Options.sustainsAsOneNote) + notes.forEachAlive(__inputProcessPressedOne); + else + notes.forEachAlive(__inputProcessPressed); } forEach(function(str:Strum) { diff --git a/source/funkin/options/Options.hx b/source/funkin/options/Options.hx index e36e0e9cb..88697208c 100644 --- a/source/funkin/options/Options.hx +++ b/source/funkin/options/Options.hx @@ -23,6 +23,7 @@ class Options public static var naughtyness:Bool = true; public static var downscroll:Bool = false; public static var ghostTapping:Bool = true; + public static var sustainsAsOneNote:Bool = true; public static var flashingMenu:Bool = true; public static var camZoomOnBeat:Bool = true; public static var fpsCounter:Bool = true; diff --git a/source/funkin/options/categories/GameplayOptions.hx b/source/funkin/options/categories/GameplayOptions.hx index 7d66f3511..8ba992b3a 100644 --- a/source/funkin/options/categories/GameplayOptions.hx +++ b/source/funkin/options/categories/GameplayOptions.hx @@ -15,6 +15,7 @@ class GameplayOptions extends TreeMenuScreen { add(new Checkbox(getNameID('naughtyness'), getDescID('naughtyness'), 'naughtyness')); add(new Checkbox(getNameID('camZoomOnBeat'), getDescID('camZoomOnBeat'), 'camZoomOnBeat')); add(new Checkbox(getNameID('autoPause'), getDescID('autoPause'), 'autoPause', __changeAutoPause)); + add(new Checkbox(getNameID('sustainsAsOneNote'), getDescID('sustainsAsOneNote'), 'sustainsAsOneNote')); add(offsetSetting = new NumOption(getNameID('songOffset'), getDescID('songOffset'), -999, 999, 1, 'songOffset', __changeOffset)); add(new SliderOption(getNameID('volumeMusic'), getDescID('volumeMusic'), 0, 1, 1, 5, 'volumeMusic', -1, __changeVolumeMusic)); add(new SliderOption(getNameID('volumeSFX'), getDescID('volumeSFX'), 0, 1, 1, 5, 'volumeSFX')); From 798703400c7244c12a6a1ac3c192eaa0498e483a Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Sat, 13 Dec 2025 14:30:53 +0800 Subject: [PATCH 11/14] languages --- assets/languages/en/Options.xml | 2 +- assets/languages/es/Options.xml | 3 +++ assets/languages/it/Options.xml | 3 +++ assets/languages/pl/Options.xml | 3 +++ assets/languages/pt/Options.xml | 3 +++ 5 files changed, 13 insertions(+), 1 deletion(-) diff --git a/assets/languages/en/Options.xml b/assets/languages/en/Options.xml index 20d0d5c6a..c13468037 100644 --- a/assets/languages/en/Options.xml +++ b/assets/languages/en/Options.xml @@ -106,7 +106,7 @@ If unchecked, trying to hit any strum that have no note that can be hit will cause a miss. Sustains as One Note - If checked,Hold Notes can't be pressedif you miss, and count as a sin9le Hit/Miss. Uncheck this if you prefer the old Input System. + If checked,Hold Notes can't be pressedif you miss, and count as a single Hit/Miss. Uncheck this if you prefer the old Input System. Naughtyness If unchecked, will censor the Week 7 cutscenes or more. diff --git a/assets/languages/es/Options.xml b/assets/languages/es/Options.xml index bd25ee90b..1f2e69f88 100644 --- a/assets/languages/es/Options.xml +++ b/assets/languages/es/Options.xml @@ -94,6 +94,9 @@ Si está desactivado, apretar una tecla sin ninguna nota contará como un Combo Break. + Sostenidos como Una Nota + Si está activado, las notas sostenidas no se pueden presionar si fallas, y cuentan como un solo Acierto/Fallo. Desactiva esto si prefieres el sistema de entrada antiguo. + Guarradas Si está desactivado, se censurarán las escenas de Week 7. diff --git a/assets/languages/it/Options.xml b/assets/languages/it/Options.xml index ed24a8ee3..489e6530e 100644 --- a/assets/languages/it/Options.xml +++ b/assets/languages/it/Options.xml @@ -101,6 +101,9 @@ Ghost Tapping Se disattivato, cliccare una strum in assenza di una nota risulterà in un miss. + Note Sostenute come Una Nota + Se attivato, le note sostenute non possono essere premute se si sbaglia, e contano come un singolo Hit/Miss. Disattiva questo se preferisci il vecchio sistema di input. + Volgarita' Se disattivato, censorerà i filmati della Week 7 o altro. diff --git a/assets/languages/pl/Options.xml b/assets/languages/pl/Options.xml index f14687f33..5d5e7e0d0 100644 --- a/assets/languages/pl/Options.xml +++ b/assets/languages/pl/Options.xml @@ -95,6 +95,9 @@ Wolne stukanie Jeśli wyłączone, próba kliknięcia struny która nie ma nuty sprawi chybienie. + Słupki jako Jeden Dźwięk + Jeśli włączone, Słupków nie da się wcisnąć po pomyłce, i liczą się jako jedno Trafienie/Pomyłka. Wyłącz jeśli wolisz stary system wejścia. + Niegrzeczność Jeśli wyłączone, cenzuruje scenki 7 Tygodnia. diff --git a/assets/languages/pt/Options.xml b/assets/languages/pt/Options.xml index c9f02aac1..1e3b86acb 100644 --- a/assets/languages/pt/Options.xml +++ b/assets/languages/pt/Options.xml @@ -105,6 +105,9 @@ Toques Fantasma Se desmarcado, bater em teclas que não tem uma nota conta como erro. + Sustenidos como Uma Nota + Se marcado, as notas seguidas não podem ser pressionadas se falharem, e contam como um único Acerto/Falha. Desmarque isto se preferir o sistema de entrada antigo. + Indecência Se desmarcado, irá censurar as cenas da Semana 7, ou mais. From a6250f1e3515e00d3df9cfa9119e9e5e5eb2acbb Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Sat, 13 Dec 2025 14:31:33 +0800 Subject: [PATCH 12/14] Update Options.xml --- assets/languages/en/Options.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/languages/en/Options.xml b/assets/languages/en/Options.xml index c13468037..b1f4d75f5 100644 --- a/assets/languages/en/Options.xml +++ b/assets/languages/en/Options.xml @@ -106,7 +106,7 @@ If unchecked, trying to hit any strum that have no note that can be hit will cause a miss. Sustains as One Note - If checked,Hold Notes can't be pressedif you miss, and count as a single Hit/Miss. Uncheck this if you prefer the old Input System. + If checked, Hold Notes can't be pressedif you miss, and count as a single Hit/Miss. Uncheck this if you prefer the old Input System. Naughtyness If unchecked, will censor the Week 7 cutscenes or more. From 27a1705691816c95575501a323b27c99e047d6a8 Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Sat, 13 Dec 2025 15:03:02 +0800 Subject: [PATCH 13/14] Update PlayState.hx --- source/funkin/game/PlayState.hx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 188c51cb8..34ae75bb9 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -1844,7 +1844,7 @@ class PlayState extends MusicBeatState } } - var event:NoteMissEvent = gameAndCharsEvent("onPlayerMiss", EventManager.get(NoteMissEvent).recycle(note, -10, 1, muteVocalsOnMiss, note != null ? ((note.isSustainNote && Options.sustainsAsOneNote) ? -0.19 : -0.0475) : -0.04, Paths.sound(FlxG.random.getObject(Flags.DEFAULT_MISS_SOUNDS)), FlxG.random.float(0.1, 0.2), note == null, combo > 5, "sad", true, true, "miss", strumLines.members[playerID].characters, playerID, note != null ? note.noteType : null, directionID, 0)); + var event:NoteMissEvent = gameAndCharsEvent("onPlayerMiss", EventManager.get(NoteMissEvent).recycle(note, -10, 1, muteVocalsOnMiss, note != null ? ((note.isSustainNote && Options.sustainsAsOneNote) ? -0.1425 : -0.0475) : -0.04, Paths.sound(FlxG.random.getObject(Flags.DEFAULT_MISS_SOUNDS)), FlxG.random.float(0.1, 0.2), note == null, combo > 5, "sad", true, true, "miss", strumLines.members[playerID].characters, playerID, note != null ? note.noteType : null, directionID, 0)); strumLine.onMiss.dispatch(event); if (event.cancelled) { gameAndCharsEvent("onPostPlayerMiss", event); From 3cc7b5cb607a0fc2566a1aa0c0688e6e5906af51 Mon Sep 17 00:00:00 2001 From: HEIHUAa <112499486+HEIHUAa@users.noreply.github.com> Date: Sat, 13 Dec 2025 20:51:52 +0800 Subject: [PATCH 14/14] Flags --- assets/languages/en/Options.xml | 3 --- assets/languages/es/Options.xml | 3 --- assets/languages/it/Options.xml | 3 --- assets/languages/pl/Options.xml | 3 --- assets/languages/pt/Options.xml | 3 --- source/funkin/backend/system/Flags.hx | 2 ++ source/funkin/game/PlayState.hx | 4 ++-- source/funkin/game/StrumLine.hx | 2 +- source/funkin/options/Options.hx | 1 - source/funkin/options/categories/GameplayOptions.hx | 1 - 10 files changed, 5 insertions(+), 20 deletions(-) diff --git a/assets/languages/en/Options.xml b/assets/languages/en/Options.xml index b1f4d75f5..d1958961f 100644 --- a/assets/languages/en/Options.xml +++ b/assets/languages/en/Options.xml @@ -105,9 +105,6 @@ Ghost Tapping If unchecked, trying to hit any strum that have no note that can be hit will cause a miss. - Sustains as One Note - If checked, Hold Notes can't be pressedif you miss, and count as a single Hit/Miss. Uncheck this if you prefer the old Input System. - Naughtyness If unchecked, will censor the Week 7 cutscenes or more. diff --git a/assets/languages/es/Options.xml b/assets/languages/es/Options.xml index 1f2e69f88..bd25ee90b 100644 --- a/assets/languages/es/Options.xml +++ b/assets/languages/es/Options.xml @@ -94,9 +94,6 @@ Si está desactivado, apretar una tecla sin ninguna nota contará como un Combo Break. - Sostenidos como Una Nota - Si está activado, las notas sostenidas no se pueden presionar si fallas, y cuentan como un solo Acierto/Fallo. Desactiva esto si prefieres el sistema de entrada antiguo. - Guarradas Si está desactivado, se censurarán las escenas de Week 7. diff --git a/assets/languages/it/Options.xml b/assets/languages/it/Options.xml index 489e6530e..ed24a8ee3 100644 --- a/assets/languages/it/Options.xml +++ b/assets/languages/it/Options.xml @@ -101,9 +101,6 @@ Ghost Tapping Se disattivato, cliccare una strum in assenza di una nota risulterà in un miss. - Note Sostenute come Una Nota - Se attivato, le note sostenute non possono essere premute se si sbaglia, e contano come un singolo Hit/Miss. Disattiva questo se preferisci il vecchio sistema di input. - Volgarita' Se disattivato, censorerà i filmati della Week 7 o altro. diff --git a/assets/languages/pl/Options.xml b/assets/languages/pl/Options.xml index 5d5e7e0d0..f14687f33 100644 --- a/assets/languages/pl/Options.xml +++ b/assets/languages/pl/Options.xml @@ -95,9 +95,6 @@ Wolne stukanie Jeśli wyłączone, próba kliknięcia struny która nie ma nuty sprawi chybienie. - Słupki jako Jeden Dźwięk - Jeśli włączone, Słupków nie da się wcisnąć po pomyłce, i liczą się jako jedno Trafienie/Pomyłka. Wyłącz jeśli wolisz stary system wejścia. - Niegrzeczność Jeśli wyłączone, cenzuruje scenki 7 Tygodnia. diff --git a/assets/languages/pt/Options.xml b/assets/languages/pt/Options.xml index 1e3b86acb..c9f02aac1 100644 --- a/assets/languages/pt/Options.xml +++ b/assets/languages/pt/Options.xml @@ -105,9 +105,6 @@ Toques Fantasma Se desmarcado, bater em teclas que não tem uma nota conta como erro. - Sustenidos como Uma Nota - Se marcado, as notas seguidas não podem ser pressionadas se falharem, e contam como um único Acerto/Falha. Desmarque isto se preferir o sistema de entrada antigo. - Indecência Se desmarcado, irá censurar as cenas da Semana 7, ou mais. diff --git a/source/funkin/backend/system/Flags.hx b/source/funkin/backend/system/Flags.hx index 97ff49ffc..1d6187b69 100644 --- a/source/funkin/backend/system/Flags.hx +++ b/source/funkin/backend/system/Flags.hx @@ -136,6 +136,8 @@ class Flags { public static var DEFAULT_MODCHART_HOLD_SUBDIVISIONS:Int = 4; #end + public static var SUSTAINS_AS_ONE_NOTE:Bool = true; + @:also(funkin.game.Character.FALLBACK_DEAD_CHARACTER) public static var DEFAULT_GAMEOVER_CHARACTER:String = "bf-dead"; diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 34ae75bb9..f38384628 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -1837,14 +1837,14 @@ class PlayState extends MusicBeatState var directionID:Null = note == null ? direction : note.strumID; if (playerID == null || directionID == null || playerID == -1) return; - if (Options.sustainsAsOneNote) { + if (Flags.SUSTAINS_AS_ONE_NOTE) { if (note != null ? (note.isSustainNote && note.prevNote != null && note.prevNote.isSustainNote && !note.prevNote.wasGoodHit) : false) { strumLine.deleteNote(note); return; } } - var event:NoteMissEvent = gameAndCharsEvent("onPlayerMiss", EventManager.get(NoteMissEvent).recycle(note, -10, 1, muteVocalsOnMiss, note != null ? ((note.isSustainNote && Options.sustainsAsOneNote) ? -0.1425 : -0.0475) : -0.04, Paths.sound(FlxG.random.getObject(Flags.DEFAULT_MISS_SOUNDS)), FlxG.random.float(0.1, 0.2), note == null, combo > 5, "sad", true, true, "miss", strumLines.members[playerID].characters, playerID, note != null ? note.noteType : null, directionID, 0)); + var event:NoteMissEvent = gameAndCharsEvent("onPlayerMiss", EventManager.get(NoteMissEvent).recycle(note, -10, 1, muteVocalsOnMiss, note != null ? ((note.isSustainNote && Flags.SUSTAINS_AS_ONE_NOTE) ? -0.1425 : -0.0475) : -0.04, Paths.sound(FlxG.random.getObject(Flags.DEFAULT_MISS_SOUNDS)), FlxG.random.float(0.1, 0.2), note == null, combo > 5, "sad", true, true, "miss", strumLines.members[playerID].characters, playerID, note != null ? note.noteType : null, directionID, 0)); strumLine.onMiss.dispatch(event); if (event.cancelled) { gameAndCharsEvent("onPostPlayerMiss", event); diff --git a/source/funkin/game/StrumLine.hx b/source/funkin/game/StrumLine.hx index 55c068b6c..8945c7d58 100644 --- a/source/funkin/game/StrumLine.hx +++ b/source/funkin/game/StrumLine.hx @@ -338,7 +338,7 @@ class StrumLine extends FlxTypedGroup { if (c.lastAnimContext != DANCE) c.__lockAnimThisFrame = true; - if (Options.sustainsAsOneNote) + if (Flags.SUSTAINS_AS_ONE_NOTE) notes.forEachAlive(__inputProcessPressedOne); else notes.forEachAlive(__inputProcessPressed); diff --git a/source/funkin/options/Options.hx b/source/funkin/options/Options.hx index 88697208c..e36e0e9cb 100644 --- a/source/funkin/options/Options.hx +++ b/source/funkin/options/Options.hx @@ -23,7 +23,6 @@ class Options public static var naughtyness:Bool = true; public static var downscroll:Bool = false; public static var ghostTapping:Bool = true; - public static var sustainsAsOneNote:Bool = true; public static var flashingMenu:Bool = true; public static var camZoomOnBeat:Bool = true; public static var fpsCounter:Bool = true; diff --git a/source/funkin/options/categories/GameplayOptions.hx b/source/funkin/options/categories/GameplayOptions.hx index 8ba992b3a..7d66f3511 100644 --- a/source/funkin/options/categories/GameplayOptions.hx +++ b/source/funkin/options/categories/GameplayOptions.hx @@ -15,7 +15,6 @@ class GameplayOptions extends TreeMenuScreen { add(new Checkbox(getNameID('naughtyness'), getDescID('naughtyness'), 'naughtyness')); add(new Checkbox(getNameID('camZoomOnBeat'), getDescID('camZoomOnBeat'), 'camZoomOnBeat')); add(new Checkbox(getNameID('autoPause'), getDescID('autoPause'), 'autoPause', __changeAutoPause)); - add(new Checkbox(getNameID('sustainsAsOneNote'), getDescID('sustainsAsOneNote'), 'sustainsAsOneNote')); add(offsetSetting = new NumOption(getNameID('songOffset'), getDescID('songOffset'), -999, 999, 1, 'songOffset', __changeOffset)); add(new SliderOption(getNameID('volumeMusic'), getDescID('volumeMusic'), 0, 1, 1, 5, 'volumeMusic', -1, __changeVolumeMusic)); add(new SliderOption(getNameID('volumeSFX'), getDescID('volumeSFX'), 0, 1, 1, 5, 'volumeSFX'));