BLSE0.6

Python mods

Items and inventories

On this page 13 sections

This page is about items: knowing what the player and NPCs carry and when that changes, reading an item's data (name, price, weight), changing it for a session, and adding new items and weapons to the game. Come here for a shop mod, a loot mod, a mod that reacts when the player picks something up, or a new weapon.

Giving, removing, counting, dropping, equipping and moving items are on the world page: Spawning, teleporting and inventories.

A minimal working example: tell the player when they pick up a blood pack, and show its price.

Python
import blse

def on_added(topic, data):
    owner_class, owner_name, item_class, item_name = data.split('\0')
    if owner_class == 'player' and item_class == 'item_g_bloodpack':
        info = blse.item_info(item_class)
        if info:
            blse.show_text('A blood pack (worth $%d)' % info[3], 'top')

blse.subscribe('blse.item_added', on_added)

Important, before you start:

  • The inventory events and blse.inventory() need a recognized game build (blse.build_id() is not None).
  • Item data is loaded when the game first needs it (a level's items, a merchant's goods when the trade screen opens). Before that, item_classes() does not list an item, but item_info() reads it from its file, and set_item remembers your change until it loads.
  • Changes made with set_item are not saved: they last for this session. Set them again at startup.
  • A save holding an item a mod added cannot be loaded without that mod (the game crashes). See New items and weapons.

Words used on this page#

Word Meaning
Item class, classname The kind of an item, such as item_g_bloodpack (a blood pack) or item_w_thirtyeight (a revolver). By convention item_w_ are weapons, item_g_ general items, item_a_ armour
Item file vdata/items/<classname>.txt, the game's data for one item: names, prices, models, damage
Owner The character or container holding an item
Equip Take an item in hand (a weapon). Unequip: put it away
Template An existing item class whose behaviour a new class copies
Merchant, vendor A character the player trades with; their stock is in vdata/system/vendors.txt
Merge file A file in a mod folder that adds to a game data file instead of replacing it (see mod folders)

Inventory#

On recognized game builds (b0.5.12):

Function / topic
blse.inventory([name]) List of the item classnames the player carries (or the first character with that targetname; other entities with the same name, such as a cop's car, are skipped), in slot order, e.g. ['item_w_fists', 'item_w_tire_iron', ..., 'item_g_wallet']. Raises ValueError if there is no such character or no level is running
blse.item_added, blse.item_removed owner_class, owner_name, item_class, item_name = data.split('\0'): an item entered or left a character's inventory (picked up, given, bought, sold, used up, removed by a script)
blse.item_equipped, blse.item_unequipped Same fields: a weapon or other item was taken in hand or put away. A weapon switch gives item_unequipped (old) then item_equipped (new)

Delivered at the end of the frame, for the player (owner_class == 'player') and NPCs. Nothing is reported while a level loads or shuts down (the game then restores the player's items after a load or a level transition and hands out NPCs' gear). The game's own HasItem, GiveItem and RemoveItem remain the way to check and change items.

blse.inventory#

Python
blse.inventory([name])   # -> ['item_w_fists', ...]

(b0.5.12) The item classnames a character carries, in the order of its inventory slots.

  • name: a character's targetname; left out (or ''): the player. The first character with that name is used; other entities with the same name (a cop's car) are skipped.
  • Returns a list of strings.
  • Errors: ValueError if there is no such character or no level is running; RuntimeError on a game build where inventories are not available.
Python
if 'item_w_tire_iron' in blse.inventory():
    blse.log('the player has a tire iron')

For counting stacks (how many blood packs), use blse.item_count.

blse.item_added, blse.item_removed#

An item entered or left a character's inventory: picked up, given, bought, sold, used up, removed by a script. Fields: owner_class, owner_name, item_class, item_name = data.split('\0'): the owner's classname ('player' for the player) and name, the item's classname and name. Sent for the player and NPCs, at the end of the frame; not while a level loads or shuts down.

When to use them: a pickup message, a quest that waits for an item, a weight limit of your own.

blse.item_equipped, blse.item_unequipped#

The same fields: an item was taken in hand or put away. A weapon switch sends item_unequipped (the old one) then item_equipped (the new one).

Python
def on_equip(topic, data):
    owner_class, owner_name, item_class, item_name = data.split('\0')
    if owner_class == 'player' and item_class.startswith('item_w_'):
        blse.log('weapon in hand: ' + item_class)
blse.subscribe('blse.item_equipped', on_equip)

Item information and changing items#

blse.item_files() (b0.5.35) lists every item there is a file for, loaded or not, and blse.item_info() also answers for items the game has not loaded, read from their file (vdata/items/<class>.txt) the way the game reads it. Files whose names a class cannot have (copies such as item_a_body_armor - hunter.txt) are left out.

