From 140afd3c31264e3c33e34bce1e252ff8f38da4cc Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Wed, 19 Jul 2023 01:30:36 -0400 Subject: [PATCH] Redo note positioning --- .../debug/charting/ChartEditorNoteSprite.hx | 7 +- .../ui/debug/charting/ChartEditorState.hx | 132 +++++++++++------- 2 files changed, 84 insertions(+), 55 deletions(-) diff --git a/source/funkin/ui/debug/charting/ChartEditorNoteSprite.hx b/source/funkin/ui/debug/charting/ChartEditorNoteSprite.hx index a20b43dbd..501c28c3c 100644 --- a/source/funkin/ui/debug/charting/ChartEditorNoteSprite.hx +++ b/source/funkin/ui/debug/charting/ChartEditorNoteSprite.hx @@ -175,8 +175,11 @@ class ChartEditorNoteSprite extends FlxSprite this.x = cursorColumn * ChartEditorState.GRID_SIZE; // Notes far in the song will start far down, but the group they belong to will have a high negative offset. - // TODO: stepTime doesn't account for fluctuating BPMs. - if (this.noteData.stepTime >= 0) this.y = this.noteData.stepTime * ChartEditorState.GRID_SIZE; + if (this.noteData.stepTime >= 0) + { + // noteData.stepTime is a calculated value which accounts for BPM changes + this.y = this.noteData.stepTime * ChartEditorState.GRID_SIZE; + } if (origin != null) { diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index d89af7d6e..4a04c0bfb 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -1,5 +1,6 @@ package funkin.ui.debug.charting; +import funkin.util.SortUtil; import funkin.ui.debug.charting.ChartEditorCommand; import flixel.input.keyboard.FlxKey; import funkin.input.TurboKeyHandler; @@ -172,29 +173,35 @@ class ChartEditorState extends HaxeUIState /** * scrollPosition, converted to steps. - * TODO: Handle BPM changes. + * NOT dependant on BPM, because the size of a grid square does not change with BPM. */ - var scrollPositionInSteps(get, null):Float; + var scrollPositionInSteps(get, set):Float; function get_scrollPositionInSteps():Float { return scrollPositionInPixels / GRID_SIZE; } + function set_scrollPositionInSteps(value:Float):Float + { + scrollPositionInPixels = value * GRID_SIZE; + return value; + } + /** * scrollPosition, converted to milliseconds. - * TODO: Handle BPM changes. + * DEPENDANT on BPM, because the duration of a grid square changes with BPM. */ var scrollPositionInMs(get, set):Float; function get_scrollPositionInMs():Float { - return scrollPositionInSteps * Conductor.stepLengthMs; + return Conductor.getStepTimeInMs(scrollPositionInSteps); } function set_scrollPositionInMs(value:Float):Float { - scrollPositionInPixels = value / Conductor.stepLengthMs; + scrollPositionInSteps = Conductor.getTimeInSteps(value); return value; } @@ -206,11 +213,26 @@ class ChartEditorState extends HaxeUIState */ var playheadPositionInPixels(default, set):Float; - var playheadPositionInSteps(get, null):Float; + function set_playheadPositionInPixels(value:Float):Float + { + // Make sure playhead doesn't go outside the song. + if (value + scrollPositionInPixels < 0) value = -scrollPositionInPixels; + if (value + scrollPositionInPixels > songLengthInPixels) value = songLengthInPixels - scrollPositionInPixels; + + this.playheadPositionInPixels = value; + + // Move the playhead sprite to the correct position. + gridPlayhead.y = this.playheadPositionInPixels + (MENU_BAR_HEIGHT + GRID_TOP_PAD); + + return this.playheadPositionInPixels; + } /** * playheadPosition, converted to steps. + * NOT dependant on BPM, because the size of a grid square does not change with BPM. */ + var playheadPositionInSteps(get, null):Float; + function get_playheadPositionInSteps():Float { return playheadPositionInPixels / GRID_SIZE; @@ -218,60 +240,76 @@ class ChartEditorState extends HaxeUIState /** * playheadPosition, converted to milliseconds. + * DEPENDANT on BPM, because the duration of a grid square changes with BPM. */ var playheadPositionInMs(get, null):Float; function get_playheadPositionInMs():Float { - return playheadPositionInSteps * Conductor.stepLengthMs; + return Conductor.getStepTimeInMs(playheadPositionInSteps); } /** - * This is the song's length in PIXELS, same format as scrollPosition. + * songLength, in milliseconds. */ - var songLengthInPixels(get, default):Int; + @:isVar var songLengthInMs(get, set):Float; - function get_songLengthInPixels():Int + function get_songLengthInMs():Float { - if (songLengthInPixels <= 0) return 1000; + if (songLengthInMs <= 0) return 1000; + return songLengthInMs; + } - return songLengthInPixels; + function set_songLengthInMs(value:Float):Float + { + this.songLengthInMs = value; + + // Make sure playhead doesn't go outside the song. + if (playheadPositionInMs > songLengthInMs) playheadPositionInMs = songLengthInMs; + + return this.songLengthInMs; } /** * songLength, converted to steps. - * TODO: Handle BPM changes. + * Dependant on BPM, because the size of a grid square does not change with BPM but the length of a beat does. */ var songLengthInSteps(get, set):Float; function get_songLengthInSteps():Float { - return songLengthInPixels / GRID_SIZE; + return Conductor.getTimeInSteps(songLengthInMs); } function set_songLengthInSteps(value:Float):Float { - songLengthInPixels = Std.int(value * GRID_SIZE); + // Getting a reasonable result from setting songLengthInSteps requires that Conductor.mapBPMChanges be called first. + songLengthInMs = Conductor.getStepTimeInMs(value); return value; } /** - * songLength, converted to milliseconds. - * TODO: Handle BPM changes. + * This is the song's length in PIXELS, same format as scrollPosition. + * Dependant on BPM, because the size of a grid square does not change with BPM but the length of a beat does. */ - var songLengthInMs(get, set):Float; + var songLengthInPixels(get, set):Int; - function get_songLengthInMs():Float + function get_songLengthInPixels():Int { - return songLengthInSteps * Conductor.stepLengthMs; + return Std.int(songLengthInSteps * GRID_SIZE); } - function set_songLengthInMs(value:Float):Float + function set_songLengthInPixels(value:Int):Int { - songLengthInSteps = Conductor.getTimeInSteps(audioInstTrack.length); + songLengthInSteps = value / GRID_SIZE; return value; } + /** + * The current theme used by the editor. + * Dictates the appearance of many UI elements. + * Currently hardcoded to just Light and Dark. + */ var currentTheme(default, set):ChartEditorTheme = null; function set_currentTheme(value:ChartEditorTheme):ChartEditorTheme @@ -1667,7 +1705,8 @@ class ChartEditorState extends HaxeUIState // The song position of the cursor, in steps. var cursorFractionalStep:Float = cursorY / GRID_SIZE / (16 / noteSnapQuant); var cursorStep:Int = Std.int(Math.floor(cursorFractionalStep)); - var cursorMs:Float = cursorStep * Conductor.stepLengthMs * (16 / noteSnapQuant); + var cursorMs:Float = Conductor.getStepTimeInMs(cursorStep); + trace('${cursorFractionalStep} ${cursorStep} ${cursorMs}'); // The direction value for the column at the cursor. var cursorColumn:Int = Math.floor(cursorX / GRID_SIZE); if (cursorColumn < 0) cursorColumn = 0; @@ -1705,7 +1744,7 @@ class ChartEditorState extends HaxeUIState // We released the mouse. Select the notes in the box. var cursorFractionalStepStart:Float = cursorYStart / GRID_SIZE; var cursorStepStart:Int = Math.floor(cursorFractionalStepStart); - var cursorMsStart:Float = cursorStepStart * Conductor.stepLengthMs; + var cursorMsStart:Float = Conductor.getStepTimeInMs(cursorStepStart); var cursorColumnBase:Int = Math.floor(cursorX / GRID_SIZE); var cursorColumnBaseStart:Int = Math.floor(cursorXStart / GRID_SIZE); @@ -1877,12 +1916,13 @@ class ChartEditorState extends HaxeUIState { // Handle extending the note as you drag. - // Since use Math.floor and stepLengthMs here, the hold notes will be beat snapped. - var dragLengthSteps:Float = Math.floor((cursorMs - currentPlaceNoteData.time) / Conductor.stepLengthMs); + // TODO: This should be beat snapped? + var dragLengthSteps:Float = Conductor.getTimeInSteps(cursorMs) - currentPlaceNoteData.stepTime; // Without this, the newly placed note feels too short compared to the user's input. var INCREMENT:Float = 1.0; - var dragLengthMs:Float = (dragLengthSteps + INCREMENT) * Conductor.stepLengthMs; + // TODO: Make this not busted with BPM changes + var dragLengthMs:Float = Math.floor(dragLengthSteps + INCREMENT) * Conductor.stepLengthMs; // TODO: Add and update some sort of preview? @@ -2187,7 +2227,7 @@ class ChartEditorState extends HaxeUIState } // Get the position the note should be at. - var noteTimePixels:Float = noteData.time / Conductor.stepLengthMs * GRID_SIZE; + var noteTimePixels:Float = noteData.stepTime * GRID_SIZE; // Make sure the note appears when scrolling up. var modifiedViewAreaTop = viewAreaTop - GRID_SIZE; @@ -2209,11 +2249,11 @@ class ChartEditorState extends HaxeUIState noteSprite.x += renderedNotes.x; noteSprite.y += renderedNotes.y; + // TODO: Replace this with SustainTrail. if (noteSprite.noteData.length > 0) { // If the note is a hold, we need to make sure it's long enough. - var noteLengthMs:Float = noteSprite.noteData.length; - var noteLengthSteps:Float = (noteLengthMs / Conductor.stepLengthMs); + var noteLengthSteps:Float = noteSprite.noteData.stepLength; var lastNoteSprite:ChartEditorNoteSprite = noteSprite; while (noteLengthSteps > 0) @@ -2252,7 +2292,7 @@ class ChartEditorState extends HaxeUIState } // Get the position the event should be at. - var eventTimePixels:Float = eventData.time / Conductor.stepLengthMs * GRID_SIZE; + var eventTimePixels:Float = eventData.stepTime * GRID_SIZE; // Make sure the event appears when scrolling up. var modifiedViewAreaTop = viewAreaTop - GRID_SIZE; @@ -2264,7 +2304,7 @@ class ChartEditorState extends HaxeUIState // Get an event sprite from the pool. // If we can reuse a deleted event, do so. // If a new event is needed, call buildEventSprite. - var eventSprite:ChartEditorEventSprite = renderedEvents.recycle(() -> new ChartEditorEventSprite(this)); + var eventSprite:ChartEditorEventSprite = renderedEvents.recycle(() -> new ChartEditorEventSprite(this), false, true); eventSprite.parentState = this; // The event sprite handles animation playback and positioning. @@ -2655,7 +2695,7 @@ class ChartEditorState extends HaxeUIState */ function handleNotePreview():Void { - // + // TODO: Finish this. if (notePreviewDirty) { notePreviewDirty = false; @@ -2924,25 +2964,6 @@ class ChartEditorState extends HaxeUIState return this.scrollPositionInPixels; } - function get_playheadPositionInPixels():Float - { - return this.playheadPositionInPixels; - } - - function set_playheadPositionInPixels(value:Float):Float - { - // Make sure playhead doesn't go outside the song. - if (value + scrollPositionInPixels < 0) value = -scrollPositionInPixels; - if (value + scrollPositionInPixels > songLengthInPixels) value = songLengthInPixels - scrollPositionInPixels; - - this.playheadPositionInPixels = value; - - // Move the playhead sprite to the correct position. - gridPlayhead.y = this.playheadPositionInPixels + (MENU_BAR_HEIGHT + GRID_TOP_PAD); - - return this.playheadPositionInPixels; - } - /** * Loads an instrumental from an absolute file path, replacing the current instrumental. * @@ -3102,7 +3123,12 @@ class ChartEditorState extends HaxeUIState Conductor.mapTimeChanges(currentSongMetadata.timeChanges); loadInstrumentalFromAsset(Paths.inst(songId)); - loadVocalsFromAsset(Paths.voices(songId)); + + var voiceList:Array = song.getDifficulty(selectedDifficulty).buildVoiceList(); + for (voicePath in voiceList) + { + loadVocalsFromAsset(voicePath); + } NotificationManager.instance.addNotification( {