System sklepów z bronią FiveM: Skrypty na licencjonowane sklepy z bronią
Stwórz licencjonowany sklep z bronią na swój serwer FiveM. Zezwolenia, weryfikacja przeszłości, amunicja, dodatki i najlepsze skrypty sklepów z bronią do roleplayu.
Agency Scripts
Założyciel i główny deweloper w Agency Scripts
Przegląd architektury sklepów z bronią
System sklepów z bronią na serwerze roleplay FiveM musi łączyć dostępność dla praworządnych obywateli z realistycznymi ograniczeniami, które nadają rozgrywce głębię. Architektura dzieli się na dwie odrębne gałęzie: legalne sklepy, które wymagają licencji na broń, sprawdzania przeszłości i czasowych ograniczeń zakupu, oraz nielegalnych handlarzy na czarnym rynku, którzy sprzedają nienamierzalną broń po wyższych cenach i bez formalności. Oba systemy dzielą tę samą bazową logikę ekwipunku i transakcji, różnią się jednak wymaganiami dostępu i metadanymi przypisanymi do każdej broni. Legalna broń otrzymuje numery seryjne powiązane z citizen ID kupującego, co umożliwia jej namierzenie przez policję, podczas gdy broń z czarnego rynku ma starte numery seryjne, których nie można powiązać z konkretnym graczem. Ta dwutorowa konstrukcja tworzy naturalne napięcie w roleplayu, ponieważ gracze muszą wybierać między wygodą i niższym kosztem legalnych zakupów a anonimowością czarnego rynku.
Schemat bazy danych dla broni i licencji
Twoja baza danych musi śledzić stan magazynowy broni w każdym sklepie, unikalne numery seryjne broni, licencje graczy i historię zakupów. System numerów seryjnych jest kluczowy, ponieważ łączy sklep z bronią z policyjnymi systemami dochodzeniowymi. Każda legalnie zakupiona broń otrzymuje unikalny numer seryjny, który policja może sprawdzić podczas kontroli drogowej lub na miejscu zbrodni. Zaprojektuj schemat tak, aby obsługiwał zarówno zarządzanie magazynem sklepu, jak i śledzenie poszczególnych egzemplarzy broni:
CREATE TABLE IF NOT EXISTS weapon_shops (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
type ENUM('legal', 'blackmarket') DEFAULT 'legal',
coords_x FLOAT NOT NULL,
coords_y FLOAT NOT NULL,
coords_z FLOAT NOT NULL,
is_active BOOLEAN DEFAULT TRUE
);
CREATE TABLE IF NOT EXISTS weapon_inventory (
id INT AUTO_INCREMENT PRIMARY KEY,
shop_id INT NOT NULL,
weapon_name VARCHAR(50) NOT NULL,
label VARCHAR(100) NOT NULL,
price INT NOT NULL,
ammo_price INT DEFAULT 0,
category ENUM('handguns', 'smgs', 'rifles', 'shotguns', 'melee', 'throwables') NOT NULL,
license_required ENUM('none', 'basic', 'advanced', 'military') DEFAULT 'none',
stock INT DEFAULT -1,
INDEX idx_shop (shop_id),
FOREIGN KEY (shop_id) REFERENCES weapon_shops(id)
);
CREATE TABLE IF NOT EXISTS weapon_serials (
serial VARCHAR(20) PRIMARY KEY,
weapon_name VARCHAR(50) NOT NULL,
owner_citizenid VARCHAR(50) DEFAULT NULL,
is_scratched BOOLEAN DEFAULT FALSE,
purchased_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
shop_id INT DEFAULT NULL,
INDEX idx_owner (owner_citizenid)
);
CREATE TABLE IF NOT EXISTS weapon_licenses (
id INT AUTO_INCREMENT PRIMARY KEY,
citizenid VARCHAR(50) NOT NULL,
license_type ENUM('basic', 'advanced', 'military') NOT NULL,
issued_by VARCHAR(50) DEFAULT NULL,
issued_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP NULL,
revoked BOOLEAN DEFAULT FALSE,
UNIQUE KEY unique_license (citizenid, license_type)
);
Ten weapon_serials tabela śledzi każdą broń indywidualnie przez cały jej cykl życia. Gdy gracz kupuje broń legalnie, generowany jest numer seryjny powiązany z jego citizen ID. Jeśli zeszlifuje numer seryjny przy użyciu usługi na czarnym rynku, to is_scratched flaga zmienia się na true i owner_citizenid zostaje unieważnione. Ten stock pole w weapon_inventory używa -1 aby oznaczyć nielimitowany stan magazynowy, podczas gdy wartości dodatnie włączają mechanikę ograniczonej podaży, która tworzy deficyt i napędza popyt na czarnym rynku.
Implementacja legalnego sklepu z bronią
Legalne sklepy z bronią to dla większości graczy podstawowy sposób na zdobycie broni palnej na serwerze roleplay. Wymuszają one posiadanie licencji, nakładają cooldowny na zakupy, aby zapobiec masowemu wykupowaniu, oraz generują możliwe do śledzenia numery seryjne dla każdej sprzedanej broni. UI sklepu powinno wyświetlać broń pogrupowaną według kategorii z wyraźnymi oznaczeniami poziomu wymaganej licencji. Gracze bez odpowiedniej licencji widzą broń, ale nie mogą jej kupić, co motywuje ich do odgrywania procesu zdobywania licencji z urzędnikami. Wdróż system cooldownów, który uniemożliwia graczom zakup większej liczby broni niż określony limit w ciągu rzeczywistego dnia, aby zapobiec gromadzeniu zapasów do odsprzedaży na czarnym rynku:
RegisterNetEvent('weaponshop:server:purchase', function(shopId, weaponName)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if not Player then return end
local citizenid = Player.PlayerData.citizenid
local item = GetShopItem(shopId, weaponName)
if not item then
TriggerClientEvent('QBCore:Notify', src, 'Item not available', 'error')
return
end
-- Check license requirement
if item.license_required ~= 'none' then
local hasLicense = HasValidLicense(citizenid, item.license_required)
if not hasLicense then
TriggerClientEvent('QBCore:Notify', src, 'You need a ' .. item.license_required .. ' weapons license', 'error')
return
end
end
-- Check purchase cooldown
local recentPurchases = GetRecentPurchaseCount(citizenid, 86400) -- last 24h
if recentPurchases >= Config.DailyPurchaseLimit then
TriggerClientEvent('QBCore:Notify', src, 'Daily purchase limit reached', 'error')
return
end
-- Check stock
if item.stock ~= -1 then
if item.stock <= 0 then
TriggerClientEvent('QBCore:Notify', src, 'Out of stock', 'error')
return
end
end
-- Check funds
if Player.PlayerData.money.bank < item.price then
TriggerClientEvent('QBCore:Notify', src, 'Insufficient funds', 'error')
return
end
-- Generate serial number
local serial = GenerateWeaponSerial()
-- Process purchase
Player.Functions.RemoveMoney('bank', item.price, 'weapon-purchase')
Player.Functions.AddItem(weaponName, 1, false, { serial = serial })
-- Record serial
MySQL.insert('INSERT INTO weapon_serials (serial, weapon_name, owner_citizenid, shop_id) VALUES (?, ?, ?, ?)',
{ serial, weaponName, citizenid, shopId })
-- Update stock
if item.stock ~= -1 then
MySQL.update('UPDATE weapon_inventory SET stock = stock - 1 WHERE shop_id = ? AND weapon_name = ?',
{ shopId, weaponName })
end
TriggerClientEvent('QBCore:Notify', src, 'Purchased ' .. item.label .. ' (S/N: ' .. serial .. ')', 'success')
end)
function GenerateWeaponSerial()
local chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'
local serial = ''
for i = 1, 3 do
serial = serial .. chars:sub(math.random(#chars), math.random(#chars))
end
serial = serial .. '-'
for i = 1, 5 do
serial = serial .. tostring(math.random(0, 9))
end
return serial
end
System amunicji i dodatków
Zarządzanie amunicją wprowadza drenaż zasobów, który angażuje graczy w gospodarkę i zapobiega exploitom na nieskończoną amunicję. Zamiast sprzedawać amunicję jako ogólny przedmiot, powiąż ją z konkretnymi typami kalibrów pasującymi do kategorii broni. Pistolet używa nabojów 9mm, SMG może współdzielić 9mm lub używać .45 ACP, a karabiny używają 5.56 lub 7.62 w zależności od modelu. Taki system kalibrów tworzy bardziej realistyczne wrażenia i daje właścicielom sklepów elastyczność w wycenie różnych typów amunicji. Przechowuj liczbę amunicji w metadanych broni, a nie jako oddzielne przedmioty w ekwipunku, aby każda broń śledziła własne załadowane naboje. Dodatki działają według tego samego schematu metadanych, gdzie każdy przedmiot broni przechowuje listę zainstalowanych komponentów, takich jak celowniki, tłumiki, powiększone magazynki i latarki:
Config.AmmoTypes = {
['ammo_9mm'] = { label = '9mm Rounds', price = 5, amount = 24, weapons = {'WEAPON_PISTOL', 'WEAPON_COMBATPISTOL', 'WEAPON_SMG'} },
['ammo_45acp'] = { label = '.45 ACP Rounds', price = 7, amount = 24, weapons = {'WEAPON_APPISTOL', 'WEAPON_MACHINEPISTOL'} },
['ammo_556'] = { label = '5.56 Rounds', price = 12, amount = 30, weapons = {'WEAPON_CARBINERIFLE', 'WEAPON_ASSAULTRIFLE'} },
['ammo_762'] = { label = '7.62 Rounds', price = 15, amount = 30, weapons = {'WEAPON_SNIPERRIFLE', 'WEAPON_MARKSMANRIFLE'} },
['ammo_12gauge'] = { label = '12ga Shells', price = 10, amount = 8, weapons = {'WEAPON_PUMPSHOTGUN', 'WEAPON_ASSAULTSHOTGUN'} },
}
Config.Attachments = {
['attachment_suppressor'] = {
label = 'Suppressor',
price = 8500,
component = 'COMPONENT_AT_PI_SUPP_02',
compatible = {'WEAPON_PISTOL', 'WEAPON_COMBATPISTOL', 'WEAPON_SMG', 'WEAPON_CARBINERIFLE'},
license_required = 'advanced',
},
['attachment_scope'] = {
label = 'Holographic Scope',
price = 3200,
component = 'COMPONENT_AT_SCOPE_MACRO_02',
compatible = {'WEAPON_SMG', 'WEAPON_CARBINERIFLE', 'WEAPON_ASSAULTRIFLE'},
license_required = 'basic',
},
['attachment_extmag'] = {
label = 'Extended Magazine',
price = 4500,
component = 'COMPONENT_CARBINERIFLE_CLIP_02',
compatible = {'WEAPON_CARBINERIFLE', 'WEAPON_ASSAULTRIFLE', 'WEAPON_SMG'},
license_required = 'basic',
},
['attachment_flashlight'] = {
label = 'Flashlight',
price = 1200,
component = 'COMPONENT_AT_AR_FLSH',
compatible = {'WEAPON_PISTOL', 'WEAPON_COMBATPISTOL', 'WEAPON_CARBINERIFLE', 'WEAPON_PUMPSHOTGUN'},
license_required = 'none',
},
}
Gdy gracz kupuje dodatek do broni, zweryfikuj, czy jego aktualna broń znajduje się w compatible listę, sprawdź poziom ich licencji, a następnie dodaj hash komponentu do tabeli metadanych broni. Przy wyposażeniu broni wykonaj iterację po zapisanych komponentach i zastosuj je za pomocą GiveWeaponComponentToPed. Takie podejście gwarantuje, że dodatki zachowują się między sesjami i nie można ich zduplikować poprzez manipulację po stronie klienta.
Mechaniki czarnego rynku
Czarny rynek to miejsce, gdzie kryje się prawdziwa głębia roleplay. W przeciwieństwie do statycznych legalnych sklepów, handlarze z czarnego rynku powinni sprawiać wrażenie niebezpiecznych i ekskluzywnych. Wprowadź rotacyjne lokalizacje handlarzy zmieniające się co kilka godzin, aby gracze potrzebowali wiedzy z pierwszej ręki lub kontaktów, by ich znaleźć. Wymagaj systemu reputacji, w którym nowi gracze muszą budować zaufanie u dealera poprzez mniejsze zakupy, zanim uzyskają dostęp do broni wyższego poziomu. Czarny rynek sprzedaje broń bez numerów seryjnych, zastrzeżony sprzęt wojskowy, którego legalne sklepy nie mogą posiadać na stanie, oraz usługi takie jak zacieranie numerów seryjnych, dzięki którym legalnie kupiona broń staje się niemożliwa do namierzenia. Ustal ceny 2-3x wyższe niż w legalnych sklepach, aby odzwierciedlić premię za ryzyko i stworzyć autentyczne napięcie ekonomiczne między ścieżką legalną a nielegalną:
Config.BlackMarket = {
rotationInterval = 10800, -- 3 hours between location changes
locations = {
{ coords = vector3(89.98, -1810.87, 24.98), heading = 230.0, label = 'Underground Parking' },
{ coords = vector3(1394.27, 1141.48, 114.33), heading = 90.0, label = 'Desert Warehouse' },
{ coords = vector3(-58.21, 6443.31, 31.43), heading = 45.0, label = 'Paleto Docks' },
{ coords = vector3(981.45, -1812.66, 31.14), heading = 180.0, label = 'Industrial Zone' },
},
reputationTiers = {
[0] = { label = 'Unknown', items = {'WEAPON_KNIFE', 'WEAPON_BAT'} },
[1] = { label = 'Associate', items = {'WEAPON_PISTOL', 'WEAPON_MICROSMG', 'ammo_9mm'} },
[2] = { label = 'Trusted', items = {'WEAPON_SMG', 'WEAPON_PUMPSHOTGUN', 'ammo_45acp', 'ammo_12gauge', 'attachment_suppressor'} },
[3] = { label = 'Inner Circle', items = {'WEAPON_ASSAULTRIFLE', 'WEAPON_CARBINERIFLE', 'ammo_556', 'service_scratch_serial'} },
[4] = { label = 'Arms Dealer', items = {'WEAPON_SNIPERRIFLE', 'WEAPON_RPG', 'WEAPON_GRENADELAUNCHER', 'ammo_762', 'armor_heavy'} },
},
priceMultiplier = 2.5,
reputationGainPerPurchase = 0.15,
}
-- Serial scratching service
RegisterNetEvent('blackmarket:server:scratchSerial', function(weaponSlot)
local src = source
local Player = QBCore.Functions.GetPlayer(src)
if not Player then return end
local citizenid = Player.PlayerData.citizenid
local reputation = GetBlackMarketReputation(citizenid)
if reputation < 3 then
TriggerClientEvent('QBCore:Notify', src, 'You don\'t have enough reputation', 'error')
return
end
local item = Player.Functions.GetItemBySlot(weaponSlot)
if not item or not item.info or not item.info.serial then
TriggerClientEvent('QBCore:Notify', src, 'No weapon with serial found', 'error')
return
end
local cost = Config.ScratchSerialPrice -- e.g., 15000
if Player.PlayerData.money.cash < cost then
TriggerClientEvent('QBCore:Notify', src, 'Not enough cash', 'error')
return
end
Player.Functions.RemoveMoney('cash', cost, 'serial-scratch')
-- Update database
MySQL.update('UPDATE weapon_serials SET is_scratched = TRUE, owner_citizenid = NULL WHERE serial = ?',
{ item.info.serial })
-- Update item metadata
item.info.serial = 'SCRATCHED'
Player.Functions.SetInventoryItem(item.name, item.amount, item.info, weaponSlot)
TriggerClientEvent('QBCore:Notify', src, 'Serial number removed', 'success')
end)
System licencji na broń
System licencjonowania tworzy ważną biurokratyczną warstwę roleplayu, która łączy sklepy z bronią z frakcjami rządowymi i organami ścigania. Gracze powinni składać wnioski o pozwolenie na broń w urzędzie miasta lub na posterunku policji, przechodzić weryfikację sprawdzającą ich kartotekę kryminalną oraz potencjalnie brać udział w teście na strzelnicy prowadzonym przez funkcjonariusza. Wdróż trzy poziomy licencji: podstawowy dla pistoletów i strzelb, zaawansowany dla pistoletów maszynowych i karabinów oraz wojskowy dla broni ciężkiej, rzadko wydawanej cywilom. Licencje powinny mieć datę ważności, zazwyczaj 30 dni czasu rzeczywistego, wymagającą odnowienia. Funkcjonariusze policji potrzebują komendy do unieważniania licencji, gdy gracze zostaną skazani za przestępstwa z użyciem przemocy, co natychmiast blokuje im możliwość zakupów w legalnych sklepach i generuje popyt na czarnym rynku. Przechowuj status licencji w bazie danych i sprawdzaj go po stronie serwera przy każdej próbie zakupu: nigdy nie ufaj weryfikacjom po stronie klienta, ponieważ można je trywialnie obejść.
Interfejs sklepu po stronie klienta
NUI sklepu z bronią powinno przypominać profesjonalną witrynę sklepową, z broniami podzielonymi na kategorie w zakładkach. Pokaż każdą broń z jej nazwą, ceną, wymaganą licencją, aktualnym stanem magazynowym oraz małym zdjęciem podglądowym lub ikoną. Dodaj osobną sekcję z amunicją, w której gracze najpierw wybierają broń, a następnie widzą tylko pasujące typy amunicji. Sekcja dodatków działa podobnie, wyświetlając wyłącznie komponenty kompatybilne z aktualnie wyposażoną bronią gracza. Wprowadź system koszyka, który pozwala graczom dodawać wiele przedmiotów i płacić w jednej transakcji, zamiast kupować każdą rzecz osobno. W przypadku UI czarnego rynku zastosuj ciemniejszy wygląd z paskiem postępu reputacji pokazującym, jak blisko kolejnego poziomu znajduje się gracz. Wyświetlaj zablokowane przedmioty jako sylwetki z etykietą wymaganej reputacji, aby gracze wiedzieli, do czego dążą. Oba typy sklepów powinny wywoływać server callback przy otwarciu, aby pobrać aktualne ceny i stan magazynowy w czasie rzeczywistym, zamiast polegać na wartościach z pamięci podręcznej konfiguracji, ponieważ administratorzy mogą dynamicznie dostosowywać ceny do warunków ekonomii serwera.
Anti-exploit i integracja z policją
Sklepy z bronią są głównym celem osób szukających luk, które próbują duplikować drogą broń lub generować przedmioty bez płacenia. Weryfikuj każdy zakup po stronie serwera, sprawdzając stan konta gracza, status licencji, odległość od sklepu oraz cooldown zakupu przed utworzeniem jakichkolwiek przedmiotów. Rejestruj każdą transakcję zakupu broni z identyfikatorami kupującego, numerem seryjnym broni, ID sklepu i znacznikiem czasu, aby administratorzy mogli prześledzić pochodzenie dowolnej broni na serwerze. Zintegruj system z policyjnym MDT, aby funkcjonariusze mogli sprawdzić numer seryjny broni podczas śledztwa i zobaczyć zarejestrowanego właściciela, datę zakupu oraz informację, czy numer seryjny został zeszlifowany. Wdróż eksport rejestru broni generujący raport wszystkich zarejestrowanych broni dla danego obywatela, przydatny podczas kontroli w okresie próbnym. Wprowadź rate limiting dla zdarzeń zakupu, aby zapobiec masowym żądaniom mogącym ominąć weryfikację cooldownu przez race conditions. Wysyłaj powiadomienia webhookiem na Discord, gdy na czarnym rynku sprzedawana jest zaawansowana broń wojskowa, co pozwoli twojemu zespołowi moderacji monitorować nadużycia. Połączenie prawidłowej walidacji po stronie serwera, szczegółowego logowania i integracji z policją tworzy gospodarkę bronią, która jest realistyczna, a zarazem odporna na exploity.