From d29d2d69049b416ba40eb5be16b48a3dab256a08 Mon Sep 17 00:00:00 2001 From: Jim Shield Date: Tue, 21 Jun 2022 15:10:35 +0100 Subject: [PATCH] Gang support for cash regsiters --- jim-payments/atms.lua | 343 +++++++++++++++++++++++++++++++++ jim-payments/atmserver.lua | 260 +++++++++++++++++++++++++ jim-payments/client.lua | 111 +++++++++++ jim-payments/config.lua | 223 +++++++++++++++++++++ jim-payments/fxmanifest.lua | 26 +++ jim-payments/images/ticket.png | Bin 0 -> 24308 bytes jim-payments/server.lua | 168 ++++++++++++++++ 7 files changed, 1131 insertions(+) create mode 100644 jim-payments/atms.lua create mode 100644 jim-payments/atmserver.lua create mode 100644 jim-payments/client.lua create mode 100644 jim-payments/config.lua create mode 100644 jim-payments/fxmanifest.lua create mode 100644 jim-payments/images/ticket.png create mode 100644 jim-payments/server.lua diff --git a/jim-payments/atms.lua b/jim-payments/atms.lua new file mode 100644 index 0000000..c80bfd7 --- /dev/null +++ b/jim-payments/atms.lua @@ -0,0 +1,343 @@ +local QBCore = exports['qb-core']:GetCoreObject() + +-- This script is a simple replacement for QB-Banking and QB-ATMs +-- It uses QB-Input and server callbacks to retreive info about accounts and cash +-- This requires QB-Target and has the location of every ATM, ATM prop and bank window + +local Targets = {} +local Peds = {} + +local BankLoc = Config.VanBankLocations +local ATMLoc = Config.VanATMLocations + +local function cv(amount) + local formatted = amount + while true do + formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2') + if (k==0) then + break + end + end + return formatted +end + +local bossroles = {} +local gangroles = {} +CreateThread(function() + if Config.Gabz then BankLoc = Config.GabzBankLocations ATMLoc = Config.GabzATMLocations end + if Config.useATM then + if Config.ATMBlips then + for k, v in pairs(Config.WallATMLocations) do + blip = AddBlipForCoord(v) + SetBlipSprite(blip, 108) + SetBlipDisplay(blip, 4) + SetBlipScale(blip, 0.55) + SetBlipColour(blip, 3) + SetBlipAsShortRange(blip, true) + BeginTextCommandSetBlipName("STRING") + AddTextComponentString("ATM") + EndTextCommandSetBlipName(blip) + end + for k, v in pairs(ATMLoc) do + blip = AddBlipForCoord(v) + SetBlipSprite(blip, 108) + SetBlipDisplay(blip, 4) + SetBlipScale(blip, 0.55) + SetBlipColour(blip, 3) + SetBlipAsShortRange(blip, true) + BeginTextCommandSetBlipName("STRING") + AddTextComponentString("ATM") + EndTextCommandSetBlipName(blip) + end + end + end + if Config.useBanks then + if Config.BankBlips then + for k,v in pairs(BankLoc) do + for l, b in pairs(v) do + blip = AddBlipForCoord(b) + SetBlipSprite(blip, 108) + SetBlipDisplay(blip, 4) + SetBlipScale(blip, 0.55) + SetBlipColour(blip, 2) + SetBlipAsShortRange(blip, true) + BeginTextCommandSetBlipName("STRING") + AddTextComponentString("Bank") + EndTextCommandSetBlipName(blip) + break + end + end + end + end + if Config.useATM or Config.useBanks then + for k, v in pairs(QBCore.Shared.Jobs) do ---Grabs the list of jobs + if QBCore.Shared.Jobs[tostring(k)] then + for l, b in pairs(QBCore.Shared.Jobs[tostring(k)].grades) do -- Grabs the list of grades + if QBCore.Shared.Jobs[tostring(k)].grades[l].isboss == true then -- Checks the grade if is boss + if bossroles[tostring(k)] ~= nil then -- checks if the line exists + if bossroles[tostring(k)] > tonumber(l) then bossroles[tostring(k)] = tonumber(l) end -- the + else bossroles[tostring(k)] = tonumber(l) + end + end + end + end + end + for k, v in pairs(QBCore.Shared.Gangs) do + if QBCore.Shared.Gangs[tostring(k)] then + for l, b in pairs(QBCore.Shared.Gangs[tostring(k)].grades) do + if tostring(k) ~= "none" then + if QBCore.Shared.Gangs[tostring(k)].grades[l].isboss == true then + if gangroles[tostring(k)] ~= nil then + if gangroles[tostring(k)] > tonumber(l) then gangroles[tostring(k)] = tonumber(l) end + else gangroles[tostring(k)] = tonumber(l) + end + end + end + end + end + end + end + if Config.useATM then + exports['qb-target']:AddTargetModel(Config.ATMModels, { options = { { event = "jim-payments:Client:ATM:use", icon = "fas fa-money-check-alt", label = "Use ATM", id = "atm" },}, distance = 1.5, }) + for k,v in pairs(Config.WallATMLocations) do + Targets["jimwallatm"..k] = + exports['qb-target']:AddCircleZone("jimwallatm"..k, vector3(tonumber(v.x), tonumber(v.y), tonumber(v.z)+0.2), 0.5, { name="jimwallatm"..k, debugPoly=Config.Debug, useZ=true, }, + { options = { { event = "jim-payments:Client:ATM:use", icon = "fas fa-money-check-alt", label = "Use ATM", id = "atm" }, + --{ event = "jim-payments:Client:ATM:use", icon = "fas fa-arrow-right-arrow-left", label = "Transfer Money", id = "transfer" }, + }, distance = 1.5 }) + end + for k,v in pairs(ATMLoc) do + Targets["jimatm"..k] = + exports['qb-target']:AddCircleZone("jimatm"..k, vector3(tonumber(v.x), tonumber(v.y), tonumber(v.z)+0.2), 0.5, { name="jimatm"..k, debugPoly=Config.Debug, useZ=true, }, + { options = { { event = "jim-payments:Client:ATM:use", icon = "fas fa-money-check-alt", label = "Use ATM", id = "atm" }, + --{ event = "jim-payments:Client:ATM:use", icon = "fas fa-arrow-right-arrow-left", label = "Transfer Money", id = "transfer" }, + }, distance = 1.5 }) + end + end + if Config.useBanks then + for k,v in pairs(BankLoc) do + for l, b in pairs(v) do + Targets["jimbank"..k..l] = + exports['qb-target']:AddCircleZone("jimbank"..k..l, vector3(tonumber(b.x), tonumber(b.y), tonumber(b.z)+0.2), 2.0, { name="jimbank"..k..l, debugPoly=Config.Debug, useZ=true, }, + { options = { { event = "jim-payments:Client:ATM:use", icon = "fas fa-piggy-bank", label = "Use Bank", id = "bank" }, + { event = "jim-payments:Client:ATM:use", icon = "fas fa-arrow-right-arrow-left", label = "Transfer Money", id = "transfer" }, + { event = "jim-payments:Client:ATM:use", icon = "fas fa-money-check-dollar", label = "Access Savings", id = "savings" }, + + { event = "jim-payments:Client:ATM:use", icon = "fas fa-building", label = "Access Society Account", id = "society", job = bossroles }, + { event = "jim-payments:Client:ATM:use", icon = "fas fa-arrow-right-arrow-left", label = "Society Money Transfer", id = "societytransfer", job = bossroles }, + + { event = "jim-payments:Client:ATM:use", icon = "fas fa-building", label = "Gang Society Account", id = "gang", gang = gangroles }, + { event = "jim-payments:Client:ATM:use", icon = "fas fa-arrow-right-arrow-left", label = "Gang Money Transfer", id = "gangtransfer", gang = gangroles }, }, + distance = 2.5 }) + if Config.Peds then + local i = math.random(1, #Config.PedPool) + RequestModel(Config.PedPool[i]) while not HasModelLoaded(Config.PedPool[i]) do Wait(0) end + if Peds["jimbank"..k..l] == nil then Peds["jimbank"..k..l] = CreatePed(0, Config.PedPool[i], vector3(tonumber(b.x), tonumber(b.y), tonumber(b.z)), b[4], false, false) end + if Config.Debug then print("Ped Created - 'jimbank"..k..l.."'") end + end + end + end + end +end) + +local function PlayATMAnimation(animation) + if animation == 'enter' then RequestAnimDict('amb@prop_human_atm@male@enter') + while not HasAnimDictLoaded('amb@prop_human_atm@male@enter') do Wait(1) end + if HasAnimDictLoaded('amb@prop_human_atm@male@enter') then TaskPlayAnim(PlayerPedId(), 'amb@prop_human_atm@male@enter', "enter", 1.0,-1.0, 1500, 1, 1, true, true, true) end + end + if animation == 'exit' then RequestAnimDict('amb@prop_human_atm@male@exit') + while not HasAnimDictLoaded('amb@prop_human_atm@male@exit') do Wait(1) end + if HasAnimDictLoaded('amb@prop_human_atm@male@exit') then TaskPlayAnim(PlayerPedId(), 'amb@prop_human_atm@male@exit', "exit", 1.0,-1.0, 3000, 1, 1, true, true, true) end + end +end + +local function GrabAccount(type, job) + local p = promise.new() + QBCore.Functions.TriggerCallback('jim-payments:ManageWrapper', function(cb) p:resolve(cb) end, type, job) + return Citizen.Await(p) +end + +RegisterNetEvent('jim-payments:Client:ATM:use', function(data) + --this grabs all the info from names to savings account numbers in the databases + local p = promise.new() + QBCore.Functions.TriggerCallback('jim-payments:ATM:Find', function(cb) p:resolve(cb) end) local info = Citizen.Await(p) + local society local gsociety local cancelled = false + if Config.Manage then + society = GrabAccount("GetAccount", PlayerJob.name) + Wait(200) + gsociety = GrabAccount("GetGangAccount", PlayerGang.name) + else + local p = promise.new() + QBCore.Functions.TriggerCallback('qb-bossmenu:server:GetAccount', function(cb) p:resolve(cb) end, PlayerJob.name) society = Citizen.Await(p) + local p2 = promise.new() + QBCore.Functions.TriggerCallback('qb-gangmenu:server:GetAccount', function(cb) p2:resolve(cb) end, PlayerGang.name) gsociety = Citizen.Await(p2) + end + local atmbartime = 2500 + local setoptions = {} + + if data.id == "atm" then + setoptions = { { value = "withdraw", text = "Withdrawl" }, } + setview = "

