--[[
    ╔══════════════════════════════════════════════════════════════════════╗
    ║                    AGENCY SHOP  -  CONFIGURATION                     ║
    ║                          (c) Agency Scripts                          ║
    ╚══════════════════════════════════════════════════════════════════════╝

    CONTENTS
     1. Core               Framework, inventory, language, money, debug
     2. Interaction        Prompt or target, keys, distances, peds, holo markers,
                           office computer, security cameras
     3. Payment            Cash, bank, card fee, weapon licences, Agency Pay
     4. Store Types        Blips, cashier ped, purchase price per type
     5. Catalogs           What each store type sells
     6. Stores             Every store on the map and its mode
     7. Realism Mode       Basket, trolley, shelves, checkout, items in the basket
     8. Ownership          Buying stores, prices, staff, register, selling to players
     9. Deliveries         Wholesale orders, NPC drivers or player couriers
    10. Gas Stations       Pumps, fuel types, moving prices, nozzle, hose, sounds
    11. Fuel System        Built-in fuel use, or the fuel script you run
    12. Admin & Editor     Who may edit stores and place shelves
    13. Standalone         Money and items without a framework
    14. Robbery            Holding up the cashier, the safe, the police

    Discord webhook addresses go into config_server.lua, which is never
    sent to players.
]]

Config = {}


--==============================================================================
--  1 · CORE
--==============================================================================

-- ── Framework ────────────────────────────────────────────────────────────────
-- 'auto'        = detect Qbox, QBCore or ESX (no framework found = standalone)
-- 'qb' / 'qbx'  = force QBCore / Qbox
-- 'esx'         = force ESX
-- 'standalone'  = no framework, see 13 · STANDALONE
Config.Framework = 'auto'

-- ── Inventory ────────────────────────────────────────────────────────────────
-- 'auto'      = ox_inventory, then qs-inventory, then the framework's own
--               (qb-inventory, ps, lj, jpr and other QB forks, ESX)
-- 'ox' / 'qs' / 'framework' = force one
Config.Inventory = 'auto'

-- Where the item pictures come from. 'auto' reads them from the inventory
-- that runs. A folder link works too: 'nui://my-inventory/html/images/'
-- false = no pictures, the store shows the first letter instead.
Config.Images = 'auto'

-- ── Language ─────────────────────────────────────────────────────────────────
-- 'en' 'de' 'fr' 'es' 'it' 'nl' 'pl' 'pt' 'cs' 'ro' 'ru' 'sv' 'tr' 'da'
-- 'fi' 'el' 'hu' 'hr' 'bg' 'uk' 'ar' 'hi' 'ja' 'ko' 'zh'
Config.Locale = 'en'

-- Your own wording, kept across updates. Any key from locales/en.lua:
--   Config.Text = { notify_title = 'Corner Shop', ui_pay = 'Checkout' }
Config.Text = {}

-- ── Money ────────────────────────────────────────────────────────────────────
Config.Currency = '$'                         -- shown in front of every price
Config.CurrencyAfter = false                  -- true = 12.50 € instead of $12.50

-- ── Database ─────────────────────────────────────────────────────────────────
-- Creates the tables from install/agency_shop.sql on start. Leave on.
Config.AutoApplySql = true

-- ── Debug ────────────────────────────────────────────────────────────────────
-- Prints shelf detection, zones and framework checks to the consoles.
-- Meant for support cases, keep false when live.
Config.Debug = false


--==============================================================================
--  2 · INTERACTION
--==============================================================================
-- Keys use FiveM key names: 'E', 'G', 'F1', 'BACK' ...
-- https://docs.fivem.net/docs/game-references/input-mapper-parameter-ids/keyboard/
-- Players can rebind them under Settings > Key Bindings > FiveM.

-- 'prompt' = the Agency key card at the bottom of the screen
-- 'target' = ox_target or qb-target (falls back to the prompt if none runs)
-- 'auto'   = target when ox_target or qb-target runs, else the prompt
-- With target: cashiers, counters without a cashier, shelves, the basket
-- stand, pumps and the depot are all targets. Holding the nozzle stays on
-- the key (text above the fuel cap).
Config.Interaction = 'prompt'

Config.Keys = {
    Interact  = 'E',                          -- open a store, take from a shelf, refuel
    Secondary = 'G',                          -- second choice on a prompt (trolley, put back)
    Cancel    = 'BACK',                       -- stop refuelling, drop a crate
}

Config.Distance = {
    Counter = 2.0,                            -- metres to the cashier to open the store
    Shelf   = 1.6,                            -- metres to a shelf in realism mode
    Pump    = 2.4,                            -- metres to a pump
    Vehicle = 4.5,                            -- a vehicle this close to the pump can be filled
}

-- A small floating triangle in shops: at the counter, every shelf and the
-- basket stand, so players see where something can be done. Not at pumps.
Config.Markers = {
    Enabled  = true,
    Type     = 2,                             -- GTA marker type (2 = triangle pointing down, 1 = ring on the floor)
    Size     = vector3(0.14, 0.14, 0.11),
    Height   = 0.45,                          -- metres above the floor
    Bob      = true,                          -- floats gently up and down
    BobTime  = 3.5,                           -- seconds for one up and down
    BobHeight = 0.03,                         -- metres it moves
    Rotate   = true,                          -- turns slowly
    Color    = { 226, 182, 73, 190 },         -- r, g, b, alpha (the gold of the Agency UI)
    Distance = 12.0,                          -- shown within this many metres
}

-- The owner's panel opens at the computer in the back office. The computer
-- is found by model; for stores without one (custom stores) place an
-- "Office computer" point with /ashops (Points). With neither, the panel
-- stays on the counter.
Config.Office = {
    Enabled  = true,
    Distance = 1.6,                           -- metres to the computer
    MarkerColor = { 220, 55, 55, 200 },       -- the triangle in front of it (owners and staff only)
    Models   = {
        'prop_monitor_01a', 'prop_monitor_01b', 'prop_monitor_01c', 'prop_monitor_01d',
        'prop_monitor_02', 'prop_monitor_03b', 'prop_monitor_04a', 'prop_monitor_li',
        'prop_monitor_w_large', 'prop_pc_01a', 'prop_pc_02a', 'prop_laptop_01a',
        'v_res_monitorwidelarge', 'v_corp_cd_monitor', 'prop_dyn_pc', 'prop_dyn_pc_02',
    },
}

