From 6c99f7161d05c3dd6134af746d61380612ec6c5c Mon Sep 17 00:00:00 2001
From: EliteMasterEric <ericmyllyoja@gmail.com>
Date: Fri, 29 Sep 2023 21:44:42 -0400
Subject: [PATCH 01/12] Working Input Style, note snapping in menu, W/S to
 scroll

---
 assets                                        |   2 +-
 .../ui/debug/charting/ChartEditorState.hx     | 131 ++++++++++++------
 2 files changed, 91 insertions(+), 42 deletions(-)

diff --git a/assets b/assets
index a62e7e50d..3805b746c 160000
--- a/assets
+++ b/assets
@@ -1 +1 @@
-Subproject commit a62e7e50d59c14d256c75da651b79dea77e1620e
+Subproject commit 3805b746cece5dd8a04c6ba0045d43136ac75753
diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx
index add65c5bf..6e559733b 100644
--- a/source/funkin/ui/debug/charting/ChartEditorState.hx
+++ b/source/funkin/ui/debug/charting/ChartEditorState.hx
@@ -690,6 +690,16 @@ class ChartEditorState extends HaxeUIState
    */
   var downKeyHandler:TurboKeyHandler = TurboKeyHandler.build(FlxKey.DOWN);
 
+  /**
+   * Variable used to track how long the user has been holding the W keybind.
+   */
+  var wKeyHandler:TurboKeyHandler = TurboKeyHandler.build(FlxKey.W);
+
+  /**
+   * Variable used to track how long the user has been holding the S keybind.
+   */
+  var sKeyHandler:TurboKeyHandler = TurboKeyHandler.build(FlxKey.S);
+
   /**
    * Variable used to track how long the user has been holding the page-up keybind.
    */
@@ -1659,16 +1669,20 @@ class ChartEditorState extends HaxeUIState
 
     addUIClickListener('menubarItemSelectNone', _ -> performCommand(new DeselectAllItemsCommand(currentNoteSelection, currentEventSelection)));
 
-    // TODO: Implement these.
-    // addUIClickListener('menubarItemSelectRegion', _ -> doSomething());
-    // addUIClickListener('menubarItemSelectBeforeCursor', _ -> doSomething());
-    // addUIClickListener('menubarItemSelectAfterCursor', _ -> doSomething());
-
     addUIClickListener('menubarItemPlaytestFull', _ -> testSongInPlayState(false));
     addUIClickListener('menubarItemPlaytestMinimal', _ -> testSongInPlayState(true));
 
-    addUIChangeListener('menubarItemInputStyleGroup', function(event:UIEvent) {
-      trace('Change input style: ${event.target}');
+    addUIClickListener('menuBarItemNoteSnapDecrease', _ -> noteSnapQuantIndex--);
+    addUIClickListener('menuBarItemNoteSnapIncrease', _ -> noteSnapQuantIndex++);
+
+    addUIChangeListener('menuBarItemInputStyleNone', function(event:UIEvent) {
+      currentLiveInputStyle = None;
+    });
+    addUIChangeListener('menuBarItemInputStyleNumberKeys', function(event:UIEvent) {
+      currentLiveInputStyle = NumberKeys;
+    });
+    addUIChangeListener('menuBarItemInputStyleWASD', function(event:UIEvent) {
+      currentLiveInputStyle = WASD;
     });
 
     addUIClickListener('menubarItemAbout', _ -> ChartEditorDialogHandler.openAboutDialog(this));
@@ -1769,6 +1783,8 @@ class ChartEditorState extends HaxeUIState
     add(redoKeyHandler);
     add(upKeyHandler);
     add(downKeyHandler);
+    add(wKeyHandler);
+    add(sKeyHandler);
     add(pageUpKeyHandler);
     add(pageDownKeyHandler);
   }
@@ -1901,13 +1917,26 @@ class ChartEditorState extends HaxeUIState
     }
 
     // Up Arrow = Scroll Up
-    if (upKeyHandler.activated && currentLiveInputStyle != LiveInputStyle.WASD)
+    if (upKeyHandler.activated && currentLiveInputStyle == None)
     {
       scrollAmount = -GRID_SIZE * 0.25 * 5.0;
       shouldPause = true;
     }
     // Down Arrow = Scroll Down
