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 ,
dob = info.dob ,
gender = info.gender ,
cash = info.cash ,
bank = info.bank ,
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
function self . setData ( key , value )
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
TriggerEvent ( "ND:moneyChange" , self.source , k , v , "set" )
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
TriggerEvent ( "ND:moneyChange" , self.source , key , value , "set" )
end
2023-07-23 00:00:08 +02:00
end
2023-07-16 12:31:20 +02:00
self.triggerEvent ( "ND:updateCharacter" , self )
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-07-13 01:23:23 +02:00
self.triggerEvent ( "ND:updateCharacter" , 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 ,
x = coords.z ,
w = heading
})
end
TriggerEvent ( "ND:characterUnloaded" , self.source , self )
self.save ()
2023-08-23 00:30:38 +02:00
NDCore.players [ self.source ] = nil
2023-07-09 21:19:35 +02:00
end
2023-07-10 02:45:32 +02:00
-- Save character information to database
function self . save ()
MySQL.update . await ( "UPDATE nd_characters SET name = ?, firstname = ?, lastname = ?, dob = ?, gender = ?, cash = ?, bank = ?, groups = ?, metadata = ?, inventory = ? WHERE charid = ?" , {
2023-07-09 21:19:35 +02:00
self.name ,
self.firstname ,
self.lastname ,
self.dob ,
self.gender ,
self.cash ,
self.bank ,
2023-07-10 02:45:32 +02:00
json.encode ( self.groups ),
2023-07-09 21:19:35 +02:00
json.encode ( self.metadata ),
json.encode ( self.inventory ),
self.id ,
})
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-07-13 01:23:23 +02:00
self.triggerEvent ( "ND:updateCharacter" , 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-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-07-10 02:45:32 +02:00
if char and char.id == self.id then return true end
2023-07-13 01:23:23 +02:00
if char then char.unload () 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-07-16 12:31:20 +02:00
self.triggerEvent ( "ND:characterLoaded" , self )
return true
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-07-10 03:17:32 +02:00
local groupInfo = Config.groups [ name ]
2023-07-11 00:35:11 +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-07-10 02:45:32 +02:00
self.groups [ name ] = {
2023-07-10 03:17:32 +02:00
label = groupInfo.label ,
2023-08-19 22:09:23 +02:00
rankName = groupInfo.ranks [ groupRank ] or groupRank ,
rank = groupRank ,
2023-07-22 23:59:39 +02:00
isJob = isJob
2023-07-10 02:45:32 +02:00
}
2023-07-13 01:23:23 +02:00
self.triggerEvent ( "ND:updateCharacter" , self )
2023-08-25 04:25:48 +02:00
lib.addPrincipal ( self.source , ( "group.%s" ): format ( name ))
2023-07-10 03:17:32 +02:00
return true
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 )
self.groups [ name ] = nil
2023-07-13 01:23:23 +02:00
self.triggerEvent ( "ND:updateCharacter" , self )
2023-08-25 04:25:48 +02:00
lib.removePrincipal ( self.source , ( "group.%s" ): format ( name ))
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 )
return self.addGroup ( name , rank , true )
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 ,
2023-07-10 02:45:32 +02:00
groups = info.groups or {},
2023-07-09 03:07:46 +02:00
metadata = info.metadata or {},
inventory = info.inventory or {},
2023-07-16 16:33:37 +02:00
}
2023-07-09 03:07:46 +02:00
2023-07-16 12:31:20 +02:00
charInfo.id = MySQL.insert . await ( "INSERT INTO nd_characters (identifier, name, firstname, lastname, dob, gender, cash, bank, 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 ,
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 ,
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 ,
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 ),
inventory = json.decode ( info.inventory ),
})
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-07-16 16:33:37 +02:00
if not src then return end
2023-07-23 23:24:36 +02:00
local character = NDCore.fetchCharacter ( id , src )
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