> For the complete documentation index, see [llms.txt](https://evo-development.gitbook.io/evo-development-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://evo-development.gitbook.io/evo-development-docs/products/evo-k9-v3/installation.md).

# Installation

## 📦 Installation Instructions for Evo-K9-V3 Script System

To ensure the proper functioning of the Evo-K9-V3 Script System, follow these installation instructions carefully. This system has essential dependencies and setup steps for a smooth experience.

***

### 🛠️ Asset Dependencies

**Mandatory Dependencies:**

* **FiveM Server** (latest recommended)
* **MySQL Database**
* **Framework:**
  * [QB-Core](https://github.com/qbcore-framework/qb-core) (recommended)
  * [es\_extended (ESX Base)](https://github.com/esx-framework/es_extended)
  * QBox
  * Standalone
* **Target System:**
  * [qb-target](https://github.com/BerkieBb/qb-target)
  * [ox\_target](https://github.com/overextended/ox_target)
* **Inventory System:**
  * [qb-inventory](https://github.com/qbcore-framework/qb-inventory)
  * [ox\_inventory](https://github.com/overextended/ox_inventory)
  * qs-inventory
  * tgiaan-inventory

***

### 1️⃣ Download & Place the Resource

* Download Evo-K9-V3 and place it in your server’s resources folder:

  ```
  [FRAMEWORK]/evo-k9-v3/
  ```

***

### 2️⃣ Database Setup

* Run the SQL commands from `database.sql`to set up required tables and columns.\
  Example:

<details>

<summary>Full Database</summary>

```sql
CREATE TABLE IF NOT EXISTS `evo_k9_dogs` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `citizenid` varchar(50) NOT NULL,
  `name` VARCHAR(50) NOT NULL DEFAULT 'Unnamed',
  `model` varchar(50) DEFAULT NULL,
  `health` int(11) DEFAULT 200 CHECK (`health` <= 200),
  `hunger` int(11) DEFAULT 100 CHECK (`hunger` <= 100),
  `thirst` int(11) DEFAULT 100 CHECK (`thirst` <= 100),
  `rep` int(11) DEFAULT 0,
  `stamina` int(11) DEFAULT 0 CHECK (`stamina` <= 100),
  `smell` int(11) DEFAULT 0 CHECK (`smell` <= 100),
  `attack` int(11) DEFAULT 0 CHECK (`attack` <= 100),
  `ui_position` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT '{"x": 0, "y": 0}' CHECK (json_valid(`ui_position`)),
  `dogid` varchar(20) DEFAULT NULL,
  `outfit` text DEFAULT NULL,
  `animation` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`animation`)),
  `cage` tinyint(1) NOT NULL DEFAULT 0,
  `defence` int(11) DEFAULT 0,
  `intelligence` int(11) DEFAULT 0,
  PRIMARY KEY (`id`),
  UNIQUE KEY `dogid` (`dogid`)
) ENGINE=InnoDB AUTO_INCREMENT=498 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;


CREATE TABLE IF NOT EXISTS `evo_k9_houses` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(100) NOT NULL DEFAULT 'Unnamed',
  `prop` varchar(64) NOT NULL,
  `coords` text NOT NULL,
  `dog_spawn` text NOT NULL,
  `camera` text NOT NULL,
  `jobs` text NOT NULL,
  `minimumgrade` int(11) NOT NULL DEFAULT 0,
  `created_by` varchar(64) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
```

</details>

> **Tip:** Always back up your database before making changes.

***

### 3️⃣ Install Items in Inventory

* Add these items to your inventory system (qb-inventory/ox\_inventory):

### 🧾 QBCore Inventory Items

<details>

<summary>💡 Click to view usable items for <strong>qb-inventory</strong>:</summary>

```lua
k9glowstick = { 
    name = 'k9glowstick', 
    label = 'Dog Glow Stick', 
    weight = 0, 
    type = 'item', 
    image = 'k9glowstick.png', 
    unique = false, 
    useable = true, 
    shouldClose = true, 
    combinable = nil, 
    description = 'A glow for your k9' 
},

k9weed = { 
    name = 'k9weed', 
    label = 'K9 Weed', 
    weight = 0.5, 
    type = 'item', 
    image = 'k9weed.png', 
    unique = false, 
    useable = true, 
    shouldClose = true, 
    combinable = nil, 
    description = 'Plant weed to train your k9 smell power' 
},

k9revivekit = { 
    name = 'k9revivekit', 
    label = 'Revivekit For K9', 
    weight = 1, 
    type = 'item', 
    image = 'k9revivekit.png', 
    unique = false, 
    useable = true, 
    shouldClose = true, 
    combinable = nil, 
    description = 'Revivekit for your k9 dog' 
},

k9food = { 
    name = 'k9food', 
    label = 'K9 Food', 
    weight = 1, 
    type = 'item', 
    image = 'k9food.png', 
    unique = false, 
    useable = true, 
    shouldClose = true, 
    combinable = nil, 
    description = 'Food For Your K9 Dog' 
},

k9drink = { 
    name = 'k9drink', 
    label = 'K9 Drink', 
    weight = 1, 
    type = 'item', 
    image = 'k9drink.png', 
    unique = false, 
    useable = true, 
    shouldClose = true, 
    combinable = nil, 
    description = 'Drink For Your K9 Dog' 
},

k9flashlight = { 
    name = 'k9flashlight', 
    label = 'K9 Flashlight', 
    weight = 1, 
    type = 'item', 
    image = 'k9flashlight.png', 
    unique = false, 
    useable = true, 
    shouldClose = true, 
    combinable = nil, 
    description = 'A flashlight for your K9 dog' 
},

k9harness = { 
    name = 'k9harness ', 
    label = 'K9 harness', 
    weight = 1, 
    type = 'item', 
    image = 'k9harness.png', 
    unique = false, 
    useable = true, 
    shouldClose = true, 
    combinable = nil, 
    description = 'A harness to carry your K9' 
},
```

</details>

### 🎄 QBCore Inventory Items For Christmas (optional)

<details>

<summary>🎄 Click to view usable items for <strong>qb-inventory</strong>:</summary>

```lua
k9christmasshoe = {
    name = 'k9christmasshoe',
    label = 'K9 Christmas Shoes',
    weight = 0,
    type = 'item',
    image = 'k9christmasshoe.png',
    unique = false,
    useable = true,
    shouldClose = true,
    combinable = nil,
    description = 'Festive shoes for your K9 partner'
},
```

</details>

***

### 📦 Ox Inventory Items

<details>

<summary>💡 Click to view usable items for <strong>ox_inventory</strong>:</summary>

{% code fullWidth="true" %}

```lua
["k9glowstick"] = {
    label = "Dog Glow Stick",
    weight = 0,
    stack = false,
    close = true,
    description = "A glow for your k9",
    client = {
        image = "k9glowstick.png",
    }
},

["k9weed"] = {
    label = "K9 Weed",
    weight = 0.5,
    stack = false,
    close = true,
    description = "Plant weed to train your k9 smell power",
    client = {
        image = "k9weed.png",
    }
},

["k9revivekit"] = {
    label = "Revivekit For K9",
    weight = 1,
    stack = false,
    close = true,
    description = "Revivekit for your k9 dog",
    client = {
        image = "k9revivekit.png",
    }
},

["k9food"] = {
    label = "K9 Food",
    weight = 1,
    stack = false,
    close = true,
    description = "Food For Your K9 Dog",
    client = {
        image = "k9food.png",
    }
},

["k9drink"] = {
    label = "K9 Drink",
    weight = 1,
    stack = false,
    close = true,
    description = "Drink For Your K9 Dog",
    client = {
        image = "k9drink.png",
    }
},

["k9flashlight"] = {
    label = "K9 Flashlight",
    weight = 1,
    stack = false,
    close = true,
    description = "A flashlight for your K9 dog",
    client = {
        image = "k9flashlight.png",
    }
},


["k9harness"] = {
    label = "K9 harness",
    weight = 1,
    stack = false,
    close = true,
    description = "A harness to carry your K9",
    client = {
        image = "k9harness.png",
    }
},
```

{% endcode %}

</details>

### 🎄 Ox Inventory Items For Christmas (optional)

<details>

<summary>🎄 Click to view usable items for <strong>ox_inventory</strong>:</summary>

{% code fullWidth="true" %}

```lua
["k9christmasshoe"] = {
    label = "K9 Christmas Shoes",
    weight = 0,
    stack = false,
    close = true,
    description = "Festive shoes that slip over your K9\'s paws",
    client = {
        event = 'evo-k9:client:toggleChristmasShoes'
    }
},
```

{% endcode %}

</details>

***

### 4️⃣ 🖼️ Images Installation

* In the `evo-k9-v3/images` directory, you’ll find all item images.
* **For qb-inventory:**\
  Copy all images to:

  ```
  qb-inventory/html/images
  ```
* **For ox\_inventory:**\
  Place images in the appropriate `web/images/items` directory.

> **Make sure all image files are correctly placed so they are visible in your inventory UI.**

***

### 5️⃣ 📝 Inventory Integration

* **For ox\_inventory:**\
  Edit `modules/inventory/server.lua` (after `loadInventoryData` function, usually around line 196) to add the new Integration.

<details>

<summary>Click to expand the code for ox_inventory</summary>

```lua
local function evok9getVehicleNetIdByPlate(plate)
    local vehicles = GetAllVehicles()
    for _, vehicle in ipairs(vehicles) do
        local vehiclePlate = GetVehicleNumberPlateText(vehicle)
        if vehiclePlate:find(plate) then
            local netid = NetworkGetNetworkIdFromEntity(vehicle)
            return netid
        end
    end
    return nil
end

---@param data table -- The inventory data, including id, type, and entity information
---@param player table -- The player data (optional)
---@return table | nil -- Returns the list of items or nil if no items found
local function evok9PrintAndReturnInventoryItems(data, player)
    local inventory = setmetatable(Inventory, {
        __call = function(self, inv, player)
            if not inv then
                return self
            elseif type(inv) == 'table' then
                if inv.__index then return inv end

                return not inv.owner and Inventories[inv.id] or loadInventoryData(inv, player)
            end

            return Inventories[inv] or loadInventoryData({ id = inv }, player)
        end
    })(data, player)

    if inventory.items then
        local items = {}

        for slot, item in pairs(inventory.items) do
            table.insert(items, {name = item.name, count = item.count or 1})
        end
        return items
    else
        return nil
    end
end

exports('evok9PrintAndReturnInventoryItems', evok9PrintAndReturnInventoryItems)


---@param plate string -- The plate of the vehicle
---@return table | nil -- Returns the list of items or nil if no items found
local function evok9getVehicleTrunkItemsByPlate(plate)
    local netid = evok9getVehicleNetIdByPlate(plate)
    if netid then
        local data = {
            id = "trunk" .. plate,
            type = "trunk",
            netid = netid,
        }
        
        local items = evok9PrintAndReturnInventoryItems(data)
        if items then
            for _, item in ipairs(items) do
            end
        end
        return items
    else
        return nil
    end
end

exports('evok9getVehicleTrunkItemsByPlate', evok9getVehicleTrunkItemsByPlate)


---@param plate string -- The vehicle plate number
---@return table -- Returns the list of items in the glovebox or nil if not found
local function evok9getVehicleGloveboxItemsByPlate(plate)
    local netid = evok9getVehicleNetIdByPlate(plate)
    if netid then
        local data = {
            id = "glove" .. plate,
            type = "glovebox",
            netid = netid,
        }
        local items = evok9PrintAndReturnInventoryItems(data)

        -- **Print Debug Information**
        if items then
            print("[DEBUG] Glovebox items for vehicle (" .. plate .. "):")
            for _, item in ipairs(items) do
                print("Item:", item.name, "| Count:", item.count)
            end
        else
            print("[ERROR] No items found in the glovebox for plate:", plate)
        end

        return items
    else
        print("[ERROR] Vehicle NetID not found for plate:", plate)
        return nil
    end
end

exports('evok9getVehicleGloveboxItemsByPlate', evok9getVehicleGloveboxItemsByPlate)
```

</details>

* **For qb-inventory:**\
  Edit `server/functions.lua` to add the new Integration.

<details>

<summary>Click to expand the code for <strong>qb-inventory</strong></summary>

<pre class="language-lua"><code class="lang-lua"><strong>--- @param source number The player's server ID.
</strong>--- @param identifier string|nil The identifier of the inventory to open.
--- @param data table|nil Additional data for initializing the inventory.
function evok9CheckInventoryItems(identifier, data)
    if not identifier then
        print("No identifier provided.")
        return nil
    end
​
    if type(identifier) ~= 'string' then
        print("Invalid identifier provided.")
        return nil
    end
​
    local inventory
    if identifier:sub(1, 7) == "player-" then
        local playerId = tonumber(identifier:sub(8))
        local Player = QBCore.Functions.GetPlayer(playerId)
        if Player then
            inventory = Player.PlayerData.items
        else
            print("Player not found.")
            return nil
        end
    else
        inventory = Inventories[identifier]
        if not inventory then
            inventory = InitializeInventory(identifier, data)
        end
        inventory = inventory.items
    end
​
    local formattedInventory = {
        name = identifier,
        label = (inventory and inventory.label) or (data and data.label) or identifier,
        maxweight = (inventory and inventory.maxweight) or (data and data.maxweight) or Config.StashSize.maxweight,
        slots = (inventory and inventory.slots) or (data and data.slots) or Config.StashSize.slots,
        inventory = inventory
    }
​
    if formattedInventory.inventory then
        return formattedInventory.inventory
    else
        print("No items found in inventory: " .. identifier)
        return nil
    end
end
​
exports('evok9CheckInventoryItems', evok9CheckInventoryItems)
​
​
--- @param source number The player's server ID.
--- @param identifier string|nil The identifier of the inventory to open.
--- @param data table|nil Additional data for initializing the inventory.
function evok9CheckPlayerInventoryItems(playerId)
    local Player = QBCore.Functions.GetPlayer(playerId)
    if not Player then
        print("Player not found.")
        return nil
    end
​
    return Player.PlayerData.items
end
​
exports('evok9CheckPlayerInventoryItems', function(playerId)
    return evok9CheckPlayerInventoryItems(playerId)
end)
</code></pre>

</details>

* **For qs-inventory:**\
  Edit `server/custom/misc/evok9.lua` to add the new Integration.

{% hint style="warning" %}
For **qs-inventory**, remove the **first 3 lines** at the top of `server/custom/misc/evok9.lua`.

Those lines check for `evo-k9-v2` and can stop the integration from loading.
{% endhint %}

***

### 6️⃣ ⚙️ Script Configuration

* Open `config.lua` and adjust settings to match your server:

  ```lua
  Config = {
      Framework = 'QB', -- QB, ESX, qbox, standalone
      target = "qb-target", -- qb-target or ox_target
      InventoryType = 'qb-inventory', -- qb-inventory or ox_inventory
      language = 'en', -- Language for localization
      DevMode = false, -- Enable debug mode
  }
  ```
* Adjust shop, training, and other options as needed for your server.

***

### 7️⃣ Add to server.cfg

* Add the resource to your `server.cfg`:

  ```
  ensure evo-k9-v3
  ```

***

### 8️⃣ Change Languages

You can fully customize the script language to fit your preferred localization. All sections are open and editable, allowing you to translate the system to any language you choose.

The language configuration is divided into two main parts:

**1. Backend Language Settings `locales.lua`**

All dynamic text and system notifications are handled here. You can modify this file to update backend language elements:

```
📁 EVO-K9-V3/locales.lua
```

**2. UI Language & Outfit Options `lang.js`**

Text that appears in the user interface is defined in this file. Additionally, this file includes an option to remove or customize the dog outfit:

```
📁 EVO-K9-V3/UI/lang.js
```

🛠️ Feel free to adjust both files to provide a fully localized and personalized experience for your community.

***

### :nine: :microphone2: Voice Commands Setup (Groq STT Key)

The K9 voice-command feature lets officers **talk to their dog** — the script records your microphone and uses **Groq Whisper** (free & fast) to turn speech into commands like *"sit"*, *"come"*, or *"search the car"*.

To keep your server secure, the API key is **never included in the script files**. Every server owner uses their **own free key**.

:key: **Get Your Free Key:**

1. Go to <https://console.groq.com> and create a free account.
2. Open **API Keys** → click **Create API Key**.
3. Copy the key (it starts with `gsk_...`).

:gear: **Add It to Your server.cfg:**

```cfg
set evo_k9_groq_key "gsk_your_key_here"
ensure evo-k9-v3
```

:warning: The `set` line must come **before** `ensure evo-k9-v3` in your server.cfg.

:test\_tube: **Optional — Change the Transcription Model:**

```cfg
set evo_k9_stt_model "whisper-large-v3-turbo"
```

:bulb: **Good to Know:**

* No key set? No problem — the whole script works normally, **only voice commands stay disabled**, and the server console shows a friendly warning at startup.
* **Never** paste your key into the script files — players download those. The server.cfg convar keeps it private on your server only.
* Voice not transcribing? Check the server console for `[EVO-K9 VOICE]` messages and verify your key is active in the Groq console.

***

## 🏠 Available Maps

* `EVO_K9Facility_Airbase`
* `EVO_K9Facility_WindFarm`

You can only use **one** map at a time!

### 🛠 Setup

1. **Choose Your Map**\
   Open `config.lua` and select the map you want to use.
2. **Edit or Remove a Map**\
   If you want to change ped positions or fully remove a map:
   * Delete the map folder you don’t need.
   * Remove the related lines from `fxmanifest.lua`.

***

### 📌 Final Steps

* Double-check all dependencies are installed and configured.
* Make sure all images and items are added to your inventory system.
* Run the SQL scripts and verify your database tables.
* Start your server and enjoy your new K9 companion!

***

## **🐾 K9 Not Spawning – Troubleshooting Guide**

* You must have an active license or a maximum player slot of 8 for the dog to spawn.
* Redownload the script from Keymaster, as sometimes it doesn't download completely.
* Check if any anti-cheat is blocking the K9 from spawning.