-    if (downKeyHandler.activated && currentLiveInputStyle != LiveInputStyle.WASD)
+    if (downKeyHandler.activated && currentLiveInputStyle == None)
+    {
+      scrollAmount = GRID_SIZE * 0.25 * 5.0;
+      shouldPause = true;
+    }
+
+    // W = Scroll Up (doesn't work with Ctrl+Scroll)
+    if (wKeyHandler.activated && currentLiveInputStyle == None && !FlxG.keys.pressed.CONTROL)
+    {
+      scrollAmount = -GRID_SIZE * 0.25 * 5.0;
+      shouldPause = true;
+    }
+    // S = Scroll Down (doesn't work with Ctrl+Scroll)
+    if (sKeyHandler.activated && currentLiveInputStyle == None && !FlxG.keys.pressed.CONTROL)
     {
       scrollAmount = GRID_SIZE * 0.25 * 5.0;
       shouldPause = true;
@@ -2045,14 +2074,17 @@ class ChartEditorState extends HaxeUIState
 
   function handleSnap():Void
   {
-    if (FlxG.keys.justPressed.LEFT && !FlxG.keys.pressed.CONTROL)
+    if (currentLiveInputStyle == None)
     {
-      noteSnapQuantIndex--;
-    }
+      if (FlxG.keys.justPressed.LEFT && !FlxG.keys.pressed.CONTROL)
+      {
+        noteSnapQuantIndex--;
+      }
 
-    if (FlxG.keys.justPressed.RIGHT && !FlxG.keys.pressed.CONTROL)
-    {
-      noteSnapQuantIndex++;
+      if (FlxG.keys.justPressed.RIGHT && !FlxG.keys.pressed.CONTROL)
+      {
+        noteSnapQuantIndex++;
+      }
     }
   }
 
@@ -3111,13 +3143,17 @@ class ChartEditorState extends HaxeUIState
    */
   function handleViewKeybinds():Void
   {
-    if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.LEFT)
+    if (currentLiveInputStyle == None)
     {
-      incrementDifficulty(-1);
-    }
-    if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.RIGHT)
-    {
-      incrementDifficulty(1);
+      if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.LEFT)
+      {
+        incrementDifficulty(-1);
+      }
+      if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.RIGHT)
+      {
+        incrementDifficulty(1);
+      }
+      // Would bind Ctrl+A and Ctrl+D here, but they are already bound to Select All and Select None.
     }
   }
 
@@ -3756,25 +3792,26 @@ class ChartEditorState extends HaxeUIState
     switch (currentLiveInputStyle)
     {
       case LiveInputStyle.WASD:
-        if (FlxG.keys.justPressed.A) placeNoteAtPlayhead(0);
-        if (FlxG.keys.justPressed.S) placeNoteAtPlayhead(1);
-        if (FlxG.keys.justPressed.W) placeNoteAtPlayhead(2);
-        if (FlxG.keys.justPressed.D) placeNoteAtPlayhead(3);
+        if (FlxG.keys.justPressed.A) placeNoteAtPlayhead(4);
+        if (FlxG.keys.justPressed.S) placeNoteAtPlayhead(5);
+        if (FlxG.keys.justPressed.W) placeNoteAtPlayhead(6);
+        if (FlxG.keys.justPressed.D) placeNoteAtPlayhead(7);
 
-        if (FlxG.keys.justPressed.LEFT) placeNoteAtPlayhead(4);
-        if (FlxG.keys.justPressed.DOWN) placeNoteAtPlayhead(5);
-        if (FlxG.keys.justPressed.UP) placeNoteAtPlayhead(6);
-        if (FlxG.keys.justPressed.RIGHT) placeNoteAtPlayhead(7);
+        if (FlxG.keys.justPressed.LEFT) placeNoteAtPlayhead(0);
+        if (FlxG.keys.justPressed.DOWN) placeNoteAtPlayhead(1);
+        if (FlxG.keys.justPressed.UP) placeNoteAtPlayhead(2);
+        if (FlxG.keys.justPressed.RIGHT) placeNoteAtPlayhead(3);
       case LiveInputStyle.NumberKeys:
-        if (FlxG.keys.justPressed.ONE) placeNoteAtPlayhead(0);
-        if (FlxG.keys.justPressed.TWO) placeNoteAtPlayhead(1);
-        if (FlxG.keys.justPressed.THREE) placeNoteAtPlayhead(2);
-        if (FlxG.keys.justPressed.FOUR) placeNoteAtPlayhead(3);
+        // Flipped because Dad is on the left but represents data 0-3.
+        if (FlxG.keys.justPressed.ONE) placeNoteAtPlayhead(4);
+        if (FlxG.keys.justPressed.TWO) placeNoteAtPlayhead(5);
+        if (FlxG.keys.justPressed.THREE) placeNoteAtPlayhead(6);
+        if (FlxG.keys.justPressed.FOUR) placeNoteAtPlayhead(7);
 
-        if (FlxG.keys.justPressed.FIVE) placeNoteAtPlayhead(4);
-        if (FlxG.keys.justPressed.SIX) placeNoteAtPlayhead(5);
-        if (FlxG.keys.justPressed.SEVEN) placeNoteAtPlayhead(6);
-        if (FlxG.keys.justPressed.EIGHT) placeNoteAtPlayhead(7);
+        if (FlxG.keys.justPressed.FIVE) placeNoteAtPlayhead(0);
+        if (FlxG.keys.justPressed.SIX) placeNoteAtPlayhead(1);
+        if (FlxG.keys.justPressed.SEVEN) placeNoteAtPlayhead(2);
+        if (FlxG.keys.justPressed.EIGHT) placeNoteAtPlayhead(3);
       case LiveInputStyle.None:
         // Do nothing.
     }