-- Security cameras: the "Cameras" tab in the owner's panel shows the store
-- live, the same way as Agency-Housing. The cameras hanging in the store
-- (Models) are used; a store can also list its own spots in 6 · STORES
-- (cameras = { vector4(x, y, z, heading), ... }). With neither, two views
-- are worked out from the counter.
Config.Cameras = {
    Enabled  = true,
    Models   = {
        'prop_cctv_cam_01a', 'prop_cctv_cam_01b', 'prop_cctv_cam_02a', 'prop_cctv_cam_03a',
        'prop_cctv_cam_04a', 'prop_cctv_cam_04b', 'prop_cctv_cam_04c', 'prop_cctv_cam_05a',
        'prop_cctv_cam_06a', 'prop_cctv_cam_07a', 'prop_cs_cctv', 'p_cctv_s',
        'hei_prop_bank_cctv_01', 'hei_prop_bank_cctv_02', 'v_serv_securitycam_03', 'v_serv_securitycam_1a',
    },
    Fov      = 62.0,
    PanSpeed = 0.45,                          -- degrees per frame with A / D
    Filter   = 'scanline_cam_cheap',          -- the camera look; false = none
    Strength = 1.0,
    Sound    = { name = 'Change_Cam', set = 'MP_CCTV_SOUNDSET', ms = 450 },  -- switching; false = none
    PanSound = { name = 'Pan', set = 'MP_CCTV_SOUNDSET' },                   -- panning; false = none
}

-- Store cashiers. false on a store type = no ped, only the prompt.
Config.Peds = {
    Enabled  = true,
    Scenario = 'WORLD_HUMAN_STAND_IMPATIENT', -- what the cashier does while waiting
    Invincible = true,
    SpawnDistance = 60.0,                     -- peds exist only near players
}

-- Use Agency-Notify when it runs, otherwise the store's own toast.
Config.UseAgencyNotify = true


--==============================================================================
--  3 · PAYMENT
--==============================================================================
Config.Payment = {
    Cash = true,                              -- pay with cash
    Bank = true,                              -- pay by card from the bank account
    CardFee = 0,                              -- percent added to card payments (0 = none)
}

-- ── Licenses ─────────────────────────────────────────────────────────────────
-- Store types that check a license before they sell. 'weapon' is the weapon
-- license: QBCore/Qbox metadata.licences.weapon, ESX esx_license ('weapon').
-- Standalone servers have no licenses, so nothing is checked there.
Config.Licenses = {
    Enabled = true,                           -- false = no license needed anywhere
    StoreTypes = {
        ammunation = 'weapon',                -- Ammu-Nation: weapon license
    },
    -- Items anyone may buy in those stores, with or without a license.
    -- Everything else there needs the license (guns, ammunition ...).
    Ignore = {
        'weapon_flashlight', 'weapon_knife', 'weapon_bat', 'weapon_crowbar', 'weapon_hatchet',
        'parachute', 'armor', 'armour',
    },
}

-- ── Agency Pay ───────────────────────────────────────────────────────────────
-- Pay with Agency Pay from Agency Phone: the phone opens its checkout, the
-- player confirms there, the money comes from the bank account. Offered on
-- its own as soon as agency-phone runs. Nothing else to set up.
Config.AgencyPay = {
    Enabled  = true,
    Resource = 'agency-phone',                -- the phone's resource name
    Timeout  = 90,                            -- seconds to confirm on the phone
}


--==============================================================================
--  4 · STORE TYPES
--==============================================================================
-- One entry per kind of store. Every store in 6 · STORES names one of these.
--   label    : the name players see
--   catalog  : which list in 5 · CATALOGS it sells
--   blip     : map icon, false = none
--   ped      : cashier model, false = no cashier
--   price    : what the store costs to buy (8 · OWNERSHIP), false = not for sale
--   mode     : 'ui' or 'realism' for every store of this type (see 6 · STORES).
--              A single store can still override it with its own mode.
Config.StoreTypes = {
    ['247'] = {
        label   = '24/7 Supermarket',
        mode    = 'ui',
        catalog = 'convenience',
        blip    = { sprite = 52, color = 2, scale = 0.7 },
        ped     = 'mp_m_shopkeep_01',
        price   = 185000,
    },
    ['ltd'] = {
        label   = 'LTD Gasoline',
        mode    = 'ui',
        catalog = 'gas_shop',
        blip    = { sprite = 52, color = 5, scale = 0.7 },
        ped     = 'mp_m_shopkeep_01',
        price   = 165000,
    },
    ['liquor'] = {
        label   = "Rob's Liquor",
        mode    = 'ui',
        catalog = 'liquor',
        blip    = { sprite = 93, color = 46, scale = 0.7 },
        ped     = 'mp_m_shopkeep_01',
        price   = 140000,
    },
    ['hardware'] = {
        label   = 'Hardware Store',
        mode    = 'ui',
        catalog = 'hardware',
        blip    = { sprite = 52, color = 47, scale = 0.7 },
        ped     = 's_m_m_autoshop_01',
        price   = 160000,
    },
    ['ammunation'] = {
        label   = 'Ammu-Nation',
        mode    = 'ui',
        catalog = 'ammunation',
        blip    = { sprite = 110, color = 1, scale = 0.7 },
        ped     = 's_m_y_ammucity_01',
        price   = 350000,
    },
}


