# esx_drugs (/docs/addons/esx_drugs)

esx_drugs is a small weed economy for ESX Legacy: players pick `cannabis` plants in a field, process them into `marijuana` and sell it to a dealer for black money. The server validates every step by position and rate limits, so it is a reasonable base for your own drug scripts.

## Overview [#overview]

| Fact | Value |
| --- | --- |
| Resource | `esx_drugs` |
| Side | Client + server |
| Dependencies | `esx_lib`, `es_extended` (plus `oxmysql`) |
| Optional | `esx_license` when `Config.LicenseEnable = true` |
| Items | `cannabis` (weight 3), `marijuana` (weight 2) |
| License | `weed_processing` |

### In-game flow [#in-game-flow]

1. **Harvest**: near the weed field the client spawns 25 local `prop_weed_02` plants within 50 m of the field center. Press **E** next to a plant to pick it. After about 3 seconds the server gives 1 to 3 `cannabis`.
2. **Process**: stand on the processing spot and press **E**. Every `Config.Delays.WeedProcessing` ms the server converts 3 `cannabis` into 1 `marijuana` until you run out, walk away or your inventory is full.
3. **Sell**: at the dealer marker press **E**, choose an item and an amount between `Config.SellMenu.Min` and `Config.SellMenu.Max`.

## Installation [#installation]

<Steps>
<Step>

### Database [#database]

The resource uses the esx_lib SQL catalog and inserts the items and the license automatically in your `esx:locale`. The manual equivalent is `esx_drugs.sql`:

```sql title="esx_drugs.sql"
INSERT INTO `items` (`name`, `label`, `weight`, `rare`, `can_remove`) VALUES
	('cannabis', 'Cannabis', 3, 0, 1),
	('marijuana', 'Marijuana', 2, 0, 1);

INSERT INTO `licenses` (`type`, `label`) VALUES
	('weed_processing', 'Weed Processing License');
```

If you use ox_inventory, add the two items to `ox_inventory/data/items.lua` instead. See [ox_inventory](/docs/guides/ox-inventory).

</Step>
<Step>

### server.cfg [#servercfg]

```bash title="server.cfg"
ensure esx_license   # only needed with Config.LicenseEnable = true
ensure esx_drugs
```

</Step>
</Steps>

## Configuration [#configuration]

| Key | Default | Description |
| --- | --- | --- |
| `Config.Locale` | `GetConvar('esx:locale', 'en')` | Locale used for texts. |
| `Config.Delays.WeedProcessing` | `1000 * 7` | Time in ms per processing step (3 cannabis → 1 marijuana). |
| `Config.DrugDealerItems` | `{ marijuana = 91 }` | Items the dealer buys and the price per unit. |
| `Config.LicenseEnable` | `false` | Ask for the `weed_processing` license before processing. Needs `esx_license`. |
| `Config.LicensePrices` | `{ weed_processing = { label = TranslateCap('license_weed'), price = 15000 } }` | License label and cash price. |
| `Config.GiveBlack` | `true` | Pay sales into `black_money`. `false` pays cash. |
| `Config.CircleZones.WeedField` | `vector3(2220.72, 5582.52, 53.81)`, radius `100.0` | Field center. Blip color `25`, sprite `496`, plus a radius blip. |
| `Config.CircleZones.WeedProcessing` | `vector3(2329.02, 2571.29, 46.68)` | Processing spot. Blip color `25`, sprite `496`. |
| `Config.CircleZones.DrugDealer` | `vector3(-1172.02, -1571.98, 4.66)` | Dealer spot. Blip color `6`, sprite `378`. |
| `Config.Marker.Distance` | `100.0` | Distance at which the dealer marker is drawn. |
| `Config.Marker.Color` | `{ r = 60, g = 230, b = 60, a = 255 }` | Dealer marker color. |
| `Config.Marker.Size` | `vector3(1.5, 1.5, 1.0)` | Dealer marker size. |
| `Config.Marker.Type` | `1` | Dealer marker type. |
| `Config.SellMenu.Min` | `1` | Minimum amount per sale. |
| `Config.SellMenu.Max` | `50` | Maximum amount per sale. The server rejects (and kicks for) amounts outside this range. |

