package funkin.menus; import flixel.sound.FlxSound; import funkin.backend.FunkinText; import flixel.tweens.FlxTween; import flixel.util.FlxColor; import funkin.backend.FunkinText; import funkin.backend.scripting.Script; import funkin.backend.scripting.events.NameEvent; import funkin.backend.scripting.events.menu.MenuChangeEvent; import funkin.backend.scripting.events.menu.pause.*; import funkin.backend.system.Conductor; import funkin.backend.utils.FunkinParentDisabler; import funkin.editors.charter.Charter; import funkin.menus.StoryMenuState; import funkin.options.OptionsMenu; import funkin.options.keybinds.KeybindsOptions; class PauseSubState extends MusicBeatSubstate { public static var script:String = Flags.DEFAULT_PAUSE_SCRIPT; var grpMenuShit:FlxTypedGroup; var levelInfo:FunkinText; var levelDifficulty:FunkinText; var deathCounter:FunkinText; var multiplayerText:FunkinText; var menuItems:Array; var curSelected:Int = 0; var pauseMusic:FlxSound; public var pauseScript:Script; public var selectCall:NameEvent->Void; // Mainly for extern stuff that aren't scripts - Nex public var game:PlayState = PlayState.instance; // shortcut private var __cancelDefault:Bool = false; public function new(?items:Array, ?selectCall:NameEvent->Void) { super(); menuItems = items != null ? items : Flags.DEFAULT_PAUSE_ITEMS.copy(); this.selectCall = selectCall; } var parentDisabler:FunkinParentDisabler; override function create() { super.create(); if (menuItems.contains("Exit to charter") && !PlayState.chartingMode) menuItems.remove("Exit to charter"); add(parentDisabler = new FunkinParentDisabler()); pauseScript = Script.create(Paths.script(script)); pauseScript.setParent(this); pauseScript.load(); var event = EventManager.get(PauseCreationEvent).recycle(Flags.DEFAULT_PAUSE_MENU_MUSIC, menuItems); pauseScript.call('create', [event]); menuItems = event.options; if (Assets.exists(Paths.music(event.music))) { pauseMusic = FlxG.sound.load(Assets.getMusic(Paths.music(event.music)), 0, true); pauseMusic.persist = false; pauseMusic.group = FlxG.sound.defaultMusicGroup; pauseMusic.play(false, FlxG.random.int(0, Std.int(pauseMusic.length / 2))); } if (__cancelDefault = event.cancelled) return; var bg:FlxSprite = new FlxSprite().makeSolid(FlxG.width + 100, FlxG.height + 100, FlxColor.BLACK); bg.updateHitbox(); bg.alpha = 0; bg.screenCenter(); bg.scrollFactor.set(); add(bg); var multiplayerInfo:String = PlayState.opponentMode ? 'pause.coopMode' : PlayState.coopMode ? 'pause.opponentMode' : null; levelInfo = new FunkinText(20, 15, 0, PlayState.SONG.meta.displayName, 32, false); levelDifficulty = new FunkinText(20, 15, 0, TU.translateDiff(PlayState.difficulty).toUpperCase(), 32, false); deathCounter = new FunkinText(20, 15, 0, TU.translate("pause.deathCounter", [PlayState.deathCounter]), 32, false); multiplayerText = null; if(multiplayerInfo != null) multiplayerText = new FunkinText(20, 15, 0, TU.translate(multiplayerInfo), 32, false); for(k=>label in [levelInfo, levelDifficulty, deathCounter, multiplayerText]) { if(label == null) continue; label.scrollFactor.set(); label.alpha = 0; label.x = FlxG.width - (label.width + 20); label.y = 15 + (32 * k); FlxTween.tween(label, {alpha: 1, y: label.y + 5}, 0.4, {ease: FlxEase.quartInOut, startDelay: 0.3 * (k+1)}); add(label); } FlxTween.tween(bg, {alpha: 0.6}, 0.4, {ease: FlxEase.quartInOut}); grpMenuShit = new FlxTypedGroup(); add(grpMenuShit); for (i in 0...menuItems.length) { var pauseId = "pause." + TU.raw2Id(menuItems[i]); var songText:Alphabet = new Alphabet(0, (70 * i) + 30, TU.translate(pauseId), "bold"); songText.isMenuItem = true; songText.targetY = i; grpMenuShit.add(songText); } changeSelection(); camera = new FlxCamera(); camera.bgColor = 0; FlxG.cameras.add(camera, false); } override function createPost() { super.createPost(); pauseScript.call("postCreate"); game.updateDiscordPresence(); } override function update(elapsed:Float) { super.update(elapsed); if (pauseMusic != null && pauseMusic.volume < 0.5) pauseMusic.volume += 0.01 * elapsed; pauseScript.call("update", [elapsed]); if (__cancelDefault) return; var upP = controls.UP_P; var downP = controls.DOWN_P; var scroll = FlxG.mouse.wheel; if (upP || downP || scroll != 0) // like this we wont break mods that expect a 0 change event when calling sometimes - Nex changeSelection((upP ? -1 : 0) + (downP ? 1 : 0) - scroll); if (controls.ACCEPT) selectOption(); } public function selectOption() { var event = EventManager.get(NameEvent).recycle(menuItems[curSelected]); if (selectCall != null) selectCall(event); pauseScript.call("onSelectOption", [event]); if (event.cancelled) return; switch (event.name) { case "Resume": close(); case "Restart Song": parentDisabler.reset(); game.registerSmoothTransition(); FlxG.resetState(); case "Change Controls": persistentDraw = false; openSubState(new KeybindsOptions()); case "Change Options": FlxG.switchState(new OptionsMenu((_) -> FlxG.switchState(new PlayState()))); case "Exit to charter": FlxG.switchState(new Charter(PlayState.SONG.meta.name, PlayState.difficulty, PlayState.variation, false)); case "Exit to menu": if (PlayState.chartingMode && Charter.undos.unsaved) game.saveWarn(false); else { if (Charter.instance != null) Charter.instance.__clearStatics(); // prevents certain notes to disappear early when exiting - Nex game.strumLines.forEachAlive(function(grp) grp.notes.__forcedSongPos = Conductor.songPosition); CoolUtil.playMenuSong(); FlxG.switchState(PlayState.isStoryMode ? new StoryMenuState() : new FreeplayState()); } } } override function destroy() { if(camera != FlxG.camera && _cameras != null) { if(FlxG.cameras.list.contains(camera)) FlxG.cameras.remove(camera, true); } pauseScript.call("destroy"); pauseScript.destroy(); if(pauseMusic != null) { @:privateAccess FlxG.sound.destroySound(pauseMusic); } super.destroy(); } function changeSelection(change:Int = 0):Void { var event = EventManager.get(MenuChangeEvent).recycle(curSelected, FlxMath.wrap(curSelected + change, 0, menuItems.length-1), change, change != 0); pauseScript.call("onChangeItem", [event]); if (event.cancelled) return; curSelected = event.value; for (i=>item in grpMenuShit.members) { item.targetY = i - curSelected; item.alpha = (item.targetY == 0) ? 1 : 0.6; } } }