Documentation Index
Fetch the complete documentation index at: https://mintlify.com/FunkinCrew/Funkin/llms.txt
Use this file to discover all available pages before exploring further.
The IScriptedClass interfaces define callback methods that scripted classes can implement to respond to game events. These interfaces enable mods to hook into the game’s lifecycle and react to player actions.
Core Interface
IScriptedClass
The base interface that all scripted classes implement:
interface IScriptedClass {
public function onScriptEvent(event:ScriptEvent):Void;
public function onCreate(event:ScriptEvent):Void;
public function onDestroy(event:ScriptEvent):Void;
public function onUpdate(event:UpdateScriptEvent):Void;
}
Lifecycle Methods
onCreate(event)
Called when the object is first created and added to the game.
onDestroy(event)
Called when the object is about to be destroyed or removed.
onUpdate(event)
Called every frame. The UpdateScriptEvent contains elapsed (delta time in seconds).
onScriptEvent(event)
Called for any script event dispatched to this object. Use this to handle custom events or as a catch-all.
State Management
IStateChangingScriptedClass
For scripts that need to persist across state changes:
interface IStateChangingScriptedClass extends IScriptedClass {
// State transitions
public function onStateChangeBegin(event:StateChangeScriptEvent):Void;
public function onStateChangeEnd(event:StateChangeScriptEvent):Void;
// Substate management
public function onSubStateOpenBegin(event:SubStateScriptEvent):Void;
public function onSubStateOpenEnd(event:SubStateScriptEvent):Void;
public function onSubStateCloseBegin(event:SubStateScriptEvent):Void;
public function onSubStateCloseEnd(event:SubStateScriptEvent):Void;
// Focus management
public function onFocusLost(event:FocusScriptEvent):Void;
public function onFocusGained(event:FocusScriptEvent):Void;
}
Example: State Transition Handler
class MyModule extends Module {
public function onStateChangeBegin(event:StateChangeScriptEvent):Void {
trace('Leaving state: ${event.currentState}');
trace('Entering state: ${event.targetState}');
// Clean up state-specific resources
if (Type.getClass(event.currentState) == PlayState) {
cleanupGameplay();
}
}
public function onStateChangeEnd(event:StateChangeScriptEvent):Void {
trace('Finished transition to: ${event.targetState}');
// Initialize resources for new state
if (Type.getClass(event.targetState) == FreeplayState) {
setupFreeplay();
}
}
}
IStateStageProp
For scripted objects that are added to the game state:
interface IStateStageProp extends IScriptedClass {
public function onAdd(event:ScriptEvent):Void;
}
onAdd(event)
Called when the element is added to the current state. Generally requires the class to be an instance of FlxBasic.
Gameplay Events
INoteScriptedClass
For scripts that respond to note events:
interface INoteScriptedClass extends IScriptedClass {
public function onNoteIncoming(event:NoteScriptEvent):Void;
public function onNoteHit(event:HitNoteScriptEvent):Void;
public function onNoteMiss(event:NoteScriptEvent):Void;
public function onNoteHoldDrop(event:HoldNoteScriptEvent):Void;
}
Event Details
onNoteIncoming(event)
Called when a note enters the field of view and approaches the strumline.
- Access the note:
event.note
- Check direction:
event.note.direction
onNoteHit(event)
Called when either player hits a note.
- Access the note:
event.note
- Check if player or CPU:
event.note.mustPress
- Get accuracy:
event.accuracy
- Get judgement:
event.judgement
onNoteMiss(event)
Called when a note is missed (usually by the player).
onNoteHoldDrop(event)
Called when a hold note is dropped early.
Example: Note Event Handler
class ScoreMultiplierMod extends Module {
var combo:Int = 0;
var multiplier:Float = 1.0;
public function onNoteHit(event:HitNoteScriptEvent):Void {
if (!event.note.mustPress) return; // Only count player notes
combo++;
// Increase multiplier every 10 notes
if (combo % 10 == 0) {
multiplier += 0.1;
trace('Multiplier increased: ${multiplier}x');
}
// Apply score multiplier
var baseScore = event.score;
event.score = Std.int(baseScore * multiplier);
}
public function onNoteMiss(event:NoteScriptEvent):Void {
if (!event.note.mustPress) return;
// Reset on miss
combo = 0;
multiplier = 1.0;
trace('Multiplier reset!');
}
}
IBPMSyncedScriptedClass
For scripts that sync with the music tempo:
interface IBPMSyncedScriptedClass extends IScriptedClass {
public function onStepHit(event:SongTimeScriptEvent):Void;
public function onBeatHit(event:SongTimeScriptEvent):Void;
}
onStepHit(event)
Called once every step (16th note) of the song.
- Get current step:
event.step
onBeatHit(event)
Called once every beat (quarter note) of the song.
- Get current beat:
event.beat
Example: Beat-Synced Animation
class BeatBounce extends Module {
public function onBeatHit(event:SongTimeScriptEvent):Void {
// Every 4 beats (one measure)
if (event.beat % 4 == 0) {
// Access the current state's camera
FlxG.camera.zoom += 0.05;
// Play a visual effect
playBeatEffect();
}
}
}
IPlayStateScriptedClass
Comprehensive interface for gameplay mods:
interface IPlayStateScriptedClass extends INoteScriptedClass extends IBPMSyncedScriptedClass {
// Pause/Resume
public function onPause(event:PauseScriptEvent):Void;
public function onResume(event:ScriptEvent):Void;
// Song lifecycle
public function onSongLoaded(event:SongLoadScriptEvent):Void;
public function onSongStart(event:ScriptEvent):Void;
public function onSongEnd(event:ScriptEvent):Void;
public function onSongRetry(event:SongRetryEvent):Void;
// Countdown
public function onCountdownStart(event:CountdownScriptEvent):Void;
public function onCountdownStep(event:CountdownScriptEvent):Void;
public function onCountdownEnd(event:CountdownScriptEvent):Void;
// Game events
public function onGameOver(event:ScriptEvent):Void;
public function onNoteGhostMiss(event:GhostMissNoteScriptEvent):Void;
public function onSongEvent(event:SongEventScriptEvent):Void;
}
Song Manipulation
class ChartModifier extends Module {
public function onSongLoaded(event:SongLoadScriptEvent):Void {
trace('Song loaded: ${event.songId}');
// Modify the chart before notes are placed
for (note in event.notes) {
// Double the speed of all notes
note.time *= 0.5;
}
}
public function onSongStart(event:ScriptEvent):Void {
trace('Song started! Current time: ${Conductor.songPosition}');
}
public function onSongEnd(event:ScriptEvent):Void {
trace('Song ended! Final score: ${PlayState.instance.songScore}');
}
}
Countdown Control
class CustomCountdown extends Module {
public function onCountdownStart(event:CountdownScriptEvent):Void {
trace('Countdown starting!');
// Cancel default countdown
event.cancel();
// Start custom countdown
startCustomCountdown();
}
public function onCountdownStep(event:CountdownScriptEvent):Void {
// event.step: 0=READY, 1=SET, 2=GO, 3=GO! (last frame)
switch (event.step) {
case 0: playCustomSound('ready');
case 1: playCustomSound('set');
case 2: playCustomSound('go');
}
}
}
UI State Events
IFreeplayScriptedClass
For Freeplay menu interactions:
interface IFreeplayScriptedClass extends IScriptedClass {
public function onCapsuleSelected(event:CapsuleScriptEvent):Void;
public function onDifficultySwitch(event:CapsuleScriptEvent):Void;
public function onSongSelected(event:CapsuleScriptEvent):Void;
public function onFreeplayIntroDone(event:FreeplayScriptEvent):Void;
public function onFreeplayOutro(event:FreeplayScriptEvent):Void;
public function onFreeplayClose(event:FreeplayScriptEvent):Void;
}
class FreeplayTracker extends Module {
public function onSongSelected(event:CapsuleScriptEvent):Void {
trace('Song selected: ${event.capsule.songData.songName}');
trace('Difficulty: ${event.difficulty}');
// Check if song has been played before
if (!hasSongBeenPlayed(event.capsule.songData.songId)) {
showNewSongPopup();
}
}
public function onDifficultySwitch(event:CapsuleScriptEvent):Void {
trace('Difficulty changed to: ${event.difficulty}');
updateDifficultyDisplay();
}
}
ICharacterSelectScriptedClass
For Character Select interactions:
interface ICharacterSelectScriptedClass extends IScriptedClass {
public function onCharacterSelect(event:CharacterSelectScriptEvent):Void;
public function onCharacterDeselect(event:CharacterSelectScriptEvent):Void;
public function onCharacterConfirm(event:CharacterSelectScriptEvent):Void;
}
IDialogueScriptedClass
For dialogue system interactions:
interface IDialogueScriptedClass extends IScriptedClass {
public function onDialogueStart(event:DialogueScriptEvent):Void;
public function onDialogueLine(event:DialogueScriptEvent):Void;
public function onDialogueCompleteLine(event:DialogueScriptEvent):Void;
public function onDialogueSkip(event:DialogueScriptEvent):Void;
public function onDialogueEnd(event:DialogueScriptEvent):Void;
}
Event Handler Interface
IEventHandler
For objects that can dispatch events to children:
interface IEventHandler {
public function dispatchEvent(event:ScriptEvent):Void;
}
PlayState and other container classes implement this to broadcast events to all child scripted elements.
Event Cancellation
Many events are cancellable, allowing mods to prevent default behavior:
class GameOverPrevention extends Module {
public function onGameOver(event:ScriptEvent):Void {
if (playerHasSecondChance()) {
// Prevent game over
event.cancel();
// Restore player health
PlayState.instance.health = 1.0;
// Show notification
showSecondChancePopup();
}
}
}
Event Propagation
Control whether events continue to other scripts:
class HighPriorityMod extends Module {
public function new() {
super('high-priority-mod', 1); // Low priority number = high priority
}
public function onNoteHit(event:HitNoteScriptEvent):Void {
if (shouldBlockOtherMods()) {
// Stop event from reaching other modules
event.stopPropagation();
}
}
}