BLSE0.6

Native plugins

Interface for plugins

On this page 39 sections

This page is the plugin reference for everything the player sees and clicks:

  • on-screen text: a message in the middle of the screen, the top-left corner or the console;
  • the game's menus and screens (the Esc menu, inventory, character sheet, Options): reading what they show, changing their labels, buttons, colours and fonts, adding your own labels, buttons, images, text fields and controls, making windows of the game's own kind, giving buttons sounds, keeping a screen from opening;
  • the HUD overlay: text, boxes, images, lines and shapes BLSE draws over the game while a level runs, with clicks and text entry fields;
  • clans while a character is made.

Come here to show something to the player or to let the player choose something. The Python pages (interface, screens) describe the same features with Python examples; this page gives the C side.

A minimal example: a console command that shows a message, and a HUD line that stays in the top-left corner.

C
#include "blse_api.h"

static const BLSE_HostAPI *host;

static void BLSE_CALL hello_cmd(int argc, const char *const *argv, const char *args, void *user) {
    if (!host->show_text(host->context, "Hello from my plugin", BLSE_TEXT_CENTER))
        host->console_print(host->context, "no level is running\n");
    /* green text, 2% from the left and top, 24 pixels high */
    host->hud_set(host->context, "status", "My mod is on", 0.02f, 0.02f, 0x40FF40FFu, 24);
}

BLSE_EXPORT int BLSE_CALL BLSE_PluginLoad(const BLSE_HostAPI *h) {
    host = h;
    if (h->api_version != BLSE_API_VERSION || !BLSE_HOST_HAS(h, hud_set)) return 0;
    h->add_command(h->context, "mymod_hello", "Shows a greeting", hello_cmd, NULL);
    return 1;
}

Important, before you start:

  • Main thread only. Every function on this page returns 0 from another thread, except translate_text (any thread). Use add_task to get onto the main thread.
  • show_text, hud_cursor (turning it on) and the HUD need a running level for what they show to be seen; the HUD overlay is hidden under game screens unless you ask otherwise (hud_in_menus).
  • Your elements, panels, windows, styles and sounds belong to your plugin. Other plugins and Python cannot change them by id, and they are removed when your plugin is disabled or unloaded (HUD elements when it is disabled).
  • The game may rebuild a screen when it opens or updates it (selecting an item, switching tabs): make your changes again from blse.menu_opened.
  • Features depend on the game's interface DLLs: menu functions need the analyzed vgui2.dll, the HUD overlay the analyzed vguimatsurface.dll (both the original game and the Unofficial Patch ship them). Otherwise the functions return 0.

Words used on this page#

Word Meaning
Screen One of the game's menus or dialogs, named by BLSE: game_menu (the Esc menu, also the main menu), inventory, character, journal, options, ... (full list under blse.menu_opened). A window your plugin makes also works as a screen, by its name
Panel One part of a screen: a label, a button, an image, a text field, or a container of other panels. The game's interface (VGUI) is a tree of panels
Path Which panel of a screen you mean: the panel names from the screen down, joined by / ("StatsPanel/HumanityLabel"); any panel can also be named by its position among its siblings, #0, #1, ... "" is the screen itself. Case does not matter
Pixels, fractions Menu functions take positions and sizes in pixels. HUD functions take fractions of the screen: x 0 is the left edge, 1 the right edge; y 0 the top, 1 the bottom
HUD overlay BLSE's own drawing layer over the game: your elements (text, boxes, images, lines, shapes, input fields), each with an id you choose
Colour 0xRRGGBBAA A 32-bit number: red, green, blue, alpha (opacity), one byte each. 0xFF0000FF is opaque red, 0x00000080 half-transparent black, alpha 00 invisible
Cursor mode A state in which the mouse moves a pointer over the HUD instead of turning the view; clicks go to HUD elements
Material A .vmt file below materials/ naming a texture and how it is drawn. HUD images take a material path without materials/ and .vmt
$KEY text A translation key: text starting with $ is replaced by its translation from the mods' translation files (see Translated interface text)
Owner Who made an element: "python" or a plugin's name. Events about HUD elements end with the owner, so you can ignore other mods' elements

On-screen text#

show_text(context, text, kind) (b0.5.16) shows text to the player: BLSE_TEXT_CENTER (middle of the screen), BLSE_TEXT_TOP (top-left line) or BLSE_TEXT_CONSOLE. Returns 0 if no level is running or the build is not recognized. See the Python reference.

show_text#

C
int show_text(void *context, const char *text, int kind);

Shows a short message through the game's own text messages. (b0.5.16)