blse.item_loaded (b0.5.34): the game loaded an item's data (payload: the classname). Items are read when first needed: a level's, a merchant's goods when the trade screen opens. Items loaded before are listed by blse.item_classes(), so a mod that changes every item calls item_classes() once and then handles item_loaded.

Function
blse.set_item(classname, field, value) Changes an item for the rest of the session (not saved): 'name' (up to 127 characters), 'description', 'price' (buying), 'sell_price', 'weight', and a weapon's ammunition: 'ammo_name', 'ammo_description', 'ammo_price' (ammunition is not an item of its own: it is part of the weapon's data). value is a string or a whole number (0..1000000000). Returns 1, or 0 if the game has not loaded the item yet: it gets the value when the game loads it (merchants' goods load when their screen opens). A script can call it in its first lines: BLSE finds the game's items a moment after scripts run, so it returns 0 then and the change is applied as soon as it does. ValueError for an unknown field or a bad value. Both of the game's copies are changed: the server's and the client's, which the barter screen prices from (shown prices still include the game's haggling adjustment)
blse.reset_item(classname) The game's own values again for every field changed from Python; 1, or 0 if none. A Python restart resets them too

blse.item_info(classname) returns (name, description, type, price, sell_price, weight, flags, model) from the game's item data (vdata/items), or None; blse.item_classes() lists the items the game has loaded so far (items are loaded as levels and inventories need them). For example item_w_thirtyeight gives ('Colt Police Positive Special', "This 'Thirty-Eight' is ...", 'weapon_firearm', 70, 15, 3, 0, 'models/weapons/ThirtyEight/info/i_ThirtyEight.mdl').

blse.item_info#

Python
blse.item_info(classname)   # -> tuple or None

An item's data, from the game's records or, for an item the game has not loaded, from its file (b0.5.35). Returns None for an unknown class.

Position Field Meaning
0 name The name the game shows (may be a # translation key)
1 description The description text
2 type The item type: 'weapon_firearm', 'armor', ...
3 price What it costs to buy
4 sell_price What a merchant pays for it
5 weight Its weight
6 flags The item's flags number
7 model Its inventory model
Python
name, desc, kind, price, sell, weight, flags, model = blse.item_info('item_g_bloodpack')

blse.item_classes, blse.item_files, blse.item_loaded#

Python
blse.item_classes()   # -> ['item_g_bloodpack', ...]  loaded so far
blse.item_files()     # -> every item with a file, loaded or not (b0.5.35)

item_classes() lists the items the game has loaded so far; item_files() every item there is a file for. The event blse.item_loaded (b0.5.34) sends a classname whenever the game loads an item's data. A mod that changes every item calls item_classes() once and then handles item_loaded:

Python
def cheaper(classname):
    info = blse.item_info(classname)
    if info and info[3] > 0:
        blse.set_item(classname, 'price', info[3] / 2)
for c in blse.item_classes():
    cheaper(c)
blse.subscribe('blse.item_loaded', lambda topic, data: cheaper(data))

blse.set_item#

Python
blse.set_item(classname, field, value)   # -> 1 or 0

Changes one field of an item for the rest of the session (not saved). When to use it: a shop mod with its own prices, a mod that renames or rebalances items.

  • field: 'name' (up to 127 characters), 'description' (up to 8191), 'price', 'sell_price', 'weight', or a weapon's ammunition 'ammo_name', 'ammo_description', 'ammo_price'.
  • value: a string or a whole number; numbers 0 to 1000000000.
  • Returns 1 if changed now, 0 if the item is not loaded yet: it gets the value when the game loads it.
  • Errors: ValueError for an unknown field, a bad value (another type, too long, out of range), or when items are not available on this game build.
  • Both of the game's copies are changed (the server's, and the client's that the barter screen prices from). Shown prices still include the game's haggling adjustment.

blse.reset_item#

Python
blse.reset_item(classname)   # -> 1 or 0

The game's own values again for every field changed from Python on that item. Returns 1, or 0 if Python changed none. A Python restart resets them too.

New items and weapons#

(b0.5.49) A mod can add items beside the game's own, without replacing any. No code is needed: Start here walks through a new sword, step by step, from the item file to a merchant selling it.

Important: a save that holds an item a mod added cannot be loaded without that mod (the game crashes). Tell players to keep the mod while they play a game they started with it. Details under Limits below.

Each of the game's items is a class in vampire.dll; BLSE lets a mod name a new class that behaves like one of them (the template) and reads its own data from vdata/items/<new class>.txt:

From the template (its C++ class) From the new item's file
What kind of item it is and what its code does: a melee weapon, a gun with a magazine, a thrown weapon, what using a blood pack does, a key Names, description, prices, weight, stacking, item type and flags; the view, world, wielded and inventory models; animation prefix, camera class and sounds; each attack's damage, rate, range, spread, recoil and crosshairs; the magazine (ammunition names, prices, size, reload); HUD and inventory pictures

So a new sword can be a katana with its own model, damage and sounds, and a new gun a Glock with its own model, magazine and stats. A completely new behavior (a new projectile, a new fire mode) is not data: a native plugin can hook the template's functions and check the classname (see the plugin reference).

Function
blse.add_item_class(classname, template) classname (item_<name>, 6-63 characters of a-z 0-9 _) becomes an item class the game can create everywhere it creates items: maps, the console's give, GiveItem, merchants' stock, blse.spawn_entity, blse.give_item, loading saves. 1, or 0 if not available on this game build. ValueError with the reason if refused: a bad name, a class the game has already (new classes only), a template BLSE cannot copy. The same class again: the later registration wins (logged). Registrations last until the next Python start (scripts run again then)
blse.item_class_template(classname) The template of an added class, or None
blse.added_item_classes() [(classname, template, who, created), ...]; who is 'python', 'mod <folder>' or a plugin's name; created: entities made this session

The console command item_classes lists them too.

blse.add_item_class#

Python
blse.add_item_class(classname, template)   # -> 1 or 0

Makes a new item class that behaves like template and reads its data from vdata/items/<classname>.txt (b0.5.49).

  • classname: item_<name>, 6-63 characters of a-z 0-9 _, not a class the game already has.
  • template: one of the game's item classes that can be templates (see Templates below), or a class added before.
  • Returns 1, or 0 if new item classes are not available on this game build.
  • Errors: ValueError with the reason if refused (a bad name, an existing game class, a template BLSE cannot copy).
  • Call it in your script's first lines (scripts run before any save loads). Registrations last until the next Python start; the script registers again when it runs again.
Python
blse.add_item_class('item_w_mymod_sword', 'item_w_katana')

blse.item_class_template, blse.added_item_classes#

Python
blse.item_class_template(classname)   # -> 'item_w_katana' or None
blse.added_item_classes()             # -> [(classname, template, who, created), ...]

item_class_template returns the template of a class added with add_item_class (or blse/items.txt), None for others. added_item_classes lists every added class: who is 'python', 'mod <folder>' or a plugin's name; created how many entities of it were made this session.

Without code: a mod folder's blse/items.txt, read at startup in load order, one class per line: <new class> <template>; # or // start a comment.

text
# my_mod/blse/items.txt
item_w_mymod_sword   item_w_katana
item_w_mymod_pistol  item_w_glock_17c

and the item files beside it, as the game's own (copy the template's file and change it):