--==============================================================================
--  5 · CATALOGS
--==============================================================================
-- What a store sells, sorted into categories.
--   name   : item name in your inventory
--   price  : base price (owners can move it inside 8 · OWNERSHIP limits)
--   ox     : optional, the item's name on ox_inventory if it differs
--   label  : optional, otherwise the inventory's label is used
--
-- Items your inventory does not know are hidden automatically and named once
-- in the server console, so a list can hold QBCore, ESX and ox names at once.
-- Category icons: food, drink, snack, alcohol, tobacco, health, tech, tools,
-- car, misc.
Config.Catalogs = {
    ['convenience'] = {
        { id = 'food', icon = 'food', items = {
            { name = 'sandwich',      price = 6 },
            { name = 'tosti',         price = 5 },
            { name = 'burger',        price = 8 },
            { name = 'bread',         price = 3 },
        }},
        { id = 'snacks', icon = 'snack', items = {
            { name = 'twerks_candy',  price = 3 },
            { name = 'snikkel_candy', price = 3 },
        }},
        { id = 'drinks', icon = 'drink', items = {
            { name = 'water_bottle',  price = 2 },
            { name = 'water',         price = 2 },
            { name = 'kurkakola',     price = 3 },
            { name = 'cola',          price = 3 },
            { name = 'sprunk',        price = 3 },
            { name = 'coffee',        price = 4 },
        }},
        { id = 'health', icon = 'health', items = {
            { name = 'bandage',       price = 45 },
            { name = 'painkillers',   price = 80 },
            { name = 'firstaid',      price = 220 },
        }},
        { id = 'misc', icon = 'misc', items = {
            { name = 'lighter',       price = 4 },
            { name = 'rolling_paper', price = 2 },
            { name = 'phone',         price = 650 },
            { name = 'radio',         price = 250 },
            { name = 'binoculars',    price = 90 },
        }},
    },

    ['gas_shop'] = {
        { id = 'food', icon = 'food', items = {
            { name = 'sandwich',      price = 7 },
            { name = 'burger',        price = 9 },
            { name = 'twerks_candy',  price = 3 },
            { name = 'snikkel_candy', price = 3 },
        }},
        { id = 'drinks', icon = 'drink', items = {
            { name = 'water_bottle',  price = 3 },
            { name = 'water',         price = 3 },
            { name = 'kurkakola',     price = 4 },
            { name = 'cola',          price = 4 },
            { name = 'coffee',        price = 5 },
        }},
        { id = 'car', icon = 'car', items = {
            { name = 'weapon_petrolcan',  price = 120, ox = 'WEAPON_PETROLCAN' },
            { name = 'repairkit',         price = 280 },
            { name = 'cleaningkit',       price = 60 },
        }},
        { id = 'misc', icon = 'misc', items = {
            { name = 'lighter',       price = 4 },
            { name = 'bandage',       price = 50 },
        }},
    },

    ['hardware'] = {
        { id = 'tools', icon = 'tools', items = {
            { name = 'screwdriverset',    price = 350 },
            { name = 'lockpick',          price = 200 },
            { name = 'weapon_wrench',     price = 250, ox = 'WEAPON_WRENCH' },
            { name = 'weapon_hammer',     price = 250, ox = 'WEAPON_HAMMER' },
            { name = 'drill',             price = 1200 },
        }},
        { id = 'car', icon = 'car', items = {
            { name = 'repairkit',         price = 250 },
            { name = 'advancedrepairkit', price = 500 },
            { name = 'cleaningkit',       price = 150 },
            { name = 'weapon_petrolcan',  price = 120, ox = 'WEAPON_PETROLCAN' },
        }},
        { id = 'tech', icon = 'tech', items = {
            { name = 'phone',             price = 850 },
            { name = 'radio',             price = 250 },
            { name = 'fitbit',            price = 400 },
            { name = 'binoculars',        price = 50 },
        }},
        { id = 'fireworks', icon = 'star', items = {
            { name = 'firework1',         price = 50 },
            { name = 'firework2',         price = 50 },
            { name = 'firework3',         price = 50 },
            { name = 'firework4',         price = 50 },
        }},
    },

    ['ammunation'] = {
        { id = 'pistols', icon = 'star', items = {
            { name = 'weapon_snspistol',     price = 1800, ox = 'WEAPON_SNSPISTOL' },
            { name = 'weapon_pistol',        price = 2500, ox = 'WEAPON_PISTOL' },
            { name = 'weapon_vintagepistol', price = 3200, ox = 'WEAPON_VINTAGEPISTOL' },
            { name = 'weapon_combatpistol',  price = 3500, ox = 'WEAPON_COMBATPISTOL' },
            { name = 'weapon_heavypistol',   price = 4500, ox = 'WEAPON_HEAVYPISTOL' },
            { name = 'weapon_pistol50',      price = 5000, ox = 'WEAPON_PISTOL50' },
        }},
        { id = 'melee', icon = 'tools', items = {
            { name = 'weapon_flashlight',    price = 150, ox = 'WEAPON_FLASHLIGHT' },
            { name = 'weapon_knife',         price = 300, ox = 'WEAPON_KNIFE' },
            { name = 'weapon_bat',           price = 250, ox = 'WEAPON_BAT' },
            { name = 'weapon_crowbar',       price = 300, ox = 'WEAPON_CROWBAR' },
            { name = 'weapon_hatchet',       price = 400, ox = 'WEAPON_HATCHET' },
        }},
        { id = 'ammo', icon = 'box', items = {
            { name = 'pistol_ammo',          price = 60 },
            { name = 'smg_ammo',             price = 80 },
            { name = 'shotgun_ammo',         price = 90 },
            { name = 'rifle_ammo',           price = 110 },
            { name = 'ammo-9',               price = 4 },
            { name = 'ammo-45',              price = 5 },
            { name = 'ammo-shotgun',         price = 6 },
        }},
        { id = 'protection', icon = 'health', items = {
            { name = 'armor',                price = 1200 },
            { name = 'armour',               price = 1200 },
            { name = 'parachute',            price = 500 },
        }},
    },

    ['liquor'] = {
        { id = 'alcohol', icon = 'alcohol', items = {
            { name = 'beer',          price = 7 },
            { name = 'whiskey',       price = 22 },
            { name = 'vodka',         price = 20 },
        }},
        { id = 'drinks', icon = 'drink', items = {
            { name = 'water_bottle',  price = 2 },
            { name = 'kurkakola',     price = 3 },
        }},
        { id = 'tobacco', icon = 'tobacco', items = {
            { name = 'lighter',       price = 4 },
            { name = 'rolling_paper', price = 2 },
        }},
        { id = 'snacks', icon = 'snack', items = {
            { name = 'twerks_candy',  price = 3 },
            { name = 'snikkel_candy', price = 3 },
        }},
    },
}


