From 7d7cf32f447aab78621e874d9facf0c15ee3f0ce Mon Sep 17 00:00:00 2001
From: Jenny Crowe <jakobwcrowe@berkeley.edu>
Date: Mon, 19 Feb 2024 18:18:32 -0700
Subject: [PATCH] Chart editor grid now updates when changing the song BPM.
 HItsound volumes converted to two separate sliders.

---
 source/funkin/save/Save.hx                    | 71 +++++----------
 .../ui/debug/charting/ChartEditorState.hx     | 88 +++++++++----------
 .../commands/ChangeStartingBPMCommand.hx      |  4 +
 .../components/ChartEditorMeasureTicks.hx     |  8 +-
 4 files changed, 76 insertions(+), 95 deletions(-)

diff --git a/source/funkin/save/Save.hx b/source/funkin/save/Save.hx
index 5bbded231..ff5e5bfb8 100644
--- a/source/funkin/save/Save.hx
+++ b/source/funkin/save/Save.hx
@@ -106,8 +106,8 @@ abstract Save(RawSaveData)
             playtestStartTime: false,
             downscroll: false,
             metronomeVolume: 1.0,
-            hitsoundsEnabledPlayer: true,
-            hitsoundsEnabledOpponent: true,
+            hitsoundVolumePlayer: 1.0,
+            hitsoundVolumeOpponent: 1.0,
             themeMusic: true
           },
       };
@@ -300,55 +300,38 @@ abstract Save(RawSaveData)
     return this.optionsChartEditor.metronomeVolume;
   }
 
-  public var chartEditorHitsoundVolume(get, set):Float;
+  public var chartEditorHitsoundVolumePlayer(get, set):Float;
 
-  function get_chartEditorHitsoundVolume():Float
+  function get_chartEditorHitsoundVolumePlayer():Float
   {
-    if (this.optionsChartEditor.hitsoundVolume == null) this.optionsChartEditor.hitsoundVolume = 1.0;
+    if (this.optionsChartEditor.hitsoundVolumePlayer == null) this.optionsChartEditor.hitsoundVolumePlayer = 1.0;
 
-    return this.optionsChartEditor.hitsoundVolume;
+    return this.optionsChartEditor.hitsoundVolumePlayer;
   }
 
-  function set_chartEditorHitsoundVolume(value:Float):Float
+  function set_chartEditorHitsoundVolumePlayer(value:Float):Float
   {
     // Set and apply.
-    this.optionsChartEditor.hitsoundVolume = value;
+    this.optionsChartEditor.hitsoundVolumePlayer = value;
     flush();
-    return this.optionsChartEditor.hitsoundVolume;
+    return this.optionsChartEditor.hitsoundVolumePlayer;
   }
 
-  public var chartEditorHitsoundsEnabledPlayer(get, set):Bool;
+  public var chartEditorHitsoundVolumeOpponent(get, set):Float;
 
-  function get_chartEditorHitsoundsEnabledPlayer():Bool
+  function get_chartEditorHitsoundVolumeOpponent():Float
   {
-    if (this.optionsChartEditor.hitsoundsEnabledPlayer == null) this.optionsChartEditor.hitsoundsEnabledPlayer = true;
+    if (this.optionsChartEditor.hitsoundVolumeOpponent == null) this.optionsChartEditor.hitsoundVolumeOpponent = 1.0;
 
-    return this.optionsChartEditor.hitsoundsEnabledPlayer;
+    return this.optionsChartEditor.hitsoundVolumeOpponent;
   }
 
-  function set_chartEditorHitsoundsEnabledPlayer(value:Bool):Bool
+  function set_chartEditorHitsoundVolumeOpponent(value:Float):Float
   {
     // Set and apply.
-    this.optionsChartEditor.hitsoundsEnabledPlayer = value;
+    this.optionsChartEditor.hitsoundVolumeOpponent = value;
     flush();
-    return this.optionsChartEditor.hitsoundsEnabledPlayer;
-  }
-
-  public var chartEditorHitsoundsEnabledOpponent(get, set):Bool;
-
-  function get_chartEditorHitsoundsEnabledOpponent():Bool
-  {
-    if (this.optionsChartEditor.hitsoundsEnabledOpponent == null) this.optionsChartEditor.hitsoundsEnabledOpponent = true;
-
-    return this.optionsChartEditor.hitsoundsEnabledOpponent;
-  }
-
-  function set_chartEditorHitsoundsEnabledOpponent(value:Bool):Bool
-  {
-    // Set and apply.
-    this.optionsChartEditor.hitsoundsEnabledOpponent = value;
-    flush();
-    return this.optionsChartEditor.hitsoundsEnabledOpponent;
+    return this.optionsChartEditor.hitsoundVolumeOpponent;
   }
 
   public var chartEditorThemeMusic(get, set):Bool;
