From d03a2f015795ef01155b861ba47a5a6387fe2fdf Mon Sep 17 00:00:00 2001 From: EliteMasterEric <ericmyllyoja@gmail.com> Date: Mon, 14 Aug 2023 23:13:12 -0400 Subject: [PATCH] WIP on improving the difficulty toolbox --- .../ui/debug/charting/ChartEditorState.hx | 172 ++++++++++++++++-- .../charting/ChartEditorToolboxHandler.hx | 89 +++++++++ source/funkin/util/SortUtil.hx | 10 +- 3 files changed, 256 insertions(+), 15 deletions(-) diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index da8dd2d86..f925d58ca 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -722,11 +722,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 +1003,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 +1279,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 +1469,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 +1618,7 @@ class ChartEditorState extends HaxeUIState handleToolboxes(); handlePlaybar(); handlePlayhead(); + // handleNotePreview(); handleFileKeybinds(); handleEditKeybinds(); @@ -2755,7 +2790,95 @@ 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) + { + // 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(); + } + else + { + // Go to previous difficulty in this variation. + var prevDifficulty = availableDifficulties[currentDifficultyIndex - 1]; + selectedDifficulty = prevDifficulty; + + refreshDifficultyTreeSelection(); + } + } + else + { + // 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; + } + else + { + // Go to next difficulty in this variation. + var nextDifficulty = availableDifficulties[currentDifficultyIndex + 1]; + selectedDifficulty = nextDifficulty; + } + } + } /** * Handle keybinds for the Test menu items. @@ -2801,7 +2924,8 @@ 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; var variations = Reflect.copy(availableVariations); @@ -2831,10 +2955,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. @@ -2941,13 +3081,16 @@ class ChartEditorState extends HaxeUIState } } - function getCurrentTreeDifficultyNode():TreeViewNode + function getCurrentTreeDifficultyNode(?treeView:TreeView = null):TreeViewNode { - var difficultyToolbox:CollapsibleDialog = ChartEditorToolboxHandler.getToolbox(this, CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT); - if (difficultyToolbox == null) return null; + if (treeView == null) + { + var difficultyToolbox:CollapsibleDialog = ChartEditorToolboxHandler.getToolbox(this, CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT); + if (difficultyToolbox == null) return null; - var treeView:TreeView = difficultyToolbox.findComponent('difficultyToolboxTree'); - 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'); @@ -2956,6 +3099,10 @@ class ChartEditorState extends HaxeUIState 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. @@ -2966,7 +3113,7 @@ class ChartEditorState extends HaxeUIState { trace('No target node!'); // Reset the user's selection. - treeView.selectedNode = getCurrentTreeDifficultyNode(); + treeView.selectedNode = getCurrentTreeDifficultyNode(treeView); return; } @@ -2981,13 +3128,14 @@ class ChartEditorState extends HaxeUIState trace('Changing difficulty to $variation:$difficulty'); selectedVariation = variation; selectedDifficulty = difficulty; + // refreshDifficultyTreeSelection(treeView); } // case 'song': // case 'variation': default: // Reset the user's selection. trace('Selected wrong node type, resetting selection.'); - treeView.selectedNode = getCurrentTreeDifficultyNode(); + treeView.selectedNode = getCurrentTreeDifficultyNode(treeView); } } diff --git a/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx b/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx index d35323f1b..cd3172ebf 100644 --- a/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx +++ b/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx @@ -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; @@ -36,6 +38,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 +62,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 +101,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 +235,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 +283,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 +332,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 +466,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); @@ -512,6 +585,10 @@ class ChartEditorToolboxHandler return toolbox; } + static function onShowToolboxMetadata(state:ChartEditorState, toolbox:CollapsibleDialog):Void {} + + 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); @@ -529,6 +606,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); @@ -553,6 +634,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); @@ -576,4 +661,8 @@ class ChartEditorToolboxHandler return toolbox; } + + static function onShowToolboxOpponentPreview(state:ChartEditorState, toolbox:CollapsibleDialog):Void {} + + static function onHideToolboxOpponentPreview(state:ChartEditorState, toolbox:CollapsibleDialog):Void {} } diff --git a/source/funkin/util/SortUtil.hx b/source/funkin/util/SortUtil.hx index 082de4b41..6f3b9c0fb 100644 --- a/source/funkin/util/SortUtil.hx +++ b/source/funkin/util/SortUtil.hx @@ -47,7 +47,7 @@ class SortUtil * 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. Defaults to `default`. + * @param defaultValue The value to prioritize. */ public static function defaultThenAlphabetically(a:String, b:String, defaultValue:String):Int { @@ -62,12 +62,16 @@ class SortUtil * 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 defaultValue The value to prioritize. Defaults to `default`. + * @param defaultValues The values to prioritize. */ public static function defaultsThenAlphabetically(a:String, b:String, defaultValues:Array<String>):Int { if (a == b) return 0; - if (defaultValues.contains(a) && defaultValues.contains(b)) return alphabetically(a, b); + 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);