diff --git a/client/functions.lua b/client/functions.lua index 06db3e2d..82310ec9 100644 --- a/client/functions.lua +++ b/client/functions.lua @@ -1,4 +1,6 @@ ESX = {} +ESX.Loops = {} +ESX.LoopsRunning = {} ESX.Modules = {} ESX.PlayerData = {} ESX.PlayerLoaded = false @@ -57,6 +59,59 @@ RegisterNUICallback('frame_message', function(data, cb) cb('') end) +ESX.LogError = function(err) + local str = err .. ' ' .. debug.traceback() + print(str) + TriggerServerEvent('esx:error:log', str); +end + +ESX.LogScopeError = function(scope, err) + local str = '[esx] error in scope (' .. scope .. ') ' .. err .. ' ' .. debug.traceback() + print(str) + TriggerServerEvent('esx:error:log', str); +end + + +ESX.LogLoopError = function(loop, err) + local str = '[esx] error in loop (' .. loop .. ') ' .. err .. ' ' .. debug.traceback() + print(str) + TriggerServerEvent('esx:error:log', str); +end + +ESX.Loop = function(name, func, wait, conditions) + + ESX.Loops[name] = { + func = func, + wait = wait, + conditons = conditions or {}, + name = name, + } + +end + +ESX.Scope = function(name, func) + + local status, result = xpcall(func, function(err) + ESX.LogScopeError(name, err) + end) + + return result +end + +ESX.MakeScope = function(name, func) + + return function(...) + + local status, result = xpcall(func, ESX.LogError, function(err) + ESX.LogScopeError(name, err) + end) + + return result + + end + +end + ESX.IsPlayerLoaded = function() return ESX.PlayerLoaded end diff --git a/client/main.lua b/client/main.lua index a66d515f..581da81b 100644 --- a/client/main.lua +++ b/client/main.lua @@ -1,5 +1,80 @@ local isLoadoutLoaded, isPaused, pickups = false, false, {} +Citizen.CreateThread(function() + + AddTextEntry('FE_THDR_GTAO', 'ESX') + + local logLoopError = function(err) + ESX.LogLoopError(name, err) + end + + local runLoop = function(name, loop) + + local conditionsMet = true + + for j=1, #loop.conditons, 1 do + if not loop.conditons[j]() then + conditionsMet = false + break + end + end + + if conditionsMet then + + ESX.LoopsRunning[name] = true + + Citizen.CreateThread(function() + while true do + + local tests = #loop.conditons + + for i=1, #loop.conditons, 1 do + + if not loop.conditons[i]() then + break + end + + tests = tests - 1 + + end + + if tests > 0 then + ESX.LoopsRunning[name] = false + return + end + + local status, result = xpcall(loop.func, logLoopError) + + if not status then + ESX.Loops[name] = nil + ESX.LoopsRunning[name] = false + return + end + + Citizen.Wait(loop.wait) + + end + end) + + end + + end + + while true do + + for name, loop in pairs(ESX.Loops) do + + if ESX.LoopsRunning[name] ~= true then + local status, result = xpcall(runLoop, ESX.LogError, name, loop) + end + + end + + Citizen.Wait(250) + + end +end) + Citizen.CreateThread(function() while true do Citizen.Wait(0) @@ -56,8 +131,6 @@ AddEventHandler('esx:playerLoaded', function(playerData) TriggerEvent('esx:onPlayerSpawn') TriggerEvent('esx:restoreLoadout') - StartServerSyncLoops() - end) end) @@ -80,7 +153,8 @@ AddEventHandler('skinchanger:modelLoaded', function() end) AddEventHandler('esx:restoreLoadout', function() - local playerPed = PlayerPedId() + + local playerPed = PlayerPedId() local ammoTypes = {} RemoveAllPedWeapons(playerPed, true) @@ -272,10 +346,11 @@ AddEventHandler('esx:createPickup', function(pickupId, label, playerId, type, na SetEntityCollision(obj, false, true) pickups[pickupId] = { - obj = obj, - label = label, + id = pickupId, + obj = obj, + label = label, inRange = false, - coords = objectCoords + coords = objectCoords } end @@ -420,116 +495,139 @@ if Config.EnableHud then end) end -function StartServerSyncLoops() - -- keep track of ammo - Citizen.CreateThread(function() - while true do - Citizen.Wait(0) +ESX.Loop('server-sync-ammo', function() - if ESX.IsDead then - Citizen.Wait(500) - else - local playerPed = PlayerPedId() + local playerPed = PlayerPedId() - if IsPedShooting(playerPed) then - local _,weaponHash = GetCurrentPedWeapon(playerPed, true) - local weapon = ESX.GetWeaponFromHash(weaponHash) + if IsPedShooting(playerPed) then + local _,weaponHash = GetCurrentPedWeapon(playerPed, true) + local weapon = ESX.GetWeaponFromHash(weaponHash) - if weapon then - local ammoCount = GetAmmoInPedWeapon(playerPed, weaponHash) - TriggerServerEvent('esx:updateWeaponAmmo', weapon.name, ammoCount) - end - end - end - end - end) + if weapon then + local ammoCount = GetAmmoInPedWeapon(playerPed, weaponHash) + TriggerServerEvent('esx:updateWeaponAmmo', weapon.name, ammoCount) + end + end - -- sync current player coords with server - Citizen.CreateThread(function() - local previousCoords = vector3(ESX.PlayerData.coords.x, ESX.PlayerData.coords.y, ESX.PlayerData.coords.z) +end, 250, { + function() return ESX.PlayerLoaded and (not ESX.IsDead) end +}) - while true do - Citizen.Wait(1000) - local playerPed = PlayerPedId() +local previousCoords - if DoesEntityExist(playerPed) then - local playerCoords = GetEntityCoords(playerPed) - local distance = #(playerCoords - previousCoords) +ESX.Loop('server-sync-coords', function() - if distance > 1 then - previousCoords = playerCoords - local playerHeading = ESX.Math.Round(GetEntityHeading(playerPed), 1) - local formattedCoords = {x = ESX.Math.Round(playerCoords.x, 1), y = ESX.Math.Round(playerCoords.y, 1), z = ESX.Math.Round(playerCoords.z, 1), heading = playerHeading} - TriggerServerEvent('esx:updateCoords', formattedCoords) - end - end - end - end) -end + local playerPed = PlayerPedId() + + if DoesEntityExist(playerPed) then + + local playerCoords = GetEntityCoords(playerPed) + previousCoords = previousCoords or playerCoords + local distance = #(playerCoords - previousCoords) + + if distance > 1 then + previousCoords = playerCoords + local playerHeading = ESX.Math.Round(GetEntityHeading(playerPed), 1) + local formattedCoords = {x = ESX.Math.Round(playerCoords.x, 1), y = ESX.Math.Round(playerCoords.y, 1), z = ESX.Math.Round(playerCoords.z, 1), heading = playerHeading} + TriggerServerEvent('esx:updateCoords', formattedCoords) + end + + end + +end, 1000, { + function() return ESX.PlayerLoaded and (not ESX.IsDead) end +}) -- Disable wanted level if Config.DisableWantedLevel then - Citizen.CreateThread(function() - while true do - Citizen.Wait(0) - local playerId = PlayerId() - if GetPlayerWantedLevel(playerId) ~= 0 then - SetPlayerWantedLevel(playerId, 0, false) - SetPlayerWantedLevelNow(playerId, false) - end - end - end) + ESX.Loop('disable-wanted-level', function() + + local playerId = PlayerId() + + if GetPlayerWantedLevel(playerId) ~= 0 then + SetPlayerWantedLevel(playerId, 0, false) + SetPlayerWantedLevelNow(playerId, false) + end + + end, 0) + end -- Pickups -Citizen.CreateThread(function() - while true do - Citizen.Wait(0) - local playerPed = PlayerPedId() - local playerCoords, letSleep = GetEntityCoords(playerPed), true - local closestPlayer, closestDistance = ESX.Game.GetClosestPlayer(playerCoords) +local pickupsInRange = {} +local closestUsablePickup = nil - for pickupId,pickup in pairs(pickups) do - local distance = #(playerCoords - pickup.coords) +ESX.Loop('get-pickups-in-range', function() - if distance < 5 then - local label = pickup.label - letSleep = false + local playerPed = PlayerPedId() + local playerCoords = GetEntityCoords(playerPed) - if distance < 1 then - if IsControlJustReleased(0, 38) then - if IsPedOnFoot(playerPed) and (closestDistance == -1 or closestDistance > 3) and not pickup.inRange then - pickup.inRange = true + pickupsInRange = {} + closestUsablePickup = nil - local dict, anim = 'weapons@first_person@aim_rng@generic@projectile@sticky_bomb@', 'plant_floor' - ESX.Streaming.RequestAnimDict(dict) - TaskPlayAnim(playerPed, dict, anim, 8.0, 1.0, 1000, 16, 0.0, false, false, false) - Citizen.Wait(1000) + for pickupId, pickup in pairs(pickups) do - TriggerServerEvent('esx:onPickup', pickupId) - PlaySoundFrontend(-1, 'PICK_UP', 'HUD_FRONTEND_DEFAULT_SOUNDSET', false) - end - end + local distance = #(playerCoords - pickup.coords) - end + if distance < 5.0 then - ESX.ShowFloatingHelpNotification(label, { - x = pickup.coords.x, - y = pickup.coords.y, - z = pickup.coords.z + 0.25 - }, 100) + pickupsInRange[#pickupsInRange + 1] = pickup - elseif pickup.inRange then - pickup.inRange = false - end - end + if distance < 1.0 then + closestUsablePickup = pickup + end - if letSleep then - Citizen.Wait(500) - end - end -end) + end + + end + +end, 500) + +ESX.Loop('draw-pickups', function() + + local playerPed = PlayerPedId() + local playerCoords = GetEntityCoords(playerPed) + + for i=1, #pickupsInRange, 1 do + + local pickup = pickupsInRange[i] + + ESX.ShowFloatingHelpNotification(pickup.label, { + x = pickup.coords.x, + y = pickup.coords.y, + z = pickup.coords.z + 0.25 + }, 100) + + end + +end, 0) + +ESX.Loop('pickup-actions', function() + + local playerPed = PlayerPedId() + local pickup = closestUsablePickup + + if IsControlJustReleased(0, 38) then + if IsPedOnFoot(playerPed) then + + Citizen.CreateThread(function() + + local dict, anim = 'weapons@first_person@aim_rng@generic@projectile@sticky_bomb@', 'plant_floor' + ESX.Streaming.RequestAnimDict(dict) + TaskPlayAnim(playerPed, dict, anim, 8.0, 1.0, 1000, 16, 0.0, false, false, false) + Citizen.Wait(1000) + TriggerServerEvent('esx:onPickup', pickup.id) + PlaySoundFrontend(-1, 'PICK_UP', 'HUD_FRONTEND_DEFAULT_SOUNDSET', false) + + end) + + end + end + +end, 0, { + function() return closestUsablePickup ~= nil end +}) AddEventHandler('luaconsole:getHandlers', function(cb) diff --git a/client/modules/death.lua b/client/modules/death.lua index 959d8255..64f38b6f 100644 --- a/client/modules/death.lua +++ b/client/modules/death.lua @@ -1,64 +1,51 @@ -Citizen.CreateThread(function() - local isDead = false +AddEventHandler('baseevents:onPlayerDied', function(killerType, deathCoords) - while true do - Citizen.Wait(0) - local player = PlayerId() + local playerPed = PlayerPedId() - if NetworkIsPlayerActive(player) then - local playerPed = PlayerPedId() + local data = { + killed = false, + killerType = killerType, + deathCoords = deathCoords, + deathCause = GetPedCauseOfDeath(playerPed) + } - if IsPedFatallyInjured(playerPed) and not isDead then - isDead = true + TriggerEvent('esx:onPlayerDeath', data) + TriggerServerEvent('esx:onPlayerDeath', data) - local killerEntity, deathCause = GetPedSourceOfDeath(playerPed), GetPedCauseOfDeath(playerPed) - local killerClientId = NetworkGetPlayerIndexFromPed(killerEntity) - - if killerEntity ~= playerPed and killerClientId and NetworkIsPlayerActive(killerClientId) then - PlayerKilledByPlayer(GetPlayerServerId(killerClientId), killerClientId, deathCause) - else - PlayerKilled(deathCause) - end - - elseif not IsPedFatallyInjured(playerPed) then - isDead = false - end - end - end end) -function PlayerKilledByPlayer(killerServerId, killerClientId, deathCause) - local victimCoords = GetEntityCoords(PlayerPedId()) - local killerCoords = GetEntityCoords(GetPlayerPed(killerClientId)) - local distance = #(victimCoords - killerCoords) - - local data = { - victimCoords = {x = ESX.Math.Round(victimCoords.x, 1), y = ESX.Math.Round(victimCoords.y, 1), z = ESX.Math.Round(victimCoords.z, 1)}, - killerCoords = {x = ESX.Math.Round(killerCoords.x, 1), y = ESX.Math.Round(killerCoords.y, 1), z = ESX.Math.Round(killerCoords.z, 1)}, - - killedByPlayer = true, - deathCause = deathCause, - distance = ESX.Math.Round(distance, 1), - - killerServerId = killerServerId, - killerClientId = killerClientId - } - - TriggerEvent('esx:onPlayerDeath', data) - TriggerServerEvent('esx:onPlayerDeath', data) -end - -function PlayerKilled(deathCause) +AddEventHandler('baseevents:onPlayerKilled', function(killerId, data) local playerPed = PlayerPedId() - local victimCoords = GetEntityCoords(playerPed) + local killer = GetPlayerFromServerId(killerId) - local data = { - victimCoords = {x = ESX.Math.Round(victimCoords.x, 1), y = ESX.Math.Round(victimCoords.y, 1), z = ESX.Math.Round(victimCoords.z, 1)}, + if NetworkIsPlayerActive(killer) then - killedByPlayer = false, - deathCause = deathCause - } + local victimCoords = data.killerpos + local weaponHash = data.weaponhash + + data.killerpos = nil + data.weaponhash = nil + + local killerPed = GetPlayerPed(killer) + local killerCoords = GetEntityCoords(killerPed) + local distance = GetDistanceBetweenCoords(victimCoords[1], victimCoords[2], victimCoords[3], killerCoords, false) + + data.victimCoords = victimCoords + data.weaponHash = weaponHash + data.deathCause = GetPedCauseOfDeath(playerPed) + data.killed = true + data.killerId = killerId + data.killerCoords = {x = killerCoords.x, y = killerCoords.y, z = killerCoords.z} + data.distance = distance + + else + + data.killed = false + data.deathCause = GetPedCauseOfDeath(playerPed) + + end TriggerEvent('esx:onPlayerDeath', data) - TriggerServerEvent('esx:onPlayerDeath', data) -end + TriggerServerEvent('esx:onPlayerDeath', data) + +end) diff --git a/fxmanifest.lua b/fxmanifest.lua index 3dce7eed..6e912dbd 100644 --- a/fxmanifest.lua +++ b/fxmanifest.lua @@ -94,7 +94,9 @@ server_exports { dependencies { 'mysql-async', - 'async' + 'async', + 'cron', + 'skinchanger', } -- ESX Modules @@ -120,6 +122,7 @@ esxmodule 'interact' -- Interact menu (marker / npc) esxmodule 'addonaccount' -- Addon account esxmodule 'addoninventory' -- Addon inventory esxmodule 'datastore' -- Arbitrary data store +esxmodule 'society' -- Society management -- UI esxmodule 'hud' -- Money / society etc... HUD @@ -127,5 +130,8 @@ esxmodule 'menu_default' -- Default menu esxmodule 'menu_dialog' -- Dialog menu esxmodule 'menu_list' -- List menu +-- Misc +esxmodule 'skin' -- Skin management + -- Jobs esxmodule 'job_police' -- Job police diff --git a/modules/input/client/main.lua b/modules/input/client/main.lua index 12f29bb1..919c6b9b 100644 --- a/modules/input/client/main.lua +++ b/modules/input/client/main.lua @@ -1,5 +1,7 @@ local self = ESX.Modules['input'] +self.InitESX() + Citizen.CreateThread(function() while true do diff --git a/modules/input/client/module.lua b/modules/input/client/module.lua index 7d8c5ea1..7221d370 100644 --- a/modules/input/client/module.lua +++ b/modules/input/client/module.lua @@ -454,8 +454,8 @@ end self.InitESX = function() - self.RegisterControl(Input.Groups.MOVE, Input.Controls[Config.InventoryKey]) - self.On('released', Input.Groups.MOVE, Input.Controls[Config.InventoryKey], function(lastPressed) + self.RegisterControl(self.Groups.MOVE, self.Controls[Config.InventoryKey]) + self.On('released', self.Groups.MOVE, self.Controls[Config.InventoryKey], function(lastPressed) if (not ESX.IsDead) and (not ESX.UI.Menu.IsOpen('default', 'es_extended', 'inventory')) then ESX.ShowInventory() diff --git a/modules/interact/client/main.lua b/modules/interact/client/main.lua index 78a956a9..f54ec156 100644 --- a/modules/interact/client/main.lua +++ b/modules/interact/client/main.lua @@ -31,7 +31,7 @@ Citizen.CreateThread(function() local distance = #(data.pos - self.Cache.player.coords); if (distance <= data.distance) and - (ESX.Table.FindIndex(self.Cache.current, function(e) return e.__id == data.__id end) == -1) + (ESX.Table.FindIndex(self.Cache.current , function(e) return e.__id == data.__id end) == -1) then if (data.check == nil) or data.check(self.Cache.player.ped, self.Cache.player.coords) then @@ -62,33 +62,29 @@ Citizen.CreateThread(function() end) -- Markers -Citizen.CreateThread(function() - while true do +ESX.Loop('input-markers', function() - Citizen.Wait(0) + for i=1, #self.Cache.current, 1 do - for i=1, #self.Cache.current, 1 do + local curr = self.Cache.current[i] - local curr = self.Cache.current[i] + if curr.type == 'marker' then - if curr.type == 'marker' then - - DrawMarker( - curr.mtype, - curr.pos.x + 0.0, curr.pos.y + 0.0, curr.pos.z + 0.0, - 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, - curr.size.x + 0.0, curr.size.y + 0.0, curr.size.z + 0.0, - curr.color.r, curr.color.g, curr.color.b, curr.color.a, - curr.bobUpAndDown, curr.faceCamera, 2, curr.rotate, nil, nil, false - ) - - end + DrawMarker( + curr.mtype, + curr.pos.x + 0.0, curr.pos.y + 0.0, curr.pos.z + 0.0, + 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, + curr.size.x + 0.0, curr.size.y + 0.0, curr.size.z + 0.0, + curr.color.r, curr.color.g, curr.color.b, curr.color.a, + curr.bobUpAndDown, curr.faceCamera, 2, curr.rotate, nil, nil, false + ) end end -end) + +end, 0) -- NPCs Citizen.CreateThread(function() diff --git a/modules/interact/client/module.lua b/modules/interact/client/module.lua index 7fab0039..ff8d95b4 100644 --- a/modules/interact/client/module.lua +++ b/modules/interact/client/module.lua @@ -9,7 +9,10 @@ self.Cache = { ped = 0, coords = vector3(0.0, 0.0, 0.0) }, - current = {}, + current = { + marker = {}, + npc = {}, + }, using = {}, } diff --git a/modules/job_police/client/main.lua b/modules/job_police/client/main.lua index b5a21e69..a4a73d33 100644 --- a/modules/job_police/client/main.lua +++ b/modules/job_police/client/main.lua @@ -108,6 +108,7 @@ Citizen.CreateThread(function() end end) +--[[ -- Enter / Exit entity zone events Citizen.CreateThread(function() local trackedEntities = { @@ -154,4 +155,4 @@ Citizen.CreateThread(function() end end end) - +]]-- diff --git a/modules/job_police/data/config.lua b/modules/job_police/data/config.lua index 7a7c0407..5b1cf85d 100644 --- a/modules/job_police/data/config.lua +++ b/modules/job_police/data/config.lua @@ -5,7 +5,7 @@ Config.MarkerType = 1 Config.MarkerSize = {x = 1.5, y = 1.5, z = 0.5} Config.MarkerColor = {r = 50, g = 50, b = 204, a = 100} -Config.EnablePlayerManagement = false +Config.EnablePlayerManagement = true Config.EnableArmoryManagement = true Config.EnableESXIdentity = false -- enable if you're using esx_identity Config.EnableLicenses = false -- enable if you're using esx_license diff --git a/modules/job_police/server/main.lua b/modules/job_police/server/main.lua index 449ba44d..15811e0d 100644 --- a/modules/job_police/server/main.lua +++ b/modules/job_police/server/main.lua @@ -4,12 +4,5 @@ if self.Config.EnableESXService then TriggerEvent('esx_service:activateService', 'police', self.Config.MaxInService) end -AddEventHandler('onResourceStart', function(resource) - - if resource == 'esx_society' then - TriggerEvent('esx_society:registerSociety', 'police', 'Police', 'society_police', 'society_police', 'society_police', {type = 'public'}) - elseif resource == 'esx_phone' then - TriggerEvent('esx_phone:registerNumber', 'police', _U('alert_police'), true, true) - end - -end) +TriggerEvent('esx_society:registerSociety', 'police', 'Police', 'society_police', 'society_police', 'society_police', {type = 'public'}) +TriggerEvent('esx_phone:registerNumber', 'police', _U('alert_police'), true, true) diff --git a/modules/menu_dialog/client/main.lua b/modules/menu_dialog/client/main.lua index 60051150..c5cfdfec 100644 --- a/modules/menu_dialog/client/main.lua +++ b/modules/menu_dialog/client/main.lua @@ -3,22 +3,3 @@ local self = ESX.Modules['menu_dialog'] ESX.UI.Menu.RegisterType(self.MenuType, self.openMenu, self.closeMenu) -Citizen.CreateThread(function() - while true do - Citizen.Wait(10) - - if ESX.Table.SizeOf(self.OpenedMenus) > 0 then - DisableControlAction(0, 1, true) -- LookLeftRight - DisableControlAction(0, 2, true) -- LookUpDown - DisableControlAction(0, 142, true) -- MeleeAttackAlternate - DisableControlAction(0, 106, true) -- VehicleMouseControlOverride - DisableControlAction(0, 12, true) -- WeaponWheelUpDown - DisableControlAction(0, 14, true) -- WeaponWheelNext - DisableControlAction(0, 15, true) -- WeaponWheelPrev - DisableControlAction(0, 16, true) -- SelectNextWeapon - DisableControlAction(0, 17, true) -- SelectPrevWeapon - else - Citizen.Wait(500) - end - end -end) diff --git a/modules/menu_dialog/client/module.lua b/modules/menu_dialog/client/module.lua index 1bfdd2c0..75ae7155 100644 --- a/modules/menu_dialog/client/module.lua +++ b/modules/menu_dialog/client/module.lua @@ -1,12 +1,55 @@ ESX.Modules['menu_dialog'] = {}; local self = ESX.Modules['menu_dialog'] +local Input = ESX.Modules['input'] self.Timeouts = {} self.OpenedMenus = {} self.MenuType = 'dialog' +self.RegisterControls = function() + + Input.RegisterControl(Input.Groups.LOOK, Input.Controls.LOOK_LR) + Input.RegisterControl(Input.Groups.LOOK, Input.Controls.LOOK_UD) + Input.RegisterControl(Input.Groups.LOOK, Input.Controls.MELEE_ATTACK_ALTERNATE) + Input.RegisterControl(Input.Groups.LOOK, Input.Controls.VEH_MOUSE_CONTROL_OVERRIDE) + Input.RegisterControl(Input.Groups.LOOK, Input.Controls.WEAPON_WHEEL_UD) + Input.RegisterControl(Input.Groups.LOOK, Input.Controls.WEAPON_WHEEL_NEXT) + Input.RegisterControl(Input.Groups.LOOK, Input.Controls.WEAPON_WHEEL_PREV) + Input.RegisterControl(Input.Groups.LOOK, Input.Controls.SELECT_NEXT_WEAPON) + Input.RegisterControl(Input.Groups.LOOK, Input.Controls.SELECT_PREV_WEAPON) + +end + +self.EnableControls = function() + + Input.EnableControl(Input.Groups.LOOK, Input.Controls.LOOK_LR) + Input.EnableControl(Input.Groups.LOOK, Input.Controls.LOOK_UD) + Input.EnableControl(Input.Groups.LOOK, Input.Controls.MELEE_ATTACK_ALTERNATE) + Input.EnableControl(Input.Groups.LOOK, Input.Controls.VEH_MOUSE_CONTROL_OVERRIDE) + Input.EnableControl(Input.Groups.LOOK, Input.Controls.WEAPON_WHEEL_UD) + Input.EnableControl(Input.Groups.LOOK, Input.Controls.WEAPON_WHEEL_NEXT) + Input.EnableControl(Input.Groups.LOOK, Input.Controls.WEAPON_WHEEL_PREV) + Input.EnableControl(Input.Groups.LOOK, Input.Controls.SELECT_NEXT_WEAPON) + Input.EnableControl(Input.Groups.LOOK, Input.Controls.SELECT_PREV_WEAPON) + +end + +self.DisableControls = function() + + Input.DisableControl(Input.Groups.LOOK, Input.Controls.LOOK_LR) + Input.DisableControl(Input.Groups.LOOK, Input.Controls.LOOK_UD) + Input.DisableControl(Input.Groups.LOOK, Input.Controls.MELEE_ATTACK_ALTERNATE) + Input.DisableControl(Input.Groups.LOOK, Input.Controls.VEH_MOUSE_CONTROL_OVERRIDE) + Input.DisableControl(Input.Groups.LOOK, Input.Controls.WEAPON_WHEEL_UD) + Input.DisableControl(Input.Groups.LOOK, Input.Controls.WEAPON_WHEEL_NEXT) + Input.DisableControl(Input.Groups.LOOK, Input.Controls.WEAPON_WHEEL_PREV) + Input.DisableControl(Input.Groups.LOOK, Input.Controls.SELECT_NEXT_WEAPON) + Input.DisableControl(Input.Groups.LOOK, Input.Controls.SELECT_PREV_WEAPON) + +end + self.openMenu = function(namespace, name, data) - + for i=1, #self.Timeouts, 1 do ESX.ClearTimeout(self.Timeouts[i]) end @@ -24,11 +67,14 @@ self.openMenu = function(namespace, name, data) ESX.FocusFrame('menu_dialog', true, true) end) - table.insert(self.Timeouts, timeoutId) + table.insert(self.Timeouts, timeoutId) + + self.DisableControls() + end self.closeMenu = function(namespace, name) - + self.OpenedMenus[namespace .. '_' .. name] = nil ESX.SendFrameMessage('menu_dialog', { @@ -38,7 +84,8 @@ self.closeMenu = function(namespace, name) data = data }) - if ESX.Table.SizeOf(self.OpenedMenus) == 0 then + if ESX.Table.SizeOf(self.OpenedMenus) == 0 then + self.EnableControls() SetNuiFocus(false) end diff --git a/modules/skin/client/events.lua b/modules/skin/client/events.lua new file mode 100644 index 00000000..455c4785 --- /dev/null +++ b/modules/skin/client/events.lua @@ -0,0 +1,58 @@ +local self = ESX.Modules['skin'] + +AddEventHandler('esx:onPlayerSpawn', function() + Citizen.CreateThread(function() + + while not ESX.PlayerLoaded do + Citizen.Wait(100) + end + + if self.firstSpawn then + ESX.TriggerServerCallback('esx_skin:getPlayerSkin', function(skin, jobSkin) + if skin == nil then + TriggerEvent('skinchanger:loadSkin', {sex = 0}, OpenSaveableMenu) + else + TriggerEvent('skinchanger:loadSkin', skin) + end + end) + + self.firstSpawn = false + + end + end) +end) + +AddEventHandler('esx_skin:getLastSkin', function(cb) + cb(self.lastSkin) +end) + +AddEventHandler('esx_skin:setLastSkin', function(skin) + self.lastSkin = skin +end) + +RegisterNetEvent('esx_skin:openMenu') +AddEventHandler('esx_skin:openMenu', function(submitCb, cancelCb) + self.OpenMenu(submitCb, cancelCb, nil) +end) + +RegisterNetEvent('esx_skin:openRestrictedMenu') +AddEventHandler('esx_skin:openRestrictedMenu', function(submitCb, cancelCb, restrict) + self.OpenMenu(submitCb, cancelCb, restrict) +end) + +RegisterNetEvent('esx_skin:openSaveableMenu') +AddEventHandler('esx_skin:openSaveableMenu', function(submitCb, cancelCb) + self.OpenSaveableMenu(submitCb, cancelCb, nil) +end) + +RegisterNetEvent('esx_skin:openSaveableRestrictedMenu') +AddEventHandler('esx_skin:openSaveableRestrictedMenu', function(submitCb, cancelCb, restrict) + self.OpenSaveableMenu(submitCb, cancelCb, restrict) +end) + +RegisterNetEvent('esx_skin:requestSaveSkin') +AddEventHandler('esx_skin:requestSaveSkin', function() + TriggerEvent('skinchanger:getSkin', function(skin) + TriggerServerEvent('esx_skin:responseSaveSkin', skin) + end) +end) diff --git a/modules/skin/client/main.lua b/modules/skin/client/main.lua new file mode 100644 index 00000000..4fec8c44 --- /dev/null +++ b/modules/skin/client/main.lua @@ -0,0 +1,84 @@ +local self = ESX.Modules['skin'] + +Citizen.CreateThread(function() + while true do + + Citizen.Wait(0) + + if self.isCameraActive then + DisableControlAction(2, 30, true) + DisableControlAction(2, 31, true) + DisableControlAction(2, 32, true) + DisableControlAction(2, 33, true) + DisableControlAction(2, 34, true) + DisableControlAction(2, 35, true) + DisableControlAction(0, 25, true) -- Input Aim + DisableControlAction(0, 24, true) -- Input Attack + + local playerPed = PlayerPedId() + local coords = GetEntityCoords(playerPed) + + local angle = heading * math.pi / 180.0 + local theta = { + x = math.cos(angle), + y = math.sin(angle) + } + + local pos = { + x = coords.x + (zoomOffset * theta.x), + y = coords.y + (zoomOffset * theta.y) + } + + local angleToLook = heading - 140.0 + if angleToLook > 360 then + angleToLook = angleToLook - 360 + elseif angleToLook < 0 then + angleToLook = angleToLook + 360 + end + + angleToLook = angleToLook * math.pi / 180.0 + local thetaToLook = { + x = math.cos(angleToLook), + y = math.sin(angleToLook) + } + + local posToLook = { + x = coords.x + (zoomOffset * thetaToLook.x), + y = coords.y + (zoomOffset * thetaToLook.y) + } + + SetCamCoord(cam, pos.x, pos.y, coords.z + camOffset) + PointCamAtCoord(cam, posToLook.x, posToLook.y, coords.z + camOffset) + + ESX.ShowHelpNotification(_U('use_rotate_view')) + else + Citizen.Wait(500) + end + end +end) + +Citizen.CreateThread(function() + local angle = 90 + + while true do + Citizen.Wait(0) + + if self.isCameraActive then + if IsControlPressed(0, 108) then + self.angle = angle - 1 + elseif IsControlPressed(0, 109) then + self.angle = angle + 1 + end + + if self.angle > 360 then + self.angle = self.angle - 360 + elseif angle < 0 then + self.angle = self.angle + 360 + end + + self.heading = angle + 0.0 + else + Citizen.Wait(500) + end + end +end) diff --git a/modules/skin/client/module.lua b/modules/skin/client/module.lua new file mode 100644 index 00000000..9331ed52 --- /dev/null +++ b/modules/skin/client/module.lua @@ -0,0 +1,178 @@ +ESX.Modules['skin'] = {} +local self = ESX.Modules['skin'] + +self.lastSkin = nil +self.playerLoaded = false +self.cam = nil +self.isCameraActive = false + +self.firstSpawn = true +self.zoomOffset = 0.0 +self.camOffset = 0.0 +self.heading = 90.0 + +self.OpenMenu = function(submitCb, cancelCb, restrict) + + local playerPed = PlayerPedId() + + TriggerEvent('skinchanger:getSkin', function(skin) + self.lastSkin = skin + end) + + TriggerEvent('skinchanger:getData', function(components, maxVals) + local elements = {} + local _components = {} + + -- Restrict menu + if restrict == nil then + for i=1, #components, 1 do + _components[i] = components[i] + end + else + for i=1, #components, 1 do + local found = false + + for j=1, #restrict, 1 do + if components[i].name == restrict[j] then + found = true + end + end + + if found then + table.insert(_components, components[i]) + end + end + end + + -- Insert elements + for i=1, #_components, 1 do + local value = _components[i].value + local componentId = _components[i].componentId + + if componentId == 0 then + value = GetPedPropIndex(playerPed, _components[i].componentId) + end + + local data = { + label = _components[i].label, + name = _components[i].name, + value = value, + min = _components[i].min, + textureof = _components[i].textureof, + zoomOffset= _components[i].zoomOffset, + camOffset = _components[i].camOffset, + type = 'slider' + } + + for k,v in pairs(maxVals) do + if k == _components[i].name then + data.max = v + break + end + end + + table.insert(elements, data) + end + + self.CreateSkinCam() + self.zoomOffset = _components[1].zoomOffset + self.camOffset = _components[1].camOffset + + ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'skin', { + title = _U('skin_menu'), + align = 'top-left', + elements = elements + }, function(data, menu) + TriggerEvent('skinchanger:getSkin', function(skin) + self.lastSkin = skin + end) + + submitCb(data, menu) + DeleteSkinCam() + end, function(data, menu) + menu.close() + self.DeleteSkinCam() + TriggerEvent('skinchanger:loadSkin', lastSkin) + + if cancelCb ~= nil then + cancelCb(data, menu) + end + end, function(data, menu) + local skin, components, maxVals + + TriggerEvent('skinchanger:getSkin', function(getSkin) + skin = getSkin + end) + + zoomOffset = data.current.zoomOffset + camOffset = data.current.camOffset + + if skin[data.current.name] ~= data.current.value then + -- Change skin element + TriggerEvent('skinchanger:change', data.current.name, data.current.value) + + -- Update max values + TriggerEvent('skinchanger:getData', function(comp, max) + components, maxVals = comp, max + end) + + local newData = {} + + for i=1, #elements, 1 do + newData = {} + newData.max = maxVals[elements[i].name] + + if elements[i].textureof ~= nil and data.current.name == elements[i].textureof then + newData.value = 0 + end + + menu.update({name = elements[i].name}, newData) + end + + menu.refresh() + end + end, function(data, menu) + DeleteSkinCam() + end) + end) +end + +self.CreateSkinCam = function() + if not DoesCamExist(cam) then + self.cam = CreateCam('DEFAULT_SCRIPTED_CAMERA', true) + end + + SetCamActive(cam, true) + RenderScriptCams(true, true, 500, true, true) + + self.isCameraActive = true + SetCamRot(cam, 0.0, 0.0, 270.0, true) + SetEntityHeading(playerPed, 90.0) +end + +self.DeleteSkinCam = function() + self.isCameraActive = false + SetCamActive(cam, false) + RenderScriptCams(false, true, 500, true, true) + cam = nil +end + +self.OpenSaveableMenu = function(submitCb, cancelCb, restrict) + TriggerEvent('skinchanger:getSkin', function(skin) + lastSkin = skin + end) + + self.OpenMenu(function(data, menu) + menu.close() + DeleteSkinCam() + + TriggerEvent('skinchanger:getSkin', function(skin) + TriggerServerEvent('esx_skin:save', skin) + + if submitCb ~= nil then + submitCb(data, menu) + end + end) + + end, cancelCb, restrict) +end diff --git a/modules/skin/data/config.lua b/modules/skin/data/config.lua new file mode 100644 index 00000000..79d7c815 --- /dev/null +++ b/modules/skin/data/config.lua @@ -0,0 +1,7 @@ +Config = {} + +Config.Locale = 'fr' + +Config.BackpackWeight = { + [40] = 16, [41] = 20, [44] = 25, [45] = 23 +} \ No newline at end of file diff --git a/modules/skin/data/locales/br.lua b/modules/skin/data/locales/br.lua new file mode 100644 index 00000000..2a9d09d4 --- /dev/null +++ b/modules/skin/data/locales/br.lua @@ -0,0 +1,6 @@ +Locales['br'] = { + ['skin_menu'] = 'Skin Menu', + ['use_rotate_view'] = 'Use ~INPUT_VEH_FLY_ROLL_LEFT_ONLY~ e ~INPUT_VEH_FLY_ROLL_RIGHT_ONLY~ para girar a visão.', + ['skin'] = 'Trocar', + ['saveskin'] = 'Salvar', +} diff --git a/modules/skin/data/locales/de.lua b/modules/skin/data/locales/de.lua new file mode 100644 index 00000000..f1439358 --- /dev/null +++ b/modules/skin/data/locales/de.lua @@ -0,0 +1,6 @@ +Locales['de'] = { + ['skin_menu'] = 'Skin Menü', + ['use_rotate_view'] = 'benutze ~INPUT_VEH_FLY_ROLL_LEFT_ONLY~ und ~INPUT_VEH_FLY_ROLL_RIGHT_ONLY~ um die Sicht zu drehen.', + ['skin'] = 'Skin ändern', + ['saveskin'] = 'Skin speichern', +} diff --git a/modules/skin/data/locales/en.lua b/modules/skin/data/locales/en.lua new file mode 100644 index 00000000..f1ee073e --- /dev/null +++ b/modules/skin/data/locales/en.lua @@ -0,0 +1,6 @@ +Locales['en'] = { + ['skin_menu'] = 'Skin Menu', + ['use_rotate_view'] = 'use ~INPUT_VEH_FLY_ROLL_LEFT_ONLY~ and ~INPUT_VEH_FLY_ROLL_RIGHT_ONLY~ to rotate the view.', + ['skin'] = 'change skin', + ['saveskin'] = 'save skin to a file', +} diff --git a/modules/skin/data/locales/fi.lua b/modules/skin/data/locales/fi.lua new file mode 100644 index 00000000..39e6d75f --- /dev/null +++ b/modules/skin/data/locales/fi.lua @@ -0,0 +1,6 @@ +Locales['fi'] = { + ['skin_menu'] = 'ulkonäkö', + ['use_rotate_view'] = 'paina ~INPUT_VEH_FLY_ROLL_LEFT_ONLY~ tai ~INPUT_VEH_FLY_ROLL_RIGHT_ONLY~ liikutaaksesi kameraa.', + ['skin'] = 'vaihda ulkonäköä', + ['saveskin'] = 'talleta ulkonäkö tiedostoon', +} diff --git a/modules/skin/data/locales/fr.lua b/modules/skin/data/locales/fr.lua new file mode 100644 index 00000000..22cfc7bc --- /dev/null +++ b/modules/skin/data/locales/fr.lua @@ -0,0 +1,6 @@ +Locales['fr'] = { + ['skin_menu'] = 'Skin Menu', + ['use_rotate_view'] = 'utilisez ~INPUT_VEH_FLY_ROLL_LEFT_ONLY~ et ~INPUT_VEH_FLY_ROLL_RIGHT_ONLY~ pour tourner la vue.', + ['skin'] = 'changer de skin', + ['saveskin'] = 'sauvegarder skin dans un fichier', +} diff --git a/modules/skin/data/locales/pl.lua b/modules/skin/data/locales/pl.lua new file mode 100644 index 00000000..9458a7a4 --- /dev/null +++ b/modules/skin/data/locales/pl.lua @@ -0,0 +1,6 @@ +Locales['pl'] = { + ['skin_menu'] = 'menu wyglądu', + ['use_rotate_view'] = 'użyj ~INPUT_VEH_FLY_ROLL_LEFT_ONLY~ i ~INPUT_VEH_FLY_ROLL_RIGHT_ONLY~ aby obrócić ekran.', + ['skin'] = 'zmień wygląd', + ['saveskin'] = 'zapisz wygląd do pliku', +} diff --git a/modules/skin/data/locales/sv.lua b/modules/skin/data/locales/sv.lua new file mode 100644 index 00000000..02ddd5f0 --- /dev/null +++ b/modules/skin/data/locales/sv.lua @@ -0,0 +1,6 @@ +Locales['sv'] = { + ['skin_menu'] = 'skin meny', + ['use_rotate_view'] = 'använd ~INPUT_VEH_FLY_ROLL_LEFT_ONLY~ och ~INPUT_VEH_FLY_ROLL_RIGHT_ONLY~ för att rotera.', + ['skin'] = 'ändra skin', + ['saveskin'] = 'spara skin till fil', +} diff --git a/modules/skin/migrations/0.sql b/modules/skin/migrations/0.sql new file mode 100644 index 00000000..055cb184 --- /dev/null +++ b/modules/skin/migrations/0.sql @@ -0,0 +1 @@ +ALTER TABLE `users` ADD COLUMN `skin` LONGTEXT NULL DEFAULT NULL; diff --git a/modules/skin/server/events.lua b/modules/skin/server/events.lua new file mode 100644 index 00000000..ffbbeb66 --- /dev/null +++ b/modules/skin/server/events.lua @@ -0,0 +1,64 @@ +local self = ESX.Modules['skin'] + +RegisterServerEvent('esx_skin:save') +AddEventHandler('esx_skin:save', function(skin) + local xPlayer = ESX.GetPlayerFromId(source) + local defaultMaxWeight = ESX.GetConfig().MaxWeight + local backpackModifier = Config.BackpackWeight[skin.bags_1] + + if backpackModifier then + xPlayer.setMaxWeight(defaultMaxWeight + backpackModifier) + else + xPlayer.setMaxWeight(defaultMaxWeight) + end + + MySQL.Async.execute('UPDATE users SET skin = @skin WHERE identifier = @identifier', { + ['@skin'] = json.encode(skin), + ['@identifier'] = xPlayer.identifier + }) +end) + +RegisterServerEvent('esx_skin:responseSaveSkin') +AddEventHandler('esx_skin:responseSaveSkin', function(skin) + local xPlayer = ESX.GetPlayerFromId(source) + + if xPlayer.getGroup() == 'admin' then + local file = io.open('resources/[esx]/esx_skin/skins.txt', "a") + + file:write(json.encode(skin) .. "\n\n") + file:flush() + file:close() + else + print(('esx_skin: %s attempted saving skin to file'):format(xPlayer.getIdentifier())) + end +end) + +ESX.RegisterServerCallback('esx_skin:getPlayerSkin', function(source, cb) + local xPlayer = ESX.GetPlayerFromId(source) + + MySQL.Async.fetchAll('SELECT skin FROM users WHERE identifier = @identifier', { + ['@identifier'] = xPlayer.identifier + }, function(users) + local user, skin = users[1] + + local jobSkin = { + skin_male = xPlayer.job.skin_male, + skin_female = xPlayer.job.skin_female + } + + if user.skin then + skin = json.decode(user.skin) + end + + cb(skin, jobSkin) + end) +end) + +ESX.RegisterCommand('skin', 'admin', function(xPlayer, args, showError) + xPlayer.triggerEvent('esx_skin:openSaveableMenu') +end, false, {help = _U('skin')}) + +ESX.RegisterCommand('skinsave', 'admin', function(xPlayer, args, showError) + xPlayer.triggerEvent('esx_skin:requestSaveSkin') +end, false, {help = _U('saveskin')}) + diff --git a/modules/skin/server/main.lua b/modules/skin/server/main.lua new file mode 100644 index 00000000..3d7a6515 --- /dev/null +++ b/modules/skin/server/main.lua @@ -0,0 +1,2 @@ +local self = ESX.Modules['skin'] + diff --git a/modules/skin/server/module.lua b/modules/skin/server/module.lua new file mode 100644 index 00000000..7102c411 --- /dev/null +++ b/modules/skin/server/module.lua @@ -0,0 +1,3 @@ +ESX.Modules['skin'] = {} +local self = ESX.Modules['skin'] + diff --git a/modules/society/client/events.lua b/modules/society/client/events.lua new file mode 100644 index 00000000..0e0b581e --- /dev/null +++ b/modules/society/client/events.lua @@ -0,0 +1,21 @@ +local self = ESX.Modules['society'] + +AddEventHandler('esx:migrations:ensure', function(register) + register('society') +end) + +RegisterNetEvent('esx_addonaccount:setMoney') +AddEventHandler('esx_addonaccount:setMoney', function(society, money) + if ESX.PlayerData.job and ESX.PlayerData.job.grade_name == 'boss' and 'society_' .. ESX.PlayerData.job.name == society then + self.UpdateSocietyMoneyHUDElement(money) + end +end) + +RegisterNetEvent('esx:setJob') +AddEventHandler('esx:setJob', function(job) + self.RefreshBossHUD() +end) + +AddEventHandler('esx_society:openBossMenu', function(society, close, options) + self.OpenBossMenu(society, close, options) +end) diff --git a/modules/society/client/main.lua b/modules/society/client/main.lua new file mode 100644 index 00000000..c61b3a56 --- /dev/null +++ b/modules/society/client/main.lua @@ -0,0 +1,13 @@ +local self = ESX.Modules['society'] + +self.Init() + +Citizen.CreateThread(function() + + while ESX.PlayerData.job == nil do + Citizen.Wait(10) + end + + self.RefreshBossHUD() + +end) diff --git a/modules/society/client/module.lua b/modules/society/client/module.lua new file mode 100644 index 00000000..7e33dd49 --- /dev/null +++ b/modules/society/client/module.lua @@ -0,0 +1,345 @@ +ESX.Modules['society'] = {} +local self = ESX.Modules['society'] + +self.Config = ESX.EvalFile(GetCurrentResourceName(), 'modules/society/data/config.lua', { + vector3 = vector3 +})['Config'] + +self.Init = function() + + local translations = ESX.EvalFile(GetCurrentResourceName(), 'modules/society/data/locales/' .. Config.Locale .. '.lua')['Translations'] + LoadLocale('society', Config.Locale, translations) + +end + +self.base64MoneyIcon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFoAAABaCAMAAAAPdrEwAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAMAUExURQAAACmvPCmwPCuwPiywPi2wPy6xQC6xQS+yQTCxQTCyQTCyQjGyQzKyRDOzRTSzRTSzRjW0RjW0Rza0SDe0STi0STm1Sjq1Szq2Szu2TDy2TDy2TT22Tj63Tz+3UEC4UEG4UUG4UkK4U0O5VES5VEW6VUa6Vke6V0i7WEm7WUu8Wku8W0y8W028XE29XU++XlC9X1C+X1G+YFK+YVO/YlW/ZFXAZFfAZVfAZljBZlnBZ1rBaVvCaVvCalzDal7CbF/DbV/EbWHEbmLEb2LEcGTFcWfGdGjHdWrHd2vId2vIeGzIeW3JenHKfXLKfnPLf3TLgHXLgXXMgHXMgXbMgnjNg3nMhHrNhnrOhnzOiH7PiYLQjYPRjoTRjoTRj4XRkIXSkIfSkojSk4rUlYzUlo7VmJDWmpHWm5LXnJTXnZTXnpXYnpbYn5nZopzapJ3bpZ7bpp7bp6DcqKLcqqPcq6Tcq6TdrKberqjfr6jfsKnfsavgs6zgs6zgtK7hta/htrDit7LiuLLiubPjurTjurXju7bkvLbkvbnlv7rlwLrmwLzmwr3nw77nxMDnxcLox8PpyMTpycXpysXqysbqy8fqzMnrzcrrz8vsz8vs0Mzs0c3t0tHu1dHu1tPv19Tv2NXv2dXw2dbw2tfw29jw3Nnx3drx3t7z4d/z4uD04+H05OP15eP15uT15uT15+X16OX26Of26en36+r37Ov37er47Ov47ez47e347u347+758PD58fD68fD68vL69PT79fX79vb79/b89vb89/f8+Pj9+fn9+vr9+/v++/v+/Pz+/P3+/f3+/v7//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALfZHJgAAAEAdFJOU////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////wBT9wclAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAAGHRFWHRTb2Z0d2FyZQBwYWludC5uZXQgNC4xLjb9TgnoAAAGdUlEQVRoQ7WZ93sURRiAFZO76O1eklt215OgBwqJgmgCRhAhRrAAiogBK4gBJEFEgWAEYiGIYEGp1kjXQECkiF0OWyjJ/k34zcy3db7Zu8fHe3/KTXnzPbPT56orRTEYAhMLUFgNKkeiGH8BNaV1KWSPVXveU9s3LW9pvhdoblm+afspTHZi5Wo1BnzpaOfcnFmWSBv29YBtpBNlZm5u59FLPDsmdJUaxQeXNugZw5IwMnrD0oO8iFKuUHNx/+bGZLWNMgm7Otm4uZ/LsVIEUs3F+Y5a3USNAlOv7cgr5ZSamQffyOnKgH1sPdfFi2PVILKaReEcmKwVIWbY2uQDvApW95HULIb+Ng0rFoXWxppcCjyqZuZjk4oNWWBrk44R7oiamXdkC3w9GTO7Q3aH1cy8OkWFnHtitstM4j/bqdWSO6Rm5nYdS4e5FbKQwxlMC6G3Q1bIHVQz8+IKLBphJLdydlRjWpiKxZAXdAfUPOak4gOO4FZOdxWmhbGTkbh9NTOvoVsDuGmAaxkdlZgWRV8Dub7bV0PyzhQWkqk5z7WMdjpqILUTslEXUEPQJ7Lq7pz9WXiBZ4iZUGBnTwTCdtVgvjgxpj+b3vTvTEteq5KbEy/6blcNVZapR3cmdc9Z4QV6Pnxzdr1GF9aWQQFUohqCPqQc3ZV17X3C6tP31kyL+Jy2dsgLW6jBPDgVc6OkR3XxCV/i9IqRRFedylxc6qk30L3DNF7ikz1Jft0oLOaT2hBSg/l8jmwOo24/amimYzkfOwfdlLtdNT1YjIaf0EFzgGhuNnB8teNcqCWDvs3vzSSLqC9Ze0F0EqaGoDeSQWt7UaHgXA0WDKFvFGEzNRS6mxot6VZhUNJNdm7zbsgSagj6UAKTQ5hnhEHJffRISIi+LdRLyNn9cTSoOExP3FZmiat2nIF66t9Xb0GFz8Df+AenVTEF2vUwAzM1BN1LfsTkcVS4bJs+fnRj84z3joqf+eFYUELvZWFzdSfZHjeeEwqXl6/myWaZ0bSVDf3uJP9NkOn01HPISXIMTJAB/hiB6WBPN+xxBpvxl4wxR6ihll8pSL1QupwLLhR22YL9ysVGrKRcfZJeAu4QSo+1yWA3rla2NGCeZGpoj13lmBJm1F/odDm+fv68phorU0l+miDlu6BFmLqbHDCWdhqVQQb/zPesW1inqxdoRqIb1SvSmBJG+wR1BF+/NkmLWUnTK1DdQq+i5gvoIbn85QPqtdRoQfU0TIgy4h/UKOi+gZ5DgGmonqIoob2CDhXHblE0ij2lgNo2YMcSy7fkhF2E2rIm/IoOFV/Q+78i1JkJheJ+jOzjnlr1GRk18swa4iC9t3U/o6LzCVLTP4dlVM14LBfC63yKIYOYeuMqcRoneZFqEW/IKAY6Q8xumla3YP2+PNnNt1Jqb6Arpifg5kXuHjNTaVi1s5/eJs0r+6gFUkxP6kkV2mKBs8UMZSYMdmAJcpxqTTGpqpcCEO1znCMTwlPFNbuF0uUs1UVwKYAWoRcwGDHsbNS/algwu+J1bvToI9TuAgZqetm1roPVk/FDazbhjaryr0SiSw/R1oFll94sWNnfsLrzS1eTkawyjKrE6HcwyeVtIix3s6Dc4ljzsTbnx74P1na83+uf8ZBn5cb0tjgsbHJjlvoMa8dxWT4XBDZmoCa3k7eH9yE0u4l9jr+dZN2P2ASnVorK8TwoVwxsglnYxNY9852oHMseogMEt+4QtnzgGDoTa8eRH4ulA4QOHCxs6ZhUBdNAIS7NIjp1+JgEaulwZzwsnXCjDDxPzB+Rwx1zS0fSoVZb/Mp4/lFqQxk5kjI1cZDWa179HTUEe8eRW9XoQZq5qeN/5fCFigWmd14VNRXLx3/et8lLC7N8XNtH0VP6mXfvV+z4iEsLFrbqqkXXho2Z8dynwgo8NdZK0SXpqxbmjrsgquhCsXrhgOYgL4h4k8Rca5VvFV7gTkySUVxrsbBjLuOGfMy1jCZFa6gv47hbeYU4ZA/XMmbRy13MFaJwqy4+Ez1cy3iSVsddfGLcdJvo33Ato5VcSeOva4WbvmQ2v+daxkrqfqXQJbNwk1fj9kOPuNwlZxdxNS7c5IW+MdRFMhd3oS/cpXmG4EOnRI8nAAt8sKsETz4AK1yahyqAy0vxvAaU7lEQcJ8yj9BPmUf+81MmR8iB//kBllOyZ2NOqR67XUAVABNjuXLlX2rCcoFjOcGoAAAAAElFTkSuQmCC' + +self.RefreshBossHUD = function() + + self.DisableSocietyMoneyHUDElement() + + if ESX.PlayerData.job.grade_name == 'boss' then + self.EnableSocietyMoneyHUDElement() + + ESX.TriggerServerCallback('esx_society:getSocietyMoney', function(money) + self.UpdateSocietyMoneyHUDElement(money) + end, ESX.PlayerData.job.name) + end + +end + +self.EnableSocietyMoneyHUDElement = function() + local societyMoneyHUDElementTpl = '
 {{money}}
' + + if ESX.GetConfig().EnableHud then + ESX.UI.HUD.RegisterElement('society_money', 3, 0, societyMoneyHUDElementTpl, { + money = 0 + }) + end + + TriggerEvent('esx_society:toggleSocietyHud', true) +end + +self.DisableSocietyMoneyHUDElement = function() + if ESX.GetConfig().EnableHud then + ESX.UI.HUD.RemoveElement('society_money') + end + + TriggerEvent('esx_society:toggleSocietyHud', false) +end + +self.UpdateSocietyMoneyHUDElement = function(money) + if ESX.GetConfig().EnableHud then + ESX.UI.HUD.UpdateElement('society_money', { + money = ESX.Math.GroupDigits(money) + }) + end + + TriggerEvent('esx_society:setSocietyMoney', money) +end + +self.OpenBossMenu = function(society, close, options) + options = options or {} + local elements = {} + + ESX.TriggerServerCallback('esx_society:isBoss', function(isBoss) + if isBoss then + local defaultOptions = { + withdraw = true, + deposit = true, + wash = true, + employees = true, + grades = true + } + + for k,v in pairs(defaultOptions) do + if options[k] == nil then + options[k] = v + end + end + + if options.withdraw then + table.insert(elements, {label = _U('society:withdraw_society_money'), value = 'withdraw_society_money'}) + end + + if options.deposit then + table.insert(elements, {label = _U('society:deposit_society_money'), value = 'deposit_money'}) + end + + if options.wash then + table.insert(elements, {label = _U('society:wash_money'), value = 'wash_money'}) + end + + if options.employees then + table.insert(elements, {label = _U('society:employee_management'), value = 'manage_employees'}) + end + + if options.grades then + table.insert(elements, {label = _U('society:salary_management'), value = 'manage_grades'}) + end + + ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'boss_actions_' .. society, { + title = _U('society:boss_menu'), + align = 'top-left', + elements = elements + }, function(data, menu) + if data.current.value == 'withdraw_society_money' then + ESX.UI.Menu.Open('dialog', GetCurrentResourceName(), 'withdraw_society_money_amount_' .. society, { + title = _U('society:withdraw_amount') + }, function(data2, menu2) + local amount = tonumber(data2.value) + + if amount == nil then + ESX.ShowNotification(_U('society:invalid_amount')) + else + menu2.close() + TriggerServerEvent('esx_society:withdrawMoney', society, amount) + end + end, function(data2, menu2) + menu2.close() + end) + elseif data.current.value == 'deposit_money' then + ESX.UI.Menu.Open('dialog', GetCurrentResourceName(), 'deposit_money_amount_' .. society, { + title = _U('society:deposit_amount') + }, function(data2, menu2) + local amount = tonumber(data2.value) + + if amount == nil then + ESX.ShowNotification(_U('society:invalid_amount')) + else + menu2.close() + TriggerServerEvent('esx_society:depositMoney', society, amount) + end + end, function(data2, menu2) + menu2.close() + end) + elseif data.current.value == 'wash_money' then + ESX.UI.Menu.Open('dialog', GetCurrentResourceName(), 'wash_money_amount_' .. society, { + title = _U('society:wash_money_amount') + }, function(data2, menu2) + local amount = tonumber(data2.value) + + if amount == nil then + ESX.ShowNotification(_U('society:invalid_amount')) + else + menu2.close() + TriggerServerEvent('esx_society:washMoney', society, amount) + end + end, function(data2, menu2) + menu2.close() + end) + elseif data.current.value == 'manage_employees' then + OpenManageEmployeesMenu(society) + elseif data.current.value == 'manage_grades' then + OpenManageGradesMenu(society) + end + end, function(data, menu) + if close then + close(data, menu) + end + end) + end + end, society) +end + +self.OpenManageEmployeesMenu = function(society) + ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'manage_employees_' .. society, { + title = _U('society:employee_management'), + align = 'top-left', + elements = { + {label = _U('society:employee_list'), value = 'employee_list'}, + {label = _U('society:recruit'), value = 'recruit'} + }}, function(data, menu) + if data.current.value == 'employee_list' then + OpenEmployeeList(society) + elseif data.current.value == 'recruit' then + OpenRecruitMenu(society) + end + end, function(data, menu) + menu.close() + end) +end + +self.OpenEmployeeList = function(society) + ESX.TriggerServerCallback('esx_society:getEmployees', function(employees) + + local elements = { + head = {_U('society:employee'), _U('society:grade'), _U('society:actions')}, + rows = {} + } + + for i=1, #employees, 1 do + local gradeLabel = (employees[i].job.grade_label == '' and employees[i].job.label or employees[i].job.grade_label) + + table.insert(elements.rows, { + data = employees[i], + cols = { + employees[i].name, + gradeLabel, + '{{' .. _U('society:promote') .. '|promote}} {{' .. _U('society:fire') .. '|fire}}' + } + }) + end + + ESX.UI.Menu.Open('list', GetCurrentResourceName(), 'employee_list_' .. society, elements, function(data, menu) + local employee = data.data + + if data.value == 'promote' then + menu.close() + OpenPromoteMenu(society, employee) + elseif data.value == 'fire' then + ESX.ShowNotification(_U('society:you_have_fired', employee.name)) + + ESX.TriggerServerCallback('esx_society:setJob', function() + OpenEmployeeList(society) + end, employee.identifier, 'unemployed', 0, 'fire') + end + end, function(data, menu) + menu.close() + OpenManageEmployeesMenu(society) + end) + end, society) +end + +self.OpenRecruitMenu = function(society) + ESX.TriggerServerCallback('esx_society:getOnlinePlayers', function(players) + local elements = {} + + for i=1, #players, 1 do + if players[i].job.name ~= society then + table.insert(elements, { + label = players[i].name, + value = players[i].source, + name = players[i].name, + identifier = players[i].identifier + }) + end + end + + ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'recruit_' .. society, { + title = _U('society:recruiting'), + align = 'top-left', + elements = elements + }, function(data, menu) + ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'recruit_confirm_' .. society, { + title = _U('society:do_you_want_to_recruit', data.current.name), + align = 'top-left', + elements = { + {label = _U('society:no'), value = 'no'}, + {label = _U('society:yes'), value = 'yes'} + }}, function(data2, menu2) + menu2.close() + + if data2.current.value == 'yes' then + ESX.ShowNotification(_U('society:you_have_hired', data.current.name)) + + ESX.TriggerServerCallback('esx_society:setJob', function() + OpenRecruitMenu(society) + end, data.current.identifier, society, 0, 'hire') + end + end, function(data2, menu2) + menu2.close() + end) + end, function(data, menu) + menu.close() + end) + end) +end + +self.OpenPromoteMenu = function(society, employee) + ESX.TriggerServerCallback('esx_society:getJob', function(job) + local elements = {} + + for i=1, #job.grades, 1 do + local gradeLabel = (job.grades[i].label == '' and job.label or job.grades[i].label) + + table.insert(elements, { + label = gradeLabel, + value = job.grades[i].grade, + selected = (employee.job.grade == job.grades[i].grade) + }) + end + + ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'promote_employee_' .. society, { + title = _U('society:promote_employee', employee.name), + align = 'top-left', + elements = elements + }, function(data, menu) + menu.close() + ESX.ShowNotification(_U('society:you_have_promoted', employee.name, data.current.label)) + + ESX.TriggerServerCallback('esx_society:setJob', function() + OpenEmployeeList(society) + end, employee.identifier, society, data.current.value, 'promote') + end, function(data, menu) + menu.close() + OpenEmployeeList(society) + end) + end, society) +end + +self.OpenManageGradesMenu = function(society) + ESX.TriggerServerCallback('esx_society:getJob', function(job) + local elements = {} + + for i=1, #job.grades, 1 do + local gradeLabel = (job.grades[i].label == '' and job.label or job.grades[i].label) + + table.insert(elements, { + label = ('%s - %s'):format(gradeLabel, _U('society:money_generic', ESX.Math.GroupDigits(job.grades[i].salary))), + value = job.grades[i].grade + }) + end + + ESX.UI.Menu.Open('default', GetCurrentResourceName(), 'manage_grades_' .. society, { + title = _U('society:salary_management'), + align = 'top-left', + elements = elements + }, function(data, menu) + ESX.UI.Menu.Open('dialog', GetCurrentResourceName(), 'manage_grades_amount_' .. society, { + title = _U('society:salary_amount') + }, function(data2, menu2) + + local amount = tonumber(data2.value) + + if amount == nil then + ESX.ShowNotification(_U('society:invalid_amount')) + elseif amount > Config.MaxSalary then + ESX.ShowNotification(_U('society:invalid_amount_max')) + else + menu2.close() + + ESX.TriggerServerCallback('esx_society:setJobSalary', function() + OpenManageGradesMenu(society) + end, society, data.current.value, amount) + end + end, function(data2, menu2) + menu2.close() + end) + end, function(data, menu) + menu.close() + end) + end, society) +end + + diff --git a/modules/society/data/config.lua b/modules/society/data/config.lua new file mode 100644 index 00000000..cb4eb304 --- /dev/null +++ b/modules/society/data/config.lua @@ -0,0 +1,5 @@ +Config = {} + +Config.Locale = 'fr' +Config.EnableESXIdentity = false +Config.MaxSalary = 3500 diff --git a/modules/society/data/locales/br.lua b/modules/society/data/locales/br.lua new file mode 100644 index 00000000..e55d2043 --- /dev/null +++ b/modules/society/data/locales/br.lua @@ -0,0 +1,38 @@ +Translations = { + ['actions'] = 'ações', + ['boss_menu'] = 'menu chefe', + ['money_generic'] = '$%s', + ['deposit_amount'] = 'valor do depósito', + ['deposit_society_money'] = 'depositar dinheiro da sociedade', + ['do_you_want_to_recruit'] = 'você quer recrutar %s?', + ['employee'] = 'empregado', + ['employee_list'] = 'lista de empregados', + ['employee_management'] = 'gestão de funcionários', + ['fire'] = 'fogo', + ['grade'] = 'grau(escolaridade)', + ['have_deposited'] = 'você depositou ~r~$%s~s~', + ['have_withdrawn'] = 'você retirou ~g~$%s~s~', + ['invalid_amount'] = 'montante inválido', + ['invalid_amount_max'] = 'esse salário não é permitido', + ['no'] = 'não', + ['promote'] = 'promover', + ['promote_employee'] = 'promover %s', + ['recruit'] = 'recrutar', + ['recruiting'] = 'recrutamento', + ['salary_amount'] = 'valor do salário', + ['salary_management'] = 'gestão salarial', + ['wash_money'] = 'lavar dinheiro', + ['wash_money_amount'] = 'quantidade a lavar', + ['withdraw_amount'] = 'retirar montante', + ['withdraw_society_money'] = 'retirar dinheiro da sociedade', + ['yes'] = 'sim', + ['you_have'] = 'você tem ~g~$%s~s~ esperando lavagem de dinheiro (24h).', + ['you_have_laundered'] = 'você lavou ~g~$%s~s~ do seu dinheiro ', + ['you_have_hired'] = 'você recrutou %s', + ['you_have_been_hired'] = 'você foi contratado por %s', + ['you_have_fired'] = 'você demitiu %s', + ['you_have_been_fired'] = 'você foi demitido de %s', + ['you_have_promoted'] = 'você promoveu %s a %s', + ['you_have_been_promoted'] = 'você foi promovido ~b~promoted~s~!', +} + diff --git a/modules/society/data/locales/cs.lua b/modules/society/data/locales/cs.lua new file mode 100644 index 00000000..24b5d933 --- /dev/null +++ b/modules/society/data/locales/cs.lua @@ -0,0 +1,37 @@ +Translations = { + ['actions'] = 'akce', + ['boss_menu'] = 'akce šéfa', + ['money_generic'] = '$%s', + ['deposit_amount'] = 'množství vkladu', + ['deposit_society_money'] = 'vložit peníze do společnosti', + ['do_you_want_to_recruit'] = 'chceš najmout %s?', + ['employee'] = 'zaměstnanec', + ['employee_list'] = 'seznam zaměstnanců', + ['employee_management'] = 'správa zaměstnanců', + ['fire'] = 'vyhodit', + ['grade'] = 'povýšit', + ['have_deposited'] = 'vložil jsi ~r~$%s~s~', + ['have_withdrawn'] = 'vybral jsi ~g~$%s~s~', + ['invalid_amount'] = 'neplatná částka', + ['invalid_amount_max'] = 'tento plat není povolen', + ['no'] = 'ne', + ['promote'] = 'povýšit', + ['promote_employee'] = 'povýšit %s', + ['recruit'] = 'najmout', + ['recruiting'] = 'najímání', + ['salary_amount'] = 'váše platu', + ['salary_management'] = 'správa platů', + ['wash_money'] = 'vyprat peníze', + ['wash_money_amount'] = 'množství na vyprání', + ['withdraw_amount'] = 'výběr peněz', + ['withdraw_society_money'] = 'vybrat peníze společnosti', + ['yes'] = 'ano', + ['you_have'] = 'máš ~g~$%s~s~ čekajících na vyprání~s~ (24h).', + ['you_have_laundered'] = '~r~Vypral jsi~s~ tvé peníze: ~g~$%s~s~', + ['you_have_hired'] = 'najmul jsi %s', + ['you_have_been_hired'] = 'byl jsi najmut hráčem %s', + ['you_have_fired'] = 'vyhodil jsi %s', + ['you_have_been_fired'] = 'byl jsi vyhozen hráčem %s', + ['you_have_promoted'] = 'byl jsi povýšen hráčem %s na %s', + ['you_have_been_promoted'] = 'byl jsi ~b~povýšen~s~!', +} diff --git a/modules/society/data/locales/en.lua b/modules/society/data/locales/en.lua new file mode 100644 index 00000000..89a35cb5 --- /dev/null +++ b/modules/society/data/locales/en.lua @@ -0,0 +1,37 @@ +Translations = { + ['actions'] = 'actions', + ['boss_menu'] = 'boss menu', + ['money_generic'] = '$%s', + ['deposit_amount'] = 'deposit amount', + ['deposit_society_money'] = 'deposit society money', + ['do_you_want_to_recruit'] = 'do you want to recruit %s?', + ['employee'] = 'employee', + ['employee_list'] = 'employee list', + ['employee_management'] = 'employee management', + ['fire'] = 'fire', + ['grade'] = 'grade', + ['have_deposited'] = 'you have deposited ~r~$%s~s~', + ['have_withdrawn'] = 'you have withdrawn ~g~$%s~s~', + ['invalid_amount'] = 'invalid amount', + ['invalid_amount_max'] = 'that salary is not allowed', + ['no'] = 'no', + ['promote'] = 'promote', + ['promote_employee'] = 'promote %s', + ['recruit'] = 'recruit', + ['recruiting'] = 'recruiting', + ['salary_amount'] = 'salary amount', + ['salary_management'] = 'salary management', + ['wash_money'] = 'wash money', + ['wash_money_amount'] = 'amount to wash', + ['withdraw_amount'] = 'witdraw amount', + ['withdraw_society_money'] = 'withdraw society money', + ['yes'] = 'yes', + ['you_have'] = 'you have ~g~$%s~s~ waiting in ~y~money laundering~s~ (24h).', + ['you_have_laundered'] = 'you have ~r~laundered~s~ your money: ~g~$%s~s~', + ['you_have_hired'] = 'you have recruited %s', + ['you_have_been_hired'] = 'you have been hired by %s', + ['you_have_fired'] = 'you have fired %s', + ['you_have_been_fired'] = 'you have been fired from %s', + ['you_have_promoted'] = 'you have promoted %s as %s', + ['you_have_been_promoted'] = 'you have been ~b~promoted~s~!', +} diff --git a/modules/society/data/locales/fi.lua b/modules/society/data/locales/fi.lua new file mode 100644 index 00000000..4937f3ac --- /dev/null +++ b/modules/society/data/locales/fi.lua @@ -0,0 +1,37 @@ +Translations = { + ['actions'] = 'toiminnot', + ['boss_menu'] = 'boss menu', + ['money_generic'] = '€%s', + ['deposit_amount'] = 'talletus summa', + ['deposit_society_money'] = 'talleta yritykselle rahaa', + ['do_you_want_to_recruit'] = 'do you want to recruit %s?', + ['employee'] = 'työntekijä', + ['employee_list'] = 'työntekijä lista', + ['employee_management'] = 'työntekijöiden hallinta', + ['fire'] = 'anna potkut', + ['grade'] = 'taso', + ['have_deposited'] = 'sinä talletit ~r~€%s~s~', + ['have_withdrawn'] = 'sinä nostit ~g~€%s~s~', + ['invalid_amount'] = 'virheellinen summa', + ['invalid_amount_max'] = 'that salary is not allowed', + ['no'] = 'ei', + ['promote'] = 'ylennä', + ['promote_employee'] = 'ylennä henkilö %s', + ['recruit'] = 'rekrytoi', + ['recruiting'] = 'rekrytointi', + ['salary_amount'] = 'palkan määrä', + ['salary_management'] = 'palkan hallinta', + ['wash_money'] = 'pese rahaa', + ['wash_money_amount'] = 'pestävä määrä', + ['withdraw_amount'] = 'nostettava määrä', + ['withdraw_society_money'] = 'nosta yrityksen rahoja', + ['yes'] = 'kyllä', + ['you_have'] = 'you have ~g~€%s~s~ waiting in ~y~money laundering~s~ (24h).', + ['you_have_laundered'] = 'olet ~r~pessyt~s~ rahojasi: ~g~€%s~s~', + ['you_have_hired'] = 'sinä palkkasit henkilön %s', + ['you_have_been_hired'] = 'sinut on palkattu pelaajan %s toimesta', + ['you_have_fired'] = 'sinä annoit potkut pelaajalle %s', + ['you_have_been_fired'] = 'sinulle annettiin potkut työstä %s', + ['you_have_promoted'] = 'sinä ylensit henkilön %s arvolle %s', + ['you_have_been_promoted'] = 'sinut ylennettiin', +} diff --git a/modules/society/data/locales/fr.lua b/modules/society/data/locales/fr.lua new file mode 100644 index 00000000..4b57ffbb --- /dev/null +++ b/modules/society/data/locales/fr.lua @@ -0,0 +1,37 @@ +Translations = { + ['actions'] = 'actions', + ['boss_menu'] = 'patron', + ['money_generic'] = '$%s', + ['deposit_amount'] = 'montant du dépôt', + ['deposit_society_money'] = 'déposer argent société', + ['do_you_want_to_recruit'] = 'Voulez-vous recruter %s?', + ['employee'] = 'employé', + ['employee_list'] = 'liste des employés', + ['employee_management'] = 'gestion employés', + ['fire'] = 'licencier', + ['grade'] = 'grade', + ['have_deposited'] = 'vous avez déposé ~r~$%s~s~', + ['have_withdrawn'] = 'vous avez retiré ~g~$%s~s~', + ['invalid_amount'] = 'montant invalide', + ['invalid_amount_max'] = 'that salary is not allowed', + ['no'] = 'non', + ['promote'] = 'promouvoir', + ['promote_employee'] = 'promouvoir %s', + ['recruit'] = 'recruter', + ['recruiting'] = 'recrutement', + ['salary_amount'] = 'montant du salaire', + ['salary_management'] = 'gestion salaires', + ['wash_money'] = 'blanchir argent', + ['wash_money_amount'] = 'montant à blanchir', + ['withdraw_amount'] = 'montant du retrait', + ['withdraw_society_money'] = 'retirer argent société', + ['yes'] = 'oui', + ['you_have'] = 'vous avez ~g~$%s~s~ en attente de ~r~blanchiement~s~ (24h).', + ['you_have_laundered'] = 'vous avez ~r~blanchi~s~ votre argent : ~g~$%s~s~', + ['you_have_hired'] = 'Vous avez recruté %s', + ['you_have_been_hired'] = 'Vous avez été recruté dans la société %s', + ['you_have_fired'] = 'Vous avez viré %s', + ['you_have_been_fired'] = 'Vous avez été viré de la société %s', + ['you_have_promoted'] = 'Vous avez promu %s en tant que %s', + ['you_have_been_promoted'] = 'Vous avez été promu', +} diff --git a/modules/society/data/locales/nl.lua b/modules/society/data/locales/nl.lua new file mode 100644 index 00000000..e3bc8922 --- /dev/null +++ b/modules/society/data/locales/nl.lua @@ -0,0 +1,38 @@ +Translations = { + ['actions'] = 'acties', + ['boss_menu'] = 'baas menu', + ['money_generic'] = '€%s', + ['deposit_amount'] = 'stort hoeveelheid', + ['deposit_society_money'] = 'stort maatschappij geld', + ['do_you_want_to_recruit'] = 'Wil je %s aannemen?', + ['employee'] = 'werknemer', + ['employee_list'] = 'werknemer lijst', + ['employee_management'] = 'werknemer beheer', + ['fire'] = 'ontslaan', + ['grade'] = 'schaal', + ['have_deposited'] = 'je hebt gestort ~r~$%s~s~', + ['have_withdrawn'] = 'je hebt opgenomen ~g~$%s~s~', + ['invalid_amount'] = 'ongeldige hoeveelheid', + ['invalid_amount_max'] = 'dit salaris is niet toegestaan', + ['no'] = 'nee', + ['promote'] = 'promoveer', + ['promote_employee'] = 'promoveer %s', + ['recruit'] = 'werv', + ['recruiting'] = 'werven', + ['salary_amount'] = 'salaris hoeveelheid', + ['salary_management'] = 'salaris beheer', + ['wash_money'] = 'witwas geld', + ['wash_money_amount'] = 'hoeveelheid om wit te wassen', + ['withdraw_amount'] = 'opneem hoeveelheid', + ['withdraw_society_money'] = 'neem maatschappelijk geld op', + ['yes'] = 'ja', + ['you_have'] = 'je hebt ~g~€%s~s~ staan in ~y~geld witwassen~s~ (24h).', + ['you_have_laundered'] = 'je hebt je geld: ~g~€%s~s~ ~r~witgewassen~s~ ', + ['you_have_hired'] = 'je bent gevraagt %s', + ['you_have_been_hired'] = 'je bent aangenomen door %s', + ['you_have_fired'] = 'je bent ontslagen %s', + ['you_have_been_fired'] = 'je bent ondslagen van %s', + ['you_have_promoted'] = 'je hebt gepromoveerd %s naar %s', + ['you_have_been_promoted'] = 'je bent ~b~gepromoveerd~s~!', +} + diff --git a/modules/society/data/locales/pl.lua b/modules/society/data/locales/pl.lua new file mode 100644 index 00000000..de917bc2 --- /dev/null +++ b/modules/society/data/locales/pl.lua @@ -0,0 +1,37 @@ +Translations = { + ['actions'] = 'akcje', + ['boss_menu'] = 'boss menu', + ['money_generic'] = '$%s', + ['deposit_amount'] = 'ilość depozytu', + ['deposit_society_money'] = 'zdeponuj społeczne pieniądze', + ['do_you_want_to_recruit'] = 'czy chcesz zrekrutować %s?', + ['employee'] = 'pracownik', + ['employee_list'] = 'lista pracowników', + ['employee_management'] = 'zarządzanie pracownikami', + ['fire'] = 'zwolnij', + ['grade'] = 'stopień', + ['have_deposited'] = 'zdeponowałeś ~r~$%s~s~', + ['have_withdrawn'] = 'wypłaciłeś ~g~$%s~s~', + ['invalid_amount'] = 'nieprawidłowa wartość', + ['invalid_amount_max'] = 'that salary is not allowed', + ['no'] = 'nie', + ['promote'] = 'awansuj', + ['promote_employee'] = 'awansuj %s', + ['recruit'] = 'zrekrutuj', + ['recruiting'] = 'rekrutacja', + ['salary_amount'] = 'wartość wynagrodzenia', + ['salary_management'] = 'zarządzaj wynagrodzeniem', + ['wash_money'] = 'wypierz pieniądze', + ['wash_money_amount'] = 'wartość do wyprania', + ['withdraw_amount'] = 'wartość wypłaty', + ['withdraw_society_money'] = 'wypłać społeczne pieniądze', + ['yes'] = 'tak', + ['you_have'] = 'masz ~g~%s$~s~ czekanie ~y~w pralni pieniędzmi~s~ (24h).', + ['you_have_laundered'] = '~r~Wyprałeś~s~ twoje pieniądze: ~g~$%s~s~', + ['you_have_hired'] = 'zatrudniłeś %s', + ['you_have_been_hired'] = 'zostałeś zatrudniony przez %s', + ['you_have_fired'] = 'zwolniłeś %s', + ['you_have_been_fired'] = 'zostałeś zwolniony %s', + ['you_have_promoted'] = 'awansowałeś %s na %s', + ['you_have_been_promoted'] = 'zostałeś awansowany', +} diff --git a/modules/society/data/locales/sv.lua b/modules/society/data/locales/sv.lua new file mode 100644 index 00000000..9c73628e --- /dev/null +++ b/modules/society/data/locales/sv.lua @@ -0,0 +1,37 @@ +Translations= { + ['actions'] = 'handlingar', + ['boss_menu'] = 'chefmeny', + ['money_generic'] = '%s SEK', + ['deposit_amount'] = 'insättningsbelopp', + ['deposit_society_money'] = 'insätt samhälls pengar', + ['do_you_want_to_recruit'] = 'vill du anställa %s?', + ['employee'] = 'anställd', + ['employee_list'] = 'anställningslista', + ['employee_management'] = 'medarbetarhantering', + ['fire'] = 'sparka', + ['grade'] = 'grade', + ['have_deposited'] = 'du har satt in ~r~%s SEK~s~', + ['have_withdrawn'] = 'du har tagit ut ~g~%s SEK~s~', + ['invalid_amount'] = 'ogiltigt belopp', + ['invalid_amount_max'] = 'den lönen är inte tillåten', + ['no'] = 'nej', + ['promote'] = 'befodra', + ['promote_employee'] = 'befodra %s', + ['recruit'] = 'rekrytera', + ['recruiting'] = 'rekrytering', + ['salary_amount'] = 'lön belopp', + ['salary_management'] = 'lönhantering', + ['wash_money'] = 'tvätta pengar', + ['wash_money_amount'] = 'belopp att tvätta', + ['withdraw_amount'] = 'Ta ut summan', + ['withdraw_society_money'] = 'ta ut samhällspengar', + ['yes'] = 'ja', + ['you_have'] = 'du har ~g~%s SEK~s~ som väntar på ~y~pengartvätt~s~ (24h).', + ['you_have_laundered'] = 'du har ~r~tvättat~s~ dina pengar: ~g~%s SEK~s~', + ['you_have_hired'] = 'du har anställt ~y~%s~s~', + ['you_have_been_hired'] = 'du har blivit anställd utav %s', + ['you_have_fired'] = 'du har ~r~sparkat~s~ ~y~%s~s~', + ['you_have_been_fired'] = 'du har blivit ~r~sparkad~s~ utav ~y~%s~s~', + ['you_have_promoted'] = 'du har befodrat ~y~%s~s~ till ~b~%s~s~', + ['you_have_been_promoted'] = 'du har blivit ~b~befodrad~s~', +} diff --git a/modules/society/migrations/0.sql b/modules/society/migrations/0.sql new file mode 100644 index 00000000..81bca1b0 --- /dev/null +++ b/modules/society/migrations/0.sql @@ -0,0 +1,10 @@ +USE `es_extended`; + +CREATE TABLE IF NOT EXISTS `society_moneywash` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `identifier` varchar(60) NOT NULL, + `society` varchar(60) NOT NULL, + `amount` int(11) NOT NULL, + + PRIMARY KEY (`id`) +); diff --git a/modules/society/server/events.lua b/modules/society/server/events.lua new file mode 100644 index 00000000..3b2c4880 --- /dev/null +++ b/modules/society/server/events.lua @@ -0,0 +1,325 @@ +local self = ESX.Modules['society'] + +AddEventHandler('esx:migrations:ensure', function(register) + register('society') +end) + +AddEventHandler('esx_society:registerSociety', function(name, label, account, datastore, inventory, data) + + local found = false + + local society = { + name = name, + label = label, + account = account, + datastore = datastore, + inventory = inventory, + data = data + } + + for i=1, #self.RegisteredSocieties, 1 do + if self.RegisteredSocieties[i].name == name then + found, self.RegisteredSocieties[i] = true, society + break + end + end + + if not found then + table.insert(self.RegisteredSocieties, society) + end +end) + +AddEventHandler('esx_society:getSocieties', function(cb) + cb(self.RegisteredSocieties) +end) + +AddEventHandler('esx_society:getSociety', function(name, cb) + cb(self.GetSociety(name)) +end) + +RegisterServerEvent('esx_society:withdrawMoney') +AddEventHandler('esx_society:withdrawMoney', function(societyName, amount) + local xPlayer = ESX.GetPlayerFromId(source) + local society = self.GetSociety(societyName) + amount = ESX.Math.Round(tonumber(amount)) + + if xPlayer.job.name == society.name then + TriggerEvent('esx_addonaccount:getSharedAccount', society.account, function(account) + if amount > 0 and account.money >= amount then + account.removeMoney(amount) + xPlayer.addMoney(amount) + xPlayer.showNotification(_U('society:have_withdrawn', ESX.Math.GroupDigits(amount))) + else + xPlayer.showNotification(_U('society:invalid_amount')) + end + end) + else + print(('esx_society: %s attempted to call withdrawMoney!'):format(xPlayer.identifier)) + end +end) + +RegisterServerEvent('esx_society:depositMoney') +AddEventHandler('esx_society:depositMoney', function(societyName, amount) + local xPlayer = ESX.GetPlayerFromId(source) + local society = self.GetSociety(societyName) + amount = ESX.Math.Round(tonumber(amount)) + + if xPlayer.job.name == society.name then + if amount > 0 and xPlayer.getMoney() >= amount then + TriggerEvent('esx_addonaccount:getSharedAccount', society.account, function(account) + xPlayer.removeMoney(amount) + xPlayer.showNotification(_U('society:have_deposited', ESX.Math.GroupDigits(amount))) + account.addMoney(amount) + end) + else + xPlayer.showNotification(_U('society:invalid_amount')) + end + else + print(('esx_society: %s attempted to call depositMoney!'):format(xPlayer.identifier)) + end +end) + +RegisterServerEvent('esx_society:washMoney') +AddEventHandler('esx_society:washMoney', function(society, amount) + local xPlayer = ESX.GetPlayerFromId(source) + local account = xPlayer.getAccount('black_money') + amount = ESX.Math.Round(tonumber(amount)) + + if xPlayer.job.name == society then + if amount and amount > 0 and account.money >= amount then + xPlayer.removeAccountMoney('black_money', amount) + + MySQL.Async.execute('INSERT INTO society_moneywash (identifier, society, amount) VALUES (@identifier, @society, @amount)', { + ['@identifier'] = xPlayer.identifier, + ['@society'] = society, + ['@amount'] = amount + }, function(rowsChanged) + xPlayer.showNotification(_U('society:you_have', ESX.Math.GroupDigits(amount))) + end) + else + xPlayer.showNotification(_U('society:invalid_amount')) + end + else + print(('esx_society: %s attempted to call washMoney!'):format(xPlayer.identifier)) + end +end) + +RegisterServerEvent('esx_society:putVehicleInGarage') +AddEventHandler('esx_society:putVehicleInGarage', function(societyName, vehicle) + local society = self.GetSociety(societyName) + + TriggerEvent('esx_datastore:getSharedDataStore', society.datastore, function(store) + local garage = store.get('garage') or {} + table.insert(garage, vehicle) + store.set('garage', garage) + end) +end) + +RegisterServerEvent('esx_society:removeVehicleFromGarage') +AddEventHandler('esx_society:removeVehicleFromGarage', function(societyName, vehicle) + local society = self.GetSociety(societyName) + + TriggerEvent('esx_datastore:getSharedDataStore', society.datastore, function(store) + local garage = store.get('garage') or {} + + for i=1, #garage, 1 do + if garage[i].plate == vehicle.plate then + table.remove(garage, i) + break + end + end + + store.set('garage', garage) + end) +end) + +ESX.RegisterServerCallback('esx_society:getSocietyMoney', function(source, cb, societyName) + local society = self.GetSociety(societyName) + + if society then + + TriggerEvent('esx_addonaccount:getSharedAccount', society.account, function(account) + cb(account.money) + end) + else + cb(0) + end +end) + +ESX.RegisterServerCallback('esx_society:getEmployees', function(source, cb, society) + if Config.EnableESXIdentity then + + MySQL.Async.fetchAll('SELECT firstname, lastname, identifier, job, job_grade FROM users WHERE job = @job ORDER BY job_grade DESC', { + ['@job'] = society + }, function(results) + local employees = {} + + for i=1, #results, 1 do + table.insert(employees, { + name = results[i].firstname .. ' ' .. results[i].lastname, + identifier = results[i].identifier, + job = { + name = results[i].job, + label = self.Jobs[results[i].job].label, + grade = results[i].job_grade, + grade_name = self.Jobs[results[i].job].grades[tostring(results[i].job_grade)].name, + grade_label = self.Jobs[results[i].job].grades[tostring(results[i].job_grade)].label + } + }) + end + + cb(employees) + end) + else + MySQL.Async.fetchAll('SELECT name, identifier, job, job_grade FROM users WHERE job = @job ORDER BY job_grade DESC', { + ['@job'] = society + }, function(result) + local employees = {} + + for i=1, #result, 1 do + table.insert(employees, { + name = result[i].name, + identifier = result[i].identifier, + job = { + name = result[i].job, + label = self.Jobs[result[i].job].label, + grade = result[i].job_grade, + grade_name = self.Jobs[result[i].job].grades[tostring(result[i].job_grade)].name, + grade_label = self.Jobs[result[i].job].grades[tostring(result[i].job_grade)].label + } + }) + end + + cb(employees) + end) + end +end) + +ESX.RegisterServerCallback('esx_society:getJob', function(source, cb, society) + local job = json.decode(json.encode(self.Jobs[society])) + local grades = {} + + for k,v in pairs(job.grades) do + table.insert(grades, v) + end + + table.sort(grades, function(a, b) + return a.grade < b.grade + end) + + job.grades = grades + + cb(job) +end) + +ESX.RegisterServerCallback('esx_society:setJob', function(source, cb, identifier, job, grade, type) + local xPlayer = ESX.GetPlayerFromId(source) + local isBoss = xPlayer.job.grade_name == 'boss' + + if isBoss then + local xTarget = ESX.GetPlayerFromIdentifier(identifier) + + if xTarget then + xTarget.setJob(job, grade) + + if type == 'hire' then + xTarget.showNotification(_U('society:you_have_been_hired', job)) + elseif type == 'promote' then + xTarget.showNotification(_U('society:you_have_been_promoted')) + elseif type == 'fire' then + xTarget.showNotification(_U('society:you_have_been_fired', xTarget.getJob().label)) + end + + cb() + else + MySQL.Async.execute('UPDATE users SET job = @job, job_grade = @job_grade WHERE identifier = @identifier', { + ['@job'] = job, + ['@job_grade'] = grade, + ['@identifier'] = identifier + }, function(rowsChanged) + cb() + end) + end + else + print(('esx_society: %s attempted to setJob'):format(xPlayer.identifier)) + cb() + end +end) + +ESX.RegisterServerCallback('esx_society:setJobSalary', function(source, cb, job, grade, salary) + local xPlayer = ESX.GetPlayerFromId(source) + + if xPlayer.job.name == job and xPlayer.job.grade_name == 'boss' then + if salary <= Config.MaxSalary then + MySQL.Async.execute('UPDATE job_grades SET salary = @salary WHERE job_name = @job_name AND grade = @grade', { + ['@salary'] = salary, + ['@job_name'] = job, + ['@grade'] = grade + }, function(rowsChanged) + self.Jobs[job].grades[tostring(grade)].salary = salary + local xPlayers = ESX.GetPlayers() + + for i=1, #xPlayers, 1 do + local xTarget = ESX.GetPlayerFromId(xPlayers[i]) + + if xTarget.job.name == job and xTarget.job.grade == grade then + xTarget.setJob(job, grade) + end + end + + cb() + end) + else + print(('esx_society: %s attempted to setJobSalary over config limit!'):format(xPlayer.identifier)) + cb() + end + else + print(('esx_society: %s attempted to setJobSalary'):format(xPlayer.identifier)) + cb() + end +end) + +ESX.RegisterServerCallback('esx_society:getOnlinePlayers', function(source, cb) + local xPlayers = ESX.GetPlayers() + local players = {} + + for i=1, #xPlayers, 1 do + local xPlayer = ESX.GetPlayerFromId(xPlayers[i]) + table.insert(players, { + source = xPlayer.source, + identifier = xPlayer.identifier, + name = xPlayer.name, + job = xPlayer.job + }) + end + + cb(players) +end) + +ESX.RegisterServerCallback('esx_society:getVehiclesInGarage', function(source, cb, societyName) + local society = self.GetSociety(societyName) + + TriggerEvent('esx_datastore:getSharedDataStore', society.datastore, function(store) + local garage = store.get('garage') or {} + cb(garage) + end) +end) + +ESX.RegisterServerCallback('esx_society:isBoss', function(source, cb, job) + cb(isPlayerBoss(source, job)) +end) + +MySQL.ready(function() + local result = MySQL.Sync.fetchAll('SELECT * FROM jobs', {}) + + for i=1, #result, 1 do + self.Jobs[result[i].name] = result[i] + self.Jobs[result[i].name].grades = {} + end + + local result2 = MySQL.Sync.fetchAll('SELECT * FROM job_grades', {}) + + for i=1, #result2, 1 do + self.Jobs[result2[i].job_name].grades[tostring(result2[i].grade)] = result2[i] + end +end) diff --git a/modules/society/server/main.lua b/modules/society/server/main.lua new file mode 100644 index 00000000..1b0a69e8 --- /dev/null +++ b/modules/society/server/main.lua @@ -0,0 +1,5 @@ +local self = ESX.Modules['society'] + +self.Init() + +TriggerEvent('cron:runAt', 3, 0, self.WashMoneyCRON) diff --git a/modules/society/server/module.lua b/modules/society/server/module.lua new file mode 100644 index 00000000..b178ccd1 --- /dev/null +++ b/modules/society/server/module.lua @@ -0,0 +1,56 @@ +ESX.Modules['society'] = {} +local self = ESX.Modules['society'] + +self.Jobs = {} +self.RegisteredSocieties = {} + +self.Init = function() + + local translations = ESX.EvalFile(GetCurrentResourceName(), 'modules/society/data/locales/' .. Config.Locale .. '.lua')['Translations'] + LoadLocale('society', Config.Locale, translations) + +end + +self.GetSociety = function(name) + for i=1, #self.RegisteredSocieties, 1 do + if self.RegisteredSocieties[i].name == name then + return self.RegisteredSocieties[i] + end + end +end + +self.isPlayerBoss = function(playerId, job) + local xPlayer = ESX.GetPlayerFromId(playerId) + + if xPlayer.job.name == job and xPlayer.job.grade_name == 'boss' then + return true + else + print(('esx_society: %s attempted open a society boss menu!'):format(xPlayer.identifier)) + return false + end +end + +self.WashMoneyCRON = function(d, h, m) + MySQL.Async.fetchAll('SELECT * FROM society_moneywash', {}, function(result) + for i=1, #result, 1 do + local society = self.GetSociety(result[i].society) + local xPlayer = ESX.GetPlayerFromIdentifier(result[i].identifier) + + -- add society money + TriggerEvent('esx_addonaccount:getSharedAccount', society.account, function(account) + account.addMoney(result[i].amount) + end) + + -- send notification if player is online + if xPlayer then + xPlayer.showNotification(_U('society:you_have_laundered', ESX.Math.GroupDigits(result[i].amount))) + end + + MySQL.Async.execute('DELETE FROM society_moneywash WHERE id = @id', { + ['@id'] = result[i].id + }) + end + end) +end + +