mirror of
https://github.com/ND-Framework/ND_Core.git
synced 2026-08-17 13:56:02 +01:00
Player can receive groups based on the discord roles they have, ace perms are also added when group is received.
533 lines
17 KiB
Lua
533 lines
17 KiB
Lua
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
|
|
|
|
local function createCharacterTable(info)
|
|
local playerInfo = PlayersInfo[info.source] or {}
|
|
|
|
local self = {
|
|
id = info.id,
|
|
source = info.source,
|
|
identifier = info.identifier,
|
|
identifiers = playerInfo.identifiers or {},
|
|
discord = playerInfo.discord or {},
|
|
name = info.name,
|
|
firstname = info.firstname,
|
|
lastname = info.lastname,
|
|
fullname = ("%s %s"):format(info.firstname, info.lastname),
|
|
dob = info.dob,
|
|
gender = info.gender,
|
|
cash = info.cash,
|
|
bank = info.bank,
|
|
phonenumber = info.phonenumber,
|
|
groups = info.groups,
|
|
metadata = info.metadata,
|
|
inventory = info.inventory
|
|
}
|
|
|
|
---@param account string
|
|
---@param amount number
|
|
---@param reason string|nil
|
|
---@return boolean
|
|
function self.deductMoney(account, amount, reason)
|
|
local amount = tonumber(amount)
|
|
if not amount or amount <= 0 or account ~= "bank" and account ~= "cash" then return end
|
|
self[account] -= amount
|
|
if NDCore.players[self.source] then
|
|
self.triggerEvent("ND:updateMoney", self.cash, self.bank)
|
|
TriggerEvent("ND:moneyChange", self.source, account, amount, "remove", reason)
|
|
end
|
|
return true
|
|
end
|
|
|
|
---@param account string
|
|
---@param amount number
|
|
---@param reason string|nil
|
|
---@return boolean
|
|
function self.addMoney(account, amount, reason)
|
|
local amount = tonumber(amount)
|
|
if not amount or amount <= 0 or account ~= "bank" and account ~= "cash" then return end
|
|
self[account] += amount
|
|
if NDCore.players[self.source] then
|
|
self.triggerEvent("ND:updateMoney", self.cash, self.bank)
|
|
TriggerEvent("ND:moneyChange", self.source, account, amount, "add", reason)
|
|
end
|
|
return true
|
|
end
|
|
|
|
---@param amount number
|
|
---@return boolean
|
|
function self.depositMoney(amount)
|
|
local amount = tonumber(amount)
|
|
if not amount or self.cash < amount or amount <= 0 then return end
|
|
return self.deductMoney("cash", amount, "Deposit") and self.addMoney("bank", amount, "Deposit")
|
|
end
|
|
|
|
---@param amount number
|
|
---@return boolean
|
|
function self.withdrawMoney(amount)
|
|
local amount = tonumber(amount)
|
|
if not amount or self.bank < amount or amount <= 0 then return end
|
|
return self.deductMoney("bank", amount, "Withdraw") and self.addMoney("cash", amount, "Withdraw")
|
|
end
|
|
|
|
---@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 = {}
|
|
for i=1, #metadata do
|
|
local data = metadata[i]
|
|
returnData[data] = self.metadata[data]
|
|
end
|
|
return returnData
|
|
end
|
|
|
|
---@param key string|table
|
|
---@param value any
|
|
function self.setData(key, value, reason)
|
|
if type(key) == "table" then
|
|
for k, v in pairs(key) do
|
|
self[k] = v
|
|
if k == "cash" or k == "bank" then
|
|
TriggerEvent("ND:moneyChange", self.source, k, v, "set", reason)
|
|
end
|
|
end
|
|
else
|
|
self[key] = value
|
|
if key == "cash" or key == "bank" then
|
|
TriggerEvent("ND:moneyChange", self.source, key, value, "set", reason)
|
|
end
|
|
end
|
|
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self))
|
|
TriggerEvent("ND:updateCharacter", self)
|
|
end
|
|
|
|
---@param key string|table
|
|
---@param value any
|
|
---@return table
|
|
function self.setMetadata(key, value)
|
|
if type(key) == "table" then
|
|
for k, v in pairs(key) do
|
|
self.metadata[k] = v
|
|
end
|
|
else
|
|
self.metadata[key] = value
|
|
end
|
|
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self))
|
|
TriggerEvent("ND:updateCharacter", self)
|
|
return self.metadata
|
|
end
|
|
|
|
-- Completely delete character
|
|
function self.delete()
|
|
local result = MySQL.query.await("DELETE FROM nd_characters WHERE charid = ?", {self.id})
|
|
if result and NDCore.players[self.source] then
|
|
NDCore.players[self.source] = nil
|
|
end
|
|
return result
|
|
end
|
|
|
|
-- Unload and save character
|
|
function self.unload()
|
|
if not NDCore.players[self.source] then return end
|
|
for name, _ in pairs(self.groups) do
|
|
lib.removePrincipal(self.source, ("group.%s"):format(name))
|
|
end
|
|
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,
|
|
z = coords.z,
|
|
w = heading
|
|
})
|
|
end
|
|
self.triggerEvent("ND:characterUnloaded")
|
|
TriggerEvent("ND:characterUnloaded", self.source, self)
|
|
local saved = self.save()
|
|
NDCore.players[self.source] = nil
|
|
return saved
|
|
end
|
|
|
|
-- Save character information to database
|
|
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
|
|
|
|
local affectedRows = MySQL.update.await("UPDATE nd_characters SET name = ?, firstname = ?, lastname = ?, dob = ?, gender = ?, cash = ?, bank = ?, phonenumber = ?, `groups` = ?, metadata = ? WHERE charid = ?", {
|
|
self.name,
|
|
self.firstname,
|
|
self.lastname,
|
|
self.dob,
|
|
self.gender,
|
|
self.cash,
|
|
self.bank,
|
|
self.phonenumber,
|
|
json.encode(self.groups),
|
|
json.encode(self.metadata),
|
|
self.id
|
|
})
|
|
return affectedRows > 0
|
|
end
|
|
|
|
---Create a license/permit for the character
|
|
---@param licenseType string
|
|
---@param expire number
|
|
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
|
|
else
|
|
self.metadata.licenses = {license}
|
|
end
|
|
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self))
|
|
TriggerEvent("ND:updateCharacter", self)
|
|
end
|
|
|
|
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
|
|
|
|
---@param coords vector3|vector4
|
|
---@return boolean
|
|
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)
|
|
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, ...)
|
|
return true
|
|
end
|
|
|
|
function self.notify(...)
|
|
if not self.source then return end
|
|
TriggerClientEvent("ox_lib:notify", self.source, ...)
|
|
return true
|
|
end
|
|
|
|
function self.revive()
|
|
self.triggerEvent("ND:revivePlayer")
|
|
self.setMetadata({
|
|
dead = false,
|
|
deathInfo = false,
|
|
})
|
|
end
|
|
|
|
---@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()
|
|
local char = NDCore.players[self.source]
|
|
if char and char.id == self.id then return end
|
|
if char then char.unload() end
|
|
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
|
|
|
|
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
|
|
|
|
for group, role in pairs(Config.groupRoles)
|
|
if lib.table.contains(roles, role) then
|
|
self.addGroup(group)
|
|
end
|
|
end
|
|
end
|
|
|
|
for name, _ in pairs(self.groups) do
|
|
lib.addPrincipal(self.source, ("group.%s"):format(name))
|
|
end
|
|
NDCore.players[self.source] = self
|
|
TriggerEvent("ND:characterLoaded", self)
|
|
self.triggerEvent("ND:characterLoaded", removeCharacterFunctions(self))
|
|
end
|
|
|
|
---@param name string
|
|
---@param rank number
|
|
---@param isJob boolean
|
|
---@return boolean
|
|
function self.addGroup(name, rank, isJob)
|
|
local groupRank = tonumber(rank) or 1
|
|
local groupInfo = Config.groups?[name]
|
|
local bossRank = groupInfo and groupInfo.minimumBossRank
|
|
|
|
-- if not groupInfo then return end
|
|
if isJob then
|
|
for _, group in pairs(self.groups) do
|
|
group.isJob = nil
|
|
end
|
|
end
|
|
|
|
self.groups[name] = {
|
|
name = name,
|
|
label = groupInfo and groupInfo.label or name,
|
|
rankName = groupInfo and groupInfo.ranks[groupRank] or groupRank,
|
|
rank = groupRank,
|
|
isJob = isJob,
|
|
isBoss = bossRank and groupRank >= bossRank
|
|
}
|
|
|
|
if not isJob then
|
|
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self))
|
|
TriggerEvent("ND:updateCharacter", self)
|
|
end
|
|
|
|
lib.addPrincipal(self.source, ("group.%s"):format(name))
|
|
|
|
return self.groups[name]
|
|
end
|
|
|
|
---@param name string
|
|
---@return table
|
|
function self.getGroup(name)
|
|
return self.groups[name]
|
|
end
|
|
|
|
---@param name string
|
|
function self.removeGroup(name)
|
|
local group = self.groups[name]
|
|
if not group then return end
|
|
|
|
if self.job == name then
|
|
self.job = nil
|
|
self.jobInfo = nil
|
|
end
|
|
|
|
self.groups[name] = nil
|
|
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self))
|
|
TriggerEvent("ND:updateCharacter", self)
|
|
lib.removePrincipal(self.source, ("group.%s"):format(name))
|
|
return group
|
|
end
|
|
|
|
---@param name string
|
|
---@param rank number
|
|
---@return boolean
|
|
function self.setJob(name, rank)
|
|
self.removeGroup(self.job)
|
|
local job = self.addGroup(name, rank, true)
|
|
if job then
|
|
self.job = job.name
|
|
self.jobInfo = job
|
|
end
|
|
self.triggerEvent("ND:updateCharacter", removeCharacterFunctions(self))
|
|
TriggerEvent("ND:updateCharacter", self)
|
|
return job
|
|
end
|
|
|
|
---@param job string
|
|
---@return boolean
|
|
function self.getJob()
|
|
for name, group in pairs(self.groups) do
|
|
if group.isJob then
|
|
return name, group
|
|
end
|
|
end
|
|
end
|
|
|
|
local jobName, jobInfo = self.getJob()
|
|
if jobInfo then
|
|
self.job = jobName
|
|
self.jobInfo = jobInfo
|
|
end
|
|
|
|
return self
|
|
end
|
|
|
|
---@param src number
|
|
---@param info table
|
|
---@return table
|
|
function NDCore.newCharacter(src, info)
|
|
local identifier = GetPlayerIdentifierByType(src, Config.characterIdentifier)
|
|
if not identifier then return end
|
|
|
|
local charInfo = {
|
|
source = src,
|
|
identifier = identifier,
|
|
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,
|
|
phonenumber = info.phonenumber,
|
|
groups = info.groups or {},
|
|
metadata = info.metadata or {},
|
|
inventory = info.inventory or {}
|
|
}
|
|
|
|
charInfo.id = MySQL.insert.await("INSERT INTO nd_characters (identifier, name, firstname, lastname, dob, gender, cash, bank, phonenumber, `groups`, metadata, inventory) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", {
|
|
identifier,
|
|
charInfo.name,
|
|
charInfo.firstname,
|
|
charInfo.lastname,
|
|
charInfo.dob,
|
|
charInfo.gender,
|
|
charInfo.cash,
|
|
charInfo.bank,
|
|
charInfo.phonenumber,
|
|
json.encode(charInfo.groups),
|
|
json.encode(charInfo.metadata),
|
|
json.encode(charInfo.inventory)
|
|
})
|
|
|
|
return createCharacterTable(charInfo)
|
|
end
|
|
|
|
---@param id number
|
|
---@return table
|
|
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
|
|
|
|
if not result then return end
|
|
local info = result[1]
|
|
return createCharacterTable({
|
|
source = src,
|
|
id = info.charid,
|
|
identifier = info.identifier,
|
|
name = info.name,
|
|
firstname = info.firstname,
|
|
lastname = info.lastname,
|
|
dob = info.dob,
|
|
gender = info.gender,
|
|
cash = info.cash,
|
|
bank = info.bank,
|
|
phonenumber = info.phonenumber,
|
|
groups = json.decode(info.groups),
|
|
metadata = json.decode(info.metadata),
|
|
inventory = json.decode(info.inventory)
|
|
})
|
|
end
|
|
|
|
---@param src number
|
|
---@return table
|
|
function NDCore.fetchAllCharacters(src)
|
|
local characters = {}
|
|
local result = MySQL.query.await("SELECT * FROM nd_characters WHERE identifier = ?", {GetPlayerIdentifierByType(src, Config.characterIdentifier)})
|
|
|
|
for i=1, #result do
|
|
local info = result[i]
|
|
characters[info.charid] = createCharacterTable({
|
|
source = src,
|
|
id = info.charid,
|
|
identifier = info.identifier,
|
|
name = info.name,
|
|
firstname = info.firstname,
|
|
lastname = info.lastname,
|
|
dob = info.dob,
|
|
gender = info.gender,
|
|
cash = info.cash,
|
|
bank = info.bank,
|
|
phonenumber = info.phonenumber,
|
|
groups = json.decode(info.groups),
|
|
metadata = json.decode(info.metadata),
|
|
inventory = json.decode(info.inventory)
|
|
})
|
|
end
|
|
return characters
|
|
end
|
|
|
|
---@param src number
|
|
---@param id number
|
|
---@return table
|
|
function NDCore.setActiveCharacter(src, id)
|
|
local char = NDCore.players[src]
|
|
if not src or char and char.id == id then return end
|
|
|
|
local character = NDCore.fetchCharacter(id, src)
|
|
if not character then return end
|
|
|
|
character.name = GetPlayerName(src)
|
|
character.active()
|
|
return character
|
|
end
|