> For the complete documentation index, see [llms.txt](https://risk-scripts.gitbook.io/risk-scripts/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://risk-scripts.gitbook.io/risk-scripts/scripts/immersive-beekeeping-v2.md).

# IMMERSIVE BEEKEEPING V2

FiveM beekeeping job with player-placed hives, a hands-on 3D harvest, a honey   extractor and 19 custom props. Supports ESX, QBCore and QBox.

## Beekeeping V2 — FiveM Honey Farming Job

### Overview

Beekeeping V2 is a FiveM beekeeping job for ESX, QBCore and QBox. Players place their own beehives, fill them with frames and bees, smoke them out, harvest the honeycombs by hand and turn them into honey jars at a honey extractor.

| Feature                 | What it does                                                             |
| ----------------------- | ------------------------------------------------------------------------ |
| **Player-placed hives** | Players place their own beehives inside the zones you define             |
| **Hands-on hive work**  | Frames are dragged into the hive, full honeycombs are pulled out by hand |
| **Sugar water**         | Decides whether a harvest gives all four combs or only one or two        |
| **Bee smoker**          | A finished hive has to be smoked out from several sides before it opens  |
| **Bee stings**          | Bees sting unprotected players near a hive, the beekeeper suit protects  |
| **Beekeeper suit**      | Clothing sequence with a custom helmet prop, male and female             |
| **Honey extractor**     | Uncap, load, spin and bottle the honey at fixed stations                 |
| **Zones**               | Hives can only be placed inside your zones, each with its own limit      |
| **Apiary Map**          | Item that shows every beekeeping zone on the map                         |
| **Shop**                | Cart-based supply shop for everything a beekeeper needs                  |
| **Honey buyer**         | Buys honey jars and raw honey, paid out to cash or bank                  |
| **Honey theft**         | Optional — other players can raid finished hives, the owner is warned    |
| **Persistence**         | Hives are stored in the database, production runs on server time         |
| **Multi-framework**     | ESX, QBCore and QBox, auto-detected                                      |

### Requirements

* `oxmysql`, `mysql-async` or `ghmattimysql`
* ESX, QBCore or QBox
* Optional: `ox_target` or `qb-target` for the shop and the honey buyer
* Optional: `ox_inventory`, `codem-inventory`, `tgiann-inventory`, `core_inventory` or `origen_inventory` — the framework native inventory is used otherwise

{% hint style="info" %}
The database table is created automatically on the first start. There is no SQL file to import for the script itself — only the item SQL for ESX.
{% endhint %}

### User Access

Everything you need is in these files.

| File                        | What it is for                                                                                |
| --------------------------- | --------------------------------------------------------------------------------------------- |
| `config/config_general.lua` | Language, identifier, hive limit per player, notify and progress bar                          |
| `config/config_hive.lua`    | Everything about the beehive: items, times, sugar water, smoker, theft, stings, sounds, blips |
| `config/config_zones.lua`   | The areas where hives may be placed and the Apiary Map item                                   |
| `config/config_station.lua` | Honey extractor positions, jar items and fill time                                            |
| `config/config_shop.lua`    | Shop position, ped, marker, blip and stock with prices                                        |
| `config/config_seller.lua`  | Honey buyer position, ped, marker, blip, payout methods and prices                            |
| `config/config_suit.lua`    | Beekeeper suit item and clothing for male and female                                          |
| `config/config_target.lua`  | `ox_target` / `qb-target` support                                                             |
| `locales/*.lua`             | en / de / es / fr / pl / pt / ru / tr / zh — every text of the script                         |
| `inventory/inventory.lua`   | Inventory bridge — add your own inventory here if it is not supported                         |
| `database/database.lua`     | Database bridge — oxmysql, mysql-async, ghmattimysql                                          |
| `_items/`                   | Item files for ESX, QBCore / QBox and ox\_inventory, plus every item image                    |

* Every text in the UI, in notifications and on blips comes from `locales/*.lua`.
* Every setting in the config files has a comment that explains it.
* Prop models, camera angles and animations of the custom props are fixed — the props only work with their own values.

{% hint style="success" %}
If something is not covered by a config option, open a ticket and we will add one.
{% endhint %}

***

### Setup Order

{% hint style="danger" %}
**Read this before anything else.**

1. **The items must exist in your inventory.** Without them nobody can buy, place or harvest anything. The item files are in the `_items` folder.

2. **Hives can only be placed inside a zone.** The default zones are examples. Outside of a zone every player gets *"You can't place a beehive here!"*.

3. **Do not delete the `_items` folder.** The shop and the honey buyer load their item pictures from `_items/images`. Copy the pictures into your inventory, do not move them.
   {% endhint %}

4. Drop `risk-beekeepingv2` into your resources folder

5. Add the items from `_items` to your inventory and copy the images from `_items/images`

6. Add `ensure risk-beekeepingv2` to your `server.cfg`

7. Set your zones in `config/config_zones.lua`

8. Set the shop and honey buyer positions in `config/config_shop.lua` and `config/config_seller.lua`

9. Start the server, type `/station` ingame and place your honey extractors (see Admin Guide)

10. Paste the printed station block into `config/config_station.lua` and restart the script

#### Adding the items

| File                            | For                                                                                            |
| ------------------------------- | ---------------------------------------------------------------------------------------------- |
| `_items/sql/<language>.sql`     | ESX — run it once on your database                                                             |
| `_items/qb-inventory/items.lua` | QBCore / QBox — paste into `qb-core/shared/items.lua`                                          |
| `_items/ox_inventory/items.lua` | ox\_inventory and every inventory using that format — paste into `ox_inventory/data/items.lua` |
| `_items/images/`                | Item pictures — copy into your inventory image folder                                          |

Where the pictures go:

* ox\_inventory → `ox_inventory/web/images`
* qb-inventory → `qb-inventory/html/images`
* other inventories → wherever they keep their item pictures

{% hint style="warning" %}
After you update the script and new files were added, type `refresh` and then `restart risk-beekeepingv2` in the server console. A plain `restart` does not pick up new files.
{% endhint %}

***

### General Configuration

```lua
-- Language. Must match a file name in the locales/ folder without .lua
-- Shipped: "en", "de", "es", "fr", "pl", "pt", "ru", "tr", "zh"
-- Copy a file, rename it and translate it to add your own.
Config.Locale = "en"

-- How a player is identified as the owner of his hives: "license" or "steam"
Config.IdentifierType = "license"

-- Maximum number of beehives a single player can have placed at the same time (0 = unlimited)
Config.MaxHivesPerPlayer = 2
```

If the chosen language misses a text, that single text falls back to English. An unknown language shows a red warning in the console and uses English.

{% hint style="warning" %}
Changing `Config.IdentifierType` on a running server makes every existing hive belong to nobody — the owners are stored with the old identifier. Decide once before players start placing hives.
{% endhint %}

#### Notify and progress bar

The script ships its own notify, help prompt and progress bar in the same honey-and-wood look as the rest of the UI. Switch to your own with one line each.

```lua
-- If true, use the custom notify below; if false, use the one built into this script
Config.UseCustomNotify = false
-- If true, use the custom helpnotify below; if false, use the one built into this script
Config.UseCustomHelpNotify = false
-- How long a notification stays on screen in ms (built-in notify only)
Config.NotifyDuration = 5000
-- If true, a short sound plays when a notification pops up (built-in notify only)
Config.NotifySound = true
-- Volume of that sound from 0.0 (mute) to 1.0 (full)
Config.NotifyVolume = 0.35

-- If true, a short sound plays when a helpnotify pops up (built-in helpnotify only)
Config.HelpNotifySound = true
-- Which native GTA sound to use for that: sound name and soundset (no audio file needed)
Config.HelpNotifySoundName = "DELETE"
Config.HelpNotifySoundSet = "HUD_DEATHMATCH_SOUNDSET"

Config.Functions = {
    ["notify"] = function(ntype, title, text, time)
        exports['risk-notify']:Notify({
            type = ntype or 'info',
            message = text,
            title = title or 'Notify',
            duration = time or 10000
        })
    end,
    ["helpnotify"] = function(key, text)
        exports["risk-notify"]:HelpNotify(key, text)
    end,
}

-- If true, use the custom progressbar below; if false, use the built-in one of this script
Config.UseCustomProgressBar = false
Config.CustomProgressBar = {
    PushProgressbar = function(msg, time)
        exports["rs_notifypack_v2"]:PushProgressbar(msg, time)
    end,
    CancelProgressbar = function()
        exports["rs_notifypack_v2"]:CancelProgressbar()
    end,
}
```

{% hint style="info" %}
The status bars that stay on screen while you work (bees collecting, smoking, uncapping, spinning) always use the built-in bar. A custom progress bar can only run a fixed duration, it cannot show a live state.
{% endhint %}

***

### Zones

Hives can only be placed inside these areas. Every zone has its own radius and its own hive limit.

```lua
-- Item that shows/hides the allowed beekeeping areas on the map when used
Config.MapItem = "apiarymap"

-- Areas where beehives may be placed. Outside of these nothing can be placed.
Config.Zones = { {
    name = "Small Grove", -- Only used for your own overview
    center = vector3(1353.7408, 1477.2067, 102.3420), -- Center of the zone
    radius = 100, -- Zone radius in meters
    color = 46, -- Blip color of the circle shown by the apiary map
    alpha = 100, -- Blip transparency (0-255)
    maxHives = 10 -- Max hives allowed inside this zone (0 = unlimited)
}, {
    name = "Big Forest",
    center = vector3(-250.0, 3500.0, 50.0),
    radius = 500,
    color = 46,
    alpha = 100,
    maxHives = 2
} }
```

Add as many zones as you want. Players use the **Apiary Map** item to see them as circles on the map, and use it again to hide them.

{% hint style="info" %}
**Why zones?** Without them hives end up in front of the police station and in the middle of the highway. Zones keep beekeeping in the countryside and `maxHives` stops one field from being covered in hives.
{% endhint %}

***

### Beehive

All hive settings live in `config/config_hive.lua`.

#### Items and production

```lua
Item = "hive", -- Item consumed when placing a beehive

-- Extra items needed to place a beehive, on top of Item above
RequiredPlaceItems = {
    enabled = false, -- true = the list below is required to place a hive
    items = { {
        item = "hammer", -- Item name in your inventory
        count = 1, -- How many of it are needed
        remove = false -- false = stays in the inventory, true = gets used up
    }, {
        item = "wood",
        count = 5,
        remove = true
    } }
},

FrameItem = "beehiveframe", -- Item consumed when putting the honeycomb frames in
FrameCount = 4, -- How many frames one hive needs

BeesItem = "bees", -- Item consumed when putting the bees in, after the frames
BeesCount = 1, -- How many of that item one hive needs

BeesKey = 38, -- Control id to place the bees at the hive (38 = E)
BeesKeyDisplay = "E", -- Key name shown in the help text
BeesAnimDict = "mp_common", -- Animation dictionary played while placing the bees
BeesAnim = "givetake2_a", -- Animation name inside that dictionary
BeesDuration = 8000, -- How long placing the bees takes in ms

-- How long the bees collect honey, in minutes (a random value per hive)
ReadyTimeMin = 1,
ReadyTimeMax = 2,

HarvestItem = "dirtyhoney", -- Item you get when harvesting a hive
HarvestPerComb = 1, -- How much of it every single honeycomb gives (FrameCount x this = what you get per harvest)

HiveUses = 5, -- How often a hive can be harvested before it disappears (0 = unlimited)
RequireFramesEachHarvest = true, -- true = new frames needed after every harvest
RequireBeesEachHarvest = true, -- true = new bees needed after every harvest
```

The production time is rolled on the server per hive and keeps running while the owner is offline.

{% hint style="warning" %}
`ReadyTimeMin` and `ReadyTimeMax` are in **minutes**. Set them to what fits your server economy before going live — see Best Practices.
{% endhint %}

The names of the `RequiredPlaceItems` shown in the error message live in `locales/*.lua` under `hive.placeItems`.

#### Sugar water

At a random point during the production the bees ask for sugar water. A sign floats over the hive and the owner gets a message.

```lua
FeedBees = {
    enabled = true, -- false = no sugar water at all, every harvest gives FrameCount full combs
    item = "sugarwater", -- Item consumed, exactly 1 per production
    key = 47, -- Control id to pour it into the hive (47 = G)
    keyDisplay = "G", -- Key name shown in the help text
    distance = 6.0, -- How close you have to be for the sign and the key, in meters
    duration = 4000, -- How long pouring it in takes in ms

    notifyOnRequest = true, -- true = the owner also gets a message when the bees ask for it

    -- How many frames come out full WITHOUT sugar water (random between the two)
    combsWithoutMin = 1,
    combsWithoutMax = 2
},
```

|                      | Full combs      | Result                                        |
| -------------------- | --------------- | --------------------------------------------- |
| **With sugar water** | all 4           | One harvest fills one extractor run           |
| **Without**          | 1 or 2 (random) | The rest has to come from the next production |

Only the full combs are pulled out at harvest. The empty frames stay in the hive, and new frames go exactly into the gaps the full ones left.

{% hint style="info" %}
Which combs come out full is decided the moment the hive is done, not at harvest. Relogging does not give a better harvest.
{% endhint %}

#### Bee smoker

```lua
Smoker = {
    Enabled = true, -- false = a finished hive can be opened without smoking it first
    Item = "smoker", -- Item you need to have for it
    Consume = false, -- true = the item is used up every time you smoke a hive
    PumpCooldown = 500, -- Minimum time in ms between two pumps, stops key spamming
    Key = 38, -- Control id that starts the smoker and pumps it (38 = E)
    KeyDisplay = "E", -- Key name shown in the help text
    CancelKey = 73, -- Control id to stop smoking early (73 = X)
    CancelKeyDisplay = "X", -- Key name shown in the help text
    Distance = 4.0, -- Walking further away from the hive than this cancels it
    HoldAnimDict = "anim@heists@humane_labs@finale@keycards", -- Animation while the smoker is in your hand
    HoldAnim = "ped_a_enter_loop" -- Animation name inside that dictionary
},
```

A hive needs **12 pumps**. After three pumps from the same side the player gets *"This side has had enough smoke! Walk around the hive."* — he has to move to another side of the hive to continue. Two reachable sides are enough, so a hive against a wall still works.

#### Bee stings

```lua
Sting = {
    enable = true, -- false = bees never sting
    distance = 5.0, -- Distance in meters at which the bees start stinging
    interval = 8000, -- Time in ms between two stings
    damage = 5, -- Health lost per sting
    minHealth = 102 -- Health never drops below this, so the bees can't kill you
},
```

Only hives with bees that were not smoked out yet sting. Players wearing the beekeeper suit are never stung, and nobody is stung inside a vehicle.

#### Placement

```lua
PlacementDistance = 1.1, -- How far in front of the player the hive is placed, in meters (max 8)

PlaceKey = 38, -- Control id to confirm the placement (38 = E)
PlaceKeyDisplay = "E", -- Key name shown in the help text
CancelKey = 73, -- Control id to cancel the placement (73 = X)
CancelKeyDisplay = "X", -- Key name shown in the help text

Scenario = "WORLD_HUMAN_VEHICLE_MECHANIC", -- GTA scenario played while placing, used when the two below are empty

-- Animation played while placing, overrides Scenario above (no tool in the hand)
PlaceAnimDict = "anim@amb@clubhouse@tutorial@bkr_tut_ig3@",
PlaceAnim = "machinic_loop_mechandplayer",

ScenarioDuration = 5000, -- How long the placement takes in ms

SpawnDistance = 150.0, -- Distance in meters at which hive props get created; further away they are removed

DeleteAfterDays = 7, -- Hives nobody touched for this many days are deleted (0 = never, not recommended)
```

{% hint style="info" %}
**Why `DeleteAfterDays`?** Players quit your server and leave their hives behind. Without a cleanup those hives block zone slots forever.
{% endhint %}

#### Camera view and status bar

```lua
-- Camera view you enter at your own hive to work on it
View = {
    OpenKey = 38, -- Control id that starts the view (38 = E)
    OpenKeyDisplay = "E", -- Key name shown in the help text
    ExitKeyDisplay = "ESC", -- Key name shown for leaving the view
    Distance = 2.0, -- How close you have to stand to start the view

    FadeDuration = 1600, -- How long the roof takes to open in ms
    MaxDistance = 4.0 -- View closes automatically above this distance from the hive
},

-- Progress bar shown at your own hive while the bees are collecting
Status = {
    Distance = 6.0 -- How close you have to be for the bar to show up
},
```

#### Sounds

```lua
Sounds = {
    enable = true, -- Master switch for every sound of this script

    beesEnable = true, -- true = bee buzzing near a hive that has bees in it
    beesDistance = 12.0, -- Meters from which you start hearing the bees
    beesVolume = 0.4, -- Loudest volume right at the hive (0.0 - 1.0)
    beesFadeOut = 4000, -- How long the buzzing takes to die down in ms after smoking

    roofVolume = 0.15, -- Volume of the sound when the roof opens
    roofDistance = 20.0, -- How far away the roof sound can be heard, in meters

    frameVolume = 0.5, -- Volume of the sound when a frame snaps into place
    frameOutVolume = 0.5, -- Volume of the sound when a frame is pulled back out while harvesting

    smokerVolume = 0.25, -- Volume of the sound every time you pump the bee smoker

    scrapeVolume = 0.08, -- Volume of the scraping loop while uncapping honeycombs

    stationCombVolume = 0.2, -- Volume of the sound when the honeycombs are put down on the station
    basketVolume = 0.3, -- Volume of the sound when a honeycomb lands in the extractor basket

    spinVolume = 0.35 -- Loudest volume of the extractor while it spins at full speed
},
```

#### Blips

```lua
Blip = {
    enable = true, -- true = show a blip for every placed hive
    sprite = 783, -- Blip icon id
    color = 5, -- Blip color id
    scale = 0.7, -- Blip size
    ownerOnly = true -- true = only the owner sees his hives on the map
}
```

[Blip reference on docs.fivem.net](https://docs.fivem.net/docs/game-references/blips/)

***

### Honey Theft

Off by default. A thief has to smoke the hive out like the owner and then break it open — which takes time, and the owner is warned.

```lua
Steal = {
    enabled = false, -- Master switch for the whole stealing mechanic
    onlyWhenOwnerOnline = true, -- true = hives of offline players cannot be touched
    requireSuit = true, -- true = the thief needs the beekeeper suit in his inventory
    requireSmoker = true, -- true = the thief has to smoke the hive out first
    tool = "", -- Extra item the thief needs to break the hive open, empty = none
    toolConsume = false, -- true = that item is used up on every theft
    animDict = "mini@repair", -- Animation dictionary played while breaking the hive open
    anim = "fixing_a_player", -- Animation name inside that dictionary
    key = 38, -- Control id to start breaking the hive open (38 = E)
    keyDisplay = "E", -- Key name shown in the help text
    cancelKey = 194, -- Control id that stops breaking the hive open (194 = BACKSPACE)
    cancelKeyDisplay = "BACKSPACE", -- Key name shown in the help text while breaking
    distance = 2.0, -- How close the thief has to stand, in meters
    duration = 600, -- How long breaking the hive open takes, in seconds (600 = 10 minutes)
    sharePercent = 50, -- How much of the honeycombs the thief gets, in percent
    warnOwner = true, -- true = the owner gets a message and a blip when it starts
    warnBlipTime = 600, -- How long that blip stays on the owner's map, in seconds (keep it as long as duration, so the owner can still find the thief)
    cooldown = 600, -- Pause between two thefts by the same player, in seconds
    oncePerProduction = true -- true = a hive can only be robbed once per cycle
},
```

#### How a theft works

1. The hive must be **finished** — there is nothing in it before that
2. The thief smokes it out *(if `requireSmoker = true`)*
3. The thief presses `E` and stays at the hive for `duration` seconds. The owner gets *"Somebody is breaking into one of your beehives!"* and a blip on his map
4. The hive opens and the thief pulls his share of the full combs out by hand, in the same camera view the owner uses
5. The roof closes again. The stolen frames stay in the hive as empty frames — the owner sees what is missing

Walking away, dying or pressing `BACKSPACE` cancels the break-in, and the owner keeps everything.

{% hint style="success" %}
Distance, break-in time, cooldown, the suit and the tool are all checked on the server. A thief never gets more combs than are really inside the hive, and never gets anything from his own hive.
{% endhint %}

The name of the tool shown in the error message lives in `locales/*.lua` under `hive.stealTool`.

***

### Honey Station

The honey extractor stands at fixed positions. Players cannot place it.

```lua
Config.Station = {

    -- Every position a honey station stands at
    -- coords = x, y, z and the heading of the station
    -- blip = true shows a blip for this one station, false hides it (the station itself still works)
    -- Useful when several stations stand next to each other or one of them should stay hidden
    -- Use the /station command ingame, place them, press ENTER and paste the block here
    Locations = {
        { coords = vector4(1536.1908, 1702.3914, 108.6863, 173.827), blip = true },
        { coords = vector4(1534.2391, 1706.0848, 108.7725, 257.827), blip = false },
        { coords = vector4(1533.1429, 1699.8198, 108.8128, 83.827), blip = false }
    },

    SnapToGround = true, -- true = small height errors are evened out with the ground below (max 3 meters)
    SpawnDistance = 100.0, -- Distance in meters at which the station props get created

    -- Putting the harvested honeycombs onto the station
    Key = 38, -- Control id to put the honeycombs down (38 = E)
    KeyDisplay = "E", -- Key name shown in the help text
    Distance = 2.5, -- How close you have to stand to the station

    -- Filling the honey into a jar at the tap
    JarItem = "emptyhoneyjar", -- Item you need to have to fill honey into
    JarFullItem = "honeyjar", -- Item you get once the jar is full
    FillTime = 9000, -- How long a jar takes to fill with the tap open, in ms

    -- How the blips look - this applies to every station
    -- Whether a single station gets a blip at all is set with blip = true/false above
    Blip = {
        sprite = 365, -- Blip icon id
        color = 46, -- Blip color id
        scale = 0.9 -- Blip size
    }
}
```

One station processes exactly **4 honeycombs** per run — one full harvest with sugar water. While a player works on a station, it is reserved for him. Other players see the combs lying on it.

{% hint style="warning" %}
The default positions are examples next to the default shop. Place your own with `/station` instead of typing coordinates by hand — the command prints them with the correct ground height.
{% endhint %}

***

### Shop

```lua
Config.Shop = {

    -- Position of the shop, use your own coordinates at player height
    Location = vector3(1531.5094, 1728.1389, 109.9249),

    -- Which account the price is taken from: "cash" or "bank"
    Currency = "cash",
    -- Symbol shown in front of every price in the UI
    CurrencySymbol = "$",
    -- How often the same item can be put into the cart (counts clicks on ADD, not single items)
    MaxPerPurchase = 10,

    Blip = {
        enable = true, -- true = show a blip on the map
        sprite = 52, -- Blip icon id
        color = 46, -- Blip color id
        scale = 0.75 -- Blip size
    },

    NPC = {
        enable = true, -- true = spawn a shop keeper ped
        model = "a_m_m_farmer_01", -- Ped model name
        coords = vector4(1531.5094, 1728.1389, 109.9249, 103.9431), -- Ped position and heading (same z as Location)
        scenario = "WORLD_HUMAN_CLIPBOARD" -- Idle animation scenario, set to nil for none
    },

    Marker = {
        enable = true, -- true = draw a marker at the shop location
        type = 27, -- Marker type id
        scale = 0.9, -- Marker size
        offsetZ = -0.95, -- Vertical offset from Location (negative = down to the ground)
        color = { r = 232, g = 166, b = 40, a = 120 }, -- Marker color (RGBA)
        rotate = false, -- true = marker spins
        bob = false, -- true = marker bobs up and down
        radius = 2.0, -- Distance in meters where the shop can be opened
        showDistance = 15.0 -- Distance in meters from which the marker is visible
    },

    OpenKey = 38, -- Control id to open the shop (38 = E)
    OpenKeyDisplay = "E", -- Key name shown in the help text
```

#### Stock

```lua
    -- Shop stock
    -- price = price of ONE single item, the script multiplies it by amount on its own
    -- amount = how many items one click on ADD puts into the cart
    -- Example below: one frame costs 15, a set of 4 is sold for 60
    -- Names and descriptions live in locales/, matched by the item name
    Items = { {
        item = "hive",
        price = 30, -- Price of one single item, in your currency (number, no symbol)
        amount = 1, -- How many the player buys at once
        image = "hive.png" -- File inside _items/images/, named after the item
    }, {
        item = "bees",
        price = 50,
        amount = 1,
        image = "bees.png"
    }, {
        item = "beehiveframe",
        price = 15,
        amount = 4, -- One hive needs 4 frames (Config.Hive.FrameCount), so a full set costs 60
        image = "beehiveframe.png"
    }, {
        item = "emptyhoneyjar",
        price = 5,
        amount = 1,
        image = "emptyhoneyjar.png"
    }, {
        item = "sugarwater",
        price = 60,
        amount = 1,
        image = "sugarwater.png"
    }, {
        item = "smoker",
        price = 120,
        amount = 1,
        image = "smoker.png"
    }, {
        item = "beekeeper_suit",
        price = 500,
        amount = 1,
        image = "beekeeper_suit.png"
    }, {
        item = "apiarymap",
        price = 100,
        amount = 1,
        image = "apiarymap.png"
    } }
}
```

`price` is always the price of **one** item. With `amount = 4` one click on **ADD** puts four frames for 60 into the cart, and the cart counts in single items (4, 8, 12 …).

{% hint style="success" %}
The client only sends which item and how many. Prices, distance, carry weight and money are all checked on the server. If an item cannot be given, the purchase is rolled back and the money returned.
{% endhint %}

***

### Honey Buyer

```lua
Config.Seller = {

    Enabled = true, -- false = no honey buyer at all (no blip, no ped, no marker)

    -- Position of the honey buyer, use your own coordinates at player height
    Location = vector3(802.98, 2175.27, 53.07),

    -- Symbol shown in front of every price in the UI
    CurrencySymbol = "$",
    -- Maximum quantity of a single item that can be put into one sale
    MaxPerSale = 50,

    -- Where the money goes, one button per entry. type must be "cash" or "bank"
    -- The button labels live in locales/, matched by the type
    PaymentMethods = { {
        type = "cash" -- Account the money is paid into
    }, {
        type = "bank"
    } },

    Blip = {
        enable = true, -- true = show a blip on the map
        sprite = 605, -- Blip icon id
        color = 5, -- Blip color id
        scale = 0.75 -- Blip size
    },

    NPC = {
        enable = true, -- true = spawn a buyer ped
        model = "a_m_m_farmer_01", -- Ped model name
        coords = vector4(802.98, 2175.27, 53.07, 246.07), -- Ped position and heading (same z as Location)
        scenario = "WORLD_HUMAN_CLIPBOARD" -- Idle animation scenario, set to nil for none
    },

    Marker = {
        enable = true, -- true = draw a marker at the seller location
        type = 27, -- Marker type id
        scale = 0.9, -- Marker size
        offsetZ = -0.95, -- Vertical offset from Location (negative = down to the ground)
        color = { r = 232, g = 166, b = 40, a = 120 }, -- Marker color (RGBA)
        rotate = false, -- true = marker spins
        bob = false, -- true = marker bobs up and down
        radius = 2.0, -- Distance in meters where the seller can be opened
        showDistance = 15.0 -- Distance in meters from which the marker is visible
    },

    OpenKey = 38, -- Control id to talk to the buyer (38 = E)
    OpenKeyDisplay = "E", -- Key name shown in the help text

    -- Everything the buyer takes. price = money per single item
    -- Names and descriptions live in locales/, matched by the item name
    Items = { {
        item = "honeyjar",
        price = 50, -- Money the player gets for one of them
        image = "honeyjar.png" -- File inside _items/images/, named after the item
    }, {
        item = "dirtyhoney",
        price = 10,
        image = "dirtyhoney.png"
    } }
}
```

The buyer takes raw honey too, but for much less. Players choose between a quick sale and the full extractor run.

Leave only `cash` or only `bank` in `PaymentMethods` to force one payout method.

***

### Target

Only the shop and the honey buyer use the target eye. Hives and stations always work with their key prompt.

```lua
Config.Target = {

    Enabled = false, -- true = use ox_target / qb-target instead of marker + keypress
    System = "auto", -- "auto" detects ox_target or qb-target, force with "ox_target" or "qb-target"
    Distance = 2.5, -- Maximum interaction distance for every target zone
    KeepMarker = true, -- true = still draw the markers while target is enabled

    Shop = {
        -- The label shown in the target eye lives in locales/
        icon = "fa-solid fa-store" -- FontAwesome icon shown in the target eye
    },

    Seller = {
        icon = "fa-solid fa-sack-dollar" -- FontAwesome icon shown in the target eye
    }
}
```

***

### Beekeeper Suit

The suit is put on and taken off by **using the item**. The character changes piece by piece with an animation for every step, and the last step attaches the beekeeper helmet prop.

```lua
Config.Suit = {

    Enabled = true, -- Enables/disables the beekeeper suit completely
    Item = "beekeeper_suit", -- Item that toggles the suit when used from the inventory
    MatchTextures = true, -- If true the texture must match too when detecting the suit; if false only the drawable is checked

    -- Male clothing sequence, played step by step when putting the suit on
    SequenceStepsMale = { {
        dict = "clothingshoes",
        anim = "try_shoes_positive_d",
        duration = 1400,
        applyComponent = true,
        componentId = 6, -- Shoes
        drawable = 72,
        texture = 9
    }, {
        dict = "clothingtrousers",
        anim = "try_trousers_neutral_c",
        duration = 1400,
        applyComponent = true,
        componentId = 4, -- Legs
        drawable = 40,
        texture = 0
    }, {
        dict = "clothingshirt",
        anim = "try_shirt_positive_d",
        duration = 1400,
        applyComponent = true,
        componentId = 11, -- Torso
        drawable = 67,
        texture = 0
    }, {
        dict = "clothingshirt",
        anim = "try_shirt_positive_d",
        duration = 1200,
        applyComponent = true,
        componentId = 3, -- Arms
        drawable = 77,
        texture = 0
    }, {
        dict = "missheist_agency2ahelmet",
        anim = "take_off_helmet_stand",
        duration = 1000,
        removeHelmet = true -- Takes off whatever hat the player is wearing
    }, {
        dict = "mp_masks@on_foot",
        anim = "put_on_mask",
        duration = 900,
        attachHelmet = true -- Attaches the beekeeper helmet prop
    } },

    -- Female clothing sequence, same rules as above
    SequenceStepsFemale = { {
        dict = "clothingshoes",
        anim = "try_shoes_positive_d",
        duration = 1400,
        applyComponent = true,
        componentId = 6, -- Shoes
        drawable = 65,
        texture = 6
    }, {
        dict = "clothingtrousers",
        anim = "try_trousers_neutral_c",
        duration = 1400,
        applyComponent = true,
        componentId = 4, -- Legs
        drawable = 40,
        texture = 0
    }, {
        dict = "clothingshirt",
        anim = "try_shirt_positive_d",
        duration = 1400,
        applyComponent = true,
        componentId = 8, -- Undershirt
        drawable = 15,
        texture = 0
    }, {
        dict = "clothingshirt",
        anim = "try_shirt_positive_d",
        duration = 1200,
        applyComponent = true,
        componentId = 11, -- Torso
        drawable = 61,
        texture = 0
    }, {
        dict = "clothingshirt",
        anim = "try_shirt_positive_d",
        duration = 1200,
        applyComponent = true,
        componentId = 3, -- Arms
        drawable = 88,
        texture = 0
    }, {
        dict = "missheist_agency2ahelmet",
        anim = "take_off_helmet_stand",
        duration = 1000,
        removeHelmet = true -- Takes off whatever hat the player is wearing
    }, {
        dict = "mp_masks@on_foot",
        anim = "put_on_mask",
        duration = 900,
        attachHelmet = true -- Attaches the beekeeper helmet prop
    } }
}
```

[Component reference on docs.fivem.net](https://docs.fivem.net/natives/?_0x262B14F48D29DE80)

{% hint style="info" %}
**How the suit is detected:** the script checks whether the player is wearing exactly these clothing pieces. That is why the protection still works after a script restart. If your server uses custom clothing packs, adjust `drawable` and `texture`, or set `MatchTextures = false` to ignore the texture.
{% endhint %}

The original outfit is saved when the suit goes on and comes back when it comes off — also after a relog. If another script changes the clothes while the suit is worn, the helmet is removed automatically.

***

### Exports and Events

The script has no exports. It fires one server event that your other scripts can listen to.

#### Events

```lua
-- Server side. Fires the moment a thief has broken a hive open.
AddEventHandler('risk-beekeepingv2:hiveRobbed', function(thief, hiveId, x, y, z)
    -- thief  = server id of the thief
    -- hiveId = id of the robbed hive
    -- x, y, z = position of the hive
end)
```

#### Examples

{% tabs %}
{% tab title="Police alert" %}

```lua
AddEventHandler('risk-beekeepingv2:hiveRobbed', function(thief, hiveId, x, y, z)
    -- Replace with the alert function of your dispatch script
    exports['your-dispatch']:Alert('Beehive robbery', vector3(x, y, z))
end)
```

{% endtab %}

{% tab title="Discord log" %}

```lua
local WEBHOOK = 'https://discord.com/api/webhooks/YOUR_WEBHOOK'

AddEventHandler('risk-beekeepingv2:hiveRobbed', function(thief, hiveId, x, y, z)
    local text = ('%s robbed beehive #%s'):format(GetPlayerName(thief), hiveId)

    PerformHttpRequest(WEBHOOK, function() end, 'POST',
        json.encode({ content = text }), { ['Content-Type'] = 'application/json' })
end)
```

{% endtab %}

{% tab title="Own notify" %}

```lua
-- config/config_general.lua
Config.UseCustomNotify = true

Config.Functions = {
    ["notify"] = function(ntype, title, text, time)
        exports['your-notify']:Notify(text, ntype, time)
    end,
}
```

{% endtab %}
{% endtabs %}

***

### Player Guide

#### Getting started

1. **Buy the supplies** at the Beekeeping Supply shop: a beehive, bees, a set of frames, a bee smoker, empty honey jars and the beekeeper suit
2. **Use the Apiary Map** to see where beekeeping is allowed
3. **Put on the beekeeper suit** by using the item — bees sting everyone without it

#### Placing a hive

Use the **Beehive** item inside a zone. A see-through hive stands on the ground in front of you.

| Key         | What it does                                               |
| ----------- | ---------------------------------------------------------- |
| Walk / turn | Moves the hive with you — it always stands in front of you |
| `E`         | Places the hive, your character builds it                  |
| `X`         | Cancels                                                    |

#### Working the hive

| Step | Where                                                                         | Keys                |
| ---- | ----------------------------------------------------------------------------- | ------------------- |
| 1    | **Open the hive** — walk up to your hive                                      | `E`                 |
| 2    | **Open the roof** — click the roof                                            | `LMB`               |
| 3    | **Insert the frames** — drag each frame down into its slot, one after another | `LMB` hold and drag |
| 4    | **Leave the view**                                                            | `ESC`               |
| 5    | **Place the bees** — at the hive                                              | `E`                 |
| 6    | **Wait** — a status bar at the hive shows the remaining time                  | —                   |
| 7    | **Sugar water** — when the sign over the hive says *Needed*                   | `G`                 |
| 8    | **Smoke the hive** — pump, walk around the hive when told to                  | `E` pump · `X` stop |
| 9    | **Harvest** — open the hive, click the roof, drag the full combs up and out   | `E` · `LMB`         |

The frames are only taken from your inventory once all four sit in the hive. Leaving the view halfway costs nothing.

You get a message the moment your bees are done, and on every login if one of your hives is waiting.

#### Using the honey station

Walk up to a free honey extractor with 4 **Dirty Honey** in your pockets.

| Step | What you do                                                                             | Keys                |
| ---- | --------------------------------------------------------------------------------------- | ------------------- |
| 1    | **Put the honeycombs down** — the camera moves over the table                           | `E`                 |
| 2    | **Uncap** — pull the fork over the highlighted comb until the wax is gone, comb by comb | `LMB` hold and drag |
| 3    | **Load the extractor** — click the combs one by one                                     | `LMB`               |
| 4    | **Spin** — pull along the line from left to right, again and again                      | `LMB` hold and drag |
| 5    | **Open the tap** — needs an **Empty Honey Jar** in your pockets                         | `LMB` on the lever  |
| 6    | **Take the jar** — once it is full                                                      | `LMB` on the jar    |

`ESC` leaves the view at any time. If you leave during uncapping, you start that step again next time — no items are lost.

#### Selling

Walk to the honey buyer, pick the amounts, choose **CASH** or **BANK** and press **SELL**. **MAX** puts everything you have on the counter.

***

### Admin Guide

#### Placing honey stations with `/station`

Type `/station` ingame. A preview station follows your aim.

| Key         | What it does                                          |
| ----------- | ----------------------------------------------------- |
| Mouse       | Moves the station to where you look                   |
| Mouse wheel | Rotates it (`SHIFT` = faster)                         |
| `E`         | Keeps this one and starts the next — up to 10 at once |
| `ENTER`     | Finishes and prints the block                         |
| `BACKSPACE` | Cancels everything                                    |

After `ENTER` the finished block is printed into the **F8 console**:

```lua
Config.Station.Locations = {
    { coords = vector4(1536.1908, 1702.3914, 108.6863, 173.827), blip = true },
    { coords = vector4(1534.2391, 1706.0848, 108.7725, 257.827), blip = true }
}
```

Copy it into `config/config_station.lua` and restart the script. Set `blip = false` on stations that stand next to each other, so the map shows only one.

{% hint style="info" %}
The preview stations exist only on your own screen and disappear when you finish. Nothing is saved until you paste the block into the config.
{% endhint %}

#### Shop and honey buyer positions

Stand where the ped should stand and copy your position and heading into `Location` and `NPC.coords`. Keep the same `z` in both.

***

### Database Tables

| Table                   | Content                                                                                             |
| ----------------------- | --------------------------------------------------------------------------------------------------- |
| `risk_beekeeping_hives` | Every placed hive — owner, position, frames, bees, sugar water, production timer and remaining uses |

The table is created automatically on the first start. Missing columns are added automatically after an update.

{% hint style="warning" %}
Without a running database resource the hives only exist until the next server restart. The console shows a red message in that case.
{% endhint %}

***

### Troubleshooting

<details>

<summary>"You can't place a beehive here!"</summary>

**Cause:** The player is not inside a zone. **Solution:** Add a zone in `config/config_zones.lua` or use the Apiary Map to find an existing one.

</details>

<details>

<summary>"This area is full"</summary>

**Cause:** The zone reached its `maxHives`. **Solution:** Raise `maxHives` for that zone, set it to `0` for unlimited, or add another zone.

</details>

<details>

<summary>"You already have the maximum of 2 beehives!"</summary>

**Cause:** `Config.MaxHivesPerPlayer` is reached. **Solution:** Raise it in `config/config_general.lua`, `0` = unlimited.

</details>

<details>

<summary>Items cannot be bought, used or given</summary>

**Cause:** The items do not exist in your inventory. **Solution:** Add them from the `_items` folder — SQL for ESX, `items.lua` for QBCore / QBox or ox\_inventory. See Adding the items.

</details>

<details>

<summary>The shop or the honey buyer shows no item pictures</summary>

**Cause:** The `_items` folder was deleted or the pictures were moved out of it. **Solution:** Put `_items/images` back. Every picture is named exactly like its item (`hive` → `hive.png`).

</details>

<details>

<summary>Hives or stations are invisible after an update</summary>

**Cause:** New files were added and the server has not picked them up yet. **Solution:** Type `refresh` and then `restart risk-beekeepingv2` in the server console.

</details>

<details>

<summary>"The bees are far too angry! Smoke them out first."</summary>

**Cause:** A finished hive has to be smoked before it opens. **Solution:** Use the bee smoker at the hive. Intended behaviour — set `Smoker.Enabled = false` to skip it.

</details>

<details>

<summary>Smoking stops counting</summary>

**Cause:** Three pumps came from the same side of the hive. **Solution:** Walk around the hive to another side. Intended behaviour.

</details>

<details>

<summary>Players get stung while wearing the suit</summary>

**Cause:** Their clothing does not match `Config.Suit` — usually a custom clothing pack or another script changed the clothes. **Solution:** Adjust `drawable` / `texture` in `config/config_suit.lua` or set `MatchTextures = false`.

</details>

<details>

<summary>A harvest gives only 1 or 2 honeycombs</summary>

**Cause:** The bees got no sugar water during this production. **Solution:** Intended behaviour. Pour sugar water with `G` when the sign over the hive says *Needed*, or set `FeedBees.enabled = false` to always get all four.

</details>

<details>

<summary>No prompt at the hive while the bees are collecting</summary>

**Cause:** A working hive cannot be opened. **Solution:** Intended behaviour — wait until the bees are done.

</details>

<details>

<summary>A hive disappeared</summary>

**Cause 1:** It was harvested `HiveUses` times. **Solution:** Raise `HiveUses` or set it to `0` for unlimited.

**Cause 2:** Nobody touched it for `DeleteAfterDays` days. **Solution:** Raise `DeleteAfterDays`.

</details>

<details>

<summary>The tap lever cannot be clicked</summary>

**Cause:** The player has no empty honey jar. **Solution:** Buy an **Empty Honey Jar** at the shop, then walk up to the station again.

</details>

<details>

<summary>"There are already honeycombs on this station!"</summary>

**Cause:** Another player is using this station. **Solution:** Use another station or wait. A station is freed automatically when that player disconnects.

</details>

<details>

<summary>A station floats or sits in the ground</summary>

**Cause:** The coordinates were typed by hand, or `SnapToGround = false`. **Solution:** Place it again with `/station` and paste the printed block.

</details>

<details>

<summary>Nobody can rob a hive</summary>

**Cause 1:** `Steal.enabled = false` — the default. **Solution:** Set it to `true`.

**Cause 2:** The owner is offline and `onlyWhenOwnerOnline = true`. **Solution:** Intended behaviour.

**Cause 3:** The hive is not finished, was already robbed this production, or the thief is on cooldown. **Solution:** Intended behaviour.

</details>

<details>

<summary>The target eye does not show at the shop</summary>

**Cause:** `Config.Target.Enabled = false`. **Solution:** Set it to `true`. Hives and stations never use the target eye — they always use their key prompt.

</details>

<details>

<summary>Hives are gone after a server restart</summary>

**Cause:** No database resource was running. **Solution:** Start `oxmysql`, `mysql-async` or `ghmattimysql` before this script.

</details>

***

### Best Practices

#### Economy

* ✅ Set `ReadyTimeMin` / `ReadyTimeMax` to a real production time — 30 to 60 minutes feels right on most servers
* ✅ Keep the honey jar clearly more valuable than raw honey, so the extractor is worth the walk
* ✅ Keep the frames at `amount = 4` — one purchase fills exactly one hive
* ❌ Don't set `HiveUses = 0` together with `RequireFramesEachHarvest = false` — honey becomes free money

#### Zones

* ✅ Put zones in the countryside, away from roads and busy areas
* ✅ Use `maxHives` so one field is not covered in hives
* ✅ Place a honey station close to the zones
* ❌ Don't make one zone for the whole map — zones are what give the job a location

#### Theft

* ✅ Keep `onlyWhenOwnerOnline = true` — robbing sleeping players is no gameplay
* ✅ Keep `warnBlipTime` as long as `duration`, so the owner can still find the thief
* ✅ Hook `risk-beekeepingv2:hiveRobbed` into your dispatch script
* ❌ Don't set `sharePercent = 100` with a short `duration` — the owner has no chance to react

#### Production

* ✅ Set `Config.Locale` to your language
* ✅ Keep `DeleteAfterDays` above `0` so abandoned hives free up their slots
* ✅ Replace the default shop, buyer and station positions with your own

***

### Quick Start Checklist

**Server setup**

* [ ] Add the resource to `server.cfg` and start it — the table is created automatically
* [ ] Add all items from `_items` to your inventory
* [ ] Copy `_items/images` into your inventory image folder
* [ ] Set `Config.Locale` if not using English

**Build the job**

* [ ] Set your zones in `config/config_zones.lua`
* [ ] Set the shop position and ped
* [ ] Set the honey buyer position and ped
* [ ] `/station` → place your stations → `ENTER` → paste the block → restart

**Configure the rules**

* [ ] Set `ReadyTimeMin` / `ReadyTimeMax`
* [ ] Adjust shop and buyer prices
* [ ] Decide on sugar water, smoker and theft
* [ ] Set `MaxHivesPerPlayer` and `maxHives` per zone

**Test everything**

* [ ] Buy everything at the shop — pictures show, money is taken
* [ ] Use the Apiary Map — the zones appear on the map
* [ ] Put on the suit — walk up to a hive with bees, no stings
* [ ] Place a hive, insert the frames, place the bees
* [ ] Pour sugar water when the sign asks for it
* [ ] Smoke the hive and harvest four combs
* [ ] Run the full station: put down, uncap, load, spin, fill, take the jar
* [ ] Sell the jar to the honey buyer
* [ ] Restart the server — the hive is still there
