Skip to content

🚀 AFU.VehicleShop

ระบบร้านขายยานพาหนะแบบ 3D Marker + NPC + Blip ผู้เล่นเดินเข้าใกล้ร้านแล้วกดปุ่มเปิดร้าน เลือกดูรถแบบ 360° ใน UI ปรับสีได้ ทดลองขับ (Test Drive) ได้ก่อนซื้อ รองรับหลายตำแหน่งต่อร้าน จำกัดอาชีพที่เข้าร้านได้ จำกัดจำนวนรถที่ขายได้ผ่าน Database และให้ Admin แจกรถผ่านคำสั่ง

ข้อมูลสคริปต์

  • Framework: ESX เท่านั้น (es_extended)
  • Dependencies:
    • es_extendedบังคับ
    • oxmysqlบังคับ
    • AFU.Toastify — แนะนำ (ใช้แสดง Hint "กด E เพื่อเข้าสู่ร้านค้ารถ")
    • unknow_notify — แนะนำ (ใช้แสดง Notification แจ้งเตือนต่างๆ)
    • AFU.VehicleKeys — แนะนำ (ซิงค์กุญแจให้ผู้เล่นหลังซื้อ/รับรถ)
    • AFU.Garage — แนะนำ (รีเฟรชรถในโรงจอดรถหลังซื้อ/รับรถ)
    • nc_discordlogs — ไม่บังคับ (ส่งบันทึกการซื้อขายรถไป Discord)
  • เวอร์ชัน: 2.8.5 · fx_version cerulean · lua54

⚠️ ต้องใช้ License ที่ถูกต้อง

สคริปต์นี้มีระบบตรวจสอบ License ผู้ซื้อต้องมี Token/License ที่ถูกต้อง จากผู้พัฒนา หากไม่มี License ที่ถูกต้อง Resource จะไม่ทำงาน

ซื้อ Token ได้ที่ awayfromus.store


🛠️ Installation

1. Download & Placement

นำ Resource ไปวางในโฟลเดอร์ resources/ ของเซิร์ฟเวอร์:

text
resources/
└── AFU.VehicleShop/
    ├── Config/        ← ✅ โฟลเดอร์เดียวที่คุณสามารถแก้ไขได้
    │   ├── settings.config.lua          (ตั้งค่าหลัก: ปุ่ม, ฟอนต์, ภาษี, Test Drive, Marker)
    │   ├── commands.config.lua          (ชื่อคำสั่ง Admin)
    │   ├── secure.config.lua            (License / Admin List / Event Framework)
    │   ├── queries.config.lua           (SQL Queries + Discord Log)
    │   ├── notify.config.lua            (ข้อความ Notification)
    │   ├── functions.config.lua         (Hooks ฝั่ง Client)
    │   ├── suspects.config.lua          (Hooks ตรวจพบพฤติกรรมผิดปกติ)
    │   └── Shop/                        (ไฟล์ร้าน — 1 ไฟล์ = 1 ร้าน)
    ├── nui/           ← 🔒 UI (ห้ามแก้ไข)
    ├── Source/        ← 🔒 ไฟล์หลักเข้ารหัสลับ (ห้ามแก้ไข)
    ├── sql/           ← ไฟล์ SQL สำหรับ Import
    └── fxmanifest.lua

2. Dependencies

ตรวจสอบและติดตั้ง Resource ที่จำเป็นก่อน Start:

  • es_extended — บังคับ (ต้อง start ก่อน)
  • oxmysql — บังคับ (ต้อง start ก่อน)
  • AFU.Toastify — แนะนำ (ถ้าไม่มี จะไม่แสดง Hint กด E แต่ร้านยังทำงานได้)
  • unknow_notify — แนะนำ (ถ้าไม่มี จะไม่แสดง Notification แจ้งเตือน แต่ร้านยังทำงานได้)
  • AFU.VehicleKeys — แนะนำ (ถ้าไม่มี ผู้เล่นอาจต้องกดรับกุญแจเองหลังซื้อรถ)
  • AFU.Garage — แนะนำ (ถ้าไม่มี รถที่ซื้อมาอาจไม่ขึ้นในโรงจอดรถทันที)
  • nc_discordlogs — ไม่บังคับ (ถ้าไม่มี จะไม่ส่ง Log การซื้อรถไป Discord)