Welcome back, "..info.name.."

- Citizen ID -
"..info.cid.."

- Balances -
🏦Bank - $"..cv(info.bank).."
πŸ’΅Cash - $"..cv(info.cash)..'

- Options -' + setheader = "πŸ’΅ ATM Banking πŸ’΅" + setinputs = { { type = 'radio', name = 'billtype', text = setview, options = setoptions }, + { type = 'number', isRequired = true, name = 'amount', text = 'πŸ’΅ Amount to transfer' }, } + for k, v in pairs(Config.ATMModels) do + local playerPed = PlayerPedId() + local playerCoords = GetEntityCoords(playerPed, true) + local hash = GetHashKey(v) + local atm = IsObjectNearPoint(hash, playerCoords.x, playerCoords.y, playerCoords.z, 1.6) + if atm then + local obj = GetClosestObjectOfType(playerCoords.x, playerCoords.y, playerCoords.z, 1.6, v, false, false, false) + local atmCoords = GetEntityCoords(obj, false) + TaskTurnPedToFaceEntity(playerPed, obj, 1000) + Wait(1000) + PlayATMAnimation('enter') + QBCore.Functions.Progressbar("accessing_atm", "Accessing ATM", atmbartime, false, true, { disableMovement = false, disableCarMovement = false, disableMouse = false, disableCombat = false, }, {}, {}, {}, function() -- Done + end, function() + TriggerEvent("QBCore:Notify", "Cancelled!", "error") + cancelled = true + end, data.icon) + end + end + + elseif data.id == "bank" then + setoptions = { { value = "withdraw", text = "Withdrawl" }, { value = "deposit", text = "Deposit" } } + setview = "Welcome back, "..info.name.."

- Account -
"..info.account.."
"..info.cid.."

- Balances -
🏦Bank - $"..cv(info.bank).."
πŸ’΅Cash - $"..cv(info.cash).."

- Options -" + setheader = "🏦 Banking 🏦" + setinputs = { { type = 'radio', name = 'billtype', text = setview, options = setoptions }, + { type = 'number', isRequired = true, name = 'amount', text = 'πŸ’΅ Amount to transfer' }, } + PlayATMAnimation('enter') + QBCore.Functions.Progressbar("accessing_atm", "Accessing Bank", atmbartime, false, true, { disableMovement = false, disableCarMovement = false, disableMouse = false, disableCombat = false, }, {}, {}, {}, function() -- Done + end, function() + TriggerEvent("QBCore:Notify", "Cancelled!", "error") + cancelled = true + end, data.icon) + + elseif data.id == "transfer" then + setoptions = { { value = "transfer", text = "Transfer" } } + setview = "Welcome back, "..info.name.."

- Account -
"..info.account.."
"..info.cid.."

- Balances -
🏦Bank - $"..cv(info.bank).."

- Options -" + setheader = "πŸ”€ Transfer Services πŸ”€" + setinputs = { { type = 'radio', name = 'billtype', text = setview, options = setoptions }, + { type = 'text', isRequired = true, name = 'account', text = '🏦 Account no.' }, + { type = 'number', isRequired = true, name = 'amount', text = 'πŸ’Έ Amount to transfer' }, } + PlayATMAnimation('enter') + QBCore.Functions.Progressbar("accessing_atm", "Accessing Transfers", atmbartime, false, true, { disableMovement = false, disableCarMovement = false, disableMouse = false, disableCombat = false, }, {}, {}, {}, function() -- Done + end, function() + TriggerEvent("QBCore:Notify", "Cancelled!", "error") + cancelled = true + end, data.icon) + + elseif data.id == "savings" then + setoptions = { { value = "withdraw", text = "Withdrawl" }, { value = "deposit", text = "Deposit" } } + setview = "Welcome back, "..info.name.."

- Account Info -
Savings ID: "..info.aid.."
"..info.cid.."

- Balances -
πŸ’°Savings - $"..cv(info.savbal).."
🏦Bank - $"..cv(info.bank).."

- Options -" + setheader = "πŸ’° Savings πŸ’°" + setinputs = { { type = 'radio', name = 'billtype', text = setview, options = setoptions }, + { type = 'number', isRequired = true, name = 'amount', text = 'πŸ’΅ Amount to transfer' }, } + PlayATMAnimation('enter') + QBCore.Functions.Progressbar("accessing_atm", "Accessing Savings", atmbartime, false, true, { disableMovement = false, disableCarMovement = false, disableMouse = false, disableCombat = false, }, {}, {}, {}, function() -- Done + end, function() + TriggerEvent("QBCore:Notify", "Cancelled!", "error") + cancelled = true + end, data.icon) + + elseif data.id == "society" then + setoptions = { { value = "withdraw", text = "Withdrawl" }, { value = "deposit", text = "Deposit" } } + setview = "Welcome back, "..info.name.."

- Society Account -
"..PlayerJob.label.."

- Balances -
🏒"..PlayerJob.label.." - $"..cv(society).."
🏦Bank - $"..cv(info.bank).."

- Options -" + setheader = "🏒 Society Banking 🏒" + setinputs = { { type = 'radio', name = 'billtype', text = setview, options = setoptions }, + { type = 'number', isRequired = true, name = 'amount', text = 'πŸ’΅ Amount to transfer' }, } + PlayATMAnimation('enter') + QBCore.Functions.Progressbar("accessing_atm", "Accessing Society Account", atmbartime, false, true, { disableMovement = false, disableCarMovement = false, disableMouse = false, disableCombat = false, }, {}, {}, {}, function() -- Done + end, function() + TriggerEvent("QBCore:Notify", "Cancelled!", "error") + cancelled = true + end, data.icon) + + elseif data.id == "societytransfer" then + setoptions = { { value = "transfer", text = "Transfer" } } + setview = "Welcome back, "..info.name.."

