Wróć do bloga
Guide7 min czytania

Magazyny i skrytki FiveM: Skrypt na współdzielone schowki

Zaprojektuj wspólne magazyny i schowki w FiveM. Schowki gangów, szafki frakcyjne, limity wagi i najlepsze skrypty magazynów dla ox_inventory i nie tylko.

Agency Scripts

Założyciel i główny deweloper w Agency Scripts

Systemy magazynów na serwerach roleplay

Każdy serwer roleplay potrzebuje solidnego systemu magazynowania wykraczającego poza podstawowy ekwipunek gracza. Osobiste ekwipunki mają ograniczoną pojemność, a gracze gromadzą przedmioty z prac, craftingu, handlu i łupów, które chcą zachować, ale nie mogą nosić ich przy sobie przez cały czas. Systemy magazynów i schowków rozwiązują ten problem, zapewniając fizyczne lokalizacje w świecie gry, w których gracze mogą deponować i odbierać przedmioty. Systemy te służą wielu celom rozgrywki: osobiste szafki magazynowe dają pojedynczym graczom dodatkową przestrzeń, magazyny organizacji pozwalają gangom i firmom łączyć zasoby, a wynajmowane jednostki magazynowe tworzą rynek nieruchomości generujący cykliczny dochód dla gospodarki serwera. Wdrożenie techniczne musi obsługiwać jednoczesny dostęp, limity wagi i slotów, uprawnienia dostępu oraz integrację z istniejącym frameworkiem ekwipunku na Twoim serwerze.

Schemat bazy danych dla schowków

Zaprojektuj bazę danych schowków tak, aby obsługiwała wiele typów magazynów z elastyczną strukturą własności i kontrolą dostępu. Każdy schowek wymaga unikalnego identyfikatora, referencji właściciela (którym może być citizenid gracza lub identyfikator organizacji), limitów pojemności określonych wagą i liczbą slotów oraz właściwych danych ekwipunku serializowanych jako JSON. Dołącz tabelę systemu wynajmu śledzącą status płatności za wynajmowane jednostki, z automatyczną blokadą dostępu w przypadku zaległości w czynszu. Tabela kontroli dostępu pozwala właścicielom nadawać innym graczom uprawnienia do korzystania z ich schowków:

CREATE TABLE IF NOT EXISTS storage_units (
    id INT AUTO_INCREMENT PRIMARY KEY,
    unit_id VARCHAR(50) UNIQUE NOT NULL,
    unit_type ENUM('personal', 'organization', 'rental', 'property') DEFAULT 'personal',
    owner_id VARCHAR(50) NOT NULL,
    label VARCHAR(100) DEFAULT NULL,
    max_weight INT DEFAULT 100000,
    max_slots INT DEFAULT 50,
    items LONGTEXT DEFAULT '[]',
    location VARCHAR(50) NOT NULL,
    is_locked BOOLEAN DEFAULT FALSE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_owner (owner_id),
    INDEX idx_location (location),
    INDEX idx_type (unit_type)
);

CREATE TABLE IF NOT EXISTS storage_access (
    unit_id VARCHAR(50) NOT NULL,
    citizenid VARCHAR(50) NOT NULL,
    permission ENUM('view', 'deposit', 'full') DEFAULT 'deposit',
    granted_by VARCHAR(50) NOT NULL,
    granted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (unit_id, citizenid)
);

CREATE TABLE IF NOT EXISTS storage_rentals (
    unit_id VARCHAR(50) PRIMARY KEY,
    tenant_id VARCHAR(50) NOT NULL,
    rent_amount INT NOT NULL,
    rent_interval ENUM('daily', 'weekly') DEFAULT 'weekly',
    last_paid TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    next_due TIMESTAMP NOT NULL,
    is_overdue BOOLEAN DEFAULT FALSE,
    INDEX idx_tenant (tenant_id),
    INDEX idx_due (next_due)
);

Konfiguracja lokalizacji magazynu

Zdefiniuj lokalizacje magazynów na mapie z uwzględnieniem kontekstu roleplay. Obszary przemysłowe w pobliżu portu świetnie sprawdzają się w przypadku dużych magazynów komercyjnych. Dzielnice mieszkalne pasują do prywatnych skrytek magazynowych. Podziemne lub ukryte miejsca posłużą jako schowki dla gangów. Każda lokalizacja wymaga konfiguracji pozycji fizycznej, typu dostępnych jednostek magazynowych, cen wynajmu oraz interior shella ładowanego po wejściu gracza. Użyj interior proxies z GTA lub własnych MLO dla wnętrz magazynów, aby gracze fizycznie wchodzili do budynku, zamiast po prostu otwierać menu na ulicy:

