From b15e3bb5aab5126943478801da72f03dcb4e3996 Mon Sep 17 00:00:00 2001 From: Ralty <78720179+Raltyro@users.noreply.github.com> Date: Sun, 27 Jul 2025 08:12:11 +0700 Subject: [PATCH] Options Rewrite WIP All Editor Menus Done --- assets/images/editors/bgs/default.png | Bin 0 -> 52219 bytes assets/languages/en/Editors.xml | 13 ++ source/funkin/editors/DebugOptions.hx | 45 ++++- source/funkin/editors/EditorTreeMenu.hx | 61 +++++-- .../editors/alphabet/AlphabetSelection.hx | 60 +++---- .../editors/character/CharacterSelection.hx | 83 ++++----- .../editors/charter/CharterSelection.hx | 169 ++++++------------ source/funkin/editors/stage/StageSelection.hx | 69 ++++--- source/funkin/menus/MainMenuState.hx | 4 +- .../funkin/menus/credits/CreditsCodename.hx | 135 ++++++++++++++ source/funkin/menus/credits/CreditsMain.hx | 103 +++++++++++ source/funkin/options/OptionsMenu.hx | 12 +- source/funkin/options/OptionsScreen.hx | 141 --------------- source/funkin/options/OptionsTree.hx | 91 ---------- source/funkin/options/TreeMenu.hx | 6 +- .../funkin/options/type/EditorIconOption.hx | 13 -- .../funkin/options/type/GithubIconOption.hx | 1 + source/funkin/options/type/NewOption.hx | 1 + source/funkin/options/type/OptionType.hx | 3 + source/funkin/options/type/TextOption.hx | 8 +- 20 files changed, 505 insertions(+), 513 deletions(-) create mode 100644 assets/images/editors/bgs/default.png create mode 100644 source/funkin/menus/credits/CreditsCodename.hx create mode 100644 source/funkin/menus/credits/CreditsMain.hx delete mode 100644 source/funkin/options/OptionsScreen.hx delete mode 100644 source/funkin/options/OptionsTree.hx delete mode 100644 source/funkin/options/type/EditorIconOption.hx diff --git a/assets/images/editors/bgs/default.png b/assets/images/editors/bgs/default.png new file mode 100644 index 0000000000000000000000000000000000000000..ba4754edfd4941acb0401f4f954918439bcc8c59 GIT binary patch literal 52219 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 UJKEvkw*UYD07*qoM6N<$f~VWW)&Kwi literal 0 HcmV?d00001 diff --git a/assets/languages/en/Editors.xml b/assets/languages/en/Editors.xml index 86ca86ac..e62add53 100644 --- a/assets/languages/en/Editors.xml +++ b/assets/languages/en/Editors.xml @@ -46,7 +46,9 @@ Press ACCEPT to edit the chart for the selected difficulty. Select a difficulty to continue. New Difficulty + Press ACCEPT to create a new chart difficulty. New Song + Press ACCEPT to create a new song. @@ -435,6 +437,11 @@ Creating New Character Character Name $* Required$ Character Image File $* Required$ + + + Creating Character: Error! + The character you are trying to create already exists, if you would like to override it delete the character first! + @@ -442,6 +449,7 @@ Press ACCEPT to edit this typeface. New Typeface + Press ACCEPT to create a new typeface. Select a typeface to edit @@ -554,6 +562,11 @@ Stage Info Stage Name Stage Path + + + Creating Stage: Error! + The stage you are trying to create alreadly exists, if you would like to override it delete the stage first! + diff --git a/source/funkin/editors/DebugOptions.hx b/source/funkin/editors/DebugOptions.hx index 0f3dc9fd..e0293319 100644 --- a/source/funkin/editors/DebugOptions.hx +++ b/source/funkin/editors/DebugOptions.hx @@ -1,18 +1,57 @@ package funkin.editors; import funkin.backend.utils.NativeAPI; -import funkin.options.OptionsScreen; import funkin.options.TreeMenu; +import funkin.options.TreeMenuScreen; import funkin.options.type.*; class DebugOptions extends TreeMenu { - public override function create() { + var bg:FlxSprite; + + override function create() { super.create(); + UIState.setResolutionAware(); + + add(bg = new FlxSprite().loadAnimatedGraphic(Paths.image('menus/menuBGBlue'))); + bg.antialiasing = true; + bg.scrollFactor.set(); + updateBG(); + + addMenu(new TreeMenuScreen('DebugOptions.title', 'DebugOptions.desc')); FlxG.camera.fade(0xFF000000, 0.5, true); } + + public function updateBG() { + var scaleX:Float = FlxG.width / bg.width; + var scaleY:Float = FlxG.height / bg.height; + bg.scale.x = bg.scale.y = Math.max(scaleX, scaleY) * 1.15; + bg.screenCenter(); + } + + override function onResize(width:Int, height:Int) { + super.onResize(width, height); + if (!UIState.resolutionAware) return; + + updateBG(); + } } -class DebugOptionsScreen extends OptionsScreen { +class DebugOptionsScreen extends TreeMenuScreen { + public function new() { + super('DebugOptions.title', 'DebugOptions.desc', 'DebugOptions.'); + #if windows + add(new TextOption(getNameID("showConsole"), getDescID("showConsole"), () -> NativeAPI.allocConsole())); + #end + add(new Checkbox(getNameID("editorsResizable"), getDescID("editorsResizable"), "editorsResizable")); + add(new Checkbox(getNameID("bypassEditorsResize"), getDescID("bypassEditorsResize"), "bypassEditorsResize")); + add(new Checkbox(getNameID("editorSFX"), getDescID("editorSFX"), "editorSFX")); + add(new Checkbox(getNameID("editorPrettyPrint"), getDescID("editorPrettyPrint"), "editorPrettyPrint")); + add(new Checkbox(getNameID("intensiveBlur"), getDescID("intensiveBlur"), "intensiveBlur")); + add(new Checkbox(getNameID("charterAutoSaves"), getDescID("charterAutoSaves"), "charterAutoSaves")); + add(new NumOption(getNameID("charterAutoSaveTime"), getDescID("charterAutoSaveTime"), 60, 60*10, 1, "charterAutoSaveTime")); + add(new NumOption(getNameID("charterAutoSaveWarningTime"), getDescID("charterAutoSaveWarningTime"), 0, 15, 1, "charterAutoSaveWarningTime")); + add(new Checkbox(getNameID("charterAutoSavesSeparateFolder"), getDescID("charterAutoSavesSeparateFolder"), "charterAutoSavesSeparateFolder")); + } } \ No newline at end of file diff --git a/source/funkin/editors/EditorTreeMenu.hx b/source/funkin/editors/EditorTreeMenu.hx index b4904866..afe6dd18 100644 --- a/source/funkin/editors/EditorTreeMenu.hx +++ b/source/funkin/editors/EditorTreeMenu.hx @@ -1,28 +1,25 @@ package funkin.editors; import flixel.addons.display.FlxBackdrop; -import funkin.menus.MainMenuState; -import funkin.options.*; +import funkin.options.type.OptionType; -class EditorTreeMenu extends TreeMenu { +class EditorTreeMenu extends funkin.options.TreeMenu { public var bg:FlxBackdrop; public var bgType:String = "default"; public var bgMovement:FlxPoint = new FlxPoint(); - public var main:OptionsScreen; - - public override function create() { + override function create() { super.create(); - UIState.setResolutionAware(); - FlxG.camera.fade(0xFF000000, 0.5, true); + } - bg = new FlxBackdrop(); + override function createPost() { + super.createPost(); + add(bg = new FlxBackdrop()); bg.loadGraphic(Paths.image('editors/bgs/${bgType}')); bg.antialiasing = true; setBackgroundRotation(-5); - add(bg); } public inline function setBackgroundRotation(rotation:Float) { @@ -30,11 +27,47 @@ class EditorTreeMenu extends TreeMenu { bg.velocity.set(85, 0).degrees = bg.rotation; } - public override function exit() { - FlxG.switchState(new MainMenuState()); + override function exit() { + FlxG.switchState(new funkin.menus.MainMenuState()); } - public function onMenuChange() { - + override function update(elapsed:Float) { + super.update(elapsed); + + bg.colorTransform.redOffset = lerp(bg.colorTransform.redOffset, 0, 0.0625); + bg.colorTransform.greenOffset = lerp(bg.colorTransform.greenOffset, 0, 0.0625); + bg.colorTransform.blueOffset = lerp(bg.colorTransform.blueOffset, 0, 0.0625); + bg.colorTransform.redMultiplier = lerp(bg.colorTransform.redMultiplier, 1, 0.0625); + bg.colorTransform.greenMultiplier = lerp(bg.colorTransform.greenMultiplier, 1, 0.0625); + bg.colorTransform.blueMultiplier = lerp(bg.colorTransform.blueMultiplier, 1, 0.0625); + } + + override function menuChanged() @:privateAccess { + super.menuChanged(); + if (previousMenus.length > 0) return; // selected a sub-menu + + var prev = tree[tree.length - 2]; + if (prev == null || prev.curOption == null || !(prev.curOption is OptionType)) return; + + // small flashbang + var color = cast(prev.curOption, OptionType).editorFlashColor; + bg.colorTransform.redOffset = 0.25 * color.red; + bg.colorTransform.greenOffset = 0.25 * color.green; + bg.colorTransform.blueOffset = 0.25 * color.blue; + bg.colorTransform.redMultiplier = FlxMath.lerp(1, color.redFloat, 0.25); + bg.colorTransform.greenMultiplier = FlxMath.lerp(1, color.greenFloat, 0.25); + bg.colorTransform.blueMultiplier = FlxMath.lerp(1, color.blueFloat, 0.25); + } +} + +class EditorTreeMenuScreen extends funkin.options.TreeMenuScreen { + public function new(name:String, desc:String, ?prefix:String, ?objects:Array, + ?newButton:String, ?newButtonDesc:String, ?newCallback:Void->Void) + { + super(name, desc, prefix, objects); + if (newCallback != null) { + insert(0, new funkin.options.type.NewOption(getID(newButton), getID(newButtonDesc), newCallback)); + curSelected = 1; + } } } \ No newline at end of file diff --git a/source/funkin/editors/alphabet/AlphabetSelection.hx b/source/funkin/editors/alphabet/AlphabetSelection.hx index 1b8155ee..9d937a83 100644 --- a/source/funkin/editors/alphabet/AlphabetSelection.hx +++ b/source/funkin/editors/alphabet/AlphabetSelection.hx @@ -1,51 +1,35 @@ package funkin.editors.alphabet; +import haxe.io.Path; import haxe.xml.Access; import funkin.game.Character; -import funkin.options.OptionsScreen; -import funkin.options.type.IconOption; +import funkin.editors.EditorTreeMenu; import funkin.options.type.NewOption; import funkin.options.type.TextOption; import funkin.options.type.OptionType; -import haxe.io.Path; -class AlphabetSelection extends EditorTreeMenu -{ - inline function translate(id:String, ?args:Array) - return TU.translate("editor.alphabet.selection." + id, args); - - public override function create() - { - bgType = "charter"; +class AlphabetSelection extends EditorTreeMenu { + override function create() { super.create(); + DiscordUtil.call("onEditorTreeLoaded", ["Alphabet Editor"]); + addMenu(new AlphabetSelectionScreen()); + } +} + +class AlphabetSelectionScreen extends EditorTreeMenuScreen { + public function new() { + super('editor.alphabet.name', 'editor.alphabet.selection.desc', 'editor.alphabet.selection', 'newTypeface', 'newTypefaceDesc', () -> { + parent.openSubState(new UIWarningSubstate(translate('warnings.notImplemented-title'), translate('warnings.notImplemented-body'), [ + {label: TU.translate("editor.ok"), color: 0xFFFF0000, onClick: (t) -> {}} + ])); + }); var modsList:Array = []; - for(file in Paths.getFolderContent('data/alphabet/', true, BOTH)) // mods ? MODS : BOTH + for (file in Paths.getFolderContent('data/alphabet/', true, BOTH)) // mods ? MODS : BOTH if (Path.extension(file) == "xml") modsList.push(CoolUtil.getFilename(file)); - var list:Array = [ - for (typeface in modsList) - new AlphabetIconOption(typeface, translate("acceptTypeface"), typeface, function() { - FlxG.switchState(new AlphabetEditor(typeface)); - }) - ]; - - var newChar = translate("newTypeface"); - list.insert(0, new NewOption(newChar, newChar, function() { - openSubState(new UIWarningSubstate(translate("warnings.notImplemented-title"), translate("warnings.notImplemented-body"), [ - {label: TU.translate("editor.ok"), color: 0xFFFF0000, onClick: function(t) {}} - ])); - })); - - main = new OptionsScreen(TU.translate("editor.alphabet.name"), translate("desc"), list); - - DiscordUtil.call("onEditorTreeLoaded", ["Alphabet Editor"]); - } - - override function createPost() { - super.createPost(); - - main.changeSelection(1); + for (typeface in modsList) + add(new AlphabetIconOption(typeface, getID('acceptTypeface'), typeface, () -> FlxG.switchState(new AlphabetEditor(typeface)))); } } @@ -57,7 +41,7 @@ class AlphabetIconOption extends TextOption { var xml = Xml.parse(Assets.getText(Paths.xml('alphabet/$typeface'))).firstElement(); var spritesheet = null; - for(node in xml.elements()) { + for (node in xml.elements()) { if (node.nodeName == "spritesheet") { spritesheet = node.firstChild().nodeValue; break; @@ -73,14 +57,14 @@ class AlphabetIconOption extends TextOption { iconSpr.frames = Paths.getFrames(spritesheet); iconSpr.antialiasing = true; var frameToUse = iconSpr.frames.frames[0]; - for(frame in iconSpr.frames.frames) { + for (frame in iconSpr.frames.frames) { if (frame.name.toUpperCase().startsWith("A")) { frameToUse = frame; break; } } iconSpr.frame = frameToUse; - if(useColorOffsets) { + if (useColorOffsets) { iconSpr.colorTransform.color = -1; } iconSpr.setPosition(90 - iconSpr.width - 20, (__text.height - iconSpr.height) / 2); diff --git a/source/funkin/editors/character/CharacterSelection.hx b/source/funkin/editors/character/CharacterSelection.hx index 9f009ccb..1b9d2d70 100644 --- a/source/funkin/editors/character/CharacterSelection.hx +++ b/source/funkin/editors/character/CharacterSelection.hx @@ -1,81 +1,69 @@ package funkin.editors.character; import haxe.xml.Printer; -import funkin.editors.ui.UIImageExplorer.ImageSaveData; import funkin.game.Character; -import funkin.options.OptionsScreen; +import funkin.editors.ui.UIImageExplorer.ImageSaveData; +import funkin.editors.EditorTreeMenu; import funkin.options.type.IconOption; import funkin.options.type.NewOption; import funkin.options.type.TextOption; import funkin.options.type.OptionType; -import funkin.backend.assets.ModsFolder; -class CharacterSelection extends EditorTreeMenu -{ - inline function translate(id:String, ?args:Array) - return TU.translate("characterSelection." + id, args); +class CharacterSelection extends EditorTreeMenu { + override function create() { + super.create(); + DiscordUtil.call("onEditorTreeLoaded", ["Character Editor"]); + addMenu(new CharacterSelectionScreen()); + } +} +class CharacterSelectionScreen extends EditorTreeMenuScreen { public var modsList:Array = []; - public override function create() - { - bgType = "charter"; - super.create(); + public function new() { + super('editor.character.name', 'characterSelection.desc', 'characterSelection.', 'newCharacter', 'newCharacterDesc', () -> { + parent.openSubState(new CharacterCreationScreen(createCharacter)); + }); var isMods:Bool = true; modsList = Character.getList(true, true); - if(modsList.length == 0) { + if (modsList.length == 0) { modsList = Character.getList(false, true); isMods = false; } - function generateList(modsList:Array, isMods:Bool, folderPath:String = ""):Array { - var list:Array = []; + function generateList(modsList:Array, isMods:Bool, folderPath:String = ""):Array { + var list:Array = []; - for(char in modsList) { - if(char.endsWith("/")) { + for (char in modsList) { + if (char.endsWith("/")) { var folderName = CoolUtil.getFilename(char.substr(0, char.length-1)); - list.push(new TextOption(folderName + " >", translate("acceptFolder"), function() { + list.push(new TextOption(folderName, getID('acceptFolder'), ' >', () -> { var newModsList = Character.getList(isMods, true, char); - var newList:Array = generateList(newModsList, isMods, folderPath + folderName + "/"); - optionsTree.add(new OptionsScreen(folderPath + folderName, translate("desc-folder", [folderPath + folderName + "/"]), newList)); + var newList:Array = generateList(newModsList, isMods, folderPath + folderName + "/"); + parent.addMenu(new EditorTreeMenuScreen(folderPath + folderName, translate('desc-folder', [folderPath + folderName + "/"]), newList)); + })); + } + else { + list.push(new IconOption(char, getID('acceptCharacter'), Character.getIconFromCharName(folderPath + char, char), () -> { + FlxG.switchState(new CharacterEditor(folderPath + char)); })); - } else { - list.push(new IconOption(char, translate("acceptCharacter"), Character.getIconFromCharName(folderPath + char, char), - function() { - FlxG.switchState(new CharacterEditor(folderPath + char)); - }) - ); } } - list.insert(0, new NewOption(translate("newCharacter"), translate("newCharacterDesc"), function() { - openSubState(new CharacterCreationScreen(createCharacter)); - })); - return list; } - var list = generateList(modsList, isMods); - - main = new OptionsScreen(TU.translate("editor.character.name"), translate("desc"), list); - - DiscordUtil.call("onEditorTreeLoaded", ["Character Editor"]); - } - - override function createPost() { - super.createPost(); - - main.changeSelection(1); + for (o in generateList(modsList, isMods)) add(o); } public function createCharacter(name:String, imageSaveData:ImageSaveData, xml:Xml) { var characterAlreadyExists:Bool = modsList.contains(name); if (characterAlreadyExists) { - openSubState(new UIWarningSubstate("Creating Character: Error!", "The character you are trying to create already exists, if you would like to override it delete the character first!", [ - {label: "Ok", color: 0xFFFF0000, onClick: function(t) {}} + parent.openSubState(new UIWarningSubstate(TU.translate('characterCreationScreen.warning.char-exists-title'), TU.translate('characterCreationScreen.warning.char-exists-body'), [ + {label: TU.translate('editor.ok'), color: 0xFFFF0000, onClick: (t) -> {}} ])); return; } @@ -88,11 +76,10 @@ class CharacterSelection extends EditorTreeMenu UIImageExplorer.saveFilesGlobal(imageSaveData, '${Paths.getAssetsRoot()}/images/characters'); // Add to Menu >:D - var option:IconOption = new IconOption(name, "Press ACCEPT to edit this character.", Character.getIconFromCharName(name), - function() { - FlxG.switchState(new CharacterEditor(name)); - } - ); - optionsTree.members[optionsTree.members.length-1].insert(1, option); + var option:IconOption = new IconOption(name, getID('acceptCharacter'), Character.getIconFromCharName(name), () -> { + FlxG.switchState(new CharacterEditor(name)); + }); + + insert(1, option); } } \ No newline at end of file diff --git a/source/funkin/editors/charter/CharterSelection.hx b/source/funkin/editors/charter/CharterSelection.hx index 467817d7..fda0f1c3 100644 --- a/source/funkin/editors/charter/CharterSelection.hx +++ b/source/funkin/editors/charter/CharterSelection.hx @@ -1,118 +1,83 @@ package funkin.editors.charter; +import flixel.util.FlxColor; import funkin.backend.chart.Chart; -import funkin.backend.chart.ChartData.ChartMetaData; import funkin.backend.chart.ChartData; -import funkin.backend.system.framerate.Framerate; -import funkin.editors.EditorTreeMenu; import funkin.editors.charter.SongCreationScreen.SongCreationData; +import funkin.editors.EditorTreeMenu; import funkin.menus.FreeplayState.FreeplaySonglist; -import funkin.options.*; import funkin.options.type.*; -import funkin.options.type.NewOption; import haxe.Json; using StringTools; class CharterSelection extends EditorTreeMenu { - public var freeplayList:FreeplaySonglist; - public var curSong:ChartMetaData; - public override function create() { - bgType = "charter"; - + override function create() { super.create(); + DiscordUtil.call("onEditorTreeLoaded", ["Chart Editor"]); + addMenu(new CharterSelectionScreen()); + bgType = 'charter'; + } +} - Framerate.offset.y = 60; +class CharterSelectionScreen extends EditorTreeMenuScreen { + public var freeplayList:FreeplaySonglist; + public var songList:Array = []; + public var curSong:ChartMetaData; + inline public function makeChartOption(d:String, name:String):TextOption { + return new TextOption(d, getID('acceptDifficulty'), () -> FlxG.switchState(new Charter(name, d))); + } + + public function makeSongOption(s:ChartMetaData):IconOption { + songList.push(s.name.toLowerCase()); + + var opt = new IconOption(s.name, getID('acceptSong'), s.icon, () -> { + curSong = s; + + var screen = new EditorTreeMenuScreen(s.name, getID('selectDifficulty'), [ + for (d in s.difficulties) if (d != '') makeChartOption(d, s.name) + ]); + + #if sys + screen.insert(0, new NewOption(getID('newDifficulty'), getID('newDifficultyDesc'), () -> { + parent.openSubState(new ChartCreationScreen(saveChart)); + })); + screen.curSelected = 1; + #end + + parent.addMenu(screen); + }); + opt.suffix = ' >'; + opt.color = s.color.getDefault(FlxColor.WHITE); + + return opt; + } + + public function new() { + super('editor.chart.name', 'charterSelection.desc', 'charterSelection.', 'newSong', 'newSongDesc', () -> { + parent.openSubState(new SongCreationScreen(saveSong)); + }); freeplayList = FreeplaySonglist.get(false); - var list:Array = [ - for(s in freeplayList.songs) new EditorIconOption(s.name, TU.translate("charterSelection.acceptSong"), s.icon, function() { - curSong = s; - var list:Array = [ - for(d in s.difficulties) if (d != "") - new TextOption(d, TU.translate("charterSelection.acceptDifficulty"), function() { - FlxG.switchState(new Charter(s.name, d)); - }) - ]; - #if sys - var newDiff = TU.translate("charterSelection.newDifficulty"); - list.push(new NewOption(newDiff, newDiff, function() { - FlxG.state.openSubState(new ChartCreationScreen(saveChart)); - })); - #end - optionsTree.add(new OptionsScreen(s.name, TU.translate("charterSelection.selectDifficulty"), list)); - }, s.color.getDefault(0xFFFFFFFF)) - ]; - - #if sys - var newSong = TU.translate("charterSelection.newSong"); - list.insert(0, new NewOption(newSong, newSong, function() { - FlxG.state.openSubState(new SongCreationScreen(saveSong)); - })); - #end - - main = new OptionsScreen(TU.translate("editor.chart.name"), TU.translate("charterSelection.desc"), list); - - DiscordUtil.call("onEditorTreeLoaded", ["Chart Editor"]); - } - - override function createPost() { - super.createPost(); - - main.changeSelection(1); - } - - public override function update(elapsed:Float) { - super.update(elapsed); - - bg.colorTransform.redOffset = lerp(bg.colorTransform.redOffset, 0, 0.0625); - bg.colorTransform.greenOffset = lerp(bg.colorTransform.greenOffset, 0, 0.0625); - bg.colorTransform.blueOffset = lerp(bg.colorTransform.blueOffset, 0, 0.0625); - bg.colorTransform.redMultiplier = lerp(bg.colorTransform.redMultiplier, 1, 0.0625); - bg.colorTransform.greenMultiplier = lerp(bg.colorTransform.greenMultiplier, 1, 0.0625); - bg.colorTransform.blueMultiplier = lerp(bg.colorTransform.blueMultiplier, 1, 0.0625); - } - - public override function onMenuChange() { - super.onMenuChange(); - if (optionsTree.members.length > 1) { // selected a song - if(main != null) { - var opt = main.members[main.curSelected]; - if(opt is EditorIconOption) { - var opt:EditorIconOption = cast opt; - - // small flashbang - var color = opt.flashColor; - bg.colorTransform.redOffset = 0.25 * color.red; - bg.colorTransform.greenOffset = 0.25 * color.green; - bg.colorTransform.blueOffset = 0.25 * color.blue; - bg.colorTransform.redMultiplier = FlxMath.lerp(1, color.redFloat, 0.25); - bg.colorTransform.greenMultiplier = FlxMath.lerp(1, color.greenFloat, 0.25); - bg.colorTransform.blueMultiplier = FlxMath.lerp(1, color.blueFloat, 0.25); - } - } - } + for (i => s in freeplayList.songs) add(makeSongOption(s)); } #if sys public function saveSong(creation:SongCreationData, ?callback:String -> SongCreationData -> Void) { - var songAlreadyExists:Bool = [for (s in freeplayList.songs) s.name.toLowerCase()].contains(creation.meta.name.toLowerCase()); - + var songAlreadyExists:Bool = songList.contains(creation.meta.name.toLowerCase()); if (songAlreadyExists) { - openSubState(new UIWarningSubstate(TU.translate("chartCreation.warnings.song-exists-title"), TU.translate("chartCreation.warnings.song-exists-body"), [ - {label: TU.translate("editor.ok"), color: 0xFFFF0000, onClick: function(t) {}}, + parent.openSubState(new UIWarningSubstate(TU.translate("chartCreation.warnings.song-exists-title"), TU.translate("chartCreation.warnings.song-exists-body"), [ + {label: TU.translate("editor.ok"), color: 0xFFFF0000, onClick: (t) -> {}}, ])); return; } - // Paths - var songsDir:String = '${Paths.getAssetsRoot()}/songs/'; - var songFolder:String = '$songsDir${creation.meta.name}'; + var songFolder:String = '${Paths.getAssetsRoot()}/songs/${creation.meta.name}'; #if sys // Make Directories - sys.FileSystem.createDirectory(songFolder); + CoolUtil.addMissingFolders(songFolder); sys.FileSystem.createDirectory('$songFolder/song'); sys.FileSystem.createDirectory('$songFolder/charts'); @@ -125,52 +90,30 @@ class CharterSelection extends EditorTreeMenu { if (creation.oppVocals != null) sys.io.File.saveBytes('$songFolder/song/Voices-Opponent.${Flags.SOUND_EXT}', creation.oppVocals); #end - if (callback != null) - callback(songFolder, creation); - - var option = new EditorIconOption(creation.meta.name, TU.translate("charterSelection.acceptSong"), creation.meta.icon, function() { - curSong = creation.meta; - var list:Array = [ - for(d in creation.meta.difficulties) - if (d != "") new TextOption(d, TU.translate("charterSelection.acceptDifficulty"), function() { - FlxG.switchState(new Charter(creation.meta.name, d)); - }) - ]; - var newDiff = TU.translate("charterSelection.newDifficulty"); - list.push(new NewOption(newDiff, newDiff, function() { - FlxG.state.openSubState(new ChartCreationScreen(saveChart)); - })); - optionsTree.insert(1, new OptionsScreen(creation.meta.name, TU.translate("charterSelection.selectDifficulty"), list)); - }, creation.meta.color.getDefault(0xFFFFFFFF)); + if (callback != null) callback(songFolder, creation); // Add to List freeplayList.songs.insert(0, creation.meta); - main.insert(1, option); + insert(1, makeSongOption(creation.meta)); } public function saveChart(name:String, data:ChartData) { var difficultyAlreadyExists:Bool = curSong.difficulties.contains(name); - if (difficultyAlreadyExists) { - openSubState(new UIWarningSubstate(TU.translate("chartCreation.warnings.chart-exists-title"), TU.translate("chartCreation.warnings.chart-exists-body"), [ - {label: TU.translate("editor.ok"), color: 0xFFFF0000, onClick: function(t) {}}, + parent.openSubState(new UIWarningSubstate(TU.translate("chartCreation.warnings.chart-exists-title"), TU.translate("chartCreation.warnings.chart-exists-body"), [ + {label: TU.translate("editor.ok"), color: 0xFFFF0000, onClick: (t) -> {}}, ])); return; } - // Paths var songFolder:String = '${Paths.getAssetsRoot()}/songs/${curSong.name}'; // Save Files CoolUtil.safeSaveFile('$songFolder/charts/${name}.json', Json.stringify(data, Flags.JSON_PRETTY_PRINT)); // Add to List - // duplicated code, todo: fix this curSong.difficulties.push(name); - var option = new TextOption(name, TU.translate("charterSelection.acceptDifficulty"), function() { - FlxG.switchState(new Charter(curSong.name, name)); - }); - optionsTree.members[optionsTree.members.length-1].insert(optionsTree.members[optionsTree.members.length-1].length-1, option); + parent.tree.last().insert(parent.tree.last().length - 1, makeChartOption(name, curSong.name)); // Add to Meta var meta = Json.parse(sys.io.File.getContent('$songFolder/meta.json')); diff --git a/source/funkin/editors/stage/StageSelection.hx b/source/funkin/editors/stage/StageSelection.hx index b78aebc3..418e0190 100644 --- a/source/funkin/editors/stage/StageSelection.hx +++ b/source/funkin/editors/stage/StageSelection.hx @@ -1,46 +1,47 @@ package funkin.editors.stage; import funkin.editors.stage.StageCreationScreen.StageCreationData; +import funkin.editors.EditorTreeMenu; import funkin.game.Stage; -import funkin.options.OptionsScreen; import funkin.options.type.NewOption; import funkin.options.type.OptionType; import funkin.options.type.TextOption; -class StageSelection extends EditorTreeMenu -{ +using StringTools; + +class StageSelection extends EditorTreeMenu { + override function create() { + super.create(); + DiscordUtil.call("onEditorTreeLoaded", ["Stage Editor"]); + addMenu(new StageSelectionScreen()); + } +} + +class StageSelectionScreen extends EditorTreeMenuScreen { public var stages:Array = []; - public override function create() - { - bgType = "charter"; - super.create(); + public function makeStageOption(stage:String):TextOption { + return new TextOption(stage, getID('acceptStage'), () -> { + FlxG.switchState(new StageEditor(stage)); + }); + } + + public function new() { + super('editor.stage.name', 'stageSelection.desc', 'stageSelection.', 'newStage', 'acceptNewStage', () -> { + parent.openSubState(new StageCreationScreen(saveStage)); + }); var modsList:Array = Stage.getList(true, true); - - var list:Array = [ - for (stage in (modsList.length == 0 ? Stage.getList(false, true) : modsList)) { - stages.push(stage); - new TextOption(stage, TU.translate("stageSelection.acceptStage"), - function() { - FlxG.switchState(new StageEditor(stage)); - }); - } - ]; - - list.insert(0, new NewOption(TU.translate("stageSelection.newStage"), TU.translate("stageSelection.acceptNewStage"), function() { - FlxG.state.openSubState(new StageCreationScreen(saveStage)); - })); - - main = new OptionsScreen(TU.translate("editor.stage.name"), TU.translate("stageSelection.desc"), list); - - DiscordUtil.call("onEditorTreeLoaded", ["Stage Editor"]); + for (stage in (modsList.length == 0 ? Stage.getList(false, true) : modsList)) { + stages.push(stage.toLowerCase()); + add(makeStageOption(stage)); + } } public function saveStage(creation:StageCreationData) { - if ([for (s in stages) s.toLowerCase()].contains(creation.name.toLowerCase())) { - openSubState(new UIWarningSubstate("Creating Stage: Error!", "The stage you are trying to create alreadly exists, if you would like to override it delete the stage first!", [ - {label: "Ok", color: 0xFFFF0000, onClick: function(t) {}} + if (stages.contains(creation.name.toLowerCase())) { + parent.openSubState(new UIWarningSubstate(TU.translate("stageCreationScreen.warnings.stage-exists-title"), TU.translate("stageCreationScreen.warnings.stage-exists-body"), [ + {label: TU.translate("editor.ok"), color: 0xFFFF0000, onClick: (t) -> {}}, ])); return; } @@ -51,15 +52,7 @@ class StageSelection extends EditorTreeMenu #end // Add to List - var option = new TextOption(creation.name, TU.translate("stageSelection.acceptStage"), function() { - FlxG.switchState(new StageEditor(creation.name)); - }); - optionsTree.members[optionsTree.members.length-1].insert(1, option); - } - - override function createPost() { - super.createPost(); - - main.changeSelection(1); + stages.push(creation.name.toLowerCase()); + parent.tree.last().insert(parent.tree.last().length - 1, makeStageOption(creation.name)); } } \ No newline at end of file diff --git a/source/funkin/menus/MainMenuState.hx b/source/funkin/menus/MainMenuState.hx index 1a931781..1a68f4a2 100644 --- a/source/funkin/menus/MainMenuState.hx +++ b/source/funkin/menus/MainMenuState.hx @@ -6,7 +6,7 @@ import flixel.tweens.FlxTween; import funkin.backend.FunkinText; import funkin.backend.scripting.events.menu.MenuChangeEvent; import funkin.backend.scripting.events.NameEvent; -//import funkin.menus.credits.CreditsMain; +import funkin.menus.credits.CreditsMain; import funkin.options.OptionsMenu; import lime.app.Application; @@ -168,7 +168,7 @@ class MainMenuState extends MusicBeatState { case 'story mode': FlxG.switchState(new StoryMenuState()); case 'freeplay': FlxG.switchState(new FreeplayState()); - //case 'donate', 'credits': FlxG.switchState(new CreditsMain()); // kept donate for not breaking scripts, if you don't want donate to bring you to the credits menu, thats easy softcodable - Nex + case 'donate', 'credits': FlxG.switchState(new CreditsMain()); // kept donate for not breaking scripts, if you don't want donate to bring you to the credits menu, thats easy softcodable - Nex case 'options': FlxG.switchState(new OptionsMenu()); } }); diff --git a/source/funkin/menus/credits/CreditsCodename.hx b/source/funkin/menus/credits/CreditsCodename.hx new file mode 100644 index 00000000..5448825c --- /dev/null +++ b/source/funkin/menus/credits/CreditsCodename.hx @@ -0,0 +1,135 @@ +package funkin.menus.credits; + +import flixel.text.FlxText; +import flixel.util.FlxColor; +import funkin.backend.system.github.GitHub; +import funkin.backend.system.github.GitHubContributor.CreditsGitHubContributor; +import funkin.options.PlayerSettings; +import funkin.options.type.GithubIconOption; + +using StringTools; + +class CreditsCodename extends funkin.options.TreeMenuScreen { + public var error:Bool = false; + public var totalContributions:Int = 0; + public var contribFormats:Array = []; + + public function new() { + super("Codename Engine", "credits.allContributors"); + tryUpdating(true); + } + + // blame the secondary threads if the code has to look this bad - Nex + private var _canReset:Bool = true; + private var _downloadingSteps:Int = 0; + override function update(elapsed:Float) { + super.update(elapsed); + + if (_downloadingSteps == 2) { + _downloadingSteps = 0; + _canReset = true; + displayList(); + } else if (_downloadingSteps == 1) { + _downloadingSteps = 0; + _canReset = true; + updateMenuDesc(); + } + else if (_canReset && PlayerSettings.solo.controls.RESET) tryUpdating(); + } + + public function tryUpdating(forceDisplaying:Bool = false) { + updateMenuDesc(TU.translate("credits.downloadingList")); + _canReset = false; + Main.execAsync(() -> { + if (checkUpdate() || forceDisplaying) _downloadingSteps = 2; + else _downloadingSteps = 1; + }); + } + + public override function updateMenuDesc(?txt:String) { + if (!_canReset) return; + super.updateMenuDesc(txt); + updateMarkup(); + } + + public function updateMarkup() { + if (parent == null) return; + var text:String = parent.descLabel.text; + parent.descLabel.text = ""; + parent.descLabel.applyMarkup(text, contribFormats = [ + new FlxTextFormatMarkerPair(new FlxTextFormat(Flags.MAIN_DEVS_COLOR), '*'), + new FlxTextFormatMarkerPair(new FlxTextFormat(FlxColor.interpolate(Flags.MIN_CONTRIBUTIONS_COLOR, Flags.MAIN_DEVS_COLOR, Options.contributors[curSelected].contributions / totalContributions)), '~') + ]); + } + + override function destroy() { + super.destroy(); + for (frmt in contribFormats) parent.descLabel.removeFormat(frmt.format); + } + + public function checkUpdate():Bool { + var curTime:Float = Date.now().getTime(); + if(Options.lastUpdated != null && curTime < Options.lastUpdated + 120000) return false; // Fuck you Github rate limits - Nex + Options.lastUpdated = curTime; + + error = false; + var idk = GitHub.getContributors(Flags.REPO_OWNER, Flags.REPO_NAME, function(e) { + error = true; + var errMsg:String = 'Error while trying to download contributors list:\n${CoolUtil.removeIP(e.message)}'; + Logs.error(errMsg.replace('\n', ' ')); + funkin.backend.utils.NativeAPI.showMessageBox("Codename Engine Warning", errMsg, MSG_WARNING); + }); + if(error) return false; + if((idk is Array)) { + var contributors:Array = []; + for(e in idk) contributors.push({ + login: e.login, + avatar_url: e.avatar_url, + html_url: e.html_url, + id: e.id, + contributions: e.contributions + }); + Options.contributors = contributors; + } + Logs.verbose('[CreditsCodename] Contributors list Updated!'); + + var errorOnMain:Bool = false; + var idk2 = GitHub.getOrganizationMembers(Flags.REPO_OWNER, function(e) { + errorOnMain = true; + var errMsg:String = 'Error while trying to download ${Flags.REPO_OWNER} members list:\n${CoolUtil.removeIP(e.message)}'; + Logs.error(errMsg.replace('\n', ' ')); + funkin.backend.utils.NativeAPI.showMessageBox("Codename Engine Warning", errMsg, MSG_WARNING); + }); + if(!errorOnMain) { + Options.mainDevs = [for(m in idk2) m.id]; + Logs.verbose('[CreditsCodename] Main Devs list Updated!'); + } + + return true; + } + + public function displayList() { + //if (curSelected > Options.contributors.length - 1) changeSelection(-(curSelected - (Options.contributors.length - 1))); + if (curSelected > Options.contributors.length - 1) curSelected = Options.contributors.length - 1; + changeSelection(0, true); + + while (members.length > 0) { + members[0].destroy(); + remove(members[0], true); + } + + totalContributions = 0; + for(c in Options.contributors) totalContributions += c.contributions; + for(c in Options.contributors) { + var text = TU.translate("credits.totalContributions", [c.contributions, totalContributions, FlxMath.roundDecimal(c.contributions / totalContributions * 100, 2)]); + var opt:GithubIconOption = new GithubIconOption(c, text); + if(Options.mainDevs.contains(c.id)) { + opt.desc += TU.translate("credits.mainDev"); + @:privateAccess opt.__text.color = Flags.MAIN_DEVS_COLOR; + } + add(opt); + } + + updateMenuDesc(); + } +} \ No newline at end of file diff --git a/source/funkin/menus/credits/CreditsMain.hx b/source/funkin/menus/credits/CreditsMain.hx new file mode 100644 index 00000000..79182bcc --- /dev/null +++ b/source/funkin/menus/credits/CreditsMain.hx @@ -0,0 +1,103 @@ +package funkin.menus.credits; + +import flixel.util.FlxColor; +import funkin.backend.assets.AssetSource; +import funkin.backend.system.github.GitHubContributor.CreditsGitHubContributor; +import funkin.options.TreeMenu; +import funkin.options.TreeMenuScreen; +import funkin.options.type.*; +import haxe.xml.Access; + +class CreditsMain extends TreeMenu { + var bg:FlxSprite; + var items:Array = []; + + override function create() { + super.create(); + + DiscordUtil.call("onMenuLoaded", ["Credits Menu"]); + + add(bg = new FlxSprite().loadAnimatedGraphic(Paths.image('menus/menuBGBlue'))); + bg.antialiasing = true; + bg.scrollFactor.set(); + updateBG(); + + var first = new TreeMenuScreen('credits.name', 'credits.madePossible'); + addMenu(first); + + for (i in funkin.backend.assets.ModsFolder.getLoadedMods()) { + var xmlPath = Paths.xml('config/credits/LIB_$i'); + + if (Paths.assetsTree.existsSpecific(xmlPath, "TEXT")) { + var access:Access = null; + try access = new Access(Xml.parse(Paths.assetsTree.getSpecificAsset(xmlPath, "TEXT"))) + catch(e) Logs.trace('[CreditsMain] Error while parsing credits.xml: ${Std.string(e)}', ERROR); + if (access != null) for (c in parseCreditsFromXML(access)) first.add(c); + } + } + + first.add(new TextOption('Codename Engine', 'credits.selectCodename', ' >', () -> addMenu(new CreditsCodename()))); + first.add(new TextOption('Friday Night Funkin\'', 'credits.selectBase', ' >', () -> CoolUtil.openURL(Flags.URL_FNF_ITCH))); + } + + public function updateBG() { + var scaleX:Float = FlxG.width / bg.width; + var scaleY:Float = FlxG.height / bg.height; + bg.scale.x = bg.scale.y = Math.max(scaleX, scaleY) * 1.15; + bg.screenCenter(); + } + + // XML STUFF + public function parseCreditsFromXML(xml:Access, source:AssetSource = BOTH):Array { + var credsMenus:Array = []; + + for(node in xml.elements) { + var desc = node.getAtt("desc").getDefault("No Description"); + + if (node.name == "github") { + if (!node.has.user) { + Logs.warn("A github node requires a user attribute.", "CreditsMain"); + continue; + } + + var username = node.getAtt("user"); + var user:CreditsGitHubContributor = { // Kind of forcing + login: username, + html_url: 'https://github.com/$username', + avatar_url: 'https://github.com/$username.png' + }; + var opt:GithubIconOption = new GithubIconOption(user, desc, null, + node.has.customName ? node.att.customName : null, + node.has.size ? Std.parseInt(node.att.size) : 96, + node.has.portrait ? node.att.portrait.toLowerCase() == "false" ? false : true : true + ); + if (node.has.color) + @:privateAccess opt.__text.color = FlxColor.fromString(node.att.color); + credsMenus.push(opt); + } else { + if (!node.has.name) { + Logs.warn("A credit node requires a name attribute.", "CreditsMain"); + continue; + } + var name = node.getAtt("name"); + + switch(node.name) { + case "credit": + var opt:PortraitOption = new PortraitOption(name, desc, function() if(node.has.url) CoolUtil.openURL(node.att.url), + node.has.icon && Paths.assetsTree.existsSpecific(Paths.image('credits/${node.att.icon}'), "IMAGE", source) ? + FlxG.bitmap.add(Paths.image('credits/${node.att.icon}')) : null, node.has.size ? Std.parseInt(node.att.size) : 96, + node.has.portrait ? node.att.portrait.toLowerCase() == "false" ? false : true : true + ); + if (node.has.color) + @:privateAccess opt.__text.color = FlxColor.fromString(node.att.color); + credsMenus.push(opt); + + case "menu": + credsMenus.push(new TextOption(name, desc, ' >', () -> addMenu(new TreeMenuScreen(name, desc, parseCreditsFromXML(node, source))))); + } + } + } + + return credsMenus; + } +} \ No newline at end of file diff --git a/source/funkin/options/OptionsMenu.hx b/source/funkin/options/OptionsMenu.hx index ae184e0c..7522b87c 100644 --- a/source/funkin/options/OptionsMenu.hx +++ b/source/funkin/options/OptionsMenu.hx @@ -94,7 +94,7 @@ class OptionsMenu extends TreeMenu { var access:Access = null; try access = new Access(Xml.parse(Paths.assetsTree.getSpecificAsset(xmlPath, "TEXT"))) catch(e) Logs.trace('Error while parsing options.xml: ${Std.string(e)}', ERROR); - //if (access != null) for (o in parseOptionsFromXML(access)) main.add(o); + if (access != null) for (o in parseOptionsFromXML(access)) tree.first().add(o); } } } @@ -120,9 +120,8 @@ class OptionsMenu extends TreeMenu { } // XML STUFF - /* - public function parseOptionsFromXML(xml:Access):Array { - var options:Array = []; + public function parseOptionsFromXML(xml:Access):Array { + var options:Array = []; for(node in xml.elements) { if (!node.has.name) { @@ -164,13 +163,10 @@ class OptionsMenu extends TreeMenu { options.push(new ArrayOption(name, desc, optionOptions, optionDisplayOptions, node.att.id, null, FlxG.save.data)); case "menu": - options.push(new TextOption(name + " >", desc, function() { - optionsTree.add(new TreeMenuScreen(name, desc, parseOptionsFromXML(node))); - })); + options.push(new TextOption(name, desc, ' >', () -> addMenu(new TreeMenuScreen(name, desc, parseOptionsFromXML(node))))); } } return options; } - */ } \ No newline at end of file diff --git a/source/funkin/options/OptionsScreen.hx b/source/funkin/options/OptionsScreen.hx deleted file mode 100644 index 672feb40..00000000 --- a/source/funkin/options/OptionsScreen.hx +++ /dev/null @@ -1,141 +0,0 @@ -// REMOVE THIS WHEN COMPLETE - -package funkin.options; - -import funkin.options.type.OptionType; - -class OptionsScreen extends FlxTypedSpriteGroup { - public static var optionHeight:Float = 120; - - public var parent:OptionsTree; - - public var curSelected:Int = 0; - public var id:Int = 0; - - private var __firstFrame:Bool = true; - - public var name:String; - public var desc:String; - /** - * The prefix to add to the translations ids. - **/ - public var prefix:String = ""; - - private var rawName(default, set):String; - private var rawDesc(default, set):String; - - public inline function getNameID(name):String { - return prefix + name + "-name"; - } - public inline function getDescID(name):String { - return prefix + name + "-desc"; - } - - public function getName(name:String, ?args:Array):String { - return TU.translate(getNameID(name), args); - } - public function getDesc(name:String, ?args:Array):String { - return TU.translate(getDescID(name), args); - } - public function translate(name:String, ?args:Array):String { - return TU.translate(prefix + name, args); - } - - public function new(name:String, desc:String, ?prefix:String, ?options:Array) { - super(); - rawName = name; - rawDesc = desc; - this.prefix = prefix != null ? prefix : ""; - if (options != null) for(o in options) add(o); - } - - function set_rawName(v:String) { - rawName = v; - this.name = TU.exists(rawName) ? TU.translate(rawName) : rawName; - return v; - } - - function set_rawDesc(v:String) { - rawDesc = v; - this.desc = TU.exists(rawDesc) ? TU.translate(rawDesc) : rawDesc; - return v; - } - - public function reloadStrings() { - this.rawName = rawName; - this.rawDesc = rawDesc; - - for(o in members) { - o.reloadStrings(); - } - } - - public override function update(elapsed:Float) { - super.update(elapsed); - - var controls = PlayerSettings.solo.controls; - - changeSelection((controls.UP_P ? -1 : 0) + (controls.DOWN_P ? 1 : 0) - FlxG.mouse.wheel); - x = id * FlxG.width; - for(k=>option in members) { - if(option == null) continue; - - var y:Float = ((FlxG.height - optionHeight) / 2) + ((k - curSelected) * optionHeight); - - option.selected = false; - option.y = __firstFrame ? y : CoolUtil.fpsLerp(option.y, y, 0.25); - option.x = x + (-50 + (Math.abs(Math.cos((option.y + (optionHeight / 2) - (FlxG.camera.scroll.y + (FlxG.camera.height / 2))) / (FlxG.camera.height * 1.25) * Math.PI)) * 150)); - } - if (__firstFrame) { - __firstFrame = false; - return; - } - - if (members.length > 0) { - members[curSelected].selected = true; - if (controls.ACCEPT || (FlxG.mouse.justReleased && Main.timeSinceFocus > 0.25)) { - members[curSelected].select(); - onSelect(members[curSelected]); - } - if (controls.LEFT_P) - members[curSelected].changeSelection(-1); - if (controls.RIGHT_P) - members[curSelected].changeSelection(1); - } - if (controls.BACK || FlxG.mouse.justReleasedRight) - close(); - } - - public function close() { - onClose(this); - } - - public function changeSelection(sel:Int, force:Bool = false) { - if (members.length <= 0 || (sel == 0 && !force)) return; - - CoolUtil.playMenuSFX(SCROLL); - curSelected = FlxMath.wrap(curSelected + sel, 0, members.length-1); - members[curSelected].selected = true; - updateMenuDesc(); - } - - public function updateMenuDesc(?customTxt:String) { - if (parent == null || parent.treeParent == null) return; - parent.treeParent.updateDesc(customTxt != null ? customTxt : members[curSelected].desc); - } - - public dynamic function onClose(o:OptionsScreen) {} - public dynamic function onSelect(o:OptionType) { - if(o is funkin.options.type.RadioButton) { - var orb:funkin.options.type.RadioButton = cast o; - for(e in members) { - if(e is funkin.options.type.RadioButton) { - var rb:funkin.options.type.RadioButton = cast e; - if(rb.forId == orb.forId && rb != o) { - rb.checked = false; - } - } - } - } - } -} \ No newline at end of file diff --git a/source/funkin/options/OptionsTree.hx b/source/funkin/options/OptionsTree.hx deleted file mode 100644 index 1f0bfcc7..00000000 --- a/source/funkin/options/OptionsTree.hx +++ /dev/null @@ -1,91 +0,0 @@ -// REMOVE THIS WHEN COMPLETE - -package funkin.options; - -class OptionsTree extends FlxTypedGroup { - public var lastMenu:OptionsScreen; - public var treeParent:TreeMenu; - public var wasClosing:Bool = false; - //public override function new() { - // super(); - //} - - public override function update(elapsed:Float) { - var last = members.last(); - if (last != null) - last.update(elapsed); - } - - public function updateAll(elapsed:Float) { - for (member in members) { - if(member == null) continue; - if(member.active && member.exists) { - member.update(elapsed); - } - } - } - - public override function draw() { - super.draw(); - if (lastMenu != null) { // manually draw lastMenu since it got removed from the members list - lastMenu.draw(); - } - } - - public function reloadStrings() { - for(o in members) { - o.reloadStrings(); - } - } - - public override function add(m:OptionsScreen) { - super.add(m); - setup(m); - clearLastMenu(); - wasClosing = false; - onMenuChange(); - return m; - } - public override function insert(pos:Int, m:OptionsScreen) { - var last = members.last(); - super.insert(pos, m); - setup(m); - if (last != members.last()) { - wasClosing = false; - onMenuChange(); - } - return m; - } - - public function setup(m:OptionsScreen) { - m.onClose = __subMenuClose; - m.id = members.indexOf(m); - m.parent = this; - m.update(0); - } - - function __subMenuClose(m:OptionsScreen) { - wasClosing = true; - clearLastMenu(); - lastMenu = m; - remove(m, true); - onMenuChange(); - onMenuClose(m); - } - - public function clearLastMenu() { - lastMenu = FlxDestroyUtil.destroy(lastMenu); - } - - public override function destroy() { - super.destroy(); - lastMenu = FlxDestroyUtil.destroy(lastMenu); - } - - public dynamic function onMenuChange() { - - } - - public dynamic function onMenuClose(m:OptionsScreen) { - } -} \ No newline at end of file diff --git a/source/funkin/options/TreeMenu.hx b/source/funkin/options/TreeMenu.hx index 9a8b933a..f5c0e177 100644 --- a/source/funkin/options/TreeMenu.hx +++ b/source/funkin/options/TreeMenu.hx @@ -32,8 +32,6 @@ class TreeMenu extends UIState { public var descLabel:FunkinText; public var bgLabel:FlxSprite; - public var optionsTree:OptionsTree; - var menuChangeTween:FlxTween; var __drawer:TreeMenuDrawer; var __treeCreated:Bool = false; @@ -74,7 +72,9 @@ class TreeMenu extends UIState { add(bgLabel); add(titleLabel); add(descLabel); - updateLabels(); + + FlxG.camera.scroll.x = -FlxG.width; + menuChanged(); __treeCreated = true; } diff --git a/source/funkin/options/type/EditorIconOption.hx b/source/funkin/options/type/EditorIconOption.hx deleted file mode 100644 index 5303fb01..00000000 --- a/source/funkin/options/type/EditorIconOption.hx +++ /dev/null @@ -1,13 +0,0 @@ -package funkin.options.type; - -import flixel.util.FlxColor; - -class EditorIconOption extends IconOption { - public var flashColor:FlxColor; - - public function new(name:String, desc:String, icon:String, callback:Void->Void, flashColor:FlxColor = 0) { - super(name, desc, icon, callback); - - this.flashColor = flashColor; - } -} \ No newline at end of file diff --git a/source/funkin/options/type/GithubIconOption.hx b/source/funkin/options/type/GithubIconOption.hx index be3567a8..16916b55 100644 --- a/source/funkin/options/type/GithubIconOption.hx +++ b/source/funkin/options/type/GithubIconOption.hx @@ -26,6 +26,7 @@ class GithubIconOption extends TextOption this.icon = new GithubUserIcon(user, size, waitUntilLoad); this.usePortrait = usePortrait; add(icon); + __text.x = 100; } } diff --git a/source/funkin/options/type/NewOption.hx b/source/funkin/options/type/NewOption.hx index 07f4fdf0..85181412 100644 --- a/source/funkin/options/type/NewOption.hx +++ b/source/funkin/options/type/NewOption.hx @@ -10,6 +10,7 @@ class NewOption extends TextOption { public function new(name:String, desc:String, callback:Void->Void) { super(name, desc, callback); + itemHeight = 160; __text.color = FlxColor.LIME; __text.x = 100; diff --git a/source/funkin/options/type/OptionType.hx b/source/funkin/options/type/OptionType.hx index 41f4302c..3d4d25f0 100644 --- a/source/funkin/options/type/OptionType.hx +++ b/source/funkin/options/type/OptionType.hx @@ -1,6 +1,7 @@ package funkin.options.type; import flixel.group.FlxSpriteGroup; +import flixel.util.FlxColor; import funkin.backend.system.Controls; import funkin.options.TreeMenu.ITreeOption; @@ -19,6 +20,8 @@ class OptionType extends FlxSpriteGroup implements ITreeOption { public var itemHeight:Float = 120; + public var editorFlashColor:FlxColor = FlxColor.WHITE; + public function new(text:String, desc:String) { super(); rawText = text; diff --git a/source/funkin/options/type/TextOption.hx b/source/funkin/options/type/TextOption.hx index 74ccfc2b..e859b668 100644 --- a/source/funkin/options/type/TextOption.hx +++ b/source/funkin/options/type/TextOption.hx @@ -6,7 +6,7 @@ import flixel.effects.FlxFlicker; * Option type that has text. **/ class TextOption extends OptionType { - public var suffix:String; + public var suffix(default, set):String; public var selectCallback:Void->Void; var __text:Alphabet; @@ -15,6 +15,12 @@ class TextOption extends OptionType { return text = v; } + function set_suffix(v:String) { + if (suffix == (suffix = v)) return v; + __text.text = text + suffix; + return v; + } + public function new(text:String, desc:String, ?suffix:String = "", ?selectCallback:Void->Void = null) { this.suffix = suffix; this.selectCallback = selectCallback;