BLSE0.6

Python mods

Keys and the mouse

On this page 11 sections

Come here when your mod reacts to keys or the mouse: a hotkey that opens your menu, a new action on a key the player already uses, a key the game should ignore while your panel is open, or a script that presses a key for the player.

A first example: F7 shows a message while playing.

Python
import blse

def on_key(topic, data):
    key, where = data.split('\0')
    if key == 'F7' and where == 'game':
        blse.show_text('Hotkey!')

blse.subscribe('blse.key_down', on_key)

Read this first

  • Prefer the player's own bindings to fixed keys. Players rebind keys; react to blse.control_down ('+jump', '+use') or look keys up with keys_for instead of assuming SPACE or E.
  • Check where: key events come in play, in menus, in the console and while typing into a field. Usually you only want 'game'.
  • Blocked keys stay blocked until you unblock them (or Python starts again): unblock when your panel closes.
  • Key events are delivered immediately, not at the next frame.
  • On a game version BLSE does not recognize, these functions raise RuntimeError.

Words used on this page#

  • Key name: a key or mouse button as the console's bind command names it: 'a', '5', 'F5', 'SPACE', 'MOUSE1' (left button), 'MWHEELUP'. Any case is accepted. An unknown name raises ValueError.
  • Binding, command: what a key does in the game: the console command bound to it (bind SPACE +jump). Commands starting with + run while the key is held ('+attack'), others once ('slot1').
  • where: where the key went: 'game' (playing), 'cursor' (a mouse button in cursor mode), 'text' (typing into a focused HUD text field), 'console' (the console is open) or 'menu' (a game screen or the Esc menu is open, or no level runs).
  • Blocking: BLSE keeps a key press from reaching the game; your mod still hears it.
  • Simulating: BLSE presses a key as if the player did, so its binding runs.
  • Payload: the text an event hands to your handler: fields separated by NUL characters (character 0), read with data.split('\0').

Keys and mouse buttons#

Since b0.5.24 (recognized builds): every key and mouse button that reaches the game, named as in the console's bind command: 'a', '5', 'F5', 'SPACE', 'ENTER', 'SHIFT', 'CTRL', 'ALT', 'TAB', 'UPARROW', 'MOUSE1'..'MOUSE5', 'MWHEELUP', 'MWHEELDOWN', 'KP_ENTER', 'KP_HOME', ... (letters in lower case; any case is accepted). Keys without a name are reported as 'KEY<number>' and accepted in that form. Laptop Fn keys for Home, End, PgUp and so on usually arrive as the numpad names (KP_HOME, KP_END, KP_PGUP).

Function / topic
blse.key_down, blse.key_up key, where = data.split('\0'). where: 'game' (playing), 'cursor' (a mouse button in cursor mode), 'text' (typing into a focused HUD input, b0.5.25), 'console' (typing in the console) or 'menu' (a game screen or the Esc menu is open, or no level runs). Auto-repeat is not reported. Keys pressed in the Esc menu are reported since b0.5.25. Delivered immediately
blse.control_down, blse.control_up command, key = data.split('\0'): a key press in play ran its binding ('+jump', '+attack', '+use', 'slot1', ...), whichever key the player bound to it; control_up when that key is released (b0.5.26). Not sent for blocked keys, typing, the console or menus. Prefer this to fixed keys for reacting to game actions
blse.is_control_down(command) 1 while a key whose press ran command is held (b0.5.26). A key already held when play resumes (pressed during a loading screen, a menu or the console) counts from its first repeat in play (0.6)
blse.is_key_down(key) 1 if the key is held, else 0
blse.key_binding(key) The command bound to it ('' if none), e.g. key_binding('F5') → 'slot4'
blse.keys_for(command) The keys bound to command, e.g. keys_for('+jump') → ['SPACE']. Use it instead of hard-coding keys the player may have changed
blse.block_key(key[, blocked]) While blocked (by any mod), pressing the key during play does not reach the game; blse.key_down is still sent, so the mod can give the key its own meaning. blocked=0 unblocks. Not applied in the console or menus
blse.clear_blocked_keys() Unblocks every key blocked from Python
blse.press_key(key[, mode]) Simulates the key: 'tap' (default: pressed now, released after the next game frame), 'down' (pressed until 'up'; released for you if the mod is disabled or the scripts run again after an engine restart), 'up'. Its binding runs as for a real press. Simulated keys are not reported as key_down/key_up and are never blocked. While the console or a menu pauses the game there are no game frames: a tap is released when play goes on
blse.key_simulated (event) (b0.5.34) key, what, mod = data.split('\0'): a mod's press_key; what is 'down' or 'up', mod is 'python' or the plugin's name
blse.mouse_move (event) (b0.5.34) dx, dy, x, y, where = data.split('\0'): the mouse moved, at most once per rendered frame, while the game window is in front. dx, dy: pixels since the last event (right and down are positive; while the game looks around with the mouse it puts the cursor back in the middle every frame, which is not counted). x, y: the cursor in the window (0..1; in play it sits in the middle). where: 'game', 'menu', 'console' or 'cursor'. Many events: keep the handler short