--==============================================================================
--  6 · STORES
--==============================================================================
-- 'ui'      = walk to the counter and buy from a menu
-- 'realism' = take a basket or trolley, pick items from the shelves, pay at the
--             counter. Needs shelves: found automatically in the vanilla
--             interiors (7 · REALISM MODE) or placed with /ashops (Points).
-- Which one a store uses, first match wins:
--   1. mode on the store below   2. mode on its type (4 · STORE TYPES)
--   3. Config.DefaultMode
-- Gas stations choose their own mode, see Config.GasStations.Mode.
Config.DefaultMode = 'ui'

-- One line per store.
--   id       : unique, never change it once players own the store
--   type     : a key from 4 · STORE TYPES
--   cashier  : where the cashier stands and the way he faces (toward the customer)
--   mode     : optional, overrides Config.DefaultMode for this store
--   radius   : optional, size of the store in metres (realism mode, default 14)
--   label    : optional, own name instead of the type's
--   price    : optional, own purchase price instead of the type's
--   delivery : optional, where couriers carry the crates (default: the counter)
Config.Stores = {
    -- 24/7
    { id = '247_innocence', type = '247', cashier = vector4(24.47, -1346.62, 29.50, 270.0), mode = 'realism' },
    { id = '247_chumash',   type = '247', cashier = vector4(-3040.47, 585.30, 7.90, 17.0) },
    { id = '247_banham',    type = '247', cashier = vector4(-3243.97, 1000.72, 12.83, 357.0) },
    { id = '247_paleto',    type = '247', cashier = vector4(1727.23, 6416.47, 35.04, 241.0) },
    { id = '247_sandy',     type = '247', cashier = vector4(1959.42, 3740.07, 32.34, 297.0) },
    { id = '247_harmony',   type = '247', cashier = vector4(548.41, 2670.85, 42.16, 99.0) },
    { id = '247_tataviam',  type = '247', cashier = vector4(2677.47, 3280.63, 55.24, 327.0) },
    { id = '247_vinewood',  type = '247', cashier = vector4(372.63, 326.42, 103.57, 253.0) },
    { id = '247_palomino',  type = '247', cashier = vector4(2556.66, 380.84, 108.62, 356.67) },

    -- LTD Gasoline
    { id = 'ltd_davis',     type = 'ltd', cashier = vector4(-46.53, -1757.52, 29.42, 48.0) },
    { id = 'ltd_mirror',    type = 'ltd', cashier = vector4(1165.03, -322.55, 69.21, 100.0) },
    { id = 'ltd_littleseoul', type = 'ltd', cashier = vector4(-706.13, -913.10, 19.22, 88.0) },
    { id = 'ltd_richman',   type = 'ltd', cashier = vector4(-1819.34, 794.06, 138.09, 126.0) },
    { id = 'ltd_grapeseed', type = 'ltd', cashier = vector4(1697.20, 4923.58, 42.06, 323.0) },

    -- Liquor
    { id = 'liquor_murrieta',  type = 'liquor', cashier = vector4(1135.81, -982.28, 46.41, 278.0) },
    { id = 'liquor_vespucci',  type = 'liquor', cashier = vector4(-1222.92, -905.31, 12.33, 30.0) },
    { id = 'liquor_morningwood', type = 'liquor', cashier = vector4(-1486.26, -377.87, 40.16, 139.0) },
    { id = 'liquor_route68',   type = 'liquor', cashier = vector4(1165.98, 2710.86, 38.16, 180.0) },
    { id = 'liquor_sandy',     type = 'liquor', cashier = vector4(1392.56, 3604.68, 34.98, 202.0) },
    { id = 'liquor_banham',    type = 'liquor', cashier = vector4(-2966.42, 391.42, 15.04, 87.0) },

    -- Hardware
    { id = 'hardware_megamall', type = 'hardware', cashier = vector4(45.68, -1749.04, 29.61, 53.13) },
    { id = 'hardware_youtool',  type = 'hardware', cashier = vector4(2747.71, 3472.85, 55.67, 255.08) },
    { id = 'hardware_paleto',   type = 'hardware', cashier = vector4(-421.83, 6136.13, 31.88, 228.2) },

    -- Ammu-Nation
    { id = 'ammu_pillbox',     type = 'ammunation', cashier = vector4(23.00, -1105.67, 29.80, 162.91) },
    { id = 'ammu_hawick',      type = 'ammunation', cashier = vector4(253.63, -51.02, 69.94, 72.91) },
    { id = 'ammu_littleseoul', type = 'ammunation', cashier = vector4(-660.92, -934.10, 21.83, 177.66) },
    { id = 'ammu_morningwood', type = 'ammunation', cashier = vector4(-1304.45, -394.69, 36.70, 74.0) },
    { id = 'ammu_chumash',     type = 'ammunation', cashier = vector4(-3172.49, 1088.75, 20.84, 244.0) },
    { id = 'ammu_lamesa',      type = 'ammunation', cashier = vector4(841.92, -1035.32, 28.19, 1.56) },
    { id = 'ammu_cypress',     type = 'ammunation', cashier = vector4(809.68, -2159.13, 29.62, 1.43) },
    { id = 'ammu_tataviam',    type = 'ammunation', cashier = vector4(2567.48, 292.59, 108.73, 349.68) },
    { id = 'ammu_route68',     type = 'ammunation', cashier = vector4(-1118.59, 2700.05, 18.55, 221.89) },
    { id = 'ammu_sandy',       type = 'ammunation', cashier = vector4(1692.67, 3761.38, 34.71, 227.65) },
    { id = 'ammu_paleto',      type = 'ammunation', cashier = vector4(-331.23, 6085.37, 31.45, 228.02) },
}


