VYREX DEVELOPERSSDKFile formatsEngineForums

VrxScript API Documentation

The official modding language for Galactic Prospectors and Starbreaker, built into the Vyrex Engine and the Vyrex Editor.

Getting Started

VrxScript is a small, safe scripting language for modding both Vyrex Engine games. You write plain-text .vrx source files in the Vyrex Editor, hit Compile, and get a sealed .vrxscr file the games load automatically. Only the Vyrex Editor and the games can read, run, or decompile sealed scripts.

  1. Open the Vyrex Editor and create a mod (pick your game: Galactic Prospectors or Starbreaker).
  2. In the Scripts tab, click New Script..., write your code (Ctrl+Space for suggestions), then Compile.
  3. Copy your mod folder into the game's Mods directory (or build it there directly):
GameMods folder
Galactic Prospectors%APPDATA%\GalacticProspectors\Mods\
Starbreaker%APPDATA%\VyrexEngine\3D\GalacticProspectors\Mods\

Launch the game and enter a world - your scripts load automatically and on init fires. That's it.

// my_first_mod.vrx - the smallest possible mod
on init {
    game.toast("Hello from my first mod!");
}

Mod Structure

Every mod is a folder with a manifest and five standard subfolders. The Vyrex Editor scaffolds all of this for you.

MyMod/
  mod.vrxcfg      # manifest: name, author, version, game, description
  Scripts/        # sealed .vrxscr scripts (+ your .vrx sources)
  Textures/       # .vrxtex textures (import PNG in the editor)
  Models/         # .vrxmdl models (import OBJ in the editor)
  Animations/     # .vrxanm animations (import JSON in the editor)
  Data/           # config overrides (.roid XML for GP, .vrxcfg for Starbreaker)

The manifest is human-readable:

mod "Meteor Madness" {
    author = "you"
    version = "1.0.0"
    game = starbreaker        # or: galacticprospectors
    description = "Regular meteor showers with bonus ore."
}

The Language

VrxScript is C-style: braces, semicolons, // and /* */ comments. It has five value types: numbers (64-bit float), strings, booleans, null, and arrays.

Variables

var credits = 100;
var name = "Prospector";
var active = true;
var nothing = null;
var list = [1, 2, 3];

credits += 50;        // also -= *= /=
list[0] = 99;        // index read/write

Variables declared at the top level of a script are globals - they keep their values between events and are shared by all handlers in the same script. Variables declared inside a function or handler are local to it.

Control flow

if (credits >= 1000 && active) {
    game.log("rich!");
} else {
    game.log("keep mining");
}

while (credits < 500) {
    credits += 10;
    if (credits == 300) break;      // continue; works too
}

for (var i = 0; i < len(list); i += 1) {
    game.log("item " + list[i]);
}

Operators: + - * / %, comparisons == != < > <= >=, logic && || !. + concatenates when either side is a string. && and || short-circuit.

Functions

fn reward_for(kills) {
    return kills * 150;
}

on init {
    player.add_money(reward_for(3));
}

Events

Handlers are declared with on. A script can define any subset of them.

on init { }

Runs once when your mod loads (world entry). Use it for setup and a hello toast.

on tick(dt) { }

Runs every frame during gameplay. dt is the time in seconds since the last tick (typically 0.016). Keep per-tick work small - accumulate dt for timers.

on key(name) { }

Fires once per key press during gameplay for "A"-"Z", "0"-"9" and "F1"-"F12". This is how you build custom keybinds. Not fired while the chat box is open.

on key(name) {
    if (name == "F6") { game.toast("custom keybind!"); }
}

Sandbox & Limits

Scripts run in a hard sandbox. The only way a script touches the game is through the functions documented on this page - there is no file, network, or reflection access, and none will be added.

  • Multiplayer-safe: every world-mutating call (marked host/SP) silently does nothing on a multiplayer client. Mods can never desync a server. Multiplayer/network internals are not exposed to scripts, by design, ever.
  • Fuel limit: each event may execute ~200,000 instructions. Runaway loops are stopped with a script error instead of freezing the game.
  • Recursion cap (64 frames) and array bounds checks - errors, not crashes.
  • Auto-disable: a script that errors 8 times is disabled for the session and logged.
  • Unknown API calls are detected at load time; the script is skipped with a clear log message (check the target game of your mod!).
Errors go to the game log. Galactic Prospectors: %APPDATA%\GalacticProspectors\Logs\Game.log. Starbreaker: %APPDATA%\VyrexEngine\3D\GalacticProspectors\game.log. Look for lines starting with [Mods] / [Mod].

Shared API both games

game.*

game.log(msg)

Writes a line to the game log, prefixed [Mod]. Your best debugging tool.

