This commit is contained in:
Lasercar 2025-04-05 05:29:48 +10:00 committed by GitHub
commit 56d8ca9f99
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 294 additions and 0 deletions

View file

@ -54,6 +54,9 @@ import funkin.ui.debug.charting.commands.DeselectItemsCommand;
import funkin.ui.debug.charting.commands.ExtendNoteLengthCommand;
import funkin.ui.debug.charting.commands.FlipNotesCommand;
import funkin.ui.debug.charting.commands.InvertSelectedItemsCommand;
import funkin.ui.debug.charting.commands.SnapEventsCommand;
import funkin.ui.debug.charting.commands.SnapItemsCommand;
import funkin.ui.debug.charting.commands.SnapNotesCommand;
import funkin.ui.debug.charting.commands.MoveEventsCommand;
import funkin.ui.debug.charting.commands.MoveItemsCommand;
import funkin.ui.debug.charting.commands.MoveNotesCommand;
@ -5490,6 +5493,23 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
performCommand(new PasteItemsCommand(targetMs));
}
// SHIFT + N = Snap
if (FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.N)
{
if (currentNoteSelection.length > 0 && currentEventSelection.length > 0)
{
performCommand(new SnapItemsCommand(currentNoteSelection, currentEventSelection));
}
else if (currentNoteSelection.length > 0)
{
performCommand(new SnapNotesCommand(currentNoteSelection));
}
else if (currentEventSelection.length > 0)
{
performCommand(new SnapEventsCommand(currentEventSelection));
}
}
// DELETE = Delete
var delete:Bool = FlxG.keys.justPressed.DELETE;

View file

@ -0,0 +1,85 @@
package funkin.ui.debug.charting.commands;
import funkin.data.song.SongData.SongEventData;
import funkin.data.song.SongData.SongNoteData;
import funkin.data.song.SongDataUtils;
/**
* Snap the given events to the current note snap in the chart editor.
*/
@:nullSafety
@:access(funkin.ui.debug.charting.ChartEditorState)
class SnapEventsCommand implements ChartEditorCommand
{
var events:Array<SongEventData>;
var snappedEvents:Array<SongEventData>;
public function new(events:Array<SongEventData>)
{
// Clone the events to prevent editing from affecting the history.
this.events = [for (event in events) event.clone()];
this.snappedEvents = [];
}
public function execute(state:ChartEditorState):Void
{
state.currentSongChartEventData = SongDataUtils.subtractEvents(state.currentSongChartEventData, events);
snappedEvents = [];
for (event in events)
{
// Clone the events to prevent editing from affecting the history.
var resultEvent = event.clone();
var targetStep:Float = Conductor.instance.getTimeInSteps(resultEvent.time);
var targetSnappedStep:Float = Math.round(targetStep / state.noteSnapRatio) * state.noteSnapRatio;
var targetSnappedMs:Float = Conductor.instance.getStepTimeInMs(targetSnappedStep);
if (targetSnappedMs != resultEvent.time) resultEvent.time = targetSnappedMs.clamp(0, Conductor.instance.getStepTimeInMs(state.songLengthInSteps));
snappedEvents.push(resultEvent);
}
state.currentSongChartEventData = SongDataUtils.subtractEvents(state.currentSongChartEventData, events);
state.currentSongChartEventData = state.currentSongChartEventData.concat(snappedEvents);
state.currentEventSelection = snappedEvents;
state.playSound(Paths.sound('chartingSounds/noteLay'));
state.saveDataDirty = true;
state.noteDisplayDirty = true;
state.notePreviewDirty = true;
state.sortChartData();
}
public function undo(state:ChartEditorState):Void
{
state.currentSongChartEventData = SongDataUtils.subtractEvents(state.currentSongChartEventData, snappedEvents);
state.currentSongChartEventData = state.currentSongChartEventData.concat(events);
state.currentEventSelection = events;
state.saveDataDirty = true;
state.noteDisplayDirty = true;
state.notePreviewDirty = true;
state.sortChartData();
}
public function shouldAddToHistory(state:ChartEditorState):Bool
{
// This command is undoable. Add to the history if we actually performed an action.
return (events.length > 0);
}
public function toString():String
{
var len:Int = events.length;
return 'Snap $len Events';
}
}

View file

