package funkin.ui.debug.charting.util; #if FEATURE_CHART_EDITOR import funkin.data.character.CharacterData; import funkin.data.character.CharacterData.CharacterDataParser; import funkin.data.event.SongEventRegistry; import funkin.data.freeplay.album.AlbumRegistry; import funkin.data.notestyle.NoteStyleRegistry; import funkin.data.song.SongData.SongTimeChange; import funkin.data.stage.StageRegistry; import funkin.data.stickers.StickerRegistry; import haxe.ui.components.DropDown; import funkin.play.stage.Stage; import funkin.play.notes.notekind.NoteKind; import funkin.play.notes.notekind.NoteKindManager; import funkin.play.character.BaseCharacter.CharacterType; import funkin.play.event.SongEvent; import funkin.play.notes.notestyle.NoteStyle; import funkin.ui.freeplay.Album; import funkin.ui.transition.stickers.StickerPack; /** * Functions for populating dropdowns based on game data. * These get used by both dialogs and toolboxes so they're in their own class to prevent "reaching over." */ @:nullSafety @:access(funkin.ui.debug.charting.ChartEditorState) class ChartEditorDropdowns { /** * Populate a dropdown with a list of characters. */ public static function populateDropdownWithCharacters(dropDown:DropDown, charType:CharacterType, startingCharId:String):DropDownEntry { dropDown.dataSource.clear(); // TODO: Filter based on charType. var charIds:Array = CharacterDataParser.listCharacterIds(); var returnValue:DropDownEntry = switch (charType) { case BF: {id: "bf", text: "Boyfriend"}; case DAD: {id: "dad", text: "Daddy Dearest"}; default: { dropDown.dataSource.add({id: "none", text: ""}); {id: "none", text: "None"}; } } for (charId in charIds) { var character:Null = CharacterDataParser.fetchCharacterData(charId); if (character == null) continue; var value = {id: charId, text: character.name}; if (startingCharId == charId) returnValue = value; dropDown.dataSource.add(value); } dropDown.dataSource.sort('text', ASCENDING); return returnValue; } /** * Populate a dropdown with a list of stages. */ public static function populateDropdownWithStages(dropDown:DropDown, startingStageId:String):DropDownEntry { dropDown.dataSource.clear(); var stageIds:Array = StageRegistry.instance.listEntryIds(); var returnValue:DropDownEntry = {id: "mainStage", text: "Main Stage"}; for (stageId in stageIds) { var stage:Null = StageRegistry.instance.fetchEntry(stageId); if (stage == null) continue; var value = {id: stage.id, text: stage.stageName}; if (startingStageId == stageId) returnValue = value; dropDown.dataSource.add(value); } dropDown.dataSource.sort('text', ASCENDING); return returnValue; } /** * Populate a dropdown with a list of time changes. */ public static function populateDropdownWithTimeChanges(dropDown:DropDown, timeChanges:Array, startingTimeChange:Int = 0):DropDownEntry { dropDown.dataSource.clear(); var returnValue:DropDownEntry = { id: "0", text: '${timeChanges[0].timeStamp} ms : BPM: ${timeChanges[0].bpm} in ${timeChanges[0].timeSignatureNum}/${timeChanges[0].timeSignatureDen}' }; for (index in 0...timeChanges.length) { var value = { id: '$index', text: '${timeChanges[index].timeStamp} ms : BPM: ${timeChanges[index].bpm} in ${timeChanges[index].timeSignatureNum}/${timeChanges[index].timeSignatureDen}' }; if (startingTimeChange == index) returnValue = value; dropDown.dataSource.add(value); } dropDown.dataSource.sort('id', ASCENDING); return returnValue; } /** * Populate a dropdown with a list of song events. */ public static function populateDropdownWithSongEvents(dropDown:DropDown, startingEventId:String):DropDownEntry { dropDown.dataSource.clear(); var returnValue:DropDownEntry = {id: "FocusCamera", text: "Focus Camera"}; var songEvents:Array = SongEventRegistry.listEvents(); for (event in songEvents) { var value = {id: event.id, text: event.getTitle()}; if (startingEventId == event.id) returnValue = value; dropDown.dataSource.add(value); } dropDown.dataSource.sort('text', ASCENDING); return returnValue; } /** * Given the ID of a dropdown element, find the corresponding entry in the dropdown's dataSource. */ public static function findDropdownElement(id:String, dropDown:DropDown):Null { // Attempt to find the entry. for (entryIndex in 0...dropDown.dataSource.size) { var entry = dropDown.dataSource.get(entryIndex); if (entry.id == id) return entry; } // Not found. return null; } /** * Populate a dropdown with a list of note styles. */ public static function populateDropdownWithNoteStyles(dropDown:DropDown, startingStyleId:String):DropDownEntry { dropDown.dataSource.clear(); var noteStyleIds:Array = NoteStyleRegistry.instance.listEntryIds(); var returnValue:DropDownEntry = {id: "funkin", text: "Funkin'"}; for (noteStyleId in noteStyleIds) { var noteStyle:Null = NoteStyleRegistry.instance.fetchEntry(noteStyleId); if (noteStyle == null) continue; // check if the note style has all necessary assets (strums, notes, holdNotes) if (noteStyle._data?.assets?.noteStrumline == null || noteStyle._data?.assets?.note == null || noteStyle._data?.assets?.holdNote == null) { continue; } var value = {id: noteStyleId, text: noteStyle.getName()}; if (startingStyleId == noteStyleId) returnValue = value; dropDown.dataSource.add(value); } dropDown.dataSource.sort('text', ASCENDING); return returnValue; } /** * Populate a dropdown with a list of albums. */ public static function populateDropdownWithAlbums(dropDown:DropDown, startingAlbumId:Null):DropDownEntry { startingAlbumId = startingAlbumId ?? Constants.DEFAULT_ALBUM_ID; dropDown.dataSource.clear(); var albumIds:Array = AlbumRegistry.instance.listEntryIds(); var returnValue:DropDownEntry = {id: "volume1", text: "Volume 1"}; for (albumId in albumIds) { var album:Null = AlbumRegistry.instance.fetchEntry(albumId); if (album == null) continue; // Check if the album has all necessary assets (art and title) if (album._data?.albumArtAsset == null || album._data?.albumTitleAsset == null) continue; var value = {id: albumId, text: album.getAlbumName()}; if (startingAlbumId == albumId) returnValue = value; dropDown.dataSource.add(value); } dropDown.dataSource.sort('text', ASCENDING); return returnValue; } /** * Populate a dropdown with a list of sticker packs. */ public static function populateDropdownWithStickerPacks(dropDown:DropDown, startingStickerPackId:Null):DropDownEntry { startingStickerPackId = startingStickerPackId ?? Constants.DEFAULT_STICKER_PACK; dropDown.dataSource.clear(); var stickerPackIds:Array = StickerRegistry.instance.listEntryIds(); var returnValue:DropDownEntry = {id: "default", text: "Default"}; for (stickerPackId in stickerPackIds) { var stickerPack:Null = StickerRegistry.instance.fetchEntry(stickerPackId); if (stickerPack == null) continue; // Check if the sticker has all necessary assets (stickers) if (stickerPack._data?.stickers == null) continue; var value = {id: stickerPackId, text: stickerPack.getStickerPackName()}; if (startingStickerPackId == stickerPackId) returnValue = value; dropDown.dataSource.add(value); } dropDown.dataSource.sort('text', ASCENDING); return returnValue; } /** * Populate the provided dropdown with the list of available note kinds. * @param dropDown The dropdown to populate * @param startingKindId The note kind to pre-select. * @return The dropdown entry for the pre-selected note kind. */ public static function populateDropdownWithNoteKinds(dropDown:DropDown, startingKindId:String):DropDownEntry { dropDown.dataSource.clear(); dropDown.dataSource.add({id: '', text: 'Default'}); dropDown.dataSource.add({id: '~CUSTOM~', text: 'Custom'}); var customNoteKinds:Array = NoteKindManager.listNoteKinds(); for (noteKindId in customNoteKinds) { dropDown.dataSource.add(lookupNoteKind(noteKindId)); } dropDown.dataSource.sort('id', ASCENDING); return lookupNoteKind(startingKindId); } /** * Generates the dropdown entry for the provided note kind ID. * @param noteKindId The note kind ID * @return The dropdown entry */ public static function lookupNoteKind(noteKindId:Null):DropDownEntry { if (noteKindId == null) return lookupNoteKind(''); if (noteKindId == '') return {id: '', text: 'Default'}; var noteKind:Null = NoteKindManager.getNoteKind(noteKindId); var noteKindDesc:Null = noteKind?.description ?? noteKindId; if (noteKind != null) return {id: noteKindId ?? 'unknown', text: noteKindDesc ?? 'unknown'}; return {id: '~CUSTOM~', text: 'Custom'}; } /** * Populate a dropdown with a list of song variations. */ public static function populateDropdownWithVariations(dropDown:DropDown, state:ChartEditorState, includeNone:Bool = true):DropDownEntry { dropDown.dataSource.clear(); var variationIds:Array = state.availableVariations; if (includeNone) { dropDown.dataSource.add({id: "none", text: ""}); } var returnValue:DropDownEntry = includeNone ? ({id: "none", text: ""}) : ({id: "default", text: "Default"}); for (variationId in variationIds) { dropDown.dataSource.add({id: variationId, text: variationId.toTitleCase()}); } dropDown.dataSource.sort('text', ASCENDING); return returnValue; } } /** * An entry in a dropdown. */ typedef DropDownEntry = { id:String, text:String }; #end