Unknown key names raise ValueError; on unrecognized builds these functions raise RuntimeError.

Python
import blse
def on_key(topic, data):
    key, where = data.split('\0')
    if where == 'game' and key == 'F7':
        blse.show_text('Hotkey!')
blse.subscribe('blse.key_down', on_key)

blse.key_down and blse.key_up#

(b0.5.24) Fire when a key or mouse button is pressed (key_down) or released (key_up). Auto-repeat (a held key typing again and again) is not reported. Keys pressed in the Esc menu are reported since b0.5.25. Keys a mod simulates with press_key are not reported here (see blse.key_simulated).

Payload, in order: key (the key name), where (see Words used on this page).

Python
def on_up(topic, data):
    key, where = data.split('\0')
    if key == 'MOUSE2' and where == 'game':
        blse.log('right button released')
blse.subscribe('blse.key_up', on_up)

blse.control_down and blse.control_up#

(b0.5.26) Fire when a key press in play runs its binding (control_down), and when that key is released (control_up), whichever key the player bound to it. Not sent for blocked keys, typing, the console or menus. Use them to react to game actions (jumping, attacking, using) without caring about the keys.

Payload, in order: command (the binding, '+jump'), key (the key that ran it).

Python
def on_control(topic, data):
    command, key = data.split('\0')
    if command == '+jump':
        blse.log('jump with ' + key)
blse.subscribe('blse.control_down', on_control)

blse.is_control_down#

Python
blse.is_control_down(command)   # -> 1 or 0

(b0.5.26) 1 while a key whose press ran command ('+duck', '+speed') is held, else 0. Use it for "while the player holds sprint" checks. A key already held when play resumes (pressed during a loading screen, a menu or the console) counts from its first repeat in play (about 0.6 seconds later).

Python
if blse.is_control_down('+duck'):
    blse.log('crouching')

blse.is_key_down#

Python
blse.is_key_down(key)   # -> 1 or 0

1 while the key is held, else 0. Raises ValueError for an unknown key name.

Python
if blse.is_key_down('SHIFT') and blse.is_key_down('F7'):
    blse.log('Shift+F7')

blse.key_binding and blse.keys_for#

Python
blse.key_binding(key)    # -> str
blse.keys_for(command)   # -> [key, ...]

key_binding gives the command bound to a key ('' if none): key_binding('F5') is 'slot4'. keys_for gives the keys bound to a command: keys_for('+jump') is ['SPACE'] ([] if none). Use keys_for to show the player's own key in a hint.

Python
keys = blse.keys_for('+use')
if keys:
    blse.show_text('Press %s to open' % keys[0])

blse.block_key and blse.clear_blocked_keys#

Python
blse.block_key(key[, blocked])   # -> 1
blse.clear_blocked_keys()        # -> None

block_key keeps a key from reaching the game during play (blocked 1, the default) or lets it through again (0); blse.key_down is still sent, so your mod can give the key its own meaning. Not applied in the console or menus. A key stays blocked while any mod blocks it. clear_blocked_keys unblocks every key Python blocked. block_key raises ValueError for an unknown key name.

Python
blse.block_key('ESCAPE')        # while your panel is open
blse.block_key('ESCAPE', 0)     # when it closes

blse.press_key#

Python
blse.press_key(key[, mode])   # -> 1

Presses a key for the player: its binding runs as for a real press.

  • key (str): a key name.
  • mode (str, optional, lower case): 'tap' (default: pressed now, released after the next game frame), 'down' (held until you send 'up'), 'up'. Another word raises ValueError.
  • Returns 1. Raises RuntimeError if the key could not be pressed.

A key held with 'down' is released for you if the mod is disabled or the scripts run again after an engine restart. Simulated keys are not reported as key_down/key_up and are never blocked. While the console or a menu pauses the game there are no game frames: a tap is released when play goes on.

Python
blse.press_key('SPACE')            # jump once (if SPACE is bound to +jump)

blse.key_simulated#

(b0.5.34) Fires when a mod presses or releases a key with press_key. Payload, in order: key, what ('down' or 'up'), mod ('python' or the plugin's name).

Python
def on_sim(topic, data):
    key, what, mod = data.split('\0')
    blse.log('%s pressed %s (%s)' % (mod, key, what))
blse.subscribe('blse.key_simulated', on_sim)

blse.mouse_move#

(b0.5.34) Fires when the mouse moved, at most once per drawn frame, while the game window is in front. Many events: keep the handler short, and listen only while needed.

Payload, in order:

  • dx, dy: whole pixels moved since the last event, as text (right and down are positive). While the game looks around with the mouse it puts the cursor back in the middle every frame; that jump is not counted.
  • x, y: the cursor's place in the window, 0 to 1, as text with 4 decimals (in play it sits in the middle).
  • where: 'game', 'menu', 'console' or 'cursor'.
Python
def on_move(topic, data):
    dx, dy, x, y, where = data.split('\0')
    if where == 'cursor':
        blse.hud_set('mymod_pos', '%s, %s' % (x, y), 0.02, 0.02)
blse.subscribe('blse.mouse_move', on_move)