├── qb-cs.sql ├── fxmanifest.lua ├── README.md ├── config.lua ├── server.lua └── client.lua /qb-cs.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE communityservice ( 2 | citizenid VARCHAR(100) NOT NULL, 3 | actions_remaining int(10) NOT NULL, 4 | PRIMARY KEY (citizenid) 5 | ); -------------------------------------------------------------------------------- /fxmanifest.lua: -------------------------------------------------------------------------------- 1 | fx_version 'cerulean' 2 | game 'gta5' 3 | 4 | description 'QB-CommunityService' 5 | version '1.0.0' 6 | 7 | shared_scripts { 8 | 'config.lua', 9 | } 10 | 11 | client_script 'client.lua' 12 | 13 | server_scripts { 14 | 'server.lua', 15 | '@oxmysql/lib/MySQL.lua', 16 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # qb-communityservice 2 | Unknown: This is a pull from Kakarots qb-community service, I have converted it from ghmattimysql to oxmysql v1.2 3 | 4 | jaylac2000: update to latest MySQL with oxmysql. made script completely functional again as it wasn't working with the verion I personally had from months ago when [qb-core](https://github.com/qbcore-framework/qb-core) used import.lua -------------------------------------------------------------------------------- /config.lua: -------------------------------------------------------------------------------- 1 | Config = {} 2 | Config.ServiceExtensionOnEscape = 8 3 | Config.ServiceLocation = vector3(168.46, -992.45, 30.09) 4 | Config.ReleaseLocation = vector4(424.30, -978.60, 30.71, 90.67) 5 | 6 | Config.ServiceLocations = { 7 | { type = "cleaning", coords = vector3(170.0, -1006.0, 29.34) }, 8 | { type = "cleaning", coords = vector3(177.0, -1007.94, 29.33) }, 9 | { type = "cleaning", coords = vector3(181.58, -1009.46, 29.34) }, 10 | { type = "cleaning", coords = vector3(189.33, -1009.48, 29.34) }, 11 | { type = "cleaning", coords = vector3(195.31, -1016.0, 29.34) }, 12 | { type = "cleaning", coords = vector3(169.97, -1001.29, 29.34) }, 13 | { type = "cleaning", coords = vector3(164.74, -1008.0, 29.43) }, 14 | { type = "cleaning", coords = vector3(163.28, -1000.55, 29.35) }, 15 | { type = "gardening", coords = vector3(181.38, -1000.05, 29.29) }, 16 | { type = "gardening", coords = vector3(188.43, -1000.38, 29.29) }, 17 | { type = "gardening", coords = vector3(194.81, -1002.0, 29.29) }, 18 | { type = "gardening", coords = vector3(198.97, -1006.85, 29.29) }, 19 | { type = "gardening", coords = vector3(201.47, -1004.37, 29.29) } 20 | } 21 | 22 | Config.Uniforms = { 23 | ['male'] = { 24 | outfitData = { 25 | ['t-shirt'] = {item = 15, texture = 0}, 26 | ['torso2'] = {item = 352, texture = 0}, 27 | ['arms'] = {item = 86, texture = 0}, 28 | ['pants'] = {item = 27, texture = 2}, 29 | ['shoes'] = {item = 71, texture = 6}, 30 | } 31 | }, 32 | ['female'] = { 33 | outfitData = { 34 | ['t-shirt'] = {item = 36, texture = 0}, 35 | ['torso2'] = {item = 262, texture = 18}, 36 | ['arms'] = {item = 105, texture = 0}, 37 | ['pants'] = {item = 106, texture = 4}, 38 | ['shoes'] = {item = 79, texture = 8}, 39 | } 40 | }, 41 | } -------------------------------------------------------------------------------- /server.lua: -------------------------------------------------------------------------------- 1 | QBCore = exports['qb-core']:GetCoreObject() 2 | -- Commands 3 | 4 | QBCore.Commands.Add('comserv', 'Send To Community Service', {{name='id', help='Target ID'}, {name='amount', help='Action Amount'}}, true, function(source, args) 5 | local src = source 6 | local target = tonumber(args[1]) 7 | local targetPlayer = GetPlayerPed(target) 8 | local amount = tonumber(args[2]) 9 | local Player = QBCore.Functions.GetPlayer(src) 10 | if Player.PlayerData.job.name == "police" then 11 | if targetPlayer ~= 0 then 12 | if amount > 0 then 13 | TriggerEvent('qb-communityservice:server:sendToCommunityService', target, amount) 14 | TriggerClientEvent('QBCore:Notify', source, 'This citizen has been sentenced to community service!', 'success') 15 | TriggerClientEvent('QBCore:Notify', target, 'You Have Been Sentenced To '..amount.. ' Month(s) of Community Service') 16 | else 17 | TriggerClientEvent('QBCore:Notify', source, 'Invalid amount of months', 'error') 18 | end 19 | else 20 | TriggerClientEvent('QBCore:Notify', source, 'Invalid citizen ID', 'error') 21 | end 22 | else 23 | TriggerClientEvent('QBCore:Notify', source, 'You are not a police officer', 'error') 24 | end 25 | end) 26 | 27 | QBCore.Commands.Add('endcomserv', 'End Community Service', {{name='id', help='Target ID'}}, true, function(source, args) 28 | local src = source 29 | local target = tonumber(args[1]) 30 | local targetPlayer = GetPlayerPed(target) 31 | local Player = QBCore.Functions.GetPlayer(src) 32 | if Player.PlayerData.job.name == "police" then 33 | if targetPlayer ~= 0 then 34 | Release(target) 35 | else 36 | TriggerClientEvent('QBCore:Notify', source, 'Invalid citizen ID', 'error') 37 | end 38 | else 39 | TriggerClientEvent('QBCore:Notify', source, 'You are not a police officer', 'error') 40 | end 41 | end) 42 | 43 | RegisterServerEvent('qb-communityservice:server:finishCommunityService') 44 | AddEventHandler('qb-communityservice:server:finishCommunityService', function(source) 45 | local src = source 46 | Release(src) 47 | end) 48 | 49 | -- Events 50 | 51 | RegisterServerEvent('qb-communityservice:server:checkIfSentenced') 52 | AddEventHandler('qb-communityservice:server:checkIfSentenced', function() 53 | local src = source 54 | local citizenid = QBCore.Functions.GetPlayer(src).PlayerData.citizenid 55 | local result = MySQL.Sync.fetchAll('SELECT * FROM communityservice WHERE citizenid = ?', {citizenid}) 56 | if result[1] ~= nil and result[1].actions_remaining > 0 then 57 | TriggerClientEvent('qb-communityservice:client:inCommunityService', src, tonumber(result[1].actions_remaining)) 58 | end 59 | end) 60 | 61 | RegisterServerEvent('qb-communityservice:server:completeService') 62 | AddEventHandler('qb-communityservice:server:completeService', function() 63 | local src = source 64 | local citizenid = QBCore.Functions.GetPlayer(src).PlayerData.citizenid 65 | MySQL.Async.execute('UPDATE communityservice SET actions_remaining = actions_remaining - 1 WHERE citizenid = ?', {citizenid}) 66 | end) 67 | 68 | RegisterServerEvent('qb-communityservice:server:extendService') 69 | AddEventHandler('qb-communityservice:server:extendService', function() 70 | local src = source 71 | local citizenid = QBCore.Functions.GetPlayer(src).PlayerData.citizenid 72 | MySQL.Async.execute('UPDATE communityservice SET actions_remaining = actions_remaining + @extension_value WHERE citizenid = ?', {citizenid, Config.ServiceExtensionOnEscape}) 73 | end) 74 | 75 | RegisterServerEvent('qb-communityservice:server:sendToCommunityService') 76 | AddEventHandler('qb-communityservice:server:sendToCommunityService', function(target, actions_count) 77 | local Ply = QBCore.Functions.GetPlayer(target) 78 | local citizenid = Ply.PlayerData.citizenid 79 | 80 | MySQL.Async.insert('INSERT INTO communityservice (citizenid, actions_remaining) VALUES (?, ?) ON DUPLICATE KEY UPDATE actions_remaining = ?', {citizenid, actions_count}) 81 | TriggerClientEvent('qb-communityservice:client:inCommunityService', target, actions_count) 82 | end) 83 | 84 | -- Functions 85 | 86 | function Release(target) 87 | local Ply = QBCore.Functions.GetPlayer(target) 88 | local citizenid = Ply.PlayerData.citizenid 89 | 90 | MySQL.Async.execute('DELETE FROM communityservice WHERE citizenid = ?', {citizenid}) 91 | TriggerClientEvent('QBCore:Notify', target, 'Community service completed', 'success') 92 | TriggerClientEvent('qb-communityservice:client:finishCommunityService', target) 93 | end -------------------------------------------------------------------------------- /client.lua: -------------------------------------------------------------------------------- 1 | QBCore = exports['qb-core']:GetCoreObject() 2 | local isSentenced = false 3 | local communityServiceFinished = false 4 | local actionsRemaining = 0 5 | local availableActions = {} 6 | local disable_actions = false 7 | 8 | -- Events 9 | 10 | RegisterNetEvent('QBCore:Client:OnPlayerLoaded') 11 | AddEventHandler('QBCore:Client:OnPlayerLoaded', function() 12 | TriggerServerEvent('qb-communityservice:server:checkIfSentenced') 13 | end) 14 | 15 | RegisterNetEvent('qb-communityservice:client:finishCommunityService') 16 | AddEventHandler('qb-communityservice:client:finishCommunityService', function(source) 17 | communityServiceFinished = true 18 | isSentenced = false 19 | actionsRemaining = 0 20 | end) 21 | 22 | RegisterNetEvent('qb-communityservice:client:inCommunityService') 23 | AddEventHandler('qb-communityservice:client:inCommunityService', function(actions_remaining) 24 | local playerPed = PlayerPedId() 25 | if isSentenced then return end 26 | actionsRemaining = actions_remaining 27 | FillActionTable() 28 | ChangeOutfit() 29 | TriggerServerEvent('prison:server:SaveJailItems') 30 | SetEntityCoords(playerPed, Config.ServiceLocation.x, Config.ServiceLocation.y, Config.ServiceLocation.z) 31 | isSentenced = true 32 | communityServiceFinished = false 33 | while actionsRemaining > 0 and communityServiceFinished ~= true do 34 | local pCoords = GetEntityCoords(playerPed) 35 | if IsPedInAnyVehicle(playerPed, false) then 36 | ClearPedTasksImmediately(playerPed) 37 | end 38 | Wait(20000) 39 | if #(pCoords - Config.ServiceLocation) > 45 then 40 | SetEntityCoords(playerPed, Config.ServiceLocation.x, Config.ServiceLocation.y, Config.ServiceLocation.z) 41 | TriggerServerEvent('qb-communityservice:server:extendService') 42 | actionsRemaining = actionsRemaining + Config.ServiceExtensionOnEscape 43 | QBCore.Functions.Notify('Escape Attempted! '..Config.ServiceExtensionOnEscape..' More Months Added!') 44 | end 45 | end 46 | TriggerServerEvent('prison:server:GiveJailItems') 47 | SetEntityCoords(playerPed, Config.ReleaseLocation.x, Config.ReleaseLocation.y, Config.ReleaseLocation.z) 48 | isSentenced = false 49 | TriggerServerEvent('qb-clothes:loadPlayerSkin') 50 | end) 51 | 52 | -- Main Thread 53 | 54 | Citizen.CreateThread(function() 55 | while true do 56 | ::start_over:: 57 | Wait(1) 58 | if actionsRemaining > 0 and isSentenced then 59 | Draw2DText('Actions Remaining: '..actionsRemaining, {0.175, 0.955}) 60 | DrawAvailableActions() 61 | local ped = PlayerPedId() 62 | local pCoords = GetEntityCoords(ped) 63 | for i = 1, #availableActions do 64 | local distance = #(pCoords - availableActions[i].coords) 65 | if distance < 1.5 then 66 | DisplayHelpText("Press ~INPUT_CONTEXT~ to start cleaning.") 67 | if (IsControlJustReleased(1, 38)) then 68 | tmp_action = availableActions[i] 69 | RemoveAction(tmp_action) 70 | FillActionTable(tmp_action) 71 | disable_actions = true 72 | TriggerServerEvent('qb-communityservice:server:completeService') 73 | actionsRemaining = actionsRemaining - 1 74 | if (tmp_action.type == "cleaning") then 75 | TaskStartScenarioInPlace(ped, 'WORLD_HUMAN_GARDENER_PLANT', 0, false) 76 | else 77 | TaskStartScenarioInPlace(ped, 'WORLD_HUMAN_GARDENER_PLANT', 0, false) 78 | end 79 | Wait(5000) 80 | ClearPedTasks(ped) 81 | goto start_over 82 | end 83 | end 84 | end 85 | else 86 | Wait(1000) 87 | end 88 | end 89 | end) 90 | 91 | -- Functions 92 | 93 | function FillActionTable(last_action) 94 | while #availableActions < 5 do 95 | local service_does_not_exist = true 96 | local random_selection = Config.ServiceLocations[math.random(1,#Config.ServiceLocations)] 97 | for i = 1, #availableActions do 98 | if random_selection.coords.x == availableActions[i].coords.x and random_selection.coords.y == availableActions[i].coords.y and random_selection.coords.z == availableActions[i].coords.z then 99 | service_does_not_exist = false 100 | end 101 | end 102 | if last_action ~= nil and random_selection.coords.x == last_action.coords.x and random_selection.coords.y == last_action.coords.y and random_selection.coords.z == last_action.coords.z then 103 | service_does_not_exist = false 104 | end 105 | if service_does_not_exist then 106 | table.insert(availableActions, random_selection) 107 | end 108 | end 109 | end 110 | 111 | function RemoveAction(action) 112 | local action_pos = -1 113 | for i=1, #availableActions do 114 | if action.coords.x == availableActions[i].coords.x and action.coords.y == availableActions[i].coords.y and action.coords.z == availableActions[i].coords.z then 115 | action_pos = i 116 | end 117 | end 118 | if action_pos ~= -1 then 119 | table.remove(availableActions, action_pos) 120 | else 121 | print("User tried to remove an unavailable action") 122 | end 123 | end 124 | 125 | function ChangeOutfit() 126 | local ped = PlayerPedId() 127 | local gender = QBCore.Functions.GetPlayerData().charinfo.gender 128 | if gender == 0 then 129 | TriggerEvent('qb-clothing:client:loadOutfit', Config.Uniforms.male) 130 | else 131 | TriggerEvent('qb-clothing:client:loadOutfit', Config.Uniforms.female) 132 | end 133 | end 134 | 135 | function DrawAvailableActions() 136 | for i = 1, #availableActions do 137 | DrawMarker(21, availableActions[i].coords, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.5, 0.5, 50, 50, 204, 100, false, true, 2, true, false, false, false) 138 | end 139 | end 140 | 141 | function DisplayHelpText(str) 142 | SetTextComponentFormat("STRING") 143 | AddTextComponentString(str) 144 | DisplayHelpTextFromStringLabel(0, 0, 1, -1) 145 | end 146 | 147 | function Draw2DText(text, pos) 148 | SetTextFont(1) 149 | SetTextProportional(1) 150 | SetTextScale(0.45, 0.45) 151 | SetTextColour(255, 255, 255, 255) 152 | SetTextDropShadow(0, 0, 0, 0, 255) 153 | SetTextEdge(1, 0, 0, 0, 255) 154 | SetTextDropShadow() 155 | SetTextOutline() 156 | BeginTextCommandDisplayText('STRING') 157 | AddTextComponentSubstringPlayerName(text) 158 | EndTextCommandDisplayText(table.unpack(pos)) 159 | end --------------------------------------------------------------------------------