@@ -3783,12 +3820,24 @@ class ChartEditorState extends HaxeUIState
   function placeNoteAtPlayhead(column:Int):Void
   {
     var playheadPos:Float = scrollPositionInPixels + playheadPositionInPixels;
-    var playheadPosFractionalStep:Float = playheadPos / GRID_SIZE / (16 / noteSnapQuant);
+    var playheadPosFractionalStep:Float = playheadPos / GRID_SIZE / noteSnapRatio;
     var playheadPosStep:Int = Std.int(Math.floor(playheadPosFractionalStep));
-    var playheadPosMs:Float = playheadPosStep * Conductor.stepLengthMs * (16 / noteSnapQuant);
+    var playheadPosSnappedMs:Float = playheadPosStep * Conductor.stepLengthMs * noteSnapRatio;
 
-    var newNoteData:SongNoteData = new SongNoteData(playheadPosMs, column, 0, selectedNoteKind);
-    performCommand(new AddNotesCommand([newNoteData], FlxG.keys.pressed.CONTROL));
+    // Look for notes within 1 step of the playhead.
+    var notesAtPos:Array<SongNoteData> = SongDataUtils.getNotesInTimeRange(currentSongChartNoteData, playheadPosSnappedMs,
+      playheadPosSnappedMs + Conductor.stepLengthMs * noteSnapRatio);
+    notesAtPos = SongDataUtils.getNotesWithData(notesAtPos, [column]);
+
+    if (notesAtPos.length == 0)
+    {
+      var newNoteData:SongNoteData = new SongNoteData(playheadPosSnappedMs, column, 0, selectedNoteKind);
+      performCommand(new AddNotesCommand([newNoteData], FlxG.keys.pressed.CONTROL));
+    }
+    else
+    {
+      trace('Already a note there.');
+    }
   }
 
   function set_scrollPositionInPixels(value:Float):Float

From 6e37b81b6796df7ceefeedd6a2e18a9f9bfab2ee Mon Sep 17 00:00:00 2001
From: EliteMasterEric <ericmyllyoja@gmail.com>
Date: Fri, 6 Oct 2023 01:21:45 -0400
Subject: [PATCH 02/12] Update assets/

---
 assets | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/assets b/assets
index 3805b746c..628569645 160000
--- a/assets
+++ b/assets
@@ -1 +1 @@
-Subproject commit 3805b746cece5dd8a04c6ba0045d43136ac75753
+Subproject commit 62856964577dab4994737d1fba32399c43add677

From d7a15dc71339fff60149802715e8e0241216d058 Mon Sep 17 00:00:00 2001
From: EliteMasterEric <ericmyllyoja@gmail.com>
Date: Wed, 11 Oct 2023 00:30:41 -0400
Subject: [PATCH 03/12] Fix exit button in Chart Editor menu

---
 .../charting/ChartEditorImportExportHandler.hx      |  8 ++++----
 source/funkin/ui/debug/charting/ChartEditorState.hx | 13 +++++++++++--
 2 files changed, 15 insertions(+), 6 deletions(-)

diff --git a/source/funkin/ui/debug/charting/ChartEditorImportExportHandler.hx b/source/funkin/ui/debug/charting/ChartEditorImportExportHandler.hx
index 9ac903e38..f116ad3f1 100644
--- a/source/funkin/ui/debug/charting/ChartEditorImportExportHandler.hx
+++ b/source/funkin/ui/debug/charting/ChartEditorImportExportHandler.hx
@@ -116,10 +116,10 @@ class ChartEditorImportExportHandler
 
   /**
    * @param force Whether to force the export without prompting the user for a file location.
-   * @param tmp If true, save to the temporary directory instead of the local `backup` directory.
    */