3. Database

Import ไฟล์ SQL ทั้ง 2 ไฟล์ในโฟลเดอร์ sql/:

ไฟล์ที่ 1: sql/initial.sql — ตาราง owned_vehicles (ตารางรถที่ผู้เล่นเป็นเจ้าของ)

sql
CREATE TABLE IF NOT EXISTS `owned_vehicles` (
  `owner` varchar(22) COLLATE utf8mb4_bin NOT NULL,
  `state` int(1) NOT NULL DEFAULT 0,
  `plate` varchar(7) COLLATE utf8mb4_bin NOT NULL,
  `vehicle` longtext COLLATE utf8mb4_bin NOT NULL DEFAULT '{}',
  `label` varchar(50) COLLATE utf8mb4_bin NOT NULL DEFAULT '',
  `type` varchar(20) COLLATE utf8mb4_bin NOT NULL DEFAULT 'car',
  `job` varchar(20) COLLATE utf8mb4_bin DEFAULT NULL,
  PRIMARY KEY (`plate`) USING BTREE,
  KEY `owner` (`owner`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin ROW_FORMAT=COMPACT;

หมายเหตุ

  • หากเซิร์ฟเวอร์คุณ มีตาราง owned_vehicles อยู่แล้ว (จาก AFU.Garage หรือระบบรถเดิม) ให้ข้ามไฟล์นี้ — ระบบจะใช้ตารางเดิม
  • คอลัมน์ plate เป็น varchar(7) — ป้ายทะเบียนที่ระบบสร้างมีรูปแบบ ABC 123 (7 ตัวอักษรพอดี) หากใช้ระบบป้ายทะเบียนยาวกว่านี้ต้องขยายคอลัมน์เอง

ไฟล์ที่ 2: sql/vehicle.pools.sql — ตาราง afu.vehicle_pools (จำกัดจำนวนรถที่ขายได้)

sql
CREATE TABLE IF NOT EXISTS `afu.vehicle_pools` (
  `label` varchar(255) NOT NULL,
  `available_total` int(11) NOT NULL DEFAULT 10
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COMMENT='Limit sell vehicle database.';

วิธีใช้ตารางจำกัดจำนวนรถ

  • ใส่แถวในตารางโดยให้ label ตรงตัว กับ label ของรถในไฟล์ Config ร้าน (เช่น T20, OFFICER C)
  • available_total = จำนวนคันที่ยังขายได้ — ทุกครั้งที่ขายได้ 1 คัน ระบบจะหักลง 1 และอัปเดต Database ตาม
  • เมื่อจำนวนลงถึง 0 รถรุ่นนั้นจะ หมดสต็อก (ผู้เล่นเปิดร้านแล้วจะปิดร้านอัตโนมัติ)
  • รถรุ่นใด ไม่ มีแถวในตาราง = ขายได้ไม่จำกัด
  • รีเซ็ตสต็อก: แก้ค่า available_total ใน Database ใหม่แล้ว Restart เซิร์ฟเวอร์

4. server.cfg

เพิ่มบรรทัดต่อไปนี้ใน server.cfg:

cfg
# ลำดับการ start ที่ถูกต้อง
ensure oxmysql
ensure es_extended
ensure AFU.Toastify
ensure unknow_notify
ensure AFU.VehicleKeys
ensure AFU.Garage
ensure AFU.VehicleShop

ข้อควรจำ

  • ลำดับการ start สำคัญมาก: oxmysql และ es_extended ต้อง start ก่อน AFU.VehicleShop เสมอ
  • Resource นี้ถูกเข้ารหัสโค้ดไว้ (Obfuscated) — แก้ไขได้เฉพาะไฟล์ในโฟลเดอร์ Config/ เท่านั้น ห้ามแก้ไขไฟล์ใน Source/ หรือ nui/ เด็ดขาด

⚙️ Configuration

คำเตือน

โปรดแก้ไขเฉพาะค่าในไฟล์ Config/ เท่านั้น ห้ามแก้ไขไฟล์ระบบหลัก (Source/, nui/) เนื่องจากมีการเข้ารหัสโค้ดไว้ การแก้ไขไฟล์หลักอาจทำให้ License สิ้นผลหรือสคริปต์ไม่ทำงาน

ไฟล์ Config/settings.config.lua — ตั้งค่าหลัก

ตัวแปร (Option)ชนิดข้อมูล (Type)ค่าเริ่มต้น (Default)คำอธิบาย (Description)
Settings.keyOpennumber38Control ID ของปุ่มเปิดร้าน (ค่าเริ่มต้น 38 = ปุ่ม E) เปลี่ยนเป็น ID ปุ่มอื่นได้ตามต้องการ
Settings.fontNamestring'Kanit'ชื่อฟอนต์ที่ใช้วาด 3D Text เหนือร้านและชื่อ Blip (ฟอนต์ Kanit ถูกฝังมาใน Resource แล้ว)
Settings.Tax.Banknumber50อัตราภาษี (%) ที่บวกเพิ่มเมื่อจ่ายเงินผ่านธนาคาร — ค่าเริ่มต้น 50% สูงมาก ปรับตามต้องการ
Settings.DebugPotentialBlackScreenbooleantrueแสดงข้อความ debug ใน console ระหว่างรอ Teleport (ตอน Test Drive) ปิดเป็น false ได้
Settings.TestDrive.Timenumber15000ระยะเวลา Test Drive (มิลลิวินาที) — 15000 = 15 วินาที
Settings.TestDrive.BucketsExcludetable<number>{[0..3] = true}Routing Buckets ที่ระบบห้ามใช้สำหรับ Test Drive (ค่าเริ่มต้นปิด Bucket 0–3 ไว้)
Settings.TestDrive.Locationstable<vector4>พิกัด 1 จุดจุด Teleport สำหรับ Test Drive รถ/เครื่องบิน — ใส่ได้หลายจุด ระบบจะสุ่มเลือก 1 จุด
Settings.TestDrive.BoatLocationstable<vector4>พิกัด 1 จุดจุด Teleport สำหรับ Test Drive เรือ — แยกจากรถ ใส่ได้หลายจุด
Settings.Marker.spritenumber2Sprite ID ของ Marker 3D เหนือร้าน (0–44)
Settings.Marker.colortable{r=56, g=158, b=166, a=255}สีของ Marker (ค่า 0–255)
Settings.Marker.sizenumber0.3ขนาดของ Marker
Settings.ExportTextUI.PressEfunctionHook แสดง Hint "กด E" เมื่อเข้าใกล้ร้าน (ค่าเริ่มต้นใช้ AFU.Toastify) — แก้ไขข้อความ/รูปแบบได้
Settings.ExportTextUI.HidePressEfunctionHook ซ่อน Hint เมื่อเดินออก — ค่าเริ่มต้นเรียก exports['AFU.Toastify']:hide()

หมายเหตุเรื่องภาษี

ภาษีจะบวกเพิ่มบนราคารถเมื่อเลือกจ่ายด้วยเงินธนาคาร เช่น ราคา 1,000,000 + ภาษี 50% = จ่ายจริง 1,500,000 (UI จะแสดงอัตราภาษีที่ปุ่มจ่าย) ปรับค่า Settings.Tax.Bank ตามนโยบายเซิร์ฟเวอร์

ไฟล์ Config/Shop/*.lua — กำหนดร้านขายรถ

ร้านแต่ละแห่งเป็นไฟล์ .lua แยกกันในโฟลเดอร์ Config/Shop/ (มีตัวอย่างมาให้ 5 ไฟล์: ร้านทั่วไป, ร้านเรือ, ร้านตำรวจ, ร้านบินตำรวจ, ร้านบินพยาบาล)

สร้างร้านใหม่: คัดลอกไฟล์ร้านใดร้านหนึ่งแล้วแก้ค่า หรือสร้างไฟล์ใหม่ตามโครงสร้างด้านล่าง (ระบบจะโหลดทุกไฟล์ในโฟลเดอร์ Shop/ อัตโนมัติ)

ตัวแปร (Option)ชนิดข้อมูล (Type)บังคับคำอธิบาย (Description)
shopNamestringชื่อร้าน (แสดงบน 3D Text, Blip และใน UI) — ชื่อซ้ำไม่ได้
pednumber (model hash)โมเดล NPC ตัวขายรถ เช่น `a_m_m_prolhost_01` ถ้าไม่ต้องการ NPC ให้ลบหรือ comment บรรทัดนี้ทิ้ง
animationtable✅ (ถ้ามี NPC)Animation ของ NPC: { dict = 'ชื่อ animation dictionary', name = 'ชื่อ animation' }
bliptableตั้งค่า Blip: hide (ใส่ true เพื่อซ่อน Blip), sprite (ID รูป), scale (ขนาด), color (สี)
coordsvector4ตำแหน่งร้าน (จุดที่ผู้เล่นเดินเข้าใกล้เพื่อเปิดร้าน, จุด spawn NPC/Blip/Marker) — w = heading
showCoordsvector4ตำแหน่งที่วางรถโชว์ 360° ในร้าน — ต้องเป็นจุดที่รถจอดได้และกล้องไม่ชนสิ่งกีดขวาง
boughtCoordstable<vector4>ตำแหน่งจุดส่งมอบรถหลังซื้อเสร็จ — ใส่ได้หลายจุด ระบบจะเลือกจุดที่ว่าง (รัศมี 18 เมตร)
requireJobtable<string>รายชื่ออาชีพ (job name ใน ESX) ที่อนุญาตให้เข้าร้านได้ ถ้าเว้นว่าง {} = ทุกคนเข้าได้
vehiclestableรายการรถ — ดูโครงสร้างด้านล่าง

โครงสร้าง vehicles (รายการรถ)

text
vehicles = {
    {
        category = 'ชื่อหมวดหมู่',        -- ชื่อหมวดที่แสดงใน UI
        cars = {
            {
                model = `adder`,         -- model hash ของรถ (ห้ามผิด ไม่อย่างนั้นร้านจะโหลดรถไม่ได้)
                label = 'Adder',         -- ชื่อรถที่แสดงใน UI + ใช้จับคู่กับตาราง afu.vehicle_pools
                type = 'car',            -- 'car' | 'boat' | 'helicopter' | 'plane' (ถ้าไม่ใส่ ระบบจะตรวจจาก model เอง)
                price = 1000000,         -- ราคารถ (ถ้าจ่ายด้วยธนาคาร จะบวกภาษี Settings.Tax.Bank)
                weight = 0,              -- น้ำหนัก (เก็บข้อมูลไว้)
                job = 'police',          -- (ไม่บังคับ) อาชีพที่รถคันนี้ผูกกับ — บันทึกเข้า Database
                job_grade = 5,           -- (ไม่บังคับ) ขั้นขั้นต่ำของอาชีพ — บันทึกเข้า Database
                defaultColor = {         -- สีเริ่มต้นของรถ (ปรับได้อีกใน UI)
                    primary = { multiplyColor = 5, r = 0, g = 0, b = 0 },
                    secondary = { multiplyColor = 5, r = 0, g = 0, b = 0 }
                }
            },
            ...
        }
    },
    ...
}

โครงสร้างตัวอย่าง (ไฟล์ร้าน)

lua
local vehicleShop = {
    shopName = 'Car',
    ped = `a_m_m_prolhost_01`,
    animation = {
        dict = 'amb@world_human_clipboard@male@idle_a',
        name = 'idle_a'
    },
    blip = {
        hide = false,
        sprite = 227,
        scale = 1.0,
        color = 0
    },
    coords = vector4(-56.62, -1099.12, 26.42, 28.74),
    showCoords = vector4(-45.55, -1097.78, 25.33, 100.48),
    boughtCoords = {
        vector4(-44.97, -1077.26, 26.61, 70.86),
        vector4(-31.04, -1081.12, 26.54, 69.45),
    },
    requireJob = {
        -- เว้นว่าง = ทุกคนเข้าได้ / ใส่ 'police' = เฉพาะตำรวจ
    },
    vehicles = {
        {
            category = 'Starter',
            cars = {
                {
                    model = `weevil`,
                    label = 'Weevil',
                    type = 'car',
                    price = 4500,
                    weight = 0,
                    defaultColor = {
                        primary = { multiplyColor = 5, r = 0, g = 0, b = 0 },
                        secondary = { multiplyColor = 5, r = 0, g = 0, b = 0 }
                    }
                }
            }
        }
    }
}

table.insert(ServerConfig.VehiclesShop, vehicleShop)

ไฟล์ Config/secure.config.lua — Security & Admin

ตัวแปร (Option)ชนิดข้อมูล (Type)ค่าเริ่มต้น (Default)คำอธิบาย (Description)
ServerConfig.getEvents.frameworkEventstring'esx:getSharedObject'Event สำหรับดึง Object ของ Framework — ไม่ต้องแก้ ยกเว้นใช้ระบบ Framework แบบ custom ที่ไม่ใช้ Event นี้
ServerConfig.UseAdminPlateByPrefixbooleanfalseถ้าเป็น true รถที่ Admin แจกผ่านคำสั่งจะใช้ป้ายทะเบียนขึ้นต้นด้วยค่าใน AdminList (เช่น HEX 123)
ServerConfig.AdminListtable<string>{}รายชื่อ Steam Hex Identifier ของ Admin ที่ใช้คำสั่งแจกรถได้ — รูปแบบ: ['steam:1100001358fb54d'] = 'HEX' (ค่าหลัง = คือคำนำหน้าป้ายทะเบียน)

วิธีหา Steam Hex Identifier

ดูได้จาก console ของเซิร์ฟเวอร์เมื่อผู้เล่นเข้าเกม หรือใช้คำสั่ง /id ในเกม — ใช้ค่า steam:xxxxxxxxxxxxxxx เป็น key ใน AdminList

ไฟล์ Config/commands.config.lua — คำสั่ง

ตัวแปร (Option)ชนิดข้อมูล (Type)ค่าเริ่มต้น (Default)คำอธิบาย (Description)
Commands.giveCarstring'gc'ชื่อคำสั่ง Admin สำหรับแจกรถ (ไม่ใส่ / นำหน้า)

ไฟล์ Config/notify.config.lua — ปรับข้อความ Notification

แก้ไขข้อความในฟังก์ชันเหล่านี้ได้ (ใช้ exports.unknow_notify:add):

ฟังก์ชันสถานการณ์
Notify.CheckingArea(second)ระหว่างรอหาจุดส่งมอบรถที่ว่างหลังซื้อรถ
Notify.NotEnoughMoney(type)เงินสด/เงินธนาคารไม่พอ (type = 'bank' หรือ 'money')
Notify.RestricJob()อาชีพไม่ได้รับอนุญาตให้เข้าร้านนี้
Notify.CantLoadModel()โหลดโมเดลรถไม่ได้ (ชื่อ model ผิด)

ฟังก์ชัน Customization (Hooks)

Config/functions.config.lua (ฝั่ง Client)

ฟังก์ชันเมื่อไหร่หมายเหตุ
Functions.boughtVehicle(plate, vehicle, vehicleProperties)หลังซื้อรถสำเร็จและรถ spawn แล้วplate = ป้ายทะเบียน, vehicle = EntityID ของรถ, vehicleProperties = ข้อมูลรถ — จุดสำหรับต่อ Log/Notification พิเศษ
Functions.createdPed(shopName, pedEntity, requireJobs)หลัง spawn NPC ร้านแล้วpedEntity = EntityID ของ NPC — ปรับแต่ง NPC ได้ (เช่น ตั้งชื่อ, ให้คุย)
Functions.openShop(shopName, requireJobs)ตอนผู้เล่นเปิดร้านว่างไว้ให้ custom เช่น เล่นเสียง, log
Functions.closedShop(shopName, requireJobs)ตอนผู้เล่นออกจากร้านว่างไว้ให้ custom
Functions.RecievedCehicleFromCommand(plate)ตอนผู้เล่นได้รับรถจากคำสั่ง Adminplate = ป้ายทะเบียนรถที่ได้รับ

Config/suspects.config.lua (ฝั่ง Server — Anti-Cheat)

ฟังก์ชันเหล่านี้ทำงานเมื่อระบบตรวจสอบฝั่ง Server ตรวจพบพฤติกรรมผิดปกติ (ค่าเริ่มต้น: ว่างเปล่า):

ฟังก์ชันสถานการณ์
Suspects.changeModelVehicle(xPlayer, playerId, vehicleProperties, price)ผู้เล่นพยายามซื้อโดยเปลี่ยน model ของรถ
Suspects.buyingNotMatchPrice(xPlayer, playerId, vehicleProperties, price)ผู้เล่นส่งราคาซื้อที่ไม่ตรงกับราคาใน Config
Suspects.buyingUnknownShop(xPlayer, playerId, vehicleProperties, price)ผู้เล่นยิง event ซื้อจากร้านที่ไม่รู้จัก
Suspects.buyingUnknownCategory(xPlayer, playerId, vehicleProperties, price)ผู้เล่นยิง event ซื้อหมวดหมู่/รถที่ไม่รู้จัก
Suspects.notHaveValidateData(xPlayer, playerId)ผู้เล่นส่งข้อมูลซื้อรถที่ไม่ครบถ้วน

TIP

แนะนำต่อฟังก์ชันใน suspects.config.lua กับระบบ Log/Discord ของเซิร์ฟเวอร์เพื่อจับผู้ดัดแปลงสคริปต์

รายละเอียดการตั้งค่าเพิ่มเติม (Detailed Setup)

📍 Locations / Coordinates

  • ระบุตำแหน่งด้วย vector4(x, y, z, w)w คือกางของทิศทาง (heading)
  • แต่ละร้านมี 3 กลุ่มพิกัด ที่ต้องตั้ง:
    • coords — จุดร้าน: จุดที่ผู้เล่นเข้าใกล้เพื่อเปิดร้าน + จุด spawn NPC/Blip/Marker
    • showCoords — จุดวางรถโชว์ 360° (ต้องเป็นพื้นที่โล่งที่รถจอดได้)
    • boughtCoords — จุดส่งมอบรถหลังซื้อ (ใส่ได้หลายจุดเพื่อป้องกันจุดติด)
  • เมื่อผู้เล่นเข้าใกล้ตำแหน่งร้าน ภายใน 18 เมตร จะเห็น 3D Text + Marker / เข้าใกล้ ภายใน 3 เมตร จะแสดง Hint กดปุ่ม
  • ใช้ /setcoords หรือ copycoords ในเกมเพื่อหาพิกัด

🚗 Items & Rewards (รายการรถ)

  • model — model hash ของรถในเกม (เช่น `adder`) — ต้องถูกต้อง ถ้าผิด รถรุ่นนั้นจะไม่แสดงในร้าน (console จะแจ้ง can't load model of ...)
  • label — ชื่อรถที่แสดงใน UI — ใช้จับคู่กับตาราง afu.vehicle_pools เพื่อกำหนดจำนวนขายได้
  • type — ประเภทยานพาหนะ: 'car', 'boat', 'helicopter', 'plane' — ถ้าไม่ใส่ ระบบจะตรวจจาก model เอง
  • price — ราคารถ (ถ้าจ่ายด้วยธนาคาร จะบวกภาษี Settings.Tax.Bank)
  • job / job_grade — (ไม่บังคับ) ผูกอาชีพกับรถคันนั้น (เช่น รถตำรวจ) — บันทึกเข้า Database
  • defaultColor — สีเริ่มต้นของรถ: multiplyColor = รหัสสีมาตรฐานของ GTA (ดูจาก palette สี) หรือใช้ค่า r/g/b สำหรับสี custom
  • จำกัดจำนวนขาย: ใส่แถวในตาราง afu.vehicle_pools โดยให้ label ตรงกับ label ของรถ

🧪 Test Drive (ทดลองขับ)

  • ผู้เล่นกดปุ่มทดลองขับใน UI → ระบบจะ Teleport ผู้เล่น + รถไปที่จุดใน Settings.TestDrive.Locations (หรือ BoatLocations สำหรับเรือ)
  • ระยะเวลา: Settings.TestDrive.Time (ค่าเริ่มต้น 15 วินาที) — หมดเวลาหรือผู้เล่นกดออก ระบบจะพาผู้เล่นกลับจุดเดิมของร้าน
  • ระหว่าง Test Drive ผู้เล่นจะถูกแยก Routing Bucket (ผู้เล่นคนอื่นไม่เห็น) — Bucket ที่ห้ามใช้กำหนดใน Settings.TestDrive.BucketsExclude
  • ตั้งจุด Test Drive ให้เป็นพื้นที่โล่ง (ถนน/ลาน) และอยู่ไม่ไกลจากแผนที่มากเกินไป

💰 เงิน & อาชีพ

  • ผู้เล่นเลือกจ่ายด้วย เงินสด (money) หรือ ธนาคาร (bank) ใน UI — จ่ายผ่านธนาคารจะบวกภาษี Settings.Tax.Bank
  • requireJob: ใส่ job name (ไม่ใช่ label) ตามที่ ESX กำหนด เช่น 'ambulance', 'police' — ถ้าใส่ผิดผู้เล่นจะเข้าร้านไม่ได้

💬 Commands & Permissions

Player Commands

ไม่มี — ผู้เล่นใช้ระบบ proximity:

  1. เดินเข้าใกล้ตำแหน่งร้าน (18 เมตร) → เห็น 3D Text + Marker
  2. เข้าใกล้ 3 เมตร → แสดง Hint กดปุ่ม
  3. กดปุ่ม E (Settings.keyOpen) → เปิด UI ร้านรถ
  4. เลือกหมวด/รุ่น → ดู 360° → ปรับสี → ทดลองขับ (ถ้าต้องการ) → ซื้อ
  5. หลังซื้อ → รถ spawn ที่จุด boughtCoords ที่ว่าง ผู้เล่นถูกส่งเข้ารถอัตโนมัติ

Admin Commands

  • /gc <playerId> <vehicleModel> [job] — แจกรถให้ผู้เล่น (Permission: ต้องมี Steam Hex อยู่ใน ServerConfig.AdminList)
    • playerId — ID ของผู้เล่นเป้าหมาย (เช่น 1)
    • vehicleModel — ชื่อ model ของรถ (เช่น adder)
    • job — (ไม่บังคับ) อาชีพที่ผูกกับรถ (เช่น mechanic)
    • ตัวอย่าง: /gc 1 adder mechanic = ให้ผู้เล่น ID 1 รับรถ Adder ในชื่ออาชีพ mechanic
    • รถที่แจกจะถูกบันทึกเข้า Database เช่นกัน (ราคาบันทึกเป็น command) และผู้เล่นจะได้รับรถทันที

🔌 Public Exports & Events

Exports (ฝั่ง Client)

lua
-- สร้างป้ายทะเบียนสุ่มที่ไม่ซ้ำ (รูปแบบ "ABC 123") — ใช้กับระบบอื่นที่ต้องการป้ายทะเบียน
local plate = exports['AFU.VehicleShop']:generatePlate()

-- ตรวจสอบว่าผู้เล่นอยู่ในร้านรถหรือไม่ (return boolean)
local isOnShop = exports['AFU.VehicleShop']:isOnVehicleShop()

Exports (ฝั่ง Server)

lua
-- สร้างป้ายทะเบียนสุ่มที่ไม่ซ้ำกับรถทั้งหมดในเซิร์ฟเวอร์
local plate = exports['AFU.VehicleShop']:generatePlate()

Events ที่ระบบเรียกใช้ (สำหรับ Resource ที่ทำงานร่วมกัน)

Event / Exportฝั่งเมื่อไหร่
exports['AFU.VehicleKeys']:syncKey(plate)Serverหลังซื้อ/แจกรถสำเร็จ — ซิงค์กุญแจรถให้ผู้เล่น (ถ้ามี AFU.VehicleKeys)
exports['AFU.Garage']:fetchVehicles(playerId)Serverหลังซื้อ/แจกรถสำเร็จ — รีเฟรชรายการรถในโรงจอดรถ (ถ้ามี AFU.Garage)
exports.nc_discordlogs:Discord(...)Serverหลังซื้อรถสำเร็จ — ส่ง Log ไป Discord webhook ชื่อ vehicle_shop (ต้องกำหนด webhook ชื่อ vehicle_shop ใน Config ของ nc_discordlogs)

Hooks ฝั่ง Client (ปรับแต่งใน Config/)

ดูตาราง Hooks ในหัวข้อ ฟังก์ชัน Customization — จุดต่อสำหรับ Log, เสียง, Notification และระบบตรวจจับการโกง


❓ Troubleshooting

สคริปต์ไม่ทำงาน / ร้านไม่ขึ้น
  1. ตรวจสอบว่า oxmysql และ es_extended start สำเร็จก่อน (ดู console)
  2. ตรวจสอบว่า License/Token ที่ถูกต้อง (console จะแจ้งปัญหา License)
  3. ตรวจสอบว่าไฟล์ร้านอยู่ในโฟลเดอร์ Config/Shop/ และจบด้วย table.insert(ServerConfig.VehiclesShop, vehicleShop)
  4. Restart เซิร์ฟเวอร์หลังแก้ Config
รถบางรุ่นไม่แสดงในร้าน
  1. Console จะแจ้ง can't load model of <label> — ตรวจสอบว่า model ใน Config ถูกต้อง (เช่น `weevil` ไม่ใช่ 'weevil')
  2. ตรวจสอบว่า model นั้นมีอยู่ในเกม (ไม่ถูก DLC/patch ตัดออก)
กด E แล้วร้านไม่เปิด
  1. ตรวจสอบ Settings.keyOpen — ค่า 38 คือปุ่ม E ถ้าถูกใช้กับ Interaction อื่น อาจต้องเปลี่ยนปุ่ม
  2. ตรวจสอบว่าผู้เล่น ไม่ได้อยู่ในรถ และ ไม่ตาย
  3. ตรวจสอบฟิลด์ requireJob — ถ้าผู้เล่นไม่ใช่อาชีพที่ได้รับอนุญาต จะถูกแจ้งเตือนแทน
  4. ตรวจสอบว่า AFU.Toastify start แล้ว (ถ้าไม่มีจะไม่มี Hint แต่กด E ได้ตามปกติ)
ซื้อรถแล้วไม่ได้รับรถ / รถไม่ขึ้นใน Garage
  1. ตรวจสอบว่า import ตาราง owned_vehicles แล้ว (หรือมีตารางเดิมอยู่)
  2. ตรวจสอบว่า AFU.Garage start แล้ว (ถ้าใช้ระบบ Garage ของ AFU)
  3. ตรวจสอบ boughtCoords — ต้องเป็นจุดที่ว่างจริง (รัศมี 18 เมตร) ถ้าจุดติดหมด ระบบจะรอจนกว่าจะว่าง
  4. ดู console ฝั่ง Server — ถ้ามีข้อความจาก Suspects แสดงว่าระบบตรวจพบความผิดปกติ
รถหมดสต็อก / เปิดร้านแล้วปิดเอง
  • ระบบอ่านจำนวนขายได้จากตาราง afu.vehicle_pools — ถ้า available_total ของรถรุ่นนั้นเป็น 0 ร้านจะปิดเองเมื่อผู้เล่นเลือกซื้อ
  • รีเซ็ตสต็อก: อัปเดตค่า available_total ใน Database แล้ว Restart เซิร์ฟเวอร์
Test Drive ไม่ทำงาน / Teleport ไม่ถึง
  1. ตรวจสอบพิกัดใน Settings.TestDrive.Locations / BoatLocations — ต้องเป็นจุดที่รถ/เรืออยู่ได้จริง
  2. Console จะแสดง wating... to test drive coords ถ้าผู้เล่นไม่ถูก teleport — ตรวจว่าพิกัดถูกต้อง
  3. ตรวจสอบว่าไม่มีการใช้ Routing Bucket 0–3 ของระบบอื่น (ค่า Settings.TestDrive.BucketsExclude)
ป้ายทะเบียนซ้ำ / Error เกี่ยวกับ plate
  1. ตรวจสอบว่าคอลัมน์ plate ในตาราง owned_vehicles เป็น varchar(7) หรือใหญ่กว่า (ป้ายที่ระบบสร้างยาว 7 ตัวอักษร)
  2. ระบบจะสุ่มป้ายใหม่จนกว่าจะไม่ซ้ำกับรถทั้งหมดในเซิร์ฟเวอร์ — ถ้า Database มีป้ายซ้ำกับค่าเริ่มต้น อาจต้องล้างข้อมูลซ้ำออก
Hint กด E ไม่แสดง
  • Hint ใช้ AFU.Toastify — ตรวจสอบว่า Resource นี้ start แล้ว
  • ถ้าไม่ต้องการ Hint สามารถปรับฟังก์ชัน Settings.ExportTextUI.PressE / HidePressE ใน Config/settings.config.lua เป็นระบบอื่นได้