@@ -990,10 +973,16 @@ typedef SaveDataChartEditorOptions =
   var ?metronomeVolume:Float;
 
   /**
-   * Hitsound volume in the Chart Editor.
+   * Hitsound volume (player) in the Chart Editor.
    * @default `1.0`
    */
-  var ?hitsoundVolume:Float;
+  var ?hitsoundVolumePlayer:Float;
+
+  /**
+   * Hitsound volume (opponent) in the Chart Editor.
+   * @default `1.0`
+   */
+  var ?hitsoundVolumeOpponent:Float;
 
   /**
    * If true, playtest songs from the current position in the Chart Editor.
@@ -1001,18 +990,6 @@ typedef SaveDataChartEditorOptions =
    */
   var ?playtestStartTime:Bool;
 
-  /**
-   * Player note hit sounds in the Chart Editor.
-   * @default `true`
-   */
-  var ?hitsoundsEnabledPlayer:Bool;
-
-  /**
-   * Opponent note hit sounds in the Chart Editor.
-   * @default `true`
-   */
-  var ?hitsoundsEnabledOpponent:Bool;
-
   /**
    * Theme music in the Chart Editor.
    * @default `true`
diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx
index 48a6e70c9..a00c1681b 100644
--- a/source/funkin/ui/debug/charting/ChartEditorState.hx
+++ b/source/funkin/ui/debug/charting/ChartEditorState.hx
@@ -310,10 +310,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
   {
     this.songLengthInMs = value;
 
-    // Make sure playhead doesn't go outside the song.
-    if (playheadPositionInMs > songLengthInMs) playheadPositionInMs = songLengthInMs;
-
-    onSongLengthChanged();
+    updateGridHeight();
 
     return this.songLengthInMs;
   }
@@ -704,19 +701,14 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
   var metronomeVolume:Float = 1.0;
 
   /**
-   * The volume to play hitsounds at.
+   * The volume to play the player's hitsounds at.
    */
-  var hitsoundVolume:Float = 1.0;
+  var hitsoundVolumePlayer:Float = 1.0;
 
   /**
-   * Whether hitsounds are enabled for the player.
+   * The volume to play the opponent's hitsounds at.
    */
-  var hitsoundsEnabledPlayer:Bool = true;
-
-  /**
-   * Whether hitsounds are enabled for the opponent.
-   */
-  var hitsoundsEnabledOpponent:Bool = true;
+  var hitsoundVolumeOpponent:Float = 1.0;
 
   /**
    * Whether hitsounds are enabled for at least one character.
@@ -725,7 +717,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
 
   function get_hitsoundsEnabled():Bool
   {
-    return hitsoundsEnabledPlayer || hitsoundsEnabledOpponent;
+    return hitsoundVolumePlayer + hitsoundVolumeOpponent > 0;
   }
 
   // Auto-save
@@ -1757,30 +1749,30 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
    */
   var menubarItemVolumeMetronome:Slider;
 
-  /**
-   * The `Audio -> Enable Player Hitsounds` menu checkbox.
-   */
-  var menubarItemPlayerHitsounds:MenuCheckBox;
-
-  /**
-   * The `Audio -> Enable Opponent Hitsounds` menu checkbox.
-   */
-  var menubarItemOpponentHitsounds:MenuCheckBox;
-
   /**
    * The `Audio -> Play Theme Music` menu checkbox.
    */
   var menubarItemThemeMusic:MenuCheckBox;
 
   /**
-   * The `Audio -> Hitsound Volume` label.
+   * The `Audio -> Player Hitsound Volume` label.
    */
