diff --git a/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx b/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx index 3d1819403..03b383544 100644 --- a/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx +++ b/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx @@ -291,6 +291,7 @@ class ChartEditorDialogHandler if (state.loadInstrumentalFromBytes(selectedFile.bytes)) { trace('Selected file: ' + selectedFile.fullPath); + #if !mac NotificationManager.instance.addNotification( { title: 'Success', @@ -298,6 +299,7 @@ class ChartEditorDialogHandler type: NotificationType.Success, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end dialog.hideDialog(DialogButton.APPLY); removeDropHandler(onDropFile); @@ -306,6 +308,7 @@ class ChartEditorDialogHandler { trace('Failed to load instrumental (${selectedFile.fullPath})'); + #if !mac NotificationManager.instance.addNotification( { title: 'Failure', @@ -313,6 +316,7 @@ class ChartEditorDialogHandler type: NotificationType.Error, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end } } }); @@ -324,6 +328,7 @@ class ChartEditorDialogHandler if (state.loadInstrumentalFromPath(path)) { // Tell the user the load was successful. + #if !mac NotificationManager.instance.addNotification( { title: 'Success', @@ -331,6 +336,7 @@ class ChartEditorDialogHandler type: NotificationType.Success, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end dialog.hideDialog(DialogButton.APPLY); removeDropHandler(onDropFile); @@ -347,6 +353,7 @@ class ChartEditorDialogHandler } // Tell the user the load was successful. + #if !mac NotificationManager.instance.addNotification( { title: 'Failure', @@ -354,6 +361,7 @@ class ChartEditorDialogHandler type: NotificationType.Error, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end } }; @@ -623,6 +631,7 @@ class ChartEditorDialogHandler if (state.loadVocalsFromPath(path, charKey)) { // Tell the user the load was successful. + #if !mac NotificationManager.instance.addNotification( { title: 'Success', @@ -630,6 +639,7 @@ class ChartEditorDialogHandler type: NotificationType.Success, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end vocalsEntryLabel.text = 'Vocals for $charName (drag and drop, or click to browse)\nSelected file: ${path.file}.${path.ext}'; dialogNoVocals.hidden = true; removeDropHandler(onDropFile); @@ -646,6 +656,7 @@ class ChartEditorDialogHandler } // Vocals failed to load. + #if !mac NotificationManager.instance.addNotification( { title: 'Failure', @@ -653,6 +664,7 @@ class ChartEditorDialogHandler type: NotificationType.Error, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end vocalsEntryLabel.text = 'Drag and drop vocals for $charName here, or click to browse.'; } @@ -785,6 +797,7 @@ class ChartEditorDialogHandler if (songMetadataVariation == null) { // Tell the user the load was not successful. + #if !mac NotificationManager.instance.addNotification( { title: 'Failure', @@ -792,12 +805,14 @@ class ChartEditorDialogHandler type: NotificationType.Error, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end return; } songMetadata.set(variation, songMetadataVariation); // Tell the user the load was successful. + #if !mac NotificationManager.instance.addNotification( { title: 'Success', @@ -805,6 +820,7 @@ class ChartEditorDialogHandler type: NotificationType.Success, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end label.text = 'Metadata file (drag and drop, or click to browse)\nSelected file: ${path.file}.${path.ext}'; @@ -826,6 +842,7 @@ class ChartEditorDialogHandler songMetadata.set(variation, songMetadataVariation); // Tell the user the load was successful. + #if !mac NotificationManager.instance.addNotification( { title: 'Success', @@ -833,6 +850,7 @@ class ChartEditorDialogHandler type: NotificationType.Success, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end label.text = 'Metadata file (drag and drop, or click to browse)\nSelected file: ${selectedFile.name}'; @@ -855,6 +873,7 @@ class ChartEditorDialogHandler state.noteDisplayDirty = true; // Tell the user the load was successful. + #if !mac NotificationManager.instance.addNotification( { title: 'Success', @@ -862,6 +881,7 @@ class ChartEditorDialogHandler type: NotificationType.Success, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end label.text = 'Chart data file (drag and drop, or click to browse)\nSelected file: ${path.file}.${path.ext}'; }; @@ -883,6 +903,7 @@ class ChartEditorDialogHandler state.noteDisplayDirty = true; // Tell the user the load was successful. + #if !mac NotificationManager.instance.addNotification( { title: 'Success', @@ -890,6 +911,7 @@ class ChartEditorDialogHandler type: NotificationType.Success, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end label.text = 'Chart data file (drag and drop, or click to browse)\nSelected file: ${selectedFile.name}'; } @@ -972,6 +994,7 @@ class ChartEditorDialogHandler state.loadSong([Constants.DEFAULT_VARIATION => songMetadata], [Constants.DEFAULT_VARIATION => songChartData]); dialog.hideDialog(DialogButton.APPLY); + #if !mac NotificationManager.instance.addNotification( { title: 'Success', @@ -979,6 +1002,7 @@ class ChartEditorDialogHandler type: NotificationType.Success, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end } }); } @@ -992,6 +1016,7 @@ class ChartEditorDialogHandler state.loadSong([Constants.DEFAULT_VARIATION => songMetadata], [Constants.DEFAULT_VARIATION => songChartData]); dialog.hideDialog(DialogButton.APPLY); + #if !mac NotificationManager.instance.addNotification( { title: 'Success', @@ -999,6 +1024,7 @@ class ChartEditorDialogHandler type: NotificationType.Success, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end }; addDropHandler(importBox, onDropFile); diff --git a/source/funkin/ui/debug/charting/ChartEditorNoteSprite.hx b/source/funkin/ui/debug/charting/ChartEditorNoteSprite.hx index 1c440f6ed..ca480291a 100644 --- a/source/funkin/ui/debug/charting/ChartEditorNoteSprite.hx +++ b/source/funkin/ui/debug/charting/ChartEditorNoteSprite.hx @@ -130,7 +130,7 @@ class ChartEditorNoteSprite extends FlxSprite return this.noteData; } - public function updateNotePosition(?origin:FlxObject) + public function updateNotePosition(?origin:FlxObject):Void { if (this.noteData == null) return; @@ -160,9 +160,7 @@ class ChartEditorNoteSprite extends FlxSprite if (this.noteData.stepTime >= 0) { // noteData.stepTime is a calculated value which accounts for BPM changes - var stepTime:Float = this.noteData.stepTime; - var roundedStepTime:Float = Math.floor(stepTime + 0.01); // Add epsilon to fix rounding issues - this.y = roundedStepTime * ChartEditorState.GRID_SIZE; + 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 1221f3720..75a9693fe 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -179,14 +179,24 @@ class ChartEditorState extends HaxeUIState */ static final SNAP_QUANTS:Array = [4, 8, 12, 16, 20, 24, 32, 48, 64, 96, 192]; + static final BASE_QUANT:Int = 16; + /** * INSTANCE DATA */ // ============================== public var currentZoomLevel:Float = 1.0; - var noteSnapQuantIndex:Int = 3; + /** + * The internal index of what note snapping value is in use. + * Increment to make placement more preceise and decrement to make placement less precise. + */ + var noteSnapQuantIndex:Int = 3; // default is 16 + /** + * The current note snapping value. + * For example, `32` when snapping to 32nd notes. + */ public var noteSnapQuant(get, never):Int; function get_noteSnapQuant():Int @@ -194,6 +204,17 @@ class ChartEditorState extends HaxeUIState return SNAP_QUANTS[noteSnapQuantIndex]; } + /** + * The ratio of the current note snapping value to the default. + * For example, `32` becomes `0.5` when snapping to 16th notes. + */ + public var noteSnapRatio(get, never):Float; + + function get_noteSnapRatio():Float + { + return BASE_QUANT / noteSnapQuant; + } + /** * scrollPosition is the current position in the song, in pixels. * One pixel is 1/40 of 1 step, and 1/160 of 1 beat. @@ -1776,7 +1797,7 @@ class ChartEditorState extends HaxeUIState // These ones only happen if the modal dialog is not open. handleScrollKeybinds(); // handleZoom(); - // handleSnap(); + handleSnap(); handleCursor(); handleMenubar(); @@ -2123,8 +2144,12 @@ class ChartEditorState extends HaxeUIState } // The song position of the cursor, in steps. - var cursorFractionalStep:Float = cursorY / GRID_SIZE / (16 / noteSnapQuant); + var cursorFractionalStep:Float = cursorY / GRID_SIZE; var cursorMs:Float = Conductor.getStepTimeInMs(cursorFractionalStep); + // Round the cursor step to the nearest snap quant. + var cursorSnappedStep:Float = Math.floor(cursorFractionalStep / noteSnapRatio) * noteSnapRatio; + var cursorSnappedMs:Float = Conductor.getStepTimeInMs(cursorSnappedStep); + // The direction value for the column at the cursor. var cursorColumn:Int = Math.floor(cursorX / GRID_SIZE); if (cursorColumn < 0) cursorColumn = 0; @@ -2379,7 +2404,7 @@ class ChartEditorState extends HaxeUIState // Handle extending the note as you drag. // TODO: This should be beat snapped? - var dragLengthSteps:Float = Conductor.getTimeInSteps(cursorMs) - currentPlaceNoteData.stepTime; + var dragLengthSteps:Float = Conductor.getTimeInSteps(cursorSnappedMs) - currentPlaceNoteData.stepTime; // Without this, the newly placed note feels too short compared to the user's input. var INCREMENT:Float = 1.0; @@ -2473,14 +2498,14 @@ class ChartEditorState extends HaxeUIState { // Create an event and place it in the chart. // TODO: Figure out configuring event data. - var newEventData:SongEventData = new SongEventData(cursorMs, selectedEventKind, selectedEventData); + var newEventData:SongEventData = new SongEventData(cursorSnappedMs, selectedEventKind, selectedEventData); performCommand(new AddEventsCommand([newEventData], FlxG.keys.pressed.CONTROL)); } else { // Create a note and place it in the chart. - var newNoteData:SongNoteData = new SongNoteData(cursorMs, cursorColumn, 0, selectedNoteKind); + var newNoteData:SongNoteData = new SongNoteData(cursorSnappedMs, cursorColumn, 0, selectedNoteKind); performCommand(new AddNotesCommand([newNoteData], FlxG.keys.pressed.CONTROL)); @@ -2549,7 +2574,7 @@ class ChartEditorState extends HaxeUIState { eventData.event = selectedEventKind; } - eventData.time = cursorMs; + eventData.time = cursorSnappedMs; gridGhostEvent.visible = true; gridGhostEvent.eventData = eventData; @@ -2563,8 +2588,7 @@ class ChartEditorState extends HaxeUIState if (gridGhostNote == null) throw "ERROR: Tried to handle cursor, but gridGhostNote is null! Check ChartEditorState.buildGrid()"; - var noteData:SongNoteData = gridGhostNote.noteData != null ? gridGhostNote.noteData : new SongNoteData(cursorMs, cursorColumn, 0, - selectedNoteKind); + var noteData:SongNoteData = gridGhostNote.noteData != null ? gridGhostNote.noteData : new SongNoteData(cursorMs, cursorColumn, 0, selectedNoteKind); if (cursorColumn != noteData.data || selectedNoteKind != noteData.kind) { @@ -2572,7 +2596,7 @@ class ChartEditorState extends HaxeUIState noteData.data = cursorColumn; gridGhostNote.playNoteAnimation(); } - noteData.time = cursorMs; + noteData.time = cursorSnappedMs; gridGhostNote.visible = true; gridGhostNote.noteData = noteData; @@ -4058,6 +4082,7 @@ class ChartEditorState extends HaxeUIState } } + #if !mac NotificationManager.instance.addNotification( { title: 'Success', @@ -4065,6 +4090,7 @@ class ChartEditorState extends HaxeUIState type: NotificationType.Success, expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME }); + #end } /**