How mods work in Vortex Engine, and the things it adds that need no script: extra characters, extra keys, new chart events, tutorials and more. For scripts, see the Lua API.
Vortex Engine is a Psych Engine 1.0.4 fork: Psych mods go in the mods folder and work as they are, Lua and HScript included.
Everything on the Psych Engine Lua Script API page works here.
They also get the V-Slice menus with no work: the V-Slice Freeplay (with the DJ), Character Select, the Results screen and the sticker transition are options of the player and work for Psych songs too.
Mods made for the official game (Polymod, V-Slice 0.8.x) go in the same mods folder and show up like any other mod, with no porting.
.hxc song, stage, character, module, dialogue, Freeplay and results scripts.-chart.json / -metadata.json, every difficulty and variation), stages, characters (Sparrow, Packer, multi-sparrow and Adobe Animate atlases), song events, note kinds and dialogue conversations.V-Slice content stays in V-Slice format: what Vortex's editors save for a V-Slice mod works in the official game too.
The options menu has a Mods tab with the settings each mod adds, under the mod's name.
data/settings.json, as in Psych Engine (read in Lua with getModSetting).createPrefItemCheckbox, createPrefItemEnum, createPrefItemNumber and createPrefItemPercentage on the options menu's pages, from a module's onStateChangeEnd.More characters singing in a song (a duet, a third character...), with no script. In the chart editor, the Extras tab adds them:
| Field | What it is |
|---|---|
| Tag | The name notes and scripts know it by ("pico2"). |
| Side | Opponent, Player or Girlfriend: whose notes it can sing, and whose spot it stands next to. |
| Character | The character file. |
| X / Y | Where it stands, from the spot of the main character of its side. |
| Sings every note / Behind | A duet with the main character / stands behind it. |
| Receptors of its own | Its notes come down receptors of its own (a strumline of its own, like Codename Engine's) instead of its side's: Receptors X (the left of the first one), Receptors Y (the top) and Seen. On the player's side, the player hits them with the same keys. |
Each extra character gets a field of its own on the grid (up to 4): a note put on it is a note of its side that only it sings. The Note tab's Sung by also picks who sings each note: the main character, an extra one, or everyone on that side. They dance, sing, hold long notes and miss like the main ones, also online and with Play as Opponent.
In the chart (Psych format), a vortexCharacters list, and the 5th value of a note says who sings it:
"vortexCharacters": [
{"tag": "pico2", "character": "pico", "side": "opponent", "x": -250, "y": 0,
"singsAll": false, "behind": true}
]
[1200, 5, 0, "", "pico2"] // "main", "all", a tag, or tags: "main,pico2"
Scripts get them by their tag, and can add more during the song: see extra characters in the Lua API.
Songs with 1 to 9 arrows per side, for mods like Shaggy's 6 and 9 key songs.
"keyCount": 6. Psych Engine Extra Keys' "mania" works too (its number plus one). The notes are 0 to keyCount - 1 on one side and keyCount to keyCount * 2 - 1 on the other.A Codename Engine chart is read like a Psych one: put it where the song's chart goes (data/<song>/<song>-<difficulty>.json) or load it in the chart editor with File > Open Codename Engine Chart...:
its strumlines get fields of their own on the grid right away, and Save suggests <song>-<difficulty>.json (Codename's file is only <difficulty>.json).
Only the chart is converted: the song's music, characters and stage are the engine's own files, with the same names.
| In the Codename chart | In Vortex |
|---|---|
meta.json (BPM, time signature) | Read from next to the chart, or from the folder above it (Codename's songs/<song>/). Without one: the chart's own meta, or 100 BPM. |
events.json | Read too, from the same places. |
| First opponent / player strumline | The opponent / the player. |
| Strumline 2 (additional) | The girlfriend. |
| Other strumlines, and the other characters of a strumline | Extra characters (the ones of the main strumlines sing every note with them). Visible strumlines get receptors of their own, where Codename puts them. |
| The main strumlines' places and visibility | Where the opponent's and the player's receptors are (not with Middlescroll). |
| No girlfriend strumline | The song has no girlfriend. |
| Strumlines that aren't visible | Their characters sing notes nobody sees (the "hidden" singer). |
noteTypes | Each note's type is noteTypes[type - 1] (0 is a normal note). |
| BPM Change, Continuous BPM Change, Time Signature Change | The sections (a measure each, cut where the tempo changes; a continuous change goes a step each beat). |
| Camera Movement / Camera Position | Camera Target, with the same time, ease and offset. |
| Add Camera Zoom, Camera Bop / Camera Zoom | Add Camera Zoom / Set Camera Zoom. |
| Camera Modulo Change / Camera Flash | Camera Bop / Flash Camera (reversed: Fade Screen). |
| Scroll Speed Change / Play Animation | Change Scroll Speed / Play Animation (extra characters too). |
| Alt Animation Toggle | The notes' "Alt Animation" type, and Alt Idle Animation. |
| HScript Call, a mod's own events | Kept with their name: scripts get them in onEvent. An event with a Codename name but more values than Codename's is taken as the mod's own. |
Saving it in the chart editor makes a Psych chart: the editor asks where, so the Codename file isn't replaced.
On the chart editor's event list, with no script needed. Values with more than one part are separated by commas (FF0000, 1.5); eases are the ones of Lua's tweens (quadOut, sineInOut...). A mod with its own event of the same name keeps its own, and scripts still get them in onEvent.
| Event | Value 1 | Value 2 |
|---|---|---|
| Flash Camera | color, duration (FFFFFF, 1) | camera: game, hud or other |
| Fade Screen | color, alpha 0-1 (000000, 1) | duration, ease (1, linear) |
| Set Camera Zoom | zoom (blank: the stage's) | duration, ease (blank: at once) |
| Fade HUD | alpha 0-1 | duration, ease (0.5, linear) |
| Cinematic Bars | size in pixels (0 takes them away) | duration, ease |
| Lyrics | text (blank clears it, \n = new line) | color |
| Camera Target | dad, bf, gf or an extra character's tag | (blank: follows the sections) |
| Camera Bop | beats between bops | how strong (1) |
| Character Trail | dad, bf, gf or an extra character's tag | color (blank removes it) |
| Notes Fade | player, opponent or both | alpha, duration, ease |
| Camera Angle | game, hud or other | angle, duration, ease |
| Change Stage | the stage's name | (optional) keep, snap, keepzoom |
The menus and editors show a Tutorial button (F6 also opens it): Boyfriend zooms into each part of the screen and explains it.
A mod adds its own with data/tutorials/<ScreenName>.json, named after the screen's class (MainMenuState, ChartingState, a custom state...). A mod's tutorial replaces the game's one for that screen.
{
"button": "top-right",
"steps": [
{"text": "Hi! This step has no target: the whole screen is shown."},
{"text": "This is the song list.", "target": "grpSongs", "zoom": 1.5},
{"text": "The Note tab.", "target": "mainBox", "set": {"mainBox.selectedName": "Note"}},
{"text": "A spot of the screen.", "rect": [100, 100, 300, 200], "pose": "up"}
]
}
| Field | What it does |
|---|---|
target | The name of something on the screen (a variable of the state), or several in a list. Dots go inside it: "menuItems.members.2". |
rect | A place on the screen instead: [x, y, width, height] (the screen is 1280x720). area picks a part of the target the same way. |
zoom | How close it goes (2 by default). |
set | Values changed while the step is shown (a tab, a menu...). Everything goes back when the tutorial ends. |
pose | What Boyfriend does: hey, left, down, up, right or idle. |
button | Where the button goes: top-left, top-center, top-right, bottom-left, bottom-center, bottom-right, or [x, y]. |
key | Translations: with "key": "mymod_menu", the language files' mymod_menu_1, mymod_menu_2... replace the steps' texts. |
HScript files in a states folder of the mod, named after the screen they're for, run on that screen: change the menus and make your own loading screen.
mods/My Mod/states/
├─ Global.hx ← every screen
├─ MainMenuState.hx ← the main menu
├─ FreeplayState.hx ← Freeplay
└─ LoadingState.hx ← the loading screen before a song
// mods/My Mod/states/MainMenuState.hx
function onCreatePost() {
var hello = new FlxText(20, FlxG.height - 70, 0, 'Hello from my mod!', 28);
add(hello);
}
onCreate, onCreatePost, onUpdate, onUpdatePost, onStepHit, onBeatHit, onOpenSubState, onCloseSubState, onDestroy.optionsGear.visible = false;), and add() / insert() / remove() work on it.onLoadingProgress(loaded, total, percent), and onFinishedLoading() returning Function_Stop keeps the screen on until finishLoading().switchState('CreditsState'), debugPrint(), parseJson() and sharedData (kept from one screen to the next).The full guide, with every screen name, callback and function: STATE_SCRIPTS.md · Template: StateTemplate.hx
.xml next to the icon's .png (images/icons/NAME.png + NAME.xml) with the animations normal, losing and winning (by prefix, 24 fps, looped).An image at images/cursor.png (the game's or a mod's: the mod being played comes first) is the cursor on every screen.
An optional images/cursor.txt next to it sets its size and the pixel that clicks: scale, x, y (for example 2, 0, 0, or 1, 16, 16 for a 32x32 crosshair clicking with its middle).
Mods made with Vortex Engine are not recommended for other engines: a mod that uses what only Vortex has will break or behave differently there. The mods menu's CHECK MOD button lists everything a mod uses that only Vortex has, with the file and line of each one. A mod with no ERROR or DIFFERENT should also work on Psych Engine 1.0.4 (or V-Slice, for V-Slice mods). See other engines.
For people making a mod with Vortex Engine, or shipping their own version of it: a copy of the engine without the songs and weeks of the base game, so only the mod's own songs show up in Freeplay and Story Mode. Nothing besides Haxe is needed.
lime build windows
haxe art/export_clean_build.hxml (or double click art/export_clean_build.bat)
haxe art/export_clean_build.hxml --zip (also makes export/clean.zip, ready to share)
The result is in export/clean. The full options are in the README.