Config.StorageLocations = {
    ['industrial_1'] = {
        label = 'Elysian Island Storage',
        entrance = vector3(-96.67, -1601.77, 29.41),
        blipSprite = 473,
        blipColor = 2,
        units = {
            {id = 'ind1_small_1', type = 'rental', size = 'small', maxWeight = 50000, maxSlots = 30, rent = 500, interval = 'weekly'},
            {id = 'ind1_small_2', type = 'rental', size = 'small', maxWeight = 50000, maxSlots = 30, rent = 500, interval = 'weekly'},
            {id = 'ind1_medium_1', type = 'rental', size = 'medium', maxWeight = 150000, maxSlots = 75, rent = 1500, interval = 'weekly'},
            {id = 'ind1_large_1', type = 'rental', size = 'large', maxWeight = 300000, maxSlots = 150, rent = 3500, interval = 'weekly'},
        },
    },
    ['gang_southside'] = {
        label = 'Southside Stash',
        entrance = vector3(93.41, -1961.08, 20.82),
        blipSprite = 0,  -- No blip for hidden locations
        restrictedTo = {'ballas', 'gsf'},  -- Gang-only access
        units = {
            {id = 'gang_ss_main', type = 'organization', size = 'large', maxWeight = 500000, maxSlots = 200},
        },
    },
}

Integracja z ekwipunkiem

Twój system magazynów musi płynnie integrować się z dowolnym frameworkiem ekwipunku używanym na twoim serwerze, niezależnie od tego, czy jest to ox_inventory, qb-inventory, czy rozwiązanie autorskie. Interfejs schowka powinien odzwierciedlać dobrze znany interfejs ekwipunku, aby gracze intuicyjnie wiedzieli, jak przenosić przedmioty między swoim ekwipunkiem a magazynem. Wdróż przenoszenie przedmiotów metodą przeciągnij i upuść z możliwością wyboru ilości dla przedmiotów, które można łączyć w stosy. Gdy gracz otwiera magazyn, załaduj przechowywane przedmioty z bazy danych i wyświetl je obok osobistego ekwipunku gracza. Wszelkie operacje przenoszenia przedmiotów muszą być weryfikowane po stronie serwera, aby zapobiec exploitom duplikowania. Sprawdź, czy ekwipunek źródłowy rzeczywiście zawiera przenoszony przedmiot, czy miejsce docelowe ma wystarczającą pojemność pod względem wagi oraz slotów i czy przedmiot nie jest oznaczony jako niezbywalny lub przypisany do gracza. Używaj transakcji bazy danych do transferów przedmiotów, aby usunięcie z jednego ekwipunku i dodanie do drugiego odbywało się atomowo:

RegisterNetEvent('storage:server:moveItem', function(unitId, direction, itemName, amount, fromSlot, toSlot)
    local src = source
    local Player = QBCore.Functions.GetPlayer(src)
    if not Player then return end

    -- Validate access
    if not HasStorageAccess(Player.PlayerData.citizenid, unitId, 'full') then
        TriggerClientEvent('QBCore:Notify', src, 'Access denied', 'error')
        return
    end

    -- Load storage unit
    local unit = GetStorageUnit(unitId)
    if not unit or unit.is_locked then
        TriggerClientEvent('QBCore:Notify', src, 'Storage is locked', 'error')
        return
    end

    amount = math.floor(tonumber(amount) or 0)
    if amount <= 0 then return end

    if direction == 'to_storage' then
        -- Player -> Storage
        local playerItem = Player.Functions.GetItemBySlot(fromSlot)
        if not playerItem or playerItem.name ~= itemName then return end
        if playerItem.amount < amount then return end

        local itemWeight = QBCore.Shared.Items[itemName].weight * amount
        local currentWeight = CalculateStorageWeight(unit.items)

        if currentWeight + itemWeight > unit.max_weight then
            TriggerClientEvent('QBCore:Notify', src, 'Storage is full (weight)', 'error')
            return
        end

        -- Atomic transfer
        Player.Functions.RemoveItem(itemName, amount, fromSlot)
        AddItemToStorage(unitId, itemName, amount, playerItem.info, toSlot)

    elseif direction == 'from_storage' then
        -- Storage -> Player
        local storageItem = GetStorageItemBySlot(unit.items, fromSlot)
        if not storageItem or storageItem.name ~= itemName then return end
        if storageItem.amount < amount then return end

        if not Player.Functions.AddItem(itemName, amount, toSlot, storageItem.info) then
            TriggerClientEvent('QBCore:Notify', src, 'Inventory full', 'error')
            return
        end

        RemoveItemFromStorage(unitId, itemName, amount, fromSlot)
    end

    -- Refresh UI for all viewers
    RefreshStorageViewers(unitId)
end)

System płatności za wynajem

