--[[ ╔══════════════════════════════════════════════════════════════════╗ ║ AGENCY VEHICLE SHOP ║ ║ Premium Buy & Sell Car Dealership ║ ║ (c) Agency Scripts ║ ╠══════════════════════════════════════════════════════════════════╣ ║ Plug & Play · QBCore / ESX / Standalone · Multi-Phone ready ║ ╚══════════════════════════════════════════════════════════════════╝ This file is the ONLY thing you need to touch as a server owner. Everything is commented. No coding knowledge required. Framework options (Config.Framework): 'auto' -> Auto-detect QBCore or ESX (recommended) 'qb' -> Force QBCore 'esx' -> Force ESX 'standalone' -> No framework. Vehicles are managed by this script. ]] Config = {} -- ===================================================================== -- AGENCY SCRIPTS INTEGRATION ⭐ -- ===================================================================== -- Official integration with other Agency Scripts. -- true = Always use (throws error if script is missing) -- false = Never use (even if installed) -- 'auto' = Auto-detect if the script is running on the server (recommended) -- Config.AgencyNotify = 'auto' -- Agency-Notify → Premium notifications Config.AgencyGarage = 'auto' -- Agency-Garage → Bought vehicles appear directly in the garage Config.AgencyVehiclekeys = 'auto' -- Agency-Vehiclekeys → Receive keys automatically after purchase -- ===================================================================== -- FRAMEWORK & AUTO-DETECTION -- ===================================================================== Config.Framework = 'auto' -- 'auto' | 'qb' | 'esx' | 'standalone' Config.QBCoreResource = 'qb-core' Config.ESXResource = 'es_extended' Config.Debug = true -- TEMP: set to false after debugging Config.AutoSetupDatabase = true -- creates standalone table / ESX columns on start -- ===================================================================== -- LANGUAGE -- ===================================================================== Config.Locale = 'en' -- 'en','de','fr','es','pt','it','nl','da','sv','no','fi','pl','cs','sk','hu','ro','bg','tr','ru','el','ar','zh','ja','ko' (texts live in locales.lua) -- ===================================================================== -- BRIDGES (auto-detected — only force if needed) -- ===================================================================== -- These settings are ONLY used if the Agency integrations above -- are NOT active. With 'auto', Agency is checked first, then these. Config.Notify = 'auto' -- 'auto' | 'ox' | 'qb' | 'esx' | 'native' Config.TextUI = 'auto' -- 'auto' | 'ox' | 'jg' | 'qb' | 'native' Config.VehicleKeys = 'auto' -- 'auto' | 'qb' | 'qs' | 'wasabi' | 'mk' | 'none' -- ===================================================================== -- MULTI-PHONE INTEGRATION ⭐ -- ===================================================================== -- A bought vehicle is inserted into the standard table (player_vehicles for -- QB, owned_vehicles for ESX) with state = 1 (stored). Every phone garage app -- and any garage script then sees the new car automatically. Config.PhoneSync = true Config.PhoneSyncEvent = 'vehicleshop:vehiclePurchased' -- ===================================================================== -- VEHICLE CATALOG ⭐ (where the buyable cars come from) -- ===================================================================== -- 'auto' -> use qb-core/shared/vehicles.lua if it exists, else the Config.Vehicles list -- 'qbcore' -> always read qb-core/shared/vehicles.lua (model/name/brand/price/category/shop) -- 'config' -> always use the Config.Vehicles list below (best for ESX / standalone) -- On QBCore you do NOT have to list any vehicles — they are all loaded for you. Config.VehicleSource = 'auto' -- Manual catalog (used when source = 'config' or as a fallback). Add your own here. -- shop = which shop it shows up in (matches a Config.Shops 'type') Config.Vehicles = { { model = 'sultan', name = 'Sultan', brand = 'Karin', price = 12000, category = 'sports', shop = 'pdm' }, { model = 'kuruma', name = 'Kuruma', brand = 'Karin', price = 95000, category = 'sports', shop = 'pdm' }, { model = 'futo', name = 'Futo', brand = 'Karin', price = 9000, category = 'sports', shop = 'pdm' }, { model = 'adder', name = 'Adder', brand = 'Truffade', price = 1000000, category = 'super', shop = 'luxury' }, { model = 'zentorno', name = 'Zentorno', brand = 'Pegassi', price = 725000, category = 'super', shop = 'luxury' }, { model = 'bati', name = 'Bati 801', brand = 'Pegassi', price = 15000, category = 'motorcycles', shop = 'moto' }, } -- ===================================================================== -- BUYING & SELLING -- ===================================================================== -- Payment methods the player can choose from IN the shop menu. -- 'bank' = card, 'cash' = cash. Both enabled = player picks in the UI. Config.PaymentMethods = { 'bank', 'cash' } -- available: 'bank', 'cash' Config.DefaultPayment = 'bank' -- pre-selected method when the shop opens -- AgencyPay (phone wallet) checkout. The purchase is verified SERVER-SIDE via -- agency-phone's VerifyAgencyPayCharge export, so a forged result cannot grant a -- free vehicle. Requires agency-phone to be running. Config.EnableAgencyPay = true Config.Sell = { enabled = true, account = 'bank', -- where the money goes when selling returnPercent = 50, -- % of the catalog price you get back when selling } -- ===================================================================== -- DEALERSHIP OWNERSHIP ⭐ (players can buy & own shops as a business) -- ===================================================================== Config.Ownership = { enabled = true, -- master switch for the ownership system maxPerPlayer = 2, -- max dealerships one player can own sellBackPercent = 50, -- % of purchasePrice you get back when selling the business defaultCommission = 0.10, -- 10% of each vehicle sale goes into the business account maxEmployees = 10, -- max employees per dealership ranks = { { name = 'owner', label = 'Owner', canHire = true, canFire = true, canWithdraw = true }, { name = 'manager', label = 'Manager', canHire = true, canFire = true, canWithdraw = false }, { name = 'employee', label = 'Employee', canHire = false, canFire = false, canWithdraw = false }, }, -- ===== STOCK & SUPPLY (only applies to OWNED dealerships) ===== -- An UNOWNED dealership sells everything at the normal price with infinite -- stock. The moment a player buys it the showroom starts EMPTY — the owner -- has to order vehicles wholesale (an up-front investment), drive a delivery -- run for each one, and only then can sell them to customers at the normal -- (higher) price. The margin lands in the business account. stock = { enabled = true, maxOrderAtOnce = 5, -- max units that can be ordered in one order (your "max 5") wholesaleFactor = 0.55, -- order price = 55% of catalog price → ~45% margin on resale maxPerModel = 10, -- max delivered units a dealership can hold per model requireDelivery = true, -- ordered units must be delivered before they can be sold }, } -- ===================================================================== -- DELIVERY MISSIONS ⭐ (owners drive ordered vehicles to the dealership) -- ===================================================================== -- After ordering stock, ONE delivery run is required per ordered unit. The -- driver picks the vehicle up at one of the warehouse points below and drives -- it to the dealership's spawn point. The game alternates pickup points so the -- route differs each time. Fully configurable — add/remove points freely. Config.Delivery = { payOnComplete = 0, -- optional extra cash per delivery (profit normally comes from sales) pickupMarker = { type = 1, size = 3.0, color = { 0, 212, 255, 140 } }, dropMarker = { type = 1, size = 3.0, color = { 0, 255, 136, 140 } }, -- Transporter vehicles used when ordering multiple vehicles at once truckSmall = 'speedo', -- Changed from 'mule' because mule is often blacklisted truckLarge = 'benson', -- Changed from 'pounder' because pounder is often blacklisted pickups = { vector4(1730.03, 3307.72, 41.22, 195.0), -- Sandy Shores Airfield (Runway) vector4(-192.51, 6224.71, 31.49, 134.42), -- Paleto Bay (Open rest stop parking) vector4(-2184.28, 4272.78, 49.03, 130.0), -- Highway 1 parking (near Zancudo) vector4(-341.36, -2600.32, 6.0, 275.0), -- Elysian Island (Open container lot) vector4(-1058.46, -2824.93, 27.7, 325.0), -- LSIA (Outer gate open parking) vector4(2112.35, 1925.15, 78.4, 130.0), -- Wind Farm (Open dirt track) vector4(-3173.34, 1079.22, 20.83, 240.0), -- Chumash (Open dirt lot next to highway) vector4(-392.21, 1177.3, 325.64, 340.0), -- Galileo Observatory (Empty parking lot) vector4(695.53, 580.4, 130.4, 270.0), -- Vinewood Bowl (Large parking area) vector4(-1203.25, -1355.22, 4.3, 120.0), -- Vespucci Beach (Giant open parking lot) vector4(-1678.5, -960.5, 7.6, 135.0), -- Del Perro Pier (Parking lot) vector4(890.3, -2200.5, 30.5, 180.0), -- Cypress Flats (Industrial open road) vector4(1420.4, -1890.6, 73.0, 290.0), -- El Burro Heights (Oil fields dirt track) vector4(1950.4, 3770.6, 32.2, 210.0), -- Harmony / Route 68 (Dirt parking) vector4(-735.6, 5835.4, 17.3, 225.0), -- Mount Chiliad (Cable car parking at bottom) vector4(2450.4, 4950.6, 41.5, 45.0), -- Grapeseed (Farms dirt road) vector4(-545.5, 5320.5, 74.5, 340.0), -- Paleto Forest (Lumber yard open space) }, } -- ===================================================================== -- SHOWCARS ⭐ (/avehicle — staff place display vehicles on fixed spots) -- ===================================================================== -- Anyone with the internal job at a dealership can drive a car onto one of its -- showcar spots and run /avehicle to display it (frozen showroom piece). Run -- /avehicle clear on a spot to empty it. Customers just look — no buy here. Config.Showcars = { enabled = true, command = 'avehicle', -- chat command for staff freeze = true, -- display vehicles are frozen & invincible setRange = 4.0, -- how close a spot must be to set/clear it -- Display spots per shop id. Add as many vector4(x,y,z,heading) as you like. spots = { pdm = { vector4(-43.92, -1097.62, 26.42, 25.0), vector4(-47.83, -1100.30, 26.42, 25.0), vector4(-51.74, -1102.98, 26.42, 25.0), }, luxury = { vector4(133.2, -138.5, 54.85, 250.0), vector4(129.6, -141.0, 54.85, 250.0), }, moto = { vector4(-1250.6, -351.2, 36.91, 120.0), }, planes = {}, boats = {}, }, } Config.TestDrive = { enabled = true, duration = 45, -- seconds, then the test car despawns and you return stopKey = 73, -- [X] key to end the test drive early routingBucket = true, -- true = puts player in a private world during test drive hudPosition = 'bottom-center', -- 'top-left', 'top-center', 'top-right', 'bottom-left', 'bottom-center', 'bottom-right' spawn = vector4(-1732.17, -2900.2, 13.94, 330.0), -- Default okok Test Track (Airport) } -- ===================================================================== -- GENERAL BEHAVIOUR -- ===================================================================== Config.InteractKey = 38 -- [E] Config.UseTarget = false -- true = ox_target / qb-target instead of marker+key Config.DrawDistance = 25.0 Config.InteractDistance = 2.5 Config.SpawnInVehicle = true -- sit in the car after buying Config.WarpDelay = 250 -- MARKER (animated icon at the shop) Config.Marker = { type = 36, size = 0.55, color = { 255, 215, 0, 200 }, -- yellow bob = false, rotate = true, pulse = false, zOffset = 0.0, } -- SELL MARKER (where you sell your car) Config.SellMarker = { type = 36, -- 36 = Car icon marker size = 0.6, color = { 255, 50, 50, 200 }, -- vibrant red bob = false, rotate = true, pulse = false, zOffset = 0.0, } -- BOSS MARKER (management menu access) Config.BossMarker = { type = 36, -- 36 = car icon on the ground (like the shop marker) size = 0.55, color = { 0, 212, 255, 200 }, -- cyan = management (only staff see it) bob = false, rotate = true, pulse = true, zOffset = 0.0, } -- OPEN HINT Config.OpenHint = { mode = 'notify' } -- 'notify' | 'textui' | 'both' -- ===================================================================== -- VEHICLE PREVIEW (3D showroom camera, mouse-drag to rotate) -- ===================================================================== Config.Preview = { enabled = true, autoRotate = false, -- false = drag with the mouse · true = spins by itself mouseSensitivity = 0.35, rotateSpeed = 18.0, heightOffset = 25.0, -- fallback only hotspots = true, -- show interactive dots (doors, hood, trunk) on the preview car } -- ===================================================================== -- VEHICLE IMAGES FOR THE UI -- ===================================================================== -- 'spawn' -> html/img/vehicles/.png (e.g. adder.png) -- 'class' -> one image per category -- 'single' -> always default.png -- Missing image -> clean placeholder icon (never a broken image). Config.VehicleImages = 'spawn' -- ===================================================================== -- BLIPS -- ===================================================================== Config.Blips = { enabled = true } -- ===================================================================== -- CATEGORY DISPLAY NAMES (GTA vehicle classes) -- ===================================================================== Config.CategoryLabels = { compacts = 'Compacts', sedans = 'Sedans', suvs = 'SUVs', coupes = 'Coupes', muscle = 'Muscle', sportsclassics = 'Classics', sports = 'Sports', super = 'Super', motorcycles = 'Motorcycles', offroad = 'Off-Road', industrial = 'Industrial', utility = 'Utility', vans = 'Vans', cycles = 'Cycles', boats = 'Boats', helicopters = 'Helicopters', planes = 'Planes', service = 'Service', emergency = 'Emergency', military = 'Military', commercial = 'Commercial', } -- ===================================================================== -- SHOPS (positions imported from the old shop) -- ===================================================================== -- type: matches the 'shop' field of the vehicles (which cars are shown) -- access: vector3 marker to open the shop -- spawn: vector4 where the preview / bought car appears -- sell: vector3 sell point (drive your car here, press [E] to sell) Config.Shops = { ['pdm'] = { label = 'Premium Deluxe Motorsport', type = 'pdm', access = vector3(-57.5, -1096.73, 26.42), spawn = vector4(-47.4, -1093.4, 26.42, 150.0), spawns = { vector4(-47.4, -1093.4, 26.42, 150.0), vector4(-50.8, -1089.2, 26.42, 150.0), }, sell = vector3(-45.3, -1082.91, 26.27), blip = { sprite = 225, color = 3, scale = 0.9 }, -- Ownership ownable = true, purchasePrice = 1000000, bossMenu = vector3(-55.0, -1098.0, 26.42), deliveryDrop = vector4(-31.84, -1090.72, 26.42, 340.0), -- Default drop for PDM }, ['moto'] = { label = 'Motorcycle Shop', type = 'moto', access = vector3(-1253.95, -349.37, 36.91), spawn = vector4(-1245.0, -353.0, 36.9, 30.0), spawns = { vector4(-1245.0, -353.0, 36.9, 30.0), vector4(-1242.0, -351.0, 36.9, 30.0), vector4(-1238.0, -348.0, 36.9, 30.0), }, sell = vector3(-1242.49, -345.42, 37.33), blip = { sprite = 226, color = 3, scale = 0.8 }, -- Ownership ownable = true, purchasePrice = 500000, bossMenu = vector3(-1250.0, -349.0, 36.91), deliveryDrop = vector4(-1256.6403, -335.6497, 36.9134, 299.5139), }, ['planes'] = { label = 'Airplane Shop', type = 'planes', access = vector3(-949.5, -2946.55, 13.95), spawn = vector4(-975.0, -2965.0, 13.95, 60.0), spawns = { vector4(-975.0, -2965.0, 13.95, 60.0), }, sell = vector3(-959.5, -2946.55, 12.76), blip = { sprite = 359, color = 3, scale = 0.8 }, -- Ownership ownable = true, purchasePrice = 3000000, bossMenu = vector3(-955.0, -2943.0, 13.95), deliveryDrop = vector4(-975.0, -2965.0, 13.95, 60.0), }, ['boats'] = { label = 'Boat Shop', type = 'boats', access = vector3(-720.77, -1324.92, 1.6), spawn = vector4(-735.0, -1338.0, 0.2, 140.0), spawns = { vector4(-735.0, -1338.0, 0.2, 140.0), }, sell = vector3(-721.56, -1306.7, 3.82), blip = { sprite = 427, color = 3, scale = 0.8 }, -- Ownership ownable = true, purchasePrice = 1500000, bossMenu = vector3(-724.0, -1322.0, 1.6), deliveryDrop = vector4(-735.0, -1338.0, 0.2, 140.0), }, } -- ===================================================================== -- BRANDING (subtle — paid script) -- ===================================================================== Config.Branding = { showFooter = true, label = 'Agency Vehicle Shop' } -- ===================================================================== -- TEXT / TRANSLATIONS -- ===================================================================== -- ===================================================================== -- TEXT / TRANSLATIONS -- ===================================================================== -- All translatable strings live in locales.lua (24 languages) and are -- loaded right after this file (see fxmanifest.lua). Edit texts there.