diff --git a/source/funkin/data/Metadata.hx b/source/funkin/data/Metadata.hx index edd1ef48..199bba37 100644 --- a/source/funkin/data/Metadata.hx +++ b/source/funkin/data/Metadata.hx @@ -10,7 +10,12 @@ typedef MetaVariables = ?composers:Array, ?charters:Array, ?artists:Array, - ?coders:Array + ?coders:Array, + + ?displayName:String, + + ?freeplayColor:String, + ?freeplayIcon:String, } // if u want to do to it hardcoded u can just fuckin go var meta:Metadata = {composers: 'penis'}; //blalblalbla diff --git a/source/funkin/states/FreeplayState.hx b/source/funkin/states/FreeplayState.hx index 8b0d4d16..f10ca9f9 100644 --- a/source/funkin/states/FreeplayState.hx +++ b/source/funkin/states/FreeplayState.hx @@ -13,6 +13,8 @@ import flixel.util.FlxColor; import flixel.tweens.FlxTween; import funkin.backend.Difficulty; +import funkin.Mods; +import funkin.data.Metadata; import funkin.states.editors.ChartEditorState; import funkin.data.WeekData; import funkin.states.*; @@ -30,6 +32,10 @@ class FreeplayState extends MusicBeatState public var songs:Array = []; + public var freeplayTabs:Array = []; + + public static var currentTab:Int = 0; + public var selector:FlxText; public static var curSelected:Int = 0; @@ -41,6 +47,8 @@ class FreeplayState extends MusicBeatState public var scoreBG:FlxSprite; public var scoreText:FlxText; public var diffText:FlxText; + public var tabText:FlxText; + public var tabHint:FlxText; public var lerpScore:Int = 0; public var lerpRating:Float = 0; public var intendedScore:Int = 0; @@ -66,39 +74,18 @@ class FreeplayState extends MusicBeatState DiscordClient.changePresence("In the Menus"); - if (WeekData.weeksList.length == 0) + loadFreeplayData(); + + if (WeekData.weeksList.length == 0 && freeplayTabs.length == 0) { CoolUtil.setTransSkip(true, false); persistentUpdate = false; - FlxG.switchState(() -> new FallbackState('Cannot load Freeplay as there are no weeks loaded.', () -> FlxG.switchState(MainMenuState.new))); + FlxG.switchState(() -> new FallbackState('Cannot load Freeplay as there are no weeks or tabs loaded.', () -> FlxG.switchState(MainMenuState.new))); return; } - for (i in 0...WeekData.weeksList.length) - { - if (weekIsLocked(WeekData.weeksList[i])) continue; - - var leWeek:WeekData = WeekData.weeksLoaded.get(WeekData.weeksList[i]); - var leSongs:Array = []; - var leChars:Array = []; - - for (j in 0...leWeek.songs.length) - { - leSongs.push(leWeek.songs[j][0]); - leChars.push(leWeek.songs[j][1]); - } - - WeekData.setDirectoryFromWeek(leWeek); - for (song in leWeek.songs) - { - var colors:Array = song[2]; - if (colors == null || colors.length < 3) - { - colors = [146, 113, 253]; - } - addSong(song[0], i, song[1], FlxColor.fromRGB(colors[0], colors[1], colors[2])); - } - } + if (freeplayTabs.length == 0) loadFreeplayFromWeeks(); + initStateScript(); scriptGroup.set('SongMetadata', SongMetadata); @@ -111,38 +98,12 @@ class FreeplayState extends MusicBeatState grpSongs = new FlxTypedGroup(); add(grpSongs); - for (i in 0...songs.length) - { - var songText:Alphabet = new Alphabet(0, (70 * i) + 30, songs[i].songName, true, false); - songText.isMenuItem = true; - songText.targetY = i; - grpSongs.add(songText); - - if (songText.width > 980) - { - var textScale:Float = 980 / songText.width; - songText.scale.x = textScale; - for (letter in songText.lettersArray) - { - letter.x *= textScale; - letter.offset.x *= textScale; - } - } - - Mods.currentModDirectory = songs[i].folder; - var icon:HealthIcon = new HealthIcon(songs[i].songCharacter); - icon.sprTracker = songText; - - // using a FlxGroup is too much fuss! - iconArray.push(icon); - add(icon); - } - WeekData.setDirectoryFromWeek(); - scoreText = new FlxText(0, 5, FlxG.width - 6, "", 32); scoreText.setFormat(Paths.DEFAULT_FONT, 32, FlxColor.WHITE, RIGHT); - scoreBG = new FlxSprite(scoreText.x - 6, 0).makeGraphic(1, 66, 0xFF000000); + final scoreBGSize = 66 + 33 * (Math.min(freeplayTabs.length, 2)); + + scoreBG = new FlxSprite(scoreText.x - 6, 0).makeScaledGraphic(1, scoreBGSize, 0xFF000000); scoreBG.alpha = 0.6; add(scoreBG); @@ -150,17 +111,21 @@ class FreeplayState extends MusicBeatState diffText.font = scoreText.font; add(diffText); - add(scoreText); + tabText = new FlxText(diffText.x, diffText.y + 28, 0, 24); + tabText.font = scoreText.font; + add(tabText); - if (curSelected >= songs.length) curSelected = 0; - bg.color = songs[curSelected].color; - intendedColor = bg.color; + tabHint = new FlxText(tabText.x, tabText.y + 28, 0, 24); + tabHint.font = scoreText.font; + tabHint.text = "Press TAB to switch tabs."; + tabHint.color = FlxColor.GRAY; + add(tabHint); - if (lastDifficultyName == '') - { - lastDifficultyName = Difficulty.defaultDifficulty; - } - curDifficulty = Math.round(Math.max(0, Difficulty.defaultDifficulties.indexOf(lastDifficultyName))); + if (freeplayTabs.length <= 1) tabHint.kill(); + + if (freeplayTabs.length == 0) tabText.kill(); + + add(scoreText); var textBG:FlxSprite = new FlxSprite(0, FlxG.height - 26).makeGraphic(FlxG.width, 26, 0xFF000000); textBG.alpha = 0.6; @@ -184,9 +149,26 @@ class FreeplayState extends MusicBeatState debugTxt.screenCenter(Y); add(debugTxt); + if (freeplayTabs.length > 0) loadTab(0); + else createSongs(); + + WeekData.setDirectoryFromWeek(); + + if (freeplayTabs.length > 0) changeTab(); + changeSelection(); changeDiff(); + if (curSelected >= songs.length) curSelected = 0; + bg.color = songs[curSelected].color; + intendedColor = bg.color; + + if (lastDifficultyName == '') + { + lastDifficultyName = Difficulty.defaultDifficulty; + } + curDifficulty = Math.round(Math.max(0, Difficulty.defaultDifficulties.indexOf(lastDifficultyName))); + super.create(); scriptGroup.call('onCreate', []); } @@ -198,9 +180,39 @@ class FreeplayState extends MusicBeatState super.closeSubState(); } - public function addSong(songName:String, weekNum:Int, songCharacter:String, color:Int) + function createSongs() + { + for (i in 0...songs.length) + { + var songText:Alphabet = new Alphabet(0, (70 * i) + 30, songs[i].displayName, true, false); + songText.isMenuItem = true; + songText.targetY = i; + grpSongs.add(songText); + + if (songText.width > 980) + { + var textScale:Float = 980 / songText.width; + songText.scale.x = textScale; + for (letter in songText.lettersArray) + { + letter.x *= textScale; + letter.offset.x *= textScale; + } + } + + Mods.currentModDirectory = songs[i].folder; + var icon:HealthIcon = new HealthIcon(songs[i].songCharacter); + icon.sprTracker = songText; + + // using a FlxGroup is too much fuss! + iconArray.push(icon); + add(icon); + } + } + + public function addSong(songName:String, displayName:String, weekNum:Int, songCharacter:String, color:Int) { - songs.push(new SongMetadata(songName, weekNum, songCharacter, color)); + songs.push(new SongMetadata(songName, displayName, weekNum, songCharacter, color)); } function weekIsLocked(name:String):Bool @@ -247,6 +259,17 @@ class FreeplayState extends MusicBeatState var shiftMult:Int = 1; if (FlxG.keys.pressed.SHIFT) shiftMult = 3; + if (freeplayTabs.length > 1) + { + if (FlxG.keys.justPressed.TAB) + { + if (FlxG.keys.pressed.SHIFT) changeTab(-1); + else changeTab(1); + + changeSelection(); + } + } + if (songs.length > 1) { if (controls.UI_UP_P) @@ -395,6 +418,85 @@ class FreeplayState extends MusicBeatState vocals = null; } + function loadFreeplayData() + { + var mods:Array<{folder:String, enabled:Bool}> = Mods.getListAsArray(); + + for (i in mods) + { + if (!i.enabled) continue; + + var freeplayData = getFreeplayData(i.folder); + Mods.currentModDirectory = i.folder; + + if (freeplayData == null) continue; + if (freeplayData.tabs == null) continue; + for (i in 0...freeplayData.tabs.length) + { + freeplayTabs.push(freeplayData.tabs[i]); + } + } + } + + function loadFreeplayFromWeeks() + { + for (i in 0...WeekData.weeksList.length) + { + if (weekIsLocked(WeekData.weeksList[i])) continue; + + var leWeek:WeekData = WeekData.weeksLoaded.get(WeekData.weeksList[i]); + + WeekData.setDirectoryFromWeek(leWeek); + for (song in leWeek.songs) + { + var colors:Array = song[2]; + if (colors == null || colors.length < 3) + { + colors = [146, 113, 253]; + } + addSong(song[0], song[0], i, song[1], FlxColor.fromRGB(colors[0], colors[1], colors[2])); + } + } + } + + function getSongMeta(song:String):Null + { + // For some reason Metadata.getDirect() only accepts a path without the .json suffix + // Which is kinda hard to get without jank, so this is just easier at this point + final songMetaPath:String = Paths.json('$song/data/meta'); + + return FunkinAssets.exists(songMetaPath) ? FunkinAssets.parseJson5(FunkinAssets.getContent(songMetaPath)) : null; + } + + function getFreeplayData(modFolder:String):Null + { + final freeplayDataPath = Paths.getPath('data/freeplay.json', modFolder, true); + + return FunkinAssets.exists(freeplayDataPath) ? FunkinAssets.parseJson5(FunkinAssets.getContent(freeplayDataPath)) : null; + } + + function loadTab(tab:Int):Void + { + var tab = freeplayTabs[tab]; + for (song in tab.songs) + { + var displayName:String = song; + var icon:String = "face"; + var weekNum:Int = 0; + var color:String = "#8DA399"; + + var songMeta = getSongMeta(song); + if (songMeta != null) + { + if (songMeta.displayName != null) displayName = songMeta.displayName; + if (songMeta.freeplayIcon != null) icon = songMeta.freeplayIcon; + if (songMeta.freeplayColor != null) color = songMeta.freeplayColor; + } + + addSong(song, displayName, weekNum, icon, FlxColor.fromString(color)); + } + } + function changeDiff(change:Int = 0) { debugBG.alpha = 0; @@ -495,26 +597,64 @@ class FreeplayState extends MusicBeatState } } + function changeTab(diff:Int = 0) + { + if (diff != 0) FlxG.sound.play(Paths.sound('scrollMenu'), 0.4); + + currentTab = FlxMath.wrap(currentTab + diff, 0, freeplayTabs.length - 1); + tabText.text = "[ " + (freeplayTabs[currentTab].title ?? 'Unknown') + " ]"; + + clearSongs(); + + loadTab(currentTab); + + curSelected = Std.int(Math.min(curSelected, freeplayTabs[currentTab].songs.length - 1)); + + createSongs(); + } + + function clearSongs() + { + songs = []; + + if (grpSongs != null) + { + grpSongs.forEach(song -> song?.destroy()); + + grpSongs.clear(); + } + + iconArray = FlxDestroyUtil.destroyArray(iconArray); + + iconArray = []; + } + private function positionHighscore() { scoreBG.scale.x = scoreText.textField.textWidth + 12 + 6; scoreBG.x = FlxG.width - (scoreBG.scale.x / 2); diffText.x = Std.int(scoreBG.x + (scoreBG.width / 2)); diffText.x -= diffText.textField.textWidth / 2; + tabText.x = Std.int(scoreBG.x + (scoreBG.width / 2)); + tabText.x -= tabText.textField.textWidth / 2; + tabHint.x = Std.int(scoreBG.x + (scoreBG.width / 2)); + tabHint.x -= tabHint.textField.textWidth / 2; } } class SongMetadata { + public var displayName:String = ""; public var songName:String = ""; public var week:Int = 0; public var songCharacter:String = ""; public var color:Int = -7179779; public var folder:String = ""; - public function new(song:String, week:Int, songCharacter:String, color:Int) + public function new(song:String, displayName:String, week:Int, songCharacter:String, color:Int) { this.songName = song; + this.displayName = displayName; this.week = week; this.songCharacter = songCharacter; this.color = color; @@ -522,3 +662,14 @@ class SongMetadata if (this.folder == null) this.folder = ''; } } + +typedef FreeplayData = +{ + var tabs:Array; +} + +typedef FreeplayTab = +{ + var ?title:String; + var songs:Array; +}