Editable Code
SERVER SIDE CODE
x
local Framework = nilif Config.Framework == 'esx' then Framework = exports['es_extended']:getSharedObject()elseif Config.Framework == 'qb' then Framework = exports['qb-core']:GetCoreObject()endfunction GetPlayer(src) if Config.Framework == 'qb' then return Framework.Functions.GetPlayer(src) elseif Config.Framework == 'esx' then return Framework.GetPlayerFromId(src) endendfunction GetIdentifier(src) if Config.Framework == 'qb' then local xPlayer = Framework.Functions.GetPlayer(src) return xPlayer.PlayerData.citizenid elseif Config.Framework == 'esx' then local xPlayer = Framework.GetPlayerFromId(src) return xPlayer.getIdentifier() endendfunction RemoveBank(src, amount) if Config.Framework == 'qb' then local xPlayer = Framework.Functions.GetPlayer(src) xPlayer.Functions.RemoveMoney('bank', amount) elseif Config.Framework == 'esx' then local xPlayer = Framework.GetPlayerFromId(src) xPlayer.removeAccountMoney('bank', amount) endendfunction GetBank(src) if Config.Framework == 'qb' then local xPlayer = Framework.Functions.GetPlayer(src) return xPlayer.PlayerData.money.bank elseif Config.Framework == 'esx' then local xPlayer = Framework.GetPlayerFromId(src) return xPlayer.getAccount('bank').money endendfunction AddCash(src, amount) if Config.Framework == 'qb' then local xPlayer = Framework.Functions.GetPlayer(src) xPlayer.Functions.AddMoney('cash', amount) elseif Config.Framework == 'esx' then local xPlayer = Framework.GetPlayerFromId(src) xPlayer.addMoney(amount) endendfunction GiveItem(src, item, count) if Config.Framework == 'qb' then local xPlayer = Framework.Functions.GetPlayer(src) if xPlayer.Functions.AddItem(item, count) then return true else return false end elseif Config.Framework == 'esx' then local xPlayer = Framework.GetPlayerFromId(src) if xPlayer.addInventoryItem(item, count) then return true else return false end endendfunction RemoveItem(src, item, count) if Config.Framework == 'qb' then local xPlayer = Framework.Functions.GetPlayer(src) if xPlayer.Functions.RemoveItem(item, count) then return true else return false end elseif Config.Framework == 'esx' then local xPlayer = Framework.GetPlayerFromId(src) if xPlayer.removeInventoryItem(item, count) then return true else return false end endendfunction GetItemCount(src, item) if Config.Framework == 'qb' then local xPlayer = Framework.Functions.GetPlayer(src) return xPlayer.Functions.GetItemByName(item) and xPlayer.Functions.GetItemByName(item).amount or 0 elseif Config.Framework == 'esx' then local xPlayer = Framework.GetPlayerFromId(src) local inventoryItem = xPlayer.getInventoryItem(item) return inventoryItem and inventoryItem.count or 0 endendfunction BanPlayer(src, reason) if not Config.Anticheat.enabled then return end print("Player with ID: "..src.." was banned for: "..reason) sendAnticheatToDiscord(src, reason)end-- Phone stuff-- item.labelfunction LBPhone_PlacedOrder(src, item, quantity, finalPrice, totalShipping, totalPrice) local phoneNumber = exports["lb-phone"]:GetEquippedPhoneNumber(src) if Config.LBPhone.useMessages then local msg = '您好,您的订单已确认,正在发送给您!' -- Hello, your order has been confirmed and is being sent to you. local result = exports["lb-phone"]:SendMessage("Ali-Boba | 阿里巴巴", phoneNumber, msg, nil) end if Config.LBPhone.useMail then local email = exports["lb-phone"]:GetEmailAddress(phoneNumber) local msg = '您好,您的订单已确认,正在发送给您!' -- Hello, your order has been confirmed and is being sent to you. local data = { to = email, sender = 'Ali-Boba | 阿里巴巴', subject = '有关您的列表的信息', -- Information about your listing message = msg, attachments = false, actions = false, } local success, id = exports["lb-phone"]:SendMail(data) endendfunction LBPhone_PlacedListing(src, item, price, count) local phoneNumber = exports["lb-phone"]:GetEquippedPhoneNumber(src) if Config.LBPhone.useMessages then local msg = 'Hi, your listing was listed successfully!' local result = exports["lb-phone"]:SendMessage("Los Santos Market", phoneNumber, msg, nil) end if Config.LBPhone.useMail then local email = exports["lb-phone"]:GetEmailAddress(phoneNumber) local msg = 'Hi, your listing was listed successfully!' local data = { to = email, sender = 'Los Santos Market', subject = 'Information about your listing', message = msg, attachments = false, actions = false, } local success, id = exports["lb-phone"]:SendMail(data) endendfunction LBPhone_RemoveListing(player, listingId) local phoneNumber = exports["lb-phone"]:GetEquippedPhoneNumber(player) if Config.LBPhone.useMessages then local msg = 'Hi, your listing was removed successfully!' local result = exports["lb-phone"]:SendMessage("Los Santos Market", phoneNumber, msg, nil) end if Config.LBPhone.useMail then local email = exports["lb-phone"]:GetEmailAddress(phoneNumber) local msg = 'Hi, your listing was removed successfully!' local data = { to = email, sender = 'Los Santos Market', subject = 'Information about your listing', message = msg, attachments = false, actions = false, } local success, id = exports["lb-phone"]:SendMail(data) endendfunction LBPhone_OrderArived(owner, item, count) local itemLabel = item local phoneNumber = exports["lb-phone"]:GetEquippedPhoneNumber(owner) if Config.LBPhone.useMessages then local msg = "Hey there! Your order of "..count.."x "..itemLabel..' has arived at Los Santos post office, come by and take your items!' local result = exports["lb-phone"]:SendMessage("LS Post Office", phoneNumber, msg, nil) end if Config.LBPhone.useMail then local email = exports["lb-phone"]:GetEmailAddress(phoneNumber) local msg = "Hey there! Your order of "..count.."x "..itemLabel..' has arived at Los Santos post office, come by and take your items!' local data = { to = email, sender = 'LS Post Office', subject = 'Delivery Awaiting', message = msg, attachments = false, actions = false, } local success, id = exports["lb-phone"]:SendMail(data) endend-- Check if order has been deliveredCreateThread(function() while true do Wait(60000) -- Check every minute -- Fetch all active orders exports.oxmysql:execute('SELECT * FROM dropship_orders WHERE shipping_timeleft > 0', {}, function(orders) for _, order in pairs(orders) do local newTimeLeft = order.shipping_timeleft - 60000 -- Decrement by 60 m seconds if newTimeLeft <= 0 and not order.player_notified then -- Notify player local player = nil local lbl = GetItemLabel(order.item) if Config.Framework == 'qb' then player = Framework.Functions.GetPlayerByCitizenId(order.owner) if player then TriggerClientEvent('ox_lib:notify', player.PlayerData.source, { type = 'success', title = 'Order Delivered', description = string.format("Your order of %d x %s has arrived!", order.count, lbl) }) end elseif Config.Framework == 'esx' then player = Framework.GetPlayerFromIdentifier(order.owner) if player then TriggerClientEvent('ox_lib:notify', player.source, { type = 'success', title = 'Order Delivered', description = string.format("Your order of %d x %s has arrived!", order.count, lbl) }) end end if Config.LBPhone.enabled then LBPhone_OrderArived(order.owner, lbl, order.count) end -- Mark order as notified exports.oxmysql:update('UPDATE dropship_orders SET shipping_timeleft = 0, player_notified = 1 WHERE id = ?', { order.id }) else -- Update remaining time exports.oxmysql:update('UPDATE dropship_orders SET shipping_timeleft = ? WHERE id = ?', { newTimeLeft, order.id }) end end end) endend)-- Phone method meeting customers:RegisterNetEvent('Domas_Dropship:FoundCustomerPhone', function(src, item, price, quantity) local sourcePhone = exports["lb-phone"]:GetEquippedPhoneNumber(src) local callId = exports["lb-phone"]:CreateCall({ phoneNumber = "Customer", source = src }, sourcePhone, { requirePhone = false, hideNumber = false }) TriggerClientEvent('Domas_Dropship:CheckIfCall', src, item, price, quantity)end)RegisterNetEvent('Domas_Dropship:EndCall', function(src) exports["lb-phone"]:EndCall(src)end)CLIENT SIDE CODE
function Negotiate(item, price, quantity) local discountAsk = math.random(1, Config.Negotiate.maxPercent) local priceAfterDiscount = price - (price * (discountAsk / 100)) local minAcceptablePrice = price - (price * (discountAsk / 200)) -- Half the NPC's discount -- Define negotiation options local options = { {value = "reject", label = "Get Lost!"}, {value = "counter", label = "Best I can do is "..(discountAsk/2).."% off ("..minAcceptablePrice..")"}, {value = "accept", label = "Sure!"} } -- Show the input dialog local input = lib.inputDialog('Hey, man, I`m really low on money right now, can you give me '..discountAsk..'% off? You`ll still get '..priceAfterDiscount..'$', { { type = 'select', label = 'What is your answer?', options = options, required = true } }) -- Handle the response if input then local choice = input[1] if choice == "reject" then return 3, 0 elseif choice == "counter" then local NPCMove = math.random(0, 100) Debug("NPCMove "..NPCMove) if NPCMove <= Config.Negotiate.counterAccept then return 2, minAcceptablePrice else return 3, 0 end elseif choice == "accept" then return 1, priceAfterDiscount end endendfunction LBPhone_PlacedListing(item, price, quantity) -- Not really requuired but if you want to you can edit hereendfunction HandleSale(npcPed, blip, item, price, quantity) -- Call server callback to process the sale local success, message, robbing, negotiate = lib.callback.await('Domas_Dropship:CompleteSale', cache.serverId, item, price, quantity) -- Notify result local title = success and Config.Text['sale_complete'] or Config.Text['sale_failed'] local notifyType = success and 'success' or 'error' Notify(title, message, notifyType) -- Cleanup target and blip exports.ox_target:removeZone('sell_item') RemoveBlip(blip) if not robbing then if negotiate then HandleNegotiation(npcPed, item, price, quantity) else PerformHandshake(npcPed) CleanupNPC(npcPed) end else HandleRobbing(npcPed) endendfunction HandleNegotiation(npcPed, item, price, quantity) local negoType, newPrice = Negotiate(item, price, quantity) if negoType == 1 or negoType == 2 then -- Accept negotiation and finalize sale TriggerServerEvent('Domas_Dropship:PoDerybu', cache.serverId, newPrice, item, quantity) PerformHandshake(npcPed) elseif negoType == 3 then -- NPC declines and leaves NPCLeave(npcPed) endendfunction HandleRobbing(npcPed) if math.random(1, 2) == 1 then NPCLeave(npcPed) else FreezeEntityPosition(npcPed, false) TaskCombatPed(npcPed, cache.ped, 0, 16) GiveWeaponToPed(npcPed, Config.PedRobbingWeapon, Config.PedRobbingAmmo, false, true) Wait(Config.PedRobbingTime) DeleteEntity(npcPed) endendfunction CleanupNPC(npcPed) FreezeEntityPosition(npcPed, false) TaskWanderStandard(npcPed, 10.0, 10) Wait(10000) DeleteEntity(npcPed)endfunction NPCLeave(npcPed) FreezeEntityPosition(npcPed, false) TaskGoToCoordAnyMeans(npcPed, 0.0, 0.0, 0.0, 3.0, 0, 0, 786603, 0) Wait(10000) DeleteEntity(npcPed)endfunction PerformHandshake(npcPed) FreezeEntityPosition(npcPed, false) Wait(100) FreezeEntityPosition(cache.ped, true) if Config.EnableHandshake then StartHandShake(cache.ped, npcPed) end ClearPedTasksImmediately(npcPed) FreezeEntityPosition(cache.ped, false) CleanupNPC(npcPed)endfunction SalesInteraction(npcPed, blip, item, price, quantity, npcCoords) exports.ox_target:addBoxZone({ coords = npcCoords, size = vector3(1.5, 1.5, 3.0), debug = Config.Debug, name = 'sell_item', options = { { icon = 'fas fa-dollar-sign', label = Config.Text['sell_items'], onSelect = function() HandleSale(npcPed, blip, item, price, quantity) end } } })endfunction GetInput_PlaceForSale(maxPRC, maxQNT) -- Show the input dialog local input = lib.inputDialog(Config.Text['place_for_sale'], {{ type = 'number', label = Config.Text['enter_price'], description = Config.Text['enter_price_description'], required = true, icon = 'hashtag', min = 1, max = maxPRC }, { type = 'number', label = Config.Text['enter_quantity'], description = Config.Text['enter_quantity_description'], required = true, icon = 'hashtag', min = 1, max = maxQNT }}) return inputendfunction PurchaseItem_Dialog(minValue, maxValue) local input = lib.inputDialog(Config.Text['order_quantity'], { { type = 'number', label = Config.Text['enter_quantity'], min = minValue, max = maxValue } }) return inputend-- Resource InitializationCreateThread(function() if Config.Enable_DropshipPlaces then for _, dropship in pairs(Config.DropshipPlaces) do -- Adding a target at the specified location exports.ox_target:addBoxZone({ coords = dropship.location, size = dropship.size, rotation = dropship.rotation, debug = dropship.debug, options = { { name = 'dropshipping_menu', event = 'Domas_Dropship:OpenDropshipMenu', icon = 'fas fa-box', label = dropship.label, } } }) end endend)CreateThread(function() if Config.Enable_PostPlaces then for _, post in pairs(Config.PostPlaces) do exports.ox_target:addBoxZone({ coords = post.location, size = post.size, rotation = post.rotation, debug = post.debug, options = { { name = 'post_menu', icon = 'fas fa-box', label = post.label, onSelect = function() TriggerEvent('Domas_Dropship:OpenPostMenu') end } } }) end endend)-- Notify the player when their items has been orderedRegisterNetEvent('Domas_Dropship:StartSaleTimer', function(waitTime, item, price, quantity) local player = PlayerPedId() local timer = waitTime / 1000 -- Convert from milliseconds to seconds local label = GetItemLabel(item) if not Config.LBPhone.enabled then -- Start the timer SetTimeout(waitTime, function() local status, result = pcall(function() return lib.alertDialog({ header = 'Sale Ready', content = ('Your sale for %s x%d at $%d is ready! Confirm to spawn the customer NPC and set a waypoint.'):format(label, quantity, price), cancel = true, -- Enable cancel button labels = { confirm = 'Confirm', cancel = 'Cancel' } }) end) -- Handle error if not status then -- Log the error for debugging print('Error in alertDialog:', result) -- Notify the player about the issue (if needed) lib.notify({ title = 'Error', description = 'There was an issue displaying the dialog. Please try again.', type = 'error' }) return end -- Logic after alertDialog resolves successfully if result == 'confirm' then -- Call the continuation function if confirmed Cont(item, price, quantity) else Debug("Sale was not confirmed.") -- Optional message end end) else SetTimeout(waitTime, function() TriggerServerEvent('Domas_Dropship:FoundCustomerPhone', cache.serverId, item, price, quantity) end) endend)RegisterNetEvent('Domas_Dropship:CheckIfCall', function(item, price, quantity) local startTime = GetGameTimer() -- Get the current game time in milliseconds local inCall = false -- Check the call status for 5 seconds Citizen.CreateThread(function() while GetGameTimer() - startTime < 10000 do -- 10000 ms = 10 seconds inCall = exports["lb-phone"]:IsInCall() -- Check if in a call if inCall then -- If in call, break the loop and trigger the appropriate event Cont(item, price, quantity) TriggerServerEvent('Domas_Dropship:EndCall', cache.serverId) return -- Exit the loop early end Citizen.Wait(500) -- Wait for 100ms before checking again end if not inCall then TriggerServerEvent('Domas_Dropship:EndCall', cache.serverId) end end)end)-- Function to create a blip at a specific locationlocal function createBlip(location, sprite, color, size, name) local blip = AddBlipForCoord(location.x, location.y, location.z) SetBlipSprite(blip, sprite) SetBlipColour(blip, color) SetBlipScale(blip, size) BeginTextCommandSetBlipName("STRING") AddTextComponentString(name) EndTextCommandSetBlipName(blip) SetBlipAsShortRange(blip, true)end-- Create dropship blips if enabledif Config.Enable_DropshipPlaces then for _, dropPlace in pairs(Config.DropshipPlaces) do if dropPlace.blip then createBlip(dropPlace.location, dropPlace.blip.sprite, dropPlace.blip.color, dropPlace.blip.size, dropPlace.blip.name) end endend-- Create post blips if enabledif Config.Enable_PostPlaces then for _, postPlace in pairs(Config.PostPlaces) do if postPlace.blip then createBlip(postPlace.location, postPlace.blip.sprite, postPlace.blip.color, postPlace.blip.size, postPlace.blip.name) end endendfunction DrawText3D(x, y, z, text) if not Config.Enable3dText then return end local onScreen, _x, _y = World3dToScreen2d(x, y, z) local px, py, pz = table.unpack(GetGameplayCamCoords()) local dist = #(vector3(px, py, pz) - vector3(x, y, z)) local scale = (dist / 10) if onScreen then SetTextScale(0.7 * scale, 0.7 * scale) SetTextFont(4) SetTextProportional(1) SetTextColour(255, 255, 255, 215) SetTextEntry("STRING") AddTextComponentString(text) DrawText(_x, _y) endendfunction Notify(title, text, type) lib.notify({ title = title, description = text, type = type })endRegisterNetEvent('Domas_Dropship:Notify', function(title, text, type) Notify(title, text, type)end)RegisterNetEvent('Domas_Dropship:ListingRemoved', function(itemName) Notify(Config.Text['listing_removed'], '', 'success')end)RegisterNetEvent('Domas_Dropship:ListingRemoveFailed', function() Notify(Config.Text['remove_failed'], '', 'success')end)Was this page helpful?