Files
Funkin/source/funkin/modding/events/ScriptEventDispatcher.hx
T

361 lines
9.8 KiB
Haxe

package funkin.modding.events;
import funkin.modding.IScriptedClass.IPlayStateScriptedClass;
import funkin.modding.IScriptedClass;
import funkin.modding.module.Module;
/**
* Utility functions to assist with handling scripted classes.
*/
@:nullSafety
class ScriptEventDispatcher
{
/**
* Invoke the given event hook on the given scripted class.
* @param target The target class to call script hooks on.
* @param event The event, which determines the script hook to call and provides parameters for it.
*/
public static function callEvent(target:Null<IScriptedClass>, event:ScriptEvent):Void
{
if (target == null || event == null) return;
target.onScriptEvent(event);
// If one target says to stop propagation, stop.
if (!event.shouldPropagate)
{
return;
}
// IScriptedClass
switch (event.type)
{
case CREATE:
target.onCreate(event);
return;
case STATE_CREATE:
if (Std.isOfType(target, Module))
{
var t:Module = cast(target, Module);
t.onStateCreate(event);
}
return;
case DESTROY:
target.onDestroy(event);
return;
case UPDATE:
target.onUpdate(cast event);
return;
default: // Continue;
}
if (Std.isOfType(target, IStateStageProp))
{
var t:IStateStageProp = cast(target, IStateStageProp);
switch (event.type)
{
case ADDED:
t.onAdd(cast event);
return;
default: // Continue;
}
}
else
{
// If the target doesn't support the event, stop trying to dispatch.
if ([ScriptEventType.ADDED].contains(event.type)) return;
}
if (Std.isOfType(target, IDialogueScriptedClass))
{
var t:IDialogueScriptedClass = cast(target, IDialogueScriptedClass);
switch (event.type)
{
case DIALOGUE_START:
t.onDialogueStart(cast event);
return;
case DIALOGUE_LINE:
t.onDialogueLine(cast event);
return;
case DIALOGUE_COMPLETE_LINE:
t.onDialogueCompleteLine(cast event);
return;
case DIALOGUE_SKIP:
t.onDialogueSkip(cast event);
return;
case DIALOGUE_END:
t.onDialogueEnd(cast event);
return;
default: // Continue;
}
}
else
{
// If the target doesn't support the event, stop trying to dispatch.
if ([
ScriptEventType.DIALOGUE_START,
ScriptEventType.DIALOGUE_LINE,
ScriptEventType.DIALOGUE_COMPLETE_LINE,
ScriptEventType.DIALOGUE_SKIP,
ScriptEventType.DIALOGUE_END
].contains(event.type)) return;
}
if (Std.isOfType(target, INoteScriptedClass))
{
var t:INoteScriptedClass = cast(target, INoteScriptedClass);
switch (event.type)
{
case NOTE_INCOMING:
t.onNoteIncoming(cast event);
return;
case NOTE_HIT:
t.onNoteHit(cast event);
return;
case NOTE_MISS:
t.onNoteMiss(cast event);
return;
case NOTE_HOLD_DROP:
t.onNoteHoldDrop(cast event);
return;
default: // Continue;
}
}
else
{
// If the target doesn't support the event, stop trying to dispatch.
if ([
ScriptEventType.NOTE_INCOMING,
ScriptEventType.NOTE_HIT,
ScriptEventType.NOTE_MISS,
ScriptEventType.NOTE_HOLD_DROP
].contains(event.type)) return;
}
if (Std.isOfType(target, IBPMSyncedScriptedClass))
{
var t:IBPMSyncedScriptedClass = cast(target, IBPMSyncedScriptedClass);
switch (event.type)
{
case SONG_BEAT_HIT:
t.onBeatHit(cast event);
return;
case SONG_STEP_HIT:
t.onStepHit(cast event);
return;
default: // Continue;
}
}
else
{
// If the target doesn't support the event, stop trying to dispatch.
if ([ScriptEventType.SONG_BEAT_HIT, ScriptEventType.SONG_STEP_HIT].contains(event.type)) return;
}
if (Std.isOfType(target, IPlayStateScriptedClass))
{
var t:IPlayStateScriptedClass = cast(target, IPlayStateScriptedClass);
switch (event.type)
{
case NOTE_GHOST_MISS:
t.onNoteGhostMiss(cast event);
return;
case SONG_START:
t.onSongStart(event);
return;
case SONG_END:
t.onSongEnd(event);
return;
case SONG_RETRY:
t.onSongRetry(cast event);
return;
case GAME_OVER:
t.onGameOver(event);
return;
case PAUSE:
t.onPause(cast event);
return;
case RESUME:
t.onResume(event);
return;
case SONG_EVENT:
t.onSongEvent(cast event);
return;
case COUNTDOWN_START:
t.onCountdownStart(cast event);
return;
case COUNTDOWN_STEP:
t.onCountdownStep(cast event);
return;
case COUNTDOWN_END:
t.onCountdownEnd(cast event);
return;
case SONG_LOADED:
t.onSongLoaded(cast event);
return;
default: // Continue;
}
}
else
{
// If the target doesn't support the event, stop trying to dispatch.
if ([
ScriptEventType.NOTE_GHOST_MISS,
ScriptEventType.SONG_START,
ScriptEventType.SONG_END,
ScriptEventType.SONG_RETRY,
ScriptEventType.GAME_OVER,
ScriptEventType.PAUSE,
ScriptEventType.RESUME,
ScriptEventType.SONG_EVENT,
ScriptEventType.COUNTDOWN_START,
ScriptEventType.COUNTDOWN_STEP,
ScriptEventType.COUNTDOWN_END,
ScriptEventType.SONG_LOADED
].contains(event.type)) return;
}
if (Std.isOfType(target, IStateChangingScriptedClass))
{
var t = cast(target, IStateChangingScriptedClass);
switch (event.type)
{
case STATE_CHANGE_BEGIN:
t.onStateChangeBegin(cast event);
return;
case STATE_CHANGE_END:
t.onStateChangeEnd(cast event);
return;
case SUBSTATE_OPEN_BEGIN:
t.onSubStateOpenBegin(cast event);
return;
case SUBSTATE_OPEN_END:
t.onSubStateOpenEnd(cast event);
return;
case SUBSTATE_CLOSE_BEGIN:
t.onSubStateCloseBegin(cast event);
return;
case SUBSTATE_CLOSE_END:
t.onSubStateCloseEnd(cast event);
return;
case FOCUS_LOST:
t.onFocusLost(cast event);
return;
case FOCUS_GAINED:
t.onFocusGained(cast event);
return;
default: // Continue;
}
}
else
{
// If the target doesn't support the event, stop trying to dispatch.
if ([
ScriptEventType.STATE_CHANGE_BEGIN,
ScriptEventType.STATE_CHANGE_END,
ScriptEventType.SUBSTATE_OPEN_BEGIN,
ScriptEventType.SUBSTATE_OPEN_END,
ScriptEventType.SUBSTATE_CLOSE_BEGIN,
ScriptEventType.SUBSTATE_CLOSE_END,
ScriptEventType.FOCUS_LOST,
ScriptEventType.FOCUS_GAINED
].contains(event.type)) return;
}
if (Std.isOfType(target, IFreeplayScriptedClass))
{
var t:IFreeplayScriptedClass = cast target;
switch (event.type)
{
case CAPSULE_SELECTED:
t.onCapsuleSelected(cast event);
return;
case DIFFICULTY_SWITCH:
t.onDifficultySwitch(cast event);
return;
case SONG_SELECTED:
t.onSongSelected(cast event);
return;
case FREEPLAY_INTRO:
t.onFreeplayIntroDone(cast event);
return;
case FREEPLAY_OUTRO:
t.onFreeplayOutro(cast event);
return;
case FREEPLAY_CLOSE:
t.onFreeplayClose(cast event);
return;
default: // Continue;
}
}
else
{
// If the target doesn't support the event, stop trying to dispatch.
if ([
ScriptEventType.CAPSULE_SELECTED,
ScriptEventType.DIFFICULTY_SWITCH,
ScriptEventType.SONG_SELECTED,
ScriptEventType.FREEPLAY_INTRO,
ScriptEventType.FREEPLAY_OUTRO,
ScriptEventType.FREEPLAY_CLOSE
].contains(event.type)) return;
}
if (Std.isOfType(target, ICharacterSelectScriptedClass))
{
var t:ICharacterSelectScriptedClass = cast target;
switch (event.type)
{
case CHARACTER_SELECTED:
t.onCharacterSelect(cast event);
return;
case CHARACTER_DESELECTED:
t.onCharacterDeselect(cast event);
return;
case CHARACTER_CONFIRMED:
t.onCharacterConfirm(cast event);
return;
default: // Continue;
}
}
else
{
// If the target doesn't support the event, stop trying to dispatch.
if ([
ScriptEventType.CHARACTER_SELECTED,
ScriptEventType.CHARACTER_DESELECTED,
ScriptEventType.CHARACTER_CONFIRMED
].contains(event.type)) return;
}
// If we reach this line, it means a script event was dispatched while not being properly handled.
// Throw an error so we know to add additional fallbacks.
throw 'No corresponding function called for dispatched event type: ${event.type}';
}
public static function callEventOnAllTargets(targets:Iterator<IScriptedClass>, event:ScriptEvent):Void
{
if (targets == null || event == null) return;
if (Std.isOfType(targets, Array))
{
var t = cast(targets, Array<Dynamic>);
if (t.length == 0) return;
}
for (target in targets)
{
var t:IScriptedClass = cast target;
if (t == null) continue;
callEvent(t, event);
// If one target says to stop propagation, stop.
if (!event.shouldPropagate)
{
return;
}
}
}
}