- Society Account -
"..PlayerJob.label.."

- Balances -
🏒"..PlayerJob.label.." - $"..cv(society).."

- Options -" + setheader = "πŸ”€ Transfer Services πŸ”€" + setinputs = { { type = 'radio', name = 'billtype', text = setview, options = setoptions }, + { type = 'text', isRequired = true, name = 'account', text = '🏦 Account no.' }, + { type = 'number', isRequired = true, name = 'amount', text = 'πŸ’Έ Amount to transfer' }, } + PlayATMAnimation('enter') + QBCore.Functions.Progressbar("accessing_atm", "Accessing Transfers", atmbartime, false, true, { disableMovement = false, disableCarMovement = false, disableMouse = false, disableCombat = false, }, {}, {}, {}, function() -- Done + end, function() + TriggerEvent("QBCore:Notify", "Cancelled!", "error") + cancelled = true + end, data.icon) + + elseif data.id == "gang" then + setheader = "🏒 Society Banking 🏒" + setview = "Welcome back, "..info.name.."

- Society Account -
"..PlayerGang.label.."

- Balances -
🏒"..PlayerGang.label.." - $"..cv(gsociety).."
🏦Bank - $"..cv(info.bank).."

- Options -" + setoptions = { { value = "withdraw", text = "Withdrawl" }, { value = "deposit", text = "Deposit" } } + setinputs = { { type = 'radio', name = 'billtype', text = setview, options = setoptions }, + { type = 'number', isRequired = true, name = 'amount', text = 'πŸ’΅ Amount to transfer' }, } + PlayATMAnimation('enter') + QBCore.Functions.Progressbar("accessing_atm", "Accessing Society Account", atmbartime, false, true, { disableMovement = false, disableCarMovement = false, disableMouse = false, disableCombat = false, }, {}, {}, {}, function() -- Done + end, function() + TriggerEvent("QBCore:Notify", "Cancelled!", "error") + cancelled = true + end, data.icon) + + elseif data.id == "gangtransfer" then + setoptions = { { value = "transfer", text = "Transfer" } } + setview = "Welcome back, "..info.name.."

- Society Account -
"..PlayerGang.label.."

- Balances -
🏒"..PlayerGang.label.." - $"..cv(gsociety).."