game.log("credits are now " + player.get_money());
game.play_sound(name [, gain])

Plays a named sound event through the game's audio system. gain is 0..1 (default 1). Unknown names are ignored safely.

game.play_sound("toast", 0.6);
game.time()

Seconds of gameplay since the world was entered (only counts while actually playing).

Returns: number

input.*

input.is_down(name)

True while a key is physically held. Same key names as on key: "A"-"Z", "0"-"9", "F1"-"F12". Combine with on tick for hold-style controls.

on tick(dt) {
    if (input.is_down("B")) { game.log("B is held"); }
}
Returns: bool

player.*

player.get_money()

Current credit balance.

Returns: number
player.add_money(n) host/SP

Adds (or with a negative value, removes) credits.

Returns: the new balance
player.get_health()

Current hull health.

Returns: number
player.get_max_health()

Maximum hull health.

Returns: number
player.heal(n) host/SP

Restores up to n health, clamped at max.

Returns: health after healing

world.*

world.enemy_count()

Number of live hostiles around you.

Returns: number
world.asteroid_count()

Number of loaded asteroids around you.

Returns: number

Galactic Prospectors API 2D

Everything in the Shared API, plus:

game.*

game.toast(title, msg)

Shows an on-screen notification with a title and message.

game.toast("My Mod", "Something happened!");

player.*

player.get_x()

Player ship X position.

Returns: number
player.get_y()

Player ship Y position.

Returns: number
player.teleport(x, y) host/SP

Instantly moves the player ship.

// panic button: jump 2000 units up on F9
on key(name) {
    if (name == "F9") {
        player.teleport(player.get_x(), player.get_y() - 2000);
        world.spawn_warp_fx(player.get_x(), player.get_y());
    }
}
player.get_scrap()

Scrap currently carried.

Returns: number
player.add_scrap(n) host/SP

Adds scrap, or removes it with a negative value. Never goes below zero.

Returns: the new total
player.set_health(n) host/SP

Sets hull health directly, clamped to the maximum.

Returns: health afterwards
player.damage(n) host/SP

Takes n off the hull. Stops at zero rather than going negative.

Returns: health afterwards
player.set_max_health(n) host/SP

Changes the hull's maximum. Current health is pulled down to fit if it has to be.

Returns: the new maximum
player.get_velocity_x()

Ship velocity along X, units per second.

Returns: number
player.get_velocity_y()

Ship velocity along Y, units per second.

Returns: number
player.set_velocity(x, y) host/SP

Sets the ship's velocity outright. Useful for a shove, a tractor pull or a hard stop.

// emergency brake on F10
on key(name) {{
    if (name == "F10") {{
        player.set_velocity(0, 0);
        game.toast("Brake", "All stop.");
    }}
}}
Returns: true on success
player.get_speed()

How fast the ship is going, whatever the direction.

Returns: number
player.get_boost()

Boost charge remaining, 0 to 100.

Returns: number
player.get_heat()

Current gun heat.

Returns: number
player.get_heat_threshold()

The heat level at which the guns cut out.

Returns: number
player.set_heat(n) host/SP

Sets gun heat. Drop it to zero for a coolant pack, raise it to punish something.

Returns: heat afterwards
player.is_on_foot()

True while the player is out of the ship in their suit.

Returns: boolean
player.is_alive()

False once the hull is at zero.

Returns: boolean
player.get_ship_id()

The id of the hull the player is flying, matching Ships.vrxcfg.

Returns: string
player.get_sector()

Which sector the player is in, by name.

Returns: string

world.*

world.vessel_count()

Live AI vessels nearby.

Returns: number
world.station_count()

Stations in the loaded sector.

Returns: number
world.spawn_vessel(id, x, y) host/SP

Spawns one AI vessel by its id from AIVessels.vrxcfg, at a position.

// an ambush where the player is standing
var p = player.get_x() + 600;
world.spawn_vessel("void_pirate_scout", p, player.get_y());
Returns: true if spawned
world.nearest_asteroid(x, y)

The closest live asteroid to a point, or to the player if no point is given. Comes back as "x,y", or empty if there is none.

Returns: string
world.nearest_enemy(x, y)

The closest live hostile, as "x,y", or empty.

Returns: string
world.nearest_station(x, y)

The closest station, as "x,y", or empty.

Returns: string
world.distance(x1, y1, x2, y2)

Straight-line distance between two points.

Returns: number
world.is_multiplayer()

True when this world has other players in it.

Returns: boolean
world.is_host()

True when this machine decides what happens. Every call marked host-only does nothing when this is false, so check it before building on one.

Returns: boolean
world.spawn_asteroid(x, y, scale) host/SP

Spawns an asteroid at a position. scale ~0.5 (small) to 2.0 (huge), 1.0 = normal.

