From 68c85d9214bc8e6d36bdb7af019855618a78b356 Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Thu, 10 Aug 2023 15:34:40 -0400 Subject: [PATCH 1/5] Made the currently-unused Divisions field optional. --- source/funkin/play/song/Song.hx | 2 +- source/funkin/play/song/SongData.hx | 4 ++-- source/funkin/play/song/SongValidator.hx | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx index 398c28753..ec89d8706 100644 --- a/source/funkin/play/song/Song.hx +++ b/source/funkin/play/song/Song.hx @@ -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 = SongValidator.DEFAULT_DIVISIONS; public var looped:Bool = SongValidator.DEFAULT_LOOPED; public var generatedBy:String = SongValidator.DEFAULT_GENERATEDBY; diff --git a/source/funkin/play/song/SongData.hx b/source/funkin/play/song/SongData.hx index 6f2475cf9..fbd7e3383 100644 --- a/source/funkin/play/song/SongData.hx +++ b/source/funkin/play/song/SongData.hx @@ -269,7 +269,7 @@ typedef RawSongMetadata = var songName:String; var artist:String; var timeFormat:SongTimeFormat; - var divisions:Int; + var divisions:Null; // Optional field var timeChanges:Array; var looped:Bool; var playData:SongPlayData; @@ -292,7 +292,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: diff --git a/source/funkin/play/song/SongValidator.hx b/source/funkin/play/song/SongValidator.hx index 936ad46f7..d91dda1d9 100644 --- a/source/funkin/play/song/SongValidator.hx +++ b/source/funkin/play/song/SongValidator.hx @@ -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 = null; public static final DEFAULT_LOOPED:Bool = false; public static final DEFAULT_STAGE:String = "mainStage"; public static final DEFAULT_SCROLLSPEED:Float = 1.0; From c6a1f5ffeac51686b23b4764edeb569a173e910e Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Fri, 11 Aug 2023 14:00:38 -0400 Subject: [PATCH 2/5] Work on fixing issues with difficulty/variation handling in charts --- source/funkin/MainMenuState.hx | 2 +- source/funkin/play/song/SongData.hx | 17 ++++----- .../charting/ChartEditorDialogHandler.hx | 7 +++- .../ui/debug/charting/ChartEditorState.hx | 10 ++++-- .../charting/ChartEditorToolboxHandler.hx | 6 +++- source/funkin/util/SerializerUtil.hx | 11 +++++- source/funkin/util/SortUtil.hx | 35 ++++++++++++++++++- 7 files changed, 70 insertions(+), 18 deletions(-) diff --git a/source/funkin/MainMenuState.hx b/source/funkin/MainMenuState.hx index fc493ef4b..5a69ea83a 100644 --- a/source/funkin/MainMenuState.hx +++ b/source/funkin/MainMenuState.hx @@ -54,7 +54,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')); } diff --git a/source/funkin/play/song/SongData.hx b/source/funkin/play/song/SongData.hx index fbd7e3383..938ee0708 100644 --- a/source/funkin/play/song/SongData.hx +++ b/source/funkin/play/song/SongData.hx @@ -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 = []; - 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; diff --git a/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx b/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx index 6641a16c0..43e8ac96c 100644 --- a/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx +++ b/source/funkin/ui/debug/charting/ChartEditorDialogHandler.hx @@ -115,7 +115,12 @@ class ChartEditorDialogHandler if (songData == null) continue; - var songName:String = songData.getDifficulty().songName; + var songName:Null = 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; diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index 26b001d7e..da8dd2d86 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -2804,7 +2804,10 @@ class ChartEditorState extends HaxeUIState var treeSong:TreeViewNode = treeView.addNode({id: 'stv_song', text: 'S: $currentSongName', icon: 'haxeui-core/styles/default/haxeui_tiny.png'}); 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); @@ -2940,13 +2943,14 @@ class ChartEditorState extends HaxeUIState function getCurrentTreeDifficultyNode():TreeViewNode { - var treeView:TreeView = findComponent('difficultyToolboxTree'); + 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; var result:TreeViewNode = treeView.findNodeByPath('stv_song/stv_variation_$selectedVariation/stv_difficulty_${selectedVariation}_$selectedDifficulty', 'id'); - if (result == null) return null; return result; diff --git a/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx b/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx index 5cace2ff6..d35323f1b 100644 --- a/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx +++ b/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx @@ -489,6 +489,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,8 +504,10 @@ 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'; return toolbox; } diff --git a/source/funkin/util/SerializerUtil.hx b/source/funkin/util/SerializerUtil.hx index 9452b7785..d731aee8f 100644 --- a/source/funkin/util/SerializerUtil.hx +++ b/source/funkin/util/SerializerUtil.hx @@ -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; + } } /** diff --git a/source/funkin/util/SortUtil.hx b/source/funkin/util/SortUtil.hx index 61418c299..082de4b41 100644 --- a/source/funkin/util/SortUtil.hx +++ b/source/funkin/util/SortUtil.hx @@ -30,8 +30,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 +41,35 @@ 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. Defaults to `default`. + */ + public static function defaultThenAlphabetically(a:String, b:String, defaultValue: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 defaultValue The value to prioritize. Defaults to `default`. + */ + public static function defaultsThenAlphabetically(a:String, b:String, defaultValues:Array):Int + { + if (a == b) return 0; + if (defaultValues.contains(a) && defaultValues.contains(b)) return alphabetically(a, b); + if (defaultValues.contains(a)) return 1; + if (defaultValues.contains(b)) return -1; + return alphabetically(a, b); + } } From 9c7aec44855161e47aa9837927fe748b9331aa4a Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 14 Aug 2023 23:12:30 -0400 Subject: [PATCH 3/5] Asset redirect fix (now disabled for Github Actions builds) --- .github/workflows/build-shit.yml | 2 +- Project.xml | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-shit.yml b/.github/workflows/build-shit.yml index ddd6e8be0..794457917 100644 --- a/.github/workflows/build-shit.yml +++ b/.github/workflows/build-shit.yml @@ -51,7 +51,7 @@ jobs: - uses: ./.github/actions/setup-haxeshit - name: Build game run: | - haxelib run lime build windows -debug + haxelib run lime build windows -debug -DNO_REDIRECT_ASSETS_FOLDER dir - uses: ./.github/actions/upload-itch with: diff --git a/Project.xml b/Project.xml index f34c9bc06..8dbb43618 100644 --- a/Project.xml +++ b/Project.xml @@ -181,7 +181,8 @@ -
+ +
+ From d03a2f015795ef01155b861ba47a5a6387fe2fdf Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Mon, 14 Aug 2023 23:13:12 -0400 Subject: [PATCH 4/5] 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; + /** + * Retrieves the list of variations for the current song. + */ var availableVariations(get, null):Array; function get_availableVariations():Array { - return [for (x in songMetadata.keys()) x]; + var variations:Array = [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; + + function get_availableDifficulties():Array + { + return songMetadata.get(selectedVariation).playData.difficulties; + } + + /** + * Retrieves the list of difficulties for ALL variations of the current song. + */ + var allDifficulties(get, null):Array; + + function get_allDifficulties():Array + { + var result:Array> = [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):FlxTypedSignalVoid> = new FlxTypedSignalVoid>(); /** * 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):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); From 8732adc144af3f49f76a61e50002b679d962e6ff Mon Sep 17 00:00:00 2001 From: EliteMasterEric Date: Tue, 15 Aug 2023 16:08:12 -0400 Subject: [PATCH 5/5] Difficulty selection now updates metadata toolbox --- .../ui/debug/charting/ChartEditorState.hx | 56 ++++++++++++++++++- .../charting/ChartEditorToolboxHandler.hx | 14 ++++- source/funkin/util/SortUtil.hx | 25 +++++++-- 3 files changed, 84 insertions(+), 11 deletions(-) diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx index f925d58ca..c26fb8998 100644 --- a/source/funkin/ui/debug/charting/ChartEditorState.hx +++ b/source/funkin/ui/debug/charting/ChartEditorState.hx @@ -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; @@ -2834,7 +2838,8 @@ class ChartEditorState extends HaxeUIState if (change < 0) { - // Decrement difficulty. + trace('Decrement difficulty.'); + // If we reached this point, we are not at the lowest difficulty. if (isFirstDiffInVariation) { @@ -2847,6 +2852,7 @@ class ChartEditorState extends HaxeUIState selectedDifficulty = prevDifficulty; refreshDifficultyTreeSelection(); + refreshSongMetadataToolbox(); } else { @@ -2855,11 +2861,13 @@ class ChartEditorState extends HaxeUIState selectedDifficulty = prevDifficulty; refreshDifficultyTreeSelection(); + refreshSongMetadataToolbox(); } } else { - // Increment difficulty. + trace('Increment difficulty.'); + // If we reached this point, we are not at the highest difficulty. if (isLastDiffInVariation) { @@ -2870,12 +2878,18 @@ class ChartEditorState extends HaxeUIState 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(); } } } @@ -3125,10 +3139,11 @@ 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': @@ -3136,9 +3151,44 @@ class ChartEditorState extends HaxeUIState // Reset the user's selection. trace('Selected wrong node type, resetting selection.'); 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 diff --git a/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx b/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx index cd3172ebf..a6e230f6e 100644 --- a/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx +++ b/source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx @@ -19,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; @@ -537,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; @@ -582,10 +583,19 @@ class ChartEditorToolboxHandler 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 {} + static function onShowToolboxMetadata(state:ChartEditorState, toolbox:CollapsibleDialog):Void + { + state.refreshSongMetadataToolbox(); + } static function onHideToolboxMetadata(state:ChartEditorState, toolbox:CollapsibleDialog):Void {} diff --git a/source/funkin/util/SortUtil.hx b/source/funkin/util/SortUtil.hx index 6f3b9c0fb..df45e0717 100644 --- a/source/funkin/util/SortUtil.hx +++ b/source/funkin/util/SortUtil.hx @@ -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 { /** @@ -49,11 +62,11 @@ class SortUtil * @param b The second string to compare. * @param defaultValue The value to prioritize. */ - public static function defaultThenAlphabetically(a:String, b:String, defaultValue:String):Int + 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; + if (a == defaultValue) return -1; + if (b == defaultValue) return 1; return alphabetically(a, b); } @@ -64,7 +77,7 @@ class SortUtil * @param b The second string to compare. * @param defaultValues The values to prioritize. */ - public static function defaultsThenAlphabetically(a:String, b:String, defaultValues:Array):Int + public static function defaultsThenAlphabetically(defaultValues:Array, a:String, b:String):Int { if (a == b) return 0; if (defaultValues.contains(a) && defaultValues.contains(b)) @@ -72,8 +85,8 @@ class SortUtil // Sort by index in defaultValues return defaultValues.indexOf(a) - defaultValues.indexOf(b); }; - if (defaultValues.contains(a)) return 1; - if (defaultValues.contains(b)) return -1; + if (defaultValues.contains(a)) return -1; + if (defaultValues.contains(b)) return 1; return alphabetically(a, b); } }