-  var menubarLabelVolumeHitsounds:Label;
+  var menubarLabelVolumeHitsoundPlayer:Label;
 
   /**
-   * The `Audio -> Hitsound Volume` slider.
+   * The `Audio -> Enemy Hitsound Volume` label.
    */
-  var menubarItemVolumeHitsounds:Slider;
+  var menubarLabelVolumeHitsoundOpponent:Label;
+
+  /**
+   * The `Audio -> Player Hitsound Volume` slider.
+   */
+  var menubarItemVolumeHitsoundPlayer:Slider;
+
+  /**
+   * The `Audio -> Enemy Hitsound Volume` slider.
+   */
+  var menubarItemVolumeHitsoundOpponent:Slider;
 
   /**
    * The `Audio -> Instrumental Volume` label.
@@ -2187,9 +2179,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
     playtestStartTime = save.chartEditorPlaytestStartTime;
     currentTheme = save.chartEditorTheme;
     metronomeVolume = save.chartEditorMetronomeVolume;
-    hitsoundVolume = save.chartEditorHitsoundVolume;
-    hitsoundsEnabledPlayer = save.chartEditorHitsoundsEnabledPlayer;
-    hitsoundsEnabledOpponent = save.chartEditorHitsoundsEnabledOpponent;
+    hitsoundVolumePlayer = save.chartEditorHitsoundVolumePlayer;
+    hitsoundVolumePlayer = save.chartEditorHitsoundVolumeOpponent;
     this.welcomeMusic.active = save.chartEditorThemeMusic;
 
     // audioInstTrack.volume = save.chartEditorInstVolume;
@@ -2217,9 +2208,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
     save.chartEditorPlaytestStartTime = playtestStartTime;
     save.chartEditorTheme = currentTheme;
     save.chartEditorMetronomeVolume = metronomeVolume;
-    save.chartEditorHitsoundVolume = hitsoundVolume;
-    save.chartEditorHitsoundsEnabledPlayer = hitsoundsEnabledPlayer;
-    save.chartEditorHitsoundsEnabledOpponent = hitsoundsEnabledOpponent;
+    save.chartEditorHitsoundVolumePlayer = hitsoundVolumePlayer;
+    save.chartEditorHitsoundVolumeOpponent = hitsoundVolumeOpponent;
     save.chartEditorThemeMusic = this.welcomeMusic.active;
 
     // save.chartEditorInstVolume = audioInstTrack.volume;
@@ -2912,24 +2902,25 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
     };
     menubarItemVolumeMetronome.value = Std.int(metronomeVolume * 100);
 
-    menubarItemPlayerHitsounds.onChange = event -> hitsoundsEnabledPlayer = event.value;
-    menubarItemPlayerHitsounds.selected = hitsoundsEnabledPlayer;
-
-    menubarItemOpponentHitsounds.onChange = event -> hitsoundsEnabledOpponent = event.value;
-    menubarItemOpponentHitsounds.selected = hitsoundsEnabledOpponent;
-
     menubarItemThemeMusic.onChange = event -> {
       this.welcomeMusic.active = event.value;
       fadeInWelcomeMusic(WELCOME_MUSIC_FADE_IN_DELAY, WELCOME_MUSIC_FADE_IN_DURATION);
     };
     menubarItemThemeMusic.selected = this.welcomeMusic.active;
 
-    menubarItemVolumeHitsound.onChange = event -> {
+    menubarItemVolumeHitsoundPlayer.onChange = event -> {
       var volume:Float = event.value.toFloat() / 100.0;
-      hitsoundVolume = volume;
-      menubarLabelVolumeHitsound.text = 'Hitsound - ${Std.int(event.value)}%';
+      hitsoundVolumePlayer = volume;
+      menubarLabelVolumeHitsoundPlayer.text = 'Player - ${Std.int(event.value)}%';
     };
-    menubarItemVolumeHitsound.value = Std.int(hitsoundVolume * 100);
+    menubarItemVolumeHitsoundPlayer.value = Std.int(hitsoundVolumePlayer * 100);
+
+    menubarItemVolumeHitsoundOpponent.onChange = event -> {
+      var volume:Float = event.value.toFloat() / 100.0;
+      hitsoundVolumeOpponent = volume;
+      menubarLabelVolumeHitsoundOpponent.text = 'Enemy - ${Std.int(event.value)}%';
+    };
+    menubarItemVolumeHitsoundOpponent.value = Std.int(hitsoundVolumeOpponent * 100);
 
     menubarItemVolumeInstrumental.onChange = event -> {
       var volume:Float = event.value.toFloat() / 100.0;
@@ -5511,8 +5502,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
     this.switchToInstrumental(currentInstrumentalId, currentSongMetadata.playData.characters.player, currentSongMetadata.playData.characters.opponent);
   }
 
-  function onSongLengthChanged():Void
+  public function updateGridHeight():Void
   {
+    // Make sure playhead doesn't go outside the song after we update the grid height.
+    if (playheadPositionInMs > songLengthInMs) playheadPositionInMs = songLengthInMs;
+
     if (gridTiledSprite != null)
     {
       gridTiledSprite.height = songLengthInPixels;
@@ -5939,9 +5933,9 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
       switch (noteData.getStrumlineIndex())
       {
         case 0: // Player
-          if (hitsoundsEnabledPlayer) this.playSound(Paths.sound('chartingSounds/hitNotePlayer'), hitsoundVolume);
+          if (hitsoundVolumePlayer > 0) this.playSound(Paths.sound('chartingSounds/hitNotePlayer'), hitsoundVolumePlayer);
         case 1: // Opponent
-          if (hitsoundsEnabledOpponent) this.playSound(Paths.sound('chartingSounds/hitNoteOpponent'), hitsoundVolume);
+          if (hitsoundVolumeOpponent > 0) this.playSound(Paths.sound('chartingSounds/hitNoteOpponent'), hitsoundVolumeOpponent);
       }
     }
   }
diff --git a/source/funkin/ui/debug/charting/commands/ChangeStartingBPMCommand.hx b/source/funkin/ui/debug/charting/commands/ChangeStartingBPMCommand.hx
index bd832fab3..a3728f337 100644
--- a/source/funkin/ui/debug/charting/commands/ChangeStartingBPMCommand.hx
+++ b/source/funkin/ui/debug/charting/commands/ChangeStartingBPMCommand.hx
@@ -40,6 +40,8 @@ class ChangeStartingBPMCommand implements ChartEditorCommand
     state.scrollPositionInPixels = 0;
 
     Conductor.instance.mapTimeChanges(state.currentSongMetadata.timeChanges);
+
+    state.updateGridHeight();
   }
 
   public function undo(state:ChartEditorState):Void
@@ -62,6 +64,8 @@ class ChangeStartingBPMCommand implements ChartEditorCommand
     state.scrollPositionInPixels = 0;
 
     Conductor.instance.mapTimeChanges(state.currentSongMetadata.timeChanges);
+
+    state.updateGridHeight();
   }
 
   public function shouldAddToHistory(state:ChartEditorState):Bool
diff --git a/source/funkin/ui/debug/charting/components/ChartEditorMeasureTicks.hx b/source/funkin/ui/debug/charting/components/ChartEditorMeasureTicks.hx
index 1a76d1e22..2d8b62cbd 100644
--- a/source/funkin/ui/debug/charting/components/ChartEditorMeasureTicks.hx
+++ b/source/funkin/ui/debug/charting/components/ChartEditorMeasureTicks.hx
@@ -58,9 +58,15 @@ class ChartEditorMeasureTicks extends FlxTypedSpriteGroup<FlxSprite>
     var measureNumberInViewport = Math.floor(viewTopPosition / ChartEditorState.GRID_SIZE / Conductor.instance.stepsPerMeasure) + 1;
     var measureNumberPosition = measureNumberInViewport * ChartEditorState.GRID_SIZE * Conductor.instance.stepsPerMeasure;
 
-    measureNumber.text = '${measureNumberInViewport + 1}';
     measureNumber.y = measureNumberPosition + this.y;
 
+    // Show the measure number only if it isn't beneath the end of the note grid.
+    // Using measureNumber + 1 because the cut-off bar at the bottom is technically a bar, but it looks bad if a measure number shows up there.
+    if ((measureNumberInViewport + 1) < chartEditorState.songLengthInSteps / Conductor.instance.stepsPerMeasure)
+      measureNumber.text = '${measureNumberInViewport + 1}';
+    else
+      measureNumber.text = '';
+
     // trace(measureNumber.text + ' at ' + measureNumber.y);
   }