### Hard-coded server values [#hard-coded-server-values]

These are local constants at the top of `server/main.lua`, not config keys:

| Constant | Value | Meaning |
| --- | --- | --- |
| `PickupCooldown` | `3500` | Min ms between pickups (rate limiter). |
| `PickupDuration` | `3000` | Delay before cannabis is added. |
| `PickupDistance` / `PickupFieldDistance` | `3.0` / `55.0` | Max distance to the plant / to the field center. |
| `ProcessDistance` | `5.0` | Max distance to the processing spot. |
| `SellDistance` / `SellCooldown` | `3.0` / `1500` | Max distance to the dealer / min ms between sales. |
| `MinCannabisPickup` / `MaxCannabisPickup` | `1` / `3` | Cannabis per plant. |

<Callout type="warn">
`Config.CircleZones.WeedField.radius` only affects the map blip. Plants spawn within 50 m and the server accepts pickups within 55 m of the field center, so a larger radius does not make the field bigger.
</Callout>

## Server events and callbacks [#server-events-and-callbacks]

All net events below can be triggered by clients. The server validates them as follows.

| Name | Type | Arguments | Server checks |
| --- | --- | --- | --- |
| `esx_drugs:pickedUpCannabis` | net event | `plantCoords` | Rate limited. Plant and player must be inside the field and within 3 m of each other; re-checked after 3 s. Fails → player is dropped. |
| `esx_drugs:processCannabis` | net event | none | Within 5 m of `WeedProcessing`, at least 3 cannabis, one loop per player. Fails position check → player is dropped. |
| `esx_drugs:sellDrug` | net event | `itemName`, `amount` | Rate limited, within 3 m of the dealer, integer amount within `SellMenu`, item listed in `DrugDealerItems`, enough items. Wrong types, position or amount → player is dropped. |
| `esx_drugs:cancelProcessing` / `esx_drugs:outofbound` | net event | none | Stops the player's processing loop. |
| `esx_drugs:buyLicense` | callback | `licenseName` | License must exist in `Config.LicensePrices` and the player must have the cash. |
| `esx_drugs:canPickUp` | callback | `item` | Returns whether the player can carry 1 `cannabis`. |
| `esx_drugs:cannabis_count` | callback | none | Returns the player's cannabis count. |

<Callout type="error" title="License is client-side only">
With `Config.LicenseEnable = true` the license is only checked on the client before processing starts. `esx_drugs:processCannabis` does not check `weed_processing` on the server, and `esx_drugs:buyLicense` has no distance check. Add an `esx_license:checkLicense` check in the server event if the license must be enforced.
</Callout>

The kick function is `FoundExploiter(src, reason)` in `server/main.lua`. Replace `DropPlayer` there with your ban system if you want stricter handling.

## Integration tips [#integration-tips]

- To sell more drugs at the dealer, add items to `Config.DrugDealerItems` (for example `{ marijuana = 91, cocaine = 150 }`) and create the items. The menu lists every item from that table that the player carries.
- Sales use `xPlayer.addAccountMoney('black_money', ...)` with the reason `"Drugs Sold"`, which shows up in ESX money logs.
- esx_drugs stops processing for a player on `esx:onPlayerDeath` and on disconnect.

## Common issues [#common-issues]

- **Plants spawn in the air or underground**: plant height is searched between Z 48 and 58. If you move the field, adjust `GetCoordZ` in `client/weed.lua`.
- **Dealer menu is empty**: the dealer menu lists items from `ESX.GetPlayerData().inventory` on the client. With a custom inventory, make sure it keeps ESX player data in sync.
- **Processing stops after one step**: the player cannot carry the marijuana (`canSwapItem` failed) or has fewer than 3 cannabis.

## Related [#related]

- [Add items](/docs/guides/add-items)
- [esx_license](/docs/addons/esx_license)
- [Money and accounts](/docs/guides/money-and-accounts)
- [xPlayer](/docs/es_extended/server/xplayer)
