Most articles about scripting will cover basic functionality of overriding already existing methods. However, there is a need for completely custom functionality instead. This is where Modules step in.
A module is a class which runs very similarly to global scripts in other game engines. They are basically mini-states that run parallel to the original state.
You can use these modules to create entirely custom functionality, examples include:
- Custom HUDs
- Custom Menu's (Mod Menu, Pause Menu, Main Menu, etc)
- Retroactively affect gameplay (Creating chances of sprites appearing on screen, and adding your own version of ghost tapping. Among other things)
Creating a Module
import funkin.modding.module.Module; class KE_QOL extends Module { public function new() { // This registers a NAME for the module, which might be needed later! super("KE-QOL"); } public override function onScriptEvent(event:ScriptEvent) { // ... } public override function onStateChangeBegin(event:StateChangeScriptEvent) { // ... } public override function onNoteIncoming(event:NoteScriptEvent) { // ... } public override function onSongStart(event:ScriptEvent) { // ... } public override function onSongLoaded(event:SongLoadScriptEvent) { // ... } }
Modules have all the events most scripts have, but the difference is. You also have access to state change events, every script event, losing/gaining focus, and sub-state changes.
Modules also have a priority system for compatibility with other mods, in your construction method, new(), you can set the priority property (which defaults to 1000). 1 is processed before anything else.
You can view a detailed list of script events and their callbacks in here
Scoped Modules
Sometimes when creating a module you might require a more specialized approach: Onlying firing events when you are in a certain state. A scoped module is the right tool.
To initalize this module you must assign the state property in the constructor, like so:
import funkin.ui.mainmenu.MainMenuState; class ScopedModule_MainMenuState extends Module { public function new():Void { // Name: ScopedModule_MainmenuState // Priority: 1 // Scoped state: MainMenuState super("ScopedModule_MainmenuState", 1, { state: MainMenuState }); } public override function onStateChangeEnd(event:StateChangeScriptEvent):Void { super.onStateChangeEnd(event); // At the end of the state change event *INTO* the state (MainMenuState) trace("We've entered the Main Menu State!"); } }