On this page 39 sections
- Words used on this page
- On-screen text
- show_text
- Menus and screens
- blse.menu_opened, blse.menu_closed
- open_menus
- menu_panels
- menu_set_text, menu_set_visible, menu_move
- menu_set_image, menu_press, menu_command
- menu_add_label, menu_add_button, menu_add_image, menu_remove
- blse.menu_button
- window_create, window_show, window_remove
- blse.window_closed
- menu_add_text_entry
- menu_set_colors, menu_set_font, menu_set_border, menu_reset_style, menu_set_hint
- set_button_sound, clear_button_sounds
- blse.menu_hover
- Controls, lists and blocked screens
- menu_add_checkbox, menu_add_slider, menu_add_dropdown, menu_add_progress
- menu_add_list, menu_list_add, menu_list_clear
- menu_value, menu_set_value, menu_set_items
- blse.menu_changed
- menu_block
- blse.menu_blocked
- hud_in_menus
- Translated interface text
- translate_text
- HUD overlay
- How the overlay works
- hud_set, hud_remove
- hud_box, hud_image
- hud_clickable, hud_cursor
- blse.hud_click, blse.hud_hover
- hud_align, hud_valign, hud_size
- hud_input, hud_focus, hud_input_text, hud_multiline, hud_hint
- blse.hud_text_changed, blse.hud_text_submit, blse.hud_text_cancel
- hud_line, hud_polygon, hud_rotate, hud_font
- screen_size
- set_chargen_clan_rule, add_chargen_clan_filter
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.
#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). Useadd_taskto 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 analyzedvguimatsurface.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#
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,
kindis invalid,textisNULL, or the build is not recognized. Main thread.
host->show_text(host->context, "Blood: 100% restored", BLSE_TEXT_TOP);Menus and screens#
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 negativeworhkeeps the size): 1 or 0.b0.5.31:
menu_panelsrecords gain two fields, kind (label,button,text_entry,image,panelor empty) and image;menu_set_textalso 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)andmenu_remove(context, screen, path)(labels this plugin added; they go when it is unloaded): 1 or 0.b0.5.32:
menu_panelsrecords gain a twelfth field, the command a button sends;menu_add_button(context, screen, parent_path, name, text, x, y, w, h)(a click publishesBLSE_MSG_MENU_BUTTON: screen, path) andmenu_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; topicBLSE_MSG_WINDOW_CLOSED) andmenu_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)andmenu_set_hint(context, screen, path, hint); a plugin's styles are undone when it is disabled or unloaded.window_createfor 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";screenandbuttonnames or patterns with*and?;filebelowsound/,""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) andplay_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. TopicBLSE_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.
/* 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#
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)
menu_panels#
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.
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);menu_set_text, menu_set_visible, menu_move#
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;$KEYtext is translated).textmust not beNULL.menu_set_visible: 1 shows, 0 hides.menu_move: the new position in pixels (relative to the parent panel);wandhthe new size, a negativeworhkeeps the size.- Return 1, or 0 if the screen is not open or the path names no panel.
menu_set_image, menu_press, menu_command#
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_imageshows another image in an image panel (kindimage): a name belowmaterials/vgui/, as in the game's.resfiles (the game's own look like"hud/signs/ventrue").menu_presspresses a button as a mouse click does: it does whatever the button does.menu_commandgives 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.
menu_add_label, menu_add_button, menu_add_image, menu_remove#
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 thenparent_path/name(orname). 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,$KEYtext is translated.image: a name belowmaterials/vgui/.x, y, w, h: pixels, relative to the parent.- A click on your button publishes
blse.menu_button. menu_removeremoves a label, button, image, text field or control your plugin added (not the game's panels; hide those withmenu_set_visible).- Your panels live as long as the screen does; a plugin's go when it is unloaded.
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#
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 ofA-Z a-z 0-9 _ -. It then works as thescreenfor everymenu_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_createagain for your window changes its title, position and size, and shows it again if the player closed it (b0.5.37). window_showhides (0) or shows (1) it;window_removedeletes 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_shutdownif it should not. - A plugin's windows go when it is unloaded.
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#
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.
menu_set_colors, menu_set_font, menu_set_border, menu_reset_style, menu_set_hint#
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 colourfgand the backgroundbgas"#RRGGBB"or"#RRGGBBAA"text;NULLkeeps one. A background with alpha00is 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;sizein pixels, 6-128;weight100-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 (""orNULL: 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.
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#
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 asmenu_panelswrites 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: belowsound/;""removes your rule.volume0 to 1,pitch100 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_soundsremoves 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.
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.
/* 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);menu_add_checkbox, menu_add_slider, menu_add_dropdown, menu_add_progress#
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:
textbeside it;checked1 or 0. - Slider: whole numbers from
minimumtomaximum(maximummust be greater thanminimum), starting atvalue. A click on it moves the value there. - Drop-down:
itemsis an array ofitem_countstrings (1 to 256);selectedthe text chosen at first. Its value is the chosen text. - Progress bar:
valuefrom 0 to 1 (values outside are clamped). - When the player changes a checkbox, slider or drop-down,
blse.menu_changedis published.
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);menu_add_list, menu_list_add, menu_list_clear#
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_addadds a row at the end, or gives an existing row new text.row: the row's name (not empty, no/, not starting with#);textwhat it shows;height8 to 512 pixels (24 is the usual).- A click on a row publishes
blse.menu_buttonwith the path<list>/<row>. menu_list_clearremoves every row.
menu_value, menu_set_value, menu_set_items#
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_valuecopies 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_valuesets a checkbox, slider, progress bar or drop-down (also the game's own) from text. It does not publishblse.menu_changed.menu_set_itemsgives a drop-down your plugin added new choices (at most 256).
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.
menu_block#
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.blocked1 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#
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#
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
textunchanged if it is not a key or has no translation; output buffer rules. - Returns 1, or 0 if
textisNULL. - 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.
/* 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_seton 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,y0 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: seescreen_size. - Availability. The overlay needs the analyzed
vguimatsurface.dll; without it everyhud_function returns 0.
hud_set, hud_remove#
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$KEYtext is translated).x,y: where the text starts, as fractions of the screen; by default the top-left corner (seehud_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_removeremoves any of your elements (text or another kind); returns 1 if it existed.
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#
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, yits top-left corner,w, hits size, all fractions of the screen.colorthe fill (alpha 0: no fill),borderthe colour of a 1-pixel outline (0: none).hud_image: the materialmaterials/<material>.vmtstretched over the rectangle ("interface/mainmenu/vtm_title"), tinted bycolor(0xFFFFFFFFunchanged). Mods can ship their own.vmt/.vtf(anUnlitGenericmaterial with$translucent,$vertexcolorand$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.
host->hud_box(host->context, "panel", 0.3f, 0.3f, 0.4f, 0.3f, 0x101018C0u, 0xC0A060FFu);hud_clickable, hud_cursor#
int hud_clickable(void *context, const char *id, int clickable);
int hud_cursor(void *context, int on);(b0.5.24)
hud_clickablemakes 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_cursorturns 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 asblse.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 (MOUSE1toMOUSE3, orMWHEELUP/MWHEELDOWNfor 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.
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#
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 itsx: 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 fory: 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,hmay beNULL. 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#
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, hthe box (fractions of the screen);textsets the text (UTF-8;NULLkeeps the current one; a new field starts empty);sizethe font height (8 to 96);max_charsthe longest text, 1 to 1023 characters (255 before b0.5.36);background,border,text_coloras0xRRGGBBAA.- 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_downwith 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_focusgives a field the keyboard;idNULLor""ends text entry if one of your fields has it. Returns 1, or 0 if there is no such input.hud_input_textcopies the field's current text (output buffer rules); 0 if there is no such input.hud_multiline1: 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 (""orNULL: none).- Esc ends text entry. To let Esc close your panel instead of opening the game menu, block
ESCAPEwhile the panel is open.
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_inputand""); - 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#
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),width1 to 32 pixels.hud_polygon:xyholdscountpoints as x, y pairs (so2 * countfloats).strip0: a filled convex polygon of 3 to 128 points. Concave shapes are drawn wrong (filled as a fan from the first point): split them.strip1: 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, ...colorfills it (alpha 0: no fill),bordera 1-pixel outline (0: none).
hud_rotate: turns a box or image around its centre bydegrees, 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 (NULLor"": the default font, Tahoma 700);weight100 to 900;flagsany ofBLSE_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 withmenu_set_font); until then the default font is used.- Clicks on shapes hit the shape itself (a line within a few pixels);
hud_sizegives a shape's bounding box.
screen_size#
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.
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#
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: pickingclan(a name such as"Brujah"; case is ignored) givesreplacementinstead;replacementNULLblocks 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 setclan(a clan number) orblock. 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.
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);