mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2025-04-21 19:31:52 -04:00
Merge 069c15c31e
into d31ef12363
This commit is contained in:
commit
bb0e9148b0
9 changed files with 142 additions and 101 deletions
source/funkin
|
@ -335,7 +335,7 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
|
|||
}
|
||||
else
|
||||
{
|
||||
throw '[${registryId}] Chart entry ${id}:${variation} does not support migration to version ${SONG_CHART_DATA_VERSION_RULE}.';
|
||||
throw '[${registryId}] Chart entry ${id}:${variation} does not support migration to version ${SONG_MUSIC_DATA_VERSION_RULE}.';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -348,7 +348,7 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
|
|||
}
|
||||
else
|
||||
{
|
||||
throw '[${registryId}] Chart entry "$fileName" does not support migration to version ${SONG_CHART_DATA_VERSION_RULE}.';
|
||||
throw '[${registryId}] Chart entry "$fileName" does not support migration to version ${SONG_MUSIC_DATA_VERSION_RULE}.';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
package funkin.play.song;
|
||||
|
||||
import funkin.data.song.SongData.SongChartData;
|
||||
import funkin.data.song.SongData.SongMetadata;
|
||||
import funkin.util.FileUtil;
|
||||
import openfl.net.FileReference;
|
||||
|
||||
/**
|
||||
* TODO: Refactor and remove this.
|
||||
*/
|
||||
class SongSerializer
|
||||
{
|
||||
/**
|
||||
* Access a SongChartData JSON file from a specific path, then load it.
|
||||
* @param path The file path to read from.
|
||||
*/
|
||||
public static function importSongChartDataSync(path:String):SongChartData
|
||||
{
|
||||
var fileData = FileUtil.readStringFromPath(path);
|
||||
|
||||
if (fileData == null) return null;
|
||||
|
||||
var songChartData:SongChartData = fileData.parseJSON();
|
||||
|
||||
return songChartData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access a SongMetadata JSON file from a specific path, then load it.
|
||||
* @param path The file path to read from.
|
||||
*/
|
||||
public static function importSongMetadataSync(path:String):SongMetadata
|
||||
{
|
||||
var fileData = FileUtil.readStringFromPath(path);
|
||||
|
||||
if (fileData == null) return null;
|
||||
|
||||
var songMetadata:SongMetadata = fileData.parseJSON();
|
||||
|
||||
return songMetadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt the user to browse for a SongChartData JSON file path, then load it.
|
||||
* @param callback The function to call when the file is loaded.
|
||||
*/
|
||||
public static function importSongChartDataAsync(callback:SongChartData->Void):Void
|
||||
{
|
||||
FileUtil.browseFileReference(function(fileReference:FileReference) {
|
||||
var data = fileReference.data.toString();
|
||||
|
||||
if (data == null) return;
|
||||
|
||||
var songChartData:SongChartData = data.parseJSON();
|
||||
|
||||
if (songChartData != null) callback(songChartData);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompt the user to browse for a SongMetadata JSON file path, then load it.
|
||||
* @param callback The function to call when the file is loaded.
|
||||
*/
|
||||
public static function importSongMetadataAsync(callback:SongMetadata->Void):Void
|
||||
{
|
||||
FileUtil.browseFileReference(function(fileReference:FileReference) {
|
||||
var data = fileReference.data.toString();
|
||||
|
||||
if (data == null) return;
|
||||
|
||||
var songMetadata:SongMetadata = data.parseJSON();
|
||||
|
||||
if (songMetadata != null) callback(songMetadata);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1294,6 +1294,17 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
function set_currentSongChartData(value:SongChartData):SongChartData
|
||||
{
|
||||
songChartData.set(selectedVariation, value);
|
||||
var variationMetadata:Null<SongMetadata> = songMetadata.get(selectedVariation);
|
||||
if (variationMetadata != null)
|
||||
{
|
||||
// Add the difficulties to the metadata if they're new so that the editor properly loads them
|
||||
// This is a silly way of getting the new difficulties but what other option do I have?
|
||||
var keys:Array<String> = [for (x in songChartData.get(selectedVariation).scrollSpeed.keys()) x];
|
||||
for (key in keys)
|
||||
{
|
||||
variationMetadata.playData.difficulties.pushUnique(key);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
@ -3491,7 +3502,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
{
|
||||
// This sprite is off-screen or was deleted.
|
||||
// Kill the note sprite and recycle it.
|
||||
noteSprite.noteData = null;
|
||||
noteSprite.kill();
|
||||
}
|
||||
}
|
||||
// Sort the note data array, using an algorithm that is fast on nearly-sorted data.
|
||||
|
@ -3509,7 +3520,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
// It will be displayed by gridGhostHoldNoteSprite instead.
|
||||
holdNoteSprite.kill();
|
||||
}
|
||||
else if (!holdNoteSprite.isHoldNoteVisible(FlxG.height - MENU_BAR_HEIGHT, GRID_TOP_PAD))
|
||||
else if (!holdNoteSprite.isHoldNoteVisible(viewAreaBottomPixels, viewAreaTopPixels))
|
||||
{
|
||||
// This hold note is off-screen.
|
||||
// Kill the hold note sprite and recycle it.
|
||||
|
@ -3533,7 +3544,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
// Update the event sprite's height and position.
|
||||
// var holdNoteHeight = holdNoteSprite.noteData.getStepLength() * GRID_SIZE;
|
||||
// holdNoteSprite.setHeightDirectly(holdNoteHeight);
|
||||
holdNoteSprite.updateHoldNotePosition(renderedNotes);
|
||||
holdNoteSprite.updateHoldNotePosition(renderedHoldNotes);
|
||||
}
|
||||
}
|
||||
// Sort the note data array, using an algorithm that is fast on nearly-sorted data.
|
||||
|
@ -3549,7 +3560,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
// Resolve an issue where dragging an event too far would cause it to be hidden.
|
||||
var isSelectedAndDragged = currentEventSelection.fastContains(eventSprite.eventData) && (dragTargetCurrentStep != 0);
|
||||
|
||||
if ((eventSprite.isEventVisible(FlxG.height - PLAYBAR_HEIGHT, MENU_BAR_HEIGHT)
|
||||
if ((eventSprite.isEventVisible(viewAreaBottomPixels, viewAreaTopPixels)
|
||||
&& currentSongChartEventData.fastContains(eventSprite.eventData))
|
||||
|| isSelectedAndDragged)
|
||||
{
|
||||
|
@ -3565,7 +3576,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
{
|
||||
// This event was deleted.
|
||||
// Kill the event sprite and recycle it.
|
||||
eventSprite.eventData = null;
|
||||
eventSprite.kill();
|
||||
}
|
||||
}
|
||||
// Sort the note data array, using an algorithm that is fast on nearly-sorted data.
|
||||
|
|
|
@ -37,7 +37,6 @@ class ChangeStartingBPMCommand implements ChartEditorCommand
|
|||
state.noteDisplayDirty = true;
|
||||
state.notePreviewDirty = true;
|
||||
state.notePreviewViewportBoundsDirty = true;
|
||||
state.scrollPositionInPixels = 0;
|
||||
|
||||
Conductor.instance.mapTimeChanges(state.currentSongMetadata.timeChanges);
|
||||
|
||||
|
@ -61,7 +60,6 @@ class ChangeStartingBPMCommand implements ChartEditorCommand
|
|||
state.noteDisplayDirty = true;
|
||||
state.notePreviewDirty = true;
|
||||
state.notePreviewViewportBoundsDirty = true;
|
||||
state.scrollPositionInPixels = 0;
|
||||
|
||||
Conductor.instance.mapTimeChanges(state.currentSongMetadata.timeChanges);
|
||||
|
||||
|
|
|
@ -1168,7 +1168,8 @@ class ChartEditorDialogHandler
|
|||
|
||||
var dialogNoteStyle:Null<DropDown> = dialog.findComponent('dialogNoteStyle', DropDown);
|
||||
if (dialogNoteStyle == null) throw 'Could not locate dialogNoteStyle DropDown in Add Variation dialog';
|
||||
dialogNoteStyle.value = state.currentSongMetadata.playData.noteStyle;
|
||||
var startingValueNoteStyle = ChartEditorDropdowns.populateDropdownWithNoteStyles(dialogNoteStyle, state.currentSongMetadata.playData.noteStyle);
|
||||
dialogNoteStyle.value = startingValueNoteStyle;
|
||||
|
||||
var dialogCharacterPlayer:Null<DropDown> = dialog.findComponent('dialogCharacterPlayer', DropDown);
|
||||
if (dialogCharacterPlayer == null) throw 'Could not locate dialogCharacterPlayer DropDown in Add Variation dialog';
|
||||
|
@ -1203,7 +1204,7 @@ class ChartEditorDialogHandler
|
|||
var pendingVariation:SongMetadata = new SongMetadata(dialogSongName.text, dialogSongArtist.text, dialogVariationName.text.toLowerCase());
|
||||
|
||||
pendingVariation.playData.stage = dialogStage.value.id;
|
||||
pendingVariation.playData.noteStyle = dialogNoteStyle.value;
|
||||
pendingVariation.playData.noteStyle = dialogNoteStyle.value.id;
|
||||
pendingVariation.timeChanges[0].bpm = dialogBPM.value;
|
||||
|
||||
state.songMetadata.set(pendingVariation.variation, pendingVariation);
|
||||
|
|
|
@ -113,7 +113,10 @@ class ChartEditorToolboxHandler
|
|||
{
|
||||
var toolbox:Null<ChartEditorBaseToolbox> = cast state.activeToolboxes.get(id);
|
||||
|
||||
if (toolbox == null) return;
|
||||
if (toolbox == null)
|
||||
{
|
||||
toolbox = cast initToolbox(state, id);
|
||||
}
|
||||
|
||||
if (toolbox != null)
|
||||
{
|
||||
|
|
|
@ -1,12 +1,24 @@
|
|||
package funkin.ui.debug.charting.toolboxes;
|
||||
|
||||
import funkin.data.song.SongData.SongChartData;
|
||||
import funkin.data.song.SongData.SongMetadata;
|
||||
import funkin.data.song.SongRegistry;
|
||||
import haxe.ui.components.Button;
|
||||
import haxe.ui.containers.dialogs.Dialogs;
|
||||
import haxe.ui.containers.dialogs.Dialog.DialogButton;
|
||||
import funkin.data.song.SongData.SongMetadata;
|
||||
import haxe.ui.components.DropDown;
|
||||
import haxe.ui.components.HorizontalSlider;
|
||||
import funkin.util.VersionUtil;
|
||||
import funkin.util.FileUtil;
|
||||
import openfl.net.FileReference;
|
||||
import haxe.ui.containers.dialogs.MessageBox.MessageBoxType;
|
||||
import funkin.play.song.SongSerializer;
|
||||
import haxe.ui.components.Label;
|
||||
import haxe.ui.components.NumberStepper;
|
||||
import haxe.ui.components.Slider;
|
||||
import haxe.ui.components.TextField;
|
||||
import funkin.play.stage.Stage;
|
||||
import haxe.ui.containers.Box;
|
||||
import haxe.ui.containers.TreeView;
|
||||
import haxe.ui.containers.TreeViewNode;
|
||||
import haxe.ui.events.UIEvent;
|
||||
|
@ -81,26 +93,96 @@ class ChartEditorDifficultyToolbox extends ChartEditorBaseToolbox
|
|||
|
||||
difficultyToolboxSaveMetadata.onClick = function(_:UIEvent) {
|
||||
var vari:String = chartEditorState.selectedVariation != Constants.DEFAULT_VARIATION ? '-${chartEditorState.selectedVariation}' : '';
|
||||
FileUtil.writeFileReference('${chartEditorState.currentSongId}$vari-metadata.json', chartEditorState.currentSongMetadata.serialize());
|
||||
FileUtil.writeFileReference('${chartEditorState.currentSongId}$vari-metadata.json', chartEditorState.currentSongMetadata.serialize(),
|
||||
function(notification:String) {
|
||||
switch (notification)
|
||||
{
|
||||
case "success":
|
||||
chartEditorState.success("Saved Metadata", 'Successfully wrote file (${chartEditorState.currentSongId}$vari-metadata.json).');
|
||||
case "info":
|
||||
chartEditorState.info("Canceled Save Metadata", '(${chartEditorState.currentSongId}$vari-metadata.json)');
|
||||
case "error":
|
||||
chartEditorState.error("Failure", 'Failed to write file (${chartEditorState.currentSongId}$vari-metadata.json).');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
difficultyToolboxSaveChart.onClick = function(_:UIEvent) {
|
||||
var vari:String = chartEditorState.selectedVariation != Constants.DEFAULT_VARIATION ? '-${chartEditorState.selectedVariation}' : '';
|
||||
FileUtil.writeFileReference('${chartEditorState.currentSongId}$vari-chart.json', chartEditorState.currentSongChartData.serialize());
|
||||
FileUtil.writeFileReference('${chartEditorState.currentSongId}$vari-chart.json', chartEditorState.currentSongChartData.serialize(),
|
||||
function(notification:String) {
|
||||
switch (notification)
|
||||
{
|
||||
case "success":
|
||||
chartEditorState.success("Saved Chart Data", 'Successfully wrote file (${chartEditorState.currentSongId}$vari-chart.json).');
|
||||
case "info":
|
||||
chartEditorState.info("Canceled Save Chart Data", '(${chartEditorState.currentSongId}$vari-chart.json)');
|
||||
case "error":
|
||||
chartEditorState.error("Failure", 'Failed to write file (${chartEditorState.currentSongId}$vari-chart.json).');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
difficultyToolboxLoadMetadata.onClick = function(_:UIEvent) {
|
||||
// Replace metadata for current variation.
|
||||
SongSerializer.importSongMetadataAsync(function(songMetadata) {
|
||||
chartEditorState.currentSongMetadata = songMetadata;
|
||||
FileUtil.browseFileReference(function(fileReference:FileReference) {
|
||||
var data = fileReference.data.toString();
|
||||
|
||||
if (data == null) return;
|
||||
|
||||
var songMetadataVersion:Null<thx.semver.Version> = VersionUtil.getVersionFromJSON(data);
|
||||
|
||||
var songMetadata:Null<SongMetadata> = null;
|
||||
if (VersionUtil.validateVersion(songMetadataVersion,
|
||||
SongRegistry.SONG_METADATA_VERSION_RULE)) songMetadata = SongRegistry.instance.parseEntryMetadataRawWithMigration(data, fileReference.name,
|
||||
songMetadataVersion);
|
||||
|
||||
if (songMetadata != null)
|
||||
{
|
||||
chartEditorState.currentSongMetadata = songMetadata;
|
||||
chartEditorState.healthIconsDirty = true;
|
||||
chartEditorState.refreshToolbox(ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
chartEditorState.success('Replaced Metadata', 'Replaced metadata with file (${fileReference.name})');
|
||||
}
|
||||
else
|
||||
{
|
||||
chartEditorState.error('Failure', 'Failed to load metadata file (${fileReference.name})');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
difficultyToolboxLoadChart.onClick = function(_:UIEvent) {
|
||||
// Replace chart data for current variation.
|
||||
SongSerializer.importSongChartDataAsync(function(songChartData) {
|
||||
chartEditorState.currentSongChartData = songChartData;
|
||||
chartEditorState.noteDisplayDirty = true;
|
||||
FileUtil.browseFileReference(function(fileReference:FileReference) {
|
||||
var data = fileReference.data.toString();
|
||||
|
||||
if (data == null) return;
|
||||
|
||||
var songChartDataVersion:Null<thx.semver.Version> = VersionUtil.getVersionFromJSON(data);
|
||||
|
||||
var songChartData:Null<SongChartData> = null;
|
||||
if (VersionUtil.validateVersion(songChartDataVersion,
|
||||
SongRegistry.SONG_CHART_DATA_VERSION_RULE)) songChartData = SongRegistry.instance.parseEntryChartDataRawWithMigration(data, fileReference.name,
|
||||
songChartDataVersion);
|
||||
|
||||
if (songChartData != null)
|
||||
{
|
||||
chartEditorState.currentSongChartData = songChartData;
|
||||
chartEditorState.refreshToolbox(ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
updateTree();
|
||||
refresh();
|
||||
chartEditorState.success('Loaded Chart Data', 'Loaded chart data file (${fileReference.name})');
|
||||
if (chartEditorState.currentNoteSelection != []) chartEditorState.currentNoteSelection = [];
|
||||
if (chartEditorState.currentEventSelection != []) chartEditorState.currentEventSelection = [];
|
||||
chartEditorState.noteDisplayDirty = true;
|
||||
chartEditorState.notePreviewDirty = true;
|
||||
chartEditorState.noteTooltipsDirty = true;
|
||||
chartEditorState.notePreviewViewportBoundsDirty = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
chartEditorState.error('Failure', 'Failed to load chart data file (${fileReference.name})');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@ package funkin.ui.debug.charting.toolboxes;
|
|||
import funkin.play.character.BaseCharacter.CharacterType;
|
||||
import funkin.play.character.CharacterData;
|
||||
import funkin.data.stage.StageRegistry;
|
||||
import funkin.data.notestyle.NoteStyleRegistry;
|
||||
import funkin.play.notes.notestyle.NoteStyle;
|
||||
import funkin.ui.debug.charting.commands.ChangeStartingBPMCommand;
|
||||
import funkin.ui.debug.charting.util.ChartEditorDropdowns;
|
||||
import haxe.ui.components.Button;
|
||||
|
@ -112,8 +114,12 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
|
|||
inputStage.value = startingValueStage;
|
||||
|
||||
inputNoteStyle.onChange = function(event:UIEvent) {
|
||||
if (event.data?.id == null) return;
|
||||
var valid:Bool = event.data != null && event.data.id != null;
|
||||
|
||||
if (valid)
|
||||
{
|
||||
chartEditorState.currentSongNoteStyle = event.data.id;
|
||||
}
|
||||
};
|
||||
var startingValueNoteStyle = ChartEditorDropdowns.populateDropdownWithNoteStyles(inputNoteStyle, chartEditorState.currentSongMetadata.playData.noteStyle);
|
||||
inputNoteStyle.value = startingValueNoteStyle;
|
||||
|
@ -122,8 +128,7 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
|
|||
if (event.value == null || event.value <= 0) return;
|
||||
|
||||
// Use a command so we can undo/redo this action.
|
||||
var startingBPM = chartEditorState.currentSongMetadata.timeChanges[0].bpm;
|
||||
if (event.value != startingBPM)
|
||||
if (event.value != Conductor.instance.bpm)
|
||||
{
|
||||
chartEditorState.performCommand(new ChangeStartingBPMCommand(event.value));
|
||||
}
|
||||
|
@ -191,7 +196,7 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
|
|||
inputSongArtist.value = chartEditorState.currentSongMetadata.artist;
|
||||
inputSongCharter.value = chartEditorState.currentSongMetadata.charter;
|
||||
inputStage.value = chartEditorState.currentSongMetadata.playData.stage;
|
||||
inputNoteStyle.value = chartEditorState.currentSongMetadata.playData.noteStyle;
|
||||
inputNoteStyle.value = chartEditorState.currentSongNoteStyle;
|
||||
inputBPM.value = chartEditorState.currentSongMetadata.timeChanges[0].bpm;
|
||||
inputDifficultyRating.value = chartEditorState.currentSongChartDifficultyRating;
|
||||
inputScrollSpeed.value = chartEditorState.currentSongChartScrollSpeed;
|
||||
|
@ -199,6 +204,11 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
|
|||
frameVariation.text = 'Variation: ${chartEditorState.selectedVariation.toTitleCase()}';
|
||||
frameDifficulty.text = 'Difficulty: ${chartEditorState.selectedDifficulty.toTitleCase()}';
|
||||
|
||||
if (chartEditorState.currentSongMetadata.timeChanges[0].bpm != Conductor.instance.bpm)
|
||||
{
|
||||
chartEditorState.performCommand(new ChangeStartingBPMCommand(chartEditorState.currentSongMetadata.timeChanges[0].bpm));
|
||||
}
|
||||
|
||||
var currentTimeSignature = '${chartEditorState.currentSongMetadata.timeChanges[0].timeSignatureNum}/${chartEditorState.currentSongMetadata.timeChanges[0].timeSignatureDen}';
|
||||
trace('Setting time signature to ${currentTimeSignature}');
|
||||
inputTimeSignature.value = {id: currentTimeSignature, text: currentTimeSignature};
|
||||
|
@ -212,6 +222,15 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
|
|||
{id: "mainStage", text: "Main Stage"};
|
||||
}
|
||||
|
||||
var noteStyleId:String = chartEditorState.currentSongNoteStyle;
|
||||
var noteStyle:Null<NoteStyle> = NoteStyleRegistry.instance.fetchEntry(noteStyleId);
|
||||
if (inputNoteStyle != null)
|
||||
{
|
||||
inputNoteStyle.value = (noteStyle != null) ?
|
||||
{id: noteStyle.id, text: noteStyle.getName()} :
|
||||
{id: "Funkin", text: "Funkin'"};
|
||||
}
|
||||
|
||||
var LIMIT = 6;
|
||||
|
||||
var charDataOpponent:Null<CharacterData> = CharacterDataParser.fetchCharacterData(chartEditorState.currentSongMetadata.playData.characters.opponent);
|
||||
|
|
|
@ -391,17 +391,20 @@ class FileUtil
|
|||
/**
|
||||
* Prompts the user to save a file to their computer.
|
||||
*/
|
||||
public static function writeFileReference(path:String, data:String)
|
||||
public static function writeFileReference(path:String, data:String, callback:String->Void)
|
||||
{
|
||||
var file = new FileReference();
|
||||
file.addEventListener(Event.COMPLETE, function(e:Event) {
|
||||
trace('Successfully wrote file.');
|
||||
callback("success");
|
||||
});
|
||||
file.addEventListener(Event.CANCEL, function(e:Event) {
|
||||
trace('Cancelled writing file.');
|
||||
callback("info");
|
||||
});
|
||||
file.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent) {
|
||||
trace('IO error writing file.');
|
||||
callback("error");
|
||||
});
|
||||
file.save(data, path);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue