1
0
Fork 0
mirror of https://github.com/ninjamuffin99/Funkin.git synced 2024-12-27 23:46:45 +00:00

Merge pull request #128 from FunkinCrew/feature/chart-editor-difficulty-stuff

Chart Editor: Difficulty tweaks and bug fixes
This commit is contained in:
Cameron Taylor 2023-08-16 04:12:07 -04:00 committed by GitHub
commit 280ecc2003
9 changed files with 399 additions and 33 deletions

View file

@ -55,7 +55,7 @@ class MainMenuState extends MusicBeatState
transIn = FlxTransitionableState.defaultTransIn;
transOut = FlxTransitionableState.defaultTransOut;
if (!FlxG.sound.music.playing)
if (!(FlxG?.sound?.music?.playing ?? false))
{
FlxG.sound.playMusic(Paths.music('freakyMenu/freakyMenu'));
}

View file

@ -297,7 +297,7 @@ class SongDifficulty
public var songName:String = SongValidator.DEFAULT_SONGNAME;
public var songArtist:String = SongValidator.DEFAULT_ARTIST;
public var timeFormat:SongTimeFormat = SongValidator.DEFAULT_TIMEFORMAT;
public var divisions:Int = SongValidator.DEFAULT_DIVISIONS;
public var divisions:Null<Int> = SongValidator.DEFAULT_DIVISIONS;
public var looped:Bool = SongValidator.DEFAULT_LOOPED;
public var generatedBy:String = SongValidator.DEFAULT_GENERATEDBY;

View file

