Skip to main content

Scripted states and substates

Phoenix loads HScript files from data/states/<Name>.hx (also .hscript / .hxs) in the current mod, global mods, mods/data/states/, and assets/ preload. Every MusicBeatState and MusicBeatSubstate participates — menus, pause, game over, credits, freeplay, the fade transition, PlayState, and so on.

Requires HSCRIPT_ALLOWED (desktop, mobile, web — not Flash/AIR). Files are scanned by script security before they run.

Overlay an existing screen​

Drop a file named after the class:

mods/MyMod/data/states/FreeplayState.hx
mods/MyMod/data/states/MainMenuState.hx
mods/MyMod/data/states/PauseSubState.hx
mods/MyMod/data/states/GameOverSubstate.hx
mods/MyMod/data/states/CreditsState.hx
mods/MyMod/data/states/CustomFadeTransition.hx

The source state still runs. Your script gets state / game pointing at that instance and can add sprites, listen to beats, or return Function_Stop from onClose to keep a substate open.

function onCreatePost() {
var watermark = new FlxText(8, 8, 0, "modded freeplay");
watermark.setFormat(Paths.font("vcr.ttf"), 16, 0xFFFFFFFF);
state.add(watermark);
}

Replace a screen entirely (ModState)​

states.ModState is an empty MusicBeatState whose whole UI comes from the script. From Lua or Python:

switchCustomState("MyMenu") -- data/states/MyMenu.hx
openModSubState("MyPause") -- data/states/MyPause.hx
if customStateExists("MyMenu") then
switchCustomState("MyMenu", {from = "freeplay"})
end

From HScript:

switchCustomState("MyMenu", {from: "freeplay"});
openModSubState("MyPause");

FlxG.switchState(() -> new ModState("MyMenu", data)) works from compiled code. data is exposed to the script as data (and on the state as state.data). ModState.lastName / lastData survive FlxG.resetState().

CustomSubstate remains the PlayState helper (openCustomSubstate(name, pauseGame) plus onCustomSubstate* hooks). It now also loads data/states/<name>.hx and can be opened from any state, not only PlayState.

Callbacks​

Psych-style names and CNE-style aliases are both called:

PsychCNE aliasWhen
onCreatecreateend of MusicBeatState.create
onCreatePostpostCreatefirst update after create
onUpdate(elapsed)updateevery frame
onUpdatePost(elapsed)postUpdateafter super.update
onDestroydestroybefore the state is destroyed
onBeatHit(curBeat)beatHitconductor beat
onStepHit(curStep)stepHitconductor step
onSectionHit(curSection)sectionHitstates only
onClosesubstates; Function_Stop cancels close()

Top-level code in the file runs when the script is loaded (start of create).

What is in scope​

Same seed as Lua-hosted HScript, plus state, ModState, ModSubState, StateScripts, switchCustomState, openModSubState, Function_Stop / Function_Continue. Pull extra types with addHaxeLibrary('FreeplayState', 'states').

See HScript and docs/TemplateState.hx.