Job management component API for employment, grades, and job operations
The Jobs component manages character employment, job grades, on-duty status, and job-related operations. It’s essential for roleplay servers with different job roles and responsibilities.
-- Get character's joblocal jobData = COMPONENTS.Jobs:GetJob(char.SID)if jobData then print('Job:', jobData.jobLabel) print('Rank:', jobData.gradeLabel) print('On Duty:', jobData.onDuty) print('Salary: $' .. jobData.salary)else print('Character is unemployed')end-- Check if character has specific jobfunction HasJob(characterId, jobName) local jobData = COMPONENTS.Jobs:GetJob(characterId) return jobData and jobData.job == jobNameend-- Check if on dutyfunction IsOnDuty(characterId) local jobData = COMPONENTS.Jobs:GetJob(characterId) return jobData and jobData.onDuty == trueend
-- Set character to police officer (grade 1)COMPONENTS.Jobs:SetJob(char.SID, 'police', 1)-- Set character to unemployedCOMPONENTS.Jobs:SetJob(char.SID, 'unemployed', 0)-- Promote characterlocal currentJob = COMPONENTS.Jobs:GetJob(char.SID)COMPONENTS.Jobs:SetJob(char.SID, currentJob.job, currentJob.grade + 1)-- Hire player callbackCOMPONENTS.Callbacks:RegisterServerCallback('jobs:hire', function(source, data, cb) local player = Fetch:Source(source) if not player then return cb(false, 'Player not found') end local char = player:GetData('Character') if not char then return cb(false, 'No character') end -- Validate job exists if not COMPONENTS.Jobs:DoesJobExist(data.job) then return cb(false, 'Invalid job') end -- Set job local stateId = char:GetData('SID') local success = COMPONENTS.Jobs:SetJob(stateId, data.job, data.grade or 0) if success then -- Give job items GiveJobItems(source, data.job) -- Notify player TriggerClientEvent('mythic-notifications:client:Send', source, { message = 'You have been hired!', type = 'success' }) cb({ success = true }) else cb({ success = false, error = 'Failed to set job' }) endend)
-- Promote characterCOMPONENTS.Jobs:SetGrade(char.SID, 3)-- Demote characterCOMPONENTS.Jobs:SetGrade(char.SID, 1)-- Promote commandRegisterCommand('promote', function(source, args) if not IsPlayerAceAllowed(source, 'admin') then return end local targetId = tonumber(args[1]) local targetPlayer = Fetch:Source(targetId) if not targetPlayer then return end local targetChar = targetPlayer:GetData('Character') if not targetChar then return end local targetSID = targetChar:GetData('SID') local currentJob = COMPONENTS.Jobs:GetJob(targetSID) local newGrade = currentJob.grade + 1 -- Check max grade local maxGrade = COMPONENTS.Jobs:GetMaxGrade(currentJob.job) if newGrade > maxGrade then TriggerClientEvent('mythic-notifications:client:Send', source, { message = 'Already at max rank', type = 'error' }) return end COMPONENTS.Jobs:SetGrade(targetSID, newGrade) TriggerClientEvent('mythic-notifications:client:Send', targetId, { message = 'You have been promoted!', type = 'success' })end, true)
-- Clock inCOMPONENTS.Jobs:ClockIn(char.SID)-- Clock in with validationfunction ClockInPlayer(source) local player = Fetch:Source(source) if not player then return false, 'Player not found' end local char = player:GetData('Character') if not char then return false, 'No character' end local stateId = char:GetData('SID') local jobData = COMPONENTS.Jobs:GetJob(stateId) if not jobData or jobData.job == 'unemployed' then return false, 'No job' end if jobData.onDuty then return false, 'Already on duty' end local success = COMPONENTS.Jobs:ClockIn(stateId) if success then -- Give job equipment GiveJobEquipment(source, jobData.job) -- Notify TriggerClientEvent('mythic-notifications:client:Send', source, { message = 'You are now on duty', type = 'success' }) -- Update client TriggerClientEvent('mythic-jobs:client:SetDuty', source, true) return true else return false, 'Failed to clock in' endend-- Clock in at job locationRegisterNetEvent('mythic-jobs:server:clockIn', function() ClockInPlayer(source)end)
-- Clock outCOMPONENTS.Jobs:ClockOut(char.SID)-- Clock out with cleanupfunction ClockOutPlayer(source) local player = Fetch:Source(source) if not player then return false end local char = player:GetData('Character') if not char then return false end local stateId = char:GetData('SID') local jobData = COMPONENTS.Jobs:GetJob(stateId) if not jobData or not jobData.onDuty then return false, 'Not on duty' end local success = COMPONENTS.Jobs:ClockOut(stateId) if success then -- Remove job equipment RemoveJobEquipment(source, jobData.job) -- End active jobs/tasks TriggerEvent('mythic-jobs:server:EndActiveTasks', source) -- Notify TriggerClientEvent('mythic-notifications:client:Send', source, { message = 'You are now off duty', type = 'info' }) -- Update client TriggerClientEvent('mythic-jobs:client:SetDuty', source, false) return true else return false, 'Failed to clock out' endend-- Auto clock out on disconnectAddEventHandler('playerDropped', function() local player = Fetch:Source(source) if player then local char = player:GetData('Character') if char then local stateId = char:GetData('SID') COMPONENTS.Jobs:ClockOut(stateId) end endend)
-- Check if job existsif COMPONENTS.Jobs:DoesJobExist('police') then print('Police job exists')end-- Validate before setting jobfunction SafeSetJob(characterId, jobName, grade) if not COMPONENTS.Jobs:DoesJobExist(jobName) then return false, 'Job does not exist' end return COMPONENTS.Jobs:SetJob(characterId, jobName, grade)end
-- Get max gradelocal maxGrade = COMPONENTS.Jobs:GetMaxGrade('police')print('Max police rank:', maxGrade) -- 5-- Check if can promotefunction CanPromote(characterId) local jobData = COMPONENTS.Jobs:GetJob(characterId) local maxGrade = COMPONENTS.Jobs:GetMaxGrade(jobData.job) return jobData.grade < maxGradeend
-- Get on-duty policelocal onDutyPolice = COMPONENTS.Jobs:GetOnDutyPlayers('police')print('On-duty police:', #onDutyPolice)for _, characterId in ipairs(onDutyPolice) do local player = Fetch:SID(characterId) if player then local source = player:GetData('Source') print('Officer:', GetPlayerName(source)) endend-- Check if any police are onlinefunction AnyPoliceOnline() local police = COMPONENTS.Jobs:GetOnDutyPlayers('police') return #police > 0end-- Notify all on-duty policefunction NotifyPolice(message) local police = COMPONENTS.Jobs:GetOnDutyPlayers('police') for _, characterId in ipairs(police) do local player = Fetch:SID(characterId) if not player then goto continue end local source = player:GetData('Source') TriggerClientEvent('mythic-notifications:client:Send', source, { message = message, type = 'info' }) ::continue:: endend-- Dispatch systemfunction DispatchCall(callType, location) local police = COMPONENTS.Jobs:GetOnDutyPlayers('police') for _, characterId in ipairs(police) do local player = Fetch:SID(characterId) if player then local source = player:GetData('Source') TriggerClientEvent('mythic-dispatch:client:NewCall', source, { type = callType, location = location, timestamp = os.time() }) end endend
-- Get all police (on and off duty)local allPolice = COMPONENTS.Jobs:GetJobPlayers('police')print('Total police officers:', #allPolice)-- Check if player has coworkers onlinefunction GetCoworkers(characterId) local jobData = COMPONENTS.Jobs:GetJob(characterId) local coworkers = COMPONENTS.Jobs:GetJobPlayers(jobData.job) -- Remove self for i, sid in ipairs(coworkers) do if sid == characterId then table.remove(coworkers, i) break end end return coworkersend
-- Issue paychecklocal amount = COMPONENTS.Jobs:IssuePaycheck(char.SID)if amount then print('Paycheck issued: $' .. amount)end-- Automatic paycheck systemCreateThread(function() while true do Wait(1800000) -- 30 minutes for _, playerId in ipairs(GetPlayers()) do local source = tonumber(playerId) local player = Fetch:Source(source) if player then local char = player:GetData('Character') if char then local stateId = char:GetData('SID') local jobData = COMPONENTS.Jobs:GetJob(stateId) -- Only pay if on duty if jobData and jobData.onDuty and jobData.job ~= 'unemployed' then local amount = COMPONENTS.Jobs:IssuePaycheck(stateId) if amount then TriggerClientEvent('mythic-notifications:client:Send', source, { message = 'Paycheck received: $' .. amount, type = 'success' }) COMPONENTS.Logger:Info('Jobs', 'Paycheck issued', { console = true, file = true }, { character = stateId, job = jobData.jobLabel, amount = amount }) end end end end endend)
function SafeHire(characterId, jobName, grade) if not COMPONENTS.Jobs:DoesJobExist(jobName) then return false, 'Invalid job' end local maxGrade = COMPONENTS.Jobs:GetMaxGrade(jobName) if grade > maxGrade then return false, 'Invalid grade' end return COMPONENTS.Jobs:SetJob(characterId, jobName, grade)end
Clean Up on Clock Out
Copy
function CleanClockOut(source) local player = Fetch:Source(source) if not player then return false end local char = player:GetData('Character') if not char then return false end local stateId = char:GetData('SID') -- Clock out COMPONENTS.Jobs:ClockOut(stateId) -- Remove job items RemoveJobItems(source) -- End active tasks EndActiveTasks(source) -- Remove job vehicles RemoveJobVehicles(source)end
function HasJobPermission(source, permission) local player = Fetch:Source(source) if not player then return false end local char = player:GetData('Character') if not char then return false end local stateId = char:GetData('SID') local jobData = COMPONENTS.Jobs:GetJob(stateId) if not jobData then return false end -- Check job permissions return jobData.permissions and jobData.permissions[permission] == trueend
Job Integration: Most job-specific features should listen to job change events and on-duty status changes rather than constantly checking the character’s job.