2023-09-24 04:45:37 +02:00
local function removeCharacterFunctions ( character )
local newData = { }
for k , v in pairs ( character ) do
if type ( v ) ~= " function " then
newData [ k ] = v
end
end
return newData
end
2023-07-09 21:19:35 +02:00
local function createCharacterTable ( info )
2023-07-11 00:35:11 +02:00
local playerInfo = PlayersInfo [ info.source ] or { }
2023-07-09 21:19:35 +02:00
local self = {
2023-07-16 16:33:37 +02:00
id = info.id ,
2023-07-09 21:19:35 +02:00
source = info.source ,
2023-07-10 03:33:01 +02:00
identifier = info.identifier ,
2023-07-11 00:35:11 +02:00
identifiers = playerInfo.identifiers or { } ,
2023-07-12 04:53:35 +02:00
discord = playerInfo.discord or { } ,
2023-07-09 21:19:35 +02:00
name = info.name ,
firstname = info.firstname ,
lastname = info.lastname ,
2023-08-31 02:53:34 +02:00
fullname = ( " %s %s " ) : format ( info.firstname , info.lastname ) ,
2023-07-09 21:19:35 +02:00
dob = info.dob ,
gender = info.gender ,
cash = info.cash ,
bank = info.bank ,
2024-01-07 21:13:56 +01:00
phonenumber = info.phonenumber ,
2023-07-10 02:45:32 +02:00
groups = info.groups ,
2023-07-09 21:19:35 +02:00
metadata = info.metadata ,
inventory = info.inventory
}
2023-07-10 02:45:32 +02:00
---@param account string
---@param amount number
---@param reason string|nil
---@return boolean
2023-07-09 21:19:35 +02:00
function self . deductMoney ( account , amount , reason )
local amount = tonumber ( amount )
2023-08-23 00:33:21 +02:00
if not amount or amount <= 0 or account ~= " bank " and account ~= " cash " then return end
2023-07-09 21:19:35 +02:00
self [ account ] -= amount
2023-08-23 00:30:38 +02:00
if NDCore.players [ self.source ] then
2023-07-13 01:23:23 +02:00
self.triggerEvent ( " ND:updateMoney " , self.cash , self.bank )
2023-07-09 21:19:35 +02:00
TriggerEvent ( " ND:moneyChange " , self.source , account , amount , " remove " , reason )
end
return true
2023-07-09 03:07:46 +02:00
end
2023-07-09 21:19:35 +02:00
2023-07-10 02:45:32 +02:00
---@param account string
---@param amount number
---@param reason string|nil
---@return boolean
2023-07-09 21:19:35 +02:00
function self . addMoney ( account , amount , reason )
local amount = tonumber ( amount )
2023-08-23 00:33:21 +02:00
if not amount or amount <= 0 or account ~= " bank " and account ~= " cash " then return end
2023-07-09 21:19:35 +02:00
self [ account ] += amount
2023-08-23 00:30:38 +02:00
if NDCore.players [ self.source ] then
2023-07-13 01:23:23 +02:00
self.triggerEvent ( " ND:updateMoney " , self.cash , self.bank )
2023-07-09 21:19:35 +02:00
TriggerEvent ( " ND:moneyChange " , self.source , account , amount , " add " , reason )
end
return true
2023-07-09 03:37:46 +02:00
end
2023-07-10 02:45:32 +02:00
---@param amount number
---@return boolean
2023-07-09 21:19:35 +02:00
function self . depositMoney ( amount )
local amount = tonumber ( amount )
if not amount or self.cash < amount or amount <= 0 then return end
2023-07-10 02:45:32 +02:00
return self.deductMoney ( " cash " , amount , " Deposit " ) and self.addMoney ( " bank " , amount , " Deposit " )
2023-07-09 03:07:46 +02:00
end
2023-07-09 21:19:35 +02:00
2023-07-10 02:45:32 +02:00
---@param amount number
---@return boolean
2023-07-09 21:19:35 +02:00
function self . withdrawMoney ( amount )
local amount = tonumber ( amount )
if not amount or self.bank < amount or amount <= 0 then return end
2023-07-10 02:45:32 +02:00
return self.deductMoney ( " bank " , amount , " Withdraw " ) and self.addMoney ( " cash " , amount , " Withdraw " )
2023-07-09 21:19:35 +02:00
end
2023-07-16 12:31:20 +02:00
2023-08-25 04:59:31 +02:00
---@param data string
---@return any
function self . getData ( data )
return self [ data ]
end
---@param metadata string|table
---@return any
function self . getMetadata ( metadata )
if type ( metadata ) ~= " table " then
return self.metadata [ metadata ]
end
local returnData = { }
2023-08-26 01:37:15 +02:00
for i = 1 , # metadata do
2023-08-25 04:59:31 +02:00
local data = metadata [ i ]
returnData [ data ] = self.metadata [ data ]
end
return returnData
end
2023-07-23 00:00:08 +02:00
---@param key string|table
2023-07-16 12:31:20 +02:00
---@param value any
2023-08-26 04:35:34 +02:00
function self . setData ( key , value , reason )
2023-07-23 00:00:08 +02:00
if type ( key ) == " table " then
for k , v in pairs ( key ) do
self [ k ] = v
2023-08-23 00:30:38 +02:00
if k == " cash " or k == " bank " then
2023-08-26 04:35:34 +02:00
TriggerEvent ( " ND:moneyChange " , self.source , k , v , " set " , reason )
2023-08-23 00:30:38 +02:00
end
2023-07-23 00:00:08 +02:00
end
else
self [ key ] = value
2023-08-23 00:30:38 +02:00
if key == " cash " or key == " bank " then
2023-08-26 04:35:34 +02:00
TriggerEvent ( " ND:moneyChange " , self.source , key , value , " set " , reason )
2023-08-23 00:30:38 +02:00
end
2023-07-23 00:00:08 +02:00
end
2023-09-24 04:45:37 +02:00
self.triggerEvent ( " ND:updateCharacter " , removeCharacterFunctions ( self ) )
2023-07-16 12:31:20 +02:00
end
2023-07-09 21:19:35 +02:00
2023-07-23 00:00:08 +02:00
---@param key string|table
2023-07-10 02:45:32 +02:00
---@param value any
---@return table
2023-07-09 21:19:35 +02:00
function self . setMetadata ( key , value )
2023-07-23 00:00:08 +02:00
if type ( key ) == " table " then
for k , v in pairs ( key ) do
self.metadata [ k ] = v
end
else
self.metadata [ key ] = value
end
2023-09-24 04:45:37 +02:00
self.triggerEvent ( " ND:updateCharacter " , removeCharacterFunctions ( self ) )
2023-07-09 21:19:35 +02:00
return self.metadata
end
2023-07-10 02:45:32 +02:00
-- Completely delete character
function self . delete ( )
local result = MySQL.query . await ( " DELETE FROM nd_characters WHERE charid = ? " , { self.id } )
2023-08-23 00:30:38 +02:00
if result and NDCore.players [ self.source ] then
NDCore.players [ self.source ] = nil
2023-07-10 02:45:32 +02:00
end
return result
end
2023-07-09 21:19:35 +02:00
2023-07-10 02:45:32 +02:00
-- Unload and save character
function self . unload ( )
2023-08-23 00:30:38 +02:00
if not NDCore.players [ self.source ] then return end
2023-08-24 01:00:54 +02:00
for name , _ in pairs ( self.groups ) do
2023-08-25 04:25:48 +02:00
lib.removePrincipal ( self.source , ( " group.%s " ) : format ( name ) )
2023-08-24 01:00:54 +02:00
end
2023-07-10 02:45:32 +02:00
local ped = GetPlayerPed ( self.source )
if ped then
local coords = GetEntityCoords ( ped )
local heading = GetEntityHeading ( ped )
self.setMetadata ( " location " , {
x = coords.x ,
y = coords.y ,
2023-10-30 11:43:06 +01:00
z = coords.z ,
2023-07-10 02:45:32 +02:00
w = heading
} )
end
2023-08-27 17:59:22 +02:00
self.triggerEvent ( " ND:characterUnloaded " )
2023-07-10 02:45:32 +02:00
TriggerEvent ( " ND:characterUnloaded " , self.source , self )
2023-08-27 17:59:22 +02:00
local saved = self.save ( )
2023-08-23 00:30:38 +02:00
NDCore.players [ self.source ] = nil
2023-08-27 17:59:22 +02:00
return saved
2023-07-09 21:19:35 +02:00
end
2023-07-10 02:45:32 +02:00
-- Save character information to database
2024-01-11 00:36:18 +01:00
function self . save ( key )
if key then
local charData = self [ key ]
if not charData or type ( charData ) == " function " then
return lib.print . error ( key , " Is not valid character data. " )
end
if type ( charData ) == " table " then
charData = json.encode ( charData )
end
local query = ( " UPDATE nd_characters SET `%s` = ? WHERE charid = ? " ) : format ( key )
local affectedRows = MySQL.update . await ( query , { charData , self.id } )
return affectedRows > 0
end
2024-01-07 21:13:56 +01:00
local affectedRows = MySQL.update . await ( " UPDATE nd_characters SET name = ?, firstname = ?, lastname = ?, dob = ?, gender = ?, cash = ?, bank = ?, phonenumber = ?, `groups` = ?, metadata = ? WHERE charid = ? " , {
2023-07-09 21:19:35 +02:00
self.name ,
self.firstname ,
self.lastname ,
self.dob ,
self.gender ,
self.cash ,
self.bank ,
2024-01-07 21:13:56 +01:00
self.phonenumber ,
2023-07-10 02:45:32 +02:00
json.encode ( self.groups ) ,
2023-07-09 21:19:35 +02:00
json.encode ( self.metadata ) ,
2023-08-27 17:59:22 +02:00
self.id
2023-07-09 21:19:35 +02:00
} )
2023-08-27 17:59:22 +02:00
return affectedRows > 0
2023-07-09 21:19:35 +02:00
end
2023-07-10 02:45:32 +02:00
---Create a license/permit for the character
---@param licenseType string
---@param expire number
2023-07-09 21:19:35 +02:00
function self . createLicense ( licenseType , expire )
local expireIn = tonumber ( expire ) or 2592000
local time = os.time ( )
local licenses = self.metadata . licenses
local identifier = { }
for i = 1 , 16 do
identifier [ i ] = math.random ( 0 , 1 ) == 1 and string.char ( math.random ( 65 , 90 ) ) or math.random ( 0 , 9 )
end
local license = {
type = licenseType ,
status = " valid " ,
issued = time ,
expires = time + expireIn ,
identifier = table.concat ( identifier )
}
if licenses then
self.metadata . licenses [ # licenses + 1 ] = license
2023-07-13 01:23:23 +02:00
else
self.metadata . licenses = { license }
2023-07-09 21:19:35 +02:00
end
2023-09-24 04:45:37 +02:00
self.triggerEvent ( " ND:updateCharacter " , removeCharacterFunctions ( self ) )
2023-07-09 21:19:35 +02:00
end
2023-08-23 03:29:22 +02:00
function self . getLicense ( identifier )
local licenses = self.metadata . licenses or { }
for i = 1 , # licenses do
local data = licenses [ i ]
if data.identifier == identifier then
return data , i
end
end
end
function self . updateLicense ( identifier , newData )
local data , i = self.getLicense ( identifier )
if not data then return end
for k , v in pairs ( newData ) do
data [ k ] = v
end
self.save ( )
end
2023-07-09 21:19:35 +02:00
2023-07-10 02:45:32 +02:00
---@param coords vector3|vector4
---@return boolean
2023-07-09 21:19:35 +02:00
function self . setCoords ( coords )
if not self.source or not coords then return end
local ped = GetPlayerPed ( self.source )
if not DoesEntityExist ( ped ) then return end
SetEntityCoords ( ped , coords.x , coords.y , coords.z )
2023-07-10 02:45:32 +02:00
if coords.w then
SetEntityHeading ( ped , coords.w )
end
return true
end
---@param eventName string
---@param ... any
---@return boolean
function self . triggerEvent ( eventName , ... )
if not self.source then return end
TriggerClientEvent ( eventName , self.source , ... )
2023-07-09 21:19:35 +02:00
return true
2023-07-09 03:07:46 +02:00
end
2023-07-12 04:53:35 +02:00
function self . notify ( ... )
if not self.source then return end
2023-08-23 03:30:15 +02:00
if GetResourceState ( " ModernHUD " ) == " started " then
TriggerClientEvent ( " ModernHUD:notify " , self.source , ... )
elseif GetResourceState ( " ox_lib " ) == " started " then
TriggerClientEvent ( " ox_lib:notify " , self.source , ... )
end
2023-07-12 04:53:35 +02:00
return true
end
2023-09-24 11:19:22 +02:00
function self . revive ( )
self.triggerEvent ( " ND:revivePlayer " )
self.setMetadata ( {
2023-10-30 11:43:06 +01:00
dead = false ,
deathInfo = false ,
2023-09-24 11:19:22 +02:00
} )
end
2023-07-10 02:45:32 +02:00
---@param reason string
function self . drop ( reason )
if not self.source then return end
DropPlayer ( self.source , reason )
end
-- Set the character as the players active character/currently playing character
function self . active ( )
2023-08-23 00:30:38 +02:00
local char = NDCore.players [ self.source ]
2023-08-29 03:20:04 +02:00
if char and char.id == self.id then return end
2023-07-13 01:23:23 +02:00
if char then char.unload ( ) end
2023-08-28 03:04:16 +02:00
for identifierType , identifier in pairs ( self.identifiers ) do
if lib.table . contains ( Config.admins , ( " %s:%s " ) : format ( identifierType , identifier ) ) then
self.addGroup ( " admin " )
end
end
2023-10-21 21:44:17 +02:00
local roles = self.discord . roles
if roles then
for i = 1 , # Config.adminDiscordRoles do
local role = Config.adminDiscordRoles [ i ]
if lib.table . contains ( roles , role ) then
self.addGroup ( " admin " )
end
end
end
2023-08-24 01:00:54 +02:00
for name , _ in pairs ( self.groups ) do
2023-08-25 04:25:48 +02:00
lib.addPrincipal ( self.source , ( " group.%s " ) : format ( name ) )
2023-08-24 01:00:54 +02:00
end
2023-08-23 00:30:38 +02:00
NDCore.players [ self.source ] = self
2023-07-10 02:45:32 +02:00
TriggerEvent ( " ND:characterLoaded " , self )
2023-09-24 04:45:37 +02:00
self.triggerEvent ( " ND:characterLoaded " , removeCharacterFunctions ( self ) )
2023-07-10 02:45:32 +02:00
end
---@param name string
---@param rank number
2023-07-23 00:00:08 +02:00
---@param isJob boolean
2023-07-10 03:17:32 +02:00
---@return boolean
2023-07-22 23:59:39 +02:00
function self . addGroup ( name , rank , isJob )
2023-08-19 22:09:23 +02:00
local groupRank = tonumber ( rank ) or 1
2023-12-23 23:47:55 +01:00
local groupInfo = Config.groups ? [ name ]
2023-12-28 20:37:59 +01:00
local bossRank = groupInfo and groupInfo.minimumBossRank
2023-08-28 03:04:16 +02:00
-- if not groupInfo then return end
2023-07-22 23:59:39 +02:00
if isJob then
for _ , group in pairs ( self.groups ) do
group.isJob = nil
end
end
2023-12-28 20:37:59 +01:00
2023-07-10 02:45:32 +02:00
self.groups [ name ] = {
2023-12-29 02:12:17 +01:00
name = name ,
2023-08-28 03:04:16 +02:00
label = groupInfo and groupInfo.label or name ,
rankName = groupInfo and groupInfo.ranks [ groupRank ] or groupRank ,
2023-08-19 22:09:23 +02:00
rank = groupRank ,
2023-12-28 20:37:59 +01:00
isJob = isJob ,
2023-12-28 23:30:18 +01:00
isBoss = bossRank and groupRank >= bossRank
2023-07-10 02:45:32 +02:00
}
2023-12-28 20:37:59 +01:00
2023-12-29 02:12:17 +01:00
if not isJob then
self.triggerEvent ( " ND:updateCharacter " , removeCharacterFunctions ( self ) )
end
2023-08-25 04:25:48 +02:00
lib.addPrincipal ( self.source , ( " group.%s " ) : format ( name ) )
2023-08-28 03:04:16 +02:00
return self.groups [ name ]
2023-07-10 03:17:32 +02:00
end
---@param name string
---@return table
function self . getGroup ( name )
return self.groups [ name ]
2023-07-10 02:45:32 +02:00
end
---@param name string
function self . removeGroup ( name )
2023-08-28 03:04:16 +02:00
local group = self.groups [ name ]
2023-09-26 11:25:31 +02:00
if not group then return end
2023-12-28 20:38:16 +01:00
if self.job == name then
self.job = nil
self.jobInfo = nil
end
2023-07-10 02:45:32 +02:00
self.groups [ name ] = nil
2023-09-24 04:45:37 +02:00
self.triggerEvent ( " ND:updateCharacter " , removeCharacterFunctions ( self ) )
2023-08-25 04:25:48 +02:00
lib.removePrincipal ( self.source , ( " group.%s " ) : format ( name ) )
2023-08-28 03:04:16 +02:00
return group
2023-07-10 02:45:32 +02:00
end
2023-07-22 23:59:39 +02:00
---@param name string
---@param rank number
---@return boolean
function self . setJob ( name , rank )
2023-09-26 11:25:31 +02:00
self.removeGroup ( self.job )
2023-09-25 00:17:11 +02:00
local job = self.addGroup ( name , rank , true )
2023-12-29 02:12:17 +01:00
if job then
self.job = job.name
self.jobInfo = job
2023-09-25 00:17:11 +02:00
end
2023-12-29 02:12:17 +01:00
self.triggerEvent ( " ND:updateCharacter " , removeCharacterFunctions ( self ) )
2023-09-25 00:17:11 +02:00
return job
2023-07-22 23:59:39 +02:00
end
---@param job string
---@return boolean
2023-08-26 01:37:15 +02:00
function self . getJob ( )
2023-07-22 23:59:39 +02:00
for name , group in pairs ( self.groups ) do
2023-08-26 01:37:15 +02:00
if group.isJob then
return name , group
2023-07-22 23:59:39 +02:00
end
end
end
2023-08-26 01:37:15 +02:00
local jobName , jobInfo = self.getJob ( )
if jobInfo then
self.job = jobName
self.jobInfo = jobInfo
end
2023-07-09 21:19:35 +02:00
return self
2023-07-09 03:07:46 +02:00
end
2023-07-10 03:17:32 +02:00
---@param src number
---@param info table
---@return table
2023-07-09 03:07:46 +02:00
function NDCore . newCharacter ( src , info )
2023-07-10 03:33:01 +02:00
local identifier = GetPlayerIdentifierByType ( src , Config.characterIdentifier )
if not identifier then return end
2023-07-09 03:07:46 +02:00
2023-07-16 16:33:37 +02:00
local charInfo = {
2023-07-09 03:07:46 +02:00
source = src ,
2023-07-10 03:33:01 +02:00
identifier = identifier ,
2023-07-09 03:07:46 +02:00
name = GetPlayerName ( src ) or " " ,
firstname = info.firstname or " " ,
lastname = info.lastname or " " ,
dob = info.dob or " " ,
gender = info.gender or " " ,
cash = info.cash or 0 ,
bank = info.bank or 0 ,
2024-01-07 21:13:56 +01:00
phonenumber = info.phonenumber ,
2023-07-10 02:45:32 +02:00
groups = info.groups or { } ,
2023-07-09 03:07:46 +02:00
metadata = info.metadata or { } ,
2024-01-07 21:13:56 +01:00
inventory = info.inventory or { }
2023-07-16 16:33:37 +02:00
}
2023-07-09 03:07:46 +02:00
2024-01-07 21:13:56 +01:00
charInfo.id = MySQL.insert . await ( " INSERT INTO nd_characters (identifier, name, firstname, lastname, dob, gender, cash, bank, phonenumber, `groups`, metadata) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) " , {
2023-07-10 03:33:01 +02:00
identifier ,
2023-07-09 03:07:46 +02:00
charInfo.name ,
charInfo.firstname ,
charInfo.lastname ,
charInfo.dob ,
charInfo.gender ,
charInfo.cash ,
charInfo.bank ,
2024-01-07 21:13:56 +01:00
charInfo.phonenumber ,
2023-07-16 12:31:20 +02:00
json.encode ( charInfo.groups ) ,
2023-07-10 02:45:32 +02:00
json.encode ( charInfo.metadata )
2023-07-09 21:19:35 +02:00
} )
2023-07-09 03:07:46 +02:00
2023-07-16 16:33:37 +02:00
return createCharacterTable ( charInfo )
2023-07-09 03:07:46 +02:00
end
2023-07-10 03:17:32 +02:00
---@param id number
---@return table
2023-07-23 23:24:36 +02:00
function NDCore . fetchCharacter ( id , src )
local result
if src then
result = MySQL.query . await ( " SELECT * FROM nd_characters WHERE charid = ? and identifier = ? " , { id , GetPlayerIdentifierByType ( src , Config.characterIdentifier ) } )
else
result = MySQL.query . await ( " SELECT * FROM nd_characters WHERE charid = ? " , { id } )
end
2023-07-09 03:07:46 +02:00
if not result then return end
local info = result [ 1 ]
2023-07-09 21:19:35 +02:00
return createCharacterTable ( {
2023-07-23 23:24:36 +02:00
source = src ,
2023-07-16 16:33:37 +02:00
id = info.charid ,
2023-07-10 03:33:01 +02:00
identifier = info.identifier ,
2023-07-09 03:07:46 +02:00
name = info.name ,
firstname = info.firstname ,
lastname = info.lastname ,
dob = info.dob ,
gender = info.gender ,
cash = info.cash ,
bank = info.bank ,
2024-01-07 21:13:56 +01:00
phonenumber = info.phonenumber ,
2023-07-10 02:45:32 +02:00
groups = json.decode ( info.groups ) ,
2023-07-09 03:07:46 +02:00
metadata = json.decode ( info.metadata ) ,
2023-07-10 02:45:32 +02:00
inventory = json.decode ( info.inventory )
2023-07-09 03:07:46 +02:00
} )
end
2023-07-10 03:17:32 +02:00
---@param src number
---@return table
2023-07-11 00:35:11 +02:00
function NDCore . fetchAllCharacters ( src )
2023-07-09 03:07:46 +02:00
local characters = { }
2023-07-10 03:33:01 +02:00
local result = MySQL.query . await ( " SELECT * FROM nd_characters WHERE identifier = ? " , { GetPlayerIdentifierByType ( src , Config.characterIdentifier ) } )
2023-07-09 03:07:46 +02:00
2023-07-11 00:35:11 +02:00
for i = 1 , # result do
2023-07-09 03:07:46 +02:00
local info = result [ i ]
2023-07-09 21:19:35 +02:00
characters [ info.charid ] = createCharacterTable ( {
2023-07-16 12:31:20 +02:00
source = src ,
2023-07-09 03:07:46 +02:00
id = info.charid ,
2023-07-10 03:33:01 +02:00
identifier = info.identifier ,
2023-07-09 03:07:46 +02:00
name = info.name ,
firstname = info.firstname ,
lastname = info.lastname ,
dob = info.dob ,
gender = info.gender ,
cash = info.cash ,
bank = info.bank ,
2024-01-07 21:13:56 +01:00
phonenumber = info.phonenumber ,
2023-07-10 02:45:32 +02:00
groups = json.decode ( info.groups ) ,
2023-07-09 03:07:46 +02:00
metadata = json.decode ( info.metadata ) ,
2024-01-07 21:13:56 +01:00
inventory = json.decode ( info.inventory )
2023-07-09 03:07:46 +02:00
} )
end
return characters
end
2023-07-10 03:17:32 +02:00
---@param src number
---@param id number
---@return table
2023-07-09 03:07:46 +02:00
function NDCore . setActiveCharacter ( src , id )
2023-08-29 03:20:04 +02:00
local char = NDCore.players [ src ]
if not src or char and char.id == id then return end
2023-07-23 23:24:36 +02:00
local character = NDCore.fetchCharacter ( id , src )
2023-08-29 03:20:04 +02:00
if not character then return end
2023-07-09 03:07:46 +02:00
character.name = GetPlayerName ( src )
2023-07-16 16:33:37 +02:00
character.active ( )
2023-08-23 00:30:38 +02:00
return character
2023-07-09 03:07:46 +02:00
end