text
my_mod/vdata/items/item_w_mymod_sword.txt
my_mod/models/weapons/mymod_sword/...       (models, if the item has its own)
my_mod/materials/...                        (textures and inventory pictures)
my_mod/sound/...

Merchants: their stock is vdata/system/vendors.txt; a mod adds an item with a merge file (blse/merge/vdata/system/vendors.txt, see mod folders):

text
VendorData
{
    Vendor
    {
        "@match"    "Name=Mercurio"
        +VendorItem { "Name" "item_w_mymod_sword" "InternalName" "item_w_mymod_sword" "AmmoOnly" "0" "MinStage" "0" }
    }
}

"@match" "Name=Mercurio" picks the merchant (the game's vendors.txt has one Vendor block per merchant, by Name), and +VendorItem adds an entry to his list. In the entry, Name and InternalName are the item's class; AmmoOnly 1 sells only ammunition for a gun instead of the gun; MinStage is the point in the story from which the item is sold (0: from the start; the game's own entries use higher numbers for later items).

Templates: 184 of the game's 202 item classes, those whose create function has the common form. Not usable as templates: the containers (item_container, item_container_animated, item_container_lock, item_container_one_item_filtered), item_g_keyring, item_g_astrolite, item_p_occult_regen, item_i_written, item_d_holy_light, item_w_remington_m_700 (use item_w_steyr_aug for a rifle with a scope), item_w_unarmed, item_w_flamethrower, item_w_torch and the boss weapons item_w_chang_energy_ball, item_w_chang_ghost, item_w_mingxiao_melee, item_w_mingxiao_tentacle, item_w_tzimisce_melee. An added class can be named as a template, also one registered later (another line or mod): its own template is used.

Limits:

  • Saves need the mod. A save holding a mod's item loads only while the class is registered. With the mod (or BLSE) removed, the game itself crashes loading that save: it creates each saved entity by its classname and does not check for a failure (vampire.dll 0x101a2ec5). Register in blse/items.txt or a script, which run before any save loads. A class in a mod's blse/items.txt stays registered even if a script registers it again and later fails (the script's template is used while it runs).
  • The game's code that checks for one item by name (a quest asking for item_w_katana, special cases in a few weapons) does not see a new class as that item.
  • The item file must exist: a registered class whose vdata/items/<class>.txt is missing is not created (logged once). A save holding such an item crashes the game on load, as for a missing mod.
  • Keep a class's template once saves hold its items: a save stores the item's fields as its template's class has them.