- Options -" + setheader = "πŸ”€ Transfer Services πŸ”€" + setinputs = { { type = 'radio', name = 'billtype', text = setview, options = setoptions }, + { type = 'text', isRequired = true, name = 'account', text = '🏦 Account no.' }, + { type = 'number', isRequired = true, name = 'amount', text = 'πŸ’Έ Amount to transfer' }, } + PlayATMAnimation('enter') + QBCore.Functions.Progressbar("accessing_atm", "Accessing Transfers", atmbartime, false, true, { disableMovement = false, disableCarMovement = false, disableMouse = false, disableCombat = false, }, {}, {}, {}, function() -- Done + + end, function() + TriggerEvent("QBCore:Notify", "Cancelled!", "error") + cancelled = true + end, data.icon) + end + + Wait(atmbartime+500) + if not cancelled then + cancelled = false + local dialog = exports['qb-input']:ShowInput({ header = setheader, txt = "test", submitText = "Transfer", inputs = setinputs }) + if dialog then + if not dialog.amount then return end + PlayATMAnimation('exit') Citizen.Wait(1000) + TriggerServerEvent('jim-payments:server:ATM:use', dialog.amount, dialog.billtype, dialog.account, data.id, society, gsociety) + end + end +end) + + +RegisterNetEvent('jim-payments:client:ATM:give', function() + local onlineList = {} + local nearbyList = {} + QBCore.Functions.TriggerCallback('jim-payments:MakePlayerList', function(cb) onlineList = cb if onlineList[1] == nil then Wait(200) end + for k, v in pairs(QBCore.Functions.GetPlayersFromCoords(GetEntityCoords(PlayerPedId()), Config.PaymentRadius)) do + local dist = #(GetEntityCoords(GetPlayerPed(v)) - GetEntityCoords(PlayerPedId())) + for i = 1, #onlineList do + if onlineList[i].value == GetPlayerServerId(v) then + if v ~= PlayerId() then + nearbyList[#nearbyList+1] = { value = onlineList[i].value, text = onlineList[i].text..' ('..math.floor(dist+0.05)..'m)' } + end + end + end + dist = nil + end + if nearbyList[#nearbyList] == nil then TriggerEvent("QBCore:Notify", "No one near by to charge", "error") return end + local dialog = exports['qb-input']:ShowInput({ header = "Give someone cash", submitText = "Give", + inputs = { + { text = " ", name = "citizen", type = "select", options = nearbyList }, + { type = 'number', isRequired = true, name = 'price', text = 'πŸ’΅ Amount to Pay' }, } + }) + if dialog then + if not dialog.citizen or not dialog.price then return end + TriggerServerEvent('jim-payments:server:ATM:give', dialog.citizen, dialog.price) + end + end) +end) + +AddEventHandler('onResourceStop', function(resource) if resource ~= GetCurrentResourceName() then return end + for k, v in pairs(Targets) do exports['qb-target']:RemoveZone(k) end + for k, v in pairs(Peds) do DeletePed(Peds[k]) end +end) \ No newline at end of file diff --git a/jim-payments/atmserver.lua b/jim-payments/atmserver.lua new file mode 100644 index 0000000..3d66f93 --- /dev/null +++ b/jim-payments/atmserver.lua @@ -0,0 +1,260 @@ +local QBCore = exports['qb-core']:GetCoreObject() + +local function cv(amount) + local formatted = amount + while true do + formatted, k = string.gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2') + if (k==0) then + break + end + end + return formatted +end + +---- BANK STUFF +QBCore.Functions.CreateCallback('jim-payments:ManageWrapper', function(source, cb, type, account) + local result = nil + if type == "GetAccount" then result = exports["qb-management"]:GetAccount(account) end + if type == "GetGangAccount" then result = exports["qb-management"]:GetGangAccount(account) end + cb(result) +end) + +RegisterServerEvent('jim-payments:server:ATM:use', function(amount, billtype, baccount, account, society, gsociety) + local src = source + local Player = QBCore.Functions.GetPlayer(src) + local cashB = Player.Functions.GetMoney("cash") + local bankB = Player.Functions.GetMoney("bank") + local amount = tonumber(amount) + + --Simple transfers from bank to wallet -- + if account == "bank" or account == "atm" then + if billtype == "withdraw" then + if bankB < amount then TriggerClientEvent("QBCore:Notify", src, "Bank Balance too low", "error") + elseif bankB >= tonumber(amount) then + TriggerClientEvent("QBCore:Notify", src, "Withdrew $"..cv(amount).." from the bank", "success") -- Don't really need this as phone gets notified when money is withdrawn + Player.Functions.RemoveMoney('bank', amount) Wait(1500) + Player.Functions.AddMoney('cash', amount) + end + elseif billtype == "deposit" then + if cashB < amount then TriggerClientEvent("QBCore:Notify", src, "Not enough cash", "error") + elseif cashB >= amount then + Player.Functions.RemoveMoney('cash', amount) Wait(1500) + Player.Functions.AddMoney('bank', amount) + TriggerClientEvent("QBCore:Notify", src, "Deposited $"..cv(amount).." into the Bank", "success") + end + end + -- Transfers from bank to savings account -- + elseif account == "savings" then + local getSavingsAccount = MySQL.Sync.fetchAll('SELECT * FROM bank_accounts WHERE citizenid = ? AND account_type = ?', { Player.PlayerData.citizenid, 'Savings' }) + if getSavingsAccount[1] ~= nil then savbal = tonumber(getSavingsAccount[1].amount) aid = getSavingsAccount[1].record_id end + + if billtype == "withdraw" then + if savbal >= amount then + savbal = savbal - amount + Player.Functions.AddMoney('bank', amount) + TriggerClientEvent("QBCore:Notify", src, "$"..cv(amount).." Withdrawn from savings into bank account", "success") + MySQL.Async.execute('UPDATE bank_accounts SET amount = ? WHERE citizenid = ? AND record_id = ?', { savbal, Player.PlayerData.citizenid, getSavingsAccount[1].record_id }, function(success) + if success then return true else return false end + end) + elseif savbal < amount then + TriggerClientEvent("QBCore:Notify", src, "Savings Balance too low", "error") + end + elseif billtype == "deposit" then + if amount < bankB then + savbal = savbal + amount + Player.Functions.RemoveMoney('bank', amount) + TriggerClientEvent("QBCore:Notify", src, "$"..cv(amount).." Deposited from bank account into savings", "success") + MySQL.Async.execute('UPDATE bank_accounts SET amount = ? WHERE citizenid = ? AND record_id = ?', { savbal, Player.PlayerData.citizenid, getSavingsAccount[1].record_id }, function(success) + if success then return true else return false end + end) + else TriggerClientEvent("QBCore:Notify", src, "Bank Balance too low", "error") + end + end + --Simple transfers from society account to bank -- + elseif account == "society" then + if billtype == "withdraw" then + if tonumber(society) < amount then TriggerClientEvent("QBCore:Notify", src, "Society balance too low", "error") + elseif tonumber(society) >= amount then + TriggerClientEvent("QBCore:Notify", src, "Withdrew $"..cv(amount).." from the "..Player.PlayerData.job.label.." account", "success") + Player.Functions.AddMoney('bank', amount) + if Config.Manage then exports["qb-management"]:RemoveMoney(tostring(Player.PlayerData.job.name), amount) + else TriggerEvent("qb-bossmenu:server:removeAccountMoney", tostring(Player.PlayerData.job.name), amount) end + end + elseif billtype == "deposit" then + if bankB < amount then TriggerClientEvent("QBCore:Notify", src, "Not enough money in your bank", "error") + elseif bankB >= amount then + if Config.Manage then exports["qb-management"]:AddMoney(tostring(Player.PlayerData.job.name), amount) + else TriggerEvent("qb-bossmenu:server:addAccountMoney", tostring(Player.PlayerData.job.name), amount) end + Player.Functions.RemoveMoney('bank', amount) Wait(1500) + TriggerClientEvent("QBCore:Notify", src, "Deposited $"..cv(amount).." into the "..Player.PlayerData.job.label.." account", "success") + end + end + -- Transfer from boss account to players -- + elseif account == "societytransfer" then + local bannedCharacters = {'%','$',';'} + local newAmount = tostring(amount) + local newiban = tostring(baccount) + for k, v in pairs(bannedCharacters) do + newAmount = string.gsub(newAmount, '%' .. v, '') + newiban = string.gsub(newiban, '%' .. v, '') + end + baccount = newiban + amount = tonumber(newAmount) + + local Player = QBCore.Functions.GetPlayer(src) + if (society - amount) >= 0 then + local query = '%"account":"' .. baccount .. '"%' + local result = MySQL.Sync.fetchAll('SELECT * FROM players WHERE charinfo LIKE ?', {query}) + if result[1] ~= nil then + local Reciever = QBCore.Functions.GetPlayerByCitizenId(result[1].citizenid) + if Config.Manage then exports["qb-management"]:RemoveMoney(tostring(Player.PlayerData.job.name), amount) + else TriggerEvent("qb-bossmenu:server:removeAccountMoney", tostring(Player.PlayerData.job.name), amount) end + if Reciever ~= nil then + Reciever.Functions.AddMoney('bank', amount) + TriggerClientEvent("QBCore:Notify", src, "Sent $"..amount.." to "..Reciever.PlayerData.charinfo.firstname.." "..Reciever.PlayerData.charinfo.lastname, "success") + TriggerClientEvent("QBCore:Notify", Reciever.PlayerData.source, "Recieved $"..cv(amount).." from "..tostring(Player.PlayerData.job.label).." account", "success") + else + local RecieverMoney = json.decode(result[1].money) + RecieverMoney.bank = (RecieverMoney.bank + amount) + MySQL.Async.execute('UPDATE players SET money = ? WHERE citizenid = ?', {json.encode(RecieverMoney), result[1].citizenid}) + end + elseif result[1] == nil then TriggerClientEvent("QBCore:Notify", src, "Error: Account '"..baccount.."' not found", "error") + end + end + + --Simple transfers from gang society account to bank -- + elseif account == "gang" then + if billtype == "withdraw" then + if tonumber(gsociety) < amount then TriggerClientEvent("QBCore:Notify", src, "Society balance too low", "error") + elseif tonumber(gsociety) >= amount then + TriggerClientEvent("QBCore:Notify", src, "Withdrew $"..cv(amount).." from the "..Player.PlayerData.gang.label.." account", "success") + Player.Functions.AddMoney('bank', amount) + if Config.Manage then exports["qb-management"]:RemoveGangMoney(tostring(Player.PlayerData.gang.name), amount) + else TriggerEvent("qb-gangmenu:server:removeAccountMoney", tostring(Player.PlayerData.gang.name), amount) end + end + elseif billtype == "deposit" then + if bankB < amount then TriggerClientEvent("QBCore:Notify", src, "Not enough money in your bank", "error") + elseif bankB >= amount then + if Config.Manage then exports["qb-management"]:AddGangMoney(tostring(Player.PlayerData.gang.name), amount) + else TriggerEvent("qb-gangmenu:server:addAccountMoney", tostring(Player.PlayerData.gang.name), amount) end + Player.Functions.RemoveMoney('bank', amount) Wait(1500) + TriggerClientEvent("QBCore:Notify", src, "Deposited $"..cv(amount).." into the "..Player.PlayerData.gang.label.." account", "success") + end + end + -- Transfer from gang account to players -- + elseif account == "gangtransfer" then + local bannedCharacters = {'%','$',';'} + local newAmount = tostring(amount) + local newiban = tostring(baccount) + for k, v in pairs(bannedCharacters) do + newAmount = string.gsub(newAmount, '%' .. v, '') + newiban = string.gsub(newiban, '%' .. v, '') + end + baccount = newiban + amount = tonumber(newAmount) + + local Player = QBCore.Functions.GetPlayer(src) + if (gsociety - amount) >= 0 then + local query = '%"account":"' .. baccount .. '"%' + local result = MySQL.Sync.fetchAll('SELECT * FROM players WHERE charinfo LIKE ?', {query}) + if result[1] ~= nil then + local Reciever = QBCore.Functions.GetPlayerByCitizenId(result[1].citizenid) + if Config.Manage then exports["qb-management"]:RemoveGangMoney(tostring(Player.PlayerData.gang.name), amount) + else TriggerEvent("qb-bossmenu:server:removeAccountMoney", tostring(Player.PlayerData.gang.name), amount) end + if Reciever ~= nil then + Reciever.Functions.AddMoney('bank', amount) + TriggerClientEvent("QBCore:Notify", src, "Sent $"..amount.." to "..Reciever.PlayerData.charinfo.firstname.." "..Reciever.PlayerData.charinfo.lastname, "success") + TriggerClientEvent("QBCore:Notify", Reciever.PlayerData.source, "Recieved $"..cv(amount).." from "..tostring(Player.PlayerData.gang.label).." account", "success") + else + local RecieverMoney = json.decode(result[1].money) + RecieverMoney.bank = (RecieverMoney.bank + amount) + MySQL.Async.execute('UPDATE players SET money = ? WHERE citizenid = ?', {json.encode(RecieverMoney), result[1].citizenid}) + end + elseif result[1] == nil then TriggerClientEvent("QBCore:Notify", src, "Error: Account '"..baccount.."' not found", "error") + + end + end + + elseif account == "transfer" then + + local bannedCharacters = {'%','$',';'} + local newAmount = tostring(amount) + local newiban = tostring(baccount) + for k, v in pairs(bannedCharacters) do + newAmount = string.gsub(newAmount, '%' .. v, '') + newiban = string.gsub(newiban, '%' .. v, '') + end + baccount = newiban + amount = tonumber(newAmount) + + local Player = QBCore.Functions.GetPlayer(src) + if (Player.PlayerData.money.bank - amount) >= 0 then + local query = '%"account":"' .. baccount .. '"%' + local result = MySQL.Sync.fetchAll('SELECT * FROM players WHERE charinfo LIKE ?', {query}) + if result[1] ~= nil then + local Reciever = QBCore.Functions.GetPlayerByCitizenId(result[1].citizenid) + Player.Functions.RemoveMoney('bank', amount) + if Reciever ~= nil then + Reciever.Functions.AddMoney('bank', amount) + TriggerClientEvent("QBCore:Notify", src, "Sent $"..cv(amount).." to "..Reciever.PlayerData.charinfo.firstname.." "..Reciever.PlayerData.charinfo.lastname, "success") + TriggerClientEvent("QBCore:Notify", Reciever.PlayerData.source, "Recieved $"..cv(amount).." from "..Player.PlayerData.charinfo.firstname.." "..Player.PlayerData.charinfo.lastname, "success") + else + local RecieverMoney = json.decode(result[1].money) + RecieverMoney.bank = (RecieverMoney.bank + amount) + MySQL.Async.execute('UPDATE players SET money = ? WHERE citizenid = ?', {json.encode(RecieverMoney), result[1].citizenid}) + end + elseif result[1] == nil then TriggerClientEvent("QBCore:Notify", src, "Error: Account '"..baccount.."' not found", "error") + + end + end + end +end) + +QBCore.Functions.CreateCallback('jim-payments:ATM:Find', function(source, cb) + local Player = QBCore.Functions.GetPlayer(source) + local name = Player.PlayerData.charinfo.firstname..' '..Player.PlayerData.charinfo.lastname + local cid = Player.PlayerData.citizenid.." ["..source.."]" + local cash = Player.Functions.GetMoney("cash") + local bank = Player.Functions.GetMoney("bank") + local account = Player.PlayerData.charinfo.account + + local getSavingsAccount = MySQL.Sync.fetchAll('SELECT * FROM bank_accounts WHERE citizenid = ? AND account_type = ?', { Player.PlayerData.citizenid, 'Savings' }) + if getSavingsAccount[1] ~= nil then + aid = getSavingsAccount[1].record_id + savbal = getSavingsAccount[1].amount + else + MySQL.Async.insert('INSERT INTO bank_accounts (citizenid, amount, account_type) VALUES (?, ?, ?)', { Player.PlayerData.citizenid, 0, 'Savings' }, function() completed = true end) repeat Wait(0) until completed == true + local getSavingsAccount = MySQL.Sync.fetchAll('SELECT * FROM bank_accounts WHERE citizenid = ? AND account_type = ?', { Player.PlayerData.citizenid, 'Savings' }) + aid = getSavingsAccount[1].record_id + savbal = getSavingsAccount[1].amount + end + local data = { + name = name, + cash = cash, + bank = bank, + account = account, + cid = cid, + savbal = savbal, + aid = aid } + cb(data) +end) + +QBCore.Commands.Add("cashgive", "Pay a user nearby", {}, false, function(source) TriggerClientEvent("jim-payments:client:ATM:give", source) end) + +RegisterServerEvent("jim-payments:server:ATM:give", function(citizen, price) + local Player = QBCore.Functions.GetPlayer(source) + local Reciever = QBCore.Functions.GetPlayer(tonumber(citizen)) + local amount = tonumber(price) + local balance = Player.Functions.GetMoney("cash") + + if amount and amount > 0 then + if balance >= amount then + Player.Functions.RemoveMoney('cash', amount) + TriggerClientEvent("QBCore:Notify", source, "You gave "..Reciever.PlayerData.charinfo.firstname.." $"..cv(amount), "success") + Reciever.Functions.AddMoney('cash', amount) + TriggerClientEvent("QBCore:Notify", tonumber(citizen), "You got $"..cv(amount).." from "..Player.PlayerData.charinfo.firstname, "success") + elseif balance < amount then + TriggerClientEvent("QBCore:Notify", source, "You don't have enough cash to give", "error") + end + else TriggerClientEvent('QBCore:Notify', source, "You can't give $0", 'error') end +end) \ No newline at end of file diff --git a/jim-payments/client.lua b/jim-payments/client.lua new file mode 100644 index 0000000..4d27da3 --- /dev/null +++ b/jim-payments/client.lua @@ -0,0 +1,111 @@ +local QBCore = exports['qb-core']:GetCoreObject() + +PlayerJob = {} +local onDuty = false +local BankPed = {} +RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function() QBCore.Functions.GetPlayerData(function(PlayerData) PlayerJob = PlayerData.job PlayerGang = PlayerData.gang end) end) +RegisterNetEvent('QBCore:Client:OnJobUpdate', function(JobInfo) PlayerJob = JobInfo onDuty = PlayerJob.onduty end) +RegisterNetEvent('QBCore:Client:SetDuty', function(duty) onDuty = duty end) +RegisterNetEvent('QBCore:Client:OnGangUpdate', function(GangInfo) PlayerGang = GangInfo end) + +--Keeps track of duty on script restarts +AddEventHandler('onResourceStart', function(resource) if GetCurrentResourceName() ~= resource then return end + QBCore.Functions.GetPlayerData(function(PlayerData) PlayerJob = PlayerData.job PlayerGang = PlayerData.gang onDuty = PlayerJob.onduty end) +end) + +CreateThread(function() + local jobroles = {} + local gangroles = {} + for k, v in pairs(Config.Jobs) do if v.gang then gangroles[tostring(k)] = 0 else jobroles[tostring(k)] = 0 end end + exports['qb-target']:AddCircleZone("JimBank", vector3(Config.CashInLocation.x, Config.CashInLocation.y, Config.CashInLocation.z), 2.0, { name="JimBank", debugPoly=Config.Debug, useZ=true, }, + { options = { + { event = "jim-payments:Tickets:Menu", icon = "fas fa-receipt", label = "Cash in Job Receipts", job = jobroles, }, + { event = "jim-payments:Tickets:Menu", icon = "fas fa-receipt", label = "Cash in Gang Receipts", gang = gangroles, } }, + distance = 2.0 }) + if Config.Peds then + local i = math.random(1, #Config.PedPool) + RequestModel(Config.PedPool[i]) while not HasModelLoaded(Config.PedPool[i]) do Wait(0) end + if not BankPed[1] then BankPed[1] = CreatePed(0, Config.PedPool[i], vector3(Config.CashInLocation.x, Config.CashInLocation.y, Config.CashInLocation.z-1), Config.CashInLocation[4], false, false) end + if Config.Debug then print("Ped Created for Ticket Trade") end + end +end) + +RegisterNetEvent('jim-payments:client:Charge', function(data, outside) + if outside == nil then outside = false end + if not outside and not onDuty and data.gang == nil then TriggerEvent("QBCore:Notify", "Not Clocked in!", "error") return end -- Require to be on duty when making a payment + local onlineList = {} + local nearbyList = {} + local p = promise.new() + QBCore.Functions.TriggerCallback('jim-payments:MakePlayerList', function(cb) p:resolve(cb) end) + onlineList = Citizen.Await(p) + for k, v in pairs(QBCore.Functions.GetPlayersFromCoords(GetEntityCoords(PlayerPedId()), Config.PaymentRadius)) do + local dist = #(GetEntityCoords(GetPlayerPed(v)) - GetEntityCoords(PlayerPedId())) + for i = 1, #onlineList do + if onlineList[i].value == GetPlayerServerId(v) then + if v ~= PlayerId() or Config.Debug then + nearbyList[#nearbyList+1] = { value = onlineList[i].value, text = onlineList[i].text..' ('..math.floor(dist+0.05)..'m)' } + end + end + end + dist = nil + end + if not data.img then img = "" else img = data.img end + if nearbyList[#nearbyList] == nil then TriggerEvent("QBCore:Notify", "No one near by to charge", "error") return end + local newinputs = {} + if Config.List then newinputs[#newinputs+1] = { text = " ", name = "citizen", type = "select", options = nearbyList } end + if not Config.List then newinputs[#newinputs+1] = { type = 'text', isRequired = true, name = 'citizen', text = '# Customer ID #' } end + newinputs[#newinputs+1] = { type = 'radio', name = 'billtype', text = 'Payment Type', options = { { value = "cash", text = "Cash" }, { value = "bank", text = "Card" } } } + newinputs[#newinputs+1] = { type = 'number', isRequired = true, name = 'price', text = 'πŸ’΅ Amount to Charge' } + if data.job then label = PlayerJob.label gang = false elseif data.gang then label = PlayerGang.label gang = true end + local dialog = exports['qb-input']:ShowInput({ header = img..label.." Cash Register", submitText = "Send", inputs = newinputs}) + if dialog then + if not dialog.citizen or not dialog.price then return end + TriggerServerEvent('jim-payments:server:Charge', dialog.citizen, dialog.price, dialog.billtype, data.img, outside, gang) + end +end) + +RegisterNetEvent('jim-payments:Tickets:Menu', function(data) + local amount = 0 + local p = promise.new() QBCore.Functions.TriggerCallback('jim-payments:Ticket:Count', function(cb) p:resolve(cb) end) amount = Citizen.Await(p) + if amount == 0 or amount == nil then TriggerEvent("QBCore:Notify", "You don't have any tickets to trade", "error") return end + local sellable = false + local name = "" local label = "" + for k, v in pairs(Config.Jobs) do + if data.gang then + if v.gang and k == PlayerGang.name then + name = k + label = PlayerGang.label + sellable = true + end + else + if not v.gang and k == PlayerJob.name then + name = k + label = PlayerJob.label + sellable = true + end + end + if sellable then + exports['qb-menu']:openMenu({ + { isMenuHeader = true, header = "🧾 "..label.." Receipts 🧾", txt = "Do you want trade your receipts for payment?" }, + { isMenuHeader = true, header = "", txt = "Amount of Tickets: "..amount.."
Total Payment: $"..(Config.Jobs[name].PayPerTicket * amount) }, + { icon = "fas fa-circle-check", header = "Yes", txt = "", params = { event = "jim-payments:Tickets:Sell:yes" } }, + { icon = "fas fa-circle-xmark", header = "No", txt = "", params = { event = "jim-payments:Tickets:Sell:no" } }, }) + end + end +end) + +RegisterNetEvent("jim-payments:client:PayPopup", function(amount, biller, billtype, img, billerjob, gang) + if not img then img = "" end + exports['qb-menu']:openMenu({ + { isMenuHeader = true, header = img.."🧾 "..billerjob.." Payment 🧾", txt = "Do you want accept the payment?" }, + { isMenuHeader = true, header = "", txt = billtype:gsub("^%l", string.upper).." Payment: $"..amount }, + { icon = "fas fa-circle-check", header = "Yes", txt = "", params = { isServer = true, event = "jim-payments:server:PayPopup", args = { accept = true, amount = amount, biller = biller, billtype = billtype, gang = gang } } }, + { icon = "fas fa-circle-xmark", header = "No", txt = "", params = { isServer = true, event = "jim-payments:server:PayPopup", args = { accept = false, amount = amount, biller = biller, billtype = billtype } } }, }) +end) + +RegisterNetEvent('jim-payments:Tickets:Sell:yes', function() TriggerServerEvent('jim-payments:Tickets:Sell') end) +RegisterNetEvent('jim-payments:Tickets:Sell:no', function() exports['qb-menu']:closeMenu() end) + +AddEventHandler('onResourceStop', function(resource) if resource ~= GetCurrentResourceName() then return end + exports['qb-target']:RemoveZone("JimBank") DeletePed(BankPed[1]) +end) \ No newline at end of file diff --git a/jim-payments/config.lua b/jim-payments/config.lua new file mode 100644 index 0000000..f277cb3 --- /dev/null +++ b/jim-payments/config.lua @@ -0,0 +1,223 @@ +print("Jim-Payments v2.6 - Payments Script by Jimathy") + +-- If you need support I now have a discord available, it helps me keep track of issues and give better support. + +-- https://discord.gg/xKgQZ6wZvS + +Config = {} + +Config.Debug = false + +Config.Manage = true -- "true" if using qb-management + -- "false" if using qb-bossmenu + +Config.TicketSystem = true -- Enable this if you want to use the ticket system false + +Config.List = true -- "true" to use nearby player list feature in the cash registers, "false" for manual id entry +Config.PaymentRadius = 30 -- This is how far the playerlist will check for nearby players (based on the person charging) + +Config.Peds = true -- "true" to enable peds spawning in banks +Config.PedPool = { + [1] = `IG_Bankman`, + [2] = `U_M_M_BankMan`, + [3] = `S_F_M_Shop_HIGH`, + [4] = `S_M_M_HighSec_02`, + [5] = `S_M_M_HighSec_03`, + [6] = `S_M_M_HighSec_04`, + [7] = `A_F_Y_Business_01`, + [8] = `A_F_Y_Business_02`, + [9] = `A_F_Y_Business_03`, + [10] = `A_F_Y_Business_04`, + [11] = `A_M_M_Business_01`, + [12] = `A_M_Y_Business_02`, + [13] = `A_M_Y_Business_03`, + [14] = `U_F_M_CasinoShop_01`, +} + +Config.Gabz = true -- "true" to enable Gabz Banking locations +Config.useATM = true -- Enable this to use my ATM's +Config.useBanks = true -- Enable this to use my banking stuff +Config.BankBlips = true -- Enable this if you disabled qb-banking and need bank locations +Config.ATMBlips = false -- Enable this if you are a pyscho and need every ATM to be on the map too + +Config.PhoneBank = true -- Set this to false to use the popup payment system FOR CARD/BANK PAYMENTS instead of using phone invoices + -- This doesn't affect Cash payments as they by default use confirmation now + -- This is helpful for phones that don't support invoices well + +Config.PhoneType = "qb" -- Change this setting to make invoices work with your phone script [still testing this currently] + -- "qb" for qb-phone + -- "gks"" for GKSPhone + +Config.Commission = true -- Set this to true to enable Commissions and give the person charging a percentage of the total +Config.CommissionAll = false -- Set this to true to give commission to workers clocked in +Config.CommissionDouble = false -- Set this to true if you want the person charging to get double Commission +Config.CommissionLimit = false -- If true, this limits the Commission to only be given if over the "MinAmountForTicket". If false, Commission will be given for any amount + +-- MinAmountforTicket should be your cheapest item +-- PayPerTicket should never be higher than MinAmountforTicket +-- Commission is a percentage eg "0.10" becomes 10% + +Config.CashInLocation = vector4(252.23, 223.11, 106.29, 159.2) -- Default Third Window along in Pacific Bank +if Config.Gabz then Config.CashInLocation = vector4(269.28, 217.24, 106.28, 69.0) end -- Middle along in Pacific Bank (Gabz) + +Config.Jobs = { + ['beanmachine'] = { MinAmountforTicket = 50, PayPerTicket = 50, Commission = 0.10, }, + ['burgershot'] = { MinAmountforTicket = 50, PayPerTicket = 50 , Commission = 0.10,}, + ['catcafe'] = { MinAmountforTicket = 50, PayPerTicket = 50, Commission = 0.10, }, + ['henhouse'] = { MinAmountforTicket = 50, PayPerTicket = 50, Commission = 0.10, }, + ['pizzathis'] = { MinAmountforTicket = 50, PayPerTicket = 50, Commission = 0.10, }, + ['popsdiner'] = { MinAmountforTicket = 50, PayPerTicket = 50, Commission = 0.10, }, + ['tequilala'] = { MinAmountforTicket = 50, PayPerTicket = 50, Commission = 0.10, }, + ['vanilla'] = { MinAmountforTicket = 50, PayPerTicket = 50, Commission = 0.10, }, + ['mechanic'] = { MinAmountforTicket = 1000, PayPerTicket = 500, Commission = 0.10, }, + ['lostmc'] = { MinAmountforTicket = 50, PayPerTicket = 50, Commission = 0.10, gang = true }, +} + +Config.ATMModels = {"prop_atm_01", "prop_atm_02", "prop_atm_03", "prop_fleeca_atm" } + +Config.GabzATMLocations = { +--PACIFIC BANK 12 ATMS (GABZ) + vector3(239.02, 212.37, 106.28), + vector3(239.46, 213.6, 106.28), + vector3(239.9, 214.82, 106.28), + vector3(240.35, 216.03, 106.28), + vector3(241.42, 218.96, 106.28), + vector3(241.86, 220.16, 106.28), + vector3(242.3, 221.41, 106.28), + vector3(242.76, 222.63, 106.28), + vector3(263.46, 203.86, 106.28), + vector3(263.96, 205.03, 106.28), + vector3(264.37, 206.28, 106.28), + vector3(264.77, 207.51, 106.28), +} + +Config.VanATMLocations = { +--PACIFIC BANK 10 ATMS + vector3(265.9, 213.86, 106.28), + vector3(265.56, 212.98, 106.28), + vector3(265.19, 211.91, 106.28), + vector3(264.86, 211.03, 106.28), + vector3(264.52, 210.06, 106.28), + vector3(236.64, 219.72, 106.29), + vector3(237.04, 218.72, 106.29), + vector3(237.5, 217.87, 106.29), + vector3(237.93, 216.94, 106.29), + vector3(238.36, 216.03, 106.29), +} + +--QB-Target doesn't seem to like ALL ATM Props so need to manually add locations +Config.WallATMLocations = { + vector3(-386.54, 6046.29, 31.5), + vector3(-282.82, 6226.24, 31.49), + vector3(-132.74, 6366.79, 31.48), + vector3(-95.76, 6457.41, 31.46), + vector3(-97.52, 6455.65, 31.47), + vector3(155.95, 6642.99, 31.6), + vector3(173.92, 6638.16, 31.57), + vector3(2558.65, 350.92, 108.62), + vector3(1077.78, -776.64, 58.35), + vector3(1138.14, -468.88, 66.73), + vector3(1166.93, -455.96, 66.81), + vector3(285.37, 143.07, 104.17), + vector3(-165.43, 234.81, 94.92), + vector3(-165.4, 232.73, 94.92), + vector3(-1410.41, -98.76, 52.43), + vector3(-1409.85, -100.51, 52.38), + vector3(-1206.0, -324.94, 37.86), + vector3(-1205.23, -326.55, 37.86), + vector3(-2072.28, -317.27, 13.32), + vector3(-2974.7, 380.15, 15.0), + vector3(-2959.01, 487.45, 15.46), + vector3(-2956.87, 487.36, 15.46), + vector3(-3043.98, 594.32, 7.74), + vector3(-3241.35, 997.74, 12.55), + vector3(-1305.59, -706.64, 25.32), + vector3(-537.85, -854.69, 29.28), + vector3(-709.98, -818.71, 23.73), + vector3(-712.87, -818.71, 23.73), + vector3(-526.71, -1223.18, 18.45), + vector3(-256.47, -715.94, 33.55), + vector3(-259.13, -723.29, 33.54), + vector3(-203.82, -861.3, 30.27), + vector3(111.38, -774.96, 31.44), + vector3(114.56, -776.13, 31.42), + vector3(112.46, -819.65, 31.34), + vector3(118.93, -883.68, 31.12), + vector3(-846.97, -340.29, 38.68), + vector3(-846.41, -341.41, 38.68), + vector3(-262.21, -2012.17, 30.15), + vector3(-273.23, -2024.32, 30.15), + vector3(24.46, -945.94, 29.36), + vector3(-254.48, -692.74, 33.61), + vector3(-1569.95, -546.94, 34.96), + vector3(-1570.91, -547.63, 34.96), + vector3(289.14, -1282.29, 29.6), + vector3(289.18, -1256.84, 29.44), + vector3(296.04, -896.22, 29.24), + vector3(296.74, -894.26, 29.24), + vector3(-301.63, -829.73, 32.43), + vector3(-303.23, -829.44, 32.43), + vector3(5.27, -919.87, 29.56), + vector3(-1200.61, -885.62, 13.26), +} + +Config.VanBankLocations = { + ["legion"] = { + vector4(149.5, -1042.08, 29.37, 342.74), -- Legion Fleeca + }, + ["hawick"] = { + vector4(313.81, -280.43, 54.16, 342.29), -- Hawick Fleeca + }, + ["vinewood"] = { + vector4(-351.4, -51.24, 49.04, 338.4), -- Vinewood Fleeca + }, + ["delperro"] = { + vector4(-1212.11, -332.01, 37.78, 25.33), -- Del Perro Fleeca + }, + ["route1"] = { + vector4(-2961.17, 482.9, 15.7, 84.68), -- Route 1 Fleeca + }, + ["route68"] = { + vector4(1175.03, 2708.2, 38.09, 180.0), -- Route 68 Fleeca + }, + ["paleto"] = { + vector4(-111.22, 6470.03, 31.63, 133.86), -- Paleto Bank + }, + ["pacific"] = { + vector4(243.58, 226.25, 106.29, 169.06), -- Pacific Bank Window 1 + vector4(247.08, 224.98, 106.29, 157.75), -- Pacific Bank Window 2 + }, +} + +Config.GabzBankLocations = { + ["legion"] = { + vector4(149.5, -1042.08, 29.37, 342.74), -- Legion Fleeca + }, + ["hawick"] = { + vector4(313.81, -280.43, 54.16, 342.29), -- Hawick Fleeca + }, + ["vinewood"] = { + vector4(-351.4, -51.24, 49.04, 338.4), -- Vinewood Fleeca + }, + ["delperro"] = { + vector4(-1212.11, -332.01, 37.78, 25.33), -- Del Perro Fleeca + }, + ["route1"] = { + vector4(-2961.17, 482.9, 15.7, 84.68), -- Route 1 Fleeca + }, + ["route68"] = { + vector4(1175.03, 2708.2, 38.09, 180.0), -- Route 68 Fleeca + }, + ["paleto"] = { + vector4(-110.72, 6469.82, 31.63, 222.94), -- Paleto Bank (GABZ) - 1 + vector4(-108.98, 6471.56, 31.63, 222.92), -- Paleto Bank (GABZ) - 2 + }, + ["pacific"] = { + vector4(258.55, 227.63, 106.28, 160.96), -- Pacficic Gabz 1+2 + vector4(263.21, 225.93, 106.28, 158.25), -- Pacficic Gabz 3+4 + vector4(267.95, 224.24, 106.28, 158.5), -- Pacficic Gabz 5+6 + vector4(263.71, 212.63, 106.28, 337.53), -- Pacficic Gabz 7+8 + vector4(259.02, 214.32, 106.28, 337.82), -- Pacficic Gabz 9+10 + vector4(254.27, 216.06, 106.28, 336.37), -- Pacficic Gabz 11+12 + }, +} \ No newline at end of file diff --git a/jim-payments/fxmanifest.lua b/jim-payments/fxmanifest.lua new file mode 100644 index 0000000..ce8b4b1 --- /dev/null +++ b/jim-payments/fxmanifest.lua @@ -0,0 +1,26 @@ +name "Jim-Payments" +author "Jimathy" +version "v2.6" +description "Payment Script By Jimathy" +fx_version "cerulean" +game "gta5" + +dependencies { + 'qb-input', + 'qb-target', +} + +client_scripts { + 'client.lua', + 'atms.lua' +} + +server_scripts { + '@oxmysql/lib/MySQL.lua', + 'server.lua', + 'atmserver.lua' +} + +shared_scripts { + 'config.lua', +} \ No newline at end of file diff --git a/jim-payments/images/ticket.png b/jim-payments/images/ticket.png new file mode 100644 index 0000000000000000000000000000000000000000..a022f40d36267434ce4cfb989fc3e1811997a7ff GIT binary patch literal 24308 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 Config.Jobs[data.society].MinAmountforTicket then + for k, v in pairs(QBCore.Functions.GetPlayers()) do + local Player = QBCore.Functions.GetPlayer(v) + if Player ~= nil then + if Player.PlayerData.job.name == data.society and Player.PlayerData.job.onduty then + Player.Functions.AddItem('payticket', 1, false, {["quality"] = nil}) + TriggerClientEvent('QBCore:Notify', Player.PlayerData.source, 'Receipt received', 'success') + TriggerClientEvent('inventory:client:ItemBox', Player.PlayerData.source, QBCore.Shared.Items['payticket'], "add", 1) + end + end + if gang then + biller.Functions.AddItem('payticket', 1, false, {["quality"] = nil}) + TriggerClientEvent('QBCore:Notify', biller.PlayerData.source, 'Receipt received', 'success') + TriggerClientEvent('inventory:client:ItemBox', biller.PlayerData.source, QBCore.Shared.Items['payticket'], "add", 1) + end + end + end + end + -- Commission section, does each config option separately + local comm = tonumber(Config.Jobs[data.society].Commission) + if Config.Commission and comm ~= 0 then + if Config.CommissionLimit and data.amount < Config.Jobs[data.society].MinAmountforTicket then return end + if Config.CommissionDouble then + biller.Functions.AddMoney("bank", math.floor(tonumber(data.amount) * (comm *2))) + TriggerClientEvent("QBCore:Notify", biller.PlayerData.source, "Recieved $"..math.floor(tonumber(data.amount) * (comm *2)).." in Commission", "success") + else biller.Functions.AddMoney("bank", math.floor(tonumber(data.amount) *comm)) + TriggerClientEvent("QBCore:Notify", biller.PlayerData.source, "Recieved $"..math.floor(tonumber(data.amount) * comm).." in Commission", "success") + end + if Config.CommissionAll then + for k, v in pairs(QBCore.Functions.GetPlayers()) do + local Player = QBCore.Functions.GetPlayer(v) + if Player ~= nil and Player ~= biller then + if Player.PlayerData.job.name == data.society and Player.PlayerData.job.onduty then + Player.Functions.AddMoney("bank", math.floor(tonumber(data.amount) * comm)) + TriggerClientEvent("QBCore:Notify", Player.PlayerData.source, "Recieved $"..math.floor(tonumber(data.amount) * comm).." in Commission", "success") + end + end + end + end + end +end) + +RegisterServerEvent('jim-payments:Tickets:Sell', function() + local Player = QBCore.Functions.GetPlayer(source) + if Player.Functions.GetItemByName("payticket") == nil then TriggerClientEvent('QBCore:Notify', source, "No tickets to trade", 'error') return + else + tickets = Player.Functions.GetItemByName("payticket").amount + Player.Functions.RemoveItem('payticket', tickets) + pay = (tickets * Config.Jobs[Player.PlayerData.job.name].PayPerTicket) + Player.Functions.AddMoney('bank', pay, 'ticket-payment') + TriggerClientEvent('inventory:client:ItemBox', source, QBCore.Shared.Items['payticket'], "remove", tickets) + TriggerClientEvent('QBCore:Notify', source, "Tickets traded: "..tickets.." Total: $"..cv(pay), 'success') + end +end) + +QBCore.Functions.CreateCallback('jim-payments:Ticket:Count', function(source, cb) + if QBCore.Functions.GetPlayer(source).Functions.GetItemByName('payticket') == nil then amount = 0 + else amount = QBCore.Functions.GetPlayer(source).Functions.GetItemByName('payticket').amount end + cb(amount) +end) + +RegisterServerEvent("jim-payments:server:Charge", function(citizen, price, billtype, img, outside, gang) + local src = source + local biller = QBCore.Functions.GetPlayer(src) + local billed = QBCore.Functions.GetPlayer(tonumber(citizen)) + local amount = tonumber(price) + local balance = billed.Functions.GetMoney(billtype) + if amount and amount > 0 then + if balance < amount then + TriggerClientEvent("QBCore:Notify", src, "Customer doesn't have enough cash to pay", "error") + TriggerClientEvent("QBCore:Notify", tonumber(citizen), "You don't have enough cash to pay", "error") + return + end + local label = biller.PlayerData.job.label + if gang == true then label = biller.PlayerData.gang.label end + if Config.PhoneBank == false or gang == true or billtype == "cash" then + TriggerClientEvent("jim-payments:client:PayPopup", billed.PlayerData.source, amount, src, billtype, img, label, gang) + else + if Config.PhoneType == "qb" then + MySQL.Async.insert( + 'INSERT INTO phone_invoices (citizenid, amount, society, sender, sendercitizenid) VALUES (?, ?, ?, ?, ?)', + {billed.PlayerData.citizenid, amount, biller.PlayerData.job.name, biller.PlayerData.charinfo.firstname, biller.PlayerData.citizenid}) + TriggerClientEvent('qb-phone:RefreshPhone', billed.PlayerData.source) + elseif Config.PhoneType == "gks" then + MySQL.Async.execute('INSERT INTO gksphone_invoices (citizenid, amount, society, sender, sendercitizenid, label) VALUES (@citizenid, @amount, @society, @sender, @sendercitizenid, @label)', { + ['@citizenid'] = billed.PlayerData.citizenid, + ['@amount'] = amount, + ['@society'] = biller.PlayerData.job.name, + ['@sender'] = biller.PlayerData.charinfo.firstname, + ['@sendercitizenid'] = biller.PlayerData.citizenid, + ['@label'] = biller.PlayerData.job.label, + }) + TriggerClientEvent('gksphone:notifi', src, {title = 'Billing', message = 'Invoice Successfully Sent', img= '/html/static/img/icons/logo.png' }) + TriggerClientEvent('gksphone:notifi', billed.PlayerData.source, {title = 'Billing', message = 'New Invoice Recieved', img= '/html/static/img/icons/logo.png' }) + end + TriggerClientEvent('QBCore:Notify', src, 'Invoice Successfully Sent', 'success') + TriggerClientEvent('QBCore:Notify', billed.PlayerData.source, 'New Invoice Received') + end + else TriggerClientEvent('QBCore:Notify', source, "You can't charge $0", 'error') return end +end) + +RegisterServerEvent("jim-payments:server:PayPopup", function(data) + local src = source + local billed = QBCore.Functions.GetPlayer(src) + local biller = QBCore.Functions.GetPlayer(tonumber(data.biller)) + local newdata = { senderCitizenId = biller.PlayerData.citizenid, society = biller.PlayerData.job.name, amount = data.amount } + if data.gang == true then newdata.society = biller.PlayerData.gang.name end + if data.accept == true then + billed.Functions.RemoveMoney(tostring(data.billtype), data.amount) + TriggerEvent('jim-payments:Tickets:Give', newdata, biller, data.gang) + TriggerClientEvent("QBCore:Notify", data.biller, billed.PlayerData.charinfo.firstname.." accepted the $"..data.amount.." payment", "success") + elseif data.accept == false then + TriggerClientEvent("QBCore:Notify", src, "You declined the payment") + TriggerClientEvent("QBCore:Notify", data.biller, billed.PlayerData.charinfo.firstname.." declined the $"..data.amount.." payment", "error") + end +end) + +QBCore.Functions.CreateCallback('jim-payments:MakePlayerList', function(source, cb) + local onlineList = {} + for k, v in pairs(QBCore.Functions.GetPlayers()) do + local P = QBCore.Functions.GetPlayer(v) + onlineList[#onlineList+1] = { value = tonumber(v), text = "["..v.."] - "..P.PlayerData.charinfo.firstname..' '..P.PlayerData.charinfo.lastname } + end + cb(onlineList) +end) \ No newline at end of file