diff --git a/assets/songs/guns/cutscene.hx b/assets/songs/guns/cutscene.hx index af4d9be9..f445ad3a 100644 --- a/assets/songs/guns/cutscene.hx +++ b/assets/songs/guns/cutscene.hx @@ -1,7 +1,9 @@ import funkin.system.FunkinSprite; var tankman:FunkinSprite; -var tankTalk1, distorto:FlxSound; +var tankTalk, distorto:FlxSound; +var timers:Array = []; +var destroyDistorto:Bool = true; function create() { FlxTween.tween(FlxG.camera, {zoom: 1}, 0.7, {ease: FlxEase.quadInOut}); @@ -28,29 +30,39 @@ function create() { tankman.playAnim('tank'); tankTalk.play(); - new FlxTimer().start(4.1, function(ugly:FlxTimer) + timer(4.1, function() { FlxTween.tween(FlxG.camera, {zoom: game.defaultCamZoom * 1.4}, 0.4, {ease: FlxEase.quadOut}); FlxTween.tween(FlxG.camera, {zoom: game.defaultCamZoom * 1.3}, 0.7, {ease: FlxEase.quadInOut, startDelay: 0.45}); game.gf.playAnim('sad', false, "DANCE"); }); - new FlxTimer().start(11, function(tmr:FlxTimer) - { - game.remove(tankman); - tankman.destroy(); - - game.camHUD.visible = true; - game.dad.visible = true; + timer(11, function() { + destroyDistorto = false; distorto.fadeOut((Conductor.crochet / 1000) * 5, 0); - FlxTween.tween(FlxG.camera, {zoom: game.defaultCamZoom}, 0.7, {ease: FlxEase.quadInOut, startDelay: 0}); - close(); }); } +function timer(duration:Float, callBack:Void->Void) { + timers.push(new FlxTimer().start(duration, function(timer) { + timers.remove(timer); + callBack(); + })); +} + function focusOn(char) { var camPos = char.getCameraPosition(); game.camFollow.setPosition(camPos.x, camPos.y); camPos.put(); +} + +function destroy() { + game.remove(tankman); + game.camHUD.visible = true; + game.dad.visible = true; + for(timer in timers) timer.cancel(); + for(thing in [tankman, tankTalk]) thing.destroy(); + if(destroyDistorto) distorto.destroy(); + FlxTween.tween(FlxG.camera, {zoom: game.defaultCamZoom}, 0.7, {ease: FlxEase.quadInOut, startDelay: 0}); } \ No newline at end of file diff --git a/assets/songs/stress/cutscene.hx b/assets/songs/stress/cutscene.hx index dd55a3da..40951864 100644 --- a/assets/songs/stress/cutscene.hx +++ b/assets/songs/stress/cutscene.hx @@ -27,11 +27,6 @@ function create() { stressCutscene.play(); stressCutscene.onComplete = function() { - for(spr in [gf, tankman, pico]) { - spr.destroy(); - game.remove(spr); - } - game.camHUD.visible = game.dad.visible = game.gf.visible = true; close(); }; @@ -59,8 +54,6 @@ function create() { } function update(elapsed) { - if (FlxG.keys.justPressed.F5) - FlxG.resetState(); switch(step) { case 0: lipSync(tankman, 0, 16750); @@ -133,4 +126,14 @@ function focusOn(char, snap:Bool = false) { game.camFollow.setPosition(camPos.x, camPos.y); if(snap) FlxG.camera.snapToTarget(); camPos.put(); +} + +function destroy() { + game.boyfriend.animation.finishCallback = null; + game.camHUD.visible = game.boyfriend.visible = game.dad.visible = game.gf.visible = true; + for(thing in [bf, gf, tankman, pico, stressCutscene]) { + if(thing == null) continue; + if(thing != stressCutscene) game.remove(thing); + thing.destroy(); + } } \ No newline at end of file diff --git a/assets/songs/ugh/cutscene.hx b/assets/songs/ugh/cutscene.hx index f1167d57..f78c6117 100644 --- a/assets/songs/ugh/cutscene.hx +++ b/assets/songs/ugh/cutscene.hx @@ -2,6 +2,8 @@ import funkin.system.FunkinSprite; var tankman:FunkinSprite; var tankTalk1, tankTalk2, bfBeep, distorto:FlxSound; +var timers:Array = []; +var destroyDistorto:Bool = true; function create() { FlxTween.tween(FlxG.camera, {zoom: 1}, 0.7, {ease: FlxEase.quadInOut}); @@ -33,35 +35,46 @@ function create() { tankman.playAnim('1'); tankTalk1.play(); - new FlxTimer().start(3, function(tmr:FlxTimer) { + timer(3, function() { focusOn(game.boyfriend); - new FlxTimer().start(1.5, function(t) { + timer(1.5, function() { game.boyfriend.playAnim("singUP"); bfBeep.play(); - new FlxTimer().start(1.5, function(t) { + timer(1.5, function() { focusOn(game.dad); tankTalk2.play(); tankman.playAnim('2'); - new FlxTimer().start(6.1, function(t) { - game.remove(tankman); - tankman.destroy(); - - game.camHUD.visible = true; - game.dad.visible = true; + timer(6.1, function() { + destroyDistorto = false; distorto.fadeOut((Conductor.crochet / 1000) * 5, 0); - close(); - }, 1); - }, 1); - }, 1); - }, 1); + }); + }); + }); + }); +} + +function timer(duration:Float, callBack:Void->Void) { + timers.push(new FlxTimer().start(duration, function(timer) { + timers.remove(timer); + callBack(); + })); } function focusOn(char) { var camPos = char.getCameraPosition(); game.camFollow.setPosition(camPos.x, camPos.y); camPos.put(); +} + +function destroy() { + game.remove(tankman); + game.camHUD.visible = true; + game.dad.visible = true; + for(timer in timers) timer.cancel(); + for(thing in [tankTalk1, tankTalk2, bfBeep, tankman]) thing.destroy(); + if(destroyDistorto) distorto.destroy(); } \ No newline at end of file diff --git a/source/funkin/backend/FunkinSprite.hx b/source/funkin/backend/FunkinSprite.hx index 33a2fe71..ac23943a 100644 --- a/source/funkin/backend/FunkinSprite.hx +++ b/source/funkin/backend/FunkinSprite.hx @@ -43,6 +43,8 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I public var debugMode:Bool = false; public var animDatas:Map = []; + public var globalCurFrame(get, set):Int; + /** * ODD interval -> asynced; EVEN interval -> synced */ @@ -374,4 +376,11 @@ class FunkinSprite extends FlxSkewedSprite implements IBeatReceiver implements I return beatInterval = v; } + + @:noCompletion private inline function get_globalCurFrame() { + return animateAtlas != null ? (animateAtlas.anim.curFrame) : (animation.curAnim != null ? animation.curAnim.curFrame : 0); + } + @:noCompletion private inline function set_globalCurFrame(val:Int) { + return animateAtlas != null ? (animateAtlas.anim.curFrame = val) : (animation.curAnim != null ? animation.curAnim.curFrame = val : val); + } } diff --git a/source/funkin/backend/MusicBeatState.hx b/source/funkin/backend/MusicBeatState.hx index e519ed0e..33da58fa 100644 --- a/source/funkin/backend/MusicBeatState.hx +++ b/source/funkin/backend/MusicBeatState.hx @@ -228,7 +228,7 @@ class MusicBeatState extends FlxState implements IBeatReceiver public override function openSubState(subState:FlxSubState) { var e = event("onOpenSubState", EventManager.get(StateEvent).recycle(subState)); if (!e.cancelled) - super.openSubState(subState); + super.openSubState(e.substate is FlxSubState ? cast e.substate : subState); } public override function onResize(w:Int, h:Int) { @@ -256,11 +256,9 @@ class MusicBeatState extends FlxState implements IBeatReceiver if (e.cancelled) return false; - if (skipTransOut) + if (skipTransOut || (subState is MusicBeatTransition && cast(subState, MusicBeatTransition).newState != null)) return true; - if (subState is MusicBeatTransition && cast(subState, MusicBeatTransition).newState != null) - return true; - openSubState(new MusicBeatTransition(nextState)); + openSubState(new MusicBeatTransition(e.substate)); persistentUpdate = false; return false; } diff --git a/source/funkin/backend/MusicBeatSubstate.hx b/source/funkin/backend/MusicBeatSubstate.hx index d8ee8076..362de9f5 100644 --- a/source/funkin/backend/MusicBeatSubstate.hx +++ b/source/funkin/backend/MusicBeatSubstate.hx @@ -217,7 +217,7 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver public override function openSubState(subState:FlxSubState) { var e = event("onOpenSubState", EventManager.get(StateEvent).recycle(subState)); if (!e.cancelled) - super.openSubState(subState); + super.openSubState(e.substate is FlxSubState ? cast e.substate : subState); } public override function onResize(w:Int, h:Int) { @@ -235,7 +235,7 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver var e = event("onStateSwitch", EventManager.get(StateEvent).recycle(nextState)); if (e.cancelled) return false; - return super.switchTo(nextState); + return super.switchTo(e.substate); } public override function onFocus() { diff --git a/source/funkin/backend/scripting/events/StateEvent.hx b/source/funkin/backend/scripting/events/StateEvent.hx index d3f35783..7426f319 100644 --- a/source/funkin/backend/scripting/events/StateEvent.hx +++ b/source/funkin/backend/scripting/events/StateEvent.hx @@ -4,7 +4,7 @@ import flixel.FlxState; final class StateEvent extends CancellableEvent { /** - * Substate that is about to be opened/closed + * Substate or State that is about to be opened/closed */ - public var substate:FlxState; + public var substate:FlxState; // WHY is it named substate :sob: - Nex } \ No newline at end of file diff --git a/source/funkin/backend/system/MainState.hx b/source/funkin/backend/system/MainState.hx index 7d41aa7a..48921f6e 100644 --- a/source/funkin/backend/system/MainState.hx +++ b/source/funkin/backend/system/MainState.hx @@ -18,8 +18,8 @@ class MainState extends FlxState { public override function create() { super.create(); if (!initiated) { - Main.loadGameSettings(); Constants.init(); + Main.loadGameSettings(); } initiated = true; diff --git a/source/funkin/game/Character.hx b/source/funkin/game/Character.hx index 94021b4d..d480f54c 100644 --- a/source/funkin/game/Character.hx +++ b/source/funkin/game/Character.hx @@ -9,10 +9,12 @@ import funkin.backend.FunkinSprite; import funkin.backend.scripting.DummyScript; import funkin.backend.scripting.Script; import funkin.backend.scripting.events.DanceEvent; +import funkin.backend.scripting.events.CancellableEvent; import funkin.backend.scripting.events.DirectionAnimEvent; import funkin.backend.scripting.events.PlayAnimContext; import funkin.backend.scripting.events.PlayAnimEvent; import funkin.backend.scripting.events.PointEvent; +import funkin.backend.scripting.events.DrawEvent; import funkin.backend.system.Conductor; import funkin.backend.system.interfaces.IBeatReceiver; import funkin.backend.system.interfaces.IOffsetCompatible; @@ -147,6 +149,11 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset } public function tryDance() { + var event = new CancellableEvent(); + script.call("onTryDance", [event]); + if (event.cancelled) + return; + switch (lastAnimContext) { case SING | MISS: if (lastHit + (Conductor.stepCrochet * holdTime) < Conductor.songPosition) @@ -173,6 +180,9 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset tryDance(); } + public override function measureHit(curMeasure:Int) + script.call("measureHit", [curMeasure]); + public override function stepHit(curStep:Int) script.call("stepHit", [curStep]); @@ -196,17 +206,24 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset return (isPlayer != playerOffsets) != (flipX != __baseFlipped); public override function draw() { - if (isFlippedOffsets()) { - __reverseDrawProcedure = true; - flipX = !flipX; - scale.x *= -1; + var e = EventManager.get(DrawEvent).recycle(); + script.call("draw", [e]); - super.draw(); + if(!e.cancelled) { + if (isFlippedOffsets()) { + __reverseDrawProcedure = true; + flipX = !flipX; + scale.x *= -1; - flipX = !flipX; - scale.x *= -1; - __reverseDrawProcedure = false; - } else super.draw(); + super.draw(); + + flipX = !flipX; + scale.x *= -1; + __reverseDrawProcedure = false; + } else super.draw(); + } + + script.call("postDraw", [e]); } public var singAnims = ["singLEFT", "singDOWN", "singUP", "singRIGHT"]; diff --git a/source/funkin/game/GameOverSubstate.hx b/source/funkin/game/GameOverSubstate.hx index 093b27d0..d17b3332 100644 --- a/source/funkin/game/GameOverSubstate.hx +++ b/source/funkin/game/GameOverSubstate.hx @@ -41,8 +41,8 @@ class GameOverSubstate extends MusicBeatSubstate super(); this.x = x; this.y = y; - this.characterName = character != null ? character : Constants.DEFAULT_GAMEOVER_CHARACTER; this.player = player; + this.characterName = character != null ? character : Constants.DEFAULT_GAMEOVER_CHARACTER; this.gameOverSong = gameOverSong != null ? gameOverSong : Constants.DEFAULT_GAMEOVER_SONG; this.lossSFXName = lossSFX != null ? lossSFXName : Constants.DEFAULT_GAMEOVER_LOSS_SFX; this.retrySFX = retrySFX != null ? retrySFX : Constants.DEFAULT_GAMEOVER_RETRY_SFX; diff --git a/source/funkin/game/Note.hx b/source/funkin/game/Note.hx index 1b29ecbe..da1129f0 100644 --- a/source/funkin/game/Note.hx +++ b/source/funkin/game/Note.hx @@ -130,7 +130,7 @@ class Note extends FlxSprite (this.noteType != null && customTypePathExists(customType)) ? 'game/notes/${this.noteType}' : 'game/notes/default', @:privateAccess strumLine.strumScale * Constants.DEFAULT_NOTE_SCALE, animSuffix); if (PlayState.instance != null) - event = PlayState.instance.scripts.event("onNoteCreation", event); + event = PlayState.instance.gameAndCharsEvent("onNoteCreation", event); this.animSuffix = event.animSuffix; if (!event.cancelled) { @@ -184,7 +184,7 @@ class Note extends FlxSprite if (PlayState.instance != null) { PlayState.instance.splashHandler.getSplashGroup(splash); - PlayState.instance.scripts.event("onPostNoteCreation", event); + PlayState.instance.gameAndCharsEvent("onPostNoteCreation", event); } } diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index 3bfaf5d3..99fd0959 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -511,7 +511,7 @@ class PlayState extends MusicBeatState if (e.percent <= acc && (rating == null || rating.percent < e.percent)) rating = e; - var event = scripts.event("onRatingUpdate", EventManager.get(RatingUpdateEvent).recycle(rating, curRating)); + var event = gameAndCharsEvent("onRatingUpdate", EventManager.get(RatingUpdateEvent).recycle(rating, curRating)); if (!event.cancelled) curRating = event.rating; } @@ -525,6 +525,24 @@ class PlayState extends MusicBeatState return this.maxHealth = v; } + public inline function callOnCharacters(func:String, ?parameters:Array) { + if(strumLines != null) strumLines.forEachAlive(function (strLine:StrumLine) { + if (strLine.characters != null) for (character in strLine.characters) + if (character != null) character.script.call(func, parameters); + }); + } + + public inline function gameAndCharsCall(func:String, ?parameters:Array, ?charsFunc:String) { + scripts.call(func, parameters); + callOnCharacters(charsFunc != null ? charsFunc : func, parameters); + } + + public inline function gameAndCharsEvent(func:String, ?event:T, ?charsFunc:String):T { + scripts.event(func, event); + callOnCharacters(charsFunc != null ? charsFunc : func, [event]); + return event; + } + @:dox(hide) override public function create() { Note.__customNoteTypeExists = []; @@ -685,9 +703,9 @@ class PlayState extends MusicBeatState // HUD INITIALIZATION & CAMERA INITIALIZATION #if REGION var event = EventManager.get(AmountEvent).recycle(Constants.DEFAULT_STRUM_AMOUNT); - if (!scripts.event("onPreGenerateStrums", event).cancelled) { + if (!gameAndCharsEvent("onPreGenerateStrums", event).cancelled) { generateStrums(event.amount); - scripts.event("onPostGenerateStrums", event); + gameAndCharsEvent("onPostGenerateStrums", event); } for(str in strumLines) @@ -787,7 +805,7 @@ class PlayState extends MusicBeatState __updateNote_event = EventManager.get(NoteUpdateEvent); - scripts.call("postCreate"); + gameAndCharsCall("postCreate", null, "gamePostCreate"); } /** @@ -851,7 +869,7 @@ class PlayState extends MusicBeatState _startCountdownCalled = true; inCutscene = false; - if (scripts.event("onStartCountdown", new CancellableEvent()).cancelled) return; + if (gameAndCharsEvent("onStartCountdown", new CancellableEvent()).cancelled) return; } startedCountdown = true; @@ -864,14 +882,14 @@ class PlayState extends MusicBeatState { countdown(swagCounter++); }, introLength); - scripts.call("onPostStartCountdown"); + gameAndCharsCall("onPostStartCountdown"); } /** * Creates a fake countdown. */ public function countdown(swagCounter:Int) { - var event:CountdownEvent = scripts.event("onCountdown", EventManager.get(CountdownEvent).recycle( + var event:CountdownEvent = gameAndCharsEvent("onCountdown", EventManager.get(CountdownEvent).recycle( swagCounter, 1, introSounds[swagCounter], @@ -914,12 +932,12 @@ class PlayState extends MusicBeatState event.spriteTween = tween; event.cancelled = false; - scripts.event("onPostCountdown", event); + gameAndCharsEvent("onPostCountdown", event); } @:dox(hide) function startSong():Void { - scripts.call("onSongStart"); + gameAndCharsCall("onSongStart"); startingSong = false; inst.onComplete = endSong; @@ -946,7 +964,7 @@ class PlayState extends MusicBeatState updateDiscordPresence(); - scripts.call("onStartSong"); + gameAndCharsCall("onStartSong"); } public override function destroy() { @@ -1024,7 +1042,7 @@ class PlayState extends MusicBeatState @:dox(hide) override function openSubState(SubState:FlxSubState) { - var event = scripts.event("onSubstateOpen", EventManager.get(StateEvent).recycle(SubState)); + var event = gameAndCharsEvent("onSubstateOpen", EventManager.get(StateEvent).recycle(SubState)); if (!postCreated) MusicBeatState.skipTransIn = true; @@ -1044,13 +1062,13 @@ class PlayState extends MusicBeatState startTimer.active = false; } - super.openSubState(SubState); + super.openSubState(event.substate is FlxSubState ? cast event.substate : SubState); } @:dox(hide) override function closeSubState() { - var event = scripts.event("onSubstateClose", EventManager.get(StateEvent).recycle(subState)); + var event = gameAndCharsEvent("onSubstateClose", EventManager.get(StateEvent).recycle(subState)); if (event.cancelled) return; if (paused) @@ -1084,7 +1102,7 @@ class PlayState extends MusicBeatState inst.resume(); vocals.resume(); } - scripts.call("onFocus"); + gameAndCharsCall("onFocus"); updateDiscordPresence(); super.onFocus(); } @@ -1097,7 +1115,7 @@ class PlayState extends MusicBeatState inst.pause(); vocals.pause(); } - scripts.call("onFocusLost"); + gameAndCharsCall("onFocusLost"); updateDiscordPresence(); super.onFocusLost(); } @@ -1116,14 +1134,14 @@ class PlayState extends MusicBeatState strumLine.vocals.play(); } vocals.play(); - scripts.call("onVocalsResync"); + gameAndCharsCall("onVocalsResync"); } /** * Pauses the game. */ public function pauseGame() { - var e = scripts.event("onGamePause", new CancellableEvent()); + var e = gameAndCharsEvent("onGamePause", new CancellableEvent()); if (e.cancelled) return; persistentUpdate = false; @@ -1291,7 +1309,7 @@ class PlayState extends MusicBeatState pos.x /= r; pos.y /= r; - var event = scripts.event("onCameraMove", EventManager.get(CamMoveEvent).recycle(pos, strumLines.members[curCameraTarget], r)); + var event = gameAndCharsEvent("onCameraMove", EventManager.get(CamMoveEvent).recycle(pos, strumLines.members[curCameraTarget], r)); if (!event.cancelled) camFollow.setPosition(pos.x, pos.y); } @@ -1339,7 +1357,10 @@ class PlayState extends MusicBeatState if (event == null) return; if (event.params == null) return; - if (scripts.event("onEvent", EventManager.get(EventGameEvent).recycle(event)).cancelled) return; + var e = EventManager.get(EventGameEvent).recycle(event); + gameAndCharsEvent("onEvent", e); + if (e.cancelled) return; + var event = e.event; switch(event.name) { case "HScript Call": @@ -1399,7 +1420,7 @@ class PlayState extends MusicBeatState */ public function gameOver(?character:Character, ?deathCharID:String, ?gameOverSong:String, ?lossSFX:String, ?retrySFX:String) { var charToUse:Character = character.getDefault(opponentMode ? dad : boyfriend); // Imma still make it check null later just in case dad or bf are also null for some weird scripts - Nex - var event:GameOverEvent = scripts.event("onGameOver", EventManager.get(GameOverEvent).recycle( + var event:GameOverEvent = gameAndCharsEvent("onGameOver", EventManager.get(GameOverEvent).recycle( charToUse == null ? 0 : charToUse.x, charToUse == null ? 0 : charToUse.y, charToUse, @@ -1428,7 +1449,7 @@ class PlayState extends MusicBeatState openSubState(new GameOverSubstate(event.x, event.y, event.deathCharID, event.isPlayer, event.gameOverSong, event.lossSFX, event.retrySFX)); - scripts.event("onPostGameOver", event); + gameAndCharsEvent("onPostGameOver", event); } /** @@ -1436,7 +1457,7 @@ class PlayState extends MusicBeatState */ public function endSong():Void { - scripts.call("onSongEnd"); + gameAndCharsCall("onSongEnd"); canPause = false; inst.volume = 0; vocals.volume = 0; @@ -1557,7 +1578,7 @@ class PlayState extends MusicBeatState var directionID:Null = note == null ? direction : note.strumID; if (playerID == null || directionID == null || playerID == -1) return; - var event:NoteMissEvent = scripts.event("onPlayerMiss", EventManager.get(NoteMissEvent).recycle(note, -10, 1, muteVocalsOnMiss, note != null ? -0.0475 : -0.04, Paths.soundRandom('missnote', 1, 3), FlxG.random.float(0.1, 0.2), note == null, combo > 5, "sad", true, true, "miss", strumLines.members[playerID].characters, playerID, note != null ? note.noteType : null, directionID, 0)); + var event:NoteMissEvent = gameAndCharsEvent("onPlayerMiss", EventManager.get(NoteMissEvent).recycle(note, -10, 1, muteVocalsOnMiss, note != null ? -0.0475 : -0.04, Paths.soundRandom('missnote', 1, 3), FlxG.random.float(0.1, 0.2), note == null, combo > 5, "sad", true, true, "miss", strumLines.members[playerID].characters, playerID, note != null ? note.noteType : null, directionID, 0)); strumLine.onMiss.dispatch(event); if (event.cancelled) return; @@ -1641,11 +1662,11 @@ class PlayState extends MusicBeatState var event:NoteHitEvent; if (strumLine != null && !strumLine.cpu) - event = scripts.event("onPlayerHit", EventManager.get(NoteHitEvent).recycle(false, !note.isSustainNote, !note.isSustainNote, note, strumLine.characters, true, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, score, note.isSustainNote ? null : accuracy, 0.023, daRating, Options.splashesEnabled && !note.isSustainNote && daRating == "sick")); + event = gameAndCharsEvent("onPlayerHit", EventManager.get(NoteHitEvent).recycle(false, !note.isSustainNote, !note.isSustainNote, note, strumLine.characters, true, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, score, note.isSustainNote ? null : accuracy, 0.023, daRating, Options.splashesEnabled && !note.isSustainNote && daRating == "sick")); else - event = scripts.event("onDadHit", EventManager.get(NoteHitEvent).recycle(false, false, false, note, strumLine.characters, false, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, 0, null, 0, daRating, false)); + event = gameAndCharsEvent("onDadHit", EventManager.get(NoteHitEvent).recycle(false, false, false, note, strumLine.characters, false, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, 0, null, 0, daRating, false)); strumLine.onHit.dispatch(event); - scripts.event("onNoteHit", event); + gameAndCharsEvent("onNoteHit", event); if (!event.cancelled) { if (!note.isSustainNote) { diff --git a/source/funkin/game/SplashHandler.hx b/source/funkin/game/SplashHandler.hx index 9ee68046..fbc7ddca 100644 --- a/source/funkin/game/SplashHandler.hx +++ b/source/funkin/game/SplashHandler.hx @@ -41,7 +41,7 @@ class SplashHandler extends FlxTypedGroup { public function showSplash(name:String, strum:Strum) { __grp = getSplashGroup(name); var event = funkin.backend.scripting.EventManager.get(funkin.backend.scripting.events.DynamicEvent).recycle(__grp.showOnStrum(strum), strum); - event = PlayState.instance.scripts.event("onSplashShown", event); + event = PlayState.instance.gameAndCharsEvent("onSplashShown", event); if (!event.cancelled) add(event.value1); diff --git a/source/funkin/game/Stage.hx b/source/funkin/game/Stage.hx index f42cc90e..4cabb52d 100644 --- a/source/funkin/game/Stage.hx +++ b/source/funkin/game/Stage.hx @@ -68,7 +68,7 @@ class Stage extends FlxBasic implements IBeatReceiver { } if (PlayState.instance != null) { - var event = PlayState.instance.scripts.event("onStageXMLParsed", EventManager.get(StageXMLEvent).recycle(this, stageXML, elems)); + var event = PlayState.instance.gameAndCharsEvent("onStageXMLParsed", EventManager.get(StageXMLEvent).recycle(this, stageXML, elems)); elems = event.elems; } @@ -138,7 +138,7 @@ class Stage extends FlxBasic implements IBeatReceiver { } if(PlayState.instance != null) { - sprite = PlayState.instance.scripts.event("onStageNodeParsed", EventManager.get(StageNodeEvent).recycle(this, node, sprite, node.name)).sprite; + sprite = PlayState.instance.gameAndCharsEvent("onStageNodeParsed", EventManager.get(StageNodeEvent).recycle(this, node, sprite, node.name)).sprite; } if (sprite != null) { diff --git a/source/funkin/game/StrumLine.hx b/source/funkin/game/StrumLine.hx index f2982ad8..66985469 100644 --- a/source/funkin/game/StrumLine.hx +++ b/source/funkin/game/StrumLine.hx @@ -247,7 +247,7 @@ class StrumLine extends FlxTypedGroup { __justReleased.push(s.__getJustReleased(this)); } - var event = PlayState.instance.scripts.event("onInputUpdate", EventManager.get(InputSystemEvent).recycle(__pressed, __justPressed, __justReleased, this, id)); + var event = PlayState.instance.gameAndCharsEvent("onInputUpdate", EventManager.get(InputSystemEvent).recycle(__pressed, __justPressed, __justReleased, this, id)); if (event.cancelled) return; __pressed = CoolUtil.getDefault(event.pressed, []); @@ -282,7 +282,7 @@ class StrumLine extends FlxTypedGroup { forEach(function(str:Strum) { str.updatePlayerInput(str.__getPressed(this), str.__getJustPressed(this), str.__getJustReleased(this)); }); - PlayState.instance.scripts.call("onPostInputUpdate"); + PlayState.instance.gameAndCharsCall("onPostInputUpdate"); } public inline function addHealth(health:Float) @@ -317,7 +317,7 @@ class StrumLine extends FlxTypedGroup { var event = EventManager.get(StrumCreationEvent).recycle(babyArrow, PlayState.instance.strumLines.members.indexOf(this), i, animPrefix); event.__doAnimation = !MusicBeatState.skipTransIn; - event = PlayState.instance.scripts.event("onStrumCreation", event); + event = PlayState.instance.gameAndCharsEvent("onStrumCreation", event); if (!event.cancelled) { babyArrow.frames = Paths.getFrames(event.sprite); @@ -348,7 +348,7 @@ class StrumLine extends FlxTypedGroup { insert(i, babyArrow); - PlayState.instance.scripts.event("onPostStrumCreation", event); + PlayState.instance.gameAndCharsEvent("onPostStrumCreation", event); return babyArrow; } diff --git a/source/funkin/game/cutscenes/Cutscene.hx b/source/funkin/game/cutscenes/Cutscene.hx index cd572812..ed1f2c60 100644 --- a/source/funkin/game/cutscenes/Cutscene.hx +++ b/source/funkin/game/cutscenes/Cutscene.hx @@ -1,18 +1,75 @@ package funkin.game.cutscenes; +import funkin.backend.scripting.events.NameEvent; +import funkin.menus.PauseSubState; + /** * Substate made for cutscenes. */ class Cutscene extends MusicBeatSubstate { var __callback:Void->Void; + var __pausable:Bool; var game:PlayState = PlayState.instance; - public function new(callback:Void->Void) { + public var paused:Bool = false; + public var pauseItems:Array; + public var skippable(default, set):Bool; + + public function set_skippable(val:Bool):Bool { + if(!val) pauseItems.remove('Skip Cutscene'); + else if(!pauseItems.contains('Skip Cutscene')) pauseItems.insert(1, 'Skip Cutscene'); + return skippable = val; + } + + public function new(callback:Void->Void, allowPause:Bool = true, canSkip:Bool = true) { super(); __callback = callback; + __pausable = allowPause; + + pauseItems = Constants.DEFAULT_CUTSCENE_PAUSE_ITEMS; + skippable = canSkip; } + public override function update(elapsed:Float) { super.update(elapsed); + if (pauseCheck()) pauseCutscene(); + } + + public function pauseCheck():Bool + return controls.PAUSE && __pausable; + + var _before:Array = [false, true]; + public function pauseCutscene() { + _before = [game.persistentUpdate, game.persistentDraw]; + game.persistentUpdate = persistentUpdate = false; + game.persistentDraw = persistentDraw = true; + paused = true; + + openSubState(new PauseSubState(pauseItems, selectPauseOption)); + } + + public override function closeSubState() { + if (paused) { + game.persistentUpdate = _before[0]; + game.persistentDraw = _before[1]; + paused = false; + } + + super.closeSubState(); + } + + public function onSkipCutscene(event:NameEvent) close(); + public function onRestartCutscene(event:NameEvent) event.name = "Restart Song"; // Making the whole PlayState restart just for precaution - Nex + public function onResumeCutscene(event:NameEvent) event.name = "Resume"; + + public function selectPauseOption(event:NameEvent) { + switch(event.name) { + case 'Skip Cutscene': onSkipCutscene(event); + case 'Restart Cutscene': onRestartCutscene(event); + case 'Resume Cutscene': onResumeCutscene(event); + } + + return true; } public override function close() { diff --git a/source/funkin/game/cutscenes/DialogueCutscene.hx b/source/funkin/game/cutscenes/DialogueCutscene.hx index b3b16c8e..39713943 100644 --- a/source/funkin/game/cutscenes/DialogueCutscene.hx +++ b/source/funkin/game/cutscenes/DialogueCutscene.hx @@ -13,7 +13,7 @@ import haxe.xml.Access; /** * Substate made for dialogue cutscenes. To use it in a scripted cutscene, call `startDialogue`. */ -class DialogueCutscene extends Cutscene { +class DialogueCutscene extends ScriptedCutscene { public var dialoguePath:String; public var dialogueData:Access; @@ -28,23 +28,29 @@ class DialogueCutscene extends Cutscene { public var curMusic:FlxSound = null; public static var cutscene:DialogueCutscene; - public var dialogueScript:Script; + public var dialogueScript(get, set):Script; public function set_curLine(val:DialogueLine) { lastLine = curLine; return curLine = val; } + // Backwards comapt funcs - Nex + public function set_dialogueScript(val:Script) return script = val; + public function get_dialogueScript() return script; + public function new(dialoguePath:String, callback:Void->Void) { - super(callback); - this.dialoguePath = dialoguePath; + super(this.dialoguePath = dialoguePath, callback); + camera = dialogueCamera = new FlxCamera(); dialogueCamera.bgColor = 0; FlxG.cameras.add(dialogueCamera, false); - dialogueScript = Script.create(Paths.script(Path.withoutExtension(dialoguePath), null, dialoguePath.startsWith('assets'))); - dialogueScript.setParent(cutscene = this); - dialogueScript.load(); + cutscene = this; + } + + public override function onErrorScriptLoading() { + Logs.trace('Could not find script for dialogue cutscene at "${scriptPath}"', WARNING, YELLOW); } var parentDisabler:FunkinParentDisabler; @@ -110,22 +116,21 @@ class DialogueCutscene extends Cutscene { next(true); } catch(e) { - Logs.trace('Error while loading dialogue at ${dialoguePath}: ${e.toString()}', ERROR); - trace(CoolUtil.getLastExceptionStack()); + var message:String = e.toString(); + Logs.trace('Error while loading dialogue at ${dialoguePath}: $message', ERROR, RED); + dialogueScript.call("loadingError", [message]); close(); } + dialogueScript.call("postCreate"); } - public override function beatHit(curBeat:Int) { - super.beatHit(curBeat); - dialogueScript.call("beatHit", [curBeat]); + public override function pauseCheck():Bool { + return super.pauseCheck() && !controls.ACCEPT; // Avoids next() and pause() being called at the same time - Nex } public override function update(elapsed:Float) { super.update(elapsed); - dialogueScript.call("update", [elapsed]); - if(controls.ACCEPT) { if(dialogueBox.dialogueEnded) next(); else dialogueBox.text.skip(); @@ -193,8 +198,6 @@ class DialogueCutscene extends Cutscene { public override function destroy() { if(curMusic != null && !curMusic.persist) curMusic.destroy(); - dialogueScript.call("destroy"); - dialogueScript.destroy(); super.destroy(); cutscene = null; diff --git a/source/funkin/game/cutscenes/ScriptedCutscene.hx b/source/funkin/game/cutscenes/ScriptedCutscene.hx index b305cca0..20a01aa2 100644 --- a/source/funkin/game/cutscenes/ScriptedCutscene.hx +++ b/source/funkin/game/cutscenes/ScriptedCutscene.hx @@ -1,20 +1,25 @@ package funkin.game.cutscenes; +import haxe.io.Path; +import flixel.FlxSubState; import funkin.backend.scripting.DummyScript; import funkin.backend.scripting.Script; +import funkin.backend.scripting.events.CancellableEvent; +import funkin.backend.scripting.events.NameEvent; +import funkin.backend.scripting.events.StateEvent; /** * Substate made for scripted cutscenes. * To add cutscenes to your songs, add a `cutscene.hx` file in your song's directory (ex: `songs/song/cutscene.hx`) */ class ScriptedCutscene extends Cutscene { + var scriptPath:String; var script:Script; - var scriptPath:String; public function new(scriptPath:String, callback:Void->Void) { super(callback); - script = Script.create(this.scriptPath = scriptPath); + script = Script.create(this.scriptPath = Paths.script(Path.withoutExtension(scriptPath), null, scriptPath.startsWith('assets'))); script.setPublicMap(PlayState.instance.scripts.publicVariables); script.setParent(this); script.load(); @@ -22,32 +27,91 @@ class ScriptedCutscene extends Cutscene { public override function create() { super.create(); - trace("fuck you"); script.call("create"); - if (Std.isOfType(script, DummyScript)) { - Logs.trace('Could not find script for scripted cutscene at ${scriptPath}', ERROR, RED); - close(); - } + + if(Std.isOfType(script, DummyScript)) + onErrorScriptLoading(); } - public override function update(elapsed:Float) { + public function onErrorScriptLoading() { + Logs.trace('Could not find script for scripted cutscene at "${scriptPath}"', ERROR, RED); + close(); + } + + public override function pauseCheck():Bool + { + var shouldClose = super.pauseCheck(); + script.call("pauseCheck", [shouldClose]); + return shouldClose; + } + + public override function update(elapsed:Float) + { script.call("update", [elapsed]); super.update(elapsed); script.call("postUpdate", [elapsed]); } - public override function beatHit(curBeat:Int) { + public override function pauseCutscene() + { + var event = new CancellableEvent(); + script.call("pauseCutscene", [event]); + if(!event.cancelled) super.pauseCutscene(); + } + + public override function onSkipCutscene(event:NameEvent) + { + script.call("onSkipCutscene", [event]); + if(!event.cancelled) super.onSkipCutscene(event); + } + + public override function onRestartCutscene(event:NameEvent) + { + script.call("onRestartCutscene", [event]); + if(!event.cancelled) super.onRestartCutscene(event); + } + + public override function onResumeCutscene(event:NameEvent) + { + script.call("onResumeCutscene", [event]); + if(!event.cancelled) super.onResumeCutscene(event); + } + + public override function measureHit(curMeasure:Int) + { + super.measureHit(curMeasure); + script.call("measureHit", [curMeasure]); + } + + public override function beatHit(curBeat:Int) + { super.beatHit(curBeat); script.call("beatHit", [curBeat]); } - public override function stepHit(curStep:Int) { + public override function stepHit(curStep:Int) + { super.stepHit(curStep); script.call("stepHit", [curStep]); } + public override function openSubState(sub:FlxSubState) + { + var event = EventManager.get(StateEvent).recycle(sub); + script.call("onSubstateClose", [event]); + if(!event.cancelled) super.openSubState(event.substate is FlxSubState ? cast event.substate : sub); + } + + public override function closeSubState() + { + var event = EventManager.get(StateEvent).recycle(subState); + script.call("onSubstateOpen", [event]); + if(!event.cancelled) super.closeSubState(); + } + public override function destroy() { script.call("destroy"); + script.destroy(); super.destroy(); } diff --git a/source/funkin/game/cutscenes/VideoCutscene.hx b/source/funkin/game/cutscenes/VideoCutscene.hx index d328f125..8906dfa0 100644 --- a/source/funkin/game/cutscenes/VideoCutscene.hx +++ b/source/funkin/game/cutscenes/VideoCutscene.hx @@ -1,5 +1,6 @@ package funkin.game.cutscenes; +import flixel.tweens.FlxTween; import flixel.addons.display.FlxBackdrop; import flixel.util.FlxColor; import flixel.util.FlxTimer; @@ -12,7 +13,7 @@ import haxe.xml.Access; import sys.io.File; #end #if VIDEO_CUTSCENES -import hxvlc.flixel.FlxVideo; +import hxvlc.flixel.FlxVideoSprite; #end /** @@ -24,22 +25,24 @@ class VideoCutscene extends Cutscene { var localPath:String; #if VIDEO_CUTSCENES - var video:FlxVideo; - public var skippable:Bool = true; - #end + var video:FlxVideoSprite; + final mutex = new sys.thread.Mutex(); + var cutsceneCamera:FlxCamera; var text:FunkinText; var loadingBackdrop:FlxBackdrop; - var videoReady:Bool = false; + private var __loaded:Bool = false; var bg:FlxSprite; var subtitle:FunkinText; public var subtitles:Array = []; + var curSubtitle:Int = 0; + #end public function new(path:String, callback:Void->Void) { - super(callback); + super(callback, false); localPath = Assets.getPath(this.path = path); } @@ -48,21 +51,17 @@ class VideoCutscene extends Cutscene { // TODO: get vlc to stop autoloading those goddamn subtitles (use different file ext??) + #if VIDEO_CUTSCENES cutsceneCamera = new FlxCamera(); cutsceneCamera.bgColor = 0; FlxG.cameras.add(cutsceneCamera, false); - #if VIDEO_CUTSCENES parseSubtitles(); - video = new FlxVideo(); - video.onEndReached.add(function() - { - video.dispose(); - FlxG.removeChild(video); - }); - video.onEndReached.add(close); - FlxG.addChildBelowMouse(video); + add(video = new FlxVideoSprite()); + video.antialiasing = true; + video.autoPause = false; // Imma handle it better inside this class, mainly because of the pause menu - Nex + video.bitmap.onEndReached.add(close); //cover = new FlxSprite(0, FlxG.height * 0.85).makeSolid(FlxG.width + 50, FlxG.height + 50, 0xFF000000); //cover.scrollFactor.set(0, 0); @@ -77,41 +76,44 @@ class VideoCutscene extends Cutscene { subtitle.alignment = CENTER; subtitle.visible = false; - if (localPath.startsWith("[ZIP]")) { - text = new FunkinText(10, 10, Std.int(FlxG.width / 2), "Loading video..."); - text.cameras = [cutsceneCamera]; - text.visible = false; - @:privateAccess - text.regenGraphic(); - add(text); + text = new FunkinText(10, 10, Std.int(FlxG.width / 2), "Loading video..."); + text.cameras = [cutsceneCamera]; + text.visible = false; + @:privateAccess + text.regenGraphic(); + add(text); - loadingBackdrop = new FlxBackdrop(text.graphic, X); - loadingBackdrop.y = FlxG.height - 20 - loadingBackdrop.height; - loadingBackdrop.cameras = [cutsceneCamera]; - add(loadingBackdrop); + loadingBackdrop = new FlxBackdrop(text.graphic, X); + loadingBackdrop.y = FlxG.height - 20 - loadingBackdrop.height; + loadingBackdrop.velocity.x = 70; + loadingBackdrop.cameras = [cutsceneCamera]; + add(loadingBackdrop); - // ZIP PATH: EXPORT - // TODO: this but better and more ram friendly - localPath = './.temp/video-${curVideo++}.${Path.extension(path)}'; - Main.execAsync(function() { + loadingBackdrop.alpha = 0; + FlxTween.tween(loadingBackdrop, {alpha: 1}, 0.5, {ease: FlxEase.sineInOut}); + + Main.execAsync(function() { + if (localPath.startsWith("[ZIP]")) { + // ZIP PATH: EXPORT + // TODO: this but better and more ram friendly + localPath = './.temp/video-${curVideo++}.mp4'; File.saveBytes(localPath, Assets.getBytes(path)); - videoReady = true; - }); - } else { - if (video.load(localPath)) - new FlxTimer().start(0.001, function(tmr:FlxTimer) { - video.play(); - }); - else - close(); - } + } + + if(video.load(localPath)) new FlxTimer().start(0.001, function(_) {mutex.acquire(); onReady(); mutex.release();}); + else { mutex.acquire(); close(); mutex.release(); } + }); + add(bg); add(subtitle); - #end cameras = [cutsceneCamera]; + #else + close(); + #end } + #if VIDEO_CUTSCENES public function parseSubtitles() { var subtitlesPath = '${Path.withoutExtension(path)}.srt'; @@ -141,22 +143,18 @@ class VideoCutscene extends Cutscene { } if (subtitleText.length <= 0) continue; // empty subtitle, skipping var lastSub = subtitles.last(); - if (lastSub != null && lastSub.subtitle == "" && lastSub.time > beginTime) + if (lastSub != null && lastSub.subtitle == "" && lastSub.time >= beginTime) subtitles.pop(); // remove last subtitle auto reset to prevent bugs subtitles.push({ - subtitle: subtitleText.join("."), - time: beginTime * 1000, - color: 0xFFFFFFFF // todo + subtitle: subtitleText.join(""), + time: beginTime * 1000 }); subtitles.push({ subtitle: "", - time: endTime * 1000, - color: 0xFFFFFFFF + time: endTime * 1000 }); } } - - trace(subtitles); } public static function splitTime(str:String):Float { @@ -173,50 +171,56 @@ class VideoCutscene extends Cutscene { return time; } + public inline function onReady() { + FlxTween.cancelTweensOf(loadingBackdrop); + FlxTween.tween(loadingBackdrop, {alpha: 0}, 0.7, {ease: FlxEase.sineInOut, onComplete: function(_) { + loadingBackdrop.destroy(); + text.destroy(); + }}); + + __pausable = __loaded = true; + video.play(); + } + public override function update(elapsed:Float) { super.update(elapsed); - #if VIDEO_CUTSCENES - if (videoReady) { - videoReady = false; - if (video.load(localPath)) - new FlxTimer().start(0.001, function(tmr:FlxTimer) { - video.play(); - }); - else - { - close(); - - if (loadingBackdrop != null) - loadingBackdrop.visible = false; - - return; - } - - if (loadingBackdrop != null) - loadingBackdrop.visible = false; + if(loadingBackdrop == null && curSubtitle < subtitles.length && haxe.Int64.toInt(video.bitmap.time) >= subtitles[curSubtitle].time) { + setSubtitle(subtitles[curSubtitle]); + curSubtitle++; } - @:privateAccess - var time:Int64 = video.time; + } - while (subtitles.length > 0 && subtitles[0].time < Math.round(FPHelper.i64ToDouble(time.low, time.high))) - setSubtitle(subtitles.shift()); + @:dox(hide) override public function onFocus() { + if(FlxG.autoPause && !paused) video.resume(); + super.onFocus(); + } - if (loadingBackdrop != null) { - loadingBackdrop.x -= elapsed * FlxG.width * 0.5; - } - if (skippable && video.isPlaying && controls.ACCEPT) { - video.onEndReached.dispatch(); - } - #else - close(); - #end + @:dox(hide) override public function onFocusLost() { + if(FlxG.autoPause && !paused) video.pause(); + super.onFocusLost(); + } + + public override function pauseCutscene() { + video.pause(); + super.pauseCutscene(); + } + + public override function onResumeCutscene(event) { + video.resume(); + super.onResumeCutscene(event); + } + + public override function onRestartCutscene(event) { + closeSubState(); + video.bitmap.position = curSubtitle = 0; + setSubtitle({time: -1, subtitle: ""}); + video.resume(); } public function setSubtitle(sub:CutsceneSubtitle) { if (bg.visible = subtitle.visible = (sub.subtitle.length > 0)) { subtitle.text = sub.subtitle; - subtitle.color = sub.color; subtitle.screenCenter(X); bg.scale.set(subtitle.width + 8, subtitle.height + 8); bg.updateHitbox(); @@ -228,10 +232,10 @@ class VideoCutscene extends Cutscene { FlxG.cameras.remove(cutsceneCamera, true); super.destroy(); } + #end } typedef CutsceneSubtitle = { var time:Float; // time in ms var subtitle:String; // subtitle text - var color:FlxColor; } diff --git a/source/funkin/game/cutscenes/dialogue/DialogueBox.hx b/source/funkin/game/cutscenes/dialogue/DialogueBox.hx index 8885f56a..988562f3 100644 --- a/source/funkin/game/cutscenes/dialogue/DialogueBox.hx +++ b/source/funkin/game/cutscenes/dialogue/DialogueBox.hx @@ -87,8 +87,11 @@ class DialogueBox extends FunkinSprite { } } catch(e) { active = false; - Logs.trace('Couldn\'t load dialogue box "$name": ${e.toString()}', ERROR); + var message:String = e.toString(); + Logs.trace('Couldn\'t load dialogue box "$name": $message', ERROR, RED); + dialogueBoxScript.call("loadingError", [message]); } + defaultTextTypeSFX = [FlxG.sound.load(textTypeSFX)]; FlxG.sound.cache(nextSFX); dialogueBoxScript.call("postCreate"); diff --git a/source/funkin/game/cutscenes/dialogue/DialogueCharacter.hx b/source/funkin/game/cutscenes/dialogue/DialogueCharacter.hx index 9fe4c44e..f59bef13 100644 --- a/source/funkin/game/cutscenes/dialogue/DialogueCharacter.hx +++ b/source/funkin/game/cutscenes/dialogue/DialogueCharacter.hx @@ -54,9 +54,10 @@ class DialogueCharacter extends FunkinSprite { x = 0; y = 0; } catch(e) { var message:String = e.toString(); - Logs.trace('Failed to load dialogue character $name: ${message}', ERROR); + Logs.trace('Failed to load dialogue character $name: ${message}', ERROR, RED); dialogueCharScript.call("loadingError", [message]); } + visible = false; dialogueCharScript.call("postCreate"); } diff --git a/source/funkin/menus/PauseSubState.hx b/source/funkin/menus/PauseSubState.hx index 4f451954..ff643dc1 100644 --- a/source/funkin/menus/PauseSubState.hx +++ b/source/funkin/menus/PauseSubState.hx @@ -20,19 +20,22 @@ class PauseSubState extends MusicBeatSubstate var grpMenuShit:FlxTypedGroup; - var menuItems:Array = Constants.DEFAULT_PAUSE_ITEMS; + 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(x:Float = 0, y:Float = 0) { + public function new(?items:Array, ?selectCall:NameEvent->Void) { super(); + menuItems = items != null ? items : Constants.DEFAULT_PAUSE_ITEMS; + this.selectCall = selectCall; } var parentDisabler:FunkinParentDisabler; @@ -134,13 +137,11 @@ class PauseSubState extends MusicBeatSubstate public function selectOption() { var event = EventManager.get(NameEvent).recycle(menuItems[curSelected]); + if (selectCall != null) selectCall(event); pauseScript.call("onSelectOption", [event]); - if (event.cancelled) return; - var daSelected:String = event.name; - - switch (daSelected) + switch (event.name) { case "Resume": close();