Main/resources/[jobs]/[medic]/visn_are/script/helpers/s_functions.lua
2025-06-07 08:51:21 +02:00

518 lines
21 KiB
Lua

--[[
-- Author: Tim Plate
-- Project: Advanced Roleplay Environment
-- Copyright (c) 2022 Tim Plate Solutions
--]]
--- Server extension module for Advanced Roleplay Environment.
---Returns true if the player has the item with the specified amount
---@param source any
---@param item any
---@param amount any
---@return boolean
function HasItem(source, item, amount)
source = tonumber(source)
if not ServerConfig.m_itemsNeeded or ShouldIgnoreItems(source) then return true end
if not amount then amount = 1 end
LogDebug(TableContains(ServerConfig.m_debugModeModules, "items"), "HasItem on player ", GetPlayerName(source),
" and item", item)
if FRAMEWORK == ENUM_DEFINED_FRAMEWORKS.ES_EXTENDED then
local frameworkPlayer = FRAMEWORK_DATA.GetPlayerFromId(source)
if not frameworkPlayer then return false end
if not ServerConfig.m_customInventory.enabled then -- Default inventory handling
return frameworkPlayer.getInventoryItem(item) and frameworkPlayer.getInventoryItem(item).count >= amount
end
if ServerConfig.m_customInventory.inventory_type == "ox_inventory" then
return exports.ox_inventory:Search(source, 'count', item) >= amount
elseif ServerConfig.m_customInventory.inventory_type == "qs-inventory" then
return exports['qs-inventory']:GetItemTotalAmount(source, item) >= amount
elseif ServerConfig.m_customInventory.inventory_type == "mf-inventory" then
return frameworkPlayer.getInventoryItem(item) and frameworkPlayer.getInventoryItem(item).count >= amount
elseif ServerConfig.m_customInventory.inventory_type == "core_inventory" then
local inventory = 'content-' .. frameworkPlayer.identifier:gsub(":", "")
return exports["core_inventory"]:getItem(inventory, item) and exports["core_inventory"]:getItem(inventory, item).count >= amount
end
elseif FRAMEWORK == ENUM_DEFINED_FRAMEWORKS.QB_CORE then
local frameworkPlayer = FRAMEWORK_DATA.Functions.GetPlayer(source)
if not frameworkPlayer then return false end
return frameworkPlayer.Functions.GetItemByName(item).amount >= amount
end
-- Implement your own logic (standalone)
return true
end
---Adds an item to the player
---@param source any
---@param item any
---@param amount any
---@return boolean
function AddItem(source, item, amount)
source = tonumber(source)
if not amount then amount = 1 end
LogDebug(TableContains(ServerConfig.m_debugModeModules, "items"), "AddItem on player ", GetPlayerName(source),
" and item", item)
if FRAMEWORK == ENUM_DEFINED_FRAMEWORKS.ES_EXTENDED then
local frameworkPlayer = FRAMEWORK_DATA.GetPlayerFromId(source)
if not frameworkPlayer then return false end
if not ServerConfig.m_customInventory.enabled then -- Default inventory handling
if frameworkPlayer.canCarryItem(item, amount) then
frameworkPlayer.addInventoryItem(item, amount)
return true
end
return false
end
if ServerConfig.m_customInventory.inventory_type == "ox_inventory" then
if exports.ox_inventory:CanCarryItem(source, item, amount) then
exports.ox_inventory:AddItem(source, item, amount)
return true
end
elseif ServerConfig.m_customInventory.inventory_type == "qs-inventory" then
if frameworkPlayer.canCarryItem(item, amount) then
frameworkPlayer.addInventoryItem(item, amount)
return true
end
elseif ServerConfig.m_customInventory.inventory_type == "mf-inventory" then
if frameworkPlayer.canCarryItem(item, amount) then
frameworkPlayer.addInventoryItem(item, amount)
return true
end
elseif ServerConfig.m_customInventory.inventory_type == "core_inventory" then
return exports["core_inventory"]:addItem('content-' .. frameworkPlayer.identifier:gsub(":", ""), item, amount, nil)
end
return false
elseif FRAMEWORK == ENUM_DEFINED_FRAMEWORKS.QB_CORE then
local frameworkPlayer = FRAMEWORK_DATA.Functions.GetPlayer(source)
if not frameworkPlayer then return false end
return frameworkPlayer.Functions.AddItem(item, amount)
end
return true
-- Implement your own logic (standalone)
end
---Removes an item from the player
---@param source any
---@param item any
---@param amount any
function RemoveItem(source, item, amount)
source = tonumber(source)
if not amount then amount = 1 end
LogDebug(TableContains(ServerConfig.m_debugModeModules, "items"), "RemoveItem on player ", GetPlayerName(source),
" and item", item)
if FRAMEWORK == ENUM_DEFINED_FRAMEWORKS.ES_EXTENDED then
local frameworkPlayer = FRAMEWORK_DATA.GetPlayerFromId(source)
if not frameworkPlayer then return end
if not ServerConfig.m_customInventory.enabled then -- Default inventory handling
if HasItem(source, item, amount) then
frameworkPlayer.removeInventoryItem(item, amount)
end
return
end
if ServerConfig.m_customInventory.inventory_type == "ox_inventory" then
exports.ox_inventory:RemoveItem(source, item, amount)
elseif ServerConfig.m_customInventory.inventory_type == "qs-inventory" then
frameworkPlayer.removeInventoryItem(item, amount)
elseif ServerConfig.m_customInventory.inventory_type == "mf-inventory" then
frameworkPlayer.removeInventoryItem(item, amount)
elseif ServerConfig.m_customInventory.inventory_type == "core_inventory" then
exports["core_inventory"]:removeItem('content-' .. frameworkPlayer.identifier:gsub(":", ""), item, amount)
end
elseif FRAMEWORK == ENUM_DEFINED_FRAMEWORKS.QB_CORE then
local frameworkPlayer = FRAMEWORK_DATA.Functions.GetPlayer(source)
if not frameworkPlayer then return end
frameworkPlayer.Functions.RemoveItem(item, amount)
end
-- Implement your own logic (standalone)
end
---Gets all items from the player
---@param source any
---@return table
function GetItems(source)
source = tonumber(source)
if not ServerConfig.m_itemsNeeded or ShouldIgnoreItems(source) then return GetAvailableItems() end
if FRAMEWORK == ENUM_DEFINED_FRAMEWORKS.ES_EXTENDED then
local frameworkPlayer = FRAMEWORK_DATA.GetPlayerFromId(source)
if not frameworkPlayer then return {} end
local items = frameworkPlayer.getInventory(true)
if ServerConfig.m_customInventory.enabled and ServerConfig.m_customInventory.inventory_type == "core_inventory" then
items = exports["core_inventory"]:getInventory('content-' .. frameworkPlayer.identifier:gsub(":", ""))
end
for _, v in pairs(items) do
if type(v) == "table" and v.count > 0 then
items[v.name] = v.count
end
end
return items
elseif FRAMEWORK == ENUM_DEFINED_FRAMEWORKS.QB_CORE then
local frameworkPlayer = FRAMEWORK_DATA.Functions.GetPlayer(source)
if not frameworkPlayer then return {} end
local items = {}
for _, v in pairs(frameworkPlayer.PlayerData.items) do
items[v.name] = v.amount
end
return items
end
-- Implement your own logic (standalone)
return {}
end
---Returns true if the player is allowed to perform a action
---@param source any
---@param category any
---@param action any
---@return boolean
function IsAllowedToPerformAction(source, category, action)
source = tonumber(source)
if not AVAILABLE_ACTIONS[category] then return false end
local actionData = nil
for _, v in pairs(AVAILABLE_ACTIONS[category]) do
if v.name == action then
actionData = v
break
end
end
if not actionData then return false end
if not actionData.requiredJobs then return true end
return TableContains(actionData.requiredJobs, GetPlayerJob(source))
end
---Returns true if the player is allowed to open the menu
---@param source any
---@return boolean
function IsAllowedToOpenMenu(source)
source = tonumber(source)
if not ServerConfig.m_limitMenuToJobs.enabled then return true end
local playerJob = GetPlayerJob(source)
if not playerJob then return false end
return TableContains(ServerConfig.m_limitMenuToJobs.jobs, playerJob)
end
---Saves the player
---@param source any
function SavePlayer(source)
source = tonumber(source)
if not ServerConfig.m_stateSaving.enabled then return end
if not ValidateSource(source) then return end
local healthBuffer = Player(source).state.healthBuffer
if not healthBuffer then return end
-- Remove logs and clientside properties
healthBuffer.logs = nil
healthBuffer.lastHeartRateChange = nil
healthBuffer.lastDamageTimestamp = nil
healthBuffer.lastReceivedPain = nil
healthBuffer.triageSelection = nil
healthBuffer.lastDamage = nil
healthBuffer._lastHealth = nil
healthBuffer.lastHandledDamageTimestamp = nil
healthBuffer.unconscious_timestamp = nil
healthBuffer.unconsciousTimeUntilDeath = nil
local playerIdentifier = GetPlayerCustomIdentifier(source)
if not playerIdentifier then return end
if ServerConfig.m_stateSaving.method == 'oxmysql' then
local databaseConfig = ServerConfig.m_stateSaving.database
exports["oxmysql"]:update("UPDATE " .. databaseConfig.table .. " SET " .. databaseConfig.column .. " = ? WHERE " .. databaseConfig.identifierColumn .. " = ?", { json.encode(healthBuffer), playerIdentifier })
elseif ServerConfig.m_stateSaving.method == 'mysql-async' then
local databaseConfig = ServerConfig.m_stateSaving.database
exports["mysql-async"]:mysql_execute("UPDATE " .. databaseConfig.table .. " SET " .. databaseConfig.column .. " = @value WHERE " .. databaseConfig.identifierColumn .. " = @identifier", {
['value'] = json.encode(healthBuffer),
['identifier'] = playerIdentifier
})
elseif ServerConfig.m_stateSaving.method == 'file' then
local filePath = ServerConfig.m_stateSaving.file.path
local fileEnding
local encodedData
if ServerConfig.m_stateSaving.file.type == 'message_pack' then
encodedData = msgpack.pack(healthBuffer)
fileEnding = ".bin"
elseif ServerConfig.m_stateSaving.file.type == 'json' then
encodedData = json.encode(healthBuffer)
fileEnding = ".json"
else
LogError("Invalid file type for state saving")
return
end
if not fileEnding or not encodedData then return end
local fileName = (filePath .. playerIdentifier .. fileEnding):gsub("%:", "_")
local file, err = io.open(GetResourcePath(GetCurrentResourceName()) .. fileName,'wb')
if err then
LogError("Player state saving (" .. playerIdentifier .. ") failed: " .. err)
return
end
if file then
file:write(encodedData)
file:close()
end
else
LogWarning("Unknown state saving method: " .. ServerConfig.m_stateSaving.method)
return
end
end
---Loads the player
---@param source any
function LoadPlayer(source)
source = tonumber(source)
if not ServerConfig.m_stateSaving.enabled then return end
if not ValidateSource(source) then return end
local playerIdentifier = GetPlayerCustomIdentifier(source) or "invalid_identifier"
if not playerIdentifier then return end
if ServerConfig.m_stateSaving.method == 'oxmysql' then
local databaseConfig = ServerConfig.m_stateSaving.database
exports["oxmysql"]:query("SELECT " .. databaseConfig.column .. " FROM " .. databaseConfig.table .. " WHERE " .. databaseConfig.identifierColumn .. " = ?", { playerIdentifier }, function(result)
if not result or not result[1] or not result[1][databaseConfig.column] then return end
local healthBuffer = json.decode(result[1][databaseConfig.column])
if not healthBuffer then return end
LogDebug(true, "Loaded health buffer for " .. playerIdentifier .. " (sql) | unconscious-state: ", healthBuffer.unconscious)
healthBuffer.unconscious = healthBuffer.unconscious == true
healthBuffer.medications = {}
Player(source).state:set("healthBuffer", healthBuffer, true)
TriggerClientEvent(ENUM_EVENT_TYPES.EVENT_SET_HEALTH_BUFFER, source, AuthToken, healthBuffer)
end)
elseif ServerConfig.m_stateSaving.method == 'mysql-async' then
local databaseConfig = ServerConfig.m_stateSaving.database
exports["mysql-async"]:mysql_fetch_all("SELECT " .. databaseConfig.column .. " FROM " .. databaseConfig.table .. " WHERE " .. databaseConfig.identifierColumn .. "=@identifier", { ['identifier'] = playerIdentifier }, function(result)
if not result or not result[1] or not result[1][databaseConfig.column] then return end
local healthBuffer = json.decode(result[1][databaseConfig.column])
if not healthBuffer then return end
LogDebug(true, "Loaded health buffer for " .. playerIdentifier .. " (sql) | unconscious-state: ", healthBuffer.unconscious)
healthBuffer.unconscious = healthBuffer.unconscious == true
healthBuffer.medications = {}
Player(source).state:set("healthBuffer", healthBuffer, true)
TriggerClientEvent(ENUM_EVENT_TYPES.EVENT_SET_HEALTH_BUFFER, source, AuthToken, healthBuffer)
end)
elseif ServerConfig.m_stateSaving.method == 'file' then
local filePath = ServerConfig.m_stateSaving.file.path
local healthBuffer
local fileName = (filePath .. playerIdentifier):gsub("%:", "_")
if ServerConfig.m_stateSaving.file.type == 'message_pack' then
local data = LoadResourceFile(GetCurrentResourceName(), fileName .. ".bin")
if not data then return end
healthBuffer = msgpack.unpack(data)
elseif ServerConfig.m_stateSaving.file.type == 'json' then
local data = LoadResourceFile(GetCurrentResourceName(), fileName .. ".json")
if not data then return end
healthBuffer = json.decode(data)
else
LogError("Invalid file type for state saving")
return
end
if not healthBuffer then return end
LogDebug(true, "Loaded health buffer for " .. playerIdentifier .. " (" .. fileName .. ") | unconscious-state: ", healthBuffer.unconscious or false)
healthBuffer.unconscious = (healthBuffer.unconscious or false) == true
healthBuffer.medications = {}
Player(source).state:set("healthBuffer", healthBuffer, true)
TriggerClientEvent(ENUM_EVENT_TYPES.EVENT_SET_HEALTH_BUFFER, source, AuthToken, healthBuffer)
else
LogWarning("Unknown state saving method: " .. ServerConfig.m_stateSaving.method)
return
end
end
---Gets the menu title name from the player
---@param source any
---@return unknown
function GetPlayerMenuTitleName(source)
source = tonumber(source)
if FRAMEWORK == ENUM_DEFINED_FRAMEWORKS.ES_EXTENDED then
return FRAMEWORK_DATA.GetPlayerFromId(source).getName()
elseif FRAMEWORK == ENUM_DEFINED_FRAMEWORKS.QB_CORE then
return FRAMEWORK_DATA.Functions.GetPlayer(source).PlayerData.charinfo.firstname ..
' ' .. FRAMEWORK_DATA.Functions.GetPlayer(source).PlayerData.charinfo.lastname
end
-- Implement your own logic (standalone)
return GetPlayerName(source)
end
---Gets the player job
---@param source any
---@return unknown
function GetPlayerJob(source)
source = tonumber(source)
if FRAMEWORK == ENUM_DEFINED_FRAMEWORKS.ES_EXTENDED then
local frameworkPlayer = FRAMEWORK_DATA.GetPlayerFromId(source)
if not frameworkPlayer then LogWarning("Tried to get player job but player doesnt have a framework player?",
source) return "" end
return frameworkPlayer.getJob().name
elseif FRAMEWORK == ENUM_DEFINED_FRAMEWORKS.QB_CORE then
local frameworkPlayer = FRAMEWORK_DATA.Functions.GetPlayer(source)
if not frameworkPlayer then LogWarning("Tried to get player job but player doesnt have a framework player?",
source) return "" end
return frameworkPlayer.PlayerData.job.name
end
-- Implement your own logic (standalone)
return ""
end
---Gets the player identifier (framework related)
---@param source any
---@return any
function GetPlayerCustomIdentifier(source)
source = tonumber(source)
if FRAMEWORK == ENUM_DEFINED_FRAMEWORKS.ES_EXTENDED then
local frameworkPlayer = FRAMEWORK_DATA.GetPlayerFromId(source)
if not frameworkPlayer then LogWarning("Tried to get player identifier but player doesnt have a framework player?"
, source) return end
return frameworkPlayer.getIdentifier()
elseif FRAMEWORK == ENUM_DEFINED_FRAMEWORKS.QB_CORE then
local frameworkPlayer = FRAMEWORK_DATA.Functions.GetPlayer(source)
if not frameworkPlayer then LogWarning("Tried to get player identifier but player doesnt have a framework player?"
, source) return end
return frameworkPlayer.PlayerData.citizenid
end
return GetRealIdentifier(source)
end
---Returns true if the player should return item amounts
---@param source any
---@return boolean
function ShouldIgnoreItems(source)
source = tonumber(source)
if not ServerConfig.m_itemsNeeded then return true end
return TableContains(ServerConfig.m_ignoreItemsNeededJobs, GetPlayerJob(source))
end
---Gets the medics on duty
---@return integer
function GetMedicsOnDutyCount()
local medicsOnDuty = 0
if FRAMEWORK == ENUM_DEFINED_FRAMEWORKS.ES_EXTENDED then
local xPlayers = ESX.GetExtendedPlayers()
for _, v in pairs(xPlayers) do
if TableContains(ServerConfig.m_dependUnconsciousTimeOnMedicCount.jobs, v.job.name) then
medicsOnDuty = medicsOnDuty + 1
end
end
elseif FRAMEWORK == ENUM_DEFINED_FRAMEWORKS.QB_CORE then
for _, player in pairs(FRAMEWORK_DATA.Functions.GetPlayers()) do
if TableContains(ServerConfig.m_dependUnconsciousTimeOnMedicCount.jobs, player.PlayerData.job.name) then
medicsOnDuty = medicsOnDuty + 1
end
end
else
-- Implement your own logic (standalone)
end
return medicsOnDuty
end
---Returns true if the player is allowed to use the triage system
---@param source any
---@return boolean
function IsAllowedToUseTriageSystem(source)
source = tonumber(source)
if not ServerConfig.m_triageSystem.enabled then return false end
if not ServerConfig.m_triageSystem.jobRestriction then return true end
local job = GetPlayerJob(source)
return TableContains(ServerConfig.m_triageSystem.jobs, job)
end
---Returns true if the player is allowed to use the revive command
---@param source any
function IsAllowedToUseReviveCommand(source)
source = tonumber(source)
if not ServerConfig.m_reviveCommand then return false end
if FRAMEWORK == ENUM_DEFINED_FRAMEWORKS.ES_EXTENDED then
local frameworkPlayer = FRAMEWORK_DATA.GetPlayerFromId(source)
if not frameworkPlayer then return false end
return frameworkPlayer.getGroup() == "admin" or frameworkPlayer.getGroup() == "superadmin"
elseif FRAMEWORK == ENUM_DEFINED_FRAMEWORKS.QB_CORE then
return FRAMEWORK_DATA.Functions.HasPermission(source, 'admin')
end
-- Your own logic (standalone)
return IsPlayerAceAllowed(source, "command.revive")
end
---Gets all available items configured in the actions.lua
---@return table
function GetAvailableItems()
local items = {}
for _, v in pairs(AVAILABLE_ACTIONS) do
for _, v2 in pairs(v) do
if v2.requiredItem then items[v2.requiredItem.name] = v2.requiredItem end
end
end
return items
end
---Sends a discord log to the discord api
---@param message any
---@param description any
function SendDiscordLog(message, description)
if not ServerConfig.m_discordLogging.enabled then return end
if not ServerConfig.m_discordLogging.webhook or ServerConfig.m_discordLogging.webhook == "" then return end
if not message or message == "" then return end
local discordEmbed = {
["title"] = message,
["description"] = description or "",
["type"] = "rich",
["color"] = "16754688",
["footer"] = {
["text"] = "Advanced Roleplay Environment (" .. CURRENT_BUILD .. ")",
},
["timestamp"] = os.date("!%Y-%m-%dT%H:%M:%SZ"),
}
PerformHttpRequest(ServerConfig.m_discordLogging.webhook, function(err, text, headers) end, 'POST', json.encode({ "Advanced Roleplay Environment", embeds = { discordEmbed }}), { ['Content-Type'] = 'application/json' })
end