When to use it: tell the player something happened ("Your sire would be proud.", "Blood restored").

  • text: UTF-8, up to about 190 bytes (longer text is cut). Any characters are safe (% is escaped for the game's formatting). A word starting with # is replaced if the game has a localization entry of that name.
  • kind: BLSE_TEXT_CENTER (4) in the middle of the screen for a few seconds, BLSE_TEXT_TOP (3) a line at the top left in the HUD's small-caps font (several lines stack), BLSE_TEXT_CONSOLE (2) a line in the console.
  • Returns 1 if sent, 0 if no level is running, kind is invalid, text is NULL, or the build is not recognized. Main thread.
C
host->show_text(host->context, "Blood: 100% restored", BLSE_TEXT_TOP);

blse.menu_opened and blse.menu_closed (b0.5.17): the screen's class name and BLSE's name for it (game_menu, inventory, character, journal, load_game, ...). See the Python reference.

What a screen shows (b0.5.30), main thread only:

  • open_menus(context, buffer, capacity, &size): the open screens' names, separated by '\n'.

  • menu_panels(context, screen, buffer, capacity, &size): the screen and every panel in it. Records are separated by '\x1e', fields by '\x1f': path, class, visible, x, y, w, h, label (0/1), text (UTF-8). 0 if the screen is not open.

  • menu_set_text(context, screen, path, text), menu_set_visible(context, screen, path, visible), menu_move(context, screen, path, x, y, w, h) (a negative w or h keeps the size): 1 or 0.

  • b0.5.31: menu_panels records gain two fields, kind (label, button, text_entry, image, panel or empty) and image; menu_set_text also changes buttons and text fields; menu_set_image(context, screen, path, image), menu_press(context, screen, path), menu_command(context, screen, path, command), menu_add_label(context, screen, parent_path, name, text, x, y, w, h) and menu_remove(context, screen, path) (labels this plugin added; they go when it is unloaded): 1 or 0.

  • b0.5.32: menu_panels records gain a twelfth field, the command a button sends; menu_add_button(context, screen, parent_path, name, text, x, y, w, h) (a click publishes BLSE_MSG_MENU_BUTTON: screen, path) and menu_add_image(context, screen, parent_path, name, image, x, y, w, h).

  • b0.5.36: window_create(context, name, title, x, y, w, h), window_show(context, name, visible), window_remove(context, name) (windows of the game's own kind whose name works as a screen; topic BLSE_MSG_WINDOW_CLOSED) and menu_add_text_entry(context, screen, parent_path, name, text, x, y, w, h, multiline).

  • b0.5.37: menu_set_colors(context, screen, path, fg, bg) ("#RRGGBB" or "#RRGGBBAA", NULL keeps one), menu_set_font(context, screen, path, face, size, weight), menu_set_border(context, screen, path, visible), menu_reset_style(context, screen, path) and menu_set_hint(context, screen, path, hint); a plugin's styles are undone when it is disabled or unloaded. window_create for an existing window of the plugin also shows it again.

  • b0.5.49, button sounds: set_button_sound(context, screen, button, kind, file, volume, pitch) (kind "hover", "leave", "press" or "click"; screen and button names or patterns with * and ?; file below sound/, "" removes this plugin's rule), clear_button_sounds(context) (all of this plugin's rules; also done when the plugin is disabled or declines to load) and play_ui_sound(context, file, volume, pitch) (as the game's main menu plays its sounds, also with no level loaded). A rule naming the button without wildcards beats a wildcard one, otherwise the later rule wins, across plugins and Python. Topic BLSE_MSG_MENU_HOVER ("blse.menu_hover"): a button lit up; payload screen, path (NUL-separated), published at the next paint while anyone subscribes. Main thread. See Button sounds.

Paths and behaviour as in the Python reference.

All menu functions: main thread; each returns 1 or 0 unless an entry says otherwise. screen is BLSE's name of an open game screen, or the name of a window your plugin made. A label keeps its size when its text changes, so longer text is cut off unless you also make the label wider with menu_move.

C
/* Rename the Esc menu's Options button whenever the menu opens. */
static void BLSE_CALL on_open(const char *topic, const void *payload, size_t size, void *user) {
    char name[32];
    if (payload_field(payload, size, 1, name, sizeof name) && !strcmp(name, "game_menu"))
        host->menu_set_text(host->context, "game_menu", "BTN_OPTIONS", "Settings");
}
host->subscribe(host->context, BLSE_MSG_MENU_OPENED, on_open, NULL);

(payload_field is the helper from Payloads.)

blse.menu_opened, blse.menu_closed#

Topics BLSE_MSG_MENU_OPENED and BLSE_MSG_MENU_CLOSED (b0.5.17). A game screen opened or closed. Payload, two fields: the screen's class name and BLSE's name for it. A close is published only after an open; tabs close with their screen. Delivered at once (menus run outside game logic). Needs the analyzed vgui2.dll.

BLSE's name Screen (class)
game_menu The Esc menu (CVMainMenu)
inventory Inventory, also used for trading (VBarterUI)
character Character screen, the C key (CharEditPanel)
journal The journal tab of the character screen (QuestLogPanel)
load_game, save_game, options, new_game, help The game menu's dialogs
map, hotkeys, character_creation, dialog, console, loading Other screens

open_menus#

C
int open_menus(void *context, char *buffer, size_t capacity, size_t *size);

Copies the names of the screens open now, separated by '\n' (no '\n' after the last), following the output buffer rules. "" if none is open. (b0.5.30)

C
int menu_panels(void *context, const char *screen, char *buffer, size_t capacity, size_t *size);

Copies a description of the screen and every panel in it, the screen first. Use it once to find the names of the panels you want to change. (b0.5.30; more fields b0.5.31 and b0.5.32)

  • Records are separated by the byte '\x1e', fields within a record by '\x1f'. Twelve fields per record:
# Field Meaning
0 path The panel's path ("" the screen itself)
1 class Its VGUI class name
2 visible 1 or 0
3-6 x, y, w, h Position and size in pixels
7 label 1 if it is a label, else 0
8 text The text of a label, button or text field (UTF-8), else empty
9 kind label, button, text_entry, image, panel or empty (b0.5.31)
10 image An image panel's image (b0.5.31)
11 command The command a button sends, such as ActionClicked or Close (b0.5.32)
  • A '\x1e' or '\x1f' inside a field (a label's text could hold one) is given as a space.
  • Returns 1, or 0 if the screen is not open.
C
size_t n = 0;
host->menu_panels(host->context, "game_menu", NULL, 0, &n);
char *all = (char *)malloc(n + 1);
if (all && host->menu_panels(host->context, "game_menu", all, n + 1, &n)) {
    for (char *rec = all; rec && *rec; ) {
        char *next = strchr(rec, '\x1e');
        if (next) *next++ = 0;
        char *path = rec, *end = strchr(rec, '\x1f');
        if (end) *end = 0;
        host->log(path);   /* one panel path per line in the plugin's log */
        rec = next;
    }
}
free(all);
C
int menu_set_text(void *context, const char *screen, const char *path, const char *text);
int menu_set_visible(void *context, const char *screen, const char *path, int visible);
int menu_move(void *context, const char *screen, const char *path, int x, int y, int w, int h);

Change one panel of a screen. (b0.5.30; menu_set_text on buttons and text fields b0.5.31)

  • menu_set_text: the new text of a label, button or text field (UTF-8; $KEY text is translated). text must not be NULL.
  • menu_set_visible: 1 shows, 0 hides.
  • menu_move: the new position in pixels (relative to the parent panel); w and h the new size, a negative w or h keeps the size.
  • Return 1, or 0 if the screen is not open or the path names no panel.
C
int menu_set_image(void *context, const char *screen, const char *path, const char *image);
int menu_press(void *context, const char *screen, const char *path);
int menu_command(void *context, const char *screen, const char *path, const char *command);

(b0.5.31)

  • menu_set_image shows another image in an image panel (kind image): a name below materials/vgui/, as in the game's .res files (the game's own look like "hud/signs/ventrue").
  • menu_press presses a button as a mouse click does: it does whatever the button does.
  • menu_command gives a panel (usually the screen, path "") a command, the way its buttons do (Panel::OnCommand). Source's standard dialogs take "Close" (Options, Load, Save, ...). Bloodlines' own screens (Esc menu, inventory, character) do not use commands: press their buttons instead.
C
int menu_add_label(void *context, const char *screen, const char *parent_path, const char *name, const char *text, int x, int y, int w, int h);
int menu_add_button(void *context, const char *screen, const char *parent_path, const char *name, const char *text, int x, int y, int w, int h);
int menu_add_image(void *context, const char *screen, const char *parent_path, const char *name, const char *image, int x, int y, int w, int h);
int menu_remove(void *context, const char *screen, const char *path);

Add your own panels to a screen or to your window. (labels b0.5.31; buttons and images b0.5.32)

  • parent_path: the panel to add it to ("": the screen itself).
  • name: your panel's name; its path is then parent_path/name (or name). Calling again with the same name updates the panel your plugin added (text or image, position, size) instead of adding another; it must be of the same kind.
  • text (label, button): UTF-8, $KEY text is translated. image: a name below materials/vgui/.
  • x, y, w, h: pixels, relative to the parent.
  • A click on your button publishes blse.menu_button.
  • menu_remove removes a label, button, image, text field or control your plugin added (not the game's panels; hide those with menu_set_visible).
  • Your panels live as long as the screen does; a plugin's go when it is unloaded.
C
host->menu_add_button(host->context, "game_menu", "", "mymod_btn", "My mod", 40, 400, 160, 30);

blse.menu_button#

Topic BLSE_MSG_MENU_BUTTON (b0.5.32). A button a mod added was clicked (yours or another mod's: compare the screen and path). Payload, two fields: the screen's name and the button's path. Delivered at the next painted frame, outside the game's click handling, so a handler may remove the button or its window. In Bloodlines' Esc menu, which keeps the mouse to its own buttons, an added button does not light up, but its clicks are reported all the same. A row of a list is a button too: its path is <list>/<row>.

window_create, window_show, window_remove#

C
int window_create(void *context, const char *name, const char *title, int x, int y, int w, int h);
int window_show(void *context, const char *name, int visible);
int window_remove(void *context, const char *name);

A window like the game's own dialogs (a vgui2 Frame: title bar, close button, moved and resized by the player). (b0.5.36)

When to use it: a settings dialog, a notes window, a shop list of your own.

  • name: 1-63 characters of A-Z a-z 0-9 _ -. It then works as the screen for every menu_ function ("" is the window itself): add labels, buttons, images, text fields and controls to it.
  • title: the title bar's text. x, y, w, h: position and size in pixels.
  • The window is shown at once and takes the mouse and keyboard while shown, as the inventory does (the console cannot be typed into until it is hidden: give it a button that closes it).
  • Calling window_create again for your window changes its title, position and size, and shows it again if the player closed it (b0.5.37).
  • window_show hides (0) or shows (1) it; window_remove deletes it with everything in it. Only your own windows.
  • A window stays over the Esc menu, the main menu and loading screens until you hide it: hide it from blse.menu_opened / blse.level_shutdown if it should not.
  • A plugin's windows go when it is unloaded.
C
host->window_create(host->context, "mymod_notes", "Notes", 120, 100, 460, 330);
host->menu_add_text_entry(host->context, "mymod_notes", "", "text", "", 20, 40, 420, 220, 1);
host->menu_add_button(host->context, "mymod_notes", "", "close", "Close", 20, 275, 120, 30);

blse.window_closed#

Topic BLSE_MSG_WINDOW_CLOSED (b0.5.36). The player closed a mod's window with its close button; it is hidden (window_show(name, 1) shows it again). Payload: the window's name.

menu_add_text_entry#

C
int menu_add_text_entry(void *context, const char *screen, const char *parent_path, const char *name, const char *text,
                        int x, int y, int w, int h, int multiline);

Adds a text field of the game's own kind (b0.5.36), like menu_add_label. multiline 1: several lines, Enter starts a new one. text is real text the player has to delete; for a faint hint use menu_set_hint. Read its text with menu_panels (kind text_entry, field 8), change it with menu_set_text.

C
int menu_set_colors(void *context, const char *screen, const char *path, const char *fg, const char *bg);
int menu_set_font(void *context, const char *screen, const char *path, const char *face, int size, int weight);
int menu_set_border(void *context, const char *screen, const char *path, int visible);
int menu_reset_style(void *context, const char *screen, const char *path);
int menu_set_hint(void *context, const char *screen, const char *path, const char *hint);

Restyle any panel: the game's screens and mods' own panels and windows. (b0.5.37)

  • menu_set_colors: the text colour fg and the background bg as "#RRGGBB" or "#RRGGBBAA" text; NULL keeps one. A background with alpha 00 is not drawn. Labels, buttons and text fields colour their text; windows and other panels their background. An invalid colour returns 0.
  • menu_set_font: a font for a label, button or text field. face: any Windows font ("Georgia") or one the game installed; size in pixels, 6-128; weight 100-900 (400 normal, 700 bold). Fonts are made through the game's drawing surface, so this works only after the game has drawn its first frame.
  • menu_set_border: whether the panel draws its outline (1) or not (0). A window's frame lines are drawn by the window and stay.
  • menu_reset_style: the game's own colours, font and outline again for a panel your plugin restyled; 0 if your plugin did not restyle it.
  • menu_set_hint: a hint shown faintly while a text field a mod added is empty ("" or NULL: none). Only fields mods added (the game's own text fields draw themselves).
  • A screen applies its colours and fonts when it is shown, sometimes again later: BLSE keeps your style and sets it again whenever the game changes it, for as long as the panel exists. A screen the game makes anew each time it opens needs it again: restyle in blse.menu_opened. The latest change wins when two mods restyle one panel. Your styles are undone when your plugin is disabled or unloaded.
C
host->menu_set_colors(host->context, "game_menu", "BTN_CONTINUE", "#E0C060", NULL);
host->menu_set_font(host->context, "game_menu", "BTN_CONTINUE", "Georgia", 22, 700);

set_button_sound, clear_button_sounds#

C
int set_button_sound(void *context, const char *screen, const char *button, const char *kind, const char *file, float volume, int pitch);
int clear_button_sounds(void *context);

Sounds for any button: the game's own (main and Esc menu, Options and the other dialogs, the character sheet, the inventory, ...) and mods' own, also on the main menu with no level loaded. (b0.5.49)

  • screen: a screen name ("game_menu" is the main menu and the Esc menu, "options", "character", ...; a mod's window name) or a pattern.
  • button: the path as menu_panels writes it, or just the button's own name, or a pattern. Patterns: * any characters (/ included), ? one; case ignored. A pattern without / matches a button's whole path or its own name ("BTN_*" matches "Panel/BTN_OPTIONS").
  • kind: "hover" (it lights up under the pointer), "leave" (it goes dark), "press" (the left mouse button goes down on it), "click" (it does its action: mouse release, keys, menu_press).
  • file: below sound/; "" removes your rule. volume 0 to 1, pitch 100 normal.
  • Which rule wins for one button and kind: one naming it without wildcards beats a wildcard one; otherwise the rule set later wins (from any plugin or Python). Setting your rule again replaces it and makes it the latest.
  • Rules apply to buttons as they appear (no need to set them again in blse.menu_opened). Disabled buttons are silent. A press sound plays even when the action closes the screen.
  • The main menu's own sounds stay (replace their files in a mod folder to change them).
  • clear_button_sounds removes all of your plugin's rules (also done when it is disabled or declines to load). Returns 1.
  • Nothing is attached to any button while no rule exists and nobody listens to blse.menu_hover.
C
host->set_button_sound(host->context, "*", "*", "hover", "interface/bubble_click_on.wav", 0.6f, 100);
host->set_button_sound(host->context, "game_menu", "BTN_QUIT", "hover", "mymod/ui/creak.wav", 1.0f, 100);

play_ui_sound, which plays an interface sound the same way, is on the world page: play_sound, stop_sound, play_ui_sound.

blse.menu_hover#

Topic BLSE_MSG_MENU_HOVER (b0.5.49). A button lit up under the pointer: any button of an open screen or a mod's window, the game's own included. Payload, two fields: the screen's name and the button's path (as menu_panels writes it). Published at the next paint, and only while someone subscribes (BLSE watches the open screens' buttons only then, or while button sounds exist).

Controls, lists and blocked screens#

The game's own controls, added like menu_add_button to a game screen or a mod's window (they live as long as it does), plus blocking screens and showing HUD elements over menus. (b0.5.46) Positions and sizes in pixels. Main thread; each returns 1 or 0. Behaviour as in the Python reference.

This game's scroll bars and sliders cannot be dragged (the game's own neither); they move by clicking and the mouse wheel. Sliders BLSE adds show a track but no knob: the player clicks along them, and you read the value with menu_value.

C
/* A mod's own inventory: block the game's, and open a window with a list instead. */
static void BLSE_CALL on_blocked(const char *topic, const void *payload, size_t size, void *user) {
    host->window_create(host->context, "bag", "Bag", 200, 100, 420, 520);
    host->menu_add_list(host->context, "bag", "", "items", 10, 40, 400, 470);
    host->menu_list_add(host->context, "bag", "items", "bloodpack", "Blood pack", 24);
}
host->menu_block(host->context, "inventory", 1);
host->subscribe(host->context, BLSE_MSG_MENU_BLOCKED, on_blocked, NULL);
C
int menu_add_checkbox(void *context, const char *screen, const char *parent_path, const char *name, const char *text,
                      int checked, int x, int y, int w, int h);
int menu_add_slider(void *context, const char *screen, const char *parent_path, const char *name,
                    int minimum, int maximum, int value, int x, int y, int w, int h);
int menu_add_dropdown(void *context, const char *screen, const char *parent_path, const char *name,
                      const char *const *items, size_t item_count, const char *selected, int x, int y, int w, int h);
int menu_add_progress(void *context, const char *screen, const char *parent_path, const char *name,
                      float value, int x, int y, int w, int h);

Settings controls for an options window. screen, parent_path, name, x, y, w, h as for menu_add_label.

  • Checkbox: text beside it; checked 1 or 0.
  • Slider: whole numbers from minimum to maximum (maximum must be greater than minimum), starting at value. A click on it moves the value there.
  • Drop-down: items is an array of item_count strings (1 to 256); selected the text chosen at first. Its value is the chosen text.
  • Progress bar: value from 0 to 1 (values outside are clamped).
  • When the player changes a checkbox, slider or drop-down, blse.menu_changed is published.
C
static const char *speeds[] = { "Slow", "Normal", "Fast" };
host->menu_add_checkbox(host->context, "mymod_opts", "", "enabled", "Enabled", 1, 20, 40, 200, 24);
host->menu_add_slider(host->context, "mymod_opts", "", "volume", 0, 100, 80, 20, 80, 200, 24);
host->menu_add_dropdown(host->context, "mymod_opts", "", "speed", speeds, 3, "Normal", 20, 120, 200, 24);
C
int menu_add_list(void *context, const char *screen, const char *parent_path, const char *name, int x, int y, int w, int h);
int menu_list_add(void *context, const char *screen, const char *list_path, const char *row, const char *text, int height);
int menu_list_clear(void *context, const char *screen, const char *list_path);

A scrolling list (scroll bar and mouse wheel) whose rows are buttons.

  • menu_list_add adds a row at the end, or gives an existing row new text. row: the row's name (not empty, no /, not starting with #); text what it shows; height 8 to 512 pixels (24 is the usual).
  • A click on a row publishes blse.menu_button with the path <list>/<row>.
  • menu_list_clear removes every row.
C
int menu_value(void *context, const char *screen, const char *path, char *buffer, size_t capacity, size_t *size);
int menu_set_value(void *context, const char *screen, const char *path, const char *value);
int menu_set_items(void *context, const char *screen, const char *path, const char *const *items, size_t item_count);
  • menu_value copies a control's value as text: "1"/"0" for a checkbox, a slider's number, a progress bar's "0.400", a drop-down's text. It also reads the game's own controls (the Options screens). Returns 0 for other panels.
  • menu_set_value sets a checkbox, slider, progress bar or drop-down (also the game's own) from text. It does not publish blse.menu_changed.
  • menu_set_items gives a drop-down your plugin added new choices (at most 256).
C
char v[16];
if (host->menu_value(host->context, "mymod_opts", "volume", v, sizeof v, NULL)) { int volume = atoi(v); }

blse.menu_changed#

Topic BLSE_MSG_MENU_CHANGED (b0.5.46). The player changed a checkbox, slider or drop-down a mod added (not changes made with menu_set_value). Payload, three fields: screen, path, the new value ("1"/"0", the slider's number, the drop-down's text). Checked at every paint.

C
int menu_block(void *context, const char *screen, int blocked);

Keeps a game screen from opening, so your mod can show its own instead. (b0.5.46)

  • screen: "inventory", "character", "journal", "map", "hotkeys" or "game_menu"; others return 0.
  • blocked 1 blocks, 0 ends your block. The key that opens the screen does nothing while the player is in the game (BLSE finds it by the command bound to it, so it follows the player's own keys; Escape for the game menu). A screen open already stays open.
  • Several mods may block a screen; it opens again when none does. Blocking the inventory also blocks bartering (the same screen).

blse.menu_blocked#

Topic BLSE_MSG_MENU_BLOCKED (b0.5.46). The game tried to open a screen a mod blocks (the player pressed its key); it stayed closed. Payload, two fields: the screen's class name, then BLSE's name for it (as blse.menu_opened). Show your own screen now.

hud_in_menus#

C
int hud_in_menus(void *context, const char *id, int on);

on 1: your HUD element id (text, box, image, input, shape) is also drawn, and clickable, while game screens are open, the game's menu is up and in the main menu. 0 returns to the default (hidden under screens). Returns 1, or 0 if the element does not exist. (b0.5.46)

Translated interface text#

translate_text(context, text, buffer, capacity, &size): a $KEY text (with {param} placeholders) translated from any mod's translation files, or the text as given; returns 1, 0 on bad arguments. BLSE's interface functions translate the texts they get themselves, so this is only for text a plugin draws on its own. Any thread.

translate_text#

C
int translate_text(void *context, const char *text, char *buffer, size_t capacity, size_t *size);

(b0.5.43) Any thread.

  • text: a $KEY ("$MYMOD_GREETING"), possibly with {param} placeholders, or any other text.
  • Copies the translation from any mod's translation files in the player's language, or text unchanged if it is not a key or has no translation; output buffer rules.
  • Returns 1, or 0 if text is NULL.
  • To read one mod's file by key, see translate. File format: Translated strings.

HUD overlay#

hud_set(context, id, text, x, y, color, size) and hud_remove(context, id) (b0.5.18) manage your plugin's HUD text elements: position as fractions of the screen, color 0xRRGGBBAA, size the font height in pixels (8..96). Element ids are per plugin; the elements are removed if the plugin is disabled. See the Python reference.

Since b0.5.24: hud_box(context, id, x, y, w, h, color, border) (border 0 = no outline), hud_image(context, id, material, x, y, w, h, color), hud_clickable(context, id, clickable) and hud_cursor(context, on). Clicks in cursor mode are published as BLSE_MSG_HUD_CLICK (blse.hud_click); its last field is the element's owner, your plugin's name for your elements. Cursor mode is on while any mod asks for it and ends at level shutdown; a disabled plugin's request is dropped. See the Python reference.

b0.5.36: hud_multiline(context, id, multiline) and hud_hint(context, id, hint); inputs take up to 1023 characters.

b0.5.49 (appended to BLSE_HostAPI; check struct_size before use): hud_line(context, id, x0, y0, x1, y1, color, width) (width 1..32 pixels), hud_polygon(context, id, xy, count, color, border, strip) (xy holds count x, y pairs; strip 0 = a filled convex polygon of 3..128 points, strip 1 = a strip of quads, an even count 4..128, for rings and arcs), hud_rotate(context, id, degrees) (boxes and images, clockwise around the centre) and hud_font(context, id, face, weight, flags) (text and inputs; face NULL or "" = the default font, weight 100..900, flags BLSE_FONT_ITALIC | BLSE_FONT_UNDERLINE | BLSE_FONT_STRIKEOUT). Shape points may lie between -1 and 2 (fractions of the screen). All return 1 or 0. See the Python reference.

C
/* A quarter ring (radial menu wedge) from 12 to 3 o'clock, 8 steps, on a 4:3 screen. */
float xy[2 * 18];
for (int i = 0; i <= 8; ++i) {
    float a = (float)(i * 90.0 / 8 * 3.14159265 / 180), s = sinf(a), c = cosf(a);
    xy[4 * i + 0] = 0.5f + 0.30f * s * 0.75f; xy[4 * i + 1] = 0.5f - 0.30f * c; /* outer */
    xy[4 * i + 2] = 0.5f + 0.12f * s * 0.75f; xy[4 * i + 3] = 0.5f - 0.12f * c; /* inner */
}
if (host->struct_size >= offsetof(BLSE_HostAPI, hud_font) + sizeof(host->hud_font))
    host->hud_polygon(host->context, "wedge", xy, 18, 0xC02020C0u, 0xFFFFFF60u, 1);

(b0.5.49) Effects the overlay cannot draw (a blurred background, glow, 3D shapes): draw with the game's Direct3D 9 device just before or after the overlay, see Drawing with Direct3D 9.

(b0.5.49) int screen_size(ctx, int* width, int* height): the screen in pixels as the HUD overlay last drew (0 before the first frame). Positions are fractions of a screen that is usually not square: multiply a round shape's x offsets by height / width.

(b0.5.49) int set_chargen_clan_rule(ctx, clan, replacement) and int add_chargen_clan_filter(ctx, filter, user): clans while a character is made, as blse.set_chargen_clan_rule in Python (replacement NULL blocks, "" removes). The filter gets a BLSE_ChargenClan (clan in/out as a clan number, clan_name, old_clan the clan applied last while this character is made or -1, block out) for every pick after the rules. When the sheet's arrows reach a clan that is blocked (or turned into the current one), BLSE tries the next clans that way, so a filter is also asked about those. A plugin's rules and filters go when it is disabled or declines to load. Main thread.

How the overlay works#

  • Elements. Each element has an id you choose, 1-63 characters (no control characters), separate per plugin (your "title" and another plugin's "title" are different elements). Setting an id again updates the element in place and keeps its drawing place, even when its kind changes (hud_set on a box id turns it into text).
  • Order. Elements are drawn in the order they were first created: later ones are on top.
  • Limits. Up to 64 elements in all (every plugin and Python together); text up to 255 bytes (UTF-8); up to 32 different image materials at once.
  • When it shows. While a level runs, in step with the game's own HUD: hidden under game screens, the Esc menu and loading screens (unless hud_in_menus), faded in with the HUD after a loading screen. Not hidden by the console.
  • Lifetime. Elements are not saved; they stay until you remove them, also across loads and level changes. Your plugin's elements are removed when it is disabled.
  • Position. Fractions of the screen: x, y 0 to 1; widths and heights above 0 up to 1; shape points -1 to 2. Values outside make the call return 0. The screen is usually wider than tall: see screen_size.
  • Availability. The overlay needs the analyzed vguimatsurface.dll; without it every hud_ function returns 0.

hud_set, hud_remove#

C
int hud_set(void *context, const char *id, const char *text, float x, float y, uint32_t color, int size);
int hud_remove(void *context, const char *id);

Shows or updates a text element. (b0.5.18)

  • text: UTF-8, up to 255 bytes (counted after a $KEY text is translated).
  • x, y: where the text starts, as fractions of the screen; by default the top-left corner (see hud_align, hud_valign). Updating the text keeps an element's alignment.
  • color: 0xRRGGBBAA. size: the font height in pixels, 8 to 96.
  • Returns 1, or 0 if an argument is invalid, 64 elements exist, or the overlay is unavailable.
  • hud_remove removes any of your elements (text or another kind); returns 1 if it existed.
C
char text[64];
snprintf(text, sizeof text, "Blood packs: %d", packs);
host->hud_set(host->context, "packs", text, 0.02f, 0.9f, 0xFF4040FFu, 24);

hud_box, hud_image#

C
int hud_box(void *context, const char *id, float x, float y, float w, float h, uint32_t color, uint32_t border);
int hud_image(void *context, const char *id, const char *material, float x, float y, float w, float h, uint32_t color);

(b0.5.24)

  • hud_box: a filled rectangle; x, y its top-left corner, w, h its size, all fractions of the screen. color the fill (alpha 0: no fill), border the colour of a 1-pixel outline (0: none).
  • hud_image: the material materials/<material>.vmt stretched over the rectangle ("interface/mainmenu/vtm_title"), tinted by color (0xFFFFFFFF unchanged). Mods can ship their own .vmt/.vtf (an UnlitGeneric material with $translucent, $vertexcolor and $vertexalpha, like the game's interface materials). A missing material is drawn as the game's error texture. Up to 32 different materials at once.
  • Return 1 or 0.
C
host->hud_box(host->context, "panel", 0.3f, 0.3f, 0.4f, 0.3f, 0x101018C0u, 0xC0A060FFu);

hud_clickable, hud_cursor#

C
int hud_clickable(void *context, const char *id, int clickable);
int hud_cursor(void *context, int on);

(b0.5.24)

  • hud_clickable makes an element clickable (1) or not (0); returns 0 if it does not exist. The clickable area of a text element is its drawn size; shapes are hit by their own outline.
  • hud_cursor turns cursor mode on (1) or off (0) for your plugin. While it is on for any mod, the mouse stops turning the view, a pointer is drawn, mouse buttons do not reach the game, and clicks are published as blse.hud_click. It ends at level shutdown; a disabled plugin's request is dropped. Returns 0 if unavailable, or when turning it on while no level runs.
  • With the console open, its window takes the mouse: HUD elements cannot be clicked until it is closed.

blse.hud_click, blse.hud_hover#

  • BLSE_MSG_HUD_CLICK (b0.5.24): a click in cursor mode. Payload, five fields: the id of the topmost clickable element under the pointer ("" if none), the button (MOUSE1 to MOUSE3, or MWHEELUP/MWHEELDOWN for the wheel), x and y as fractions of the screen, and the element's owner ("python" or a plugin's name; "" if none). Check the owner: your ids may equal another mod's.
  • BLSE_MSG_HUD_HOVER (b0.5.25): the clickable element (or input) under the pointer changed. Payload, two fields: id and owner, both "" when the pointer left every element. Checked every game frame.
C
static void BLSE_CALL on_click(const char *topic, const void *payload, size_t size, void *user) {
    char id[64], owner[64];
    if (!payload_field(payload, size, 0, id, sizeof id) || !payload_field(payload, size, 4, owner, sizeof owner)) return;
    if (!strcmp(owner, "myplugin") && !strcmp(id, "ok")) {   /* your plugin's name */
        host->hud_remove(host->context, "ok");
        host->hud_cursor(host->context, 0);
    }
}

hud_align, hud_valign, hud_size#

C
int hud_align(void *context, const char *id, int align);
int hud_valign(void *context, const char *id, int valign);
int hud_size(void *context, const char *id, float *w, float *h);
  • hud_align (b0.5.25): how a text element sits on its x: 0 left (x is the left edge), 1 centre (x is the middle), 2 right (x is the right edge). Text elements only.
  • hud_valign (b0.5.26): the same for y: 0 top, 1 middle, 2 bottom. Useful to centre a label on a box.
  • hud_size (b0.5.25) stores the element's width and height as fractions of the screen (text as last drawn), for laying out boxes around text. w, h may be NULL. Returns 0 if the element does not exist or was not drawn yet.
  • Values outside 0-2 return 0.

hud_input, hud_focus, hud_input_text, hud_multiline, hud_hint#

C
int hud_input(void *context, const char *id, float x, float y, float w, float h, const char *text, int size,
              int max_chars, uint32_t background, uint32_t border, uint32_t text_color);
int hud_focus(void *context, const char *id);
int hud_input_text(void *context, const char *id, char *buffer, size_t capacity, size_t *size);
int hud_multiline(void *context, const char *id, int multiline);
int hud_hint(void *context, const char *id, const char *hint);

A text entry field on the HUD: a box with editable text. (b0.5.25; hud_multiline, hud_hint b0.5.36)

When to use it: a name prompt, a search box, a chat line.

  • hud_input: x, y, w, h the box (fractions of the screen); text sets the text (UTF-8; NULL keeps the current one; a new field starts empty); size the font height (8 to 96); max_chars the longest text, 1 to 1023 characters (255 before b0.5.36); background, border, text_color as 0xRRGGBBAA.
  • A left click on the field in cursor mode focuses it (white outline, blinking caret). While a field has focus, keys pressed in play type into it and do not reach the game (they are still published as blse.key_down with the context "text"). Editing keys: arrows, Ctrl+arrows by word, Home, End, Backspace, Delete, Shift to select, Ctrl+A, Ctrl+C/X/V through the clipboard; dragging selects (b0.5.36).
  • hud_focus gives a field the keyboard; id NULL or "" ends text entry if one of your fields has it. Returns 1, or 0 if there is no such input.
  • hud_input_text copies the field's current text (output buffer rules); 0 if there is no such input.
  • hud_multiline 1: several lines; words wrap, Enter starts a new line, Up/Down move between lines, Ctrl+Enter submits.
  • hud_hint: a hint shown faintly while the field is empty ("" or NULL: none).
  • Esc ends text entry. To let Esc close your panel instead of opening the game menu, block ESCAPE while the panel is open.
C
host->hud_input(host->context, "name", 0.35f, 0.45f, 0.3f, 0.05f, NULL, 20, 32, 0x000000C0u, 0xFFFFFFFFu, 0xFFFFFFFFu);
host->hud_hint(host->context, "name", "Your name");
host->hud_focus(host->context, "name");

blse.hud_text_changed, blse.hud_text_submit, blse.hud_text_cancel#

Topics BLSE_MSG_HUD_TEXT_CHANGED, BLSE_MSG_HUD_TEXT_SUBMIT, BLSE_MSG_HUD_TEXT_CANCEL (b0.5.25). Payload, three fields: the input's id, its text (UTF-8), its owner ("python" or a plugin's name).

  • changed: after each typed character or Backspace;
  • submit: Enter was pressed (Ctrl+Enter in a several-line field); focus ends, the text stays (clear it with hud_input and "");
  • cancel: Escape, or a left click outside the field; focus ends, the text stays.

Changing focus with hud_focus sends no events.

hud_line, hud_polygon, hud_rotate, hud_font#

C
int hud_line(void *context, const char *id, float x0, float y0, float x1, float y1, uint32_t color, int width);
int hud_polygon(void *context, const char *id, const float *xy, size_t count, uint32_t color, uint32_t border, int strip);
int hud_rotate(void *context, const char *id, float degrees);
int hud_font(void *context, const char *id, const char *face, int weight, int flags);

Shapes and fonts, drawn with the game's own surface functions: a wheel, a gauge, an arrow. (b0.5.49; check BLSE_HOST_HAS(host, hud_font).) Points are fractions of the screen and may lie between -1 and 2, so a shape can reach past the screen's edges (it is clipped).

  • hud_line: a line from (x0, y0) to (x1, y1), width 1 to 32 pixels.
  • hud_polygon: xy holds count points as x, y pairs (so 2 * count floats).
    • strip 0: a filled convex polygon of 3 to 128 points. Concave shapes are drawn wrong (filled as a fan from the first point): split them.
    • strip 1: a strip of quads for rings, arcs and thick curves: an even count of 4 to 128 points taken in pairs, typically (outer, inner); points 0 1 3 2 make the first quad, 2 3 5 4 the next, ...
    • color fills it (alpha 0: no fill), border a 1-pixel outline (0: none).
  • hud_rotate: turns a box or image around its centre by degrees, clockwise (0 upright). Kept when the element is set again. Returns 0 for other kinds.
  • hud_font: the font of a text element or input. face: a Windows font face or one the game installed (NULL or "": the default font, Tahoma 700); weight 100 to 900; flags any of BLSE_FONT_ITALIC, BLSE_FONT_UNDERLINE, BLSE_FONT_STRIKEOUT (inputs show italic only). The size stays the element's. Returns 0 if the element does not exist, is another kind or a value is invalid. Fonts are made once the game has drawn a frame (up to 64 combinations, shared with menu_set_font); until then the default font is used.
  • Clicks on shapes hit the shape itself (a line within a few pixels); hud_size gives a shape's bounding box.

screen_size#

C
int screen_size(void *context, int *width, int *height);

The screen in pixels as the HUD overlay last drew it. (b0.5.49) Returns 1, or 0 (and 0 sizes) before the first frame. HUD positions are fractions of a screen that is usually not square: multiply a round shape's x offsets by height / width.

C
int w = 0, h = 0;
float aspect = host->screen_size(host->context, &w, &h) && w ? (float)h / (float)w : 0.75f;

set_chargen_clan_rule, add_chargen_clan_filter#

C
int set_chargen_clan_rule(void *context, const char *clan, const char *replacement);

typedef struct BLSE_ChargenClan {
    int clan;              /* in/out: the clan number the game is about to apply */
    const char *clan_name; /* in: its name */
    int old_clan;          /* in: the clan applied last while this character is made, -1 none yet */
    int block;             /* out: 1 keeps the current clan */
} BLSE_ChargenClan;
typedef void (BLSE_CALL *BLSE_ChargenClanFilterFn)(BLSE_ChargenClan *c, void *user);
int add_chargen_clan_filter(void *context, BLSE_ChargenClanFilterFn filter, void *user);

Control which clans a new character can be. (b0.5.49) Every way the clan is set goes through these: the questionnaire, the sheet's clan arrows, the console; only while a character is being made.

  • set_chargen_clan_rule: picking clan (a name such as "Brujah"; case is ignored) gives replacement instead; replacement NULL blocks it (the current clan stays); "" removes the rule. The later rule for the same clan wins.
  • add_chargen_clan_filter: your filter sees every pick after the rules and may set clan (a clan number) or block. A blocked pick leaves the game's current clan, and the sheet shows it. The sheet's arrows skip a blocked clan (or one turned into the current one): the filter is then also asked about the next clans that way.
  • A plugin's rules and filters go when it is disabled or declines to load. A crash in the filter disables the plugin (the pick then goes through unchanged). Main thread. Return 1 or 0.
  • What the player chose is reported by the character creation events.
C
static void BLSE_CALL no_malkavians(BLSE_ChargenClan *c, void *user) {
    if (c->clan_name && strstr(c->clan_name, "Malkavian")) c->block = 1;
}
host->add_chargen_clan_filter(host->context, no_malkavians, NULL);