From 8c3620769f246fb1f22ebc5db116406899d29e51 Mon Sep 17 00:00:00 2001 From: Alx <96348049+AlexRXWindy@users.noreply.github.com> Date: Wed, 15 Oct 2025 20:30:46 +0200 Subject: [PATCH 1/4] feat(player-stats): add new configurable stats and new character stat options --- .../client/modules/adjustments.lua | 79 +++++++++++++++++++ .../es_extended/shared/config/adjustments.lua | 14 ++++ 2 files changed, 93 insertions(+) diff --git a/[core]/es_extended/client/modules/adjustments.lua b/[core]/es_extended/client/modules/adjustments.lua index 1c83b148..ffb5e43e 100644 --- a/[core]/es_extended/client/modules/adjustments.lua +++ b/[core]/es_extended/client/modules/adjustments.lua @@ -235,6 +235,73 @@ function Adjustments:Multipliers() end) end +function Adjustments:ApplyPlayerStats() + if not Config.PlayerStatsByGender.enabled then return end + + local gender = self:GetPlayerGender() + if not gender then return end + + local stats = Config.PlayerStatsByGender[gender] + if not stats then return end + + + SetPlayerMaxStamina(ESX.playerId, 100.0) + RestorePlayerStamina(ESX.playerId, 100.0) + SetRunSprintMultiplierForPlayer(ESX.playerId, stats.stamina) + SetSwimMultiplierForPlayer(ESX.playerId, stats.stamina) + + SetPedMoveRateOverride(ESX.PlayerData.ped, stats.stamina) + + SetPlayerMeleeWeaponDamageModifier(ESX.playerId, stats.strength) + SetPlayerWeaponDamageModifier(ESX.playerId, stats.strength) +end + +function Adjustments:GetPlayerGender() + if not ESX.PlayerLoaded then return end + + if Config.PlayerStatsByGender.useCharacterData then + if ESX.PlayerData.sex then + return ESX.PlayerData.sex == 'm' and 'male' or 'female' + end + + if ESX.PlayerData.gender then + return ESX.PlayerData.gender == 0 and 'male' or 'female' + end + else + local model = GetEntityModel(ESX.PlayerData.ped) + if model == `mp_m_freemode_01` then + return 'male' + elseif model == `mp_f_freemode_01` then + return 'female' + end + + if IsPedMale(ESX.PlayerData.ped) then + return 'male' + else + return 'female' + end + end + + return nil +end + +function Adjustments:RefreshPlayerStats() + if not Config.PlayerStatsByGender.enabled then return end + + local gender = self:GetPlayerGender() + if not gender then return end + + local stats = Config.PlayerStatsByGender[gender] + if not stats then return end + + SetRunSprintMultiplierForPlayer(ESX.playerId, stats.stamina) + SetSwimMultiplierForPlayer(ESX.playerId, stats.stamina) + SetPedMoveRateOverride(ESX.PlayerData.ped, stats.stamina) + + SetPlayerMeleeWeaponDamageModifier(ESX.playerId, stats.strength) + SetPlayerWeaponDamageModifier(ESX.playerId, stats.strength) +end + function Adjustments:Load() self:RemoveHudComponents() self:DisableAimAssist() @@ -250,4 +317,16 @@ function Adjustments:Load() self:WantedLevel() self:DisableRadio() self:Multipliers() + + AddEventHandler('esx:playerLoaded', function(xPlayer, isNew, skin) + self:ApplyPlayerStats() + end) + + AddEventHandler('skinchanger:modelLoaded', function() + self:RefreshPlayerStats() + end) + + AddEventHandler('esx:onPlayerSpawn', function() + self:RefreshPlayerStats() + end) end diff --git a/[core]/es_extended/shared/config/adjustments.lua b/[core]/es_extended/shared/config/adjustments.lua index 334e95d4..e798311e 100644 --- a/[core]/es_extended/shared/config/adjustments.lua +++ b/[core]/es_extended/shared/config/adjustments.lua @@ -44,6 +44,20 @@ Config.Multipliers = { vehicleDensity = 1.0 } +Config.PlayerStatsByGender = { + enabled = true, + debugMode = true, + useCharacterData = true, -- true: uses ESX.PlayerData.sex | false: uses ped model/skin + male = { + stamina = 1.0, + strength = 1.0 + }, + female = { + stamina = 1.15, + strength = 0.85 + } +} + -- Pattern string format --1 will lead to a random number from 0-9. --A will lead to a random letter from A-Z. From d91c4aff09e3018d672d22fc262661df102472e4 Mon Sep 17 00:00:00 2001 From: Alx <96348049+AlexRXWindy@users.noreply.github.com> Date: Wed, 15 Oct 2025 23:51:43 +0200 Subject: [PATCH 2/4] refactor: address code review feedback --- .../client/modules/adjustments.lua | 157 ++++++++++++++---- .../es_extended/shared/config/adjustments.lua | 23 ++- 2 files changed, 141 insertions(+), 39 deletions(-) diff --git a/[core]/es_extended/client/modules/adjustments.lua b/[core]/es_extended/client/modules/adjustments.lua index ffb5e43e..acff4987 100644 --- a/[core]/es_extended/client/modules/adjustments.lua +++ b/[core]/es_extended/client/modules/adjustments.lua @@ -236,43 +236,119 @@ function Adjustments:Multipliers() end function Adjustments:ApplyPlayerStats() - if not Config.PlayerStatsByGender.enabled then return end + if not Config.PlayerStatsByGender.enabled then return end + + if Config.PlayerStatsByGender.debugMode then + print('[^3adjustments^7] applying player stats...') + end local gender = self:GetPlayerGender() - if not gender then return end + if not gender then + if Config.PlayerStatsByGender.debugMode then + print('[^1adjustments^7] failed to detect gender') + end + return + end local stats = Config.PlayerStatsByGender[gender] - if not stats then return end + if not stats then + if Config.PlayerStatsByGender.debugMode then + print('[^1adjustments^7] no stats found for gender: ' .. gender) + end + return + end + if stats.stamina then + SetRunSprintMultiplierForPlayer(ESX.playerId, stats.stamina) + if Config.PlayerStatsByGender.debugMode then + print('[^2adjustments^7] stamina: ' .. tostring(stats.stamina)) + end + end - SetPlayerMaxStamina(ESX.playerId, 100.0) - RestorePlayerStamina(ESX.playerId, 100.0) - SetRunSprintMultiplierForPlayer(ESX.playerId, stats.stamina) - SetSwimMultiplierForPlayer(ESX.playerId, stats.stamina) + if stats.strength then + SetPlayerMeleeWeaponDamageModifier(ESX.playerId, stats.strength) + if Config.PlayerStatsByGender.debugMode then + print('[^2adjustments^7] strength: ' .. tostring(stats.strength)) + end + end - SetPedMoveRateOverride(ESX.PlayerData.ped, stats.stamina) + if stats.swimSpeed then + SetSwimMultiplierForPlayer(ESX.playerId, stats.swimSpeed) + if Config.PlayerStatsByGender.debugMode then + print('[^2adjustments^7] swimSpeed: ' .. tostring(stats.swimSpeed)) + end + end - SetPlayerMeleeWeaponDamageModifier(ESX.playerId, stats.strength) - SetPlayerWeaponDamageModifier(ESX.playerId, stats.strength) + if stats.moveSpeed then + self.currentMoveSpeed = stats.moveSpeed + if not self.moveSpeedThreadRunning then + self.moveSpeedThreadRunning = true + CreateThread(function() + while Config.PlayerStatsByGender.enabled and self.currentMoveSpeed do + if ESX.PlayerData.ped then + SetPedMoveRateOverride(ESX.PlayerData.ped, self.currentMoveSpeed) + end + Wait(0) + end + self.moveSpeedThreadRunning = false + end) + end + if Config.PlayerStatsByGender.debugMode then + print('[^2adjustments^7] moveSpeed: ' .. tostring(stats.moveSpeed)) + end + end + + -- ADD NEW STATS HERE + -- ======================================================================== + -- 1. Find your native: https://docs.fivem.net/natives/ + -- 2. Uncomment template below (remove --) + -- 3. Replace "statName" with your stat name + -- 4. Replace "YourNative" with native from docs + -- 5. Add same stat to Config file + -- 6. Debug print is OPTIONAL (can be removed) + -- ======================================================================== + + -- if stats.statName then + -- YourNative(ESX.playerId, stats.statName) + -- if Config.PlayerStatsByGender.debugMode then + -- print('[^2adjustments^7] statName: ' .. tostring(stats.statName)) + -- end + -- end + + if Config.PlayerStatsByGender.debugMode then + print('[^2adjustments^7] stats applied for gender: ' .. gender) + end end function Adjustments:GetPlayerGender() - if not ESX.PlayerLoaded then return end + if not ESX.PlayerLoaded then + if Config.PlayerStatsByGender.debugMode then + print('[^1adjustments^7] player not loaded yet') + end + return + end + + if Config.PlayerStatsByGender.debugMode then + print('[^3adjustments^7] detecting gender using: ' .. (Config.PlayerStatsByGender.useCharacterData and 'character data' or 'ped model')) + end if Config.PlayerStatsByGender.useCharacterData then if ESX.PlayerData.sex then return ESX.PlayerData.sex == 'm' and 'male' or 'female' end - - if ESX.PlayerData.gender then - return ESX.PlayerData.gender == 0 and 'male' or 'female' - end else local model = GetEntityModel(ESX.PlayerData.ped) - if model == `mp_m_freemode_01` then - return 'male' - elseif model == `mp_f_freemode_01` then - return 'female' + + for i = 1, #Config.PlayerStatsByGender.malePeds do + if model == Config.PlayerStatsByGender.malePeds[i] then + return 'male' + end + end + + for i = 1, #Config.PlayerStatsByGender.femalePeds do + if model == Config.PlayerStatsByGender.femalePeds[i] then + return 'female' + end end if IsPedMale(ESX.PlayerData.ped) then @@ -285,21 +361,9 @@ function Adjustments:GetPlayerGender() return nil end + function Adjustments:RefreshPlayerStats() - if not Config.PlayerStatsByGender.enabled then return end - - local gender = self:GetPlayerGender() - if not gender then return end - - local stats = Config.PlayerStatsByGender[gender] - if not stats then return end - - SetRunSprintMultiplierForPlayer(ESX.playerId, stats.stamina) - SetSwimMultiplierForPlayer(ESX.playerId, stats.stamina) - SetPedMoveRateOverride(ESX.PlayerData.ped, stats.stamina) - - SetPlayerMeleeWeaponDamageModifier(ESX.playerId, stats.strength) - SetPlayerWeaponDamageModifier(ESX.playerId, stats.strength) + self:ApplyPlayerStats() end function Adjustments:Load() @@ -319,14 +383,35 @@ function Adjustments:Load() self:Multipliers() AddEventHandler('esx:playerLoaded', function(xPlayer, isNew, skin) + if Config.PlayerStatsByGender.debugMode then + print('[^3adjustments^7] esx:playerloaded event triggered') + end self:ApplyPlayerStats() end) - AddEventHandler('skinchanger:modelLoaded', function() - self:RefreshPlayerStats() - end) + if not Config.PlayerStatsByGender.useCharacterData then + AddEventHandler('skinchanger:modelLoaded', function() + if Config.PlayerStatsByGender.debugMode then + print('[^3adjustments^7] skinchanger:modelloaded event triggered') + end + self:RefreshPlayerStats() + end) + else + if Config.PlayerStatsByGender.debugMode then + print('[^3adjustments^7] skinchanger event not registered (using character data)') + end + end AddEventHandler('esx:onPlayerSpawn', function() + if Config.PlayerStatsByGender.debugMode then + print('[^3adjustments^7] esx:onplayerspawn event triggered') + end self:RefreshPlayerStats() end) + if Config.PlayerStatsByGender.enabled and Config.PlayerStatsByGender.debugMode then + print('[^2adjustments^7] player stats by gender loaded') + print('[^3adjustments^7] enabled: ' .. tostring(Config.PlayerStatsByGender.enabled)) + print('[^3adjustments^7] use character data: ' .. tostring(Config.PlayerStatsByGender.useCharacterData)) + print('[^3adjustments^7] debug mode: active') + end end diff --git a/[core]/es_extended/shared/config/adjustments.lua b/[core]/es_extended/shared/config/adjustments.lua index e798311e..e41b6ece 100644 --- a/[core]/es_extended/shared/config/adjustments.lua +++ b/[core]/es_extended/shared/config/adjustments.lua @@ -44,17 +44,34 @@ Config.Multipliers = { vehicleDensity = 1.0 } + Config.PlayerStatsByGender = { enabled = true, debugMode = true, useCharacterData = true, -- true: uses ESX.PlayerData.sex | false: uses ped model/skin + malePeds = { + `mp_m_freemode_01`, + }, + femalePeds = { + `mp_f_freemode_01`, + }, male = { stamina = 1.0, - strength = 1.0 + strength = 1.0, + swimSpeed = 1.25, + moveSpeed = 1.0, + + -- ADD NEW STATS HERE + -- 1. Uncomment line below (remove --) + -- 2. Change value: 1.0 = normal, 1.5 = +50%, 0.5 = -50% + -- 3. Find natives: https://docs.fivem.net/natives/ + -- statName = 1.0, }, - female = { + female = { stamina = 1.15, - strength = 0.85 + strength = 0.85, + swimSpeed = 1.25, + moveSpeed = 7.00, } } From 5d73f63bb976ac5e205ed35ad1ab8e315b6e7a8c Mon Sep 17 00:00:00 2001 From: Alx <96348049+AlexRXWindy@users.noreply.github.com> Date: Thu, 16 Oct 2025 18:43:55 +0200 Subject: [PATCH 3/4] refactor(player-stats): improve code structure in ESX --- .../client/modules/adjustments.lua | 57 ++++++------------- 1 file changed, 16 insertions(+), 41 deletions(-) diff --git a/[core]/es_extended/client/modules/adjustments.lua b/[core]/es_extended/client/modules/adjustments.lua index acff4987..4e0f5224 100644 --- a/[core]/es_extended/client/modules/adjustments.lua +++ b/[core]/es_extended/client/modules/adjustments.lua @@ -236,15 +236,15 @@ function Adjustments:Multipliers() end function Adjustments:ApplyPlayerStats() - if not Config.PlayerStatsByGender.enabled then return end + if not Config.PlayerStatsByGender.enabled then return end - if Config.PlayerStatsByGender.debugMode then + if Config.EnableDebug then print('[^3adjustments^7] applying player stats...') end local gender = self:GetPlayerGender() if not gender then - if Config.PlayerStatsByGender.debugMode then + if Config.EnableDebug then print('[^1adjustments^7] failed to detect gender') end return @@ -252,7 +252,7 @@ function Adjustments:ApplyPlayerStats() local stats = Config.PlayerStatsByGender[gender] if not stats then - if Config.PlayerStatsByGender.debugMode then + if Config.EnableDebug then print('[^1adjustments^7] no stats found for gender: ' .. gender) end return @@ -260,25 +260,17 @@ function Adjustments:ApplyPlayerStats() if stats.stamina then SetRunSprintMultiplierForPlayer(ESX.playerId, stats.stamina) - if Config.PlayerStatsByGender.debugMode then - print('[^2adjustments^7] stamina: ' .. tostring(stats.stamina)) - end end if stats.strength then SetPlayerMeleeWeaponDamageModifier(ESX.playerId, stats.strength) - if Config.PlayerStatsByGender.debugMode then - print('[^2adjustments^7] strength: ' .. tostring(stats.strength)) - end end if stats.swimSpeed then SetSwimMultiplierForPlayer(ESX.playerId, stats.swimSpeed) - if Config.PlayerStatsByGender.debugMode then - print('[^2adjustments^7] swimSpeed: ' .. tostring(stats.swimSpeed)) - end end + -- moveSpeed requires continuous loop if stats.moveSpeed then self.currentMoveSpeed = stats.moveSpeed if not self.moveSpeedThreadRunning then @@ -293,9 +285,6 @@ function Adjustments:ApplyPlayerStats() self.moveSpeedThreadRunning = false end) end - if Config.PlayerStatsByGender.debugMode then - print('[^2adjustments^7] moveSpeed: ' .. tostring(stats.moveSpeed)) - end end -- ADD NEW STATS HERE @@ -310,33 +299,35 @@ function Adjustments:ApplyPlayerStats() -- if stats.statName then -- YourNative(ESX.playerId, stats.statName) - -- if Config.PlayerStatsByGender.debugMode then + -- if Config.EnableDebug then -- print('[^2adjustments^7] statName: ' .. tostring(stats.statName)) -- end -- end - if Config.PlayerStatsByGender.debugMode then + if Config.EnableDebug then print('[^2adjustments^7] stats applied for gender: ' .. gender) end end function Adjustments:GetPlayerGender() if not ESX.PlayerLoaded then - if Config.PlayerStatsByGender.debugMode then + if Config.EnableDebug then print('[^1adjustments^7] player not loaded yet') end return end - if Config.PlayerStatsByGender.debugMode then + if Config.EnableDebug then print('[^3adjustments^7] detecting gender using: ' .. (Config.PlayerStatsByGender.useCharacterData and 'character data' or 'ped model')) end if Config.PlayerStatsByGender.useCharacterData then + -- Option 1: character data if ESX.PlayerData.sex then return ESX.PlayerData.sex == 'm' and 'male' or 'female' end else + -- Option 2: ped model local model = GetEntityModel(ESX.PlayerData.ped) for i = 1, #Config.PlayerStatsByGender.malePeds do @@ -351,6 +342,7 @@ function Adjustments:GetPlayerGender() end end + -- Native fallback if IsPedMale(ESX.PlayerData.ped) then return 'male' else @@ -361,11 +353,6 @@ function Adjustments:GetPlayerGender() return nil end - -function Adjustments:RefreshPlayerStats() - self:ApplyPlayerStats() -end - function Adjustments:Load() self:RemoveHudComponents() self:DisableAimAssist() @@ -383,32 +370,20 @@ function Adjustments:Load() self:Multipliers() AddEventHandler('esx:playerLoaded', function(xPlayer, isNew, skin) - if Config.PlayerStatsByGender.debugMode then - print('[^3adjustments^7] esx:playerloaded event triggered') - end self:ApplyPlayerStats() end) if not Config.PlayerStatsByGender.useCharacterData then AddEventHandler('skinchanger:modelLoaded', function() - if Config.PlayerStatsByGender.debugMode then - print('[^3adjustments^7] skinchanger:modelloaded event triggered') - end - self:RefreshPlayerStats() + self:ApplyPlayerStats() end) - else - if Config.PlayerStatsByGender.debugMode then - print('[^3adjustments^7] skinchanger event not registered (using character data)') - end end AddEventHandler('esx:onPlayerSpawn', function() - if Config.PlayerStatsByGender.debugMode then - print('[^3adjustments^7] esx:onplayerspawn event triggered') - end - self:RefreshPlayerStats() + self:ApplyPlayerStats() end) - if Config.PlayerStatsByGender.enabled and Config.PlayerStatsByGender.debugMode then + + if Config.PlayerStatsByGender.enabled and Config.EnableDebug then print('[^2adjustments^7] player stats by gender loaded') print('[^3adjustments^7] enabled: ' .. tostring(Config.PlayerStatsByGender.enabled)) print('[^3adjustments^7] use character data: ' .. tostring(Config.PlayerStatsByGender.useCharacterData)) From 35c8abb699a5fc47f1b64dc1293119193bf69a25 Mon Sep 17 00:00:00 2001 From: Alx <96348049+AlexRXWindy@users.noreply.github.com> Date: Thu, 16 Oct 2025 18:46:10 +0200 Subject: [PATCH 4/4] refactor(player-stats): improve code structure in ESX --- .../es_extended/client/modules/adjustments.lua | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/[core]/es_extended/client/modules/adjustments.lua b/[core]/es_extended/client/modules/adjustments.lua index 4e0f5224..51c3f648 100644 --- a/[core]/es_extended/client/modules/adjustments.lua +++ b/[core]/es_extended/client/modules/adjustments.lua @@ -287,23 +287,6 @@ function Adjustments:ApplyPlayerStats() end end - -- ADD NEW STATS HERE - -- ======================================================================== - -- 1. Find your native: https://docs.fivem.net/natives/ - -- 2. Uncomment template below (remove --) - -- 3. Replace "statName" with your stat name - -- 4. Replace "YourNative" with native from docs - -- 5. Add same stat to Config file - -- 6. Debug print is OPTIONAL (can be removed) - -- ======================================================================== - - -- if stats.statName then - -- YourNative(ESX.playerId, stats.statName) - -- if Config.EnableDebug then - -- print('[^2adjustments^7] statName: ' .. tostring(stats.statName)) - -- end - -- end - if Config.EnableDebug then print('[^2adjustments^7] stats applied for gender: ' .. gender) end