Returns: true if spawned
world.spawn_pirate() host/SP

Spawns one hostile ship using the game's normal hostile-spawn rules.

Returns: true if spawned
world.spawn_warp_fx(x, y)

Plays the warp-in visual effect at a position. Cosmetic only, safe anywhere.

cargo.*

cargo.slots_used()

How many distinct items are in the active hold.

Returns: number
cargo.count(id)

How much of one item is held. The id is the item's guid, as it appears in Craftables.vrxcfg or Resources.vrxcfg.

Returns: number
cargo.add(id, amount) host/SP

Puts items in the hold. amount defaults to 1 and may be negative.

Returns: how much is held afterwards
cargo.remove(id, amount) host/SP

Takes items out of the hold, stopping at empty.

Returns: how much is held afterwards
cargo.ids()

Every item id in the hold, comma separated. Split it to walk the hold.

Returns: string

enemy.*

enemy.count_within(x, y, radius)

How many live hostiles are inside a circle.

Returns: number
enemy.kill_nearest(x, y) host/SP

Destroys the closest hostile to a point, or to the player if no point is given.

Returns: true if one died

asteroid.*

asteroid.count_within(x, y, radius)

How many live asteroids are inside a circle.

Returns: number

faction.*

faction.reputation(factionId)

Your standing with an AI faction, by its id from Factions.vrxcfg.

Returns: number
faction.add_reputation(factionId, amount) host/SP

Moves your standing with a faction up or down.

Returns: true on success
faction.at_war(factionId)

True while that faction is at war with you.

Returns: boolean

ui.*

ui.chat(message)

Writes a line into the in-game chat, the way the game announces things.

Returns: true on success
ui.add_marker(name, x, y)

Drops a GPS marker the player can navigate to.

var rock = world.nearest_asteroid();
if (rock != "") {{
    ui.add_marker("Survey target", 0, 0);
}}
Returns: true on success

data.*

data.get(file, path)

Reads a value straight out of a .vrxcfg content file, including a mod's override of it. The path walks child nodes with /, and Kind[id] picks one by id.

var hp = data.get("AIVessels", "Vessel[void_pirate_scout]/Health");
game.log("scout has " + hp + " hull");
Returns: the value as a string, or empty
data.ids(file, kind)

Every id of one kind in a data file, comma separated. Walk it to react to whatever content is installed rather than a hardcoded list.

var all = data.ids("AIVessels", "Vessel");
game.log("vessel types: " + all);
Returns: string

server.*

server.broadcast(text) host/SP

Posts a SERVER chat line every player sees.

server.broadcast("Double ore weekend is live!");
Returns: bool
server.send(id, text) host/SP

Sends a private server notice (toast) to one player by SteamID string.

Returns: bool
server.players() host/SP

Names of all connected players.

Returns: array of strings
server.player_count() host/SP

Number of connected players.

Returns: number
server.player_name(id) host/SP

Display name for a SteamID string.

Returns: string or null
server.find_player(name) host/SP

SteamID string of the connected player with that display name (case-insensitive), or null.

Returns: string or null
server.is_admin(id) host/SP

True when that SteamID has Admin (or higher) permission on this server. Always gate kick/give commands with this.

Returns: bool
server.kick(id, reason) host/SP

Kicks a player (same path as the admin panel). Cannot kick the host.

Returns: bool
server.give_money(id, amount) host/SP

Grants credits to a player through the host-authoritative reward pipe.

Returns: bool
server.give_scrap(id, amount) host/SP

Grants scrap to a player through the host-authoritative reward pipe.

Returns: bool

Starbreaker API 3D

Everything in the Shared API, plus:

game.*

game.toast(text [, colorHex])

Shows a HUD notification. Optional hex color, default "#7FE9FF".

game.toast("Something happened!", "#FFB63D");

player.*

player.get_pos()

The player ship's position as an array [x, y, z] - handy for math.

var p = player.get_pos();
game.log("at " + p[0] + ", " + p[1] + ", " + p[2]);
Returns: [x, y, z]
player.get_pos_x()

Player ship X position.

Returns: number
player.get_pos_y()

Player ship Y position.

Returns: number
player.get_pos_z()

Player ship Z position.

Returns: number
player.teleport(x, y, z) host/SP

Instantly moves the player ship and zeroes its velocity.

player.get_shield()

Current shield points.

Returns: number
player.get_speed()

Current ship speed in m/s.

Returns: number
player.get_cargo_count()

Ore units currently in the hold.

Returns: number
player.get_cargo_capacity()

Total cargo hold size.

Returns: number
player.give_ammo(missiles, decoys) host/SP

Grants missiles (capped at 20 total) and decoys (capped at 10).

