Appearance
🚀 AFU.SuperAirdrop
ข้อมูลสคริปต์
- Framework: ESX (es_extended)
- Version: 7.3
- Lua: 5.4
- Dependencies:
oxmysql— ฐานข้อมูล (เรียกใช้ skin ของผู้เล่นสำหรับฉาก Cutscene)es_extended— Framework หลักAFU.Notify— ระบบแจ้งเตือน (Toast)AFU.Toastify— ระบบแจ้งเตือนแบบ Toast ในเกมAFU.Inventory— รูปภาพไอเทม (ดึง path จากnui://AFU.Inventory/...)xcore-gang— ระบบแก๊ง (จำเป็นสำหรับ Gang Mode)LTS_Ambulance— ระบบฟื้นผู้เล่น (ใช้โดยค่าเริ่มต้นในAirdrop.Revive)
สิ่งที่ต้องรู้ก่อนติดตั้ง
- Resource นี้ ต้องมีการซื้อ License ที่ถูกต้องจาก AFU Squad เพื่อใช้งาน (token จาก awayfromus.store)
- หากไม่มี License ที่ถูกต้อง Resource จะไม่สามารถทำงานได้
🛠️ Installation
1. Download & Placement
นำ Resource ไปวางในโฟลเดอร์ resources/ ของเซิร์ฟเวอร์:
text
resources/
└── AFU.SuperAirdrop/
├── Config/
│ ├── Airdrop/ ← ตั้งค่ากล่องแอรดรอปแต่ละตัว
│ │ ├── prachachon.lua
│ │ ├── gang_yellow.lua
│ │ ├── gang_newmode.lua
│ │ ├── crew_drop_2_site1.lua
│ │ └── crew_drop_2_site2.lua
│ ├── airdrop.settings.lua ← ตั้งค่า global
│ ├── framework.config.lua
│ ├── permission.config.lua
│ ├── player.config.lua
│ ├── path.config.lua
│ ├── notify.config.lua
│ ├── routers.config.lua
│ ├── queries.config.lua
│ ├── suspect.config.lua
│ └── discord.config.lua
├── Source/
├── web/
├── stream/
└── fxmanifest.lua2. Dependencies — ติดตั้ง Resource ที่จำเป็น
ตรวจสอบว่า Resource ต่อไปนี้ถูกติดตั้งและรันก่อน AFU.SuperAirdrop:
cfg
# server.cfg — ลำดับสำคัญ (dependencies ต้อง start ก่อน)
ensure oxmysql
ensure es_extended
ensure AFU.Notify
ensure AFU.Toastify
ensure AFU.Inventory
ensure xcore-gang
# แล้วค่อย start AFU.SuperAirdrop
ensure AFU.SuperAirdropลำดับการ ensure
oxmysql และ es_extended ต้อง start ก่อนเสมอ เพราะ AFU.SuperAirdrop จะรอ object ของ framework จนกว่าจะพร้อม
3. Database
ฐานข้อมูล
Resource นี้ ไม่ต้องการ Import SQL ไม่มีตารางเฉพาะที่ต้องสร้าง
ระบบจะอ่านข้อมูลจากตาราง users ของ framework (คอลัมน์ skin) เพื่อแสดง skin จริงของผู้เล่นในฉาก Cutscene เท่านั้น — ตารางนี้มีอยู่แล้วใน framework ของคุณ
4. เริ่มต้นใช้งาน
- แก้ไข
Config/permission.config.lua— เพิ่ม Steam ID ของคุณ (ดูหัวข้อ Permissions) - แก้ไข
Config/Airdrop/*.lua— ตั้งค่ากล่องแอรดรอปที่ต้องการ (ดูหัวข้อ Airdrop Boxes) - ตรวจสอบ
Config/discord.config.lua— ตั้งค่า Webhook และ Token (ดูหัวข้อ Discord) - Start เซิร์ฟเวอร์
- ใช้คำสั่ง
/airdrop(หรือชื่อที่แก้) เพื่อเปิดแผงควบคุม
⚙️ Configuration
🌐 Global Settings — Config/airdrop.settings.lua
ตั้งค่าระดับระบบทั้งหมด:
| ตัวแปร | Type | Default | คำอธิบาย |
|---|---|---|---|
Airdrop.BlockVehicle | boolean | false | true = ห้ามขับยานพาหนะเข้าโซนแอรดรอป / false = อนุญาต |
Airdrop.CommandControl | string | 'airdrop' | ชื่อคำสั่งสำหรับเปิดแผงควบคุมแอรดรอป (Admin เท่านั้น) |
Airdrop.SceneTest | string | 'Scene' | ชื่อคำสั่งสำหรับทดสอบ Cutscene (Admin เท่านั้น) |
Airdrop.UsingRemoveTime | number | 10000 | เวลา (ms) ที่รอหลังจากจบแอรดรอป ก่อนลบกล่องออกจากโลก |
Airdrop.delayBeforeExit | number | 6000 | เวลา (ms) ที่รอหลังจากจบแอรดรอป ก่อนย้ายผู้เล่นกลับจุดเดิม |
Airdrop.ShowTimer | boolean | true | แสดง UI นับเวลาแอรดรอป (แถบ Active Strip) หรือไม่ |
Airdrop.PlayAnnouncementSound | boolean | true | เปิด/ปิดเสียงประกาศเมื่อแอรดรอปเริ่ม |
Airdrop.Debug | boolean | true | เปิดโหมด Debug (แสดงข้อมูลใน console) — ควรปิด false ในเซิร์ฟเวอร์จริง |
Airdrop.ShuffleItemsInAirdrop | boolean | true | สลับตำแหน่งไอเทมในกล่องก่อนเปิด |
Airdrop.UseAirdropInventory | boolean | false | true = เปิด UI ให้กดเลือกหยิบไอเทม / false = หลอดเต็ม 100% แล้วไอเทมทั้งหมดเข้าตัวทันทีและจบแอร์ดรอป |
Airdrop.limitTakeItem.enable | boolean | false | เปิด/ปิดการจำกัดจำนวนไอเทมที่เก็บได้ต่อรอบ |
Airdrop.limitTakeItem.limit | number | 2 | จำนวนไอเทมสูงสุดที่เก็บได้ต่อรอบ (ใช้เมื่อ enable = true) |
Airdrop.RestrictZone.enable | boolean | true | เปิด/ปิดโซนอันตราย (ลดเลือดเมื่อวงหด) |
Airdrop.RestrictZone.HealthDecrease | number | 1 | จำนวนเลือดที่ลดลงต่อวินาทีเมื่ออยู่ในวงที่หดเข้ามา |
Airdrop.RestrictZone.closestRing | number | 4.00 | รัศมี (เมตร) ของวงในสุดที่เริ่มทำอันตราย |
Airdrop.RestrictZone.effectName | string | 'DeathFailNeutralIn' | ชื่อ Post-Process FX ที่แสดงเมื่ออยู่ในโซนอันตราย |
Airdrop.Revive.enable | boolean | true | เปิด/ปิดระบบฟื้นอัตโนมัติในแอรดรอป |
Airdrop.Revive.delay | number | 2000 | เวลา (ms) หลังตายก่อนฟื้นอัตโนมัติ |
Airdrop.Revive.revive | function | — | ฟังก์ชันที่เรียกเมื่อฟื้น (ค่าเริ่มต้นเรียก LTS_Ambulance:revive) |
Airdrop.Revive.after | function | — | ฟังก์ชันที่เรียกหลังฟื้นเสร็จ (ว่างไว้ได้) |
Airdrop.Heal.Enable | boolean | true | เปิด/ปิดระบบฮีลอัตโนมัติในแอรดรอป |
Airdrop.Heal.delay | number | 4000 | เวลา (ms) หลังเข้าโซนก่อนฮีลเต็ม |
Airdrop.Heal.heal | function | — | ฟังก์ชันที่เรียกเมื่อฮีล (ค่าเริ่มต้นฮีลเต็มเลือด) |
Airdrop.Heal.after | function | — | ฟังก์ชันที่เรียกหลังฮีลเสร็จ (ว่างไว้ได้) |
Airdrop.Debug
ค่าเริ่มต้นเป็น true — ควรตั้งเป็น false ในเซิร์ฟเวอร์จริงเพื่อปิดข้อมูล debug ใน console
ตัวอย่างการเปลี่ยนระบบ Revive
lua
Airdrop.Revive = {
enable = true,
delay = 2000,
revive = function()
-- เรียก event ของระบบ ambulance ของคุณ
TriggerEvent('esx_Ambulance:revive')
end,
after = function()
-- ทำอะไรเพิ่มหลังฟื้น (เช่น แจ้งเตือน)
end
}🎮 Player Settings — Config/player.config.lua
ตั้งค่าพฤติกรรมผู้เล่นในแอรดรอป:
| ตัวแปร | Type | Default | คำอธิบาย |
|---|---|---|---|
Player.OpenAirdropKey | number | 38 | คีย์สำหรับเปิดกล่องแอรดรอป (38 = E) — ดูรหัสคีย์ที่ docs.fivem.net |
Player.TimeOpenAirdrop | number | 5000 | เวลา (ms) ที่ต้องกดคีย์ค้างเพื่อเปิดกล่อง |
Player.distanceOpenAirdrop | number | 2.5 | ระยะ (เมตร) ที่สามารถกดเปิดกล่องได้ |
Player.DelayTakeItem | number | 0 | เวลาหน่วง (ms) ก่อนเก็บไอเทมได้ |
Player.freezeTimeAfterTeleport | number | 10000 | เวลา (ms) ที่แช่แข็งผู้เล่นหลังวาร์ปไปแอรดรอป |
Player.Animation.dict | string | — | Animation Dictionary สำหรับท่าเก็บไอเทม |
Player.Animation.anim | string | — | Animation Name สำหรับท่าเก็บไอเทม |
Player.TeleportOptions.Enable | boolean | true | เปิด/ปิดคำสั่งวาร์ปไปแอรดรอป |
Player.TeleportOptions.Command | string | 'goairdrop' | ชื่อคำสั่งสำหรับเปิดแผงวาร์ป |
Player.blockKeyList | number[] | {38, 72} | คีย์ที่ถูกบล็อกเมื่ออยู่ในโซนแอรดรอป |
Player.blockTeleportAllowCoords | table[] | — | โซนที่บล็อกการวาร์ป (ดูด้านล่าง) |
Block Teleport Zones
กำหนดโซนที่ไม่สามารถวาร์ปเข้า-ออกได้:
lua
Player.blockTeleportAllowCoords = {
{
coords = vector3(-259.5214, -976.136, 31.22), -- พิกัดศูนย์กลาง
radius = 50.0, -- รัศมี (เมตร)
showDebug = false -- แสดงวงกลม debug หรือไม่
},
}Callback Functions (Hooks)
ฟังก์ชันเหล่านี้ให้คุณเขียน Logic เพิ่มตามต้องการ:
| ฟังก์ชัน | เมื่อไหร่ | Return |
|---|---|---|
Player.willExitAirdrop() | ก่อนออกจากแอรดรอป | true = ให้ออก / false = ห้ามออก |
Player.beforeTeleportToAirdrop(airdropName) | ก่อนวาร์ปไปแอรดรอป | true = ให่วาร์ป / false = ห้ามวาร์ป |
Player.afterTeleportToAirdrop(airdropName) | หลังวาร์ปถึงแอรดรอปแล้ว | — |
Player.afterTakeItemAirdrop(name, total) | หลังเก็บไอเทม | — |
Player.DeadInSideAirdrop() | เมื่อผู้เล่นตายในแอรดรอป | — |
ตัวอย่าง: บล็อกการวาร์ปถ้าผู้เล่นไม่มีไอเทม
lua
Player.beforeTeleportToAirdrop = function(airdropName)
local ESX = exports.es_extended:getSharedObject()
local player = ESX.GetPlayerFromId(source)
if not player or not player.inventory:count('mine_orb') > 0 then
TriggerEvent('AFU.Notify:add', {
position = 'middleLeft',
type = 'alert',
title = 'แอร์ดรอป',
description = 'ต้องมียาก่อนถึงจะเข้าได้',
time = 5000,
})
return false
end
return true
end🔐 Permissions — Config/permission.config.lua
| ตัวแปร | Type | คำอธิบาย |
|---|---|---|
Permission.AllowControl | table<string, boolean> | Steam ID ที่มีสิทธิ์ควบคุมแอรดรอป (เปิดแผง, สร้าง/ลบกล่อง) |
Permission.BypassJob | table<string, boolean> | (Optional) Job ที่มีสิทธิ์ควบคุมโดยไม่ต้องใส่ Steam ID |
lua
Permission.AllowControl = {
['steam:11000010bb8ee4b'] = true, -- ← เปลี่ยนเป็น Steam ID ของคุณ
}
-- (Optional) ให้ Job บางตัวมีสิทธิ์ควบคุม
Permission.BypassJob = {
['admin'] = true,
['police'] = true,
}Steam ID
- หา Steam ID ของคุณ: เปิด FiveM → F8 → พิมพ์
GetPlayerIdentifier(-1, 0) - รูปแบบ:
steam:1100001xxxxxxx - อย่าใช้ Steam ID ของคนอื่น — ผู้เล่นที่ไม่มีสิทธิ์พยายามควบคุมจะถูกจัดการตาม
Suspectconfig
📡 Routers — Config/routers.config.lua
กำหนด Event ที่ Resource จะฟัง:
| ตัวแปร | Type | Default | คำอธิบาย |
|---|---|---|---|
Routers.onPlayerSpawn | string[] | {"esx:onPlayerSpawn"} | Event ที่เรียกเมื่อผู้เล่นเกิด/Spawn |
Routers.onPlayerDead | string[] | {"esx:onPlayerDeath"} | Event ที่เรียกเมื่อผู้เล่นตาย |
Routers.onServerPlayerSpawnHandler | table<string, function> | — | Event server-side เพิ่ม (ดูตัวอย่างด้านล่าง) |
ตัวอย่าง: เพิ่ม handler สำหรับ event อื่น
lua
Routers.onServerPlayerSpawnHandler = {
["esx_ambulancejob:setDeathStatus"] = function(isDead)
if isDead then return end
TriggerClientEvent('afu-ua-reset-dead', source)
end
}ถ้าใช้ Framework อื่น
เปลี่ยน event name ใน onPlayerSpawn และ onPlayerDead ให้ตรงกับ framework ของคุณ
🖼️ Path — Config/path.config.lua
| ตัวแปร | Type | Default | คำอธิบาย |
|---|---|---|---|
Path.Inventory | string | 'nui://AFU.Inventory/web/assets/img/item/' | Path ของรูปไอเทม (ต้องตรงกับ Resource Inventory ของคุณ) |
Path.FontName | string | 'Kanit' | ชื่อฟอนต์ที่ใช้ในเกม (ต้องโหลดฟอนต์นั้นใน Resource อื่น) |
Path.Inventory
ค่านี้ต้องชี้ไปยังโฟลเดอร์ที่มีรูปไอเทม .png ของคุณ — ถ้าใช้ Inventory อื่นที่ไม่ใช่ AFU.Inventory ต้องแก้ path ให้ตรง
📢 Discord — Config/discord.config.lua
| ตัวแปร | Type | คำอธิบาย |
|---|---|---|
DiscordWebhookAPI.ItemRewards | string | Discord Webhook URL สำหรับแจ้งเตือนรางวัล |
DiscordWebhookAPI.DiscordToken | string | Discord Bot Token สำหรับดึง Avatar ของผู้เล่น |
DiscordWebhookAPI.DefaultID | string | Discord User ID สำหรับ Avatar fallback (เมื่อดึง avatar ไม่ได้) |
lua
DiscordWebhookAPI.ItemRewards = 'https://discord.com/api/webhooks/YOUR_WEBHOOK_ID/YOUR_TOKEN'
DiscordWebhookAPI.DiscordToken = 'YOUR_BOT_TOKEN'
DiscordWebhookAPI.DefaultID = 'YOUR_DEFAULT_USER_ID'ความปลอดภัย
- อย่า commit ไฟล์นี้ที่มี Token จริงขึ้น Git
- Bot Token ใช้สำหรับ REST API
GET /users/:idเพื่อดึง avatar — ใช้ Bot ที่คุณสร้างเอง - Webhook URL สร้างจาก Discord Server → Server Settings → Integrations → Webhooks
ถ้าไม่ใช้ Discord
ตั้งค่าทั้ง 3 ตัวเป็น string ว่าง '' — ระบบจะ skip การแจ้งเตือน/ดึง avatar โดยไม่เกิด error
🔔 Notifications — Config/notify.config.lua
ไฟล์นี้รวมฟังก์ชันแจ้งเตือนทั้งหมด ใช้ AFU.Notify (ต้องติดตั้ง)
| ฟังก์ชัน | เมื่อไหร่ |
|---|---|
Notify.addItemFromPlayerJob | ผู้เล่นเก็บไอเทมให้ทีม |
Notify.limitTakeItem | ถึงขีดจำกัดการเก็บไอเทม |
Notify.BlockVehicle | พยายามขับรถเข้าโซน |
Notify.removedAirdrop | แอรดรอปถูกลบ |
Notify.delayTake | ยังเก็บไอเทมไม่ได้ (รอเวลา) |
Notify.requireItem | ต้องมีไอเทมถึงจะเข้าได้ |
Notify.requireJob | แอรดรอปจำกัดบางอาชีพ |
Notify.notJoinOnWaiting | แอรดรอปเริ่มแล้ว เข้าไม่ทัน |
Notify.AirdropEnd | แอรดรอปในโซนจบแล้ว |
Notify.ExitAirdrop | ถูกลบกลับเพราะ Fast Travel |
Notify.AirdropStartedCantTP | แอรดรอปเริ่มแล้ว ห้าม Fast Travel ออก |
Notify.AirdropNotAllowTP | แอรดรอปนี้ต้องขับรถไปเท่านั้น |
Notify.blacklistItem | มีไอเทมที่ห้ามพกเข้าโซน |
Notify.blockTPZone | อยู่ในโซนที่บล็อก TP |
ปรับข้อความแจ้งเตือน
แก้ข้อความภายในแต่ละฟังก์ชันได้ตามต้องการ — ทุกฟังก์ชันใช้ exports['AFU.Notify']:add({...})
🚨 Suspect — Config/suspect.config.lua
| ตัวแปร | Type | คำอธิบาย |
|---|---|---|
Suspect.tryToControlAirdrop | function(source) | เรียกเมื่อผู้เล่นที่ไม่มีสิทธิ์พยายามควบคุมแอรดรอป |
ค่าเริ่มต้น: Kick ผู้เล่น พร้อมเหตุผล #000DETECTED
lua
Suspect.tryToControlAirdrop = function(source)
DropPlayer(source, '#000DETECTED')
endตัวอย่าง: เปลี่ยนเป็น Ban หรือ Log แทน Kick
lua
Suspect.tryToControlAirdrop = function(source)
local identifier = GetPlayerIdentifier(source, 0)
print(('[SUSPECT] %s tried to control airdrop'):format(identifier))
-- BanPlayer(source, identifier, 'Unauthorized airdrop control')
end🏗️ Framework — Config/framework.config.lua
lua
Framework.Libs = function()
local ESX = nil
while ESX == nil do
ESX = exports.es_extended:getSharedObject()
Wait(0)
end
return ESX
endINFO
Resource นี้ใช้ ESX เท่านั้น — ถ้าใช้ QBCore ต้องแก้ไฟล์นี้ให้ return object ของ QBCore แทน
📦 Airdrop Boxes — Config/Airdrop/*.lua
โครงสร้าง
แต่ละไฟล์ใน Config/Airdrop/ = 1 กล่องแอรดรอป
- ไฟล์ที่ ไม่มี
createและcoords→ สร้างด้วย Admin เท่านั้น (ผ่านแผงควบคุม) - ไฟล์ที่ มี
create→ สปอนอัตโนมัติตามเวลา - เพิ่มกล่องใหม่ = สร้างไฟล์
.luaใหม่ในโฟลเดอร์นี้
Fields ทั้งหมด
| Field | Type | Required | คำอธิบาย |
|---|---|---|---|
name | string | ✅ | ชื่อกล่อง (แสดงใน UI และใช้เรียกคำสั่ง Scene) |
mode | string | ✅ | 'normal' = ปกติ / 'gang' = โหมดแข่งแก๊ง |
create | string[] | ❌ | เวลาที่สปอนอัตโนมัติ (รูปแบบ 'HH:MM') — เว้นว่าง = สร้างด้วย Admin เท่านั้น |
coords | vector3[] | ❌* | พิกัดจุดตก — ถ้ามีหลายจุด ระบบสุ่ม 1 จุด (*จำเป็นถ้ามี create) |
registerTime | number | ❌ | (Gang Mode เท่านั้น) เวลาเฟสลงทะเบียน (วินาที) |
model | string | ✅ | Prop Model ของกล่อง (เช่น "prop_mb_crate_01a") |
imageName | string | table | ✅ | ชื่อรูปใน web/assets/img/ (ดูด้านล่าง) |
MaxPlayer | number | ❌ | จำนวนผู้เล่นสูงสุด (Optional) |
Tier | table | ❌ | { Name = 'DIAMOND', Color = 'FF00FFFF' } — แสดง Tier ใน UI (Optional) |
takeable | number | ✅ | เวลาหน่วง (วินาที) ก่อนเก็บของได้ |
removeAfter | number | ✅ | เวลา (วินาที) ที่กล่องหายไปหลังจบกิจกรรม |
circleCloseTime | number | ✅ | เวลา (วินาที) ที่วงโซนหดจนสุด |
startRadius | number | ✅ | รัศมีเริ่มต้นของวงโซน (เมตร) |
allowTeleport | boolean | ✅ | อนุญาตให้วาร์ปเข้าแอรดรอปหรือไม่ |
scoreboard | table | ❌ | ตั้งค่ากระดานคะแนน (ดูด้านล่าง) |
giveToAllJobInCircle | boolean | ❌ | true = คนในวงเดียวกันได้ของด้วย / false = คนที่กดเก็บเท่านั้น |
blip | table | ✅ | { id, color, size } — Blip บนแผนที่ |
paticleFX | table | ✅ | { dict, name } — Particle Effect บนกล่อง |
zoneColor | table | ✅ | { r, g, b, a } — สีวงโซน (RGBA 0-255) |
blacklistItems | string[] | ✅ | ไอเทมที่ห้ามพกเข้าโซน (ถ้ามี = เข้าไม่ได้) |
requireAccess | table | ✅ | { items = {}, jobs = {} } — เงื่อนไขเข้า (ว่าง = ไม่จำกัด) |
items | table[] | ✅ | รายการของรางวัล (ดูด้านล่าง) |
Scene | table | ❌ | ตั้งค่า Cutscene (Gang Mode — ดูด้านล่าง) |
imageName — 2 รูปแบบ
lua
-- รูปแบบ 1: รูปเดียว (ใช้ทั้ง active/inactive)
imageName = 'case-box1',
-- รูปแบบ 2: รูปแยกตามสถานะ
imageName = {
active = 'Active',
inactive = 'non-Active',
},INFO
ชื่อรูปต้องตรงกับไฟล์ .png ใน web/assets/img/
case-box1→web/assets/img/case-box1.png- ถ้าไฟล์ไม่พบ ระบบจะ fallback เป็น
Active.pngหรือnon-Active.png
items — Loot Table
lua
items = {
{ name = 'money', label = 'เงินสด', total = { 100, 500 } }, -- สุ่ม 100-500
{ name = 'weapon_bat', type = 'item_weapon', label = 'ไม้เบสบอล', total = 1 }, -- อาวุธ
{ name = 'mine_orb', label = 'หินแร่', total = 15 }, -- คงที่ 15
{ name = 'gold', label = 'แท่งทอง', total = { 10, 30 } }, -- สุ่ม 10-30
}| Field | Type | คำอธิบาย |
|---|---|---|
name | string | รหัสไอเทมในระบบ (spawn name) |
type | string | (Optional) ใส่ 'item_weapon' ถ้าเป็นอาวุธ |
label | string | ชื่อที่แสดงใน UI |
total | number | number[] | จำนวน — ตัวเลขคงที่ หรือ {min, max} สำหรับสุ่ม |
WARNING
name ต้องตรงกับชื่อไอเทมใน Inventory ของคุณ — ถ้าชื่อไม่ตรง ไอเทมจะไม่ spawn
scoreboard — กระดานคะแนน
lua
scoreboard = {
title = 'Airdrop Gang', -- หัวข้อตารางคะแนน
tier = 3, -- ระดับ Tier (1, 2, 3...)
maxEntries = 20, -- จำนวนอันดับสูงสุดที่แสดง
hintKey = 'Enter', -- คีย์ที่แนะนำสำหรับเปิด/ปิด Scoreboard
hideDelay = 10 -- เวลา (วินาที) ที่ซ่อน Scoreboard อัตโนมัติ
}blip — แผนที่
lua
blip = {
id = 307, -- Sprite ID (ดูที่ https://docs.fivem.net/docs/game-references/blips/)
color = 2, -- สี (2 = เขียว, ดูรหัสสีที่ docs.fivem.net)
size = 0.8 -- ขนาด (0.1 - 1.5)
}requireAccess — เงื่อนไขเข้า
lua
requireAccess = {
items = { 'mine_orb', 'gold' }, -- ต้องมีไอเทมเหล่านี้ (ว่าง = ไม่จำกัด)
jobs = { 'gang_yellow' }, -- ต้องเป็น Job/Gang นี้ (ว่าง = ทุก Job)
}INFO
ทั้งสองเงื่อนไขทำงานแบบ AND — ถ้ากำหนดทั้งสอง ผู้เล่นต้องผ่านทั้งสองเงื่อนไข
🏴 gang mode — โหมดแข่งแก๊ง
INFO
Gang Mode เป็นโหมดพิเศษที่ผู้เล่นจากแก๊งต่าง ๆ ลงทะเบียน แข่งกันเก็บของในโซนแยกกัน ต้องมี xcore-gang ติดตั้ง และผู้เล่นต้องอยู่ในแก๊งถึงจะเข้าร่วมได้
การตั้งค่า
สร้างไฟล์ใหม่ใน Config/Airdrop/ แล้วตั้ง mode = 'gang':
lua
table.insert(Airdrop.Boxes, {
name = 'gang',
mode = 'gang',
create = { '12:00', '23:00' }, -- เวลาที่เริ่ม (หรือเว้นว่าง = Admin สร้าง)
coords = {
vector3(-2253.65, 3001.627, 32.809),
},
registerTime = 60, -- เวลาเฟสลงทะเบียน (วินาที)
-- ... fields อื่น ๆ เหมือน normal mode ...
})Flow ของ Gang Mode
กล่อง mode='gang' ถูกสร้าง
↓
เฟสลงทะเบียน (นับถอยหลัง registerTime วินาที)
→ ผู้เล่นกด JOIN บน NUI → ระบบตรวจสอบแก๊งจาก xcore-gang
↓
หมดเวลา → จับคู่ทีมละ 2 แก๊ง → แต่ละคู่ = 1 ห้อง
↓
แต่ละห้อง: แยก RoutingBucket + สร้างกล่องของแยก
→ ย้ายผู้เล่นเข้าห้อง + วาร์ป
↓
ในห้อง: ต่อสู้ 2 ทีม, วงหดโซน, เปิดกล่อง, เก็บ kill/scoreboard/MVP
↓
จบ (ของหมด/หมดเวลา) → ย้ายกลับจุดเดิม → ลบกล่องกฎของ Gang Mode
| สถานการณ์ | ผล |
|---|---|
| ผู้เล่นไม่มีแก๊งกด JOIN | ปฏิเสธ + แจ้งเตือน "ต้องมีแก๊งก่อน" |
| แก๊งน้อยกว่า 2 หรือเป็นเลขคี่ | จับคู่เท่าที่มี — ห้องที่มีทีมเดียว = เปิดกล่องได้แต่ไม่มีคู่ต่อสู้ |
| ผู้เล่นหลุดระหว่างแข่ง | คืน Bucket, ลบออกจากทีม/ห้อง |
| Resource stop | คืน Bucket ทุกคน + เคลียร์ session ทั้งหมด |
Scene — Cutscene แสดงผลผู้ชนะ (Top 5 MVP)
lua
Scene = {
prop = {
{
pos = vector4(-2217.29, 3010.715, 31.809, 60.50), -- X, Y, Z, Heading
model = "rcnk_podium_01", -- Prop Model
},
},
Cam = {
pos = vector4(-2221.94, 3008.304, 32.810, 284.28), -- ตำแหน่งกล้อง
focus = vector4(-2217.20, 3010.881, 33.391, 151.22), -- จุดที่กล้องโฟกัส
},
Ped = {
[1] = { pos = vector4(-2217.20, 3010.881, 33.391, 151.22) }, -- อันดับ 1 (MVP)
[2] = { pos = vector4(-2218.60, 3011.547, 33.183, 157.38) }, -- อันดับ 2
[3] = { pos = vector4(-2215.83, 3010.096, 33.041, 152.61) }, -- อันดับ 3
[4] = { pos = vector4(-2219.71, 3012.368, 32.810, 147.97) }, -- อันดับ 4
[5] = { pos = vector4(-2214.71, 3009.549, 32.810, 153.45) }, -- อันดับ 5
},
Anim = {
{
dict = "rcmbarry3igtalk", -- Animation Dict
name = "rcmbarry3igtalk", -- Animation Name
},
},
}INFO
- ระบบจะดึง skin จริง ของผู้เล่น Top 5 มาแสดงในฉาก (อ่านจากตาราง
usersผ่านoxmysql) - ถ้าใส่
Animหลายท่า ระบบจะสุ่มท่าให้ Ped แต่ละตัว - Prop
rcnk_podium_01/02/03อยู่ในstream/ของ Resource (โหลดอัตโนมัติ)
ทดสอบ Scene
ใช้คำสั่ง /Scene [ชื่อกล่อง] (Admin) เพื่อทดสอบ Cutscene โดยไม่ต้องเล่นจริง
/Scene gang→ เล่น Scene ของกล่องชื่อ "gang"/Scene stopหรือ/Scene clear→ หยุดและล้าง Scene
🎯 Commands
| คำสั่ง | Default | สิทธิ์ | คำอธิบาย |
|---|---|---|---|
/airdrop | Airdrop.CommandControl | Admin | เปิดแผงควบคุมแอรดรอป (สร้าง/ลบ/วาร์ป) |
/Scene | Airdrop.SceneTest | Admin | ทดสอบ Cutscene — /Scene [name] หรือ /Scene stop |
/goairdrop | Player.TeleportOptions.Command | ทุกผู้เล่น | เปิดแผงวาร์ปไปแอรดรอป (ถ้า Enable = true) |
INFO
/airdropและ/Sceneจะถูก register เฉพาะเมื่อผู้เล่นมีสิทธิ์ (อยู่ในPermission.AllowControlหรือPermission.BypassJob)/goairdropใช้ได้กับผู้เล่นทุกคน — แต่จะวาร์ปได้เฉพาะแอรดรอปที่allowTeleport = true
📍 Locations & Spawning
| ประเภท | วิธีกำหนด |
|---|---|
| สปอนอัตโนมัติ | ตั้ง create = { '12:00', '23:00' } + coords — ระบบจะสุ่ม 1 พิกัดจาก coords ทุกครั้งที่ถึงเวลา |
| สร้างด้วย Admin | เว้น create และ coords ว่าง — ใช้ /airdrop → เลือกกล่อง → สร้าง |
| หลายพิกัด | ใส่ coords หลายจุด — ระบบสุ่ม 1 จุดเมื่อถึงเวลา |
ตัวอย่าง: กล่องที่สปอน 2 เวลาต่อวัน
lua
create = { '12:00', '23:00' },
coords = {
vector3(-640.65, -841.37, 24.86),
vector3(167.84, -798.81, 30.98), -- สุ่ม 1 จาก 2
},🖥️ UI Settings
| ส่วน UI | ควบคุมโดย |
|---|---|
| Active Strip (แถบนับเวลา) | Airdrop.ShowTimer |
| Timer บนกล่อง | Airdrop.ShowTimer |
| Scoreboard | scoreboard.hideDelay (ซ่อนอัตโนมัติ), scoreboard.hintKey (คีย์เปิด) |
| Gang Mode Overlay | แสดงอัตโนมัติเมื่ออยู่ใน Gang Mode |
| Winner Screen | แสดงอัตโนมัติเมื่อ Gang Mode จบ |
| Toast แจ้งเตือน | AFU.Toastify (ใน Player.afterTeleportToAirdrop) |
INFO
UI ทั้งหมดอยู่ใน web/ — ถ้าต้องการปรับดีไซน์ แก้ web/assets/style.css และ web/assets/ui.app.js
🗄️ Database
INFO
ไม่จำเป็นต้อง Import SQL — Resource นี้ไม่สร้างตารางเอง
ข้อมูลที่ใช้:
| ตาราง | คอลัมน์ | วัตถุประสงค์ |
|---|---|---|
users (ของ framework) | skin | ดึง skin จริงของผู้เล่น Top 5 สำหรับ Cutscene |
ถ้า framework ของคุณไม่เก็บ skin ในตาราง users → Cutscene จะใช้ skin default ของตัวละครแทน (ไม่เกิด error)
🚀 Exports & Events
INFO
Resource นี้ ไม่มี Exports สำหรับ Resource อื่นเรียกใช้
Events ทั้งหมดภายใน (AFU.SuperAirdrop:*) เป็น internal — ไม่จำเป็นต้องใช้จากภายนอก
🐛 Troubleshooting
| ปัญหา | สาเหตุ | วิธีแก้ |
|---|---|---|
| Resource ไม่ start | ขาด dependencies | ตรวจสอบว่า oxmysql, es_extended, AFU.Notify, AFU.Toastify, AFU.Inventory start ก่อน |
ไม่เห็นคำสั่ง /airdrop | Steam ID ไม่ตรง | ตรวจสอบ Permission.AllowControl — ใช้ GetPlayerIdentifier(-1, 0) ใน F8 |
| กล่องไม่สปอนตามเวลา | create ว่างหรือเวลาไม่ถูก | ตรวจสอบ create = { 'HH:MM' } — ใช้ 24 ชั่วโมง |
| ไอเทมไม่ spawn | name ไม่ตรง | ตรวจสอบว่า name ใน items ตรงกับ spawn name ใน Inventory |
| รูปไอเทมไม่แสดง | Path.Inventory ผิด | แก้ path ให้ชี้ไปยังโฟลเดอร์รูปไอเทมที่ถูกต้อง |
| Gang Mode ไม่ทำงาน | ขาด xcore-gang หรือผู้เล่นไม่มีแก๊ง | ตรวจสอบว่า xcore-gang start แล้ว และผู้เล่นอยู่ในแก๊ง |
| Revive ไม่ทำงาน | LTS_Ambulance ไม่ start | ติดตั้ง LTS_Ambulance หรือแก้ Airdrop.Revive.revive ให้เรียก event ของระบบคุณ |
| Cutscene ไม่แสดง skin | ไม่มี oxmysql หรือไม่มี skin ใน users | ติดตั้ง oxmysql + ตรวจสอบว่า framework เก็บ skin |
| Discord ไม่แจ้งเตือน | Webhook/Token ผิด | ตรวจสอบ URL และ Token ใน discord.config.lua |
ผู้เล่นถูก kick เมื่อเปิด /airdrop | ไม่มีสิทธิ์ใน Permission.AllowControl | เพิ่ม Steam ID ของคุณ |
| เสียงประกาศไม่ดัง | Airdrop.PlayAnnouncementSound = false | ตั้งเป็น true |
| วงโซนไม่หด | Airdrop.RestrictZone.enable = false | ตั้งเป็น true |
License
ถ้า Resource ไม่ทำงานแม้ dependencies ครบทั้งหมด → ตรวจสอบ License — Resource นี้ต้องการ License ที่ถูกต้องจาก AFU Squad
📋 Quick Start Checklist
- [ ] ติดตั้ง dependencies ทั้งหมด (
oxmysql,es_extended,AFU.Notify,AFU.Toastify,AFU.Inventory,xcore-gang) - [ ] เพิ่ม Steam ID ใน
Config/permission.config.lua - [ ] ตั้ง
Airdrop.Debug = falseในConfig/airdrop.settings.lua - [ ] ตั้งค่า
Config/discord.config.lua(หรือเว้นว่างถ้าไม่ใช้) - [ ] แก้ไข/สร้างไฟล์ใน
Config/Airdrop/ตามต้องการ - [ ] ตรวจสอบ
Path.Inventoryให้ตรงกับ Inventory ของคุณ - [ ] Start เซิร์ฟเวอร์ →
/airdrop→ สร้างกล่อง → ทดสอบ