@ -0,0 +1,108 @@
package funkin.ui.debug.charting.commands;
import funkin.data.song.SongData.SongEventData;
import funkin.data.song.SongData.SongNoteData;
import funkin.data.song.SongDataUtils;
/**
* Snap the given items to the current note snap in the chart editor.
*/
@:nullSafety
@:access(funkin.ui.debug.charting.ChartEditorState)
class SnapItemsCommand implements ChartEditorCommand
{
var notes:Array<SongNoteData>;
var snappedNotes:Array<SongNoteData>;
var events:Array<SongEventData>;
var snappedEvents:Array<SongEventData>;
public function new(notes:Array<SongNoteData>, events:Array<SongEventData>)
{
// Clone the notes to prevent editing from affecting the history.
this.notes = notes.clone();
this.events = events.clone();
this.snappedNotes = [];
this.snappedEvents = [];
}
public function execute(state:ChartEditorState):Void
{
state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, notes);
state.currentSongChartEventData = SongDataUtils.subtractEvents(state.currentSongChartEventData, events);
snappedNotes = [];
snappedEvents = [];
for (note in notes)
{
// Clone the notes to prevent editing from affecting the history.
var resultNote = note.clone();
var targetStep:Float = Conductor.instance.getTimeInSteps(resultNote.time);
var targetSnappedStep:Float = Math.round(targetStep / state.noteSnapRatio) * state.noteSnapRatio;
var targetSnappedMs:Float = Conductor.instance.getStepTimeInMs(targetSnappedStep);
if (targetSnappedMs != resultNote.time) resultNote.time = targetSnappedMs.clamp(0, Conductor.instance.getStepTimeInMs(state.songLengthInSteps));
snappedNotes.push(resultNote);
}
for (event in events)
{
// Clone the notes to prevent editing from affecting the history.
var resultEvent = event.clone();
var targetStep:Float = Conductor.instance.getTimeInSteps(resultEvent.time);
var targetSnappedStep:Float = Math.round(targetStep / state.noteSnapRatio) * state.noteSnapRatio;
var targetSnappedMs:Float = Conductor.instance.getStepTimeInMs(targetSnappedStep);
if (targetSnappedMs != resultEvent.time) resultEvent.time = targetSnappedMs.clamp(0, Conductor.instance.getStepTimeInMs(state.songLengthInSteps));
snappedEvents.push(resultEvent);
}
state.currentSongChartNoteData = state.currentSongChartNoteData.concat(snappedNotes);
state.currentSongChartEventData = state.currentSongChartEventData.concat(snappedEvents);
state.currentNoteSelection = snappedNotes;
state.currentEventSelection = snappedEvents;
state.playSound(Paths.sound('chartingSounds/noteLay'));
state.saveDataDirty = true;
state.noteDisplayDirty = true;
state.notePreviewDirty = true;
state.sortChartData();
}
public function undo(state:ChartEditorState):Void
{
state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, snappedNotes);
state.currentSongChartEventData = SongDataUtils.subtractEvents(state.currentSongChartEventData, snappedEvents);
state.currentSongChartNoteData = state.currentSongChartNoteData.concat(notes);
state.currentSongChartEventData = state.currentSongChartEventData.concat(events);
state.currentNoteSelection = notes;
state.currentEventSelection = events;
state.saveDataDirty = true;
state.noteDisplayDirty = true;
state.notePreviewDirty = true;
state.sortChartData();
}
public function shouldAddToHistory(state:ChartEditorState):Bool
{
// This command is undoable. Add to the history if we actually performed an action.
return (notes.length > 0 || events.length > 0);
}
public function toString():String
{
var len:Int = notes.length + events.length;
return 'Snap $len Items';
}
}

View file

@ -0,0 +1,81 @@
package funkin.ui.debug.charting.commands;
import funkin.data.song.SongData.SongNoteData;
import funkin.data.song.SongDataUtils;
/**
* Snap the given notes to the current note snap in the chart editor.
*/
@:nullSafety
@:access(funkin.ui.debug.charting.ChartEditorState)
class SnapNotesCommand implements ChartEditorCommand
{
var notes:Array<SongNoteData>;
var snappedNotes:Array<SongNoteData>;
public function new(notes:Array<SongNoteData>)
{
// Clone the notes to prevent editing from affecting the history.
this.notes = [for (note in notes) note.clone()];
this.snappedNotes = [];
}
public function execute(state:ChartEditorState):Void
{
state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, notes);
snappedNotes = [];
for (note in notes)
{
// Clone the notes to prevent editing from affecting the history.
var resultNote = note.clone();
var targetStep:Float = Conductor.instance.getTimeInSteps(resultNote.time);
var targetSnappedStep:Float = Math.round(targetStep / state.noteSnapRatio) * state.noteSnapRatio;
var targetSnappedMs:Float = Conductor.instance.getStepTimeInMs(targetSnappedStep);
if (targetSnappedMs != resultNote.time) resultNote.time = targetSnappedMs.clamp(0, Conductor.instance.getStepTimeInMs(state.songLengthInSteps));
snappedNotes.push(resultNote);
}
state.currentSongChartNoteData = state.currentSongChartNoteData.concat(snappedNotes);
state.currentNoteSelection = snappedNotes;
state.playSound(Paths.sound('chartingSounds/noteLay'));
state.saveDataDirty = true;
state.noteDisplayDirty = true;
state.notePreviewDirty = true;
state.sortChartData();
}
public function undo(state:ChartEditorState):Void
{
state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, snappedNotes);
state.currentSongChartNoteData = state.currentSongChartNoteData.concat(notes);
state.currentNoteSelection = notes;
state.saveDataDirty = true;
state.noteDisplayDirty = true;
state.notePreviewDirty = true;
state.sortChartData();
}
public function shouldAddToHistory(state:ChartEditorState):Bool
{
// This command is undoable. Add to the history if we actually performed an action.
return (notes.length > 0);
}
public function toString():String
{
var len:Int = notes.length;
return 'Snap $len Notes';
}
}