# Source file format (`traderx_build.py`) The source is a single YAML (or JSON – identical keys) file with four top-level sections. Everything is optional except `categories`. ```yaml defaults: # global defaults groups: # named, reusable lists of class names items: # products that are NOT shown in any category (attachments, variants…) categories: # what the traders sell ``` Class names are matched case-insensitively. Product and category ids are derived from names automatically (`AKM` → `prod_akm_001`, `Assault Rifles` → `cat_assault_rifles_001`) and are stable across builds as long as the name doesn't change. Set `id:` explicitly to keep an id from an existing config (the reverse-importer does this for you). --- ## `defaults` | Key | Default | Meaning | |---|---|---| | `stock` | `unlimited` | `unlimited` or an integer max stock | | `coefficient` | `1.0` | Dynamic price factor; only has an effect with a finite `stock` | | `sell_percent` | `25` | Sell price as % of buy price when `sell` isn't given | | `buy` | `full` | Item quantity on purchase – see *Quantity words* | | `sell` | `full` | Required quantity on sale | | `restart` | `keep` | Stock behaviour at server restart: `keep` / `reset` / `random` | | `destock` | `0` | Fraction (0–1) of stock removed at every restart | | `attachment_price` | `50` | Buy price for attachments/variants that are not defined anywhere else (auto-created hidden products) | | `attachment_sell_percent` | `25` | Sell % for those auto-created products | Every key except the two `attachment_*` ones can be overridden per category and per item. Resolution order is **item → category → defaults**. --- ## `groups` ```yaml groups: ak_optics: [PSO1Optic, KobraOptic, SNAFU_AKKahles] ak_muzzle: [AK_Suppressor, SNAFU_AK19Muzzel] ak_full_kit: [ak_optics, ak_muzzle, Mag_AKM_30Rnd] # groups can contain groups ``` A group name can be used anywhere a list of class names is expected: `attachments`, `variants`, preset contents. Duplicates are removed; recursion is detected. --- ## `items` Products that exist but are not listed in any category. Typical use: attachments and variants that you price explicitly rather than relying on `attachment_price`. ```yaml items: - SNAFU_AKKahles: {price: 800} - PSO1Optic: {price: 300, sell: 60} - AKM_Tan # all defaults, price -1 (unbuyable) – only useful as variant ``` --- ## `categories` ```yaml categories: - name: Assault Rifles # required, shown in the trader UI id: cat_assault_rifles_001 # optional, otherwise derived from name traders: [Boris, 3] # givenName or npcId from TraderXGeneralSettings.json licenses: [Weapons License] # license *names*; created if missing visible: true # false hides the category (products still work as attachments) icon: "" # image path for the category card price: 1200 # default buy price for items below stock / coefficient / sell_percent / buy / sell / restart / destock # category defaults items: [...] ``` `traders` controls `categoriesId` in `TraderXGeneralSettings.json`. Every trader that is named by at least one category gets its list rebuilt in source order; traders not named anywhere are left untouched (with a warning). --- ## Items Three spellings are accepted: ```yaml items: - AKM # bare class, all defaults - AKM: {price: 1000, stock: 5} # class as key - {class: AKM, price: 1000} # explicit key ``` Item options: | Key | Meaning | |---|---| | `id` | Keep a specific product id (`prod_…_NNN`). | | `price` | Buy price. `-1` = cannot be bought. Required somewhere (item, category or `price` in defaults is *not* a thing – an item without a resolvable price is an error). | | `sell` | Sell price (integer). `-1` = cannot be sold. If omitted, derived from `sell_percent`. | | `sell_percent` | Override the percentage for this item. | | `stock` | `unlimited` or integer. | | `coefficient` | Dynamic price factor (see the configuration guide, §4.2). | | `buy` | Purchase quantity word. | | `sell_mode` | Sale quantity word (named `sell_mode` because `sell` is the price). At category/defaults level the key is `sell`. | | `restart`, `destock` | Stock behaviour at restart. | | `attachments` | List of class names / group names / `@compat` refs. | | `variants` | List of class names / groups. Variants that don't define their own `attachments` inherit the base item's. | | `presets` | Map of preset name → attachment list (see below). | An item may appear in several categories; the first occurrence with options defines it, later occurrences just add it to that category (a warning is printed if they carry conflicting options). --- ## Quantity words Used for `buy` (item state when purchased) and `sell` / `sell_mode` (state required when sold): | Word | Buy | Sell | |---|---|---| | `full` | item spawns full | must be full | | `empty` | spawns empty | must be empty | | `any` | – | any amount accepted, paid pro-rata | | `static N` | exactly N units | exactly N units | | `coef 50%` (or `coef 0.5`) | 50 % of max | 50 % of max | The build packs these into the mod's `tradeQuantity` bitfield using the layout the mod's runtime actually reads (`TraderXTradeQuantity`), not the one in its CSV packer, which is inconsistent. --- ## Presets ```yaml - AKM: attachments: [ak_optics, ak_muzzle, Mag_AKM_30Rnd] presets: "CQB*": [KobraOptic, AK_Suppressor, Mag_AKM_30Rnd] # * marks the default preset "DMR": [PSO1Optic, Mag_AKM_30Rnd] ``` Each item with `presets` produces `TraderXPresets/presets_.json`. Rules enforced at build time: - every preset attachment must be in the item's own `attachments` list (the mod won't let players select it otherwise) – error; - exactly one preset can carry `*`; if none does, the first one becomes the default; - preset ids are derived (`preset_akm_cqb_001`) and stable. Player-saved presets live in the same folder and are not touched. --- ## `@compat` references Require `build --compat FILE`. See [compat.md](compat.md). | Ref | Expands to | |---|---| | `@compat` | every attachment the compat file lists for this weapon **that is defined as a product somewhere in the source** | | `@compat_mags` | same, for magazines | | `@compat_all` | everything compatible, auto-creating products that don't exist (rarely what you want) | --- ## What the build checks Errors (nothing is written): - item without a resolvable price - unknown quantity/restart word, out-of-range values - preset using a class not in the weapon's attachments, or not defined at all - `@compat` used without `--compat` - trader or category reference that doesn't exist - trader `className` with the wrong `SurvivorM_`/`SurvivorF_` prefix for a known survivor name - duplicate explicit ids - with `--strict`: attachment/variant class not defined anywhere Warnings: - `coefficient` set while stock is unlimited (the mod ignores it) - sell price higher than buy price - attachment not attachable according to the compat file - item listed twice in a category; item defined twice with options - category with no items; trader not referenced by any category - auto-created hidden products (as notes) --- ## Minimal complete example ```yaml defaults: sell_percent: 30 categories: - name: Food traders: [Eve] price: 50 items: [Apple, Pear, BakedBeansCan] ``` builds three product files, one category file and sets Eve's `categoriesId`.