-  public static function exportAllSongData(state:ChartEditorState, force:Bool = false, tmp:Bool = false):Void
+  public static function exportAllSongData(state:ChartEditorState, force:Bool = false):Void
   {
+    var tmp = false;
     var zipEntries:Array<haxe.zip.Entry> = [];
 
     for (variation in state.availableVariations)
@@ -133,9 +133,9 @@ class ChartEditorImportExportHandler
       if (variationId == '')
       {
         var variationMetadata:Null<SongMetadata> = state.songMetadata.get(variation);
-        if (variationMetadata != null) zipEntries.push(FileUtil.makeZIPEntry('${state.currentSongId}-metadata.json', SerializerUtil.toJSON(variationMetadata)));
+        if (variationMetadata != null) zipEntries.push(FileUtil.makeZIPEntry('${state.currentSongId}-metadata.json', variationMetadata.serialize()));
         var variationChart:Null<SongChartData> = state.songChartData.get(variation);
-        if (variationChart != null) zipEntries.push(FileUtil.makeZIPEntry('${state.currentSongId}-chart.json', SerializerUtil.toJSON(variationChart)));
+        if (variationChart != null) zipEntries.push(FileUtil.makeZIPEntry('${state.currentSongId}-chart.json', variationChart.serialize()));
       }
       else
       {
diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx
index b94041afd..38c14bb08 100644
--- a/source/funkin/ui/debug/charting/ChartEditorState.hx
+++ b/source/funkin/ui/debug/charting/ChartEditorState.hx
@@ -1609,6 +1609,7 @@ class ChartEditorState extends HaxeUIState
     addUIClickListener('menubarItemSaveChartAs', _ -> ChartEditorImportExportHandler.exportAllSongData(this));
     addUIClickListener('menubarItemLoadInst', _ -> ChartEditorDialogHandler.openUploadInstDialog(this, true));
     addUIClickListener('menubarItemImportChart', _ -> ChartEditorDialogHandler.openImportChartDialog(this, 'legacy', true));
+    addUIClickListener('menubarItemExit', _ -> quitChartEditor());
 
     addUIClickListener('menubarItemUndo', _ -> undoLastCommand());
 
@@ -1795,7 +1796,7 @@ class ChartEditorState extends HaxeUIState
     // Auto-save to local storage.
     #else
     // Auto-save to temp file.
-    ChartEditorImportExportHandler.exportAllSongData(this, true, true);
+    ChartEditorImportExportHandler.exportAllSongData(this, true);
     #end
   }
 
@@ -3032,10 +3033,16 @@ class ChartEditorState extends HaxeUIState
     // CTRL + Q = Quit to Menu
     if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.Q)
     {
-      FlxG.switchState(new MainMenuState());
+      quitChartEditor();
     }
   }
 
+  function quitChartEditor():Void
+  {
+    autoSave();
+    FlxG.switchState(new MainMenuState());
+  }
+
   /**
    * Handle keybinds for edit menu items.
    */