Returns: true on success
player.give_ore(name, units) host/SP

Adds ore directly to your cargo hold, respecting free capacity. Ore names match the in-game deposits, e.g. "Iron", "Copper", "Titanium", "Platinum".

var granted = player.give_ore("Iron", 25);
game.toast("Received " + granted + " iron");
Returns: units actually granted (0 if the hold is full)

world.*

world.spawn_asteroid(x, y, z, oreType, ore) host/SP

Spawns an ore asteroid. oreType is the ore index (0 = Iron, 1 = Copper, 2 = Titanium, 3 = Platinum...), ore is the amount it holds.

Returns: true if spawned
world.spawn_pirates(x, y, z, count) host/SP

Spawns a pirate group (1-8 ships) centered on a position. They will come for you.

world.spawn_ore(x, y, z, type, units, drops) host/SP

Scatters collectible ore pickups: units total ore split across drops pieces around the position.

world.nearest_asteroid()

Position of the closest live asteroid as [x, y, z], or null if none are loaded.

var rock = world.nearest_asteroid();
if (rock != null) {
    var p = player.get_pos();
    var d = sqrt(pow(rock[0]-p[0],2) + pow(rock[1]-p[1],2) + pow(rock[2]-p[2],2));
    game.toast("Nearest rock: " + round(d) + " m");
}
Returns: [x, y, z] or null

Builtins both games

Pure functions, always available, no game access.

FunctionDescription
abs(n)Absolute value.
floor(n)Round down.
ceil(n)Round up.
round(n)Round to nearest.
sqrt(n)Square root.
pow(base, exp)Exponentiation.
min(a, b)Smaller of two.
max(a, b)Larger of two.
clamp(n, lo, hi)Constrain to a range.
sin(rad)Sine (radians).
cos(rad)Cosine (radians).
atan2(y, x)Angle of a vector (radians).
random()Random number 0..1.
random_range(a, b)Random number in [a, b).
random_int(a, b)Random whole number in [a, b] inclusive.
str(v)Convert any value to a string.
num(s)Parse a number (null on failure).
len(v)Length of a string or array.
upper(s)Uppercase.
lower(s)Lowercase.
contains(s, sub)Case-insensitive substring test.
substring(s, start [, count])Slice a string.
split(s, sep)Split a string into an array.
push(arr, v)Append to an array.
pop(arr)Remove and return the last element.
remove_at(arr, i)Remove the element at an index.
index_of(arr, v)Find the index of a value (-1 if absent).

Cookbook: Example Mods

1. Custom keybind toolkit both games

// Bind gameplay actions to any key you like.
on key(name) {
    if (name == "F5") { game.toast("Balance: " + player.get_money()); }
    if (name == "F6") { player.heal(100); game.play_sound("toast"); }
    if (name == "F7") { game.log("time: " + round(game.time()) + "s"); }
}

2. Meteor shower events Starbreaker

var next_shower = 120;

on tick(dt) {
    if (game.time() < next_shower) return;
    next_shower = game.time() + random_range(180, 360);

    game.toast("Meteor shower incoming!", "#FFB63D");
    var p = player.get_pos();
    for (var i = 0; i < 5; i += 1) {
        world.spawn_asteroid(
            p[0] + random_range(-400, 400),
            p[1] + random_range(300, 600),
            p[2] + random_range(-400, 400),
            random_int(0, 3), random_int(40, 90));
    }
}

3. Hardcore mode Galactic Prospectors

// More pirates when you get rich - risk scales with wealth.
var timer = 0;

on tick(dt) {
    timer += dt;
    if (timer < 60) return;
    timer = 0;

    var wealth_waves = clamp(floor(player.get_money() / 50000), 0, 4);
    for (var i = 0; i < wealth_waves; i += 1) {
        world.spawn_pirate();
    }
    if (wealth_waves > 0) {
        game.toast("Bounty Hunters", "Your wealth attracts attention...");
    }
}

4. Mining assistant Starbreaker

// Press M for a range ping to the nearest rock + cargo report.
on key(name) {
    if (name != "M") return;

    var rock = world.nearest_asteroid();
    if (rock == null) { game.toast("No asteroids nearby"); return; }

    var p = player.get_pos();
    var d = sqrt(pow(rock[0]-p[0],2) + pow(rock[1]-p[1],2) + pow(rock[2]-p[2],2));
    var hold = player.get_cargo_count() + "/" + player.get_cargo_capacity();
    game.toast("Rock " + round(d) + " m | Cargo " + hold);
}

5. Daily login bonus both games

var paid = false;

on tick(dt) {
    if (paid || game.time() < 10) return;
    paid = true;
    player.add_money(500);
    game.play_sound("toast");
    game.log("login bonus granted");
}