├── LICENSE ├── README.md ├── client └── client.lua ├── config.lua ├── fxmanifest.lua ├── locales ├── en.lua └── no.lua ├── server └── server.lua └── wash_database.sql /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2023, teigCoding 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Video Preview: https://youtu.be/G00KYyNhuFQ 2 | 3 | Discord Support: https://discord.gg/mAFcFpamZ9 4 | 5 | # required scripts: 6 | qb-management 7 | 8 | # add this to (qb-management > client > cl_boss.lua) 9 | 10 | { 11 | header = "Wash money", 12 | icon = "fa-solid fa-money-bill-transfer", 13 | txt = "Wash your markedbills", 14 | params = { 15 | event = "brp-vasking:client:startVask", 16 | args = comma_value(cb) 17 | } 18 | }, 19 | { 20 | header = "Collect money", 21 | icon = "fa-solid fa-money-bill-transfer", 22 | txt = "Collect money from wash", 23 | params = { 24 | event = "brp-vasking:client:hentPenger", 25 | args = comma_value(cb) 26 | } 27 | }, 28 | 29 | 30 | # It should be placed under this in qb-management (you can freely choose order): 31 | 32 | RegisterNetEvent('qb-bossmenu:client:SocietyMenu', function() 33 | QBCore.Functions.TriggerCallback('qb-bossmenu:server:GetAccount', function(cb) 34 | local SocietyMenu = { 35 | { 36 | header = Lang:t("body.balance").. comma_value(cb) .. " - " .. string.upper(PlayerJob.label), 37 | isMenuHeader = true, 38 | icon = "fa-solid fa-circle-info", 39 | }, 40 | 41 | 42 | -------------------------------------------------------------------------------- /client/client.lua: -------------------------------------------------------------------------------- 1 | local QBCore = exports['qb-core']:GetCoreObject() 2 | 3 | RegisterNetEvent("brp-vasking:client:showDisplay", function(result,timeDiff) 4 | 5 | Citizen.CreateThread(function () 6 | local theMenu = {} 7 | theMenu[#theMenu+1] = 8 | { 9 | header = Lang:t("label.header"), 10 | isMenuHeader = true 11 | } 12 | for i=1,#result do 13 | local timeDiff = timeDiff 14 | local howLong = result[i].howlong 15 | local total_seconds = howLong - timeDiff 16 | local hours = math.floor(total_seconds / 3600) 17 | local minutes = math.floor((total_seconds % 3600) / 60) 18 | local seconds = total_seconds % 60 19 | local output = "" 20 | if hours >= 0 and minutes >= 0 and seconds >= 0 then 21 | if hours ~= 0 then 22 | output = hours .. Lang:t("label.hours") 23 | end 24 | if minutes ~= 0 then 25 | output = output .. minutes .. Lang:t("label.minutes") 26 | end 27 | if seconds ~= 0 then 28 | output = output .. seconds .. Lang:t("label.seconds") 29 | end 30 | else 31 | output = Lang:t("label.ready") 32 | end 33 | theMenu[#theMenu+1] = 34 | { 35 | header = "$"..result[i].amount, 36 | txt = output, 37 | params = { 38 | isServer = false, 39 | event = "brp-vasking:client:fullforVask", 40 | args = { 41 | number = i, 42 | result = result, 43 | } 44 | }} 45 | end 46 | theMenu[#theMenu+1] = 47 | { 48 | header = Lang:t("label.quit"), 49 | params = { 50 | isServer = false, 51 | event = "", 52 | 53 | }} 54 | 55 | 56 | exports['qb-menu']:openMenu(theMenu) 57 | end) 58 | 59 | end) 60 | 61 | RegisterNetEvent("brp-vasking:client:fullforVask",function(args) 62 | TriggerServerEvent("brp-vasking:server:fullforVask",args) 63 | end) 64 | 65 | RegisterNetEvent("brp-vasking:client:startVask",function() 66 | 67 | local dialog = exports['qb-input']:ShowInput({ 68 | header = Lang:t("label.header"), 69 | submitText = Lang:t("label.send"), 70 | inputs = { 71 | { 72 | text = Lang:t("label.amount"), 73 | name = "washdata", 74 | type = "number", 75 | isRequired = true 76 | }, 77 | { 78 | text = Lang:t("label.method"), -- text you want to be displayed as a input header 79 | name = "vaskemetode", -- name of the input should be unique otherwise it might override 80 | type = "radio", -- type of the input - Radio is useful for "or" options e.g; billtype = Cash OR Bill OR bank 81 | options = { -- The options (in this case for a radio) you want displayed, more than 6 is not recommended 82 | { value = Config.Options[1], text = Lang:t("label.option1"),checked = true}, -- Options MUST include a value and a text option 83 | { value = Config.Options[2], text = Lang:t("label.option2") }, -- Options MUST include a value and a text option 84 | { value = Config.Options[3], text = Lang:t("label.option3") } -- Options MUST include a value and a text option 85 | }, 86 | -- default = "cash", -- Default radio option, must match a value from above, this is optional 87 | }, 88 | } 89 | }) 90 | 91 | if tonumber(dialog['washdata']) > 0 then 92 | if QBCore.Functions.HasItem("markedbills",tonumber(dialog['washdata'])) then 93 | QBCore.Functions.Notify(Lang:t("success.started_wash")..tonumber(dialog['washdata'])..Lang:t("success.wash_start"), "success") 94 | TriggerServerEvent('brp-vasking:server:startVask', tonumber(dialog['washdata']),tonumber(dialog['vaskemetode'])) 95 | else 96 | QBCore.Functions.Notify(Lang:t("error.not_enough"), "error") 97 | end 98 | else 99 | QBCore.Functions.Notify(Lang:t("error.not_value"), "error") 100 | end 101 | 102 | 103 | end) 104 | 105 | RegisterNetEvent("brp-vasking:client:hentPenger",function() 106 | TriggerServerEvent('brp-vasking:server:hentPenger') 107 | end) -------------------------------------------------------------------------------- /config.lua: -------------------------------------------------------------------------------- 1 | local QBCore = exports['qb-core']:GetCoreObject() 2 | 3 | Config = {} 4 | 5 | 6 | --How much should the user lose in fees 7 | Config.Options = { 8 | 0.25, --option1 9 | 0.1, --option2 10 | 0, --option3 11 | } 12 | 13 | --in seconds 14 | Config.TimeWait = { 15 | 0,--option1 16 | 86400,--option2 17 | 172800,--option3 18 | } -------------------------------------------------------------------------------- /fxmanifest.lua: -------------------------------------------------------------------------------- 1 | fx_version 'bodacious' 2 | author 'teig' 3 | description 'qb-managementwash (Made by teig)' 4 | games { 'gta5' } 5 | 6 | client_script {'client/client.lua' 7 | } 8 | server_script {'server/server.lua', '@oxmysql/lib/MySQL.lua',} 9 | 10 | 11 | escrow_ignore { 12 | "config.lua", 13 | 'locales/en.lua', 14 | 'locales/no.lua', 15 | 'client/client.lua', 16 | 'server/server.lua', 17 | 'wash_database.sql' 18 | } 19 | shared_script { '@qb-core/shared/locale.lua', 20 | 'config.lua', 21 | 'locales/en.lua', 22 | } 23 | 24 | 25 | lua54 'yes' 26 | -------------------------------------------------------------------------------- /locales/en.lua: -------------------------------------------------------------------------------- 1 | local Translations = { 2 | error = { 3 | not_started = "You have not started washing!", 4 | cannot_collect = "You cannot collect ", 5 | have_to_wait = " You have to wait ", 6 | not_enough = "You don't have enough marked money!", 7 | not_value = "Invalid amount!", 8 | 9 | }, 10 | success = { 11 | wash = "You got ", 12 | started_wash = "You started washing ", 13 | wash_start = " USD!", 14 | }, 15 | label = { 16 | hours = " hours, ", 17 | minutes = " minutes, ", 18 | seconds = " seconds", 19 | ready = "Ready for pickup!", 20 | quit = "Quit", 21 | header = "Wash Menu", 22 | send = "Send", 23 | amount = "Amount", 24 | method = "Washing method", 25 | option1 = "Quick Wash", 26 | option2 = "1 day", 27 | option3 = "2 days", 28 | } 29 | } 30 | 31 | Lang = Locale:new({ 32 | phrases = Translations, 33 | warnOnMissing = true 34 | }) -------------------------------------------------------------------------------- /locales/no.lua: -------------------------------------------------------------------------------- 1 | local Translations = { 2 | error = { 3 | not_started = "Du har ikke startet vasking!", 4 | cannot_collect = "Du kan ikke hente ", 5 | have_to_wait = " Du må vente ", 6 | not_enough = "Du har ikke nok markerte penger!", 7 | not_value = "Ugyldig beløp!", 8 | 9 | }, 10 | success = { 11 | wash = "Du fikk ", 12 | started_wash = "Du startet vasking av ", 13 | wash_start = " kr!", 14 | }, 15 | label = { 16 | hours = " timer, ", 17 | minutes = " minutter, ", 18 | seconds = " sekunder", 19 | ready = "Klar for henting!", 20 | quit = "Avslutt", 21 | header = "Vaske Meny", 22 | send = "Send", 23 | amount = "Beløp", 24 | method = "Vaskemetode", 25 | option1 = "Hurtigvask", 26 | option2 = "1 dag", 27 | option3 = "2 dager", 28 | } 29 | } 30 | 31 | Lang = Locale:new({ 32 | phrases = Translations, 33 | warnOnMissing = true 34 | }) 35 | -------------------------------------------------------------------------------- /server/server.lua: -------------------------------------------------------------------------------- 1 | local QBCore = exports['qb-core']:GetCoreObject() 2 | 3 | 4 | RegisterNetEvent('brp-vasking:server:startVask', function(amount,percent) 5 | local src = source 6 | local Player = QBCore.Functions.GetPlayer(src) 7 | local citizenid = Player.PlayerData.citizenid 8 | local currentTime = os.time() 9 | Player.Functions.RemoveItem("markedbills", amount) 10 | amount = amount * (1-percent) 11 | local howlong 12 | 13 | if percent == Config.Options[1] then 14 | howlong = Config.TimeWait[1] 15 | elseif percent == Config.Options[2] then 16 | howlong = Config.TimeWait[2] 17 | elseif percent == Config.Options[3] then 18 | howlong = Config.TimeWait[3] 19 | 20 | end 21 | 22 | MySQL.Async.insert('INSERT INTO wash_database (citizenid, amount, time, howlong) VALUES (?, ?, ?, ?)', { 23 | citizenid, 24 | amount, 25 | currentTime, 26 | howlong, 27 | }) 28 | 29 | end) 30 | 31 | RegisterNetEvent("brp-vasking:server:hentPenger", function() 32 | local src = source 33 | local Player = QBCore.Functions.GetPlayer(src) 34 | local citizenid = Player.PlayerData.citizenid 35 | local currentTime = os.time() 36 | MySQL.query('SELECT * FROM wash_database WHERE citizenid = ?', {citizenid}, function(result) 37 | if result ~= nil and result[1] ~= nil then 38 | local timeDiff = currentTime - result[1].time 39 | local howLong = result[1].howlong 40 | 41 | TriggerClientEvent("brp-vasking:client:showDisplay",src,result,timeDiff) 42 | else 43 | TriggerClientEvent('QBCore:Notify', src, Lang:t("error.not_started"), "error") 44 | end 45 | end) 46 | 47 | end) 48 | 49 | RegisterNetEvent("brp-vasking:server:fullforVask",function(args) 50 | local src = source 51 | local result = args.result 52 | local number = args.number 53 | local Player = QBCore.Functions.GetPlayer(src) 54 | local citizenid = Player.PlayerData.citizenid 55 | local currentTime = os.time() 56 | local timeDiff = currentTime - result[number].time 57 | local howLong = result[number].howlong 58 | if timeDiff >= howLong then 59 | TriggerClientEvent('QBCore:Notify', src, Lang:t("success.wash") ..result[number].amount.. "!", "success") 60 | MySQL.query('SELECT * FROM wash_database WHERE citizenid = ?', {citizenid}, function(result) 61 | if result ~= nil and result[1] ~= nil then 62 | local idToDelete = result[number].ID 63 | MySQL.Async.execute("DELETE FROM wash_database WHERE id = @id", { ['@id'] = idToDelete }, function() 64 | Player.Functions.AddMoney("cash", result[number].amount, "Washed cash") 65 | 66 | end) 67 | end 68 | end) 69 | 70 | -- MySQL.Async.execute("DELETE FROM wash_database WHERE citizenid = @citizenid", { ['@citizenid'] = citizenid }, function() 71 | -- Player.Functions.AddMoney("cash", result[number].amount, "Vasket penger") 72 | -- end) 73 | else 74 | local total_seconds = howLong - timeDiff 75 | local hours = math.floor(total_seconds / 3600) 76 | local minutes = math.floor((total_seconds % 3600) / 60) 77 | local seconds = total_seconds % 60 78 | local output = "" 79 | if hours ~= 0 then 80 | output = hours .. Lang:t("label.hours") 81 | end 82 | if minutes ~= 0 then 83 | output = output .. minutes ..Lang:t("label.minutes") 84 | end 85 | if seconds ~= 0 then 86 | output = output .. seconds .. Lang:t("label.seconds") 87 | end 88 | TriggerClientEvent('QBCore:Notify', src, Lang:t("error.cannot_collect")..result[number].amount.."!".. Lang:t("error.have_to_wait") .. output, "error") 89 | end 90 | end) -------------------------------------------------------------------------------- /wash_database.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE `wash_database` ( 2 | `ID` INT(11) NOT NULL AUTO_INCREMENT, 3 | `citizenid` VARCHAR(255) NOT NULL COLLATE 'utf8mb4_general_ci', 4 | `amount` INT(11) NOT NULL DEFAULT '0', 5 | `time` VARCHAR(255) NULL DEFAULT NULL COLLATE 'utf8mb4_general_ci', 6 | `howlong` INT(11) NOT NULL DEFAULT '0', 7 | PRIMARY KEY (`ID`) USING BTREE 8 | ) 9 | COLLATE='utf8mb4_general_ci' 10 | ENGINE=InnoDB 11 | AUTO_INCREMENT=9 12 | ; 13 | --------------------------------------------------------------------------------