@@ -3920,6 +3927,8 @@ class ChartEditorState extends HaxeUIState
    */
   public function testSongInPlayState(minimal:Bool = false):Void
   {
+    autoSave();
+
     var startTimestamp:Float = 0;
     if (playtestStartTime) startTimestamp = scrollPositionInMs + playheadPositionInMs;
 

From 05044d9af3b5995c11b522dbfade43349346f74d Mon Sep 17 00:00:00 2001
From: EliteMasterEric <ericmyllyoja@gmail.com>
Date: Wed, 11 Oct 2023 00:39:16 -0400
Subject: [PATCH 04/12] Override F4 behavior

---
 source/funkin/ui/debug/charting/ChartEditorState.hx | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx
index 38c14bb08..7ddc749df 100644
--- a/source/funkin/ui/debug/charting/ChartEditorState.hx
+++ b/source/funkin/ui/debug/charting/ChartEditorState.hx
@@ -1818,6 +1818,13 @@ class ChartEditorState extends HaxeUIState
 
   public override function update(elapsed:Float):Void
   {
+    // Override F4 behavior to include the autosave.
+    if (FlxG.keys.justPressed.F4)
+    {
+      quitChartEditor();
+      return;
+    }
+
     // dispatchEvent gets called here.
     super.update(elapsed);
 

From 0c0829b2d0ea349f983c6c3478927a9a285e06f9 Mon Sep 17 00:00:00 2001
From: Cameron Taylor <cameron.taylor.ninja@gmail.com>
Date: Wed, 11 Oct 2023 07:43:44 -0400
Subject: [PATCH 05/12] sound effects for stickersubstate

---
 assets                              |  2 +-
 source/funkin/ui/StickerSubState.hx | 57 +++++++++++++++++++++++++++++
 2 files changed, 58 insertions(+), 1 deletion(-)

diff --git a/assets b/assets
index d946072ac..775a0c0a5 160000
--- a/assets
+++ b/assets
@@ -1 +1 @@
-Subproject commit d946072ac531e049e2a817048b65fa1ca541134a
+Subproject commit 775a0c0a5b2d3009fbec17560c7364a4ac61e3c7
diff --git a/source/funkin/ui/StickerSubState.hx b/source/funkin/ui/StickerSubState.hx
index 067f50c31..bde36b160 100644
--- a/source/funkin/ui/StickerSubState.hx
+++ b/source/funkin/ui/StickerSubState.hx
@@ -17,6 +17,9 @@ import openfl.geom.Matrix;
 import openfl.display.Sprite;
 import openfl.display.Bitmap;
 
+using Lambda;
+using StringTools;
+
 class StickerSubState extends MusicBeatSubState
 {
   public var grpStickers:FlxTypedGroup<StickerSprite>;
@@ -26,10 +29,60 @@ class StickerSubState extends MusicBeatSubState
 
   var nextState:NEXTSTATE = FREEPLAY;
 
+  // what "folders" to potentially load from (as of writing only "keys" exist)
+  var soundSelections:Array<String> = [];
+  // what "folder" was randomly selected
+  var soundSelection:String = "";
+  var sounds:Array<String> = [];
+
   public function new(?oldStickers:Array<StickerSprite>, ?nextState:NEXTSTATE = FREEPLAY):Void
   {
     super();
 
+    // todo still
+    // make sure that ONLY plays mp3/ogg files
+    // if there's no mp3/ogg file, then it regenerates/reloads the random folder
+
+    var assetsInList = openfl.utils.Assets.list();
+
+    var soundFilterFunc = function(a:String) {
+      return a.startsWith('assets/shared/sounds/stickersounds/');
+    };
+
+    soundSelections = assetsInList.filter(soundFilterFunc);
+    soundSelections = soundSelections.map(function(a:String) {
+      return a.replace('assets/shared/sounds/stickersounds/', '').split('/')[0];
+    });
+
+    // cracked cleanup... yuchh...
+    for (i in soundSelections)
+    {
+      while (soundSelections.contains(i))
+      {
+        soundSelections.remove(i);
+      }
+      soundSelections.push(i);
+    }
+
+    trace(soundSelections);
+
+    soundSelection = FlxG.random.getObject(soundSelections);
+
+    var filterFunc = function(a:String) {
+      return a.startsWith('assets/shared/sounds/stickersounds/' + soundSelection + '/');
+    };
+    var assetsInList3 = openfl.utils.Assets.list();
+    sounds = assetsInList3.filter(filterFunc);
+    for (i in 0...sounds.length)
+    {
+      sounds[i] = sounds[i].replace('assets/shared/sounds/', '');
+      sounds[i] = sounds[i].substring(0, sounds[i].lastIndexOf('.'));
+    }
+
+    trace(sounds);
+
+    // trace(assetsInList);
+
     this.nextState = nextState;
 
     grpStickers = new FlxTypedGroup<StickerSprite>();
@@ -66,6 +119,8 @@ class StickerSubState extends MusicBeatSubState
     {
       new FlxTimer().start(sticker.timing, _ -> {
         sticker.visible = false;
+        var daSound:String = FlxG.random.getObject(sounds);
+        FlxG.sound.play(Paths.sound(daSound));
 
         if (ind == grpStickers.members.length - 1)
         {
@@ -152,6 +207,8 @@ class StickerSubState extends MusicBeatSubState
 
       new FlxTimer().start(sticker.timing, _ -> {
         sticker.visible = true;
+        var daSound:String = FlxG.random.getObject(sounds);
+        FlxG.sound.play(Paths.sound(daSound));
 
         var frameTimer:Int = FlxG.random.int(0, 2);
 

From 0327aeb45f55d92f2a8bdc45d81ed19d440c4d54 Mon Sep 17 00:00:00 2001
From: EliteMasterEric <ericmyllyoja@gmail.com>
Date: Wed, 11 Oct 2023 15:34:05 -0400
Subject: [PATCH 06/12] Replace assets/ commit reference with one that exists

---
 assets | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/assets b/assets
index 775a0c0a5..d2b3dcab9 160000
--- a/assets
+++ b/assets
@@ -1 +1 @@
-Subproject commit 775a0c0a5b2d3009fbec17560c7364a4ac61e3c7
+Subproject commit d2b3dcab92f5cb4b11774a80cbe2e270972a9577

From 652a1836a080dd5ec5fd49cfca1dbb103480ca9b Mon Sep 17 00:00:00 2001
From: Cameron Taylor <cameron.taylor.ninja@gmail.com>
Date: Wed, 11 Oct 2023 16:50:16 -0400
Subject: [PATCH 07/12] assets submodule update

---
 assets | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/assets b/assets
index d2b3dcab9..6e5ed4602 160000
--- a/assets
+++ b/assets
@@ -1 +1 @@
-Subproject commit d2b3dcab92f5cb4b11774a80cbe2e270972a9577
+Subproject commit 6e5ed46026a2eb1e575c5accf9192b90c13ff857

From f3737601f881bfb1406389e37ed22841ac66a222 Mon Sep 17 00:00:00 2001
From: EliteMasterEric <ericmyllyoja@gmail.com>
Date: Wed, 11 Oct 2023 18:54:58 -0400
Subject: [PATCH 08/12] Can't spam enter on pause menu or freeplay menu anymore

---
 source/funkin/FreeplayState.hx      | 23 ++++++++++++++++++-----
 source/funkin/PauseSubState.hx      | 12 +++++++++++-
 source/funkin/ui/StickerSubState.hx | 10 ++++++----
 3 files changed, 35 insertions(+), 10 deletions(-)

diff --git a/source/funkin/FreeplayState.hx b/source/funkin/FreeplayState.hx
index 3ae32c2e4..ccb89a7ca 100644
--- a/source/funkin/FreeplayState.hx
+++ b/source/funkin/FreeplayState.hx
@@ -570,7 +570,7 @@ class FreeplayState extends MusicBeatSubState
     var randomCapsule:SongMenuItem = grpCapsules.recycle(SongMenuItem);
     randomCapsule.init(FlxG.width, 0, "Random");
     randomCapsule.onConfirm = function() {
-      trace("RANDOM SELECTED");
+      capsuleOnConfirmRandom(randomCapsule);
     };
     randomCapsule.y = randomCapsule.intendedY(0) + 10;
     randomCapsule.targetPos.x = randomCapsule.x;
@@ -643,6 +643,8 @@ class FreeplayState extends MusicBeatSubState
   var spamTimer:Float = 0;
   var spamming:Bool = false;
 
+  var busy:Bool = false; // Set to true once the user has pressed enter to select a song.
+
   override function update(elapsed:Float)
   {
     super.update(elapsed);
@@ -690,11 +692,13 @@ class FreeplayState extends MusicBeatSubState
     fp.updateScore(Std.int(lerpScore));
 
     txtCompletion.text = Math.floor(lerpCompletion * 100) + "%";
-    // trace(Highscore.getCompletion(songs[curSelected].songName, curDifficulty));
 
-    // trace(intendedScore);
-    // trace(lerpScore);
-    // Highscore.getAllScores();
+    handleInputs(elapsed);
+  }
+
+  function handleInputs(elapsed:Float):Void
+  {
+    if (busy) return;
 
     var upP = controls.UI_UP_P;
     var downP = controls.UI_DOWN_P;
@@ -949,6 +953,13 @@ class FreeplayState extends MusicBeatSubState
     }
   }
 
+  function capsuleOnConfirmRandom(cap:SongMenuItem):Void
+  {
+    trace("RANDOM SELECTED");
+
+    busy = true;
+  }
+
   function capsuleOnConfirmDefault(cap:SongMenuItem):Void
   {
     // var poop:String = songs[curSelected].songName.toLowerCase();
@@ -963,6 +974,8 @@ class FreeplayState extends MusicBeatSubState
         curDifficulty = 1;
     }*/
 
+    busy = true;
+
     PlayStatePlaylist.isStoryMode = false;
 
     var songId:String = cap.songTitle.toLowerCase();
diff --git a/source/funkin/PauseSubState.hx b/source/funkin/PauseSubState.hx
index f93e5a450..54c3a530b 100644
--- a/source/funkin/PauseSubState.hx
+++ b/source/funkin/PauseSubState.hx
@@ -150,6 +150,11 @@ class PauseSubState extends MusicBeatSubState
 
     super.update(elapsed);
 
+    handleInputs();
+  }
+
+  function handleInputs():Void
+  {
     var upP = controls.UI_UP_P;
     var downP = controls.UI_DOWN_P;
     var accepted = controls.ACCEPT;
@@ -229,9 +234,14 @@ class PauseSubState extends MusicBeatSubState
             FlxTransitionableState.skipNextTransIn = true;
             FlxTransitionableState.skipNextTransOut = true;
 
-            if (PlayStatePlaylist.isStoryMode) openSubState(new funkin.ui.StickerSubState(null, STORY));
+            if (PlayStatePlaylist.isStoryMode)
+            {
+              openSubState(new funkin.ui.StickerSubState(null, STORY));
+            }
             else
+            {
               openSubState(new funkin.ui.StickerSubState(null, FREEPLAY));
+            }
 
           case 'Exit to Chart Editor':
             this.close();
diff --git a/source/funkin/ui/StickerSubState.hx b/source/funkin/ui/StickerSubState.hx
index bde36b160..ebf75cb34 100644
--- a/source/funkin/ui/StickerSubState.hx
+++ b/source/funkin/ui/StickerSubState.hx
@@ -206,6 +206,8 @@ class StickerSubState extends MusicBeatSubState
       sticker.timing = FlxMath.remapToRange(ind, 0, grpStickers.members.length, 0, 0.9);
 
       new FlxTimer().start(sticker.timing, _ -> {
+        if (grpStickers == null) return;
+
         sticker.visible = true;
         var daSound:String = FlxG.random.getObject(sounds);
         FlxG.sound.play(Paths.sound(daSound));
@@ -269,10 +271,10 @@ class StickerSubState extends MusicBeatSubState
   {
     super.update(elapsed);
 
-    if (FlxG.keys.justPressed.ANY)
-    {
-      regenStickers();
-    }
+    // if (FlxG.keys.justPressed.ANY)
+    // {
+    //   regenStickers();
+    // }
   }
 
   var switchingState:Bool = false;

From dd3d1116c09dff9d26dc6838e0507aafb16c0646 Mon Sep 17 00:00:00 2001
From: EliteMasterEric <ericmyllyoja@gmail.com>
Date: Thu, 12 Oct 2023 13:22:59 -0400
Subject: [PATCH 09/12] Add a flag to make Git hash display on launcher builds.

---
 Project.xml                     | 7 +++++++
 source/funkin/util/Constants.hx | 2 +-
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/Project.xml b/Project.xml
index ccf6c83a3..9d09d2457 100644
--- a/Project.xml
+++ b/Project.xml
@@ -196,6 +196,13 @@
 		<haxedef name="REDIRECT_ASSETS_FOLDER" />
 	</section>
 
+	<section>
+		<!-- TODO: Add a flag to Github Actions to turn this on or something. -->
+
+		<!-- Forces the version string to include the Git hash even on release builds (which are used for performance reasons). -->
+		<haxedef name="FORCE_DEBUG_VERSION" />
+	</section>
+
 	<!-- Run a script before and after building. -->
 	<postbuild haxe="source/Prebuild.hx"/> -->
 	<postbuild haxe="source/Postbuild.hx"/> -->
diff --git a/source/funkin/util/Constants.hx b/source/funkin/util/Constants.hx
index efabf10c3..ffaf7b9ec 100644
--- a/source/funkin/util/Constants.hx
+++ b/source/funkin/util/Constants.hx
@@ -39,7 +39,7 @@ class Constants
    */
   public static final VERSION_SUFFIX:String = ' PROTOTYPE';
 
-  #if debug
+  #if (debug || FORCE_DEBUG_VERSION)
   static function get_VERSION():String
   {
     return 'v${Application.current.meta.get('version')} (${GIT_BRANCH} : ${GIT_HASH})' + VERSION_SUFFIX;

From 9ec3f7aaacd9c7f05b09ab2c7bcf9f8e07dc22fe Mon Sep 17 00:00:00 2001
From: EliteMasterEric <ericmyllyoja@gmail.com>
Date: Thu, 12 Oct 2023 19:57:16 -0400
Subject: [PATCH 10/12] Fix to FORCE_DEBUG_VERSION flag

---
 source/funkin/util/Constants.hx | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/funkin/util/Constants.hx b/source/funkin/util/Constants.hx
index ffaf7b9ec..28f864d0e 100644
--- a/source/funkin/util/Constants.hx
+++ b/source/funkin/util/Constants.hx
@@ -71,7 +71,7 @@ class Constants
    */
   // ==============================
 
-  #if debug
+  #if (debug || FORCE_DEBUG_VERSION)
   /**
    * The current Git branch.
    */

From c1684b218e3051786e0f1eaa9acbb4d78a88c9f3 Mon Sep 17 00:00:00 2001
From: EliteMasterEric <ericmyllyoja@gmail.com>
Date: Thu, 12 Oct 2023 20:17:41 -0400
Subject: [PATCH 11/12] More fixes to github actions

---
 Project.xml                           | 10 +++++++++-
 source/funkin/util/macro/GitCommit.hx |  2 +-
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/Project.xml b/Project.xml
index 9d09d2457..69400d8b1 100644
--- a/Project.xml
+++ b/Project.xml
@@ -156,7 +156,6 @@
 	<haxedef name="HXCPP_CHECK_POINTER" />
 	<haxedef name="HXCPP_STACK_LINE" />
 	<haxedef name="HXCPP_STACK_TRACE" />
-	<haxedef name="openfl-enable-handle-error" />
 	<!-- This macro allows addition of new functionality to existing Flixel. -->
 	<haxeflag name="--macro" value="addMetadata('@:build(funkin.util.macro.FlxMacro.buildFlxBasic())', 'flixel.FlxBasic')" />
 	<!--Place custom nodes like icons here (higher priority to override the HaxeFlixel icon)-->
@@ -196,6 +195,15 @@
 		<haxedef name="REDIRECT_ASSETS_FOLDER" />
 	</section>
 
+
+	<section>
+		<!--
+			This flag enables the popup/crashlog error handler.
+			However, it also messes with breakpoints on some platforms.
+		-->
+		<haxedef name="openfl-enable-handle-error" />
+	</section>
+
 	<section>
 		<!-- TODO: Add a flag to Github Actions to turn this on or something. -->
 
diff --git a/source/funkin/util/macro/GitCommit.hx b/source/funkin/util/macro/GitCommit.hx
index 0449857cd..d0c034828 100644
--- a/source/funkin/util/macro/GitCommit.hx
+++ b/source/funkin/util/macro/GitCommit.hx
@@ -1,6 +1,6 @@
 package funkin.util.macro;
 
-#if debug
+#if (debug || FORCE_DEBUG_VERSION)
 class GitCommit
 {
   /**

From dd38dbbadfc5d04c7df7b1ff31cb31bbc0886c09 Mon Sep 17 00:00:00 2001
From: EliteMasterEric <ericmyllyoja@gmail.com>
Date: Sat, 14 Oct 2023 23:57:40 -0400
Subject: [PATCH 12/12] Tweaked the numbers on scrolling

---
 .../funkin/ui/debug/charting/ChartEditorState.hx   | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx
index 67e3c1dc8..5e4dded91 100644
--- a/source/funkin/ui/debug/charting/ChartEditorState.hx
+++ b/source/funkin/ui/debug/charting/ChartEditorState.hx
@@ -1920,33 +1920,33 @@ class ChartEditorState extends HaxeUIState
     // Mouse Wheel = Scroll
     if (FlxG.mouse.wheel != 0 && !FlxG.keys.pressed.CONTROL)
     {
-      scrollAmount = -10 * FlxG.mouse.wheel;
+      scrollAmount = -50 * FlxG.mouse.wheel;
       shouldPause = true;
     }
 
     // Up Arrow = Scroll Up
     if (upKeyHandler.activated && currentLiveInputStyle == None)
     {
-      scrollAmount = -GRID_SIZE * 0.25 * 5.0;
+      scrollAmount = -GRID_SIZE * 0.25 * 25.0;
       shouldPause = true;
     }
     // Down Arrow = Scroll Down
     if (downKeyHandler.activated && currentLiveInputStyle == None)
     {
-      scrollAmount = GRID_SIZE * 0.25 * 5.0;
+      scrollAmount = GRID_SIZE * 0.25 * 25.0;
       shouldPause = true;
     }
 
     // W = Scroll Up (doesn't work with Ctrl+Scroll)
     if (wKeyHandler.activated && currentLiveInputStyle == None && !FlxG.keys.pressed.CONTROL)
     {
-      scrollAmount = -GRID_SIZE * 0.25 * 5.0;
+      scrollAmount = -GRID_SIZE * 0.25 * 25.0;
       shouldPause = true;
     }
     // S = Scroll Down (doesn't work with Ctrl+Scroll)
     if (sKeyHandler.activated && currentLiveInputStyle == None && !FlxG.keys.pressed.CONTROL)
     {
-      scrollAmount = GRID_SIZE * 0.25 * 5.0;
+      scrollAmount = GRID_SIZE * 0.25 * 25.0;
       shouldPause = true;
     }
 
@@ -2011,7 +2011,7 @@ class ChartEditorState extends HaxeUIState
     // SHIFT + Scroll = Scroll Fast
     if (FlxG.keys.pressed.SHIFT)
     {
-      scrollAmount *= 5;
+      scrollAmount *= 2;
     }
     // CONTROL + Scroll = Scroll Precise
     if (FlxG.keys.pressed.CONTROL)
@@ -3287,7 +3287,7 @@ class ChartEditorState extends HaxeUIState
    */
   function handleTestKeybinds():Void
   {
-    if (!isHaxeUIDialogOpen && FlxG.keys.justPressed.ENTER)
+    if (!isHaxeUIDialogOpen && !isCursorOverHaxeUI && FlxG.keys.justPressed.ENTER)
     {
       var minimal = FlxG.keys.pressed.SHIFT;
       testSongInPlayState(minimal);