This commit is contained in:
cyn 2025-04-04 13:30:16 -07:00 committed by GitHub
commit 0097631437
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 166 additions and 17 deletions

View file

@ -193,7 +193,7 @@ class InitState extends FlxState
ModuleHandler.buildModuleCallbacks();
ModuleHandler.loadModuleCache();
ModuleHandler.callOnCreate();
ModuleHandler.callOnGameInit();
funkin.input.Cursor.hide();

View file

@ -107,6 +107,16 @@ interface IBPMSyncedScriptedClass extends IScriptedClass
*/
interface IPlayStateScriptedClass extends INoteScriptedClass extends IBPMSyncedScriptedClass
{
/**
* Called after `PlayState` creation, right before the countdown starts.
*/
public function onPlayStateCreate(event:ScriptEvent):Void;
/**
* Called immediately before `PlayState` cleanup.
*/
public function onPlayStateClose(event:ScriptEvent):Void;
/**
* Called when the game is paused.
* Has properties to set whether the pause easter egg will happen,
@ -186,3 +196,21 @@ interface IDialogueScriptedClass extends IScriptedClass
public function onDialogueSkip(event:DialogueScriptEvent):Void;
public function onDialogueEnd(event:DialogueScriptEvent):Void;
}
/**
* Defines a set of callbacks available to scripted classes which are global and not tied to a specific state.
*/
interface IGlobalScriptedClass extends IPlayStateScriptedClass extends IStateChangingScriptedClass
{
/**
* Called when the game is first initialized, before the title screen appears.
* This event is only called once.
*/
public function onGameInit(event:ScriptEvent):Void;
/**
* Called when the game is closed for any reason.
* This event is only called once.
*/
public function onGameClose(event:GameCloseScriptEvent):Void;
}

View file

@ -511,3 +511,20 @@ class PauseScriptEvent extends ScriptEvent
this.gitaroo = gitaroo;
}
}
/**
* An event which is called when the game is closed for any reason.
*/
class GameCloseScriptEvent extends ScriptEvent
{
/**
* The exit code. Any non-zero value is likely an error.
*/
public var exitCode(default, null):Int;
public function new(exitCode:Int):Void
{
super(GAME_CLOSE, false);
this.exitCode = exitCode;
}
}

View file

