diff --git a/source/funkin/backend/MusicBeatState.hx b/source/funkin/backend/MusicBeatState.hx index 31bd204b..2883f5ed 100644 --- a/source/funkin/backend/MusicBeatState.hx +++ b/source/funkin/backend/MusicBeatState.hx @@ -9,6 +9,7 @@ import flixel.FlxState; import flixel.FlxSubState; import funkin.backend.scripting.events.*; import funkin.backend.scripting.Script; +import funkin.backend.scripting.ScriptPack; import funkin.backend.system.interfaces.IBeatReceiver; import funkin.backend.system.Conductor; import funkin.options.PlayerSettings; @@ -89,7 +90,7 @@ class MusicBeatState extends FlxState implements IBeatReceiver /** * Current injected script attached to the state. To add one, create a file at path "data/states/stateName" (ex: data/states/FreeplayState) */ - public var stateScript:Script; + public var scripts:ScriptPack; public var scriptsAllowed:Bool = true; @@ -111,26 +112,44 @@ class MusicBeatState extends FlxState implements IBeatReceiver this.scriptName = scriptName; } + public function addScript(file:String) + scripts.add(Script.create(Paths.script(file, null, true))); + function loadScript() { if (scriptsAllowed) { - if (stateScript == null || stateScript is DummyScript) { + if (scripts == null) { var className = Type.getClassName(Type.getClass(this)); var scriptName = this.scriptName != null ? this.scriptName : className.substr(className.lastIndexOf(".")+1); - - stateScript = Script.create(Paths.script('data/states/${scriptName}')); - stateScript.setParent(this); - stateScript.load(); - } else - stateScript.reload(); + var scriptPaths = []; + (scripts = new ScriptPack(scriptName)).setParent(this); + scriptPaths = Paths.getScriptPaths('assets/data/states/$scriptName'); + for (i in Paths.getScriptPaths('assets/data/states', '/$scriptName.')) + scriptPaths.push(i); + if (scriptPaths.length > 0) for (i in scriptPaths) addScript(i); + if (Type.getClass(this) != PlayState) scripts.load(); + } + else + scripts.reload(); } } + public function call(name:String, ?args:Array, ?defaultVal:Dynamic):Dynamic { + // calls the function on the assigned script + if (scripts == null) return defaultVal; + return scripts.call(name, args); + } + + public function event(name:String, event:T):T { + if (scripts == null) return event; + scripts.event(name, event); + return event; + } public override function tryUpdate(elapsed:Float):Void { if (persistentUpdate || subState == null) { - call("preUpdate", [elapsed]); + scripts.call("preUpdate", [elapsed]); update(elapsed); - call("postUpdate", [elapsed]); + scripts.call("postUpdate", [elapsed]); } if (_requestSubStateReset) @@ -145,42 +164,31 @@ class MusicBeatState extends FlxState implements IBeatReceiver } override function create() { - loadScript(); + if (Type.getClass(this) != PlayState) loadScript(); Framerate.offset.y = 0; super.create(); - call("create"); + scripts.call("create"); } public override function createPost() { super.createPost(); persistentUpdate = true; - call("postCreate"); + scripts.call("postCreate"); if (!skipTransIn) openSubState(new MusicBeatTransition(null)); skipTransIn = false; skipTransOut = false; } - public function call(name:String, ?args:Array, ?defaultVal:Dynamic):Dynamic { - // calls the function on the assigned script - if (stateScript == null) return defaultVal; - return stateScript.call(name, args); - } - - public function event(name:String, event:T):T { - if (stateScript == null) return event; - stateScript.call(name, [event]); - return event; - } override function update(elapsed:Float) { // TODO: DEBUG MODE!! if (FlxG.keys.justPressed.F5) { loadScript(); - if (stateScript != null && !(stateScript is DummyScript)) + if (scripts != null) Logs.trace('State script successfully reloaded', WARNING, GREEN); } - call("update", [elapsed]); + scripts.call("update", [elapsed]); super.update(elapsed); } @@ -188,19 +196,19 @@ class MusicBeatState extends FlxState implements IBeatReceiver @:dox(hide) public function stepHit(curStep:Int):Void { for(e in members) if (e != null && e is IBeatReceiver) cast(e, IBeatReceiver).stepHit(curStep); - call("stepHit", [curStep]); + scripts.call("stepHit", [curStep]); } @:dox(hide) public function beatHit(curBeat:Int):Void { for(e in members) if (e != null && e is IBeatReceiver) cast(e, IBeatReceiver).beatHit(curBeat); - call("beatHit", [curBeat]); + scripts.call("beatHit", [curBeat]); } @:dox(hide) public function measureHit(curMeasure:Int):Void { for(e in members) if (e != null && e is IBeatReceiver) cast(e, IBeatReceiver).measureHit(curMeasure); - call("measureHit", [curMeasure]); + scripts.call("measureHit", [curMeasure]); } /** @@ -221,34 +229,34 @@ class MusicBeatState extends FlxState implements IBeatReceiver * SCRIPTING STUFF */ public override function openSubState(subState:FlxSubState) { - var e = event("onOpenSubState", EventManager.get(StateEvent).recycle(subState)); + var e = scripts.event("onOpenSubState", EventManager.get(StateEvent).recycle(subState)); if (!e.cancelled) super.openSubState(subState); } public override function onResize(w:Int, h:Int) { super.onResize(w, h); - event("onResize", EventManager.get(ResizeEvent).recycle(w, h, null, null)); + scripts.event("onResize", EventManager.get(ResizeEvent).recycle(w, h, null, null)); } public override function destroy() { super.destroy(); graphicCache.destroy(); - call("onDestroy"); - if (stateScript != null) - stateScript.destroy(); + scripts.call("onDestroy"); + scripts.call("destroy"); + scripts = FlxDestroyUtil.destroy(scripts); } public override function draw() { graphicCache.draw(); - var e = event("draw", EventManager.get(DrawEvent).recycle()); + var e = scripts.event("draw", EventManager.get(DrawEvent).recycle()); if (!e.cancelled) super.draw(); - event("postDraw", e); + scripts.event("postDraw", e); } public override function switchTo(nextState:FlxState) { - var e = event("onStateSwitch", EventManager.get(StateEvent).recycle(nextState)); + var e = scripts.event("onStateSwitch", EventManager.get(StateEvent).recycle(nextState)); if (e.cancelled) return false; @@ -263,12 +271,12 @@ class MusicBeatState extends FlxState implements IBeatReceiver public override function onFocus() { super.onFocus(); - call("onFocus"); + scripts.call("onFocus"); } public override function onFocusLost() { super.onFocusLost(); - call("onFocusLost"); + scripts.call("onFocusLost"); } public override function resetSubState() { diff --git a/source/funkin/backend/MusicBeatSubstate.hx b/source/funkin/backend/MusicBeatSubstate.hx index 8324e5ca..00477bd1 100644 --- a/source/funkin/backend/MusicBeatSubstate.hx +++ b/source/funkin/backend/MusicBeatSubstate.hx @@ -3,6 +3,7 @@ package funkin.backend; import flixel.FlxState; import funkin.backend.scripting.events.*; import funkin.backend.scripting.Script; +import funkin.backend.scripting.ScriptPack; import funkin.backend.scripting.DummyScript; import funkin.backend.system.interfaces.IBeatReceiver; import funkin.backend.system.Conductor.BPMChangeEvent; @@ -63,7 +64,7 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver /** * Current injected script attached to the state. To add one, create a file at path "data/states/stateName" (ex: "data/states/PauseMenuSubstate.hx") */ - public var stateScript:Script; + public var scripts:ScriptPack; public var scriptsAllowed:Bool = true; @@ -97,27 +98,43 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver this.scriptsAllowed = #if SOFTCODED_STATES scriptsAllowed #else false #end; this.scriptName = scriptName; } + public function addScript(file:String) + scripts.add(Script.create(Paths.script(file, null, true))); function loadScript() { if (scriptsAllowed) { - if (stateScript == null || stateScript is DummyScript) { + if (scripts == null) { + (scripts = new ScriptPack(scriptName)).setParent(this); var className = Type.getClassName(Type.getClass(this)); var scriptName = this.scriptName != null ? this.scriptName : className.substr(className.lastIndexOf(".")+1); - - stateScript = Script.create(Paths.script('data/states/${scriptName}')); - stateScript.setParent(this); - stateScript.load(); - } else - stateScript.reload(); + var scriptPaths = []; + scriptPaths = Paths.getScriptPaths('assets/data/states/$scriptName'); + for (i in Paths.getScriptPaths('assets/data/states', '/$scriptName.')) + scriptPaths.push(i); + if (scriptPaths.length > 0) for (i in scriptPaths) addScript(i); + scripts.load(); + } + else + scripts.reload(); } } + public function call(name:String, ?args:Array, ?defaultVal:Dynamic):Dynamic { + // calls the function on the assigned script + if (scripts == null) return defaultVal; + return scripts.call(name, args); + } + public function event(name:String, event:T):T { + if (scripts == null) return event; + scripts.call(name, [event]); + return event; + } public override function tryUpdate(elapsed:Float):Void { if (persistentUpdate || subState == null) { - call("preUpdate", [elapsed]); + scripts.call("preUpdate", [elapsed]); update(elapsed); - call("postUpdate", [elapsed]); + scripts.call("postUpdate", [elapsed]); } if (_requestSubStateReset) @@ -132,10 +149,10 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver } override function close() { - var event = event("onClose", new CancellableEvent()); + var event = scripts.event("onClose", new CancellableEvent()); if (!event.cancelled) { super.close(); - call("onClosePost"); + scripts.call("onClosePost"); } } @@ -143,23 +160,12 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver { loadScript(); super.create(); - call("create"); + scripts.call("create"); } public override function createPost() { super.createPost(); - call("postCreate"); - } - public function call(name:String, ?args:Array, ?defaultVal:Dynamic):Dynamic { - // calls the function on the assigned script - if (stateScript == null) return defaultVal; - return stateScript.call(name, args); - } - - public function event(name:String, event:T):T { - if (stateScript == null) return event; - stateScript.call(name, [event]); - return event; + scripts.call("postCreate"); } override function update(elapsed:Float) @@ -167,29 +173,28 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver // TODO: DEBUG MODE!! if (FlxG.keys.justPressed.F5) { loadScript(); - if (stateScript != null && !(stateScript is DummyScript)) - Logs.trace('State script successfully reloaded', WARNING, GREEN); + Logs.trace('State script successfully reloaded', WARNING, GREEN); } - call("update", [elapsed]); + scripts.call("update", [elapsed]); super.update(elapsed); } @:dox(hide) public function stepHit(curStep:Int):Void { for(e in members) if (e is IBeatReceiver) cast(e, IBeatReceiver).stepHit(curStep); - call("stepHit", [curStep]); + scripts.call("stepHit", [curStep]); } @:dox(hide) public function beatHit(curBeat:Int):Void { for(e in members) if (e is IBeatReceiver) cast(e, IBeatReceiver).beatHit(curBeat); - call("beatHit", [curBeat]); + scripts.call("beatHit", [curBeat]); } @:dox(hide) public function measureHit(curMeasure:Int):Void { for(e in members) if (e is IBeatReceiver) cast(e, IBeatReceiver).measureHit(curMeasure); - call("measureHit", [curMeasure]); + scripts.call("measureHit", [curMeasure]); } /** @@ -210,25 +215,25 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver * SCRIPTING STUFF */ public override function openSubState(subState:FlxSubState) { - var e = event("onOpenSubState", EventManager.get(StateEvent).recycle(subState)); + var e = scripts.event("onOpenSubState", EventManager.get(StateEvent).recycle(subState)); if (!e.cancelled) super.openSubState(subState); } public override function onResize(w:Int, h:Int) { super.onResize(w, h); - event("onResize", EventManager.get(ResizeEvent).recycle(w, h, null, null)); + scripts.event("onResize", EventManager.get(ResizeEvent).recycle(w, h, null, null)); } public override function destroy() { super.destroy(); - call("onDestroy"); - if(stateScript != null) - stateScript.destroy(); + scripts.call("onDestroy"); + scripts.call("destroy"); + scripts = FlxDestroyUtil.destroy(scripts); } public override function switchTo(nextState:FlxState) { - var e = event("onStateSwitch", EventManager.get(StateEvent).recycle(nextState)); + var e = scripts.event("onStateSwitch", EventManager.get(StateEvent).recycle(nextState)); if (e.cancelled) return false; return super.switchTo(nextState); @@ -236,12 +241,12 @@ class MusicBeatSubstate extends FlxSubState implements IBeatReceiver public override function onFocus() { super.onFocus(); - call("onFocus"); + scripts.call("onFocus"); } public override function onFocusLost() { super.onFocusLost(); - call("onFocusLost"); + scripts.call("onFocusLost"); } public var parent:FlxState; diff --git a/source/funkin/backend/assets/Paths.hx b/source/funkin/backend/assets/Paths.hx index 531a0ca1..f05dd5e2 100644 --- a/source/funkin/backend/assets/Paths.hx +++ b/source/funkin/backend/assets/Paths.hx @@ -97,10 +97,25 @@ class Paths } return getPath('images/$key.png', library); } - + static public function getScriptPaths(key:String, excep:String = ""):Array { + var scriptsPaths:Array = []; + var reallib = assetsTree.libraries.copy(); + reallib.pop(); + if (!key.endsWith("/")) key += "/"; + for (e in reallib) { + if (!OpenFlAssets.exists(e.getPath(key).startsWith(".") ? "." + e.getPath(key) : e.getPath(key))) continue; + for (i in sys.FileSystem.readDirectory(e.getPath(key))) + if (Script.scriptExtensions.contains(Path.extension(i))) { + var path = '${e.getPath(key)}$i'; + if (path.startsWith(".")) path = "." + path; + if (path.contains(excep) || excep == "") scriptsPaths.push(path.replace("//", "/")); + } + } + return scriptsPaths; + } inline static public function script(key:String, ?library:String, isAssetsPath:Bool = false) { var scriptPath = isAssetsPath ? key : getPath(key, library); - if (!OpenFlAssets.exists(scriptPath)) { + if (!Script.scriptExtensions.contains(Path.extension(scriptPath))) { var p:String; for(ex in Script.scriptExtensions) { p = '$scriptPath.$ex'; diff --git a/source/funkin/backend/scripting/GlobalScript.hx b/source/funkin/backend/scripting/GlobalScript.hx index 14803c8b..8c2ef66b 100644 --- a/source/funkin/backend/scripting/GlobalScript.hx +++ b/source/funkin/backend/scripting/GlobalScript.hx @@ -9,7 +9,7 @@ import funkin.backend.assets.ModsFolder; * Class for THE Global Script, aka script that runs in the background at all times. */ class GlobalScript { - public static var script:Script; + public static var scripts:ScriptPack; public static function init() { #if MOD_SUPPORT @@ -20,92 +20,78 @@ class GlobalScript { Conductor.onStepHit.add(stepHit); FlxG.signals.focusGained.add(function() { - call("focusGained"); + scripts.call("focusGained"); }); FlxG.signals.focusLost.add(function() { - call("focusLost"); + scripts.call("focusLost"); }); FlxG.signals.gameResized.add(function(w:Int, h:Int) { - call("gameResized", [w, h]); + scripts.call("gameResized", [w, h]); }); FlxG.signals.postDraw.add(function() { - call("postDraw"); + scripts.call("postDraw"); }); FlxG.signals.postGameReset.add(function() { - call("postGameReset"); + scripts.call("postGameReset"); }); FlxG.signals.postGameStart.add(function() { - call("postGameStart"); + scripts.call("postGameStart"); }); FlxG.signals.postStateSwitch.add(function() { - call("postStateSwitch"); + scripts.call("postStateSwitch"); }); FlxG.signals.postUpdate.add(function() { - call("postUpdate", [FlxG.elapsed]); + scripts.call("postUpdate", [FlxG.elapsed]); if (FlxG.keys.justPressed.F5) { - if (script != null && !(script is DummyScript)) { + if (scripts != null) { Logs.trace('Reloading global script...', WARNING, YELLOW); - script.reload(); + scripts.reload(); Logs.trace('Global script successfully reloaded.', WARNING, GREEN); } else { Logs.trace('Loading global script...', WARNING, YELLOW); onModSwitch(#if MOD_SUPPORT ModsFolder.currentModFolder #else null #end); - if (script is DummyScript) - Logs.trace('Global script not found. Are you sure "data/global.hx" exists?', ERROR, RED); - else - Logs.trace('Global script successfully loaded.', WARNING, GREEN); } } }); FlxG.signals.preDraw.add(function() { - call("preDraw"); + scripts.call("preDraw"); }); FlxG.signals.preGameReset.add(function() { - call("preGameReset"); + scripts.call("preGameReset"); }); FlxG.signals.preGameStart.add(function() { - call("preGameStart"); + scripts.call("preGameStart"); }); FlxG.signals.preStateCreate.add(function(state:FlxState) { - call("preStateCreate", [state]); + scripts.call("preStateCreate", [state]); }); FlxG.signals.preStateSwitch.add(function() { - call("preStateSwitch", []); + scripts.call("preStateSwitch", []); }); FlxG.signals.preUpdate.add(function() { - call("preUpdate", [FlxG.elapsed]); - call("update", [FlxG.elapsed]); + scripts.call("preUpdate", [FlxG.elapsed]); + scripts.call("update", [FlxG.elapsed]); }); onModSwitch(#if MOD_SUPPORT ModsFolder.currentModFolder #else null #end); } - public static function event(name:String, event:T):T { - if (script == null) return event; - script.call(name, [event]); - return event; - } - - public static function call(name:String, ?args:Array) { - if (script == null) return; - script.call(name, args); - } public static function onModSwitch(newMod:String) { - call("onDestroy"); - if (script != null) { - script.destroy(); - script = null; - } - script = Script.create(Paths.script('data/global')); - script.load(); + if (scripts != null) scripts.call("onDestroy"); + scripts = FlxDestroyUtil.destroy(scripts); + scripts = new ScriptPack("global"); + var scriptPaths = Paths.getScriptPaths("assets/data", "/global."); + for (i in Paths.getScriptPaths("assets/data/global")) scriptPaths.push(i); + for (i in scriptPaths) scripts.add(Script.create(Paths.script(i, null, true))); + scripts.load(); } public static function beatHit(curBeat:Int) { - call("beatHit", [curBeat]); + scripts.call("beatHit", [curBeat]); } public static function stepHit(curStep:Int) { - call("stepHit", [curStep]); + scripts.call("stepHit", [curStep]); } } #end \ No newline at end of file diff --git a/source/funkin/backend/utils/DiscordUtil.hx b/source/funkin/backend/utils/DiscordUtil.hx index 31ec1383..ad01f4a9 100644 --- a/source/funkin/backend/utils/DiscordUtil.hx +++ b/source/funkin/backend/utils/DiscordUtil.hx @@ -96,7 +96,7 @@ class DiscordUtil { data.largeImageText = DiscordUtil.data.logoText; #if GLOBAL_SCRIPT - var event = funkin.backend.scripting.GlobalScript.event("onDiscordPresenceUpdate", EventManager.get(DiscordPresenceUpdateEvent).recycle(data)); + var event = funkin.backend.scripting.GlobalScript.scripts.event("onDiscordPresenceUpdate", EventManager.get(DiscordPresenceUpdateEvent).recycle(data)); if (event.cancelled) return; #end diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index cf4a5c20..285e8a52 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -80,11 +80,6 @@ class PlayState extends MusicBeatState */ public static var coopMode:Bool = false; - /** - * Script Pack of all the scripts being ran. - */ - public var scripts:ScriptPack; - /** * Array of all the players in the stage. */ @@ -511,7 +506,7 @@ class PlayState extends MusicBeatState if (FlxG.sound.music != null) FlxG.sound.music.stop(); PauseSubState.script = ""; - (scripts = new ScriptPack("PlayState")).setParent(this); + loadScript(); camGame = camera; FlxG.cameras.add(camHUD = new HudCamera(), false); @@ -561,21 +556,20 @@ class PlayState extends MusicBeatState // case "": // ADD YOUR HARDCODED SCRIPTS HERE! default: - for(content in [Paths.getFolderContent('songs/${SONG.meta.name.toLowerCase()}/scripts', true, fromMods ? MODS : BOTH), Paths.getFolderContent('data/charts/', true, fromMods ? MODS : BOTH)]) + for(content in [Paths.getScriptPaths('assets/songs/${SONG.meta.name.toLowerCase()}/scripts'), Paths.getScriptPaths('assets/data/charts/')]) for(file in content) addScript(file); var songEvents:Array = []; for (event in SONG.events) if (!songEvents.contains(event.name)) songEvents.push(event.name); - for (file in Paths.getFolderContent('data/events/', true, fromMods ? MODS : BOTH)) { + for (file in Paths.getScriptPaths('assets/data/events/')) { var fileName:String = Path.withoutExtension(Path.withoutDirectory(file)); if (EventsData.eventsList.contains(fileName) && songEvents.contains(fileName)) addScript(file); } } } - add(comboGroup); #end @@ -647,7 +641,6 @@ class PlayState extends MusicBeatState add(splashHandler); scripts.load(); - scripts.call("create"); #end // CAMERA & HUD INITIALISATION @@ -724,7 +717,6 @@ class PlayState extends MusicBeatState #end startingSong = true; - super.create(); for(s in introSprites) @@ -743,8 +735,6 @@ class PlayState extends MusicBeatState updateDiscordPresence(); __updateNote_event = EventManager.get(NoteUpdateEvent); - - scripts.call("postCreate"); } /** @@ -897,11 +887,9 @@ class PlayState extends MusicBeatState } public override function destroy() { - scripts.call("destroy"); for(g in __cachedGraphics) g.useCount--; super.destroy(); - scripts = FlxDestroyUtil.destroy(scripts); @:privateAccess { FlxG.sound.destroySound(inst); FlxG.sound.destroySound(vocals); @@ -1075,11 +1063,9 @@ class PlayState extends MusicBeatState @:dox(hide) override public function update(elapsed:Float) { - scripts.call("update", [elapsed]); if (inCutscene) { super.update(elapsed); - scripts.call("postUpdate", [elapsed]); return; } @@ -1197,13 +1183,6 @@ class PlayState extends MusicBeatState scripts.call("postUpdate", [elapsed]); } - override function draw() { - var e = scripts.event("draw", EventManager.get(DrawEvent).recycle()); - if (!e.cancelled) - super.draw(); - scripts.event("postDraw", e); - } - public function executeEvent(event:ChartEvent) { if (event == null) return; if (event.params == null) return; @@ -1627,12 +1606,6 @@ class PlayState extends MusicBeatState scripts.call("beatHit", [curBeat]); } - public function addScript(file:String) { - var ext = Path.extension(file).toLowerCase(); - if (Script.scriptExtensions.contains(ext)) - scripts.add(Script.create(file)); - } - // GETTERS & SETTERS #if REGION private inline function get_player():StrumLine