--==============================================================================
--  7 · REALISM MODE
--==============================================================================
Config.Realism = {
    -- What the player carries. The first model that exists in the game is used.
    -- A model can be a name (uses the bone/pos/rot/anim below it) or a table
    -- with its own placement, because every model sits differently in the hand.
    --   capacity : how many pieces fit in
    --   bone/pos/rot : where it sits on the ped
    --   anim     : played while carrying (dict, name), false = none
    --
    -- GTA itself has no shopping basket, only shopping bags. The basket below
    -- is bzzz_prop_shop_basket from the free bzzz props that come with
    -- rpemotes: if your server runs rpemotes, players carry a real basket,
    -- otherwise the shopping bag. Any other streamed basket works the same way:
    -- add it on top of the list with its own placement.
    -- The basket hangs beside the right leg from a relaxed arm (the jerry can
    -- carry). bone = 0 is the ped's root: x = right, y = forward, z = up,
    -- so the numbers mean what they say. Fine-tune it in game: /ashops >
    -- Stores > Adjust basket, or /shopfit basket. That is saved for everyone.
    Containers = {
        basket = {
            models = {
                { model = 'bzzz_prop_shop_basket_a', bone = 57005,      -- right hand, by the handle
                  pos = vector3(0.51, -0.07, -0.03), rot = vector3(-48.0, -36.0, 80.0),
                  anim = { dict = 'move_weapon@jerrycan@generic', name = 'idle' } },
                { model = 'bzzz_prop_shop_basket_b', bone = 57005,      -- right hand, by the handle
                  pos = vector3(0.51, -0.07, -0.03), rot = vector3(-48.0, -36.0, 80.0),
                  anim = { dict = 'move_weapon@jerrycan@generic', name = 'idle' } },
                'prop_cs_shopping_bag',
            },
            capacity = 15,
            -- What is taken from the shelves shows up in the basket: up to
            -- cols x rows x layers pieces, each as a small prop (ItemProps).
            fill     = { cols = 3, rows = 2, layers = 2, inset = 0.08, floor = 0.05, layerHeight = 0.08 },
            bone     = 57005,
            pos      = vector3(0.42, 0.0, -0.02),
            rot      = vector3(-90.0, 0.0, 80.0),
            anim     = false,
        },
        trolley = {
            models   = { 'prop_rub_trolley01a', 'prop_rub_trolley02a', 'prop_rub_trolley03a' },
            capacity = 60,
            bone     = 11816,                 -- pelvis, the trolley rolls in front
            pos      = vector3(0.0, 1.05, -0.45),
            rot      = vector3(0.0, 0.0, 180.0),
            anim     = { dict = 'anim@heists@box_carry@', name = 'idle' },
            slowWalk = true,                  -- no sprinting with a trolley
        },
    },

    -- Without an entrance point the first shelf hands out a basket on its own.
    -- Trolleys are only at entrance points (placed with /ashops (Points)..
    AutoBasket = true,

    -- The prop shown in the basket for an item. Items not listed get 'default'.
    -- A model that does not exist on the server falls back to 'default' too.
    ItemProps = {
        default       = 'prop_cs_package_01',
        sandwich      = 'prop_sandwich_01',
        tosti         = 'prop_sandwich_01',
        burger        = 'prop_cs_burger_01',
        bread         = 'v_res_fa_bread03',
        twerks_candy  = 'prop_choc_ego',
        snikkel_candy = 'prop_choc_meto',
        water_bottle  = 'prop_ld_flow_bottle',
        water         = 'prop_ld_flow_bottle',
        kurkakola     = 'prop_ecola_can',
        cola          = 'prop_ecola_can',
        sprunk        = 'ng_proc_sodacan_01b',
        coffee        = 'p_amb_coffeecup_01',
        beer          = 'prop_cs_beer_bot_01',
        whiskey       = 'prop_cs_whiskey_bottle',
        vodka         = 'prop_vodka_bottle',
        bandage       = 'prop_ld_health_pack',
        firstaid      = 'prop_ld_health_pack',
        painkillers   = 'prop_cs_pills',
        lighter       = 'p_cs_lighter_01',
        rolling_paper = 'p_cs_papers_01',
        phone         = 'prop_npc_phone_02',
        radio         = 'prop_cs_hand_radio',
        binoculars    = 'prop_binoc_01',
    },

    -- Shelves in the vanilla store interiors are found by their model, so the
    -- vanilla stores work in realism mode without placing anything. Every
    -- shelf found gets the next category of the store's catalog in turn.
    AutoDetectShelves = true,
    ShelfModels = {
        'v_ret_247shelves01', 'v_ret_247shelves02', 'v_ret_247shelves03',
        'v_ret_247shelves04', 'v_ret_247shelves05', 'v_ret_247_cereal',
        'v_ret_ml_shelfrk', 'v_ret_ml_liqshelfa', 'v_ret_ml_liqshelfb',
        'v_ret_ml_liqshelfc', 'v_ret_ml_liqshelfd', 'v_ret_ml_liqshelfe',
        'v_ret_ml_beerdus', 'v_ret_ml_beeram', 'v_ret_ml_beerpis',
        'v_ret_247_fridge', 'v_ret_fh_fridge', 'v_ret_ml_fridge',
        'v_ret_ml_fridge02', 'prop_ff_shelves_01',
    },

    -- What happens to the basket when the player walks out without paying:
    -- 'return'  = everything goes back on the shelf, nothing is lost
    -- 'steal'   = the player keeps it and the police get an alert (see Dispatch)
    LeaveAction = 'return',

    -- Taking an item plays this short animation.
    PickAnim = { dict = 'mp_common', name = 'givetake1_a', duration = 900 },
}

-- Shoplifting alert. 'auto' = ps-dispatch, cd_dispatch, qs-dispatch or
-- core_dispatch when one runs; 'event' = only fires agency-shop:server:shoplift.
Config.Dispatch = {
    System = 'auto',
    Jobs   = { 'police', 'sheriff' },
}


--==============================================================================
--  8 · OWNERSHIP
--==============================================================================
Config.Ownership = {
    Enabled        = true,                    -- players can buy stores and gas stations
    MaxPerPlayer   = 2,                       -- stores + stations one character may own
    BuyAccount     = 'bank',                  -- paid from 'bank' or 'cash'
    -- Selling to another player: he has to stand this close, and has this
    -- many seconds to accept. Paid from and to BuyAccount.
    TransferDistance = 5.0,
    TransferSeconds  = 30,
    TransferMaxPrice = 10000000,
    SellBackPercent = 60,                     -- percent of the price back when selling to the city

    -- Owners set their own prices inside these limits (x base price).
    MinPriceFactor = 0.6,
    MaxPriceFactor = 2.5,

    -- Takings go into the store's register; owners withdraw them.
    SalesTaxPercent = 5,                      -- cut the city keeps from every sale
    RegisterAccount = 'cash',                 -- register pays out to / takes from 'cash' or 'bank'

    -- Stock. An owned store sells only what was delivered. Stores nobody owns
    -- sell without limit.
    StartStock     = 20,                      -- pieces per item when a store is bought
    MaxStock       = 500,                     -- pieces per item a store can hold

    MaxEmployees   = 6,                       -- staff may order stock and deliver

    -- A store that earned nothing this long goes back on the market.
    -- 0 = never. Counted in real days.
    InactiveDays   = 0,
}


--==============================================================================
--  9 · DELIVERIES
--==============================================================================
-- Owners order goods from the wholesaler. Every order becomes a delivery job:
-- someone drives to the depot, takes the van, loads the crates, drives to the
-- store and carries them in. Only then the stock goes up.
Config.Delivery = {
    Enabled = true,

    -- Who brings the goods:
    -- 'npc'    = an NPC driver, after a while (see Npc below). No depot needed.
    -- 'player' = a delivery job at the depot that players take
    -- 'both'   = the owner chooses for every order
    Mode = 'npc',

    -- What the goods cost from the wholesaler (x base price).
    WholesaleFactor = 0.55,

    -- The courier's pay, part of every order and paid by the store.
    FeePerCrate = 35,
    PiecesPerCrate = 20,                      -- one crate holds this many pieces
    MaxCrates = 10,                           -- a bigger order is split into several jobs

    -- Who may take delivery jobs. Empty = everyone. Owners and their staff may
    -- always deliver to their own store.
    Jobs = {},

    -- The wholesale depot: board with the open jobs, van spawn.
    Depot = {
        coords  = vector4(153.68, -3211.88, 5.91, 274.5),
        vehicle = vector4(141.12, -3204.31, 5.85, 267.5),
        blip    = { sprite = 478, color = 5, scale = 0.75 },
        ped     = 's_m_m_dockwork_01',
    },

    Vehicle = 'speedo',                       -- the delivery van
    VehiclePlate = 'SHOP',                    -- first letters of the plate
    Crate = {
        model = 'prop_cs_cardbox_01',
        bone  = 60309,
        pos   = vector3(0.025, 0.08, 0.255),
        rot   = vector3(-145.0, 290.0, 0.0),
        anim  = { dict = 'anim@heists@box_carry@', name = 'idle' },
    },
    TimeLimit = 30,                           -- minutes before a taken job goes back on the board
    ReturnVan = true,                         -- the van must be brought back to the depot

    -- NPC delivery (Mode 'npc' or 'both'): the stock arrives after a while.
    -- Players close to the store see the van drive up and the driver carry the
    -- crates in.
    Npc = {
        FeeMultiplier = 1.0,                  -- x the normal courier pay (e.g. 1.5 with Mode 'both')
        BaseMinutes = 2,                      -- every NPC run takes at least this long
        MinutesPerKm = 1.0,                   -- plus this per km from the depot
        Vehicle = 'mule',                     -- van for goods
        FuelVehicle = 'phantom',              -- truck for fuel
        Driver = 's_m_m_ups_01',
    },
}


--==============================================================================
--  10 · GAS STATIONS
--==============================================================================
Config.GasStations = {
    Enabled = true,

    -- How filling up works. A station in Stations below can override it with
    -- its own mode = '...'.
    -- 'simple'  = choose fuel and amount at the pump, pay, the tank fills while
    --             the player stands at the pump
    -- 'realism' = the nozzle comes off the pump on a hose, the player puts it
    --             into the fuel cap, the tank fills, then the nozzle goes back
    Mode = 'realism',

    -- Pumps are found by their model anywhere on the map.
    PumpModels = {
        'prop_gas_pump_1a', 'prop_gas_pump_1b', 'prop_gas_pump_1c', 'prop_gas_pump_1d',
        'prop_vintage_pump', 'prop_gas_pump_old2', 'prop_gas_pump_old3',
    },

    -- Fuel types. price = per litre. Remove a line to drop that type.
    FuelTypes = {
        { id = 'regular', price = 1.65 },
        { id = 'premium', price = 1.95 },
        { id = 'diesel',  price = 1.75 },
        { id = 'electric', price = 0.55, electricOnly = true },
    },

    -- Fuel prices that move. Every few minutes each fuel type goes up or down a
    -- little (price above = the middle it swings around). Every station also
    -- sits a bit above or below the others, always the same. Owned stations
    -- keep the prices their owner set; the wholesale price for the tanker
    -- follows the market.
    Market = {
        Enabled     = true,
        Interval    = 30,                     -- minutes between price changes
        MaxChange   = 0.05,                   -- at most 5 % per change
        Range       = { 0.80, 1.30 },         -- never below 80 % or above 130 % of the price
        Pull        = 0.10,                   -- how strongly it drifts back to the price
        StationSpread = 0.06,                 -- stations differ by up to +/- 6 %
    },

    -- Litres per full tank by vehicle class (GetVehicleClass). Everything else: Default.
    TankSize = {
        Default = 60,
        [0] = 45,  [1] = 55,  [2] = 80,  [3] = 55,  [4] = 70,  [5] = 60,
        [6] = 65,  [7] = 70,  [8] = 18,  [9] = 85,  [10] = 300, [11] = 120,
        [12] = 90, [17] = 120, [18] = 90, [20] = 300,
    },

    -- Holding a jerry can (WEAPON_PETROLCAN) at a pump fills it for this many
    -- litres of the given type. false = no refills at the pump.
    JerryCan = { litres = 20, type = 'regular' },

    RefuelSpeed = 1.6,                        -- litres per second at the pump
    SoundVolume = 0.1,                        -- nozzle sounds (0.0 - 1.0), 0 = off
    Nozzle = {
        model = 'prop_cs_fuel_nozle',
        bone  = 60309,                        -- left hand
        pos   = vector3(0.055, 0.05, 0.0),
        rot   = vector3(-50.0, -90.0, -50.0),
    },
    Hose = true,                              -- a black hose from the pump to the nozzle
    HoseLength = 7.0,                         -- metres before the hose tears off
    CapText = true,                           -- text with the key above the fuel cap
    -- Height of the hose outlet per pump model (metres above the pump's foot).
    HoseHeight = {
        prop_gas_pump_1a = 2.3, prop_gas_pump_1b = 2.3, prop_gas_pump_1c = 2.3, prop_gas_pump_1d = 2.3,
        prop_vintage_pump = 1.8, prop_gas_pump_old2 = 1.6, prop_gas_pump_old3 = 1.6,
    },
    Anim = { dict = 'timetable@gardener@filling_can', name = 'gar_ig_5_filling_can' },

    -- Electric models: only 'electric' at the pump, and no other type for them.
    ElectricModels = {
        'voltic', 'voltic2', 'neon', 'raiden', 'cyclone', 'tezeract', 'surge',
        'dilettante', 'dilettante2', 'khamelion', 'imorgon', 'iwagen', 'omnisegt',
        'virtue', 'buffalo5', 'powersurge', 'envisage', 'caddy', 'caddy2', 'caddy3',
        'airtug', 'rcbandito',
    },

    -- Stations that can be bought. Pumps within `radius` belong to it; pumps
    -- outside every station are public and sell at FuelTypes prices.
    --   id     : unique, never change it once players own the station
    --   coords : centre of the station
    --   price  : purchase price, false = not for sale
    Ownable = true,
    DefaultPrice = 220000,
    StartFuel = 2000,                         -- litres in the tanks when a station is bought
    MaxFuel = 20000,                          -- litres the tanks hold
    Stations = {
        { id = 'gas_grove',      coords = vector3(-70.2, -1761.8, 29.53) },
        { id = 'gas_strawberry', coords = vector3(265.6, -1261.3, 29.29) },
        { id = 'gas_lamesa',     coords = vector3(819.7, -1028.6, 26.40) },
        { id = 'gas_elburro',    coords = vector3(1208.6, -1402.6, 35.23) },
        { id = 'gas_mirror',     coords = vector3(1181.4, -330.5, 69.32) },
        { id = 'gas_downtown',   coords = vector3(620.8, 269.0, 103.09) },
        { id = 'gas_littleseoul', coords = vector3(-724.6, -935.2, 19.21) },
        { id = 'gas_innocence',  coords = vector3(-526.0, -1211.0, 18.18) },
        { id = 'gas_davis',      coords = vector3(176.6, -1562.0, 29.26) },
        { id = 'gas_vespucci',   coords = vector3(-319.3, -1471.6, 30.55) },
        { id = 'gas_morningwood', coords = vector3(-1437.6, -276.8, 46.21) },
        { id = 'gas_pacific',    coords = vector3(-2096.2, -320.3, 13.17) },
        { id = 'gas_richman',    coords = vector3(-1799.1, 803.7, 138.65) },
        { id = 'gas_tataviam',   coords = vector3(2581.4, 362.1, 108.47) },
        { id = 'gas_route68',    coords = vector3(49.4, 2778.8, 58.00) },
        { id = 'gas_harmony',    coords = vector3(263.9, 2606.5, 44.98) },
        { id = 'gas_grandsenora', coords = vector3(1039.9, 2671.1, 39.55) },
        { id = 'gas_route68east', coords = vector3(1207.3, 2660.8, 37.90) },
        { id = 'gas_senora',     coords = vector3(2539.7, 2594.4, 38.00) },
        { id = 'gas_sandy',      coords = vector3(2005.6, 3773.9, 32.40) },
        { id = 'gas_sandyairfield', coords = vector3(1784.3, 3330.3, 41.24) },
        { id = 'gas_tataviam2',  coords = vector3(2679.9, 3263.9, 55.24) },
        { id = 'gas_grapeseed',  coords = vector3(1687.2, 4929.4, 42.08) },
        { id = 'gas_paleto',     coords = vector3(1701.3, 6416.1, 32.76) },
        { id = 'gas_paleto2',    coords = vector3(179.9, 6602.8, 31.87) },
        { id = 'gas_paleto3',    coords = vector3(-94.5, 6419.5, 31.49) },
        { id = 'gas_zancudo',    coords = vector3(-2554.6, 2334.1, 33.08) },
        { id = 'gas_marina',     coords = vector3(-735.88, -1348.16, 1.60), label = 'Puerto del Sol Marina' },
    },
    StationRadius = 32.0,
    Blip = { sprite = 361, color = 1, scale = 0.6 },

    -- Fuel deliveries for owned stations: a tanker from the depot.
    Delivery = {
        WholesalePerLitre = 0.85,
        FeePerThousand = 120,                 -- courier pay per 1000 litres
        MaxLitres = 8000,                     -- per tanker run
        Vehicle = 'phantom',
        Trailer = 'tanker',                   -- false = the truck alone
        UnloadSeconds = 25,
    },
}


--==============================================================================
--  11 · FUEL SYSTEM
--==============================================================================
-- Who keeps track of the fuel level.
-- 'auto'     = LegacyFuel, ox_fuel, cdn-fuel, ps-fuel, lc_fuel, Renewed-Fuel or
--              okokGasStation when one runs, otherwise 'builtin'
-- 'builtin'  = Agency Shops burns fuel itself (no other fuel script needed)
-- 'none'     = only GTA's own fuel level, nothing burns
-- Running another fuel script: switch off its own pump interaction, or both
-- answer the same key at the pump.
Config.FuelSystem = 'auto'

Config.BuiltinFuel = {
    -- Litres per minute at full throttle, by class. Idle uses a fraction.
    Consumption = 1.0,                        -- overall multiplier
    IdleFactor  = 0.08,
    ClassFactor = {
        [13] = 0.0, [14] = 0.6, [15] = 1.4, [16] = 1.6, [21] = 0.0,  -- cycles, boats, heli, planes, trains
    },
    LowFuelWarning = 12,                      -- percent, a notice once below it
    StallWhenEmpty = true,                    -- the engine dies at 0
}


--==============================================================================
--  12 · ADMIN & EDITOR
--==============================================================================
-- /ashops opens the admin panel. Everything there is saved in the database
-- and laid over this file, so nothing here has to be edited by hand:
--   Catalogs  categories, icons and products with their prices
--   Stores    name, catalog, mode (menu / realism) and price of every store
--   Points    shelves, entrances and delivery points, owners, stock
-- "Reset" in the panel brings back what this file says.
Config.Admin = {
    Command = 'ashops',
    Ace = 'agency.shop',                      -- add_ace group.admin agency.shop allow
    Groups = { 'admin', 'god', 'superadmin' }, -- QBCore permissions / ESX groups
}


--==============================================================================
--  13 · STANDALONE
--==============================================================================
-- Used with Config.Framework = 'standalone', or 'auto' with no framework.
--   Money: 'auto' takes Agency-Banking when it runs, otherwise a simple wallet
--   saved per Rockstar licence. 'hooks' = the functions below.
--   Items: without ox_inventory or qs-inventory, fill in the item hooks, or
--   only weapons reach the player. Every purchase also fires the server event
--   agency-shop:server:purchased (source, items) for your own scripts.
Config.Standalone = {
    money = 'auto',                           -- 'auto' | 'banking' | 'wallet' | 'hooks'
    startCash = 500,                          -- wallet cash of a new player
    admins = {},                              -- licences counted as admin: { 'license:abc...' }

    getMoney    = nil,  -- function(source, account) return balance end
    removeMoney = nil,  -- function(source, account, amount) return true end
    addMoney    = nil,  -- function(source, account, amount) return true end
    itemCount   = nil,  -- function(source, item) return count end
    addItem     = nil,  -- function(source, item, amount) return true end
    removeItem  = nil,  -- function(source, item, amount) return true end
}


--==============================================================================
--  14 · ROBBERY
--==============================================================================
-- Aim a gun at the cashier for a few seconds and the robbery starts: the
-- alarm goes off, the police are alerted, and the money is in the safe in the
-- back, which has to be cracked. An owned store also loses a small part of
-- its register.
Config.Robbery = {
    Enabled    = true,
    AimSeconds = 4,                           -- aiming at the cashier this long starts it
    Exclude    = { 'ammunation' },            -- store types that cannot be robbed
    Timeout    = 300,                         -- seconds to crack the safe before it is over

    -- Cooldowns in minutes. 0 = none.
    Cooldown = {
        Store  = 30,                          -- the same store again
        Global = 5,                           -- any store on the server again
        Player = 20,                          -- the same player again
    },

    -- Who may start a robbery. AllowedJobs empty = everyone (except the
    -- blocked jobs). With jobs listed, only those may (e.g. gangs).
    AllowedJobs = {},
    BlockedJobs = {},                         -- e.g. { 'police', 'sheriff', 'ambulance' }

    -- Which weapons count. 'all' = any weapon, melee too. {} = every firearm.
    -- With weapons listed only those, melee weapons included if you list them.
    Weapons = 'all',
    -- Weapons = { 'WEAPON_PISTOL', 'WEAPON_COMBATPISTOL', 'WEAPON_KNIFE' },

    MinPolice  = 0,                           -- police on duty needed
    PoliceJobs = { 'police', 'sheriff' },

    -- 'auto' = ps-dispatch or cd_dispatch when one runs, else the built-in
    -- alert (a message and a flashing blip for every police officer).
    -- 'builtin' | 'ps-dispatch' | 'cd_dispatch' | 'none'
    Dispatch   = 'auto',

    Safe = {
        -- The cracking game: turn the dial (A / D), stop where it clicks and
        -- set the number (SPACE). A wrong number costs time.
        Minigame  = true,
        Numbers   = 3,                        -- numbers in the combination
        TimeLimit = 60,                       -- seconds for the whole combination
        CrackSeconds = 30,                    -- without the game: a plain progress bar this long
        -- The safe in the store's back office. GTA's office safes are part of
        -- the building, so they are listed by position (the vanilla stores).
        -- Other stores: a "Safe" point with /ashops (Points), or a safe prop
        -- found by model. No safe, no robbery.
        Spots = {
            vector3(-43.43, -1748.3, 29.42),
            vector3(-1478.94, -375.5, 39.16),
            vector3(-1220.85, -916.05, 11.32),
            vector3(-709.74, -904.15, 19.21),
            vector3(28.21, -1339.14, 29.49),
            vector3(1126.77, -980.1, 45.41),
            vector3(1159.46, -314.05, 69.2),
            vector3(378.17, 333.44, 103.56),
            vector3(-1829.27, 798.76, 138.19),
            vector3(-2959.64, 387.08, 14.04),
            vector3(-3047.88, 585.61, 7.9),
            vector3(-3250.02, 1004.43, 12.83),
            vector3(546.41, 2662.8, 42.15),
            vector3(1169.31, 2717.79, 37.15),
            vector3(2672.69, 3286.63, 55.24),
            vector3(1959.26, 3748.92, 32.34),
            vector3(1734.78, 6420.84, 35.03),
            vector3(-168.40, 6318.80, 30.58),
            vector3(168.95, 6644.74, 31.70),
        },
        Models = { 'prop_ld_int_safe_01', 'p_v_43_safe_s', 'v_ilev_gangsafe', 'v_ilev_gangsafedoor', 'prop_ld_int_safe_02', 'prop_ld_safe_01' },
    },

    -- What the safe holds. item = false pays into the account; with an item
    -- name (e.g. 'markedbills') the amount is given as that many items.
    Reward = { min = 1500, max = 4500, account = 'cash', item = false },

    -- An owned store loses this part of its register (percent, at most Max).
    OwnerLoss = { Percent = 5, Max = 2500 },
}

