Skip to content

🚀 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.lua

2. 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. เริ่มต้นใช้งาน

  1. แก้ไข Config/permission.config.lua — เพิ่ม Steam ID ของคุณ (ดูหัวข้อ Permissions)
  2. แก้ไข Config/Airdrop/*.lua — ตั้งค่ากล่องแอรดรอปที่ต้องการ (ดูหัวข้อ Airdrop Boxes)
  3. ตรวจสอบ Config/discord.config.lua — ตั้งค่า Webhook และ Token (ดูหัวข้อ Discord)
  4. Start เซิร์ฟเวอร์
  5. ใช้คำสั่ง /airdrop (หรือชื่อที่แก้) เพื่อเปิดแผงควบคุม

⚙️ Configuration

🌐 Global Settings — Config/airdrop.settings.lua

ตั้งค่าระดับระบบทั้งหมด:

ตัวแปรTypeDefaultคำอธิบาย
Airdrop.BlockVehiclebooleanfalsetrue = ห้ามขับยานพาหนะเข้าโซนแอรดรอป / false = อนุญาต
Airdrop.CommandControlstring'airdrop'ชื่อคำสั่งสำหรับเปิดแผงควบคุมแอรดรอป (Admin เท่านั้น)
Airdrop.SceneTeststring'Scene'ชื่อคำสั่งสำหรับทดสอบ Cutscene (Admin เท่านั้น)
Airdrop.UsingRemoveTimenumber10000เวลา (ms) ที่รอหลังจากจบแอรดรอป ก่อนลบกล่องออกจากโลก
Airdrop.delayBeforeExitnumber6000เวลา (ms) ที่รอหลังจากจบแอรดรอป ก่อนย้ายผู้เล่นกลับจุดเดิม
Airdrop.ShowTimerbooleantrueแสดง UI นับเวลาแอรดรอป (แถบ Active Strip) หรือไม่
Airdrop.PlayAnnouncementSoundbooleantrueเปิด/ปิดเสียงประกาศเมื่อแอรดรอปเริ่ม
Airdrop.Debugbooleantrueเปิดโหมด Debug (แสดงข้อมูลใน console) — ควรปิด false ในเซิร์ฟเวอร์จริง
Airdrop.ShuffleItemsInAirdropbooleantrueสลับตำแหน่งไอเทมในกล่องก่อนเปิด
Airdrop.UseAirdropInventorybooleanfalsetrue = เปิด UI ให้กดเลือกหยิบไอเทม / false = หลอดเต็ม 100% แล้วไอเทมทั้งหมดเข้าตัวทันทีและจบแอร์ดรอป
Airdrop.limitTakeItem.enablebooleanfalseเปิด/ปิดการจำกัดจำนวนไอเทมที่เก็บได้ต่อรอบ
Airdrop.limitTakeItem.limitnumber2จำนวนไอเทมสูงสุดที่เก็บได้ต่อรอบ (ใช้เมื่อ enable = true)
Airdrop.RestrictZone.enablebooleantrueเปิด/ปิดโซนอันตราย (ลดเลือดเมื่อวงหด)
Airdrop.RestrictZone.HealthDecreasenumber1จำนวนเลือดที่ลดลงต่อวินาทีเมื่ออยู่ในวงที่หดเข้ามา
Airdrop.RestrictZone.closestRingnumber4.00รัศมี (เมตร) ของวงในสุดที่เริ่มทำอันตราย
Airdrop.RestrictZone.effectNamestring'DeathFailNeutralIn'ชื่อ Post-Process FX ที่แสดงเมื่ออยู่ในโซนอันตราย
Airdrop.Revive.enablebooleantrueเปิด/ปิดระบบฟื้นอัตโนมัติในแอรดรอป
Airdrop.Revive.delaynumber2000เวลา (ms) หลังตายก่อนฟื้นอัตโนมัติ
Airdrop.Revive.revivefunctionฟังก์ชันที่เรียกเมื่อฟื้น (ค่าเริ่มต้นเรียก LTS_Ambulance:revive)
Airdrop.Revive.afterfunctionฟังก์ชันที่เรียกหลังฟื้นเสร็จ (ว่างไว้ได้)
Airdrop.Heal.Enablebooleantrueเปิด/ปิดระบบฮีลอัตโนมัติในแอรดรอป
Airdrop.Heal.delaynumber4000เวลา (ms) หลังเข้าโซนก่อนฮีลเต็ม
Airdrop.Heal.healfunctionฟังก์ชันที่เรียกเมื่อฮีล (ค่าเริ่มต้นฮีลเต็มเลือด)
Airdrop.Heal.afterfunctionฟังก์ชันที่เรียกหลังฮีลเสร็จ (ว่างไว้ได้)

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

ตั้งค่าพฤติกรรมผู้เล่นในแอรดรอป:

ตัวแปรTypeDefaultคำอธิบาย
Player.OpenAirdropKeynumber38คีย์สำหรับเปิดกล่องแอรดรอป (38 = E) — ดูรหัสคีย์ที่ docs.fivem.net
Player.TimeOpenAirdropnumber5000เวลา (ms) ที่ต้องกดคีย์ค้างเพื่อเปิดกล่อง
Player.distanceOpenAirdropnumber2.5ระยะ (เมตร) ที่สามารถกดเปิดกล่องได้
Player.DelayTakeItemnumber0เวลาหน่วง (ms) ก่อนเก็บไอเทมได้
Player.freezeTimeAfterTeleportnumber10000เวลา (ms) ที่แช่แข็งผู้เล่นหลังวาร์ปไปแอรดรอป
Player.Animation.dictstringAnimation Dictionary สำหรับท่าเก็บไอเทม
Player.Animation.animstringAnimation Name สำหรับท่าเก็บไอเทม
Player.TeleportOptions.Enablebooleantrueเปิด/ปิดคำสั่งวาร์ปไปแอรดรอป
Player.TeleportOptions.Commandstring'goairdrop'ชื่อคำสั่งสำหรับเปิดแผงวาร์ป
Player.blockKeyListnumber[]{38, 72}คีย์ที่ถูกบล็อกเมื่ออยู่ในโซนแอรดรอป
Player.blockTeleportAllowCoordstable[]โซนที่บล็อกการวาร์ป (ดูด้านล่าง)

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.AllowControltable<string, boolean>Steam ID ที่มีสิทธิ์ควบคุมแอรดรอป (เปิดแผง, สร้าง/ลบกล่อง)
Permission.BypassJobtable<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 ของคนอื่น — ผู้เล่นที่ไม่มีสิทธิ์พยายามควบคุมจะถูกจัดการตาม Suspect config

📡 Routers — Config/routers.config.lua

กำหนด Event ที่ Resource จะฟัง:

ตัวแปรTypeDefaultคำอธิบาย
Routers.onPlayerSpawnstring[]{"esx:onPlayerSpawn"}Event ที่เรียกเมื่อผู้เล่นเกิด/Spawn
Routers.onPlayerDeadstring[]{"esx:onPlayerDeath"}Event ที่เรียกเมื่อผู้เล่นตาย
Routers.onServerPlayerSpawnHandlertable<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

ตัวแปรTypeDefaultคำอธิบาย
Path.Inventorystring'nui://AFU.Inventory/web/assets/img/item/'Path ของรูปไอเทม (ต้องตรงกับ Resource Inventory ของคุณ)
Path.FontNamestring'Kanit'ชื่อฟอนต์ที่ใช้ในเกม (ต้องโหลดฟอนต์นั้นใน Resource อื่น)

Path.Inventory

ค่านี้ต้องชี้ไปยังโฟลเดอร์ที่มีรูปไอเทม .png ของคุณ — ถ้าใช้ Inventory อื่นที่ไม่ใช่ AFU.Inventory ต้องแก้ path ให้ตรง


📢 Discord — Config/discord.config.lua

ตัวแปรTypeคำอธิบาย
DiscordWebhookAPI.ItemRewardsstringDiscord Webhook URL สำหรับแจ้งเตือนรางวัล
DiscordWebhookAPI.DiscordTokenstringDiscord Bot Token สำหรับดึง Avatar ของผู้เล่น
DiscordWebhookAPI.DefaultIDstringDiscord 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.tryToControlAirdropfunction(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
end

INFO

Resource นี้ใช้ ESX เท่านั้น — ถ้าใช้ QBCore ต้องแก้ไฟล์นี้ให้ return object ของ QBCore แทน


📦 Airdrop Boxes — Config/Airdrop/*.lua

โครงสร้าง

แต่ละไฟล์ใน Config/Airdrop/ = 1 กล่องแอรดรอป

  • ไฟล์ที่ ไม่มี create และ coords → สร้างด้วย Admin เท่านั้น (ผ่านแผงควบคุม)
  • ไฟล์ที่ มี create → สปอนอัตโนมัติตามเวลา
  • เพิ่มกล่องใหม่ = สร้างไฟล์ .lua ใหม่ในโฟลเดอร์นี้

Fields ทั้งหมด

FieldTypeRequiredคำอธิบาย
namestringชื่อกล่อง (แสดงใน UI และใช้เรียกคำสั่ง Scene)
modestring'normal' = ปกติ / 'gang' = โหมดแข่งแก๊ง
createstring[]เวลาที่สปอนอัตโนมัติ (รูปแบบ 'HH:MM') — เว้นว่าง = สร้างด้วย Admin เท่านั้น
coordsvector3[]❌*พิกัดจุดตก — ถ้ามีหลายจุด ระบบสุ่ม 1 จุด (*จำเป็นถ้ามี create)
registerTimenumber(Gang Mode เท่านั้น) เวลาเฟสลงทะเบียน (วินาที)
modelstringProp Model ของกล่อง (เช่น "prop_mb_crate_01a")
imageNamestring | tableชื่อรูปใน web/assets/img/ (ดูด้านล่าง)
MaxPlayernumberจำนวนผู้เล่นสูงสุด (Optional)
Tiertable{ Name = 'DIAMOND', Color = 'FF00FFFF' } — แสดง Tier ใน UI (Optional)
takeablenumberเวลาหน่วง (วินาที) ก่อนเก็บของได้
removeAfternumberเวลา (วินาที) ที่กล่องหายไปหลังจบกิจกรรม
circleCloseTimenumberเวลา (วินาที) ที่วงโซนหดจนสุด
startRadiusnumberรัศมีเริ่มต้นของวงโซน (เมตร)
allowTeleportbooleanอนุญาตให้วาร์ปเข้าแอรดรอปหรือไม่
scoreboardtableตั้งค่ากระดานคะแนน (ดูด้านล่าง)
giveToAllJobInCirclebooleantrue = คนในวงเดียวกันได้ของด้วย / false = คนที่กดเก็บเท่านั้น
bliptable{ id, color, size } — Blip บนแผนที่
paticleFXtable{ dict, name } — Particle Effect บนกล่อง
zoneColortable{ r, g, b, a } — สีวงโซน (RGBA 0-255)
blacklistItemsstring[]ไอเทมที่ห้ามพกเข้าโซน (ถ้ามี = เข้าไม่ได้)
requireAccesstable{ items = {}, jobs = {} } — เงื่อนไขเข้า (ว่าง = ไม่จำกัด)
itemstable[]รายการของรางวัล (ดูด้านล่าง)
Scenetableตั้งค่า 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-box1web/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
}
FieldTypeคำอธิบาย
namestringรหัสไอเทมในระบบ (spawn name)
typestring(Optional) ใส่ 'item_weapon' ถ้าเป็นอาวุธ
labelstringชื่อที่แสดงใน UI
totalnumber | 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สิทธิ์คำอธิบาย
/airdropAirdrop.CommandControlAdminเปิดแผงควบคุมแอรดรอป (สร้าง/ลบ/วาร์ป)
/SceneAirdrop.SceneTestAdminทดสอบ Cutscene — /Scene [name] หรือ /Scene stop
/goairdropPlayer.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
Scoreboardscoreboard.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 ก่อน
ไม่เห็นคำสั่ง /airdropSteam ID ไม่ตรงตรวจสอบ Permission.AllowControl — ใช้ GetPlayerIdentifier(-1, 0) ใน F8
กล่องไม่สปอนตามเวลาcreate ว่างหรือเวลาไม่ถูกตรวจสอบ create = { 'HH:MM' } — ใช้ 24 ชั่วโมง
ไอเทมไม่ spawnname ไม่ตรงตรวจสอบว่า 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 → สร้างกล่อง → ทดสอบ