# Config

{% code title="Config.lua" %}

```lua
Config = {}

-- ════════════════════════════════════════════════════════════════════════════════════
--  GENERAL SETTINGS
-- ════════════════════════════════════════════════════════════════════════════════════

-- Interface language: 'pl' (Polish), 'en' (English)
-- Translation files located in: locales/pl.lua, locales/en.lua
Config.Locale = 'en'

-- ════════════════════════════════════════════════════════════════════════════════════
--  UI SYSTEM
-- ════════════════════════════════════════════════════════════════════════════════════

-- Notification system: 'ox_lib', 'ox_notify', 'vms_notify', 'oct_notify', 'custom'
Config.NotificationSystem = 'ox_lib'

-- Progress bar system: 'ox_lib', 'fc_progress', 'oct_notify', 'custom'
Config.ProgressBarSystem = 'ox_lib'

Config.CustomNotify = function(title, message, duration, type)
    local notifyType = type or 'info'
    local notifyDuration = duration or 5000
    local system = Config.NotificationSystem

    if system == 'ox_notify' or system == 'ox_lib' then
        lib.notify({
            title = title,
            description = message,
            type = notifyType,
            duration = notifyDuration,
            animation = {
                type = 'slideIn',
                duration = 500
            }
        })
    elseif system == 'vms_notify' then
        local colors = {
            success = '#4CAF50',
            error = '#F44336',
            info = '#2196F3'
        }
        local icons = {
            success = 'check',
            error = 'times',
            info = 'info'
        }
        exports['vms_notify']:Notification(
            title,
            message,
            notifyDuration,
            colors[notifyType] or '#FFFFFF',
            icons[notifyType] or 'info'
        )
    elseif system == 'oct_notify' then
        exports['oct_notify']:Alert(title, message, notifyDuration, notifyType)
    else
        -- Add your custom notification logic here
        -- Example: exports['your_notify']:Alert(title, message, notifyDuration, notifyType)
    end
end

Config.CustomProgressBar = function(options)
    local opts = options or {}
    local system = Config.ProgressBarSystem or 'ox_lib'

    if system == 'ox_lib' then
        return lib.progressBar({
            duration = opts.duration,
            label = opts.label,
            useWhileDead = opts.useWhileDead or false,
            canCancel = opts.canCancel,
            disable = opts.disable,
            anim = opts.anim,
            prop = opts.prop
        })
    elseif system == 'oct_notify' then
        local payload = {}
        for k, v in pairs(opts) do payload[k] = v end
        payload.duration = opts.duration
        payload.label = opts.label
        payload.icon = payload.icon or 'fa-solid fa-arrows-rotate'
        payload.iconColor = payload.iconColor or '#66bb6a'
        payload.canCancel = opts.canCancel
        payload.disable = opts.disable
        return exports['oct_notify']:progressBar(payload)
    elseif system == 'custom' then
        -- Add your custom progress bar logic here
        -- Example: return exports['your_progressbar']:Start(opts)
    end

    return false
end

Config.CustomCancelProgress = function()
    local system = Config.ProgressBarSystem or 'ox_lib'

    if system == 'ox_lib' then
        if lib.progressActive() then
            lib.cancelProgress()
        end
    elseif system == 'oct_notify' then
        TriggerEvent('oct_notify:cancelProgress')
    elseif system == 'custom' then
        -- Add your custom cancel logic here
        -- Example: exports['your_progressbar']:Cancel()
    end
end

-- ════════════════════════════════════════════════════════════════════════════════════
--  POLICE & DISPATCH
-- ════════════════════════════════════════════════════════════════════════════════════

-- Minimum number of police officers required to start a robbery (0 = no requirement)
Config.PoliceRequired = 0

-- Jobs that count as police
Config.PoliceJobs = {
    ['police'] = true,
    ['sheriff'] = true
}

-- Dispatch system for police alerts:
--   'piotreq'       - Piotreq Police MDT V2
--   'ps-dispatch'   - Project Sloth Dispatch
--   'cd_dispatch'   - Codesign Dispatch
--   'core_dispatch' - Core Dispatch
--   'custom'        - Your own system (define below)
--   'none'          - Disable dispatch
Config.DispatchSystem = 'ps-dispatch'

-- Piotreq MDT settings (only used when Config.DispatchSystem = 'piotreq')
Config.PiotreqDispatch = {
    code = '10-90',
    title = 'Shop Robbery',
    description = 'Shop robbery in progress',
    sprite = 375,
    color = 1,
    scale = 1.0,
    length = 60000,
    sound = 'Lose_1st',
}

local function unpackDispatchCoords(vec)
    if vec == nil then return 0.0, 0.0, 0.0 end
    if type(vec) == 'vector3' then return vec.x, vec.y, vec.z end
    if type(vec) == 'table' then
        local x = vec.x or vec[1] or 0.0
        local y = vec.y or vec[2] or 0.0
        local z = vec.z or vec[3] or 0.0
        return x, y, z
    end
    return 0.0, 0.0, 0.0
end

Config.CustomDispatch = function(coords, shopName, playerSource)
    local system = Config.DispatchSystem or 'none'

    if system == 'none' then
        return
    end

    if system == 'piotreq' then
        if playerSource then
            TriggerClientEvent('oct_shopheist:sendPiotreqDispatch', playerSource, coords, shopName)
        end
        return
    elseif system == 'ps-dispatch' then
        exports['ps-dispatch']:CustomAlert({
            coords = coords,
            message = 'Shop robbery in progress at ' .. shopName,
            dispatchCode = '10-90',
            description = 'Shop Robbery',
            radius = 0,
            sprite = 375,
            color = 1,
            scale = 1.0,
            length = 3,
        })
        return
    elseif system == 'core_dispatch' then
        local x, y, z = unpackDispatchCoords(coords)
        exports['core_dispatch']:addCall('10-90', 'Shop Robbery at ' .. shopName, {
            {icon = 'fa-solid fa-store', info = shopName}
        }, {x, y, z}, 'police', 3000, 120, 5)
        return
    elseif system == 'custom' then
        -- Add your custom dispatch logic here
        -- Example:
        -- exports['your_dispatch']:ShopRobbery({ coords = coords, shopName = shopName })
        return
    end
end

-- ════════════════════════════════════════════════════════════════════════════════════
--  GAMEPLAY
-- ════════════════════════════════════════════════════════════════════════════════════

-- Maximum distance from the shop in meters — exceeding this cancels the robbery
Config.MaxRobberyDistance = 50.0

-- Interaction distance for ox_target zones (in meters)
Config.InteractionDistance = 2.0

-- Height offset (meters) for cooldown text drawn above clerks
Config.CooldownTextHeight = 1.0

-- Max distance the clerk can be from their spawn before cooldown text appears (meters)
Config.CooldownTextReturnDistance = 1.0

-- ════════════════════════════════════════════════════════════════════════════════════
--  NPC & INTIMIDATION
-- ════════════════════════════════════════════════════════════════════════════════════

-- Require player to aim at the clerk to intimidate them
Config.RequireAimAtClerk = true

-- Allow intimidating clerk with a firearm (aim at clerk within 5m)
Config.AllowGunIntimidate = true

-- Allow intimidating clerk with a melee weapon (hold right-click within 5m)
Config.AllowMeleeIntimidate = true

-- List of melee weapons that can be used for intimidation
Config.MeleeWeapons = {
    `WEAPON_KNIFE`,
    `WEAPON_SWITCHBLADE`,
    `WEAPON_DAGGER`,
    `WEAPON_MACHETE`,
    `WEAPON_HATCHET`,
    `WEAPON_BATTLEAXE`,
    `WEAPON_POOLCUE`,
    `WEAPON_BAT`,
    `WEAPON_CROWBAR`,
    `WEAPON_WRENCH`,
    `WEAPON_FLASHLIGHT`,
    `WEAPON_HAMMER`,
    `WEAPON_GOLFCLUB`,
}

-- Default clerk ped model (can be overridden per shop in shops.lua)
Config.ClerkModel = 'mp_m_shopkeep_01'

-- ════════════════════════════════════════════════════════════════════════════════════
--  SILENT APPROACH
-- ════════════════════════════════════════════════════════════════════════════════════

Config.SilentApproach = {
    -- Enable or disable the silent approach system entirely
    Enabled = true,

    -- Enable blackout effect (turns off shop lights for all players)
    BlackoutEnabled = true,

    -- Item required to open the electrical box
    ElectricalBoxItem = 'elec_box_keys',

    -- Time in ms to open the electrical box
    OpenBoxDuration = 8000,

    -- Time in ms to cut the wires
    CutWiresDuration = 10000,

    -- Chance (0-100) that the alarm triggers even during silent approach
    -- On success (100 - this value), player receives the safe code instead of triggering alarm
    AlarmChance = 20,

    -- Item given to player on successful silent approach (contains the safe code)
    SafeCodeItem = 'safe_code',

    -- Time in ms to open the safe using the code (faster than C4 method)
    OpenSafeWithCodeDuration = 15000,

    -- Remove items after a successful power cut
    RemoveElectricalBoxItem = true,  -- Remove keycard/tool after opening box
    RemoveSafeCodeItem = true,        -- Remove code item after using it

    -- Chance (0-100) to drop ElectricalBoxItem (elec_box_keys) when robbing a register or safe
    -- Set to 0 to disable the drop entirely
    ElecBoxKeysDropChance = 5,
}

-- ════════════════════════════════════════════════════════════════════════════════════
--  FUSE BOX SABOTAGE (oct_minigames integration)
-- ════════════════════════════════════════════════════════════════════════════════════
-- Allows players to sabotage a shop's fuse box using the 'fuses' minigame.
-- On success: alarm system is disabled — police will NOT be called during that robbery.
-- On failure: alarm stays active, robbery proceeds normally.
--
-- To enable per shop, add these fields inside the shop entry in shops.lua:
--   fuseBoxEnabled = true,
--   fuseBoxCoords  = vector3(x, y, z),   -- position of the fuse box
--   fuseBoxHeading = 0.0,                -- (optional, not used for targets)

Config.FuseBoxSabotage = {
    -- Enable or disable the fuse box sabotage system entirely
    Enabled = true,

    -- Minigame difficulty: 'easy' | 'medium' | 'hard'
    Difficulty = 'medium',
}

-- ════════════════════════════════════════════════════════════════════════════════════
--  ANIMATIONS & TIMING
-- ════════════════════════════════════════════════════════════════════════════════════
-- All durations are in milliseconds (1000ms = 1 second)

Config.Animations = {
    -- Time in ms to intimidate the clerk
    IntimidateClerkDuration = 5000,

    -- Time in ms to intimidate a customer
    IntimidateCustomerDuration = 3000,

    -- C4 bomb configuration
    Bomb = {
        -- Time in ms to place the bomb on the safe
        duration = 5000,

        -- Time in ms until the bomb explodes after being planted
        timer = 10000,

        -- Animation played when placing the bomb
        anim = {
            dict = 'anim@narcotics@trash',
            clip = 'drop_front',
            flag = 16
        },

        -- Bomb prop position offset on top of the safe
        offset = {
            forward = 0.0,
            up = 0.0,
            pitch = -90.0,
            roll = 0.0,
            yaw = 0.0
        }
    }
}

-- Shop locations are defined in shops.lua
```

{% endcode %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://octavista-development.gitbook.io/octavista-development/scripts/shop-heist-soon/config.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
