211 lines
7.5 KiB
Markdown
211 lines
7.5 KiB
Markdown
# 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_<productId>.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`.
|