363 lines
14 KiB
Lua
Raw Normal View History

2025-04-07 01:41:12 +00:00
lib.versionCheck('Qbox-project/qbx_management')
if not lib.checkDependency('qbx_core', '1.18.0', true) then error() return end
if not lib.checkDependency('ox_lib', '3.13.0', true) then error() return end
local config = require 'config.server'
local logger = require '@qbx_core.modules.logger'
local JOBS = exports.qbx_core:GetJobs()
local GANGS = exports.qbx_core:GetGangs()
local playersClockedIn = {}
local menus = {}
for groupName, menuInfo in pairs(config.menus) do
---@diagnostic disable-next-line: inject-field
menuInfo.groupName = groupName
menus[#menus + 1] = menuInfo
end
local function getMenuEntries(groupName, groupType)
local menuEntries = {}
local groupEntries = exports.qbx_core:GetGroupMembers(groupName, groupType)
for i = 1, #groupEntries do
local citizenid = groupEntries[i].citizenid
local grade = groupEntries[i].grade
local player = exports.qbx_core:GetPlayerByCitizenId(citizenid) or exports.qbx_core:GetOfflinePlayer(citizenid)
local namePrefix = player.Offline and '' or '🟢 '
local playerActivityData = groupType == 'job' and GetPlayerActivityData(citizenid, groupName) or nil
local playerClockData = playersClockedIn[player.PlayerData.source]
local playerLastCheckIn = playerClockData and os.date(config.formatDateTime, playerClockData.time) or playerActivityData?.last_checkin
menuEntries[#menuEntries + 1] = {
cid = citizenid,
grade = grade,
name = namePrefix..player.PlayerData.charinfo.firstname..' '..player.PlayerData.charinfo.lastname,
onduty = player.PlayerData.job.onduty and not player.Offline,
hours = playerActivityData?.hours,
last_checkin = playerLastCheckIn
}
end
return menuEntries
end
-- Get a list of employees for a given group.
---@param groupName string Name of job/gang to get employees of
---@param groupType GroupType
---@return table?
lib.callback.register('qbx_management:server:getEmployees', function(source, groupName, groupType)
local player = exports.qbx_core:GetPlayer(source)
if not player.PlayerData[groupType].isboss then return end
local menuEntries = getMenuEntries(groupName, groupType)
table.sort(menuEntries, function(a, b)
return a.grade > b.grade
end)
logger.log({
source = source,
event = 'qbx_management:server:getEmployees',
message = locale('logs.retrieved_employees', groupName, groupType),
webhook = config.discordWebhook
})
return menuEntries
end)
-- Callback for updating the grade information of online players
---@param source number
---@param citizenId string CitizenId of player who is being promoted/demoted
---@param oldGrade integer Old grade number of target employee
---@param newGrade integer New grade number of target employee
---@param groupType GroupType
lib.callback.register('qbx_management:server:updateGrade', function(source, citizenId, oldGrade, newGrade, groupType)
local player = exports.qbx_core:GetPlayer(source)
local employee = exports.qbx_core:GetPlayerByCitizenId(citizenId)
local jobName = player.PlayerData[groupType].name
local gradeLevel = player.PlayerData[groupType].grade.level
if not player.PlayerData[groupType].isboss then return end
if player.PlayerData.citizenid == citizenId then
exports.qbx_core:Notify(source, locale('error.cant_promote_self'), 'error')
return
end
if oldGrade >= gradeLevel or newGrade >= gradeLevel then
exports.qbx_core:Notify(source, locale('error.cant_promote'), 'error')
return
end
if groupType == 'job' then
local success, errorResult = exports.qbx_core:AddPlayerToJob(citizenId, jobName, newGrade)
assert(success, errorResult?.message)
else
local success, errorResult = exports.qbx_core:AddPlayerToGang(citizenId, jobName, newGrade)
assert(success, errorResult?.message)
end
if employee then
local gradeName = groupType == 'gang' and GANGS[jobName].grades[newGrade].name or JOBS[jobName].grades[newGrade].name
exports.qbx_core:Notify(employee.PlayerData.source, locale('success.promoted_to')..gradeName..'.', 'success')
end
exports.qbx_core:Notify(source, locale('success.promoted'), 'success')
logger.log({
source = source,
event = 'qbx_management:server:updateGrade',
message = locale('logs.updated_grade', citizenId, oldGrade, newGrade, jobName, groupType),
webhook = config.discordWebhook
})
end)
-- Callback to hire online player as employee of a given group
---@param employee integer Server ID of target employee to be hired
---@param groupType GroupType
lib.callback.register('qbx_management:server:hireEmployee', function(source, employee, groupType)
local player = exports.qbx_core:GetPlayer(source)
local target = exports.qbx_core:GetPlayer(employee)
if not player.PlayerData[groupType].isboss then return end
if not target then
exports.qbx_core:Notify(source, locale('error.not_around'), 'error')
return
end
local groupName = player.PlayerData[groupType].name
local logArea = groupType == 'gang' and 'Gang' or 'Boss'
if groupType == 'job' then
local success, errorResult = exports.qbx_core:AddPlayerToJob(target.PlayerData.citizenid, groupName, 0)
assert(success, errorResult?.message)
success, errorResult = exports.qbx_core:SetPlayerPrimaryJob(target.PlayerData.citizenid, groupName)
assert(success, errorResult?.message)
else
local success, errorResult = exports.qbx_core:AddPlayerToGang(target.PlayerData.citizenid, groupName, 0)
assert(success, errorResult?.message)
success, errorResult = exports.qbx_core:SetPlayerPrimaryGang(target.PlayerData.citizenid, groupName)
assert(success, errorResult?.message)
end
local playerFullName = player.PlayerData.charinfo.firstname..' '..player.PlayerData.charinfo.lastname
local targetFullName = target.PlayerData.charinfo.firstname..' '..target.PlayerData.charinfo.lastname
local organizationLabel = player.PlayerData[groupType].label
exports.qbx_core:Notify(source, locale('success.hired_into', targetFullName, organizationLabel), 'success')
exports.qbx_core:Notify(target.PlayerData.source, locale('success.hired_to')..organizationLabel, 'success')
logger.log({
source = source,
event = 'qbx_management:server:hireEmployee',
message = locale('logs.hired_employee', logArea, playerFullName, targetFullName, organizationLabel, 0),
webhook = config.discordWebhook
})
end)
-- Returns playerdata for a given table of player server ids.
---@param closePlayers table Table of player data for possible hiring
---@return table
lib.callback.register('qbx_management:server:getPlayers', function(_, closePlayers)
local players = {}
for _, v in pairs(closePlayers) do
local player = exports.qbx_core:GetPlayer(v.id)
players[#players + 1] = {
id = v.id,
name = player.PlayerData.charinfo.firstname..' '..player.PlayerData.charinfo.lastname,
citizenid = player.PlayerData.citizenid,
job = player.PlayerData.job,
gang = player.PlayerData.gang,
source = player.PlayerData.source
}
end
table.sort(players, function(a, b)
return a.name < b.name
end)
logger.log({
source = 'qbx_management',
event = 'qbx_management:server:getPlayers',
message = locale('logs.retrieved_players'),
webhook = config.discordWebhook
})
return players
end)
---@param employeeCitizenId string
---@diagnostic disable-next-line: undefined-doc-name
---@param boss Player | table
---@param groupName string
---@param groupType GroupType
---@return boolean success
local function fireEmployee(employeeCitizenId, boss, groupName, groupType)
local employee = exports.qbx_core:GetPlayerByCitizenId(employeeCitizenId) or exports.qbx_core:GetOfflinePlayer(employeeCitizenId)
if employee.PlayerData.citizenid == boss.PlayerData.citizenid then
local message = groupType == 'gang' and locale('error.kick_yourself') or locale('error.fire_yourself')
exports.qbx_core:Notify(boss.PlayerData.source, message, 'error')
return false
end
if not employee then
exports.qbx_core:Notify(boss.PlayerData.source, locale('error.person_doesnt_exist'), 'error')
return false
end
local employeeGrade = groupType == 'job' and employee.PlayerData.jobs?[groupName] or employee.PlayerData.gangs?[groupName]
local bossGrade = groupType == 'job' and boss.PlayerData.jobs?[groupName] or boss.PlayerData.gangs?[groupName]
if employeeGrade >= bossGrade then
exports.qbx_core:Notify(boss.PlayerData.source, locale('error.fire_boss'), 'error')
return false
end
if groupType == 'job' then
local success, errorResult = exports.qbx_core:RemovePlayerFromJob(employee.PlayerData.citizenid, groupName)
assert(success, errorResult?.message)
else
local success, errorResult = exports.qbx_core:RemovePlayerFromGang(employee.PlayerData.citizenid, groupName)
assert(success, errorResult?.message)
end
if not employee.Offline then
local message = groupType == 'gang' and locale('error.you_gang_fired', GANGS[groupName].label) or locale('error.you_job_fired', JOBS[groupName].label)
exports.qbx_core:Notify(employee.PlayerData.source, message, 'error')
end
logger.log({
source = boss.PlayerData.source,
event = 'qbx_management:server:fireEmployee',
message = locale('logs.fired_employee', employeeCitizenId, groupName, groupType),
webhook = config.discordWebhook
})
return true
end
-- Callback for firing a player from a given society.
---@param employee string citizenid of employee to be fired
---@param groupType GroupType
lib.callback.register('qbx_management:server:fireEmployee', function(source, employee, groupType)
local player = exports.qbx_core:GetPlayer(source)
local firedEmployee = exports.qbx_core:GetPlayerByCitizenId(employee) or exports.qbx_core:GetOfflinePlayer(employee)
local playerFullName = player.PlayerData.charinfo.firstname..' '..player.PlayerData.charinfo.lastname
local organizationLabel = player.PlayerData[groupType].label
if not player.PlayerData[groupType].isboss then return end
if not firedEmployee then lib.print.error("not able to find player with citizenid", employee) return end
local success = fireEmployee(employee, player, player.PlayerData[groupType].name, groupType)
local employeeFullName = firedEmployee.PlayerData.charinfo.firstname..' '..firedEmployee.PlayerData.charinfo.lastname
if success then
local logArea = groupType == 'gang' and 'Gang' or 'Boss'
local logType = groupType == 'gang' and locale('error.gang_fired') or locale('error.job_fired')
exports.qbx_core:Notify(source, logType, 'success')
logger.log({
source = source,
event = 'qbx_management:server:fireEmployee',
message = locale('logs.fired_employee_success', logArea, playerFullName, employeeFullName, organizationLabel),
webhook = config.discordWebhook
})
else
exports.qbx_core:Notify(source, locale('error.unable_fire'), 'error')
end
end)
lib.callback.register('qbx_management:server:getBossMenus', function()
logger.log({
source = 'qbx_management',
event = 'qbx_management:server:getBossMenus',
message = locale('logs.retrieved_boss_menus'),
webhook = config.discordWebhook
})
return menus
end)
---Creates a boss zone for the specified group
---@param menuInfo MenuInfo
local function registerBossMenu(menuInfo)
menus[#menus + 1] = menuInfo
TriggerClientEvent('qbx_management:client:bossMenuRegistered', -1, menuInfo)
logger.log({
source = 'qbx_management',
event = 'qbx_management:server:registerBossMenu',
message = locale('logs.registered_boss_menu', menuInfo.groupName),
webhook = config.discordWebhook
})
end
exports('RegisterBossMenu', registerBossMenu)
---@param source number
---@param citizenid string
---@param job string
local function doPlayerCheckIn(source, citizenid, job)
playersClockedIn[source] = { citizenid = citizenid, job = job, time = os.time() }
logger.log({
source = source,
event = 'qbx_management:server:doPlayerCheckIn',
message = locale('logs.player_checkin', citizenid, job),
webhook = config.discordWebhook
})
end
---@param source number
local function onPlayerUnload(source)
if playersClockedIn[source] then
OnPlayerCheckOut(playersClockedIn[source])
playersClockedIn[source] = nil
logger.log({
source = source,
event = 'qbx_management:server:onPlayerUnload',
message = locale('logs.player_unload', source),
webhook = config.discordWebhook
})
end
end
---@param source number
RegisterNetEvent('QBCore:Server:OnPlayerLoaded', function()
local player = exports.qbx_core:GetPlayer(source)
if player == nil then return end
if player.PlayerData.job.onduty then
doPlayerCheckIn(player.PlayerData.source, player.PlayerData.citizenid, player.PlayerData.job.name)
end
end)
---@param source number
---@param job table?
AddEventHandler('QBCore:Server:OnJobUpdate', function(source, job)
if playersClockedIn[source] then
onPlayerUnload(source)
end
local player = exports.qbx_core:GetPlayer(source)
if player == nil then return end
if player.PlayerData.job.onduty then
doPlayerCheckIn(player.PlayerData.source, player.PlayerData.citizenid, job.name)
end
end)
---@param source number
---@param duty boolean
AddEventHandler('QBCore:Server:SetDuty', function(source, duty)
local player = exports.qbx_core:GetPlayer(source)
if player == nil then return end
if duty then
doPlayerCheckIn(player.PlayerData.source, player.PlayerData.citizenid, player.PlayerData.job.name)
else
onPlayerUnload(player.PlayerData.source)
end
end)
---@param source number
AddEventHandler('QBCore:Server:OnPlayerUnload', function()
onPlayerUnload(source)
end)
---@param source number
AddEventHandler('playerDropped', function()
onPlayerUnload(source)
end)