Modding

Modding guide

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.

Psych mods

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.

V-Slice mods

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.

  • Scripts: .hxc song, stage, character, module, dialogue, Freeplay and results scripts.
  • Content: charts (-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.
  • Utility mods: a mod with scripts and no songs of its own (a pause menu, a HUD, a camera module) runs on the V-Slice songs of every mod.
  • Playable characters appear in Character Select and bring their own Freeplay DJ and results animations.
  • Base game content: the V-Slice weeks, stages and characters come with the engine, so mods that depend on them work without the official game.

V-Slice content stays in V-Slice format: what Vortex's editors save for a V-Slice mod works in the official game too.

Mod settings

The options menu has a Mods tab with the settings each mod adds, under the mod's name.

  • Psych mods: data/settings.json, as in Psych Engine (read in Lua with getModSetting).
  • V-Slice mods: the way they do on V-Slice: createPrefItemCheckbox, createPrefItemEnum, createPrefItemNumber and createPrefItemPercentage on the options menu's pages, from a module's onStateChangeEnd.

Extra characters

More characters singing in a song (a duet, a third character...), with no script. In the chart editor, the Extras tab adds them:

FieldWhat it is
TagThe name notes and scripts know it by ("pico2").
SideOpponent, Player or Girlfriend: whose notes it can sing, and whose spot it stands next to.
CharacterThe character file.
X / YWhere it stands, from the spot of the main character of its side.
Sings every note / BehindA duet with the main character / stands behind it.
Receptors of its ownIts 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.

Extra keys

Songs with 1 to 9 arrows per side, for mods like Shaggy's 6 and 9 key songs.

  • In the chart: "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.
  • In the chart editor: "Arrows per side" in the Song tab. Changing it keeps each note on its side and in its column.
  • The arrows: each column uses one of the 4 arrows or the middle one (the "space" key), with the color of its direction. They get smaller the more there are.
  • Keys: Options → More → Extra Keys. Characters sing the animation of the arrow's direction (the middle one sings up).

Codename Engine charts

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 chartIn 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.jsonRead too, from the same places.
First opponent / player strumlineThe opponent / the player.
Strumline 2 (additional)The girlfriend.
Other strumlines, and the other characters of a strumlineExtra 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 visibilityWhere the opponent's and the player's receptors are (not with Middlescroll).
No girlfriend strumlineThe song has no girlfriend.
Strumlines that aren't visibleTheir characters sing notes nobody sees (the "hidden" singer).
noteTypesEach note's type is noteTypes[type - 1] (0 is a normal note).
BPM Change, Continuous BPM Change, Time Signature ChangeThe sections (a measure each, cut where the tempo changes; a continuous change goes a step each beat).
Camera Movement / Camera PositionCamera Target, with the same time, ease and offset.
Add Camera Zoom, Camera Bop / Camera ZoomAdd Camera Zoom / Set Camera Zoom.
Camera Modulo Change / Camera FlashCamera Bop / Flash Camera (reversed: Fade Screen).
Scroll Speed Change / Play AnimationChange Scroll Speed / Play Animation (extra characters too).
Alt Animation ToggleThe notes' "Alt Animation" type, and Alt Idle Animation.
HScript Call, a mod's own eventsKept 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.

Chart events

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.

EventValue 1Value 2
Flash Cameracolor, duration (FFFFFF, 1)camera: game, hud or other
Fade Screencolor, alpha 0-1 (000000, 1)duration, ease (1, linear)
Set Camera Zoomzoom (blank: the stage's)duration, ease (blank: at once)
Fade HUDalpha 0-1duration, ease (0.5, linear)
Cinematic Barssize in pixels (0 takes them away)duration, ease
Lyricstext (blank clears it, \n = new line)color
Camera Targetdad, bf, gf or an extra character's tag(blank: follows the sections)
Camera Bopbeats between bopshow strong (1)
Character Traildad, bf, gf or an extra character's tagcolor (blank removes it)
Notes Fadeplayer, opponent or bothalpha, duration, ease
Camera Anglegame, hud or otherangle, duration, ease
Change Stagethe stage's name(optional) keep, snap, keepzoom

Tutorials

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"}
    ]
}
FieldWhat it does
targetThe name of something on the screen (a variable of the state), or several in a list. Dots go inside it: "menuItems.members.2".
rectA place on the screen instead: [x, y, width, height] (the screen is 1280x720). area picks a part of the target the same way.
zoomHow close it goes (2 by default).
setValues changed while the step is shown (a tab, a menu...). Everything goes back when the tutorial ends.
poseWhat Boyfriend does: hey, left, down, up, right or idle.
buttonWhere the button goes: top-left, top-center, top-right, bottom-left, bottom-center, bottom-right, or [x, y].
keyTranslations: with "key": "mymod_menu", the language files' mymod_menu_1, mymod_menu_2... replace the steps' texts.

State scripts

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);
}
  • Callbacks: onCreate, onCreatePost, onUpdate, onUpdatePost, onStepHit, onBeatHit, onOpenSubState, onCloseSubState, onDestroy.
  • The screen's own things are reached by their names (optionsGear.visible = false;), and add() / insert() / remove() work on it.
  • Loading screens: 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

Health icons

  • Animated icons: an .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).
  • Winning icons: an icon image with 3 frames side by side (normal, losing, winning) instead of 2.

Mouse cursor

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).

CHECK MOD

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.

A build without base songs

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.