local QBCore = exports['qb-core']:GetCoreObject() local marketItems = {} local pendingOrders = {} local availableLocations = {} local function getMarketItems() if not Config.randomItems then for i=1, #Config.items do local randomStock = math.random(Config.items[i].minStock, Config.items[i].maxStock) marketItems[i] = Config.items[i] marketItems[i].stock = randomStock end TriggerClientEvent("qb-blackmarket_cl:updateMarketItems", -1, marketItems) SetTimeout(Config.reset * 60000, getMarketItems) return end local copyTable = {} for i=1, #Config.items do copyTable[i] = Config.items[i] end local newLen = #copyTable local chosenItems = {} for i=1, Config.randomItems do local randomIndex = math.random(newLen) local randomStock = math.random(copyTable[randomIndex].minStock, copyTable[randomIndex].maxStock) chosenItems[i] = copyTable[randomIndex] chosenItems[i].stock = randomStock table.remove(copyTable, randomIndex) newLen -= 1 end marketItems = chosenItems TriggerClientEvent("qb-blackmarket_cl:updateMarketItems", -1, marketItems) SetTimeout(Config.reset * 60000, getMarketItems) end local function isMarketItem(item) local inMarket = false for i=1, #marketItems do if marketItems[i].item == item then inMarket = true break end end return inMarket end local function checkStock(item, quantity) local hasStock = false for i=1, #marketItems do if marketItems[i].item == item then if marketItems[i].stock >= quantity then hasStock = true end break end end return hasStock end local function getItemPrice(item) local price = 0 for i=1, #marketItems do if marketItems[i].item == item then price = marketItems[i].price break end end return price end local function updateMarketStock(item, quantity) for i=1, #marketItems do if marketItems[i].item == item then marketItems[i].stock -= quantity break end end end local function generateStashId() local id = "GBM_" for i=1, 6 do id = id .. tostring(math.random(0,9)) end return id end local function saveOrderStash(stashId, items, cb) if not stashId or not items then return end for _, item in pairs(items) do item.description = nil end MySQL.insert('INSERT INTO stashitems (stash, items) VALUES (:stash, :items) ON DUPLICATE KEY UPDATE items = :items', { ['stash'] = stashId, ['items'] = json.encode(items) }, function(id) cb(id) end) end local function deleteStash(stashId) MySQL.query("DELETE FROM stashitems WHERE stash = ?", {stashId}) end local function setupAvailableLocations() for i=1, #Config.deliveryLocations do availableLocations[#availableLocations + 1] = i end end local function getRandomAvailLocation() local chosenIndex = math.random(#availableLocations) local locationIndex = availableLocations[chosenIndex] table.remove(availableLocations, chosenIndex) return locationIndex end local function orderComplete(index, stashId) if not pendingOrders[index] or not pendingOrders[index].stashId == stashId then return end if pendingOrders[index].props then for _, v in pairs(pendingOrders[index].props) do local entity = NetworkGetEntityFromNetworkId(v) if DoesEntityExist(entity) then DeleteEntity(entity) end end end if pendingOrders[index].isOpen then TriggerClientEvent("qb-blackmarket_cl:removeLootTarget", -1, index) else TriggerClientEvent("qb-blackmarket_cl:removeLockTarget", -1, index) end deleteStash(pendingOrders[index].stashId) if pendingOrders[index].src then local Player = QBCore.Functions.GetPlayer(pendingOrders[index].src) if Player.PlayerData.citizenid == pendingOrders[index].cid then TriggerClientEvent("qb-blackmarket_cl:orderComplete", pendingOrders[index].src) end end availableLocations[#availableLocations + 1] = index pendingOrders[index] = nil end local function orderReady(index, stashId) if pendingOrders[index].src then local Player = QBCore.Functions.GetPlayer(pendingOrders[index].src) if Player.PlayerData.citizenid == pendingOrders[index].cid then TriggerClientEvent("qb-blackmarket_cl:orderReady", pendingOrders[index].src, index, Config.deliveryLocations[index]) end end SetTimeout(Config.orderTimeout * 60000, function() orderComplete(index, stashId) end) end local function cleanUpProps() for _, v in pairs(pendingOrders) do if v.props then for _, prop in pairs(v.props) do local entity = NetworkGetEntityFromNetworkId(prop) if DoesEntityExist(entity) then DeleteEntity(entity) end end end end end local function cleanUpStashes() for _, v in pairs(pendingOrders) do deleteStash(v.stashId) end end QBCore.Functions.CreateUseableItem(Config.useItem, function(source) local Player = QBCore.Functions.GetPlayer(source) if not Player or not Player.Functions.GetItemByName(Config.useItem) then return end TriggerClientEvent("qb-blackmarket_cl:openUI", source) end) QBCore.Functions.CreateCallback("qb-blackmarket_sv:getMarketItems", function(src, cb) cb(marketItems) end) QBCore.Functions.CreateCallback("qb-blackmarket_sv:attemptOrder", function(src, cb, order) if not order or #order == 0 then return end local cost = 0 local Player = QBCore.Functions.GetPlayer(src) if not Player or not Player.Functions.GetItemByName(Config.useItem) then return end local numOfOrders = 0 for _, v in pairs(pendingOrders) do if v.src == src or v.cid == cid then cb({success = false, notif = "Ordre fejlet", error = "Spilleren har allerede en ordre"}) return end numOfOrders += 1 end if numOfOrders >= Config.maxOrderQueue then cb({success = false, notif = Config.notifText.maxOrder}) return end local playerOrder = {} for i=1, #order do local itemQuant = tonumber(order[i].quantity) if not isMarketItem(order[i].item) then cb({success = false, notif = "Ordre fejlet", error = "Ugyldig genstand fundet"}) return end if not checkStock(order[i].item, itemQuant) then cb({success = false, notif = Config.notifText.insufficientStock}) return end if playerOrder[order[i].item] then cb({success = false, notif = "Ordre fejlet", error = "Ordre har flere af samme genstand"}) return end playerOrder[order[i].item] = itemQuant cost += getItemPrice(order[i].item) * itemQuant end if Player.Functions.RemoveMoney(Config.paymentType, cost) then local locationIndex = getRandomAvailLocation() local deliveryMins = math.random(Config.deliveryTime.min, Config.deliveryTime.max) local deliveryTime = os.time() + (deliveryMins * 60) local stashId = generateStashId() pendingOrders[locationIndex] = { src = src, cid = Player.PlayerData.citizenid, order = playerOrder, deliveryTime = deliveryTime, stashId = stashId, lockInProgress = false, lootInProgress = false, isOpen = false, isLooted = false, } for k,v in pairs(playerOrder) do updateMarketStock(k, v) end SetTimeout(deliveryMins * 60000, function() orderReady(locationIndex, stashId) end) TriggerClientEvent("qb-blackmarket_cl:updateStock", -1, marketItems, src) cb({success = true, notif = Config.notifText.orderSuccess, epochTime = deliveryTime}) else cb({success = false, notif = Config.notifText.cantAfford}) end end) RegisterNetEvent("qb-blackmarket_sv:propsSpawned", function(netIds, locationIndex) if not pendingOrders[locationIndex] then return end local lock = NetworkGetEntityFromNetworkId(netIds.lock) local lockCoords = vec4(GetEntityCoords(lock), GetEntityHeading(lock)) pendingOrders[locationIndex].props = netIds TriggerClientEvent("qb-blackmarket_cl:addLockTarget", -1, locationIndex, lockCoords) end) RegisterNetEvent("qb-blackmarket_sv:attemptContainer", function(index) local src = source if #(GetEntityCoords(GetPlayerPed(src)) - vec3(Config.deliveryLocations[index])) > 5 or not pendingOrders[index] then return end if pendingOrders[index].lockInProgress then TriggerClientEvent('QBCore:Notify', src, "Nogen er allerede igang med dette", "error") return end if pendingOrders[index].isOpen then TriggerClientEvent('QBCore:Notify', src, "Dette er allerede åbnet", "error") return end pendingOrders[index].lockInProgress = true TriggerClientEvent("qb-blackmarket_cl:openContainer", src, index, pendingOrders[index].props) end) RegisterNetEvent("qb-blackmarket_sv:openContainer", function(index) local src = source local crate = NetworkGetEntityFromNetworkId(pendingOrders[index].props.crate) local crateCoords = vec4(GetEntityCoords(crate), GetEntityHeading(crate)) pendingOrders[index].lockInProgress = false pendingOrders[index].isOpen = true pendingOrders[index].props.lock = nil TriggerClientEvent("qb-blackmarket_cl:updateOpenContainer", -1, index, pendingOrders[index].props.container, pendingOrders[index].props.collision, crateCoords, true) end) RegisterNetEvent("qb-blackmarket_sv:attemptLoot", function(index) local src = source if #(GetEntityCoords(GetPlayerPed(src)) - vec3(Config.deliveryLocations[index])) > 5 or not pendingOrders[index] then return end if pendingOrders[index].lootInProgress then TriggerClientEvent('QBCore:Notify', src, "Nogen er allerede igang med dette", "error") return end if pendingOrders[index].isLooted then TriggerClientEvent("inventory:client:SetCurrentStash", src, pendingOrders[index].stashId) exports["ps-inventory"]:OpenInventory("stash", pendingOrders[index].stashId, nil, src) return end pendingOrders[index].lootInProgress = true TriggerClientEvent("qb-blackmarket_cl:lootContainer", src, index) end) RegisterNetEvent("qb-blackmarket_sv:finishLooting", function(index) local src = source if #(GetEntityCoords(GetPlayerPed(src)) - vec3(Config.deliveryLocations[index])) > 5 or not pendingOrders[index] or pendingOrders[index].isLooted then return end local orderItems = {} local stashId = pendingOrders[index].stashId local slot = 1 for k, v in pairs(pendingOrders[index].order) do local itemInfo = QBCore.Shared.Items[k:lower()] if itemInfo then itemInfo.info = {} itemInfo.amount = v itemInfo.slot = slot -- If item has metedata add it here if itemInfo.type == "weapon" then itemInfo.info.serie = tostring(QBCore.Shared.RandomInt(2) .. QBCore.Shared.RandomStr(3) .. QBCore.Shared.RandomInt(1) .. QBCore.Shared.RandomStr(2) .. QBCore.Shared.RandomInt(3) .. QBCore.Shared.RandomStr(4)) itemInfo.info.quality = 100 end orderItems[#orderItems + 1] = itemInfo end slot += 1 end saveOrderStash(stashId, orderItems, function(id) if id then TriggerClientEvent("inventory:client:SetCurrentStash", src, stashId) exports["ps-inventory"]:OpenInventory("stash", stashId, nil, src) end end) pendingOrders[index].lootInProgress = false pendingOrders[index].isLooted = true SetTimeout(Config.lootTimeout * 60000, function() orderComplete(index, stashId) end) end) RegisterNetEvent("qb-blackmarket_sv:cancelLooting", function(index) pendingOrders[index].lootInProgress = false end) RegisterNetEvent("qb-blackmarket_sv:initPendingOrders", function() local src = source local Player = QBCore.Functions.GetPlayer(src) for k, v in pairs(pendingOrders) do -- check if player has any pending orders if v.cid == Player.PlayerData.citizenid then v.src = src TriggerClientEvent("qb-blackmarket_cl:hasPendingOrder", src, marketItems, v.order, v.deliveryTime) -- create props if needed local propDespawned = false if v.props then for prop, netId in pairs(v.props) do local entity = NetworkGetEntityFromNetworkId(netId) if not DoesEntityExist(entity) then if not (v.isOpen and prop == "lock") then propDespawned = true end end end end if propDespawned then for prop, netId in pairs(v.props) do local entity = NetworkGetEntityFromNetworkId(netId) if not DoesEntityExist(entity) then DeleteEntity(entity) end end v.props = nil end if os.time() - v.deliveryTime > 0 then if not v.props then -- removes targets since it gets recreated later if v.isOpen then TriggerClientEvent("qb-blackmarket_cl:removeLootTarget", -1, k) v.isOpen = false end TriggerClientEvent("qb-blackmarket_cl:orderReady", src, k, Config.deliveryLocations[k]) else TriggerClientEvent("qb-blackmarket_cl:enableLocateButton", src, k) end end end -- Create target zones for pending orders if v.props then if not v.isOpen then local lock = NetworkGetEntityFromNetworkId(v.props.lock) local lockCoords = vec4(GetEntityCoords(lock), GetEntityHeading(lock)) TriggerClientEvent("qb-blackmarket_cl:addLockTarget", src, k, lockCoords) else local crate = NetworkGetEntityFromNetworkId(v.props.crate) local crateCoords = vec4(GetEntityCoords(crate), GetEntityHeading(crate)) TriggerClientEvent("qb-blackmarket_cl:updateOpenContainer", src, k, v.props.container, v.props.collision, crateCoords, false) end end end TriggerClientEvent("qb-blackmarket_cl:updateMarketItems", src, marketItems) end) AddEventHandler('playerDropped', function() local src = source for _, v in pairs(pendingOrders) do if v.src == src then v.src = nil end end end) AddEventHandler('onResourceStart', function(resource) if resource ~= GetCurrentResourceName() then return end getMarketItems() setupAvailableLocations() end) AddEventHandler("onResourceStop", function(resource) if resource ~= GetCurrentResourceName() then return end cleanUpProps() cleanUpStashes() end)