@ -11,6 +11,7 @@ import haxe.DynamicAccess;
import haxe.Json;
import openfl.utils.Assets;
import thx.semver.Version;
import funkin.util.SerializerUtil;
/**
* Contains utilities for loading and parsing stage data.
@ -138,13 +139,8 @@ class SongDataParser
{
var result:Array<SongMetadata> = [];
var rawJson:String = loadSongMetadataFile(songId);
var jsonData:Dynamic = null;
try
{
jsonData = Json.parse(rawJson);
}
catch (e) {}
var jsonStr:String = loadSongMetadataFile(songId);
var jsonData:Dynamic = SerializerUtil.fromJSON(jsonStr);
var songMetadata:SongMetadata = SongMigrator.migrateSongMetadata(jsonData, songId);
songMetadata = SongValidator.validateSongMetadata(songMetadata, songId);
@ -160,9 +156,10 @@ class SongDataParser
for (variation in variations)
{
var variationRawJson:String = loadSongMetadataFile(songId, variation);
var variationSongMetadata:SongMetadata = SongMigrator.migrateSongMetadata(variationRawJson, '${songId}_${variation}');
variationSongMetadata = SongValidator.validateSongMetadata(variationSongMetadata, '${songId}_${variation}');
var variationJsonStr:String = loadSongMetadataFile(songId, variation);
var variationJsonData:Dynamic = SerializerUtil.fromJSON(variationJsonStr);
var variationSongMetadata:SongMetadata = SongMigrator.migrateSongMetadata(variationJsonData, '${songId}:${variation}');
variationSongMetadata = SongValidator.validateSongMetadata(variationSongMetadata, '${songId}:${variation}');
if (variationSongMetadata != null)
{
variationSongMetadata.variation = variation;
@ -269,7 +266,7 @@ typedef RawSongMetadata =
var songName:String;
var artist:String;
var timeFormat:SongTimeFormat;
var divisions:Int;
var divisions:Null<Int>; // Optional field
var timeChanges:Array<SongTimeChange>;
var looped:Bool;
var playData:SongPlayData;
@ -292,7 +289,7 @@ abstract SongMetadata(RawSongMetadata)
songName: songName,
artist: artist,
timeFormat: 'ms',
divisions: 96,
divisions: null,
timeChanges: [new SongTimeChange(-1, 0, 100, 4, 4, [4, 4, 4, 4])],
looped: false,
playData:

View file

@ -15,7 +15,7 @@ class SongValidator
public static final DEFAULT_SONGNAME:String = "Unknown";
public static final DEFAULT_ARTIST:String = "Unknown";
public static final DEFAULT_TIMEFORMAT:SongTimeFormat = SongTimeFormat.MILLISECONDS;
public static final DEFAULT_DIVISIONS:Int = -1;
public static final DEFAULT_DIVISIONS:Null<Int> = null;
public static final DEFAULT_LOOPED:Bool = false;
public static final DEFAULT_STAGE:String = "mainStage";
public static final DEFAULT_SCROLLSPEED:Float = 1.0;

View file

@ -115,7 +115,12 @@ class ChartEditorDialogHandler
if (songData == null) continue;
var songName:String = songData.getDifficulty().songName;
var songName:Null<String> = songData.getDifficulty('normal') ?.songName;
if (songName == null) songName = songData.getDifficulty() ?.songName;
if (songName == null)
{
trace('[WARN] Could not fetch song name for ${targetSongId}');
}
var linkTemplateSong:Link = new Link();
linkTemplateSong.text = songName;

View file

@ -1,5 +1,9 @@
package funkin.ui.debug.charting;
import haxe.ui.components.TextField;
import haxe.ui.components.DropDown;
import haxe.ui.components.NumberStepper;
import haxe.ui.containers.Frame;
import flixel.addons.display.FlxSliceSprite;
import flixel.addons.display.FlxTiledSprite;
import flixel.FlxCamera;
@ -722,11 +726,38 @@ class ChartEditorState extends HaxeUIState
*/
var songMetadata:Map<String, SongMetadata>;
/**
* Retrieves the list of variations for the current song.
*/
var availableVariations(get, null):Array<String>;
function get_availableVariations():Array<String>
{
return [for (x in songMetadata.keys()) x];
var variations:Array<String> = [for (x in songMetadata.keys()) x];
variations.sort(SortUtil.defaultThenAlphabetically.bind('default'));
return variations;
}
/**
* Retrieves the list of difficulties for the current variation of the current song.
* ONLY CONTAINS DIFFICULTIES FOR THE CURRENT VARIATION so if on the default variation, erect/nightmare won't be included.
*/
var availableDifficulties(get, null):Array<String>;
function get_availableDifficulties():Array<String>
{
return songMetadata.get(selectedVariation).playData.difficulties;
}
/**
* Retrieves the list of difficulties for ALL variations of the current song.
*/
var allDifficulties(get, null):Array<String>;
function get_allDifficulties():Array<String>
{
var result:Array<Array<String>> = [for (x in availableVariations) songMetadata.get(x).playData.difficulties];
return result.flatten();
}
/**
@ -976,6 +1007,11 @@ class ChartEditorState extends HaxeUIState
return playableCharData.opponent = value;
}
/**
* SIGNALS
*/
// ==============================
// public var onDifficultyChange(default, null):FlxTypedSignal<ChartEditorState->Void> = new FlxTypedSignal<ChartEditorState->Void>();
/**
* RENDER OBJECTS
*/
@ -1247,7 +1283,6 @@ class ChartEditorState extends HaxeUIState
var height:Int = FlxG.height - MENU_BAR_HEIGHT - GRID_TOP_PAD - 200;
notePreview = new ChartEditorNotePreview(height);
notePreview.y = MENU_BAR_HEIGHT + GRID_TOP_PAD;
// TODO: Re-enable.
// add(notePreview);
}
@ -1438,6 +1473,9 @@ class ChartEditorState extends HaxeUIState
addUIChangeListener('menubarItemDownscroll', event -> isViewDownscroll = event.value);
setUICheckboxSelected('menubarItemDownscroll', isViewDownscroll);
addUIClickListener('menubarItemDifficultyUp', _ -> incrementDifficulty(1));
addUIClickListener('menubarItemDifficultyDown', _ -> incrementDifficulty(-1));
addUIChangeListener('menubarItemPlaytestStartTime', event -> playtestStartTime = event.value);
setUICheckboxSelected('menubarItemPlaytestStartTime', playtestStartTime);
@ -1584,6 +1622,7 @@ class ChartEditorState extends HaxeUIState
handleToolboxes();
handlePlaybar();
handlePlayhead();
// handleNotePreview();
handleFileKeybinds();
handleEditKeybinds();
@ -2755,7 +2794,105 @@ class ChartEditorState extends HaxeUIState
/**
* Handle keybinds for View menu items.
*/
function handleViewKeybinds():Void {}
function handleViewKeybinds():Void
{
if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.LEFT)
{
incrementDifficulty(-1);
}
if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.RIGHT)
{
incrementDifficulty(1);
}
}
function incrementDifficulty(change:Int):Void
{
var currentDifficultyIndex:Int = availableDifficulties.indexOf(selectedDifficulty);
var currentAllDifficultyIndex:Int = allDifficulties.indexOf(selectedDifficulty);
if (currentDifficultyIndex == -1 || currentAllDifficultyIndex == -1)
{
trace('ERROR determining difficulty index!');
}
var isFirstDiff:Bool = currentAllDifficultyIndex == 0;
var isLastDiff:Bool = (currentAllDifficultyIndex == allDifficulties.length - 1);
var isFirstDiffInVariation:Bool = currentDifficultyIndex == 0;
var isLastDiffInVariation:Bool = (currentDifficultyIndex == availableDifficulties.length - 1);
trace(allDifficulties);
if (change < 0 && isFirstDiff)
{
trace('At lowest difficulty! Do nothing.');
return;
}
if (change > 0 && isLastDiff)
{
trace('At highest difficulty! Do nothing.');
return;
}
if (change < 0)
{
trace('Decrement difficulty.');
// If we reached this point, we are not at the lowest difficulty.
if (isFirstDiffInVariation)
{
// Go to the previous variation, then last difficulty in that variation.
var currentVariationIndex:Int = availableVariations.indexOf(selectedVariation);
var prevVariation = availableVariations[currentVariationIndex - 1];
selectedVariation = prevVariation;
var prevDifficulty = availableDifficulties[availableDifficulties.length - 1];
selectedDifficulty = prevDifficulty;
refreshDifficultyTreeSelection();
refreshSongMetadataToolbox();
}
else
{
// Go to previous difficulty in this variation.
var prevDifficulty = availableDifficulties[currentDifficultyIndex - 1];
selectedDifficulty = prevDifficulty;
refreshDifficultyTreeSelection();
refreshSongMetadataToolbox();
}
}
else
{
trace('Increment difficulty.');
// If we reached this point, we are not at the highest difficulty.
if (isLastDiffInVariation)
{
// Go to next variation, then first difficulty in that variation.
var currentVariationIndex:Int = availableVariations.indexOf(selectedVariation);
var nextVariation = availableVariations[currentVariationIndex + 1];
selectedVariation = nextVariation;
var nextDifficulty = availableDifficulties[0];
selectedDifficulty = nextDifficulty;
refreshDifficultyTreeSelection();
refreshSongMetadataToolbox();
}
else
{
// Go to next difficulty in this variation.
var nextDifficulty = availableDifficulties[currentDifficultyIndex + 1];
selectedDifficulty = nextDifficulty;
refreshDifficultyTreeSelection();
refreshSongMetadataToolbox();
}
}
}
/**
* Handle keybinds for the Test menu items.
@ -2801,10 +2938,14 @@ class ChartEditorState extends HaxeUIState
// Clear the tree view so we can rebuild it.
treeView.clearNodes();
var treeSong:TreeViewNode = treeView.addNode({id: 'stv_song', text: 'S: $currentSongName', icon: 'haxeui-core/styles/default/haxeui_tiny.png'});
// , icon: 'haxeui-core/styles/default/haxeui_tiny.png'
var treeSong:TreeViewNode = treeView.addNode({id: 'stv_song', text: 'S: $currentSongName'});
treeSong.expanded = true;
for (curVariation in availableVariations)
var variations = Reflect.copy(availableVariations);
variations.sort(SortUtil.alphabetically);
for (curVariation in variations)
{
var variationMetadata:SongMetadata = songMetadata.get(curVariation);
@ -2828,10 +2969,26 @@ class ChartEditorState extends HaxeUIState
}
treeView.onChange = onChangeTreeDifficulty;
treeView.selectedNode = getCurrentTreeDifficultyNode();
refreshDifficultyTreeSelection(treeView);
}
}
function refreshDifficultyTreeSelection(?treeView:TreeView):Void
{
if (treeView == null)
{
// Manage the Select Difficulty tree view.
var difficultyToolbox:CollapsibleDialog = ChartEditorToolboxHandler.getToolbox(this, CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
if (difficultyToolbox == null) return;
treeView = difficultyToolbox.findComponent('difficultyToolboxTree');
if (treeView == null) return;
}
trace(treeView);
treeView.selectedNode = getCurrentTreeDifficultyNode(treeView);
}
function handlePlayerPreviewToolbox():Void
{
// Manage the Select Difficulty tree view.
@ -2938,20 +3095,28 @@ class ChartEditorState extends HaxeUIState
}
}
function getCurrentTreeDifficultyNode():TreeViewNode
function getCurrentTreeDifficultyNode(?treeView:TreeView = null):TreeViewNode
{
var treeView:TreeView = findComponent('difficultyToolboxTree');
if (treeView == null)
{
var difficultyToolbox:CollapsibleDialog = ChartEditorToolboxHandler.getToolbox(this, CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
if (difficultyToolbox == null) return null;
if (treeView == null) return null;
treeView = difficultyToolbox.findComponent('difficultyToolboxTree');
if (treeView == null) return null;
}
var result:TreeViewNode = treeView.findNodeByPath('stv_song/stv_variation_$selectedVariation/stv_difficulty_${selectedVariation}_$selectedDifficulty',
'id');
if (result == null) return null;
return result;
}
/**
* Called when selecting a tree element in the Difficulty toolbox.
* @param event The click event.
*/
function onChangeTreeDifficulty(event:UIEvent):Void
{
// Get the newly selected node.
@ -2962,7 +3127,7 @@ class ChartEditorState extends HaxeUIState
{
trace('No target node!');
// Reset the user's selection.
treeView.selectedNode = getCurrentTreeDifficultyNode();
treeView.selectedNode = getCurrentTreeDifficultyNode(treeView);
return;
}
@ -2974,19 +3139,56 @@ class ChartEditorState extends HaxeUIState
if (variation != null && difficulty != null)
{
trace('Changing difficulty to $variation:$difficulty');
trace('Changing difficulty to "$variation:$difficulty"');
selectedVariation = variation;
selectedDifficulty = difficulty;
// refreshDifficultyTreeSelection(treeView);
refreshSongMetadataToolbox();
}
// case 'song':
// case 'variation':
default:
// Reset the user's selection.
trace('Selected wrong node type, resetting selection.');
treeView.selectedNode = getCurrentTreeDifficultyNode();
treeView.selectedNode = getCurrentTreeDifficultyNode(treeView);
refreshSongMetadataToolbox();
}
}
/**
* When the difficulty changes, update the song metadata toolbox to reflect the new data.
*/
function refreshSongMetadataToolbox():Void
{
var toolbox:CollapsibleDialog = ChartEditorToolboxHandler.getToolbox(this, CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
var inputSongName:TextField = toolbox.findComponent('inputSongName', TextField);
inputSongName.value = currentSongMetadata.songName;
var inputSongArtist:TextField = toolbox.findComponent('inputSongArtist', TextField);
inputSongArtist.value = currentSongMetadata.artist;
var inputStage:DropDown = toolbox.findComponent('inputStage', DropDown);
inputStage.value = currentSongMetadata.playData.stage;
var inputNoteSkin:DropDown = toolbox.findComponent('inputNoteSkin', DropDown);
inputNoteSkin.value = currentSongMetadata.playData.noteSkin;
var inputBPM:NumberStepper = toolbox.findComponent('inputBPM', NumberStepper);
inputBPM.value = currentSongMetadata.timeChanges[0].bpm;
var labelScrollSpeed:Label = toolbox.findComponent('labelScrollSpeed', Label);
labelScrollSpeed.text = 'Scroll Speed: ${currentSongChartScrollSpeed}x';
var inputScrollSpeed:Slider = toolbox.findComponent('inputScrollSpeed', Slider);
inputScrollSpeed.value = currentSongChartScrollSpeed;
var frameVariation:Frame = toolbox.findComponent('frameVariation', Frame);
frameVariation.text = 'Variation: ${selectedVariation.toTitleCase()}';
var frameDifficulty:Frame = toolbox.findComponent('frameDifficulty', Frame);
frameDifficulty.text = 'Difficulty: ${selectedDifficulty.toTitleCase()}';
}
function addDifficulty(variation:String):Void {}
function addVariation(variationId:String):Void

View file

@ -1,5 +1,7 @@
package funkin.ui.debug.charting;
import haxe.ui.containers.TreeView;
import haxe.ui.containers.TreeViewNode;
import funkin.play.character.BaseCharacter.CharacterType;
import funkin.play.event.SongEvent;
import funkin.play.event.SongEventData;
@ -17,6 +19,7 @@ import haxe.ui.containers.Box;
import haxe.ui.containers.Grid;
import haxe.ui.containers.Group;
import haxe.ui.containers.VBox;
import haxe.ui.containers.Frame;
import haxe.ui.containers.dialogs.CollapsibleDialog;
import haxe.ui.containers.dialogs.Dialog.DialogButton;
import haxe.ui.containers.dialogs.Dialog.DialogEvent;
@ -36,6 +39,7 @@ enum ChartEditorToolMode
/**
* Static functions which handle building themed UI elements for a provided ChartEditorState.
*/
@:allow(funkin.ui.debug.charting.ChartEditorState)
class ChartEditorToolboxHandler
{
public static function setToolboxState(state:ChartEditorState, id:String, shown:Bool):Void
@ -59,6 +63,29 @@ class ChartEditorToolboxHandler
if (toolbox != null)
{
toolbox.showDialog(false);
switch (id)
{
case ChartEditorState.CHART_EDITOR_TOOLBOX_TOOLS_LAYOUT:
onShowToolboxTools(state, toolbox);
case ChartEditorState.CHART_EDITOR_TOOLBOX_NOTEDATA_LAYOUT:
onShowToolboxNoteData(state, toolbox);
case ChartEditorState.CHART_EDITOR_TOOLBOX_EVENTDATA_LAYOUT:
onShowToolboxEventData(state, toolbox);
case ChartEditorState.CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT:
onShowToolboxDifficulty(state, toolbox);
case ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT:
onShowToolboxMetadata(state, toolbox);
case ChartEditorState.CHART_EDITOR_TOOLBOX_CHARACTERS_LAYOUT:
onShowToolboxCharacters(state, toolbox);
case ChartEditorState.CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT:
onShowToolboxPlayerPreview(state, toolbox);
case ChartEditorState.CHART_EDITOR_TOOLBOX_OPPONENT_PREVIEW_LAYOUT:
onShowToolboxOpponentPreview(state, toolbox);
default:
// This happens if you try to load an unknown layout.
trace('ChartEditorToolboxHandler.showToolbox() - Unknown toolbox ID: $id');
}
}
else
{
@ -75,6 +102,29 @@ class ChartEditorToolboxHandler
if (toolbox != null)
{
toolbox.hideDialog(DialogButton.CANCEL);
switch (id)
{
case ChartEditorState.CHART_EDITOR_TOOLBOX_TOOLS_LAYOUT:
onHideToolboxTools(state, toolbox);
case ChartEditorState.CHART_EDITOR_TOOLBOX_NOTEDATA_LAYOUT:
onHideToolboxNoteData(state, toolbox);
case ChartEditorState.CHART_EDITOR_TOOLBOX_EVENTDATA_LAYOUT:
onHideToolboxEventData(state, toolbox);
case ChartEditorState.CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT:
onHideToolboxDifficulty(state, toolbox);
case ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT:
onHideToolboxMetadata(state, toolbox);
case ChartEditorState.CHART_EDITOR_TOOLBOX_CHARACTERS_LAYOUT:
onHideToolboxCharacters(state, toolbox);
case ChartEditorState.CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT:
onHideToolboxPlayerPreview(state, toolbox);
case ChartEditorState.CHART_EDITOR_TOOLBOX_OPPONENT_PREVIEW_LAYOUT:
onHideToolboxOpponentPreview(state, toolbox);
default:
// This happens if you try to load an unknown layout.
trace('ChartEditorToolboxHandler.hideToolbox() - Unknown toolbox ID: $id');
}
}
else
{
@ -186,6 +236,10 @@ class ChartEditorToolboxHandler
return toolbox;
}
static function onShowToolboxTools(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
static function onHideToolboxTools(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
static function buildToolboxNoteDataLayout(state:ChartEditorState):CollapsibleDialog
{
var toolbox:CollapsibleDialog = cast state.buildComponent(ChartEditorState.CHART_EDITOR_TOOLBOX_NOTEDATA_LAYOUT);
@ -230,6 +284,10 @@ class ChartEditorToolboxHandler
return toolbox;
}
static function onShowToolboxNoteData(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
static function onHideToolboxNoteData(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
static function buildToolboxEventDataLayout(state:ChartEditorState):CollapsibleDialog
{
var toolbox:CollapsibleDialog = cast state.buildComponent(ChartEditorState.CHART_EDITOR_TOOLBOX_EVENTDATA_LAYOUT);
@ -275,6 +333,10 @@ class ChartEditorToolboxHandler
return toolbox;
}
static function onShowToolboxEventData(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
static function onHideToolboxEventData(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
static function buildEventDataFormFromSchema(state:ChartEditorState, target:Box, schema:SongEventSchema):Void
{
trace(schema);
@ -405,6 +467,18 @@ class ChartEditorToolboxHandler
return toolbox;
}
static function onShowToolboxDifficulty(state:ChartEditorState, toolbox:CollapsibleDialog):Void
{
// Update the selected difficulty when reopening the toolbox.
var treeView:TreeView = toolbox.findComponent('difficultyToolboxTree');
if (treeView == null) return;
treeView.selectedNode = state.getCurrentTreeDifficultyNode(treeView);
trace('selected node: ${treeView.selectedNode}');
}
static function onHideToolboxDifficulty(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
static function buildToolboxMetadataLayout(state:ChartEditorState):CollapsibleDialog
{
var toolbox:CollapsibleDialog = cast state.buildComponent(ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
@ -464,7 +538,7 @@ class ChartEditorToolboxHandler
var inputNoteSkin:DropDown = toolbox.findComponent('inputNoteSkin', DropDown);
inputNoteSkin.onChange = function(event:UIEvent) {
if (event.data.id == null) return;
if ((event?.data?.id ?? null) == null) return;
state.currentSongMetadata.playData.noteSkin = event.data.id;
};
inputNoteSkin.value = state.currentSongMetadata.playData.noteSkin;
@ -489,6 +563,8 @@ class ChartEditorToolboxHandler
};
inputBPM.value = state.currentSongMetadata.timeChanges[0].bpm;
var labelScrollSpeed:Label = toolbox.findComponent('labelScrollSpeed', Label);
var inputScrollSpeed:Slider = toolbox.findComponent('inputScrollSpeed', Slider);
inputScrollSpeed.onChange = function(event:UIEvent) {
var valid:Bool = event.target.value != null && event.target.value > 0;
@ -502,12 +578,27 @@ class ChartEditorToolboxHandler
{
state.currentSongChartScrollSpeed = 1.0;
}
labelScrollSpeed.text = 'Scroll Speed: ${state.currentSongChartScrollSpeed}x';
};
inputScrollSpeed.value = state.currentSongChartData.scrollSpeed;
inputScrollSpeed.value = state.currentSongChartScrollSpeed;
labelScrollSpeed.text = 'Scroll Speed: ${state.currentSongChartScrollSpeed}x';
var frameVariation:Frame = toolbox.findComponent('frameVariation', Frame);
frameVariation.text = 'Variation: ${state.selectedVariation.toTitleCase()}';
var frameDifficulty:Frame = toolbox.findComponent('frameDifficulty', Frame);
frameDifficulty.text = 'Difficulty: ${state.selectedDifficulty.toTitleCase()}';
return toolbox;
}
static function onShowToolboxMetadata(state:ChartEditorState, toolbox:CollapsibleDialog):Void
{
state.refreshSongMetadataToolbox();
}
static function onHideToolboxMetadata(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
static function buildToolboxCharactersLayout(state:ChartEditorState):CollapsibleDialog
{
var toolbox:CollapsibleDialog = cast state.buildComponent(ChartEditorState.CHART_EDITOR_TOOLBOX_CHARACTERS_LAYOUT);
@ -525,6 +616,10 @@ class ChartEditorToolboxHandler
return toolbox;
}
static function onShowToolboxCharacters(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
static function onHideToolboxCharacters(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
static function buildToolboxPlayerPreviewLayout(state:ChartEditorState):CollapsibleDialog
{
var toolbox:CollapsibleDialog = cast state.buildComponent(ChartEditorState.CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT);
@ -549,6 +644,10 @@ class ChartEditorToolboxHandler
return toolbox;
}
static function onShowToolboxPlayerPreview(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
static function onHideToolboxPlayerPreview(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
static function buildToolboxOpponentPreviewLayout(state:ChartEditorState):CollapsibleDialog
{
var toolbox:CollapsibleDialog = cast state.buildComponent(ChartEditorState.CHART_EDITOR_TOOLBOX_OPPONENT_PREVIEW_LAYOUT);
@ -572,4 +671,8 @@ class ChartEditorToolboxHandler
return toolbox;
}
static function onShowToolboxOpponentPreview(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
static function onHideToolboxOpponentPreview(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
}

View file

@ -36,7 +36,16 @@ class SerializerUtil
*/
public static function fromJSON(input:String):Dynamic
{
return Json.parse(input);
try
{
return Json.parse(input);
}
catch (e)
{
trace('An error occurred while parsing JSON from string data');
trace(e);
return null;
}
}
/**

View file

@ -6,6 +6,19 @@ import flixel.util.FlxSort;
#end
import funkin.play.notes.NoteSprite;
/**
* A set of functions related to sorting.
*
* NOTE: `Array.sort()` takes a function `(x, y) -> Int`.
* If the objects are in the correct order (x before y), return a negative value.
* If the objects need to be swapped (y before x), return a negative value.
* If the objects are equal, return 0.
*
* NOTE: `Array.sort()` does NOT guarantee that the order of equal elements. `haxe.ds.ArraySort.sort()` does guarantee this.
* NOTE: `Array.sort()` may not be the most efficient sorting algorithm for all use cases (especially if the array is known to be mostly sorted).
* You may consider using one of the functions in `funkin.util.tools.ArraySortTools` instead.
* NOTE: Both sort functions modify the array in-place. You may consider using `Reflect.copy()` to make a copy of the array before sorting.
*/
class SortUtil
{
/**
@ -30,8 +43,10 @@ class SortUtil
/**
* Sort predicate for sorting strings alphabetically.
* @param a The first string to compare.
* @param b The second string to compare.
*/
public static function alphabetically(a:String, b:String)
public static function alphabetically(a:String, b:String):Int
{
a = a.toUpperCase();
b = b.toUpperCase();
@ -39,4 +54,39 @@ class SortUtil
// Sort alphabetically. Yes that's how this works.
return a == b ? 0 : a > b ? 1 : -1;
}
/**
* Sort predicate which sorts two strings alphabetically, but prioritizes a specific string first.
* Example usage: `array.sort(defaultThenAlphabetical.bind('test'))` will sort the array so that the string 'test' is first.
* @param a The first string to compare.
* @param b The second string to compare.
* @param defaultValue The value to prioritize.
*/
public static function defaultThenAlphabetically(defaultValue:String, a:String, b:String):Int
{
if (a == b) return 0;
if (a == defaultValue) return -1;
if (b == defaultValue) return 1;
return alphabetically(a, b);
}
/**
* Sort predicate which sorts two strings alphabetically, but prioritizes a specific string first.
* Example usage: `array.sort(defaultsThenAlphabetical.bind(['test']))` will sort the array so that the string 'test' is first.
* @param a The first string to compare.
* @param b The second string to compare.
* @param defaultValues The values to prioritize.
*/
public static function defaultsThenAlphabetically(defaultValues:Array<String>, a:String, b:String):Int
{
if (a == b) return 0;
if (defaultValues.contains(a) && defaultValues.contains(b))
{
// Sort by index in defaultValues
return defaultValues.indexOf(a) - defaultValues.indexOf(b);
};
if (defaultValues.contains(a)) return -1;
if (defaultValues.contains(b)) return 1;
return alphabetically(a, b);
}
}