@ -1,6 +1,5 @@
package funkin.modding.events;
import funkin.modding.IScriptedClass.IPlayStateScriptedClass;
import funkin.modding.IScriptedClass;
/**
@ -114,6 +113,12 @@ class ScriptEventDispatcher
var t:IPlayStateScriptedClass = cast(target, IPlayStateScriptedClass);
switch (event.type)
{
case PLAYSTATE_CREATE:
t.onPlayStateCreate(event);
return;
case PLAYSTATE_CLOSE:
t.onPlayStateClose(event);
return;
case NOTE_GHOST_MISS:
t.onNoteGhostMiss(cast event);
return;
@ -186,6 +191,21 @@ class ScriptEventDispatcher
default: // Continue;
}
}
if (Std.isOfType(target, IGlobalScriptedClass))
{
var t = cast(target, IGlobalScriptedClass);
switch (event.type)
{
case GAME_INIT:
t.onGameInit(event);
return;
case GAME_CLOSE:
t.onGameClose(cast event);
return;
default: // Continue;
}
}
else
{
// Prevent "NO HELPER error."

View file

@ -2,6 +2,22 @@ package funkin.modding.events;
enum abstract ScriptEventType(String) from String to String
{
/**
* Called when the game is first initialized, before the title screen appears.
* This event is only called once.
*
* This event is not cancelable.
*/
var GAME_INIT = 'GAME_INIT';
/**
* Called when the game is closed for any reason.
* This event is only called once.
*
* This event is not cancelable.
*/
var GAME_CLOSE = 'GAME_CLOSE';
/**
* Called when the relevant object is created.
* Keep in mind that the constructor may be called before the object is needed,
@ -35,6 +51,20 @@ enum abstract ScriptEventType(String) from String to String
*/
var UPDATE = 'UPDATE';
/**
* Called after `PlayState` creation, right before the countdown starts.
*
* This event is not cancelable.
*/
var PLAYSTATE_CREATE = 'PLAYSTATE_CREATE';
/**
* Called immediately before `PlayState` cleanup.
*
* This event is not cancelable.
*/
var PLAYSTATE_CLOSE = 'PLAYSTATE_CLOSE';
/**
* Called when the player moves to pause the game.
*

View file

@ -1,14 +1,13 @@
package funkin.modding.module;
import funkin.modding.IScriptedClass.IPlayStateScriptedClass;
import funkin.modding.IScriptedClass.IStateChangingScriptedClass;
import funkin.modding.IScriptedClass.IGlobalScriptedClass;
import funkin.modding.events.ScriptEvent;
/**
* A module is a scripted class which receives all events without requiring a specific context.
* You may have the module active at all times, or only when another script enables it.
*/
class Module implements IPlayStateScriptedClass implements IStateChangingScriptedClass
class Module implements IGlobalScriptedClass
{
/**
* Whether the module is currently active.
@ -60,19 +59,35 @@ class Module implements IPlayStateScriptedClass implements IStateChangingScripte
public function onScriptEvent(event:ScriptEvent) {}
/**
* Called when the module is first created.
* This happens before the title screen appears!
* Called when the game is first initialized, before the title screen appears.
* This happens only once, immediately after the module's first `onCreate` event.
*/
public function onGameInit(event:ScriptEvent) {}
/**
* Called when the game is closed for any reason.
* This happens only once, immediately before the module's last `onDestroy` event.
*/
public function onGameClose(event:GameCloseScriptEvent) {}
/**
* Called when the module is created.
* This happens when the game is first initialized or after a mod reload.
*/
public function onCreate(event:ScriptEvent) {}
/**
* Called when a module is destroyed.
* This currently only happens when reloading modules with F5.
* This happens when reloading modules with F5 or when the game is closed.
*/
public function onDestroy(event:ScriptEvent) {}
public function onUpdate(event:UpdateScriptEvent) {}
public function onPlayStateCreate(event:ScriptEvent) {}
public function onPlayStateClose(event:ScriptEvent) {}
public function onPause(event:PauseScriptEvent) {}
public function onResume(event:ScriptEvent) {}

View file

@ -6,6 +6,7 @@ import funkin.modding.events.ScriptEvent;
import funkin.modding.events.ScriptEventDispatcher;
import funkin.modding.module.Module;
import funkin.modding.module.ScriptedModule;
import funkin.util.WindowUtil;
/**
* Utility functions for loading and manipulating active modules.
@ -46,11 +47,15 @@ class ModuleHandler
reorderModuleCache();
trace("[MODULEHANDLER] Module cache loaded.");
callEvent(new ScriptEvent(CREATE, false), true);
}
public static function buildModuleCallbacks():Void
{
FlxG.signals.postStateSwitch.add(onStateSwitchComplete);
WindowUtil.windowExit.add(callOnGameClose);
}
static function onStateSwitchComplete():Void
@ -122,9 +127,14 @@ class ModuleHandler
var event = new ScriptEvent(DESTROY, false);
// Note: Ignore stopPropagation()
for (key => value in moduleCache)
for (i in 0...modulePriorityOrder.length)
{
ScriptEventDispatcher.callEvent(value, event);
var moduleId = modulePriorityOrder[modulePriorityOrder.length - 1 - i];
var module:Module = moduleCache.get(moduleId);
if (module != null)
{
ScriptEventDispatcher.callEvent(module, event);
}
}
moduleCache.clear();
@ -132,21 +142,26 @@ class ModuleHandler
}
}
public static function callEvent(event:ScriptEvent):Void
public static function callEvent(event:ScriptEvent, force:Bool = false):Void
{
for (moduleId in modulePriorityOrder)
{
var module:Module = moduleCache.get(moduleId);
// The module needs to be active to receive events.
if (module != null && module.active)
if (module != null && (module.active || force))
{
ScriptEventDispatcher.callEvent(module, event);
}
}
}
public static inline function callOnCreate():Void
public static inline function callOnGameInit():Void
{
callEvent(new ScriptEvent(CREATE, false));
callEvent(new ScriptEvent(GAME_INIT, false));
}
public static inline function callOnGameClose(exitCode:Int):Void
{
callEvent(new GameCloseScriptEvent(exitCode));
}
}

View file

@ -720,6 +720,9 @@ class PlayState extends MusicBeatSubState
FlxG.worldBounds.set(0, 0, FlxG.width, FlxG.height);
// Dispatch the `PlayState` create event.
dispatchEvent(new ScriptEvent(PLAYSTATE_CREATE, false));
// The song is loaded and in the process of starting.
// This gets set back to false when the chart actually starts.
startingSong = true;
@ -1185,7 +1188,7 @@ class PlayState extends MusicBeatSubState
// Modules should get the first chance to cancel the event.
// super.dispatchEvent(event) dispatches event to module scripts.
super.dispatchEvent(event);
if (event.type != DESTROY) super.dispatchEvent(event);
// Dispatch event to note kind scripts
NoteKindManager.callEvent(event);
@ -3185,11 +3188,18 @@ class PlayState extends MusicBeatSubState
super.close();
}
private var cleanupDone:Bool = false;
/**
* Perform necessary cleanup before leaving the PlayState.
*/
* Perform necessary cleanup before leaving the PlayState.
*/
function performCleanup():Void
{
if (cleanupDone) return;
// Dispatch the `PlayState` close event.
dispatchEvent(new ScriptEvent(PLAYSTATE_CLOSE, false));
// If the camera is being tweened, stop it.
cancelAllCameraTweens();
@ -3242,6 +3252,8 @@ class PlayState extends MusicBeatSubState
// Clear the static reference to this state.
instance = null;
cleanupDone = true;
}
/**

View file

@ -631,6 +631,10 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
public function destroy():Void {}
public function onPlayStateCreate(event:ScriptEvent) {}
public function onPlayStateClose(event:ScriptEvent) {}
public function onPause(event:PauseScriptEvent):Void {};
public function onResume(event:ScriptEvent):Void {};

View file

@ -357,6 +357,10 @@ class Bopper extends StageProp implements IPlayStateScriptedClass
return output;
}
public function onPlayStateCreate(event:ScriptEvent) {}
public function onPlayStateClose(event:ScriptEvent) {}
public function onPause(event:PauseScriptEvent) {}
public function onResume(event:ScriptEvent) {}

View file

@ -879,6 +879,10 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
}
}
public function onPlayStateCreate(event:ScriptEvent) {}
public function onPlayStateClose(event:ScriptEvent) {}
public function onPause(event:PauseScriptEvent) {}
public function onResume(event:ScriptEvent) {}