Wynajmowane magazyny wymagają systemu cyklicznych płatności, który automatycznie pobiera opłaty od najemców i obsługuje zaległości. Uruchom zaplanowane zadanie po stronie serwera, które w regularnych odstępach czasu sprawdza wszystkie jednostki, przetwarza należne płatności i blokuje jednostki z zaległościami. Gdy płatność jest wymagalna, spróbuj pobrać kwotę czynszu z konta bankowego najemcy. Jeśli na koncie brakuje środków, oznacz wynajem jako zaległy i wyślij graczowi powiadomienie. Daj zalegającym najemcom kilkudniowy okres karencji przed zablokowaniem ich jednostki. Zablokowana jednostka uniemożliwia najemcy dostęp do przechowywanych przedmiotów do momentu uregulowania zaległości. Jeśli wynajem pozostaje nieopłacony po przekroczeniu dłuższego terminu, zawartość może zostać zlicytowana lub usunięta, co stworzy ciekawy dodatkowy gameplay. Wdróż rejestr historii płatności, aby najemcy mogli sprawdzać naliczone opłaty i daty płatności w interfejsie zarządzania magazynem.

Organizacja i wspólny magazyn

Organizacje, takie jak gangi, firmy i departamenty rządowe, potrzebują wspólnego magazynu, do którego wielu członków ma dostęp z odpowiednimi poziomami uprawnień. Lider organizacji ma pełną kontrolę i może dodawać lub usuwać członków z listy dostępu, ustawiać indywidualne poziomy uprawnień oraz przeglądać dzienniki audytu wszystkich zdeponowanych lub wypłaconych przedmiotów. Zwykli członkowie mogą mieć dostęp wyłącznie do wpłat, gdzie mogą dodawać przedmioty, ale nie mogą ich wyjmować, co jest przydatne dla członków gangu dokładających materiały do wspólnej puli. Zaufani członkowie otrzymują pełny dostęp zarówno do wpłat, jak i wypłat. Zaimplementuj szczegółowy dziennik audytu, który rejestruje każdy ruch przedmiotu do i ze wspólnego magazynu, w tym gracza wykonującego akcję, przedmiot, ilość oraz znacznik czasu. Taki rejestr audytu pomaga liderom organizacji śledzić, kto wnosi wkład, a kto może kraść ze wspólnej puli. Dziennik powinien być widoczny w UI magazynu z opcjami filtrowania według gracza, typu przedmiotu i zakresu dat.

Ulepszenia pojemności i poziomy

Pozwól graczom na ulepszanie pojemności magazynu za pomocą systemu ulepszeń, który stanowi zachętę do progresji. Zacznij od małych jednostek magazynowych o ograniczonej wadze i liczbie slotów, a następnie zaoferuj poziomy ulepszeń, które zwiększają te limity za określoną cenę. Każdy poziom ulepszenia powinien kosztować stopniowo więcej, tworząc istotny money sink dla gospodarki serwera. Ulepszenia mogą zwiększać maksymalną wagę, dodawać dodatkowe sloty lub odblokowywać specjalne funkcje, takie jak magazyn z kontrolowaną temperaturą dla łatwo psujących się przedmiotów czy wzmocnione zabezpieczenia, które uodparniają jednostkę na włamania. Wyświetlaj bieżący poziom i dostępne ulepszenia w interfejsie zarządzania magazynem, wraz z przejrzystymi cenami i korzyściami, jakie zapewnia każdy poziom. Rozważ powiązanie niektórych poziomów ulepszeń z osiągnięciami lub kamieniami milowymi czasu gry zamiast wyłącznie kosztu pieniężnego, nagradzając oddanych graczy korzyściami magazynowymi premium, których nie mogą po prostu kupić za pieniądze.

Bezpieczeństwo i mechanika włamań

Jednostki magazynowe nie powinny być całkowicie bezpieczne przed działalnością przestępczą. Wdróż mechanikę włamań, która pozwala graczom próbować uzyskać dostęp do cudzych magazynów, tworząc ryzyko dla przechowywanych kosztowności i generując scenariusze napadów. Proces włamania powinien wymagać określonych narzędzi, takich jak wytrychy lub elektroniczne urządzenia obejściowe, zajmować sporo czasu z widocznymi i słyszalnymi sygnałami ostrzegającymi pobliskich graczy oraz uruchamiać powiadomienie dla właściciela magazynu i służb porządkowych. Magazyny wyższego poziomu oraz te z ulepszeniami bezpieczeństwa powinny być coraz trudniejsze do sforsowania, wymagając lepszych narzędzi i więcej czasu. Jeśli włamanie się powiedzie, przestępca uzyskuje tymczasowy dostęp do ekwipunku magazynu i może zabrać przedmioty, ale system powinien rejestrować włamanie i dostarczać właścicielowi dowody, takie jak częściowe identyfikatory, które może zbadać policja. Tworzy to pełną pętlę rozgrywki wokół nalotów na magazyny, śledztwa i konsekwencji, co wzbogaca dynamikę świata przestępczego i służb porządkowych na serwerze.

Gotowy, aby zacząć?

Kup skrypty w naszym sklepie lub dołącz do Discorda, aby uzyskać wsparcie, aktualizacje i sprawdzić nadchodzące nowości.