diff --git a/[SQL]/legacy.sql b/[SQL]/legacy.sql index 308c608c..b041f32f 100644 --- a/[SQL]/legacy.sql +++ b/[SQL]/legacy.sql @@ -821,6 +821,13 @@ ALTER TABLE `licenses` ALTER TABLE `owned_vehicles` ADD PRIMARY KEY (`plate`); +-- +-- +-- Indexes for table `vehicles` +-- +ALTER TABLE `vehicles` + ADD PRIMARY KEY (`model`); + -- -- Indexes for table `rented_vehicles` -- diff --git a/[core]/es_extended/client/functions.lua b/[core]/es_extended/client/functions.lua index 42569381..58252a0f 100644 --- a/[core]/es_extended/client/functions.lua +++ b/[core]/es_extended/client/functions.lua @@ -1019,7 +1019,7 @@ function ESX.Game.SetVehicleProperties(vehicle, props) for extraId, enabled in pairs(props.extras) do extraId = tonumber(extraId) if extraId then - SetVehicleExtra(vehicle, extraId, enabled) + SetVehicleExtra(vehicle, extraId, not enabled) end end end diff --git a/[core]/es_extended/client/main.lua b/[core]/es_extended/client/main.lua index b989857f..08d85b78 100644 --- a/[core]/es_extended/client/main.lua +++ b/[core]/es_extended/client/main.lua @@ -4,16 +4,6 @@ ESX.SecureNetEvent("esx:requestModel", function(model) ESX.Streaming.RequestModel(model) end) -local function ApplyMetadata(metadata) - if metadata.health then - SetEntityHealth(ESX.PlayerData.ped, metadata.health) - end - - if metadata.armor and metadata.armor > 0 then - SetPedArmour(ESX.PlayerData.ped, metadata.armor) - end -end - RegisterNetEvent("esx:playerLoaded", function(xPlayer, _, skin) ESX.PlayerData = xPlayer @@ -34,8 +24,6 @@ RegisterNetEvent("esx:playerLoaded", function(xPlayer, _, skin) ESX.PlayerLoaded = true - ApplyMetadata(ESX.PlayerData.metadata) - local timer = GetGameTimer() while not HaveAllStreamingRequestsCompleted(ESX.PlayerData.ped) and (GetGameTimer() - timer) < 2000 do Wait(0) @@ -56,10 +44,13 @@ RegisterNetEvent("esx:playerLoaded", function(xPlayer, _, skin) Actions:Init() StartPointsLoop() StartServerSyncLoops() + NetworkSetLocalPlayerSyncLookAt(true) end) +local isFirstSpawn = true ESX.SecureNetEvent("esx:onPlayerLogout", function() ESX.PlayerLoaded = false + isFirstSpawn = true end) ESX.SecureNetEvent("esx:setMaxWeight", function(newMaxWeight) @@ -72,7 +63,21 @@ local function onPlayerSpawn() end AddEventHandler("playerSpawned", onPlayerSpawn) -AddEventHandler("esx:onPlayerSpawn", onPlayerSpawn) +AddEventHandler("esx:onPlayerSpawn", function() + onPlayerSpawn() + + if isFirstSpawn then + isFirstSpawn = false + + if ESX.PlayerData.metadata.health and (ESX.PlayerData.metadata.health > 0 or Config.SaveDeathStatus) then + SetEntityHealth(ESX.PlayerData.ped, ESX.PlayerData.metadata.health) + end + + if ESX.PlayerData.metadata.armor and ESX.PlayerData.metadata.armor > 0 then + SetPedArmour(ESX.PlayerData.ped, ESX.PlayerData.metadata.armor) + end + end +end) AddEventHandler("esx:onPlayerDeath", function() ESX.SetPlayerData("ped", PlayerPedId()) @@ -282,29 +287,58 @@ end function StartServerSyncLoops() if Config.CustomInventory then return end - -- keep track of ammo + + local currentWeapon = { + ---@type number + ---@diagnostic disable-next-line: assign-type-mismatch + hash = `WEAPON_UNARMED`, + ammo = 0, + } + + local function updateCurrentWeaponAmmo(weaponName) + local newAmmo = GetAmmoInPedWeapon(ESX.PlayerData.ped, currentWeapon.hash) + + if newAmmo ~= currentWeapon.ammo then + currentWeapon.ammo = newAmmo + TriggerServerEvent("esx:updateWeaponAmmo", weaponName, newAmmo) + end + end + CreateThread(function() - local currentWeapon = { Ammo = 0 } while ESX.PlayerLoaded do - local sleep = 1500 - if GetSelectedPedWeapon(ESX.PlayerData.ped) ~= -1569615261 then - sleep = 1000 - local _, weaponHash = GetCurrentPedWeapon(ESX.PlayerData.ped, true) - local weapon = ESX.GetWeaponFromHash(weaponHash) - if weapon then - local ammoCount = GetAmmoInPedWeapon(ESX.PlayerData.ped, weaponHash) - if weapon.name ~= currentWeapon.name then - currentWeapon.Ammo = ammoCount - currentWeapon.name = weapon.name - else - if ammoCount ~= currentWeapon.Ammo then - currentWeapon.Ammo = ammoCount - TriggerServerEvent("esx:updateWeaponAmmo", weapon.name, ammoCount) - end + currentWeapon.hash = GetSelectedPedWeapon(ESX.PlayerData.ped) + + if currentWeapon.hash ~= `WEAPON_UNARMED` then + local weaponConfig = ESX.GetWeaponFromHash(currentWeapon.hash) + + if weaponConfig then + currentWeapon.ammo = GetAmmoInPedWeapon(ESX.PlayerData.ped, currentWeapon.hash) + + while GetSelectedPedWeapon(ESX.PlayerData.ped) == currentWeapon.hash do + updateCurrentWeaponAmmo(weaponConfig.name) + Wait(1000) end + + updateCurrentWeaponAmmo(weaponConfig.name) end end - Wait(sleep) + Wait(250) + end + end) + + CreateThread(function() + local PARACHUTE_OPENING = 1 + local PARACHUTE_OPEN = 2 + + while ESX.PlayerLoaded do + local parachuteState = GetPedParachuteState(ESX.PlayerData.ped) + + if parachuteState == PARACHUTE_OPENING or parachuteState == PARACHUTE_OPEN then + TriggerServerEvent("esx:updateWeaponAmmo", "GADGET_PARACHUTE", 0) + + while GetPedParachuteState(ESX.PlayerData.ped) ~= -1 do Wait(1000) end + end + Wait(500) end end) end diff --git a/[core]/es_extended/client/modules/adjustments.lua b/[core]/es_extended/client/modules/adjustments.lua index 42fa9398..b4d4fe8a 100644 --- a/[core]/es_extended/client/modules/adjustments.lua +++ b/[core]/es_extended/client/modules/adjustments.lua @@ -4,6 +4,7 @@ function Adjustments:RemoveHudComponents() for i = 1, #Config.RemoveHudComponents do if Config.RemoveHudComponents[i] then SetHudComponentSize(i, 0.0, 0.0) + SetHudComponentPosition(i, 900, 900) end end end @@ -215,6 +216,15 @@ function Adjustments:WantedLevel() end end +function Adjustments:DisableRadio() + if Config.RemoveHudComponents[16] then + AddEventHandler("esx:enteredVehicle", function(vehicle, plate, seat, displayName, netId) + SetVehRadioStation(vehicle,"OFF") + SetUserRadioControlEnabled(false) + end) + end +end + function Adjustments:Load() self:RemoveHudComponents() self:DisableAimAssist() @@ -228,4 +238,5 @@ function Adjustments:Load() self:LicensePlates() self:DiscordPresence() self:WantedLevel() + self:DisableRadio() end diff --git a/[core]/es_extended/client/modules/callback.lua b/[core]/es_extended/client/modules/callback.lua index 20fa7463..0230aa87 100644 --- a/[core]/es_extended/client/modules/callback.lua +++ b/[core]/es_extended/client/modules/callback.lua @@ -51,8 +51,11 @@ function Callbacks:ServerRecieve(requestId, invoker, ...) end end -function Callbacks:Register(name, cb) - self.storage[name] = cb +function Callbacks:Register(name, resource, cb) + self.storage[name] = { + resource = resource, + cb = cb + } end function Callbacks:ClientRecieve(eventName, requestId, invoker, ...) @@ -64,7 +67,7 @@ function Callbacks:ClientRecieve(eventName, requestId, invoker, ...) local returnCb = function(...) TriggerServerEvent("esx:clientCallback", requestId, invoker, ...) end - local callback = self.storage[eventName] + local callback = self.storage[eventName].cb self:Execute(callback, requestId, returnCb, ...) end @@ -115,13 +118,17 @@ function ESX.AwaitServerCallback(eventName, ...) return table.unpack(p.value) end ----@param eventName string ----@param callback function ----@return nil -function ESX.RegisterClientCallback(eventName, callback) - Callbacks:Register(eventName, callback) +ESX.RegisterClientCallback = function(eventName, callback) + local invokingResource = GetInvokingResource() + local invoker = (invokingResource and invokingResource ~= "Unknown") and invokingResource or "es_extended" + + Callbacks:Register(eventName, invoker, callback) end +ESX.SecureNetEvent("esx:triggerClientCallback", function(...) + Callbacks:ClientRecieve(...) +end) + ---@param eventName string ---@return boolean function ESX.DoesClientCallbackExist(eventName) @@ -133,3 +140,11 @@ end function ESX.GetClientCallbackInfo(eventName) return Callbacks.storage[eventName] end + +AddEventHandler("onResourceStop", function(resource) + for k, v in pairs(Callbacks.storage) do + if v.resource == resource then + Callbacks.storage[k] = nil + end + end +end) diff --git a/[core]/es_extended/client/modules/death.lua b/[core]/es_extended/client/modules/death.lua index 4054421e..28e860de 100644 --- a/[core]/es_extended/client/modules/death.lua +++ b/[core]/es_extended/client/modules/death.lua @@ -29,7 +29,7 @@ function Death:ByPlayer() TriggerServerEvent("esx:onPlayerDeath", data) end -function Death:Natual() +function Death:Natural() local coords = GetEntityCoords(ESX.PlayerData.ped) local data = { @@ -43,25 +43,7 @@ function Death:Natual() TriggerServerEvent("esx:onPlayerDeath", data) end -function Death:Damaged(victim, victimDied) - if not victimDied then - return - end - - if not IsEntityAPed(victim) then - return - end - - if not IsPedAPlayer(victim) then - return - end - - local victimId = NetworkGetPlayerIndexFromPed(victim) - local isDead = IsPedDeadOrDying(victim, true) or IsPedFatallyInjured(victim) - if victimId ~= ESX.playerId or not isDead then - return - end - +function Death:Died() self.killerEntity = GetPedSourceOfDeath(ESX.PlayerData.ped) self.deathCause = GetPedCauseOfDeath(ESX.PlayerData.ped) self.killerId = NetworkGetPlayerIndexFromPed(self.killerEntity) @@ -72,15 +54,20 @@ function Death:Damaged(victim, victimDied) if self.killerEntity ~= ESX.PlayerData.ped and self.killerId and isActive then self:ByPlayer() else - self:Natual() + self:Natural() end self:ResetValues() end -AddEventHandler("gameEventTriggered", function(event, data) - if event ~= "CEventNetworkEntityDamage" then - return - end - Death:Damaged(data[1], data[4]) +AddEventHandler("esx:onPlayerSpawn", function() + Citizen.CreateThreadNow(function() + while not ESX.PlayerData.dead do + if IsPedDeadOrDying(ESX.PlayerData.ped, true) or IsPedFatallyInjured(ESX.PlayerData.ped) then + Death:Died() + break + end + Citizen.Wait(250) + end + end) end) diff --git a/[core]/es_extended/server/classes/player.lua b/[core]/es_extended/server/classes/player.lua index 2bfeb79f..b5e66e7b 100644 --- a/[core]/es_extended/server/classes/player.lua +++ b/[core]/es_extended/server/classes/player.lua @@ -608,8 +608,17 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, function self.updateWeaponAmmo(weaponName, ammoCount) local _, weapon = self.getWeapon(weaponName) - if weapon then - weapon.ammo = ammoCount + if not weapon then + return + end + + weapon.ammo = ammoCount + + if weapon.ammo <= 0 then + local _, weaponConfig = ESX.GetWeapon(weaponName) + if weaponConfig.throwable then + self.removeWeapon(weaponName) + end end end diff --git a/[core]/es_extended/server/functions.lua b/[core]/es_extended/server/functions.lua index 606ab0e7..cec340b5 100644 --- a/[core]/es_extended/server/functions.lua +++ b/[core]/es_extended/server/functions.lua @@ -276,15 +276,15 @@ end ESX.GetPlayers = GetPlayers -local function checkTable(key, val, player, xPlayers) +local function checkTable(key, val, xPlayer, xPlayers) for valIndex = 1, #val do local value = val[valIndex] if not xPlayers[value] then xPlayers[value] = {} end - if (key == "job" and player.job.name == value) or player[key] == value then - xPlayers[value][#xPlayers[value] + 1] = player + if (key == "job" and xPlayer.job.name == value) or xPlayer[key] == value then + xPlayers[value][#xPlayers[value] + 1] = xPlayer end end end @@ -293,20 +293,22 @@ end ---@param val? string|table ---@return table function ESX.GetExtendedPlayers(key, val) + if not key then + return ESX.Table.ToArray(ESX.Players) + end + local xPlayers = {} if type(val) == "table" then - for _, v in pairs(ESX.Players) do - checkTable(key, val, v, xPlayers) + for i, xPlayer in pairs(ESX.Players) do + checkTable(key, val, xPlayer, xPlayers) end - else - for _, v in pairs(ESX.Players) do - if key then - if (key == "job" and v.job.name == val) or v[key] == val then - xPlayers[#xPlayers + 1] = v - end - else - xPlayers[#xPlayers + 1] = v - end + + return xPlayers + end + + for i, xPlayer in pairs(ESX.Players) do + if (key == "job" and xPlayer.job.name == val) or xPlayer[key] == val then + xPlayers[#xPlayers + 1] = xPlayer end end diff --git a/[core]/es_extended/server/modules/actions.lua b/[core]/es_extended/server/modules/actions.lua index 52eeeb5b..e7c91e6a 100644 --- a/[core]/es_extended/server/modules/actions.lua +++ b/[core]/es_extended/server/modules/actions.lua @@ -1,9 +1,9 @@ -RegisterServerEvent("esx:playerPedChanged") -RegisterServerEvent("esx:playerJumping") -RegisterServerEvent("esx:enteringVehicle") -RegisterServerEvent("esx:enteringVehicleAborted") -RegisterServerEvent("esx:enteredVehicle") -RegisterServerEvent("esx:exitedVehicle") +RegisterNetEvent("esx:playerPedChanged") +RegisterNetEvent("esx:playerJumping") +RegisterNetEvent("esx:enteringVehicle") +RegisterNetEvent("esx:enteringVehicleAborted") +RegisterNetEvent("esx:enteredVehicle") +RegisterNetEvent("esx:exitedVehicle") if Config.EnableDebug then AddEventHandler("esx:playerPedChanged", function(netId) diff --git a/[core]/es_extended/server/modules/callback.lua b/[core]/es_extended/server/modules/callback.lua index f84ae987..59fa7435 100644 --- a/[core]/es_extended/server/modules/callback.lua +++ b/[core]/es_extended/server/modules/callback.lua @@ -6,8 +6,11 @@ Callbacks.requests = {} Callbacks.storage = {} Callbacks.id = 0 -function Callbacks:Register(name, cb) - self.storage[name] = cb +function Callbacks:Register(name, resource, cb) + self.storage[name] = { + resource = resource, + cb = cb + } end function Callbacks:Execute(cb, ...) @@ -39,7 +42,7 @@ function Callbacks:ServerRecieve(player, event, requestId, invoker, ...) local returnCb = function(...) TriggerClientEvent("esx:serverCallback", player, requestId, invoker, ...) end - local callback = self.storage[event] + local callback = self.storage[event].cb self:Execute(callback, player, returnCb, ...) end @@ -73,15 +76,19 @@ end) ---@param ... any function ESX.TriggerClientCallback(player, eventName, callback, ...) local invokingResource = GetInvokingResource() - local invoker = (invokingResource and invokingResource ~= "unknown") and invokingResource or "es_extended" + local invoker = (invokingResource and invokingResource ~= "Unknown") and invokingResource or "es_extended" Callbacks:Trigger(player, eventName, callback, invoker, ...) end ---@param eventName string ---@param callback function -function ESX.RegisterServerCallback(eventName, callback) - Callbacks:Register(eventName, callback) +---@return nil +ESX.RegisterServerCallback = function(eventName, callback) + local invokingResource = GetInvokingResource() + local invoker = (invokingResource and invokingResource ~= "Unknown") and invokingResource or "es_extended" + + Callbacks:Register(eventName, invoker, callback) end ---@param eventName string @@ -95,3 +102,11 @@ end function ESX.GetServerCallbackInfo(eventName) return Callbacks.storage[eventName] end + +AddEventHandler("onResourceStop", function(resource) + for k, v in pairs(Callbacks.storage) do + if v.resource == resource then + Callbacks.storage[k] = nil + end + end +end) diff --git a/[core]/es_extended/shared/config/main.lua b/[core]/es_extended/shared/config/main.lua index 2c375605..7db1e472 100644 --- a/[core]/es_extended/shared/config/main.lua +++ b/[core]/es_extended/shared/config/main.lua @@ -40,6 +40,7 @@ Config.LogPaycheck = false -- Logs paychecks to a nominated Discord channel via Config.EnableSocietyPayouts = false -- pay from the society account that the player is employed at? Requirement: esx_society Config.MaxWeight = 24 -- the max inventory weight without a backpack Config.PaycheckInterval = 7 * 60000 -- how often to receive paychecks in milliseconds +Config.SaveDeathStatus = true -- Save the death status of a player Config.EnableDebug = false -- Use Debug options? Config.DefaultJobDuty = true -- A players default duty status when changing jobs diff --git a/[core]/es_extended/shared/config/weapons.lua b/[core]/es_extended/shared/config/weapons.lua index d26ab75a..e9392f30 100644 --- a/[core]/es_extended/shared/config/weapons.lua +++ b/[core]/es_extended/shared/config/weapons.lua @@ -1008,22 +1008,22 @@ Config.Weapons = { { name = "WEAPON_RPG", label = TranslateCap("weapon_rpg"), tints = Config.DefaultWeaponTints, components = {}, ammo = { label = TranslateCap("ammo_rockets"), hash = `AMMO_RPG` } }, { name = "WEAPON_RAYMINIGUN", label = TranslateCap("weapon_rayminigun"), tints = Config.DefaultWeaponTints, components = {}, ammo = { label = TranslateCap("ammo_rounds"), hash = `AMMO_MINIGUN` } }, -- Thrown - { name = "WEAPON_BALL", label = TranslateCap("weapon_ball"), components = {}, ammo = { label = TranslateCap("ammo_ball"), hash = `AMMO_BALL` } }, - { name = "WEAPON_BZGAS", label = TranslateCap("weapon_bzgas"), components = {}, ammo = { label = TranslateCap("ammo_bzgas"), hash = `AMMO_BZGAS` } }, - { name = "WEAPON_FLARE", label = TranslateCap("weapon_flare"), components = {}, ammo = { label = TranslateCap("ammo_flare"), hash = `AMMO_FLARE` } }, - { name = "WEAPON_GRENADE", label = TranslateCap("weapon_grenade"), components = {}, ammo = { label = TranslateCap("ammo_grenade"), hash = `AMMO_GRENADE` } }, - { name = "WEAPON_PETROLCAN", label = TranslateCap("weapon_petrolcan"), components = {}, ammo = { label = TranslateCap("ammo_petrol"), hash = `AMMO_PETROLCAN` } }, - { name = "WEAPON_HAZARDCAN", label = TranslateCap("weapon_hazardcan"), components = {}, ammo = { label = TranslateCap("ammo_petrol"), hash = `AMMO_PETROLCAN` } }, - { name = "WEAPON_MOLOTOV", label = TranslateCap("weapon_molotov"), components = {}, ammo = { label = TranslateCap("ammo_molotov"), hash = `AMMO_MOLOTOV` } }, - { name = "WEAPON_PROXMINE", label = TranslateCap("weapon_proxmine"), components = {}, ammo = { label = TranslateCap("ammo_proxmine"), hash = `AMMO_PROXMINE` } }, - { name = "WEAPON_PIPEBOMB", label = TranslateCap("weapon_pipebomb"), components = {}, ammo = { label = TranslateCap("ammo_pipebomb"), hash = `AMMO_PIPEBOMB` } }, - { name = "WEAPON_SNOWBALL", label = TranslateCap("weapon_snowball"), components = {}, ammo = { label = TranslateCap("ammo_snowball"), hash = `AMMO_SNOWBALL` } }, - { name = "WEAPON_STICKYBOMB", label = TranslateCap("weapon_stickybomb"), components = {}, ammo = { label = TranslateCap("ammo_stickybomb"), hash = `AMMO_STICKYBOMB` } }, - { name = "WEAPON_SMOKEGRENADE", label = TranslateCap("weapon_smokegrenade"), components = {}, ammo = { label = TranslateCap("ammo_smokebomb"), hash = `AMMO_SMOKEGRENADE` } }, + { name = "WEAPON_BALL", label = TranslateCap("weapon_ball"), components = {}, ammo = { label = TranslateCap("ammo_ball"), hash = `AMMO_BALL` }, throwable = true }, + { name = "WEAPON_BZGAS", label = TranslateCap("weapon_bzgas"), components = {}, ammo = { label = TranslateCap("ammo_bzgas"), hash = `AMMO_BZGAS` }, throwable = true }, + { name = "WEAPON_FLARE", label = TranslateCap("weapon_flare"), components = {}, ammo = { label = TranslateCap("ammo_flare"), hash = `AMMO_FLARE` }, throwable = true }, + { name = "WEAPON_GRENADE", label = TranslateCap("weapon_grenade"), components = {}, ammo = { label = TranslateCap("ammo_grenade"), hash = `AMMO_GRENADE` }, throwable = true }, + { name = "WEAPON_PETROLCAN", label = TranslateCap("weapon_petrolcan"), components = {}, ammo = { label = TranslateCap("ammo_petrol"), hash = `AMMO_PETROLCAN` }, throwable = true }, + { name = "WEAPON_HAZARDCAN", label = TranslateCap("weapon_hazardcan"), components = {}, ammo = { label = TranslateCap("ammo_petrol"), hash = `AMMO_PETROLCAN` }, throwable = true }, + { name = "WEAPON_MOLOTOV", label = TranslateCap("weapon_molotov"), components = {}, ammo = { label = TranslateCap("ammo_molotov"), hash = `AMMO_MOLOTOV` }, throwable = true }, + { name = "WEAPON_PROXMINE", label = TranslateCap("weapon_proxmine"), components = {}, ammo = { label = TranslateCap("ammo_proxmine"), hash = `AMMO_PROXMINE` }, throwable = true }, + { name = "WEAPON_PIPEBOMB", label = TranslateCap("weapon_pipebomb"), components = {}, ammo = { label = TranslateCap("ammo_pipebomb"), hash = `AMMO_PIPEBOMB` }, throwable = true }, + { name = "WEAPON_SNOWBALL", label = TranslateCap("weapon_snowball"), components = {}, ammo = { label = TranslateCap("ammo_snowball"), hash = `AMMO_SNOWBALL` }, throwable = true }, + { name = "WEAPON_STICKYBOMB", label = TranslateCap("weapon_stickybomb"), components = {}, ammo = { label = TranslateCap("ammo_stickybomb"), hash = `AMMO_STICKYBOMB` }, throwable = true }, + { name = "WEAPON_SMOKEGRENADE", label = TranslateCap("weapon_smokegrenade"), components = {}, ammo = { label = TranslateCap("ammo_smokebomb"), hash = `AMMO_SMOKEGRENADE` }, throwable = true }, -- Tools { name = "WEAPON_FIREEXTINGUISHER", label = TranslateCap("weapon_fireextinguisher"), components = {}, ammo = { label = TranslateCap("ammo_charge"), hash = `AMMO_FIREEXTINGUISHER` } }, { name = "WEAPON_DIGISCANNER", label = TranslateCap("weapon_digiscanner"), components = {} }, - { name = "GADGET_PARACHUTE", label = TranslateCap("gadget_parachute"), components = {} }, + { name = "GADGET_PARACHUTE", label = TranslateCap("gadget_parachute"), components = {}, throwable = true }, { name = "WEAPON_TACTICALRIFLE", label = TranslateCap("weapon_tactilerifle"), @@ -1057,7 +1057,7 @@ Config.Weapons = { { name = "suppressor", label = TranslateCap("component_suppressor"), hash = `COMPONENT_PISTOLXM3_SUPP` }, }, }, - { name = "WEAPON_ACIDPACKAGE", label = TranslateCap("weapon_acidpackage"), components = {} }, + { name = "WEAPON_ACIDPACKAGE", label = TranslateCap("weapon_acidpackage"), components = {}, throwable = true }, { name = "WEAPON_CANDYCANE", label = TranslateCap("weapon_candycane"), components = {} }, { name = "WEAPON_RAILGUNXM3", diff --git a/[core]/es_extended/shared/functions.lua b/[core]/es_extended/shared/functions.lua index fc342bce..1a5d0090 100644 --- a/[core]/es_extended/shared/functions.lua +++ b/[core]/es_extended/shared/functions.lua @@ -164,3 +164,10 @@ function ESX.AssertType(...) return matches end + +---@param val unknown +function ESX.IsFunctionReference(val) + local typeVal = type(val) + + return typeVal == "function" or (typeVal == "table" and type(getmetatable(val)?.__call) == "function") +end \ No newline at end of file diff --git a/[core]/es_extended/shared/modules/table.lua b/[core]/es_extended/shared/modules/table.lua index fc345200..aacb2584 100644 --- a/[core]/es_extended/shared/modules/table.lua +++ b/[core]/es_extended/shared/modules/table.lua @@ -223,3 +223,11 @@ function ESX.Table.Sort(t, order) end end end + +function ESX.Table.ToArray(table) + local array = {} + for _, v in pairs(table) do + array[#array + 1] = v + end + return array +end diff --git a/[core]/esx_multicharacter/client/modules/multicharacter.lua b/[core]/esx_multicharacter/client/modules/multicharacter.lua index 9ea9b974..ef099f58 100644 --- a/[core]/esx_multicharacter/client/modules/multicharacter.lua +++ b/[core]/esx_multicharacter/client/modules/multicharacter.lua @@ -272,7 +272,6 @@ function Multicharacter:PlayerLoaded(playerData, isNew, skin) TriggerServerEvent("esx:onPlayerSpawn") TriggerEvent("esx:onPlayerSpawn") - TriggerEvent("playerSpawned") TriggerEvent("esx:restoreLoadout") self:Reset() diff --git a/[core]/esx_multicharacter/server/modules/multicharacter.lua b/[core]/esx_multicharacter/server/modules/multicharacter.lua index 923dd55e..c43be581 100644 --- a/[core]/esx_multicharacter/server/modules/multicharacter.lua +++ b/[core]/esx_multicharacter/server/modules/multicharacter.lua @@ -13,7 +13,7 @@ function Multicharacter:SetupCharacters(source) local identifier = Server:GetIdentifier(source) ESX.Players[identifier] = true - local slots = Database:GetPlayerSlots() + local slots = Database:GetPlayerSlots(identifier) identifier = Server.prefix .. "%:" .. identifier local rawCharacters = Database:GetPlayerInfo(identifier, slots) diff --git a/[core]/esx_skin/client/modules/menu.lua b/[core]/esx_skin/client/modules/menu.lua index 6a43e4a6..b6de6745 100644 --- a/[core]/esx_skin/client/modules/menu.lua +++ b/[core]/esx_skin/client/modules/menu.lua @@ -47,7 +47,7 @@ function Menu:InsertElements() value = GetPedPropIndex(playerPed, self.components[i].componentId) end - local data = self.components[i] + local data = table.clone(self.components[i]) data.value = value data.type = "slider" data.max = self.maxValues[self.components[i].name] @@ -57,7 +57,7 @@ function Menu:InsertElements() end function Menu:Submit(data, menu) - Skin.last = exports["skinchanger"]:GetSkin() + Skin.Last = exports["skinchanger"]:GetSkin() self.submitCb(data, menu) Camera:Destroy() end @@ -65,7 +65,7 @@ end function Menu:Cancel(data, menu) menu.close() Camera:Destroy() - TriggerEvent("skinchanger:loadSkin", Skin.last) + TriggerEvent("skinchanger:loadSkin", Skin.Last) if self.cancelCb then self.cancelCb(data, menu) @@ -78,27 +78,39 @@ function Menu:Change(data, menu) Skin.zoomOffset = data.current.zoomOffset Skin.camOffset = data.current.camOffset - if skin[data.current.name] ~= data.current.value then - -- Change skin element - exports["skinchanger"]:Change(data.current.name, data.current.value) - skin[data.current.name] = data.current.value + if skin[data.current.name] == data.current.value then + return + end - local newData = {} + -- Change skin element + exports["skinchanger"]:Change(data.current.name, data.current.value) + skin[data.current.name] = data.current.value - for i = 1, #self.elements, 1 do + -- Texture variation changed. We don't have to update anything. + if data.current.textureof then + return + end + + -- Texture changed. Update variation max value. + for i = 1, #self.elements, 1 do + local element = self.elements[i] + + if element.textureof == data.current.name then local component = self.components[i] - newData.max = type(component.max) == "function" and component.max(PlayerPedId(), skin) or component.max - - if self.elements[i].textureof ~= nil and data.current.name == self.elements[i].textureof then - newData.value = 0 + if ESX.IsFunctionReference(component.max) then + self.elements[i].max = component.max(PlayerPedId(), skin) end + self.elements[i].value = 0 - menu.update({ name = self.elements[i].name }, newData) + -- Change skin element + exports["skinchanger"]:Change(element.name, 0) + skin[element.name] = data.current.value + + menu.update({ name = self.elements[i].name }, self.elements[i]) + menu.refresh() + break end - - self.elements = newData - menu.refresh() end end @@ -111,7 +123,7 @@ function Menu:Open(submit, cancel, restrict) self.submitCb = submit self.cancelCb = cancel self.restricted = restrict - Skin.last = exports["skinchanger"]:GetSkin() + Skin.Last = exports["skinchanger"]:GetSkin() self.components, self.maxValues = exports["skinchanger"]:GetData() if restrict then @@ -128,7 +140,7 @@ function Menu:Open(submit, cancel, restrict) end function Menu:Saveable(submitCb, cancelCb, restrict) - Skin.last = exports["skinchanger"]:GetSkin() + Skin.Last = exports["skinchanger"]:GetSkin() self:Open(function(data, menu) menu.close() diff --git a/[core]/skinchanger/config.lua b/[core]/skinchanger/config.lua index 2401b9c7..1d5a0fed 100644 --- a/[core]/skinchanger/config.lua +++ b/[core]/skinchanger/config.lua @@ -237,6 +237,7 @@ Config.Components = { min = 0, zoomOffset = 0.6, camOffset = 0.65, + textureof = "hair_1", max = function(playerPed, Character) return GetNumberOfPedTextureVariations(playerPed, 2, Character["hair_1"]) - 1 end