From ac954631de576b8e88aa4f3eb84a7102d23d447e Mon Sep 17 00:00:00 2001 From: bitpredator <67551273+bitpredator@users.noreply.github.com> Date: Fri, 29 Nov 2024 20:38:46 +0100 Subject: [PATCH 01/22] chore: replace esx-qalle-jail > wx_jail --- .../[esx_addons]/esx-qalle-jail/README.md | 57 -- .../[esx_addons]/esx-qalle-jail/cell.ogg | Bin 61671 -> 0 bytes .../esx-qalle-jail/client/client.lua | 388 ---------- .../esx-qalle-jail/client/utils.lua | 173 ----- .../[esx_addons]/esx-qalle-jail/config.lua | 106 --- .../esx-qalle-jail/fxmanifest.lua | 23 - .../[esx_addons]/esx-qalle-jail/jail.sql | 2 - .../esx-qalle-jail/locales/en.lua | 16 - .../esx-qalle-jail/locales/it.lua | 16 - .../esx-qalle-jail/server/server.lua | 169 ----- .../resources/[esx_addons]/wx_jail/LICENSE | 674 ++++++++++++++++++ .../resources/[esx_addons]/wx_jail/README.md | 20 + .../[esx_addons]/wx_jail/SQL/INSERT-THIS.sql | 2 + .../[esx_addons]/wx_jail/client/client.lua | 285 ++++++++ .../[esx_addons]/wx_jail/client/font.lua | 1 + .../[esx_addons]/wx_jail/client/functions.lua | 70 ++ .../[esx_addons]/wx_jail/client/jailmenu.lua | 412 +++++++++++ .../wx_jail/configs/jail_config.lua | 48 ++ .../[esx_addons]/wx_jail/fxmanifest.lua | 20 + .../[esx_addons]/wx_jail/locale/locale.lua | 26 + .../[esx_addons]/wx_jail/server/callbacks.lua | 15 + .../[esx_addons]/wx_jail/server/server.lua | 419 +++++++++++ .../[esx_addons]/wx_jail/stream/BBN.gfx | Bin 0 -> 50567 bytes .../[esx_addons]/wx_logs/ARGUMENTS.md | 28 + .../resources/[esx_addons]/wx_logs/EXAMPLE.md | 25 + .../resources/[esx_addons]/wx_logs/LICENSE | 674 ++++++++++++++++++ .../resources/[esx_addons]/wx_logs/README.md | 2 + .../wx_logs/configs/logs_config.lua | 20 + .../[esx_addons]/wx_logs/fxmanifest.lua | 8 + .../[esx_addons]/wx_logs/server/server.lua | 60 ++ 30 files changed, 2809 insertions(+), 950 deletions(-) delete mode 100644 server-data/resources/[esx_addons]/esx-qalle-jail/README.md delete mode 100644 server-data/resources/[esx_addons]/esx-qalle-jail/cell.ogg delete mode 100644 server-data/resources/[esx_addons]/esx-qalle-jail/client/client.lua delete mode 100644 server-data/resources/[esx_addons]/esx-qalle-jail/client/utils.lua delete mode 100644 server-data/resources/[esx_addons]/esx-qalle-jail/config.lua delete mode 100644 server-data/resources/[esx_addons]/esx-qalle-jail/fxmanifest.lua delete mode 100644 server-data/resources/[esx_addons]/esx-qalle-jail/jail.sql delete mode 100644 server-data/resources/[esx_addons]/esx-qalle-jail/locales/en.lua delete mode 100644 server-data/resources/[esx_addons]/esx-qalle-jail/locales/it.lua delete mode 100644 server-data/resources/[esx_addons]/esx-qalle-jail/server/server.lua create mode 100644 server-data/resources/[esx_addons]/wx_jail/LICENSE create mode 100644 server-data/resources/[esx_addons]/wx_jail/README.md create mode 100644 server-data/resources/[esx_addons]/wx_jail/SQL/INSERT-THIS.sql create mode 100644 server-data/resources/[esx_addons]/wx_jail/client/client.lua create mode 100644 server-data/resources/[esx_addons]/wx_jail/client/font.lua create mode 100644 server-data/resources/[esx_addons]/wx_jail/client/functions.lua create mode 100644 server-data/resources/[esx_addons]/wx_jail/client/jailmenu.lua create mode 100644 server-data/resources/[esx_addons]/wx_jail/configs/jail_config.lua create mode 100644 server-data/resources/[esx_addons]/wx_jail/fxmanifest.lua create mode 100644 server-data/resources/[esx_addons]/wx_jail/locale/locale.lua create mode 100644 server-data/resources/[esx_addons]/wx_jail/server/callbacks.lua create mode 100644 server-data/resources/[esx_addons]/wx_jail/server/server.lua create mode 100644 server-data/resources/[esx_addons]/wx_jail/stream/BBN.gfx create mode 100644 server-data/resources/[esx_addons]/wx_logs/ARGUMENTS.md create mode 100644 server-data/resources/[esx_addons]/wx_logs/EXAMPLE.md create mode 100644 server-data/resources/[esx_addons]/wx_logs/LICENSE create mode 100644 server-data/resources/[esx_addons]/wx_logs/README.md create mode 100644 server-data/resources/[esx_addons]/wx_logs/configs/logs_config.lua create mode 100644 server-data/resources/[esx_addons]/wx_logs/fxmanifest.lua create mode 100644 server-data/resources/[esx_addons]/wx_logs/server/server.lua diff --git a/server-data/resources/[esx_addons]/esx-qalle-jail/README.md b/server-data/resources/[esx_addons]/esx-qalle-jail/README.md deleted file mode 100644 index 9fa9db2b4..000000000 --- a/server-data/resources/[esx_addons]/esx-qalle-jail/README.md +++ /dev/null @@ -1,57 +0,0 @@ -# esx-qalle-jail - -[INFO] - -* Anti Combat Log -* Prison Work -* Working Teleporters -* Cool Interior -* Photo Taken (Mugshot) - -*This is a script made for you that wan't something out of the ordinary, this is a jail script that will add some cool side effects of getting jailed, one of em is that when you get jailed you will be teleported to the charactermugshot creation, and an sheriff ped will take a photo of you. The other thing is that you can work for a small amount of cash while in jail.* - -[EXTRAS] - -* Add To Police Menu Example -> - -```lua - elements = { - {label = TranslateCap('citizen_interaction'), value = 'citizen_interaction'}, - {label = TranslateCap('vehicle_interaction'), value = 'vehicle_interaction'}, - {label = TranslateCap('object_spawner'), value = 'object_spawner'}, - {label = "Jail Menu", value = 'jail_menu'} -- You add this line - } - }, function(data, menu) - - --You add this - - if data.current.value == 'jail_menu' then - TriggerEvent("esx-qalle-jail:openJailMenu") - end - - --Above This - if data.current.value == 'citizen_interaction' then -``` - -[USAGE] - -* `/jail ID jailTime(minutes) "description"` command (only players who have the job "police") -* `/unjail ID` to unjail a player (only players who have the job "police") -* `/jailmenu` quick command if you dont want the menu. (only players who have the job "police") - -[INSTALLATION] - -1) CD in your resources/[esx] folder - -2) Import ``jail.sql`` into your database - -3) Download ``prison-map-addon`` from https://github.com/qalle-fivem/prison-map-addon - -4) Add this in your server.cfg : -``start esx-qalle-jail`` - -[SCREENSHOTS] - -[Jail Menu](https://gyazo.com/7f46b8a80c59e02c4af7a96de34c9e6c) - -[UnJail Menu](https://gyazo.com/aa065a6c748c0d2850d3d38d33eb666f) \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/esx-qalle-jail/cell.ogg b/server-data/resources/[esx_addons]/esx-qalle-jail/cell.ogg deleted file mode 100644 index 940d10f182309c2f7a4f1c8919f6be30800d441b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61671 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 0 do - jailTime = jailTime - 1 - ESX.ShowNotification(TranslateCap("few_minutes")) - - TriggerServerEvent("esx-qalle-jail:updateJailTime", jailTime) - if jailTime == 0 then - UnJail() - TriggerServerEvent("esx-qalle-jail:updateJailTime", 0) - end - - Wait(60000) - end - end) - --Jail Timer-- - - --Prison Work-- - CreateThread(function() - while jailTime > 0 do - local sleepThread = 500 - local Packages = Config.PrisonWork["Packages"] - local Ped = PlayerPedId() - local PedCoords = GetEntityCoords(Ped) - - for posId, v in pairs(Packages) do - local DistanceCheck = GetDistanceBetweenCoords(PedCoords, v["x"], v["y"], v["z"], true) - - if DistanceCheck <= 10.0 then - sleepThread = 5 - - local PackageText = "Pack" - - if not v["state"] then - PackageText = "Already Taken" - end - - ESX.Game.Utils.DrawText3D(v, "[E] " .. PackageText, 0.4) - - if DistanceCheck <= 1.5 then - if IsControlJustPressed(0, 38) then - if v["state"] then - PackPackage(posId) - else - ESX.ShowNotification(TranslateCap("package_taken")) - end - end - end - end - end - Wait(sleepThread) - end - end) - --Prison Work-- -end - -function LoadTeleporters() - CreateThread(function() - while true do - local sleepThread = 500 - local Ped = PlayerPedId() - local PedCoords = GetEntityCoords(Ped) - - for _, v in pairs(Config.Teleports) do - local DistanceCheck = GetDistanceBetweenCoords(PedCoords, v["x"], v["y"], v["z"], true) - - if DistanceCheck <= 7.5 then - sleepThread = 5 - - ESX.Game.Utils.DrawText3D(v, "[E] Open Door", 0.4) - - if DistanceCheck <= 1.0 then - if IsControlJustPressed(0, 38) then - TeleportPlayer(v) - end - end - end - end - Wait(sleepThread) - end - end) -end - -function PackPackage(packageId) - local Package = Config.PrisonWork["Packages"][packageId] - - LoadModel("prop_cs_cardbox_01") - - local PackageObject = CreateObject(GetHashKey("prop_cs_cardbox_01"), Package["x"], Package["y"], Package["z"], true) - - PlaceObjectOnGroundProperly(PackageObject) - - TaskStartScenarioInPlace(PlayerPedId(), "PROP_HUMAN_BUM_BIN", 0, false) - - local Packaging = true - local StartTime = GetGameTimer() - - while Packaging do - Wait(1) - - local TimeToTake = 60000 * 1.20 -- Minutes - local PackPercent = (GetGameTimer() - StartTime) / TimeToTake * 100 - - if not IsPedUsingScenario(PlayerPedId(), "PROP_HUMAN_BUM_BIN") then - DeleteEntity(PackageObject) - ESX.ShowNotification(TranslateCap("canceled")) - Packaging = false - end - - if PackPercent >= 100 then - Packaging = false - DeliverPackage(PackageObject) - Package["state"] = false - else - ESX.Game.Utils.DrawText3D(Package, "Packaging... " .. math.ceil(tonumber(PackPercent)) .. "%", 0.4) - end - end -end - -function DeliverPackage(packageId) - if DoesEntityExist(packageId) then - AttachEntityToEntity(packageId, PlayerPedId(), GetPedBoneIndex(PlayerPedId(), 28422), 0.0, -0.03, 0.0, 5.0, 0.0, 0.0, true, true, false, true, 0, true) - - ClearPedTasks(PlayerPedId()) - else - return - end - - local Packaging = true - - LoadAnim("anim@heists@box_carry@") - - while Packaging do - Wait(5) - - if not IsEntityPlayingAnim(PlayerPedId(), "anim@heists@box_carry@", "idle", 3) then - TaskPlayAnim(PlayerPedId(), "anim@heists@box_carry@", "idle", 8.0, 8.0, -1, 50, 0, false, false, false) - end - - if not IsEntityAttachedToEntity(packageId, PlayerPedId()) then - Packaging = false - DeleteEntity(packageId) - else - local DeliverPosition = Config.PrisonWork["DeliverPackage"] - local PedPosition = GetEntityCoords(PlayerPedId()) - local DistanceCheck = GetDistanceBetweenCoords(PedPosition, DeliverPosition["x"], DeliverPosition["y"], DeliverPosition["z"], true) - - ESX.Game.Utils.DrawText3D(DeliverPosition, "[E] Leave Package", 0.4) - - if DistanceCheck <= 2.0 then - if IsControlJustPressed(0, 38) then - DeleteEntity(packageId) - ClearPedTasksImmediately(PlayerPedId()) - Packaging = false - - TriggerServerEvent("esx-qalle-jail:prisonWorkReward") - end - end - end - end -end - -function OpenJailMenu() - ESX.UI.Menu.Open("default", GetCurrentResourceName(), "jail_prison_menu", { - title = "Prison Menu", - align = "center", - elements = { - { label = "Jail Closest Person", value = "jail_closest_player" }, - { label = "Unjail Person", value = "unjail_player" }, - }, - }, function(data, menu) - local action = data.current.value - - if action == "jail_closest_player" then - menu.close() - - ESX.UI.Menu.Open("dialog", GetCurrentResourceName(), "jail_choose_time_menu", { - title = "Jail Time (minutes)", - }, function(data2, menu2) - local jailTime = tonumber(data2.value) - if jailTime == nil then - ESX.ShowNotification(TranslateCap("time_minutes")) - else - menu2.close() - local closestPlayer, closestDistance = ESX.Game.GetClosestPlayer() - - if closestPlayer == -1 or closestDistance > 3.0 then - ESX.ShowNotification(TranslateCap("players_nearby")) - else - ESX.UI.Menu.Open("dialog", GetCurrentResourceName(), "jail_choose_reason_menu", { - title = "Jail Reason", - }, function(data3, menu3) - local reason = data3.value - if reason == nil then - ESX.ShowNotification(TranslateCap("put_something")) - else - menu3.close() - local closestPlayer, closestDistance = ESX.Game.GetClosestPlayer() - if closestPlayer == -1 or closestDistance > 3.0 then - ESX.ShowNotification(TranslateCap("players_nearby")) - else - TriggerServerEvent("esx-qalle-jail:jailPlayer", GetPlayerServerId(closestPlayer), jailTime, reason) - end - end - end, function(_, menu3) - menu3.close() - end) - end - end - end, function(_, menu2) - menu2.close() - end) - elseif action == "unjail_player" then - local elements = {} - - ESX.TriggerServerCallback("esx-qalle-jail:retrieveJailedPlayers", function(playerArray) - if #playerArray == 0 then - ESX.ShowNotification(TranslateCap("jail_empty")) - return - end - - for i = 1, #playerArray, 1 do - table.insert(elements, { - label = "Prisoner: " .. playerArray[i].name .. " | Jail Time: " .. playerArray[i].jailTime .. " minutes", - value = playerArray[i].identifier, - }) - end - - ESX.UI.Menu.Open("default", GetCurrentResourceName(), "jail", TranslateCap("jail_menu"), { - title = "Unjail Player", - align = "center", - elements = elements, - }, function(data2, menu2) - local action = data2.current.value - - TriggerServerEvent("esx-qalle-jail:unJailPlayer", action) - - menu2.close() - end, function(_, menu2) - menu2.close() - end) - end) - end - end, function(_, menu) - menu.close() - end) -end diff --git a/server-data/resources/[esx_addons]/esx-qalle-jail/client/utils.lua b/server-data/resources/[esx_addons]/esx-qalle-jail/client/utils.lua deleted file mode 100644 index 068f6ddc1..000000000 --- a/server-data/resources/[esx_addons]/esx-qalle-jail/client/utils.lua +++ /dev/null @@ -1,173 +0,0 @@ -RegisterCommand("jailmenu", function() - if PlayerData.job.name == "police" then - OpenJailMenu() - else - ESX.ShowNotification(TranslateCap("not_officer")) - end -end) - -function LoadAnim(animDict) - RequestAnimDict(animDict) - while not HasAnimDictLoaded(animDict) do - Wait(10) - end -end - -function LoadModel(model) - RequestModel(model) - while not HasModelLoaded(model) do - Wait(10) - end -end - -function HideHUDThisFrame() - HideHelpTextThisFrame() - HideHudAndRadarThisFrame() - HideHudComponentThisFrame(1) - HideHudComponentThisFrame(2) - HideHudComponentThisFrame(3) - HideHudComponentThisFrame(4) - HideHudComponentThisFrame(6) - HideHudComponentThisFrame(7) - HideHudComponentThisFrame(8) - HideHudComponentThisFrame(9) - HideHudComponentThisFrame(13) - HideHudComponentThisFrame(11) - HideHudComponentThisFrame(12) - HideHudComponentThisFrame(15) - HideHudComponentThisFrame(18) - HideHudComponentThisFrame(19) -end - -function Cutscene() - DoScreenFadeOut(100) - Wait(250) - TriggerEvent("skinchanger:getSkin", function(skin) - local hash = GetEntityModel(PlayerPedId()) - if hash == "mp_m_freemode_01" then - local clothesSkin = { - ["tshirt_1"] = 20, - ["tshirt_2"] = 15, - ["torso_1"] = 33, - ["torso_2"] = 0, - ["arms"] = 0, - ["pants_1"] = 7, - ["pants_2"] = 0, - ["shoes_1"] = 34, - ["shoes_2"] = 0, - } - TriggerEvent("skinchanger:loadClothes", skin, clothesSkin) - else - local clothesSkin = { - ["tshirt_1"] = 15, - ["tshirt_2"] = 0, - ["torso_1"] = 2, - ["torso_2"] = 6, - ["arms"] = 2, - ["pants_1"] = 2, - ["pants_2"] = 0, - ["shoes_1"] = 35, - ["shoes_2"] = 0, - } - TriggerEvent("skinchanger:loadClothes", skin, clothesSkin) - end - end) - - LoadModel(-1320879687) - - local PolicePosition = Config.Cutscene["PolicePosition"] - local Police = CreatePed(5, -1320879687, PolicePosition["x"], PolicePosition["y"], PolicePosition["z"], PolicePosition["h"], false) - TaskStartScenarioInPlace(Police, "WORLD_HUMAN_PAPARAZZI", 0, false) - - local PlayerPosition = Config.Cutscene["PhotoPosition"] - local PlayerPed = PlayerPedId() - SetEntityCoords(PlayerPed, PlayerPosition["x"], PlayerPosition["y"], PlayerPosition["z"] - 1) - SetEntityHeading(PlayerPed, PlayerPosition["h"]) - FreezeEntityPosition(PlayerPed, true) - - Cam() - Wait(1000) - DoScreenFadeIn(100) - Wait(10000) - DoScreenFadeOut(250) - - local JailPosition = Config.JailPositions["Cell"] - SetEntityCoords(PlayerPed, JailPosition["x"], JailPosition["y"], JailPosition["z"]) - DeleteEntity(Police) - SetModelAsNoLongerNeeded(-1320879687) - - Wait(1000) - DoScreenFadeIn(250) - TriggerServerEvent("InteractSound_SV:PlayOnSource", "cell", 0.3) - RenderScriptCams(false, false, 0, true, true) - FreezeEntityPosition(PlayerPed, false) - DestroyCam(Config.Cutscene["CameraPos"]["cameraId"]) - - InJail() -end - -function Cam() - local CamOptions = Config.Cutscene["CameraPos"] - - CamOptions["cameraId"] = CreateCam("DEFAULT_SCRIPTED_CAMERA", true) - - SetCamCoord(CamOptions["cameraId"], CamOptions["x"], CamOptions["y"], CamOptions["z"]) - SetCamRot(CamOptions["cameraId"], CamOptions["rotationX"], CamOptions["rotationY"], CamOptions["rotationZ"]) - - RenderScriptCams(true, false, 0, true, true) -end - -function TeleportPlayer(pos) - local Values = pos - - if #Values["goal"] > 1 then - local elements = {} - - for _, v in pairs(Values["goal"]) do - table.insert(elements, { label = v, value = v }) - end - - ESX.UI.Menu.Open("default", GetCurrentResourceName(), "teleport_jail", { - title = "Choose Position", - align = "center", - elements = elements, - }, function(data, menu) - local action = data.current.value - local position = Config.Teleports[action] - - if action == "Boiling Broke" or action == "Security" then - if PlayerData.job.name ~= "police" then - ESX.ShowNotification(TranslateCap("not_keys")) - return - end - end - menu.close() - DoScreenFadeOut(100) - Wait(250) - SetEntityCoords(PlayerPedId(), position["x"], position["y"], position["z"]) - Wait(250) - DoScreenFadeIn(100) - end, function(_, menu) - menu.close() - end) - else - local position = Config.Teleports[Values["goal"][1]] - DoScreenFadeOut(100) - Wait(250) - SetEntityCoords(PlayerPedId(), position["x"], position["y"], position["z"]) - Wait(250) - DoScreenFadeIn(100) - end -end - -CreateThread(function() - local blip = AddBlipForCoord(Config.Teleports["Boiling Broke"]["x"], Config.Teleports["Boiling Broke"]["y"], Config.Teleports["Boiling Broke"]["z"]) - SetBlipSprite(blip, 188) - SetBlipDisplay(blip, 4) - SetBlipScale(blip, 0.8) - SetBlipColour(blip, 49) - SetBlipAsShortRange(blip, true) - BeginTextCommandSetBlipName("STRING") - AddTextComponentString("Penitentiary") - EndTextCommandSetBlipName(blip) -end) diff --git a/server-data/resources/[esx_addons]/esx-qalle-jail/config.lua b/server-data/resources/[esx_addons]/esx-qalle-jail/config.lua deleted file mode 100644 index 904bfe5ea..000000000 --- a/server-data/resources/[esx_addons]/esx-qalle-jail/config.lua +++ /dev/null @@ -1,106 +0,0 @@ -Config = {} -Config.Locale = "it" -Config.JailPositions = { - ["Cell"] = { ["x"] = 1799.8345947266, ["y"] = 2489.1350097656, ["z"] = -119.02998352051, ["h"] = 179.03021240234 }, -} - -Config.Cutscene = { - ["PhotoPosition"] = { - ["x"] = 402.91567993164, - ["y"] = -996.75970458984, - ["z"] = -99.000259399414, - ["h"] = 186.22499084473, - }, - ["CameraPos"] = { - ["x"] = 402.88830566406, - ["y"] = -1003.8851318359, - ["z"] = -97.419647216797, - ["rotationX"] = -15.433070763946, - ["rotationY"] = 0.0, - ["rotationZ"] = -0.31496068835258, - ["cameraId"] = 0, - }, - ["PolicePosition"] = { - ["x"] = 402.91702270508, - ["y"] = -1000.6376953125, - ["z"] = -99.004028320313, - ["h"] = 356.88052368164, - }, -} - -Config.PrisonWork = { - ["DeliverPackage"] = { - ["x"] = 1027.2347412109, - ["y"] = -3101.419921875, - ["z"] = -38.999870300293, - ["h"] = 267.89135742188, - }, - - ["Packages"] = { - [1] = { ["x"] = 1003.6661987305, ["y"] = -3108.4221191406, ["z"] = -38.999866485596, ["state"] = true }, - [2] = { ["x"] = 1006.0420532227, ["y"] = -3103.0024414063, ["z"] = -38.999866485596, ["state"] = true }, - [3] = { ["x"] = 1015.7958374023, ["y"] = -3102.8337402344, ["z"] = -38.99991607666, ["state"] = true }, - [4] = { ["x"] = 1012.8907470703, ["y"] = -3108.2907714844, ["z"] = -38.999912261963, ["state"] = true }, - [5] = { ["x"] = 1018.2017822266, ["y"] = -3109.1982421875, ["z"] = -38.999897003174, ["state"] = true }, - [6] = { ["x"] = 1018.0194091797, ["y"] = -3096.5700683594, ["z"] = -38.999897003174, ["state"] = true }, - [7] = { ["x"] = 1015.6422119141, ["y"] = -3091.7392578125, ["z"] = -38.999897003174, ["state"] = true }, - [8] = { ["x"] = 1010.7862548828, ["y"] = -3096.6135253906, ["z"] = -38.999897003174, ["state"] = true }, - [9] = { ["x"] = 1005.7819824219, ["y"] = -3096.8415527344, ["z"] = -38.999897003174, ["state"] = true }, - [10] = { ["x"] = 1003.4543457031, ["y"] = -3096.7048339844, ["z"] = -38.999897003174, ["state"] = true }, - }, -} - -Config.Teleports = { - ["Prison Work"] = { - ["x"] = 992.51770019531, - ["y"] = -3097.8413085938, - ["z"] = -38.995861053467, - ["h"] = 81.15771484375, - ["goal"] = { - "Jail", - }, - }, - - ["Boiling Broke"] = { - ["x"] = 1845.6022949219, - ["y"] = 2585.8029785156, - ["z"] = 45.672061920166, - ["h"] = 92.469093322754, - ["goal"] = { - "Security", - }, - }, - - ["Jail"] = { - ["x"] = 1800.6979980469, - ["y"] = 2483.0979003906, - ["z"] = -122.68814849854, - ["h"] = 271.75274658203, - ["goal"] = { - "Prison Work", - "Security", - "Visitor", - }, - }, - - ["Security"] = { - ["x"] = 1706.7625732422, - ["y"] = 2581.0793457031, - ["z"] = -69.407371520996, - ["h"] = 267.72802734375, - ["goal"] = { - "Jail", - "Boiling Broke", - }, - }, - - ["Visitor"] = { - ["x"] = 1699.7196044922, - ["y"] = 2574.5314941406, - ["z"] = -69.403930664063, - ["h"] = 169.65020751953, - ["goal"] = { - "Jail", - }, - }, -} diff --git a/server-data/resources/[esx_addons]/esx-qalle-jail/fxmanifest.lua b/server-data/resources/[esx_addons]/esx-qalle-jail/fxmanifest.lua deleted file mode 100644 index d6acec268..000000000 --- a/server-data/resources/[esx_addons]/esx-qalle-jail/fxmanifest.lua +++ /dev/null @@ -1,23 +0,0 @@ -fx_version("adamant") -game("gta5") -author("qalle-git; rework by: bitpredator") -lua54("yes") -version("1.0.2") -description("Jail Script With Working Job") - -shared_script("@es_extended/imports.lua") - -server_scripts({ - "@es_extended/locale.lua", - "@mysql-async/lib/MySQL.lua", - "config.lua", - "locales/*.lua", - "server/server.lua", -}) - -client_scripts({ - "@es_extended/locale.lua", - "config.lua", - "client/*.lua", - "locales/*.lua", -}) diff --git a/server-data/resources/[esx_addons]/esx-qalle-jail/jail.sql b/server-data/resources/[esx_addons]/esx-qalle-jail/jail.sql deleted file mode 100644 index 3e3411acd..000000000 --- a/server-data/resources/[esx_addons]/esx-qalle-jail/jail.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE `users` - ADD COLUMN `jail` INT(11) NOT NULL DEFAULT '0'; \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/esx-qalle-jail/locales/en.lua b/server-data/resources/[esx_addons]/esx-qalle-jail/locales/en.lua deleted file mode 100644 index b7c2b9d05..000000000 --- a/server-data/resources/[esx_addons]/esx-qalle-jail/locales/en.lua +++ /dev/null @@ -1,16 +0,0 @@ -Locales["en"] = { - ["not_keys"] = "You don't have an key to go here!", - ["back_in_jail"] = "Last time you went to sleep you were imprisoned, so now you're here again!", - ["you_released"] = "You are released, keep calm outside! Good luck", - ["few_minutes"] = "You have minutes left in jail!", - ["package_taken"] = "You've already taken this package", - ["canceled"] = "Canceled", - ["time_minutes"] = "The time needs to be in minutes!", - ["players_nearb"] = "No players nearby!", - ["put_something"] = "You need to put something here!", - ["jail_empty"] = "Your jail is empty!", - ["not_officer"] = "You are not an officer!", - ["invalid_time"] = "This time is invalid", - ["id_not_online"] = "This ID is not online", - ["money_for_food"] = "Here you have som cash for food!", -} diff --git a/server-data/resources/[esx_addons]/esx-qalle-jail/locales/it.lua b/server-data/resources/[esx_addons]/esx-qalle-jail/locales/it.lua deleted file mode 100644 index 550d1d0c9..000000000 --- a/server-data/resources/[esx_addons]/esx-qalle-jail/locales/it.lua +++ /dev/null @@ -1,16 +0,0 @@ -Locales["it"] = { - ["not_keys"] = "Non hai una chiave per andare qui!", - ["back_in_jail"] = "L'ultima volta che sei andato a dormire eri stato imprigionato, quindi ora sei di nuovo qui!", - ["you_released"] = "Sei libero, mantieni la calma li fuori! Buona fortuna", - ["few_minutes"] = "Ti restano pochi minuti di prigione!", - ["package_taken"] = "Hai già preso questo pacchetto", - ["canceled"] = "Annullata", - ["time_minutes"] = "Il tempo deve essere in minuti!", - ["players_nearb"] = "Nessun giocatore nelle vicinanze!", - ["put_something"] = "Devi mettere qualcosa qui!", - ["jail_empty"] = "La tua prigione è vuota!", - ["not_officer"] = "Non sei un ufficiale!", - ["invalid_time"] = "Questo tempo non è valido", - ["id_not_online"] = "Questo ID non è online", - ["money_for_food"] = "Eccoti un po' di soldi per il cibo!", -} diff --git a/server-data/resources/[esx_addons]/esx-qalle-jail/server/server.lua b/server-data/resources/[esx_addons]/esx-qalle-jail/server/server.lua deleted file mode 100644 index 714ab5900..000000000 --- a/server-data/resources/[esx_addons]/esx-qalle-jail/server/server.lua +++ /dev/null @@ -1,169 +0,0 @@ -ESX = exports["es_extended"]:getSharedObject() - -RegisterCommand("jail", function(src, args) - local xPlayer = ESX.GetPlayerFromId(src) - - if xPlayer["job"]["name"] == "police" then - local jailPlayer = args[1] - local jailTime = tonumber(args[2]) - local jailReason = args[3] - - if GetPlayerName(jailPlayer) ~= nil then - if jailTime ~= nil then - JailPlayer(jailPlayer, jailTime) - - TriggerClientEvent("esx:showNotification", src, GetPlayerName(jailPlayer) .. " Jailed for " .. jailTime .. " minutes!") - - if args[3] ~= nil then - GetRPName(jailPlayer, function(Firstname, Lastname) - TriggerClientEvent("chat:addMessage", -1, { - args = { - "JUDGE", - Firstname .. " " .. Lastname .. " Is now in jail for the reason: " .. args[3], - }, - color = { 249, 166, 0 }, - }) - end) - end - else - TriggerClientEvent("esx:showNotification", src, TranslateCap("invalid_time")) - end - else - TriggerClientEvent("esx:showNotification", src, TranslateCap("id_not_online")) - end - else - TriggerClientEvent("esx:showNotification", src, TranslateCap("not_officer")) - end -end) - -RegisterCommand("unjail", function(src, args) - local xPlayer = ESX.GetPlayerFromId(src) - - if xPlayer["job"]["name"] == "police" then - local jailPlayer = args[1] - - if GetPlayerName(jailPlayer) ~= nil then - UnJail(jailPlayer) - else - TriggerClientEvent("esx:showNotification", src, TranslateCap("id_not_online")) - end - else - TriggerClientEvent("esx:showNotification", src, TranslateCap("not_officer")) - end -end) - -RegisterServerEvent("esx-qalle-jail:jailPlayer") -AddEventHandler("esx-qalle-jail:jailPlayer", function(targetSrc, jailTime, jailReason) - local src = source - local targetSrc = tonumber(targetSrc) - local xPlayer = ESX.GetPlayerFromId(src) - - if xPlayer.job.name == "police" then - JailPlayer(targetSrc, jailTime) - - GetRPName(targetSrc, function(Firstname, Lastname) - TriggerClientEvent("chat:addMessage", -1, { - args = { "JUDGE", Firstname .. " " .. Lastname .. " Is now in jail for the reason: " .. jailReason }, - color = { 249, 166, 0 }, - }) - end) - else - DropPlayer(src, "[esx-qalle-jail]: You have been kicked for trying to exploit a server event.") - end - - TriggerClientEvent("esx:showNotification", src, GetPlayerName(targetSrc) .. " Jailed for " .. jailTime .. " minutes!") -end) - -RegisterServerEvent("esx-qalle-jail:unJailPlayer") -AddEventHandler("esx-qalle-jail:unJailPlayer", function(targetIdentifier) - local src = source - local xPlayer = ESX.GetPlayerFromIdentifier(targetIdentifier) - - if xPlayer ~= nil then - UnJail(xPlayer.source) - else - MySQL.Async.execute("UPDATE users SET jail = @newJailTime WHERE identifier = @identifier", { - ["@identifier"] = targetIdentifier, - ["@newJailTime"] = 0, - }) - end - - TriggerClientEvent("esx:showNotification", src, xPlayer.name .. " Unjailed!") -end) - -RegisterServerEvent("esx-qalle-jail:updateJailTime") -AddEventHandler("esx-qalle-jail:updateJailTime", function(newJailTime) - local src = source - - EditJailTime(src, newJailTime) -end) - -RegisterServerEvent("esx-qalle-jail:prisonWorkReward") -AddEventHandler("esx-qalle-jail:prisonWorkReward", function() - local src = source - local xPlayer = ESX.GetPlayerFromId(src) - xPlayer.addMoney(math.random(13, 21)) - TriggerClientEvent("esx:showNotification", src, TranslateCap("money_for_food")) -end) - -function JailPlayer(jailPlayer, jailTime) - TriggerClientEvent("esx-qalle-jail:jailPlayer", jailPlayer, jailTime) - - EditJailTime(jailPlayer, jailTime) -end - -function UnJail(jailPlayer) - TriggerClientEvent("esx-qalle-jail:unJailPlayer", jailPlayer) - - EditJailTime(jailPlayer, 0) -end - -function EditJailTime(source, jailTime) - local src = source - local xPlayer = ESX.GetPlayerFromId(src) - local Identifier = xPlayer.identifier - - MySQL.Async.execute("UPDATE users SET jail = @newJailTime WHERE identifier = @identifier", { - ["@identifier"] = Identifier, - ["@newJailTime"] = tonumber(jailTime), - }) -end - -function GetRPName(playerId, data) - local Identifier = ESX.GetPlayerFromId(playerId).identifier - - MySQL.Async.fetchAll("SELECT firstname, lastname FROM users WHERE identifier = @identifier", { ["@identifier"] = Identifier }, function(result) - data(result[1].firstname, result[1].lastname) - end) -end - -ESX.RegisterServerCallback("esx-qalle-jail:retrieveJailedPlayers", function(_, cb) - local jailedPersons = {} - - MySQL.Async.fetchAll("SELECT firstname, lastname, jail, identifier FROM users WHERE jail > @jail", { ["@jail"] = 0 }, function(result) - for i = 1, #result, 1 do - table.insert(jailedPersons, { - name = result[i].firstname .. " " .. result[i].lastname, - jailTime = result[i].jail, - identifier = result[i].identifier, - }) - end - cb(jailedPersons) - end) -end) - -ESX.RegisterServerCallback("esx-qalle-jail:retrieveJailTime", function(source, cb) - local src = source - local xPlayer = ESX.GetPlayerFromId(src) - local Identifier = xPlayer.identifier - - MySQL.Async.fetchAll("SELECT jail FROM users WHERE identifier = @identifier", { ["@identifier"] = Identifier }, function(result) - local JailTime = tonumber(result[1].jail) - - if JailTime > 0 then - cb(true, JailTime) - else - cb(false, 0) - end - end) -end) diff --git a/server-data/resources/[esx_addons]/wx_jail/LICENSE b/server-data/resources/[esx_addons]/wx_jail/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/server-data/resources/[esx_addons]/wx_jail/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/server-data/resources/[esx_addons]/wx_jail/README.md b/server-data/resources/[esx_addons]/wx_jail/README.md new file mode 100644 index 000000000..e92ec9563 --- /dev/null +++ b/server-data/resources/[esx_addons]/wx_jail/README.md @@ -0,0 +1,20 @@ +# NOTICE +This script is 100% functional and ready to be used on your server BUT: +- I made this script for one of the servers I've been developing +- Some of the functions are taken from qalle's jail system +- There might be some translation errors and/or untranslated parts (mostly in czech), however I've tried to translate most of the parts + +# WX JAIL - Advanced jail system made using ox_lib + +# Features +- OX Lib implementation (notifications, target, context menu, ...) +- Target support +- Admin menu +- Exploit protection +- Optimized +- Large config file +- Multiple jobs support +- Multiple jail locations +- [WX Logs](https://github.com/nwvh/wx_logs) implementation + +# NOTE: MOST OF THE FUNCTIONS ARE FROM [QALLE'S JAIL SYSTEM](https://github.com/qalle-git/esx-qalle-jail)! MOST OF THE CREDITS GOES TO HIM! diff --git a/server-data/resources/[esx_addons]/wx_jail/SQL/INSERT-THIS.sql b/server-data/resources/[esx_addons]/wx_jail/SQL/INSERT-THIS.sql new file mode 100644 index 000000000..9a907b278 --- /dev/null +++ b/server-data/resources/[esx_addons]/wx_jail/SQL/INSERT-THIS.sql @@ -0,0 +1,2 @@ +ALTER TABLE `users` + ADD COLUMN `jail` INT(16) NOT NULL DEFAULT '0'; \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/wx_jail/client/client.lua b/server-data/resources/[esx_addons]/wx_jail/client/client.lua new file mode 100644 index 000000000..db153ee43 --- /dev/null +++ b/server-data/resources/[esx_addons]/wx_jail/client/client.lua @@ -0,0 +1,285 @@ +ESX = exports["es_extended"]:getSharedObject() +PlayerData = {} +local jailTime = 0 + +-- ## Check player's job ## -- +Citizen.CreateThread(function() + while ESX.GetPlayerData() == nil do + Citizen.Wait(150) + end + while true do + Wait(2500) + PlayerData = ESX.GetPlayerData() or {} + end +end) + +-- ## Check if player is in jail when he selects character ## -- +-- ## Fix on correct jailtime display +RegisterNetEvent("esx:playerLoaded") +AddEventHandler("esx:playerLoaded", function() + Citizen.Wait(15000) + ESX.TriggerServerCallback("wx_jail:retrieveJailTime", function(inJail, time) + if inJail then + jailTime = time --Time display quickfix :D + SetEntityCoords(PlayerPedId(), wx.Locations.JailEnter[math.random(#wx.Locations.JailEnter)]) + + lib.notify({ + title = Locale["JailTitle"], + description = Locale["Disconnected"], + type = 'error', + position = 'top' + }) + TimeLeft() + end + end) +end) + +RegisterNetEvent("wx_jail:jailPlayer") +AddEventHandler("wx_jail:jailPlayer", function(newJailTime) + jailTime = newJailTime + Cutscene() +end) + +RegisterNetEvent("wx_jail:unJailPlayer") +AddEventHandler("wx_jail:unJailPlayer", function() + jailTime = 0 + UnJail() +end) + + +local options = { + { + name = 'wx_jail:send', + distance = 2, + icon = 'fa-solid fa-handcuffs', + label = Locale["SendToJail"], + canInteract = function () + if wx.Jobs[PlayerData["job"].name] then return true end + if IsPedDeadOrDying(PlayerPedId(),false) or IsPedFatallyInjured(PlayerPedId()) then return false end + return false + end, + onSelect = function (data) + local id = GetPlayerServerId(NetworkGetPlayerIndexFromPed(data.entity)) + local jail = lib.inputDialog('Vězení', { + {type = 'input', label = Locale["pID"], disabled = true, placeholder = id, icon = 'list-ol',}, + {type = 'number', label = Locale["pTime"], description = Locale["pTimeDesc"]:format(wx.MinuteToYears), icon = 'clock', placeholder = "10"}, + {type = 'input', label = Locale["pReason"], description = Locale["pReasonDesc"], icon = 'font', placeholder = "Jízda bez ŘP"}, + }) + if jail then + if jail[2] <= 100 then + TriggerServerEvent('wx_jail:sendToJail',id,jail[2],jail[3]) + lib.notify({ + title = Locale["Success"], + description = Locale["Jailed"]:format(jail[2],jail[3]), + type = 'info', + position = 'top' + }) + else + lib.notify({ + title = Locale["Error"], + description = Locale["MaxTime"]:format(wx.MaxTime), + type = 'error', + position = 'top' + }) + end + else + lib.notify({ + title = Locale["Cancelled"], + type = 'info', + position = 'top' + }) + end + end + }, +} + +if wx.Target then + exports.ox_target:addGlobalPlayer(options) +end +if wx.ManualJail then + exports.ox_target:addSphereZone({ + coords = vec3(1840.4167, 2577.7600, 46.0144), + radius = 1.0, + options = { + { + label = Locale["JailTarget"], + icon = "fa-solid fa-handcuffs", + distance = 1.5, + canInteract = function () + if wx.Jobs[PlayerData["job"].name] then return true end + return false + end, + onSelect = function () + local pl = {} + local nearby = lib.getNearbyPlayers(GetEntityCoords(PlayerPedId()), 25.0, true) + if json.encode(nearby) == '[]' then + table.insert(pl,{ + title = Locale["Nearby"], + icon = 'triangle-exclamation' + }) + else + for k,v in pairs(nearby) do + local name = lib.callback.await('wx_jail:getCharName',GetPlayerServerId(v.id)) + print(GetPlayerServerId(v.id)) + table.insert(pl,{ + title = ("[%s] %s"):format(GetPlayerServerId(v.id),GetPlayerName(v.id)), + icon = 'user', + onSelect = function () + local jail = lib.inputDialog(Locale["JailTitle"], { + {type = 'input', label = Locale["pID"], disabled = true, placeholder = GetPlayerServerId(v.id), icon = 'list-ol',}, + {type = 'number', label = Locale["pTime"], description = Locale["pTimeDesc"]:format(wx.MinuteToYears), icon = 'clock', placeholder = "10"}, + {type = 'input', label = Locale["pReason"], description = Locale["pReasonDesc"], icon = 'font', placeholder = "Jízda bez ŘP"}, + }) + if jail then + if jail[2] <= wx.MaxTime then + local alert = lib.alertDialog({ + header = Locale["Confirmation"], + content = Locale["ConfirmationDesc"]:format(name,jail[2]*wx.MinuteToYears..' roky',jail[3]), + centered = true, + cancel = true, + labels = { + confirm = Locale["Confirm"], + cancel = Locale["Cancel"] + } + }) + if alert == 'confirm' then + TriggerServerEvent('wx_jail:sendToJail',GetPlayerServerId(v.id),jail[2],jail[3]) + lib.notify({ + title = Locale["Success"], + description = Locale["Jailed"]:format(jail[2],jail[3]), + type = 'info', + position = 'top' + }) + end + else + lib.notify({ + title = Locale["Error"], + description = Locale["MaxTime"]:format(wx.MaxTime), + type = 'error', + position = 'top' + }) + + end + else + lib.notify({ + title = Locale["Cancelled"], + type = 'info', + position = 'top' + }) + end + end + }) + lib.registerContext({ + id = 'nearby', + title = Locale["Select"], + options = pl + }) + end + end + + lib.showContext('nearby') + end + } + } + }) +end + + + +function UnJail() + TimeLeft() + + ESX.Game.Teleport(PlayerPedId(), wx.Locations.JailExit) + + lib.notify({ + title = 'Věznice', + description = Locale["JailDone"], + type = 'success', + position = 'top' + }) +end + +function DrawTxt(x,y, width, height, scale, text, r, g, b, a, outline) + SetTextFont(0) + SetTextScale(scale, scale) + SetTextColour(r, g, b, a) + SetTextDropshadow(0, 0, 0, 0,255) + SetTextDropShadow() + if outline then + SetTextOutline() + end + BeginTextCommandDisplayText('STRING') + AddTextComponentSubstringPlayerName(text) + EndTextCommandDisplayText(x - width / 2, y - height / 2 + 0.005) +end + +function CheckTeleport() + if GetDistanceBetweenCoords(wx.Locations.JailCenter,GetEntityCoords(PlayerPedId())) > wx.Radius+0.0 then + if GetDistanceBetweenCoords(GetEntityCoords(PlayerPedId()),vector3(402.9156, -996.7591, -99.0002)) > 10.0 then + SetEntityCoords(PlayerPedId(), wx.Locations.JailEnter[ math.random( #wx.Locations.JailEnter ) ]) + if wx.BanOnLeave then + TriggerServerEvent('wx_jail:ban') + end + + lib.notify({ + title = 'Věznice', + description = Locale["TooFar"], + type = 'error', + position = 'top' + }) + return + end + end +end + +function TimeLeft() + + Citizen.CreateThread(function() + + while jailTime > 0 do + jailTime = jailTime - 1 + if jailTime == 0 then + UnJail() + TriggerServerEvent("wx_jail:updateJailTime", 0) + end + TriggerServerEvent("wx_jail:updateJailTime", jailTime) + Citizen.Wait(60000*wx.MinuteToYears) + end + + end) + Citizen.CreateThread(function () + while jailTime > 0 do Wait(0) + if wx.AntiLeave then CheckTeleport() end + if jailTime == 1 then + DrawTxt(1.0, 1.45, 1.0, 1.0, 0.4, (""..Locale["YearR"].." "):format(jailTime), 255, 255, 255, 255) + elseif jailTime > 5 then + DrawTxt(1.0, 1.45, 1.0, 1.0, 0.4, (""..Locale["YearsR"].." "):format(jailTime), 255, 255, 255, 255) + end + + end + end) + +end + +--same usage as playerLoaded +AddEventHandler('onResourceStart', function(resourceName) + if (GetCurrentResourceName() ~= resourceName) then + return + end + Citizen.Wait(2000) + ESX.TriggerServerCallback("wx_jail:retrieveJailTime", function(inJail, time) + if inJail then + jailTime = time --Time display quickfix :D + SetEntityCoords(PlayerPedId(), wx.Locations.JailEnter[math.random(#wx.Locations.JailEnter)]) + + lib.notify({ + title = Locale["JailTitle"], + description = Locale["Disconnected"], + type = 'error', + position = 'top' + }) + TimeLeft() + end + end) +end) + diff --git a/server-data/resources/[esx_addons]/wx_jail/client/font.lua b/server-data/resources/[esx_addons]/wx_jail/client/font.lua new file mode 100644 index 000000000..0a0cded9a --- /dev/null +++ b/server-data/resources/[esx_addons]/wx_jail/client/font.lua @@ -0,0 +1 @@ +RegisterFontFile('BBN') diff --git a/server-data/resources/[esx_addons]/wx_jail/client/functions.lua b/server-data/resources/[esx_addons]/wx_jail/client/functions.lua new file mode 100644 index 000000000..6bf839ac6 --- /dev/null +++ b/server-data/resources/[esx_addons]/wx_jail/client/functions.lua @@ -0,0 +1,70 @@ +local CamOpt = { + ["CameraPos"] = { ["x"] = 402.88830566406, ["y"] = -1003.8851318359, ["z"] = -97.419647216797, ["rotationX"] = -15.433070763946, ["rotationY"] = 0.0, ["rotationZ"] = -0.31496068835258, ["cameraId"] = 0 } +} + +CS = false + + +function LoadAnim(animDict) + RequestAnimDict(animDict) + + while not HasAnimDictLoaded(animDict) do + Citizen.Wait(10) + end +end + +function LoadModel(model) + RequestModel(model) + + while not HasModelLoaded(model) do + Citizen.Wait(10) + end +end + +function Cutscene() + SwitchOutPlayer(PlayerPedId(), 0, 1) + Citizen.Wait(250) + + LoadModel(wx.PhotoNPC) + local photoNPC = CreatePed(5, wx.PhotoNPC, 402.9170, -1000.6376, -99.0040, 356.8805, false) + TaskStartScenarioInPlace(photoNPC, "WORLD_HUMAN_PAPARAZZI", 0, false) + + local PlayerPed = PlayerPedId() + SetEntityCoords(PlayerPed, 428.3845, -980.2968, 30.7114 - 1) + SetEntityHeading(PlayerPed, 186.2249) + FreezeEntityPosition(PlayerPed, true) + RemoveAllPedWeapons(PlayerPedId()) + + Citizen.Wait(5500) + SwitchInPlayer(PlayerPedId()) + DoScreenFadeIn(1500) + SetEntityCoords(PlayerPed, 402.9156, -996.7591, -99.0002 - 1) + Cam() + Citizen.Wait(1000) + DoScreenFadeIn(100) + Citizen.Wait(10000) + Citizen.Wait(1000) + DoScreenFadeIn(250) + + RenderScriptCams(false, false, 0, true, true) + FreezeEntityPosition(PlayerPed, false) + DestroyCam(CamOpt["CameraPos"]["cameraId"]) + SetEntityCoords(PlayerPed, wx.Locations.JailEnter[ math.random( #wx.Locations.JailEnter ) ]) + DeleteEntity(photoNPC) + SetModelAsNoLongerNeeded(-1320879687) + CS = true + + + TimeLeft() +end + +function Cam() + local CamOptions = CamOpt["CameraPos"] + + CamOptions["cameraId"] = CreateCam("DEFAULT_SCRIPTED_CAMERA", true) + + SetCamCoord(CamOptions["cameraId"], CamOptions["x"], CamOptions["y"], CamOptions["z"]) + SetCamRot(CamOptions["cameraId"], CamOptions["rotationX"], CamOptions["rotationY"], CamOptions["rotationZ"]) + + RenderScriptCams(true, false, 0, true, true) +end diff --git a/server-data/resources/[esx_addons]/wx_jail/client/jailmenu.lua b/server-data/resources/[esx_addons]/wx_jail/client/jailmenu.lua new file mode 100644 index 000000000..c17871736 --- /dev/null +++ b/server-data/resources/[esx_addons]/wx_jail/client/jailmenu.lua @@ -0,0 +1,412 @@ +-- I won't translate this lol + +ESX = exports["es_extended"]:getSharedObject() +lib.registerContext({ + id = 'jailmenu', + title = "Prison", + options = { + { + title = "Imprison", + disabled = wx.ManualJail, + icon = "handcuffs", + onSelect = function () + local jail = lib.inputDialog('Jail', { + {type = 'number', label = 'Player ID', disabled = false, placeholder = "123", icon = 'list-ol',}, + {type = 'number', label = 'Time', description = 'Time in minutes (1 minute = '..wx.MinuteToYears..' years)', icon = 'clock', placeholder = "10"}, + {type = 'input', label = 'Reason', description = 'Reason for imprisoning the player', icon = 'font', placeholder = "Driving without a license"}, + }) + if jail then + if jail[2] <= wx.MaxTime then + ESX.TriggerServerCallback('wx_jail:checkJailUser', function(jailed) + for k, v in pairs(jailed) do + local serverId = GetPlayerServerIdFromIdentifier(v.identifier) + if serverId == jail[1] then + lib.notify({ + title = Locale["Error"], + description = ('L\'utente è già in carcere!'), + type = 'error', + position = 'top' + }) + break + else + TriggerServerEvent('wx_jail:sendToJail', jail[1], jail[2], jail[3]) + lib.notify({ + title = "Success", + description = 'Player has been imprisoned for ' .. + jail[2] .. ' minutes due to ' .. jail[3], + type = 'info', + position = 'top' + }) + end + end + end) + else + lib.notify({ + title = "Error", + description = ('The maximum time for imprisonment is %s minutes!'):format(wx.MaxTime), + type = 'error', + position = 'top' + }) + end + else + lib.notify({ + title = 'Action Cancelled', + type = 'info', + position = 'top' + }) + end + end + }, + { + title = "Release from Prison", + icon = 'user-large-slash', + onSelect = function () + local jailedPly = { + { + title = "Back", + icon = 'angle-left', + menu = 'jailmenu', + arrow = false + } + } + ESX.TriggerServerCallback("wx_jail:retrieveJailedPlayers", function(jailed) + if json.encode(jailed) == '[]' then + table.insert(jailedPly,{ + title = "No one is in prison", + icon = 'exclamation-triangle' + }) + end + for k,v in pairs(jailed) do + table.insert(jailedPly,{ + title = v.name, + metadata = { + {label = 'Remaining Time', value = v.jailTime*wx.MinuteToYears..' minutes'}, + {label = 'Identifier', value = v.identifier}, + }, + onSelect = function () + local alert = lib.alertDialog({ + header = 'Release prisoner **'..v.name..'**', + content = 'Are you sure you want to release the selected prisoner?', + centered = true, + cancel = true, + labels = { + cancel = "Do Not Release", + confirm = "Yes, Release" + } + }) + if alert == 'confirm' then + TriggerServerEvent("wx_jail:unJailPlayer", v.identifier) + lib.notify({ + title = 'Prisoner '..v.name..' has been released!', + type = 'success', + position = 'top' + }) + else + lib.notify({ + title = 'Action Cancelled', + type = 'info', + position = 'top' + }) + end + end + }) + end + lib.registerContext({ + id='jailedPlayers', + title = "List of Prisoners", + options=jailedPly + }) + lib.showContext('jailedPlayers') + end) + end + -- menu = 'jailedPlayers' + }, + { + title = "Change Time", + icon = "clock", + onSelect = function () + local editJailTime = { + { + title = "Back", + icon = 'angle-left', + menu = 'jailmenu', + arrow = false + } + } + ESX.TriggerServerCallback("wx_jail:retrieveJailedPlayers", function(jailed) + if json.encode(jailed) == '[]' then + table.insert(editJailTime,{ + title = "No one is in prison", + icon = 'exclamation-triangle' + }) + end + for k,v in pairs(jailed) do + table.insert(editJailTime,{ + title = v.name, + metadata = { + {label = 'Remaining Time', value = v.jailTime*wx.MinuteToYears..' minutes'}, + {label = 'Identifier', value = v.identifier}, + }, + onSelect = function () + local jailtime = lib.inputDialog('Change Imprisonment Time', { + {type = 'number', label = 'New Time', description = 'Time in minutes, how long the player should stay in prison', icon = 'clock', placeholder = "10"}, + }) + if jailtime then + TriggerServerEvent("wx_jail:newTime", v.identifier,jailtime[1]) + lib.notify({ + title = "Success", + description = ('Prisoner %s is now in prison for %s'):format(v.name,jailtime[1]), + type = 'success', + position = 'top' + }) + else + lib.notify({ + title = "Cancelled", + type = 'info', + position = 'top' + }) + end + end + }) + end + lib.registerContext({ + id='editJailTime', + title = "List of Prisoners", + options=editJailTime + }) + lib.showContext('editJailTime') + end) + end + }, + + } +}) + +function GetPlayerServerIdFromIdentifier(identifier) + local playerServerId = nil + + for _, player in ipairs(GetActivePlayers()) do + local playerId = GetPlayerServerId(player) + + if playerId and playerId > 0 then + local playerIdentifier = ESX.GetPlayerData(player).identifier + + -- here we slip the identifiers so they are individual + local identifiers = splitIdentifiers(identifier) + + -- here we check each identifier coz why not + for _, id in ipairs(identifiers) do + if playerIdentifier == id then + playerServerId = playerId + break + end + end + --lol not working, quick fix made at line 240 no worries 👍 + if playerServerId then + break + end + end + end + + return playerServerId +end + +function splitIdentifiers(identifierrec) + local identifiers = {} + for identifier in string.gmatch(identifierrec, "[^,]+") do + table.insert(identifiers, identifier) + end + return identifiers +end + +RegisterCommand(wx.Command,function () + if not wx.Jobs[PlayerData["job"].name] then return end + lib.showContext('jailmenu') +end,false) + +RegisterCommand('adminjail',function () + lib.registerContext({ + id = 'adminjail', + title = 'Admin Jail', + options = { + { + title = "Send to jail", + icon = 'handcuffs', + onSelect = function () + local jail = lib.inputDialog('ADMIN JAIL', { + {type = 'number', label = 'Player ID', description = "ID of the player you want to jail", icon = 'list-ol',}, + {type = 'number', label = 'Time', description = 'Time in minutes', icon = 'clock', placeholder = "10"}, + {type = 'input', label = 'Reason', description = 'Jail reason', icon = 'font', placeholder = "Jízda bez ŘP"}, + {type = 'checkbox', label = 'Send to logs (Recommended)', checked=true}, + }) + if jail then + if jail[2] <= wx.MaxTime then + ESX.TriggerServerCallback('wx_jail:checkJailUser', function(jailed) + for k, v in pairs(jailed) do + local serverId = GetPlayerServerIdFromIdentifier(v.identifier) + if serverId == jail[1] then + lib.notify({ + title = Locale["Error"], + description = ('L\'utente è già in carcere!'), + type = 'error', + position = 'top' + }) + break + else + TriggerServerEvent('wx_jail:adminJail', jail[1], jail[2], jail[3], jail[4]) + lib.notify({ + title = Locale["Success"], + description = 'Il giocatore è stato incarcerato per ' .. + jail[2] .. ' minuti. Motivo: ' .. jail + [3], + type = 'info', + position = 'top' + }) + end + end + end) + else + lib.notify({ + title = Locale["Error"], + description = ('Maximum jail time is %s minutes!'):format(wx.MaxTime), + type = 'error', + position = 'top' + }) + end + else + lib.notify({ + title = Locale["Cancelled"], + type = 'info', + position = 'top' + }) + end + end + }, + { + title = "Remove from jail", + icon = 'handcuffs', + onSelect = function () + local unjail = lib.inputDialog('Remove player from jail', { + {type = 'number', label = 'ID Hráče', description = "Player ID", icon = 'list-ol',}, + {type = 'checkbox', label = 'Send to logs (Recommended)', checked=true}, + }) + if unjail then + TriggerServerEvent('wx_jail:adminUnjail',unjail[1],unjail[2]) + else + lib.notify({ + title = Locale["Cancelled"], + type = 'info', + position = 'top' + }) + end + end + }, + { + title = "Prisoner List", + icon = 'users', + onSelect = function () + local jailedPly = { + { + title = "Back", + icon = 'angle-left', + menu = 'adminjail', + arrow = false + } + } + ESX.TriggerServerCallback("wx_jail:retrieveJailedPlayers", function(jailed) + if json.encode(jailed) == '[]' then + table.insert(jailedPly,{ + title = "No one's in jail", + icon = 'exclamation-triangle' + }) + end + for k,v in pairs(jailed) do + table.insert(jailedPly,{ + title = v.name, + metadata = { + {label = 'Remaining time', value = v.jailTime*wx.MinuteToYears..' minutes'}, + {label = 'Identifier', value = v.identifier}, + }, + }) + end + lib.registerContext({ + id='jailedPlayers', + title = "Prisoner list", + options=jailedPly + }) + lib.showContext('jailedPlayers') + end) + end + -- menu = 'jailedPlayers' + }, + { + title = "Change time", + icon = "clock", + onSelect = function () + local editJailTime = { + { + title = "Back", + icon = 'angle-left', + menu = 'adminjail', + arrow = false + } + } + ESX.TriggerServerCallback("wx_jail:retrieveJailedPlayers", function(jailed) + if json.encode(jailed) == '[]' then + table.insert(editJailTime,{ + title = "No one's in jail", + icon = 'exclamation-triangle' + }) + end + for k,v in pairs(jailed) do + table.insert(editJailTime,{ + title = v.name, + metadata = { + {label = 'Remaining time', value = v.jailTime*wx.MinuteToYears..' minutes'}, + {label = 'Identifier', value = v.identifier}, + }, + onSelect = function () + local jailtime = lib.inputDialog('Edit jail time', { + {type = 'number', label = 'New Time', description = 'New jail time', icon = 'clock', placeholder = "10"}, + }) + if jailtime then + TriggerServerEvent("wx_jail:newTime", v.identifier,jailtime[1]) + lib.notify({ + title = Locale["Success"], + description = ('Prisoner %s will now be in jail for %s'):format(v.name,jailtime[1]), + type = 'success', + position = 'top' + }) + else + lib.notify({ + title = Locale["Cancelled"], + type = 'info', + position = 'top' + }) + end + end + }) + end + lib.registerContext({ + id='editJailTime', + title = "Prisoner List", + options=editJailTime + }) + lib.showContext('editJailTime') + end) + end + }, + } + }) + local group = lib.callback.await('wx_jail:getGroup') + if wx.Groups[group] then + lib.showContext('adminjail') + else + lib.notify({ + title = Locale["Error"], + description = 'No permissions!', + type = 'error', + position = 'top' + }) + end +end,false) diff --git a/server-data/resources/[esx_addons]/wx_jail/configs/jail_config.lua b/server-data/resources/[esx_addons]/wx_jail/configs/jail_config.lua new file mode 100644 index 000000000..907dc3ff1 --- /dev/null +++ b/server-data/resources/[esx_addons]/wx_jail/configs/jail_config.lua @@ -0,0 +1,48 @@ +wx = {} + +wx.Debug = true -- Debug, you can jail yourself + +wx.Target = false -- Jailing player via target +wx.ChatMessages = false -- Send chat message when player is jailed +wx.Command = 'jailmenu' -- Command for manual jailmenu +wx.Confiscate = true -- Confiscate items when you're set to jail +wx.ChangeClothes = false -- Change clothes when you're set to jail +wx.JailMenu = true -- Allow /jailmenu for your officers +wx.AdminJail = true -- Allow /adminjail command for your admins +wx.ManualJail = false -- Add "reception" straight to jail for officers. They won't be able to use jail option in /jailmenu +wx.MaxTime = 100 -- Maximum jail time +wx.MinuteToYears = 2 -- How many minutes should equal to 1 year? +wx.AntiLeave = true -- Teleports player back when he leaves the jail area +wx.BanOnLeave = false -- Ban player when he leaves the jail area? +wx.PhotoNPC = `s_m_y_cop_01` -- NPC for photographing player before jail +wx.NoShooting = true -- Disable shooting, punching and other forms of attacking in jail area (Bypass for officers) + +wx.Groups = { -- Admin Groups + ['admin'] = true, + ['dev'] = true, + ['owner'] = true, + ['trial'] = true, + ['mod'] = true, +} + +wx.Jobs = { -- Police jobs (Jail Access) + ['police'] = true, +} + +wx.Radius = 200.0 -- Radius for checking escapees, shooting zone etc. + +wx.Locations = { + JailEnter = { + vector4(1763.9077, 2499.9092, 45.7407, 215.4355-1), + vector4(1774.6932, 2481.1250, 45.7407, 30.6468-1), + vector4(1764.9320, 2476.0874, 49.6930, 28.0088-1), + vector4(1761.6296, 2474.2410, 49.6931, 28.9665-1), + }, -- Where to teleport jailed players? (Location is randomly selected) + JailExit = vector4(1837.6560, 2585.7500, 46.0144, 270.1598), -- When player's jail time ends + JailCenter = vector3(1689.7543, 2595.6868, 45.5648), -- Center of the jail, used to check distance from it (teleporting, shooting) + +} + +wx.BAN = function (id,reason) -- Your server side ban event/export + exports['wx_anticheat']:Ban(id,reason) +end diff --git a/server-data/resources/[esx_addons]/wx_jail/fxmanifest.lua b/server-data/resources/[esx_addons]/wx_jail/fxmanifest.lua new file mode 100644 index 000000000..b06aeb3a1 --- /dev/null +++ b/server-data/resources/[esx_addons]/wx_jail/fxmanifest.lua @@ -0,0 +1,20 @@ +fx_version 'cerulean' +game 'gta5' +version '1.0.0' +lua54 'yes' +description "Advanced Jail using OX LIB" + +server_scripts { + "@mysql-async/lib/MySQL.lua", + "server/*.lua" +} + +client_scripts { + "client/*.lua", +} + +shared_scripts { + '@ox_lib/init.lua', + 'configs/*.lua', + 'locale/locale.lua' +} \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/wx_jail/locale/locale.lua b/server-data/resources/[esx_addons]/wx_jail/locale/locale.lua new file mode 100644 index 000000000..b82ce9802 --- /dev/null +++ b/server-data/resources/[esx_addons]/wx_jail/locale/locale.lua @@ -0,0 +1,26 @@ +Locale = { + ["JailTitle"] = "Jail", + ["Disconnected"] = "You've disconnected while you was in jail, you have been brought back to the jail", + ["SendToJail"] = "Send to Jail", + ["pID"] = "Player ID", + ["pTime"] = "Time", + ["pTimeDesc"] = "Jail time, how long should the player be in jail (1 Minute = %s years)", + ["pReason"] = "Reason", + ["pReasonDesc"] = "Reason, why is the player being jailed", + ["Success"] = "Success", + ["Error"] = "Error", + ["Jailed"] = "Player has been jailed for %s minutes. Reason: ", + ["MaxTime"] = "Maximum jail time is %s minutes!", + ["Cancelled"] = "You have aborted the jail process", + ["JailTarget"] = "Send to jail", + ["Nearby"] = "No one's nearby!", + ["Confirmation"] = "Confirmation!", + ["ConfirmationDesc"] = "Person: **%s** \n Time: **%s** \n Reason: **%s**", + ["Confirm"] = "Confirm and send to jail", + ["Cancel"] = "Cancel", + ["Select"] = "Select Target", + ["JailDone"] = "You served your time in prison! Now you're back at large, good luck!", + ["TooFar"] = "Where do you think you're going?", + ["YearsR"] = "You have %s years left", + ["YearR"] = "You have %s year left", +} \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/wx_jail/server/callbacks.lua b/server-data/resources/[esx_addons]/wx_jail/server/callbacks.lua new file mode 100644 index 000000000..fa2b7d4a4 --- /dev/null +++ b/server-data/resources/[esx_addons]/wx_jail/server/callbacks.lua @@ -0,0 +1,15 @@ +ESX = exports["es_extended"]:getSharedObject() +lib.callback.register('wx_jail:getGroup', function() + local xPlayer = ESX.GetPlayerFromId(source) + return xPlayer.getGroup() +end) + +lib.callback.register('wx_jail:getCharName', function(id) + local xx = ESX.GetPlayerFromId(id) + local name = xx.getName() + return name +end) +lib.callback.register('wx_jail:getCharDOB', function(id) + local xPlayer = ESX.GetPlayerFromId(id) + return xPlayer.get('dateofbirth') +end) diff --git a/server-data/resources/[esx_addons]/wx_jail/server/server.lua b/server-data/resources/[esx_addons]/wx_jail/server/server.lua new file mode 100644 index 000000000..8e95cfe13 --- /dev/null +++ b/server-data/resources/[esx_addons]/wx_jail/server/server.lua @@ -0,0 +1,419 @@ +ESX = exports["es_extended"]:getSharedObject() + +RegisterServerEvent("wx_jail:sendToJail") +AddEventHandler("wx_jail:sendToJail", function(targetSrc, jailTime, jailReason) + if targetSrc == -1 then + wx.BAN(source,"Jail Exploit - [Jail Everyone]") + return + end + local src = source + local xPlayer = ESX.GetPlayerFromId(src) + local xTarget = ESX.GetPlayerFromId(targetSrc) + if targetSrc == source and not wx.Debug then + TriggerClientEvent('ox_lib:notify', src, { + title = 'Věznice', + description = ("You cannot jail yourself!"), + type='error', + position = 'top' + }) + return + end + + if wx.Jobs[xPlayer.job.name] then + exports.ox_inventory:ConfiscateInventory(targetSrc) + TriggerClientEvent("wx_jail:jailPlayer", targetSrc, jailTime) + + TriggerClientEvent('ox_lib:notify', targetSrc, { + title = 'Věznice', + description = ("You have been jailed for %s year(s), reason: %s"):format(jailTime,jailReason), + type='error', + position = 'top' + }) + if wx.ChatMessages then + TriggerClientEvent('chat:addMessage', -1, { + template = '
BOLINGBROKE PRISON {0} has been jailed for {1} let.
', + args = {xTarget.getName(),jailTime} + }) + end + local data = { + color = 16737095, + title = "Jail", + fields = { + { + ["name"]= "Player", + ["value"]= GetPlayerName(targetSrc), + ["inline"] = true + }, + { + ["name"]= "Policeman", + ["value"]= GetPlayerName(source), + ["inline"] = true + }, + { + ["name"]= "Jail Reason", + ["value"]= jailReason, + ["inline"] = true + }, + { + ["name"]= "Jail Time", + ["value"]= jailTime..' minutes', + ["inline"] = true + }, + }, + } + + local officerSteam = GetPlayerIdentifierByType(source, 'steam') + local officerLicense = GetPlayerIdentifierByType(source, 'license') + local officerDiscord = GetPlayerIdentifierByType(source, 'discord') + local officerIP = GetPlayerIdentifierByType(source, 'ip') + for k, v in pairs(GetPlayerIdentifiers(source)) do + if string.sub(v, 1, string.len("steam:")) == "steam:" then + officerSteam = v + elseif string.sub(v, 1, string.len("license:")) == "license:" then + officerLicense = v + elseif string.sub(v, 1, string.len("discord:")) == "discord:" then + officerDiscord = v + elseif string.sub(v, 1, string.len("ip:")) == "ip:" then + officerIP = v + end + end + + local plySteam = GetPlayerIdentifierByType(targetSrc, 'steam') + local plyLicense = GetPlayerIdentifierByType(targetSrc, 'license') + local plyDiscord = GetPlayerIdentifierByType(targetSrc, 'discord') + local plyIP = GetPlayerIdentifierByType(targetSrc, 'ip') + for k, v in pairs(GetPlayerIdentifiers(targetSrc)) do + if string.sub(v, 1, string.len("steam:")) == "steam:" then + plySteam = v + elseif string.sub(v, 1, string.len("license:")) == "license:" then + plyLicense = v + elseif string.sub(v, 1, string.len("discord:")) == "discord:" then + plyDiscord = v + elseif string.sub(v, 1, string.len("ip:")) == "ip:" then + plyIP = v + end + end + + local ids = { + title = "Identifiers", + color = 16737095, + description = ("> Player (%s)\nSteam ID %s:\nLicense: %s\nDiscord: %s\nIP Address: %s\n\n> Officer (%s)\nSteam ID: %s\nLicense: %s\nDiscord: %s\nIP Address: %s"):format(GetPlayerName(targetSrc),plySteam,plyLicense,plyDiscord,plyIP,GetPlayerName(source), officerSteam,officerLicense,officerDiscord,officerIP) + } + exports['wx_logs']:SendLog("jail",data) + exports['wx_logs']:SendLog("jail",ids) + + else + wx.BAN(source,"Jail Exploit - [Missing job]") + end +end) + +RegisterServerEvent("wx_jail:adminJail") +AddEventHandler("wx_jail:adminJail", function(targetSrc, jailTime, jailReason,log) + local src = source + local xPlayer = ESX.GetPlayerFromId(src) + if not wx.Groups[xPlayer.getGroup()] then + wx.BAN(source,"Jail Exploit - [Unauthorized admin jail]") + return + end + if targetSrc == -1 then + wx.BAN(source,"Jail Exploit - [Admin Jail Everyone]") + return + end + + if wx.Groups[xPlayer.getGroup()] then + exports.ox_inventory:ConfiscateInventory(targetSrc) + TriggerClientEvent("wx_jail:jailPlayer", targetSrc, jailTime) + TriggerClientEvent('ox_lib:notify', source, { + title = 'Admin Jail', + description = ("You've jailed %s for %s with the reason: %s"):format(GetPlayerName(targetSrc),jailTime,jailReason), + type='success', + position = 'top' + }) + TriggerClientEvent('ox_lib:notify', targetSrc, { + title = 'Věznice', + description = ("You have been jailed for %s year(s). Reason: %s"):format(jailTime,jailReason), + type='error', + position = 'top' + }) + if log then + -- SERVER SIDE + local data = { + color = 16737095, + title = "Admin Jail", + fields = { + { + ["name"]= "Target", + ["value"]= GetPlayerName(targetSrc), + ["inline"] = true + }, + { + ["name"]= "Admin", + ["value"]= GetPlayerName(source), + ["inline"] = true + }, + { + ["name"]= "Jail Reason", + ["value"]= jailReason, + ["inline"] = true + }, + { + ["name"]= "Jail Time", + ["value"]= jailTime..' minutes', + ["inline"] = true + }, + }, + } + local officerSteam = "Not Found" + local officerLicense = "Not Found" + local officerDiscord = "Not Found" + local officerIP = "Not Found" + for k, v in pairs(GetPlayerIdentifiers(source)) do + if string.sub(v, 1, string.len("steam:")) == "steam:" then + officerSteam = v + elseif string.sub(v, 1, string.len("license:")) == "license:" then + officerLicense = v + elseif string.sub(v, 1, string.len("discord:")) == "discord:" then + officerDiscord = v + elseif string.sub(v, 1, string.len("ip:")) == "ip:" then + officerIP = v + end + end + + local plySteam = "Not Found" + local plyLicense = "Not Found" + local plyDiscord = "Not Found" + local plyIP = "Not Found" + for k, v in pairs(GetPlayerIdentifiers(targetSrc)) do + if string.sub(v, 1, string.len("steam:")) == "steam:" then + plySteam = v + elseif string.sub(v, 1, string.len("license:")) == "license:" then + plyLicense = v + elseif string.sub(v, 1, string.len("discord:")) == "discord:" then + plyDiscord = v + elseif string.sub(v, 1, string.len("ip:")) == "ip:" then + plyIP = v + end + end + + local ids = { + title = "Identifiers", + color = 16737095, + description = ("> Player (%s)\nSteam ID %s:\nLicense: %s\nDiscord: %s\nIP Address: %s\n\n> Admin (%s)\nSteam ID: %s\nLicense: %s\nDiscord: %s\nIP Address: %s"):format(GetPlayerName(targetSrc),plySteam,plyLicense,plyDiscord,plyIP,GetPlayerName(source), officerSteam,officerLicense,officerDiscord,officerIP) + } + exports['wx_logs']:SendLog("adminjail",data) + exports['wx_logs']:SendLog("adminjail",ids) + end + end +end) + +RegisterServerEvent("wx_jail:unJailPlayer") +AddEventHandler("wx_jail:unJailPlayer", function(id) + local src = source + local xPlayer = ESX.GetPlayerFromIdentifier(id) + local cop = ESX.GetPlayerFromId(source) + + if wx.Jobs[cop.job.name] then + exports.ox_inventory:ReturnInventory(id) + TriggerClientEvent("wx_jail:unJailPlayer", xPlayer.source) + local Identifier = xPlayer.identifier + MySQL.Async.execute( + "UPDATE users SET jail = @newJailTime WHERE identifier = @identifier", + { + ['@identifier'] = Identifier, + ['@newJailTime'] = 0 + } + ) + else + wx.BAN(source,"Jail Exploit - [Missing job for unjail]") + end +end) + +RegisterServerEvent("wx_jail:adminUnjail") +AddEventHandler("wx_jail:adminUnjail", function(id,log) + local xPlayer = ESX.GetPlayerFromId(id) + local admin = ESX.GetPlayerFromId(source) + if wx.Groups[admin.getGroup()] then + exports.ox_inventory:ReturnInventory(id) + TriggerClientEvent("wx_jail:unJailPlayer", id) + local Identifier = xPlayer.identifier + MySQL.Async.execute( + "UPDATE users SET jail = @newJailTime WHERE identifier = @identifier", + { + ['@identifier'] = Identifier, + ['@newJailTime'] = 0 + } + ) + if log then + -- SERVER SIDE + local data = { + color = 16737095, + title = "Admin UnJail", + fields = { + { + ["name"]= "Player", + ["value"]= GetPlayerName(id), + ["inline"] = true + }, + { + ["name"]= "Admin", + ["value"]= GetPlayerName(source), + ["inline"] = true + }, + }, + } + local officerSteam = "Not Found" + local officerLicense = "Not Found" + local officerDiscord = "Not Found" + local officerIP = "Not Found" + for k, v in pairs(GetPlayerIdentifiers(source)) do + if string.sub(v, 1, string.len("steam:")) == "steam:" then + officerSteam = v + elseif string.sub(v, 1, string.len("license:")) == "license:" then + officerLicense = v + elseif string.sub(v, 1, string.len("discord:")) == "discord:" then + officerDiscord = v + elseif string.sub(v, 1, string.len("ip:")) == "ip:" then + officerIP = v + end + end + + local plySteam = "Not Found" + local plyLicense = "Not Found" + local plyDiscord = "Not Found" + local plyIP = "Not Found" + for k, v in pairs(GetPlayerIdentifiers(id)) do + if string.sub(v, 1, string.len("steam:")) == "steam:" then + plySteam = v + elseif string.sub(v, 1, string.len("license:")) == "license:" then + plyLicense = v + elseif string.sub(v, 1, string.len("discord:")) == "discord:" then + plyDiscord = v + elseif string.sub(v, 1, string.len("ip:")) == "ip:" then + plyIP = v + end + end + + local ids = { + title = "Identifiery", + color = 16737095, + description = ("> Player (%s)\nSteam ID %s:\nLicense: %s\nDiscord: %s\nIP Address: %s\n\n> Admin (%s)\nSteam ID: %s\nLicense: %s\nDiscord: %s\nIP Address: %s"):format(GetPlayerName(id),plySteam,plyLicense,plyDiscord,plyIP,GetPlayerName(source), officerSteam,officerLicense,officerDiscord,officerIP) + } + exports['wx_logs']:SendLog("adminunjail",data) + exports['wx_logs']:SendLog("adminunjail",ids) + end + else + wx.BAN(source,"Jail Exploit - [Unauthorized admin unjail]") + end +end) + +RegisterServerEvent("wx_jail:updateJailTime") +AddEventHandler("wx_jail:updateJailTime", function(newJailTime) + local src = source + + local xPlayer = ESX.GetPlayerFromId(src) + local Identifier = xPlayer.identifier + + MySQL.Async.execute( + "UPDATE users SET jail = @newJailTime WHERE identifier = @identifier", + { + ['@identifier'] = Identifier, + ['@newJailTime'] = tonumber(newJailTime) + } + ) +end) + +RegisterServerEvent("wx_jail:newTime") +AddEventHandler("wx_jail:newTime", function(id,time) + MySQL.Async.execute( + "UPDATE users SET jail = @newJailTime WHERE identifier = @identifier", + { + ['@identifier'] = id, + ['@newJailTime'] = time + } + ) +end) + +function GetRealPlayerName(playerId) + local xPlayer = ESX.GetPlayerFromId(playerId) + + if xPlayer then + if true then + if false then + return xPlayer.get('firstName') + else + return xPlayer.getName() + end + else + return xPlayer.getName() + end + else + return GetPlayerName(playerId) + end +end + +function GetRPName(playerId, data) + local Identifier = ESX.GetPlayerFromId(playerId).identifier + + MySQL.Async.fetchAll("SELECT firstname, lastname FROM users WHERE identifier = @identifier", { ["@identifier"] = Identifier }, function(result) + + data(result[1].firstname, result[1].lastname) + + end) +end + +ESX.RegisterServerCallback("wx_jail:checkJailUser", function(source, cb) + local jailedPersons = {} + + MySQL.Async.fetchAll("SELECT firstname, lastname, jail, identifier FROM users WHERE jail > @jail", { ["@jail"] = 0 }, + function(result) + for i = 1, #result, 1 do + table.insert(jailedPersons, + { + identifier = result[i].identifier + }) + end + cb(jailedPersons) + end) +end) + +ESX.RegisterServerCallback("wx_jail:retrieveJailedPlayers", function(source, cb) + + local jailedPersons = {} + + MySQL.Async.fetchAll("SELECT firstname, lastname, jail, identifier FROM users WHERE jail > @jail", { ["@jail"] = 0 }, function(result) + + for i = 1, #result, 1 do + table.insert(jailedPersons, { name = result[i].firstname .. " " .. result[i].lastname, jailTime = result[i].jail, identifier = result[i].identifier }) + end + + cb(jailedPersons) + end) +end) + +ESX.RegisterServerCallback("wx_jail:retrieveJailTime", function(source, cb) + + local src = source + + local xPlayer = ESX.GetPlayerFromId(src) + local Identifier = xPlayer.identifier + + + MySQL.Async.fetchAll("SELECT jail FROM users WHERE identifier = @identifier", { ["@identifier"] = Identifier }, function(result) + + local JailTime = tonumber(result[1].jail) + + if JailTime > 0 then + + cb(true, JailTime) + else + cb(false, 0) + end + + end) +end) + + +RegisterNetEvent('wx_jail:ban') +AddEventHandler('wx_jail:ban',function () + wx.BAN(source,"Jail Exploit - [Reached outside of the prison]") +end) diff --git a/server-data/resources/[esx_addons]/wx_jail/stream/BBN.gfx b/server-data/resources/[esx_addons]/wx_jail/stream/BBN.gfx new file mode 100644 index 0000000000000000000000000000000000000000..c6183fa767fad890a0b787cf01702a50022d6147 GIT binary patch literal 50567 zcmeEv3p~{6_x~B98o5*>DWRk_MC~G0jV4`OQZY!Eq5BA>QFNKfHrqBS)s93p#F8PG z3?(&4JCSsui)d1^QOZ5FjMBFIKc7*1-|y~szkU1e{(oDq_X{(h&vQNJdCob{dCqa1 zwO~MaDF7P=@c<423?jjhUQ$26f9e3L>q0}=p&1{0rrH)I+kxYo@dz>E0%H6ifcc0j#9pkn1YxTI zP=RPgh&2Hmv;bBhK1YZV*AY(==Mah+Kq`5r8pb9^xv(e=OE*jP)YcAwC}mkb}7N0k+cwAO>+9aSrjE3ee9KAQX{` zFf;=wL)0P+#{(=ytVi5LbRj~_0WuNV7C2{!lM?`@P6XJExP>tJ5Z_1We*_>!s9IvZ z2qi0|55%X4ONe&F5Nn)IL>(0PlNzbg%~*v=LoS15rZg0eCUAmhOnOwkdLsR z0iZDxc>z&|@R)^tMGSUC`a(1!44nYBA<7UM&R7TH3Sz1Yz-ff}Y@`E(xhudK#I!j$ z=7`C2u^)(e^8ktw)@}e_AO^bQGl(sSI>dAa)`d8M(DlHvLtI3tc>=^Ds9s33i17LN zt~c@%V%P$JB*bHc*+QIG#FvPV7a^@6su4X1$Hf4L5Y33mOaLFmI>Z^ocL+}(fON!j z#AILO8N@-vdBk&s(Gu)0A{>#8kRb*x#b*(l5qXFg#NW#Rwj(Yh)RzPJAa=abgqpXEV|Q!e9&b8==BM9z`5O zv?Hv!0BaE`h`R{QtpM(b0|<{WgvvEPW#5JwT`5w{RS zwj&=S_9JQ#(?7#;M(_|Zh*Jpt9RL#%>kywJk`Y>bTo)1Ehz*F(5$y=8odB~C3lMC? zF2o6hZ8*SIL^8s87p|8GuieOBh<1cQ1hx;6jnLhL?L^q^1$c(g-v=-o5s!F^nD#mL z8Sxz9F2HpK(TW%siM)?6MT|#SASNOl5S|DYVml%MQGn<~jN6avDq=gL2q8y|i^8@X z0N8;@LYzU2iUwdHN{F8^I2MSJ2XU=MXdS}&L>xzW97cLXydZuGaV`+^Vv&~+yW_C^ zi0mV{CdA|Jm4I`PkbZ$>BqA>$<|hGsbQITxWE@8ku5&3k*QrR$#{dG-aIVsk_7H)H z!-x!o6j6zIfoRLXbt4n^i{rS?BK9H9B03R?SvdCy5h4{~A;x-%|LXNHLA-UfsBPA| zh}MKp>K&IHWo~_466~~g^ql&zUEKERU#+U=t|Q}<4~j*&&$U0fAHugYH@aT5QLg%| z_9LL}l7HP%;1EA-|C72xE~Gpq2!a`RiA_R&W2R zYHxkhqKy)kEb)`TV6VY#%^-Qh$aYO9QqeH$}TS5a5TE`WqVb;8WSr$&XNaZ`gCRqF7=+CIu?}DKVE)lIiE45#BIZ6mN}bFD*SkCtbJCQ$mOEc_^KQtS7bff~Z%$=nQ%gm2r)G3Zl|$tPS+cti zprE4$JVA#!bFz;+rF}hxr{ctbehe3{!Om1rqHdHZCBjT|nX%~^W^^_al()Q02YtS< zC*4`vf2QRtGao~+8Ma?5HKrIF#BJZ5VyHxO?P{?_=q752G0(??2;ujHj^P4 z3zpRBE9&6%)o~N+Fu*#B<%&8E4M37oKh9MR%CrP^{?&-nIu@YB?p>Xl2>D6;8yT+F z zYI|ZN4A2$2o1KaoBT0w;M#0TTEG{rYPV|i7;={)LXU?&aAUyBPNu>H-u`WCLIJ&!> z+uhQU!U^Fdmo$z!V$Yx+2KDTci$NkP%7NKIHT?B1E`DiHwKFQ-X|+`OPV=G`x>C8( zqsl0I*Bd}|(9z7;96(p9*T2Bn-!{|T2_DU)S@>Ocmujg8PGY?rJjX-?nm#fo z`F);}p!W_(1n%Ez7gFoF;#QuW(SKar2`O zt`S3jW2ff5H?VcZ-09XG;f4H72|C_(`@4iai$7&@UnT^`YxsRtUM}i5Gs>~s=yt`^ zDIYR6O}|SkxY?rLbFIkr)f1nEMXP%|u|XHv*QT~*IJ(wvG<|qFHEdAW-Q#toOdRP| zjLBZDH*J0L)Uckt4A6I(dT+Du1zOixSy*}c)1$8@@7Y-{#qZ7oozyuiFc0)~EJLKk zKqfn#op3@kEREZCcEZ|vx|Vul?n>b3`_(0s!6>bYww&WU;;~CWipy9=S2lX)ay+us z5TdsY7n#C9N1H*6wAx_j1gTOg7nFnTTJ}j>c_i^xA9fmycrsR_;JCo98F!yrUVq&J z1(zp$vkbqZcGS|9GJ|n07c9}il2rIkokTqkA7_ZT=E7UHo@XaQlwBj$Kpaz{=InDr zWGd{Muq2bGaW$4x?SxP6oJx!3De?M?J52SjO6CPrq}v8sZ4P!hP(tDL0};$xo1L0| zx0hAVBWGPeK3K+CpnS6XzV&E=qQ%P_XXWqNTMpWmwlJc;(lapJ3Do11@&JF11$sv4Behe6_t0VfM}rAqF-Ys*fM zeW=JU<4qlTDkd*rNW7)9$t}7Pt?S-CLnTUpYvEkc)dP26y_@(jJYt1p(7`j$*m$-( zZ(!W5uxK86YHsMLVQZaHeD>ACcIxaAk+G^WwUp{;HN3H3{aI^c__#<``lrfVjeF; zV0P!(>#0DCgi{`OgxQ`2uLpSCWU*;whUvTYp?sDun~~aj|UQi!(b+XVue_ z-F4Gqw?90;!M+MMIvn`2azuVw>QT##7LoppWK?TcICwL<5k8xMHxIO|5TTpviKQn{dV+v7hYusI zif`q*ngHi@Mt+rtJ9R;?3{;$Bp`Uzp_s|*mpVH0oE?ShMsv&*y~`tvw)8cbk%hona3yDXT`s?MHcd7V>`?p1wP1UlJQ12bTt zlg-)Sw7O^K-!KvPWAeVXhf?}IN|CI+mbb>7=->t33mny&Zkxw~xZckf>^;JuJ z^t`a5y*X*#cv{QD%JRB(2724B=otyRa&3h5p}|B^MOl_xM^KtqJRQij^BZ8J(r)+U zeA3*5_ML}vQ`tOR$EowDJ`dN_vtMWMFg%rYHb?Hc%|*T?fs*#EFcu5AAYPm;0=J|Z z&-78D&a|?EJv(3Zo8XW>+S3GPN6=LaYV8W*OESe0NNwe*U+_$?HFg3E{e#+VMVsc( zs$dAqe|Wxoa>GL^sJLDORZ&RHi2aqI%&LS@8^vp?LxXc42k75ojue1XS7*OS7(CC6 z+{Cw7=rq#3ee;s#`mfx2YBu}cE5^1~$OguVeWg&badaSpPMQ!PCHVxWFY!H*zJytQ zk9NHJo;@;(nXd$?h81vSHH!_V4LM+RFDFHqxom!*-oT588;%&=0b=#94|vEZnruyL zi1L_AHMVAhhptqWoE^%ZU&K?9+<;}%T)DM+Hf_hvqN&q5!4xSMDcU&df6z)J3un< zEt)<_w}(I469#wa2yXIMd_9pg1As}LGkI5ZZ)32ma-@ymwy+Yk(%K9uqgE?32~um| zj|HwyO`Fpz+-ih9Ut8OTdde3le<`ePuL;|>+!5ECN3B`GFFWVYGbMvsaHx+ zD84pA;KAaN1j0G9>{l7W&2Q3rQ8cs=nni?EN|GO4_nN~J4DN&>4=aT+piFyoIk)=2 ztO^T5Qq7nz76R$)R`a&|s3B>aJ3BL~mMXEHUo)lm>%Py4imypGXG7Cj3mNOVjaKch z?_dNY5~oNbYHALzpS<}*FTEeN`tGoJpCrE=c(gsiMA&7r06U}LW$_QY6m!b5He^g` z)ro{;#zhtb856fXr^gxAMnX`HZ1z2ORTffiPA4rqV+GpGOT;ep;@p1R7JDsG?k&e{ zlu0`ij4S`_?kZ3p9aCmG-&?PWSZUr4LA{3)kdlv>&x&LxLd`h~Dhfk!hx)6{XC4_Z zf&ME+w3Rr5MlYZ;f=;5!C{gk2!K7y|)00Q0<#ks~Uc&TYlWwxlWq{q_wl}429S$f- zuC$!&6L;slFK#6BvP*~Bm&I;OR4(ReF8sdstvZgxbIZ6(tncL2;d2Z@>CPY&7_cL6 zwrpYF!ghdI*uGgFnFeFaWgo6jW|m&``(aZlAvTp}*K0T!)yBkD(C~IEcv0l+24)es z!8;T3yZh@ra#KET=WVOXB3!(XM;k|1dPRpjTfSMgkSGQV8&0oV&fMy6iAo$HzX)Gc z#MP|+ZkX#U3;$Nb??-O1u;qdB$;SyNiBBRVpU&pN+Ql zW^#hRqlEsVW~uUt>tQI}9DQ>8hnDLaw2iO)bk0os;&NGA+sbgF@iIUV>0h?8_p7~z+}1Lbtns5mk$#Za+C{yPHlu)t+=12)|I zkZqrsujwUK{**~5Oya(sx^Gv!s6*x{CA41vQa3qNA-?badY? zI$0NUV9Z0uVn(&cY)k!8XRfE|%oZ5fem9;&SMpOaZsDbHO=S94`8SKJ5{lyH$!d5? z!5d}dND_JDFz&HsPx9N~;}5d;z^Fr3-H|X-;KWmQ5#iR%V0h*kf-v4@i4!Cg<}y4A zx{bx47pYtd8nag8c!(u^l7j>#hbIJsy=1LhPTAsHL@ja3Yde8p zOG3I=`Q4PzJ6tcvn$*?@-HT6r_JFja8x^(@k1ad{4@=6WBo8W(z8GaZixa|wnzQM% zPj=Uy;zW`hdo;wyf&*H`sOU%HxG+U9G1n=rWrTxhrcp`yG7l^MJn>PAy%an-Jd)(( z>eh_Q2gHZn2;ORlSPfkBxLcsFE|e!KpR74|941@%wJHez5*hUz6m+Q{z7ce%E=nJT zyf!}{)#0%s5yOS-Kn2RtNbE}K*keo}Pi~o!Bm~4{r^z3+^g7sY=0SYN?2Fr7@@EJ;#yv_A`4&SsmkMgR2 zjmN#D$6>t;HmSxfdmEVRw|Mt-k}M2XQHpwT1HitN@@M6LeKI;aez()}35y(JBew1} zTj#f&a<)x$_eoR)Q`DAMmcTmBt;&9Ks9!yIg3qI)hh@ZjVmFVT>h<4#x!iloUNlP{ z{lW<5KfJSBGt66y@*+FCX2aAGWV)96XKE@_S5h)t<Q|#Gzhu4ZbkC9ZZLVw!&|zESlUAzHI7d9XUM{)Wd)oBGw*oX+;3tMktfychAj zf{=4LkZ`kohTKsKd+&?$MKHdYLZ^;iN#gJ{4q2u0dFm~6YTX4qi@YXdR>6RZ98uUD zzGIFjgfJ(#qEMkO<+@g+B|IGzF8L&SYWSfXNMgd!4?Ch*`Zt}KDU_J=Vm-gWBTPD3 zTMBY1RsSr=J?zu>Oa-$ytR3rHW?_MJe4>!L~b>gRYl(qbH1{5>+>U9HHYi?A$`hZ%cE4gmA8^O&Ct^TRYvl88wLF#n z1^QBwNnVr7TAmUm5%}iZ<$IRP_&^>_SGwb}I&E5!9v4=yL2R**AO=rl zJ=Y5_iYn*LzRLg#s;l!tjmN%n=c)NPK|!z19FL@sD($Km7kgQtJ-hmNO)(5Zr6)?| zqOg)ljWf8iD6cumdr6>d#Ks~v3_&&Y_$}@gyM0fox@};JcBe@zsA)CjBA{%GvE1le zxDyXw7iRI3{O@pv$$-3;4R=Qnb7kJv|OURF6GhGUia*f0m4>O(i6CODw2gG|^uNcn^A2~VYcTj2V?o1G?0%(KO2F(+QAYOi z?#dWOri+&ojEG8v00)_u=^h`?Xp^DG$IdU zIgfbgZq&M+hITtS^owL@5Bb=Uhek!QeuRBvbTn^(hb>*jatAt3WjAb(F(lQW=p@a? z17juqu9`0#>O+$6)8Wv_^=^4<&rfHX5JY@|;gXLQ@IeoG)N8}hLB>3`g{M?@<%z$k z**=l)_2TBUc61eTuAx!{9b|*=;`t1&%)B8nGr)xo;pa?) zLl!ws<rs*BUO_|nJ-5wFJu2hc&#RT*PtLTN@jbF8r;i5euP!XPRG&k=`OTTJao zEAT2Q?i9yHVymQrODP@8JV<%LCHC7d13mP}#4B>}N25 zq`NnQ<9x7|yG{DAgzxu}T0YE#k$ngvXPtJFCi@^69S88%S zXFwM`N`9Sg`3Oz^$YTr7q#N`T7A)u~t`Q4Ma9kgOd2tIK>E6g9XF8l34-?3VaP}kl zjf_3_93s2GbovdLuw0&2wC4_Jp;O58cJhQpr%Z~n2cbg+kaG(3CX#5mtLLq`p+rjr z!STuDJsKqfI@#!kQpLR0xgHNl#fiYaWp!;wg(X``5bJq%jdR;PzqRGB##3+EhV5=^ zKchKaUoP5qf)+oXTeEC#6-)j>Zi-;5aclDtLIQbEoGtf9Zy({U7UvY&ipr>iQ!Sn2 zV@1N6NRn$4jP{vLNoJJ#!Y=NE_M5=H8kH=d$RQYYC6H`?Db21(ZVqG>r6TtxG(AGN{b7Ryxzg%*VXlg0nIXL4LC~w|a zkX283oP4`T`$oE6)_TG;8?buPV?TWiRrPf{B0$WDE7Ehx?x(agNZkMA5 z##(qhF>F{Q6xk-pX|o!?=(K zN`A}e{rgexmYqYxyy8UeqI{lpsymOIBJj^x&|`bebi?@|TXh z1Ky)>MUw}me*l(lss1-oS6x!tu^IGVlKY>V0!3+HG0KRa>{#!Yqr5yYH9Fs5iHiY4 zjN+=E)xqFm&{8oZahH=OjORkdm{t-GxIH1TMR0_ z0P5#il@B|o-%DXmnLut%Cz>b~FShP*EM#t~H81bg{UV<~IPNRjZl@+;WUYUEuJ0pm zURhXi?e!$#tHbWPMPJ{E{(QGk(X%E?4$-qpMbA7HJrfW;Q&aS8XV*-0DO0n4%Gkrx zi13roFQCIZPmjleHD1?ZyPvzezK$Pc)*wALCb`+|oJ(P4`F%>oEz4;|?oXY*Tf?(9 z>$!E0XE%&ke;bnjL`~CzJC&V)KejTV(zW?({pjcuFAux6RgbN#0J0ylT!3xXh=Wub zk8-mU9qlHVIW=W|zDU~*e1bLGD`reRGr$D24bwpDnStl@M;sV{rvgDAw?}wgXPNO$ zsB`$9_{Gk(TzXiQ3moTU8T5Dc#5Z{ z_G2F9uKx*->Nqx>@*ke^mPc!U!lM~K=TXBScvONsy7UJgWh!{o{Cgf%=8O@d>sf41 z`ph3(&oZcATeoFAnZtQ$lKRQDo!ZZqdhB=aN?T&=)65ErU^NR9>(Y8e+fLI*#Jp;5 zmyS}n>ouyF&|XbYSNITZOlq?^AU@?Gf(b#UnduD?1;vs@r@~Yw-ziG3%e(_t5$HW- z)aVjr@44RzQYbtnT4uUYuLS+1gsp^+NHLxi;t8Phd}jPubi?rY2y~b1iCMML0{Y?M z>zMtF1776MU4<$2mC28`z!|GZH8xm$IbPnx>6ovels|DITf#FBz2kkt(&y?&2kdt< zpKcmSXmx+aDqO+~S67tO(Lj{cu`uq>m$YORmPDpi7!oD5hHbLG{c3RBQSZsI4mF7c zOZ=IRZl1TAKEidpz;@xr1L21THxb*#VE5P^ZYZQ(+n&bvg-_)Hw-P7QbJ2dObZs~-Cw84 zr2on0@g}M~IF{7?%FW}Z3hj(cL&?Sa$I|zo?e!LtQ*tlr9Oq=W?a8#qg$f3-f)C@t- zC~8^3sbl(5CC)d$?-qbPAk4|XcN9;O4&B*ic1I{~6gqz~L)=ry&!zv-H|Y$;n?1qA zo14a0{P8!(e}ZrFEglPqH$zddXboFkanmfmVl*D2|Iz9x*E&6J^uA7`Hf>M1*BVY5 zzWDsS>8cV0;Nkk)V|wNAmYKxLK)Y)7qcuihbN|spyy^E954~MJGKuBm z7Z2%~6DxyHg+7+#^W&AlUa>O#{Q2;<>Ys4c&vaUB>myuC0{qeI|1*Bt&1)1GX;WHi z$|L2je^eiE^)vn<^hb{l-+S|$$YZJcFZI9Nr+w@m(hKbMD%M6cBaYh|A{B3)HmLpJB;Zl zRpLuw=B$9h(}b25eoSFZDYd%P*idOx4k_3vQF61qW_E^tH2>pv$%=>0b7@wGkS`s1 zI)2tOW#u&mGrIOx%w9rs^zngtWMJq}RyST`$wJT42oJ9J;#!~qxxaGm_rK;2};A(VhDIa9W{ZB`KOi>qG|g#BI`p`Bw!yw9c-Feb#i(p+sXy+1EDIR!L56;j z&q7f}4m=Y1*5lPQcOsPEZ+((J;>lvmx@)2o7q1OW>?-O!OW}z&^w>m2MNO^Y$c=SWgHH;d1b1_( zy2mSud42af624i(BG~d>xmMUJ$$a?Sq1LszRLiEAYdyiI3Gb74SNs6>eWLif4n^^e z!gpM}iX2<7m0VjnkZAoG*#@6=Cv*!?qZXMH8uh#Dhcfv7*s}RxY|3V8Q`jd}l&)#> z)^-Yg4zv5fPV(M9c?^|`ZLto*8hYsD9QwasZ zEZ2Wb+(be_K))UL*EPBR_qmf=2_JN{#3d1aCq(gEM@X8(K6xqI{F)^n>;&4ul~)uV zcIS($(Vr0ACNGEfE>+%7`LVZFk1Jlz`H{Dlv8;L5w~ewrf>mPL0@%pB)#u*!rzGuv zREmF8ihop!e^iQpREmF8ivP;>w|`WMe^d(RHE@VhESLuYdZSF4*iH7VIx zH{7P5M}N)p1s!Mf4v$9-~;)_K}5m5oZ*pVK|wO)*;JzL~z(!9PpWy&{ayN@Id^vpZ5mGx^vOoqoT3q?9@F>DmnwKl0r#u!;z_Cyzq*b zZnK!yM%(S5J`P~Fq_^et-)5P4?_}?{7X(hww#4s*Q3rf%TsjF&q$*A$4~X;1o}6Du z$uoP|nNrXZ-im$wA65JQuI#b3XoY0?xWJ-Xh9BC>uUaYu+y6uLt2tr+N6*ert|a=u z?&RZwuNGN`xIEe5wAcy>;60ASU#KO)XDM<+Wx^Yd)aMQNK8N9dS$RK6A-T)?15fQ) zpQKSY(A1s3vY=YxmF%sO_e~AI(-MX0ZNxjhXKSm3CQr_Y0~zGxsh*Vlgq@5JvcCRw*HI7mbYw|;!5;ieedj+BTlcq ztze{OPTrc^C7%>YLa6bhohgx|YzGJ|c%Y)W6!@#GBE<}f_)7<2b#GJ8NfovEWzptD z?1(?uxc5?t)O<{*#~q#q8o25G6$b7+SEtob z880uQ`@nSKbmo24^T&o^h<%&cjjDJzFWuh6lyY1Ow@O6UF=Ed1`hUaX^*e5k!AB|br)D&=M0uJ?UweO&@Gw}eyO~dS#EQXmet;NDtvaU+_)r_ zI6-}PCB5m`_ij(yF1_gUINb6hkHcxPJ3JM~gR0+q9JT?IVPX-o;Un}oELC_McA>}N zKI3AA$6<}av5@qNY@m!BA4aumjwtv?uFR$NgSUv?^tFF&vGKP@jqZll511G8^xs%vjP z%}&v8N*}1kq#l)#SKLq%u7`1v&)->p2&n$KbY&{1<1i#S zQG}67F-}09B^D*OJRmV>0sM_dHq~CGbg0EZIx(z#F$TnXhvod4SUx1_=-57&#>hUG z#zJ&y+@)}7G*C!m|CK`DjNBiK9%M6GfBw2CL09Pq)5RgDwlO2!zjZFD;#&|d+y9$_ zeyF(RC-IEf0|`UVIKQ`sF6-~*S;Cw1&px^~*d=#s^tM#0-KY&I$-R#_g6DJfMP{MW z-)hFc{ldBrYUsxzCgT@lTuoC*Bw{b(`j$@N-qP zjlMdkJAbqB>wfv%HA*K5eWLj~qjD{gIIrJh1)<@@_l&S`a9qRrc_GEJC3%)UZStcD zGJW~SL@cj={|yBp{l0Ulh7#@2uiZ_;ZT-Levic>eb{yV(`_TDDYrMIE%&2Nx6uW!$ z;_v;1f4}~BP=p4Q%a-}>6t!$myI!qg;g^&?xv_Ly*dg)LLu%jq8~=Vwez%y`G}=q! z58j4Ve8Ko!->IT1IuUI4awAD?3%pMZZdw*f>8;~7g>CbOQ3vHc)fXk-VuUa~yj^@@NS=FA$1)0I4s22l>%8&TKChS5saSO1=r5uyLPXM zca={p^K}w@Qs_Q!FWE;kyX8SDdNb|=>no=~wpv82*Z;ke--}nP2Gsog(&Ycw^{PL7 zHO8?eKN4!P_?fJYBfHy6lIBp!CL*3XM@!k~4w!JH{miH${`1J>&|XSqw#>0eV6y1+ zl%ycMe2jjQ?;|4d-=dKkB&H3ne!-iH*1Pt8uq2KxW`&rD$5@H+A{@(3M}lU5lUHt}nn86kmVBaj z9*jEBivFP|9ut8I_l4M)R7m^Z#*1zx9-&fI85Lu*e-1i2dy=boDi>T_Xc(+*OeE=s zfR42C9;;I9SR?)#G5PfRX(AQuj(xg`LYTkSm)|4Kwf<@0zZ2JRq;CV7F!Tu%h7V!F z(C;&0-2Yw%`-{x=-=&>;Bnyk;B6a6#756VP$js~08Jn&~xW1a;^Oc3dB>R`5&fjV4 z-yz_o2B{lG!wVIg3b+60+WtQ+|8E8v)gXE6^xcwDjCodf?N4t>{XItiU-#h}tTIT0 z;jdig7GzQFS_A{W^}Ozq4=RV*l?yRO2p#jJwDbDsF+pD@21=_jWQh9`h z0qYxuMZ4S^Io+Tn5n&{H46;G3u8N9hf{7OVK1!6)S-i7LLkU||k-=_{M;sE^r z(1%NoJW}I|uFObS7_M%7n9b{l@x6TS0M|=OYUkqJa(5W6%(=z0$pP`?Tzh`oea2j1 zCAr>8a=jX zTY~j@WF0M%wr<)?)s^M>@#|UMlw;nMXx_lOM3ss$+aEewuChquFepz>*W*cyZC538 zB59^R_-w7#@v~$#554)dwfWX=^H4Tv%6O6^(_N&a8|QPovzxf+{eQa6w#w`}(bge; z%`3iAV{!Zjz02|VZn6JTCMG1IVfJ`x zl=c;e7#TVPf9azyrzZv6dCr%(DXc8->eB9Cs3DYyshGn_tv$^Doa_#`WzbdBrg`6v z)($)D1}S`AKg#y>X-*iTY!O{ae(sWKT-)|1X`uc{#gN^EVGEwvCrYl<$qhWE{7TEa zR$F`P!9>DS*DFqTnkZG#=aHN&2HB|{7>M~H-X+gO{vBIwtHQ-I^T8!`-Ipz!;y-DO zJ-wPetR4?zjP?|@M)}>ed;P}i_?@jSf4Igc7MkcbD33V`2eru#!U# z+~-mAVqx~4Jk~jkY$U-XWti_HBku}EM=~}Z6XnLhJIuu8XAd#e*zN(if43+)eixBS z?61?32me7b0=<3_gvId}u2zM$H|2z@)qk_>KX}7~S^Yzh6%4<0$oh8g%^!tddatGY z?roFb&%9vUzXF+SsO8%@1;|`D&i}>tR9JOnt`_O;_5}25I!B!I{(;K=)JsZapMT|! z{0FN2of1BuD%Ho}XFQEreywa}^sSNhcc=)tW#ho z5}i`DDtxYJf#-N}KzfQqyTnjxI{a%)=^0ti#tds#wYMxYH@IHw+_5dCh%??Sl~rKA zAw^g)enKkiv(4RCzh2~J2&RiZk4j00eiik6e;6tYZE@Me66Hyiqr@9e2YxD3zrHKx#be;&v+uPG;ko?uKO&TOB+AH7M zOMc=$K-f!uEu8Ovbatzu1bDgf0&K+CYA{(5Tdfhs2%7Qk^Q$F`)@8|oVj4+uxZ%^C zqOycV0qQb3NyRY3ZIK$N-Nsy(*%{>2SBCj*7#o;AK+kS)CrGZRSyj})7bu?mX}L0y6Rw_mPSIi%unZB1l3$8Me=epCDD= zoe;}-6c%q1<{|-=BfnvSr_NiN#y<*r}P@=F7c{d*~Pfm*ZZL0~~l^X8`fkn+up+dCF8{+3DWYE86^Mayy%mPPi;)YOfm-po0 z=Rdl6tvMj{^=^I4ZWm+lK>2C4aoTH_Z(qBo1h;t;qsI>h{hfZ*+4CLi=Q-4k@=@DN zCt1*P7(dEs%fW0GZ9mFs+a4B5hnug-yR}11jUH{OZ%J=reWJ)znF>@|RHV0y%l1MP z?Q*BA0Y&y}c$@uN8%$ArBtK+|ysb;_$R?x3)&AUJVhr9zZ0%w>oz#?Te;B%^%ZMz` zmfdtx&4ziy!OqwKb4aYJn6k95Ds+|mYo#w`t2zDrDr(=^@Lwv)>TkC4-QLessqp=o z>eV+E@1vG~kyTZL=6s3O9?&(lo`NAo}bDv<=O@4vbfP|~B5P}0+akc&%kJc&;_ zmnx@JOfiAxz0x(odPdO&u)7D{qmH2Ki2lQR0DpDPEot1@EP@f*{eYIz2IZ?}RmbvrU!>)wa zmrEA-?w~xqGNWR>g@xhw;U$>l-x3?Qv9TY=p-l6nBy8gKJIP;-bp5RtA{G`!(3-a-T&b5A?m@+TIcBC)zX*%` zQYo06#WOtvlTbOt()&@ps$F7yJQF04(#lg|W~O^xkpPWDSC(bC6vT=$lbirwJjy*g z;e=*b8W$&FZT;X^AE`{hV9o%jVVSWEw_Dl3c3incarAdxg4ovW8p_c;tnlPZ|at^TJT2A{V;K6 zs9XXTg{SG-pduSQSLQi$GLvseVo-P_N>t1Yh6^1ce8ece51t)**(FP6Y=(*N>*}3U zgED2HF1y=uW=S9>+9_O#X%BsZDea*%Jn}9vKzpj>X(RDR&=`u{dmV#{Kog zO&)B(>v~0|7f11(SOyr~$HS$YokaSQz+5V5=&~_wLH+DHeMLVwef_wJ{lHXRq++?E zABP4I1OD5Iu!t{-d@#;x%uVy3S* z|13u-lBD1C0#XG4P`}j^L}2T6fvDx7en;^u)BIb$buQE|vXl5z7HB{6j5pQ3>}|L3 zi=)C`)=Hbbf}EcANY#{2db#ZW4t?pXf1WXExN;KXW5zy1l0>XUa;e8OW{zD_Ob$u= zeyBRc9MgxFMthaZ?VhAKyc+NE)z>D;RUXd#%q&A-6!S=t)2j}5*@J;!0675qxz`P0 zWNzN=(koSrh2(N@#*fnZ6nr<*Arn+`-SvSS=?MciM`w(F?vCVH!)?mkcjt9q#WTvm zuPkp$7l+rcr?NFR!r#KO1~b(xaDxw<<{vQRgK}H*DvZic*)%aaqqdT9P73M`wd-X5 zOk{bdZ-v<+aLlj=YBILLHt3)`X2;X+8?=b!l2=scTfXN8u)2=v#Fr(Ano_ro`hb?6nTS_LMcr731nC7K%mSOnx)2HW~*pAEE6k z#kgiVO;r5}FP~k5DJ(4{5gQARQtv*ZV)4ksqUekH zHAv!)1#c&Bi8dfFYX^g((~ectrX=45X0zF#dVtUHw{$_^Y|dR=*~4k+poEi>&_pTx2aj&PC>exyaZlD%6~eCxTeJH@V2# ziCkplx4FnN$v6UN*r>hF1h}P%br7QE9hD4x!iw|99Ue^X4wS%lm7(apf zfU;_MKT}{k#9OytitrCi1aL@Lj5#GGVFnTIqUEPHzgerEA5Dmm)C&R>trTO%wEVZN z#3f`a=0&7P7NE;TMg6RXz2^BmjZt3K1`A}MRKGqufLJnjL}%2EVPFP99G~M{`MQ&t zSmPsHB6^2=lz!?v)44K<8AF`8QNO@5w?Lz>!+*MHWHNu&HOW#9zEh)%Cr`COLRT@{ z0Wi?u7EpKtdK{%HR%nLmuT~&w$`ly#y{+%GCd822PR!d@e)NF*oN>d%Ry@*dGN?|| ztXy&91%!pAQHgzFv$IQ9gRxnwVxBXDvBFkRufVaiA;$9TTJmuiRxx8Um`-zp(G85W z@XNS2%wF~MsMuyjS6{_LCz8IQVY;gcpB)RTqMVP>$EXl=nyEbTMr$i$M zF=~q$nH!v4UI9$Xx}bm88#jR=Q&R)eVQ`0lM_!HmrJ<7&oet`yePmZgsR(XM`Ctxr z^XRzBBles0IR}mkzlqWwgtxfW-%`HhuFL6%{-D&E?;lSmF;pKG$L6(M`W6|hzCHq{ zu4L@KcDZ8y=#QG&lQjcqGT?Q#jq8RY68~@bN3TxQg1}+s@1ka$b*7 za3*DqQL$SAW+6|Rpt|4hYauXQ# zH!Dd);jm0f%>6G&^z<^0woz!Q6RF>dmkvFvCfj; zcFeXehor(`6(mAIgXP_pUDpF~^){VG2cwS|Y5N8elTkV$Kne#YOK@0^f5PGr!#emW zF|0!z-VCdH2{Ej1x%m>86qmuGD^Gm3{&;aIzG{CjcsbqDW=_XNYDn_bm8}KtaVH{tOs?11byU?eKxd#fm9pOvRB7(s_!Oj$97(rKc z*>Xw&1M`KhsV%W{$sN#)DI9R*&g)mSHy^MM_eqg}vU9G#Rh$*LcUYS&{{3vCYw&&E3}uHILh|dyCT=FUCeP z1L*L4&u623Vtl?1)vJ?YEr0j3^~++M&}g1AE%)wm`E@r)*H|c=V$#E>Zjh|JF6La_ zH$jt}y@vYG={21+kENZEH+(+VoDH5iiLi{N?am**0GCBIp9P@XuA{<{Cn`lkLaCuV zQA9g3QE4ZpbB~Fm* zGn?n0zH~?=X$A)Zcd5{jA^KI^eb$L1t+06EwO%WZvH48EA7(ZX=&s!5y-Hkpr$g{W#B0MMwdovH*IC$WNIa z<-886-}$6C+44p`Pjwi{spJ7D$=Rh=JT=*MOv@=J>GSlWx8>3G_UynJx=?>*YAPAj zY9joLHlsjrZg@|Ux9y@9l$||!L>LN;KAH&piE6$Hq`c+?K`LWKdv;j*-JU9#7eMb9 z>2PLeGEi^wCdX1GTL(KfyMG%&#bo5TbF3DrH!@dZ zkm!m>Ry+9Cl$V39)m^(_xmnV4)7##f8sPo){q=eGb$p%_v_$8by~y;rx#HUoi4@j< zk>iyn2Og{S+;I3v`+Tp+tbcZdx9K@u9~eMO1HD&)n%fm3tJOGX?kAAP~H%glG>|+na0*SCk;MG*R4vpz{-=7 z81+0Qe&9YAZSe;_?4kiZUNX}umBW2JoR+y41F7f>)`#CTb)uAWPsJ3}H;^S6S6nH% zwEB$lo``f%Dv;w|oW@PCuLoPMZ@w+bFc~<;J8@S2R02(Rwz7?v98^2oLl>7|<3ejs znW5sesL_yAVxdKHXaJqzDPx{Q@YMKME&XAbPIM>vqa#WbupR9iD}OYTl6M1O^gMHv zpqUne($$q8x=cZ(4mqWMi~GEpOmdso<$~>pPdb_S@;<_>g*Odt7cymJ%t1~%x z*05T7|6v)ig5!Za5Q!#EM zE6R_>Q?W#Tu^vBv-)u^Pl)U-{N;xBAlDoXh%*iOJpADBc95uMNgYrf#6QEi~!-Vmu zIqAtuO`qgy7e`Z4i!(pB_oqI*7zu{zvNOxm?L5tjUw^UyM&6`cg0AthA{Ps=qkd7d zs2L|{rgYGK?>jKEB(Hj%2IjAnt-m2gu`w{3*MAU-jcU7qxjcGLf+<+&jK`Gc3w%AK zCRf-4+~5J96L@bwg(0H@@TZ(Eh$OKKrfMfUm6~+E%^^IKrX5^jKv+4Zzw&X{#JsS$ z9+;xoF-g*i(nsnvh`Zwh?5K}(f2NPO{GgAEf1c*H&#)5nFAXbp3d2gp zM#8XCs4%RE6o!?3Zw)J53d710g<*y9gJH$yC^eGQv@n|!&4dt-sVDRBQXWUJRJQ=_ zd6ORco8H@(j2cH{YA6w-TR;wwWg5Nk1hep+sB29a5H%_4sMFmUeMi=JP*B;l@vHWKfK}=9ohyudl1Qa1qL;`|T5Tz7BYXz*27EntClu84L_b8&LDx|d! z!ABLf)?P$GEa0<9rA4CBQfL)L@GV#$AX>$O*5{qGCyDj8_uk**uixj#en`IAGdnvw zJ2N}GNlwz+!NlSFyEhx7BFb0IL~<4G;rrfC?zP~ngF}84j_mT!O+E10C|unGt`^~T zFT)R`3hw@TYHRUl<}G?-I75<)3?+f9+I_qkL6YfskO;h8X`ihSB*oPLZSu;>_^b@! z9Oj~M4rB3$bC|yjC81>)N=jf*%D#2;j-B95S~^eq^O9}7YqETj8gn13K%vuF;jcum z2pbd@F?gP%{><~!@$34J@c@Z~M#vRc{lay%7ZsilEkp8k@#r8KU-$TC%K6n-Z}RY7 z#I53U0NMI7=&h-p z>!WkUnc4%#pT@&C!{&N? za~42_zis#MYV@yNN3znMe6SsF4!<^o&c_D@u1t|G+NWcw8xLRi4k81cGsdp7>5_eQ zEOPSKHm_c}Ox|RwAh9#PD@?9#FX?sbbj_(Hdo&w!pchjJ_D-Hndv@t!D?)v0i`XWo z72tNr1-!28op_)%@AJ8V)w)H=w)qcBX9Ud60o6VpbY3yV=0BvRAOGpAreIHq^5@B{ z55r`mN=yBwC8wt)eG;aDo}2cqW0D*Cy>{(Xi0?*FwXkvpL|r`uz|{H4Ls zk6O!r`+3imrwgOCWk^-x`mx7O0Q2suoqL-kRT4b4kVSYNawx1W%S^A5tj`98O?^S6 z-H~l~x~+;ryaHxju9z}FmdQ)Dqs7Wb&;8g1w^&K|>FUy-<64RXR%NdYl5AK6s%z@) zC2`|pEEYHyReo}8%NK9XY3ml!Tl=tSQ`RvcznHPD^7@|KNWZ+*>j#~U-x1Jc?^9fK z(!Zhw(`6p&Zm2fbE#sw_VtD4W%Zi5IaE-*Mu*P@#7Hn@ z5s_dWNnV$;))DbtU)!zfU6t`>ZsC?+&9vo9){fRTmBE+MU^)Eu>UbtMET+I}-Qg0u zp-G#ggJg;-=&o{Q4ncQRU=fq-^R9~dweQ_1?)Ba>u$n9+EhqObeyQoy+6$RswTl;b zZeGEWkZ~|;oXIG6epX1{>GP>Uf+gjh^SN;-Ft7T?l5Y!%tNmI<+h&iNx~||G^*7@1 z7cH-4nq@T9Tag}(yl>-{f%LwO)w1#O+fP2x+<*Vo@{0I`A1&S) zH@|d7UdyHiPu1^l+G!FbNxaitv`yEMuwM3{9zT^g zBeo1?M5j$pDZDoO@OsaO+hcT1H$nDA8Q5sBj~stS5VH*72^4xd|5Ip1Sv_wk|< zkZs3@*!>=g=(l*s3}m-#oYMpFOkJbx#17OnBvyKEAs))e>u^_KdKqjBOTZH(> zVnq~W02xTEi4CzOgNPlmCk$~QEKw3i;zS0MA;g&sB`(C3sE8YJCod5XGK_eV;lzum zi8t{fBZx2YBO{4Fd6|qN0VI%&CP8Ej8B2o6DQN zG$e(jk~A`(q>}~YZ-gTY$s&?L7L!b}gsdiy$rJLFJR{Z;8;PxCki=JVNODY4Bl$tn zEIA`ND>)}QFS#HADUnK~QfUvVskEomOe&MgrRLK9QfKKlZ7D*3D ztE3V}%E%Zw!!Qm^5Hp6E!c1jmGP9VuOcKL0?=#Do<;)5ui`l|-GWQq>D`k7KW~`j; z&swrptTk)H+Ol@6JjJJwP;Ksiv!Djk*1N)Kg}a> zv~jd`WE|Zb$2rb)e8+LNW9^V1hdjId1kL8z19TuefS=5<^li1h&k~j$7Ne9~CL0 z$`Ce?Jz~$=tJoYS$9*xI?Qq{tF@SYY3_u>%#N~h19@}AiL3*QnE{I&@FDDFWwIwV{ z|1z2@%APYIX8>_YU~f60)GorSpKJrYg(A0Cu+K|^M*$82(sPtXQ%g zTA7?bv{tbH{r1@o&wE#(4qq&b*~M(LvRP@6>5tE04lkBX%1uhSlibN5_aC2g2Ima+ zRtR$8fD#zy~250TXF{?aO9)UQAhH}LI#UgMp6oYpD`8mfu$35FY zDEIB27lJxV@DQ{sa1?lWD+u7M{c{1V{f|X~Gn2ro21lVi3c3@i{ zZB75^c+g+LdJk!(HnpiAaZXi>ZNx`vvpD*LUS@bw!i~P0w+HZ#^IZL~6JC<>oYnF! zvBpE1f5K*&8aVf(D~H!ZJkPl;e}H)}0?%B&KP3)sn9~m=*xbF^|1JfVgZnjrR|jy8 z%PYFF7Rva<6HQs%coyz>f&6=KR(L5feJJHico_zD)r{! zbye6Serr`E_RKHrq%wF&_llR?((NCiXYaJ1BWV|I%h!rs zc&AkuF3ssrFL|d$h&6)DV()3SVk_^IWveNTRceSAV0yxN&+jSL7+ZPIe~_x@a~^5v z*!61hPAe9PRq8vt>m_+S=WS4(ozqfrq!v+G{b{RVQQMIZ6@G6dt+Dh;!Wv}Q$MT*R z1@h%*_E2CQoY+ah_*DHo8YjiQ+AWt(sC5*M!lNG37;_*SMsDCIe1B|S^AzQs|3~Vv zZCyr{vCuT~-XwGz;2b(XK0(!E5!d3oa(>4PzT~4TLQASgv(uIBrY1EszidA>vObog zNj>LeA9r+keUDI{e|_4Wg3H@cueMEYdOMCx%B}ZJ0j}Q_&jT$Ba1Q(?mXFcp6dHWV zNXk!{+4gz+t9G7TOkBwDx?4KnJfvG1ncq`@w;uO-`r}IuiM*m}(${8pH;*39rBGoMdwr-d zT=<9y4b*zVN4Tw~z%6^i2|8YzH5#f{E9mhXa2sE6z4mV-=hYauc zroAhy+pjiHGDY?LC1|YDU%I^2G?7y~tyff*XYrgcYZ*ia{J_FC>sD+L~P~HDE{b11l?OH{^|5iU#V@8u`57OI)5j3Nbh^iB;g@$R9 zmgfL1{i?kTo{1SevE)xCKXJuOe3HYQd<>76#yNM0iY$| zvLVQ90DocO3hV2=^_KZZFpU}{));^&JQ53lE}8W`SMf%GoIBU)MhH*w@Sg>S)F_&wIWn_C?n` z5%4m<3iM@3juD8q)HHHf;h9IQ7{qwnTGGv_tF!}`CkLh&WZ0=FU*$#{J9l+6J`>k zH;DiptwoHz1|ZT1$~2@MH2Tp{DTF~Iph^&GjDRYEhDt#j0x16{@Kp5NfrTQ#L=6)f zK^_C(d*!`(f$GNy3<1^{X`crWbM|ZiC4K==XB29T-~}4aI=FVYko)qcb7rGFXe36^ zr|j-)&6E0W_b~)=103ihD$}x z$cGB3ZOySyKa#Dy^PZJ9+82j6@8-|FZVzeUlQnf253jcH3vLe%t;z^w)7#9VgX%{$ zFI9~x&P^cZ8z$pr4ih&)YllnsZ4#1 z0gMd9FfKGV0>AdFdKx_tT!ThSBM?lQ(inl!sQv?>tppkYpoi)g^go%Ru+0(+2R*>r zW^X+&q|$X-qXfPwiDg>-}q^gQLaz;8>)KE$XKA(?6`!3G@-|KwnRu zQ!hMo_GuM&>GsJ#IqH*+a*$bh + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/server-data/resources/[esx_addons]/wx_logs/README.md b/server-data/resources/[esx_addons]/wx_logs/README.md new file mode 100644 index 000000000..c72ba19d6 --- /dev/null +++ b/server-data/resources/[esx_addons]/wx_logs/README.md @@ -0,0 +1,2 @@ +# wx_logs +Simple discord webhook logging system utilized via server-side export diff --git a/server-data/resources/[esx_addons]/wx_logs/configs/logs_config.lua b/server-data/resources/[esx_addons]/wx_logs/configs/logs_config.lua new file mode 100644 index 000000000..721995d7a --- /dev/null +++ b/server-data/resources/[esx_addons]/wx_logs/configs/logs_config.lua @@ -0,0 +1,20 @@ +wx = {} -- Don't touch + +wx.Webhooks = { -- Main config section + ['deaths'] --[[ Webhook Name/ID that will be used in the export ]] = { + Username = "Player Deaths", -- Webhook username + Icon = '', -- Image URL that will be used in footer and profile picture + URL = '', -- Webhook URL + }, + ['test']= { + Username = "Test Webhook", + Icon = '', + URL = '', + }, + -- ['Add more webhooks here!']= { + -- Username = "You can add how many webhooks you want!", + -- Icon = '', + -- URL = '', + -- }, + +} \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/wx_logs/fxmanifest.lua b/server-data/resources/[esx_addons]/wx_logs/fxmanifest.lua new file mode 100644 index 000000000..473ee95dc --- /dev/null +++ b/server-data/resources/[esx_addons]/wx_logs/fxmanifest.lua @@ -0,0 +1,8 @@ +fx_version 'cerulean' +version '1.0.0' +game 'gta5' +author 'wx / woox' +description 'Advanced & Free Discord logging system via webhooks and exports' +lua54 'yes' + +server_scripts {'configs/*.lua','server/*.lua'} diff --git a/server-data/resources/[esx_addons]/wx_logs/server/server.lua b/server-data/resources/[esx_addons]/wx_logs/server/server.lua new file mode 100644 index 000000000..a62d5d683 --- /dev/null +++ b/server-data/resources/[esx_addons]/wx_logs/server/server.lua @@ -0,0 +1,60 @@ +local availableWebhooks = {} + +for webhook, data in pairs(wx.Webhooks) do + if webhook then + table.insert(availableWebhooks,webhook) + if data.URL == '' or data.URL == 'webhook' or data.URL == nil then + print("^7[^6WX LOGS^7] ^7[^3WARNING^7] Webhook type ^3"..webhook.."^7 doesn't have a webhook link set!") + end + else + print("^7[^6WX LOGS^7] ^7[^1ERROR^7] No webhooks found!") + return + end +end + +function SendLog(webhook,data) + if wx.Webhooks[webhook] == nil then + print("^7[^6WX LOGS^7] ^7[^1ERROR^7] Invalid webhook - [^3"..webhook.."^7]. Available webhooks: \n"..json.encode(availableWebhooks)) + return + end + if not data then + print("^7[^6WX LOGS^7] ^7[^1ERROR^7] No data has been parsed!") + return + end + if data.username == nil then data.username = "WX Logs" end + + local embed = { + { + ["author"] = data.author, + ["color"] = data.color, + ["title"] = data.title, + ["url"] = data.url, + ["description"] = data.description, + ["fields"] = data.fields, + ["footer"] = { + ["text"] = "🌠 WX Logs - [ "..os.date('%d.%m.%Y - %H:%M:%S').." ]", + ["icon_url"] = data.icon + }, + ["thumbnail"] = data.thumbnail, + ["image"] = data.image, + } + } + PerformHttpRequest(wx.Webhooks[webhook].URL, function(err, text, headers) end, "POST",json.encode({username = wx.Webhooks[webhook].Username,embeds = embed,avatar_url = wx.Webhooks[webhook].Icon}),{["Content-Type"] = "application/json"}) +end + +exports('SendLog',function (webhook,data) + SendLog(webhook,data) +end) + +-- exports['wx_logs']:SendLog('test',{ +-- title = "Hello!", +-- description = "This is a simple log system!", +-- }) + +AddEventHandler('onResourceStart',function (r) + if r == GetCurrentResourceName() then + if GetCurrentResourceName() ~= 'wx_logs' then + print("^7[^6WX LOGS^7] ^7[^3WARNING^7] You have renamed the resource, make sure you also rename the exports!") + end + end +end) \ No newline at end of file From 1dca6fdb8f817cb19c2501e976f1cee4e84525e3 Mon Sep 17 00:00:00 2001 From: bitpredator <67551273+bitpredator@users.noreply.github.com> Date: Sat, 30 Nov 2024 19:30:11 +0100 Subject: [PATCH 02/22] fix(es_extended/client/functions): properly call SetVehicleExtra --- server-data/resources/[esx]/es_extended/client/functions.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-data/resources/[esx]/es_extended/client/functions.lua b/server-data/resources/[esx]/es_extended/client/functions.lua index 332823d88..435c4e28c 100644 --- a/server-data/resources/[esx]/es_extended/client/functions.lua +++ b/server-data/resources/[esx]/es_extended/client/functions.lua @@ -1008,7 +1008,7 @@ function ESX.Game.SetVehicleProperties(vehicle, props) for extraId, enabled in pairs(props.extras) do extraId = tonumber(extraId) if extraId then - SetVehicleExtra(vehicle, extraId, enabled) + SetVehicleExtra(vehicle, extraId, not enabled) end end end From fed1d54392384a2f8a3b10f6ffb1e8b80389e878 Mon Sep 17 00:00:00 2001 From: bitpredator <67551273+bitpredator@users.noreply.github.com> Date: Sat, 30 Nov 2024 19:50:14 +0100 Subject: [PATCH 03/22] chore: update the repository readme file --- README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 07a865b29..924595c0e 100644 --- a/README.md +++ b/README.md @@ -4,23 +4,22 @@ # EMPIRETOWN ![](https://img.shields.io/github/downloads/bitpredator/empiretown/total?logo=github) -![](https://img.shields.io/github/downloads/bitpredator/empiretown/latest/total?logo=github) ![](https://img.shields.io/github/contributors/bitpredator/empiretown?logo=github) ![](https://img.shields.io/github/v/release/bitpredator/empiretown?logo=github) -This program is a project for the fivem community, you have legal permission to distribute and / or modify it . +Source code of my Fivem Empiretown server, this server aims to simulate real life as much as possible. -ATTENTION: You are not authorized to change the name of the resources present within it. +# ATTENTION: +You are not authorized to sell this software this is a free and open source project, if you want to contribute to it feel free to fork and forward your pull requests, report any bugs or improvements to be made. +You can use this software as the basis for your fivem server and customize it as you see fit. Database Installation: https://bitpredator.github.io/bptdevelopment/docs/empiretown/database Server installation procedure: https://bitpredator.github.io/bptdevelopment/docs/empiretown/install-server -You are not authorized to sell this software (this is a free project). +This program is distributed in the hope that it will be useful, WITHOUT ANY WARRANTY; without even the implied warranty OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/. +You should have received a copy of the GNU General Public License with this program. If not, see http://www.gnu.org/licenses/. ### 📌 License From f62dd39c2f81fc4e4df47fd4beeb990ac265b366 Mon Sep 17 00:00:00 2001 From: bitpredator <67551273+bitpredator@users.noreply.github.com> Date: Sat, 30 Nov 2024 19:52:18 +0100 Subject: [PATCH 04/22] chore: increased the spawn of the import vehicle by one point --- server-data/resources/[bpt_addons]/bpt_importjob/config.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-data/resources/[bpt_addons]/bpt_importjob/config.lua b/server-data/resources/[bpt_addons]/bpt_importjob/config.lua index 97e035e04..9300d6c22 100644 --- a/server-data/resources/[bpt_addons]/bpt_importjob/config.lua +++ b/server-data/resources/[bpt_addons]/bpt_importjob/config.lua @@ -20,7 +20,7 @@ Config.Zones = { }, VehicleSpawnPoint = { - Pos = { x = 1015.740662, y = -2366.624268, z = 30.593042 }, + Pos = { x = 1015.740662, y = -2366.624268, z = 31.593042 }, Size = { x = 1.5, y = 1.5, z = 1.0 }, Type = -1, Rotate = false, From c444e5f39f5fa79d6dc6e502486f034b048400a5 Mon Sep 17 00:00:00 2001 From: bitpredator <67551273+bitpredator@users.noreply.github.com> Date: Sat, 30 Nov 2024 19:53:29 +0100 Subject: [PATCH 05/22] feat: logo server empiretown --- server-data/empiretown.png | Bin 0 -> 22721 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 server-data/empiretown.png diff --git a/server-data/empiretown.png b/server-data/empiretown.png new file mode 100644 index 0000000000000000000000000000000000000000..5fc673af6f639ae7c719e9eae1f9189075d321b6 GIT binary patch literal 22721 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 literal 0 HcmV?d00001 From 5a9fd4c300baf002d95aa40d5ee0ded9ada2f159 Mon Sep 17 00:00:00 2001 From: bitpredator <67551273+bitpredator@users.noreply.github.com> Date: Sat, 30 Nov 2024 19:58:05 +0100 Subject: [PATCH 06/22] chore: make sure to load and launch the server logo --- server-data/server.cfg | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server-data/server.cfg b/server-data/server.cfg index dafaefe68..d16107431 100644 --- a/server-data/server.cfg +++ b/server-data/server.cfg @@ -9,6 +9,8 @@ sv_hostname "EmpireTown" set steam_webApiKey "" sv_licenseKey "" sv_maxclients 48 # Allow access to features usually locked behind a FiveM patreon key +# Loading a server icon (96x96 PNG file) +load_server_icon empiretown.png sets sv_projectName "EmpireTown framework" sets sv_projectDesc "" From 54398ca60b465ba64fc5673dfeea18f3717d2b4d Mon Sep 17 00:00:00 2001 From: bitpredator <67551273+bitpredator@users.noreply.github.com> Date: Sun, 1 Dec 2024 11:13:52 +0100 Subject: [PATCH 07/22] chore: remove discordActivity/Presence --- .../client/modules/adjustments.lua | 39 ------------------- .../es_extended/shared/config/adjustments.lua | 25 ------------ 2 files changed, 64 deletions(-) diff --git a/server-data/resources/[esx]/es_extended/client/modules/adjustments.lua b/server-data/resources/[esx]/es_extended/client/modules/adjustments.lua index 0097215ed..8c4743af0 100644 --- a/server-data/resources/[esx]/es_extended/client/modules/adjustments.lua +++ b/server-data/resources/[esx]/es_extended/client/modules/adjustments.lua @@ -171,44 +171,6 @@ local placeHolders = { end, } -function Adjustments:PresencePlaceholders() - local presence = Config.DiscordActivity.presence - - for placeholder, cb in pairs(placeHolders) do - local success, result = pcall(cb) - - if not success then - error(("Failed to execute presence placeholder: ^5%s^7"):format(placeholder)) - error(result) - return "Unknown" - end - - presence = presence:gsub(("{%s}"):format(placeholder), result) - end - - return presence -end - -function Adjustments:DiscordPresence() - if Config.DiscordActivity.appId ~= 0 then - CreateThread(function() - while true do - SetDiscordAppId(Config.DiscordActivity.appId) - SetDiscordRichPresenceAsset(Config.DiscordActivity.assetName) - SetDiscordRichPresenceAssetText(Config.DiscordActivity.assetText) - - for i = 1, #Config.DiscordActivity.buttons do - local button = Config.DiscordActivity.buttons[i] - SetDiscordRichPresenceAction(i - 1, button.label, button.url) - end - - SetRichPresence(self:PresencePlaceholders()) - Wait(Config.DiscordActivity.refresh) - end - end) - end -end - function Adjustments:WantedLevel() if not Config.EnableWantedLevel then ClearPlayerWantedLevel(ESX.playerId) @@ -236,7 +198,6 @@ function Adjustments:Load() self:DispatchServices() self:NPCScenarios() self:LicensePlates() - self:DiscordPresence() self:WantedLevel() self:DisableRadio() end diff --git a/server-data/resources/[esx]/es_extended/shared/config/adjustments.lua b/server-data/resources/[esx]/es_extended/shared/config/adjustments.lua index ae7b8a466..059bfb459 100644 --- a/server-data/resources/[esx]/es_extended/shared/config/adjustments.lua +++ b/server-data/resources/[esx]/es_extended/shared/config/adjustments.lua @@ -43,28 +43,3 @@ Config.RemoveHudComponents = { --Any other character will lead to said character being emitted. -- A string shorter than 8 characters will be padded on the right. Config.CustomAIPlates = "........" -- Custom plates for AI vehicles - ---[[ - PlaceHolders: - {server_name} - Server Display Name - {server_endpoint} - Server IP:Server Port - {server_players} - Current Player Count - {server_maxplayers} - Max Player Count - - {player_name} - Player Name - {player_rp_name} - Player RP Name - {player_id} - Player ID - {player_street} - Player Street Name -]] - -Config.DiscordActivity = { - appId = 0, -- Discord Application ID, - assetName = "LargeIcon", --image name for the "large" icon. - assetText = "{server_name}", -- Text to display on the asset - buttons = { - { label = "Join Server", url = "fivem://connect/{server_endpoint}" }, - { label = "Discord", url = "https://discord.esx-framework.org" }, - }, - presence = "{player_name} [{player_id}] | {server_players}/{server_maxplayers}", - refresh = 1 * 60 * 1000, -- 1 minute -} From fd53706136ae74a075547c23cbba1fb71ee6405e Mon Sep 17 00:00:00 2001 From: bitpredator <67551273+bitpredator@users.noreply.github.com> Date: Sun, 1 Dec 2024 11:14:38 +0100 Subject: [PATCH 08/22] refactor(es_extended/client/modules/death): replace gameEventTriggered --- .../es_extended/client/modules/death.lua | 39 +++++++------------ 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/server-data/resources/[esx]/es_extended/client/modules/death.lua b/server-data/resources/[esx]/es_extended/client/modules/death.lua index 4054421e2..28e860de8 100644 --- a/server-data/resources/[esx]/es_extended/client/modules/death.lua +++ b/server-data/resources/[esx]/es_extended/client/modules/death.lua @@ -29,7 +29,7 @@ function Death:ByPlayer() TriggerServerEvent("esx:onPlayerDeath", data) end -function Death:Natual() +function Death:Natural() local coords = GetEntityCoords(ESX.PlayerData.ped) local data = { @@ -43,25 +43,7 @@ function Death:Natual() TriggerServerEvent("esx:onPlayerDeath", data) end -function Death:Damaged(victim, victimDied) - if not victimDied then - return - end - - if not IsEntityAPed(victim) then - return - end - - if not IsPedAPlayer(victim) then - return - end - - local victimId = NetworkGetPlayerIndexFromPed(victim) - local isDead = IsPedDeadOrDying(victim, true) or IsPedFatallyInjured(victim) - if victimId ~= ESX.playerId or not isDead then - return - end - +function Death:Died() self.killerEntity = GetPedSourceOfDeath(ESX.PlayerData.ped) self.deathCause = GetPedCauseOfDeath(ESX.PlayerData.ped) self.killerId = NetworkGetPlayerIndexFromPed(self.killerEntity) @@ -72,15 +54,20 @@ function Death:Damaged(victim, victimDied) if self.killerEntity ~= ESX.PlayerData.ped and self.killerId and isActive then self:ByPlayer() else - self:Natual() + self:Natural() end self:ResetValues() end -AddEventHandler("gameEventTriggered", function(event, data) - if event ~= "CEventNetworkEntityDamage" then - return - end - Death:Damaged(data[1], data[4]) +AddEventHandler("esx:onPlayerSpawn", function() + Citizen.CreateThreadNow(function() + while not ESX.PlayerData.dead do + if IsPedDeadOrDying(ESX.PlayerData.ped, true) or IsPedFatallyInjured(ESX.PlayerData.ped) then + Death:Died() + break + end + Citizen.Wait(250) + end + end) end) From ffa8af61c3c029687e9ade458f229d38571eae3d Mon Sep 17 00:00:00 2001 From: bitpredator <67551273+bitpredator@users.noreply.github.com> Date: Sun, 1 Dec 2024 16:30:42 +0100 Subject: [PATCH 09/22] patch release 1.0.2 --- .../es_extended/client/modules/death.lua | 37 +- .../[esx]/es_extended/shared/config/main.lua | 4 +- .../[esx_addons]/PolyZone/BoxZone.lua | 226 +++++ .../[esx_addons]/PolyZone/CircleZone.lua | 98 ++ .../[esx_addons]/PolyZone/ComboZone.lua | 369 ++++++++ .../[esx_addons]/PolyZone/EntityZone.lua | 143 +++ .../resources/[esx_addons]/PolyZone/LICENSE | 21 + .../resources/[esx_addons]/PolyZone/README.md | 51 ++ .../[esx_addons]/PolyZone/client.lua | 601 +++++++++++++ .../PolyZone/creation/client/BoxZone.lua | 113 +++ .../PolyZone/creation/client/CircleZone.lua | 54 ++ .../PolyZone/creation/client/PolyZone.lua | 60 ++ .../PolyZone/creation/client/commands.lua | 68 ++ .../PolyZone/creation/client/creation.lua | 158 ++++ .../PolyZone/creation/client/utils.lua | 75 ++ .../PolyZone/creation/server/config.lua | 56 ++ .../PolyZone/creation/server/creation.lua | 115 +++ .../[esx_addons]/PolyZone/fxmanifest.lua | 20 + .../[esx_addons]/PolyZone/server.lua | 10 + .../[esx_addons]/esx-qalle-jail/README.md | 57 ++ .../[esx_addons]/esx-qalle-jail/cell.ogg | Bin 0 -> 61671 bytes .../esx-qalle-jail/client/client.lua | 388 ++++++++ .../esx-qalle-jail/client/utils.lua | 173 ++++ .../[esx_addons]/esx-qalle-jail/config.lua | 106 +++ .../esx-qalle-jail/fxmanifest.lua | 23 + .../[esx_addons]/esx-qalle-jail/jail.sql | 2 + .../esx-qalle-jail/locales/en.lua | 16 + .../esx-qalle-jail/locales/it.lua | 16 + .../esx-qalle-jail/server/server.lua | 169 ++++ .../[esx_addons]/esx_property/properties.json | 2 +- .../{wx_jail => esx_strippers}/LICENSE | 0 .../[esx_addons]/esx_strippers/README.md | 10 + .../esx_strippers/client/client.lua | 35 + .../[esx_addons]/esx_strippers/fxmanifest.lua | 12 + .../esx_strippers/server/server.lua | 9 + .../[esx_addons]/kc-unicorn/client/loop.lua | 80 ++ .../[esx_addons]/kc-unicorn/client/main.lua | 840 ++++++++++++++++++ .../[esx_addons]/kc-unicorn/config.lua | 78 ++ .../[esx_addons]/kc-unicorn/fxmanifest.lua | 26 + .../[esx_addons]/kc-unicorn/locales/en.lua | 15 + .../[esx_addons]/kc-unicorn/locales/fr.lua | 15 + .../[esx_addons]/kc-unicorn/server/main.lua | 414 +++++++++ .../resources/[esx_addons]/wx_jail/README.md | 20 - .../[esx_addons]/wx_jail/SQL/INSERT-THIS.sql | 2 - .../[esx_addons]/wx_jail/client/client.lua | 285 ------ .../[esx_addons]/wx_jail/client/font.lua | 1 - .../[esx_addons]/wx_jail/client/functions.lua | 70 -- .../[esx_addons]/wx_jail/client/jailmenu.lua | 412 --------- .../wx_jail/configs/jail_config.lua | 48 - .../[esx_addons]/wx_jail/fxmanifest.lua | 20 - .../[esx_addons]/wx_jail/locale/locale.lua | 26 - .../[esx_addons]/wx_jail/server/callbacks.lua | 15 - .../[esx_addons]/wx_jail/server/server.lua | 419 --------- .../[esx_addons]/wx_jail/stream/BBN.gfx | Bin 50567 -> 0 bytes .../[esx_addons]/wx_logs/ARGUMENTS.md | 28 - .../resources/[esx_addons]/wx_logs/EXAMPLE.md | 25 - .../resources/[esx_addons]/wx_logs/LICENSE | 674 -------------- .../resources/[esx_addons]/wx_logs/README.md | 2 - .../wx_logs/configs/logs_config.lua | 20 - .../[esx_addons]/wx_logs/fxmanifest.lua | 8 - .../[esx_addons]/wx_logs/server/server.lua | 60 -- 61 files changed, 4750 insertions(+), 2150 deletions(-) create mode 100644 server-data/resources/[esx_addons]/PolyZone/BoxZone.lua create mode 100644 server-data/resources/[esx_addons]/PolyZone/CircleZone.lua create mode 100644 server-data/resources/[esx_addons]/PolyZone/ComboZone.lua create mode 100644 server-data/resources/[esx_addons]/PolyZone/EntityZone.lua create mode 100644 server-data/resources/[esx_addons]/PolyZone/LICENSE create mode 100644 server-data/resources/[esx_addons]/PolyZone/README.md create mode 100644 server-data/resources/[esx_addons]/PolyZone/client.lua create mode 100644 server-data/resources/[esx_addons]/PolyZone/creation/client/BoxZone.lua create mode 100644 server-data/resources/[esx_addons]/PolyZone/creation/client/CircleZone.lua create mode 100644 server-data/resources/[esx_addons]/PolyZone/creation/client/PolyZone.lua create mode 100644 server-data/resources/[esx_addons]/PolyZone/creation/client/commands.lua create mode 100644 server-data/resources/[esx_addons]/PolyZone/creation/client/creation.lua create mode 100644 server-data/resources/[esx_addons]/PolyZone/creation/client/utils.lua create mode 100644 server-data/resources/[esx_addons]/PolyZone/creation/server/config.lua create mode 100644 server-data/resources/[esx_addons]/PolyZone/creation/server/creation.lua create mode 100644 server-data/resources/[esx_addons]/PolyZone/fxmanifest.lua create mode 100644 server-data/resources/[esx_addons]/PolyZone/server.lua create mode 100644 server-data/resources/[esx_addons]/esx-qalle-jail/README.md create mode 100644 server-data/resources/[esx_addons]/esx-qalle-jail/cell.ogg create mode 100644 server-data/resources/[esx_addons]/esx-qalle-jail/client/client.lua create mode 100644 server-data/resources/[esx_addons]/esx-qalle-jail/client/utils.lua create mode 100644 server-data/resources/[esx_addons]/esx-qalle-jail/config.lua create mode 100644 server-data/resources/[esx_addons]/esx-qalle-jail/fxmanifest.lua create mode 100644 server-data/resources/[esx_addons]/esx-qalle-jail/jail.sql create mode 100644 server-data/resources/[esx_addons]/esx-qalle-jail/locales/en.lua create mode 100644 server-data/resources/[esx_addons]/esx-qalle-jail/locales/it.lua create mode 100644 server-data/resources/[esx_addons]/esx-qalle-jail/server/server.lua rename server-data/resources/[esx_addons]/{wx_jail => esx_strippers}/LICENSE (100%) create mode 100644 server-data/resources/[esx_addons]/esx_strippers/README.md create mode 100644 server-data/resources/[esx_addons]/esx_strippers/client/client.lua create mode 100644 server-data/resources/[esx_addons]/esx_strippers/fxmanifest.lua create mode 100644 server-data/resources/[esx_addons]/esx_strippers/server/server.lua create mode 100644 server-data/resources/[esx_addons]/kc-unicorn/client/loop.lua create mode 100644 server-data/resources/[esx_addons]/kc-unicorn/client/main.lua create mode 100644 server-data/resources/[esx_addons]/kc-unicorn/config.lua create mode 100644 server-data/resources/[esx_addons]/kc-unicorn/fxmanifest.lua create mode 100644 server-data/resources/[esx_addons]/kc-unicorn/locales/en.lua create mode 100644 server-data/resources/[esx_addons]/kc-unicorn/locales/fr.lua create mode 100644 server-data/resources/[esx_addons]/kc-unicorn/server/main.lua delete mode 100644 server-data/resources/[esx_addons]/wx_jail/README.md delete mode 100644 server-data/resources/[esx_addons]/wx_jail/SQL/INSERT-THIS.sql delete mode 100644 server-data/resources/[esx_addons]/wx_jail/client/client.lua delete mode 100644 server-data/resources/[esx_addons]/wx_jail/client/font.lua delete mode 100644 server-data/resources/[esx_addons]/wx_jail/client/functions.lua delete mode 100644 server-data/resources/[esx_addons]/wx_jail/client/jailmenu.lua delete mode 100644 server-data/resources/[esx_addons]/wx_jail/configs/jail_config.lua delete mode 100644 server-data/resources/[esx_addons]/wx_jail/fxmanifest.lua delete mode 100644 server-data/resources/[esx_addons]/wx_jail/locale/locale.lua delete mode 100644 server-data/resources/[esx_addons]/wx_jail/server/callbacks.lua delete mode 100644 server-data/resources/[esx_addons]/wx_jail/server/server.lua delete mode 100644 server-data/resources/[esx_addons]/wx_jail/stream/BBN.gfx delete mode 100644 server-data/resources/[esx_addons]/wx_logs/ARGUMENTS.md delete mode 100644 server-data/resources/[esx_addons]/wx_logs/EXAMPLE.md delete mode 100644 server-data/resources/[esx_addons]/wx_logs/LICENSE delete mode 100644 server-data/resources/[esx_addons]/wx_logs/README.md delete mode 100644 server-data/resources/[esx_addons]/wx_logs/configs/logs_config.lua delete mode 100644 server-data/resources/[esx_addons]/wx_logs/fxmanifest.lua delete mode 100644 server-data/resources/[esx_addons]/wx_logs/server/server.lua diff --git a/server-data/resources/[esx]/es_extended/client/modules/death.lua b/server-data/resources/[esx]/es_extended/client/modules/death.lua index 28e860de8..96205feec 100644 --- a/server-data/resources/[esx]/es_extended/client/modules/death.lua +++ b/server-data/resources/[esx]/es_extended/client/modules/death.lua @@ -43,7 +43,25 @@ function Death:Natural() TriggerServerEvent("esx:onPlayerDeath", data) end -function Death:Died() +function Death:Damaged(victim, victimDied) + if not victimDied then + return + end + + if not IsEntityAPed(victim) then + return + end + + if not IsPedAPlayer(victim) then + return + end + + local victimId = NetworkGetPlayerIndexFromPed(victim) + local isDead = IsPedDeadOrDying(victim, true) or IsPedFatallyInjured(victim) + if victimId ~= ESX.playerId or not isDead then + return + end + self.killerEntity = GetPedSourceOfDeath(ESX.PlayerData.ped) self.deathCause = GetPedCauseOfDeath(ESX.PlayerData.ped) self.killerId = NetworkGetPlayerIndexFromPed(self.killerEntity) @@ -60,14 +78,9 @@ function Death:Died() self:ResetValues() end -AddEventHandler("esx:onPlayerSpawn", function() - Citizen.CreateThreadNow(function() - while not ESX.PlayerData.dead do - if IsPedDeadOrDying(ESX.PlayerData.ped, true) or IsPedFatallyInjured(ESX.PlayerData.ped) then - Death:Died() - break - end - Citizen.Wait(250) - end - end) -end) +AddEventHandler("gameEventTriggered", function(event, data) + if event ~= "CEventNetworkEntityDamage" then + return + end + Death:Damaged(data[1], data[4]) +end) \ No newline at end of file diff --git a/server-data/resources/[esx]/es_extended/shared/config/main.lua b/server-data/resources/[esx]/es_extended/shared/config/main.lua index 6c09ffe93..2602239fc 100644 --- a/server-data/resources/[esx]/es_extended/shared/config/main.lua +++ b/server-data/resources/[esx]/es_extended/shared/config/main.lua @@ -57,7 +57,7 @@ if GetResourceState("ox_inventory") ~= "missing" then Config.CustomInventory = "ox" end -local txAdminLocale = GetConvar("txAdmin-locale", "en") +local txAdminLocale = GetConvar("txAdmin-locale", "it") local esxLocale = GetConvar("esx:locale", "invalid") -Config.Locale = (esxLocale ~= "invalid") and esxLocale or (txAdminLocale ~= "custom" and txAdminLocale) or "en" +Config.Locale = (esxLocale ~= "invalid") and esxLocale or (txAdminLocale ~= "custom" and txAdminLocale) or "it" diff --git a/server-data/resources/[esx_addons]/PolyZone/BoxZone.lua b/server-data/resources/[esx_addons]/PolyZone/BoxZone.lua new file mode 100644 index 000000000..d07eb008a --- /dev/null +++ b/server-data/resources/[esx_addons]/PolyZone/BoxZone.lua @@ -0,0 +1,226 @@ +BoxZone = {} +-- Inherits from PolyZone +setmetatable(BoxZone, { __index = PolyZone }) + +-- Utility functions +local rad, cos, sin = math.rad, math.cos, math.sin +function PolyZone.rotate(origin, point, theta) + if theta == 0.0 then return point end + + local p = point - origin + local pX, pY = p.x, p.y + theta = rad(theta) + local cosTheta = cos(theta) + local sinTheta = sin(theta) + local x = pX * cosTheta - pY * sinTheta + local y = pX * sinTheta + pY * cosTheta + return vector2(x, y) + origin +end + +function BoxZone.calculateMinAndMaxZ(minZ, maxZ, scaleZ, offsetZ) + local minScaleZ, maxScaleZ, minOffsetZ, maxOffsetZ = scaleZ[1] or 1.0, scaleZ[2] or 1.0, offsetZ[1] or 0.0, offsetZ[2] or 0.0 + if (minZ == nil and maxZ == nil) or (minScaleZ == 1.0 and maxScaleZ == 1.0 and minOffsetZ == 0.0 and maxOffsetZ == 0.0) then + return minZ, maxZ + end + + if minScaleZ ~= 1.0 or maxScaleZ ~= 1.0 then + if minZ ~= nil and maxZ ~= nil then + local halfHeight = (maxZ - minZ) / 2 + local centerZ = minZ + halfHeight + minZ = centerZ - halfHeight * minScaleZ + maxZ = centerZ + halfHeight * maxScaleZ + else + print(string.format( + "[PolyZone] Warning: The minZ/maxZ of a BoxZone can only be scaled if both minZ and maxZ are non-nil (minZ=%s, maxZ=%s)", + tostring(minZ), + tostring(maxZ) + )) + end + end + + if minZ then minZ = minZ - minOffsetZ end + if maxZ then maxZ = maxZ + maxOffsetZ end + + return minZ, maxZ +end + +local function _calculateScaleAndOffset(options) + -- Scale and offset tables are both formatted as {forward, back, left, right, up, down} + -- or if symmetrical {forward/back, left/right, up/down} + local scale = options.scale or {1.0, 1.0, 1.0, 1.0, 1.0, 1.0} + local offset = options.offset or {0.0, 0.0, 0.0, 0.0, 0.0, 0.0} + assert(#scale == 3 or #scale == 6, "Scale must be of length 3 or 6") + assert(#offset == 3 or #offset == 6, "Offset must be of length 3 or 6") + if #scale == 3 then + scale = {scale[1], scale[1], scale[2], scale[2], scale[3], scale[3]} + end + if #offset == 3 then + offset = {offset[1], offset[1], offset[2], offset[2], offset[3], offset[3]} + end + local minOffset = vector3(offset[3], offset[2], offset[6]) + local maxOffset = vector3(offset[4], offset[1], offset[5]) + local minScale = vector3(scale[3], scale[2], scale[6]) + local maxScale = vector3(scale[4], scale[1], scale[5]) + return minOffset, maxOffset, minScale, maxScale +end + +local function _calculatePoints(center, length, width, minScale, maxScale, minOffset, maxOffset) + local halfLength, halfWidth = length / 2, width / 2 + local min = vector3(-halfWidth, -halfLength, 0.0) + local max = vector3(halfWidth, halfLength, 0.0) + + min = min * minScale - minOffset + max = max * maxScale + maxOffset + + -- Box vertices + local p1 = center.xy + vector2(min.x, min.y) + local p2 = center.xy + vector2(max.x, min.y) + local p3 = center.xy + vector2(max.x, max.y) + local p4 = center.xy + vector2(min.x, max.y) + return {p1, p2, p3, p4} +end + +-- Debug drawing functions +function BoxZone:TransformPoint(point) + -- Overriding TransformPoint function to take into account rotation and position offset + return PolyZone.rotate(self.startPos, point, self.offsetRot) + self.offsetPos +end + + +-- Initialization functions +local function _initDebug(zone, options) + if options.debugBlip then zone:addDebugBlip() end + if not options.debugPoly then + return + end + + Citizen.CreateThread(function() + while not zone.destroyed do + zone:draw(false) + Citizen.Wait(0) + end + end) +end + +local defaultMinOffset, defaultMaxOffset, defaultMinScale, defaultMaxScale = vector3(0.0, 0.0, 0.0), vector3(0.0, 0.0, 0.0), vector3(1.0, 1.0, 1.0), vector3(1.0, 1.0, 1.0) +local defaultScaleZ, defaultOffsetZ = {defaultMinScale.z, defaultMaxScale.z}, {defaultMinOffset.z, defaultMaxOffset.z} +function BoxZone:new(center, length, width, options) + local minOffset, maxOffset, minScale, maxScale = defaultMinOffset, defaultMaxOffset, defaultMinScale, defaultMaxScale + local scaleZ, offsetZ = defaultScaleZ, defaultOffsetZ + if options.scale ~= nil or options.offset ~= nil then + minOffset, maxOffset, minScale, maxScale = _calculateScaleAndOffset(options) + scaleZ, offsetZ = {minScale.z, maxScale.z}, {minOffset.z, maxOffset.z} + end + + local points = _calculatePoints(center, length, width, minScale, maxScale, minOffset, maxOffset) + local min = points[1] + local max = points[3] + local size = max - min + + local minZ, maxZ = BoxZone.calculateMinAndMaxZ(options.minZ, options.maxZ, scaleZ, offsetZ) + options.minZ = minZ + options.maxZ = maxZ + + -- Box Zones don't use the grid optimization because they are already rectangles/cubes + options.useGrid = false + + -- Pre-setting all these values to avoid PolyZone:new() having to calculate them + options.min = min + options.max = max + options.size = size + options.center = center + options.area = size.x * size.y + + local zone = PolyZone:new(points, options) + zone.length = length + zone.width = width + zone.startPos = center.xy + zone.offsetPos = vector2(0.0, 0.0) + zone.offsetRot = options.heading or 0.0 + zone.minScale, zone.maxScale = minScale, maxScale + zone.minOffset, zone.maxOffset = minOffset, maxOffset + zone.scaleZ, zone.offsetZ = scaleZ, offsetZ + zone.isBoxZone = true + + setmetatable(zone, self) + self.__index = self + return zone +end + +function BoxZone:Create(center, length, width, options) + local zone = BoxZone:new(center, length, width, options) + _initDebug(zone, options) + return zone +end + + +-- Helper functions +function BoxZone:isPointInside(point) + if self.destroyed then + print("[PolyZone] Warning: Called isPointInside on destroyed zone {name=" .. self.name .. "}") + return false + end + + local startPos = self.startPos + local actualPos = point.xy - self.offsetPos + if #(actualPos - startPos) > self.boundingRadius then + return false + end + + local rotatedPoint = PolyZone.rotate(startPos, actualPos, -self.offsetRot) + local pX, pY, pZ = rotatedPoint.x, rotatedPoint.y, point.z + local min, max = self.min, self.max + local minX, minY, maxX, maxY = min.x, min.y, max.x, max.y + local minZ, maxZ = self.minZ, self.maxZ + if pX < minX or pX > maxX or pY < minY or pY > maxY then + return false + end + if (minZ and pZ < minZ) or (maxZ and pZ > maxZ) then + return false + end + return true +end + +function BoxZone:getHeading() + return self.offsetRot +end + +function BoxZone:setHeading(heading) + if not heading then + return + end + self.offsetRot = heading +end + +function BoxZone:setCenter(center) + if not center or center == self.center then + return + end + self.center = center + self.startPos = center.xy + self.points = _calculatePoints(self.center, self.length, self.width, self.minScale, self.maxScale, self.minOffset, self.maxOffset) +end + +function BoxZone:getLength() + return self.length +end + +function BoxZone:setLength(length) + if not length or length == self.length then + return + end + self.length = length + self.points = _calculatePoints(self.center, self.length, self.width, self.minScale, self.maxScale, self.minOffset, self.maxOffset) +end + +function BoxZone:getWidth() + return self.width +end + +function BoxZone:setWidth(width) + if not width or width == self.width then + return + end + self.width = width + self.points = _calculatePoints(self.center, self.length, self.width, self.minScale, self.maxScale, self.minOffset, self.maxOffset) +end diff --git a/server-data/resources/[esx_addons]/PolyZone/CircleZone.lua b/server-data/resources/[esx_addons]/PolyZone/CircleZone.lua new file mode 100644 index 000000000..794bc74ff --- /dev/null +++ b/server-data/resources/[esx_addons]/PolyZone/CircleZone.lua @@ -0,0 +1,98 @@ +CircleZone = {} +-- Inherits from PolyZone +setmetatable(CircleZone, { __index = PolyZone }) + +function CircleZone:draw(forceDraw) + if not forceDraw and not self.debugPoly then return end + local center = self.center + local debugColor = self.debugColor + local r, g, b = debugColor[1], debugColor[2], debugColor[3] + if self.useZ then + local radius = self.radius + DrawMarker(28, center.x, center.y, center.z, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, radius, radius, radius, r, g, b, 48, false, false, 2, nil, nil, false) + else + local diameter = self.diameter + DrawMarker(1, center.x, center.y, -200.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, diameter, diameter, 400.0, r, g, b, 96, false, false, 2, nil, nil, false) + end +end + + +local function _initDebug(zone, options) + if options.debugBlip then zone:addDebugBlip() end + if not options.debugPoly then + return + end + + Citizen.CreateThread(function() + while not zone.destroyed do + zone:draw(false) + Citizen.Wait(0) + end + end) +end + +function CircleZone:new(center, radius, options) + options = options or {} + local zone = { + name = tostring(options.name) or nil, + center = center, + radius = radius + 0.0, + diameter = radius * 2.0, + useZ = options.useZ or false, + debugPoly = options.debugPoly or false, + debugColor = options.debugColor or {0, 255, 0}, + data = options.data or {}, + isCircleZone = true, + } + if zone.useZ then + assert(type(zone.center) == "vector3", "Center must be vector3 if useZ is true {center=" .. center .. "}") + end + setmetatable(zone, self) + self.__index = self + return zone +end + +function CircleZone:Create(center, radius, options) + local zone = CircleZone:new(center, radius, options) + _initDebug(zone, options) + return zone +end + +function CircleZone:isPointInside(point) + if self.destroyed then + print("[PolyZone] Warning: Called isPointInside on destroyed zone {name=" .. self.name .. "}") + return false + end + + local center = self.center + local radius = self.radius + + if self.useZ then + return #(point - center) < radius + else + return #(point.xy - center.xy) < radius + end +end + +function CircleZone:getRadius() + return self.radius +end + +function CircleZone:setRadius(radius) + if not radius or radius == self.radius then + return + end + self.radius = radius + self.diameter = radius * 2.0 +end + +function CircleZone:getCenter() + return self.center +end + +function CircleZone:setCenter(center) + if not center or center == self.center then + return + end + self.center = center +end diff --git a/server-data/resources/[esx_addons]/PolyZone/ComboZone.lua b/server-data/resources/[esx_addons]/PolyZone/ComboZone.lua new file mode 100644 index 000000000..63a70e9e6 --- /dev/null +++ b/server-data/resources/[esx_addons]/PolyZone/ComboZone.lua @@ -0,0 +1,369 @@ +local mapMinX, mapMinY, mapMaxX, mapMaxY = -3700, -4400, 4500, 8000 +local xDivisions = 34 +local yDivisions = 50 +local xDelta = (mapMaxX - mapMinX) / xDivisions +local yDelta = (mapMaxY - mapMinY) / yDivisions + +ComboZone = {} + +-- Finds all values in tblA that are not in tblB, using the "id" property +local function tblDifference(tblA, tblB) + local diff + for _, a in ipairs(tblA) do + local found = false + for _, b in ipairs(tblB) do + if b.id == a.id then + found = true + break + end + end + if not found then + diff = diff or {} + diff[#diff+1] = a + end + end + return diff +end + +local function _differenceBetweenInsideZones(insideZones, newInsideZones) + local insideZonesCount, newInsideZonesCount = #insideZones, #newInsideZones + if insideZonesCount == 0 and newInsideZonesCount == 0 then + -- No zones to check + return false, nil, nil + elseif insideZonesCount == 0 and newInsideZonesCount > 0 then + -- Was in no zones last check, but in 1 or more zones now (just entered all zones in newInsideZones) + return true, copyTbl(newInsideZones), nil + elseif insideZonesCount > 0 and newInsideZonesCount == 0 then + -- Was in 1 or more zones last check, but in no zones now (just left all zones in insideZones) + return true, nil, copyTbl(insideZones) + end + + -- Check for zones that were in insideZones, but are not in newInsideZones (zones the player just left) + local leftZones = tblDifference(insideZones, newInsideZones) + -- Check for zones that are in newInsideZones, but were not in insideZones (zones the player just entered) + local enteredZones = tblDifference(newInsideZones, insideZones) + + local isDifferent = enteredZones ~= nil or leftZones ~= nil + return isDifferent, enteredZones, leftZones +end + +local function _getZoneBounds(zone) + local center = zone.center + local radius = zone.radius or zone.boundingRadius + local minY = (center.y - radius - mapMinY) // yDelta + local maxY = (center.y + radius - mapMinY) // yDelta + local minX = (center.x - radius - mapMinX) // xDelta + local maxX = (center.x + radius - mapMinX) // xDelta + return minY, maxY, minX, maxX +end + +local function _removeZoneByFunction(predicateFn, zones) + if predicateFn == nil or zones == nil or #zones == 0 then return end + + for i=1, #zones do + local possibleZone = zones[i] + if possibleZone and predicateFn(possibleZone) then + table.remove(zones, i) + return possibleZone + end + end + return nil +end + +local function _addZoneToGrid(grid, zone) + local minY, maxY, minX, maxX = _getZoneBounds(zone) + for y=minY, maxY do + local row = grid[y] or {} + for x=minX, maxX do + local cell = row[x] or {} + cell[#cell+1] = zone + row[x] = cell + end + grid[y] = row + end +end + +local function _getGridCell(pos) + local x = (pos.x - mapMinX) // xDelta + local y = (pos.y - mapMinY) // yDelta + return x, y +end + + +function ComboZone:draw(forceDraw) + local zones = self.zones + for i=1, #zones do + local zone = zones[i] + if zone and not zone.destroyed then + zone:draw(forceDraw) + end + end +end + + +local function _initDebug(zone, options) + if options.debugBlip then zone:addDebugBlip() end + if not options.debugPoly then + return + end + + Citizen.CreateThread(function() + while not zone.destroyed do + zone:draw(false) + Citizen.Wait(0) + end + end) +end + +function ComboZone:new(zones, options) + options = options or {} + local useGrid = options.useGrid + if useGrid == nil then useGrid = true end + + local grid = {} + -- Add a unique id for each zone in the ComboZone and add to grid cache + for i=1, #zones do + local zone = zones[i] + if zone then + zone.id = i + end + if useGrid then _addZoneToGrid(grid, zone) end + end + + local zone = { + name = tostring(options.name) or nil, + zones = zones, + useGrid = useGrid, + grid = grid, + debugPoly = options.debugPoly or false, + data = options.data or {}, + isComboZone = true, + } + setmetatable(zone, self) + self.__index = self + return zone +end + +function ComboZone:Create(zones, options) + local zone = ComboZone:new(zones, options) + _initDebug(zone, options) + AddEventHandler("polyzone:pzcomboinfo", function () + zone:printInfo() + end) + return zone +end + +function ComboZone:getZones(point) + if not self.useGrid then + return self.zones + end + + local grid = self.grid + local x, y = _getGridCell(point) + local row = grid[y] + if row == nil or row[x] == nil then + return nil + end + return row[x] +end + +function ComboZone:AddZone(zone) + local zones = self.zones + local newIndex = #zones+1 + zone.id = newIndex + zones[newIndex] = zone + if self.useGrid then + _addZoneToGrid(self.grid, zone) + end + if self.debugBlip then zone:addDebugBlip() end +end + +function ComboZone:RemoveZone(nameOrFn) + local predicateFn = nameOrFn + if type(nameOrFn) == "string" then + -- Create on the fly predicate function if nameOrFn is a string (zone name) + predicateFn = function (zone) return zone.name == nameOrFn end + elseif type(nameOrFn) ~= "function" then + return nil + end + + -- Remove from zones table + local zone = _removeZoneByFunction(predicateFn, self.zones) + if not zone then return nil end + + -- Remove from grid cache + local grid = self.grid + local minY, maxY, minX, maxX = _getZoneBounds(zone) + for y=minY, maxY do + local row = grid[y] + if row then + for x=minX, maxX do + _removeZoneByFunction(predicateFn, row[x]) + end + end + end + return zone +end + +function ComboZone:isPointInside(point, zoneName) + if self.destroyed then + print("[PolyZone] Warning: Called isPointInside on destroyed zone {name=" .. self.name .. "}") + return false, {} + end + + local zones = self:getZones(point) + if not zones or #zones == 0 then return false end + + for i=1, #zones do + local zone = zones[i] + if zone and (zoneName == nil or zoneName == zone.name) and zone:isPointInside(point) then + return true, zone + end + end + return false, nil +end + +function ComboZone:isPointInsideExhaustive(point, insideZones) + if self.destroyed then + print("[PolyZone] Warning: Called isPointInside on destroyed zone {name=" .. self.name .. "}") + return false, {} + end + + if insideZones ~= nil then + insideZones = clearTbl(insideZones) + else + insideZones = {} + end + local zones = self:getZones(point) + if not zones or #zones == 0 then return false, insideZones end + for i=1, #zones do + local zone = zones[i] + if zone and zone:isPointInside(point) then + insideZones[#insideZones+1] = zone + end + end + return #insideZones > 0, insideZones +end + +function ComboZone:destroy() + PolyZone.destroy(self) + local zones = self.zones + for i=1, #zones do + local zone = zones[i] + if zone and not zone.destroyed then + zone:destroy() + end + end +end + +function ComboZone:onPointInOut(getPointCb, onPointInOutCb, waitInMS) + -- Localize the waitInMS value for performance reasons (default of 500 ms) + local _waitInMS = 500 + if waitInMS ~= nil then _waitInMS = waitInMS end + + Citizen.CreateThread(function() + local isInside = nil + local insideZone = nil + while not self.destroyed do + if not self.paused then + local point = getPointCb() + local newIsInside, newInsideZone = self:isPointInside(point) + if newIsInside ~= isInside then + onPointInOutCb(newIsInside, point, newInsideZone or insideZone) + isInside = newIsInside + insideZone = newInsideZone + end + end + Citizen.Wait(_waitInMS) + end + end) +end + +function ComboZone:onPointInOutExhaustive(getPointCb, onPointInOutCb, waitInMS) + -- Localize the waitInMS value for performance reasons (default of 500 ms) + local _waitInMS = 500 + if waitInMS ~= nil then _waitInMS = waitInMS end + + Citizen.CreateThread(function() + local isInside, insideZones = nil, {} + local newIsInside, newInsideZones = nil, {} + while not self.destroyed do + if not self.paused then + local point = getPointCb() + newIsInside, newInsideZones = self:isPointInsideExhaustive(point, newInsideZones) + local isDifferent, enteredZones, leftZones = _differenceBetweenInsideZones(insideZones, newInsideZones) + if newIsInside ~= isInside or isDifferent then + isInside = newIsInside + insideZones = copyTbl(newInsideZones) + onPointInOutCb(isInside, point, insideZones, enteredZones, leftZones) + end + end + Citizen.Wait(_waitInMS) + end + end) +end + +function ComboZone:onPlayerInOut(onPointInOutCb, waitInMS) + self:onPointInOut(PolyZone.getPlayerPosition, onPointInOutCb, waitInMS) +end + +function ComboZone:onPlayerInOutExhaustive(onPointInOutCb, waitInMS) + self:onPointInOutExhaustive(PolyZone.getPlayerPosition, onPointInOutCb, waitInMS) +end + +function ComboZone:addEvent(eventName, zoneName) + if self.events == nil then self.events = {} end + local internalEventName = eventPrefix .. eventName + RegisterNetEvent(internalEventName) + self.events[eventName] = AddEventHandler(internalEventName, function (...) + if self:isPointInside(PolyZone.getPlayerPosition(), zoneName) then + TriggerEvent(eventName, ...) + end + end) +end + +function ComboZone:removeEvent(name) + PolyZone.removeEvent(self, name) +end + +function ComboZone:addDebugBlip() + self.debugBlip = true + local zones = self.zones + for i=1, #zones do + local zone = zones[i] + if zone then zone:addDebugBlip() end + end +end + +function ComboZone:printInfo() + local zones = self.zones + local polyCount, boxCount, circleCount, entityCount, comboCount = 0, 0, 0, 0, 0 + for i=1, #zones do + local zone = zones[i] + if zone then + if zone.isEntityZone then entityCount = entityCount + 1 + elseif zone.isCircleZone then circleCount = circleCount + 1 + elseif zone.isComboZone then comboCount = comboCount + 1 + elseif zone.isBoxZone then boxCount = boxCount + 1 + elseif zone.isPolyZone then polyCount = polyCount + 1 end + end + end + local name = self.name ~= nil and ("\"" .. self.name .. "\"") or nil + print("-----------------------------------------------------") + print("[PolyZone] Info for ComboZone { name = " .. tostring(name) .. " }:") + print("[PolyZone] Total zones: " .. #zones) + if boxCount > 0 then print("[PolyZone] BoxZones: " .. boxCount) end + if circleCount > 0 then print("[PolyZone] CircleZones: " .. circleCount) end + if polyCount > 0 then print("[PolyZone] PolyZones: " .. polyCount) end + if entityCount > 0 then print("[PolyZone] EntityZones: " .. entityCount) end + if comboCount > 0 then print("[PolyZone] ComboZones: " .. comboCount) end + print("-----------------------------------------------------") +end + +function ComboZone:setPaused(paused) + self.paused = paused +end + +function ComboZone:isPaused() + return self.paused +end diff --git a/server-data/resources/[esx_addons]/PolyZone/EntityZone.lua b/server-data/resources/[esx_addons]/PolyZone/EntityZone.lua new file mode 100644 index 000000000..5213cf029 --- /dev/null +++ b/server-data/resources/[esx_addons]/PolyZone/EntityZone.lua @@ -0,0 +1,143 @@ +EntityZone = {} +-- Inherits from BoxZone +setmetatable(EntityZone, { __index = BoxZone }) + +-- Utility functions +local deg, atan2 = math.deg, math.atan2 +local function GetRotation(entity) + local fwdVector = GetEntityForwardVector(entity) + return deg(atan2(fwdVector.y, fwdVector.x)) +end + +local function _calculateMinAndMaxZ(entity, dimensions, scaleZ, offsetZ) + local min, max = dimensions[1], dimensions[2] + local minX, minY, minZ, maxX, maxY, maxZ = min.x, min.y, min.z, max.x, max.y, max.z + + -- Bottom vertices + local p1 = GetOffsetFromEntityInWorldCoords(entity, minX, minY, minZ).z + local p2 = GetOffsetFromEntityInWorldCoords(entity, maxX, minY, minZ).z + local p3 = GetOffsetFromEntityInWorldCoords(entity, maxX, maxY, minZ).z + local p4 = GetOffsetFromEntityInWorldCoords(entity, minX, maxY, minZ).z + + -- Top vertices + local p5 = GetOffsetFromEntityInWorldCoords(entity, minX, minY, maxZ).z + local p6 = GetOffsetFromEntityInWorldCoords(entity, maxX, minY, maxZ).z + local p7 = GetOffsetFromEntityInWorldCoords(entity, maxX, maxY, maxZ).z + local p8 = GetOffsetFromEntityInWorldCoords(entity, minX, maxY, maxZ).z + + local entityMinZ = math.min(p1, p2, p3, p4, p5, p6, p7, p8) + local entityMaxZ = math.max(p1, p2, p3, p4, p5, p6, p7, p8) + return BoxZone.calculateMinAndMaxZ(entityMinZ, entityMaxZ, scaleZ, offsetZ) +end + +-- Initialization functions +local function _initDebug(zone, options) + if options.debugBlip then zone:addDebugBlip() end + if not options.debugPoly and not options.debugBlip then + return + end + + Citizen.CreateThread(function() + local entity = zone.entity + local shouldDraw = options.debugPoly + while not zone.destroyed do + UpdateOffsets(entity, zone) + if shouldDraw then zone:draw(false) end + Citizen.Wait(0) + end + end) +end + +function EntityZone:new(entity, options) + assert(DoesEntityExist(entity), "Entity does not exist") + + local min, max = GetModelDimensions(GetEntityModel(entity)) + local dimensions = {min, max} + + local length = max.y - min.y + local width = max.x - min.x + local pos = GetEntityCoords(entity) + + local zone = BoxZone:new(pos, length, width, options) + if options.useZ == true then + options.minZ, options.maxZ = _calculateMinAndMaxZ(entity, dimensions, zone.scaleZ, zone.offsetZ) + else + options.minZ = nil + options.maxZ = nil + end + zone.entity = entity + zone.dimensions = dimensions + zone.useZ = options.useZ + zone.damageEventHandlers = {} + zone.isEntityZone = true + setmetatable(zone, self) + self.__index = self + return zone +end + +function EntityZone:Create(entity, options) + local zone = EntityZone:new(entity, options) + _initDebug(zone, options) + return zone +end + +function UpdateOffsets(entity, zone) + local pos = GetEntityCoords(entity) + local rot = GetRotation(entity) + zone.offsetPos = pos.xy - zone.startPos + zone.offsetRot = rot - 90.0 + + if zone.useZ then + zone.minZ, zone.maxZ = _calculateMinAndMaxZ(entity, zone.dimensions, zone.scaleZ, zone.offsetZ) + end + if zone.debugBlip then SetBlipCoords(zone.debugBlip, pos.x, pos.y, 0.0) end +end + + +-- Helper functions +function EntityZone:isPointInside(point) + local entity = self.entity + if entity == nil then + print("[PolyZone] Error: Called isPointInside on Entity zone with no entity {name=" .. self.name .. "}") + return false + end + + UpdateOffsets(entity, self) + return BoxZone.isPointInside(self, point) +end + +function EntityZone:onEntityDamaged(onDamagedCb) + local entity = self.entity + if not entity then + print("[PolyZone] Error: Called onEntityDamage on Entity Zone with no entity {name=" .. self.name .. "}") + return + end + + self.damageEventHandlers[#self.damageEventHandlers + 1] = AddEventHandler('gameEventTriggered', function (name, args) + if self.destroyed or self.paused then + return + end + + if name == 'CEventNetworkEntityDamage' then + local victim, attacker, victimDied, weaponHash, isMelee = args[1], args[2], args[4], args[5], args[10] + --print(entity, victim, attacker, victimDied, weaponHash, isMelee) + if victim ~= entity then return end + onDamagedCb(victimDied == 1, attacker, weaponHash, isMelee == 1) + end + end) +end + +function EntityZone:destroy() + for i=1, #self.damageEventHandlers do + print("Destroying damageEventHandler:", self.damageEventHandlers[i]) + RemoveEventHandler(self.damageEventHandlers[i]) + end + self.damageEventHandlers = {} + PolyZone.destroy(self) +end + +function EntityZone:addDebugBlip() + local blip = PolyZone.addDebugBlip(self) + self.debugBlip = blip + return blip +end diff --git a/server-data/resources/[esx_addons]/PolyZone/LICENSE b/server-data/resources/[esx_addons]/PolyZone/LICENSE new file mode 100644 index 000000000..abfbc57de --- /dev/null +++ b/server-data/resources/[esx_addons]/PolyZone/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019-2021 Michael Afrin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/server-data/resources/[esx_addons]/PolyZone/README.md b/server-data/resources/[esx_addons]/PolyZone/README.md new file mode 100644 index 000000000..1bef136e5 --- /dev/null +++ b/server-data/resources/[esx_addons]/PolyZone/README.md @@ -0,0 +1,51 @@ +# PolyZone +PolyZone is a FiveM mod to define zones of different shapes and test whether a point is inside or outside of the zone + +![PolyZone around the prison](https://i.imgur.com/InKNaoL.jpg) + +## Download + +Click [here](https://github.com/mkafrin/PolyZone/releases) to go to the releases page and download the latest release + +## Using PolyZone in a Script + +In order to use PolyZone in your script, you must _at least_ include PolyZone's client.lua directly in your __resource.lua or fxmanifest.lua. You can do that by using FiveM's @ syntax for importing resource files: + +```lua +client_scripts { + '@PolyZone/client.lua', + 'your_scripts_client.lua', +} +``` + +This will allow you to create PolyZones in your script, but will not import other zones, such as CircleZone, BoxZone, etc. All the other zones are extra, and require their own explicit imports. Here is a `client_scripts` value that will include all the zones. Note the relative order of these imports, as the ordering is necessary! Many zones rely on each other, for example EntityZone inherits from BoxZone, and all zones inherit from PolyZone (client.lua). + +```lua +client_scripts { + '@PolyZone/client.lua', + '@PolyZone/BoxZone.lua', + '@PolyZone/EntityZone.lua', + '@PolyZone/CircleZone.lua', + '@PolyZone/ComboZone.lua', + 'your_scripts_client.lua' +} +``` + +## Documentation +For additional information on how to use PolyZone, please take a look at the [wiki](https://github.com/mkafrin/PolyZone/wiki) + +## Troubleshooting and Support +For help troubleshooting issues you've encountered (that aren't in the FAQ), or to suggest new features, use the [issues page](https://github.com/mkafrin/PolyZone/issues). Just a reminder though, I do this in my free time and so there is no guarantee an issue will be fixed or a feature will be added. In lieu of my limited time, I will prioritize issues and bugs over features. + +## FAQ - Frequently Asked Questions +**I'm getting the error `attempt to index a nil value` when creating a zone, what's wrong?** +> Did you include all the necessary scripts in your \_\_resource.lua or fxmanifest.lua? Remember some zones require other zones, like EntityZone.lua requires BoxZone.lua and BoxZone.lua requires client.lua. + +**I'm getting no errors, but I can't see my zone in the right place when I turn on debug drawing** +> If you are using them, is minZ and maxZ set correctly? Or if you are using a CircleZone with useZ=true, is your center's Z value correct? If using a PolyZone, did you manually select all your points, or use the creation script? If you did it manually, the ordering of the points could be causing issues. Are you using the correct option to enable debug drawing? For PolyZones, you can use `debugPoly` and `debugGrid`, but for other zones, `debugPoly` is the only one that works. + +**Is PolyZone faster than a distance check?** +> There's a page in the wiki for that, [here](https://github.com/mkafrin/PolyZone/wiki/Is-PolyZone-faster-than-a-distance-check%3F). + +## License +**Please see the LICENSE file. That file will always overrule anything mentioned in the README.md or wiki** diff --git a/server-data/resources/[esx_addons]/PolyZone/client.lua b/server-data/resources/[esx_addons]/PolyZone/client.lua new file mode 100644 index 000000000..a71a5e7b4 --- /dev/null +++ b/server-data/resources/[esx_addons]/PolyZone/client.lua @@ -0,0 +1,601 @@ +eventPrefix = '__PolyZone__:' +PolyZone = {} + +local defaultColorWalls = {0, 255, 0} +local defaultColorOutline = {255, 0, 0} +local defaultColorGrid = {255, 255, 255} + +-- Utility functions +local abs = math.abs +local function _isLeft(p0, p1, p2) + local p0x = p0.x + local p0y = p0.y + return ((p1.x - p0x) * (p2.y - p0y)) - ((p2.x - p0x) * (p1.y - p0y)) +end + +local function _wn_inner_loop(p0, p1, p2, wn) + local p2y = p2.y + if (p0.y <= p2y) then + if (p1.y > p2y) then + if (_isLeft(p0, p1, p2) > 0) then + return wn + 1 + end + end + else + if (p1.y <= p2y) then + if (_isLeft(p0, p1, p2) < 0) then + return wn - 1 + end + end + end + return wn +end + +function addBlip(pos) + local blip = AddBlipForCoord(pos.x, pos.y, 0.0) + SetBlipColour(blip, 7) + SetBlipDisplay(blip, 8) + SetBlipScale(blip, 1.0) + SetBlipAsShortRange(blip, true) + return blip +end + +function clearTbl(tbl) + -- Only works with contiguous (array-like) tables + if tbl == nil then return end + for i=1, #tbl do + tbl[i] = nil + end + return tbl +end + +function copyTbl(tbl) + -- Only a shallow copy, and only works with contiguous (array-like) tables + if tbl == nil then return end + local ret = {} + for i=1, #tbl do + ret[i] = tbl[i] + end + return ret +end + +-- Winding Number Algorithm - http://geomalgorithms.com/a03-_inclusion.html +local function _windingNumber(point, poly) + local wn = 0 -- winding number counter + + -- loop through all edges of the polygon + for i = 1, #poly - 1 do + wn = _wn_inner_loop(poly[i], poly[i + 1], point, wn) + end + -- test last point to first point, completing the polygon + wn = _wn_inner_loop(poly[#poly], poly[1], point, wn) + + -- the point is outside only when this winding number wn===0, otherwise it's inside + return wn ~= 0 +end + +-- Detects intersection between two lines +local function _isIntersecting(a, b, c, d) + -- Store calculations in local variables for performance + local ax_minus_cx = a.x - c.x + local bx_minus_ax = b.x - a.x + local dx_minus_cx = d.x - c.x + local ay_minus_cy = a.y - c.y + local by_minus_ay = b.y - a.y + local dy_minus_cy = d.y - c.y + local denominator = ((bx_minus_ax) * (dy_minus_cy)) - ((by_minus_ay) * (dx_minus_cx)) + local numerator1 = ((ay_minus_cy) * (dx_minus_cx)) - ((ax_minus_cx) * (dy_minus_cy)) + local numerator2 = ((ay_minus_cy) * (bx_minus_ax)) - ((ax_minus_cx) * (by_minus_ay)) + + -- Detect coincident lines + if denominator == 0 then return numerator1 == 0 and numerator2 == 0 end + + local r = numerator1 / denominator + local s = numerator2 / denominator + + return (r >= 0 and r <= 1) and (s >= 0 and s <= 1) +end + +-- https://rosettacode.org/wiki/Shoelace_formula_for_polygonal_area#Lua +local function _calculatePolygonArea(points) + local function det2(i,j) + return points[i].x*points[j].y-points[j].x*points[i].y + end + local sum = #points>2 and det2(#points,1) or 0 + for i=1,#points-1 do sum = sum + det2(i,i+1)end + return abs(0.5 * sum) +end + + +-- Debug drawing functions +function _drawWall(p1, p2, minZ, maxZ, r, g, b, a) + local bottomLeft = vector3(p1.x, p1.y, minZ) + local topLeft = vector3(p1.x, p1.y, maxZ) + local bottomRight = vector3(p2.x, p2.y, minZ) + local topRight = vector3(p2.x, p2.y, maxZ) + + DrawPoly(bottomLeft,topLeft,bottomRight,r,g,b,a) + DrawPoly(topLeft,topRight,bottomRight,r,g,b,a) + DrawPoly(bottomRight,topRight,topLeft,r,g,b,a) + DrawPoly(bottomRight,topLeft,bottomLeft,r,g,b,a) +end + +function PolyZone:TransformPoint(point) + -- No point transform necessary for regular PolyZones, unlike zones like Entity Zones, whose points can be rotated and offset + return point +end + +function PolyZone:draw(forceDraw) + if not forceDraw and not self.debugPoly and not self.debugGrid then return end + + local zDrawDist = 45.0 + local oColor = self.debugColors.outline or defaultColorOutline + local oR, oG, oB = oColor[1], oColor[2], oColor[3] + local wColor = self.debugColors.walls or defaultColorWalls + local wR, wG, wB = wColor[1], wColor[2], wColor[3] + local plyPed = PlayerPedId() + local plyPos = GetEntityCoords(plyPed) + local minZ = self.minZ or plyPos.z - zDrawDist + local maxZ = self.maxZ or plyPos.z + zDrawDist + + local points = self.points + for i=1, #points do + local point = self:TransformPoint(points[i]) + DrawLine(point.x, point.y, minZ, point.x, point.y, maxZ, oR, oG, oB, 164) + + if i < #points then + local p2 = self:TransformPoint(points[i+1]) + DrawLine(point.x, point.y, maxZ, p2.x, p2.y, maxZ, oR, oG, oB, 184) + _drawWall(point, p2, minZ, maxZ, wR, wG, wB, 48) + end + end + + if #points > 2 then + local firstPoint = self:TransformPoint(points[1]) + local lastPoint = self:TransformPoint(points[#points]) + DrawLine(firstPoint.x, firstPoint.y, maxZ, lastPoint.x, lastPoint.y, maxZ, oR, oG, oB, 184) + _drawWall(firstPoint, lastPoint, minZ, maxZ, wR, wG, wB, 48) + end +end + +function PolyZone.drawPoly(poly, forceDraw) + PolyZone.draw(poly, forceDraw) +end + +-- Debug drawing all grid cells that are completly within the polygon +local function _drawGrid(poly) + local minZ = poly.minZ + local maxZ = poly.maxZ + if not minZ or not maxZ then + local plyPed = PlayerPedId() + local plyPos = GetEntityCoords(plyPed) + minZ = plyPos.z - 46.0 + maxZ = plyPos.z - 45.0 + end + + local lines = poly.lines + local color = poly.debugColors.grid or defaultColorGrid + local r, g, b = color[1], color[2], color[3] + for i=1, #lines do + local line = lines[i] + local min = line.min + local max = line.max + DrawLine(min.x + 0.0, min.y + 0.0, maxZ + 0.0, max.x + 0.0, max.y + 0.0, maxZ + 0.0, r, g, b, 196) + end +end + + +local function _pointInPoly(point, poly) + local x = point.x + local y = point.y + local min = poly.min + local minX = min.x + local minY = min.y + local max = poly.max + + -- Checks if point is within the polygon's bounding box + if x < minX or + x > max.x or + y < minY or + y > max.y then + return false + end + + -- Checks if point is within the polygon's height bounds + local minZ = poly.minZ + local maxZ = poly.maxZ + local z = point.z + if (minZ and z < minZ) or (maxZ and z > maxZ) then + return false + end + + -- Returns true if the grid cell associated with the point is entirely inside the poly + local grid = poly.grid + if grid then + local gridDivisions = poly.gridDivisions + local size = poly.size + local gridPosX = x - minX + local gridPosY = y - minY + local gridCellX = (gridPosX * gridDivisions) // size.x + local gridCellY = (gridPosY * gridDivisions) // size.y + local gridCellValue = grid[gridCellY + 1][gridCellX + 1] + if gridCellValue == nil and poly.lazyGrid then + gridCellValue = _isGridCellInsidePoly(gridCellX, gridCellY, poly) + grid[gridCellY + 1][gridCellX + 1] = gridCellValue + end + if gridCellValue then return true end + end + + return _windingNumber(point, poly.points) +end + + +-- Grid creation functions +-- Calculates the points of the rectangle that make up the grid cell at grid position (cellX, cellY) +local function _calculateGridCellPoints(cellX, cellY, poly) + local gridCellWidth = poly.gridCellWidth + local gridCellHeight = poly.gridCellHeight + local min = poly.min + -- min added to initial point, in order to shift the grid cells to the poly's starting position + local x = cellX * gridCellWidth + min.x + local y = cellY * gridCellHeight + min.y + return { + vector2(x, y), + vector2(x + gridCellWidth, y), + vector2(x + gridCellWidth, y + gridCellHeight), + vector2(x, y + gridCellHeight), + vector2(x, y) + } +end + + +function _isGridCellInsidePoly(cellX, cellY, poly) + gridCellPoints = _calculateGridCellPoints(cellX, cellY, poly) + local polyPoints = {table.unpack(poly.points)} + -- Connect the polygon to its starting point + polyPoints[#polyPoints + 1] = polyPoints[1] + + -- If none of the points of the grid cell are in the polygon, the grid cell can't be in it + local isOnePointInPoly = false + for i=1, #gridCellPoints - 1 do + local cellPoint = gridCellPoints[i] + local x = cellPoint.x + local y = cellPoint.y + if _windingNumber(cellPoint, poly.points) then + isOnePointInPoly = true + -- If we are drawing the grid (poly.lines ~= nil), we need to go through all the points, + -- and therefore can't break out of the loop early + if poly.lines then + if not poly.gridXPoints[x] then poly.gridXPoints[x] = {} end + if not poly.gridYPoints[y] then poly.gridYPoints[y] = {} end + poly.gridXPoints[x][y] = true + poly.gridYPoints[y][x] = true + else break end + end + end + if isOnePointInPoly == false then + return false + end + + -- If any of the grid cell's lines intersects with any of the polygon's lines + -- then the grid cell is not completely within the poly + for i=1, #gridCellPoints - 1 do + local gridCellP1 = gridCellPoints[i] + local gridCellP2 = gridCellPoints[i+1] + for j=1, #polyPoints - 1 do + if _isIntersecting(gridCellP1, gridCellP2, polyPoints[j], polyPoints[j+1]) then + return false + end + end + end + + return true +end + + +local function _calculateLinesForDrawingGrid(poly) + local lines = {} + for x, tbl in pairs(poly.gridXPoints) do + local yValues = {} + -- Turn dict/set of values into array + for y, _ in pairs(tbl) do yValues[#yValues + 1] = y end + if #yValues >= 2 then + table.sort(yValues) + local minY = yValues[1] + local lastY = yValues[1] + for i=1, #yValues do + local y = yValues[i] + -- Checks for breaks in the grid. If the distance between the last value and the current one + -- is greater than the size of a grid cell, that means the line between them must go outside the polygon. + -- Therefore, a line must be created between minY and the lastY, and a new line started at the current y + if y - lastY > poly.gridCellHeight + 0.01 then + lines[#lines+1] = {min=vector2(x, minY), max=vector2(x, lastY)} + minY = y + elseif i == #yValues then + -- If at the last point, create a line between minY and the last point + lines[#lines+1] = {min=vector2(x, minY), max=vector2(x, y)} + end + lastY = y + end + end + end + -- Setting nil to allow the GC to clear it out of memory, since we no longer need this + poly.gridXPoints = nil + + -- Same as above, but for gridYPoints instead of gridXPoints + for y, tbl in pairs(poly.gridYPoints) do + local xValues = {} + for x, _ in pairs(tbl) do xValues[#xValues + 1] = x end + if #xValues >= 2 then + table.sort(xValues) + local minX = xValues[1] + local lastX = xValues[1] + for i=1, #xValues do + local x = xValues[i] + if x - lastX > poly.gridCellWidth + 0.01 then + lines[#lines+1] = {min=vector2(minX, y), max=vector2(lastX, y)} + minX = x + elseif i == #xValues then + lines[#lines+1] = {min=vector2(minX, y), max=vector2(x, y)} + end + lastX = x + end + end + end + poly.gridYPoints = nil + return lines +end + + +-- Calculate for each grid cell whether it is entirely inside the polygon, and store if true +local function _createGrid(poly, options) + poly.gridArea = 0.0 + poly.gridCellWidth = poly.size.x / poly.gridDivisions + poly.gridCellHeight = poly.size.y / poly.gridDivisions + Citizen.CreateThread(function() + -- Calculate all grid cells that are entirely inside the polygon + local isInside = {} + local gridCellArea = poly.gridCellWidth * poly.gridCellHeight + for y=1, poly.gridDivisions do + Citizen.Wait(0) + isInside[y] = {} + for x=1, poly.gridDivisions do + if _isGridCellInsidePoly(x-1, y-1, poly) then + poly.gridArea = poly.gridArea + gridCellArea + isInside[y][x] = true + end + end + end + poly.grid = isInside + poly.gridCoverage = poly.gridArea / poly.area + -- A lot of memory is used by this pre-calc. Force a gc collect after to clear it out + collectgarbage("collect") + + if options.debugGrid then + local coverage = string.format("%.2f", poly.gridCoverage * 100) + print("[PolyZone] Debug: Grid Coverage at " .. coverage .. "% with " .. poly.gridDivisions + .. " divisions. Optimal coverage for memory usage and startup time is 80-90%") + + Citizen.CreateThread(function() + poly.lines = _calculateLinesForDrawingGrid(poly) + -- A lot of memory is used by this pre-calc. Force a gc collect after to clear it out + collectgarbage("collect") + end) + end + end) +end + + +-- Initialization functions +local function _calculatePoly(poly, options) + if not poly.min or not poly.max or not poly.size or not poly.center or not poly.area then + local minX, minY = math.maxinteger, math.maxinteger + local maxX, maxY = math.mininteger, math.mininteger + for _, p in ipairs(poly.points) do + minX = math.min(minX, p.x) + minY = math.min(minY, p.y) + maxX = math.max(maxX, p.x) + maxY = math.max(maxY, p.y) + end + poly.min = vector2(minX, minY) + poly.max = vector2(maxX, maxY) + poly.size = poly.max - poly.min + poly.center = (poly.max + poly.min) / 2 + poly.area = _calculatePolygonArea(poly.points) + end + + poly.boundingRadius = math.sqrt(poly.size.y * poly.size.y + poly.size.x * poly.size.x) / 2 + + if poly.useGrid and not poly.lazyGrid then + if options.debugGrid then + poly.gridXPoints = {} + poly.gridYPoints = {} + poly.lines = {} + end + _createGrid(poly, options) + elseif poly.useGrid then + local isInside = {} + for y=1, poly.gridDivisions do + isInside[y] = {} + end + poly.grid = isInside + poly.gridCellWidth = poly.size.x / poly.gridDivisions + poly.gridCellHeight = poly.size.y / poly.gridDivisions + end +end + + +local function _initDebug(poly, options) + if options.debugBlip then poly:addDebugBlip() end + local debugEnabled = options.debugPoly or options.debugGrid + if not debugEnabled then + return + end + + Citizen.CreateThread(function() + while not poly.destroyed do + poly:draw(false) + if options.debugGrid and poly.lines then + _drawGrid(poly) + end + Citizen.Wait(0) + end + end) +end + +function PolyZone:new(points, options) + if not points then + print("[PolyZone] Error: Passed nil points table to PolyZone:Create() {name=" .. options.name .. "}") + return + end + if #points < 3 then + print("[PolyZone] Warning: Passed points table with less than 3 points to PolyZone:Create() {name=" .. options.name .. "}") + end + + options = options or {} + local useGrid = options.useGrid + if useGrid == nil then useGrid = true end + local lazyGrid = options.lazyGrid + if lazyGrid == nil then lazyGrid = true end + local poly = { + name = tostring(options.name) or nil, + points = points, + center = options.center, + size = options.size, + max = options.max, + min = options.min, + area = options.area, + minZ = tonumber(options.minZ) or nil, + maxZ = tonumber(options.maxZ) or nil, + useGrid = useGrid, + lazyGrid = lazyGrid, + gridDivisions = tonumber(options.gridDivisions) or 30, + debugColors = options.debugColors or {}, + debugPoly = options.debugPoly or false, + debugGrid = options.debugGrid or false, + data = options.data or {}, + isPolyZone = true, + } + if poly.debugGrid then poly.lazyGrid = false end + _calculatePoly(poly, options) + setmetatable(poly, self) + self.__index = self + return poly +end + +function PolyZone:Create(points, options) + local poly = PolyZone:new(points, options) + _initDebug(poly, options) + return poly +end + +function PolyZone:isPointInside(point) + if self.destroyed then + print("[PolyZone] Warning: Called isPointInside on destroyed zone {name=" .. self.name .. "}") + return false + end + + return _pointInPoly(point, self) +end + +function PolyZone:destroy() + self.destroyed = true + if self.debugPoly or self.debugGrid then + print("[PolyZone] Debug: Destroying zone {name=" .. self.name .. "}") + end +end + +-- Helper functions +function PolyZone.getPlayerPosition() + return GetEntityCoords(PlayerPedId()) +end + +HeadBone = 0x796e; +function PolyZone.getPlayerHeadPosition() + return GetPedBoneCoords(PlayerPedId(), HeadBone); +end + +function PolyZone.ensureMetatable(zone) + if zone.isComboZone then + setmetatable(zone, ComboZone) + elseif zone.isEntityZone then + setmetatable(zone, EntityZone) + elseif zone.isBoxZone then + setmetatable(zone, BoxZone) + elseif zone.isCircleZone then + setmetatable(zone, CircleZone) + elseif zone.isPolyZone then + setmetatable(zone, PolyZone) + end +end + +function PolyZone:onPointInOut(getPointCb, onPointInOutCb, waitInMS) + -- Localize the waitInMS value for performance reasons (default of 500 ms) + local _waitInMS = 500 + if waitInMS ~= nil then _waitInMS = waitInMS end + + Citizen.CreateThread(function() + local isInside = false + while not self.destroyed do + if not self.paused then + local point = getPointCb() + local newIsInside = self:isPointInside(point) + if newIsInside ~= isInside then + onPointInOutCb(newIsInside, point) + isInside = newIsInside + end + end + Citizen.Wait(_waitInMS) + end + end) +end + +function PolyZone:onPlayerInOut(onPointInOutCb, waitInMS) + self:onPointInOut(PolyZone.getPlayerPosition, onPointInOutCb, waitInMS) +end + +function PolyZone:addEvent(eventName) + if self.events == nil then self.events = {} end + local internalEventName = eventPrefix .. eventName + RegisterNetEvent(internalEventName) + self.events[eventName] = AddEventHandler(internalEventName, function (...) + if self:isPointInside(PolyZone.getPlayerPosition()) then + TriggerEvent(eventName, ...) + end + end) +end + +function PolyZone:removeEvent(eventName) + if self.events and self.events[eventName] then + RemoveEventHandler(self.events[eventName]) + self.events[eventName] = nil + end +end + +function PolyZone:addDebugBlip() + return addBlip(self.center or self:getBoundingBoxCenter()) +end + +function PolyZone:setPaused(paused) + self.paused = paused +end + +function PolyZone:isPaused() + return self.paused +end + +function PolyZone:getBoundingBoxMin() + return self.min +end + +function PolyZone:getBoundingBoxMax() + return self.max +end + +function PolyZone:getBoundingBoxSize() + return self.size +end + +function PolyZone:getBoundingBoxCenter() + return self.center +end diff --git a/server-data/resources/[esx_addons]/PolyZone/creation/client/BoxZone.lua b/server-data/resources/[esx_addons]/PolyZone/creation/client/BoxZone.lua new file mode 100644 index 000000000..69eeec139 --- /dev/null +++ b/server-data/resources/[esx_addons]/PolyZone/creation/client/BoxZone.lua @@ -0,0 +1,113 @@ +local function handleInput(useZ, heading, length, width, center) + if not useZ then + local scaleDelta, headingDelta = 0.2, 5 + BlockWeaponWheelThisFrame() + + if IsDisabledControlPressed(0, 36) then -- ctrl held down + scaleDelta, headingDelta = 0.05, 1 + end + + if IsDisabledControlJustPressed(0, 81) then -- scroll wheel down just pressed + + if IsDisabledControlPressed(0, 19) then -- alt held down + return heading, length, math.max(0.0, width - scaleDelta), center + end + if IsDisabledControlPressed(0, 21) then -- shift held down + return heading, math.max(0.0, length - scaleDelta), width, center + end + return (heading - headingDelta) % 360, length, width, center + end + + + if IsDisabledControlJustPressed(0, 99) then -- scroll wheel up just pressed + + if IsDisabledControlPressed(0, 19) then -- alt held down + return heading, length, math.max(0.0, width + scaleDelta), center + end + if IsDisabledControlPressed(0, 21) then -- shift held down + return heading, math.max(0.0, length + scaleDelta), width, center + end + return (heading + headingDelta) % 360, length, width, center + end + end + + local rot = GetGameplayCamRot(2) + center = handleArrowInput(center, rot.z) + + return heading, length, width, center +end + +function handleZ(minZ, maxZ) + local delta = 0.2 + + if IsDisabledControlPressed(0, 36) then -- ctrl held down + delta = 0.05 + end + + BlockWeaponWheelThisFrame() + + if IsDisabledControlJustPressed(0, 81) then -- scroll wheel down just pressed + + if IsDisabledControlPressed(0, 19) then -- alt held down + return minZ - delta, maxZ + end + if IsDisabledControlPressed(0, 21) then -- shift held down + return minZ, maxZ - delta + end + return minZ - delta, maxZ - delta + end + + if IsDisabledControlJustPressed(0, 99) then -- scroll wheel up just pressed + + if IsDisabledControlPressed(0, 19) then -- alt held down + return minZ + delta, maxZ + end + if IsDisabledControlPressed(0, 21) then -- shift held down + return minZ, maxZ + delta + end + return minZ + delta, maxZ + delta + end + return minZ, maxZ +end + +function boxStart(name, heading, length, width, minHeight, maxHeight) + local center = GetEntityCoords(PlayerPedId()) + createdZone = BoxZone:Create(center, length, width, {name = tostring(name)}) + local useZ, minZ, maxZ = false, center.z - 1.0, center.z + 3.0 + if minHeight then + minZ = center.z - minHeight + createdZone.minZ = minZ + end + if maxHeight then + maxZ = center.z + maxHeight + createdZone.maxZ = maxZ + end + Citizen.CreateThread(function() + while createdZone do + if IsDisabledControlJustPressed(0, 20) then -- Z pressed + useZ = not useZ + if useZ then + createdZone.debugColors.walls = {255, 0, 0} + else + createdZone.debugColors.walls = {0, 255, 0} + end + end + heading, length, width, center = handleInput(useZ, heading, length, width, center) + if useZ then + minZ, maxZ = handleZ(minZ, maxZ) + createdZone.minZ = minZ + createdZone.maxZ = maxZ + end + createdZone:setLength(length) + createdZone:setWidth(width) + createdZone:setHeading(heading) + createdZone:setCenter(center) + Wait(0) + end + end) +end + +function boxFinish() + TriggerServerEvent("polyzone:printBox", + {name=createdZone.name, center=createdZone.center, length=createdZone.length, width=createdZone.width, heading=createdZone.offsetRot, minZ=createdZone.minZ, maxZ=createdZone.maxZ}) +end \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/PolyZone/creation/client/CircleZone.lua b/server-data/resources/[esx_addons]/PolyZone/creation/client/CircleZone.lua new file mode 100644 index 000000000..5e6cb8f3a --- /dev/null +++ b/server-data/resources/[esx_addons]/PolyZone/creation/client/CircleZone.lua @@ -0,0 +1,54 @@ +local function handleInput(radius, center, useZ) + local delta = 0.05 + BlockWeaponWheelThisFrame() + + if IsDisabledControlPressed(0, 36) then -- ctrl held down + delta = 0.01 + end + + if IsDisabledControlJustPressed(0, 81) then -- scroll wheel down just pressed + + if IsDisabledControlPressed(0, 19) then -- alt held down + return radius, vector3(center.x, center.y, center.z - delta), useZ + end + return math.max(0.0, radius - delta), center, useZ + end + + + if IsDisabledControlJustPressed(0, 99) then -- scroll wheel up just pressed + + if IsDisabledControlPressed(0, 19) then -- alt held down + return radius, vector3(center.x, center.y, center.z + delta), useZ + end + return radius + delta, center, useZ + end + + if IsDisabledControlJustPressed(0, 20) then -- Z pressed + return radius, center, not useZ + end + + local rot = GetGameplayCamRot(2) + center = handleArrowInput(center, rot.z) + + return radius, center, useZ +end + +function circleStart(name, radius, useZ) + local center = GetEntityCoords(PlayerPedId()) + useZ = useZ or false + createdZone = CircleZone:Create(center, radius, {name = tostring(name), useZ = useZ}) + Citizen.CreateThread(function() + while createdZone do + radius, center, useZ = handleInput(radius, center, useZ) + createdZone:setRadius(radius) + createdZone:setCenter(center) + createdZone.useZ = useZ + Wait(0) + end + end) +end + +function circleFinish() + TriggerServerEvent("polyzone:printCircle", + {name=createdZone.name, center=createdZone.center, radius=createdZone.radius, useZ=createdZone.useZ}) +end \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/PolyZone/creation/client/PolyZone.lua b/server-data/resources/[esx_addons]/PolyZone/creation/client/PolyZone.lua new file mode 100644 index 000000000..e5ff53266 --- /dev/null +++ b/server-data/resources/[esx_addons]/PolyZone/creation/client/PolyZone.lua @@ -0,0 +1,60 @@ +local minZ, maxZ = nil, nil + +local function handleInput(center) + local rot = GetGameplayCamRot(2) + center = handleArrowInput(center, rot.z) + return center +end + +function polyStart(name) + local coords = GetEntityCoords(PlayerPedId()) + createdZone = PolyZone:Create({vector2(coords.x, coords.y)}, {name = tostring(name), useGrid=false}) + Citizen.CreateThread(function() + while createdZone do + -- Have to convert the point to a vector3 prior to calling handleInput, + -- then convert it back to vector2 afterwards + lastPoint = createdZone.points[#createdZone.points] + lastPoint = vector3(lastPoint.x, lastPoint.y, 0.0) + lastPoint = handleInput(lastPoint) + createdZone.points[#createdZone.points] = lastPoint.xy + Wait(0) + end + end) + minZ, maxZ = coords.z, coords.z +end + +function polyFinish() + TriggerServerEvent("polyzone:printPoly", + {name=createdZone.name, points=createdZone.points, minZ=minZ, maxZ=maxZ}) +end + +RegisterNetEvent("polyzone:pzadd") +AddEventHandler("polyzone:pzadd", function() + if createdZone == nil or createdZoneType ~= 'poly' then + return + end + + local coords = GetEntityCoords(PlayerPedId()) + + if (coords.z > maxZ) then + maxZ = coords.z + end + + if (coords.z < minZ) then + minZ = coords.z + end + + createdZone.points[#createdZone.points + 1] = vector2(coords.x, coords.y) +end) + +RegisterNetEvent("polyzone:pzundo") +AddEventHandler("polyzone:pzundo", function() + if createdZone == nil or createdZoneType ~= 'poly' then + return + end + + createdZone.points[#createdZone.points] = nil + if #createdZone.points == 0 then + TriggerEvent("polyzone:pzcancel") + end +end) \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/PolyZone/creation/client/commands.lua b/server-data/resources/[esx_addons]/PolyZone/creation/client/commands.lua new file mode 100644 index 000000000..c3f6652a3 --- /dev/null +++ b/server-data/resources/[esx_addons]/PolyZone/creation/client/commands.lua @@ -0,0 +1,68 @@ +RegisterCommand("pzcreate", function(src, args) + local zoneType = args[1] + if zoneType == nil then + TriggerEvent('chat:addMessage', { + color = { 255, 0, 0}, + multiline = true, + args = {"Me", "Please add zone type to create (poly, circle, box)!"} + }) + return + end + if zoneType ~= 'poly' and zoneType ~= 'circle' and zoneType ~= 'box' then + TriggerEvent('chat:addMessage', { + color = { 255, 0, 0}, + multiline = true, + args = {"Me", "Zone type must be one of: poly, circle, box"} + }) + return + end + local name = nil + if #args >= 2 then name = args[2] + else name = GetUserInput("Enter name of zone:") end + if name == nil or name == "" then + TriggerEvent('chat:addMessage', { + color = { 255, 0, 0}, + multiline = true, + args = {"Me", "Please add a name!"} + }) + return + end + TriggerEvent("polyzone:pzcreate", zoneType, name, args) +end) + +RegisterCommand("pzadd", function(src, args) + TriggerEvent("polyzone:pzadd") +end) + +RegisterCommand("pzundo", function(src, args) + TriggerEvent("polyzone:pzundo") +end) + +RegisterCommand("pzfinish", function(src, args) + TriggerEvent("polyzone:pzfinish") +end) + +RegisterCommand("pzlast", function(src, args) + TriggerEvent("polyzone:pzlast") +end) + +RegisterCommand("pzcancel", function(src, args) + TriggerEvent("polyzone:pzcancel") +end) + +RegisterCommand("pzcomboinfo", function (src, args) + TriggerEvent("polyzone:pzcomboinfo") +end) + +Citizen.CreateThread(function() + TriggerEvent('chat:addSuggestion', '/pzcreate', 'Starts creation of a zone for PolyZone of one of the available types: circle, box, poly', { + {name="zoneType", help="Zone Type (required)"}, + }) + + TriggerEvent('chat:addSuggestion', '/pzadd', 'Adds point to zone.', {}) + TriggerEvent('chat:addSuggestion', '/pzundo', 'Undoes the last point added.', {}) + TriggerEvent('chat:addSuggestion', '/pzfinish', 'Finishes and prints zone.', {}) + TriggerEvent('chat:addSuggestion', '/pzlast', 'Starts creation of the last zone you finished (only works on BoxZone and CircleZone)', {}) + TriggerEvent('chat:addSuggestion', '/pzcancel', 'Cancel zone creation.', {}) + TriggerEvent('chat:addSuggestion', '/pzcomboinfo', 'Prints some useful info for all created ComboZones', {}) +end) \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/PolyZone/creation/client/creation.lua b/server-data/resources/[esx_addons]/PolyZone/creation/client/creation.lua new file mode 100644 index 000000000..bb69b8daa --- /dev/null +++ b/server-data/resources/[esx_addons]/PolyZone/creation/client/creation.lua @@ -0,0 +1,158 @@ +lastCreatedZoneType = nil +lastCreatedZone = nil +createdZoneType = nil +createdZone = nil +drawZone = false + +RegisterNetEvent("polyzone:pzcreate") +AddEventHandler("polyzone:pzcreate", function(zoneType, name, args) + if createdZone ~= nil then + TriggerEvent('chat:addMessage', { + color = { 255, 0, 0}, + multiline = true, + args = {"Me", "A shape is already being created!"} + }) + return + end + + if zoneType == 'poly' then + polyStart(name) + elseif zoneType == "circle" then + local radius = nil + if #args >= 3 then radius = tonumber(args[3]) + else radius = tonumber(GetUserInput("Enter radius:")) end + if radius == nil then + TriggerEvent('chat:addMessage', { + color = { 255, 0, 0}, + multiline = true, + args = {"Me", "CircleZone requires a radius (must be a number)!"} + }) + return + end + circleStart(name, radius) + elseif zoneType == "box" then + local length = nil + if #args >= 3 then length = tonumber(args[3]) + else length = tonumber(GetUserInput("Enter length:")) end + if length == nil or length < 0.0 then + TriggerEvent('chat:addMessage', { + color = { 255, 0, 0}, + multiline = true, + args = {"Me", "BoxZone requires a length (must be a positive number)!"} + }) + return + end + local width = nil + if #args >= 4 then width = tonumber(args[4]) + else width = tonumber(GetUserInput("Enter width:")) end + if width == nil or width < 0.0 then + TriggerEvent('chat:addMessage', { + color = { 255, 0, 0}, + multiline = true, + args = {"Me", "BoxZone requires a width (must be a positive number)!"} + }) + return + end + boxStart(name, 0, length, width) + else + return + end + createdZoneType = zoneType + drawZone = true + disableControlKeyInput() + drawThread() +end) + +RegisterNetEvent("polyzone:pzfinish") +AddEventHandler("polyzone:pzfinish", function() + if createdZone == nil then + return + end + + if createdZoneType == 'poly' then + polyFinish() + elseif createdZoneType == "circle" then + circleFinish() + elseif createdZoneType == "box" then + boxFinish() + end + + TriggerEvent('chat:addMessage', { + color = { 0, 255, 0}, + multiline = true, + args = {"Me", "Check your server root folder for polyzone_created_zones.txt to get the zone!"} + }) + + lastCreatedZoneType = createdZoneType + lastCreatedZone = createdZone + + drawZone = false + createdZone = nil + createdZoneType = nil +end) + +RegisterNetEvent("polyzone:pzlast") +AddEventHandler("polyzone:pzlast", function() + if createdZone ~= nil or lastCreatedZone == nil then + return + end + if lastCreatedZoneType == 'poly' then + TriggerEvent('chat:addMessage', { + color = { 0, 255, 0}, + multiline = true, + args = {"Me", "The command pzlast only supports BoxZone and CircleZone for now"} + }) + end + + local name = GetUserInput("Enter name (or leave empty to reuse last zone's name):") + if name == nil then + return + elseif name == "" then + name = lastCreatedZone.name + end + createdZoneType = lastCreatedZoneType + if createdZoneType == 'box' then + local minHeight, maxHeight + if lastCreatedZone.minZ then + minHeight = lastCreatedZone.center.z - lastCreatedZone.minZ + end + if lastCreatedZone.maxZ then + maxHeight = lastCreatedZone.maxZ - lastCreatedZone.center.z + end + boxStart(name, lastCreatedZone.offsetRot, lastCreatedZone.length, lastCreatedZone.width, minHeight, maxHeight) + elseif createdZoneType == 'circle' then + circleStart(name, lastCreatedZone.radius, lastCreatedZone.useZ) + end + drawZone = true + disableControlKeyInput() + drawThread() +end) + +RegisterNetEvent("polyzone:pzcancel") +AddEventHandler("polyzone:pzcancel", function() + if createdZone == nil then + return + end + + TriggerEvent('chat:addMessage', { + color = {255, 0, 0}, + multiline = true, + args = {"Me", "Zone creation canceled!"} + }) + + drawZone = false + createdZone = nil + createdZoneType = nil +end) + +-- Drawing +function drawThread() + Citizen.CreateThread(function() + while drawZone do + if createdZone then + createdZone:draw(true) + end + Wait(0) + end + end) +end diff --git a/server-data/resources/[esx_addons]/PolyZone/creation/client/utils.lua b/server-data/resources/[esx_addons]/PolyZone/creation/client/utils.lua new file mode 100644 index 000000000..786397dd0 --- /dev/null +++ b/server-data/resources/[esx_addons]/PolyZone/creation/client/utils.lua @@ -0,0 +1,75 @@ +-- GetUserInput function inspired by vMenu (https://github.com/TomGrobbe/vMenu/blob/master/vMenu/CommonFunctions.cs) +function GetUserInput(windowTitle, defaultText, maxInputLength) + -- Create the window title string. + local resourceName = string.upper(GetCurrentResourceName()) + local textEntry = resourceName .. "_WINDOW_TITLE" + if windowTitle == nil then + windowTitle = "Enter:" + end + AddTextEntry(textEntry, windowTitle) + + -- Display the input box. + DisplayOnscreenKeyboard(1, textEntry, "", defaultText or "", "", "", "", maxInputLength or 30) + Wait(0) + -- Wait for a result. + while true do + local keyboardStatus = UpdateOnscreenKeyboard(); + if keyboardStatus == 3 then -- not displaying input field anymore somehow + return nil + elseif keyboardStatus == 2 then -- cancelled + return nil + elseif keyboardStatus == 1 then -- finished editing + return GetOnscreenKeyboardResult() + else + Wait(0) + end + end +end + +function handleArrowInput(center, heading) + delta = 0.05 + + if IsDisabledControlPressed(0, 36) then -- ctrl held down + delta = 0.01 + end + + if IsDisabledControlPressed(0, 172) then -- arrow up + local newCenter = PolyZone.rotate(center.xy, vector2(center.x, center.y + delta), heading) + return vector3(newCenter.x, newCenter.y, center.z) + end + + if IsDisabledControlPressed(0, 173) then -- arrow down + local newCenter = PolyZone.rotate(center.xy, vector2(center.x, center.y - delta), heading) + return vector3(newCenter.x, newCenter.y, center.z) + end + + if IsDisabledControlPressed(0, 174) then -- arrow left + local newCenter = PolyZone.rotate(center.xy, vector2(center.x - delta, center.y), heading) + return vector3(newCenter.x, newCenter.y, center.z) + end + + if IsDisabledControlPressed(0, 175) then -- arrow right + local newCenter = PolyZone.rotate(center.xy, vector2(center.x + delta, center.y), heading) + return vector3(newCenter.x, newCenter.y, center.z) + end + + return center +end + +function disableControlKeyInput() + Citizen.CreateThread(function() + while drawZone do + DisableControlAction(0, 36, true) -- Ctrl + DisableControlAction(0, 19, true) -- Alt + DisableControlAction(0, 20, true) -- 'Z' + DisableControlAction(0, 21, true) -- Shift + DisableControlAction(0, 81, true) -- Scroll Wheel Down + DisableControlAction(0, 99, true) -- Scroll Wheel Up + DisableControlAction(0, 172, true) -- Arrow Up + DisableControlAction(0, 173, true) -- Arrow Down + DisableControlAction(0, 174, true) -- Arrow Left + DisableControlAction(0, 175, true) -- Arrow Right + Wait(0) + end + end) +end \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/PolyZone/creation/server/config.lua b/server-data/resources/[esx_addons]/PolyZone/creation/server/config.lua new file mode 100644 index 000000000..099d06946 --- /dev/null +++ b/server-data/resources/[esx_addons]/PolyZone/creation/server/config.lua @@ -0,0 +1,56 @@ +Config = Config or {} +Config.ConfigFormatEnabled = false +-- Default Format + +-- Name: TestBox | 2022-04-13T22:46:17Z +-- BoxZone:Create(vector3(-344.16, -103.25, 39.02), 1, 1, { +-- name = "TestBox", +-- heading = 0, +-- --debugPoly = true +-- }) + +-- Name: TestCircle | 2022-04-13T22:46:39Z +-- CircleZone:Create(vector3(-344.16, -103.25, 39.02), 1.0, { +-- name = "TestCircle", +-- useZ = false, +-- --debugPoly = true +-- }) + +-- Name: TestPoly | 2022-04-13T22:46:55Z +-- PolyZone:Create({ +-- vector2(-344.15713500977, -103.24993896484), +-- vector2(-343.69491577148, -100.99839019775), +-- vector2(-345.53350830078, -102.00588226318) +-- }, { +-- name = "TestPoly", +-- minZ = 39.015644073486, +-- maxZ = 39.015865325928 +-- }) + +-- Config Format + +-- Name: TestBox | 2022-04-13T22:34:48Z +-- coords = vector3(-342.92, -102.09, 39.02), +-- length = 1, +-- width = 1, +-- name = "TestBox", +-- heading = 0, +-- debugPoly = true + +-- Name: TestCircle | 2022-04-13T22:35:09Z +-- coords = vector3(-342.92, -102.09, 39.02), +-- radius = 1.0, +-- name = "TestCircle", +-- useZ = false, +-- debugPoly = true + +-- Name: TestPoly | 2022-04-13T22:35:43Z +-- points = { +-- vector2(-342.91537475586, -102.09281158447), +-- vector2(-344.09732055664, -104.0821762085), +-- vector2(-342.01580810547, -105.60903167725) +-- }, +-- name = "TestPoly", +-- minZ = 39.015701293945, +-- maxZ = 39.015705108643, +-- debugPoly = true diff --git a/server-data/resources/[esx_addons]/PolyZone/creation/server/creation.lua b/server-data/resources/[esx_addons]/PolyZone/creation/server/creation.lua new file mode 100644 index 000000000..6170f38cc --- /dev/null +++ b/server-data/resources/[esx_addons]/PolyZone/creation/server/creation.lua @@ -0,0 +1,115 @@ +RegisterNetEvent("polyzone:printPoly") +AddEventHandler("polyzone:printPoly", function(zone) + file = io.open('polyzone_created_zones.txt', "a") + io.output(file) + local output = parsePoly(zone) + io.write(output) + io.close(file) +end) + +RegisterNetEvent("polyzone:printCircle") +AddEventHandler("polyzone:printCircle", function(zone) + file = io.open('polyzone_created_zones.txt', "a") + io.output(file) + local output = parseCircle(zone) + io.write(output) + io.close(file) +end) + +RegisterNetEvent("polyzone:printBox") +AddEventHandler("polyzone:printBox", function(zone) + file = io.open('polyzone_created_zones.txt', "a") + io.output(file) + local output = parseBox(zone) + io.write(output) + io.close(file) +end) + +function round(num, numDecimalPlaces) + local mult = 10^(numDecimalPlaces or 0) + return math.floor(num * mult + 0.5) / mult +end + +function printoutHeader(name) + return "--Name: " .. name .. " | " .. os.date("!%Y-%m-%dT%H:%M:%SZ\n") +end + +function parsePoly(zone) + if Config.ConfigFormatEnabled then + local printout = printoutHeader(zone.name) + printout = printout .. "points = {\n" + for i = 1, #zone.points do + if i ~= #zone.points then + printout = printout .. " vector2(" .. tostring(zone.points[i].x) .. ", " .. tostring(zone.points[i].y) .."),\n" + else + printout = printout .. " vector2(" .. tostring(zone.points[i].x) .. ", " .. tostring(zone.points[i].y) ..")\n" + end + end + printout = printout .. "},\nname = \"" .. zone.name .. "\",\n--minZ = " .. zone.minZ .. ",\n--maxZ = " .. zone.maxZ .. ",\n--debugPoly = true\n\n" + return printout + else + local printout = printoutHeader(zone.name) + printout = printout .. "PolyZone:Create({\n" + for i = 1, #zone.points do + if i ~= #zone.points then + printout = printout .. " vector2(" .. tostring(zone.points[i].x) .. ", " .. tostring(zone.points[i].y) .."),\n" + else + printout = printout .. " vector2(" .. tostring(zone.points[i].x) .. ", " .. tostring(zone.points[i].y) ..")\n" + end + end + printout = printout .. "}, {\n name = \"" .. zone.name .. "\",\n --minZ = " .. zone.minZ .. ",\n --maxZ = " .. zone.maxZ .. "\n})\n\n" + return printout + end +end + +function parseCircle(zone) + if Config.ConfigFormatEnabled then + local printout = printoutHeader(zone.name) + printout = printout .. "coords = " + printout = printout .. "vector3(" .. tostring(round(zone.center.x, 2)) .. ", " .. tostring(round(zone.center.y, 2)) .. ", " .. tostring(round(zone.center.z, 2)) .."),\n" + printout = printout .. "radius = " .. tostring(zone.radius) .. ",\n" + printout = printout .. "name = \"" .. zone.name .. "\",\nuseZ = " .. tostring(zone.useZ) .. ",\n--debugPoly = true\n\n" + return printout + else + local printout = printoutHeader(zone.name) + printout = printout .. "CircleZone:Create(" + printout = printout .. "vector3(" .. tostring(round(zone.center.x, 2)) .. ", " .. tostring(round(zone.center.y, 2)) .. ", " .. tostring(round(zone.center.z, 2)) .."), " + printout = printout .. tostring(zone.radius) .. ", " + printout = printout .. "{\n name = \"" .. zone.name .. "\",\n useZ = " .. tostring(zone.useZ) .. ",\n --debugPoly = true\n})\n\n" + return printout + end +end + +function parseBox(zone) + if Config.ConfigFormatEnabled then + local printout = printoutHeader(zone.name) + printout = printout .. "coords = " + printout = printout .. "vector3(" .. tostring(round(zone.center.x, 2)) .. ", " .. tostring(round(zone.center.y, 2)) .. ", " .. tostring(round(zone.center.z, 2)) .."),\n" + printout = printout .. "length = " .. tostring(zone.length) .. ",\n" + printout = printout .. "width = " .. tostring(zone.width) .. ",\n" + printout = printout .. "name = \"" .. zone.name .. "\",\nheading = " .. zone.heading .. ",\n--debugPoly = true" + if zone.minZ then + printout = printout .. ",\nminZ = " .. tostring(round(zone.minZ, 2)) + end + if zone.maxZ then + printout = printout .. ",\nmaxZ = " .. tostring(round(zone.maxZ, 2)) + end + printout = printout .. "\n\n" + return printout + else + local printout = printoutHeader(zone.name) + printout = printout .. "BoxZone:Create(" + printout = printout .. "vector3(" .. tostring(round(zone.center.x, 2)) .. ", " .. tostring(round(zone.center.y, 2)) .. ", " .. tostring(round(zone.center.z, 2)) .."), " + printout = printout .. tostring(zone.length) .. ", " + printout = printout .. tostring(zone.width) .. ", " + printout = printout .. "{\n name = \"" .. zone.name .. "\",\n heading = " .. zone.heading .. ",\n --debugPoly = true" + if zone.minZ then + printout = printout .. ",\n minZ = " .. tostring(round(zone.minZ, 2)) + end + if zone.maxZ then + printout = printout .. ",\n maxZ = " .. tostring(round(zone.maxZ, 2)) + end + printout = printout .. "\n})\n\n" + return printout + end +end diff --git a/server-data/resources/[esx_addons]/PolyZone/fxmanifest.lua b/server-data/resources/[esx_addons]/PolyZone/fxmanifest.lua new file mode 100644 index 000000000..b550b693f --- /dev/null +++ b/server-data/resources/[esx_addons]/PolyZone/fxmanifest.lua @@ -0,0 +1,20 @@ +games {'gta5'} + +fx_version 'cerulean' + +description 'Define zones of different shapes and test whether a point is inside or outside of the zone' +version '2.6.1' + +client_scripts { + 'client.lua', + 'BoxZone.lua', + 'EntityZone.lua', + 'CircleZone.lua', + 'ComboZone.lua', + 'creation/client/*.lua' +} + +server_scripts { + 'creation/server/*.lua', + 'server.lua' +} diff --git a/server-data/resources/[esx_addons]/PolyZone/server.lua b/server-data/resources/[esx_addons]/PolyZone/server.lua new file mode 100644 index 000000000..185a2ac67 --- /dev/null +++ b/server-data/resources/[esx_addons]/PolyZone/server.lua @@ -0,0 +1,10 @@ +local eventPrefix = '__PolyZone__:' + +function triggerZoneEvent(eventName, ...) + TriggerClientEvent(eventPrefix .. eventName, -1, ...) +end + +RegisterNetEvent("PolyZone:TriggerZoneEvent") +AddEventHandler("PolyZone:TriggerZoneEvent", triggerZoneEvent) + +exports("TriggerZoneEvent", triggerZoneEvent) \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/esx-qalle-jail/README.md b/server-data/resources/[esx_addons]/esx-qalle-jail/README.md new file mode 100644 index 000000000..9fa9db2b4 --- /dev/null +++ b/server-data/resources/[esx_addons]/esx-qalle-jail/README.md @@ -0,0 +1,57 @@ +# esx-qalle-jail + +[INFO] + +* Anti Combat Log +* Prison Work +* Working Teleporters +* Cool Interior +* Photo Taken (Mugshot) + +*This is a script made for you that wan't something out of the ordinary, this is a jail script that will add some cool side effects of getting jailed, one of em is that when you get jailed you will be teleported to the charactermugshot creation, and an sheriff ped will take a photo of you. The other thing is that you can work for a small amount of cash while in jail.* + +[EXTRAS] + +* Add To Police Menu Example -> + +```lua + elements = { + {label = TranslateCap('citizen_interaction'), value = 'citizen_interaction'}, + {label = TranslateCap('vehicle_interaction'), value = 'vehicle_interaction'}, + {label = TranslateCap('object_spawner'), value = 'object_spawner'}, + {label = "Jail Menu", value = 'jail_menu'} -- You add this line + } + }, function(data, menu) + + --You add this + + if data.current.value == 'jail_menu' then + TriggerEvent("esx-qalle-jail:openJailMenu") + end + + --Above This + if data.current.value == 'citizen_interaction' then +``` + +[USAGE] + +* `/jail ID jailTime(minutes) "description"` command (only players who have the job "police") +* `/unjail ID` to unjail a player (only players who have the job "police") +* `/jailmenu` quick command if you dont want the menu. (only players who have the job "police") + +[INSTALLATION] + +1) CD in your resources/[esx] folder + +2) Import ``jail.sql`` into your database + +3) Download ``prison-map-addon`` from https://github.com/qalle-fivem/prison-map-addon + +4) Add this in your server.cfg : +``start esx-qalle-jail`` + +[SCREENSHOTS] + +[Jail Menu](https://gyazo.com/7f46b8a80c59e02c4af7a96de34c9e6c) + +[UnJail Menu](https://gyazo.com/aa065a6c748c0d2850d3d38d33eb666f) \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/esx-qalle-jail/cell.ogg b/server-data/resources/[esx_addons]/esx-qalle-jail/cell.ogg new file mode 100644 index 0000000000000000000000000000000000000000..940d10f182309c2f7a4f1c8919f6be30800d441b GIT binary patch literal 61671 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 0 do + jailTime = jailTime - 1 + ESX.ShowNotification(TranslateCap("few_minutes")) + + TriggerServerEvent("esx-qalle-jail:updateJailTime", jailTime) + if jailTime == 0 then + UnJail() + TriggerServerEvent("esx-qalle-jail:updateJailTime", 0) + end + + Wait(60000) + end + end) + --Jail Timer-- + + --Prison Work-- + CreateThread(function() + while jailTime > 0 do + local sleepThread = 500 + local Packages = Config.PrisonWork["Packages"] + local Ped = PlayerPedId() + local PedCoords = GetEntityCoords(Ped) + + for posId, v in pairs(Packages) do + local DistanceCheck = GetDistanceBetweenCoords(PedCoords, v["x"], v["y"], v["z"], true) + + if DistanceCheck <= 10.0 then + sleepThread = 5 + + local PackageText = "Pack" + + if not v["state"] then + PackageText = "Already Taken" + end + + ESX.Game.Utils.DrawText3D(v, "[E] " .. PackageText, 0.4) + + if DistanceCheck <= 1.5 then + if IsControlJustPressed(0, 38) then + if v["state"] then + PackPackage(posId) + else + ESX.ShowNotification(TranslateCap("package_taken")) + end + end + end + end + end + Wait(sleepThread) + end + end) + --Prison Work-- +end + +function LoadTeleporters() + CreateThread(function() + while true do + local sleepThread = 500 + local Ped = PlayerPedId() + local PedCoords = GetEntityCoords(Ped) + + for _, v in pairs(Config.Teleports) do + local DistanceCheck = GetDistanceBetweenCoords(PedCoords, v["x"], v["y"], v["z"], true) + + if DistanceCheck <= 7.5 then + sleepThread = 5 + + ESX.Game.Utils.DrawText3D(v, "[E] Open Door", 0.4) + + if DistanceCheck <= 1.0 then + if IsControlJustPressed(0, 38) then + TeleportPlayer(v) + end + end + end + end + Wait(sleepThread) + end + end) +end + +function PackPackage(packageId) + local Package = Config.PrisonWork["Packages"][packageId] + + LoadModel("prop_cs_cardbox_01") + + local PackageObject = CreateObject(GetHashKey("prop_cs_cardbox_01"), Package["x"], Package["y"], Package["z"], true) + + PlaceObjectOnGroundProperly(PackageObject) + + TaskStartScenarioInPlace(PlayerPedId(), "PROP_HUMAN_BUM_BIN", 0, false) + + local Packaging = true + local StartTime = GetGameTimer() + + while Packaging do + Wait(1) + + local TimeToTake = 60000 * 1.20 -- Minutes + local PackPercent = (GetGameTimer() - StartTime) / TimeToTake * 100 + + if not IsPedUsingScenario(PlayerPedId(), "PROP_HUMAN_BUM_BIN") then + DeleteEntity(PackageObject) + ESX.ShowNotification(TranslateCap("canceled")) + Packaging = false + end + + if PackPercent >= 100 then + Packaging = false + DeliverPackage(PackageObject) + Package["state"] = false + else + ESX.Game.Utils.DrawText3D(Package, "Packaging... " .. math.ceil(tonumber(PackPercent)) .. "%", 0.4) + end + end +end + +function DeliverPackage(packageId) + if DoesEntityExist(packageId) then + AttachEntityToEntity(packageId, PlayerPedId(), GetPedBoneIndex(PlayerPedId(), 28422), 0.0, -0.03, 0.0, 5.0, 0.0, 0.0, true, true, false, true, 0, true) + + ClearPedTasks(PlayerPedId()) + else + return + end + + local Packaging = true + + LoadAnim("anim@heists@box_carry@") + + while Packaging do + Wait(5) + + if not IsEntityPlayingAnim(PlayerPedId(), "anim@heists@box_carry@", "idle", 3) then + TaskPlayAnim(PlayerPedId(), "anim@heists@box_carry@", "idle", 8.0, 8.0, -1, 50, 0, false, false, false) + end + + if not IsEntityAttachedToEntity(packageId, PlayerPedId()) then + Packaging = false + DeleteEntity(packageId) + else + local DeliverPosition = Config.PrisonWork["DeliverPackage"] + local PedPosition = GetEntityCoords(PlayerPedId()) + local DistanceCheck = GetDistanceBetweenCoords(PedPosition, DeliverPosition["x"], DeliverPosition["y"], DeliverPosition["z"], true) + + ESX.Game.Utils.DrawText3D(DeliverPosition, "[E] Leave Package", 0.4) + + if DistanceCheck <= 2.0 then + if IsControlJustPressed(0, 38) then + DeleteEntity(packageId) + ClearPedTasksImmediately(PlayerPedId()) + Packaging = false + + TriggerServerEvent("esx-qalle-jail:prisonWorkReward") + end + end + end + end +end + +function OpenJailMenu() + ESX.UI.Menu.Open("default", GetCurrentResourceName(), "jail_prison_menu", { + title = "Prison Menu", + align = "center", + elements = { + { label = "Jail Closest Person", value = "jail_closest_player" }, + { label = "Unjail Person", value = "unjail_player" }, + }, + }, function(data, menu) + local action = data.current.value + + if action == "jail_closest_player" then + menu.close() + + ESX.UI.Menu.Open("dialog", GetCurrentResourceName(), "jail_choose_time_menu", { + title = "Jail Time (minutes)", + }, function(data2, menu2) + local jailTime = tonumber(data2.value) + if jailTime == nil then + ESX.ShowNotification(TranslateCap("time_minutes")) + else + menu2.close() + local closestPlayer, closestDistance = ESX.Game.GetClosestPlayer() + + if closestPlayer == -1 or closestDistance > 3.0 then + ESX.ShowNotification(TranslateCap("players_nearby")) + else + ESX.UI.Menu.Open("dialog", GetCurrentResourceName(), "jail_choose_reason_menu", { + title = "Jail Reason", + }, function(data3, menu3) + local reason = data3.value + if reason == nil then + ESX.ShowNotification(TranslateCap("put_something")) + else + menu3.close() + local closestPlayer, closestDistance = ESX.Game.GetClosestPlayer() + if closestPlayer == -1 or closestDistance > 3.0 then + ESX.ShowNotification(TranslateCap("players_nearby")) + else + TriggerServerEvent("esx-qalle-jail:jailPlayer", GetPlayerServerId(closestPlayer), jailTime, reason) + end + end + end, function(_, menu3) + menu3.close() + end) + end + end + end, function(_, menu2) + menu2.close() + end) + elseif action == "unjail_player" then + local elements = {} + + ESX.TriggerServerCallback("esx-qalle-jail:retrieveJailedPlayers", function(playerArray) + if #playerArray == 0 then + ESX.ShowNotification(TranslateCap("jail_empty")) + return + end + + for i = 1, #playerArray, 1 do + table.insert(elements, { + label = "Prisoner: " .. playerArray[i].name .. " | Jail Time: " .. playerArray[i].jailTime .. " minutes", + value = playerArray[i].identifier, + }) + end + + ESX.UI.Menu.Open("default", GetCurrentResourceName(), "jail", TranslateCap("jail_menu"), { + title = "Unjail Player", + align = "center", + elements = elements, + }, function(data2, menu2) + local action = data2.current.value + + TriggerServerEvent("esx-qalle-jail:unJailPlayer", action) + + menu2.close() + end, function(_, menu2) + menu2.close() + end) + end) + end + end, function(_, menu) + menu.close() + end) +end diff --git a/server-data/resources/[esx_addons]/esx-qalle-jail/client/utils.lua b/server-data/resources/[esx_addons]/esx-qalle-jail/client/utils.lua new file mode 100644 index 000000000..068f6ddc1 --- /dev/null +++ b/server-data/resources/[esx_addons]/esx-qalle-jail/client/utils.lua @@ -0,0 +1,173 @@ +RegisterCommand("jailmenu", function() + if PlayerData.job.name == "police" then + OpenJailMenu() + else + ESX.ShowNotification(TranslateCap("not_officer")) + end +end) + +function LoadAnim(animDict) + RequestAnimDict(animDict) + while not HasAnimDictLoaded(animDict) do + Wait(10) + end +end + +function LoadModel(model) + RequestModel(model) + while not HasModelLoaded(model) do + Wait(10) + end +end + +function HideHUDThisFrame() + HideHelpTextThisFrame() + HideHudAndRadarThisFrame() + HideHudComponentThisFrame(1) + HideHudComponentThisFrame(2) + HideHudComponentThisFrame(3) + HideHudComponentThisFrame(4) + HideHudComponentThisFrame(6) + HideHudComponentThisFrame(7) + HideHudComponentThisFrame(8) + HideHudComponentThisFrame(9) + HideHudComponentThisFrame(13) + HideHudComponentThisFrame(11) + HideHudComponentThisFrame(12) + HideHudComponentThisFrame(15) + HideHudComponentThisFrame(18) + HideHudComponentThisFrame(19) +end + +function Cutscene() + DoScreenFadeOut(100) + Wait(250) + TriggerEvent("skinchanger:getSkin", function(skin) + local hash = GetEntityModel(PlayerPedId()) + if hash == "mp_m_freemode_01" then + local clothesSkin = { + ["tshirt_1"] = 20, + ["tshirt_2"] = 15, + ["torso_1"] = 33, + ["torso_2"] = 0, + ["arms"] = 0, + ["pants_1"] = 7, + ["pants_2"] = 0, + ["shoes_1"] = 34, + ["shoes_2"] = 0, + } + TriggerEvent("skinchanger:loadClothes", skin, clothesSkin) + else + local clothesSkin = { + ["tshirt_1"] = 15, + ["tshirt_2"] = 0, + ["torso_1"] = 2, + ["torso_2"] = 6, + ["arms"] = 2, + ["pants_1"] = 2, + ["pants_2"] = 0, + ["shoes_1"] = 35, + ["shoes_2"] = 0, + } + TriggerEvent("skinchanger:loadClothes", skin, clothesSkin) + end + end) + + LoadModel(-1320879687) + + local PolicePosition = Config.Cutscene["PolicePosition"] + local Police = CreatePed(5, -1320879687, PolicePosition["x"], PolicePosition["y"], PolicePosition["z"], PolicePosition["h"], false) + TaskStartScenarioInPlace(Police, "WORLD_HUMAN_PAPARAZZI", 0, false) + + local PlayerPosition = Config.Cutscene["PhotoPosition"] + local PlayerPed = PlayerPedId() + SetEntityCoords(PlayerPed, PlayerPosition["x"], PlayerPosition["y"], PlayerPosition["z"] - 1) + SetEntityHeading(PlayerPed, PlayerPosition["h"]) + FreezeEntityPosition(PlayerPed, true) + + Cam() + Wait(1000) + DoScreenFadeIn(100) + Wait(10000) + DoScreenFadeOut(250) + + local JailPosition = Config.JailPositions["Cell"] + SetEntityCoords(PlayerPed, JailPosition["x"], JailPosition["y"], JailPosition["z"]) + DeleteEntity(Police) + SetModelAsNoLongerNeeded(-1320879687) + + Wait(1000) + DoScreenFadeIn(250) + TriggerServerEvent("InteractSound_SV:PlayOnSource", "cell", 0.3) + RenderScriptCams(false, false, 0, true, true) + FreezeEntityPosition(PlayerPed, false) + DestroyCam(Config.Cutscene["CameraPos"]["cameraId"]) + + InJail() +end + +function Cam() + local CamOptions = Config.Cutscene["CameraPos"] + + CamOptions["cameraId"] = CreateCam("DEFAULT_SCRIPTED_CAMERA", true) + + SetCamCoord(CamOptions["cameraId"], CamOptions["x"], CamOptions["y"], CamOptions["z"]) + SetCamRot(CamOptions["cameraId"], CamOptions["rotationX"], CamOptions["rotationY"], CamOptions["rotationZ"]) + + RenderScriptCams(true, false, 0, true, true) +end + +function TeleportPlayer(pos) + local Values = pos + + if #Values["goal"] > 1 then + local elements = {} + + for _, v in pairs(Values["goal"]) do + table.insert(elements, { label = v, value = v }) + end + + ESX.UI.Menu.Open("default", GetCurrentResourceName(), "teleport_jail", { + title = "Choose Position", + align = "center", + elements = elements, + }, function(data, menu) + local action = data.current.value + local position = Config.Teleports[action] + + if action == "Boiling Broke" or action == "Security" then + if PlayerData.job.name ~= "police" then + ESX.ShowNotification(TranslateCap("not_keys")) + return + end + end + menu.close() + DoScreenFadeOut(100) + Wait(250) + SetEntityCoords(PlayerPedId(), position["x"], position["y"], position["z"]) + Wait(250) + DoScreenFadeIn(100) + end, function(_, menu) + menu.close() + end) + else + local position = Config.Teleports[Values["goal"][1]] + DoScreenFadeOut(100) + Wait(250) + SetEntityCoords(PlayerPedId(), position["x"], position["y"], position["z"]) + Wait(250) + DoScreenFadeIn(100) + end +end + +CreateThread(function() + local blip = AddBlipForCoord(Config.Teleports["Boiling Broke"]["x"], Config.Teleports["Boiling Broke"]["y"], Config.Teleports["Boiling Broke"]["z"]) + SetBlipSprite(blip, 188) + SetBlipDisplay(blip, 4) + SetBlipScale(blip, 0.8) + SetBlipColour(blip, 49) + SetBlipAsShortRange(blip, true) + BeginTextCommandSetBlipName("STRING") + AddTextComponentString("Penitentiary") + EndTextCommandSetBlipName(blip) +end) diff --git a/server-data/resources/[esx_addons]/esx-qalle-jail/config.lua b/server-data/resources/[esx_addons]/esx-qalle-jail/config.lua new file mode 100644 index 000000000..4a186220a --- /dev/null +++ b/server-data/resources/[esx_addons]/esx-qalle-jail/config.lua @@ -0,0 +1,106 @@ +Config = {} +Config.Locale = "it" +Config.JailPositions = { + ["Cell"] = { ["x"] = 1777.463745, ["y"] = 2581.595703, ["z"] = 45.792725, ["h"] = 179.03021240234 }, +} + +Config.Cutscene = { + ["PhotoPosition"] = { + ["x"] = 402.91567993164, + ["y"] = -996.75970458984, + ["z"] = -99.000259399414, + ["h"] = 186.22499084473, + }, + ["CameraPos"] = { + ["x"] = 402.88830566406, + ["y"] = -1003.8851318359, + ["z"] = -97.419647216797, + ["rotationX"] = -15.433070763946, + ["rotationY"] = 0.0, + ["rotationZ"] = -0.31496068835258, + ["cameraId"] = 0, + }, + ["PolicePosition"] = { + ["x"] = 402.91702270508, + ["y"] = -1000.6376953125, + ["z"] = -99.004028320313, + ["h"] = 356.88052368164, + }, +} + +Config.PrisonWork = { + ["DeliverPackage"] = { + ["x"] = 1027.2347412109, + ["y"] = -3101.419921875, + ["z"] = -38.999870300293, + ["h"] = 267.89135742188, + }, + + ["Packages"] = { + [1] = { ["x"] = 1003.6661987305, ["y"] = -3108.4221191406, ["z"] = -38.999866485596, ["state"] = true }, + [2] = { ["x"] = 1006.0420532227, ["y"] = -3103.0024414063, ["z"] = -38.999866485596, ["state"] = true }, + [3] = { ["x"] = 1015.7958374023, ["y"] = -3102.8337402344, ["z"] = -38.99991607666, ["state"] = true }, + [4] = { ["x"] = 1012.8907470703, ["y"] = -3108.2907714844, ["z"] = -38.999912261963, ["state"] = true }, + [5] = { ["x"] = 1018.2017822266, ["y"] = -3109.1982421875, ["z"] = -38.999897003174, ["state"] = true }, + [6] = { ["x"] = 1018.0194091797, ["y"] = -3096.5700683594, ["z"] = -38.999897003174, ["state"] = true }, + [7] = { ["x"] = 1015.6422119141, ["y"] = -3091.7392578125, ["z"] = -38.999897003174, ["state"] = true }, + [8] = { ["x"] = 1010.7862548828, ["y"] = -3096.6135253906, ["z"] = -38.999897003174, ["state"] = true }, + [9] = { ["x"] = 1005.7819824219, ["y"] = -3096.8415527344, ["z"] = -38.999897003174, ["state"] = true }, + [10] = { ["x"] = 1003.4543457031, ["y"] = -3096.7048339844, ["z"] = -38.999897003174, ["state"] = true }, + }, +} + +Config.Teleports = { + ["Prison Work"] = { + ["x"] = 992.51770019531, + ["y"] = -3097.8413085938, + ["z"] = -38.995861053467, + ["h"] = 81.15771484375, + ["goal"] = { + "Jail", + }, + }, + + ["Boiling Broke"] = { + ["x"] = 1845.6022949219, + ["y"] = 2585.8029785156, + ["z"] = 45.672061920166, + ["h"] = 92.469093322754, + ["goal"] = { + "Security", + }, + }, + + ["Jail"] = { + ["x"] = 1800.6979980469, + ["y"] = 2483.0979003906, + ["z"] = -122.68814849854, + ["h"] = 271.75274658203, + ["goal"] = { + "Prison Work", + "Security", + "Visitor", + }, + }, + + ["Security"] = { + ["x"] = 1706.7625732422, + ["y"] = 2581.0793457031, + ["z"] = -69.407371520996, + ["h"] = 267.72802734375, + ["goal"] = { + "Jail", + "Boiling Broke", + }, + }, + + ["Visitor"] = { + ["x"] = 1699.7196044922, + ["y"] = 2574.5314941406, + ["z"] = -69.403930664063, + ["h"] = 169.65020751953, + ["goal"] = { + "Jail", + }, + }, +} diff --git a/server-data/resources/[esx_addons]/esx-qalle-jail/fxmanifest.lua b/server-data/resources/[esx_addons]/esx-qalle-jail/fxmanifest.lua new file mode 100644 index 000000000..d6acec268 --- /dev/null +++ b/server-data/resources/[esx_addons]/esx-qalle-jail/fxmanifest.lua @@ -0,0 +1,23 @@ +fx_version("adamant") +game("gta5") +author("qalle-git; rework by: bitpredator") +lua54("yes") +version("1.0.2") +description("Jail Script With Working Job") + +shared_script("@es_extended/imports.lua") + +server_scripts({ + "@es_extended/locale.lua", + "@mysql-async/lib/MySQL.lua", + "config.lua", + "locales/*.lua", + "server/server.lua", +}) + +client_scripts({ + "@es_extended/locale.lua", + "config.lua", + "client/*.lua", + "locales/*.lua", +}) diff --git a/server-data/resources/[esx_addons]/esx-qalle-jail/jail.sql b/server-data/resources/[esx_addons]/esx-qalle-jail/jail.sql new file mode 100644 index 000000000..3e3411acd --- /dev/null +++ b/server-data/resources/[esx_addons]/esx-qalle-jail/jail.sql @@ -0,0 +1,2 @@ +ALTER TABLE `users` + ADD COLUMN `jail` INT(11) NOT NULL DEFAULT '0'; \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/esx-qalle-jail/locales/en.lua b/server-data/resources/[esx_addons]/esx-qalle-jail/locales/en.lua new file mode 100644 index 000000000..b7c2b9d05 --- /dev/null +++ b/server-data/resources/[esx_addons]/esx-qalle-jail/locales/en.lua @@ -0,0 +1,16 @@ +Locales["en"] = { + ["not_keys"] = "You don't have an key to go here!", + ["back_in_jail"] = "Last time you went to sleep you were imprisoned, so now you're here again!", + ["you_released"] = "You are released, keep calm outside! Good luck", + ["few_minutes"] = "You have minutes left in jail!", + ["package_taken"] = "You've already taken this package", + ["canceled"] = "Canceled", + ["time_minutes"] = "The time needs to be in minutes!", + ["players_nearb"] = "No players nearby!", + ["put_something"] = "You need to put something here!", + ["jail_empty"] = "Your jail is empty!", + ["not_officer"] = "You are not an officer!", + ["invalid_time"] = "This time is invalid", + ["id_not_online"] = "This ID is not online", + ["money_for_food"] = "Here you have som cash for food!", +} diff --git a/server-data/resources/[esx_addons]/esx-qalle-jail/locales/it.lua b/server-data/resources/[esx_addons]/esx-qalle-jail/locales/it.lua new file mode 100644 index 000000000..550d1d0c9 --- /dev/null +++ b/server-data/resources/[esx_addons]/esx-qalle-jail/locales/it.lua @@ -0,0 +1,16 @@ +Locales["it"] = { + ["not_keys"] = "Non hai una chiave per andare qui!", + ["back_in_jail"] = "L'ultima volta che sei andato a dormire eri stato imprigionato, quindi ora sei di nuovo qui!", + ["you_released"] = "Sei libero, mantieni la calma li fuori! Buona fortuna", + ["few_minutes"] = "Ti restano pochi minuti di prigione!", + ["package_taken"] = "Hai già preso questo pacchetto", + ["canceled"] = "Annullata", + ["time_minutes"] = "Il tempo deve essere in minuti!", + ["players_nearb"] = "Nessun giocatore nelle vicinanze!", + ["put_something"] = "Devi mettere qualcosa qui!", + ["jail_empty"] = "La tua prigione è vuota!", + ["not_officer"] = "Non sei un ufficiale!", + ["invalid_time"] = "Questo tempo non è valido", + ["id_not_online"] = "Questo ID non è online", + ["money_for_food"] = "Eccoti un po' di soldi per il cibo!", +} diff --git a/server-data/resources/[esx_addons]/esx-qalle-jail/server/server.lua b/server-data/resources/[esx_addons]/esx-qalle-jail/server/server.lua new file mode 100644 index 000000000..714ab5900 --- /dev/null +++ b/server-data/resources/[esx_addons]/esx-qalle-jail/server/server.lua @@ -0,0 +1,169 @@ +ESX = exports["es_extended"]:getSharedObject() + +RegisterCommand("jail", function(src, args) + local xPlayer = ESX.GetPlayerFromId(src) + + if xPlayer["job"]["name"] == "police" then + local jailPlayer = args[1] + local jailTime = tonumber(args[2]) + local jailReason = args[3] + + if GetPlayerName(jailPlayer) ~= nil then + if jailTime ~= nil then + JailPlayer(jailPlayer, jailTime) + + TriggerClientEvent("esx:showNotification", src, GetPlayerName(jailPlayer) .. " Jailed for " .. jailTime .. " minutes!") + + if args[3] ~= nil then + GetRPName(jailPlayer, function(Firstname, Lastname) + TriggerClientEvent("chat:addMessage", -1, { + args = { + "JUDGE", + Firstname .. " " .. Lastname .. " Is now in jail for the reason: " .. args[3], + }, + color = { 249, 166, 0 }, + }) + end) + end + else + TriggerClientEvent("esx:showNotification", src, TranslateCap("invalid_time")) + end + else + TriggerClientEvent("esx:showNotification", src, TranslateCap("id_not_online")) + end + else + TriggerClientEvent("esx:showNotification", src, TranslateCap("not_officer")) + end +end) + +RegisterCommand("unjail", function(src, args) + local xPlayer = ESX.GetPlayerFromId(src) + + if xPlayer["job"]["name"] == "police" then + local jailPlayer = args[1] + + if GetPlayerName(jailPlayer) ~= nil then + UnJail(jailPlayer) + else + TriggerClientEvent("esx:showNotification", src, TranslateCap("id_not_online")) + end + else + TriggerClientEvent("esx:showNotification", src, TranslateCap("not_officer")) + end +end) + +RegisterServerEvent("esx-qalle-jail:jailPlayer") +AddEventHandler("esx-qalle-jail:jailPlayer", function(targetSrc, jailTime, jailReason) + local src = source + local targetSrc = tonumber(targetSrc) + local xPlayer = ESX.GetPlayerFromId(src) + + if xPlayer.job.name == "police" then + JailPlayer(targetSrc, jailTime) + + GetRPName(targetSrc, function(Firstname, Lastname) + TriggerClientEvent("chat:addMessage", -1, { + args = { "JUDGE", Firstname .. " " .. Lastname .. " Is now in jail for the reason: " .. jailReason }, + color = { 249, 166, 0 }, + }) + end) + else + DropPlayer(src, "[esx-qalle-jail]: You have been kicked for trying to exploit a server event.") + end + + TriggerClientEvent("esx:showNotification", src, GetPlayerName(targetSrc) .. " Jailed for " .. jailTime .. " minutes!") +end) + +RegisterServerEvent("esx-qalle-jail:unJailPlayer") +AddEventHandler("esx-qalle-jail:unJailPlayer", function(targetIdentifier) + local src = source + local xPlayer = ESX.GetPlayerFromIdentifier(targetIdentifier) + + if xPlayer ~= nil then + UnJail(xPlayer.source) + else + MySQL.Async.execute("UPDATE users SET jail = @newJailTime WHERE identifier = @identifier", { + ["@identifier"] = targetIdentifier, + ["@newJailTime"] = 0, + }) + end + + TriggerClientEvent("esx:showNotification", src, xPlayer.name .. " Unjailed!") +end) + +RegisterServerEvent("esx-qalle-jail:updateJailTime") +AddEventHandler("esx-qalle-jail:updateJailTime", function(newJailTime) + local src = source + + EditJailTime(src, newJailTime) +end) + +RegisterServerEvent("esx-qalle-jail:prisonWorkReward") +AddEventHandler("esx-qalle-jail:prisonWorkReward", function() + local src = source + local xPlayer = ESX.GetPlayerFromId(src) + xPlayer.addMoney(math.random(13, 21)) + TriggerClientEvent("esx:showNotification", src, TranslateCap("money_for_food")) +end) + +function JailPlayer(jailPlayer, jailTime) + TriggerClientEvent("esx-qalle-jail:jailPlayer", jailPlayer, jailTime) + + EditJailTime(jailPlayer, jailTime) +end + +function UnJail(jailPlayer) + TriggerClientEvent("esx-qalle-jail:unJailPlayer", jailPlayer) + + EditJailTime(jailPlayer, 0) +end + +function EditJailTime(source, jailTime) + local src = source + local xPlayer = ESX.GetPlayerFromId(src) + local Identifier = xPlayer.identifier + + MySQL.Async.execute("UPDATE users SET jail = @newJailTime WHERE identifier = @identifier", { + ["@identifier"] = Identifier, + ["@newJailTime"] = tonumber(jailTime), + }) +end + +function GetRPName(playerId, data) + local Identifier = ESX.GetPlayerFromId(playerId).identifier + + MySQL.Async.fetchAll("SELECT firstname, lastname FROM users WHERE identifier = @identifier", { ["@identifier"] = Identifier }, function(result) + data(result[1].firstname, result[1].lastname) + end) +end + +ESX.RegisterServerCallback("esx-qalle-jail:retrieveJailedPlayers", function(_, cb) + local jailedPersons = {} + + MySQL.Async.fetchAll("SELECT firstname, lastname, jail, identifier FROM users WHERE jail > @jail", { ["@jail"] = 0 }, function(result) + for i = 1, #result, 1 do + table.insert(jailedPersons, { + name = result[i].firstname .. " " .. result[i].lastname, + jailTime = result[i].jail, + identifier = result[i].identifier, + }) + end + cb(jailedPersons) + end) +end) + +ESX.RegisterServerCallback("esx-qalle-jail:retrieveJailTime", function(source, cb) + local src = source + local xPlayer = ESX.GetPlayerFromId(src) + local Identifier = xPlayer.identifier + + MySQL.Async.fetchAll("SELECT jail FROM users WHERE identifier = @identifier", { ["@identifier"] = Identifier }, function(result) + local JailTime = tonumber(result[1].jail) + + if JailTime > 0 then + cb(true, JailTime) + else + cb(false, 0) + end + end) +end) diff --git a/server-data/resources/[esx_addons]/esx_property/properties.json b/server-data/resources/[esx_addons]/esx_property/properties.json index df4526f42..524dedb6e 100644 --- a/server-data/resources/[esx_addons]/esx_property/properties.json +++ b/server-data/resources/[esx_addons]/esx_property/properties.json @@ -1 +1 @@ -[{"cctv":{"maxright":-88.02642059326172,"enabled":true,"rot":{"x":-9.43104267120361,"y":2.7045992112562097e-8,"z":2.7542073726654},"maxleft":90.71163940429688},"setName":"","OwnerName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"1076 Procopio Dr","Locked":false,"Entrance":{"x":-467.88,"y":6206.16,"z":28.57},"Owner":"","Keys":[],"positions":{"Wardrobe":{"x":259.9942932128906,"y":-1003.45947265625,"z":-99.00859832763672}},"Interior":"low-end","plysinside":[],"Price":25000},{"cctv":{"maxright":70.65039825439453,"enabled":true,"rot":{"x":3.75689458847045,"y":-0.0,"z":162.9940643310547},"maxleft":-113.61099243164064},"setName":"","OwnerName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"1075 Procopio Dr","Locked":false,"Entrance":{"x":-448.02,"y":6260.23,"z":29.07},"Owner":"","Keys":[],"positions":{"Wardrobe":{"x":259.9942932128906,"y":-1003.45947265625,"z":-99.00859832763672}},"Interior":"low-end","plysinside":[],"Price":25000},{"cctv":{"maxright":135.13072204589845,"enabled":true,"rot":{"x":-1.16786921024322,"y":-0.0,"z":-138.95835876464845},"maxleft":-54.77568435668945},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"1074 Procopio Dr","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":-407.35,"y":6314.13,"z":27.96},"positions":{"Wardrobe":{"x":259.9942932128906,"y":-1003.45947265625,"z":-99.00859832763672}},"Interior":"low-end","Owner":"","Price":25000},{"cctv":{"maxright":132.70008850097657,"enabled":true,"rot":{"x":-2.56643605232238,"y":-0.0,"z":-139.94163513183598},"maxleft":-51.56189727783203},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"1073 Procopio Dr","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":-368.23,"y":6341.62,"z":28.86},"positions":{"Wardrobe":{"x":259.9942932128906,"y":-1003.45947265625,"z":-99.00859832763672}},"Interior":"low-end","Owner":"","Price":25000},{"cctv":{"maxright":123.2501983642578,"enabled":true,"rot":{"x":-7.54235982894897,"y":-0.0,"z":-152.39810180664066},"maxleft":-65.34387969970703},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"1071 Procopio Dr","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":-272.61,"y":6400.88,"z":30.52},"positions":{"Wardrobe":{"x":259.9942932128906,"y":-1003.45947265625,"z":-99.00859832763672}},"Interior":"low-end","Owner":"","Price":25000},{"cctv":{"maxright":38.33782577514648,"enabled":true,"rot":{"x":-12.92249584197998,"y":4.379791960218428e-7,"z":128.27305603027345},"maxleft":-148.38644409179688},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"1071 Procopio Dr","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":-246.14,"y":6414.46,"z":30.48},"positions":{"Wardrobe":{"x":259.9942932128906,"y":-1003.45947265625,"z":-99.00859832763672}},"Interior":"low-end","Owner":"","Price":25000},{"cctv":{"maxright":48.30059432983398,"enabled":true,"rot":{"x":-5.53862285614013,"y":2.1444458297992242e-7,"z":137.8870849609375},"maxleft":-135.48313903808598},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"1070 Procopio Dr","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":-229.63,"y":6445.48,"z":30.22},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":27500},{"cctv":{"maxright":138.85032653808598,"enabled":true,"rot":{"x":3.44796895980834,"y":1.0691521623584777e-7,"z":-134.93411254882813},"maxleft":-46.79578399658203},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"1067 Procopio Dr","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":-130.8,"y":6551.76,"z":28.89},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":27500},{"cctv":{"maxright":129.07382202148438,"enabled":true,"rot":{"x":-0.24611411988735,"y":-1.3340336835199196e-8,"z":-144.0503692626953},"maxleft":-56.39479827880859},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"1065 Procopio Dr","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":-41.72,"y":6637.3,"z":30.11},"positions":{"Wardrobe":{"x":259.9942932128906,"y":-1003.45947265625,"z":-99.00859832763672}},"Interior":"low-end","Owner":"","Price":25000},{"cctv":{"maxright":116.67019653320313,"enabled":true,"rot":{"x":-2.71219563484191,"y":-1.068413766347477e-7,"z":-155.01153564453126},"maxleft":-68.07413482666016},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"1064 Procopio Dr","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":-9.62,"y":6654.13,"z":30.72},"positions":{"Wardrobe":{"x":259.9942932128906,"y":-1003.45947265625,"z":-99.00859832763672}},"Interior":"low-end","Owner":"","Price":25000},{"cctv":{"maxright":135.7574005126953,"enabled":true,"rot":{"x":-2.77191710472106,"y":-0.0,"z":-133.8831787109375},"maxleft":-45.94052505493164},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"1034 Paleto Blvd","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":-356.82,"y":6207.33,"z":30.86},"positions":{"Wardrobe":{"x":259.9942932128906,"y":-1003.45947265625,"z":-99.00859832763672}},"Interior":"low-end","Owner":"","Price":25000},{"cctv":{"maxright":-131.2773895263672,"enabled":true,"rot":{"x":-8.85426330566406,"y":4.32035335506953e-7,"z":-40.4444465637207},"maxleft":42.09032440185547},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"1034 Procopio Dr","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":-379.92,"y":6252.75,"z":30.87},"positions":{"Wardrobe":{"x":259.9942932128906,"y":-1003.45947265625,"z":-99.00859832763672}},"Interior":"low-end","Owner":"","Price":25000},{"cctv":{"maxright":-42.54082870483398,"enabled":true,"rot":{"x":-25.26673126220703,"y":-9.440947223993136e-7,"z":44.16030502319336},"maxleft":133.55870056152345},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"1047 Paleto Blvd","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":-302.19,"y":6326.99,"z":31.91},"positions":{"Wardrobe":{"x":259.9942932128906,"y":-1003.45947265625,"z":-99.00859832763672}},"Interior":"low-end","Owner":"","Price":25000},{"cctv":{"maxright":134.7738800048828,"enabled":true,"rot":{"x":-1.57497417926788,"y":-0.0,"z":-137.5096435546875},"maxleft":-43.60107040405273},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"1043 Paleto Blvd","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":-347.3,"y":6225.21,"z":30.9},"positions":{"Wardrobe":{"x":259.9942932128906,"y":-1003.45947265625,"z":-99.00859832763672}},"Interior":"low-end","Owner":"","Price":25000},{"cctv":{"maxright":-133.83499145507813,"enabled":true,"rot":{"x":-11.80154037475586,"y":-0.0,"z":-45.87245941162109},"maxleft":43.89247512817383},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"1059 Paleto Blvd","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":-15.3,"y":6557.45,"z":32.26},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":25000},{"cctv":{"maxright":44.53319549560547,"enabled":true,"rot":{"x":-5.62087059020996,"y":2.144746105159357e-7,"z":129.4863433837891},"maxleft":-139.30752563476566},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"1061 Paleto Blvd","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":4.43,"y":6568.19,"z":32.1},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":27500},{"cctv":{"maxright":-159.96804809570313,"enabled":true,"rot":{"x":-5.83577966690063,"y":-0.0,"z":-94.7632293701172},"maxleft":-43.43285751342773},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"1061 Paleto Blvd","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":31.0,"y":6596.46,"z":31.84},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":27500},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-6.35934734344482,"y":-0.0,"z":-63.00457000732422},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"2016 Grapeseed Main St","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1725.06,"y":4642.33,"z":42.9},"positions":{"Wardrobe":{"x":259.9942932128906,"y":-1003.45947265625,"z":-99.00859832763672}},"Interior":"low-end","Owner":"","Price":12500},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-16.72389602661132,"y":2.7858778395284394e-8,"z":91.53390502929688},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"2015 Grapeseed Main St","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1674.01,"y":4658.22,"z":42.39},"positions":{"Wardrobe":{"x":259.9942932128906,"y":-1003.45947265625,"z":-99.00859832763672}},"Interior":"low-end","Owner":"","Price":12500},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-29.83987236022949,"y":-0.0,"z":-93.13695526123049},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"2014 Grapeseed Main St","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1718.91,"y":4677.34,"z":42.68},"positions":{"Wardrobe":{"x":259.9942932128906,"y":-1003.45947265625,"z":-99.00859832763672}},"Interior":"low-end","Owner":"","Price":7500},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-6.32857131958007,"y":-0.0,"z":91.7155303955078},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"2013 Grapeseed Main St","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1682.85,"y":4689.52,"z":42.09},"positions":{"Wardrobe":{"x":259.9942932128906,"y":-1003.45947265625,"z":-99.00859832763672}},"Interior":"low-end","Owner":"","Price":7500},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-18.96443367004394,"y":-0.0,"z":110.03758239746094},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"2011 Grapeseed Main St","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1664.0,"y":4739.5,"z":41.03},"positions":{"Wardrobe":{"x":259.9942932128906,"y":-1003.45947265625,"z":-99.00859832763672}},"Interior":"low-end","Owner":"","Price":10000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-14.36374378204345,"y":-0.0,"z":-77.30774688720703},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"3012 Niland Ave","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1880.27,"y":3920.59,"z":32.23},"positions":{"Wardrobe":{"x":259.9942932128906,"y":-1003.45947265625,"z":-99.00859832763672}},"Interior":"low-end","Owner":"","Price":10000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-10.75266551971435,"y":-2.1725810483985698e-7,"z":103.60260009765624},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"3013 Niland Ave","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1846.06,"y":3914.59,"z":32.48},"positions":{"Wardrobe":{"x":259.9942932128906,"y":-1003.45947265625,"z":-99.00859832763672}},"Interior":"low-end","Owner":"","Price":9000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-20.23343658447265,"y":-0.0,"z":-76.59026336669922},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"3013 Marina Dr","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1803.57,"y":3913.8,"z":36.08},"positions":{"Wardrobe":{"x":259.9942932128906,"y":-1003.45947265625,"z":-99.00859832763672}},"Interior":"low-end","Owner":"","Price":11500},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-5.87784767150878,"y":-0.0,"z":95.02767944335938},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"3025 Lesbos Ln","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1435.31,"y":3657.13,"z":33.45},"positions":{"Wardrobe":{"x":259.9942932128906,"y":-1003.45947265625,"z":-99.00859832763672}},"Interior":"low-end","Owner":"","Price":11500},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-20.86568069458007,"y":-0.0,"z":-166.2356719970703},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"4018 Route 68","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":471.15,"y":2607.8,"z":43.5},"positions":{"Wardrobe":{"x":259.9942932128906,"y":-1003.45947265625,"z":-99.00859832763672}},"Interior":"low-end","Owner":"","Price":12500},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-11.2086534500122,"y":-0.0,"z":-93.07178497314452},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"4013 Joshua Rd","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":181.03,"y":3087.1,"z":42.28},"positions":{"Wardrobe":{"x":259.9942932128906,"y":-1003.45947265625,"z":-99.00859832763672}},"Interior":"low-end","Owner":"","Price":7500},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-9.48588275909423,"y":-0.0,"z":-127.72049713134766},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7340 Nikola Pl","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1386.22,"y":-593.38,"z":73.50999999999999},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":45000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-9.84068393707275,"y":-0.0,"z":-69.5700912475586},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7341 Nikola Pl","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1388.94,"y":-569.49,"z":73.52},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":45000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-12.52571487426757,"y":-0.0,"z":-114.24230194091796},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7341 Nikola Pl","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1373.19,"y":-555.82,"z":73.71},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":4500},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-10.93433380126953,"y":-0.0,"z":-177.74884033203126},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7340 Nikola Pl","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1367.33,"y":-606.62,"z":73.72999999999999},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":45000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-17.39539337158203,"y":-0.0,"z":62.37366104125976},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7339 Nikola Pl","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1341.64,"y":-597.26,"z":73.72},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":45000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-10.56496334075927,"y":4.342483350683324e-7,"z":-27.96370315551757},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7342 Nikola Pl","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1348.31,"y":-546.97,"z":72.91},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":45000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-16.03893852233886,"y":4.441767487151084e-7,"z":152.2796173095703},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7339 Nikola Pl","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1323.3,"y":-583.06,"z":72.27},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":45000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-53.85140228271484,"y":-0.00000144736395,"z":-111.59012603759766},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7342 Nikola Pl","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1328.57,"y":-535.91,"z":71.46},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":45000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-22.11302947998047,"y":4.6078031346041828e-7,"z":164.71957397460938},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7339 Nikola Pl","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1301.02,"y":-574.08,"z":70.75},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":45000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-11.92860603332519,"y":4.363083689895575e-7,"z":-19.26140213012695},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7342 Nikola Pl","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1303.1,"y":-527.3,"z":70.47999999999999},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":45000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-58.0705451965332,"y":-0.0,"z":125.62109375},"maxleft":80},"setName":"","OwnerName":"Jhonatha Dinozzo","garage":{"enabled":false,"StoredVehicles":[]},"Owned":true,"furniture":[],"Name":"7322 West Mirror Drive","Locked":false,"Entrance":{"x":996.89,"y":-729.5,"z":56.84},"Owner":"char1:4da15f5fc4b8fa838372ac19665a1a11b82c4c74","Keys":[],"positions":{"Storage":{"x":348.37,"y":-1008.07,"z":-99.2},"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","plysinside":[],"Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-15.06083393096923,"y":-0.0,"z":131.13587951660157},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7320 West Mirror Drive","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":979.11,"y":-716.31,"z":57.24},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-0.14884614944458,"y":3.3350637806961464e-9,"z":170.76788330078126},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7320 West Mirror Drive","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":970.88,"y":-701.3,"z":57.5},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-10.81925582885742,"y":4.346124455878453e-7,"z":115.8887939453125},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7319 West Mirror Drive","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":960.15,"y":-669.85,"z":57.47},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-59.54927444458008,"y":0.00000168464464,"z":38.34314727783203},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7319 West Mirror Drive","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":943.45,"y":-653.51,"z":57.45},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-36.16230392456055,"y":-0.0,"z":-144.15000915527345},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7318 West Mirror Drive","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":980.25,"y":-627.65,"z":58.26},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-15.65773868560791,"y":-0.0,"z":137.4777984619141},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7317 West Mirror Drive","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":928.82,"y":-639.71,"z":57.26},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-57.77318572998047,"y":-0.00000160100864,"z":46.20460510253906},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7317 West Mirror Drive","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":902.95,"y":-615.51,"z":57.47},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-11.68268966674804,"y":-8.718344588487525e-7,"z":138.07872009277345},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7316 West Mirror Drive","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":886.77,"y":-608.21,"z":57.47},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-3.38041257858276,"y":-0.0,"z":-176.59844970703126},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7316 West Mirror Drive","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":861.7,"y":-583.65,"z":57.18},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-11.14390087127685,"y":-2.1754524937023245e-7,"z":12.50613498687744},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7312 West Mirror Drive","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":844.12,"y":-562.66,"z":57.01},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-5.74017667770385,"y":-5.362977262279856e-8,"z":85.02394104003906},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7314 West Mirror Drive","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":850.25,"y":-532.68,"z":56.95},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-56.35844421386719,"y":0.00000154111978,"z":46.49221038818359},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7313 West Mirror Drive","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":861.51,"y":-508.98,"z":56.74},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-64.80823516845703,"y":-0.0,"z":44.91729354858398},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7313 West Mirror Drive","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":878.44,"y":-497.87,"z":57.11000000000001},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-14.44456386566162,"y":-0.0,"z":19.29909515380859},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7311 West Mirror Drive","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":906.46,"y":-489.76,"z":58.46},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-19.33738899230957,"y":-0.0,"z":-8.25692367553711},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7311 West Mirror Drive","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":921.78,"y":-477.8,"z":60.1},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-17.53591918945312,"y":-8.953837777880835e-7,"z":-55.23443603515625},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7309 West Mirror Drive","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":944.54,"y":-463.17,"z":60.57},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-17.17726707458496,"y":8.936335120779404e-7,"z":33.32376480102539},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7309 West Mirror Drive","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":967.16,"y":-451.65,"z":61.81},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-13.24740600585937,"y":8.771137913754501e-7,"z":34.80197525024414},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7307 West Mirror Drive","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":987.57,"y":-433.03,"z":63.06000000000001},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-14.16050624847412,"y":-4.402644719903037e-7,"z":123.14452362060549},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7307 West Mirror Drive","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1010.38,"y":-423.38,"z":64.36999999999999},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-13.28245830535888,"y":-4.386202476780454e-7,"z":41.270263671875},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7305 West Mirror Drive","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1028.78,"y":-408.36,"z":65.36},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-14.47243404388427,"y":-8.817532943794504e-7,"z":54.3464241027832},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7305 Bridge St","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1060.52,"y":-378.13,"z":67.25},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-19.33366966247558,"y":-9.047982416632294e-7,"z":-144.52737426757813},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7306 West Mirror Drive","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1014.67,"y":-469.3,"z":63.52},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-8.07564449310302,"y":-0.0,"z":-107.44925689697266},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7308 West Mirror Drive","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":970.55,"y":-502.36,"z":61.16},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-61.49639892578125,"y":-0.0,"z":119.70294189453124},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7310 West Mirror Drive","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":945.9,"y":-518.94,"z":59.64},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-17.66978073120117,"y":-0.0,"z":-151.43536376953126},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7312 West Mirror Drive","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":924.39,"y":-526.05,"z":58.81},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-22.57007217407226,"y":-9.24586970540986e-7,"z":-69.45809936523438},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7312 West Mirror Drive","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":893.24,"y":-540.58,"z":57.53},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-14.65564727783203,"y":4.412431167111208e-7,"z":28.74713134765625},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7312 Nikola Ave","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":919.7,"y":-569.56,"z":57.39},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-13.03651428222656,"y":-4.381802227726439e-7,"z":32.70853805541992},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7310 Nikola Ave","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":965.25,"y":-541.93,"z":58.75},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-24.28778457641601,"y":9.366781910102872e-7,"z":30.89822769165039},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7310 Nikola Ave","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":987.84,"y":-525.76,"z":59.71},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-16.08703422546386,"y":-0.0,"z":-65.29899597167969},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7308 Nikola Ave","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1006.48,"y":-510.98,"z":60.01},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-20.10037040710449,"y":9.091479000744585e-7,"z":144.13137817382813},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7306 Bridge St","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1046.29,"y":-498.06,"z":63.1},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-11.98972129821777,"y":-0.0,"z":76.4620132446289},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7306 Bridge St","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1051.04,"y":-470.58,"z":63.32},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-8.78071689605712,"y":-0.0,"z":162.82366943359376},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7306 Bridge St","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1056.24,"y":-448.93,"z":65.28},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-15.54805183410644,"y":-4.4310172597761268e-7,"z":-102.9858856201172},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7344 Bridge St","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1090.46,"y":-484.37,"z":64.67999999999999},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-10.81984233856201,"y":2.17306606487e-7,"z":-15.62946987152099},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7346 Bridge St","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1098.58,"y":-464.52,"z":66.33999999999999},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-11.32894706726074,"y":-0.0,"z":179.49822998046876},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7346 Bridge St","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1099.44,"y":-438.69,"z":66.81},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-8.21971225738525,"y":-5.391471091797939e-8,"z":-93.88529205322266},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7348 Bridge St","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1101.06,"y":-411.33,"z":66.58},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-24.5419979095459,"y":-0.0,"z":-112.49811553955078},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7348 West Mirror Drive","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1114.44,"y":-391.27,"z":67.97},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-5.51487922668457,"y":-2.144359569911103e-7,"z":112.37248229980468},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7345 East Mirror Dr","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1262.35,"y":-429.86,"z":69.03999999999999},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-14.86165904998779,"y":-0.0,"z":94.98734283447266},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7945 East Mirror Dr","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1265.74,"y":-458.06,"z":69.53999999999999},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-10.50986766815185,"y":-0.0,"z":125.15465545654296},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7343 East Mirror Dr","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1259.51,"y":-480.18,"z":69.21},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-16.31183433532715,"y":-0.0,"z":63.34117126464844},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7343 East Mirror Dr","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1251.53,"y":-494.1,"z":68.92999999999999},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-17.28229522705078,"y":-0.0,"z":77.79755401611328},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7343 East Mirror Dr","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1250.9,"y":-515.5,"z":68.36999999999999},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-22.43321228027343,"y":-0.0,"z":130.3302459716797},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7338 East Mirror Dr","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1241.53,"y":-566.43,"z":68.67999999999999},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-6.82489442825317,"y":-1.3435412782314417e-8,"z":91.53223419189452},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7338 East Mirror Dr","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1240.54,"y":-601.65,"z":68.8},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-5.87094974517822,"y":-2.145688853261163e-7,"z":27.77294158935547},"maxleft":80},"setName":"","OwnerName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7336 East Mirror Dr","Locked":false,"Entrance":{"x":1250.79,"y":-620.99,"z":68.58999999999999},"Owner":"","Keys":[],"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","plysinside":[],"Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-14.65502071380615,"y":-0.0,"z":-152.5341949462891},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7333 East Mirror Dr","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1265.69,"y":-648.63,"z":67.14},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-12.5356969833374,"y":-0.0,"z":-171.3050994873047},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7333 East Mirror Dr","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1270.98,"y":-683.53,"z":65.05},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-12.71638584136962,"y":0.00000175048376,"z":58.4541015625},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7328 East Mirror Dr","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1264.75,"y":-702.82,"z":63.93},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-11.09668827056884,"y":-0.0,"z":-88.46087646484375},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7328 East Mirror Dr","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1229.58,"y":-725.27,"z":59.98},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-17.37718200683593,"y":-0.0,"z":-81.23240661621094},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7328 Mirror Park Blvd","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1223.0,"y":-696.93,"z":59.82},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-11.55613040924072,"y":-0.0,"z":-164.38833618164066},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7333 Mirror Park Blvd","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1221.46,"y":-669.31,"z":62.71},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-5.38702487945556,"y":1.3399391818325056e-8,"z":-87.679443359375},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7336 Mirror Park Blvd","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1207.46,"y":-620.26,"z":65.46},"positions":{"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-16.58763313293457,"y":-0.0,"z":3.90023970603942},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7338 Mirror Park Blvd","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1203.75,"y":-598.41,"z":67.08},"positions":{"Storage":{"x":343.86859130859377,"y":-1001.140380859375,"z":-99.19619750976564},"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000},{"cctv":{"maxright":-20,"enabled":false,"rot":{"x":-20.23956108093261,"y":9.099596240957908e-7,"z":-37.31895446777344},"maxleft":80},"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"Owned":false,"furniture":[],"Name":"7338 Mirror Park Blvd","Keys":[],"plysinside":[],"Locked":false,"Entrance":{"x":1201.01,"y":-575.59,"z":68.16},"positions":{"Storage":{"x":343.86859130859377,"y":-1001.140380859375,"z":-99.19619750976564},"Wardrobe":{"x":350.7424926757813,"y":-994.2987060546876,"z":-99.14720153808594}},"Interior":"mid-end","Owner":"","Price":40000}] \ No newline at end of file +[{"Interior":"low-end","Name":"1076 Procopio Dr","plysinside":[],"Owned":false,"cctv":{"enabled":true,"maxleft":90.71163940429688,"rot":{"z":2.7542073726654,"y":2.7045992112562097e-8,"x":-9.43104267120361},"maxright":-88.02642059326172},"Locked":false,"Entrance":{"z":28.57,"y":6206.16,"x":-467.88},"Price":25000,"Keys":[],"setName":"","furniture":[],"OwnerName":"","positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"low-end","Name":"1075 Procopio Dr","plysinside":[],"Owned":false,"cctv":{"enabled":true,"maxleft":-113.61099243164064,"rot":{"z":162.9940643310547,"y":-0.0,"x":3.75689458847045},"maxright":70.65039825439453},"Locked":false,"Entrance":{"z":29.07,"y":6260.23,"x":-448.02},"Price":25000,"Keys":[],"setName":"","furniture":[],"OwnerName":"","positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"low-end","Name":"1074 Procopio Dr","plysinside":[],"Owned":false,"cctv":{"enabled":true,"maxleft":-54.77568435668945,"rot":{"z":-138.95835876464845,"y":-0.0,"x":-1.16786921024322},"maxright":135.13072204589845},"Entrance":{"z":27.96,"y":6314.13,"x":-407.35},"Locked":false,"Keys":[],"Price":25000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"low-end","Name":"1073 Procopio Dr","plysinside":[],"Owned":false,"cctv":{"enabled":true,"maxleft":-51.56189727783203,"rot":{"z":-139.94163513183598,"y":-0.0,"x":-2.56643605232238},"maxright":132.70008850097657},"Entrance":{"z":28.86,"y":6341.62,"x":-368.23},"Locked":false,"Keys":[],"Price":25000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"low-end","Name":"1071 Procopio Dr","plysinside":[],"Owned":false,"cctv":{"enabled":true,"maxleft":-65.34387969970703,"rot":{"z":-152.39810180664066,"y":-0.0,"x":-7.54235982894897},"maxright":123.2501983642578},"Entrance":{"z":30.52,"y":6400.88,"x":-272.61},"Locked":false,"Keys":[],"Price":25000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"low-end","Name":"1071 Procopio Dr","plysinside":[],"Owned":false,"cctv":{"enabled":true,"maxleft":-148.38644409179688,"rot":{"z":128.27305603027345,"y":4.379791960218428e-7,"x":-12.92249584197998},"maxright":38.33782577514648},"Entrance":{"z":30.48,"y":6414.46,"x":-246.14},"Locked":false,"Keys":[],"Price":25000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"1070 Procopio Dr","plysinside":[],"Owned":false,"cctv":{"enabled":true,"maxleft":-135.48313903808598,"rot":{"z":137.8870849609375,"y":2.1444458297992242e-7,"x":-5.53862285614013},"maxright":48.30059432983398},"Entrance":{"z":30.22,"y":6445.48,"x":-229.63},"Locked":false,"Keys":[],"Price":27500,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"1067 Procopio Dr","plysinside":[],"Owned":false,"cctv":{"enabled":true,"maxleft":-46.79578399658203,"rot":{"z":-134.93411254882813,"y":1.0691521623584777e-7,"x":3.44796895980834},"maxright":138.85032653808598},"Entrance":{"z":28.89,"y":6551.76,"x":-130.8},"Locked":false,"Keys":[],"Price":27500,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"low-end","Name":"1065 Procopio Dr","plysinside":[],"Owned":false,"cctv":{"enabled":true,"maxleft":-56.39479827880859,"rot":{"z":-144.0503692626953,"y":-1.3340336835199196e-8,"x":-0.24611411988735},"maxright":129.07382202148438},"Entrance":{"z":30.11,"y":6637.3,"x":-41.72},"Locked":false,"Keys":[],"Price":25000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"low-end","Name":"1064 Procopio Dr","plysinside":[],"Owned":false,"cctv":{"enabled":true,"maxleft":-68.07413482666016,"rot":{"z":-155.01153564453126,"y":-1.068413766347477e-7,"x":-2.71219563484191},"maxright":116.67019653320313},"Entrance":{"z":30.72,"y":6654.13,"x":-9.62},"Locked":false,"Keys":[],"Price":25000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"low-end","Name":"1034 Paleto Blvd","plysinside":[],"Owned":false,"cctv":{"enabled":true,"maxleft":-45.94052505493164,"rot":{"z":-133.8831787109375,"y":-0.0,"x":-2.77191710472106},"maxright":135.7574005126953},"Entrance":{"z":30.86,"y":6207.33,"x":-356.82},"Locked":false,"Keys":[],"Price":25000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"low-end","Name":"1034 Procopio Dr","plysinside":[],"Owned":false,"cctv":{"enabled":true,"maxleft":42.09032440185547,"rot":{"z":-40.4444465637207,"y":4.32035335506953e-7,"x":-8.85426330566406},"maxright":-131.2773895263672},"Entrance":{"z":30.87,"y":6252.75,"x":-379.92},"Locked":false,"Keys":[],"Price":25000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"low-end","Name":"1047 Paleto Blvd","plysinside":[],"Owned":false,"cctv":{"enabled":true,"maxleft":133.55870056152345,"rot":{"z":44.16030502319336,"y":-9.440947223993136e-7,"x":-25.26673126220703},"maxright":-42.54082870483398},"Entrance":{"z":31.91,"y":6326.99,"x":-302.19},"Locked":false,"Keys":[],"Price":25000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"low-end","Name":"1043 Paleto Blvd","plysinside":[],"Owned":false,"cctv":{"enabled":true,"maxleft":-43.60107040405273,"rot":{"z":-137.5096435546875,"y":-0.0,"x":-1.57497417926788},"maxright":134.7738800048828},"Entrance":{"z":30.9,"y":6225.21,"x":-347.3},"Locked":false,"Keys":[],"Price":25000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"1059 Paleto Blvd","plysinside":[],"Owned":false,"cctv":{"enabled":true,"maxleft":43.89247512817383,"rot":{"z":-45.87245941162109,"y":-0.0,"x":-11.80154037475586},"maxright":-133.83499145507813},"Entrance":{"z":32.26,"y":6557.45,"x":-15.3},"Locked":false,"Keys":[],"Price":25000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"1061 Paleto Blvd","plysinside":[],"Owned":false,"cctv":{"enabled":true,"maxleft":-139.30752563476566,"rot":{"z":129.4863433837891,"y":2.144746105159357e-7,"x":-5.62087059020996},"maxright":44.53319549560547},"Entrance":{"z":32.1,"y":6568.19,"x":4.43},"Locked":false,"Keys":[],"Price":27500,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"1061 Paleto Blvd","plysinside":[],"Owned":false,"cctv":{"enabled":true,"maxleft":-43.43285751342773,"rot":{"z":-94.7632293701172,"y":-0.0,"x":-5.83577966690063},"maxright":-159.96804809570313},"Entrance":{"z":31.84,"y":6596.46,"x":31.0},"Locked":false,"Keys":[],"Price":27500,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"low-end","Name":"2016 Grapeseed Main St","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-63.00457000732422,"y":-0.0,"x":-6.35934734344482},"maxright":-20},"Entrance":{"z":42.9,"y":4642.33,"x":1725.06},"Locked":false,"Keys":[],"Price":12500,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"low-end","Name":"2015 Grapeseed Main St","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":91.53390502929688,"y":2.7858778395284394e-8,"x":-16.72389602661132},"maxright":-20},"Entrance":{"z":42.39,"y":4658.22,"x":1674.01},"Locked":false,"Keys":[],"Price":12500,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"low-end","Name":"2014 Grapeseed Main St","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-93.13695526123049,"y":-0.0,"x":-29.83987236022949},"maxright":-20},"Entrance":{"z":42.68,"y":4677.34,"x":1718.91},"Locked":false,"Keys":[],"Price":7500,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"low-end","Name":"2013 Grapeseed Main St","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":91.7155303955078,"y":-0.0,"x":-6.32857131958007},"maxright":-20},"Entrance":{"z":42.09,"y":4689.52,"x":1682.85},"Locked":false,"Keys":[],"Price":7500,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"low-end","Name":"2011 Grapeseed Main St","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":110.03758239746094,"y":-0.0,"x":-18.96443367004394},"maxright":-20},"Entrance":{"z":41.03,"y":4739.5,"x":1664.0},"Locked":false,"Keys":[],"Price":10000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"low-end","Name":"3012 Niland Ave","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-77.30774688720703,"y":-0.0,"x":-14.36374378204345},"maxright":-20},"Entrance":{"z":32.23,"y":3920.59,"x":1880.27},"Locked":false,"Keys":[],"Price":10000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"low-end","Name":"3013 Niland Ave","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":103.60260009765624,"y":-2.1725810483985698e-7,"x":-10.75266551971435},"maxright":-20},"Entrance":{"z":32.48,"y":3914.59,"x":1846.06},"Locked":false,"Keys":[],"Price":9000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"low-end","Name":"3013 Marina Dr","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-76.59026336669922,"y":-0.0,"x":-20.23343658447265},"maxright":-20},"Entrance":{"z":36.08,"y":3913.8,"x":1803.57},"Locked":false,"Keys":[],"Price":11500,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"low-end","Name":"3025 Lesbos Ln","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":95.02767944335938,"y":-0.0,"x":-5.87784767150878},"maxright":-20},"Entrance":{"z":33.45,"y":3657.13,"x":1435.31},"Locked":false,"Keys":[],"Price":11500,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"low-end","Name":"4018 Route 68","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-166.2356719970703,"y":-0.0,"x":-20.86568069458007},"maxright":-20},"Entrance":{"z":43.5,"y":2607.8,"x":471.15},"Locked":false,"Keys":[],"Price":12500,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"low-end","Name":"4013 Joshua Rd","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-93.07178497314452,"y":-0.0,"x":-11.2086534500122},"maxright":-20},"Entrance":{"z":42.28,"y":3087.1,"x":181.03},"Locked":false,"Keys":[],"Price":7500,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7340 Nikola Pl","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-127.72049713134766,"y":-0.0,"x":-9.48588275909423},"maxright":-20},"Entrance":{"z":73.50999999999999,"y":-593.38,"x":1386.22},"Locked":false,"Keys":[],"Price":45000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7341 Nikola Pl","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-69.5700912475586,"y":-0.0,"x":-9.84068393707275},"maxright":-20},"Entrance":{"z":73.52,"y":-569.49,"x":1388.94},"Locked":false,"Keys":[],"Price":45000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7341 Nikola Pl","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-114.24230194091796,"y":-0.0,"x":-12.52571487426757},"maxright":-20},"Entrance":{"z":73.71,"y":-555.82,"x":1373.19},"Locked":false,"Keys":[],"Price":4500,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7340 Nikola Pl","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-177.74884033203126,"y":-0.0,"x":-10.93433380126953},"maxright":-20},"Entrance":{"z":73.72999999999999,"y":-606.62,"x":1367.33},"Locked":false,"Keys":[],"Price":45000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7339 Nikola Pl","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":62.37366104125976,"y":-0.0,"x":-17.39539337158203},"maxright":-20},"Entrance":{"z":73.72,"y":-597.26,"x":1341.64},"Locked":false,"Keys":[],"Price":45000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7342 Nikola Pl","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-27.96370315551757,"y":4.342483350683324e-7,"x":-10.56496334075927},"maxright":-20},"Entrance":{"z":72.91,"y":-546.97,"x":1348.31},"Locked":false,"Keys":[],"Price":45000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":true,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7339 Nikola Pl","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":152.2796173095703,"y":4.441767487151084e-7,"x":-16.03893852233886},"maxright":-20},"Entrance":{"z":72.27,"y":-583.06,"x":1323.3},"Locked":false,"Keys":[],"Price":45000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7342 Nikola Pl","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-111.59012603759766,"y":-0.00000144736395,"x":-53.85140228271484},"maxright":-20},"Entrance":{"z":71.46,"y":-535.91,"x":1328.57},"Locked":false,"Keys":[],"Price":45000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7339 Nikola Pl","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":164.71957397460938,"y":4.6078031346041828e-7,"x":-22.11302947998047},"maxright":-20},"Entrance":{"z":70.75,"y":-574.08,"x":1301.02},"Locked":false,"Keys":[],"Price":45000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7342 Nikola Pl","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-19.26140213012695,"y":4.363083689895575e-7,"x":-11.92860603332519},"maxright":-20},"Locked":false,"Entrance":{"z":70.47999999999999,"y":-527.3,"x":1303.1},"Price":45000,"Keys":[],"setName":"","furniture":[],"OwnerName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7322 West Mirror Drive","plysinside":[],"Owned":true,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":125.62109375,"y":-0.0,"x":-58.0705451965332},"maxright":-20},"Locked":true,"Entrance":{"z":56.84,"y":-729.5,"x":996.89},"Price":40000,"Keys":[],"setName":"Baffo Il Mecccanico","furniture":[],"OwnerName":"Cris Baffo","positions":{"Storage":{"z":-99.2,"y":-1008.07,"x":348.37},"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":"char1:37d07f947c0c7c14a8563ea0a8e1caaf8731a0cd"},{"Interior":"tinsel_towers","Name":"7320 West Mirror Drive","plysinside":[],"Owned":true,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":131.13587951660157,"y":-0.0,"x":-15.06083393096923},"maxright":-20},"Locked":true,"Entrance":{"z":57.24,"y":-716.31,"x":979.11},"Price":40000,"Keys":[],"setName":"i grappini boy","furniture":[],"OwnerName":"Cris Baffo","positions":{"Storage":{"z":97.6,"y":54.76,"x":-622.81},"Wardrobe":{"z":96.99960327148438,"y":55.68030166625976,"x":-594.5938110351563}},"garage":{"Heading":137.44815063476566,"pos":{"z":57.7222900390625,"y":-711.5736083984375,"x":978.6329956054688},"enabled":true,"StoredVehicles":[{"owner":"char1:37d07f947c0c7c14a8563ea0a8e1caaf8731a0cd","vehicle":{"modTurbo":1,"modFrame":-1,"modSpoilers":-1,"tyreBurst":{"2":false,"1":false,"0":false,"3":false},"neonColor":[0,150,255],"modHood":-1,"modFrontBumper":-1,"modTrunk":-1,"modSteeringWheel":-1,"modTrimA":-1,"modSuspension":-1,"modTank":-1,"modDoorSpeaker":-1,"modEngine":3,"xenonColor":2,"modSeats":-1,"modCustomFrontWheels":false,"modExhaust":-1,"modRoof":-1,"wheels":9,"plateIndex":0,"modWindows":-1,"modXenon":1,"modGrille":-1,"doorsBroken":{"2":false,"1":false,"0":false},"color2":112,"modLightbar":-1,"modArmor":4,"modEngineBlock":-1,"modAPlate":-1,"modSpeakers":-1,"modPlateHolder":-1,"interiorColor":0,"modLivery":2,"modFender":-1,"tyreSmokeColor":[0,150,255],"modAirFilter":-1,"model":-1403128555,"plate":"BQI 617","windowsBroken":{"6":false,"5":true,"0":false,"7":true,"2":true,"1":false,"4":true,"3":true},"bodyHealth":1000.0,"windowTint":1,"modSideSkirt":-1,"modRightFender":-1,"color1":112,"neonEnabled":[1,1,1,1],"tankHealth":1000.0,"modShifterLeavers":-1,"modBackWheels":-1,"modArchCover":-1,"modStruts":-1,"dirtLevel":4.9,"extras":[],"modFrontWheels":112,"engineHealth":1000.0,"modVanityPlate":-1,"modTrimB":-1,"tyresCanBurst":1,"modSmokeEnabled":1,"modTransmission":2,"pearlescentColor":112,"modDashboard":-1,"fuelLevel":55.0,"dashboardColor":0,"modBrakes":2,"DisplayName":"Zentorno","modOrnaments":-1,"modAerials":-1,"modRearBumper":-1,"wheelColor":73,"modRoofLivery":-1,"modDial":-1,"modHorns":2,"modHydrolic":-1,"modCustomBackWheels":false}},{"owner":"char1:37d07f947c0c7c14a8563ea0a8e1caaf8731a0cd","vehicle":{"modTurbo":1,"modFrame":-1,"modSpoilers":-1,"tyreBurst":{"2":false,"1":false,"0":false,"3":false},"neonColor":[255,255,255],"modHood":-1,"modFrontBumper":-1,"modTrunk":-1,"modSteeringWheel":-1,"modTrimA":-1,"modSuspension":3,"modTank":-1,"modDoorSpeaker":-1,"modEngine":3,"xenonColor":255,"modSeats":-1,"modCustomFrontWheels":false,"modExhaust":-1,"modRoof":-1,"wheels":2,"plateIndex":5,"pearlescentColor":12,"modTrimB":-1,"modGrille":-1,"modHydrolic":-1,"color2":12,"modLightbar":-1,"modArmor":4,"modEngineBlock":-1,"modAPlate":-1,"modSpeakers":-1,"modPlateHolder":-1,"interiorColor":8,"modLivery":-1,"modFender":-1,"tyreSmokeColor":[255,255,255],"modAirFilter":-1,"model":1878062887,"plate":"NYZ 217","windowsBroken":{"6":false,"5":false,"0":false,"7":false,"2":false,"1":false,"4":false,"3":false},"bodyHealth":993.1,"windowTint":1,"modSideSkirt":-1,"modRightFender":-1,"color1":12,"neonEnabled":[1,1,1,1],"tyresCanBurst":1,"modShifterLeavers":-1,"modDial":-1,"modArchCover":-1,"modStruts":-1,"dirtLevel":10.0,"modDashboard":-1,"modBackWheels":-1,"modFrontWheels":26,"engineHealth":928.7,"doorsBroken":{"6":false,"5":false,"0":false,"2":false,"1":false,"4":false,"3":false},"modBrakes":2,"modWindows":-1,"modAerials":-1,"extras":{"10":false,"12":1},"tankHealth":999.3,"fuelLevel":62.5,"dashboardColor":156,"DisplayName":"Baller LE","modXenon":1,"modOrnaments":-1,"modTransmission":2,"modRearBumper":-1,"modSmokeEnabled":1,"modRoofLivery":-1,"modVanityPlate":-1,"modHorns":-1,"wheelColor":7,"modCustomBackWheels":false}},{"owner":"char1:37d07f947c0c7c14a8563ea0a8e1caaf8731a0cd","vehicle":{"modTurbo":1,"modFrame":-1,"modSpoilers":-1,"tyreBurst":{"0":false,"1":false},"neonColor":[153,0,153],"modHood":-1,"modFrontBumper":-1,"modTrunk":-1,"modSteeringWheel":-1,"modTrimA":-1,"modSuspension":-1,"modTank":-1,"modDoorSpeaker":-1,"modEngine":3,"xenonColor":11,"modSeats":-1,"modCustomFrontWheels":false,"modExhaust":-1,"modRoof":-1,"wheels":6,"plateIndex":0,"pearlescentColor":148,"modTrimB":-1,"modGrille":-1,"modHydrolic":-1,"color2":148,"modLightbar":-1,"modArmor":4,"modEngineBlock":-1,"modAPlate":-1,"modSpeakers":-1,"modPlateHolder":-1,"interiorColor":0,"modLivery":-1,"modFender":-1,"tyreSmokeColor":[153,0,153],"modAirFilter":-1,"model":-114291515,"plate":"BIR 004","windowsBroken":{"6":false,"5":true,"0":true,"7":true,"2":true,"1":true,"4":true,"3":true},"bodyHealth":989.9,"windowTint":1,"modSideSkirt":-1,"modRightFender":-1,"color1":148,"neonEnabled":[1,1,1,1],"tyresCanBurst":1,"modShifterLeavers":-1,"modDial":-1,"modArchCover":-1,"modStruts":-1,"dirtLevel":0.3,"modDashboard":-1,"modBackWheels":8,"modFrontWheels":8,"engineHealth":940.9,"doorsBroken":{"0":false,"1":false},"modBrakes":2,"modWindows":-1,"modAerials":-1,"extras":[],"tankHealth":996.3,"fuelLevel":75.8,"dashboardColor":0,"DisplayName":"Bati 801","modXenon":1,"modOrnaments":-1,"modTransmission":2,"modRearBumper":-1,"modSmokeEnabled":1,"modRoofLivery":-1,"modVanityPlate":-1,"modHorns":-1,"wheelColor":148,"modCustomBackWheels":false}},{"owner":"char1:37d07f947c0c7c14a8563ea0a8e1caaf8731a0cd","vehicle":{"modTurbo":1,"modFrame":-1,"modSpoilers":-1,"tyreBurst":{"2":false,"1":false,"0":false,"3":false},"neonColor":[255,255,255],"modHood":-1,"modFrontBumper":-1,"modTrunk":-1,"modSteeringWheel":-1,"modTrimA":-1,"modSuspension":3,"modTank":-1,"modDoorSpeaker":-1,"modEngine":3,"xenonColor":255,"modSeats":-1,"modCustomFrontWheels":false,"modExhaust":-1,"modRoof":-1,"wheels":9,"plateIndex":5,"pearlescentColor":121,"modTrimB":-1,"modGrille":-1,"modHydrolic":-1,"color2":121,"modLightbar":-1,"modArmor":4,"modEngineBlock":-1,"modAPlate":-1,"modSpeakers":-1,"modPlateHolder":-1,"interiorColor":0,"modLivery":-1,"modFender":-1,"tyreSmokeColor":[255,255,255],"modAirFilter":-1,"model":2136773105,"plate":"TEW 107","windowsBroken":{"6":false,"5":false,"0":false,"7":false,"2":false,"1":false,"4":false,"3":false},"bodyHealth":1000.0,"windowTint":1,"modSideSkirt":-1,"modRightFender":-1,"color1":140,"neonEnabled":[1,1,1,1],"tyresCanBurst":1,"modShifterLeavers":-1,"modDial":-1,"modArchCover":-1,"modStruts":-1,"dirtLevel":11.6,"modDashboard":-1,"modBackWheels":-1,"modFrontWheels":112,"engineHealth":1000.0,"doorsBroken":{"6":false,"5":false,"0":false,"2":false,"1":false,"4":false,"3":false},"modBrakes":2,"modWindows":-1,"modAerials":-1,"extras":{"10":false,"12":false},"tankHealth":1000.0,"fuelLevel":54.7,"dashboardColor":0,"DisplayName":"Rocoto","modXenon":1,"modOrnaments":-1,"modTransmission":2,"modRearBumper":-1,"modSmokeEnabled":1,"modRoofLivery":-1,"modVanityPlate":-1,"modHorns":42,"wheelColor":111,"modCustomBackWheels":false}},{"owner":"char1:37d07f947c0c7c14a8563ea0a8e1caaf8731a0cd","vehicle":{"modTurbo":1,"modFrame":-1,"modSpoilers":-1,"tyreBurst":{"2":false,"1":false,"0":false,"3":false},"neonColor":[0,245,255],"modHood":-1,"modFrontBumper":-1,"modTrunk":-1,"modSteeringWheel":-1,"modTrimA":-1,"modSuspension":-1,"modTank":-1,"modDoorSpeaker":-1,"modEngine":3,"xenonColor":2,"modSeats":-1,"modCustomFrontWheels":false,"modExhaust":-1,"modRoof":-1,"wheels":3,"plateIndex":0,"pearlescentColor":111,"modTrimB":-1,"modGrille":-1,"modHydrolic":-1,"color2":64,"modLightbar":-1,"modArmor":4,"modEngineBlock":-1,"modAPlate":-1,"modSpeakers":-1,"modPlateHolder":-1,"interiorColor":0,"modLivery":-1,"modFender":-1,"tyreSmokeColor":[255,255,255],"modAirFilter":-1,"model":-845961253,"plate":"UHT 389","windowsBroken":{"6":false,"5":true,"0":true,"7":true,"2":true,"1":true,"4":true,"3":true},"bodyHealth":998.9,"windowTint":1,"modSideSkirt":-1,"modRightFender":-1,"color1":111,"neonEnabled":[1,1,1,1],"tyresCanBurst":false,"modShifterLeavers":-1,"modDial":-1,"modArchCover":-1,"modStruts":-1,"dirtLevel":15.0,"modDashboard":-1,"modBackWheels":-1,"modFrontWheels":-1,"engineHealth":992.2,"doorsBroken":{"2":false,"1":false,"0":false,"3":false},"modBrakes":2,"modWindows":-1,"modAerials":-1,"extras":[],"tankHealth":999.9,"fuelLevel":69.9,"dashboardColor":0,"DisplayName":"The Liberator","modXenon":1,"modOrnaments":-1,"modTransmission":2,"modRearBumper":-1,"modSmokeEnabled":1,"modRoofLivery":-1,"modVanityPlate":-1,"modHorns":-1,"wheelColor":156,"modCustomBackWheels":false}},{"owner":"char1:37d07f947c0c7c14a8563ea0a8e1caaf8731a0cd","vehicle":{"modTurbo":1,"modFrame":-1,"modSpoilers":-1,"tyreBurst":{"0":false,"1":false},"neonColor":[255,0,255],"modHood":-1,"modFrontBumper":-1,"modTrunk":-1,"modSteeringWheel":-1,"modTrimA":-1,"modSuspension":-1,"modTank":-1,"modDoorSpeaker":-1,"modEngine":3,"xenonColor":255,"modSeats":-1,"modCustomFrontWheels":false,"modExhaust":-1,"modRoof":-1,"wheels":6,"plateIndex":4,"pearlescentColor":111,"modTrimB":-1,"modGrille":-1,"modHydrolic":-1,"color2":111,"modLightbar":-1,"modArmor":4,"modEngineBlock":-1,"modAPlate":-1,"modSpeakers":-1,"modPlateHolder":-1,"interiorColor":0,"modLivery":-1,"modFender":-1,"tyreSmokeColor":[0,255,0],"modAirFilter":-1,"model":-1453280962,"plate":"UKV 055","windowsBroken":{"6":true,"5":true,"0":true,"7":true,"2":true,"1":true,"4":true,"3":true},"bodyHealth":998.7,"windowTint":-1,"modSideSkirt":-1,"modRightFender":-1,"color1":111,"neonEnabled":[false,false,false,false],"tyresCanBurst":1,"modShifterLeavers":-1,"modDial":-1,"modArchCover":-1,"modStruts":-1,"dirtLevel":15.0,"modDashboard":-1,"modBackWheels":-1,"modFrontWheels":-1,"engineHealth":992.5,"doorsBroken":{"0":false,"1":false},"modBrakes":2,"modWindows":-1,"modAerials":-1,"extras":[],"tankHealth":999.5,"fuelLevel":81.7,"dashboardColor":0,"DisplayName":"Sanchez","modXenon":1,"modOrnaments":-1,"modTransmission":2,"modRearBumper":-1,"modSmokeEnabled":1,"modRoofLivery":-1,"modVanityPlate":-1,"modHorns":-1,"wheelColor":0,"modCustomBackWheels":false}},{"owner":"char1:37d07f947c0c7c14a8563ea0a8e1caaf8731a0cd","vehicle":{"modTurbo":1,"modFrame":-1,"modSpoilers":-1,"tyreBurst":{"2":false,"1":false,"0":false,"3":false},"neonColor":[255,215,0],"modHood":-1,"modFrontBumper":-1,"modTrunk":-1,"modSteeringWheel":-1,"modTrimA":-1,"modSuspension":3,"modTank":-1,"modDoorSpeaker":-1,"modEngine":3,"DisplayName":"NULL","modSeats":-1,"modCustomFrontWheels":false,"modExhaust":-1,"modRoof":-1,"wheels":0,"plateIndex":3,"modWindows":-1,"modXenon":1,"modGrille":-1,"doorsBroken":{"6":false,"5":false,"0":false,"2":false,"1":false,"4":false,"3":false},"color2":160,"modLightbar":-1,"modArmor":4,"modEngineBlock":-1,"modAPlate":-1,"modSpeakers":-1,"modPlateHolder":-1,"interiorColor":0,"modLivery":-1,"modFender":-1,"tyreSmokeColor":[255,255,0],"modAirFilter":-1,"model":-252651765,"plate":"BFI 997","windowsBroken":{"6":false,"5":false,"0":false,"7":false,"2":false,"1":false,"4":false,"3":false},"bodyHealth":1000.0,"windowTint":1,"modOrnaments":-1,"modRightFender":-1,"color1":160,"neonEnabled":[1,1,1,1],"tankHealth":1000.0,"modShifterLeavers":-1,"modTransmission":2,"modArchCover":-1,"modStruts":-1,"dirtLevel":4.3,"modDial":-1,"modFrontWheels":-1,"engineHealth":1000.0,"modVanityPlate":-1,"modDashboard":-1,"tyresCanBurst":1,"wheelColor":156,"modTrimB":-1,"pearlescentColor":37,"xenonColor":5,"dashboardColor":0,"modHorns":-1,"fuelLevel":64.9,"modAerials":-1,"extras":{"11":false,"12":1},"modSmokeEnabled":1,"modRearBumper":-1,"modBrakes":2,"modRoofLivery":-1,"modSideSkirt":-1,"modBackWheels":-1,"modHydrolic":-1,"modCustomBackWheels":false}}]},"Owner":"char1:37d07f947c0c7c14a8563ea0a8e1caaf8731a0cd"},{"Interior":"mid-end","Name":"7320 West Mirror Drive","plysinside":[],"Owned":true,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":170.76788330078126,"y":3.3350637806961464e-9,"x":-0.14884614944458},"maxright":-20},"Locked":true,"Entrance":{"z":57.5,"y":-701.3,"x":970.88},"Price":40000,"Keys":[],"setName":"","furniture":[],"OwnerName":"Jhonni Paleto","positions":{"Wardrobe":{"z":-99.2,"y":-1008.97,"x":346.37}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":"char1:4da15f5fc4b8fa838372ac19665a1a11b82c4c74"},{"Interior":"dell_perro_heights","Name":"7319 West Mirror Drive","plysinside":[],"Owned":true,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":115.8887939453125,"y":4.346124455878453e-7,"x":-10.81925582885742},"maxright":-20},"Locked":false,"Entrance":{"z":57.47,"y":-669.85,"x":960.15},"Price":40000,"Keys":[],"setName":"","furniture":[],"OwnerName":"Lorenzo Fasan","positions":{"Storage":{"z":56.93730163574219,"y":-531.0081787109375,"x":-1457.0120849609376},"Wardrobe":{"z":50.73249816894531,"y":-536.9598999023438,"x":-1467.2574462890626}},"garage":{"Heading":291.20819091796877,"pos":{"z":58.0087890625,"y":-669.4154052734375,"x":945.3230590820313},"enabled":true,"StoredVehicles":[{"owner":"char1:fb7b49cca917dfb5bb74305db82ac07a98d4abf6","vehicle":{"modTurbo":1,"modFrame":-1,"modSpoilers":-1,"tyreBurst":{"2":false,"1":false,"0":false,"3":false},"neonColor":[255,0,255],"modHood":-1,"modFrontBumper":-1,"modTrunk":-1,"modSteeringWheel":-1,"modTrimA":-1,"modSuspension":-1,"modTank":-1,"modDoorSpeaker":-1,"modEngine":-1,"xenonColor":255,"modSeats":-1,"modCustomFrontWheels":false,"modExhaust":-1,"modRoof":-1,"wheels":5,"plateIndex":0,"modWindows":-1,"modXenon":false,"modGrille":-1,"doorsBroken":{"0":false,"2":false,"1":false,"4":false,"3":false},"color2":4,"modLightbar":-1,"modArmor":-1,"modEngineBlock":-1,"modAPlate":-1,"modSpeakers":-1,"modPlateHolder":-1,"interiorColor":111,"modLivery":-1,"modFender":-1,"tyreSmokeColor":[255,255,255],"modAirFilter":-1,"model":1549126457,"plate":"VKD 248","windowsBroken":{"6":false,"5":true,"0":false,"7":false,"2":false,"1":false,"4":true,"3":false},"bodyHealth":985.1,"windowTint":-1,"modSideSkirt":-1,"modRightFender":-1,"color1":0,"neonEnabled":[false,false,false,false],"tankHealth":998.5,"modShifterLeavers":-1,"modBackWheels":-1,"modArchCover":-1,"modStruts":-1,"dirtLevel":10.2,"extras":[],"modFrontWheels":-1,"engineHealth":809.9,"modVanityPlate":-1,"modTrimB":-1,"tyresCanBurst":1,"modSmokeEnabled":1,"modTransmission":2,"pearlescentColor":5,"modDashboard":-1,"fuelLevel":76.8,"dashboardColor":111,"modBrakes":-1,"DisplayName":"Brioso R/A","modOrnaments":-1,"modAerials":-1,"modRearBumper":-1,"wheelColor":112,"modRoofLivery":-1,"modDial":-1,"modHorns":-1,"modHydrolic":-1,"modCustomBackWheels":false}},{"owner":"char1:fb7b49cca917dfb5bb74305db82ac07a98d4abf6","vehicle":{"modTurbo":1,"modFrame":-1,"modSpoilers":0,"tyreBurst":{"2":false,"1":false,"0":false,"3":false},"neonColor":[255,0,255],"modHood":-1,"modFrontBumper":-1,"modTrunk":-1,"modSteeringWheel":-1,"modTrimA":-1,"modSuspension":2,"modTank":-1,"modDoorSpeaker":-1,"modEngine":3,"xenonColor":11,"modSeats":-1,"modCustomFrontWheels":false,"modExhaust":1,"modRoof":-1,"wheels":12,"plateIndex":1,"modWindows":-1,"modXenon":1,"modGrille":-1,"doorsBroken":{"2":false,"1":false,"0":false,"3":false},"color2":0,"modLightbar":-1,"modArmor":4,"modEngineBlock":-1,"modAPlate":-1,"modSpeakers":-1,"modPlateHolder":-1,"interiorColor":111,"modLivery":-1,"modFender":-1,"tyreSmokeColor":[153,0,153],"modAirFilter":-1,"model":2067820283,"plate":"OQZ 201","windowsBroken":{"6":false,"5":true,"0":false,"7":true,"2":true,"1":false,"4":true,"3":true},"bodyHealth":1000.0,"windowTint":1,"modSideSkirt":-1,"modRightFender":-1,"color1":148,"neonEnabled":[1,1,1,1],"tankHealth":1000.0,"modShifterLeavers":-1,"modBackWheels":-1,"modArchCover":-1,"modStruts":-1,"dirtLevel":7.0,"extras":[],"modFrontWheels":96,"engineHealth":993.0,"modVanityPlate":-1,"modTrimB":-1,"tyresCanBurst":1,"modSmokeEnabled":1,"modTransmission":2,"pearlescentColor":135,"modDashboard":-1,"fuelLevel":86.8,"dashboardColor":111,"modBrakes":2,"DisplayName":"Tyrus","modOrnaments":-1,"modAerials":-1,"modRearBumper":-1,"wheelColor":148,"modRoofLivery":-1,"modDial":-1,"modHorns":-1,"modHydrolic":-1,"modCustomBackWheels":false}},{"owner":"char1:fb7b49cca917dfb5bb74305db82ac07a98d4abf6","vehicle":{"modTurbo":1,"modFrame":-1,"modSpoilers":-1,"tyreBurst":{"0":false,"1":false},"neonColor":[255,255,255],"modHood":-1,"modFrontBumper":-1,"modTrunk":-1,"modSteeringWheel":-1,"modTrimA":-1,"modSuspension":-1,"modTank":-1,"modDoorSpeaker":-1,"modEngine":3,"xenonColor":255,"modSeats":-1,"modCustomFrontWheels":false,"modExhaust":-1,"modRoof":-1,"wheels":6,"plateIndex":3,"pearlescentColor":0,"modTrimB":-1,"modGrille":-1,"modHydrolic":-1,"color2":0,"modLightbar":-1,"modArmor":4,"modEngineBlock":-1,"modAPlate":-1,"modSpeakers":-1,"modPlateHolder":-1,"interiorColor":0,"modLivery":0,"modFender":-1,"tyreSmokeColor":[255,255,255],"modAirFilter":-1,"model":-891462355,"plate":"MLV 711","windowsBroken":{"6":false,"5":true,"0":true,"7":true,"2":true,"1":true,"4":true,"3":true},"bodyHealth":966.0,"windowTint":-1,"modSideSkirt":-1,"modRightFender":-1,"color1":0,"neonEnabled":[1,1,1,1],"tyresCanBurst":1,"modShifterLeavers":-1,"modDial":-1,"modArchCover":-1,"modStruts":-1,"dirtLevel":15.0,"modDashboard":-1,"modBackWheels":-1,"modFrontWheels":-1,"engineHealth":861.0,"doorsBroken":{"0":false,"1":false},"modBrakes":2,"modWindows":-1,"modAerials":-1,"extras":[],"tankHealth":991.0,"fuelLevel":12.5,"dashboardColor":0,"DisplayName":"Bati 801RR","modXenon":1,"modOrnaments":-1,"modTransmission":2,"modRearBumper":-1,"modSmokeEnabled":1,"modRoofLivery":-1,"modVanityPlate":-1,"modHorns":-1,"wheelColor":156,"modCustomBackWheels":false}},{"owner":"char1:fb7b49cca917dfb5bb74305db82ac07a98d4abf6","vehicle":{"modTurbo":false,"modFrame":-1,"modSpoilers":-1,"tyreBurst":{"2":false,"1":false,"0":false,"3":false},"neonColor":[255,0,0],"modHood":-1,"modFrontBumper":-1,"modTrunk":-1,"modSteeringWheel":-1,"modTrimA":-1,"modSuspension":-1,"modTank":-1,"modDoorSpeaker":-1,"modEngine":-1,"xenonColor":8,"modSeats":-1,"modCustomFrontWheels":false,"modExhaust":-1,"modRoof":-1,"wheels":0,"plateIndex":0,"pearlescentColor":12,"modTrimB":-1,"modGrille":-1,"modHydrolic":-1,"color2":12,"modLightbar":-1,"modArmor":-1,"modEngineBlock":-1,"modAPlate":-1,"modSpeakers":-1,"modPlateHolder":-1,"interiorColor":0,"modLivery":0,"modFender":-1,"tyreSmokeColor":[255,0,0],"modAirFilter":-1,"model":2117711508,"plate":"AXE 777","windowsBroken":{"6":false,"5":true,"0":false,"7":false,"2":false,"1":false,"4":true,"3":false},"bodyHealth":1000.0,"windowTint":1,"modSideSkirt":-1,"modRightFender":-1,"color1":12,"neonEnabled":[1,1,1,1],"tyresCanBurst":1,"modShifterLeavers":-1,"modDial":-1,"modArchCover":-1,"modStruts":-1,"dirtLevel":4.1,"modDashboard":-1,"modBackWheels":-1,"modFrontWheels":-1,"engineHealth":1000.0,"doorsBroken":{"5":false,"0":false,"2":false,"1":false,"4":false,"3":false},"modBrakes":-1,"modWindows":-1,"modAerials":-1,"extras":{"5":false,"2":false,"1":1,"4":false,"3":false},"tankHealth":1000.0,"fuelLevel":65.2,"dashboardColor":131,"DisplayName":"NULL","modXenon":1,"modOrnaments":-1,"modTransmission":-1,"modRearBumper":-1,"modSmokeEnabled":1,"modRoofLivery":-1,"modVanityPlate":-1,"modHorns":-1,"wheelColor":12,"modCustomBackWheels":false}},{"owner":"char1:fb7b49cca917dfb5bb74305db82ac07a98d4abf6","vehicle":{"modTurbo":1,"modFrame":-1,"modSpoilers":-1,"tyreBurst":{"2":false,"1":false,"0":false,"3":false},"neonColor":[153,0,153],"modHood":-1,"modFrontBumper":-1,"modTrunk":-1,"modSteeringWheel":-1,"modTrimA":-1,"modSuspension":3,"modTank":-1,"modDoorSpeaker":-1,"modEngine":3,"DisplayName":"NULL","modSeats":-1,"modCustomFrontWheels":false,"modExhaust":-1,"modRoof":-1,"wheels":0,"plateIndex":0,"modWindows":-1,"modXenon":1,"modGrille":-1,"doorsBroken":{"6":false,"5":false,"0":false,"2":false,"1":false,"4":false,"3":false},"color2":148,"modLightbar":-1,"modArmor":4,"modEngineBlock":-1,"modAPlate":-1,"modSpeakers":-1,"modPlateHolder":-1,"interiorColor":0,"modLivery":-1,"modFender":-1,"tyreSmokeColor":[255,255,255],"modAirFilter":-1,"model":-252651765,"plate":"AXE 104","windowsBroken":{"6":false,"5":false,"0":false,"7":false,"2":false,"1":false,"4":false,"3":false},"bodyHealth":999.5,"windowTint":-1,"modOrnaments":-1,"modRightFender":-1,"color1":21,"neonEnabled":[1,1,1,1],"tankHealth":999.9,"modShifterLeavers":-1,"modTransmission":2,"modArchCover":-1,"modStruts":-1,"dirtLevel":12.7,"modDial":-1,"modFrontWheels":-1,"engineHealth":995.0,"modVanityPlate":-1,"modDashboard":-1,"tyresCanBurst":1,"wheelColor":156,"modTrimB":-1,"pearlescentColor":21,"xenonColor":11,"dashboardColor":0,"modHorns":-1,"fuelLevel":61.2,"modAerials":-1,"extras":{"11":false,"12":1},"modSmokeEnabled":1,"modRearBumper":-1,"modBrakes":2,"modRoofLivery":-1,"modSideSkirt":-1,"modBackWheels":-1,"modHydrolic":-1,"modCustomBackWheels":false}}]},"Owner":"char1:fb7b49cca917dfb5bb74305db82ac07a98d4abf6"},{"Interior":"mid-end","Name":"7319 West Mirror Drive","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":38.34314727783203,"y":0.00000168464464,"x":-59.54927444458008},"maxright":-20},"Entrance":{"z":57.45,"y":-653.51,"x":943.45},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7318 West Mirror Drive","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-144.15000915527345,"y":-0.0,"x":-36.16230392456055},"maxright":-20},"Entrance":{"z":58.26,"y":-627.65,"x":980.25},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"wild_oats","Name":"7317 West Mirror Drive","plysinside":[],"Owned":true,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":137.4777984619141,"y":-0.0,"x":-15.65773868560791},"maxright":-20},"Locked":true,"Entrance":{"z":57.26,"y":-639.71,"x":928.82},"Price":40000,"Keys":[],"setName":"7317 house Busco","furniture":[],"OwnerName":"Miki Smile","positions":{"Storage":{"z":130.04359436035157,"y":492.5582885742188,"x":-174.4463043212891},"Wardrobe":{"z":133.0,"y":487.71728515625,"x":-167.42269897460938}},"garage":{"Heading":141.49365234375,"pos":{"z":57.857177734375,"y":-631.2395629882813,"x":924.8307495117188},"enabled":true,"StoredVehicles":[{"owner":"char1:381e7ba5926bac97d5b585aac38ba38df2909d0d","vehicle":{"modTurbo":false,"modFrame":-1,"modSpoilers":-1,"tyreBurst":{"2":false,"1":false,"0":false,"3":false},"neonColor":[255,0,255],"modHood":-1,"modFrontBumper":-1,"modTrunk":-1,"modSteeringWheel":-1,"modTrimA":-1,"modSuspension":-1,"modTank":-1,"modDoorSpeaker":-1,"modEngine":-1,"xenonColor":255,"modSeats":-1,"modCustomFrontWheels":false,"modExhaust":-1,"modRoof":-1,"wheels":3,"plateIndex":0,"pearlescentColor":111,"modTrimB":-1,"modGrille":-1,"modHydrolic":-1,"color2":64,"modLightbar":-1,"modArmor":-1,"modEngineBlock":-1,"modAPlate":-1,"modSpeakers":-1,"modPlateHolder":-1,"interiorColor":0,"modLivery":-1,"modFender":-1,"tyreSmokeColor":[255,255,255],"modAirFilter":-1,"model":-845961253,"plate":"BNS 339","windowsBroken":{"6":false,"5":true,"0":true,"7":true,"2":true,"1":true,"4":true,"3":true},"bodyHealth":994.0,"windowTint":-1,"modSideSkirt":-1,"modRightFender":-1,"color1":111,"neonEnabled":[false,false,false,false],"tyresCanBurst":false,"modShifterLeavers":-1,"modDial":-1,"modArchCover":-1,"modStruts":-1,"dirtLevel":11.0,"modDashboard":-1,"modBackWheels":-1,"modFrontWheels":-1,"engineHealth":936.8,"doorsBroken":{"2":false,"1":false,"0":false,"3":false},"modBrakes":-1,"modWindows":-1,"modAerials":-1,"extras":[],"tankHealth":998.6,"fuelLevel":73.5,"dashboardColor":0,"DisplayName":"The Liberator","modXenon":false,"modOrnaments":-1,"modTransmission":-1,"modRearBumper":-1,"modSmokeEnabled":1,"modRoofLivery":-1,"modVanityPlate":-1,"modHorns":-1,"wheelColor":156,"modCustomBackWheels":false}},{"owner":"char1:381e7ba5926bac97d5b585aac38ba38df2909d0d","vehicle":{"modTurbo":false,"modFrame":-1,"modSpoilers":-1,"tyreBurst":{"2":false,"1":false,"0":false,"3":false},"neonColor":[255,0,255],"modHood":-1,"modFrontBumper":-1,"modTrunk":-1,"modSteeringWheel":-1,"modTrimA":-1,"modSuspension":-1,"modTank":-1,"modDoorSpeaker":-1,"modEngine":-1,"DisplayName":"Guardian","modSeats":-1,"modCustomFrontWheels":false,"modExhaust":-1,"modRoof":-1,"wheels":0,"plateIndex":0,"modWindows":-1,"modXenon":false,"modGrille":-1,"doorsBroken":{"6":false,"5":false,"0":false,"2":false,"1":false,"4":false,"3":false},"color2":12,"modLightbar":-1,"modArmor":4,"modEngineBlock":-1,"modAPlate":-1,"modSpeakers":-1,"modPlateHolder":-1,"interiorColor":0,"modLivery":-1,"modFender":-1,"tyreSmokeColor":[255,255,255],"modAirFilter":-1,"model":-2107990196,"plate":"NVN 395","windowsBroken":{"6":false,"5":true,"0":false,"7":false,"2":false,"1":false,"4":true,"3":false},"bodyHealth":993.4,"windowTint":-1,"modOrnaments":-1,"modRightFender":-1,"color1":12,"neonEnabled":[false,false,false,false],"tankHealth":996.7,"modShifterLeavers":-1,"modTransmission":-1,"modArchCover":-1,"modStruts":-1,"dirtLevel":15.0,"modDial":-1,"modFrontWheels":-1,"engineHealth":947.9,"modVanityPlate":-1,"modDashboard":-1,"tyresCanBurst":1,"wheelColor":0,"modTrimB":-1,"pearlescentColor":13,"xenonColor":255,"dashboardColor":0,"modHorns":-1,"fuelLevel":97.1,"modAerials":-1,"extras":[],"modSmokeEnabled":1,"modRearBumper":-1,"modBrakes":2,"modRoofLivery":-1,"modSideSkirt":-1,"modBackWheels":-1,"modHydrolic":-1,"modCustomBackWheels":false}}]},"Owner":"char1:381e7ba5926bac97d5b585aac38ba38df2909d0d"},{"Interior":"mid-end","Name":"7316 West Mirror Drive","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":138.07872009277345,"y":-8.718344588487525e-7,"x":-11.68268966674804},"maxright":-20},"Locked":false,"Entrance":{"z":57.47,"y":-608.21,"x":886.77},"Price":40000,"Keys":[],"setName":"","furniture":[],"OwnerName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7316 West Mirror Drive","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-176.59844970703126,"y":-0.0,"x":-3.38041257858276},"maxright":-20},"Entrance":{"z":57.18,"y":-583.65,"x":861.7},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7312 West Mirror Drive","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":12.50613498687744,"y":-2.1754524937023245e-7,"x":-11.14390087127685},"maxright":-20},"Entrance":{"z":57.01,"y":-562.66,"x":844.12},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7314 West Mirror Drive","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":85.02394104003906,"y":-5.362977262279856e-8,"x":-5.74017667770385},"maxright":-20},"Entrance":{"z":56.95,"y":-532.68,"x":850.25},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7313 West Mirror Drive","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":46.49221038818359,"y":0.00000154111978,"x":-56.35844421386719},"maxright":-20},"Entrance":{"z":56.74,"y":-508.98,"x":861.51},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7313 West Mirror Drive","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":44.91729354858398,"y":-0.0,"x":-64.80823516845703},"maxright":-20},"Entrance":{"z":57.11000000000001,"y":-497.87,"x":878.44},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7311 West Mirror Drive","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":19.29909515380859,"y":-0.0,"x":-14.44456386566162},"maxright":-20},"Entrance":{"z":58.46,"y":-489.76,"x":906.46},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7311 West Mirror Drive","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-8.25692367553711,"y":-0.0,"x":-19.33738899230957},"maxright":-20},"Entrance":{"z":60.1,"y":-477.8,"x":921.78},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7309 West Mirror Drive","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-55.23443603515625,"y":-8.953837777880835e-7,"x":-17.53591918945312},"maxright":-20},"Entrance":{"z":60.57,"y":-463.17,"x":944.54},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7309 West Mirror Drive","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":33.32376480102539,"y":8.936335120779404e-7,"x":-17.17726707458496},"maxright":-20},"Entrance":{"z":61.81,"y":-451.65,"x":967.16},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7307 West Mirror Drive","plysinside":[],"Owned":true,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":34.80197525024414,"y":8.771137913754501e-7,"x":-13.24740600585937},"maxright":-20},"Locked":false,"Entrance":{"z":63.06000000000001,"y":-433.03,"x":987.57},"Price":40000,"Keys":[],"setName":"","furniture":[],"OwnerName":"Matteo Frecciaros","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":"char1:42113697d2a85837ecbc374266aab838a9e5f8fc"},{"Interior":"mid-end","Name":"7307 West Mirror Drive","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":123.14452362060549,"y":-4.402644719903037e-7,"x":-14.16050624847412},"maxright":-20},"Entrance":{"z":64.36999999999999,"y":-423.38,"x":1010.38},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7305 West Mirror Drive","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":41.270263671875,"y":-4.386202476780454e-7,"x":-13.28245830535888},"maxright":-20},"Entrance":{"z":65.36,"y":-408.36,"x":1028.78},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7305 Bridge St","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":54.3464241027832,"y":-8.817532943794504e-7,"x":-14.47243404388427},"maxright":-20},"Entrance":{"z":67.25,"y":-378.13,"x":1060.52},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7306 West Mirror Drive","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-144.52737426757813,"y":-9.047982416632294e-7,"x":-19.33366966247558},"maxright":-20},"Entrance":{"z":63.52,"y":-469.3,"x":1014.67},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7308 West Mirror Drive","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-107.44925689697266,"y":-0.0,"x":-8.07564449310302},"maxright":-20},"Entrance":{"z":61.16,"y":-502.36,"x":970.55},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7310 West Mirror Drive","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":119.70294189453124,"y":-0.0,"x":-61.49639892578125},"maxright":-20},"Entrance":{"z":59.64,"y":-518.94,"x":945.9},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7312 West Mirror Drive","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-151.43536376953126,"y":-0.0,"x":-17.66978073120117},"maxright":-20},"Entrance":{"z":58.81,"y":-526.05,"x":924.39},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7312 West Mirror Drive","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-69.45809936523438,"y":-9.24586970540986e-7,"x":-22.57007217407226},"maxright":-20},"Entrance":{"z":57.53,"y":-540.58,"x":893.24},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7312 Nikola Ave","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":28.74713134765625,"y":4.412431167111208e-7,"x":-14.65564727783203},"maxright":-20},"Entrance":{"z":57.39,"y":-569.56,"x":919.7},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7310 Nikola Ave","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":32.70853805541992,"y":-4.381802227726439e-7,"x":-13.03651428222656},"maxright":-20},"Entrance":{"z":58.75,"y":-541.93,"x":965.25},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7310 Nikola Ave","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":30.89822769165039,"y":9.366781910102872e-7,"x":-24.28778457641601},"maxright":-20},"Entrance":{"z":59.71,"y":-525.76,"x":987.84},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7308 Nikola Ave","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-65.29899597167969,"y":-0.0,"x":-16.08703422546386},"maxright":-20},"Entrance":{"z":60.01,"y":-510.98,"x":1006.48},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7306 Bridge St","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":144.13137817382813,"y":9.091479000744585e-7,"x":-20.10037040710449},"maxright":-20},"Entrance":{"z":63.1,"y":-498.06,"x":1046.29},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7306 Bridge St","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":76.4620132446289,"y":-0.0,"x":-11.98972129821777},"maxright":-20},"Entrance":{"z":63.32,"y":-470.58,"x":1051.04},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7306 Bridge St","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":162.82366943359376,"y":-0.0,"x":-8.78071689605712},"maxright":-20},"Entrance":{"z":65.28,"y":-448.93,"x":1056.24},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7344 Bridge St","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-102.9858856201172,"y":-4.4310172597761268e-7,"x":-15.54805183410644},"maxright":-20},"Entrance":{"z":64.67999999999999,"y":-484.37,"x":1090.46},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7346 Bridge St","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-15.62946987152099,"y":2.17306606487e-7,"x":-10.81984233856201},"maxright":-20},"Entrance":{"z":66.33999999999999,"y":-464.52,"x":1098.58},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7346 Bridge St","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":179.49822998046876,"y":-0.0,"x":-11.32894706726074},"maxright":-20},"Entrance":{"z":66.81,"y":-438.69,"x":1099.44},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7348 Bridge St","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-93.88529205322266,"y":-5.391471091797939e-8,"x":-8.21971225738525},"maxright":-20},"Entrance":{"z":66.58,"y":-411.33,"x":1101.06},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7348 West Mirror Drive","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-112.49811553955078,"y":-0.0,"x":-24.5419979095459},"maxright":-20},"Entrance":{"z":67.97,"y":-391.27,"x":1114.44},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7345 East Mirror Dr","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":112.37248229980468,"y":-2.144359569911103e-7,"x":-5.51487922668457},"maxright":-20},"Entrance":{"z":69.03999999999999,"y":-429.86,"x":1262.35},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7945 East Mirror Dr","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":94.98734283447266,"y":-0.0,"x":-14.86165904998779},"maxright":-20},"Entrance":{"z":69.53999999999999,"y":-458.06,"x":1265.74},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7343 East Mirror Dr","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":125.15465545654296,"y":-0.0,"x":-10.50986766815185},"maxright":-20},"Entrance":{"z":69.21,"y":-480.18,"x":1259.51},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7343 East Mirror Dr","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":63.34117126464844,"y":-0.0,"x":-16.31183433532715},"maxright":-20},"Entrance":{"z":68.92999999999999,"y":-494.1,"x":1251.53},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7343 East Mirror Dr","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":77.79755401611328,"y":-0.0,"x":-17.28229522705078},"maxright":-20},"Entrance":{"z":68.36999999999999,"y":-515.5,"x":1250.9},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7338 East Mirror Dr","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":130.3302459716797,"y":-0.0,"x":-22.43321228027343},"maxright":-20},"Entrance":{"z":68.67999999999999,"y":-566.43,"x":1241.53},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7338 East Mirror Dr","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":91.53223419189452,"y":-1.3435412782314417e-8,"x":-6.82489442825317},"maxright":-20},"Entrance":{"z":68.8,"y":-601.65,"x":1240.54},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7336 East Mirror Dr","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":27.77294158935547,"y":-2.145688853261163e-7,"x":-5.87094974517822},"maxright":-20},"Locked":false,"Entrance":{"z":68.58999999999999,"y":-620.99,"x":1250.79},"Price":40000,"Keys":[],"setName":"","furniture":[],"OwnerName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7333 East Mirror Dr","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-152.5341949462891,"y":-0.0,"x":-14.65502071380615},"maxright":-20},"Entrance":{"z":67.14,"y":-648.63,"x":1265.69},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7333 East Mirror Dr","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-171.3050994873047,"y":-0.0,"x":-12.5356969833374},"maxright":-20},"Entrance":{"z":65.05,"y":-683.53,"x":1270.98},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7328 East Mirror Dr","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":58.4541015625,"y":0.00000175048376,"x":-12.71638584136962},"maxright":-20},"Entrance":{"z":63.93,"y":-702.82,"x":1264.75},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7328 East Mirror Dr","plysinside":[],"Owned":true,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-88.46087646484375,"y":-0.0,"x":-11.09668827056884},"maxright":-20},"Locked":false,"Entrance":{"z":59.98,"y":-725.27,"x":1229.58},"Price":40000,"Keys":[],"setName":"","furniture":[],"OwnerName":"Andrew Garfield","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":"char1:2ff7cd91bf28802a638493f7a718dd66a187ba3d"},{"Interior":"mid-end","Name":"7328 Mirror Park Blvd","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-81.23240661621094,"y":-0.0,"x":-17.37718200683593},"maxright":-20},"Entrance":{"z":59.82,"y":-696.93,"x":1223.0},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7333 Mirror Park Blvd","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-164.38833618164066,"y":-0.0,"x":-11.55613040924072},"maxright":-20},"Entrance":{"z":62.71,"y":-669.31,"x":1221.46},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7336 Mirror Park Blvd","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-87.679443359375,"y":1.3399391818325056e-8,"x":-5.38702487945556},"maxright":-20},"Entrance":{"z":65.46,"y":-620.26,"x":1207.46},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7338 Mirror Park Blvd","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":3.90023970603942,"y":-0.0,"x":-16.58763313293457},"maxright":-20},"Entrance":{"z":67.08,"y":-598.41,"x":1203.75},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Storage":{"z":-99.19619750976564,"y":-1001.140380859375,"x":343.86859130859377},"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"mid-end","Name":"7338 Mirror Park Blvd","plysinside":[],"Owned":false,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":-37.31895446777344,"y":9.099596240957908e-7,"x":-20.23956108093261},"maxright":-20},"Entrance":{"z":68.16,"y":-575.59,"x":1201.01},"Locked":false,"Keys":[],"Price":40000,"furniture":[],"setName":"","positions":{"Storage":{"z":-99.19619750976564,"y":-1001.140380859375,"x":343.86859130859377},"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"garage":{"enabled":false,"StoredVehicles":[]},"Owner":""},{"Interior":"\tapa_v_mp_h_07_c","Name":"3572 Cox Way","plysinside":[],"Owned":true,"cctv":{"enabled":true,"maxleft":-54.732666015625,"rot":{"z":78.07438659667969,"y":4.912774329568492e-7,"x":-29.66522789001465},"maxright":79.87556457519531},"Locked":false,"Entrance":{"z":109.33,"y":432.38,"x":-305.19},"Price":2500000,"Keys":[],"setName":"Software House","furniture":[],"OwnerName":"Jhonson Dinozzo","positions":{"Storage":{"z":187.31309509277345,"y":326.7345886230469,"x":-796.018798828125},"Wardrobe":{"z":190.72,"y":328.71,"x":-798.21}},"garage":{"Heading":200.67819213867188,"pos":{"z":109.78602600097656,"y":431.7410278320313,"x":-321.0353698730469},"enabled":true,"StoredVehicles":[]},"Owner":"char1:4da15f5fc4b8fa838372ac19665a1a11b82c4c74"},{"Interior":"dell_perro_heights","Name":"87 Picture Perfect Drive","plysinside":[],"Owned":true,"cctv":{"enabled":false,"maxleft":80,"rot":{"z":22.56479454040527,"y":-9.300920282839796e-7,"x":-23.37250518798828},"maxright":-20},"Locked":false,"Entrance":{"z":106.78,"y":529.97,"x":-595.38},"Price":2500000,"Keys":[],"setName":"MATTEO FRECCIAROSSA","furniture":[],"OwnerName":"Matteo Frecciaros","positions":{"Storage":{"z":56.93730163574219,"y":-531.0081787109375,"x":-1457.0120849609376},"Wardrobe":{"z":50.73249816894531,"y":-536.9598999023438,"x":-1467.2574462890626}},"garage":{"Heading":21.3232307434082,"pos":{"z":108.02190399169922,"y":529.7655639648438,"x":-588.715087890625},"enabled":true,"StoredVehicles":[{"owner":"char1:42113697d2a85837ecbc374266aab838a9e5f8fc","vehicle":{"modTurbo":1,"modFrame":-1,"modSpoilers":-1,"tyreBurst":{"2":false,"1":false,"0":false,"3":false},"neonColor":[0,0,255],"modHood":-1,"modFrontBumper":-1,"modTrunk":-1,"modSteeringWheel":-1,"modTrimA":-1,"modSuspension":-1,"modTank":-1,"modDoorSpeaker":-1,"modEngine":-1,"xenonColor":1,"modSeats":-1,"modCustomFrontWheels":false,"modExhaust":-1,"modRoof":-1,"wheels":7,"plateIndex":0,"modWindows":-1,"modXenon":1,"modGrille":-1,"doorsBroken":{"2":false,"1":false,"0":false},"color2":70,"modLightbar":-1,"modArmor":-1,"modEngineBlock":-1,"modAPlate":-1,"modSpeakers":-1,"modPlateHolder":-1,"interiorColor":0,"modLivery":-1,"modFender":-1,"tyreSmokeColor":[0,0,255],"modAirFilter":-1,"model":-1403128555,"plate":"RNL 017","windowsBroken":{"6":false,"5":true,"0":false,"7":true,"2":true,"1":false,"4":true,"3":true},"bodyHealth":996.4,"windowTint":1,"modSideSkirt":-1,"modRightFender":-1,"color1":83,"neonEnabled":[1,1,1,1],"tankHealth":999.6,"modShifterLeavers":-1,"modBackWheels":-1,"modArchCover":-1,"modStruts":-1,"dirtLevel":13.6,"extras":[],"modFrontWheels":-1,"engineHealth":953.7,"modVanityPlate":-1,"modTrimB":-1,"tyresCanBurst":1,"modSmokeEnabled":1,"modTransmission":-1,"pearlescentColor":7,"modDashboard":-1,"fuelLevel":78.0,"dashboardColor":0,"modBrakes":2,"DisplayName":"Zentorno","modOrnaments":-1,"modAerials":-1,"modRearBumper":-1,"wheelColor":156,"modRoofLivery":-1,"modDial":-1,"modHorns":-1,"modHydrolic":-1,"modCustomBackWheels":false}},{"owner":"char1:42113697d2a85837ecbc374266aab838a9e5f8fc","vehicle":{"modTurbo":1,"modFrame":-1,"modSpoilers":-1,"tyreBurst":{"2":false,"1":false,"0":false,"3":false},"neonColor":[0,150,255],"modHood":-1,"modFrontBumper":-1,"modTrunk":-1,"modSteeringWheel":-1,"modTrimA":-1,"modSuspension":3,"modTank":-1,"modDoorSpeaker":-1,"modEngine":3,"xenonColor":2,"modSeats":-1,"modCustomFrontWheels":false,"modExhaust":-1,"modRoof":-1,"wheels":3,"plateIndex":0,"modWindows":-1,"modXenon":1,"modGrille":-1,"doorsBroken":{"6":false,"5":false,"0":false,"2":false,"1":false,"4":false,"3":false},"color2":83,"modLightbar":-1,"modArmor":4,"modEngineBlock":-1,"modAPlate":-1,"modSpeakers":-1,"modPlateHolder":-1,"interiorColor":8,"modLivery":-1,"modFender":-1,"tyreSmokeColor":[0,150,255],"modAirFilter":-1,"model":683047626,"plate":"YWL 584","windowsBroken":{"6":false,"5":true,"0":false,"7":false,"2":false,"1":false,"4":true,"3":false},"bodyHealth":985.5,"windowTint":1,"modSideSkirt":-1,"modRightFender":-1,"color1":83,"neonEnabled":[1,1,1,1],"tankHealth":998.5,"modShifterLeavers":-1,"modBackWheels":-1,"modArchCover":-1,"modStruts":-1,"dirtLevel":11.7,"extras":{"1":1},"modFrontWheels":-1,"engineHealth":854.7,"modVanityPlate":-1,"modTrimB":-1,"tyresCanBurst":1,"modSmokeEnabled":1,"modTransmission":2,"pearlescentColor":83,"modDashboard":-1,"fuelLevel":58.0,"dashboardColor":156,"modBrakes":2,"DisplayName":"Contender","modOrnaments":-1,"modAerials":-1,"modRearBumper":-1,"wheelColor":7,"modRoofLivery":-1,"modDial":-1,"modHorns":13,"modHydrolic":-1,"modCustomBackWheels":false}}]},"Owner":"char1:42113697d2a85837ecbc374266aab838a9e5f8fc"}] \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/wx_jail/LICENSE b/server-data/resources/[esx_addons]/esx_strippers/LICENSE similarity index 100% rename from server-data/resources/[esx_addons]/wx_jail/LICENSE rename to server-data/resources/[esx_addons]/esx_strippers/LICENSE diff --git a/server-data/resources/[esx_addons]/esx_strippers/README.md b/server-data/resources/[esx_addons]/esx_strippers/README.md new file mode 100644 index 000000000..161564281 --- /dev/null +++ b/server-data/resources/[esx_addons]/esx_strippers/README.md @@ -0,0 +1,10 @@ +# esx_strippers +Spawns a bunch of strippers in the Vanilla Unicorn on server start for maximum RPness. + +### Preview: + +![](https://i.gyazo.com/32b5510961be24360f41e9ba151841c3.jpg) + +### Installation + +Download the latest version, drag the folder from the zip file into your [ESX] folder and add `start esx_strippers` to your server.cfg file. \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/esx_strippers/client/client.lua b/server-data/resources/[esx_addons]/esx_strippers/client/client.lua new file mode 100644 index 000000000..e169ace39 --- /dev/null +++ b/server-data/resources/[esx_addons]/esx_strippers/client/client.lua @@ -0,0 +1,35 @@ +ESX = exports["es_extended"]:getSharedObject() +local foodPeds = { + { model="s_f_y_stripper_02", x=112.68, y=-1288.3, z=28.46, a=238.85, animation="mini@strip_club@private_dance@idle", animationName="priv_dance_idle"}, + { model="s_f_y_stripperlite", x=111.99, y=-1286.03, z=28.46, a=308.8, animation="mini@strip_club@lap_dance@ld_girl_a_song_a_p1", animationName="ld_girl_a_song_a_p1_f"}, +-- { model="s_f_y_stripperlite", x=112.312088, y=-1287.204346, z=28.454224, a=289.133850, animation="mini@strip_club@pole_dance@pole_dance1", animationName="pd_dance_01"}, + { model="csb_stripper_01", x=108.72, y=-1289.33, z=28.86, a=295.53, animation="mini@strip_club@private_dance@part2", animationName="priv_dance_p2"}, + { model="s_f_y_stripper_01", x=103.21, y=-1292.59, z=29.26, a=296.21, animation="mini@strip_club@private_dance@part1", animationName="priv_dance_p1"}, + { model="s_f_y_stripper_02", x=104.66, y=-1294.46, z=29.26, a=287.12, animation="mini@strip_club@lap_dance@ld_girl_a_song_a_p1", animationName="ld_girl_a_song_a_p1_f"}, + { model="a_f_y_topless_01", x=102.26, y=-1289.92, z=29.26, a=292.05, animation="mini@strip_club@private_dance@idle", animationName="priv_dance_idle"}, +} + +ESX.TriggerServerCallback('callbackname', function( spawned ) + if not spawned then + for k,v in ipairs(foodPeds) do + RequestModel(GetHashKey(v.model)) + while not HasModelLoaded(GetHashKey(v.model)) do + Wait(0) + end + RequestAnimDict(v.animation) + while not HasAnimDictLoaded(v.animation) do + Wait(1) + end + local storePed = CreatePed(4, GetHashKey(v.model), v.x, v.y, v.z, v.a, true, true) + SetBlockingOfNonTemporaryEvents(storePed, false) + SetPedFleeAttributes(storePed, 0, 0) + SetPedArmour(storePed, 100) + SetPedMaxHealth(storePed, 100) + SetPedDiesWhenInjured(storePed, false) + SetAmbientVoiceName(storePed, v.voice) + + TaskPlayAnim(storePed, v.animation, v.animationName, 8.0, 0.0, -1, 1, 0, 0, 0, 0) + SetModelAsNoLongerNeeded(GetHashKey(v.model)) + end + end +end) diff --git a/server-data/resources/[esx_addons]/esx_strippers/fxmanifest.lua b/server-data/resources/[esx_addons]/esx_strippers/fxmanifest.lua new file mode 100644 index 000000000..fd21b077c --- /dev/null +++ b/server-data/resources/[esx_addons]/esx_strippers/fxmanifest.lua @@ -0,0 +1,12 @@ +fx_version("cerulean") +game("gta5") + +version("1.0.2") + +client_scripts({ + "client/*.lua", +}) + +server_script({ + "server/*.lua", +}) diff --git a/server-data/resources/[esx_addons]/esx_strippers/server/server.lua b/server-data/resources/[esx_addons]/esx_strippers/server/server.lua new file mode 100644 index 000000000..d6c052ca4 --- /dev/null +++ b/server-data/resources/[esx_addons]/esx_strippers/server/server.lua @@ -0,0 +1,9 @@ +ESX = exports["es_extended"]:getSharedObject() +local spawned = false + +ESX.RegisterServerCallback('callbackname', function( source, cb ) + cb(spawned) + if not spawned then + spawned = true + end +end) \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/kc-unicorn/client/loop.lua b/server-data/resources/[esx_addons]/kc-unicorn/client/loop.lua new file mode 100644 index 000000000..e613526bf --- /dev/null +++ b/server-data/resources/[esx_addons]/kc-unicorn/client/loop.lua @@ -0,0 +1,80 @@ +-- Check player and his coords +Citizen.CreateThread(function() + while true do + + Cache.Player = PlayerPedId() + Cache.PlyCoo = GetEntityCoords(Cache.Player) + + + if Cache.inuniPoly then + Citizen.Wait(1) + else + Citizen.Wait(1500) + end + end +end) + +-- Get which seats are taken for lapdance +Citizen.CreateThread(function() + + while true do + + if Cache.inlapPoly then + Citizen.Wait(300) + TriggerServerEvent('kc-unicorn:GetPlayerSeated') + else + Citizen.Wait(1000) + end + end + +end) + +-- +Citizen.CreateThread(function() + + while true do + + while Cache.InLapdance do + DisableControlAction(2, 0, true) + DisableControlAction(2, 24, true) + DisableControlAction(2, 257, true) + DisableControlAction(2, 25, true) + DisableControlAction(2, 263, true) + DisableControlAction(2, 32, true) + DisableControlAction(2, 34, true) + DisableControlAction(2, 8, true) + DisableControlAction(2, 9, true) + Citizen.Wait(0) + end + + while Cache.InLean do + DisableControlAction(2, 0, true) + Citizen.Wait(0) + end + + Citizen.Wait(1000) + end +end) + +-- +Citizen.CreateThread(function() + + while true do + + while Cache.InLapdance do + Citizen.Wait(400) + if GetEntityHealth(Cache.SpawnPed) <= 0 or IsPedRagdoll(Cache.SpawnPed) or IsPedFleeing(Cache.SpawnPed) then + FreezeEntityPosition(Cache.SpawnPed, false) + DrawText2D(Loc('lapStopped')) + Cache.InLapdance = false + Cache.lapStop = true + Citizen.Wait(1000) + Cache.InCooldown = false + Citizen.Wait(10000) + DeleteEntity(Cache.SpawnPed) + end + end + + Citizen.Wait(1000) + end +end) \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/kc-unicorn/client/main.lua b/server-data/resources/[esx_addons]/kc-unicorn/client/main.lua new file mode 100644 index 000000000..86f1f2946 --- /dev/null +++ b/server-data/resources/[esx_addons]/kc-unicorn/client/main.lua @@ -0,0 +1,840 @@ +-- Import QBCore +local QBCore +if Config.Framework == "QBCore" then + QBCore = exports['qb-core']:GetCoreObject() +end + +Cache = {} + +local PlayerSitted = false +local PlayerSeated = 0 + +-- Server Seat Locales +local Seat1 = false +local Seat2 = false +local Seat3 = false +local Seat4 = false +local Seat5 = false +local Seat6 = false +local Seat7 = false + +-- Client Seat Locales +local Seat1Taken = false +local Seat2Taken = false +local Seat3Taken = false +local Seat4Taken = false +local Seat5Taken = false +local Seat6Taken = false +local Seat7Taken = false + +local SpawnObject, PreviousCamViewMode, factor, lines, PlayerMoney + +-- PolyZones +local uniPoly = BoxZone:Create(vector3(115.01, -1293.19, 28.27), 25.1, 32.0, { + name = "uniPoly", + heading = 8, + debugPoly = Config.DebugPolyzones, + minZ = 22.37, + maxZ = 31.17 +}) +local lapPoly = BoxZone:Create(vector3(117.06, -1294.85, 29.27), 1.24, 1.0, { + name = "lapPoly", + heading = 30, + debugPoly = Config.DebugPolyzones, + minZ = 28.17, + maxZ = 30.57 +}) +local leanPoly = BoxZone:Create(vector3(113.22, -1286.74, 28.46), 8.4, 6.4, { + name="leanPoly", + heading=30, + debugPoly = Config.DebugPolyzones, + minZ=27.26, + maxZ=28.66 +}) +local leanPoly1 = BoxZone:Create(vector3(114.31, -1289.97, 28.26), 0.2, 2.5, { + name="leanPoly1", + heading=30, + debugPoly = Config.DebugPolyzones, + minZ=27.26, + maxZ=28.66 +}) +local leanPoly2 = BoxZone:Create(vector3(114.75, -1285.88, 28.26), 4.2, 0.2, { + name="leanPoly2", + heading=30, + debugPoly = Config.DebugPolyzones, + minZ=27.26, + maxZ=28.66 +}) +local leanPoly3 = BoxZone:Create(vector3(110.98, -1284.24, 28.26), 0.2, 2.5, { + name="leanPoly3", + heading=30, + debugPoly = Config.DebugPolyzones, + minZ=27.26, + maxZ=28.66 +}) + +-- Create Blips +Citizen.CreateThread(function() + LoadDict("mini@strip_club@pole_dance@pole_dance1", false) + LoadDict("mini@strip_club@pole_dance@pole_dance2", false) + + if Config.Blip then + + local blip = AddBlipForCoord(Config.BlipCoord.x, Config.BlipCoord.y, Config.BlipCoord.z) + + SetBlipSprite (blip, Config.BlipStripclub.Sprite) + SetBlipDisplay(blip, Config.BlipStripclub.Display) + SetBlipScale (blip, Config.BlipStripclub.Scale) + SetBlipColour (blip, Config.BlipStripclub.Colour) + SetBlipAsShortRange(blip, true) + + BeginTextCommandSetBlipName("STRING") + AddTextComponentString(Config.BlipName) + EndTextCommandSetBlipName(blip) + end +end) + +RegisterNetEvent('kc-unicorn:showNotify') +AddEventHandler('kc-unicorn:showNotify', function(notify) + ShowAboveRadarMessage(notify) +end) + +function ShowAboveRadarMessage(message) + SetNotificationTextEntry("STRING") + AddTextComponentString(message) + DrawNotification(0,1) +end + +function DrawText3D(xyz, text) + + if Config.Text == 'Better3D' then + AdvancedDrawText3D(xyz, text) + return + elseif Config.Text == '2D' then + DrawText2D(text) + return + elseif Config.Text == 'None' then + return + elseif Config.Text ~= '3D' then + DrawText2D('Config.Text is not correctly configured.') + return + end + + SetTextScale(0.35, 0.35) + SetTextFont(4) + SetTextProportional(1) + SetTextColour(255, 255, 255, 215) + SetTextEntry("STRING") + SetTextCentre(true) + AddTextComponentString(text) + SetDrawOrigin(xyz, 0) + DrawText(0.0, 0.0) + DrawRect(0.0, 0.0115*lines, 0.017+ factor, 0.028*lines, 0, 0, 0, 75) + ClearDrawOrigin() + +end + +function AdvancedDrawText3D(xyz, text) + + AddTextEntry(GetCurrentResourceName(), text) + BeginTextCommandDisplayHelp(GetCurrentResourceName()) + EndTextCommandDisplayHelp(2, false, false, -1) + + SetFloatingHelpTextWorldPosition(1, xyz) + SetFloatingHelpTextStyle(1, 1, 2, -1, 3, 0) + +end + +function DrawText2D(text) + + AddTextEntry(GetCurrentResourceName(), text) + BeginTextCommandDisplayHelp(GetCurrentResourceName()) + EndTextCommandDisplayHelp(0, 0, true, -1) + +end + +Citizen.CreateThread(function() + + uniPoly:onPointInOut(PolyZone.getPlayerPosition, function(isPointInside, point) + Cache.inuniPoly = isPointInside + if isPointInside then + lapPoly:onPointInOut(PolyZone.getPlayerPosition, function(isPointInside, point) + Cache.inlapPoly = isPointInside + if isPointInside then + FactorAndLines(Loc('LapText', Config.LapDanceCost)) + end + TriggerServerEvent('kc-unicorn:GetPlayerSeated') + end) + leanPoly:onPointInOut(PolyZone.getPlayerPosition, function(isPointInside, point) + Cache.inleanPoly = isPointInside + if isPointInside then + FactorAndLines(Loc('Lean')) + end + end) + end + end, 2000) + +end) + +-- Better to not repeat this in DrawText3D, save a bit of perf ¯\_(ツ)_/¯ +function FactorAndLines(text) + + factor = (string.len(text)) / 370 + if Config.SelectStrippers and Cache.inlapPoly then + lines = 2 + else + lines = 1 + end + +end + +---- Lapdance + +Citizen.CreateThread(function() + + local InputLeft, InputRight + + local text = Loc('LapText', Config.LapDanceCost) + local CustomStrippers = 0 + local Stripper = 1 + + for _ in pairs(Config.Strippers) do CustomStrippers = CustomStrippers + 1 end + + if Config.Framework == "Standalone" then + text = Loc('StandaloneLapText') + end + + + if Config.Text == '3D' then + Input = '~r~E~w~ - ' + InputLeft = '\n~b~ {-- ~w~' + InputRight = '~b~ --} ~w~' + else + Input = '~INPUT_CONTEXT~ ' + InputLeft = '\n~INPUT_CELLPHONE_LEFT~ ' + InputRight = ' ~INPUT_CELLPHONE_RIGHT~' + end + + while true do + + if Config.SelectStrippers then + if Stripper > CustomStrippers then + Stripper = 1 + elseif Stripper <= 0 then + Stripper = CustomStrippers + end + DrawnText = Input .. text .. InputLeft .. Config.Strippers[Stripper].Name .. InputRight + else + DrawnText = Input .. text + end + + if not Cache.inuniPoly then + Citizen.Wait(800) + else + Citizen.Wait(0) + end + + if Cache.inuniPoly and not Cache.InCooldown and not Cache.InLean then + if Config.LapMarker then + DrawMarker(2, 117.04, -1294.8, 29.0, 0.0, 0.0, 0.0, 0, 0.0, 0.0, 0.25, 0.2, 0.1, 255, 255, 255, 100, false, true, 2, false, false, false, false) + end + if Cache.inlapPoly then + DrawText3D(vector3(117.04, -1294.8, 29.25), DrawnText) + if IsControlJustPressed(0, 38) then + if PlayerSeated >= 7 then + QBCore.Functions.Notify(Loc('AllPlacesTaken'), "error") + else + TriggerServerEvent('kc-unicorn:buy', Stripper) + Citizen.Wait(500) + end + elseif IsControlJustPressed(0, 174) then + Stripper = Stripper - 1 + elseif IsControlJustPressed(0, 175) then + Stripper = Stripper + 1 + end + end + end + end +end) + +function PlayerFunc(xyz, heading, camheading, xyz2, wait) + Dict = LoadDict("mini@strip_club@lap_dance_2g@ld_2g_reach", true) + Anim = "ld_2g_sit_idle" + + PlayerSitted = true + + SetFollowPedCamViewMode(4) + + SetEntityCoords(Cache.Player, xyz) + FreezeEntityPosition(Cache.Player, true) + SetEntityHeading(Cache.Player, heading) + TaskPlayAnim(Cache.Player, Dict, Anim, 8.0, -8.0, -1, 0, 0, false, false, false) + SetGameplayCamRelativeHeading(camheading) + + while Cache.InLapdance do + if GetEntityAnimCurrentTime(Cache.Player, Dict, Anim) >= 0.97 and GetEntityAnimCurrentTime(Cache.Player, Dict, Anim) < 1.0 then + TaskPlayAnim(Cache.Player, Dict, Anim, 8.0, -8.0, -1, 0, 0, false, false, false) + end + Citizen.Wait(50) + end + + FreezeEntityPosition(Cache.Player, false) + PlayerSitted = false + + if not Cache.lapStop then + SetEntityCoords(Cache.Player, xyz2) + Citizen.Wait(200) + SetFollowPedCamViewMode(PreviousCamViewMode) + TaskGoToCoordAnyMeans(Cache.Player, 117.48, -1294.82, 28.43, 1.0, 0, 0, 786603, 1.0) + Citizen.Wait(wait) + end + +end + +function StripperFunc(ped, heading, heading2, heading3, xyz, xyz2, xyz3, xyz4, wait) + + SetEntityHeading(ped, heading) + FreezeEntityPosition(ped, true) + TaskGoToCoordAnyMeans(Cache.Player, xyz, 1.0, 0, 0, 786603, 1.0) + Citizen.Wait(wait) + + Repeatcount = -13 + Repeatcount2 = 0 + + repeat + Citizen.Wait(200) + Repeatcount = Repeatcount + 1 + Repeatcount2 = Repeatcount2 + 1 + if Repeatcount == 17 then + TaskPlayAnim(Cache.SpawnPed, LoadDict("mini@strip_club@idles@stripper", true), "stripper_idle_02", 8.0, -8.0, -1, 0, 0, false, false, false) + Repeatcount = 0 + end + if Repeatcount2 >= 160 then + DeleteEntity(Cache.SpawnPed) + end + until PlayerSitted or Cache.lapStop + + FreezeEntityPosition(ped, false) + TaskGoToCoordAnyMeans(ped, xyz2, 174.93, 0, 0, 0, 0xbf800000) + Citizen.Wait(1000) + + TaskGoToCoordAnyMeans(ped, xyz3, 174.93, 0, 0, 0, 0xbf800000) + Citizen.Wait(2100) + + FreezeEntityPosition(ped, true) + SetEntityHeading(ped, heading2) + + StripperAnim() + + if not lapStop then + TaskGoToCoordAnyMeans(ped, xyz4, 174.93, 0, 0, 0, 0xbf800000) + Wait(2000) + + TaskPlayAnim(ped, LoadDict("mini@strip_club@idles@stripper", true), "stripper_idle_02", 8.0, -8.0, -1, 0, 0, false, false, false) + SetEntityHeading(ped, heading3) + end + + Cache.InLapdance = false + + Citizen.Wait(10000) + +end + +function StripperAnim() + + FreezeEntityPosition(Cache.SpawnPed, false) + + if not Cache.lapStop then + TaskPlayAnim(Cache.SpawnPed, LoadDict("mini@strip_club@private_dance@part1", true), "priv_dance_p1", 8.0, -8.0, -1, 0, 0, false, false, false) + Wait(22300) + end + + if not Cache.lapStop then + TaskPlayAnim(Cache.SpawnPed, LoadDict("mini@strip_club@private_dance@part2", true), "priv_dance_p2", 8.0, -8.0, -1, 0, 0, false, false, false) + Wait(31200) + end + + if not Cache.lapStop then + TaskPlayAnim(Cache.SpawnPed, LoadDict("mini@strip_club@private_dance@exit", true), "priv_dance_exit", 8.0, -8.0, -1, 0, 0, false, false, false) + Wait(8000) + end +end + +function LoadDict(Dict, Bool) + RequestAnimDict(Dict) + while not HasAnimDictLoaded(Dict) do + Wait(20) + RequestAnimDict(Dict) + end + + if Bool then + return Dict + end +end + +RegisterNetEvent('kc-unicorn:lapdance') +AddEventHandler('kc-unicorn:lapdance', function(PlayerMoney, PlayerBirthdate, TodayDate, Stripper) + Birthdate = {} + Date = {} + Cache.lapStop = false + Cache.InLapdance = true + PreviousCamViewMode = GetFollowPedCamViewMode(Cache.Player) + + if Config.Framework == 'QBCore' then + index = 1 + for value in string.gmatch(PlayerBirthdate, "[^-]+") do + Birthdate[index] = value + index = index + 1 + end + + index = 1 + for value in string.gmatch(TodayDate, "[^-]+") do + Date[index] = value + index = index + 1 + end + + Year = Date[1] - Birthdate[1] - 4 + Month = Date[2] - Birthdate[2] + Day = Date[3] - Birthdate[3] + else + Year = 1000 + Month = 0 + Day = 0 + end + + RequestModel(Config.Strippers[Stripper].Model) + Cache.InCooldown = true + while not HasModelLoaded(1846523796) do + Wait(20) + end + Citizen.Wait(200) + + SetEntityHeading(Cache.Player, 120.18) + + if not Seat1 and not Seat1Taken then + Seat1Taken = true + TriggerServerEvent('kc-unicorn:Seat1') + elseif not Seat2 and not Seat2Taken then + Seat2Taken = true + TriggerServerEvent('kc-unicorn:Seat2') + elseif not Seat3 and not Seat3Taken then + Seat3Taken = true + TriggerServerEvent('kc-unicorn:Seat3') + elseif not Seat4 and not Seat4Taken then + Seat4Taken = true + TriggerServerEvent('kc-unicorn:Seat4') + elseif not Seat5 and not Seat5Taken then + Seat5Taken = true + TriggerServerEvent('kc-unicorn:Seat5') + elseif not Seat6 and not Seat6Taken then + Seat6Taken = true + TriggerServerEvent('kc-unicorn:Seat6') + elseif not Seat7 and not Seat7Taken then + Seat7Taken = true + TriggerServerEvent('kc-unicorn:Seat7') + end + + if Seat1Taken then + SpawnObject = CreatePed(4, 1846523796, 117.1, -1301.25, 28.05, 303.19, true, false) + elseif Seat2Taken then + SpawnObject = CreatePed(4, 1846523796, 114.98, -1302.43, 28.05, 303.19, true, false) + elseif Seat3Taken then + SpawnObject = CreatePed(4, 1846523796, 112.89, -1303.69, 28.05, 303.19, true, false) + elseif Seat4Taken then + SpawnObject = CreatePed(4, 1846523796, 111.32, -1304.58, 28.05, 303.19, true, false) + elseif Seat5Taken then + SpawnObject = CreatePed(4, 1846523796, 110.89, -1303.84, 28.05, 303.19, true, false) + elseif Seat6Taken then + SpawnObject = CreatePed(4, 1846523796, 112.42, -1303.04, 28.05, 303.19, true, false) + elseif Seat7Taken then + SpawnObject = CreatePed(4, 1846523796, 114.29, -1301.91, 28.05, 303.19, true, false) + end + + Citizen.Wait(100) + Cache.SpawnPed = SpawnObject + FreezeEntityPosition(Cache.SpawnPed, true) + + SetPedComponentVariation(Cache.SpawnPed, 1, Config.Strippers[Stripper].Drawables.Head) -- Head + SetPedComponentVariation(Cache.SpawnPed, 2, Config.Strippers[Stripper].Drawables.Hair, Config.Strippers[Stripper].Textures.HairTex) -- Hair + SetPedComponentVariation(Cache.SpawnPed, 3, Config.Strippers[Stripper].Drawables.Torso) -- Torso + SetPedComponentVariation(Cache.SpawnPed, 4, Config.Strippers[Stripper].Drawables.Shoes, Config.Strippers[Stripper].Textures.ShoesTex) -- Shoes + SetPedComponentVariation(Cache.SpawnPed, 6, Config.Strippers[Stripper].Drawables.Underwear, Config.Strippers[Stripper].Textures.UnderwearTex) -- Underwear + SetPedComponentVariation(Cache.SpawnPed, 10, 0) -- Decals ?? + SetPedComponentVariation(Cache.SpawnPed, 11, Config.Strippers[Stripper].Drawables.Torso2) -- Auxiliary parts for torso + + if Year >= Config.NudityAge + 1 then + underage = false + elseif Year == Config.NudityAge then + if Month == 0 and Day <= 0 then + underage = false + else + underage = true + end + else + underage = true + end + + if underage then + SetPedComponentVariation(Cache.SpawnPed, 8, 0, Config.Strippers[Stripper].Textures.BraTex) -- Not Topless + elseif not underage and not Config.Nudity then + SetPedComponentVariation(Cache.SpawnPed, 8, 0, Config.Strippers[Stripper].Textures.BraTex) -- Not Topless + else + SetPedComponentVariation(Cache.SpawnPed, 8, 1) -- Topless + end + print("Shoes | Drawable: " .. GetPedDrawableVariation(Cache.SpawnPed, 4), "Drawables: " .. GetNumberOfPedDrawableVariations(Cache.SpawnPed, 4), "Texture: " .. GetPedTextureVariation(Cache.SpawnPed, 4), "Textures: " .. GetNumberOfPedTextureVariations(Cache.SpawnPed, 4), "Palette: " .. GetPedPaletteVariation(Cache.SpawnPed, 4)) + print("Underwear | Drawable: " .. GetPedDrawableVariation(Cache.SpawnPed, 6), "Drawables: " .. GetNumberOfPedDrawableVariations(Cache.SpawnPed, 6), "Texture: " .. GetPedTextureVariation(Cache.SpawnPed, 6), "Textures: " .. GetNumberOfPedTextureVariations(Cache.SpawnPed, 6), "Palette: " .. GetPedPaletteVariation(Cache.SpawnPed, 6)) + print("Underwear?? | Drawable: " .. GetPedDrawableVariation(Cache.SpawnPed, 10), "Drawables: " .. GetNumberOfPedDrawableVariations(Cache.SpawnPed, 10), "Texture: " .. GetPedTextureVariation(Cache.SpawnPed, 10), "Textures: " .. GetNumberOfPedTextureVariations(Cache.SpawnPed, 10), "Palette: " .. GetPedPaletteVariation(Cache.SpawnPed, 10)) + + if Config.Framework ~= 'Standalone' then + if PlayerMoney >= Config.LegMoney then + SetPedComponentVariation(Cache.SpawnPed, 9, 1) + else + SetPedComponentVariation(Cache.SpawnPed, 9, 0) + end + end + + if Config.Debug then + print("------ kc-unicorn : Debug print start ------") + print("Player cash: ", PlayerMoney, "Player birthdate: ", PlayerBirthdate, "Today date: ", TodayDate) + print('Config.Language is set to: ' .. Config.Language) + print('Framework used: ' .. Config.Framework) + print(Config.Language .. "", lg) + --[[ print("Birthdate split: ", Birthdate[1], Birthdate[2], Birthdate[3]) + print("Date split: ", Date[1], Date[2], Date[3]) ]] + print("Year: ", Year, "Month: ", Month, "Day: ", Day) + print(GetPedDrawableVariation(Cache.SpawnPed, 1), "Head") + print(GetPedDrawableVariation(Cache.SpawnPed, 2), "Beard") + print(GetPedDrawableVariation(Cache.SpawnPed, 3), "Torso") + print(GetPedDrawableVariation(Cache.SpawnPed, 4), "Legs") + print(GetPedDrawableVariation(Cache.SpawnPed, 5), "Hands") + print(GetPedDrawableVariation(Cache.SpawnPed, 6), "Foot") + print(GetPedDrawableVariation(Cache.SpawnPed, 7), "???") + print(GetPedDrawableVariation(Cache.SpawnPed, 8), "Accessories 1") + print(GetPedDrawableVariation(Cache.SpawnPed, 9), "Accessories 2") + print(GetPedDrawableVariation(Cache.SpawnPed, 10), "Decals") + print(GetPedDrawableVariation(Cache.SpawnPed, 11), "Auxiliary parts for torso") + print('') + if Config.NudityAge ~= 0 then + if underage then + print("Player is underage") + else + print("Player isn't underage") + end + end + if not Config.Nudity and underage then + print("Stripper isn't topless") + elseif not Config.Nudity and not underage then + print("Stripper isn't topless") + else + print("Stripper is topless") + end + print("Player in lap dance: " .. PlayerSeated) + print("------ kc-unicorn : Debug print end ------") + end + + TaskPlayAnim(Cache.SpawnPed, LoadDict("mini@strip_club@idles@stripper", true), "stripper_idle_02", 8.0, -8.0, -1, 0, 0, false, false, false) + + SetEntityCoords(Cache.Player, 116.88, -1295.04, 28.42) + + ------------------- First seat ---------------------- + if Seat1Taken then + StripperFunc(Cache.SpawnPed, 303.19, 216.6, 308.6, vector3(118.71, -1301.93, 28.42), vector3(118.0, -1300.3, 28.17), vector3(118.74, -1301.91, 29.27), vector3(117.1, -1301.25, 28.05), 4900) + + Seat1Taken = false + TriggerServerEvent('kc-unicorn:RemoveSeat1') + + ------------------- Second seat ---------------------- + elseif Seat2Taken then + StripperFunc(Cache.SpawnPed, 303.19, 216.6, 308.6, vector3(116.67, -1303.35, 29.27), vector3(115.85, -1302.02, 29.02), vector3(116.25, -1302.85, 29.27), vector3(114.98, -1302.43, 28.05), 6000) + + Seat2Taken = false + TriggerServerEvent('kc-unicorn:RemoveSeat2') + + ------------------- Third seat ---------------------- + elseif Seat3Taken then + StripperFunc(Cache.SpawnPed, 303.19, 216.6, 308.6, vector3(114.64, -1304.54, 29.27), vector3(113.56, -1302.94, 29.02), vector3(114.33, -1304.23, 29.27), vector3(112.89, -1303.69, 28.05), 7000) + Seat3Taken = false + TriggerServerEvent('kc-unicorn:RemoveSeat3') + + ------------------- Fourth seat ---------------------- + elseif Seat4Taken then + StripperFunc(Cache.SpawnPed, 303.19, 216.6, 308.6, vector3(112.66, -1305.52, 29.27), vector3(111.82, -1304.17, 29.02), vector3(112.42, -1305.11, 29.27), vector3(111.32, -1304.58, 28.05), 8000) + Seat4Taken = false + TriggerServerEvent('kc-unicorn:RemoveSeat4') + + ------------------- Fifth seat ---------------------- + elseif Seat5Taken then + StripperFunc(Cache.SpawnPed, 303.19, 29.5, 308.6, vector3(111.18, -1302.62, 29.27), vector3(111.46, -1303.61, 29.02), vector3(111.17, -1302.81, 29.27), vector3(110.89, -1303.84, 28.05), 8000) + Seat5Taken = false + TriggerServerEvent('kc-unicorn:RemoveSeat5') + + ------------------- Sixth seat ---------------------- + elseif Seat6Taken then + StripperFunc(Cache.SpawnPed, 303.19, 29.5, 308.6, vector3(112.64, -1301.27, 29.27), vector3(113.35, -1302.36, 29.02), vector3(112.91, -1301.62, 29.27), vector3(112.42, -1303.04, 28.05), 7000) + Seat6Taken = false + TriggerServerEvent('kc-unicorn:RemoveSeat6') + + ------------------- Seventh seat ---------------------- + elseif Seat7Taken then + StripperFunc(Cache.SpawnPed, 303.19, 29.5, 308.6, vector3(114.73, -1300.41, 29.27), vector3(115.08, -1301.39, 29.02), vector3(114.88, -1300.64, 29.27), vector3(114.29, -1301.91, 28.05), 7000) + Seat7Taken = false + TriggerServerEvent('kc-unicorn:RemoveSeat7') + end + + TriggerServerEvent('kc-unicorn:idle') + Citizen.Wait(1000) + Cache.InCooldown = false + + if not Cache.lapStop then + DeleteEntity(Cache.SpawnPed) + end +end) + +Citizen.CreateThread(function() + + while true do + + if not Cache.InLapdance then + Citizen.Wait(1000) + else + Citizen.Wait(0) + end + + if Seat1Taken then + local sitdist = #(Cache.PlyCoo - vector3(118.75, -1301.97, 28.42)) + if sitdist < 1 and Cache.InLapdance then + PlayerFunc(vector3(119.06, -1302.66, 27.78), 40.0, -10.0, vector3(118.75, -1301.99, 28.42), 5000) + end + elseif Seat2Taken then + local sitdist = #(Cache.PlyCoo - vector3(116.59, -1303.01, 28.42)) + if sitdist < 1 and Cache.InLapdance then + PlayerFunc(vector3(116.9, -1303.75, 27.78), 40.0, -10.0, vector3(116.59, -1303.01, 28.42), 6200) + end + elseif Seat3Taken then + local sitdist = #(Cache.PlyCoo - vector3(114.64, -1304.54, 29.27)) + if sitdist < 1 and Cache.InLapdance then + PlayerFunc(vector3(114.86, -1305.0, 27.78), 40.0, -10.0, vector3(114.64, -1304.54, 29.27), 6800) + end + elseif Seat4Taken then + local sitdist = #(Cache.PlyCoo - vector3(112.66, -1305.52, 29.27)) + if sitdist < 1 and Cache.InLapdance then + PlayerFunc(vector3(113.08, -1306.17, 27.78), 40.0, -10.0, vector3(112.66, -1305.52, 29.27), 7500) + end + elseif Seat5Taken then + local sitdist = #(Cache.PlyCoo - vector3(111.18, -1302.62, 29.27)) + if sitdist < 1 and Cache.InLapdance then + PlayerFunc(vector3(110.57, -1301.82, 27.78), 216.6, -10.0, vector3(111.18, -1302.62, 29.27), 7500) + end + elseif Seat6Taken then + local sitdist = #(Cache.PlyCoo - vector3(112.64, -1301.27, 29.27)) + if sitdist < 1 and Cache.InLapdance then + PlayerFunc(vector3(112.34, -1300.76, 27.78), 216.6, -10.0, vector3(112.64, -1301.27, 29.27), 6800) + end + elseif Seat7Taken then + local sitdist = #(Cache.PlyCoo - vector3(114.73, -1300.41, 29.27)) + if sitdist < 1 and Cache.InLapdance then + PlayerFunc(vector3(114.27, -1299.66, 27.78), 216.6, -10.0, vector3(114.73, -1300.41, 29.27), 6000) + end + end + + DisableControlAction(2, 0, false) + end +end) + +RegisterNetEvent("kc-unicorn:SetPlayerSeated") +AddEventHandler("kc-unicorn:SetPlayerSeated", function(GetPlayerSeated, Seat1Busy, Seat2Busy, Seat3Busy, Seat4Busy, Seat5Busy, Seat6Busy, Seat7Busy) + Seat1 = Seat1Busy + Seat2 = Seat2Busy + Seat3 = Seat3Busy + Seat4 = Seat4Busy + Seat5 = Seat5Busy + Seat6 = Seat6Busy + Seat7 = Seat7Busy + PlayerSeated = GetPlayerSeated +end) + +-------- + +---- Lean + +Citizen.CreateThread(function() + + if Config.Text == '3D' then + Input = '~r~E~w~ - ' + else + Input = '~INPUT_CONTEXT~ ' + end + + while true do + if Cache.inleanPoly then + if leanPoly1:isPointInside(Cache.PlyCoo) then + Citizen.Wait(1) + DrawText3D(Cache.PlyCoo, Input .. Loc('Lean')) + if IsControlJustReleased(0, 51) then + LeanStart(29.0) + end + elseif leanPoly2:isPointInside(Cache.PlyCoo) then + Citizen.Wait(1) + DrawText3D(Cache.PlyCoo, Input .. Loc('Lean')) + if IsControlJustReleased(0, 51) then + LeanStart(116.2) + end + elseif leanPoly3:isPointInside(Cache.PlyCoo) then + Citizen.Wait(1) + DrawText3D(Cache.PlyCoo, Input .. Loc('Lean')) + if IsControlJustReleased(0, 51) then + LeanStart(210.0) + end + else + Citizen.Wait(200) + end + else + Citizen.Wait(200) + end + end +end) + +function LoadModel(model) + model = GetHashKey(model) + + if not HasModelLoaded(model) and IsModelInCdimage(model) then + RequestModel(model) + while not HasModelLoaded(model) do + Wait(20) + end + end +end + +function LeanStart(PlyHeading) + + local text = Loc('LeanNotice', Config.ThrowCost) + LoadModel("prop_anim_cash_note_b") + local is_female = function() + return(GetEntityModel(Cache.Player) == "mp_f_freemode_01" and "_female" or "") + end + + if Config.Framework == "Standalone" then + text = Loc('StandaloneLeanNotice') + end + + SetEntityCoordsNoOffset(Cache.Player, Cache.PlyCoo) + FreezeEntityPosition(Cache.Player, true) + SetEntityHeading(Cache.Player, PlyHeading) + Cache.InLean = true + Wait(50) + + TaskPlayAnim(Cache.Player, LoadDict("mini@strip_club@leaning@enter", true), "enter" .. is_female(), 8.0, -8.0, -1, 0, 0, false, false, false) + Wait(2750) + PreviousCamViewMode = GetFollowPedCamViewMode(Cache.Player) + SetFollowPedCamViewMode(4) + SetGameplayCamRelativeHeading(0.0) + TaskPlayAnim(Cache.Player, LoadDict("mini@strip_club@leaning@base", true), "base" .. is_female(), 8.0, -8.0, -1, 1, 0, false, false, false) + + while true do + Wait(0) + + DrawText2D(text) + if IsControlJustReleased(0, 22) then + if Config.Framework == "QBCore" then + PlayerData = QBCore.Functions.GetPlayerData() + PlayerMoney = PlayerData.money["cash"] + elseif Config.Framework == "ESX" then + TriggerServerEvent('kc-unicorn:esxmoney') + elseif Config.Framework == "Standalone" then + PlayerMoney = Config.ThrowCost + 1 + end + repeat + Citizen.Wait(100) + until PlayerMoney ~= nil + if Config.Debug then + print("Player money: " .. PlayerMoney, "Throw cost: " .. Config.ThrowCost) + end + if PlayerMoney >= Config.ThrowCost then + + TriggerServerEvent('kc-unicorn:leanthrow') + local pos = GetPedBoneCoords(Cache.Player, 28422, 0.0, 0.0, 0.0) + local cash = CreateObject("prop_anim_cash_note_b", pos, true, false, false) + SetEntityAlpha(cash, 0, false) + AttachEntityToEntity(cash, Cache.Player, GetPedBoneIndex(Cache.Player, 28422), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false, false, false, false, 2, true) + TaskPlayAnim(Cache.Player, LoadDict("mini@strip_club@leaning@toss", true), "toss" .. is_female(), 8.0, -8.0, -1, 2, 0, false, false, false) + Citizen.Wait(150) + SetEntityAlpha(cash, 255, false) + while true do + if GetEntityAnimCurrentTime(Cache.Player, "mini@strip_club@leaning@toss", "toss" .. is_female()) >= 0.74 then + local alphaLevel = 255 + repeat + SetEntityAlpha(cash, alphaLevel, false) + alphaLevel = alphaLevel - 51 + Citizen.Wait(40) + until(alphaLevel == 0) + DeleteEntity(cash) + break + end + Citizen.Wait(50) + end + TaskPlayAnim(Cache.Player, LoadDict("mini@strip_club@leaning@base", true), "base" .. is_female(), 8.0, -8.0, -1, 1, 0, false, false, false) + + else + if Config.Framework == 'QBCore' then + QBCore.Functions.Notify(Loc('NotEnoughCashLean'), "error") + else + TriggerEvent('kc-unicorn:showNotify', Loc('NotEnoughCashLean')) + end + end + elseif IsControlJustReleased(0, 51) then + break + end + end + + Cache.InLean = false + SetFollowPedCamViewMode(PreviousCamViewMode) + Wait(100) + TaskPlayAnim(Cache.Player, LoadDict("mini@strip_club@leaning@exit", true), "exit" .. is_female(), 8.0, -8.0, -1, 0, 0, false, false, false) + FreezeEntityPosition(Cache.Player, false) +end + +-------- + +---- ???? + + +RegisterNetEvent('kc-unicorn:poledancescene') +AddEventHandler('kc-unicorn:poledancescene', function(ped, number) + PedPole = NetworkGetEntityFromNetworkId(ped) + local scene = NetworkCreateSynchronisedScene(vector3(112.65, -1286.74, 28.5), vector3(0.0, 0.0, 0.0), 2, false, false, 1065353216, 0, 1.3) + NetworkAddPedToSynchronisedScene(PedPole, scene, 'mini@strip_club@pole_dance@pole_dance'..number, 'pd_dance_0'..number, 1.5, -4.0, 1, 1, 1148846080, 0) + NetworkStartSynchronisedScene(scene) + Citizen.Wait(30000) + TriggerServerEvent('kc-unicorn:poledancestop') +end) + + +-------- + +---- Debug + +Citizen.CreateThread(function() + local Count = 0 + while true do + + Citizen.Wait(Config.MoreDebugRefreshTime*1000) + if Cache.inuniPoly and Config.MoreDebug then + print("In Unicorn PolyZone: " .. tostring(Cache.inuniPoly), "In Lapdance PolyZone: " .. tostring(Cache.inlapPoly), "In Lean PolyZone: " .. tostring(Cache.inleanPoly) .. "\n") + print("Server: ", "Seat1: " .. tostring(Seat1), "Seat2: " .. tostring(Seat2), "Seat3: " .. tostring(Seat3), "Seat4: " .. tostring(Seat4), "Seat5: " .. tostring(Seat5), "Seat6: " .. tostring(Seat6), "Seat7: " .. tostring(Seat7), PlayerSeated) + print("Client: ", "Seat1: " .. tostring(Seat1Taken), "Seat2: " .. tostring(Seat2Taken), "Seat3: " .. tostring(Seat3Taken), "Seat4: " .. tostring(Seat4Taken), "Seat5: " .. tostring(Seat5Taken), "Seat6: " .. tostring(Seat6Taken), "Seat7: " .. tostring(Seat7Taken), PlayerSeated) + elseif Config.MoreDebug then + if Count == 0 then + print("In Unicorn PolyZone: " .. tostring(Cache.inuniPoly)) + Count = Count + 1 + end + end + end + +end) + +-------- + +RegisterNetEvent('kc-unicorn:esxplayermoney') +AddEventHandler('kc-unicorn:esxplayermoney', function(money) + PlayerMoney = money +end) diff --git a/server-data/resources/[esx_addons]/kc-unicorn/config.lua b/server-data/resources/[esx_addons]/kc-unicorn/config.lua new file mode 100644 index 000000000..b629c7515 --- /dev/null +++ b/server-data/resources/[esx_addons]/kc-unicorn/config.lua @@ -0,0 +1,78 @@ +Config = Config or {} + +-- Framework +Config.Framework = 'Standalone' -- Choose which framework your server is using so kc-unicorn can work with it [ESX, QBCore, Standalone] +-- Since there is too many esx version, only this one is supported (https://github.com/esx-framework/es_extended/tree/v1-final). Maybe I will test other version, but not a priority + +-- Lap dance +Config.LapDanceCost = 120 -- Cost of the lap dance +Config.ThrowCost = 40 -- Cost of the lap dance +Config.LegMoney = 10000 -- Little easter egg for your player, will add an accessory "Leg money" to the stripper if the player has more than 'Config.LegMoney' in cash. Set to a huge value if you don't want this +Config.Nudity = false -- Set to true if you want the stripper to be topless (Only for player above 'Config.NudityAge') +Config.NudityAge = 21 -- (QBCore only!!) Player age restriction. If underage and 'Config.Nudity' is set to true, the stripper won't be topless. Set to 0 if you don't want age restriction + +-- Text/Blip/Marker +Config.Text = 'Better3D' -- 2D, 3D, Better3D, None (Set the one you like to suit your server, the performance difference is mostly non-existant) +Config.Blip = true -- Set to false if you don't want the blip on the map +Config.BlipName = "Strip-Club" -- Blip name +Config.BlipStripclub = { + Sprite = 121, -- Sprite list can be found here: https://docs.fivem.net/docs/game-references/blips/ + Colour = 50, -- Color list in the same URL as above + Display = 27, -- Explanation here: https://docs.fivem.net/natives/?_0x9029B2F3DA924928 + Scale = 0.7 -- Self explanatory, let you set the scale of the blip +} +Config.BlipCoord = { x=128.87, y=-1298.93, z=4.0 } -- If you want to move the blip on the map, change this. +Config.LapMarker = true -- Set to true if you want the marker for the lapdance to be "drawn" + +-- Strippers +Config.SelectStrippers = true -- Set to true if you want to let your player choose which stripper they want for their lapdance +Config.Strippers = { + [1] = { + Name = "Eisha", + Model = 1846523796, + Drawables = { Head = 0, Hair = 0, Torso = 0, Torso2 = 0, Shoes = 1, Underwear = 0 }, + Textures = { HairTex = 0, BraTex = 2, ShoesTex = 2, UnderwearTex = 0 } + }, + [2] = { + Name = "Jade", + Model = 1846523796, + Drawables = { Head = 0, Hair = 2, Torso = 0, Torso2 = 1, Shoes = 1, Underwear = 0 }, + Textures = { HairTex = 0, BraTex = 5, ShoesTex = 1, UnderwearTex = 0 } + } +} + +-- Locale +Config.Language = 'en' -- Currently Available: fr, en +-- Locales can be easily created or modified in `kc-unicorn/locales` + +-- Misc +Config.Debug = false -- If you think something is not working, you can set 'Config.Debug' to true. It will then print a lot of debug information in your console +Config.DebugPolyzones = false -- Set to true if you want to display the Polyzones +Config.MoreDebug = false -- If set to true, this will print more debug about server and client lapdance seat status +Config.MoreDebugRefreshTime = 5 -- Refresh time of "Config.MoreDebug" in seconds +Config.UpdateChecker = true -- Set to false if you don't want to check for resource update on start +Config.ChangeLog = true -- Set to false if you don't want to display the changelog if new version is find + + + +----- DO NOT TOUCH! /!\ LOCALE SYSTEM /!\ +Language = {} + +function Loc(text,replacement) + Language = (load)("return Locale." .. Config.Language)() + + if (load)("return Locale." .. Config.Language)() == nil then + return 'Locale [' .. Config.Language .. '] (Does not exist/Is mispelled)' + end + if (load)("return ".."Language."..text)() == nil then + return 'Error with [' .. Config.Language .. '.' .. text .. '] translation (Does not exist/Is mispelled)' + end + + if replacement then + text = (string.gsub((load)("return ".."Language."..text)(), "%%", replacement)) + else + text = (load)("return ".."Language."..text)() + end + return text +end +----------------------------------------- \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/kc-unicorn/fxmanifest.lua b/server-data/resources/[esx_addons]/kc-unicorn/fxmanifest.lua new file mode 100644 index 000000000..8bce901e4 --- /dev/null +++ b/server-data/resources/[esx_addons]/kc-unicorn/fxmanifest.lua @@ -0,0 +1,26 @@ +fx_version 'cerulean' +game 'gta5' + +author 'Clementinise' +name '^6KC Unicorn Script' +description 'Bring life & interactions to the Unicorn Club' +github 'https://github.com/clementinise/kc-unicorn' +version '1.1' + +shared_scripts { + 'locales/*.lua', + 'config.lua', +} + +client_scripts { + '@PolyZone/client.lua', + '@PolyZone/BoxZone.lua', + '@PolyZone/EntityZone.lua', + '@PolyZone/CircleZone.lua', + '@PolyZone/ComboZone.lua', + 'client/*.lua' +} + +server_script 'server/*.lua' + +fivem_checker 'yes' \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/kc-unicorn/locales/en.lua b/server-data/resources/[esx_addons]/kc-unicorn/locales/en.lua new file mode 100644 index 000000000..6683b167d --- /dev/null +++ b/server-data/resources/[esx_addons]/kc-unicorn/locales/en.lua @@ -0,0 +1,15 @@ +Locale = Locale or {} + +Locale.en = { -- 'fr' is the reference that will be used for 'Config.Language' + StandaloneLapText = "Ask for a lap dance", -- Set the text that will be displayed above marker if 'Config.Framework' is set to 'standalone' + LapText = "Buy a lap dance (~g~%$~w~)", -- Set the text that will be displayed above marker + BoughtLapdance = "You just bought a lap dance for %$", -- Notification text when a lap dance is bought + StripperActive = "The stripper is already busy!", -- Notification text if a stripper is already active when you try to buy a lap dance + NotEnoughMoney = "You do not have enough money. A lap dance costs %$", -- Notification text if player don't have enough cash + AllPlacesTaken = "All places are already taken", -- Notification text if all places are already taken + lapStopped = "Your lap dance has been interrupted", -- Notification text if lapdance is interrupted + Lean = "Lean", -- Text displayed when near poledance + StandaloneLeanNotice = "Press ~INPUT_CONTEXT~ to stop leaning\nPress ~INPUT_JUMP~ to throw money", -- Set the text that will be displayed once lean if 'Config.Framework' is set to 'standalone' + LeanNotice = "Press ~INPUT_CONTEXT~ to stop leaning\nPress ~INPUT_JUMP~ to throw money (~g~%$~w~)", -- Set the text that will be displayed once lean + NotEnoughCashLean = "Not enough cash to throw" -- Text if player doesn't have enough money to throw once lean +} \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/kc-unicorn/locales/fr.lua b/server-data/resources/[esx_addons]/kc-unicorn/locales/fr.lua new file mode 100644 index 000000000..b7c372bd1 --- /dev/null +++ b/server-data/resources/[esx_addons]/kc-unicorn/locales/fr.lua @@ -0,0 +1,15 @@ +Locale = Locale or {} + +Locale.fr = { -- 'fr' est la référence qui sera utilisé par 'Config.Language' + StandaloneLapText = "Demander un lap dance", -- Définit le texte qui sera affiché au-dessus du marqueur si 'Config.Framework' est mis sur 'standalone' + LapText = "Acheter un lap dance (~g~%$~w~)", -- Définit le texte qui sera affiché au-dessus du marqueur + BoughtLapdance = "Vous avez acheté un lap dance pour %$", -- Texte de la notification lorsqu'un lap dance est acheté + StripperActive = "La strip-teaseuse est déjà occupé !", -- Texte de la notification si une strip-teaseuse est déjà occupé lorsque vous essayez d'acheter un lap dance + NotEnoughMoney = "Vous n'avez pas assez d'argent. Un lap dance coûte %$", -- Texte de la notification si le joueur n'a pas assez d'argent + AllPlacesTaken = "Toutes les places sont déjà prises", -- Texte de la notification si toutes les places sont déjà prises + lapStopped = "Ta lap dance a été interrompu !", -- Texte si la lap dance est interrompu + Lean = "Se pencher", -- Texte pour se pencher au niveau de la bar de poledance + StandaloneLeanNotice = "Appuyez sur ~INPUT_CONTEXT~ pour arrêter de vous pencher\nAppuyez sur ~INPUT_JUMP~ pour jeter de l'argent", -- Définit le texte qui sera affiché une fois penché si 'Config.Framework' est mis sur 'standalone' + LeanNotice = "Appuyez sur ~INPUT_CONTEXT~ pour arrêter de vous pencher\nAppuyez sur ~INPUT_JUMP~ pour jeter de l'argent (~g~%$~w~)", -- Définit le texte qui sera affiché une fois penché + NotEnoughCashLean = "Vous n'avez pas assez d'argent" -- Texte si le joueur n'a pas assez d'argent pour jeter un billet une fois penché +} \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/kc-unicorn/server/main.lua b/server-data/resources/[esx_addons]/kc-unicorn/server/main.lua new file mode 100644 index 000000000..a759633ed --- /dev/null +++ b/server-data/resources/[esx_addons]/kc-unicorn/server/main.lua @@ -0,0 +1,414 @@ +local GithubL, Changelog, ESX, QBCore, inPoleDance + +local Version = GetResourceMetadata(GetCurrentResourceName(), 'version', 0) -- Do Not Change This Value +local Github = GetResourceMetadata(GetCurrentResourceName(), 'github', 0) +local Updater = false +local INPOLEDANCE = false +local PedNumber = 2 +local PoledancePed1, PoledancePed2 + +-- Server Seat Locales +local GetPlayerSeated = 0 +local Seat1Taken = false +local Seat2Taken = false +local Seat3Taken = false +local Seat4Taken = false +local Seat5Taken = false +local Seat6Taken = false +local Seat7Taken = false + +-- Import +if Config.Framework == "ESX" then + TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end) +elseif Config.Framework == "QBCore" then + QBCore = exports['qb-core']:GetCoreObject() +end + +-------- Check if new update available +Citizen.CreateThread(function() + + local Resources = GetNumResources() + + for i = 0, Resources, 1 do + local resource = GetResourceByFindIndex(i) + UpdateResource(resource) + end + + Citizen.Wait(4000) + + if Updater == false then + if Config.UpdateChecker then + Updater = not Updater + UpdateChecker() + end + end + +end) + +function UpdateResource(resource) + if resource == 'fivem-checker' then + if GetResourceState(resource) == 'started' or GetResourceState(resource) == 'starting' then + if Config.UpdateChecker then + Updater = true + end + end + end +end + +---------------- + +function UpdateChecker() + + if string.find(Github, "github") then + if string.find(Github, "github.com") then + GithubL = Github + Github = string.gsub(Github, "github", "raw.githubusercontent")..'/master/version' + else + GithubL = string.gsub(Github, "raw.githubusercontent", "github"):gsub("/master", "") + Github = Github..'/version' + end + else + end + PerformHttpRequest(Github, function(Error, V, Header) + NewestVersion = V + end) + repeat + Citizen.Wait(10) + until NewestVersion ~= nil + local _, strings = string.gsub(NewestVersion, "\n", "\n") + Version1 = NewestVersion:match("[^\n]*"):gsub("[<>]", "") + if string.find(Version1, Version) then + else + if strings > 0 then + Changelog = NewestVersion:gsub(Version1,""):match("(.*" .. Version .. ")"):gsub(Version,"") + Changelog = string.gsub(Changelog, "\n", "") + Changelog = string.gsub(Changelog, "-", " \n-"):gsub("%b<>", ""):sub(1, -2) + NewestVersion = Version1 + end + end + + Citizen.Wait(500) + + print('') + if Config.Framework == 'QBCore' then + print('^8QBCore ^6KC Unicorn Script ('..GetCurrentResourceName()..')') + elseif Config.Framework == 'Standalone' then + print('^9Standalone ^6KC Unicorn Script ('..GetCurrentResourceName()..')') + elseif Config.Framework == 'ESX' then + print('^3ESX ^6KC Unicorn Script ('..GetCurrentResourceName()..')') + end + if Version == Version1 then + print('^2Version ' .. Version .. ' - Up to date!') + else + print('^1Version ' .. Version .. ' - Outdated!') + print('^1New version: v' .. Version1) + if Config.ChangeLog then + print('\n^3Changelog:') + print('^4'..Changelog..'\n') + end + print('^1Please check the GitHub and download the last update') + print('^2'..GithubL..'/releases/latest') + end + print('') +end + +-- Poledance Girls (WIP) +Citizen.CreateThread(function() + + PoledancePed1 = CreatePed(4, 1381498905, 112.27, -1287.22, 28.46, 303.19, true, false) + PoledancePed2 = CreatePed(4, 1846523796, 102.13, -1296.19, 28.77, 303.19, true, false) + SharedPed1 = NetworkGetNetworkIdFromEntity(PoledancePed1) + SharedPed2 = NetworkGetNetworkIdFromEntity(PoledancePed2) + + inPoleDance = true + + SetPedComponentVariation(PoledancePed2, 8, 0) + SetPedComponentVariation(PoledancePed1, 8, 0) + + + repeat + inPoleDance = true + + TaskGoToCoordAnyMeans(PoledancePed1, 102.13, -1296.19, 28.77, 1.0, 0, 0, 0, 0xbf800000) + TaskGoToCoordAnyMeans(PoledancePed2, 112.27, -1287.22, 28.46, 1.0, 0, 0, 0, 0xbf800000) + PoleDance(PoledancePed2) + + inPoleDance = true + + if PedNumber == 2 then + PedNumber = 1 + else + PedNumber = 2 + end + + TaskGoToCoordAnyMeans(PoledancePed2, 102.13, -1296.19, 28.77, 1.0, 0, 0, 0, 0xbf800000) + TaskGoToCoordAnyMeans(PoledancePed1, 112.27, -1287.22, 28.46, 1.0, 0, 0, 0, 0xbf800000) + PoleDance(PoledancePed1) + + if PedNumber == 2 then + PedNumber = 1 + else + PedNumber = 2 + end + + until (Poledance == false) +end) + +function PoleDance(Ped) + while inPoleDance do + PedCoo = GetEntityCoords(Ped) + local poledist = #(PedCoo - vector3(112.27, -1287.22, 28.46)) + local dict = "mini@strip_club@pole_dance@pole_dance"..PedNumber + if poledist < 0.4 then + INPOLEDANCE = true + TriggerClientEvent('kc-unicorn:poledancescene', -1, NetworkGetNetworkIdFromEntity(Ped), PedNumber) + repeat + Citizen.Wait(200) + until INPOLEDANCE == false + inPoleDance = false + end + Citizen.Wait(10) + end +end + +-- Buy function +RegisterServerEvent('kc-unicorn:buy') +AddEventHandler('kc-unicorn:buy', function(Stripper) + + if Config.Framework == "QBCore" then + + local src = source + local Player = QBCore.Functions.GetPlayer(src) + Cost = Config.LapDanceCost + PlayerMoney = Player.PlayerData.money["cash"] + PlayerBirthdate = Player.PlayerData.charinfo.birthdate + TodayDate = os.date("%Y-%m-%d") + + if GetPlayerSeated >= 7 then + TriggerClientEvent('QBCore:Notify', src, Loc('AllPlacesTaken'), "error", 1700) + else + if PlayerMoney >= Cost then + if not LapDanceActive then + Player.Functions.RemoveMoney("cash", Cost) + TriggerClientEvent('QBCore:Notify', src, Loc('BoughtLapdance', Config.LapDanceCost), "success", 1700) + TriggerClientEvent('kc-unicorn:lapdance', src, PlayerMoney, PlayerBirthdate, TodayDate, Stripper) + else + TriggerClientEvent('QBCore:Notify', src, Loc('StripperActive'), "error", 1700) + end + else + TriggerClientEvent('QBCore:Notify', src, Loc('NotEnoughMoney', Config.LapDanceCost), "error", 1700) + end + end + + elseif Config.Framework == "ESX" then + + local src = source + local Player = ESX.GetPlayerFromId(src) + Cost = Config.LapDanceCost + PlayerMoney = Player.getMoney() + + if GetPlayerSeated >= 7 then + TriggerClientEvent('esx:showNotification', src, Loc('AllPlacesTaken')) + else + if PlayerMoney >= Cost then + if not LapDanceActive then + Player.removeMoney(Cost) + TriggerClientEvent('esx:showNotification', src, Loc('BoughtLapdance', Config.LapDanceCost)) + TriggerClientEvent('kc-unicorn:lapdance', src, PlayerMoney, PlayerBirthdate, TodayDate, Stripper) + else + TriggerClientEvent('esx:showNotification', src, Loc('StripperActive')) + end + else + TriggerClientEvent('esx:showNotification', src, Loc('NotEnoughMoney', Config.LapDanceCost)) + end + end + + elseif Config.Framework == 'Standalone' then + + local src = source + + if GetPlayerSeated >= 7 then + TriggerClientEvent('kc-unicorn:showNotify', src, Loc('AllPlacesTaken')) + else + TriggerClientEvent('kc-unicorn:lapdance', src, PlayerMoney, PlayerBirthdate, TodayDate, Stripper) + end + end +end) + +-- Lean Throw function +RegisterServerEvent('kc-unicorn:leanthrow') +AddEventHandler('kc-unicorn:leanthrow', function() + + if Config.Framework == "QBCore" then + + local src = source + local Player = QBCore.Functions.GetPlayer(src) + Cost = Config.ThrowCost + PlayerMoney = Player.PlayerData.money["cash"] + + Player.Functions.RemoveMoney("cash", Cost) + + elseif Config.Framework == "ESX" then + + local src = source + local Player = ESX.GetPlayerFromId(src) + Cost = Config.ThrowCost + PlayerMoney = Player.getMoney() + + Player.removeMoney(Cost) + + end +end) + +-- Lean ESX Get Money +RegisterServerEvent('kc-unicorn:esxmoney') +AddEventHandler('kc-unicorn:esxmoney', function() + + local src = source + local Player = ESX.GetPlayerFromId(src) + Cost = Config.LapDanceCost + PlayerMoney = Player.getMoney() + + TriggerClientEvent("kc-unicorn:esxplayermoney", src, PlayerMoney) + +end) + +RegisterServerEvent('kc-unicorn:SyncLapDanceSpawn') +AddEventHandler('kc-unicorn:SyncLapDanceSpawn', function() + + TriggerClientEvent("kc-unicorn:LapDanceSpawn", 0) + +end) + +RegisterServerEvent('kc-unicorn:GetPlayerSeated') +AddEventHandler('kc-unicorn:GetPlayerSeated', function() + local src = source + + if Seat1Taken then + GetPlayerSeated = GetPlayerSeated + 1 + end + if Seat2Taken then + GetPlayerSeated = GetPlayerSeated + 1 + end + if Seat3Taken then + GetPlayerSeated = GetPlayerSeated + 1 + end + if Seat4Taken then + GetPlayerSeated = GetPlayerSeated + 1 + end + if Seat5Taken then + GetPlayerSeated = GetPlayerSeated + 1 + end + if Seat6Taken then + GetPlayerSeated = GetPlayerSeated + 1 + end + if Seat7Taken then + GetPlayerSeated = GetPlayerSeated + 1 + end + if not Seat1Taken and not Seat2Taken and not Seat3Taken and not Seat4Taken and not Seat5Taken and not Seat6Taken and not Seat7Taken then + GetPlayerSeated = 0 + end + + if Config.Debug then + print("Server: ", "Seat1: ", Seat1Taken, "Seat2: ", Seat2Taken, "Seat3: ", Seat3Taken, "Seat4: ", Seat4Taken, "Seat5: ", Seat5Taken, "Seat6: ", Seat6Taken, "Seat7: ", Seat7Taken, " ", GetPlayerSeated) + end + TriggerClientEvent("kc-unicorn:SetPlayerSeated", src, GetPlayerSeated, Seat1Taken, Seat2Taken, Seat3Taken, Seat4Taken, Seat5Taken, Seat6Taken, Seat7Taken) + GetPlayerSeated = 0 + +end) + +RegisterServerEvent('kc-unicorn:Seat1') +AddEventHandler('kc-unicorn:Seat1', function(seat) + Seat1Taken = true +end) + +RegisterServerEvent('kc-unicorn:Seat2') +AddEventHandler('kc-unicorn:Seat2', function(seat) + Seat2Taken = true +end) + +RegisterServerEvent('kc-unicorn:Seat3') +AddEventHandler('kc-unicorn:Seat3', function(seat) + Seat3Taken = true +end) + +RegisterServerEvent('kc-unicorn:Seat4') +AddEventHandler('kc-unicorn:Seat4', function(seat) + Seat4Taken = true +end) + +RegisterServerEvent('kc-unicorn:Seat5') +AddEventHandler('kc-unicorn:Seat5', function(seat) + Seat5Taken = true +end) + +RegisterServerEvent('kc-unicorn:Seat6') +AddEventHandler('kc-unicorn:Seat6', function(seat) + Seat6Taken = true +end) + +RegisterServerEvent('kc-unicorn:Seat7') +AddEventHandler('kc-unicorn:Seat7', function(seat) + Seat7Taken = true +end) + +RegisterServerEvent('kc-unicorn:RemoveSeat1') +AddEventHandler('kc-unicorn:RemoveSeat1', function(seat) + Seat1Taken = false +end) + +RegisterServerEvent('kc-unicorn:RemoveSeat2') +AddEventHandler('kc-unicorn:RemoveSeat2', function(seat) + Seat2Taken = false +end) + +RegisterServerEvent('kc-unicorn:RemoveSeat3') +AddEventHandler('kc-unicorn:RemoveSeat3', function(seat) + Seat3Taken = false +end) + +RegisterServerEvent('kc-unicorn:RemoveSeat4') +AddEventHandler('kc-unicorn:RemoveSeat4', function(seat) + Seat4Taken = false +end) + +RegisterServerEvent('kc-unicorn:RemoveSeat5') +AddEventHandler('kc-unicorn:RemoveSeat5', function(seat) + Seat5Taken = false +end) + +RegisterServerEvent('kc-unicorn:RemoveSeat6') +AddEventHandler('kc-unicorn:RemoveSeat6', function(seat) + Seat6Taken = false +end) + +RegisterServerEvent('kc-unicorn:RemoveSeat7') +AddEventHandler('kc-unicorn:RemoveSeat7', function(seat) + Seat7Taken = false +end) + +RegisterServerEvent('kc-unicorn:NoPlaceAvailable') +AddEventHandler('kc-unicorn:NoPlaceAvailable', function() + + local src = source + local Player = QBCore.Functions.GetPlayer(src) + Cost = Config.LapDanceCost + + Player.Functions.AddMoney("cash", Cost) + TriggerClientEvent('QBCore:Notify', src, Loc('AllPlacesTaken'), "error", 1700) + + +end) + +AddEventHandler('onResourceStop', function() + DeleteEntity(PoledancePed1) + DeleteEntity(PoledancePed2) + print("Ped succesfully deleted") + end) + +RegisterServerEvent('kc-unicorn:poledancestop') +AddEventHandler('kc-unicorn:poledancestop', function() + + INPOLEDANCE = false + +end) diff --git a/server-data/resources/[esx_addons]/wx_jail/README.md b/server-data/resources/[esx_addons]/wx_jail/README.md deleted file mode 100644 index e92ec9563..000000000 --- a/server-data/resources/[esx_addons]/wx_jail/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# NOTICE -This script is 100% functional and ready to be used on your server BUT: -- I made this script for one of the servers I've been developing -- Some of the functions are taken from qalle's jail system -- There might be some translation errors and/or untranslated parts (mostly in czech), however I've tried to translate most of the parts - -# WX JAIL - Advanced jail system made using ox_lib - -# Features -- OX Lib implementation (notifications, target, context menu, ...) -- Target support -- Admin menu -- Exploit protection -- Optimized -- Large config file -- Multiple jobs support -- Multiple jail locations -- [WX Logs](https://github.com/nwvh/wx_logs) implementation - -# NOTE: MOST OF THE FUNCTIONS ARE FROM [QALLE'S JAIL SYSTEM](https://github.com/qalle-git/esx-qalle-jail)! MOST OF THE CREDITS GOES TO HIM! diff --git a/server-data/resources/[esx_addons]/wx_jail/SQL/INSERT-THIS.sql b/server-data/resources/[esx_addons]/wx_jail/SQL/INSERT-THIS.sql deleted file mode 100644 index 9a907b278..000000000 --- a/server-data/resources/[esx_addons]/wx_jail/SQL/INSERT-THIS.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE `users` - ADD COLUMN `jail` INT(16) NOT NULL DEFAULT '0'; \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/wx_jail/client/client.lua b/server-data/resources/[esx_addons]/wx_jail/client/client.lua deleted file mode 100644 index db153ee43..000000000 --- a/server-data/resources/[esx_addons]/wx_jail/client/client.lua +++ /dev/null @@ -1,285 +0,0 @@ -ESX = exports["es_extended"]:getSharedObject() -PlayerData = {} -local jailTime = 0 - --- ## Check player's job ## -- -Citizen.CreateThread(function() - while ESX.GetPlayerData() == nil do - Citizen.Wait(150) - end - while true do - Wait(2500) - PlayerData = ESX.GetPlayerData() or {} - end -end) - --- ## Check if player is in jail when he selects character ## -- --- ## Fix on correct jailtime display -RegisterNetEvent("esx:playerLoaded") -AddEventHandler("esx:playerLoaded", function() - Citizen.Wait(15000) - ESX.TriggerServerCallback("wx_jail:retrieveJailTime", function(inJail, time) - if inJail then - jailTime = time --Time display quickfix :D - SetEntityCoords(PlayerPedId(), wx.Locations.JailEnter[math.random(#wx.Locations.JailEnter)]) - - lib.notify({ - title = Locale["JailTitle"], - description = Locale["Disconnected"], - type = 'error', - position = 'top' - }) - TimeLeft() - end - end) -end) - -RegisterNetEvent("wx_jail:jailPlayer") -AddEventHandler("wx_jail:jailPlayer", function(newJailTime) - jailTime = newJailTime - Cutscene() -end) - -RegisterNetEvent("wx_jail:unJailPlayer") -AddEventHandler("wx_jail:unJailPlayer", function() - jailTime = 0 - UnJail() -end) - - -local options = { - { - name = 'wx_jail:send', - distance = 2, - icon = 'fa-solid fa-handcuffs', - label = Locale["SendToJail"], - canInteract = function () - if wx.Jobs[PlayerData["job"].name] then return true end - if IsPedDeadOrDying(PlayerPedId(),false) or IsPedFatallyInjured(PlayerPedId()) then return false end - return false - end, - onSelect = function (data) - local id = GetPlayerServerId(NetworkGetPlayerIndexFromPed(data.entity)) - local jail = lib.inputDialog('Vězení', { - {type = 'input', label = Locale["pID"], disabled = true, placeholder = id, icon = 'list-ol',}, - {type = 'number', label = Locale["pTime"], description = Locale["pTimeDesc"]:format(wx.MinuteToYears), icon = 'clock', placeholder = "10"}, - {type = 'input', label = Locale["pReason"], description = Locale["pReasonDesc"], icon = 'font', placeholder = "Jízda bez ŘP"}, - }) - if jail then - if jail[2] <= 100 then - TriggerServerEvent('wx_jail:sendToJail',id,jail[2],jail[3]) - lib.notify({ - title = Locale["Success"], - description = Locale["Jailed"]:format(jail[2],jail[3]), - type = 'info', - position = 'top' - }) - else - lib.notify({ - title = Locale["Error"], - description = Locale["MaxTime"]:format(wx.MaxTime), - type = 'error', - position = 'top' - }) - end - else - lib.notify({ - title = Locale["Cancelled"], - type = 'info', - position = 'top' - }) - end - end - }, -} - -if wx.Target then - exports.ox_target:addGlobalPlayer(options) -end -if wx.ManualJail then - exports.ox_target:addSphereZone({ - coords = vec3(1840.4167, 2577.7600, 46.0144), - radius = 1.0, - options = { - { - label = Locale["JailTarget"], - icon = "fa-solid fa-handcuffs", - distance = 1.5, - canInteract = function () - if wx.Jobs[PlayerData["job"].name] then return true end - return false - end, - onSelect = function () - local pl = {} - local nearby = lib.getNearbyPlayers(GetEntityCoords(PlayerPedId()), 25.0, true) - if json.encode(nearby) == '[]' then - table.insert(pl,{ - title = Locale["Nearby"], - icon = 'triangle-exclamation' - }) - else - for k,v in pairs(nearby) do - local name = lib.callback.await('wx_jail:getCharName',GetPlayerServerId(v.id)) - print(GetPlayerServerId(v.id)) - table.insert(pl,{ - title = ("[%s] %s"):format(GetPlayerServerId(v.id),GetPlayerName(v.id)), - icon = 'user', - onSelect = function () - local jail = lib.inputDialog(Locale["JailTitle"], { - {type = 'input', label = Locale["pID"], disabled = true, placeholder = GetPlayerServerId(v.id), icon = 'list-ol',}, - {type = 'number', label = Locale["pTime"], description = Locale["pTimeDesc"]:format(wx.MinuteToYears), icon = 'clock', placeholder = "10"}, - {type = 'input', label = Locale["pReason"], description = Locale["pReasonDesc"], icon = 'font', placeholder = "Jízda bez ŘP"}, - }) - if jail then - if jail[2] <= wx.MaxTime then - local alert = lib.alertDialog({ - header = Locale["Confirmation"], - content = Locale["ConfirmationDesc"]:format(name,jail[2]*wx.MinuteToYears..' roky',jail[3]), - centered = true, - cancel = true, - labels = { - confirm = Locale["Confirm"], - cancel = Locale["Cancel"] - } - }) - if alert == 'confirm' then - TriggerServerEvent('wx_jail:sendToJail',GetPlayerServerId(v.id),jail[2],jail[3]) - lib.notify({ - title = Locale["Success"], - description = Locale["Jailed"]:format(jail[2],jail[3]), - type = 'info', - position = 'top' - }) - end - else - lib.notify({ - title = Locale["Error"], - description = Locale["MaxTime"]:format(wx.MaxTime), - type = 'error', - position = 'top' - }) - - end - else - lib.notify({ - title = Locale["Cancelled"], - type = 'info', - position = 'top' - }) - end - end - }) - lib.registerContext({ - id = 'nearby', - title = Locale["Select"], - options = pl - }) - end - end - - lib.showContext('nearby') - end - } - } - }) -end - - - -function UnJail() - TimeLeft() - - ESX.Game.Teleport(PlayerPedId(), wx.Locations.JailExit) - - lib.notify({ - title = 'Věznice', - description = Locale["JailDone"], - type = 'success', - position = 'top' - }) -end - -function DrawTxt(x,y, width, height, scale, text, r, g, b, a, outline) - SetTextFont(0) - SetTextScale(scale, scale) - SetTextColour(r, g, b, a) - SetTextDropshadow(0, 0, 0, 0,255) - SetTextDropShadow() - if outline then - SetTextOutline() - end - BeginTextCommandDisplayText('STRING') - AddTextComponentSubstringPlayerName(text) - EndTextCommandDisplayText(x - width / 2, y - height / 2 + 0.005) -end - -function CheckTeleport() - if GetDistanceBetweenCoords(wx.Locations.JailCenter,GetEntityCoords(PlayerPedId())) > wx.Radius+0.0 then - if GetDistanceBetweenCoords(GetEntityCoords(PlayerPedId()),vector3(402.9156, -996.7591, -99.0002)) > 10.0 then - SetEntityCoords(PlayerPedId(), wx.Locations.JailEnter[ math.random( #wx.Locations.JailEnter ) ]) - if wx.BanOnLeave then - TriggerServerEvent('wx_jail:ban') - end - - lib.notify({ - title = 'Věznice', - description = Locale["TooFar"], - type = 'error', - position = 'top' - }) - return - end - end -end - -function TimeLeft() - - Citizen.CreateThread(function() - - while jailTime > 0 do - jailTime = jailTime - 1 - if jailTime == 0 then - UnJail() - TriggerServerEvent("wx_jail:updateJailTime", 0) - end - TriggerServerEvent("wx_jail:updateJailTime", jailTime) - Citizen.Wait(60000*wx.MinuteToYears) - end - - end) - Citizen.CreateThread(function () - while jailTime > 0 do Wait(0) - if wx.AntiLeave then CheckTeleport() end - if jailTime == 1 then - DrawTxt(1.0, 1.45, 1.0, 1.0, 0.4, (""..Locale["YearR"].." "):format(jailTime), 255, 255, 255, 255) - elseif jailTime > 5 then - DrawTxt(1.0, 1.45, 1.0, 1.0, 0.4, (""..Locale["YearsR"].." "):format(jailTime), 255, 255, 255, 255) - end - - end - end) - -end - ---same usage as playerLoaded -AddEventHandler('onResourceStart', function(resourceName) - if (GetCurrentResourceName() ~= resourceName) then - return - end - Citizen.Wait(2000) - ESX.TriggerServerCallback("wx_jail:retrieveJailTime", function(inJail, time) - if inJail then - jailTime = time --Time display quickfix :D - SetEntityCoords(PlayerPedId(), wx.Locations.JailEnter[math.random(#wx.Locations.JailEnter)]) - - lib.notify({ - title = Locale["JailTitle"], - description = Locale["Disconnected"], - type = 'error', - position = 'top' - }) - TimeLeft() - end - end) -end) - diff --git a/server-data/resources/[esx_addons]/wx_jail/client/font.lua b/server-data/resources/[esx_addons]/wx_jail/client/font.lua deleted file mode 100644 index 0a0cded9a..000000000 --- a/server-data/resources/[esx_addons]/wx_jail/client/font.lua +++ /dev/null @@ -1 +0,0 @@ -RegisterFontFile('BBN') diff --git a/server-data/resources/[esx_addons]/wx_jail/client/functions.lua b/server-data/resources/[esx_addons]/wx_jail/client/functions.lua deleted file mode 100644 index 6bf839ac6..000000000 --- a/server-data/resources/[esx_addons]/wx_jail/client/functions.lua +++ /dev/null @@ -1,70 +0,0 @@ -local CamOpt = { - ["CameraPos"] = { ["x"] = 402.88830566406, ["y"] = -1003.8851318359, ["z"] = -97.419647216797, ["rotationX"] = -15.433070763946, ["rotationY"] = 0.0, ["rotationZ"] = -0.31496068835258, ["cameraId"] = 0 } -} - -CS = false - - -function LoadAnim(animDict) - RequestAnimDict(animDict) - - while not HasAnimDictLoaded(animDict) do - Citizen.Wait(10) - end -end - -function LoadModel(model) - RequestModel(model) - - while not HasModelLoaded(model) do - Citizen.Wait(10) - end -end - -function Cutscene() - SwitchOutPlayer(PlayerPedId(), 0, 1) - Citizen.Wait(250) - - LoadModel(wx.PhotoNPC) - local photoNPC = CreatePed(5, wx.PhotoNPC, 402.9170, -1000.6376, -99.0040, 356.8805, false) - TaskStartScenarioInPlace(photoNPC, "WORLD_HUMAN_PAPARAZZI", 0, false) - - local PlayerPed = PlayerPedId() - SetEntityCoords(PlayerPed, 428.3845, -980.2968, 30.7114 - 1) - SetEntityHeading(PlayerPed, 186.2249) - FreezeEntityPosition(PlayerPed, true) - RemoveAllPedWeapons(PlayerPedId()) - - Citizen.Wait(5500) - SwitchInPlayer(PlayerPedId()) - DoScreenFadeIn(1500) - SetEntityCoords(PlayerPed, 402.9156, -996.7591, -99.0002 - 1) - Cam() - Citizen.Wait(1000) - DoScreenFadeIn(100) - Citizen.Wait(10000) - Citizen.Wait(1000) - DoScreenFadeIn(250) - - RenderScriptCams(false, false, 0, true, true) - FreezeEntityPosition(PlayerPed, false) - DestroyCam(CamOpt["CameraPos"]["cameraId"]) - SetEntityCoords(PlayerPed, wx.Locations.JailEnter[ math.random( #wx.Locations.JailEnter ) ]) - DeleteEntity(photoNPC) - SetModelAsNoLongerNeeded(-1320879687) - CS = true - - - TimeLeft() -end - -function Cam() - local CamOptions = CamOpt["CameraPos"] - - CamOptions["cameraId"] = CreateCam("DEFAULT_SCRIPTED_CAMERA", true) - - SetCamCoord(CamOptions["cameraId"], CamOptions["x"], CamOptions["y"], CamOptions["z"]) - SetCamRot(CamOptions["cameraId"], CamOptions["rotationX"], CamOptions["rotationY"], CamOptions["rotationZ"]) - - RenderScriptCams(true, false, 0, true, true) -end diff --git a/server-data/resources/[esx_addons]/wx_jail/client/jailmenu.lua b/server-data/resources/[esx_addons]/wx_jail/client/jailmenu.lua deleted file mode 100644 index c17871736..000000000 --- a/server-data/resources/[esx_addons]/wx_jail/client/jailmenu.lua +++ /dev/null @@ -1,412 +0,0 @@ --- I won't translate this lol - -ESX = exports["es_extended"]:getSharedObject() -lib.registerContext({ - id = 'jailmenu', - title = "Prison", - options = { - { - title = "Imprison", - disabled = wx.ManualJail, - icon = "handcuffs", - onSelect = function () - local jail = lib.inputDialog('Jail', { - {type = 'number', label = 'Player ID', disabled = false, placeholder = "123", icon = 'list-ol',}, - {type = 'number', label = 'Time', description = 'Time in minutes (1 minute = '..wx.MinuteToYears..' years)', icon = 'clock', placeholder = "10"}, - {type = 'input', label = 'Reason', description = 'Reason for imprisoning the player', icon = 'font', placeholder = "Driving without a license"}, - }) - if jail then - if jail[2] <= wx.MaxTime then - ESX.TriggerServerCallback('wx_jail:checkJailUser', function(jailed) - for k, v in pairs(jailed) do - local serverId = GetPlayerServerIdFromIdentifier(v.identifier) - if serverId == jail[1] then - lib.notify({ - title = Locale["Error"], - description = ('L\'utente è già in carcere!'), - type = 'error', - position = 'top' - }) - break - else - TriggerServerEvent('wx_jail:sendToJail', jail[1], jail[2], jail[3]) - lib.notify({ - title = "Success", - description = 'Player has been imprisoned for ' .. - jail[2] .. ' minutes due to ' .. jail[3], - type = 'info', - position = 'top' - }) - end - end - end) - else - lib.notify({ - title = "Error", - description = ('The maximum time for imprisonment is %s minutes!'):format(wx.MaxTime), - type = 'error', - position = 'top' - }) - end - else - lib.notify({ - title = 'Action Cancelled', - type = 'info', - position = 'top' - }) - end - end - }, - { - title = "Release from Prison", - icon = 'user-large-slash', - onSelect = function () - local jailedPly = { - { - title = "Back", - icon = 'angle-left', - menu = 'jailmenu', - arrow = false - } - } - ESX.TriggerServerCallback("wx_jail:retrieveJailedPlayers", function(jailed) - if json.encode(jailed) == '[]' then - table.insert(jailedPly,{ - title = "No one is in prison", - icon = 'exclamation-triangle' - }) - end - for k,v in pairs(jailed) do - table.insert(jailedPly,{ - title = v.name, - metadata = { - {label = 'Remaining Time', value = v.jailTime*wx.MinuteToYears..' minutes'}, - {label = 'Identifier', value = v.identifier}, - }, - onSelect = function () - local alert = lib.alertDialog({ - header = 'Release prisoner **'..v.name..'**', - content = 'Are you sure you want to release the selected prisoner?', - centered = true, - cancel = true, - labels = { - cancel = "Do Not Release", - confirm = "Yes, Release" - } - }) - if alert == 'confirm' then - TriggerServerEvent("wx_jail:unJailPlayer", v.identifier) - lib.notify({ - title = 'Prisoner '..v.name..' has been released!', - type = 'success', - position = 'top' - }) - else - lib.notify({ - title = 'Action Cancelled', - type = 'info', - position = 'top' - }) - end - end - }) - end - lib.registerContext({ - id='jailedPlayers', - title = "List of Prisoners", - options=jailedPly - }) - lib.showContext('jailedPlayers') - end) - end - -- menu = 'jailedPlayers' - }, - { - title = "Change Time", - icon = "clock", - onSelect = function () - local editJailTime = { - { - title = "Back", - icon = 'angle-left', - menu = 'jailmenu', - arrow = false - } - } - ESX.TriggerServerCallback("wx_jail:retrieveJailedPlayers", function(jailed) - if json.encode(jailed) == '[]' then - table.insert(editJailTime,{ - title = "No one is in prison", - icon = 'exclamation-triangle' - }) - end - for k,v in pairs(jailed) do - table.insert(editJailTime,{ - title = v.name, - metadata = { - {label = 'Remaining Time', value = v.jailTime*wx.MinuteToYears..' minutes'}, - {label = 'Identifier', value = v.identifier}, - }, - onSelect = function () - local jailtime = lib.inputDialog('Change Imprisonment Time', { - {type = 'number', label = 'New Time', description = 'Time in minutes, how long the player should stay in prison', icon = 'clock', placeholder = "10"}, - }) - if jailtime then - TriggerServerEvent("wx_jail:newTime", v.identifier,jailtime[1]) - lib.notify({ - title = "Success", - description = ('Prisoner %s is now in prison for %s'):format(v.name,jailtime[1]), - type = 'success', - position = 'top' - }) - else - lib.notify({ - title = "Cancelled", - type = 'info', - position = 'top' - }) - end - end - }) - end - lib.registerContext({ - id='editJailTime', - title = "List of Prisoners", - options=editJailTime - }) - lib.showContext('editJailTime') - end) - end - }, - - } -}) - -function GetPlayerServerIdFromIdentifier(identifier) - local playerServerId = nil - - for _, player in ipairs(GetActivePlayers()) do - local playerId = GetPlayerServerId(player) - - if playerId and playerId > 0 then - local playerIdentifier = ESX.GetPlayerData(player).identifier - - -- here we slip the identifiers so they are individual - local identifiers = splitIdentifiers(identifier) - - -- here we check each identifier coz why not - for _, id in ipairs(identifiers) do - if playerIdentifier == id then - playerServerId = playerId - break - end - end - --lol not working, quick fix made at line 240 no worries 👍 - if playerServerId then - break - end - end - end - - return playerServerId -end - -function splitIdentifiers(identifierrec) - local identifiers = {} - for identifier in string.gmatch(identifierrec, "[^,]+") do - table.insert(identifiers, identifier) - end - return identifiers -end - -RegisterCommand(wx.Command,function () - if not wx.Jobs[PlayerData["job"].name] then return end - lib.showContext('jailmenu') -end,false) - -RegisterCommand('adminjail',function () - lib.registerContext({ - id = 'adminjail', - title = 'Admin Jail', - options = { - { - title = "Send to jail", - icon = 'handcuffs', - onSelect = function () - local jail = lib.inputDialog('ADMIN JAIL', { - {type = 'number', label = 'Player ID', description = "ID of the player you want to jail", icon = 'list-ol',}, - {type = 'number', label = 'Time', description = 'Time in minutes', icon = 'clock', placeholder = "10"}, - {type = 'input', label = 'Reason', description = 'Jail reason', icon = 'font', placeholder = "Jízda bez ŘP"}, - {type = 'checkbox', label = 'Send to logs (Recommended)', checked=true}, - }) - if jail then - if jail[2] <= wx.MaxTime then - ESX.TriggerServerCallback('wx_jail:checkJailUser', function(jailed) - for k, v in pairs(jailed) do - local serverId = GetPlayerServerIdFromIdentifier(v.identifier) - if serverId == jail[1] then - lib.notify({ - title = Locale["Error"], - description = ('L\'utente è già in carcere!'), - type = 'error', - position = 'top' - }) - break - else - TriggerServerEvent('wx_jail:adminJail', jail[1], jail[2], jail[3], jail[4]) - lib.notify({ - title = Locale["Success"], - description = 'Il giocatore è stato incarcerato per ' .. - jail[2] .. ' minuti. Motivo: ' .. jail - [3], - type = 'info', - position = 'top' - }) - end - end - end) - else - lib.notify({ - title = Locale["Error"], - description = ('Maximum jail time is %s minutes!'):format(wx.MaxTime), - type = 'error', - position = 'top' - }) - end - else - lib.notify({ - title = Locale["Cancelled"], - type = 'info', - position = 'top' - }) - end - end - }, - { - title = "Remove from jail", - icon = 'handcuffs', - onSelect = function () - local unjail = lib.inputDialog('Remove player from jail', { - {type = 'number', label = 'ID Hráče', description = "Player ID", icon = 'list-ol',}, - {type = 'checkbox', label = 'Send to logs (Recommended)', checked=true}, - }) - if unjail then - TriggerServerEvent('wx_jail:adminUnjail',unjail[1],unjail[2]) - else - lib.notify({ - title = Locale["Cancelled"], - type = 'info', - position = 'top' - }) - end - end - }, - { - title = "Prisoner List", - icon = 'users', - onSelect = function () - local jailedPly = { - { - title = "Back", - icon = 'angle-left', - menu = 'adminjail', - arrow = false - } - } - ESX.TriggerServerCallback("wx_jail:retrieveJailedPlayers", function(jailed) - if json.encode(jailed) == '[]' then - table.insert(jailedPly,{ - title = "No one's in jail", - icon = 'exclamation-triangle' - }) - end - for k,v in pairs(jailed) do - table.insert(jailedPly,{ - title = v.name, - metadata = { - {label = 'Remaining time', value = v.jailTime*wx.MinuteToYears..' minutes'}, - {label = 'Identifier', value = v.identifier}, - }, - }) - end - lib.registerContext({ - id='jailedPlayers', - title = "Prisoner list", - options=jailedPly - }) - lib.showContext('jailedPlayers') - end) - end - -- menu = 'jailedPlayers' - }, - { - title = "Change time", - icon = "clock", - onSelect = function () - local editJailTime = { - { - title = "Back", - icon = 'angle-left', - menu = 'adminjail', - arrow = false - } - } - ESX.TriggerServerCallback("wx_jail:retrieveJailedPlayers", function(jailed) - if json.encode(jailed) == '[]' then - table.insert(editJailTime,{ - title = "No one's in jail", - icon = 'exclamation-triangle' - }) - end - for k,v in pairs(jailed) do - table.insert(editJailTime,{ - title = v.name, - metadata = { - {label = 'Remaining time', value = v.jailTime*wx.MinuteToYears..' minutes'}, - {label = 'Identifier', value = v.identifier}, - }, - onSelect = function () - local jailtime = lib.inputDialog('Edit jail time', { - {type = 'number', label = 'New Time', description = 'New jail time', icon = 'clock', placeholder = "10"}, - }) - if jailtime then - TriggerServerEvent("wx_jail:newTime", v.identifier,jailtime[1]) - lib.notify({ - title = Locale["Success"], - description = ('Prisoner %s will now be in jail for %s'):format(v.name,jailtime[1]), - type = 'success', - position = 'top' - }) - else - lib.notify({ - title = Locale["Cancelled"], - type = 'info', - position = 'top' - }) - end - end - }) - end - lib.registerContext({ - id='editJailTime', - title = "Prisoner List", - options=editJailTime - }) - lib.showContext('editJailTime') - end) - end - }, - } - }) - local group = lib.callback.await('wx_jail:getGroup') - if wx.Groups[group] then - lib.showContext('adminjail') - else - lib.notify({ - title = Locale["Error"], - description = 'No permissions!', - type = 'error', - position = 'top' - }) - end -end,false) diff --git a/server-data/resources/[esx_addons]/wx_jail/configs/jail_config.lua b/server-data/resources/[esx_addons]/wx_jail/configs/jail_config.lua deleted file mode 100644 index 907dc3ff1..000000000 --- a/server-data/resources/[esx_addons]/wx_jail/configs/jail_config.lua +++ /dev/null @@ -1,48 +0,0 @@ -wx = {} - -wx.Debug = true -- Debug, you can jail yourself - -wx.Target = false -- Jailing player via target -wx.ChatMessages = false -- Send chat message when player is jailed -wx.Command = 'jailmenu' -- Command for manual jailmenu -wx.Confiscate = true -- Confiscate items when you're set to jail -wx.ChangeClothes = false -- Change clothes when you're set to jail -wx.JailMenu = true -- Allow /jailmenu for your officers -wx.AdminJail = true -- Allow /adminjail command for your admins -wx.ManualJail = false -- Add "reception" straight to jail for officers. They won't be able to use jail option in /jailmenu -wx.MaxTime = 100 -- Maximum jail time -wx.MinuteToYears = 2 -- How many minutes should equal to 1 year? -wx.AntiLeave = true -- Teleports player back when he leaves the jail area -wx.BanOnLeave = false -- Ban player when he leaves the jail area? -wx.PhotoNPC = `s_m_y_cop_01` -- NPC for photographing player before jail -wx.NoShooting = true -- Disable shooting, punching and other forms of attacking in jail area (Bypass for officers) - -wx.Groups = { -- Admin Groups - ['admin'] = true, - ['dev'] = true, - ['owner'] = true, - ['trial'] = true, - ['mod'] = true, -} - -wx.Jobs = { -- Police jobs (Jail Access) - ['police'] = true, -} - -wx.Radius = 200.0 -- Radius for checking escapees, shooting zone etc. - -wx.Locations = { - JailEnter = { - vector4(1763.9077, 2499.9092, 45.7407, 215.4355-1), - vector4(1774.6932, 2481.1250, 45.7407, 30.6468-1), - vector4(1764.9320, 2476.0874, 49.6930, 28.0088-1), - vector4(1761.6296, 2474.2410, 49.6931, 28.9665-1), - }, -- Where to teleport jailed players? (Location is randomly selected) - JailExit = vector4(1837.6560, 2585.7500, 46.0144, 270.1598), -- When player's jail time ends - JailCenter = vector3(1689.7543, 2595.6868, 45.5648), -- Center of the jail, used to check distance from it (teleporting, shooting) - -} - -wx.BAN = function (id,reason) -- Your server side ban event/export - exports['wx_anticheat']:Ban(id,reason) -end diff --git a/server-data/resources/[esx_addons]/wx_jail/fxmanifest.lua b/server-data/resources/[esx_addons]/wx_jail/fxmanifest.lua deleted file mode 100644 index b06aeb3a1..000000000 --- a/server-data/resources/[esx_addons]/wx_jail/fxmanifest.lua +++ /dev/null @@ -1,20 +0,0 @@ -fx_version 'cerulean' -game 'gta5' -version '1.0.0' -lua54 'yes' -description "Advanced Jail using OX LIB" - -server_scripts { - "@mysql-async/lib/MySQL.lua", - "server/*.lua" -} - -client_scripts { - "client/*.lua", -} - -shared_scripts { - '@ox_lib/init.lua', - 'configs/*.lua', - 'locale/locale.lua' -} \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/wx_jail/locale/locale.lua b/server-data/resources/[esx_addons]/wx_jail/locale/locale.lua deleted file mode 100644 index b82ce9802..000000000 --- a/server-data/resources/[esx_addons]/wx_jail/locale/locale.lua +++ /dev/null @@ -1,26 +0,0 @@ -Locale = { - ["JailTitle"] = "Jail", - ["Disconnected"] = "You've disconnected while you was in jail, you have been brought back to the jail", - ["SendToJail"] = "Send to Jail", - ["pID"] = "Player ID", - ["pTime"] = "Time", - ["pTimeDesc"] = "Jail time, how long should the player be in jail (1 Minute = %s years)", - ["pReason"] = "Reason", - ["pReasonDesc"] = "Reason, why is the player being jailed", - ["Success"] = "Success", - ["Error"] = "Error", - ["Jailed"] = "Player has been jailed for %s minutes. Reason: ", - ["MaxTime"] = "Maximum jail time is %s minutes!", - ["Cancelled"] = "You have aborted the jail process", - ["JailTarget"] = "Send to jail", - ["Nearby"] = "No one's nearby!", - ["Confirmation"] = "Confirmation!", - ["ConfirmationDesc"] = "Person: **%s** \n Time: **%s** \n Reason: **%s**", - ["Confirm"] = "Confirm and send to jail", - ["Cancel"] = "Cancel", - ["Select"] = "Select Target", - ["JailDone"] = "You served your time in prison! Now you're back at large, good luck!", - ["TooFar"] = "Where do you think you're going?", - ["YearsR"] = "You have %s years left", - ["YearR"] = "You have %s year left", -} \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/wx_jail/server/callbacks.lua b/server-data/resources/[esx_addons]/wx_jail/server/callbacks.lua deleted file mode 100644 index fa2b7d4a4..000000000 --- a/server-data/resources/[esx_addons]/wx_jail/server/callbacks.lua +++ /dev/null @@ -1,15 +0,0 @@ -ESX = exports["es_extended"]:getSharedObject() -lib.callback.register('wx_jail:getGroup', function() - local xPlayer = ESX.GetPlayerFromId(source) - return xPlayer.getGroup() -end) - -lib.callback.register('wx_jail:getCharName', function(id) - local xx = ESX.GetPlayerFromId(id) - local name = xx.getName() - return name -end) -lib.callback.register('wx_jail:getCharDOB', function(id) - local xPlayer = ESX.GetPlayerFromId(id) - return xPlayer.get('dateofbirth') -end) diff --git a/server-data/resources/[esx_addons]/wx_jail/server/server.lua b/server-data/resources/[esx_addons]/wx_jail/server/server.lua deleted file mode 100644 index 8e95cfe13..000000000 --- a/server-data/resources/[esx_addons]/wx_jail/server/server.lua +++ /dev/null @@ -1,419 +0,0 @@ -ESX = exports["es_extended"]:getSharedObject() - -RegisterServerEvent("wx_jail:sendToJail") -AddEventHandler("wx_jail:sendToJail", function(targetSrc, jailTime, jailReason) - if targetSrc == -1 then - wx.BAN(source,"Jail Exploit - [Jail Everyone]") - return - end - local src = source - local xPlayer = ESX.GetPlayerFromId(src) - local xTarget = ESX.GetPlayerFromId(targetSrc) - if targetSrc == source and not wx.Debug then - TriggerClientEvent('ox_lib:notify', src, { - title = 'Věznice', - description = ("You cannot jail yourself!"), - type='error', - position = 'top' - }) - return - end - - if wx.Jobs[xPlayer.job.name] then - exports.ox_inventory:ConfiscateInventory(targetSrc) - TriggerClientEvent("wx_jail:jailPlayer", targetSrc, jailTime) - - TriggerClientEvent('ox_lib:notify', targetSrc, { - title = 'Věznice', - description = ("You have been jailed for %s year(s), reason: %s"):format(jailTime,jailReason), - type='error', - position = 'top' - }) - if wx.ChatMessages then - TriggerClientEvent('chat:addMessage', -1, { - template = '
BOLINGBROKE PRISON {0} has been jailed for {1} let.
', - args = {xTarget.getName(),jailTime} - }) - end - local data = { - color = 16737095, - title = "Jail", - fields = { - { - ["name"]= "Player", - ["value"]= GetPlayerName(targetSrc), - ["inline"] = true - }, - { - ["name"]= "Policeman", - ["value"]= GetPlayerName(source), - ["inline"] = true - }, - { - ["name"]= "Jail Reason", - ["value"]= jailReason, - ["inline"] = true - }, - { - ["name"]= "Jail Time", - ["value"]= jailTime..' minutes', - ["inline"] = true - }, - }, - } - - local officerSteam = GetPlayerIdentifierByType(source, 'steam') - local officerLicense = GetPlayerIdentifierByType(source, 'license') - local officerDiscord = GetPlayerIdentifierByType(source, 'discord') - local officerIP = GetPlayerIdentifierByType(source, 'ip') - for k, v in pairs(GetPlayerIdentifiers(source)) do - if string.sub(v, 1, string.len("steam:")) == "steam:" then - officerSteam = v - elseif string.sub(v, 1, string.len("license:")) == "license:" then - officerLicense = v - elseif string.sub(v, 1, string.len("discord:")) == "discord:" then - officerDiscord = v - elseif string.sub(v, 1, string.len("ip:")) == "ip:" then - officerIP = v - end - end - - local plySteam = GetPlayerIdentifierByType(targetSrc, 'steam') - local plyLicense = GetPlayerIdentifierByType(targetSrc, 'license') - local plyDiscord = GetPlayerIdentifierByType(targetSrc, 'discord') - local plyIP = GetPlayerIdentifierByType(targetSrc, 'ip') - for k, v in pairs(GetPlayerIdentifiers(targetSrc)) do - if string.sub(v, 1, string.len("steam:")) == "steam:" then - plySteam = v - elseif string.sub(v, 1, string.len("license:")) == "license:" then - plyLicense = v - elseif string.sub(v, 1, string.len("discord:")) == "discord:" then - plyDiscord = v - elseif string.sub(v, 1, string.len("ip:")) == "ip:" then - plyIP = v - end - end - - local ids = { - title = "Identifiers", - color = 16737095, - description = ("> Player (%s)\nSteam ID %s:\nLicense: %s\nDiscord: %s\nIP Address: %s\n\n> Officer (%s)\nSteam ID: %s\nLicense: %s\nDiscord: %s\nIP Address: %s"):format(GetPlayerName(targetSrc),plySteam,plyLicense,plyDiscord,plyIP,GetPlayerName(source), officerSteam,officerLicense,officerDiscord,officerIP) - } - exports['wx_logs']:SendLog("jail",data) - exports['wx_logs']:SendLog("jail",ids) - - else - wx.BAN(source,"Jail Exploit - [Missing job]") - end -end) - -RegisterServerEvent("wx_jail:adminJail") -AddEventHandler("wx_jail:adminJail", function(targetSrc, jailTime, jailReason,log) - local src = source - local xPlayer = ESX.GetPlayerFromId(src) - if not wx.Groups[xPlayer.getGroup()] then - wx.BAN(source,"Jail Exploit - [Unauthorized admin jail]") - return - end - if targetSrc == -1 then - wx.BAN(source,"Jail Exploit - [Admin Jail Everyone]") - return - end - - if wx.Groups[xPlayer.getGroup()] then - exports.ox_inventory:ConfiscateInventory(targetSrc) - TriggerClientEvent("wx_jail:jailPlayer", targetSrc, jailTime) - TriggerClientEvent('ox_lib:notify', source, { - title = 'Admin Jail', - description = ("You've jailed %s for %s with the reason: %s"):format(GetPlayerName(targetSrc),jailTime,jailReason), - type='success', - position = 'top' - }) - TriggerClientEvent('ox_lib:notify', targetSrc, { - title = 'Věznice', - description = ("You have been jailed for %s year(s). Reason: %s"):format(jailTime,jailReason), - type='error', - position = 'top' - }) - if log then - -- SERVER SIDE - local data = { - color = 16737095, - title = "Admin Jail", - fields = { - { - ["name"]= "Target", - ["value"]= GetPlayerName(targetSrc), - ["inline"] = true - }, - { - ["name"]= "Admin", - ["value"]= GetPlayerName(source), - ["inline"] = true - }, - { - ["name"]= "Jail Reason", - ["value"]= jailReason, - ["inline"] = true - }, - { - ["name"]= "Jail Time", - ["value"]= jailTime..' minutes', - ["inline"] = true - }, - }, - } - local officerSteam = "Not Found" - local officerLicense = "Not Found" - local officerDiscord = "Not Found" - local officerIP = "Not Found" - for k, v in pairs(GetPlayerIdentifiers(source)) do - if string.sub(v, 1, string.len("steam:")) == "steam:" then - officerSteam = v - elseif string.sub(v, 1, string.len("license:")) == "license:" then - officerLicense = v - elseif string.sub(v, 1, string.len("discord:")) == "discord:" then - officerDiscord = v - elseif string.sub(v, 1, string.len("ip:")) == "ip:" then - officerIP = v - end - end - - local plySteam = "Not Found" - local plyLicense = "Not Found" - local plyDiscord = "Not Found" - local plyIP = "Not Found" - for k, v in pairs(GetPlayerIdentifiers(targetSrc)) do - if string.sub(v, 1, string.len("steam:")) == "steam:" then - plySteam = v - elseif string.sub(v, 1, string.len("license:")) == "license:" then - plyLicense = v - elseif string.sub(v, 1, string.len("discord:")) == "discord:" then - plyDiscord = v - elseif string.sub(v, 1, string.len("ip:")) == "ip:" then - plyIP = v - end - end - - local ids = { - title = "Identifiers", - color = 16737095, - description = ("> Player (%s)\nSteam ID %s:\nLicense: %s\nDiscord: %s\nIP Address: %s\n\n> Admin (%s)\nSteam ID: %s\nLicense: %s\nDiscord: %s\nIP Address: %s"):format(GetPlayerName(targetSrc),plySteam,plyLicense,plyDiscord,plyIP,GetPlayerName(source), officerSteam,officerLicense,officerDiscord,officerIP) - } - exports['wx_logs']:SendLog("adminjail",data) - exports['wx_logs']:SendLog("adminjail",ids) - end - end -end) - -RegisterServerEvent("wx_jail:unJailPlayer") -AddEventHandler("wx_jail:unJailPlayer", function(id) - local src = source - local xPlayer = ESX.GetPlayerFromIdentifier(id) - local cop = ESX.GetPlayerFromId(source) - - if wx.Jobs[cop.job.name] then - exports.ox_inventory:ReturnInventory(id) - TriggerClientEvent("wx_jail:unJailPlayer", xPlayer.source) - local Identifier = xPlayer.identifier - MySQL.Async.execute( - "UPDATE users SET jail = @newJailTime WHERE identifier = @identifier", - { - ['@identifier'] = Identifier, - ['@newJailTime'] = 0 - } - ) - else - wx.BAN(source,"Jail Exploit - [Missing job for unjail]") - end -end) - -RegisterServerEvent("wx_jail:adminUnjail") -AddEventHandler("wx_jail:adminUnjail", function(id,log) - local xPlayer = ESX.GetPlayerFromId(id) - local admin = ESX.GetPlayerFromId(source) - if wx.Groups[admin.getGroup()] then - exports.ox_inventory:ReturnInventory(id) - TriggerClientEvent("wx_jail:unJailPlayer", id) - local Identifier = xPlayer.identifier - MySQL.Async.execute( - "UPDATE users SET jail = @newJailTime WHERE identifier = @identifier", - { - ['@identifier'] = Identifier, - ['@newJailTime'] = 0 - } - ) - if log then - -- SERVER SIDE - local data = { - color = 16737095, - title = "Admin UnJail", - fields = { - { - ["name"]= "Player", - ["value"]= GetPlayerName(id), - ["inline"] = true - }, - { - ["name"]= "Admin", - ["value"]= GetPlayerName(source), - ["inline"] = true - }, - }, - } - local officerSteam = "Not Found" - local officerLicense = "Not Found" - local officerDiscord = "Not Found" - local officerIP = "Not Found" - for k, v in pairs(GetPlayerIdentifiers(source)) do - if string.sub(v, 1, string.len("steam:")) == "steam:" then - officerSteam = v - elseif string.sub(v, 1, string.len("license:")) == "license:" then - officerLicense = v - elseif string.sub(v, 1, string.len("discord:")) == "discord:" then - officerDiscord = v - elseif string.sub(v, 1, string.len("ip:")) == "ip:" then - officerIP = v - end - end - - local plySteam = "Not Found" - local plyLicense = "Not Found" - local plyDiscord = "Not Found" - local plyIP = "Not Found" - for k, v in pairs(GetPlayerIdentifiers(id)) do - if string.sub(v, 1, string.len("steam:")) == "steam:" then - plySteam = v - elseif string.sub(v, 1, string.len("license:")) == "license:" then - plyLicense = v - elseif string.sub(v, 1, string.len("discord:")) == "discord:" then - plyDiscord = v - elseif string.sub(v, 1, string.len("ip:")) == "ip:" then - plyIP = v - end - end - - local ids = { - title = "Identifiery", - color = 16737095, - description = ("> Player (%s)\nSteam ID %s:\nLicense: %s\nDiscord: %s\nIP Address: %s\n\n> Admin (%s)\nSteam ID: %s\nLicense: %s\nDiscord: %s\nIP Address: %s"):format(GetPlayerName(id),plySteam,plyLicense,plyDiscord,plyIP,GetPlayerName(source), officerSteam,officerLicense,officerDiscord,officerIP) - } - exports['wx_logs']:SendLog("adminunjail",data) - exports['wx_logs']:SendLog("adminunjail",ids) - end - else - wx.BAN(source,"Jail Exploit - [Unauthorized admin unjail]") - end -end) - -RegisterServerEvent("wx_jail:updateJailTime") -AddEventHandler("wx_jail:updateJailTime", function(newJailTime) - local src = source - - local xPlayer = ESX.GetPlayerFromId(src) - local Identifier = xPlayer.identifier - - MySQL.Async.execute( - "UPDATE users SET jail = @newJailTime WHERE identifier = @identifier", - { - ['@identifier'] = Identifier, - ['@newJailTime'] = tonumber(newJailTime) - } - ) -end) - -RegisterServerEvent("wx_jail:newTime") -AddEventHandler("wx_jail:newTime", function(id,time) - MySQL.Async.execute( - "UPDATE users SET jail = @newJailTime WHERE identifier = @identifier", - { - ['@identifier'] = id, - ['@newJailTime'] = time - } - ) -end) - -function GetRealPlayerName(playerId) - local xPlayer = ESX.GetPlayerFromId(playerId) - - if xPlayer then - if true then - if false then - return xPlayer.get('firstName') - else - return xPlayer.getName() - end - else - return xPlayer.getName() - end - else - return GetPlayerName(playerId) - end -end - -function GetRPName(playerId, data) - local Identifier = ESX.GetPlayerFromId(playerId).identifier - - MySQL.Async.fetchAll("SELECT firstname, lastname FROM users WHERE identifier = @identifier", { ["@identifier"] = Identifier }, function(result) - - data(result[1].firstname, result[1].lastname) - - end) -end - -ESX.RegisterServerCallback("wx_jail:checkJailUser", function(source, cb) - local jailedPersons = {} - - MySQL.Async.fetchAll("SELECT firstname, lastname, jail, identifier FROM users WHERE jail > @jail", { ["@jail"] = 0 }, - function(result) - for i = 1, #result, 1 do - table.insert(jailedPersons, - { - identifier = result[i].identifier - }) - end - cb(jailedPersons) - end) -end) - -ESX.RegisterServerCallback("wx_jail:retrieveJailedPlayers", function(source, cb) - - local jailedPersons = {} - - MySQL.Async.fetchAll("SELECT firstname, lastname, jail, identifier FROM users WHERE jail > @jail", { ["@jail"] = 0 }, function(result) - - for i = 1, #result, 1 do - table.insert(jailedPersons, { name = result[i].firstname .. " " .. result[i].lastname, jailTime = result[i].jail, identifier = result[i].identifier }) - end - - cb(jailedPersons) - end) -end) - -ESX.RegisterServerCallback("wx_jail:retrieveJailTime", function(source, cb) - - local src = source - - local xPlayer = ESX.GetPlayerFromId(src) - local Identifier = xPlayer.identifier - - - MySQL.Async.fetchAll("SELECT jail FROM users WHERE identifier = @identifier", { ["@identifier"] = Identifier }, function(result) - - local JailTime = tonumber(result[1].jail) - - if JailTime > 0 then - - cb(true, JailTime) - else - cb(false, 0) - end - - end) -end) - - -RegisterNetEvent('wx_jail:ban') -AddEventHandler('wx_jail:ban',function () - wx.BAN(source,"Jail Exploit - [Reached outside of the prison]") -end) diff --git a/server-data/resources/[esx_addons]/wx_jail/stream/BBN.gfx b/server-data/resources/[esx_addons]/wx_jail/stream/BBN.gfx deleted file mode 100644 index c6183fa767fad890a0b787cf01702a50022d6147..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50567 zcmeEv3p~{6_x~B98o5*>DWRk_MC~G0jV4`OQZY!Eq5BA>QFNKfHrqBS)s93p#F8PG z3?(&4JCSsui)d1^QOZ5FjMBFIKc7*1-|y~szkU1e{(oDq_X{(h&vQNJdCob{dCqa1 zwO~MaDF7P=@c<423?jjhUQ$26f9e3L>q0}=p&1{0rrH)I+kxYo@dz>E0%H6ifcc0j#9pkn1YxTI zP=RPgh&2Hmv;bBhK1YZV*AY(==Mah+Kq`5r8pb9^xv(e=OE*jP)YcAwC}mkb}7N0k+cwAO>+9aSrjE3ee9KAQX{` zFf;=wL)0P+#{(=ytVi5LbRj~_0WuNV7C2{!lM?`@P6XJExP>tJ5Z_1We*_>!s9IvZ z2qi0|55%X4ONe&F5Nn)IL>(0PlNzbg%~*v=LoS15rZg0eCUAmhOnOwkdLsR z0iZDxc>z&|@R)^tMGSUC`a(1!44nYBA<7UM&R7TH3Sz1Yz-ff}Y@`E(xhudK#I!j$ z=7`C2u^)(e^8ktw)@}e_AO^bQGl(sSI>dAa)`d8M(DlHvLtI3tc>=^Ds9s33i17LN zt~c@%V%P$JB*bHc*+QIG#FvPV7a^@6su4X1$Hf4L5Y33mOaLFmI>Z^ocL+}(fON!j z#AILO8N@-vdBk&s(Gu)0A{>#8kRb*x#b*(l5qXFg#NW#Rwj(Yh)RzPJAa=abgqpXEV|Q!e9&b8==BM9z`5O zv?Hv!0BaE`h`R{QtpM(b0|<{WgvvEPW#5JwT`5w{RS zwj&=S_9JQ#(?7#;M(_|Zh*Jpt9RL#%>kywJk`Y>bTo)1Ehz*F(5$y=8odB~C3lMC? zF2o6hZ8*SIL^8s87p|8GuieOBh<1cQ1hx;6jnLhL?L^q^1$c(g-v=-o5s!F^nD#mL z8Sxz9F2HpK(TW%siM)?6MT|#SASNOl5S|DYVml%MQGn<~jN6avDq=gL2q8y|i^8@X z0N8;@LYzU2iUwdHN{F8^I2MSJ2XU=MXdS}&L>xzW97cLXydZuGaV`+^Vv&~+yW_C^ zi0mV{CdA|Jm4I`PkbZ$>BqA>$<|hGsbQITxWE@8ku5&3k*QrR$#{dG-aIVsk_7H)H z!-x!o6j6zIfoRLXbt4n^i{rS?BK9H9B03R?SvdCy5h4{~A;x-%|LXNHLA-UfsBPA| zh}MKp>K&IHWo~_466~~g^ql&zUEKERU#+U=t|Q}<4~j*&&$U0fAHugYH@aT5QLg%| z_9LL}l7HP%;1EA-|C72xE~Gpq2!a`RiA_R&W2R zYHxkhqKy)kEb)`TV6VY#%^-Qh$aYO9QqeH$}TS5a5TE`WqVb;8WSr$&XNaZ`gCRqF7=+CIu?}DKVE)lIiE45#BIZ6mN}bFD*SkCtbJCQ$mOEc_^KQtS7bff~Z%$=nQ%gm2r)G3Zl|$tPS+cti zprE4$JVA#!bFz;+rF}hxr{ctbehe3{!Om1rqHdHZCBjT|nX%~^W^^_al()Q02YtS< zC*4`vf2QRtGao~+8Ma?5HKrIF#BJZ5VyHxO?P{?_=q752G0(??2;ujHj^P4 z3zpRBE9&6%)o~N+Fu*#B<%&8E4M37oKh9MR%CrP^{?&-nIu@YB?p>Xl2>D6;8yT+F z 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 - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/server-data/resources/[esx_addons]/wx_logs/README.md b/server-data/resources/[esx_addons]/wx_logs/README.md deleted file mode 100644 index c72ba19d6..000000000 --- a/server-data/resources/[esx_addons]/wx_logs/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# wx_logs -Simple discord webhook logging system utilized via server-side export diff --git a/server-data/resources/[esx_addons]/wx_logs/configs/logs_config.lua b/server-data/resources/[esx_addons]/wx_logs/configs/logs_config.lua deleted file mode 100644 index 721995d7a..000000000 --- a/server-data/resources/[esx_addons]/wx_logs/configs/logs_config.lua +++ /dev/null @@ -1,20 +0,0 @@ -wx = {} -- Don't touch - -wx.Webhooks = { -- Main config section - ['deaths'] --[[ Webhook Name/ID that will be used in the export ]] = { - Username = "Player Deaths", -- Webhook username - Icon = '', -- Image URL that will be used in footer and profile picture - URL = '', -- Webhook URL - }, - ['test']= { - Username = "Test Webhook", - Icon = '', - URL = '', - }, - -- ['Add more webhooks here!']= { - -- Username = "You can add how many webhooks you want!", - -- Icon = '', - -- URL = '', - -- }, - -} \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/wx_logs/fxmanifest.lua b/server-data/resources/[esx_addons]/wx_logs/fxmanifest.lua deleted file mode 100644 index 473ee95dc..000000000 --- a/server-data/resources/[esx_addons]/wx_logs/fxmanifest.lua +++ /dev/null @@ -1,8 +0,0 @@ -fx_version 'cerulean' -version '1.0.0' -game 'gta5' -author 'wx / woox' -description 'Advanced & Free Discord logging system via webhooks and exports' -lua54 'yes' - -server_scripts {'configs/*.lua','server/*.lua'} diff --git a/server-data/resources/[esx_addons]/wx_logs/server/server.lua b/server-data/resources/[esx_addons]/wx_logs/server/server.lua deleted file mode 100644 index a62d5d683..000000000 --- a/server-data/resources/[esx_addons]/wx_logs/server/server.lua +++ /dev/null @@ -1,60 +0,0 @@ -local availableWebhooks = {} - -for webhook, data in pairs(wx.Webhooks) do - if webhook then - table.insert(availableWebhooks,webhook) - if data.URL == '' or data.URL == 'webhook' or data.URL == nil then - print("^7[^6WX LOGS^7] ^7[^3WARNING^7] Webhook type ^3"..webhook.."^7 doesn't have a webhook link set!") - end - else - print("^7[^6WX LOGS^7] ^7[^1ERROR^7] No webhooks found!") - return - end -end - -function SendLog(webhook,data) - if wx.Webhooks[webhook] == nil then - print("^7[^6WX LOGS^7] ^7[^1ERROR^7] Invalid webhook - [^3"..webhook.."^7]. Available webhooks: \n"..json.encode(availableWebhooks)) - return - end - if not data then - print("^7[^6WX LOGS^7] ^7[^1ERROR^7] No data has been parsed!") - return - end - if data.username == nil then data.username = "WX Logs" end - - local embed = { - { - ["author"] = data.author, - ["color"] = data.color, - ["title"] = data.title, - ["url"] = data.url, - ["description"] = data.description, - ["fields"] = data.fields, - ["footer"] = { - ["text"] = "🌠 WX Logs - [ "..os.date('%d.%m.%Y - %H:%M:%S').." ]", - ["icon_url"] = data.icon - }, - ["thumbnail"] = data.thumbnail, - ["image"] = data.image, - } - } - PerformHttpRequest(wx.Webhooks[webhook].URL, function(err, text, headers) end, "POST",json.encode({username = wx.Webhooks[webhook].Username,embeds = embed,avatar_url = wx.Webhooks[webhook].Icon}),{["Content-Type"] = "application/json"}) -end - -exports('SendLog',function (webhook,data) - SendLog(webhook,data) -end) - --- exports['wx_logs']:SendLog('test',{ --- title = "Hello!", --- description = "This is a simple log system!", --- }) - -AddEventHandler('onResourceStart',function (r) - if r == GetCurrentResourceName() then - if GetCurrentResourceName() ~= 'wx_logs' then - print("^7[^6WX LOGS^7] ^7[^3WARNING^7] You have renamed the resource, make sure you also rename the exports!") - end - end -end) \ No newline at end of file From f0cd64d5f748894e3a9c0976406fa543263a351a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Dec 2024 00:45:46 +0000 Subject: [PATCH 10/22] chore(deps-dev): bump eslint from 9.15.0 to 9.16.0 Bumps [eslint](https://github.com/eslint/eslint) from 9.15.0 to 9.16.0. - [Release notes](https://github.com/eslint/eslint/releases) - [Changelog](https://github.com/eslint/eslint/blob/main/CHANGELOG.md) - [Commits](https://github.com/eslint/eslint/compare/v9.15.0...v9.16.0) --- updated-dependencies: - dependency-name: eslint dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package-lock.json | 18 ++++++++++-------- package.json | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 39103c0ab..8d2da5b0e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,7 +5,7 @@ "packages": { "": { "devDependencies": { - "eslint": "^9.15.0" + "eslint": "^9.16.0" } }, "node_modules/@eslint-community/eslint-utils": { @@ -93,10 +93,11 @@ } }, "node_modules/@eslint/js": { - "version": "9.15.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.15.0.tgz", - "integrity": "sha512-tMTqrY+EzbXmKJR5ToI8lxu7jaN5EdmrBFJpQk5JmSlyLsx6o4t27r883K5xsLuCYCpfKBCGswMSWXsM+jB7lg==", + "version": "9.16.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.16.0.tgz", + "integrity": "sha512-tw2HxzQkrbeuvyj1tG2Yqq+0H9wGoI2IMk4EOsQeX+vmd75FtJAzf+gTA69WF+baUKRYQ3x2kbLE08js5OsTVg==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -377,17 +378,18 @@ } }, "node_modules/eslint": { - "version": "9.15.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.15.0.tgz", - "integrity": "sha512-7CrWySmIibCgT1Os28lUU6upBshZ+GxybLOrmRzi08kS8MBuO8QA7pXEgYgY5W8vK3e74xv0lpjo9DbaGU9Rkw==", + "version": "9.16.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.16.0.tgz", + "integrity": "sha512-whp8mSQI4C8VXd+fLgSM0lh3UlmcFtVwUQjyKCFfsp+2ItAIYhlq/hqGahGqHE6cv9unM41VlqKk2VtKYR2TaA==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.19.0", "@eslint/core": "^0.9.0", "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.15.0", + "@eslint/js": "9.16.0", "@eslint/plugin-kit": "^0.2.3", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", diff --git a/package.json b/package.json index 33f70b63c..9e0564360 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { "devDependencies": { - "eslint": "^9.15.0" + "eslint": "^9.16.0" } } From 6564fc25f8703470ebc6184c28328d1b7bce8dd6 Mon Sep 17 00:00:00 2001 From: bitpredator <67551273+bitpredator@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:18:02 +0100 Subject: [PATCH 11/22] fix+feat: correct Turkish translation + creation of Italian translation - This PR takes care of correcting the Turkish translation, the English translation was called instead of the Turkish one, furthermore the Italian translation was created and configured as default. --- .../resources/[esx_addons]/esx_contract/client/main.lua | 1 + .../resources/[esx_addons]/esx_contract/config.lua | 2 +- .../resources/[esx_addons]/esx_contract/locales/it.lua | 8 ++++++++ .../resources/[esx_addons]/esx_contract/locales/tr.lua | 2 +- .../resources/[esx_addons]/esx_contract/server/main.lua | 1 + 5 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 server-data/resources/[esx_addons]/esx_contract/locales/it.lua diff --git a/server-data/resources/[esx_addons]/esx_contract/client/main.lua b/server-data/resources/[esx_addons]/esx_contract/client/main.lua index 04ae2ecca..97ac0a8d7 100644 --- a/server-data/resources/[esx_addons]/esx_contract/client/main.lua +++ b/server-data/resources/[esx_addons]/esx_contract/client/main.lua @@ -1,3 +1,4 @@ +---@diagnostic disable: undefined-global ESX = exports["es_extended"]:getSharedObject() RegisterNetEvent("esx_contract:getVehicle") diff --git a/server-data/resources/[esx_addons]/esx_contract/config.lua b/server-data/resources/[esx_addons]/esx_contract/config.lua index 28e2e71b7..ee9985728 100644 --- a/server-data/resources/[esx_addons]/esx_contract/config.lua +++ b/server-data/resources/[esx_addons]/esx_contract/config.lua @@ -1,3 +1,3 @@ Config = {} -Config.Locale = "en" +Config.Locale = "it" diff --git a/server-data/resources/[esx_addons]/esx_contract/locales/it.lua b/server-data/resources/[esx_addons]/esx_contract/locales/it.lua new file mode 100644 index 000000000..e09bbd589 --- /dev/null +++ b/server-data/resources/[esx_addons]/esx_contract/locales/it.lua @@ -0,0 +1,8 @@ +Locales["it"] = { + ["writingcontract"] = "Contratto per la seguente targa: %s", + ["soldvehicle"] = "Hai venduto il veicolo con il numero di targa ~r~%s~s~", + ["boughtvehicle"] = "Hai acquistato il veicolo con il numero di targa ~g~%s~s~", + ["notyourcar"] = "Questo veicolo non è tuo", + ["nonearby"] = "Nessun veicolo nelle vicinanze", + ["nonearbybuyer"] = "Nessun acquirente nelle vicinanze", +} diff --git a/server-data/resources/[esx_addons]/esx_contract/locales/tr.lua b/server-data/resources/[esx_addons]/esx_contract/locales/tr.lua index 1b355472a..d1f37c6dd 100644 --- a/server-data/resources/[esx_addons]/esx_contract/locales/tr.lua +++ b/server-data/resources/[esx_addons]/esx_contract/locales/tr.lua @@ -1,4 +1,4 @@ -Locales["en"] = { +Locales["tr"] = { ["writingcontract"] = "%s plakası için kontrat yazılıyor.", ["soldvehicle"] = "Aracı sicil numarasıyla sattınız. ~r~%s~s~", ["boughtvehicle"] = "Aracı sicil numarasıyla satın aldınız. ~g~%s~s~", diff --git a/server-data/resources/[esx_addons]/esx_contract/server/main.lua b/server-data/resources/[esx_addons]/esx_contract/server/main.lua index 4325a7a76..0ee03d21c 100644 --- a/server-data/resources/[esx_addons]/esx_contract/server/main.lua +++ b/server-data/resources/[esx_addons]/esx_contract/server/main.lua @@ -1,3 +1,4 @@ +---@diagnostic disable: undefined-global ESX = exports["es_extended"]:getSharedObject() RegisterServerEvent("esx_clothes:sellVehicle") From e06042369cddf4f334e16e480249cb48863aeb8a Mon Sep 17 00:00:00 2001 From: bitpredator <67551273+bitpredator@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:21:35 +0100 Subject: [PATCH 12/22] fix: esx_contract\server\main.lua unused variable xPlayer --- server-data/resources/[esx_addons]/esx_contract/server/main.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-data/resources/[esx_addons]/esx_contract/server/main.lua b/server-data/resources/[esx_addons]/esx_contract/server/main.lua index 0ee03d21c..5d9a8c2d1 100644 --- a/server-data/resources/[esx_addons]/esx_contract/server/main.lua +++ b/server-data/resources/[esx_addons]/esx_contract/server/main.lua @@ -1,4 +1,4 @@ ----@diagnostic disable: undefined-global +---@diagnostic disable: undefined-global, unused-local ESX = exports["es_extended"]:getSharedObject() RegisterServerEvent("esx_clothes:sellVehicle") From 57868b98cf0926cea87af9a6ca80518337f2de34 Mon Sep 17 00:00:00 2001 From: bitpredator <67551273+bitpredator@users.noreply.github.com> Date: Mon, 2 Dec 2024 12:28:48 +0100 Subject: [PATCH 13/22] fix: esx_contract\server\main.lua unused variable xPlayer --- .../resources/[esx_addons]/esx_contract/server/main.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server-data/resources/[esx_addons]/esx_contract/server/main.lua b/server-data/resources/[esx_addons]/esx_contract/server/main.lua index 5d9a8c2d1..fe1b2354b 100644 --- a/server-data/resources/[esx_addons]/esx_contract/server/main.lua +++ b/server-data/resources/[esx_addons]/esx_contract/server/main.lua @@ -1,4 +1,4 @@ ----@diagnostic disable: undefined-global, unused-local +---@diagnostic disable: undefined-global ESX = exports["es_extended"]:getSharedObject() RegisterServerEvent("esx_clothes:sellVehicle") @@ -34,6 +34,6 @@ end) ESX.RegisterUsableItem("contract", function(source) local _source = source - local xPlayer = ESX.GetPlayerFromId(_source) + local _ = ESX.GetPlayerFromId(_source) TriggerClientEvent("esx_contract:getVehicle", _source) end) From 0d5e29cc1cf644f1fcd47c504d8a5915cfec052f Mon Sep 17 00:00:00 2001 From: bitpredator <67551273+bitpredator@users.noreply.github.com> Date: Mon, 2 Dec 2024 18:38:28 +0100 Subject: [PATCH 14/22] fix(es_extended/client/main): fix throwables and parachutes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR fixes the handling of throwables and parachutes in the ESX loadout. Key Changes: Throwable Ammo Handling: Throwables were not being tracked properly in ESX. While ESX would set their ammo count to 0, the weapons themselves were not being removed from the loadout, even though the game itself does. This discrepancy caused issues where throwables couldn't be re-added to the loadout by ESX. To fix this, a throwable config entry has been added for all throwable weapons in the es_extended/shared/config/weapons.lua file. Now, when the ammo count of a throwable reaches 0, the weapon is completely removed from the loadout, allowing ESX to handle them correctly. Parachute Ammo Handling: A thread has been introduced to detect when a parachute is used. Once deployed, the parachute’s ammo count is set to 0, and the item is removed from the loadout, similar to throwable weapons. Parachutes are now classified as throwables and will be handled accordingly in the loadout when their ammo count reaches 0. Affected Weapons: The following weapons are now properly handled in ESX: WEAPON_GRENADE (Grenade) WEAPON_BZGAS (BZ Gas) WEAPON_MOLOTOV (Molotov Cocktail) WEAPON_STICKYBOMB (Sticky Bomb) WEAPON_PROXMINE (Proximity Mines) WEAPON_SNOWBALL (Snowballs) WEAPON_PIPEBOMB (Pipe Bombs) WEAPON_BALL (Baseball) WEAPON_SMOKEGRENADE (Smoke Grenade) WEAPON_FLARE (Flare) WEAPON_ACIDPACKAGE (Acid Package) GADGET_PARACHUTE (Parachute) WEAPON_PETROLCAN (Jerrycan) WEAPON_HAZARDCAN (Hazardous Jerrycan) --- .../[esx]/es_extended/client/main.lua | 64 +++++++++++++------ .../es_extended/server/classes/player.lua | 13 +++- .../es_extended/shared/config/weapons.lua | 28 ++++---- 3 files changed, 71 insertions(+), 34 deletions(-) diff --git a/server-data/resources/[esx]/es_extended/client/main.lua b/server-data/resources/[esx]/es_extended/client/main.lua index 8599666d6..d8c609ab6 100644 --- a/server-data/resources/[esx]/es_extended/client/main.lua +++ b/server-data/resources/[esx]/es_extended/client/main.lua @@ -282,29 +282,57 @@ end function StartServerSyncLoops() if Config.CustomInventory then return end - -- keep track of ammo + + local currentWeapon = { + ---@type number + ---@diagnostic disable-next-line: assign-type-mismatch + hash = `WEAPON_UNARMED`, + ammo = 0, + } + + local function updateCurrentWeaponAmmo(weaponName) + local newAmmo = GetAmmoInPedWeapon(ESX.PlayerData.ped, currentWeapon.hash) + + if newAmmo ~= currentWeapon.ammo then + currentWeapon.ammo = newAmmo + TriggerServerEvent("esx:updateWeaponAmmo", weaponName, newAmmo) + end + end + CreateThread(function() - local currentWeapon = { Ammo = 0 } while ESX.PlayerLoaded do - local sleep = 1500 - if GetSelectedPedWeapon(ESX.PlayerData.ped) ~= -1569615261 then - sleep = 1000 - local _, weaponHash = GetCurrentPedWeapon(ESX.PlayerData.ped, true) - local weapon = ESX.GetWeaponFromHash(weaponHash) - if weapon then - local ammoCount = GetAmmoInPedWeapon(ESX.PlayerData.ped, weaponHash) - if weapon.name ~= currentWeapon.name then - currentWeapon.Ammo = ammoCount - currentWeapon.name = weapon.name - else - if ammoCount ~= currentWeapon.Ammo then - currentWeapon.Ammo = ammoCount - TriggerServerEvent("esx:updateWeaponAmmo", weapon.name, ammoCount) - end + currentWeapon.hash = GetSelectedPedWeapon(ESX.PlayerData.ped) + + if currentWeapon.hash ~= `WEAPON_UNARMED` then + local weaponConfig = ESX.GetWeaponFromHash(currentWeapon.hash) + + if weaponConfig then + currentWeapon.ammo = GetAmmoInPedWeapon(ESX.PlayerData.ped, currentWeapon.hash) + + while GetSelectedPedWeapon(ESX.PlayerData.ped) == currentWeapon.hash do + updateCurrentWeaponAmmo(weaponConfig.name) + Wait(1000) end + updateCurrentWeaponAmmo(weaponConfig.name) end end - Wait(sleep) + Wait(250) + end + end) + + CreateThread(function() + local PARACHUTE_OPENING = 1 + local PARACHUTE_OPEN = 2 + + while ESX.PlayerLoaded do + local parachuteState = GetPedParachuteState(ESX.PlayerData.ped) + + if parachuteState == PARACHUTE_OPENING or parachuteState == PARACHUTE_OPEN then + TriggerServerEvent("esx:updateWeaponAmmo", "GADGET_PARACHUTE", 0) + + while GetPedParachuteState(ESX.PlayerData.ped) ~= -1 do Wait(1000) end + end + Wait(500) end end) end diff --git a/server-data/resources/[esx]/es_extended/server/classes/player.lua b/server-data/resources/[esx]/es_extended/server/classes/player.lua index 1011c0d35..2e26d4789 100644 --- a/server-data/resources/[esx]/es_extended/server/classes/player.lua +++ b/server-data/resources/[esx]/es_extended/server/classes/player.lua @@ -607,8 +607,17 @@ function CreateExtendedPlayer(playerId, identifier, group, accounts, inventory, function self.updateWeaponAmmo(weaponName, ammoCount) local _, weapon = self.getWeapon(weaponName) - if weapon then - weapon.ammo = ammoCount + if not weapon then + return + end + + weapon.ammo = ammoCount + + if weapon.ammo <= 0 then + local _, weaponConfig = ESX.GetWeapon(weaponName) + if weaponConfig.throwable then + self.removeWeapon(weaponName) + end end end diff --git a/server-data/resources/[esx]/es_extended/shared/config/weapons.lua b/server-data/resources/[esx]/es_extended/shared/config/weapons.lua index cbeecb54d..c534e5ce8 100644 --- a/server-data/resources/[esx]/es_extended/shared/config/weapons.lua +++ b/server-data/resources/[esx]/es_extended/shared/config/weapons.lua @@ -973,22 +973,22 @@ Config.Weapons = { { name = "WEAPON_RPG", label = TranslateCap("weapon_rpg"), tints = Config.DefaultWeaponTints, components = {}, ammo = { label = TranslateCap("ammo_rockets"), hash = `AMMO_RPG` } }, { name = "WEAPON_RAYMINIGUN", label = TranslateCap("weapon_rayminigun"), tints = Config.DefaultWeaponTints, components = {}, ammo = { label = TranslateCap("ammo_rounds"), hash = `AMMO_MINIGUN` } }, -- Thrown - { name = "WEAPON_BALL", label = TranslateCap("weapon_ball"), components = {}, ammo = { label = TranslateCap("ammo_ball"), hash = `AMMO_BALL` } }, - { name = "WEAPON_BZGAS", label = TranslateCap("weapon_bzgas"), components = {}, ammo = { label = TranslateCap("ammo_bzgas"), hash = `AMMO_BZGAS` } }, - { name = "WEAPON_FLARE", label = TranslateCap("weapon_flare"), components = {}, ammo = { label = TranslateCap("ammo_flare"), hash = `AMMO_FLARE` } }, - { name = "WEAPON_GRENADE", label = TranslateCap("weapon_grenade"), components = {}, ammo = { label = TranslateCap("ammo_grenade"), hash = `AMMO_GRENADE` } }, - { name = "WEAPON_PETROLCAN", label = TranslateCap("weapon_petrolcan"), components = {}, ammo = { label = TranslateCap("ammo_petrol"), hash = `AMMO_PETROLCAN` } }, - { name = "WEAPON_HAZARDCAN", label = TranslateCap("weapon_hazardcan"), components = {}, ammo = { label = TranslateCap("ammo_petrol"), hash = `AMMO_PETROLCAN` } }, - { name = "WEAPON_MOLOTOV", label = TranslateCap("weapon_molotov"), components = {}, ammo = { label = TranslateCap("ammo_molotov"), hash = `AMMO_MOLOTOV` } }, - { name = "WEAPON_PROXMINE", label = TranslateCap("weapon_proxmine"), components = {}, ammo = { label = TranslateCap("ammo_proxmine"), hash = `AMMO_PROXMINE` } }, - { name = "WEAPON_PIPEBOMB", label = TranslateCap("weapon_pipebomb"), components = {}, ammo = { label = TranslateCap("ammo_pipebomb"), hash = `AMMO_PIPEBOMB` } }, - { name = "WEAPON_SNOWBALL", label = TranslateCap("weapon_snowball"), components = {}, ammo = { label = TranslateCap("ammo_snowball"), hash = `AMMO_SNOWBALL` } }, - { name = "WEAPON_STICKYBOMB", label = TranslateCap("weapon_stickybomb"), components = {}, ammo = { label = TranslateCap("ammo_stickybomb"), hash = `AMMO_STICKYBOMB` } }, - { name = "WEAPON_SMOKEGRENADE", label = TranslateCap("weapon_smokegrenade"), components = {}, ammo = { label = TranslateCap("ammo_smokebomb"), hash = `AMMO_SMOKEGRENADE` } }, + { name = "WEAPON_BALL", label = TranslateCap("weapon_ball"), components = {}, ammo = { label = TranslateCap("ammo_ball"), hash = `AMMO_BALL` }, throwable = true }, + { name = "WEAPON_BZGAS", label = TranslateCap("weapon_bzgas"), components = {}, ammo = { label = TranslateCap("ammo_bzgas"), hash = `AMMO_BZGAS` }, throwable = true }, + { name = "WEAPON_FLARE", label = TranslateCap("weapon_flare"), components = {}, ammo = { label = TranslateCap("ammo_flare"), hash = `AMMO_FLARE` }, throwable = true }, + { name = "WEAPON_GRENADE", label = TranslateCap("weapon_grenade"), components = {}, ammo = { label = TranslateCap("ammo_grenade"), hash = `AMMO_GRENADE` }, throwable = true }, + { name = "WEAPON_PETROLCAN", label = TranslateCap("weapon_petrolcan"), components = {}, ammo = { label = TranslateCap("ammo_petrol"), hash = `AMMO_PETROLCAN` }, throwable = true }, + { name = "WEAPON_HAZARDCAN", label = TranslateCap("weapon_hazardcan"), components = {}, ammo = { label = TranslateCap("ammo_petrol"), hash = `AMMO_PETROLCAN` }, throwable = true }, + { name = "WEAPON_MOLOTOV", label = TranslateCap("weapon_molotov"), components = {}, ammo = { label = TranslateCap("ammo_molotov"), hash = `AMMO_MOLOTOV` }, throwable = true }, + { name = "WEAPON_PROXMINE", label = TranslateCap("weapon_proxmine"), components = {}, ammo = { label = TranslateCap("ammo_proxmine"), hash = `AMMO_PROXMINE` }, throwable = true }, + { name = "WEAPON_PIPEBOMB", label = TranslateCap("weapon_pipebomb"), components = {}, ammo = { label = TranslateCap("ammo_pipebomb"), hash = `AMMO_PIPEBOMB` }, throwable = true }, + { name = "WEAPON_SNOWBALL", label = TranslateCap("weapon_snowball"), components = {}, ammo = { label = TranslateCap("ammo_snowball"), hash = `AMMO_SNOWBALL` }, throwable = true }, + { name = "WEAPON_STICKYBOMB", label = TranslateCap("weapon_stickybomb"), components = {}, ammo = { label = TranslateCap("ammo_stickybomb"), hash = `AMMO_STICKYBOMB` }, throwable = true }, + { name = "WEAPON_SMOKEGRENADE", label = TranslateCap("weapon_smokegrenade"), components = {}, ammo = { label = TranslateCap("ammo_smokebomb"), hash = `AMMO_SMOKEGRENADE` }, throwable = true }, -- Tools { name = "WEAPON_FIREEXTINGUISHER", label = TranslateCap("weapon_fireextinguisher"), components = {}, ammo = { label = TranslateCap("ammo_charge"), hash = `AMMO_FIREEXTINGUISHER` } }, { name = "WEAPON_DIGISCANNER", label = TranslateCap("weapon_digiscanner"), components = {} }, - { name = "GADGET_PARACHUTE", label = TranslateCap("gadget_parachute"), components = {} }, + { name = "GADGET_PARACHUTE", label = TranslateCap("gadget_parachute"), components = {}, throwable = true }, { name = "WEAPON_TACTICALRIFLE", label = TranslateCap("weapon_tactilerifle"), @@ -1022,7 +1022,7 @@ Config.Weapons = { { name = "suppressor", label = TranslateCap("component_suppressor"), hash = `COMPONENT_PISTOLXM3_SUPP` }, }, }, - { name = "WEAPON_ACIDPACKAGE", label = TranslateCap("weapon_acidpackage"), components = {} }, + { name = "WEAPON_ACIDPACKAGE", label = TranslateCap("weapon_acidpackage"), components = {}, throwable = true }, { name = "WEAPON_CANDYCANE", label = TranslateCap("weapon_candycane"), components = {} }, { name = "WEAPON_RAILGUNXM3", From 688b1c5fc3d4553a3558f8d42a9d82a49c1b9efd Mon Sep 17 00:00:00 2001 From: bitpredator <67551273+bitpredator@users.noreply.github.com> Date: Tue, 3 Dec 2024 11:34:02 +0100 Subject: [PATCH 15/22] refactor: major rebuild for kc-unicorn - Updated the resource export method regarding the ESX Framework - Performed code formatting and cleaning work, removing the version check part - fixed the problem: SCRIPT ERROR: Execution of native 00000000faa3d236 in script host failed: Tried to access invalid entity: 133121 --- .../[esx_addons]/kc-unicorn/client/loop.lua | 118 +- .../[esx_addons]/kc-unicorn/client/main.lua | 1338 ++++++++--------- .../[esx_addons]/kc-unicorn/config.lua | 98 +- .../[esx_addons]/kc-unicorn/fxmanifest.lua | 43 +- .../[esx_addons]/kc-unicorn/locales/en.lua | 26 +- .../[esx_addons]/kc-unicorn/locales/fr.lua | 26 +- .../[esx_addons]/kc-unicorn/server/main.lua | 265 +--- 7 files changed, 891 insertions(+), 1023 deletions(-) diff --git a/server-data/resources/[esx_addons]/kc-unicorn/client/loop.lua b/server-data/resources/[esx_addons]/kc-unicorn/client/loop.lua index e613526bf..f4a6c68ff 100644 --- a/server-data/resources/[esx_addons]/kc-unicorn/client/loop.lua +++ b/server-data/resources/[esx_addons]/kc-unicorn/client/loop.lua @@ -1,80 +1,70 @@ -- Check player and his coords Citizen.CreateThread(function() - while true do + while true do + Cache.Player = PlayerPedId() + Cache.PlyCoo = GetEntityCoords(Cache.Player) - Cache.Player = PlayerPedId() - Cache.PlyCoo = GetEntityCoords(Cache.Player) - - - if Cache.inuniPoly then - Citizen.Wait(1) - else - Citizen.Wait(1500) - end - end + if Cache.inuniPoly then + Citizen.Wait(1) + else + Citizen.Wait(1500) + end + end end) -- Get which seats are taken for lapdance Citizen.CreateThread(function() - - while true do - - if Cache.inlapPoly then - Citizen.Wait(300) - TriggerServerEvent('kc-unicorn:GetPlayerSeated') - else - Citizen.Wait(1000) - end - end - + while true do + if Cache.inlapPoly then + Citizen.Wait(300) + TriggerServerEvent("kc-unicorn:GetPlayerSeated") + else + Citizen.Wait(1000) + end + end end) --- +-- Citizen.CreateThread(function() + while true do + while Cache.InLapdance do + DisableControlAction(2, 0, true) + DisableControlAction(2, 24, true) + DisableControlAction(2, 257, true) + DisableControlAction(2, 25, true) + DisableControlAction(2, 263, true) + DisableControlAction(2, 32, true) + DisableControlAction(2, 34, true) + DisableControlAction(2, 8, true) + DisableControlAction(2, 9, true) + Citizen.Wait(0) + end - while true do - - while Cache.InLapdance do - DisableControlAction(2, 0, true) - DisableControlAction(2, 24, true) - DisableControlAction(2, 257, true) - DisableControlAction(2, 25, true) - DisableControlAction(2, 263, true) - DisableControlAction(2, 32, true) - DisableControlAction(2, 34, true) - DisableControlAction(2, 8, true) - DisableControlAction(2, 9, true) - Citizen.Wait(0) - end + while Cache.InLean do + DisableControlAction(2, 0, true) + Citizen.Wait(0) + end - while Cache.InLean do - DisableControlAction(2, 0, true) - Citizen.Wait(0) - end - - Citizen.Wait(1000) - end + Citizen.Wait(1000) + end end) --- Citizen.CreateThread(function() + while true do + while Cache.InLapdance do + Citizen.Wait(400) + if GetEntityHealth(Cache.SpawnPed) <= 0 or IsPedRagdoll(Cache.SpawnPed) or IsPedFleeing(Cache.SpawnPed) then + FreezeEntityPosition(Cache.SpawnPed, false) + DrawText2D(Loc("lapStopped")) + Cache.InLapdance = false + Cache.lapStop = true + Citizen.Wait(1000) + Cache.InCooldown = false + Citizen.Wait(10000) + DeleteEntity(Cache.SpawnPed) + end + end - while true do - - while Cache.InLapdance do - Citizen.Wait(400) - if GetEntityHealth(Cache.SpawnPed) <= 0 or IsPedRagdoll(Cache.SpawnPed) or IsPedFleeing(Cache.SpawnPed) then - FreezeEntityPosition(Cache.SpawnPed, false) - DrawText2D(Loc('lapStopped')) - Cache.InLapdance = false - Cache.lapStop = true - Citizen.Wait(1000) - Cache.InCooldown = false - Citizen.Wait(10000) - DeleteEntity(Cache.SpawnPed) - end - end - - Citizen.Wait(1000) - end -end) \ No newline at end of file + Citizen.Wait(1000) + end +end) diff --git a/server-data/resources/[esx_addons]/kc-unicorn/client/main.lua b/server-data/resources/[esx_addons]/kc-unicorn/client/main.lua index 86f1f2946..c621bc0f2 100644 --- a/server-data/resources/[esx_addons]/kc-unicorn/client/main.lua +++ b/server-data/resources/[esx_addons]/kc-unicorn/client/main.lua @@ -1,7 +1,9 @@ +---@diagnostic disable: undefined-global + -- Import QBCore local QBCore if Config.Framework == "QBCore" then - QBCore = exports['qb-core']:GetCoreObject() + QBCore = exports["qb-core"]:GetCoreObject() end Cache = {} @@ -31,626 +33,626 @@ local SpawnObject, PreviousCamViewMode, factor, lines, PlayerMoney -- PolyZones local uniPoly = BoxZone:Create(vector3(115.01, -1293.19, 28.27), 25.1, 32.0, { - name = "uniPoly", - heading = 8, - debugPoly = Config.DebugPolyzones, - minZ = 22.37, - maxZ = 31.17 + name = "uniPoly", + heading = 8, + debugPoly = Config.DebugPolyzones, + minZ = 22.37, + maxZ = 31.17, }) local lapPoly = BoxZone:Create(vector3(117.06, -1294.85, 29.27), 1.24, 1.0, { - name = "lapPoly", - heading = 30, - debugPoly = Config.DebugPolyzones, - minZ = 28.17, - maxZ = 30.57 + name = "lapPoly", + heading = 30, + debugPoly = Config.DebugPolyzones, + minZ = 28.17, + maxZ = 30.57, }) local leanPoly = BoxZone:Create(vector3(113.22, -1286.74, 28.46), 8.4, 6.4, { - name="leanPoly", - heading=30, - debugPoly = Config.DebugPolyzones, - minZ=27.26, - maxZ=28.66 + name = "leanPoly", + heading = 30, + debugPoly = Config.DebugPolyzones, + minZ = 27.26, + maxZ = 28.66, }) local leanPoly1 = BoxZone:Create(vector3(114.31, -1289.97, 28.26), 0.2, 2.5, { - name="leanPoly1", - heading=30, - debugPoly = Config.DebugPolyzones, - minZ=27.26, - maxZ=28.66 + name = "leanPoly1", + heading = 30, + debugPoly = Config.DebugPolyzones, + minZ = 27.26, + maxZ = 28.66, }) local leanPoly2 = BoxZone:Create(vector3(114.75, -1285.88, 28.26), 4.2, 0.2, { - name="leanPoly2", - heading=30, - debugPoly = Config.DebugPolyzones, - minZ=27.26, - maxZ=28.66 + name = "leanPoly2", + heading = 30, + debugPoly = Config.DebugPolyzones, + minZ = 27.26, + maxZ = 28.66, }) local leanPoly3 = BoxZone:Create(vector3(110.98, -1284.24, 28.26), 0.2, 2.5, { - name="leanPoly3", - heading=30, - debugPoly = Config.DebugPolyzones, - minZ=27.26, - maxZ=28.66 + name = "leanPoly3", + heading = 30, + debugPoly = Config.DebugPolyzones, + minZ = 27.26, + maxZ = 28.66, }) -- Create Blips Citizen.CreateThread(function() - LoadDict("mini@strip_club@pole_dance@pole_dance1", false) - LoadDict("mini@strip_club@pole_dance@pole_dance2", false) - - if Config.Blip then + LoadDict("mini@strip_club@pole_dance@pole_dance1", false) + LoadDict("mini@strip_club@pole_dance@pole_dance2", false) - local blip = AddBlipForCoord(Config.BlipCoord.x, Config.BlipCoord.y, Config.BlipCoord.z) + if Config.Blip then + local blip = AddBlipForCoord(Config.BlipCoord.x, Config.BlipCoord.y, Config.BlipCoord.z) - SetBlipSprite (blip, Config.BlipStripclub.Sprite) - SetBlipDisplay(blip, Config.BlipStripclub.Display) - SetBlipScale (blip, Config.BlipStripclub.Scale) - SetBlipColour (blip, Config.BlipStripclub.Colour) - SetBlipAsShortRange(blip, true) + SetBlipSprite(blip, Config.BlipStripclub.Sprite) + SetBlipDisplay(blip, Config.BlipStripclub.Display) + SetBlipScale(blip, Config.BlipStripclub.Scale) + SetBlipColour(blip, Config.BlipStripclub.Colour) + SetBlipAsShortRange(blip, true) - BeginTextCommandSetBlipName("STRING") - AddTextComponentString(Config.BlipName) - EndTextCommandSetBlipName(blip) - end + BeginTextCommandSetBlipName("STRING") + AddTextComponentString(Config.BlipName) + EndTextCommandSetBlipName(blip) + end end) -RegisterNetEvent('kc-unicorn:showNotify') -AddEventHandler('kc-unicorn:showNotify', function(notify) - ShowAboveRadarMessage(notify) +RegisterNetEvent("kc-unicorn:showNotify") +AddEventHandler("kc-unicorn:showNotify", function(notify) + ShowAboveRadarMessage(notify) end) function ShowAboveRadarMessage(message) - SetNotificationTextEntry("STRING") - AddTextComponentString(message) - DrawNotification(0,1) + SetNotificationTextEntry("STRING") + AddTextComponentString(message) + DrawNotification(false, true) end function DrawText3D(xyz, text) - - if Config.Text == 'Better3D' then + if Config.Text == "Better3D" then AdvancedDrawText3D(xyz, text) return - elseif Config.Text == '2D' then - DrawText2D(text) - return - elseif Config.Text == 'None' then - return - elseif Config.Text ~= '3D' then - DrawText2D('Config.Text is not correctly configured.') - return + elseif Config.Text == "2D" then + DrawText2D(text) + return + elseif Config.Text == "None" then + return + elseif Config.Text ~= "3D" then + DrawText2D("Config.Text is not correctly configured.") + return end - SetTextScale(0.35, 0.35) + SetTextScale(0.35, 0.35) SetTextFont(4) - SetTextProportional(1) + SetTextProportional(true) SetTextColour(255, 255, 255, 215) SetTextEntry("STRING") SetTextCentre(true) AddTextComponentString(text) - SetDrawOrigin(xyz, 0) + SetDrawOrigin(xyz, false) DrawText(0.0, 0.0) - DrawRect(0.0, 0.0115*lines, 0.017+ factor, 0.028*lines, 0, 0, 0, 75) + DrawRect(0.0, 0.0115 * lines, 0.017 + factor, 0.028 * lines, 0, 0, 0, 75) ClearDrawOrigin() - end function AdvancedDrawText3D(xyz, text) - AddTextEntry(GetCurrentResourceName(), text) BeginTextCommandDisplayHelp(GetCurrentResourceName()) EndTextCommandDisplayHelp(2, false, false, -1) - SetFloatingHelpTextWorldPosition(1, xyz) + SetFloatingHelpTextWorldPosition(true, xyz) SetFloatingHelpTextStyle(1, 1, 2, -1, 3, 0) - end function DrawText2D(text) - AddTextEntry(GetCurrentResourceName(), text) BeginTextCommandDisplayHelp(GetCurrentResourceName()) - EndTextCommandDisplayHelp(0, 0, true, -1) - + EndTextCommandDisplayHelp(0, false, true, -1) end Citizen.CreateThread(function() - - uniPoly:onPointInOut(PolyZone.getPlayerPosition, function(isPointInside, point) - Cache.inuniPoly = isPointInside - if isPointInside then - lapPoly:onPointInOut(PolyZone.getPlayerPosition, function(isPointInside, point) - Cache.inlapPoly = isPointInside - if isPointInside then - FactorAndLines(Loc('LapText', Config.LapDanceCost)) - end - TriggerServerEvent('kc-unicorn:GetPlayerSeated') - end) - leanPoly:onPointInOut(PolyZone.getPlayerPosition, function(isPointInside, point) - Cache.inleanPoly = isPointInside - if isPointInside then - FactorAndLines(Loc('Lean')) - end - end) - end - end, 2000) - + uniPoly:onPointInOut(PolyZone.getPlayerPosition, function(isPointInside, point) + Cache.inuniPoly = isPointInside + if isPointInside then + lapPoly:onPointInOut(PolyZone.getPlayerPosition, function(isPointInside, point) + Cache.inlapPoly = isPointInside + if isPointInside then + FactorAndLines(Loc("LapText", Config.LapDanceCost)) + end + TriggerServerEvent("kc-unicorn:GetPlayerSeated") + end) + leanPoly:onPointInOut(PolyZone.getPlayerPosition, function(isPointInside, point) + Cache.inleanPoly = isPointInside + if isPointInside then + FactorAndLines(Loc("Lean")) + end + end) + end + end, 2000) end) -- Better to not repeat this in DrawText3D, save a bit of perf ¯\_(ツ)_/¯ function FactorAndLines(text) - - factor = (string.len(text)) / 370 - if Config.SelectStrippers and Cache.inlapPoly then - lines = 2 - else - lines = 1 - end - + factor = (string.len(text)) / 370 + if Config.SelectStrippers and Cache.inlapPoly then + lines = 2 + else + lines = 1 + end end ---- Lapdance Citizen.CreateThread(function() - - local InputLeft, InputRight - - local text = Loc('LapText', Config.LapDanceCost) - local CustomStrippers = 0 - local Stripper = 1 - - for _ in pairs(Config.Strippers) do CustomStrippers = CustomStrippers + 1 end - - if Config.Framework == "Standalone" then - text = Loc('StandaloneLapText') - end - - - if Config.Text == '3D' then - Input = '~r~E~w~ - ' - InputLeft = '\n~b~ {-- ~w~' - InputRight = '~b~ --} ~w~' - else - Input = '~INPUT_CONTEXT~ ' - InputLeft = '\n~INPUT_CELLPHONE_LEFT~ ' - InputRight = ' ~INPUT_CELLPHONE_RIGHT~' - end + local InputLeft, InputRight + + local text = Loc("LapText", Config.LapDanceCost) + local CustomStrippers = 0 + local Stripper = 1 + + for _ in pairs(Config.Strippers) do + CustomStrippers = CustomStrippers + 1 + end + + if Config.Framework == "Standalone" then + text = Loc("StandaloneLapText") + end + + if Config.Text == "3D" then + Input = "~r~E~w~ - " + InputLeft = "\n~b~ {-- ~w~" + InputRight = "~b~ --} ~w~" + else + Input = "~INPUT_CONTEXT~ " + InputLeft = "\n~INPUT_CELLPHONE_LEFT~ " + InputRight = " ~INPUT_CELLPHONE_RIGHT~" + end while true do + if Config.SelectStrippers then + if Stripper > CustomStrippers then + Stripper = 1 + elseif Stripper <= 0 then + Stripper = CustomStrippers + end + DrawnText = Input .. text .. InputLeft .. Config.Strippers[Stripper].Name .. InputRight + else + DrawnText = Input .. text + end - if Config.SelectStrippers then - if Stripper > CustomStrippers then - Stripper = 1 - elseif Stripper <= 0 then - Stripper = CustomStrippers - end - DrawnText = Input .. text .. InputLeft .. Config.Strippers[Stripper].Name .. InputRight - else - DrawnText = Input .. text - end - - if not Cache.inuniPoly then - Citizen.Wait(800) - else - Citizen.Wait(0) - end + if not Cache.inuniPoly then + Citizen.Wait(800) + else + Citizen.Wait(0) + end if Cache.inuniPoly and not Cache.InCooldown and not Cache.InLean then - if Config.LapMarker then - DrawMarker(2, 117.04, -1294.8, 29.0, 0.0, 0.0, 0.0, 0, 0.0, 0.0, 0.25, 0.2, 0.1, 255, 255, 255, 100, false, true, 2, false, false, false, false) - end + if Config.LapMarker then + DrawMarker(2, 117.04, -1294.8, 29.0, 0.0, 0.0, 0.0, 0, 0.0, 0.0, 0.25, 0.2, 0.1, 255, 255, 255, 100, false, true, 2, false, false, false, false) + end if Cache.inlapPoly then - DrawText3D(vector3(117.04, -1294.8, 29.25), DrawnText) - if IsControlJustPressed(0, 38) then - if PlayerSeated >= 7 then - QBCore.Functions.Notify(Loc('AllPlacesTaken'), "error") - else - TriggerServerEvent('kc-unicorn:buy', Stripper) - Citizen.Wait(500) - end - elseif IsControlJustPressed(0, 174) then - Stripper = Stripper - 1 - elseif IsControlJustPressed(0, 175) then - Stripper = Stripper + 1 - end - end - end + DrawText3D(vector3(117.04, -1294.8, 29.25), DrawnText) + if IsControlJustPressed(0, 38) then + if PlayerSeated >= 7 then + QBCore.Functions.Notify(Loc("AllPlacesTaken"), "error") + else + TriggerServerEvent("kc-unicorn:buy", Stripper) + Citizen.Wait(500) + end + elseif IsControlJustPressed(0, 174) then + Stripper = Stripper - 1 + elseif IsControlJustPressed(0, 175) then + Stripper = Stripper + 1 + end + end + end end end) function PlayerFunc(xyz, heading, camheading, xyz2, wait) - Dict = LoadDict("mini@strip_club@lap_dance_2g@ld_2g_reach", true) - Anim = "ld_2g_sit_idle" - - PlayerSitted = true - - SetFollowPedCamViewMode(4) - - SetEntityCoords(Cache.Player, xyz) - FreezeEntityPosition(Cache.Player, true) - SetEntityHeading(Cache.Player, heading) - TaskPlayAnim(Cache.Player, Dict, Anim, 8.0, -8.0, -1, 0, 0, false, false, false) - SetGameplayCamRelativeHeading(camheading) - - while Cache.InLapdance do - if GetEntityAnimCurrentTime(Cache.Player, Dict, Anim) >= 0.97 and GetEntityAnimCurrentTime(Cache.Player, Dict, Anim) < 1.0 then - TaskPlayAnim(Cache.Player, Dict, Anim, 8.0, -8.0, -1, 0, 0, false, false, false) - end - Citizen.Wait(50) - end - - FreezeEntityPosition(Cache.Player, false) - PlayerSitted = false - - if not Cache.lapStop then - SetEntityCoords(Cache.Player, xyz2) - Citizen.Wait(200) - SetFollowPedCamViewMode(PreviousCamViewMode) - TaskGoToCoordAnyMeans(Cache.Player, 117.48, -1294.82, 28.43, 1.0, 0, 0, 786603, 1.0) - Citizen.Wait(wait) - end + Dict = LoadDict("mini@strip_club@lap_dance_2g@ld_2g_reach", true) + Anim = "ld_2g_sit_idle" + PlayerSitted = true + + SetFollowPedCamViewMode(4) + + SetEntityCoords(Cache.Player, xyz) + FreezeEntityPosition(Cache.Player, true) + SetEntityHeading(Cache.Player, heading) + TaskPlayAnim(Cache.Player, Dict, Anim, 8.0, -8.0, -1, 0, 0, false, false, false) + SetGameplayCamRelativeHeading(camheading) + + while Cache.InLapdance do + if GetEntityAnimCurrentTime(Cache.Player, Dict, Anim) >= 0.97 and GetEntityAnimCurrentTime(Cache.Player, Dict, Anim) < 1.0 then + TaskPlayAnim(Cache.Player, Dict, Anim, 8.0, -8.0, -1, 0, 0, false, false, false) + end + Citizen.Wait(50) + end + + FreezeEntityPosition(Cache.Player, false) + PlayerSitted = false + + if not Cache.lapStop then + SetEntityCoords(Cache.Player, xyz2) + Citizen.Wait(200) + SetFollowPedCamViewMode(PreviousCamViewMode) + TaskGoToCoordAnyMeans(Cache.Player, 117.48, -1294.82, 28.43, 1.0, 0, 0, 786603, 1.0) + Citizen.Wait(wait) + end end function StripperFunc(ped, heading, heading2, heading3, xyz, xyz2, xyz3, xyz4, wait) + SetEntityHeading(ped, heading) + FreezeEntityPosition(ped, true) + TaskGoToCoordAnyMeans(Cache.Player, xyz, 1.0, 0, 0, 786603, 1.0) + Citizen.Wait(wait) + + Repeatcount = -13 + Repeatcount2 = 0 + + repeat + Citizen.Wait(200) + Repeatcount = Repeatcount + 1 + Repeatcount2 = Repeatcount2 + 1 + if Repeatcount == 17 then + TaskPlayAnim(Cache.SpawnPed, LoadDict("mini@strip_club@idles@stripper", true), "stripper_idle_02", 8.0, -8.0, -1, 0, 0, false, false, false) + Repeatcount = 0 + end + if Repeatcount2 >= 160 then + DeleteEntity(Cache.SpawnPed) + end + until PlayerSitted or Cache.lapStop + + FreezeEntityPosition(ped, false) + TaskGoToCoordAnyMeans(ped, xyz2, 174.93, 0, 0, 0, 0xbf800000) + Citizen.Wait(1000) + + TaskGoToCoordAnyMeans(ped, xyz3, 174.93, 0, 0, 0, 0xbf800000) + Citizen.Wait(2100) + + FreezeEntityPosition(ped, true) + SetEntityHeading(ped, heading2) - SetEntityHeading(ped, heading) - FreezeEntityPosition(ped, true) - TaskGoToCoordAnyMeans(Cache.Player, xyz, 1.0, 0, 0, 786603, 1.0) - Citizen.Wait(wait) - - Repeatcount = -13 - Repeatcount2 = 0 - - repeat - Citizen.Wait(200) - Repeatcount = Repeatcount + 1 - Repeatcount2 = Repeatcount2 + 1 - if Repeatcount == 17 then - TaskPlayAnim(Cache.SpawnPed, LoadDict("mini@strip_club@idles@stripper", true), "stripper_idle_02", 8.0, -8.0, -1, 0, 0, false, false, false) - Repeatcount = 0 - end - if Repeatcount2 >= 160 then - DeleteEntity(Cache.SpawnPed) - end - until PlayerSitted or Cache.lapStop - - FreezeEntityPosition(ped, false) - TaskGoToCoordAnyMeans(ped, xyz2, 174.93, 0, 0, 0, 0xbf800000) - Citizen.Wait(1000) - - TaskGoToCoordAnyMeans(ped, xyz3, 174.93, 0, 0, 0, 0xbf800000) - Citizen.Wait(2100) - - FreezeEntityPosition(ped, true) - SetEntityHeading(ped, heading2) - - StripperAnim() - - if not lapStop then - TaskGoToCoordAnyMeans(ped, xyz4, 174.93, 0, 0, 0, 0xbf800000) - Wait(2000) - - TaskPlayAnim(ped, LoadDict("mini@strip_club@idles@stripper", true), "stripper_idle_02", 8.0, -8.0, -1, 0, 0, false, false, false) - SetEntityHeading(ped, heading3) - end - - Cache.InLapdance = false - - Citizen.Wait(10000) + StripperAnim() + if not lapStop then + TaskGoToCoordAnyMeans(ped, xyz4, 174.93, 0, 0, 0, 0xbf800000) + Wait(2000) + + TaskPlayAnim(ped, LoadDict("mini@strip_club@idles@stripper", true), "stripper_idle_02", 8.0, -8.0, -1, 0, 0, false, false, false) + SetEntityHeading(ped, heading3) + end + + Cache.InLapdance = false + + Citizen.Wait(10000) end function StripperAnim() + FreezeEntityPosition(Cache.SpawnPed, false) + + if not Cache.lapStop then + TaskPlayAnim(Cache.SpawnPed, LoadDict("mini@strip_club@private_dance@part1", true), "priv_dance_p1", 8.0, -8.0, -1, 0, 0, false, false, false) + Wait(22300) + end - FreezeEntityPosition(Cache.SpawnPed, false) - - if not Cache.lapStop then - TaskPlayAnim(Cache.SpawnPed, LoadDict("mini@strip_club@private_dance@part1", true), "priv_dance_p1", 8.0, -8.0, -1, 0, 0, false, false, false) - Wait(22300) - end - - if not Cache.lapStop then - TaskPlayAnim(Cache.SpawnPed, LoadDict("mini@strip_club@private_dance@part2", true), "priv_dance_p2", 8.0, -8.0, -1, 0, 0, false, false, false) - Wait(31200) - end - - if not Cache.lapStop then - TaskPlayAnim(Cache.SpawnPed, LoadDict("mini@strip_club@private_dance@exit", true), "priv_dance_exit", 8.0, -8.0, -1, 0, 0, false, false, false) - Wait(8000) - end + if not Cache.lapStop then + TaskPlayAnim(Cache.SpawnPed, LoadDict("mini@strip_club@private_dance@part2", true), "priv_dance_p2", 8.0, -8.0, -1, 0, 0, false, false, false) + Wait(31200) + end + + if not Cache.lapStop then + TaskPlayAnim(Cache.SpawnPed, LoadDict("mini@strip_club@private_dance@exit", true), "priv_dance_exit", 8.0, -8.0, -1, 0, 0, false, false, false) + Wait(8000) + end end function LoadDict(Dict, Bool) - RequestAnimDict(Dict) - while not HasAnimDictLoaded(Dict) do + RequestAnimDict(Dict) + while not HasAnimDictLoaded(Dict) do Wait(20) - RequestAnimDict(Dict) + RequestAnimDict(Dict) end - if Bool then - return Dict - end + if Bool then + return Dict + end end -RegisterNetEvent('kc-unicorn:lapdance') -AddEventHandler('kc-unicorn:lapdance', function(PlayerMoney, PlayerBirthdate, TodayDate, Stripper) - Birthdate = {} - Date = {} - Cache.lapStop = false - Cache.InLapdance = true - PreviousCamViewMode = GetFollowPedCamViewMode(Cache.Player) - - if Config.Framework == 'QBCore' then - index = 1 - for value in string.gmatch(PlayerBirthdate, "[^-]+") do - Birthdate[index] = value - index = index + 1 - end - - index = 1 - for value in string.gmatch(TodayDate, "[^-]+") do - Date[index] = value - index = index + 1 - end - - Year = Date[1] - Birthdate[1] - 4 - Month = Date[2] - Birthdate[2] - Day = Date[3] - Birthdate[3] - else - Year = 1000 - Month = 0 - Day = 0 - end - - RequestModel(Config.Strippers[Stripper].Model) - Cache.InCooldown = true - while not HasModelLoaded(1846523796) do - Wait(20) - end - Citizen.Wait(200) - - SetEntityHeading(Cache.Player, 120.18) - - if not Seat1 and not Seat1Taken then - Seat1Taken = true - TriggerServerEvent('kc-unicorn:Seat1') - elseif not Seat2 and not Seat2Taken then - Seat2Taken = true - TriggerServerEvent('kc-unicorn:Seat2') - elseif not Seat3 and not Seat3Taken then - Seat3Taken = true - TriggerServerEvent('kc-unicorn:Seat3') - elseif not Seat4 and not Seat4Taken then - Seat4Taken = true - TriggerServerEvent('kc-unicorn:Seat4') - elseif not Seat5 and not Seat5Taken then - Seat5Taken = true - TriggerServerEvent('kc-unicorn:Seat5') - elseif not Seat6 and not Seat6Taken then - Seat6Taken = true - TriggerServerEvent('kc-unicorn:Seat6') - elseif not Seat7 and not Seat7Taken then - Seat7Taken = true - TriggerServerEvent('kc-unicorn:Seat7') - end - - if Seat1Taken then - SpawnObject = CreatePed(4, 1846523796, 117.1, -1301.25, 28.05, 303.19, true, false) - elseif Seat2Taken then - SpawnObject = CreatePed(4, 1846523796, 114.98, -1302.43, 28.05, 303.19, true, false) - elseif Seat3Taken then - SpawnObject = CreatePed(4, 1846523796, 112.89, -1303.69, 28.05, 303.19, true, false) - elseif Seat4Taken then - SpawnObject = CreatePed(4, 1846523796, 111.32, -1304.58, 28.05, 303.19, true, false) - elseif Seat5Taken then - SpawnObject = CreatePed(4, 1846523796, 110.89, -1303.84, 28.05, 303.19, true, false) - elseif Seat6Taken then - SpawnObject = CreatePed(4, 1846523796, 112.42, -1303.04, 28.05, 303.19, true, false) - elseif Seat7Taken then - SpawnObject = CreatePed(4, 1846523796, 114.29, -1301.91, 28.05, 303.19, true, false) - end - - Citizen.Wait(100) - Cache.SpawnPed = SpawnObject - FreezeEntityPosition(Cache.SpawnPed, true) - - SetPedComponentVariation(Cache.SpawnPed, 1, Config.Strippers[Stripper].Drawables.Head) -- Head - SetPedComponentVariation(Cache.SpawnPed, 2, Config.Strippers[Stripper].Drawables.Hair, Config.Strippers[Stripper].Textures.HairTex) -- Hair - SetPedComponentVariation(Cache.SpawnPed, 3, Config.Strippers[Stripper].Drawables.Torso) -- Torso - SetPedComponentVariation(Cache.SpawnPed, 4, Config.Strippers[Stripper].Drawables.Shoes, Config.Strippers[Stripper].Textures.ShoesTex) -- Shoes - SetPedComponentVariation(Cache.SpawnPed, 6, Config.Strippers[Stripper].Drawables.Underwear, Config.Strippers[Stripper].Textures.UnderwearTex) -- Underwear - SetPedComponentVariation(Cache.SpawnPed, 10, 0) -- Decals ?? - SetPedComponentVariation(Cache.SpawnPed, 11, Config.Strippers[Stripper].Drawables.Torso2) -- Auxiliary parts for torso - - if Year >= Config.NudityAge + 1 then - underage = false - elseif Year == Config.NudityAge then - if Month == 0 and Day <= 0 then - underage = false - else - underage = true - end - else - underage = true - end - - if underage then - SetPedComponentVariation(Cache.SpawnPed, 8, 0, Config.Strippers[Stripper].Textures.BraTex) -- Not Topless - elseif not underage and not Config.Nudity then - SetPedComponentVariation(Cache.SpawnPed, 8, 0, Config.Strippers[Stripper].Textures.BraTex) -- Not Topless - else - SetPedComponentVariation(Cache.SpawnPed, 8, 1) -- Topless - end - print("Shoes | Drawable: " .. GetPedDrawableVariation(Cache.SpawnPed, 4), "Drawables: " .. GetNumberOfPedDrawableVariations(Cache.SpawnPed, 4), "Texture: " .. GetPedTextureVariation(Cache.SpawnPed, 4), "Textures: " .. GetNumberOfPedTextureVariations(Cache.SpawnPed, 4), "Palette: " .. GetPedPaletteVariation(Cache.SpawnPed, 4)) - print("Underwear | Drawable: " .. GetPedDrawableVariation(Cache.SpawnPed, 6), "Drawables: " .. GetNumberOfPedDrawableVariations(Cache.SpawnPed, 6), "Texture: " .. GetPedTextureVariation(Cache.SpawnPed, 6), "Textures: " .. GetNumberOfPedTextureVariations(Cache.SpawnPed, 6), "Palette: " .. GetPedPaletteVariation(Cache.SpawnPed, 6)) - print("Underwear?? | Drawable: " .. GetPedDrawableVariation(Cache.SpawnPed, 10), "Drawables: " .. GetNumberOfPedDrawableVariations(Cache.SpawnPed, 10), "Texture: " .. GetPedTextureVariation(Cache.SpawnPed, 10), "Textures: " .. GetNumberOfPedTextureVariations(Cache.SpawnPed, 10), "Palette: " .. GetPedPaletteVariation(Cache.SpawnPed, 10)) - - if Config.Framework ~= 'Standalone' then - if PlayerMoney >= Config.LegMoney then - SetPedComponentVariation(Cache.SpawnPed, 9, 1) - else - SetPedComponentVariation(Cache.SpawnPed, 9, 0) - end - end - - if Config.Debug then - print("------ kc-unicorn : Debug print start ------") - print("Player cash: ", PlayerMoney, "Player birthdate: ", PlayerBirthdate, "Today date: ", TodayDate) - print('Config.Language is set to: ' .. Config.Language) - print('Framework used: ' .. Config.Framework) - print(Config.Language .. "", lg) - --[[ print("Birthdate split: ", Birthdate[1], Birthdate[2], Birthdate[3]) +RegisterNetEvent("kc-unicorn:lapdance") +AddEventHandler("kc-unicorn:lapdance", function(PlayerMoney, PlayerBirthdate, TodayDate, Stripper) + Birthdate = {} + Date = {} + Cache.lapStop = false + Cache.InLapdance = true + PreviousCamViewMode = GetFollowPedCamViewMode(Cache.Player) + + if Config.Framework == "QBCore" then + index = 1 + for value in string.gmatch(PlayerBirthdate, "[^-]+") do + Birthdate[index] = value + index = index + 1 + end + + index = 1 + for value in string.gmatch(TodayDate, "[^-]+") do + Date[index] = value + index = index + 1 + end + + Year = Date[1] - Birthdate[1] - 4 + Month = Date[2] - Birthdate[2] + Day = Date[3] - Birthdate[3] + else + Year = 1000 + Month = 0 + Day = 0 + end + + RequestModel(Config.Strippers[Stripper].Model) + Cache.InCooldown = true + while not HasModelLoaded(1846523796) do + Wait(20) + end + Citizen.Wait(200) + + SetEntityHeading(Cache.Player, 120.18) + + if not Seat1 and not Seat1Taken then + Seat1Taken = true + TriggerServerEvent("kc-unicorn:Seat1") + elseif not Seat2 and not Seat2Taken then + Seat2Taken = true + TriggerServerEvent("kc-unicorn:Seat2") + elseif not Seat3 and not Seat3Taken then + Seat3Taken = true + TriggerServerEvent("kc-unicorn:Seat3") + elseif not Seat4 and not Seat4Taken then + Seat4Taken = true + TriggerServerEvent("kc-unicorn:Seat4") + elseif not Seat5 and not Seat5Taken then + Seat5Taken = true + TriggerServerEvent("kc-unicorn:Seat5") + elseif not Seat6 and not Seat6Taken then + Seat6Taken = true + TriggerServerEvent("kc-unicorn:Seat6") + elseif not Seat7 and not Seat7Taken then + Seat7Taken = true + TriggerServerEvent("kc-unicorn:Seat7") + end + + if Seat1Taken then + SpawnObject = CreatePed(4, 1846523796, 117.1, -1301.25, 28.05, 303.19, true, false) + elseif Seat2Taken then + SpawnObject = CreatePed(4, 1846523796, 114.98, -1302.43, 28.05, 303.19, true, false) + elseif Seat3Taken then + SpawnObject = CreatePed(4, 1846523796, 112.89, -1303.69, 28.05, 303.19, true, false) + elseif Seat4Taken then + SpawnObject = CreatePed(4, 1846523796, 111.32, -1304.58, 28.05, 303.19, true, false) + elseif Seat5Taken then + SpawnObject = CreatePed(4, 1846523796, 110.89, -1303.84, 28.05, 303.19, true, false) + elseif Seat6Taken then + SpawnObject = CreatePed(4, 1846523796, 112.42, -1303.04, 28.05, 303.19, true, false) + elseif Seat7Taken then + SpawnObject = CreatePed(4, 1846523796, 114.29, -1301.91, 28.05, 303.19, true, false) + end + + Citizen.Wait(100) + Cache.SpawnPed = SpawnObject + FreezeEntityPosition(Cache.SpawnPed, true) + + SetPedComponentVariation(Cache.SpawnPed, 1, Config.Strippers[Stripper].Drawables.Head) -- Head + SetPedComponentVariation(Cache.SpawnPed, 2, Config.Strippers[Stripper].Drawables.Hair, Config.Strippers[Stripper].Textures.HairTex) -- Hair + SetPedComponentVariation(Cache.SpawnPed, 3, Config.Strippers[Stripper].Drawables.Torso) -- Torso + SetPedComponentVariation(Cache.SpawnPed, 4, Config.Strippers[Stripper].Drawables.Shoes, Config.Strippers[Stripper].Textures.ShoesTex) -- Shoes + SetPedComponentVariation(Cache.SpawnPed, 6, Config.Strippers[Stripper].Drawables.Underwear, Config.Strippers[Stripper].Textures.UnderwearTex) -- Underwear + SetPedComponentVariation(Cache.SpawnPed, 10, 0) -- Decals ?? + SetPedComponentVariation(Cache.SpawnPed, 11, Config.Strippers[Stripper].Drawables.Torso2) -- Auxiliary parts for torso + + if Year >= Config.NudityAge + 1 then + Underage = false + elseif Year == Config.NudityAge then + if Month == 0 and Day <= 0 then + Underage = false + else + Underage = true + end + else + Underage = true + end + + if Underage then + SetPedComponentVariation(Cache.SpawnPed, 8, 0, Config.Strippers[Stripper].Textures.BraTex) -- Not Topless + elseif not Underage and not Config.Nudity then + SetPedComponentVariation(Cache.SpawnPed, 8, 0, Config.Strippers[Stripper].Textures.BraTex) -- Not Topless + else + SetPedComponentVariation(Cache.SpawnPed, 8, 1) -- Topless + end + print( + "Shoes | Drawable: " .. GetPedDrawableVariation(Cache.SpawnPed, 4), + "Drawables: " .. GetNumberOfPedDrawableVariations(Cache.SpawnPed, 4), + "Texture: " .. GetPedTextureVariation(Cache.SpawnPed, 4), + "Textures: " .. GetNumberOfPedTextureVariations(Cache.SpawnPed, 4), + "Palette: " .. GetPedPaletteVariation(Cache.SpawnPed, 4) + ) + print( + "Underwear | Drawable: " .. GetPedDrawableVariation(Cache.SpawnPed, 6), + "Drawables: " .. GetNumberOfPedDrawableVariations(Cache.SpawnPed, 6), + "Texture: " .. GetPedTextureVariation(Cache.SpawnPed, 6), + "Textures: " .. GetNumberOfPedTextureVariations(Cache.SpawnPed, 6), + "Palette: " .. GetPedPaletteVariation(Cache.SpawnPed, 6) + ) + print( + "Underwear?? | Drawable: " .. GetPedDrawableVariation(Cache.SpawnPed, 10), + "Drawables: " .. GetNumberOfPedDrawableVariations(Cache.SpawnPed, 10), + "Texture: " .. GetPedTextureVariation(Cache.SpawnPed, 10), + "Textures: " .. GetNumberOfPedTextureVariations(Cache.SpawnPed, 10), + "Palette: " .. GetPedPaletteVariation(Cache.SpawnPed, 10) + ) + + if Config.Framework ~= "Standalone" then + if PlayerMoney >= Config.LegMoney then + SetPedComponentVariation(Cache.SpawnPed, 9, 1) + else + SetPedComponentVariation(Cache.SpawnPed, 9, 0) + end + end + + if Config.Debug then + print("------ kc-unicorn : Debug print start ------") + print("Player cash: ", PlayerMoney, "Player birthdate: ", PlayerBirthdate, "Today date: ", TodayDate) + print("Config.Language is set to: " .. Config.Language) + print("Framework used: " .. Config.Framework) + print(Config.Language .. "", lg) + --[[ print("Birthdate split: ", Birthdate[1], Birthdate[2], Birthdate[3]) print("Date split: ", Date[1], Date[2], Date[3]) ]] - print("Year: ", Year, "Month: ", Month, "Day: ", Day) - print(GetPedDrawableVariation(Cache.SpawnPed, 1), "Head") - print(GetPedDrawableVariation(Cache.SpawnPed, 2), "Beard") - print(GetPedDrawableVariation(Cache.SpawnPed, 3), "Torso") - print(GetPedDrawableVariation(Cache.SpawnPed, 4), "Legs") - print(GetPedDrawableVariation(Cache.SpawnPed, 5), "Hands") - print(GetPedDrawableVariation(Cache.SpawnPed, 6), "Foot") - print(GetPedDrawableVariation(Cache.SpawnPed, 7), "???") - print(GetPedDrawableVariation(Cache.SpawnPed, 8), "Accessories 1") - print(GetPedDrawableVariation(Cache.SpawnPed, 9), "Accessories 2") - print(GetPedDrawableVariation(Cache.SpawnPed, 10), "Decals") - print(GetPedDrawableVariation(Cache.SpawnPed, 11), "Auxiliary parts for torso") - print('') - if Config.NudityAge ~= 0 then - if underage then - print("Player is underage") - else - print("Player isn't underage") - end - end - if not Config.Nudity and underage then - print("Stripper isn't topless") - elseif not Config.Nudity and not underage then - print("Stripper isn't topless") - else - print("Stripper is topless") - end - print("Player in lap dance: " .. PlayerSeated) - print("------ kc-unicorn : Debug print end ------") - end - - TaskPlayAnim(Cache.SpawnPed, LoadDict("mini@strip_club@idles@stripper", true), "stripper_idle_02", 8.0, -8.0, -1, 0, 0, false, false, false) - - SetEntityCoords(Cache.Player, 116.88, -1295.04, 28.42) - - ------------------- First seat ---------------------- - if Seat1Taken then - StripperFunc(Cache.SpawnPed, 303.19, 216.6, 308.6, vector3(118.71, -1301.93, 28.42), vector3(118.0, -1300.3, 28.17), vector3(118.74, -1301.91, 29.27), vector3(117.1, -1301.25, 28.05), 4900) - - Seat1Taken = false - TriggerServerEvent('kc-unicorn:RemoveSeat1') - - ------------------- Second seat ---------------------- - elseif Seat2Taken then - StripperFunc(Cache.SpawnPed, 303.19, 216.6, 308.6, vector3(116.67, -1303.35, 29.27), vector3(115.85, -1302.02, 29.02), vector3(116.25, -1302.85, 29.27), vector3(114.98, -1302.43, 28.05), 6000) - - Seat2Taken = false - TriggerServerEvent('kc-unicorn:RemoveSeat2') - - ------------------- Third seat ---------------------- - elseif Seat3Taken then - StripperFunc(Cache.SpawnPed, 303.19, 216.6, 308.6, vector3(114.64, -1304.54, 29.27), vector3(113.56, -1302.94, 29.02), vector3(114.33, -1304.23, 29.27), vector3(112.89, -1303.69, 28.05), 7000) - Seat3Taken = false - TriggerServerEvent('kc-unicorn:RemoveSeat3') - - ------------------- Fourth seat ---------------------- - elseif Seat4Taken then - StripperFunc(Cache.SpawnPed, 303.19, 216.6, 308.6, vector3(112.66, -1305.52, 29.27), vector3(111.82, -1304.17, 29.02), vector3(112.42, -1305.11, 29.27), vector3(111.32, -1304.58, 28.05), 8000) - Seat4Taken = false - TriggerServerEvent('kc-unicorn:RemoveSeat4') - - ------------------- Fifth seat ---------------------- - elseif Seat5Taken then - StripperFunc(Cache.SpawnPed, 303.19, 29.5, 308.6, vector3(111.18, -1302.62, 29.27), vector3(111.46, -1303.61, 29.02), vector3(111.17, -1302.81, 29.27), vector3(110.89, -1303.84, 28.05), 8000) - Seat5Taken = false - TriggerServerEvent('kc-unicorn:RemoveSeat5') - - ------------------- Sixth seat ---------------------- - elseif Seat6Taken then - StripperFunc(Cache.SpawnPed, 303.19, 29.5, 308.6, vector3(112.64, -1301.27, 29.27), vector3(113.35, -1302.36, 29.02), vector3(112.91, -1301.62, 29.27), vector3(112.42, -1303.04, 28.05), 7000) - Seat6Taken = false - TriggerServerEvent('kc-unicorn:RemoveSeat6') - - ------------------- Seventh seat ---------------------- - elseif Seat7Taken then - StripperFunc(Cache.SpawnPed, 303.19, 29.5, 308.6, vector3(114.73, -1300.41, 29.27), vector3(115.08, -1301.39, 29.02), vector3(114.88, -1300.64, 29.27), vector3(114.29, -1301.91, 28.05), 7000) - Seat7Taken = false - TriggerServerEvent('kc-unicorn:RemoveSeat7') - end - - TriggerServerEvent('kc-unicorn:idle') - Citizen.Wait(1000) - Cache.InCooldown = false - - if not Cache.lapStop then - DeleteEntity(Cache.SpawnPed) - end + print("Year: ", Year, "Month: ", Month, "Day: ", Day) + print(GetPedDrawableVariation(Cache.SpawnPed, 1), "Head") + print(GetPedDrawableVariation(Cache.SpawnPed, 2), "Beard") + print(GetPedDrawableVariation(Cache.SpawnPed, 3), "Torso") + print(GetPedDrawableVariation(Cache.SpawnPed, 4), "Legs") + print(GetPedDrawableVariation(Cache.SpawnPed, 5), "Hands") + print(GetPedDrawableVariation(Cache.SpawnPed, 6), "Foot") + print(GetPedDrawableVariation(Cache.SpawnPed, 7), "???") + print(GetPedDrawableVariation(Cache.SpawnPed, 8), "Accessories 1") + print(GetPedDrawableVariation(Cache.SpawnPed, 9), "Accessories 2") + print(GetPedDrawableVariation(Cache.SpawnPed, 10), "Decals") + print(GetPedDrawableVariation(Cache.SpawnPed, 11), "Auxiliary parts for torso") + print("") + if Config.NudityAge ~= 0 then + if Underage then + print("Player is underage") + else + print("Player isn't underage") + end + end + if not Config.Nudity and Underage then + print("Stripper isn't topless") + elseif not Config.Nudity and not Underage then + print("Stripper isn't topless") + else + print("Stripper is topless") + end + print("Player in lap dance: " .. PlayerSeated) + print("------ kc-unicorn : Debug print end ------") + end + + TaskPlayAnim(Cache.SpawnPed, LoadDict("mini@strip_club@idles@stripper", true), "stripper_idle_02", 8.0, -8.0, -1, 0, 0, false, false, false) + + SetEntityCoords(Cache.Player, 116.88, -1295.04, 28.42) + + ------------------- First seat ---------------------- + if Seat1Taken then + StripperFunc(Cache.SpawnPed, 303.19, 216.6, 308.6, vector3(118.71, -1301.93, 28.42), vector3(118.0, -1300.3, 28.17), vector3(118.74, -1301.91, 29.27), vector3(117.1, -1301.25, 28.05), 4900) + + Seat1Taken = false + TriggerServerEvent("kc-unicorn:RemoveSeat1") + + ------------------- Second seat ---------------------- + elseif Seat2Taken then + StripperFunc(Cache.SpawnPed, 303.19, 216.6, 308.6, vector3(116.67, -1303.35, 29.27), vector3(115.85, -1302.02, 29.02), vector3(116.25, -1302.85, 29.27), vector3(114.98, -1302.43, 28.05), 6000) + + Seat2Taken = false + TriggerServerEvent("kc-unicorn:RemoveSeat2") + + ------------------- Third seat ---------------------- + elseif Seat3Taken then + StripperFunc(Cache.SpawnPed, 303.19, 216.6, 308.6, vector3(114.64, -1304.54, 29.27), vector3(113.56, -1302.94, 29.02), vector3(114.33, -1304.23, 29.27), vector3(112.89, -1303.69, 28.05), 7000) + Seat3Taken = false + TriggerServerEvent("kc-unicorn:RemoveSeat3") + + ------------------- Fourth seat ---------------------- + elseif Seat4Taken then + StripperFunc(Cache.SpawnPed, 303.19, 216.6, 308.6, vector3(112.66, -1305.52, 29.27), vector3(111.82, -1304.17, 29.02), vector3(112.42, -1305.11, 29.27), vector3(111.32, -1304.58, 28.05), 8000) + Seat4Taken = false + TriggerServerEvent("kc-unicorn:RemoveSeat4") + + ------------------- Fifth seat ---------------------- + elseif Seat5Taken then + StripperFunc(Cache.SpawnPed, 303.19, 29.5, 308.6, vector3(111.18, -1302.62, 29.27), vector3(111.46, -1303.61, 29.02), vector3(111.17, -1302.81, 29.27), vector3(110.89, -1303.84, 28.05), 8000) + Seat5Taken = false + TriggerServerEvent("kc-unicorn:RemoveSeat5") + + ------------------- Sixth seat ---------------------- + elseif Seat6Taken then + StripperFunc(Cache.SpawnPed, 303.19, 29.5, 308.6, vector3(112.64, -1301.27, 29.27), vector3(113.35, -1302.36, 29.02), vector3(112.91, -1301.62, 29.27), vector3(112.42, -1303.04, 28.05), 7000) + Seat6Taken = false + TriggerServerEvent("kc-unicorn:RemoveSeat6") + + ------------------- Seventh seat ---------------------- + elseif Seat7Taken then + StripperFunc(Cache.SpawnPed, 303.19, 29.5, 308.6, vector3(114.73, -1300.41, 29.27), vector3(115.08, -1301.39, 29.02), vector3(114.88, -1300.64, 29.27), vector3(114.29, -1301.91, 28.05), 7000) + Seat7Taken = false + TriggerServerEvent("kc-unicorn:RemoveSeat7") + end + + TriggerServerEvent("kc-unicorn:idle") + Citizen.Wait(1000) + Cache.InCooldown = false + + if not Cache.lapStop then + DeleteEntity(Cache.SpawnPed) + end end) Citizen.CreateThread(function() + while true do + if not Cache.InLapdance then + Citizen.Wait(1000) + else + Citizen.Wait(0) + end + + if Seat1Taken then + local sitdist = #(Cache.PlyCoo - vector3(118.75, -1301.97, 28.42)) + if sitdist < 1 and Cache.InLapdance then + PlayerFunc(vector3(119.06, -1302.66, 27.78), 40.0, -10.0, vector3(118.75, -1301.99, 28.42), 5000) + end + elseif Seat2Taken then + local sitdist = #(Cache.PlyCoo - vector3(116.59, -1303.01, 28.42)) + if sitdist < 1 and Cache.InLapdance then + PlayerFunc(vector3(116.9, -1303.75, 27.78), 40.0, -10.0, vector3(116.59, -1303.01, 28.42), 6200) + end + elseif Seat3Taken then + local sitdist = #(Cache.PlyCoo - vector3(114.64, -1304.54, 29.27)) + if sitdist < 1 and Cache.InLapdance then + PlayerFunc(vector3(114.86, -1305.0, 27.78), 40.0, -10.0, vector3(114.64, -1304.54, 29.27), 6800) + end + elseif Seat4Taken then + local sitdist = #(Cache.PlyCoo - vector3(112.66, -1305.52, 29.27)) + if sitdist < 1 and Cache.InLapdance then + PlayerFunc(vector3(113.08, -1306.17, 27.78), 40.0, -10.0, vector3(112.66, -1305.52, 29.27), 7500) + end + elseif Seat5Taken then + local sitdist = #(Cache.PlyCoo - vector3(111.18, -1302.62, 29.27)) + if sitdist < 1 and Cache.InLapdance then + PlayerFunc(vector3(110.57, -1301.82, 27.78), 216.6, -10.0, vector3(111.18, -1302.62, 29.27), 7500) + end + elseif Seat6Taken then + local sitdist = #(Cache.PlyCoo - vector3(112.64, -1301.27, 29.27)) + if sitdist < 1 and Cache.InLapdance then + PlayerFunc(vector3(112.34, -1300.76, 27.78), 216.6, -10.0, vector3(112.64, -1301.27, 29.27), 6800) + end + elseif Seat7Taken then + local sitdist = #(Cache.PlyCoo - vector3(114.73, -1300.41, 29.27)) + if sitdist < 1 and Cache.InLapdance then + PlayerFunc(vector3(114.27, -1299.66, 27.78), 216.6, -10.0, vector3(114.73, -1300.41, 29.27), 6000) + end + end - while true do - - if not Cache.InLapdance then - Citizen.Wait(1000) - else - Citizen.Wait(0) - end - - if Seat1Taken then - local sitdist = #(Cache.PlyCoo - vector3(118.75, -1301.97, 28.42)) - if sitdist < 1 and Cache.InLapdance then - PlayerFunc(vector3(119.06, -1302.66, 27.78), 40.0, -10.0, vector3(118.75, -1301.99, 28.42), 5000) - end - elseif Seat2Taken then - local sitdist = #(Cache.PlyCoo - vector3(116.59, -1303.01, 28.42)) - if sitdist < 1 and Cache.InLapdance then - PlayerFunc(vector3(116.9, -1303.75, 27.78), 40.0, -10.0, vector3(116.59, -1303.01, 28.42), 6200) - end - elseif Seat3Taken then - local sitdist = #(Cache.PlyCoo - vector3(114.64, -1304.54, 29.27)) - if sitdist < 1 and Cache.InLapdance then - PlayerFunc(vector3(114.86, -1305.0, 27.78), 40.0, -10.0, vector3(114.64, -1304.54, 29.27), 6800) - end - elseif Seat4Taken then - local sitdist = #(Cache.PlyCoo - vector3(112.66, -1305.52, 29.27)) - if sitdist < 1 and Cache.InLapdance then - PlayerFunc(vector3(113.08, -1306.17, 27.78), 40.0, -10.0, vector3(112.66, -1305.52, 29.27), 7500) - end - elseif Seat5Taken then - local sitdist = #(Cache.PlyCoo - vector3(111.18, -1302.62, 29.27)) - if sitdist < 1 and Cache.InLapdance then - PlayerFunc(vector3(110.57, -1301.82, 27.78), 216.6, -10.0, vector3(111.18, -1302.62, 29.27), 7500) - end - elseif Seat6Taken then - local sitdist = #(Cache.PlyCoo - vector3(112.64, -1301.27, 29.27)) - if sitdist < 1 and Cache.InLapdance then - PlayerFunc(vector3(112.34, -1300.76, 27.78), 216.6, -10.0, vector3(112.64, -1301.27, 29.27), 6800) - end - elseif Seat7Taken then - local sitdist = #(Cache.PlyCoo - vector3(114.73, -1300.41, 29.27)) - if sitdist < 1 and Cache.InLapdance then - PlayerFunc(vector3(114.27, -1299.66, 27.78), 216.6, -10.0, vector3(114.73, -1300.41, 29.27), 6000) - end - end - - DisableControlAction(2, 0, false) - end + DisableControlAction(2, 0, false) + end end) RegisterNetEvent("kc-unicorn:SetPlayerSeated") -AddEventHandler("kc-unicorn:SetPlayerSeated", function(GetPlayerSeated, Seat1Busy, Seat2Busy, Seat3Busy, Seat4Busy, Seat5Busy, Seat6Busy, Seat7Busy) - Seat1 = Seat1Busy - Seat2 = Seat2Busy - Seat3 = Seat3Busy - Seat4 = Seat4Busy - Seat5 = Seat5Busy - Seat6 = Seat6Busy - Seat7 = Seat7Busy - PlayerSeated = GetPlayerSeated +AddEventHandler("kc-unicorn:SetPlayerSeated", function(GetPlayerSeated, Seat1Busy, Seat2Busy, Seat3Busy, Seat4Busy, Seat5Busy, Seat6Busy, Seat7Busy) + Seat1 = Seat1Busy + Seat2 = Seat2Busy + Seat3 = Seat3Busy + Seat4 = Seat4Busy + Seat5 = Seat5Busy + Seat6 = Seat6Busy + Seat7 = Seat7Busy + PlayerSeated = GetPlayerSeated end) -------- @@ -658,44 +660,43 @@ end) ---- Lean Citizen.CreateThread(function() + if Config.Text == "3D" then + Input = "~r~E~w~ - " + else + Input = "~INPUT_CONTEXT~ " + end - if Config.Text == '3D' then - Input = '~r~E~w~ - ' - else - Input = '~INPUT_CONTEXT~ ' - end - - while true do - if Cache.inleanPoly then - if leanPoly1:isPointInside(Cache.PlyCoo) then - Citizen.Wait(1) - DrawText3D(Cache.PlyCoo, Input .. Loc('Lean')) - if IsControlJustReleased(0, 51) then - LeanStart(29.0) - end - elseif leanPoly2:isPointInside(Cache.PlyCoo) then - Citizen.Wait(1) - DrawText3D(Cache.PlyCoo, Input .. Loc('Lean')) - if IsControlJustReleased(0, 51) then - LeanStart(116.2) - end - elseif leanPoly3:isPointInside(Cache.PlyCoo) then - Citizen.Wait(1) - DrawText3D(Cache.PlyCoo, Input .. Loc('Lean')) - if IsControlJustReleased(0, 51) then - LeanStart(210.0) - end - else - Citizen.Wait(200) - end - else - Citizen.Wait(200) - end - end + while true do + if Cache.inleanPoly then + if leanPoly1:isPointInside(Cache.PlyCoo) then + Citizen.Wait(1) + DrawText3D(Cache.PlyCoo, Input .. Loc("Lean")) + if IsControlJustReleased(0, 51) then + LeanStart(29.0) + end + elseif leanPoly2:isPointInside(Cache.PlyCoo) then + Citizen.Wait(1) + DrawText3D(Cache.PlyCoo, Input .. Loc("Lean")) + if IsControlJustReleased(0, 51) then + LeanStart(116.2) + end + elseif leanPoly3:isPointInside(Cache.PlyCoo) then + Citizen.Wait(1) + DrawText3D(Cache.PlyCoo, Input .. Loc("Lean")) + if IsControlJustReleased(0, 51) then + LeanStart(210.0) + end + else + Citizen.Wait(200) + end + else + Citizen.Wait(200) + end + end end) function LoadModel(model) - model = GetHashKey(model) + model = GetHashKey(model) if not HasModelLoaded(model) and IsModelInCdimage(model) then RequestModel(model) @@ -706,135 +707,128 @@ function LoadModel(model) end function LeanStart(PlyHeading) - - local text = Loc('LeanNotice', Config.ThrowCost) - LoadModel("prop_anim_cash_note_b") - local is_female = function() - return(GetEntityModel(Cache.Player) == "mp_f_freemode_01" and "_female" or "") - end - - if Config.Framework == "Standalone" then - text = Loc('StandaloneLeanNotice') - end - - SetEntityCoordsNoOffset(Cache.Player, Cache.PlyCoo) - FreezeEntityPosition(Cache.Player, true) - SetEntityHeading(Cache.Player, PlyHeading) - Cache.InLean = true - Wait(50) - - TaskPlayAnim(Cache.Player, LoadDict("mini@strip_club@leaning@enter", true), "enter" .. is_female(), 8.0, -8.0, -1, 0, 0, false, false, false) - Wait(2750) - PreviousCamViewMode = GetFollowPedCamViewMode(Cache.Player) - SetFollowPedCamViewMode(4) - SetGameplayCamRelativeHeading(0.0) - TaskPlayAnim(Cache.Player, LoadDict("mini@strip_club@leaning@base", true), "base" .. is_female(), 8.0, -8.0, -1, 1, 0, false, false, false) - - while true do - Wait(0) - - DrawText2D(text) - if IsControlJustReleased(0, 22) then - if Config.Framework == "QBCore" then - PlayerData = QBCore.Functions.GetPlayerData() - PlayerMoney = PlayerData.money["cash"] - elseif Config.Framework == "ESX" then - TriggerServerEvent('kc-unicorn:esxmoney') - elseif Config.Framework == "Standalone" then - PlayerMoney = Config.ThrowCost + 1 - end - repeat - Citizen.Wait(100) - until PlayerMoney ~= nil - if Config.Debug then - print("Player money: " .. PlayerMoney, "Throw cost: " .. Config.ThrowCost) - end - if PlayerMoney >= Config.ThrowCost then - - TriggerServerEvent('kc-unicorn:leanthrow') - local pos = GetPedBoneCoords(Cache.Player, 28422, 0.0, 0.0, 0.0) - local cash = CreateObject("prop_anim_cash_note_b", pos, true, false, false) - SetEntityAlpha(cash, 0, false) - AttachEntityToEntity(cash, Cache.Player, GetPedBoneIndex(Cache.Player, 28422), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false, false, false, false, 2, true) - TaskPlayAnim(Cache.Player, LoadDict("mini@strip_club@leaning@toss", true), "toss" .. is_female(), 8.0, -8.0, -1, 2, 0, false, false, false) - Citizen.Wait(150) - SetEntityAlpha(cash, 255, false) - while true do - if GetEntityAnimCurrentTime(Cache.Player, "mini@strip_club@leaning@toss", "toss" .. is_female()) >= 0.74 then - local alphaLevel = 255 - repeat - SetEntityAlpha(cash, alphaLevel, false) - alphaLevel = alphaLevel - 51 - Citizen.Wait(40) - until(alphaLevel == 0) - DeleteEntity(cash) - break - end - Citizen.Wait(50) - end - TaskPlayAnim(Cache.Player, LoadDict("mini@strip_club@leaning@base", true), "base" .. is_female(), 8.0, -8.0, -1, 1, 0, false, false, false) - - else - if Config.Framework == 'QBCore' then - QBCore.Functions.Notify(Loc('NotEnoughCashLean'), "error") - else - TriggerEvent('kc-unicorn:showNotify', Loc('NotEnoughCashLean')) - end - end - elseif IsControlJustReleased(0, 51) then - break - end - end - - Cache.InLean = false - SetFollowPedCamViewMode(PreviousCamViewMode) - Wait(100) - TaskPlayAnim(Cache.Player, LoadDict("mini@strip_club@leaning@exit", true), "exit" .. is_female(), 8.0, -8.0, -1, 0, 0, false, false, false) - FreezeEntityPosition(Cache.Player, false) + local text = Loc("LeanNotice", Config.ThrowCost) + LoadModel("prop_anim_cash_note_b") + local is_female = function() + return (GetEntityModel(Cache.Player) == "mp_f_freemode_01" and "_female" or "") + end + + if Config.Framework == "Standalone" then + text = Loc("StandaloneLeanNotice") + end + + SetEntityCoordsNoOffset(Cache.Player, Cache.PlyCoo) + FreezeEntityPosition(Cache.Player, true) + SetEntityHeading(Cache.Player, PlyHeading) + Cache.InLean = true + Wait(50) + + TaskPlayAnim(Cache.Player, LoadDict("mini@strip_club@leaning@enter", true), "enter" .. is_female(), 8.0, -8.0, -1, 0, 0, false, false, false) + Wait(2750) + PreviousCamViewMode = GetFollowPedCamViewMode(Cache.Player) + SetFollowPedCamViewMode(4) + SetGameplayCamRelativeHeading(0.0) + TaskPlayAnim(Cache.Player, LoadDict("mini@strip_club@leaning@base", true), "base" .. is_female(), 8.0, -8.0, -1, 1, 0, false, false, false) + + while true do + Wait(0) + + DrawText2D(text) + if IsControlJustReleased(0, 22) then + if Config.Framework == "QBCore" then + PlayerData = QBCore.Functions.GetPlayerData() + PlayerMoney = PlayerData.money["cash"] + elseif Config.Framework == "ESX" then + TriggerServerEvent("kc-unicorn:esxmoney") + elseif Config.Framework == "Standalone" then + PlayerMoney = Config.ThrowCost + 1 + end + repeat + Citizen.Wait(100) + until PlayerMoney ~= nil + if Config.Debug then + print("Player money: " .. PlayerMoney, "Throw cost: " .. Config.ThrowCost) + end + if PlayerMoney >= Config.ThrowCost then + TriggerServerEvent("kc-unicorn:leanthrow") + local pos = GetPedBoneCoords(Cache.Player, 28422, 0.0, 0.0, 0.0) + local cash = CreateObject("prop_anim_cash_note_b", pos, true, false, false) + SetEntityAlpha(cash, 0, false) + AttachEntityToEntity(cash, Cache.Player, GetPedBoneIndex(Cache.Player, 28422), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, false, false, false, false, 2, true) + TaskPlayAnim(Cache.Player, LoadDict("mini@strip_club@leaning@toss", true), "toss" .. is_female(), 8.0, -8.0, -1, 2, 0, false, false, false) + Citizen.Wait(150) + SetEntityAlpha(cash, 255, false) + while true do + if GetEntityAnimCurrentTime(Cache.Player, "mini@strip_club@leaning@toss", "toss" .. is_female()) >= 0.74 then + local alphaLevel = 255 + repeat + SetEntityAlpha(cash, alphaLevel, false) + alphaLevel = alphaLevel - 51 + Citizen.Wait(40) + until alphaLevel == 0 + DeleteEntity(cash) + break + end + Citizen.Wait(50) + end + TaskPlayAnim(Cache.Player, LoadDict("mini@strip_club@leaning@base", true), "base" .. is_female(), 8.0, -8.0, -1, 1, 0, false, false, false) + else + if Config.Framework == "QBCore" then + QBCore.Functions.Notify(Loc("NotEnoughCashLean"), "error") + else + TriggerEvent("kc-unicorn:showNotify", Loc("NotEnoughCashLean")) + end + end + elseif IsControlJustReleased(0, 51) then + break + end + end + + Cache.InLean = false + SetFollowPedCamViewMode(PreviousCamViewMode) + Wait(100) + TaskPlayAnim(Cache.Player, LoadDict("mini@strip_club@leaning@exit", true), "exit" .. is_female(), 8.0, -8.0, -1, 0, 0, false, false, false) + FreezeEntityPosition(Cache.Player, false) end -------- ---- ???? - -RegisterNetEvent('kc-unicorn:poledancescene') -AddEventHandler('kc-unicorn:poledancescene', function(ped, number) - PedPole = NetworkGetEntityFromNetworkId(ped) - local scene = NetworkCreateSynchronisedScene(vector3(112.65, -1286.74, 28.5), vector3(0.0, 0.0, 0.0), 2, false, false, 1065353216, 0, 1.3) - NetworkAddPedToSynchronisedScene(PedPole, scene, 'mini@strip_club@pole_dance@pole_dance'..number, 'pd_dance_0'..number, 1.5, -4.0, 1, 1, 1148846080, 0) - NetworkStartSynchronisedScene(scene) - Citizen.Wait(30000) - TriggerServerEvent('kc-unicorn:poledancestop') +RegisterNetEvent("kc-unicorn:poledancescene") +AddEventHandler("kc-unicorn:poledancescene", function(ped, number) + PedPole = NetworkGetEntityFromNetworkId(ped) + local scene = NetworkCreateSynchronisedScene(vector3(112.65, -1286.74, 28.5), vector3(0.0, 0.0, 0.0), 2, false, false, 1065353216, 0, 1.3) + NetworkAddPedToSynchronisedScene(PedPole, scene, "mini@strip_club@pole_dance@pole_dance" .. number, "pd_dance_0" .. number, 1.5, -4.0, 1, 1, 1148846080, 0) + NetworkStartSynchronisedScene(scene) + Citizen.Wait(30000) + TriggerServerEvent("kc-unicorn:poledancestop") end) - -------- ---- Debug Citizen.CreateThread(function() - local Count = 0 - while true do - - Citizen.Wait(Config.MoreDebugRefreshTime*1000) - if Cache.inuniPoly and Config.MoreDebug then - print("In Unicorn PolyZone: " .. tostring(Cache.inuniPoly), "In Lapdance PolyZone: " .. tostring(Cache.inlapPoly), "In Lean PolyZone: " .. tostring(Cache.inleanPoly) .. "\n") - print("Server: ", "Seat1: " .. tostring(Seat1), "Seat2: " .. tostring(Seat2), "Seat3: " .. tostring(Seat3), "Seat4: " .. tostring(Seat4), "Seat5: " .. tostring(Seat5), "Seat6: " .. tostring(Seat6), "Seat7: " .. tostring(Seat7), PlayerSeated) - print("Client: ", "Seat1: " .. tostring(Seat1Taken), "Seat2: " .. tostring(Seat2Taken), "Seat3: " .. tostring(Seat3Taken), "Seat4: " .. tostring(Seat4Taken), "Seat5: " .. tostring(Seat5Taken), "Seat6: " .. tostring(Seat6Taken), "Seat7: " .. tostring(Seat7Taken), PlayerSeated) - elseif Config.MoreDebug then - if Count == 0 then - print("In Unicorn PolyZone: " .. tostring(Cache.inuniPoly)) - Count = Count + 1 - end - end - end - + local Count = 0 + while true do + Citizen.Wait(Config.MoreDebugRefreshTime * 1000) + if Cache.inuniPoly and Config.MoreDebug then + print("In Unicorn PolyZone: " .. tostring(Cache.inuniPoly), "In Lapdance PolyZone: " .. tostring(Cache.inlapPoly), "In Lean PolyZone: " .. tostring(Cache.inleanPoly) .. "\n") + print("Server: ", "Seat1: " .. tostring(Seat1), "Seat2: " .. tostring(Seat2), "Seat3: " .. tostring(Seat3), "Seat4: " .. tostring(Seat4), "Seat5: " .. tostring(Seat5), "Seat6: " .. tostring(Seat6), "Seat7: " .. tostring(Seat7), PlayerSeated) + print("Client: ", "Seat1: " .. tostring(Seat1Taken), "Seat2: " .. tostring(Seat2Taken), "Seat3: " .. tostring(Seat3Taken), "Seat4: " .. tostring(Seat4Taken), "Seat5: " .. tostring(Seat5Taken), "Seat6: " .. tostring(Seat6Taken), "Seat7: " .. tostring(Seat7Taken), PlayerSeated) + elseif Config.MoreDebug then + if Count == 0 then + print("In Unicorn PolyZone: " .. tostring(Cache.inuniPoly)) + Count = Count + 1 + end + end + end end) -------- -RegisterNetEvent('kc-unicorn:esxplayermoney') -AddEventHandler('kc-unicorn:esxplayermoney', function(money) - PlayerMoney = money +RegisterNetEvent("kc-unicorn:esxplayermoney") +AddEventHandler("kc-unicorn:esxplayermoney", function(money) + PlayerMoney = money end) diff --git a/server-data/resources/[esx_addons]/kc-unicorn/config.lua b/server-data/resources/[esx_addons]/kc-unicorn/config.lua index b629c7515..ce837632b 100644 --- a/server-data/resources/[esx_addons]/kc-unicorn/config.lua +++ b/server-data/resources/[esx_addons]/kc-unicorn/config.lua @@ -1,78 +1,76 @@ -Config = Config or {} +Config = Config or {} -- Framework -Config.Framework = 'Standalone' -- Choose which framework your server is using so kc-unicorn can work with it [ESX, QBCore, Standalone] +Config.Framework = "ESX" -- Choose which framework your server is using so kc-unicorn can work with it [ESX, QBCore, Standalone] -- Since there is too many esx version, only this one is supported (https://github.com/esx-framework/es_extended/tree/v1-final). Maybe I will test other version, but not a priority --- Lap dance -Config.LapDanceCost = 120 -- Cost of the lap dance -Config.ThrowCost = 40 -- Cost of the lap dance -Config.LegMoney = 10000 -- Little easter egg for your player, will add an accessory "Leg money" to the stripper if the player has more than 'Config.LegMoney' in cash. Set to a huge value if you don't want this -Config.Nudity = false -- Set to true if you want the stripper to be topless (Only for player above 'Config.NudityAge') -Config.NudityAge = 21 -- (QBCore only!!) Player age restriction. If underage and 'Config.Nudity' is set to true, the stripper won't be topless. Set to 0 if you don't want age restriction +-- Lap dance +Config.LapDanceCost = 120 -- Cost of the lap dance +Config.ThrowCost = 40 -- Cost of the lap dance +Config.LegMoney = 10000 -- Little easter egg for your player, will add an accessory "Leg money" to the stripper if the player has more than 'Config.LegMoney' in cash. Set to a huge value if you don't want this +Config.Nudity = true -- Set to true if you want the stripper to be topless (Only for player above 'Config.NudityAge') +Config.NudityAge = 21 -- (QBCore only!!) Player age restriction. If underage and 'Config.Nudity' is set to true, the stripper won't be topless. Set to 0 if you don't want age restriction -- Text/Blip/Marker -Config.Text = 'Better3D' -- 2D, 3D, Better3D, None (Set the one you like to suit your server, the performance difference is mostly non-existant) -Config.Blip = true -- Set to false if you don't want the blip on the map -Config.BlipName = "Strip-Club" -- Blip name +Config.Text = "Better3D" -- 2D, 3D, Better3D, None (Set the one you like to suit your server, the performance difference is mostly non-existant) +Config.Blip = true -- Set to false if you don't want the blip on the map +Config.BlipName = "Strip-Club" -- Blip name Config.BlipStripclub = { - Sprite = 121, -- Sprite list can be found here: https://docs.fivem.net/docs/game-references/blips/ - Colour = 50, -- Color list in the same URL as above - Display = 27, -- Explanation here: https://docs.fivem.net/natives/?_0x9029B2F3DA924928 - Scale = 0.7 -- Self explanatory, let you set the scale of the blip + Sprite = 121, -- Sprite list can be found here: https://docs.fivem.net/docs/game-references/blips/ + Colour = 50, -- Color list in the same URL as above + Display = 27, -- Explanation here: https://docs.fivem.net/natives/?_0x9029B2F3DA924928 + Scale = 0.7, -- Self explanatory, let you set the scale of the blip } -Config.BlipCoord = { x=128.87, y=-1298.93, z=4.0 } -- If you want to move the blip on the map, change this. -Config.LapMarker = true -- Set to true if you want the marker for the lapdance to be "drawn" +Config.BlipCoord = { x = 128.87, y = -1298.93, z = 4.0 } -- If you want to move the blip on the map, change this. +Config.LapMarker = true -- Set to true if you want the marker for the lapdance to be "drawn" -- Strippers -Config.SelectStrippers = true -- Set to true if you want to let your player choose which stripper they want for their lapdance +Config.SelectStrippers = true -- Set to true if you want to let your player choose which stripper they want for their lapdance Config.Strippers = { - [1] = { - Name = "Eisha", - Model = 1846523796, - Drawables = { Head = 0, Hair = 0, Torso = 0, Torso2 = 0, Shoes = 1, Underwear = 0 }, - Textures = { HairTex = 0, BraTex = 2, ShoesTex = 2, UnderwearTex = 0 } - }, - [2] = { - Name = "Jade", - Model = 1846523796, - Drawables = { Head = 0, Hair = 2, Torso = 0, Torso2 = 1, Shoes = 1, Underwear = 0 }, - Textures = { HairTex = 0, BraTex = 5, ShoesTex = 1, UnderwearTex = 0 } - } + [1] = { + Name = "Eisha", + Model = 1846523796, + Drawables = { Head = 0, Hair = 0, Torso = 0, Torso2 = 0, Shoes = 1, Underwear = 0 }, + Textures = { HairTex = 0, BraTex = 2, ShoesTex = 2, UnderwearTex = 0 }, + }, + [2] = { + Name = "Jade", + Model = 1846523796, + Drawables = { Head = 0, Hair = 2, Torso = 0, Torso2 = 1, Shoes = 1, Underwear = 0 }, + Textures = { HairTex = 0, BraTex = 5, ShoesTex = 1, UnderwearTex = 0 }, + }, } -- Locale -Config.Language = 'en' -- Currently Available: fr, en +Config.Language = "en" -- Currently Available: fr, en -- Locales can be easily created or modified in `kc-unicorn/locales` -- Misc -Config.Debug = false -- If you think something is not working, you can set 'Config.Debug' to true. It will then print a lot of debug information in your console -Config.DebugPolyzones = false -- Set to true if you want to display the Polyzones -Config.MoreDebug = false -- If set to true, this will print more debug about server and client lapdance seat status -Config.MoreDebugRefreshTime = 5 -- Refresh time of "Config.MoreDebug" in seconds -Config.UpdateChecker = true -- Set to false if you don't want to check for resource update on start -Config.ChangeLog = true -- Set to false if you don't want to display the changelog if new version is find - - +Config.Debug = false -- If you think something is not working, you can set 'Config.Debug' to true. It will then print a lot of debug information in your console +Config.DebugPolyzones = false -- Set to true if you want to display the Polyzones +Config.MoreDebug = false -- If set to true, this will print more debug about server and client lapdance seat status +Config.MoreDebugRefreshTime = 5 -- Refresh time of "Config.MoreDebug" in seconds +Config.UpdateChecker = false -- Set to false if you don't want to check for resource update on start +Config.ChangeLog = false -- Set to false if you don't want to display the changelog if new version is find ----- DO NOT TOUCH! /!\ LOCALE SYSTEM /!\ Language = {} -function Loc(text,replacement) +function Loc(text, replacement) Language = (load)("return Locale." .. Config.Language)() if (load)("return Locale." .. Config.Language)() == nil then - return 'Locale [' .. Config.Language .. '] (Does not exist/Is mispelled)' + return "Locale [" .. Config.Language .. "] (Does not exist/Is mispelled)" end - if (load)("return ".."Language."..text)() == nil then - return 'Error with [' .. Config.Language .. '.' .. text .. '] translation (Does not exist/Is mispelled)' + if (load)("return " .. "Language." .. text)() == nil then + return "Error with [" .. Config.Language .. "." .. text .. "] translation (Does not exist/Is mispelled)" end - if replacement then - text = (string.gsub((load)("return ".."Language."..text)(), "%%", replacement)) - else - text = (load)("return ".."Language."..text)() - end - return text + if replacement then + text = (string.gsub((load)("return " .. "Language." .. text)(), "%%", replacement)) + else + text = (load)("return " .. "Language." .. text)() + end + return text end ------------------------------------------ \ No newline at end of file +----------------------------------------- diff --git a/server-data/resources/[esx_addons]/kc-unicorn/fxmanifest.lua b/server-data/resources/[esx_addons]/kc-unicorn/fxmanifest.lua index 8bce901e4..0a45977cf 100644 --- a/server-data/resources/[esx_addons]/kc-unicorn/fxmanifest.lua +++ b/server-data/resources/[esx_addons]/kc-unicorn/fxmanifest.lua @@ -1,26 +1,27 @@ -fx_version 'cerulean' -game 'gta5' +fx_version("cerulean") +game("gta5") -author 'Clementinise' -name '^6KC Unicorn Script' -description 'Bring life & interactions to the Unicorn Club' -github 'https://github.com/clementinise/kc-unicorn' -version '1.1' +author("Clementinise") +name("^6KC Unicorn Script") +description("Bring life & interactions to the Unicorn Club") +github("https://github.com/clementinise/kc-unicorn") +version("1.0.2") -shared_scripts { - 'locales/*.lua', - 'config.lua', -} +shared_scripts({ + "@es_extended/imports.lua", + "locales/*.lua", + "config.lua", +}) -client_scripts { - '@PolyZone/client.lua', - '@PolyZone/BoxZone.lua', - '@PolyZone/EntityZone.lua', - '@PolyZone/CircleZone.lua', - '@PolyZone/ComboZone.lua', - 'client/*.lua' -} +client_scripts({ + "@PolyZone/client.lua", + "@PolyZone/BoxZone.lua", + "@PolyZone/EntityZone.lua", + "@PolyZone/CircleZone.lua", + "@PolyZone/ComboZone.lua", + "client/*.lua", +}) -server_script 'server/*.lua' +server_script("server/*.lua") -fivem_checker 'yes' \ No newline at end of file +fivem_checker("yes") diff --git a/server-data/resources/[esx_addons]/kc-unicorn/locales/en.lua b/server-data/resources/[esx_addons]/kc-unicorn/locales/en.lua index 6683b167d..6fcc4dcbc 100644 --- a/server-data/resources/[esx_addons]/kc-unicorn/locales/en.lua +++ b/server-data/resources/[esx_addons]/kc-unicorn/locales/en.lua @@ -1,15 +1,15 @@ -Locale = Locale or {} +Locale = Locale or {} Locale.en = { -- 'fr' is the reference that will be used for 'Config.Language' - StandaloneLapText = "Ask for a lap dance", -- Set the text that will be displayed above marker if 'Config.Framework' is set to 'standalone' - LapText = "Buy a lap dance (~g~%$~w~)", -- Set the text that will be displayed above marker - BoughtLapdance = "You just bought a lap dance for %$", -- Notification text when a lap dance is bought - StripperActive = "The stripper is already busy!", -- Notification text if a stripper is already active when you try to buy a lap dance - NotEnoughMoney = "You do not have enough money. A lap dance costs %$", -- Notification text if player don't have enough cash - AllPlacesTaken = "All places are already taken", -- Notification text if all places are already taken - lapStopped = "Your lap dance has been interrupted", -- Notification text if lapdance is interrupted - Lean = "Lean", -- Text displayed when near poledance - StandaloneLeanNotice = "Press ~INPUT_CONTEXT~ to stop leaning\nPress ~INPUT_JUMP~ to throw money", -- Set the text that will be displayed once lean if 'Config.Framework' is set to 'standalone' - LeanNotice = "Press ~INPUT_CONTEXT~ to stop leaning\nPress ~INPUT_JUMP~ to throw money (~g~%$~w~)", -- Set the text that will be displayed once lean - NotEnoughCashLean = "Not enough cash to throw" -- Text if player doesn't have enough money to throw once lean -} \ No newline at end of file + StandaloneLapText = "Ask for a lap dance", -- Set the text that will be displayed above marker if 'Config.Framework' is set to 'standalone' + LapText = "Buy a lap dance (~g~%$~w~)", -- Set the text that will be displayed above marker + BoughtLapdance = "You just bought a lap dance for %$", -- Notification text when a lap dance is bought + StripperActive = "The stripper is already busy!", -- Notification text if a stripper is already active when you try to buy a lap dance + NotEnoughMoney = "You do not have enough money. A lap dance costs %$", -- Notification text if player don't have enough cash + AllPlacesTaken = "All places are already taken", -- Notification text if all places are already taken + lapStopped = "Your lap dance has been interrupted", -- Notification text if lapdance is interrupted + Lean = "Lean", -- Text displayed when near poledance + StandaloneLeanNotice = "Press ~INPUT_CONTEXT~ to stop leaning\nPress ~INPUT_JUMP~ to throw money", -- Set the text that will be displayed once lean if 'Config.Framework' is set to 'standalone' + LeanNotice = "Press ~INPUT_CONTEXT~ to stop leaning\nPress ~INPUT_JUMP~ to throw money (~g~%$~w~)", -- Set the text that will be displayed once lean + NotEnoughCashLean = "Not enough cash to throw", -- Text if player doesn't have enough money to throw once lean +} diff --git a/server-data/resources/[esx_addons]/kc-unicorn/locales/fr.lua b/server-data/resources/[esx_addons]/kc-unicorn/locales/fr.lua index b7c372bd1..701e445d3 100644 --- a/server-data/resources/[esx_addons]/kc-unicorn/locales/fr.lua +++ b/server-data/resources/[esx_addons]/kc-unicorn/locales/fr.lua @@ -1,15 +1,15 @@ -Locale = Locale or {} +Locale = Locale or {} Locale.fr = { -- 'fr' est la référence qui sera utilisé par 'Config.Language' - StandaloneLapText = "Demander un lap dance", -- Définit le texte qui sera affiché au-dessus du marqueur si 'Config.Framework' est mis sur 'standalone' - LapText = "Acheter un lap dance (~g~%$~w~)", -- Définit le texte qui sera affiché au-dessus du marqueur - BoughtLapdance = "Vous avez acheté un lap dance pour %$", -- Texte de la notification lorsqu'un lap dance est acheté - StripperActive = "La strip-teaseuse est déjà occupé !", -- Texte de la notification si une strip-teaseuse est déjà occupé lorsque vous essayez d'acheter un lap dance - NotEnoughMoney = "Vous n'avez pas assez d'argent. Un lap dance coûte %$", -- Texte de la notification si le joueur n'a pas assez d'argent - AllPlacesTaken = "Toutes les places sont déjà prises", -- Texte de la notification si toutes les places sont déjà prises - lapStopped = "Ta lap dance a été interrompu !", -- Texte si la lap dance est interrompu - Lean = "Se pencher", -- Texte pour se pencher au niveau de la bar de poledance - StandaloneLeanNotice = "Appuyez sur ~INPUT_CONTEXT~ pour arrêter de vous pencher\nAppuyez sur ~INPUT_JUMP~ pour jeter de l'argent", -- Définit le texte qui sera affiché une fois penché si 'Config.Framework' est mis sur 'standalone' - LeanNotice = "Appuyez sur ~INPUT_CONTEXT~ pour arrêter de vous pencher\nAppuyez sur ~INPUT_JUMP~ pour jeter de l'argent (~g~%$~w~)", -- Définit le texte qui sera affiché une fois penché - NotEnoughCashLean = "Vous n'avez pas assez d'argent" -- Texte si le joueur n'a pas assez d'argent pour jeter un billet une fois penché -} \ No newline at end of file + StandaloneLapText = "Demander un lap dance", -- Définit le texte qui sera affiché au-dessus du marqueur si 'Config.Framework' est mis sur 'standalone' + LapText = "Acheter un lap dance (~g~%$~w~)", -- Définit le texte qui sera affiché au-dessus du marqueur + BoughtLapdance = "Vous avez acheté un lap dance pour %$", -- Texte de la notification lorsqu'un lap dance est acheté + StripperActive = "La strip-teaseuse est déjà occupé !", -- Texte de la notification si une strip-teaseuse est déjà occupé lorsque vous essayez d'acheter un lap dance + NotEnoughMoney = "Vous n'avez pas assez d'argent. Un lap dance coûte %$", -- Texte de la notification si le joueur n'a pas assez d'argent + AllPlacesTaken = "Toutes les places sont déjà prises", -- Texte de la notification si toutes les places sont déjà prises + lapStopped = "Ta lap dance a été interrompu !", -- Texte si la lap dance est interrompu + Lean = "Se pencher", -- Texte pour se pencher au niveau de la bar de poledance + StandaloneLeanNotice = "Appuyez sur ~INPUT_CONTEXT~ pour arrêter de vous pencher\nAppuyez sur ~INPUT_JUMP~ pour jeter de l'argent", -- Définit le texte qui sera affiché une fois penché si 'Config.Framework' est mis sur 'standalone' + LeanNotice = "Appuyez sur ~INPUT_CONTEXT~ pour arrêter de vous pencher\nAppuyez sur ~INPUT_JUMP~ pour jeter de l'argent (~g~%$~w~)", -- Définit le texte qui sera affiché une fois penché + NotEnoughCashLean = "Vous n'avez pas assez d'argent", -- Texte si le joueur n'a pas assez d'argent pour jeter un billet une fois penché +} diff --git a/server-data/resources/[esx_addons]/kc-unicorn/server/main.lua b/server-data/resources/[esx_addons]/kc-unicorn/server/main.lua index a759633ed..478864642 100644 --- a/server-data/resources/[esx_addons]/kc-unicorn/server/main.lua +++ b/server-data/resources/[esx_addons]/kc-unicorn/server/main.lua @@ -1,11 +1,7 @@ -local GithubL, Changelog, ESX, QBCore, inPoleDance - -local Version = GetResourceMetadata(GetCurrentResourceName(), 'version', 0) -- Do Not Change This Value -local Github = GetResourceMetadata(GetCurrentResourceName(), 'github', 0) -local Updater = false +local ESX, QBCore, inPoleDance local INPOLEDANCE = false local PedNumber = 2 -local PoledancePed1, PoledancePed2 +local PoledancePed1, PoledancePed2, LapDanceActive, Poledance -- Server Seat Locales local GetPlayerSeated = 0 @@ -19,102 +15,13 @@ local Seat7Taken = false -- Import if Config.Framework == "ESX" then - TriggerEvent('esx:getSharedObject', function(obj) ESX = obj end) + ESX = exports["es_extended"]:getSharedObject() elseif Config.Framework == "QBCore" then - QBCore = exports['qb-core']:GetCoreObject() -end - --------- Check if new update available -Citizen.CreateThread(function() - - local Resources = GetNumResources() - - for i = 0, Resources, 1 do - local resource = GetResourceByFindIndex(i) - UpdateResource(resource) - end - - Citizen.Wait(4000) - - if Updater == false then - if Config.UpdateChecker then - Updater = not Updater - UpdateChecker() - end - end - -end) - -function UpdateResource(resource) - if resource == 'fivem-checker' then - if GetResourceState(resource) == 'started' or GetResourceState(resource) == 'starting' then - if Config.UpdateChecker then - Updater = true - end - end - end -end - ----------------- - -function UpdateChecker() - - if string.find(Github, "github") then - if string.find(Github, "github.com") then - GithubL = Github - Github = string.gsub(Github, "github", "raw.githubusercontent")..'/master/version' - else - GithubL = string.gsub(Github, "raw.githubusercontent", "github"):gsub("/master", "") - Github = Github..'/version' - end - else - end - PerformHttpRequest(Github, function(Error, V, Header) - NewestVersion = V - end) - repeat - Citizen.Wait(10) - until NewestVersion ~= nil - local _, strings = string.gsub(NewestVersion, "\n", "\n") - Version1 = NewestVersion:match("[^\n]*"):gsub("[<>]", "") - if string.find(Version1, Version) then - else - if strings > 0 then - Changelog = NewestVersion:gsub(Version1,""):match("(.*" .. Version .. ")"):gsub(Version,"") - Changelog = string.gsub(Changelog, "\n", "") - Changelog = string.gsub(Changelog, "-", " \n-"):gsub("%b<>", ""):sub(1, -2) - NewestVersion = Version1 - end - end - - Citizen.Wait(500) - - print('') - if Config.Framework == 'QBCore' then - print('^8QBCore ^6KC Unicorn Script ('..GetCurrentResourceName()..')') - elseif Config.Framework == 'Standalone' then - print('^9Standalone ^6KC Unicorn Script ('..GetCurrentResourceName()..')') - elseif Config.Framework == 'ESX' then - print('^3ESX ^6KC Unicorn Script ('..GetCurrentResourceName()..')') - end - if Version == Version1 then - print('^2Version ' .. Version .. ' - Up to date!') - else - print('^1Version ' .. Version .. ' - Outdated!') - print('^1New version: v' .. Version1) - if Config.ChangeLog then - print('\n^3Changelog:') - print('^4'..Changelog..'\n') - end - print('^1Please check the GitHub and download the last update') - print('^2'..GithubL..'/releases/latest') - end - print('') + QBCore = exports["qb-core"]:GetCoreObject() end -- Poledance Girls (WIP) Citizen.CreateThread(function() - PoledancePed1 = CreatePed(4, 1381498905, 112.27, -1287.22, 28.46, 303.19, true, false) PoledancePed2 = CreatePed(4, 1846523796, 102.13, -1296.19, 28.77, 303.19, true, false) SharedPed1 = NetworkGetNetworkIdFromEntity(PoledancePed1) @@ -122,10 +29,9 @@ Citizen.CreateThread(function() inPoleDance = true - SetPedComponentVariation(PoledancePed2, 8, 0) - SetPedComponentVariation(PoledancePed1, 8, 0) + SetPedComponentVariation(PoledancePed2, 8, 0) + SetPedComponentVariation(PoledancePed1, 8, 0) - repeat inPoleDance = true @@ -134,34 +40,34 @@ Citizen.CreateThread(function() PoleDance(PoledancePed2) inPoleDance = true - + if PedNumber == 2 then PedNumber = 1 else PedNumber = 2 end - TaskGoToCoordAnyMeans(PoledancePed2, 102.13, -1296.19, 28.77, 1.0, 0, 0, 0, 0xbf800000) - TaskGoToCoordAnyMeans(PoledancePed1, 112.27, -1287.22, 28.46, 1.0, 0, 0, 0, 0xbf800000) + TaskGoToCoordAnyMeans(PoledancePed2, 102.13, -1296.19, 28.77, 1.0, 0, false, 0, 0xbf800000) + TaskGoToCoordAnyMeans(PoledancePed1, 112.27, -1287.22, 28.46, 1.0, 0, false, 0, 0xbf800000) PoleDance(PoledancePed1) - + if PedNumber == 2 then PedNumber = 1 else PedNumber = 2 end - - until (Poledance == false) + + until Poledance == false end) function PoleDance(Ped) while inPoleDance do PedCoo = GetEntityCoords(Ped) local poledist = #(PedCoo - vector3(112.27, -1287.22, 28.46)) - local dict = "mini@strip_club@pole_dance@pole_dance"..PedNumber + local dict = "mini@strip_club@pole_dance@pole_dance" .. PedNumber if poledist < 0.4 then INPOLEDANCE = true - TriggerClientEvent('kc-unicorn:poledancescene', -1, NetworkGetNetworkIdFromEntity(Ped), PedNumber) + TriggerClientEvent("kc-unicorn:poledancescene", -1, NetworkGetNetworkIdFromEntity(Ped), PedNumber) repeat Citizen.Wait(200) until INPOLEDANCE == false @@ -172,11 +78,9 @@ function PoleDance(Ped) end -- Buy function -RegisterServerEvent('kc-unicorn:buy') -AddEventHandler('kc-unicorn:buy', function(Stripper) - +RegisterServerEvent("kc-unicorn:buy") +AddEventHandler("kc-unicorn:buy", function(Stripper) if Config.Framework == "QBCore" then - local src = source local Player = QBCore.Functions.GetPlayer(src) Cost = Config.LapDanceCost @@ -185,103 +89,90 @@ AddEventHandler('kc-unicorn:buy', function(Stripper) TodayDate = os.date("%Y-%m-%d") if GetPlayerSeated >= 7 then - TriggerClientEvent('QBCore:Notify', src, Loc('AllPlacesTaken'), "error", 1700) + TriggerClientEvent("QBCore:Notify", src, Loc("AllPlacesTaken"), "error", 1700) else if PlayerMoney >= Cost then if not LapDanceActive then Player.Functions.RemoveMoney("cash", Cost) - TriggerClientEvent('QBCore:Notify', src, Loc('BoughtLapdance', Config.LapDanceCost), "success", 1700) - TriggerClientEvent('kc-unicorn:lapdance', src, PlayerMoney, PlayerBirthdate, TodayDate, Stripper) + TriggerClientEvent("QBCore:Notify", src, Loc("BoughtLapdance", Config.LapDanceCost), "success", 1700) + TriggerClientEvent("kc-unicorn:lapdance", src, PlayerMoney, PlayerBirthdate, TodayDate, Stripper) else - TriggerClientEvent('QBCore:Notify', src, Loc('StripperActive'), "error", 1700) + TriggerClientEvent("QBCore:Notify", src, Loc("StripperActive"), "error", 1700) end else - TriggerClientEvent('QBCore:Notify', src, Loc('NotEnoughMoney', Config.LapDanceCost), "error", 1700) + TriggerClientEvent("QBCore:Notify", src, Loc("NotEnoughMoney", Config.LapDanceCost), "error", 1700) end end - elseif Config.Framework == "ESX" then - local src = source local Player = ESX.GetPlayerFromId(src) Cost = Config.LapDanceCost PlayerMoney = Player.getMoney() if GetPlayerSeated >= 7 then - TriggerClientEvent('esx:showNotification', src, Loc('AllPlacesTaken')) + TriggerClientEvent("esx:showNotification", src, Loc("AllPlacesTaken")) else if PlayerMoney >= Cost then if not LapDanceActive then Player.removeMoney(Cost) - TriggerClientEvent('esx:showNotification', src, Loc('BoughtLapdance', Config.LapDanceCost)) - TriggerClientEvent('kc-unicorn:lapdance', src, PlayerMoney, PlayerBirthdate, TodayDate, Stripper) + TriggerClientEvent("esx:showNotification", src, Loc("BoughtLapdance", Config.LapDanceCost)) + TriggerClientEvent("kc-unicorn:lapdance", src, PlayerMoney, PlayerBirthdate, TodayDate, Stripper) else - TriggerClientEvent('esx:showNotification', src, Loc('StripperActive')) + TriggerClientEvent("esx:showNotification", src, Loc("StripperActive")) end else - TriggerClientEvent('esx:showNotification', src, Loc('NotEnoughMoney', Config.LapDanceCost)) + TriggerClientEvent("esx:showNotification", src, Loc("NotEnoughMoney", Config.LapDanceCost)) end end - - elseif Config.Framework == 'Standalone' then - + elseif Config.Framework == "Standalone" then local src = source - + if GetPlayerSeated >= 7 then - TriggerClientEvent('kc-unicorn:showNotify', src, Loc('AllPlacesTaken')) + TriggerClientEvent("kc-unicorn:showNotify", src, Loc("AllPlacesTaken")) else - TriggerClientEvent('kc-unicorn:lapdance', src, PlayerMoney, PlayerBirthdate, TodayDate, Stripper) + TriggerClientEvent("kc-unicorn:lapdance", src, PlayerMoney, PlayerBirthdate, TodayDate, Stripper) end end end) -- Lean Throw function -RegisterServerEvent('kc-unicorn:leanthrow') -AddEventHandler('kc-unicorn:leanthrow', function() - +RegisterServerEvent("kc-unicorn:leanthrow") +AddEventHandler("kc-unicorn:leanthrow", function() if Config.Framework == "QBCore" then - local src = source local Player = QBCore.Functions.GetPlayer(src) Cost = Config.ThrowCost PlayerMoney = Player.PlayerData.money["cash"] Player.Functions.RemoveMoney("cash", Cost) - elseif Config.Framework == "ESX" then - local src = source local Player = ESX.GetPlayerFromId(src) Cost = Config.ThrowCost PlayerMoney = Player.getMoney() Player.removeMoney(Cost) - end end) -- Lean ESX Get Money -RegisterServerEvent('kc-unicorn:esxmoney') -AddEventHandler('kc-unicorn:esxmoney', function() - +RegisterServerEvent("kc-unicorn:esxmoney") +AddEventHandler("kc-unicorn:esxmoney", function() local src = source local Player = ESX.GetPlayerFromId(src) Cost = Config.LapDanceCost PlayerMoney = Player.getMoney() - - TriggerClientEvent("kc-unicorn:esxplayermoney", src, PlayerMoney) + TriggerClientEvent("kc-unicorn:esxplayermoney", src, PlayerMoney) end) -RegisterServerEvent('kc-unicorn:SyncLapDanceSpawn') -AddEventHandler('kc-unicorn:SyncLapDanceSpawn', function() - +RegisterServerEvent("kc-unicorn:SyncLapDanceSpawn") +AddEventHandler("kc-unicorn:SyncLapDanceSpawn", function() TriggerClientEvent("kc-unicorn:LapDanceSpawn", 0) - end) -RegisterServerEvent('kc-unicorn:GetPlayerSeated') -AddEventHandler('kc-unicorn:GetPlayerSeated', function() +RegisterServerEvent("kc-unicorn:GetPlayerSeated") +AddEventHandler("kc-unicorn:GetPlayerSeated", function() local src = source if Seat1Taken then @@ -312,103 +203,97 @@ AddEventHandler('kc-unicorn:GetPlayerSeated', function() if Config.Debug then print("Server: ", "Seat1: ", Seat1Taken, "Seat2: ", Seat2Taken, "Seat3: ", Seat3Taken, "Seat4: ", Seat4Taken, "Seat5: ", Seat5Taken, "Seat6: ", Seat6Taken, "Seat7: ", Seat7Taken, " ", GetPlayerSeated) end - TriggerClientEvent("kc-unicorn:SetPlayerSeated", src, GetPlayerSeated, Seat1Taken, Seat2Taken, Seat3Taken, Seat4Taken, Seat5Taken, Seat6Taken, Seat7Taken) + TriggerClientEvent("kc-unicorn:SetPlayerSeated", src, GetPlayerSeated, Seat1Taken, Seat2Taken, Seat3Taken, Seat4Taken, Seat5Taken, Seat6Taken, Seat7Taken) GetPlayerSeated = 0 - end) -RegisterServerEvent('kc-unicorn:Seat1') -AddEventHandler('kc-unicorn:Seat1', function(seat) +RegisterServerEvent("kc-unicorn:Seat1") +AddEventHandler("kc-unicorn:Seat1", function(seat) Seat1Taken = true end) -RegisterServerEvent('kc-unicorn:Seat2') -AddEventHandler('kc-unicorn:Seat2', function(seat) +RegisterServerEvent("kc-unicorn:Seat2") +AddEventHandler("kc-unicorn:Seat2", function(seat) Seat2Taken = true end) -RegisterServerEvent('kc-unicorn:Seat3') -AddEventHandler('kc-unicorn:Seat3', function(seat) +RegisterServerEvent("kc-unicorn:Seat3") +AddEventHandler("kc-unicorn:Seat3", function(seat) Seat3Taken = true end) -RegisterServerEvent('kc-unicorn:Seat4') -AddEventHandler('kc-unicorn:Seat4', function(seat) +RegisterServerEvent("kc-unicorn:Seat4") +AddEventHandler("kc-unicorn:Seat4", function(seat) Seat4Taken = true end) -RegisterServerEvent('kc-unicorn:Seat5') -AddEventHandler('kc-unicorn:Seat5', function(seat) +RegisterServerEvent("kc-unicorn:Seat5") +AddEventHandler("kc-unicorn:Seat5", function(seat) Seat5Taken = true end) -RegisterServerEvent('kc-unicorn:Seat6') -AddEventHandler('kc-unicorn:Seat6', function(seat) +RegisterServerEvent("kc-unicorn:Seat6") +AddEventHandler("kc-unicorn:Seat6", function(seat) Seat6Taken = true end) -RegisterServerEvent('kc-unicorn:Seat7') -AddEventHandler('kc-unicorn:Seat7', function(seat) +RegisterServerEvent("kc-unicorn:Seat7") +AddEventHandler("kc-unicorn:Seat7", function(seat) Seat7Taken = true end) -RegisterServerEvent('kc-unicorn:RemoveSeat1') -AddEventHandler('kc-unicorn:RemoveSeat1', function(seat) +RegisterServerEvent("kc-unicorn:RemoveSeat1") +AddEventHandler("kc-unicorn:RemoveSeat1", function(seat) Seat1Taken = false end) -RegisterServerEvent('kc-unicorn:RemoveSeat2') -AddEventHandler('kc-unicorn:RemoveSeat2', function(seat) +RegisterServerEvent("kc-unicorn:RemoveSeat2") +AddEventHandler("kc-unicorn:RemoveSeat2", function(seat) Seat2Taken = false end) -RegisterServerEvent('kc-unicorn:RemoveSeat3') -AddEventHandler('kc-unicorn:RemoveSeat3', function(seat) +RegisterServerEvent("kc-unicorn:RemoveSeat3") +AddEventHandler("kc-unicorn:RemoveSeat3", function(seat) Seat3Taken = false end) -RegisterServerEvent('kc-unicorn:RemoveSeat4') -AddEventHandler('kc-unicorn:RemoveSeat4', function(seat) +RegisterServerEvent("kc-unicorn:RemoveSeat4") +AddEventHandler("kc-unicorn:RemoveSeat4", function(seat) Seat4Taken = false end) -RegisterServerEvent('kc-unicorn:RemoveSeat5') -AddEventHandler('kc-unicorn:RemoveSeat5', function(seat) +RegisterServerEvent("kc-unicorn:RemoveSeat5") +AddEventHandler("kc-unicorn:RemoveSeat5", function(seat) Seat5Taken = false end) -RegisterServerEvent('kc-unicorn:RemoveSeat6') -AddEventHandler('kc-unicorn:RemoveSeat6', function(seat) +RegisterServerEvent("kc-unicorn:RemoveSeat6") +AddEventHandler("kc-unicorn:RemoveSeat6", function(seat) Seat6Taken = false end) -RegisterServerEvent('kc-unicorn:RemoveSeat7') -AddEventHandler('kc-unicorn:RemoveSeat7', function(seat) +RegisterServerEvent("kc-unicorn:RemoveSeat7") +AddEventHandler("kc-unicorn:RemoveSeat7", function(seat) Seat7Taken = false end) -RegisterServerEvent('kc-unicorn:NoPlaceAvailable') -AddEventHandler('kc-unicorn:NoPlaceAvailable', function() - +RegisterServerEvent("kc-unicorn:NoPlaceAvailable") +AddEventHandler("kc-unicorn:NoPlaceAvailable", function() local src = source local Player = QBCore.Functions.GetPlayer(src) Cost = Config.LapDanceCost Player.Functions.AddMoney("cash", Cost) - TriggerClientEvent('QBCore:Notify', src, Loc('AllPlacesTaken'), "error", 1700) - - + TriggerClientEvent("QBCore:Notify", src, Loc("AllPlacesTaken"), "error", 1700) end) -AddEventHandler('onResourceStop', function() +AddEventHandler("onResourceStop", function() DeleteEntity(PoledancePed1) DeleteEntity(PoledancePed2) print("Ped succesfully deleted") - end) +end) -RegisterServerEvent('kc-unicorn:poledancestop') -AddEventHandler('kc-unicorn:poledancestop', function() - +RegisterServerEvent("kc-unicorn:poledancestop") +AddEventHandler("kc-unicorn:poledancestop", function() INPOLEDANCE = false - end) From 390c66f32ae303dad2ef0169135c46bd38956301 Mon Sep 17 00:00:00 2001 From: bitpredator <67551273+bitpredator@users.noreply.github.com> Date: Tue, 3 Dec 2024 18:21:12 +0100 Subject: [PATCH 16/22] fix: corrected the name and weight of the trout item In this PR the weight and name of the trout item have been corrected. --- server-data/resources/[ox]/ox_inventory/data/items.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server-data/resources/[ox]/ox_inventory/data/items.lua b/server-data/resources/[ox]/ox_inventory/data/items.lua index 8eb79d2a2..2a29fe2b9 100644 --- a/server-data/resources/[ox]/ox_inventory/data/items.lua +++ b/server-data/resources/[ox]/ox_inventory/data/items.lua @@ -463,8 +463,8 @@ return { }, ["trout"] = { - label = "20", - weight = 100, + label = "trota", + weight = 20, stack = true, }, From dbec0dd0666edaac9c75d8182c26cf287d4afedf Mon Sep 17 00:00:00 2001 From: bitpredator <67551273+bitpredator@users.noreply.github.com> Date: Wed, 4 Dec 2024 10:20:12 +0100 Subject: [PATCH 17/22] fix: (bpt_importjob) correct missing translation - In this PR the problem of the missing translation in the invoice manual for import export work is resolved --- server-data/resources/[bpt_addons]/bpt_importjob/locales/it.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/server-data/resources/[bpt_addons]/bpt_importjob/locales/it.lua b/server-data/resources/[bpt_addons]/bpt_importjob/locales/it.lua index 06883aefb..7b9b0e54d 100644 --- a/server-data/resources/[bpt_addons]/bpt_importjob/locales/it.lua +++ b/server-data/resources/[bpt_addons]/bpt_importjob/locales/it.lua @@ -29,5 +29,6 @@ Locales["it"] = { ["import"] = "import", -- billing ["bill_amount"] = "importo della fattura", + ["amount"] = "importo", ["confirm"] = "Conferma", } From 12144531e480936213fe47f4f9015f75f0c3d9ae Mon Sep 17 00:00:00 2001 From: bitpredator <67551273+bitpredator@users.noreply.github.com> Date: Fri, 6 Dec 2024 13:28:12 +0100 Subject: [PATCH 18/22] refactor: introduced into the most real garages --- .../[esx_addons]/esx_realparking/LICENSE | 674 ++++++++++++++++++ .../[esx_addons]/esx_realparking/README.md | 62 ++ .../esx_realparking/client/main.lua | 571 +++++++++++++++ .../[esx_addons]/esx_realparking/config.lua | 72 ++ .../esx_realparking/esx_realparking.sql | 11 + .../esx_realparking_newESX.sql | 11 + .../esx_realparking/fxmanifest.lua | 20 + .../esx_realparking/locales/bg.lua | 16 + .../esx_realparking/locales/de.lua | 17 + .../esx_realparking/locales/en.lua | 16 + .../esx_realparking/locales/es.lua | 16 + .../esx_realparking/locales/fr.lua | 16 + .../esx_realparking/locales/zh.lua | 16 + .../esx_realparking/server/main.lua | 280 ++++++++ .../[ox]/ox_inventory/data/items.lua | 6 +- 15 files changed, 1803 insertions(+), 1 deletion(-) create mode 100644 server-data/resources/[esx_addons]/esx_realparking/LICENSE create mode 100644 server-data/resources/[esx_addons]/esx_realparking/README.md create mode 100644 server-data/resources/[esx_addons]/esx_realparking/client/main.lua create mode 100644 server-data/resources/[esx_addons]/esx_realparking/config.lua create mode 100644 server-data/resources/[esx_addons]/esx_realparking/esx_realparking.sql create mode 100644 server-data/resources/[esx_addons]/esx_realparking/esx_realparking_newESX.sql create mode 100644 server-data/resources/[esx_addons]/esx_realparking/fxmanifest.lua create mode 100644 server-data/resources/[esx_addons]/esx_realparking/locales/bg.lua create mode 100644 server-data/resources/[esx_addons]/esx_realparking/locales/de.lua create mode 100644 server-data/resources/[esx_addons]/esx_realparking/locales/en.lua create mode 100644 server-data/resources/[esx_addons]/esx_realparking/locales/es.lua create mode 100644 server-data/resources/[esx_addons]/esx_realparking/locales/fr.lua create mode 100644 server-data/resources/[esx_addons]/esx_realparking/locales/zh.lua create mode 100644 server-data/resources/[esx_addons]/esx_realparking/server/main.lua diff --git a/server-data/resources/[esx_addons]/esx_realparking/LICENSE b/server-data/resources/[esx_addons]/esx_realparking/LICENSE new file mode 100644 index 000000000..f288702d2 --- /dev/null +++ b/server-data/resources/[esx_addons]/esx_realparking/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/server-data/resources/[esx_addons]/esx_realparking/README.md b/server-data/resources/[esx_addons]/esx_realparking/README.md new file mode 100644 index 000000000..d638b20d4 --- /dev/null +++ b/server-data/resources/[esx_addons]/esx_realparking/README.md @@ -0,0 +1,62 @@ +# esx_realparking +A real car parking script for FiveM + +![Parking example](https://i.imgur.com/J6SqHBK.png) + +## Features + +- Store the car like the real life +- Support esx_vehicleshop +- Parking anywhere +- Parking fees for player + +## Requirements + +1. [ESX](https://github.com/ESX-Org/es_extended) +2. [esx_vehicleshop](https://github.com/ESX-Org/esx_vehicleshop) + +## Download & Installation + +### Using YumV + +``` +yum install esx_realparking +``` + +### Using Git + +``` +git clone https://github.com/kasuganosoras/esx_realparking +``` + +### Manually + +- Download https://github.com/kasuganosoras/esx_realparking/archive/master.zip +- Put it in the `[esx]` directory + +## Installation + +- Import esx_realparking.sql to your database +- Add this in your `server.cfg` + +``` +start esx_realparking +``` + +## Videos + +https://www.youtube.com/watch?v=sDsqSuw2yKg + +## Legal + +### License + +esx_realparking - car parking script for ESX + +Copyright (C) 2021 Akkariin + +This program Is free software: you can redistribute it And/Or modify it under the terms Of the GNU General Public License As published by the Free Software Foundation, either version 3 Of the License, Or (at your option) any later version. + +This program Is distributed In the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty Of MERCHANTABILITY Or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License For more details. + +You should have received a copy Of the GNU General Public License along with this program. If Not, see http://www.gnu.org/licenses/. diff --git a/server-data/resources/[esx_addons]/esx_realparking/client/main.lua b/server-data/resources/[esx_addons]/esx_realparking/client/main.lua new file mode 100644 index 000000000..0e6ce945e --- /dev/null +++ b/server-data/resources/[esx_addons]/esx_realparking/client/main.lua @@ -0,0 +1,571 @@ +--[[ ===================================================== ]] +-- +--[[ FiveM Real Parking Script by Akkariin ]] +-- +--[[ ===================================================== ]] +-- + +-- Arrays +PlayerData = {} +LocalVehicles = {} +GlobalVehicles = {} + +-- Variables +ESX = nil +CurrentFee = nil +LastPlate = nil +PlayerIdentifier = nil +SpawnedVehicles = false +DeletingEntities = false + +-- Refresh the vehicles + +RegisterNetEvent("esx_realparking:refreshVehicles") +AddEventHandler("esx_realparking:refreshVehicles", function(vehicles) + DebugLog("Server request refresh vehicles list") + GlobalVehicles = vehicles + RemoveVehicles(vehicles) + Citizen.Wait(1000) + SpawnVehicles(vehicles) +end) + +RegisterNetEvent("esx_realparking:addVehicle") +AddEventHandler("esx_realparking:addVehicle", function(vehicle, owner) + if owner == GetPlayerServerId(PlayerId()) then + DeleteEntity(GetVehiclePedIsIn(GetPlayerPed(-1), false)) + end + SpawnVehicle(vehicle) +end) + +RegisterNetEvent("esx_realparking:deleteVehicle") +AddEventHandler("esx_realparking:deleteVehicle", function(vehicle) + DeleteLocalVehicle(vehicle) +end) + +RegisterNetEvent("esx_realparking:impoundVehicle") +AddEventHandler("esx_realparking:impoundVehicle", function(vehicle) + ImpoundVehicle(vehicle) +end) + +function DebugLog(text) + if Config.debug then + print(string.format("[DEBUG] %s", text)) + end +end + +-- Get the stored vehicle player is in + +function GetPedInStoredCar(ped) + local tempVeh = GetVehiclePedIsIn(ped) + local findVeh = false + for i = 1, #LocalVehicles do + if LocalVehicles[i] ~= nil and LocalVehicles[i].entity == tempVeh then + findVeh = LocalVehicles[i] + break + end + end + -- Clean memory + tempVeh = nil + return findVeh +end + +-- Spawn local vehicles + +function SpawnVehicles(vehicles) + Citizen.CreateThread(function() + while DeletingEntities do + Citizen.Wait(100) + end + DebugLog("Start spawn all vehicles") + for i = 1, #vehicles, 1 do + local vehicleProps = vehicles[i].vehicle.props + DeleteLocalVehicle(vehicles[i].vehicle) + DeleteNearVehicle(vec3(vehicles[i].vehicle.location.x, vehicles[i].vehicle.location.y, vehicles[i].vehicle.location.z)) + local carLivery = -1 + if type(vehicles[i].vehicle.livery) ~= "nil" then + carLivery = vehicles[i].vehicle.livery + end + LoadModel(vehicleProps["model"]) + local tempVeh = CreateVehicle(vehicleProps["model"], vehicles[i].vehicle.location.x, vehicles[i].vehicle.location.y, vehicles[i].vehicle.location.z, vehicles[i].vehicle.location.h, false) + ESX.Game.SetVehicleProperties(tempVeh, vehicleProps) + RequestCollisionAtCoord(vehicles[i].vehicle.location.x, vehicles[i].vehicle.location.y, vehicles[i].vehicle.location.z) + SetVehicleOnGroundProperly(tempVeh) + SetEntityAsMissionEntity(tempVeh, true, true) + SetModelAsNoLongerNeeded(vehicleProps["model"]) + SetEntityInvincible(tempVeh, true) + SetVehicleLivery(tempVeh, vehicles[i].vehicle.livery) + NetworkFadeInEntity(tempVeh, false) + Wait(100) + FreezeEntityPosition(tempVeh, true) + if vehicles[i].owner ~= PlayerData.identifier then + SetVehicleDoorsLocked(tempVeh, 2) + end + table.insert(LocalVehicles, { + entity = tempVeh, + data = vehicles[i].vehicle, + plate = vehicles[i].plate, + fee = vehicles[i].fee, + owner = vehicles[i].owner, + name = vehicles[i].name, + livery = carLivery, + health = vehicles[i].vehicle.health, + location = vehicles[i].vehicle.location, + }) + if LastPlate ~= nil and vehicles[i].plate == LastPlate then + TaskWarpPedIntoVehicle(GetPlayerPed(-1), tempVeh, -1) + Wait(500) + TaskLeaveVehicle(GetPlayerPed(-1), tempVeh) + LastPlate = nil + end + Wait(300) + -- Clean memory + vehicleProps, carLivery, tempVeh = nil + end + end) +end + +-- Spawn single vehicle + +function SpawnVehicle(vehicleData) + Citizen.CreateThread(function() + while DeletingEntities do + Citizen.Wait(100) + end + DeleteLocalVehicle(vehicleData.vehicle) + DeleteNearVehicle(vec3(vehicleData.vehicle.location.x, vehicleData.vehicle.location.y, vehicleData.vehicle.location.z)) + local vehicleProps = vehicleData.vehicle.props + local carLivery = -1 + if type(vehicleData.vehicle.livery) ~= "nil" then + carLivery = vehicleData.vehicle.livery + end + LoadModel(vehicleProps["model"]) + local tempVeh = CreateVehicle(vehicleProps["model"], vehicleData.vehicle.location.x, vehicleData.vehicle.location.y, vehicleData.vehicle.location.z, vehicleData.vehicle.location.h, false) + ESX.Game.SetVehicleProperties(tempVeh, vehicleProps) + RequestCollisionAtCoord(vehicleData.vehicle.location.x, vehicleData.vehicle.location.y, vehicleData.vehicle.location.z) + SetVehicleOnGroundProperly(tempVeh) + SetEntityAsMissionEntity(tempVeh, true, true) + SetModelAsNoLongerNeeded(vehicleProps["model"]) + SetEntityInvincible(tempVeh, true) + SetVehicleLivery(tempVeh, vehicleData.vehicle.livery) + SetVehicleEngineHealth(tempVeh, vehicleData.vehicle.health.engine) + SetVehicleBodyHealth(tempVeh, vehicleData.vehicle.health.body) + SetVehiclePetrolTankHealth(tempVeh, vehicleData.vehicle.health.tank) + NetworkFadeInEntity(tempVeh, false) + Wait(100) + FreezeEntityPosition(tempVeh, true) + if vehicleData.owner ~= PlayerData.identifier then + SetVehicleDoorsLocked(tempVeh, 2) + end + table.insert(LocalVehicles, { + entity = tempVeh, + data = vehicleData.vehicle, + plate = vehicleData.plate, + fee = vehicleData.fee, + owner = vehicleData.owner, + name = vehicleData.name, + livery = carLivery, + health = vehicleData.vehicle.health, + location = vehicleData.vehicle.location, + }) + if LastPlate ~= nil and vehicleData.plate == LastPlate then + TaskWarpPedIntoVehicle(GetPlayerPed(-1), tempVeh, -1) + Wait(500) + TaskLeaveVehicle(GetPlayerPed(-1), tempVeh) + LastPlate = nil + end + -- Clean memory + vehicleProps, carLivery, tempVeh = nil + end) +end + +-- When player drive the car + +function DriveVehicle(vehicle) + -- Delete the local entity first + DeleteNearVehicle(vector3(vehicle.location.x, vehicle.location.y, vehicle.location.z)) + local vehicleProps = vehicle.props + LoadModel(vehicleProps["model"]) + local tempVeh = CreateVehicle(vehicleProps["model"], vehicle.location.x, vehicle.location.y, vehicle.location.z, vehicle.location.h, true) + ESX.Game.SetVehicleProperties(tempVeh, vehicleProps) + SetVehicleOnGroundProperly(tempVeh) + SetVehicleLivery(tempVeh, vehicle.livery) + SetVehicleEngineHealth(tempVeh, vehicle.health.engine) + SetVehicleBodyHealth(tempVeh, vehicle.health.body) + SetVehiclePetrolTankHealth(tempVeh, vehicle.health.tank) + TaskWarpPedIntoVehicle(GetPlayerPed(-1), tempVeh, -1) + -- Clean memory + vehicleProps, tempVeh = nil +end + +-- Remove the local vehicles + +function RemoveVehicles(vehicles) + DebugLog("Start delete vehicles") + DeletingEntities = true + for i = 1, #vehicles, 1 do + local tmpLoc = { + ["x"] = vehicles[i].vehicle.location.x, + ["y"] = vehicles[i].vehicle.location.y, + ["z"] = vehicles[i].vehicle.location.z, + ["h"] = 0.0, + } + local veh, distance = ESX.Game.GetClosestVehicle(tmpLoc) + if NetworkGetEntityIsLocal(veh) and distance < 1 then + local driver = GetPedInVehicleSeat(veh, -1) + if not DoesEntityExist(driver) or not IsPedAPlayer(driver) then + local tmpModel = GetEntityModel(veh) + SetModelAsNoLongerNeeded(tmpModel) + DeleteEntity(veh) + Citizen.Wait(300) + end + end + -- Clean memory + tmpLoc, veh, distance, driver, tmpModel = nil + end + LocalVehicles = {} + DeletingEntities = false + DebugLog("Finished delete vehicles") +end + +function CheckVehicleImpound() + for i = 1, #LocalVehicles do + if type(LocalVehicles[i]) ~= "nil" and type(LocalVehicles[i].entity) ~= "nil" then + if not DoesEntityExist(LocalVehicles[i].entity) then + DebugLog("Vehicle" .. tostring(LocalVehicles[i].entity) .. " has been impound, remove from database") + ImpoundVehicle(LocalVehicles[i].entity) + LocalVehicles[i] = nil + end + end + end +end + +function UpdateVehicleStatus() + for i = 1, #LocalVehicles do + if type(LocalVehicles[i]) ~= "nil" and type(LocalVehicles[i].entity) ~= "nil" then + if DoesEntityExist(LocalVehicles[i].entity) and type(LocalVehicles[i].onground) == "nil" then + if GetDistanceBetweenCoords(GetEntityCoords(LocalVehicles[i].entity), GetEntityCoords(GetPlayerPed(-1))) < 50.0 then + SetEntityCoords(LocalVehicles[i].entity, LocalVehicles[i].location.x, LocalVehicles[i].location.y, LocalVehicles[i].location.z) + SetVehicleOnGroundProperly(LocalVehicles[i].entity) + LocalVehicles[i].onground = true + end + end + end + end +end + +-- Delete single vehicle + +function DeleteLocalVehicle(vehicle) + for i = 1, #LocalVehicles do + if type(vehicle.plate) ~= "nil" and type(LocalVehicles[i]) ~= "nil" and type(LocalVehicles[i].plate) ~= "nil" then + if vehicle.plate == LocalVehicles[i].plate then + NetworkRequestControlOfEntity(LocalVehicles[i].entity) + local tmpModel = GetEntityModel(LocalVehicles[i].entity) + SetModelAsNoLongerNeeded(tmpModel) + DeleteEntity(LocalVehicles[i].entity) + LocalVehicles[i] = nil + tmpModel = nil + end + end + end +end + +-- Delete the vehicle near the location + +function DeleteNearVehicle(location) + local veh, distance = ESX.Game.GetClosestVehicle(location) + if distance <= 1 then + for i = 1, #LocalVehicles do + if LocalVehicles[i] ~= nil and LocalVehicles[i].entity == veh then + LocalVehicles[i] = nil + end + end + local driver = GetPedInVehicleSeat(veh, -1) + if not DoesEntityExist(driver) or not IsPedAPlayer(driver) then + NetworkRequestControlOfEntity(veh) + local tmpModel = GetEntityModel(veh) + SetModelAsNoLongerNeeded(tmpModel) + DeleteEntity(veh) + tmpModel = nil + end + end +end + +-- Impound vehicle for esx_policejob + +function ImpoundVehicle(vehicle) + for i = 1, #LocalVehicles do + if LocalVehicles[i] ~= nil and vehicle == LocalVehicles[i].entity then + local deleteData = LocalVehicles[i] + ESX.TriggerServerCallback("esx_realparking:impoundVehicle", function(callback) + if callback.status then + DeleteEntity(deleteData.entity) + LocalVehicles[i] = nil + end + deleteData = nil + end, LocalVehicles[i]) + end + end +end + +-- Just some help text + +function DisplayHelpText(text) + SetTextComponentFormat("STRING") + AddTextComponentString(text) + DisplayHelpTextFromStringLabel(0, 0, 1, -1) +end + +-- Load car model + +function LoadModel(model) + while not HasModelLoaded(model) do + RequestModel(model) + Citizen.Wait(1) + end +end + +-- Draw 3d text on screen + +function Draw3DText(x, y, z, textInput, fontId, scaleX, scaleY) + local px, py, pz = table.unpack(GetGameplayCamCoords()) + local dist = GetDistanceBetweenCoords(px, py, pz, x, y, z, 1) + local scale = (1 / dist) * 20 + local fov = (1 / GetGameplayCamFov()) * 100 + local scale = scale * fov + SetTextScale(scaleX * scale, scaleY * scale) + SetTextFont(fontId) + SetTextProportional(1) + SetTextColour(250, 250, 250, 255) + SetTextDropshadow(1, 1, 1, 1, 255) + SetTextEdge(2, 0, 0, 0, 150) + SetTextDropShadow() + SetTextOutline() + SetTextEntry("STRING") + SetTextCentre(1) + AddTextComponentString(textInput) + SetDrawOrigin(x, y, z + 2, 0) + DrawText(0.0, 0.0) + ClearDrawOrigin() + px, py, pz, dist, scale, fov, scale = nil +end + +-- Main thread + +Citizen.CreateThread(function() + while ESX == nil do + Citizen.Wait(10) + ESX = exports["es_extended"]:getSharedObject() + end + Wait(1000) + while not ESX.IsPlayerLoaded() do + Citizen.Wait(10) + end + PlayerData = ESX.GetPlayerData() + ESX.TriggerServerCallback("esx_realparking:getPlayerIdentifier", function(callback) + PlayerIdentifier = callback + Citizen.Wait(500) + end) +end) + +-- Check distance + +Citizen.CreateThread(function() + while PlayerIdentifier == nil do + Citizen.Wait(10) + end + while true do + Wait(100) + local pl = GetEntityCoords(GetPlayerPed(-1)) + local inParking = false + local crParking = nil + for k, v in pairs(Config.ParkingLocations) do + if GetDistanceBetweenCoords(pl.x, pl.y, pl.z, v.x, v.y, v.z, true) < v.size + 20.0 then + inParking = true + crParking = k + end + end + if inParking then + if not SpawnedVehicles then + RemoveVehicles(GlobalVehicles) + while DeletingEntities do + Wait(100) + end + DebugLog("Player enter parking radius") + TriggerServerEvent("esx_realparking:refreshVehicles", crParking) + SpawnedVehicles = true + Wait(2000) + end + CheckVehicleImpound() + UpdateVehicleStatus() + else + if SpawnedVehicles then + DebugLog("Player leave parking radius") + RemoveVehicles(GlobalVehicles) + SpawnedVehicles = false + end + end + -- Clean memory + pl, inParking, crParking = nil + end +end) + +-- Update vehicle coords + +Citizen.CreateThread(function() + while true do + if #LocalVehicles ~= 0 then + UpdateVehicleStatus() + end + collectgarbage("collect") + Wait(1000) + end +end) + +-- Draw text thread + +Citizen.CreateThread(function() + while true do + Wait(0) + local fd = true + local pl = GetEntityCoords(GetPlayerPed(-1)) + if fd then + fd = false + for k, v in pairs(Config.ParkingLocations) do + if GetDistanceBetweenCoords(pl.x, pl.y, pl.z, v.enter.x, v.enter.y, v.enter.z, true) < 20 then + Draw3DText(v.enter.x, v.enter.y, v.enter.z, v.name, 0, 0.2, 0.2) + Draw3DText(v.enter.x, v.enter.y, v.enter.z - 0.5, string.format(_U("parking_fee", v.fee)), 0, 0.1, 0.1) + fd = true + end + end + for k, v in pairs(LocalVehicles) do + if GetDistanceBetweenCoords(pl.x, pl.y, pl.z, v.data.location.x, v.data.location.y, v.data.location.z, true) < 3.0 then + Draw3DText(v.data.location.x, v.data.location.y, v.data.location.z, string.format(_U("owner", v.name)), 0, 0.08, 0.08) + Draw3DText(v.data.location.x, v.data.location.y, v.data.location.z - 0.2, string.format(_U("plate", v.plate)), 0, 0.05, 0.05) + fd = true + end + end + else + Wait(100) + end + -- Clean memory + pl = nil + end +end) + +-- Creating blips + +Citizen.CreateThread(function() + for k, v in pairs(Config.ParkingLocations) do + local tempBlip = AddBlipForCoord(v.enter.x, v.enter.y, v.enter.z) + SetBlipSprite(tempBlip, 523) + SetBlipColour(tempBlip, 11) + SetBlipAsShortRange(tempBlip, true) + BeginTextCommandSetBlipName("STRING") + AddTextComponentString(v.name) + EndTextCommandSetBlipName(tempBlip) + -- Clean memory + tempBlip = nil + end +end) + +-- Logic thread + +Citizen.CreateThread(function() + while true do + Citizen.Wait(0) + local pl = GetEntityCoords(GetPlayerPed(-1)) + local inParking = false + local parkName = nil + local parkData = nil + for k, v in pairs(Config.ParkingLocations) do + if pl.x > v.x - v.size and pl.x < v.x + v.size and pl.y > v.y - v.size and pl.y < v.y + v.size and pl.z > v.z - v.height and pl.z < v.z + v.height then + inParking = true + parkName = k + parkData = v + end + end + if inParking and IsPedInAnyVehicle(GetPlayerPed(-1)) then + local storedVehicle = GetPedInStoredCar(GetPlayerPed(-1)) + if storedVehicle ~= false then + DisplayHelpText(string.format(_U("need_parking_fee", storedVehicle.fee))) + else + if parkData.notify == nil or parkData.notify then + DisplayHelpText(_U("press_to_save")) + end + end + if IsControlJustReleased(0, Config.KeyToSave) then + if storedVehicle ~= false then + DoScreenFadeOut(250) + Wait(500) + ESX.TriggerServerCallback("esx_realparking:driveCar", function(callback) + if callback.status then + DeleteVehicle(storedVehicle.entity) + DeleteVehicle(GetVehiclePedIsIn(GetPlayerPed(-1))) + storedVehicle = nil + Wait(500) + DriveVehicle(callback.vehData) + ESX.ShowNotification(callback.message) + else + ESX.ShowNotification(callback.message) + end + Wait(1000) + DoScreenFadeIn(250) + -- Clean memory + storedVehicle = nil + end, storedVehicle) + else + local veh = GetVehiclePedIsIn(GetPlayerPed(-1)) + if veh ~= 0 then + local speed = GetEntitySpeed(veh) + if speed > 0 then + ESX.ShowNotification(_U("stop_the_car")) + elseif IsThisModelACar(GetEntityModel(veh)) or IsThisModelABike(GetEntityModel(veh)) or IsThisModelABicycle(GetEntityModel(veh)) then + local vehProps = ESX.Game.GetVehicleProperties(veh) + local vehPos = GetEntityCoords(veh) + local vehHead = GetEntityHeading(veh) + local engHealth = GetVehicleEngineHealth(veh) + local bdyHealth = GetVehicleBodyHealth(veh) + local tnkHealth = GetVehiclePetrolTankHealth(veh) + LastPlate = vehProps.plate + DoScreenFadeOut(250) + Wait(500) + ESX.TriggerServerCallback("esx_realparking:saveCar", function(callback) + if callback.status then + DeleteVehicle(veh) + ESX.ShowNotification(callback.message) + else + ESX.ShowNotification(callback.message) + end + Wait(1000) + DoScreenFadeIn(250) + -- Clean memory + vehProps, vehPos, vehHead, engHealth, bdyHealth, tnkHealth = nil + end, { + location = { x = vehPos.x, y = vehPos.y, z = vehPos.z, h = vehHead }, + props = vehProps, + parking = parkName, + livery = GetVehicleLivery(veh), + health = { + engine = engHealth, + body = bdyHealth, + tank = tnkHealth, + }, + }) + else + ESX.ShowNotification(_U("only_allow_car")) + end + end + -- Clean memory + veh = nil + end + end + else + Citizen.Wait(500) + end + -- Clean memory + pl = nil + parkName = nil + parkData = nil + end +end) diff --git a/server-data/resources/[esx_addons]/esx_realparking/config.lua b/server-data/resources/[esx_addons]/esx_realparking/config.lua new file mode 100644 index 000000000..d32b5d9fa --- /dev/null +++ b/server-data/resources/[esx_addons]/esx_realparking/config.lua @@ -0,0 +1,72 @@ +--[[ ===================================================== ]] +-- +--[[ FiveM Real Parking Script by Akkariin ]] +-- +--[[ ===================================================== ]] +-- + +Config = {} + +-- If you are using ESX 1.2.0 or higher please leave this to false +Config.UsingOldESX = false + +-- locale +Config.Locale = "en" + +-- The key to save the car, default key is "E" (horn) +Config.KeyToSave = 51 + +-- Enable debug mode +Config.debug = false + +-- Parking locations +Config.ParkingLocations = { + parking1 = { + x = -327.73, -- Central location X, Y, Z of the parking + y = -934.12, -- Y + z = 31.08, -- Z + size = 55.0, -- The parking range radius (Horizontal), set to 10000.0 then you can parking anywhere + height = 10.0, -- The parking range radius (Vertical) + name = "Public Parking", -- The name of the parking (blips) + fee = 1000, -- How much parking fee per day (Real life time), set 0 or false to disable + enter = { x = -279.25, y = -890.39, z = 30.08 }, -- The entrance of the parking + maxcar = 500, -- Max vehicles can save on this parking + notify = true, -- Display the "Press E to save" notification, set to false can disable it + }, + parking2 = { + x = -340.03, + y = 285.19, + z = 84.77, + size = 15.0, + height = 10.0, + name = "Public Parking", + fee = 500, + enter = { x = -338.57, y = 267.16, z = 85.73 }, + maxcar = 10, + notify = true, + }, + parking3 = { + x = 446.98, + y = 246.07, + z = 103.86, + size = 25.0, + height = 10.0, + name = "Public Parking", + fee = 800, + enter = { x = 467.96, y = 265.07, z = 103.09 }, + maxcar = 20, + notify = true, + }, + parking4 = { + x = 374.35, + y = 279.49, + z = 103.32, + size = 20.0, + height = 10.0, + name = "Public Parking", + fee = 700, + enter = { x = 364.77, y = 298.98, z = 103.5 }, + maxcar = 15, + notify = true, + }, +} diff --git a/server-data/resources/[esx_addons]/esx_realparking/esx_realparking.sql b/server-data/resources/[esx_addons]/esx_realparking/esx_realparking.sql new file mode 100644 index 000000000..c929db72c --- /dev/null +++ b/server-data/resources/[esx_addons]/esx_realparking/esx_realparking.sql @@ -0,0 +1,11 @@ +CREATE TABLE `car_parking` ( + `id` int(10) NOT NULL AUTO_INCREMENT, + `owner` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `plate` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `data` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL, + `time` bigint(20) NOT NULL, + `parking` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic; + +INSERT INTO `items`(`name`, `label`, `limit`) VALUES ('parkingcard', 'Parking Card', -1); diff --git a/server-data/resources/[esx_addons]/esx_realparking/esx_realparking_newESX.sql b/server-data/resources/[esx_addons]/esx_realparking/esx_realparking_newESX.sql new file mode 100644 index 000000000..4607d369e --- /dev/null +++ b/server-data/resources/[esx_addons]/esx_realparking/esx_realparking_newESX.sql @@ -0,0 +1,11 @@ +CREATE TABLE `car_parking` ( + `id` int(10) NOT NULL AUTO_INCREMENT, + `owner` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `plate` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + `data` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL, + `time` bigint(20) NOT NULL, + `parking` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci ROW_FORMAT = Dynamic; + +INSERT INTO `items`(`name`, `label`, `weight`) VALUES ('parkingcard', 'Parking Card', 0); diff --git a/server-data/resources/[esx_addons]/esx_realparking/fxmanifest.lua b/server-data/resources/[esx_addons]/esx_realparking/fxmanifest.lua new file mode 100644 index 000000000..be2487fc0 --- /dev/null +++ b/server-data/resources/[esx_addons]/esx_realparking/fxmanifest.lua @@ -0,0 +1,20 @@ +fx_version("adamant") +game("gta5") +version("1.0.2") + +shared_script("@es_extended/imports.lua") + +client_scripts({ + "@es_extended/locale.lua", + "config.lua", + "locales/*.lua", + "client/main.lua", +}) + +server_scripts({ + "@mysql-async/lib/MySQL.lua", + "@es_extended/locale.lua", + "config.lua", + "locales/*.lua", + "server/main.lua", +}) diff --git a/server-data/resources/[esx_addons]/esx_realparking/locales/bg.lua b/server-data/resources/[esx_addons]/esx_realparking/locales/bg.lua new file mode 100644 index 000000000..fef27a27d --- /dev/null +++ b/server-data/resources/[esx_addons]/esx_realparking/locales/bg.lua @@ -0,0 +1,16 @@ +Locales["en"] = { + ["only_allow_car"] = "Тук може да паркирате само леки автомобили", + ["car_saved"] = "Колата Ви беше паркирана и съхранена", + ["press_to_save"] = "Натиснете ~INPUT_CONTEXT~ за да паркирате и съхраните Вашият автомобил", + ["not_your_car"] = "Тази кола не е ваша собственост, че да я съхраните", + ["need_parking_fee"] = "Press ~INPUT_CONTEXT~ to pay the parking fees for ~g~$%s~s~", + ["not_enough_money"] = "Нямате достатъчно средства", + ["parking_fee"] = "Такса паркинг: ~g~$%s/ден", + ["pay_success"] = "Вие заплатихте такса от ~g~$%s~s~ и може да продължите своето пътуване", + ["parking_full"] = "Паркингът е пълен, може да опитате другите ни локации", + ["invalid_car"] = "Това не е Вашият автомобил", + ["already_parking"] = "Съществува автомобил с Вашият регистрационен номер на този паркинг", + ["owner"] = "Собственик: ~y~%s~s~", + ["plate"] = "Регистрационен номер: ~g~%s~s~", + ["stop_the_car"] = "You should stop the car first", +} diff --git a/server-data/resources/[esx_addons]/esx_realparking/locales/de.lua b/server-data/resources/[esx_addons]/esx_realparking/locales/de.lua new file mode 100644 index 000000000..28ec7ac71 --- /dev/null +++ b/server-data/resources/[esx_addons]/esx_realparking/locales/de.lua @@ -0,0 +1,17 @@ +-- German translate by moistnutzz +Locales["de"] = { + ["only_allow_car"] = "Du kannst nur hier Parken", + ["car_saved"] = "Dein Auto wurde eingeparkt", + ["press_to_save"] = "Drücke ~INPUT_CONTEXT~ um dein Auto einzuparken", + ["not_your_car"] = "Du musst dieses Auto besitzen um es einzuparken", + ["need_parking_fee"] = "Drücke ~INPUT_CONTEXT~ um die Parkgebühren zu bezahlen ~g~$%s~s~", + ["not_enough_money"] = "Du hast nicht genügend Geld", + ["parking_fee"] = "Parkgebühren: ~g~$%s/Tag", + ["pay_success"] = "Du hast die Parkgebühr von: ~g~$%s~s~ bezahlt, du kannst dein Auto nun fahren", + ["parking_full"] = "Dieser Parkplatz ist voll, du kannst hier nicht Parken", + ["invalid_car"] = "Du hast dieses Auto hier nicht eingeparkt oder hast keine Erlaubnis, dieses Auto zu fahren", + ["already_parking"] = "Dieses Auto ist schon auf dem Parkplatz mit dem selben Nummernschild", + ["owner"] = "Besitzer: ~y~%s~s~", + ["plate"] = "Nummernschild: ~g~%s~s~", + ["stop_the_car"] = "You should stop the car first", +} diff --git a/server-data/resources/[esx_addons]/esx_realparking/locales/en.lua b/server-data/resources/[esx_addons]/esx_realparking/locales/en.lua new file mode 100644 index 000000000..463461b4b --- /dev/null +++ b/server-data/resources/[esx_addons]/esx_realparking/locales/en.lua @@ -0,0 +1,16 @@ +Locales["en"] = { + ["only_allow_car"] = "You only can store cars here", + ["car_saved"] = "You car has been stored", + ["press_to_save"] = "Press ~INPUT_CONTEXT~ to store your car", + ["not_your_car"] = "You have to own the car to store it", + ["need_parking_fee"] = "Press ~INPUT_CONTEXT~ to pay the parking fees for ~g~$%s~s~", + ["not_enough_money"] = "You don't have enough money", + ["parking_fee"] = "Parking fees: ~g~$%s/day", + ["pay_success"] = "You paid parking fee for ~g~$%s~s~ and you can drive the car now", + ["parking_full"] = "This parking is full, you can't parking here", + ["invalid_car"] = "You didn't store the car here or you don't have permission to drive this car", + ["already_parking"] = "This car parking has already stored a car with same plate", + ["owner"] = "Owner: ~y~%s~s~", + ["plate"] = "Plate: ~g~%s~s~", + ["stop_the_car"] = "You should stop the car first", +} diff --git a/server-data/resources/[esx_addons]/esx_realparking/locales/es.lua b/server-data/resources/[esx_addons]/esx_realparking/locales/es.lua new file mode 100644 index 000000000..0c36a7926 --- /dev/null +++ b/server-data/resources/[esx_addons]/esx_realparking/locales/es.lua @@ -0,0 +1,16 @@ +Locales["es"] = { + ["only_allow_car"] = "Solo puedes guardar coches aquí", + ["car_saved"] = "Tu coche ha sido guardado", + ["press_to_save"] = "Presiona ~INPUT_CONTEXT~ para guardar tu coche", + ["not_your_car"] = "Tiene que ser tu coche para poder guardarlo", + ["need_parking_fee"] = "Presiona ~INPUT_CONTEXT~ para pagar las tasas de aparcamiento por ~g~$%s~s~", + ["not_enough_money"] = "No tienes suficiente dinero", + ["parking_fee"] = "Tasas de Apartamiento: ~g~$%s/día", + ["pay_success"] = "Has pagado las tasas de aparcamiento por ~g~$%s~s~ y ya puede recoger tu coche", + ["parking_full"] = "El aparcamiento está completo,no puedes aparcar aquí", + ["invalid_car"] = "No has guardado el vehículo aquí o no tienes autorización para conducir este coche", + ["already_parking"] = "Este aparcamiento ya ha guardado un coche con la misma matrícula", + ["owner"] = "Dueño: ~y~%s~s~", + ["plate"] = "Matrícula: ~g~%s~s~", + ["stop_the_car"] = "You should stop the car first", +} diff --git a/server-data/resources/[esx_addons]/esx_realparking/locales/fr.lua b/server-data/resources/[esx_addons]/esx_realparking/locales/fr.lua new file mode 100644 index 000000000..0809ebc98 --- /dev/null +++ b/server-data/resources/[esx_addons]/esx_realparking/locales/fr.lua @@ -0,0 +1,16 @@ +Locales["fr"] = { + ["only_allow_car"] = "Vous ne pouvez stocker que des voitures ici", + ["car_saved"] = "Votre voiture est garé", + ["press_to_save"] = "Appuyez sur ~INPUT_CONTEXT~ pour garer votre véhicule", + ["not_your_car"] = "Vous devez posséder la voiture pour la garer", + ["need_parking_fee"] = "Appuyez sur ~INPUT_CONTEXT~ pour payer les frais de stationnement pour ~g~$%s~s~", + ["not_enough_money"] = "Vous n'avez pas assez d'argent", + ["parking_fee"] = "Frais de stationnement: ~g~$%s/jour", + ["pay_success"] = "Vous avez payé des frais de stationnement pour ~g~$%s~s~ et vous pouvez conduire la voiture maintenant", + ["parking_full"] = "Ce parking est plein, vous ne pouvez pas vous garer ici", + ["invalid_car"] = "Vous n'avez pas entreposé la voiture ici ou vous n'avez pas l'autorisation de conduire cette voiture", + ["already_parking"] = "Ce parking a déjà stocké une voiture avec la même plaque", + ["owner"] = "Propriétaire: ~y~%s~s~", + ["plate"] = "Plaque d'immatriculation: ~g~%s~s~", + ["stop_the_car"] = "You should stop the car first", +} diff --git a/server-data/resources/[esx_addons]/esx_realparking/locales/zh.lua b/server-data/resources/[esx_addons]/esx_realparking/locales/zh.lua new file mode 100644 index 000000000..8d8b3d04c --- /dev/null +++ b/server-data/resources/[esx_addons]/esx_realparking/locales/zh.lua @@ -0,0 +1,16 @@ +Locales["zh"] = { + ["only_allow_car"] = "你只能在這裡停放汽車", + ["car_saved"] = "你的車輛已經保存", + ["press_to_save"] = "按下 ~INPUT_CONTEXT~ 來保存你的車輛", + ["not_your_car"] = "你必須擁有這輛車的所有權才能保存", + ["need_parking_fee"] = "按下 ~INPUT_CONTEXT~ 來繳納 ~g~¥%s元~s~ 停車費後即可離開", + ["not_enough_money"] = "你沒有足夠的錢", + ["parking_fee"] = "停車費用:~g~¥%s元/天", + ["pay_success"] = "你已支付了停車費 ~g~¥%s元~s~,現在可以將車開走了", + ["parking_full"] = "這個停車場已經不能再停放更多的車了", + ["invalid_car"] = "你沒有在這個停車場存放這輛車", + ["already_parking"] = "這個停車場已經停著一輛相同車牌的車了", + ["owner"] = "車主:~y~%s~s~", + ["plate"] = "車牌:~g~%s~s~", + ["stop_the_car"] = "請先把車停下來才能保存", +} diff --git a/server-data/resources/[esx_addons]/esx_realparking/server/main.lua b/server-data/resources/[esx_addons]/esx_realparking/server/main.lua new file mode 100644 index 000000000..2e3af817e --- /dev/null +++ b/server-data/resources/[esx_addons]/esx_realparking/server/main.lua @@ -0,0 +1,280 @@ +ESX = exports["es_extended"]:getSharedObject() + +-- When the client request to refresh the vehicles + +RegisterServerEvent("esx_realparking:refreshVehicles") +AddEventHandler("esx_realparking:refreshVehicles", function(parkingName) + local xPlayer = ESX.GetPlayerFromId(source) + RefreshVehicles(xPlayer, source, parkingName) +end) + +-- Save the car to database + +ESX.RegisterServerCallback("esx_realparking:saveCar", function(source, cb, vehicleData) + local xPlayer = ESX.GetPlayerFromId(source) + local plate = vehicleData.props.plate + local isFound = false + FindPlayerVehicles(xPlayer.identifier, function(vehicles) + for k, v in pairs(vehicles) do + if type(v.plate) ~= "nil" and string.trim(plate) == string.trim(v.plate) then + isFound = true + end + end + if GetVehicleNumOfParking(vehicleData.parking) > Config.ParkingLocations[vehicleData.parking].maxcar then + cb({ + status = false, + message = _U("parking_full"), + }) + elseif isFound then + MySQL.Async.fetchAll("SELECT * FROM `car_parking` WHERE `owner` = @identifier AND `plate` = @plate", { + ["@identifier"] = xPlayer.identifier, + ["@plate"] = plate, + }, function(rs) + if type(rs) == "table" and #rs > 0 then + cb({ + status = false, + message = _U("already_parking"), + }) + else + MySQL.Async.execute("INSERT INTO `car_parking` (`owner`, `plate`, `data`, `time`, `parking`) VALUES (@owner, @plate, @data, @time, @parking)", { + ["@owner"] = xPlayer.identifier, + ["@plate"] = plate, + ["@data"] = json.encode(vehicleData), + ["@time"] = os.time(), + ["@parking"] = vehicleData.parking, + }) + MySQL.Async.execute("UPDATE `owned_vehicles` SET `stored` = 2 WHERE `plate` = @plate AND `owner` = @identifier", { + ["@plate"] = plate, + ["@identifier"] = xPlayer.identifier, + }) + MySQL.Async.execute("UPDATE `owned_vehicles` SET `vehicle` = @vehicle WHERE `owner` = @owner AND `plate` = @plate", { + ["@owner"] = xPlayer.identifier, + ["@vehicle"] = json.encode(vehicleData.props), + ["@plate"] = plate, + }) + MySQL.Async.execute("UPDATE `owned_vehicles` SET `parking` = @parking WHERE `plate` = @plate AND `owner` = @identifier", { + ["@plate"] = plate, + ["@identifier"] = xPlayer.identifier, + ["@parking"] = "parking", + }) + cb({ + status = true, + message = _U("car_saved"), + }) + Wait(100) + TriggerClientEvent("esx_realparking:addVehicle", -1, { vehicle = vehicleData, plate = plate, fee = 0.0, owner = xPlayer.identifier, name = xPlayer.getName() }, xPlayer.source) + end + end) + else + cb({ + status = false, + message = _U("not_your_car"), + }) + end + end) +end) + +-- When player request to drive the car + +ESX.RegisterServerCallback("esx_realparking:driveCar", function(source, cb, vehicleData) + local xPlayer = ESX.GetPlayerFromId(source) + local plate = vehicleData.plate + local isFound = false + FindPlayerVehicles2(xPlayer.identifier, function(vehicles) + for k, v in pairs(vehicles) do + if type(v.plate) ~= "nil" and string.trim(plate) == string.trim(v.plate) then + isFound = true + end + end + if isFound then + MySQL.Async.fetchAll("SELECT * FROM `car_parking` WHERE `owner` = @identifier AND `plate` = @plate", { + ["@identifier"] = xPlayer.identifier, + ["@plate"] = plate, + }, function(rs) + if type(rs) == "table" and #rs > 0 and rs[1] ~= nil then + local fee = math.floor(((os.time() - rs[1].time) / 86400) * Config.ParkingLocations[rs[1].parking].fee) + local playerMoney = xPlayer.getMoney() + local parkingCard = xPlayer.getInventoryItem("parkingcard").count + if parkingCard > 0 then + fee = 0 + end + if playerMoney >= fee then + xPlayer.removeMoney(fee) + MySQL.Async.execute("DELETE FROM `car_parking` WHERE `plate` = @plate AND `owner` = @identifier", { + ["@plate"] = plate, + ["@identifier"] = xPlayer.identifier, + }) + MySQL.Async.execute("UPDATE `owned_vehicles` SET `stored` = 0 WHERE `plate` = @plate", { + ["@plate"] = plate, + }) + MySQL.Async.execute("UPDATE `owned_vehicles` SET `owner` = @identifier WHERE `plate` = @plate", { + ["@plate"] = plate, + ["@identifier"] = xPlayer.identifier, + }) + cb({ + status = true, + message = string.format(_U("pay_success", fee)), + vehData = json.decode(rs[1].data), + }) + TriggerClientEvent("esx_realparking:deleteVehicle", -1, { + plate = plate, + }) + else + cb({ + status = false, + message = _U("not_enough_money"), + }) + end + else + cb({ + status = false, + message = _U("invalid_car"), + }) + end + end) + else + cb({ + status = false, + message = _U("not_your_car"), + }) + end + end) +end) + +-- When the police impound the car, support for esx_policejob + +ESX.RegisterServerCallback("esx_realparking:impoundVehicle", function(source, cb, vehicleData) + local xPlayer = ESX.GetPlayerFromId(source) + local plate = vehicleData.plate + MySQL.Async.fetchAll("SELECT * FROM `car_parking` WHERE `plate` = @plate", { + ["@plate"] = plate, + }, function(rs) + if type(rs) == "table" and #rs > 0 and rs[1] ~= nil then + print("Police impound the vehicle: ", vehicleData.plate, rs[1].owner) + MySQL.Async.execute("DELETE FROM `car_parking` WHERE `plate` = @plate AND `owner` = @identifier", { + ["@plate"] = plate, + ["@identifier"] = rs[1].owner, + }) + MySQL.Async.execute("UPDATE `owned_vehicles` SET `stored` = 0 WHERE `plate` = @plate", { + ["@plate"] = plate, + }) + MySQL.Async.execute("UPDATE `owned_vehicles` SET `owner` = @identifier WHERE `plate` = @plate", { + ["@plate"] = plate, + ["@identifier"] = rs[1].owner, + }) + cb({ + status = true, + }) + TriggerClientEvent("esx_realparking:deleteVehicle", -1, { + plate = plate, + }) + else + cb({ + status = false, + message = _U("invalid_car"), + }) + end + end) +end) + +-- Send the identifier to client + +ESX.RegisterServerCallback("esx_realparking:getPlayerIdentifier", function(source, cb) + local xPlayer = ESX.GetPlayerFromId(source) + local playerId = xPlayer.identifier + if type(playerId) ~= "nil" then + cb(playerId) + else + print("[RealParking][ERROR] Failed to get the player identifier!") + end +end) + +-- Refresh client local vehicles entity + +function RefreshVehicles(xPlayer, src, parkingName) + if src == nil then + src = -1 + end + local vehicles = {} + local nameList = {} + if Config.UsingOldESX then + local nrs = MySQL.Sync.fetchAll("SELECT `identifier`, `name` FROM `users`") + if type(nrs) == "table" then + for k, v in pairs(nrs) do + nameList[v.identifier] = v.name + end + end + else + local nrs = MySQL.Sync.fetchAll("SELECT `identifier`, `firstname`, `lastname` FROM `users`") + if type(nrs) == "table" then + for k, v in pairs(nrs) do + nameList[v.identifier] = tostring(v.firstname) .. " " .. tostring(v.lastname) + end + end + end + local querySQL = "SELECT * FROM `car_parking`" + local queryArg = {} + if parkingName ~= nil then + querySQL = "SELECT * FROM `car_parking` WHERE `parking` = @parkingName" + queryArg = { + ["@parkingName"] = parkingName, + } + end + MySQL.Async.fetchAll(querySQL, queryArg, function(rs) + for k, v in pairs(rs) do + local vehicle = json.decode(v.data) + local plate = v.plate + local fee = math.floor(((os.time() - v.time) / 86400) * Config.ParkingLocations[v.parking].fee) + if fee < 0 then + fee = 0 + end + table.insert(vehicles, { vehicle = vehicle, plate = plate, fee = fee, owner = v.owner, name = nameList[v.owner] }) + end + TriggerClientEvent("esx_realparking:refreshVehicles", src, vehicles) + end) +end + +-- Get the number of the vehicles + +function GetVehicleNumOfParking(name) + local rs = MySQL.Sync.fetchAll("SELECT `id` FROM `car_parking` WHERE `parking` = @parking", { ["@parking"] = name }) + if type(rs) == "table" then + return #rs + else + return 0 + end +end + +-- Get all vehicles the player owned + +function FindPlayerVehicles(id, cb) + local vehicles = {} + MySQL.Async.fetchAll("SELECT * FROM `owned_vehicles` WHERE `owner` = @identifier", { ["@identifier"] = id }, function(rs) + for k, v in pairs(rs) do + local vehicle = json.decode(v.vehicle) + local plate = v.plate + table.insert(vehicles, { vehicle = vehicle, plate = plate }) + end + cb(vehicles) + end) +end +function FindPlayerVehicles2(id, cb) + local vehicles = {} + MySQL.Async.fetchAll("SELECT * FROM `car_parking` WHERE `owner` = @identifier", { ["@identifier"] = id }, function(rs) + for k, v in pairs(rs) do + local vehicle = json.decode(v.vehicle) + local plate = v.plate + table.insert(vehicles, { vehicle = vehicle, plate = plate }) + end + cb(vehicles) + end) +end +-- Clear the text + +string.trim = function(text) + if text ~= nil then + return text:match("^%s*(.-)%s*$") + else + return nil + end +end diff --git a/server-data/resources/[ox]/ox_inventory/data/items.lua b/server-data/resources/[ox]/ox_inventory/data/items.lua index 2a29fe2b9..da1fc6a4b 100644 --- a/server-data/resources/[ox]/ox_inventory/data/items.lua +++ b/server-data/resources/[ox]/ox_inventory/data/items.lua @@ -14,6 +14,10 @@ return { label = "Dirty Money", }, + ["parkingcard"] = { + label = "Tessera parcheggio", + }, + ["burger"] = { label = "Burger", weight = 220, @@ -34,7 +38,7 @@ return { anim = "eating", prop = "burger", usetime = 2500, - notification = "Stai mangiano una fetta di panettone", + notification = "Stai mangiando una fetta di panettone", }, }, From 5ec6ed9babd215305eb04d7c51d604b299d0fe50 Mon Sep 17 00:00:00 2001 From: bitpredator <67551273+bitpredator@users.noreply.github.com> Date: Sat, 7 Dec 2024 09:59:13 +0100 Subject: [PATCH 19/22] feat+chore: the Italian language was created and configured as default In this new request, the Italian language translation was created and configured as default --- .../[esx_addons]/esx_realparking/config.lua | 9 +-------- .../[esx_addons]/esx_realparking/locales/it.lua | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 8 deletions(-) create mode 100644 server-data/resources/[esx_addons]/esx_realparking/locales/it.lua diff --git a/server-data/resources/[esx_addons]/esx_realparking/config.lua b/server-data/resources/[esx_addons]/esx_realparking/config.lua index d32b5d9fa..2e7f20945 100644 --- a/server-data/resources/[esx_addons]/esx_realparking/config.lua +++ b/server-data/resources/[esx_addons]/esx_realparking/config.lua @@ -1,17 +1,10 @@ ---[[ ===================================================== ]] --- ---[[ FiveM Real Parking Script by Akkariin ]] --- ---[[ ===================================================== ]] --- - Config = {} -- If you are using ESX 1.2.0 or higher please leave this to false Config.UsingOldESX = false -- locale -Config.Locale = "en" +Config.Locale = "it" -- The key to save the car, default key is "E" (horn) Config.KeyToSave = 51 diff --git a/server-data/resources/[esx_addons]/esx_realparking/locales/it.lua b/server-data/resources/[esx_addons]/esx_realparking/locales/it.lua new file mode 100644 index 000000000..d2473f1f4 --- /dev/null +++ b/server-data/resources/[esx_addons]/esx_realparking/locales/it.lua @@ -0,0 +1,16 @@ +Locales["it"] = { + ["only_allow_car"] = "Puoi depositare solo le auto qui", + ["car_saved"] = "La tua auto è stata depositata", + ["press_to_save"] = "Premi ~INPUT_CONTEXT~ per posteggiare la tua auto", + ["not_your_car"] = "Devi possedere l'auto per conservarla", + ["need_parking_fee"] = "Premi ~INPUT_CONTEXT~ per pagare le tariffe del parcheggio ~g~$%s~s~", + ["not_enough_money"] = "Non hai abbastanza soldi", + ["parking_fee"] = "Tasse di parcheggio: ~g~$%s/day", + ["pay_success"] = "Hai pagato il parcheggio ~g~$%s~s~ ora puoi guidare", + ["parking_full"] = "Questo parcheggio è pieno, non puoi parcheggiare qui", + ["invalid_car"] = "Non hai lasciato l'auto qui o non hai il permesso di guidarla", + ["already_parking"] = "In questo parcheggio è già custodita un'auto con la stessa targa", + ["owner"] = "Proprietario: ~y~%s~s~", + ["plate"] = "Targa: ~g~%s~s~", + ["stop_the_car"] = "Dovresti prima fermare la macchina", +} From e224cb98fe06728fff17099b86bd382192ef2d3a Mon Sep 17 00:00:00 2001 From: bitpredator <67551273+bitpredator@users.noreply.github.com> Date: Sat, 7 Dec 2024 10:51:52 +0100 Subject: [PATCH 20/22] refactor: hud reconstruction In this PR a lot of reconstruction work has been done for the hud, now it will be shown through a small symbol and percentages, the position has been moved to the left under the minimap (minimap which will only be activated when you are on a vehicle) . --- .../[bpt_addons]/bpt_hud/client/status.lua | 35 - .../[bpt_addons]/bpt_hud/fxmanifest.lua | 3 - .../[bpt_addons]/bpt_hud/html/burger.png | Bin 2172 -> 0 bytes .../[bpt_addons]/bpt_hud/html/heart.png | Bin 1622 -> 0 bytes .../[bpt_addons]/bpt_hud/html/oxygen.png | Bin 1201 -> 0 bytes .../bpt_hud/{html-map => html}/script.js | 0 .../[bpt_addons]/bpt_hud/html/sheild.png | Bin 2616 -> 0 bytes .../[bpt_addons]/bpt_hud/html/style.css | 401 ----------- .../[bpt_addons]/bpt_hud/html/style.js | 40 -- .../[bpt_addons]/bpt_hud/html/tension.png | Bin 3563 -> 0 bytes .../[bpt_addons]/bpt_hud/html/ui.html | 55 -- .../[bpt_addons]/bpt_hud/html/water.png | Bin 2438 -> 0 bytes .../resources/[esx_addons]/FLX_hud/LICENSE | 674 ++++++++++++++++++ .../resources/[esx_addons]/FLX_hud/README.md | 1 + .../resources/[esx_addons]/FLX_hud/client.lua | 136 ++++ .../[esx_addons]/FLX_hud/fxmanifest.lua | 12 + 16 files changed, 823 insertions(+), 534 deletions(-) delete mode 100644 server-data/resources/[bpt_addons]/bpt_hud/client/status.lua delete mode 100644 server-data/resources/[bpt_addons]/bpt_hud/html/burger.png delete mode 100644 server-data/resources/[bpt_addons]/bpt_hud/html/heart.png delete mode 100644 server-data/resources/[bpt_addons]/bpt_hud/html/oxygen.png rename server-data/resources/[bpt_addons]/bpt_hud/{html-map => html}/script.js (100%) delete mode 100644 server-data/resources/[bpt_addons]/bpt_hud/html/sheild.png delete mode 100644 server-data/resources/[bpt_addons]/bpt_hud/html/style.css delete mode 100644 server-data/resources/[bpt_addons]/bpt_hud/html/style.js delete mode 100644 server-data/resources/[bpt_addons]/bpt_hud/html/tension.png delete mode 100644 server-data/resources/[bpt_addons]/bpt_hud/html/ui.html delete mode 100644 server-data/resources/[bpt_addons]/bpt_hud/html/water.png create mode 100644 server-data/resources/[esx_addons]/FLX_hud/LICENSE create mode 100644 server-data/resources/[esx_addons]/FLX_hud/README.md create mode 100644 server-data/resources/[esx_addons]/FLX_hud/client.lua create mode 100644 server-data/resources/[esx_addons]/FLX_hud/fxmanifest.lua diff --git a/server-data/resources/[bpt_addons]/bpt_hud/client/status.lua b/server-data/resources/[bpt_addons]/bpt_hud/client/status.lua deleted file mode 100644 index 666d7a0fe..000000000 --- a/server-data/resources/[bpt_addons]/bpt_hud/client/status.lua +++ /dev/null @@ -1,35 +0,0 @@ -local health = 0 -local armor = 0 -local food = 0 -local thirst = 0 -local posi = "bottom" - -ESX = exports["es_extended"]:getSharedObject() - -AddEventHandler("playerSpawned", function() -- Enable hud only after player spawn - CreateThread(function() - while true do - Wait(0) - if IsEntityDead(PlayerPedId()) then - health = 0 - else - health = math.ceil(GetEntityHealth(PlayerPedId()) - 100) - end - armor = math.ceil(GetPedArmour(PlayerPedId())) - TriggerEvent("bpt_status:getStatus", "hunger", function(status) - food = status.getPercent() - end) - TriggerEvent("bpt_status:getStatus", "thirst", function(status) - thirst = status.getPercent() - end) - SendNUIMessage({ - posi = posi, - show = IsPauseMenuActive(), -- Disable hud if settings menu is active - health = health, - armor = armor, - food = food, - thirst = thirst, - }) - end - end) -end) diff --git a/server-data/resources/[bpt_addons]/bpt_hud/fxmanifest.lua b/server-data/resources/[bpt_addons]/bpt_hud/fxmanifest.lua index 13839ba0d..4272eb0ec 100644 --- a/server-data/resources/[bpt_addons]/bpt_hud/fxmanifest.lua +++ b/server-data/resources/[bpt_addons]/bpt_hud/fxmanifest.lua @@ -10,10 +10,7 @@ ui_page("html/ui.html") shared_script("@es_extended/imports.lua") files({ - "html/*.css", "html/*.js", - "html/*.png", - "html/ui.html", }) client_scripts({ diff --git a/server-data/resources/[bpt_addons]/bpt_hud/html/burger.png b/server-data/resources/[bpt_addons]/bpt_hud/html/burger.png deleted file mode 100644 index 1be3a3748a95e17dcc3d3da3f3e6e66b9fe85f16..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2172 zcmZvedpHw{8^`C8S+>z|npre$x!*6j9G5X;!)zKM$3gBXw-k|>VIi$CM~EdN6;k=- z5(`I&LK;R)xs}KzMB?W>&+qr&dEV#yexB!j|9rpC`@Yx6Bs<7K#6bW603q1pT=wbz zkHp3HbHqnBbsu7W4t6-e-ao;4QIP`xi0cw?SgJc{@m5@zOaMH+H3XX-A)0#{2Svg9 z#l;!43?&MonJA@Wub~?%gBqY)8VD<_9Mp|sP^49814r$3N`(&5=r(s(E*@p{&76M6mduV3)hS+D;qsX5Fhj~ZQ9mmx0Ey0J=^h!?M*oE%?9*llb^Lalev5D zyQF|Undah+ai(mS+_%(k$w_R@+cFD{{E4rA!6?J6QBnOmDycnHCk}r+BovtP$o#nA z1}FzLY=H53z+Py(nHK?2dtga(Q|c05UN|4Gl>+gVpDrMdkQL)=u4Gj^<)pj zpcT%Drm(Q4-;x0wd{=G~uo@#_OHwWQ(A=j`UWRzsKZj8EZdaYe&LcbZK7%F{1KG|; zCv1mK&^pR1pInyuGo$UIVyKZ=VlQFS+7%Nn&^L*b?E-dn=l-%npBcN~C7&qK(}>HFp}Ucn|d(VSO5dbziCaB$j0;GeQ>pQE zPB-$41h+&Z||?vKAR0VNtB)}89yL0%|g9h@Hil1D)NA^b76 zG5?*|1Yir3)%G5gt!<_Mc3J~b^zKjwN2Uk!xcpm&hBcjpa<|Az{*%i)gvb$;_UG7$ z8CM)mfYxse!Vj$5kRHqjX79M$P7R0>x-5&U>pCMd4(LFqWyexo=AF016GW9!j_L4N z7=r4BwNEuUHzmvTcM&Bn3F5LYKtR$XeLD%UfW%Zsez(BAO9Jd4qgNr{48%y?`vvZ$ zH*tWBOKpHJ(o+t`z{D3{`}N<$*N`1|{AgM<_Of}A(9Hy%oc)XfTU!O-xtcC|%>Ir+6c}>FCa7T1zW$dJ)=rLcFX`7+#%%l_~Gz~INFac_% z@JnsH<~>Iw|2|4sw2Euc#rK|d^f>)AOX5n_;gQ7j40V>0yjq#AJhj`nHY4C(4kISF zS`DV8$xtXye+ygM$Lc44FZ+gYY$noJe_2>dOu zW2^37LW6rrM#4?r?ZJJU6;7#CmPT?wzL(paI^n>cm?EM!YqcriajfMfhUS)MoUk-i z*Cc85L_4K+y{ZXmuFmB-EF~%U#Q@qRxIAA(_Bg7ka-`9Kr#>L47J)Buv9<3ByRSp% z-l&-6t|G@yxxGJA1QkSQ{D^<)7ES09dSI8h`c&E<5@JXMKG#Hk+$zEUM5D&wwXVS@ zo+=cI{71UMJa<9rQXeHwQqG}1?Djnh*}~!V{1NVCVgw+pkIA0CUM?it;9yGOZ>M$c zFJKe0?Ht#Yj)1OSD2<58xSIeSReh`5;NU+fW-6be76>j%ghwebjwicc3cmCq2`(Ln< zW(^ci0}WB#@x_t0PWJeCmf{c{{VlFj)(|DPNJu10vzq7RP7TrhpDJ%*o$ObOgS-Yx zn6SVKD|VJh(9Y{vSlgx=i+OC9z|ZbW{7a5;NvBwNKk9Cnkg_FRt2~!lJhbjN!E8f4f)mvW8Ez6t$YY`QAqAE;7rJR w+L)75&B(p|-mfCFDOLY^m;YZ(E{YadUbc{9SGZ=*?!OZNf-MPGZS6z*F9UPmUH||9 diff --git a/server-data/resources/[bpt_addons]/bpt_hud/html/heart.png b/server-data/resources/[bpt_addons]/bpt_hud/html/heart.png deleted file mode 100644 index 840e4a93e3250b38bceaa930cc3d7072ba1030b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1622 zcmV-c2C4apP)BW#;Q)ZDl-XpELWMvoCYz z{C_V4>#V)@x7Ign?{(X2f#W!i<2a7vIF92uj^j9v)BISLiAx#<+y>kN+yD#%E(0zG zx`1}zD6j|E4*U$PHnU%|)YVPjNMHsq78o7r8v+a>luZY4B+|DF*c$2Eork_ukTg!x zE0XrAetwg*Skg@y=o=$xiKO2f(D#?5mnDr$TVET`6e1ThiJL z^sSRLPg15&o5V<(AZcR;87*avq$zdio2qP#DCJv8A+u-cl(b0F zfCTymNO~!{CNIt?JZgK?mnxD*NZOQRMoZZ$>G~@AhD+L3puQg^4bRJrNs|69FtcYl zBx!n#zNwN96s%2>j!2r8wHb3HbrhV@?i`nNdx^di1#9X~hosrHlyTygfuza6*TBG9 zZjb}VfjPiw;44BOr#EpNm~Ce3lSEErh@=s~Mnb2qH*z@oJG4+0l{o}VF|$8#U3+2%?S zD%KE%p@27VGt#6#@LIJ9)gD>%fXS)KaR8=Cnp^3|YKAltUr;2eZFfwHoWGo-go z@bqN{l?Egkes^*t&X8ldt7u)0mIk;X#Zp$*afY<#AjAnIGUPxGLY%+TR z4eb*sX&~?=aBCy!^dZ&*bIk0&dJ|}P<3>rB04woL1~vn;%-%$8?$gUt6UkLKk+ruhdX_W}#dY*Usu$$arbG%r7iu!U>0vKE+_wHc7pYLci0 zb|B^$ZDOVH@ zfq7EW$zsd?JWfk`USTA=HBmTiVRx}UNRoC*npkvs^a>9XsIzQ9C}BZr)F zT>g06@aS;))8(?^;G&iHn>@Pu&aa(WwkmY4x&1rIVzuXIYHQ!$tv+wN{Ql0*YLh>n z=DWdYoxuL4L3RUk*#SNt%L!-GmLK~2!RikS^S)1I;&N@e&gQ>zlh_Sbah{WDKY7H~ zech#U(Hb`OL(zKgWL`d99LSe&&g=(=U(56u8@}}3S1}*huUkD#w~+kvWLYii-C7Bm z15;!R1pLGlXNWG$SA2Pg>vy7|#Diz0t&gvKHB2)(6uu#X<#D*!tm6#F6Qp9aWtOk& zFU;5U=@vK@u++feM)je62NJb%mR!%du}03rxs}yVJMoX%uWyGMrtaB#Xv+@Y<8~Di zUW*;0V$~-9|7IE5aw`YEs>v#C^m3*n4QV&^AIn2A(hn*)W`7gbo8vv{3D?^l33EQ=zP1vd zexP3QjreWJ_t*C4efcURqcBDCgUBDo|81vZA|ei%T-+75)Jbcj6;J$`9lI0N=P=(t z$o|15w6Oi!2Ii&K0h>jIruj`hvq1Nc*YDO>6=!$Oa`k0(V%S``i@oe%?*`d_o&9rm z@UK7g;p5jS*EE(hvuxyyYj~*Gzf9xKlXrPH0{nbSEydK%wJUPWnj|wwUi=mO@KVaD3LKs#&|_HNPD2 z3S&Fm@06dlQS_C@XZa>y*)@hXncFXfaX0&HnVLRbdW%rTgn*CHM?7a`wH+*(x#8dC zKgK@%^%@2oUF(>gw<#^+KIXtaS*}HB!jBV56FyAbXes>u>iH|&lT?1XNsC_8ZZVkl z@W3;lJK@%s%MZWkkmKNc)5OKGf1>O4g#sQo>=#KlHt2?AE9Zu|@Uk!;lG|psidlK` z_JSQ8Zmw4xB7WN3;0YD@ws}QV68j~w(yz(t{Ih@G*c@sjY%MU8ebb!h7gw8>DO@za z7$fj=nP$Df>RwroHKnJ|y>>XVs8E5=6pQsR1~8~k@i_iuPe{|sb0V(w~AvU zA1DVp@LEhba%INZwCTqG1y?j}_M8;cqp9|7*}Zd*zB`HDarU_4@JGgp$*VxYD|5lo z#)KvznT{Z-j6#JO3s~3}O<{|Y;ZV~!*r>6zQH;Hb6QWG@TYjW&Mg(){hdK8*#jWgs j*aXe%HyVp9KX89LbP0l+XkKhe#7s diff --git a/server-data/resources/[bpt_addons]/bpt_hud/html-map/script.js b/server-data/resources/[bpt_addons]/bpt_hud/html/script.js similarity index 100% rename from server-data/resources/[bpt_addons]/bpt_hud/html-map/script.js rename to server-data/resources/[bpt_addons]/bpt_hud/html/script.js diff --git a/server-data/resources/[bpt_addons]/bpt_hud/html/sheild.png b/server-data/resources/[bpt_addons]/bpt_hud/html/sheild.png deleted file mode 100644 index a4b71fbd99bef8f766f8df76caf6b817bbc8a2bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2616 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 { - display: inline-block; - margin-left: 5px -} - -img { - width: 70%; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - background-color: transparent -} - -.circle { - position: :absolute; - top: 50%; - left: 50%; - width: 2vw; - height: 2vw; - border: 2px solid transparent; - box-shadow: 0 0 0 2px #37AD6F; - border-radius: 50%; - overflow: hidden; - background-color: transparent -} - -.circle2 { - position: :absolute; - top: 50%; - left: 50%; - width: 2vw; - height: 2vw; - - border: 2px solid transparent; - box-shadow: 0 0 0 2px rgba(40, 40, 198, 1); - border-radius: 50%; - overflow: hidden; -} - -.circle3 { - position: :absolute; - top: 50%; - left: 50%; - width: 2vw; - height: 2vw; - /*background: #ccc;*/ - border: 2px solid transparent; - box-shadow: 0 0 0 2px #FAAA03; - border-radius: 50%; - overflow: hidden; -} - -.circle4 { - position: :absolute; - top: 50%; - left: 50%; - width: 2vw; - height: 2vw; - border: 2px solid transparent; - box-shadow: 0 0 0 2px #1EB7E6; - border-radius: 50%; - overflow: hidden; - -} - -.circle5 { - position: :absolute; - top: 50%; - left: 50%; - width: 2vw; - height: 2vw; - /*background: #ccc;*/ - border: 2px solid transparent; - box-shadow: 0 0 0 2px #677BBB; - border-radius: 50%; - overflow: hidden; -} - -.circle6 { - position: :absolute; - top: 50%; - left: 50%; - width: 2vw; - height: 2vw; - /*background: #ccc;*/ - border: 2px solid transparent; - box-shadow: 0 0 0 2px rgba(198.40, 40, 1); - border-radius: 50%; - overflow: hidden; -} - -.wave { - position: relative; - width: 100%; - height: 100%; - - background: #37AD6F; - border-radius: 20%; - /*color: rgba(225,0,0,1)*/ - - -} - -.wave2 { - position: relative; - width: 100%; - height: 100%; - background: rgba(40, 40, 198, 1); - border-radius: 20%; - /*color: rgba(225,0,0,1)*/ - -} - -.wave2:before, -.wave2:after { - content: ''; - position: absolute; - width: 200%; - height: 200%; - top: var(--top, -50%); - left: 50%; - transform: translate(-50%, -75%); - background: #000 -} - -.wave2:before { - border-radius: 75%; - animation: animate 5s linear infinite; -} - -.wave2:after { - border-radius: 40%; - background: rgba(225, 225, 225, .5); - animation: animate 5s linear infinite; -} - -.wave:before, -.wave:after { - content: ''; - position: absolute; - width: 200%; - height: 200%; - top: var(--top, -40%); - left: 50%; - transform: translate(-50%, -75%); - background: #000 -} - -.wave:before { - border-radius: 75%; - animation: animate 5s linear infinite; -} - -.wave:after { - border-radius: 40%; - background: rgba(225, 225, 225, .5); - animation: animate 5s linear infinite; -} - -.wave3 { - position: relative; - width: 100%; - height: 100%; - background: #FAAA03; - border-radius: 20%; - -} - -.wave3:before, -.wave3:after { - content: ''; - position: absolute; - width: 200%; - height: 200%; - top: var(--top, -50%); - left: 50%; - transform: translate(-50%, -75%); - background: #000 -} - -.wave3:before { - border-radius: 75%; - animation: animate 5s linear infinite; -} - -.wave3:after { - border-radius: 40%; - background: rgba(225, 225, 225, .5); - animation: animate 5s linear infinite; -} - -.wave4 { - position: relative; - width: 100%; - height: 100%; - background: #1EB7E6; - border-radius: 20%; - /*color: rgba(225,0,0,1)*/ - -} - -.wave4:before, -.wave4:after { - content: ''; - position: absolute; - width: 200%; - height: 200%; - top: var(--top, -50%); - left: 50%; - transform: translate(-50%, -75%); - background: #000 -} - -.wave4:before { - border-radius: 75%; - animation: animate 5s linear infinite; -} - -.wave4:after { - border-radius: 40%; - background: rgba(225, 225, 225, .5); - animation: animate 5s linear infinite; -} - -.wave5 { - position: relative; - width: 100%; - height: 100%; - background: #677BBB; - border-radius: 20%; - /*color: rgba(225,0,0,1)*/ - -} - -.wave5:before, -.wave5:after { - content: ''; - position: absolute; - width: 200%; - height: 200%; - top: var(--top, -50%); - left: 50%; - transform: translate(-50%, -75%); - background: #000 -} - -.wave5:before { - border-radius: 75%; - animation: animate 5s linear infinite; -} - -.wave5:after { - border-radius: 40%; - background: rgba(225, 225, 225, .5); - animation: animate 5s linear infinite; -} - -.wave6 { - position: relative; - width: 100%; - height: 100%; - background: rgba(198.40, 40, 1); - border-radius: 20%; - /*color: rgba(225,0,0,1)*/ - -} - -.wave6:before, -.wave6:after { - content: ''; - position: absolute; - width: 200%; - height: 200%; - top: var(--top, -50%); - left: 50%; - transform: translate(-50%, -75%); - background: #000 -} - -.wave6:before { - border-radius: 75%; - animation: animate 5s linear infinite; -} - -.wave6:after { - border-radius: 40%; - background: rgba(225, 225, 225, .5); - animation: animate 5s linear infinite; -} - -@keyframes animate { - 0% { - transform: translate(-50%, -75%) rotate(0deg); - } - - 100% { - transform: translate(-50%, -75%) rotate(360deg); - } -} - -.outline { - position: fixed; - bottom: 8vh; - left: 1.1vh; - /* overflow: hidden; */ - width: 29.529vh; - text-align: center; -} - -.mapborder { - width: 24.25vh; - height: 21.5vh; - border: 0.45vh solid rgb(159, 163, 171, 0.85); - border-radius: 50%; - box-shadow: 0px 0px 16px rgba(0, 0, 0, 0.5) inset; - position: relative; - right: 2.8vh; - top: 2.75vh; - display: inline-block; -} - -@media only screen and (max-width: 1366px) { - .outline { - position: fixed; - bottom: 10vh; - left: 2.6vh; - width: 29.529vh; - text-align: center; - } - - .mapborder { - width: 24.25vh; - height: 21.5vh; - border: 4px solid #9fa3ab; - border-radius: 50%; - box-shadow: 0px 0px 16px rgba(0, 0, 0, 0.5); - position: relative; - right: 2.8vh; - top: 2.4vh; - display: inline-block; - } -} - -@media only screen and (max-width: 1280px) { - .outline { - position: fixed; - bottom: 10vh; - left: 2.8vh; - width: 29.529vh; - text-align: center; - } - - .mapborder { - width: 180px; - height: 150.25px; - border: 4px solid #9fa3ab; - border-radius: 50%; - box-shadow: 0px 0px 16px rgba(0, 0, 0, 0.5); - position: relative; - right: 36.50px; - top: 39.5px; - display: inline-block; - } -} - -@media only screen and (max-width: 1768px) { - .outline { - position: fixed; - bottom: 8.5vh; - left: 1.4vh; - width: 29.529vh; - text-align: center; - } - - .mapborder { - width: 240px; - height: 210.25px; - border: 4px solid #9fa3ab; - border-radius: 50%; - box-shadow: 0px 0px 16px rgba(0, 0, 0, 0.5); - position: relative; - right: 36.50px; - top: 39.5px; - display: inline-block; - } -} \ No newline at end of file diff --git a/server-data/resources/[bpt_addons]/bpt_hud/html/style.js b/server-data/resources/[bpt_addons]/bpt_hud/html/style.js deleted file mode 100644 index a49f8cc26..000000000 --- a/server-data/resources/[bpt_addons]/bpt_hud/html/style.js +++ /dev/null @@ -1,40 +0,0 @@ -$(document).ready(function() { - window.addEventListener('message', function(event) { - const data = event.data; - if (data.show == false) { - if (data.posi == 'bottom') { - $('body').show(); - soMany = 50 - data.health; - document.querySelectorAll('.wave')[0].style.setProperty('--top', `${soMany}%`); - - armorJS = 50 - data.armor; - document.querySelectorAll('.wave2')[0].style.setProperty('--top', `${armorJS}%`); - - foodJS = 50 - data.food; - document.querySelectorAll('.wave3')[0].style.setProperty('--top', `${foodJS}%`); - - waterJS = 50 - data.thirst; - document.querySelectorAll('.wave4')[0].style.setProperty('--top', `${waterJS}%`); - } - else { - $('.main').css('top', '15vh'); - $('.main').css('right', '5vw'); - $('body').show(); - soMany = 50 - data.health; - document.querySelectorAll('.wave')[0].style.setProperty('--top', `${soMany}%`); - - armorJS = 50 - data.armor; - document.querySelectorAll('.wave2')[0].style.setProperty('--top', `${armorJS}%`); - - foodJS = 50 - data.food; - document.querySelectorAll('.wave3')[0].style.setProperty('--top', `${foodJS}%`); - - waterJS = 50 - data.thirst; - document.querySelectorAll('.wave4')[0].style.setProperty('--top', `${waterJS}%`); - } - } - else { - $('body').hide(); - } - }); -}); \ No newline at end of file diff --git a/server-data/resources/[bpt_addons]/bpt_hud/html/tension.png b/server-data/resources/[bpt_addons]/bpt_hud/html/tension.png deleted file mode 100644 index c1240d4f99ed316a9f35868588e8d9ef1652e82e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3563 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 diff --git a/server-data/resources/[bpt_addons]/bpt_hud/html/ui.html b/server-data/resources/[bpt_addons]/bpt_hud/html/ui.html deleted file mode 100644 index 2b61e6bae..000000000 --- a/server-data/resources/[bpt_addons]/bpt_hud/html/ui.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - - - - - -
- -
- - \ No newline at end of file diff --git a/server-data/resources/[bpt_addons]/bpt_hud/html/water.png b/server-data/resources/[bpt_addons]/bpt_hud/html/water.png deleted file mode 100644 index 40562cb392a43e2f428768b6334e9dbb02f8d4ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2438 zcmV;133>L3P)kHDCyTKm?5uNdW%< z0>%ewkcbguj6|bRjDIQ7sGuN$hzJrC4>K!wE$Lwj>=e6G;#6*HDEL z5Ra@hInlT#onV#VN&m&L6LP5&7%x`wE2_B#QEjBK&nwKm8gi+27+*+Hn$ccRrVNmv z;gE!=G(D3q`8uA+*T(X`RG>A-c0f0XN@v)pB#}t$qqRad-LhUGBT*KE?L6@a{5~hc zgX*w9)zu97;G=^70WS!J`OrsmrAATJQXq?54qBNUflo4 zTxU25s``foy@L5+Tny!qD65wJ zK%8|nN?OJ2I!`@}pM$A z03FQ=CIJKDR<)TnYP}Zp!|6VmyA%$As&=!WMKE~A(;*)cA3FO6eQ~x=_Fe#0RkNTp z7&PPV5aq3k?ds^Wpg&Ib38xdB235T+2aJKwA>X>wbNP?#%>`Bc#expOxEYTG8?@eY z^^ffJ1aa@BQPQ+v%&cq!hrt$&3i{_vpX|L4sw!iF{EvJN$86FMI8)$EA%vqi+#iyl zs?lP-4~xZBR!M7{?(=jwPZ8m5zBUm=rlPNf)3_m&YU~n@n2oB!sc!gxm^KuJd4&wcBb8z>-TR7Qg2fO;ogv$x3^f)t@UQMeAWh z__Ce;0B}tsiDP9LPKQ<5u{fhr45oW;5kyTf!~kvw7mI=0-URUy2I8zVw!_&iK11A~ zV5pM0ohe8;ZH=(8jLzNQqE6#EtDC!=xapSBKd2gm?+iKG2U#_i~d!ObT*|#>V2qQ*c=v z3!;;kbWThPQkb@qeMjCemHkp@W;QqRR8EkJE%6Pf%6u`mjd%%A5$MOId&(pblY;1M ze4!x!Q!SH%=v*Pdx?LK_VW^Xrbllnn;VY1n zSUCnN8YoTPC5_`S+-DMmc0p=4-CNsc+w|iT{zZYwd*~m8xTt~?)#x^CT96%znJi@f zO;9787mvLwYXVIRTB_B&;mxt?2ollq;;P!JF>yLvp1Q7N~nyQBpVGd=~bN}N>2z8e>Wv*IvbtN~TM zNl{w*i?CBheFTq&G1*!F5Zf&d5^kwoaNQu9&>e?$g^2y8#-}V1CB&iA52s+x&qF(YI9P0oJAqh6>5Ke`5b;$7>k8y4QoC6&lWzpb z!YEh^QR_Gxc4^E%tKP%e9w44RXtZ^jm9wE$W5MyhH~|jPMx`Vhjcm*(E8oLuarRcx zn(8nHo`)+l1&7&n9<_L=1utpbDbaS~k43$t69TfJsaVybBeOONq}epR&P-Z2W5S zb4b)BFag9T^RS9jiMAwSk~hlWXV?Rq;A?(~ zJVR@>tS?BW>`|z8yeEnxez9vwzY0!4IaHARYFg*?9} + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/server-data/resources/[esx_addons]/FLX_hud/README.md b/server-data/resources/[esx_addons]/FLX_hud/README.md new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/server-data/resources/[esx_addons]/FLX_hud/README.md @@ -0,0 +1 @@ + diff --git a/server-data/resources/[esx_addons]/FLX_hud/client.lua b/server-data/resources/[esx_addons]/FLX_hud/client.lua new file mode 100644 index 000000000..73fcc5219 --- /dev/null +++ b/server-data/resources/[esx_addons]/FLX_hud/client.lua @@ -0,0 +1,136 @@ +ESX = exports["es_extended"]:getSharedObject() +PlayerData = {} + +local ShowHUD = true +HUD = {} + + +RegisterNetEvent("esx:playerLoaded") +AddEventHandler("esx:playerLoaded", function(xPlayer) + ESX.PlayerLoaded = true + ESX.PlayerData = xPlayer + + Cash = ESX.PlayerData.money +end) + +RegisterNetEvent("esx:playerLoaded") +AddEventHandler("esx:playerLoaded", function(xPlayer) + ESX.PlayerLoaded = true + ESX.PlayerData = xPlayer + + Cash = ESX.PlayerData.money + + PlayerData = xPlayer + if PlayerData.job.grade_name == "boss" then + Boss = true + else + Boss = false + end +end) + +function Roundxx(num, numDecimalPlaces) + local mult = 10 ^ (numDecimalPlaces or 0) + return math.floor(num * mult + 0.5) / mult +end + +function DrawRct2(x, y, width, height, r, g, b, a) + DrawRect(x + width / 4, y + height / 1, width, height, r, g, b, a) +end + +function DrawTxt3(x, y, width, height, scale, text, r, g, b, a) + SetTextFont(6) + SetTextProportional(0) + SetTextScale(scale, scale) + SetTextColour(r, g, b, a) + SetTextDropShadow(0, 0, 0, 0, 255) + SetTextEdge(2, 0, 0, 0, 255) + SetTextDropShadow() + SetTextOutline() + SetTextEntry("STRING") + AddTextComponentString(text) + DrawText(x - width / 5.5, y - height / 20000) +end + +RegisterNetEvent("sendWeight") +AddEventHandler("sendWeight", function(mm) + Weight = mm +end) + +RegisterNetEvent("showhud:toggle") +AddEventHandler("showhud:toggle", function(m) + ShowHUD = m +end) + +local rdy = true +function ShowHud() + ShowHUD = not ShowHUD +end + +RegisterNetEvent("esx:playerLoaded") +AddEventHandler("esx:playerLoaded", function() + rdy = true +end) + +local faimVal = 0 +local soifVal = 0 +Citizen.CreateThread(function() + while true do + Citizen.Wait(5000) + + TriggerEvent("bpt_status:getStatus", "hunger", function(status) + faimVal = status.val / 1000000 * 100 + end) + TriggerEvent("bpt_status:getStatus", "thirst", function(status) + soifVal = status.val / 1000000 * 100 + end) + end +end) + +Citizen.CreateThread(function() + Hea = 200 + while true do + Citizen.Wait(0) + if rdy then + if ShowHUD then + local myPed = GetPlayerPed(-1) + local veh = GetVehiclePedIsIn(myPed, true) + local health = GetEntityHealth(myPed) - 100 + local armor = GetPedArmour(myPed) + local vitesse = GetEntitySpeed(veh) * 3.6 + local plate = GetVehicleNumberPlateText(veh) + if health < 0 then + health = GetEntityHealth(myPed) + end + local armor = GetPedArmour(myPed) + DrawTxt3(0.200, 0.963, 1.0, 1.0, 0.40, "" .. "❤️", 255, 255, 255, 255) -- -20 + DrawTxt3(0.220, 0.963, 1.0, 1.0, 0.50, "" .. math.ceil(health) .. "", 255, 255, 255, 255) + + DrawTxt3(0.250, 0.963, 1.0, 1.0, 0.40, "" .. "🛡", 255, 255, 255, 255) + DrawTxt3(0.270, 0.963, 1.0, 1.0, 0.50, "" .. math.ceil(armor) .. "", 255, 255, 255, 255) + + DrawTxt3(0.290, 0.963, 1.0, 1.0, 0.40, "" .. "🍔", 255, 255, 255, 255) + DrawTxt3(0.310, 0.963, 1.0, 1.0, 0.50, "" .. math.ceil(faimVal) .. "", 255, 255, 255, 255) + + DrawTxt3(0.330, 0.963, 1.0, 1.0, 0.40, "" .. "🥤", 255, 255, 255, 255) + DrawTxt3(0.350, 0.963, 1.0, 1.0, 0.50, "" .. math.ceil(soifVal) .. "", 255, 255, 255, 255) + end + end + end +end) + +function DrawTxt(text, font, centre, x, y, scale, r, g, b, a) + SetTextFont(font) + SetTextProportional(false) + SetTextScale(scale, scale) + SetTextColour(r, g, b, a) + SetTextDropShadow(0, 0, 0, 0, 255) + SetTextEdge(1, 0, 0, 0, 255) + SetTextDropShadow(0) + SetTextOutline(0) + SetTextCentre(centre) + SetTextEntry("STRING") + AddTextComponentString(text) + DrawText(x, y) +end + +SendNUIMessage({ hud = ShowHUD }) diff --git a/server-data/resources/[esx_addons]/FLX_hud/fxmanifest.lua b/server-data/resources/[esx_addons]/FLX_hud/fxmanifest.lua new file mode 100644 index 000000000..9b09a1f59 --- /dev/null +++ b/server-data/resources/[esx_addons]/FLX_hud/fxmanifest.lua @@ -0,0 +1,12 @@ +fx_version("adamant") +game("gta5") + +description("bpt_RealisticVehicle") +lua54("yes") +version("1.0.2") + +shared_script("@es_extended/imports.lua") + +client_scripts({ + "client.lua", +}) From df25ba30898d5188b001ce82e84bc48af9e9c677 Mon Sep 17 00:00:00 2001 From: bitpredator <67551273+bitpredator@users.noreply.github.com> Date: Sat, 7 Dec 2024 11:07:27 +0100 Subject: [PATCH 21/22] chore: organize the file path in the best possible way --- .../[bpt_addons]/bpt_hud/client/carhud.lua | 3 - .../bpt_hud/client/hud.lua} | 1 - .../client/{client.lua => minimap.lua} | 0 .../resources/[esx_addons]/FLX_hud/LICENSE | 674 ------------------ .../resources/[esx_addons]/FLX_hud/README.md | 1 - .../[esx_addons]/FLX_hud/fxmanifest.lua | 12 - 6 files changed, 691 deletions(-) rename server-data/resources/{[esx_addons]/FLX_hud/client.lua => [bpt_addons]/bpt_hud/client/hud.lua} (99%) rename server-data/resources/[bpt_addons]/bpt_hud/client/{client.lua => minimap.lua} (100%) delete mode 100644 server-data/resources/[esx_addons]/FLX_hud/LICENSE delete mode 100644 server-data/resources/[esx_addons]/FLX_hud/README.md delete mode 100644 server-data/resources/[esx_addons]/FLX_hud/fxmanifest.lua diff --git a/server-data/resources/[bpt_addons]/bpt_hud/client/carhud.lua b/server-data/resources/[bpt_addons]/bpt_hud/client/carhud.lua index bc3e4a185..cba561dbc 100644 --- a/server-data/resources/[bpt_addons]/bpt_hud/client/carhud.lua +++ b/server-data/resources/[bpt_addons]/bpt_hud/client/carhud.lua @@ -71,9 +71,6 @@ CreateThread(function() -- Display Location and time when in any vehicle or on foot (if enabled) if pedInVeh or locationAlwaysOn then - -- Get time and display - DrawTxt(timeText, 4, locationColorText, 0.4, screenPosX, screenPosY + 0.048) - -- Display remainder of HUD when engine is on and vehicle is not a bicycle local vehicleClass = GetVehicleClass(vehicle) if pedInVeh and GetIsVehicleEngineRunning(vehicle) and vehicleClass ~= 13 then diff --git a/server-data/resources/[esx_addons]/FLX_hud/client.lua b/server-data/resources/[bpt_addons]/bpt_hud/client/hud.lua similarity index 99% rename from server-data/resources/[esx_addons]/FLX_hud/client.lua rename to server-data/resources/[bpt_addons]/bpt_hud/client/hud.lua index 73fcc5219..17d82be7a 100644 --- a/server-data/resources/[esx_addons]/FLX_hud/client.lua +++ b/server-data/resources/[bpt_addons]/bpt_hud/client/hud.lua @@ -127,7 +127,6 @@ function DrawTxt(text, font, centre, x, y, scale, r, g, b, a) SetTextEdge(1, 0, 0, 0, 255) SetTextDropShadow(0) SetTextOutline(0) - SetTextCentre(centre) SetTextEntry("STRING") AddTextComponentString(text) DrawText(x, y) diff --git a/server-data/resources/[bpt_addons]/bpt_hud/client/client.lua b/server-data/resources/[bpt_addons]/bpt_hud/client/minimap.lua similarity index 100% rename from server-data/resources/[bpt_addons]/bpt_hud/client/client.lua rename to server-data/resources/[bpt_addons]/bpt_hud/client/minimap.lua diff --git a/server-data/resources/[esx_addons]/FLX_hud/LICENSE b/server-data/resources/[esx_addons]/FLX_hud/LICENSE deleted file mode 100644 index e62ec04cd..000000000 --- a/server-data/resources/[esx_addons]/FLX_hud/LICENSE +++ /dev/null @@ -1,674 +0,0 @@ -GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/server-data/resources/[esx_addons]/FLX_hud/README.md b/server-data/resources/[esx_addons]/FLX_hud/README.md deleted file mode 100644 index 8b1378917..000000000 --- a/server-data/resources/[esx_addons]/FLX_hud/README.md +++ /dev/null @@ -1 +0,0 @@ - diff --git a/server-data/resources/[esx_addons]/FLX_hud/fxmanifest.lua b/server-data/resources/[esx_addons]/FLX_hud/fxmanifest.lua deleted file mode 100644 index 9b09a1f59..000000000 --- a/server-data/resources/[esx_addons]/FLX_hud/fxmanifest.lua +++ /dev/null @@ -1,12 +0,0 @@ -fx_version("adamant") -game("gta5") - -description("bpt_RealisticVehicle") -lua54("yes") -version("1.0.2") - -shared_script("@es_extended/imports.lua") - -client_scripts({ - "client.lua", -}) From cf3941e6f937dfb8dc317df2f49e3191dcc094a1 Mon Sep 17 00:00:00 2001 From: bitpredator <67551273+bitpredator@users.noreply.github.com> Date: Sat, 7 Dec 2024 14:38:14 +0100 Subject: [PATCH 22/22] fix: Fixed invisible belt icon issue --- .../[esx_addons]/pNotify/cl_notify.lua | 166 + .../[esx_addons]/pNotify/fxmanifest.lua | 19 + .../[esx_addons]/pNotify/html/index.html | 13 + .../[esx_addons]/pNotify/html/noty.css | 650 ++++ .../[esx_addons]/pNotify/html/noty.js | 3055 +++++++++++++++++ .../[esx_addons]/pNotify/html/pNotify.js | 11 + .../[esx_addons]/pNotify/html/themes.css | 131 + .../[esx_addons]/seatbelt/fxmanifest.lua | 16 + .../[esx_addons]/seatbelt/html/app.js | 26 + .../[esx_addons]/seatbelt/html/style.css | 91 + .../[esx_addons]/seatbelt/html/ui.html | 18 + .../[esx_addons]/seatbelt/img/seatbelt.png | Bin 0 -> 24990 bytes .../resources/[esx_addons]/seatbelt/main.lua | 193 ++ 13 files changed, 4389 insertions(+) create mode 100644 server-data/resources/[esx_addons]/pNotify/cl_notify.lua create mode 100644 server-data/resources/[esx_addons]/pNotify/fxmanifest.lua create mode 100644 server-data/resources/[esx_addons]/pNotify/html/index.html create mode 100644 server-data/resources/[esx_addons]/pNotify/html/noty.css create mode 100644 server-data/resources/[esx_addons]/pNotify/html/noty.js create mode 100644 server-data/resources/[esx_addons]/pNotify/html/pNotify.js create mode 100644 server-data/resources/[esx_addons]/pNotify/html/themes.css create mode 100644 server-data/resources/[esx_addons]/seatbelt/fxmanifest.lua create mode 100644 server-data/resources/[esx_addons]/seatbelt/html/app.js create mode 100644 server-data/resources/[esx_addons]/seatbelt/html/style.css create mode 100644 server-data/resources/[esx_addons]/seatbelt/html/ui.html create mode 100644 server-data/resources/[esx_addons]/seatbelt/img/seatbelt.png create mode 100644 server-data/resources/[esx_addons]/seatbelt/main.lua diff --git a/server-data/resources/[esx_addons]/pNotify/cl_notify.lua b/server-data/resources/[esx_addons]/pNotify/cl_notify.lua new file mode 100644 index 000000000..0762023a9 --- /dev/null +++ b/server-data/resources/[esx_addons]/pNotify/cl_notify.lua @@ -0,0 +1,166 @@ +--[[ +Complete List of Options: + type + layout + theme + text + timeout + progressBar + closeWith + animation = { + open + close + } + sounds = { + volume + conditions + sources + } + docTitle = { + conditions + } + modal + id + force + queue + killer + container + buttons + +More details below or visit the creators website http://ned.im/noty/options.html + +Layouts: + top + topLeft + topCenter + topRight + center + centerLeft + centerRight + bottom + bottomLeft + bottomCenter + bottomRight + +Types: + alert + success + error + warning + info + +Themes: -- You can create more themes inside html/themes.css, use the gta theme as a template. + gta + mint + relax + metroui + +Animations: + open: + noty_effects_open + gta_effects_open + gta_effects_open_left + gta_effects_fade_in + close: + noty_effects_close + gta_effects_close + gta_effects_close_left + gta_effects_fade_out + +closeWith: -- array, You will probably never use this. + click + button + +sounds: + volume: 0.0 - 1.0 + conditions: -- array + docVisible + docHidden + sources: -- array of sound files + +modal: + true + false + +force: + true + false + +queue: -- default is global, you can make it what ever you want though. + global + +killer: -- will close all visible notifications and show only this one + true + false + +visit the creators website http://ned.im/noty/options.html for more information +--]] + +function SetQueueMax(queue, max) + local tmp = { + queue = tostring(queue), + max = tonumber(max) + } + + SendNUIMessage({maxNotifications = tmp}) +end + +function SendNotification(options) + options.animation = options.animation or {} + options.sounds = options.sounds or {} + options.docTitle = options.docTitle or {} + + local options = { + type = options.type or "success", + layout = options.layout or "topRight", + theme = options.theme or "gta", + text = options.text or "Empty Notification", + timeout = options.timeout or 5000, + progressBar = options.progressBar ~= false and true or false, + closeWith = options.closeWith or {}, + animation = { + open = options.animation.open or "gta_effects_open", + close = options.animation.close or "gta_effects_close" + }, + sounds = { + volume = options.sounds.volume or 1, + conditions = options.sounds.conditions or {}, + sources = options.sounds.sources or {} + }, + docTitle = { + conditions = options.docTitle.conditions or {} + }, + modal = options.modal or false, + id = options.id or false, + force = options.force or false, + queue = options.queue or "global", + killer = options.killer or false, + container = options.container or false, + buttons = options.button or false + } + + SendNUIMessage({options = options}) +end + +RegisterNetEvent("pNotify:SendNotification") +AddEventHandler("pNotify:SendNotification", function(options) + SendNotification(options) +end) + +RegisterNetEvent("pNotify:SetQueueMax") +AddEventHandler("pNotify:SetQueueMax", function(queue, max) + SetQueueMax(queue, max) +end) + +--[[RegisterNetEvent("chatMessage") +AddEventHandler("chatMessage", function(author, color, text) + TriggerEvent("pNotify:SendNotification", {text = "" .. text .. "", + layout = "centerLeft", + timeout = 2000, + progressBar = false, + type = "error", + animation = { + open = "gta_effects_fade_in", + close = "gta_effects_fade_out" + }}) +end)]] \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/pNotify/fxmanifest.lua b/server-data/resources/[esx_addons]/pNotify/fxmanifest.lua new file mode 100644 index 000000000..bf90816f9 --- /dev/null +++ b/server-data/resources/[esx_addons]/pNotify/fxmanifest.lua @@ -0,0 +1,19 @@ +fx_version("adamant") +game("gta5") +lua54("yes") +version("1.0.2") + +ui_page("html/index.html") + +client_script("cl_notify.lua") + +files({ + "html/index.html", + "html/pNotify.js", + "html/noty.js", + "html/noty.css", + "html/themes.css", +}) + +export("SetQueueMax") +export("SendNotification") diff --git a/server-data/resources/[esx_addons]/pNotify/html/index.html b/server-data/resources/[esx_addons]/pNotify/html/index.html new file mode 100644 index 000000000..30a51c29d --- /dev/null +++ b/server-data/resources/[esx_addons]/pNotify/html/index.html @@ -0,0 +1,13 @@ + + + + pNotify + + + + + + + + + diff --git a/server-data/resources/[esx_addons]/pNotify/html/noty.css b/server-data/resources/[esx_addons]/pNotify/html/noty.css new file mode 100644 index 000000000..2df92f1f4 --- /dev/null +++ b/server-data/resources/[esx_addons]/pNotify/html/noty.css @@ -0,0 +1,650 @@ +.noty_layout_mixin, #noty_layout__top, #noty_layout__topLeft, #noty_layout__topCenter, #noty_layout__topRight, #noty_layout__bottom, #noty_layout__bottomLeft, #noty_layout__bottomCenter, #noty_layout__bottomRight, #noty_layout__center, #noty_layout__centerLeft, #noty_layout__centerRight { + position: fixed; + margin: 0; + padding: 0; + z-index: 9999999; + -webkit-transform: translateZ(0) scale(1, 1); + transform: translateZ(0) scale(1, 1); + -webkit-backface-visibility: hidden; + backface-visibility: hidden; + -webkit-font-smoothing: subpixel-antialiased; + filter: blur(0); + -webkit-filter: blur(0); + max-width: 90%; } + +#noty_layout__top { + top: 0; + left: 5%; + width: 90%; } + +#noty_layout__topLeft { + top: 20px; + left: 20px; + width: 325px; } + +#noty_layout__topCenter { + top: 5%; + left: 50%; + width: 325px; + -webkit-transform: translate(-webkit-calc(-50% - .5px)) translateZ(0) scale(1, 1); + transform: translate(calc(-50% - .5px)) translateZ(0) scale(1, 1); } + +#noty_layout__topRight { + top: 20px; + right: 20px; + width: 325px; } + +#noty_layout__bottom { + bottom: 0; + left: 5%; + width: 90%; } + +#noty_layout__bottomLeft { + bottom: 20px; + left: 20px; + width: 325px; } + +#noty_layout__bottomCenter { + bottom: 5%; + left: 50%; + width: 325px; + -webkit-transform: translate(-webkit-calc(-50% - .5px)) translateZ(0) scale(1, 1); + transform: translate(calc(-50% - .5px)) translateZ(0) scale(1, 1); } + +#noty_layout__bottomRight { + bottom: 20px; + right: 20px; + width: 325px; } + +#noty_layout__center { + top: 50%; + left: 50%; + width: 325px; + -webkit-transform: translate(-webkit-calc(-50% - .5px), -webkit-calc(-50% - .5px)) translateZ(0) scale(1, 1); + transform: translate(calc(-50% - .5px), calc(-50% - .5px)) translateZ(0) scale(1, 1); } + +#noty_layout__centerLeft { + top: 50%; + left: 20px; + width: 325px; + -webkit-transform: translate(0, -webkit-calc(-50% - .5px)) translateZ(0) scale(1, 1); + transform: translate(0, calc(-50% - .5px)) translateZ(0) scale(1, 1); } + +#noty_layout__centerRight { + top: 50%; + right: 20px; + width: 325px; + -webkit-transform: translate(0, -webkit-calc(-50% - .5px)) translateZ(0) scale(1, 1); + transform: translate(0, calc(-50% - .5px)) translateZ(0) scale(1, 1); } + +.noty_progressbar { + display: none; } + +.noty_has_timeout .noty_progressbar { + display: block; + position: absolute; + left: 0; + bottom: 0; + height: 3px; + width: 100%; + background-color: #646464; + opacity: 0.2; + filter: alpha(opacity=10); } + +.noty_bar { + -webkit-backface-visibility: hidden; + -webkit-transform: translate(0, 0) translateZ(0) scale(1, 1); + -ms-transform: translate(0, 0) scale(1, 1); + transform: translate(0, 0) scale(1, 1); + -webkit-font-smoothing: subpixel-antialiased; + overflow: hidden; } + +.noty_effects_open { + opacity: 0; + -webkit-transform: translate(50%); + -ms-transform: translate(50%); + transform: translate(50%); + -webkit-animation: noty_anim_in 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55); + animation: noty_anim_in 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55); + -webkit-animation-fill-mode: forwards; + animation-fill-mode: forwards; } + +.noty_effects_close { + -webkit-animation: noty_anim_out 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55); + animation: noty_anim_out 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55); + -webkit-animation-fill-mode: forwards; + animation-fill-mode: forwards; } + +.noty_fix_effects_height { + -webkit-animation: noty_anim_height 75ms ease-out; + animation: noty_anim_height 75ms ease-out; } + +.noty_close_with_click { + cursor: pointer; } + +.noty_close_button { + position: absolute; + top: 2px; + right: 2px; + font-weight: bold; + width: 20px; + height: 20px; + text-align: center; + line-height: 20px; + background-color: rgba(0, 0, 0, 0.05); + border-radius: 2px; + cursor: pointer; + -webkit-transition: all .2s ease-out; + transition: all .2s ease-out; } + +.noty_close_button:hover { + background-color: rgba(0, 0, 0, 0.1); } + +.noty_modal { + position: fixed; + width: 100%; + height: 100%; + background-color: #000; + z-index: 10000; + opacity: .3; + left: 0; + top: 0; } + +.noty_modal.noty_modal_open { + opacity: 0; + -webkit-animation: noty_modal_in .3s ease-out; + animation: noty_modal_in .3s ease-out; } + +.noty_modal.noty_modal_close { + -webkit-animation: noty_modal_out .3s ease-out; + animation: noty_modal_out .3s ease-out; + -webkit-animation-fill-mode: forwards; + animation-fill-mode: forwards; } + +@-webkit-keyframes noty_modal_in { + 100% { + opacity: .3; } } + +@keyframes noty_modal_in { + 100% { + opacity: .3; } } + +@-webkit-keyframes noty_modal_out { + 100% { + opacity: 0; } } + +@keyframes noty_modal_out { + 100% { + opacity: 0; } } + +@keyframes noty_modal_out { + 100% { + opacity: 0; } } + +@-webkit-keyframes noty_anim_in { + 100% { + -webkit-transform: translate(0); + transform: translate(0); + opacity: 1; } } + +@keyframes noty_anim_in { + 100% { + -webkit-transform: translate(0); + transform: translate(0); + opacity: 1; } } + +@-webkit-keyframes noty_anim_out { + 100% { + -webkit-transform: translate(50%); + transform: translate(50%); + opacity: 0; } } + +@keyframes noty_anim_out { + 100% { + -webkit-transform: translate(50%); + transform: translate(50%); + opacity: 0; } } + +@-webkit-keyframes noty_anim_height { + 100% { + height: 0; } } + +@keyframes noty_anim_height { + 100% { + height: 0; } } + +.noty_theme__relax.noty_bar { + margin: 4px 0; + overflow: hidden; + border-radius: 2px; + position: relative; } + .noty_theme__relax.noty_bar .noty_body { + padding: 10px; } + .noty_theme__relax.noty_bar .noty_buttons { + border-top: 1px solid #e7e7e7; + padding: 5px 10px; } + +.noty_theme__relax.noty_type__alert, +.noty_theme__relax.noty_type__notification { + background-color: #fff; + border: 1px solid #dedede; + color: #444; } + +.noty_theme__relax.noty_type__warning { + background-color: #FFEAA8; + border: 1px solid #FFC237; + color: #826200; } + .noty_theme__relax.noty_type__warning .noty_buttons { + border-color: #dfaa30; } + +.noty_theme__relax.noty_type__error { + background-color: #FF8181; + border: 1px solid #e25353; + color: #FFF; } + .noty_theme__relax.noty_type__error .noty_buttons { + border-color: darkred; } + +.noty_theme__relax.noty_type__info, +.noty_theme__relax.noty_type__information { + background-color: #78C5E7; + border: 1px solid #3badd6; + color: #FFF; } + .noty_theme__relax.noty_type__info .noty_buttons, + .noty_theme__relax.noty_type__information .noty_buttons { + border-color: #0B90C4; } + +.noty_theme__relax.noty_type__success { + background-color: #BCF5BC; + border: 1px solid #7cdd77; + color: darkgreen; } + .noty_theme__relax.noty_type__success .noty_buttons { + border-color: #50C24E; } + +.noty_theme__metroui.noty_bar { + margin: 4px 0; + overflow: hidden; + position: relative; + box-shadow: rgba(0, 0, 0, 0.298039) 0 0 5px 0; } + .noty_theme__metroui.noty_bar .noty_progressbar { + position: absolute; + left: 0; + bottom: 0; + height: 3px; + width: 100%; + background-color: #000; + opacity: 0.2; + filter: alpha(opacity=20); } + .noty_theme__metroui.noty_bar .noty_body { + padding: 1.25em; + font-size: 14px; } + .noty_theme__metroui.noty_bar .noty_buttons { + padding: 0 10px .5em 10px; } + +.noty_theme__metroui.noty_type__alert, +.noty_theme__metroui.noty_type__notification { + background-color: #fff; + color: #1d1d1d; } + +.noty_theme__metroui.noty_type__warning { + background-color: #FA6800; + color: #fff; } + +.noty_theme__metroui.noty_type__error { + background-color: #CE352C; + color: #FFF; } + +.noty_theme__metroui.noty_type__info, +.noty_theme__metroui.noty_type__information { + background-color: #1BA1E2; + color: #FFF; } + +.noty_theme__metroui.noty_type__success { + background-color: #60A917; + color: #fff; } + +.noty_theme__mint.noty_bar { + margin: 4px 0; + overflow: hidden; + border-radius: 2px; + position: relative; } + .noty_theme__mint.noty_bar .noty_body { + padding: 10px; + font-size: 14px; } + .noty_theme__mint.noty_bar .noty_buttons { + padding: 10px; } + +.noty_theme__mint.noty_type__alert, +.noty_theme__mint.noty_type__notification { + background-color: #fff; + border-bottom: 1px solid #D1D1D1; + color: #2F2F2F; } + +.noty_theme__mint.noty_type__warning { + background-color: #FFAE42; + border-bottom: 1px solid #E89F3C; + color: #fff; } + +.noty_theme__mint.noty_type__error { + background-color: #DE636F; + border-bottom: 1px solid #CA5A65; + color: #fff; } + +.noty_theme__mint.noty_type__info, +.noty_theme__mint.noty_type__information { + background-color: #7F7EFF; + border-bottom: 1px solid #7473E8; + color: #fff; } + +.noty_theme__mint.noty_type__success { + background-color: #AFC765; + border-bottom: 1px solid #A0B55C; + color: #fff; } + +.noty_theme__sunset.noty_bar { + margin: 4px 0; + overflow: hidden; + border-radius: 2px; + position: relative; } + .noty_theme__sunset.noty_bar .noty_body { + padding: 10px; + font-size: 14px; + text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1); } + .noty_theme__sunset.noty_bar .noty_buttons { + padding: 10px; } + +.noty_theme__sunset.noty_type__alert, +.noty_theme__sunset.noty_type__notification { + background-color: #073B4C; + color: #fff; } + .noty_theme__sunset.noty_type__alert .noty_progressbar, + .noty_theme__sunset.noty_type__notification .noty_progressbar { + background-color: #fff; } + +.noty_theme__sunset.noty_type__warning { + background-color: #FFD166; + color: #fff; } + +.noty_theme__sunset.noty_type__error { + background-color: #EF476F; + color: #fff; } + .noty_theme__sunset.noty_type__error .noty_progressbar { + opacity: .4; } + +.noty_theme__sunset.noty_type__info, +.noty_theme__sunset.noty_type__information { + background-color: #118AB2; + color: #fff; } + .noty_theme__sunset.noty_type__info .noty_progressbar, + .noty_theme__sunset.noty_type__information .noty_progressbar { + opacity: .6; } + +.noty_theme__sunset.noty_type__success { + background-color: #06D6A0; + color: #fff; } + +.noty_theme__bootstrap-v3.noty_bar { + margin: 4px 0; + overflow: hidden; + position: relative; + border: 1px solid transparent; + border-radius: 4px; } + .noty_theme__bootstrap-v3.noty_bar .noty_body { + padding: 15px; } + .noty_theme__bootstrap-v3.noty_bar .noty_buttons { + padding: 10px; } + .noty_theme__bootstrap-v3.noty_bar .noty_close_button { + font-size: 21px; + font-weight: 700; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + filter: alpha(opacity=20); + opacity: .2; + background: transparent; } + .noty_theme__bootstrap-v3.noty_bar .noty_close_button:hover { + background: transparent; + text-decoration: none; + cursor: pointer; + filter: alpha(opacity=50); + opacity: .5; } + +.noty_theme__bootstrap-v3.noty_type__alert, +.noty_theme__bootstrap-v3.noty_type__notification { + background-color: #fff; + color: inherit; } + +.noty_theme__bootstrap-v3.noty_type__warning { + background-color: #fcf8e3; + color: #8a6d3b; + border-color: #faebcc; } + +.noty_theme__bootstrap-v3.noty_type__error { + background-color: #f2dede; + color: #a94442; + border-color: #ebccd1; } + +.noty_theme__bootstrap-v3.noty_type__info, +.noty_theme__bootstrap-v3.noty_type__information { + background-color: #d9edf7; + color: #31708f; + border-color: #bce8f1; } + +.noty_theme__bootstrap-v3.noty_type__success { + background-color: #dff0d8; + color: #3c763d; + border-color: #d6e9c6; } + +.noty_theme__bootstrap-v4.noty_bar { + margin: 4px 0; + overflow: hidden; + position: relative; + border: 1px solid transparent; + border-radius: .25rem; } + .noty_theme__bootstrap-v4.noty_bar .noty_body { + padding: .75rem 1.25rem; } + .noty_theme__bootstrap-v4.noty_bar .noty_buttons { + padding: 10px; } + .noty_theme__bootstrap-v4.noty_bar .noty_close_button { + font-size: 1.5rem; + font-weight: 700; + line-height: 1; + color: #000; + text-shadow: 0 1px 0 #fff; + filter: alpha(opacity=20); + opacity: .5; + background: transparent; } + .noty_theme__bootstrap-v4.noty_bar .noty_close_button:hover { + background: transparent; + text-decoration: none; + cursor: pointer; + filter: alpha(opacity=50); + opacity: .75; } + +.noty_theme__bootstrap-v4.noty_type__alert, +.noty_theme__bootstrap-v4.noty_type__notification { + background-color: #fff; + color: inherit; } + +.noty_theme__bootstrap-v4.noty_type__warning { + background-color: #fcf8e3; + color: #8a6d3b; + border-color: #faebcc; } + +.noty_theme__bootstrap-v4.noty_type__error { + background-color: #f2dede; + color: #a94442; + border-color: #ebccd1; } + +.noty_theme__bootstrap-v4.noty_type__info, +.noty_theme__bootstrap-v4.noty_type__information { + background-color: #d9edf7; + color: #31708f; + border-color: #bce8f1; } + +.noty_theme__bootstrap-v4.noty_type__success { + background-color: #dff0d8; + color: #3c763d; + border-color: #d6e9c6; } + +.noty_theme__semanticui.noty_bar { + margin: 4px 0; + overflow: hidden; + position: relative; + border: 1px solid transparent; + font-size: 1em; + border-radius: .28571429rem; + box-shadow: 0 0 0 1px rgba(34, 36, 38, 0.22) inset, 0 0 0 0 transparent; } + .noty_theme__semanticui.noty_bar .noty_body { + padding: 1em 1.5em; + line-height: 1.4285em; } + .noty_theme__semanticui.noty_bar .noty_buttons { + padding: 10px; } + +.noty_theme__semanticui.noty_type__alert, +.noty_theme__semanticui.noty_type__notification { + background-color: #f8f8f9; + color: rgba(0, 0, 0, 0.87); } + +.noty_theme__semanticui.noty_type__warning { + background-color: #fffaf3; + color: #573a08; + box-shadow: 0 0 0 1px #c9ba9b inset, 0 0 0 0 transparent; } + +.noty_theme__semanticui.noty_type__error { + background-color: #fff6f6; + color: #9f3a38; + box-shadow: 0 0 0 1px #e0b4b4 inset, 0 0 0 0 transparent; } + +.noty_theme__semanticui.noty_type__info, +.noty_theme__semanticui.noty_type__information { + background-color: #f8ffff; + color: #276f86; + box-shadow: 0 0 0 1px #a9d5de inset, 0 0 0 0 transparent; } + +.noty_theme__semanticui.noty_type__success { + background-color: #fcfff5; + color: #2c662d; + box-shadow: 0 0 0 1px #a3c293 inset, 0 0 0 0 transparent; } + +.noty_theme__nest.noty_bar { + margin: 0 0 15px 0; + overflow: hidden; + border-radius: 2px; + position: relative; + box-shadow: rgba(0, 0, 0, 0.098039) 5px 4px 10px 0; } + .noty_theme__nest.noty_bar .noty_body { + padding: 10px; + font-size: 14px; + text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1); } + .noty_theme__nest.noty_bar .noty_buttons { + padding: 10px; } + +.noty_layout .noty_theme__nest.noty_bar { + z-index: 5; } + +.noty_layout .noty_theme__nest.noty_bar:nth-child(2) { + position: absolute; + top: 0; + margin-top: 4px; + margin-right: -4px; + margin-left: 4px; + z-index: 4; + width: 100%; } + +.noty_layout .noty_theme__nest.noty_bar:nth-child(3) { + position: absolute; + top: 0; + margin-top: 8px; + margin-right: -8px; + margin-left: 8px; + z-index: 3; + width: 100%; } + +.noty_layout .noty_theme__nest.noty_bar:nth-child(4) { + position: absolute; + top: 0; + margin-top: 12px; + margin-right: -12px; + margin-left: 12px; + z-index: 2; + width: 100%; } + +.noty_layout .noty_theme__nest.noty_bar:nth-child(5) { + position: absolute; + top: 0; + margin-top: 16px; + margin-right: -16px; + margin-left: 16px; + z-index: 1; + width: 100%; } + +.noty_layout .noty_theme__nest.noty_bar:nth-child(n+6) { + position: absolute; + top: 0; + margin-top: 20px; + margin-right: -20px; + margin-left: 20px; + z-index: -1; + width: 100%; } + +#noty_layout__bottomLeft .noty_theme__nest.noty_bar:nth-child(2), +#noty_layout__topLeft .noty_theme__nest.noty_bar:nth-child(2) { + margin-top: 4px; + margin-left: -4px; + margin-right: 4px; } + +#noty_layout__bottomLeft .noty_theme__nest.noty_bar:nth-child(3), +#noty_layout__topLeft .noty_theme__nest.noty_bar:nth-child(3) { + margin-top: 8px; + margin-left: -8px; + margin-right: 8px; } + +#noty_layout__bottomLeft .noty_theme__nest.noty_bar:nth-child(4), +#noty_layout__topLeft .noty_theme__nest.noty_bar:nth-child(4) { + margin-top: 12px; + margin-left: -12px; + margin-right: 12px; } + +#noty_layout__bottomLeft .noty_theme__nest.noty_bar:nth-child(5), +#noty_layout__topLeft .noty_theme__nest.noty_bar:nth-child(5) { + margin-top: 16px; + margin-left: -16px; + margin-right: 16px; } + +#noty_layout__bottomLeft .noty_theme__nest.noty_bar:nth-child(n+6), +#noty_layout__topLeft .noty_theme__nest.noty_bar:nth-child(n+6) { + margin-top: 20px; + margin-left: -20px; + margin-right: 20px; } + +.noty_theme__nest.noty_type__alert, +.noty_theme__nest.noty_type__notification { + background-color: #073B4C; + color: #fff; } + .noty_theme__nest.noty_type__alert .noty_progressbar, + .noty_theme__nest.noty_type__notification .noty_progressbar { + background-color: #fff; } + +.noty_theme__nest.noty_type__warning { + background-color: #FFD166; + color: #fff; } + +.noty_theme__nest.noty_type__error { + background-color: #EF476F; + color: #fff; } + .noty_theme__nest.noty_type__error .noty_progressbar { + opacity: .4; } + +.noty_theme__nest.noty_type__info, +.noty_theme__nest.noty_type__information { + background-color: #118AB2; + color: #fff; } + .noty_theme__nest.noty_type__info .noty_progressbar, + .noty_theme__nest.noty_type__information .noty_progressbar { + opacity: .6; } + +.noty_theme__nest.noty_type__success { + background-color: #06D6A0; + color: #fff; } + +/*# sourceMappingURL=noty.css.map*/ \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/pNotify/html/noty.js b/server-data/resources/[esx_addons]/pNotify/html/noty.js new file mode 100644 index 000000000..f3f165c07 --- /dev/null +++ b/server-data/resources/[esx_addons]/pNotify/html/noty.js @@ -0,0 +1,3055 @@ +(function webpackUniversalModuleDefinition(root, factory) { + if(typeof exports === 'object' && typeof module === 'object') + module.exports = factory(); + else if(typeof define === 'function' && define.amd) + define("Noty", [], factory); + else if(typeof exports === 'object') + exports["Noty"] = factory(); + else + root["Noty"] = factory(); +})(this, function() { +return /******/ (function(modules) { // webpackBootstrap +/******/ // The module cache +/******/ var installedModules = {}; +/******/ +/******/ // The require function +/******/ function __webpack_require__(moduleId) { +/******/ +/******/ // Check if module is in cache +/******/ if(installedModules[moduleId]) { +/******/ return installedModules[moduleId].exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = installedModules[moduleId] = { +/******/ i: moduleId, +/******/ l: false, +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); +/******/ +/******/ // Flag the module as loaded +/******/ module.l = true; +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/******/ +/******/ // expose the modules object (__webpack_modules__) +/******/ __webpack_require__.m = modules; +/******/ +/******/ // expose the module cache +/******/ __webpack_require__.c = installedModules; +/******/ +/******/ // identity function for calling harmony imports with the correct context +/******/ __webpack_require__.i = function(value) { return value; }; +/******/ +/******/ // define getter function for harmony exports +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!__webpack_require__.o(exports, name)) { +/******/ Object.defineProperty(exports, name, { +/******/ configurable: false, +/******/ enumerable: true, +/******/ get: getter +/******/ }); +/******/ } +/******/ }; +/******/ +/******/ // getDefaultExport function for compatibility with non-harmony modules +/******/ __webpack_require__.n = function(module) { +/******/ var getter = module && module.__esModule ? +/******/ function getDefault() { return module['default']; } : +/******/ function getModuleExports() { return module; }; +/******/ __webpack_require__.d(getter, 'a', getter); +/******/ return getter; +/******/ }; +/******/ +/******/ // Object.prototype.hasOwnProperty.call +/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; +/******/ +/******/ // __webpack_public_path__ +/******/ __webpack_require__.p = ""; +/******/ +/******/ // Load entry module and return exports +/******/ return __webpack_require__(__webpack_require__.s = 6); +/******/ }) +/************************************************************************/ +/******/ ([ +/* 0 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.css = exports.deepExtend = exports.animationEndEvents = undefined; + +var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; + +exports.inArray = inArray; +exports.stopPropagation = stopPropagation; +exports.generateID = generateID; +exports.outerHeight = outerHeight; +exports.addListener = addListener; +exports.hasClass = hasClass; +exports.addClass = addClass; +exports.removeClass = removeClass; +exports.remove = remove; +exports.classList = classList; +exports.visibilityChangeFlow = visibilityChangeFlow; +exports.createAudioElements = createAudioElements; + +var _api = __webpack_require__(1); + +var API = _interopRequireWildcard(_api); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +var animationEndEvents = exports.animationEndEvents = 'webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend'; + +function inArray(needle, haystack, argStrict) { + var key = void 0; + var strict = !!argStrict; + + if (strict) { + for (key in haystack) { + if (haystack.hasOwnProperty(key) && haystack[key] === needle) { + return true; + } + } + } else { + for (key in haystack) { + if (haystack.hasOwnProperty(key) && haystack[key] === needle) { + return true; + } + } + } + return false; +} + +function stopPropagation(evt) { + evt = evt || window.event; + + if (typeof evt.stopPropagation !== 'undefined') { + evt.stopPropagation(); + } else { + evt.cancelBubble = true; + } +} + +var deepExtend = exports.deepExtend = function deepExtend(out) { + out = out || {}; + + for (var i = 1; i < arguments.length; i++) { + var obj = arguments[i]; + + if (!obj) continue; + + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + if (Array.isArray(obj[key])) { + out[key] = obj[key]; + } else if (_typeof(obj[key]) === 'object' && obj[key] !== null) { + out[key] = deepExtend(out[key], obj[key]); + } else { + out[key] = obj[key]; + } + } + } + } + + return out; +}; + +function generateID() { + var prefix = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; + + var id = 'noty_' + prefix + '_'; + + id += 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { + var r = Math.random() * 16 | 0; + var v = c === 'x' ? r : r & 0x3 | 0x8; + return v.toString(16); + }); + + return id; +} + +function outerHeight(el) { + var height = el.offsetHeight; + var style = window.getComputedStyle(el); + + height += parseInt(style.marginTop) + parseInt(style.marginBottom); + return height; +} + +var css = exports.css = function () { + var cssPrefixes = ['Webkit', 'O', 'Moz', 'ms']; + var cssProps = {}; + + function camelCase(string) { + return string.replace(/^-ms-/, 'ms-').replace(/-([\da-z])/gi, function (match, letter) { + return letter.toUpperCase(); + }); + } + + function getVendorProp(name) { + var style = document.body.style; + if (name in style) return name; + + var i = cssPrefixes.length; + var capName = name.charAt(0).toUpperCase() + name.slice(1); + var vendorName = void 0; + + while (i--) { + vendorName = cssPrefixes[i] + capName; + if (vendorName in style) return vendorName; + } + + return name; + } + + function getStyleProp(name) { + name = camelCase(name); + return cssProps[name] || (cssProps[name] = getVendorProp(name)); + } + + function applyCss(element, prop, value) { + prop = getStyleProp(prop); + element.style[prop] = value; + } + + return function (element, properties) { + var args = arguments; + var prop = void 0; + var value = void 0; + + if (args.length === 2) { + for (prop in properties) { + if (properties.hasOwnProperty(prop)) { + value = properties[prop]; + if (value !== undefined && properties.hasOwnProperty(prop)) { + applyCss(element, prop, value); + } + } + } + } else { + applyCss(element, args[1], args[2]); + } + }; +}(); + +function addListener(el, events, cb) { + var useCapture = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; + + events = events.split(' '); + for (var i = 0; i < events.length; i++) { + if (document.addEventListener) { + el.addEventListener(events[i], cb, useCapture); + } else if (document.attachEvent) { + el.attachEvent('on' + events[i], cb); + } + } +} + +function hasClass(element, name) { + var list = typeof element === 'string' ? element : classList(element); + return list.indexOf(' ' + name + ' ') >= 0; +} + +function addClass(element, name) { + var oldList = classList(element); + var newList = oldList + name; + + if (hasClass(oldList, name)) return; + + // Trim the opening space. + element.className = newList.substring(1); +} + +function removeClass(element, name) { + var oldList = classList(element); + var newList = void 0; + + if (!hasClass(element, name)) return; + + // Replace the class name. + newList = oldList.replace(' ' + name + ' ', ' '); + + // Trim the opening and closing spaces. + element.className = newList.substring(1, newList.length - 1); +} + +function remove(element) { + if (element.parentNode) { + element.parentNode.removeChild(element); + } +} + +function classList(element) { + return (' ' + (element && element.className || '') + ' ').replace(/\s+/gi, ' '); +} + +function visibilityChangeFlow() { + var hidden = void 0; + var visibilityChange = void 0; + if (typeof document.hidden !== 'undefined') { + // Opera 12.10 and Firefox 18 and later support + hidden = 'hidden'; + visibilityChange = 'visibilitychange'; + } else if (typeof document.msHidden !== 'undefined') { + hidden = 'msHidden'; + visibilityChange = 'msvisibilitychange'; + } else if (typeof document.webkitHidden !== 'undefined') { + hidden = 'webkitHidden'; + visibilityChange = 'webkitvisibilitychange'; + } + + function onVisibilityChange() { + API.PageHidden = document[hidden]; + handleVisibilityChange(); + } + + function onBlur() { + API.PageHidden = true; + handleVisibilityChange(); + } + + function onFocus() { + API.PageHidden = false; + handleVisibilityChange(); + } + + function handleVisibilityChange() { + if (API.PageHidden) stopAll();else resumeAll(); + } + + function stopAll() { + setTimeout(function () { + Object.keys(API.Store).forEach(function (id) { + if (API.Store.hasOwnProperty(id)) { + if (API.Store[id].options.visibilityControl) { + API.Store[id].stop(); + } + } + }); + }, 100); + } + + function resumeAll() { + setTimeout(function () { + Object.keys(API.Store).forEach(function (id) { + if (API.Store.hasOwnProperty(id)) { + if (API.Store[id].options.visibilityControl) { + API.Store[id].resume(); + } + } + }); + API.queueRenderAll(); + }, 100); + } + + addListener(document, visibilityChange, onVisibilityChange); + addListener(window, 'blur', onBlur); + addListener(window, 'focus', onFocus); +} + +function createAudioElements(ref) { + if (ref.hasSound) { + var audioElement = document.createElement('audio'); + + ref.options.sounds.sources.forEach(function (s) { + var source = document.createElement('source'); + source.src = s; + source.type = 'audio/' + getExtension(s); + audioElement.appendChild(source); + }); + + if (ref.barDom) { + ref.barDom.appendChild(audioElement); + } else { + document.querySelector('body').appendChild(audioElement); + } + + audioElement.volume = ref.options.sounds.volume; + + if (!ref.soundPlayed) { + audioElement.play(); + ref.soundPlayed = true; + } + + audioElement.onended = function () { + remove(audioElement); + }; + } +} + +function getExtension(fileName) { + return fileName.match(/\.([^.]+)$/)[1]; +} + +/***/ }), +/* 1 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.Defaults = exports.Store = exports.Queues = exports.DefaultMaxVisible = exports.docTitle = exports.DocModalCount = exports.PageHidden = undefined; +exports.getQueueCounts = getQueueCounts; +exports.addToQueue = addToQueue; +exports.removeFromQueue = removeFromQueue; +exports.queueRender = queueRender; +exports.queueRenderAll = queueRenderAll; +exports.ghostFix = ghostFix; +exports.build = build; +exports.hasButtons = hasButtons; +exports.handleModal = handleModal; +exports.handleModalClose = handleModalClose; +exports.queueClose = queueClose; +exports.dequeueClose = dequeueClose; +exports.fire = fire; +exports.openFlow = openFlow; +exports.closeFlow = closeFlow; + +var _utils = __webpack_require__(0); + +var Utils = _interopRequireWildcard(_utils); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +var PageHidden = exports.PageHidden = false; +var DocModalCount = exports.DocModalCount = 0; + +var DocTitleProps = { + originalTitle: null, + count: 0, + changed: false, + timer: -1 +}; + +var docTitle = exports.docTitle = { + increment: function increment() { + DocTitleProps.count++; + + docTitle._update(); + }, + + decrement: function decrement() { + DocTitleProps.count--; + + if (DocTitleProps.count <= 0) { + docTitle._clear(); + return; + } + + docTitle._update(); + }, + + _update: function _update() { + var title = document.title; + + if (!DocTitleProps.changed) { + DocTitleProps.originalTitle = title; + document.title = '(' + DocTitleProps.count + ') ' + title; + DocTitleProps.changed = true; + } else { + document.title = '(' + DocTitleProps.count + ') ' + DocTitleProps.originalTitle; + } + }, + + _clear: function _clear() { + if (DocTitleProps.changed) { + DocTitleProps.count = 0; + document.title = DocTitleProps.originalTitle; + DocTitleProps.changed = false; + } + } +}; + +var DefaultMaxVisible = exports.DefaultMaxVisible = 5; + +var Queues = exports.Queues = { + global: { + maxVisible: DefaultMaxVisible, + queue: [] + } +}; + +var Store = exports.Store = {}; + +var Defaults = exports.Defaults = { + type: 'alert', + layout: 'topRight', + theme: 'mint', + text: '', + timeout: false, + progressBar: true, + closeWith: ['click'], + animation: { + open: 'noty_effects_open', + close: 'noty_effects_close' + }, + id: false, + force: false, + killer: false, + queue: 'global', + container: false, + buttons: [], + callbacks: { + beforeShow: null, + onShow: null, + afterShow: null, + onClose: null, + afterClose: null, + onHover: null, + onTemplate: null + }, + sounds: { + sources: [], + volume: 1, + conditions: [] + }, + titleCount: { + conditions: [] + }, + modal: false, + visibilityControl: true +}; + +/** + * @param {string} queueName + * @return {object} + */ +function getQueueCounts() { + var queueName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'global'; + + var count = 0; + var max = DefaultMaxVisible; + + if (Queues.hasOwnProperty(queueName)) { + max = Queues[queueName].maxVisible; + Object.keys(Store).forEach(function (i) { + if (Store[i].options.queue === queueName && !Store[i].closed) count++; + }); + } + + return { + current: count, + maxVisible: max + }; +} + +/** + * @param {Noty} ref + * @return {void} + */ +function addToQueue(ref) { + if (!Queues.hasOwnProperty(ref.options.queue)) { + Queues[ref.options.queue] = { maxVisible: DefaultMaxVisible, queue: [] }; + } + + Queues[ref.options.queue].queue.push(ref); +} + +/** + * @param {Noty} ref + * @return {void} + */ +function removeFromQueue(ref) { + if (Queues.hasOwnProperty(ref.options.queue)) { + var queue = []; + Object.keys(Queues[ref.options.queue].queue).forEach(function (i) { + if (Queues[ref.options.queue].queue[i].id !== ref.id) { + queue.push(Queues[ref.options.queue].queue[i]); + } + }); + Queues[ref.options.queue].queue = queue; + } +} + +/** + * @param {string} queueName + * @return {void} + */ +function queueRender() { + var queueName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'global'; + + if (Queues.hasOwnProperty(queueName)) { + var noty = Queues[queueName].queue.shift(); + + if (noty) noty.show(); + } +} + +/** + * @return {void} + */ +function queueRenderAll() { + Object.keys(Queues).forEach(function (queueName) { + queueRender(queueName); + }); +} + +/** + * @param {Noty} ref + * @return {void} + */ +function ghostFix(ref) { + var ghostID = Utils.generateID('ghost'); + var ghost = document.createElement('div'); + ghost.setAttribute('id', ghostID); + Utils.css(ghost, { + height: Utils.outerHeight(ref.barDom) + 'px' + }); + + ref.barDom.insertAdjacentHTML('afterend', ghost.outerHTML); + + Utils.remove(ref.barDom); + ghost = document.getElementById(ghostID); + Utils.addClass(ghost, 'noty_fix_effects_height'); + Utils.addListener(ghost, Utils.animationEndEvents, function () { + Utils.remove(ghost); + }); +} + +/** + * @param {Noty} ref + * @return {void} + */ +function build(ref) { + findOrCreateContainer(ref); + + var markup = '
' + ref.options.text + '
' + buildButtons(ref) + (ref.options.progressBar ? '
' : ''); + + ref.barDom = document.createElement('div'); + ref.barDom.setAttribute('id', ref.id); + Utils.addClass(ref.barDom, 'noty_bar noty_type__' + ref.options.type + ' noty_theme__' + ref.options.theme); + + ref.barDom.innerHTML = markup; + + fire(ref, 'onTemplate'); +} + +/** + * @param {Noty} ref + * @return {boolean} + */ +function hasButtons(ref) { + return !!(ref.options.buttons && Object.keys(ref.options.buttons).length); +} + +/** + * @param {Noty} ref + * @return {string} + */ +function buildButtons(ref) { + if (hasButtons(ref)) { + var buttons = document.createElement('div'); + Utils.addClass(buttons, 'noty_buttons'); + + Object.keys(ref.options.buttons).forEach(function (key) { + buttons.appendChild(ref.options.buttons[key].dom); + }); + + ref.options.buttons.forEach(function (btn) { + buttons.appendChild(btn.dom); + }); + return buttons.outerHTML; + } + return ''; +} + +/** + * @param {Noty} ref + * @return {void} + */ +function handleModal(ref) { + if (ref.options.modal) { + if (DocModalCount === 0) { + createModal(ref); + } + + exports.DocModalCount = DocModalCount += 1; + } +} + +/** + * @param {Noty} ref + * @return {void} + */ +function handleModalClose(ref) { + if (ref.options.modal && DocModalCount > 0) { + exports.DocModalCount = DocModalCount -= 1; + + if (DocModalCount <= 0) { + var modal = document.querySelector('.noty_modal'); + + if (modal) { + Utils.removeClass(modal, 'noty_modal_open'); + Utils.addClass(modal, 'noty_modal_close'); + Utils.addListener(modal, Utils.animationEndEvents, function () { + Utils.remove(modal); + }); + } + } + } +} + +/** + * @return {void} + */ +function createModal() { + var body = document.querySelector('body'); + var modal = document.createElement('div'); + Utils.addClass(modal, 'noty_modal'); + body.insertBefore(modal, body.firstChild); + Utils.addClass(modal, 'noty_modal_open'); + + Utils.addListener(modal, Utils.animationEndEvents, function () { + Utils.removeClass(modal, 'noty_modal_open'); + }); +} + +/** + * @param {Noty} ref + * @return {void} + */ +function findOrCreateContainer(ref) { + if (ref.options.container) { + ref.layoutDom = document.querySelector(ref.options.container); + return; + } + + var layoutID = 'noty_layout__' + ref.options.layout; + ref.layoutDom = document.querySelector('div#' + layoutID); + + if (!ref.layoutDom) { + ref.layoutDom = document.createElement('div'); + ref.layoutDom.setAttribute('id', layoutID); + Utils.addClass(ref.layoutDom, 'noty_layout'); + document.querySelector('body').appendChild(ref.layoutDom); + } +} + +/** + * @param {Noty} ref + * @return {void} + */ +function queueClose(ref) { + if (ref.options.timeout) { + if (ref.options.progressBar && ref.progressDom) { + Utils.css(ref.progressDom, { + transition: 'width ' + ref.options.timeout + 'ms linear', + width: '0%' + }); + } + + clearTimeout(ref.closeTimer); + + ref.closeTimer = setTimeout(function () { + ref.close(); + }, ref.options.timeout); + } +} + +/** + * @param {Noty} ref + * @return {void} + */ +function dequeueClose(ref) { + if (ref.options.timeout && ref.closeTimer) { + clearTimeout(ref.closeTimer); + ref.closeTimer = -1; + + if (ref.options.progressBar && ref.progressDom) { + Utils.css(ref.progressDom, { + transition: 'width 0ms linear', + width: '100%' + }); + } + } +} + +/** + * @param {Noty} ref + * @param {string} eventName + * @return {void} + */ +function fire(ref, eventName) { + if (ref.listeners.hasOwnProperty(eventName)) { + ref.listeners[eventName].forEach(function (cb) { + if (typeof cb === 'function') { + cb.apply(ref); + } + }); + } +} + +/** + * @param {Noty} ref + * @return {void} + */ +function openFlow(ref) { + fire(ref, 'afterShow'); + queueClose(ref); + + Utils.addListener(ref.barDom, 'mouseenter', function () { + dequeueClose(ref); + }); + + Utils.addListener(ref.barDom, 'mouseleave', function () { + queueClose(ref); + }); +} + +/** + * @param {Noty} ref + * @return {void} + */ +function closeFlow(ref) { + delete Store[ref.id]; + ref.closing = false; + fire(ref, 'afterClose'); + + Utils.remove(ref.barDom); + + if (ref.layoutDom.querySelectorAll('.noty_bar').length === 0 && !ref.options.container) { + Utils.remove(ref.layoutDom); + } + + if (Utils.inArray('docVisible', ref.options.titleCount.conditions) || Utils.inArray('docHidden', ref.options.titleCount.conditions)) { + docTitle.decrement(); + } + + queueRender(ref.options.queue); +} + +/***/ }), +/* 2 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports.NotyButton = undefined; + +var _utils = __webpack_require__(0); + +var Utils = _interopRequireWildcard(_utils); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var NotyButton = exports.NotyButton = function NotyButton(html, classes, cb) { + var _this = this; + + var attributes = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; + + _classCallCheck(this, NotyButton); + + this.dom = document.createElement('button'); + this.dom.innerHTML = html; + this.id = attributes.id = attributes.id || Utils.generateID('button'); + this.cb = cb; + Object.keys(attributes).forEach(function (propertyName) { + _this.dom.setAttribute(propertyName, attributes[propertyName]); + }); + Utils.addClass(this.dom, classes || 'noty_btn'); + + return this; +}; + +/***/ }), +/* 3 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var Push = exports.Push = function () { + function Push() { + var workerPath = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '/service-worker.js'; + + _classCallCheck(this, Push); + + this.subData = {}; + this.workerPath = workerPath; + this.listeners = { + onPermissionGranted: [], + onPermissionDenied: [], + onSubscriptionSuccess: [], + onSubscriptionCancel: [], + onWorkerError: [], + onWorkerSuccess: [], + onWorkerNotSupported: [] + }; + return this; + } + + /** + * @param {string} eventName + * @param {function} cb + * @return {Push} + */ + + + _createClass(Push, [{ + key: 'on', + value: function on(eventName) { + var cb = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {}; + + if (typeof cb === 'function' && this.listeners.hasOwnProperty(eventName)) { + this.listeners[eventName].push(cb); + } + + return this; + } + }, { + key: 'fire', + value: function fire(eventName) { + var _this = this; + + var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; + + if (this.listeners.hasOwnProperty(eventName)) { + this.listeners[eventName].forEach(function (cb) { + if (typeof cb === 'function') { + cb.apply(_this, params); + } + }); + } + } + }, { + key: 'create', + value: function create() { + console.log('NOT IMPLEMENTED YET'); + } + + /** + * @return {boolean} + */ + + }, { + key: 'isSupported', + value: function isSupported() { + var result = false; + + try { + result = window.Notification || window.webkitNotifications || navigator.mozNotification || window.external && window.external.msIsSiteMode() !== undefined; + } catch (e) {} + + return result; + } + + /** + * @return {string} + */ + + }, { + key: 'getPermissionStatus', + value: function getPermissionStatus() { + var perm = 'default'; + + if (window.Notification && window.Notification.permissionLevel) { + perm = window.Notification.permissionLevel; + } else if (window.webkitNotifications && window.webkitNotifications.checkPermission) { + switch (window.webkitNotifications.checkPermission()) { + case 1: + perm = 'default'; + break; + case 0: + perm = 'granted'; + break; + default: + perm = 'denied'; + } + } else if (window.Notification && window.Notification.permission) { + perm = window.Notification.permission; + } else if (navigator.mozNotification) { + perm = 'granted'; + } else if (window.external && window.external.msIsSiteMode() !== undefined) { + perm = window.external.msIsSiteMode() ? 'granted' : 'default'; + } + + return perm.toString().toLowerCase(); + } + + /** + * @return {string} + */ + + }, { + key: 'getEndpoint', + value: function getEndpoint(subscription) { + var endpoint = subscription.endpoint; + var subscriptionId = subscription.subscriptionId; + + // fix for Chrome < 45 + if (subscriptionId && endpoint.indexOf(subscriptionId) === -1) { + endpoint += '/' + subscriptionId; + } + + return endpoint; + } + + /** + * @return {boolean} + */ + + }, { + key: 'isSWRegistered', + value: function isSWRegistered() { + try { + return navigator.serviceWorker.controller.state === 'activated'; + } catch (e) { + return false; + } + } + + /** + * @return {void} + */ + + }, { + key: 'unregisterWorker', + value: function unregisterWorker() { + var self = this; + if ('serviceWorker' in navigator) { + navigator.serviceWorker.getRegistrations().then(function (registrations) { + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = registrations[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var registration = _step.value; + + registration.unregister(); + self.fire('onSubscriptionCancel'); + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator.return) { + _iterator.return(); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + }); + } + } + + /** + * @return {void} + */ + + }, { + key: 'requestSubscription', + value: function requestSubscription() { + var _this2 = this; + + var userVisibleOnly = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; + + var self = this; + var current = this.getPermissionStatus(); + var cb = function cb(result) { + if (result === 'granted') { + _this2.fire('onPermissionGranted'); + + if ('serviceWorker' in navigator) { + navigator.serviceWorker.register(_this2.workerPath).then(function () { + navigator.serviceWorker.ready.then(function (serviceWorkerRegistration) { + self.fire('onWorkerSuccess'); + serviceWorkerRegistration.pushManager.subscribe({ + userVisibleOnly: userVisibleOnly + }).then(function (subscription) { + var key = subscription.getKey('p256dh'); + var token = subscription.getKey('auth'); + + self.subData = { + endpoint: self.getEndpoint(subscription), + p256dh: key ? window.btoa(String.fromCharCode.apply(null, new Uint8Array(key))) : null, + auth: token ? window.btoa(String.fromCharCode.apply(null, new Uint8Array(token))) : null + }; + + self.fire('onSubscriptionSuccess', [self.subData]); + }).catch(function (err) { + self.fire('onWorkerError', [err]); + }); + }); + }); + } else { + self.fire('onWorkerNotSupported'); + } + } else if (result === 'denied') { + _this2.fire('onPermissionDenied'); + _this2.unregisterWorker(); + } + }; + + if (current === 'default') { + if (window.Notification && window.Notification.requestPermission) { + window.Notification.requestPermission(cb); + } else if (window.webkitNotifications && window.webkitNotifications.checkPermission) { + window.webkitNotifications.requestPermission(cb); + } + } else { + cb(current); + } + } + }]); + + return Push; +}(); + +/***/ }), +/* 4 */ +/***/ (function(module, exports, __webpack_require__) { + +/* WEBPACK VAR INJECTION */(function(process, global) {var require;/*! + * @overview es6-promise - a tiny implementation of Promises/A+. + * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) + * @license Licensed under MIT license + * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE + * @version 4.1.0 + */ + +(function (global, factory) { + true ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global.ES6Promise = factory()); +}(this, (function () { 'use strict'; + +function objectOrFunction(x) { + return typeof x === 'function' || typeof x === 'object' && x !== null; +} + +function isFunction(x) { + return typeof x === 'function'; +} + +var _isArray = undefined; +if (!Array.isArray) { + _isArray = function (x) { + return Object.prototype.toString.call(x) === '[object Array]'; + }; +} else { + _isArray = Array.isArray; +} + +var isArray = _isArray; + +var len = 0; +var vertxNext = undefined; +var customSchedulerFn = undefined; + +var asap = function asap(callback, arg) { + queue[len] = callback; + queue[len + 1] = arg; + len += 2; + if (len === 2) { + // If len is 2, that means that we need to schedule an async flush. + // If additional callbacks are queued before the queue is flushed, they + // will be processed by this flush that we are scheduling. + if (customSchedulerFn) { + customSchedulerFn(flush); + } else { + scheduleFlush(); + } + } +}; + +function setScheduler(scheduleFn) { + customSchedulerFn = scheduleFn; +} + +function setAsap(asapFn) { + asap = asapFn; +} + +var browserWindow = typeof window !== 'undefined' ? window : undefined; +var browserGlobal = browserWindow || {}; +var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver; +var isNode = typeof self === 'undefined' && typeof process !== 'undefined' && ({}).toString.call(process) === '[object process]'; + +// test for web worker but not in IE10 +var isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined'; + +// node +function useNextTick() { + // node version 0.10.x displays a deprecation warning when nextTick is used recursively + // see https://github.com/cujojs/when/issues/410 for details + return function () { + return process.nextTick(flush); + }; +} + +// vertx +function useVertxTimer() { + if (typeof vertxNext !== 'undefined') { + return function () { + vertxNext(flush); + }; + } + + return useSetTimeout(); +} + +function useMutationObserver() { + var iterations = 0; + var observer = new BrowserMutationObserver(flush); + var node = document.createTextNode(''); + observer.observe(node, { characterData: true }); + + return function () { + node.data = iterations = ++iterations % 2; + }; +} + +// web worker +function useMessageChannel() { + var channel = new MessageChannel(); + channel.port1.onmessage = flush; + return function () { + return channel.port2.postMessage(0); + }; +} + +function useSetTimeout() { + // Store setTimeout reference so es6-promise will be unaffected by + // other code modifying setTimeout (like sinon.useFakeTimers()) + var globalSetTimeout = setTimeout; + return function () { + return globalSetTimeout(flush, 1); + }; +} + +var queue = new Array(1000); +function flush() { + for (var i = 0; i < len; i += 2) { + var callback = queue[i]; + var arg = queue[i + 1]; + + callback(arg); + + queue[i] = undefined; + queue[i + 1] = undefined; + } + + len = 0; +} + +function attemptVertx() { + try { + var r = require; + var vertx = __webpack_require__(9); + vertxNext = vertx.runOnLoop || vertx.runOnContext; + return useVertxTimer(); + } catch (e) { + return useSetTimeout(); + } +} + +var scheduleFlush = undefined; +// Decide what async method to use to triggering processing of queued callbacks: +if (isNode) { + scheduleFlush = useNextTick(); +} else if (BrowserMutationObserver) { + scheduleFlush = useMutationObserver(); +} else if (isWorker) { + scheduleFlush = useMessageChannel(); +} else if (browserWindow === undefined && "function" === 'function') { + scheduleFlush = attemptVertx(); +} else { + scheduleFlush = useSetTimeout(); +} + +function then(onFulfillment, onRejection) { + var _arguments = arguments; + + var parent = this; + + var child = new this.constructor(noop); + + if (child[PROMISE_ID] === undefined) { + makePromise(child); + } + + var _state = parent._state; + + if (_state) { + (function () { + var callback = _arguments[_state - 1]; + asap(function () { + return invokeCallback(_state, child, callback, parent._result); + }); + })(); + } else { + subscribe(parent, child, onFulfillment, onRejection); + } + + return child; +} + +/** + `Promise.resolve` returns a promise that will become resolved with the + passed `value`. It is shorthand for the following: + + ```javascript + let promise = new Promise(function(resolve, reject){ + resolve(1); + }); + + promise.then(function(value){ + // value === 1 + }); + ``` + + Instead of writing the above, your code now simply becomes the following: + + ```javascript + let promise = Promise.resolve(1); + + promise.then(function(value){ + // value === 1 + }); + ``` + + @method resolve + @static + @param {Any} value value that the returned promise will be resolved with + Useful for tooling. + @return {Promise} a promise that will become fulfilled with the given + `value` +*/ +function resolve(object) { + /*jshint validthis:true */ + var Constructor = this; + + if (object && typeof object === 'object' && object.constructor === Constructor) { + return object; + } + + var promise = new Constructor(noop); + _resolve(promise, object); + return promise; +} + +var PROMISE_ID = Math.random().toString(36).substring(16); + +function noop() {} + +var PENDING = void 0; +var FULFILLED = 1; +var REJECTED = 2; + +var GET_THEN_ERROR = new ErrorObject(); + +function selfFulfillment() { + return new TypeError("You cannot resolve a promise with itself"); +} + +function cannotReturnOwn() { + return new TypeError('A promises callback cannot return that same promise.'); +} + +function getThen(promise) { + try { + return promise.then; + } catch (error) { + GET_THEN_ERROR.error = error; + return GET_THEN_ERROR; + } +} + +function tryThen(then, value, fulfillmentHandler, rejectionHandler) { + try { + then.call(value, fulfillmentHandler, rejectionHandler); + } catch (e) { + return e; + } +} + +function handleForeignThenable(promise, thenable, then) { + asap(function (promise) { + var sealed = false; + var error = tryThen(then, thenable, function (value) { + if (sealed) { + return; + } + sealed = true; + if (thenable !== value) { + _resolve(promise, value); + } else { + fulfill(promise, value); + } + }, function (reason) { + if (sealed) { + return; + } + sealed = true; + + _reject(promise, reason); + }, 'Settle: ' + (promise._label || ' unknown promise')); + + if (!sealed && error) { + sealed = true; + _reject(promise, error); + } + }, promise); +} + +function handleOwnThenable(promise, thenable) { + if (thenable._state === FULFILLED) { + fulfill(promise, thenable._result); + } else if (thenable._state === REJECTED) { + _reject(promise, thenable._result); + } else { + subscribe(thenable, undefined, function (value) { + return _resolve(promise, value); + }, function (reason) { + return _reject(promise, reason); + }); + } +} + +function handleMaybeThenable(promise, maybeThenable, then$$) { + if (maybeThenable.constructor === promise.constructor && then$$ === then && maybeThenable.constructor.resolve === resolve) { + handleOwnThenable(promise, maybeThenable); + } else { + if (then$$ === GET_THEN_ERROR) { + _reject(promise, GET_THEN_ERROR.error); + GET_THEN_ERROR.error = null; + } else if (then$$ === undefined) { + fulfill(promise, maybeThenable); + } else if (isFunction(then$$)) { + handleForeignThenable(promise, maybeThenable, then$$); + } else { + fulfill(promise, maybeThenable); + } + } +} + +function _resolve(promise, value) { + if (promise === value) { + _reject(promise, selfFulfillment()); + } else if (objectOrFunction(value)) { + handleMaybeThenable(promise, value, getThen(value)); + } else { + fulfill(promise, value); + } +} + +function publishRejection(promise) { + if (promise._onerror) { + promise._onerror(promise._result); + } + + publish(promise); +} + +function fulfill(promise, value) { + if (promise._state !== PENDING) { + return; + } + + promise._result = value; + promise._state = FULFILLED; + + if (promise._subscribers.length !== 0) { + asap(publish, promise); + } +} + +function _reject(promise, reason) { + if (promise._state !== PENDING) { + return; + } + promise._state = REJECTED; + promise._result = reason; + + asap(publishRejection, promise); +} + +function subscribe(parent, child, onFulfillment, onRejection) { + var _subscribers = parent._subscribers; + var length = _subscribers.length; + + parent._onerror = null; + + _subscribers[length] = child; + _subscribers[length + FULFILLED] = onFulfillment; + _subscribers[length + REJECTED] = onRejection; + + if (length === 0 && parent._state) { + asap(publish, parent); + } +} + +function publish(promise) { + var subscribers = promise._subscribers; + var settled = promise._state; + + if (subscribers.length === 0) { + return; + } + + var child = undefined, + callback = undefined, + detail = promise._result; + + for (var i = 0; i < subscribers.length; i += 3) { + child = subscribers[i]; + callback = subscribers[i + settled]; + + if (child) { + invokeCallback(settled, child, callback, detail); + } else { + callback(detail); + } + } + + promise._subscribers.length = 0; +} + +function ErrorObject() { + this.error = null; +} + +var TRY_CATCH_ERROR = new ErrorObject(); + +function tryCatch(callback, detail) { + try { + return callback(detail); + } catch (e) { + TRY_CATCH_ERROR.error = e; + return TRY_CATCH_ERROR; + } +} + +function invokeCallback(settled, promise, callback, detail) { + var hasCallback = isFunction(callback), + value = undefined, + error = undefined, + succeeded = undefined, + failed = undefined; + + if (hasCallback) { + value = tryCatch(callback, detail); + + if (value === TRY_CATCH_ERROR) { + failed = true; + error = value.error; + value.error = null; + } else { + succeeded = true; + } + + if (promise === value) { + _reject(promise, cannotReturnOwn()); + return; + } + } else { + value = detail; + succeeded = true; + } + + if (promise._state !== PENDING) { + // noop + } else if (hasCallback && succeeded) { + _resolve(promise, value); + } else if (failed) { + _reject(promise, error); + } else if (settled === FULFILLED) { + fulfill(promise, value); + } else if (settled === REJECTED) { + _reject(promise, value); + } +} + +function initializePromise(promise, resolver) { + try { + resolver(function resolvePromise(value) { + _resolve(promise, value); + }, function rejectPromise(reason) { + _reject(promise, reason); + }); + } catch (e) { + _reject(promise, e); + } +} + +var id = 0; +function nextId() { + return id++; +} + +function makePromise(promise) { + promise[PROMISE_ID] = id++; + promise._state = undefined; + promise._result = undefined; + promise._subscribers = []; +} + +function Enumerator(Constructor, input) { + this._instanceConstructor = Constructor; + this.promise = new Constructor(noop); + + if (!this.promise[PROMISE_ID]) { + makePromise(this.promise); + } + + if (isArray(input)) { + this._input = input; + this.length = input.length; + this._remaining = input.length; + + this._result = new Array(this.length); + + if (this.length === 0) { + fulfill(this.promise, this._result); + } else { + this.length = this.length || 0; + this._enumerate(); + if (this._remaining === 0) { + fulfill(this.promise, this._result); + } + } + } else { + _reject(this.promise, validationError()); + } +} + +function validationError() { + return new Error('Array Methods must be provided an Array'); +}; + +Enumerator.prototype._enumerate = function () { + var length = this.length; + var _input = this._input; + + for (var i = 0; this._state === PENDING && i < length; i++) { + this._eachEntry(_input[i], i); + } +}; + +Enumerator.prototype._eachEntry = function (entry, i) { + var c = this._instanceConstructor; + var resolve$$ = c.resolve; + + if (resolve$$ === resolve) { + var _then = getThen(entry); + + if (_then === then && entry._state !== PENDING) { + this._settledAt(entry._state, i, entry._result); + } else if (typeof _then !== 'function') { + this._remaining--; + this._result[i] = entry; + } else if (c === Promise) { + var promise = new c(noop); + handleMaybeThenable(promise, entry, _then); + this._willSettleAt(promise, i); + } else { + this._willSettleAt(new c(function (resolve$$) { + return resolve$$(entry); + }), i); + } + } else { + this._willSettleAt(resolve$$(entry), i); + } +}; + +Enumerator.prototype._settledAt = function (state, i, value) { + var promise = this.promise; + + if (promise._state === PENDING) { + this._remaining--; + + if (state === REJECTED) { + _reject(promise, value); + } else { + this._result[i] = value; + } + } + + if (this._remaining === 0) { + fulfill(promise, this._result); + } +}; + +Enumerator.prototype._willSettleAt = function (promise, i) { + var enumerator = this; + + subscribe(promise, undefined, function (value) { + return enumerator._settledAt(FULFILLED, i, value); + }, function (reason) { + return enumerator._settledAt(REJECTED, i, reason); + }); +}; + +/** + `Promise.all` accepts an array of promises, and returns a new promise which + is fulfilled with an array of fulfillment values for the passed promises, or + rejected with the reason of the first passed promise to be rejected. It casts all + elements of the passed iterable to promises as it runs this algorithm. + + Example: + + ```javascript + let promise1 = resolve(1); + let promise2 = resolve(2); + let promise3 = resolve(3); + let promises = [ promise1, promise2, promise3 ]; + + Promise.all(promises).then(function(array){ + // The array here would be [ 1, 2, 3 ]; + }); + ``` + + If any of the `promises` given to `all` are rejected, the first promise + that is rejected will be given as an argument to the returned promises's + rejection handler. For example: + + Example: + + ```javascript + let promise1 = resolve(1); + let promise2 = reject(new Error("2")); + let promise3 = reject(new Error("3")); + let promises = [ promise1, promise2, promise3 ]; + + Promise.all(promises).then(function(array){ + // Code here never runs because there are rejected promises! + }, function(error) { + // error.message === "2" + }); + ``` + + @method all + @static + @param {Array} entries array of promises + @param {String} label optional string for labeling the promise. + Useful for tooling. + @return {Promise} promise that is fulfilled when all `promises` have been + fulfilled, or rejected if any of them become rejected. + @static +*/ +function all(entries) { + return new Enumerator(this, entries).promise; +} + +/** + `Promise.race` returns a new promise which is settled in the same way as the + first passed promise to settle. + + Example: + + ```javascript + let promise1 = new Promise(function(resolve, reject){ + setTimeout(function(){ + resolve('promise 1'); + }, 200); + }); + + let promise2 = new Promise(function(resolve, reject){ + setTimeout(function(){ + resolve('promise 2'); + }, 100); + }); + + Promise.race([promise1, promise2]).then(function(result){ + // result === 'promise 2' because it was resolved before promise1 + // was resolved. + }); + ``` + + `Promise.race` is deterministic in that only the state of the first + settled promise matters. For example, even if other promises given to the + `promises` array argument are resolved, but the first settled promise has + become rejected before the other promises became fulfilled, the returned + promise will become rejected: + + ```javascript + let promise1 = new Promise(function(resolve, reject){ + setTimeout(function(){ + resolve('promise 1'); + }, 200); + }); + + let promise2 = new Promise(function(resolve, reject){ + setTimeout(function(){ + reject(new Error('promise 2')); + }, 100); + }); + + Promise.race([promise1, promise2]).then(function(result){ + // Code here never runs + }, function(reason){ + // reason.message === 'promise 2' because promise 2 became rejected before + // promise 1 became fulfilled + }); + ``` + + An example real-world use case is implementing timeouts: + + ```javascript + Promise.race([ajax('foo.json'), timeout(5000)]) + ``` + + @method race + @static + @param {Array} promises array of promises to observe + Useful for tooling. + @return {Promise} a promise which settles in the same way as the first passed + promise to settle. +*/ +function race(entries) { + /*jshint validthis:true */ + var Constructor = this; + + if (!isArray(entries)) { + return new Constructor(function (_, reject) { + return reject(new TypeError('You must pass an array to race.')); + }); + } else { + return new Constructor(function (resolve, reject) { + var length = entries.length; + for (var i = 0; i < length; i++) { + Constructor.resolve(entries[i]).then(resolve, reject); + } + }); + } +} + +/** + `Promise.reject` returns a promise rejected with the passed `reason`. + It is shorthand for the following: + + ```javascript + let promise = new Promise(function(resolve, reject){ + reject(new Error('WHOOPS')); + }); + + promise.then(function(value){ + // Code here doesn't run because the promise is rejected! + }, function(reason){ + // reason.message === 'WHOOPS' + }); + ``` + + Instead of writing the above, your code now simply becomes the following: + + ```javascript + let promise = Promise.reject(new Error('WHOOPS')); + + promise.then(function(value){ + // Code here doesn't run because the promise is rejected! + }, function(reason){ + // reason.message === 'WHOOPS' + }); + ``` + + @method reject + @static + @param {Any} reason value that the returned promise will be rejected with. + Useful for tooling. + @return {Promise} a promise rejected with the given `reason`. +*/ +function reject(reason) { + /*jshint validthis:true */ + var Constructor = this; + var promise = new Constructor(noop); + _reject(promise, reason); + return promise; +} + +function needsResolver() { + throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); +} + +function needsNew() { + throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); +} + +/** + Promise objects represent the eventual result of an asynchronous operation. The + primary way of interacting with a promise is through its `then` method, which + registers callbacks to receive either a promise's eventual value or the reason + why the promise cannot be fulfilled. + + Terminology + ----------- + + - `promise` is an object or function with a `then` method whose behavior conforms to this specification. + - `thenable` is an object or function that defines a `then` method. + - `value` is any legal JavaScript value (including undefined, a thenable, or a promise). + - `exception` is a value that is thrown using the throw statement. + - `reason` is a value that indicates why a promise was rejected. + - `settled` the final resting state of a promise, fulfilled or rejected. + + A promise can be in one of three states: pending, fulfilled, or rejected. + + Promises that are fulfilled have a fulfillment value and are in the fulfilled + state. Promises that are rejected have a rejection reason and are in the + rejected state. A fulfillment value is never a thenable. + + Promises can also be said to *resolve* a value. If this value is also a + promise, then the original promise's settled state will match the value's + settled state. So a promise that *resolves* a promise that rejects will + itself reject, and a promise that *resolves* a promise that fulfills will + itself fulfill. + + + Basic Usage: + ------------ + + ```js + let promise = new Promise(function(resolve, reject) { + // on success + resolve(value); + + // on failure + reject(reason); + }); + + promise.then(function(value) { + // on fulfillment + }, function(reason) { + // on rejection + }); + ``` + + Advanced Usage: + --------------- + + Promises shine when abstracting away asynchronous interactions such as + `XMLHttpRequest`s. + + ```js + function getJSON(url) { + return new Promise(function(resolve, reject){ + let xhr = new XMLHttpRequest(); + + xhr.open('GET', url); + xhr.onreadystatechange = handler; + xhr.responseType = 'json'; + xhr.setRequestHeader('Accept', 'application/json'); + xhr.send(); + + function handler() { + if (this.readyState === this.DONE) { + if (this.status === 200) { + resolve(this.response); + } else { + reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); + } + } + }; + }); + } + + getJSON('/posts.json').then(function(json) { + // on fulfillment + }, function(reason) { + // on rejection + }); + ``` + + Unlike callbacks, promises are great composable primitives. + + ```js + Promise.all([ + getJSON('/posts'), + getJSON('/comments') + ]).then(function(values){ + values[0] // => postsJSON + values[1] // => commentsJSON + + return values; + }); + ``` + + @class Promise + @param {function} resolver + Useful for tooling. + @constructor +*/ +function Promise(resolver) { + this[PROMISE_ID] = nextId(); + this._result = this._state = undefined; + this._subscribers = []; + + if (noop !== resolver) { + typeof resolver !== 'function' && needsResolver(); + this instanceof Promise ? initializePromise(this, resolver) : needsNew(); + } +} + +Promise.all = all; +Promise.race = race; +Promise.resolve = resolve; +Promise.reject = reject; +Promise._setScheduler = setScheduler; +Promise._setAsap = setAsap; +Promise._asap = asap; + +Promise.prototype = { + constructor: Promise, + + /** + The primary way of interacting with a promise is through its `then` method, + which registers callbacks to receive either a promise's eventual value or the + reason why the promise cannot be fulfilled. + + ```js + findUser().then(function(user){ + // user is available + }, function(reason){ + // user is unavailable, and you are given the reason why + }); + ``` + + Chaining + -------- + + The return value of `then` is itself a promise. This second, 'downstream' + promise is resolved with the return value of the first promise's fulfillment + or rejection handler, or rejected if the handler throws an exception. + + ```js + findUser().then(function (user) { + return user.name; + }, function (reason) { + return 'default name'; + }).then(function (userName) { + // If `findUser` fulfilled, `userName` will be the user's name, otherwise it + // will be `'default name'` + }); + + findUser().then(function (user) { + throw new Error('Found user, but still unhappy'); + }, function (reason) { + throw new Error('`findUser` rejected and we're unhappy'); + }).then(function (value) { + // never reached + }, function (reason) { + // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'. + // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'. + }); + ``` + If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. + + ```js + findUser().then(function (user) { + throw new PedagogicalException('Upstream error'); + }).then(function (value) { + // never reached + }).then(function (value) { + // never reached + }, function (reason) { + // The `PedgagocialException` is propagated all the way down to here + }); + ``` + + Assimilation + ------------ + + Sometimes the value you want to propagate to a downstream promise can only be + retrieved asynchronously. This can be achieved by returning a promise in the + fulfillment or rejection handler. The downstream promise will then be pending + until the returned promise is settled. This is called *assimilation*. + + ```js + findUser().then(function (user) { + return findCommentsByAuthor(user); + }).then(function (comments) { + // The user's comments are now available + }); + ``` + + If the assimliated promise rejects, then the downstream promise will also reject. + + ```js + findUser().then(function (user) { + return findCommentsByAuthor(user); + }).then(function (comments) { + // If `findCommentsByAuthor` fulfills, we'll have the value here + }, function (reason) { + // If `findCommentsByAuthor` rejects, we'll have the reason here + }); + ``` + + Simple Example + -------------- + + Synchronous Example + + ```javascript + let result; + + try { + result = findResult(); + // success + } catch(reason) { + // failure + } + ``` + + Errback Example + + ```js + findResult(function(result, err){ + if (err) { + // failure + } else { + // success + } + }); + ``` + + Promise Example; + + ```javascript + findResult().then(function(result){ + // success + }, function(reason){ + // failure + }); + ``` + + Advanced Example + -------------- + + Synchronous Example + + ```javascript + let author, books; + + try { + author = findAuthor(); + books = findBooksByAuthor(author); + // success + } catch(reason) { + // failure + } + ``` + + Errback Example + + ```js + + function foundBooks(books) { + + } + + function failure(reason) { + + } + + findAuthor(function(author, err){ + if (err) { + failure(err); + // failure + } else { + try { + findBoooksByAuthor(author, function(books, err) { + if (err) { + failure(err); + } else { + try { + foundBooks(books); + } catch(reason) { + failure(reason); + } + } + }); + } catch(error) { + failure(err); + } + // success + } + }); + ``` + + Promise Example; + + ```javascript + findAuthor(). + then(findBooksByAuthor). + then(function(books){ + // found books + }).catch(function(reason){ + // something went wrong + }); + ``` + + @method then + @param {Function} onFulfilled + @param {Function} onRejected + Useful for tooling. + @return {Promise} + */ + then: then, + + /** + `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same + as the catch block of a try/catch statement. + + ```js + function findAuthor(){ + throw new Error('couldn't find that author'); + } + + // synchronous + try { + findAuthor(); + } catch(reason) { + // something went wrong + } + + // async with promises + findAuthor().catch(function(reason){ + // something went wrong + }); + ``` + + @method catch + @param {Function} onRejection + Useful for tooling. + @return {Promise} + */ + 'catch': function _catch(onRejection) { + return this.then(null, onRejection); + } +}; + +function polyfill() { + var local = undefined; + + if (typeof global !== 'undefined') { + local = global; + } else if (typeof self !== 'undefined') { + local = self; + } else { + try { + local = Function('return this')(); + } catch (e) { + throw new Error('polyfill failed because global object is unavailable in this environment'); + } + } + + var P = local.Promise; + + if (P) { + var promiseToString = null; + try { + promiseToString = Object.prototype.toString.call(P.resolve()); + } catch (e) { + // silently ignored + } + + if (promiseToString === '[object Promise]' && !P.cast) { + return; + } + } + + local.Promise = Promise; +} + +// Strange compat.. +Promise.polyfill = polyfill; +Promise.Promise = Promise; + +return Promise; + +}))); +//# sourceMappingURL=es6-promise.map + +/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(7), __webpack_require__(8))) + +/***/ }), +/* 5 */ +/***/ (function(module, exports) { + +// removed by extract-text-webpack-plugin + +/***/ }), +/* 6 */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /* global VERSION */ + +__webpack_require__(5); + +var _es6Promise = __webpack_require__(4); + +var _es6Promise2 = _interopRequireDefault(_es6Promise); + +var _utils = __webpack_require__(0); + +var Utils = _interopRequireWildcard(_utils); + +var _api = __webpack_require__(1); + +var API = _interopRequireWildcard(_api); + +var _button = __webpack_require__(2); + +var _push = __webpack_require__(3); + +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var Noty = function () { + /** + * @param {object} options + * @return {Noty} + */ + function Noty() { + var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; + + _classCallCheck(this, Noty); + + this.options = Utils.deepExtend({}, API.Defaults, options); + this.id = this.options.id || Utils.generateID('bar'); + this.closeTimer = -1; + this.barDom = null; + this.layoutDom = null; + this.progressDom = null; + this.showing = false; + this.shown = false; + this.closed = false; + this.closing = false; + this.killable = this.options.timeout || this.options.closeWith.length > 0; + this.hasSound = this.options.sounds.sources.length > 0; + this.soundPlayed = false; + this.listeners = { + beforeShow: [], + onShow: [], + afterShow: [], + onClose: [], + afterClose: [], + onHover: [], + onTemplate: [] + }; + this.promises = { + show: null, + close: null + }; + this.on('beforeShow', this.options.callbacks.beforeShow); + this.on('onShow', this.options.callbacks.onShow); + this.on('afterShow', this.options.callbacks.afterShow); + this.on('onClose', this.options.callbacks.onClose); + this.on('afterClose', this.options.callbacks.afterClose); + this.on('onHover', this.options.callbacks.onHover); + this.on('onTemplate', this.options.callbacks.onTemplate); + + return this; + } + + /** + * @param {string} eventName + * @param {function} cb + * @return {Noty} + */ + + + _createClass(Noty, [{ + key: 'on', + value: function on(eventName) { + var cb = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {}; + + if (typeof cb === 'function' && this.listeners.hasOwnProperty(eventName)) { + this.listeners[eventName].push(cb); + } + + return this; + } + + /** + * @return {Noty} + */ + + }, { + key: 'show', + value: function show() { + var _this = this; + + if (this.options.killer === true && !API.PageHidden) { + Noty.closeAll(); + } else if (typeof this.options.killer === 'string' && !API.PageHidden) { + Noty.closeAll(this.options.killer); + } else { + var queueCounts = API.getQueueCounts(this.options.queue); + + if (queueCounts.current >= queueCounts.maxVisible || API.PageHidden) { + API.addToQueue(this); + + if (API.PageHidden && this.hasSound && Utils.inArray('docHidden', this.options.sounds.conditions)) { + Utils.createAudioElements(this); + } + + if (API.PageHidden && Utils.inArray('docHidden', this.options.titleCount.conditions)) { + API.docTitle.increment(); + } + + return this; + } + } + + API.Store[this.id] = this; + + API.fire(this, 'beforeShow'); + + this.showing = true; + + if (this.closing) { + this.showing = false; + return this; + } + + API.build(this); + API.handleModal(this); + + if (this.options.force) { + this.layoutDom.insertBefore(this.barDom, this.layoutDom.firstChild); + } else { + this.layoutDom.appendChild(this.barDom); + } + + if (this.hasSound && !this.soundPlayed && Utils.inArray('docVisible', this.options.sounds.conditions)) { + Utils.createAudioElements(this); + } + + if (Utils.inArray('docVisible', this.options.titleCount.conditions)) { + API.docTitle.increment(); + } + + this.shown = true; + this.closed = false; + + // bind button events if any + if (API.hasButtons(this)) { + Object.keys(this.options.buttons).forEach(function (key) { + var btn = _this.barDom.querySelector('#' + _this.options.buttons[key].id); + Utils.addListener(btn, 'click', function (e) { + Utils.stopPropagation(e); + _this.options.buttons[key].cb(); + }); + }); + } + + this.progressDom = this.barDom.querySelector('.noty_progressbar'); + + if (Utils.inArray('click', this.options.closeWith)) { + Utils.addClass(this.barDom, 'noty_close_with_click'); + Utils.addListener(this.barDom, 'click', function (e) { + Utils.stopPropagation(e); + _this.close(); + }, false); + } + + Utils.addListener(this.barDom, 'mouseenter', function () { + API.fire(_this, 'onHover'); + }, false); + + if (this.options.timeout) Utils.addClass(this.barDom, 'noty_has_timeout'); + + if (Utils.inArray('button', this.options.closeWith)) { + Utils.addClass(this.barDom, 'noty_close_with_button'); + + var closeButton = document.createElement('div'); + Utils.addClass(closeButton, 'noty_close_button'); + closeButton.innerHTML = '×'; + this.barDom.appendChild(closeButton); + + Utils.addListener(closeButton, 'click', function (e) { + Utils.stopPropagation(e); + _this.close(); + }, false); + } + + API.fire(this, 'onShow'); + + if (this.options.animation.open === null) { + this.promises.show = new _es6Promise2.default(function (resolve) { + resolve(); + }); + } else if (typeof this.options.animation.open === 'function') { + this.promises.show = new _es6Promise2.default(this.options.animation.open.bind(this)); + } else { + Utils.addClass(this.barDom, this.options.animation.open); + this.promises.show = new _es6Promise2.default(function (resolve) { + Utils.addListener(_this.barDom, Utils.animationEndEvents, function () { + Utils.removeClass(_this.barDom, _this.options.animation.open); + resolve(); + }); + }); + } + + this.promises.show.then(function () { + var _t = _this; + setTimeout(function () { + API.openFlow(_t); + }, 100); + }); + + return this; + } + + /** + * @return {Noty} + */ + + }, { + key: 'stop', + value: function stop() { + API.dequeueClose(this); + return this; + } + + /** + * @return {Noty} + */ + + }, { + key: 'resume', + value: function resume() { + API.queueClose(this); + return this; + } + + /** + * @param {int|boolean} ms + * @return {Noty} + */ + + }, { + key: 'setTimeout', + value: function (_setTimeout) { + function setTimeout(_x) { + return _setTimeout.apply(this, arguments); + } + + setTimeout.toString = function () { + return _setTimeout.toString(); + }; + + return setTimeout; + }(function (ms) { + this.stop(); + this.options.timeout = ms; + + if (this.barDom) { + if (this.options.timeout) { + Utils.addClass(this.barDom, 'noty_has_timeout'); + } else { + Utils.removeClass(this.barDom, 'noty_has_timeout'); + } + + var _t = this; + setTimeout(function () { + // ugly fix for progressbar display bug + _t.resume(); + }, 100); + } + + return this; + }) + + /** + * @param {string} html + * @param {boolean} optionsOverride + * @return {Noty} + */ + + }, { + key: 'setText', + value: function setText(html) { + var optionsOverride = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + if (this.barDom) { + this.barDom.querySelector('.noty_body').innerHTML = html; + } + + if (optionsOverride) this.options.text = html; + + return this; + } + + /** + * @param {string} type + * @param {boolean} optionsOverride + * @return {Noty} + */ + + }, { + key: 'setType', + value: function setType(type) { + var _this2 = this; + + var optionsOverride = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + if (this.barDom) { + var classList = Utils.classList(this.barDom).split(' '); + + classList.forEach(function (c) { + if (c.substring(0, 11) === 'noty_type__') { + Utils.removeClass(_this2.barDom, c); + } + }); + + Utils.addClass(this.barDom, 'noty_type__' + type); + } + + if (optionsOverride) this.options.type = type; + + return this; + } + + /** + * @param {string} theme + * @param {boolean} optionsOverride + * @return {Noty} + */ + + }, { + key: 'setTheme', + value: function setTheme(theme) { + var _this3 = this; + + var optionsOverride = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + if (this.barDom) { + var classList = Utils.classList(this.barDom).split(' '); + + classList.forEach(function (c) { + if (c.substring(0, 12) === 'noty_theme__') { + Utils.removeClass(_this3.barDom, c); + } + }); + + Utils.addClass(this.barDom, 'noty_theme__' + theme); + } + + if (optionsOverride) this.options.theme = theme; + + return this; + } + + /** + * @return {Noty} + */ + + }, { + key: 'close', + value: function close() { + var _this4 = this; + + if (this.closed) return this; + + if (!this.shown) { + // it's in the queue + API.removeFromQueue(this); + return this; + } + + API.fire(this, 'onClose'); + + this.closing = true; + + if (this.options.animation.close === null) { + this.promises.close = new _es6Promise2.default(function (resolve) { + resolve(); + }); + } else if (typeof this.options.animation.close === 'function') { + this.promises.close = new _es6Promise2.default(this.options.animation.close.bind(this)); + } else { + Utils.addClass(this.barDom, this.options.animation.close); + this.promises.close = new _es6Promise2.default(function (resolve) { + Utils.addListener(_this4.barDom, Utils.animationEndEvents, function () { + if (_this4.options.force) { + Utils.remove(_this4.barDom); + } else { + API.ghostFix(_this4); + } + resolve(); + }); + }); + } + + this.promises.close.then(function () { + API.closeFlow(_this4); + API.handleModalClose(_this4); + }); + + this.closed = true; + + return this; + } + + // API functions + + /** + * @param {boolean|string} queueName + * @return {Noty} + */ + + }], [{ + key: 'closeAll', + value: function closeAll() { + var queueName = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; + + Object.keys(API.Store).forEach(function (id) { + if (queueName) { + if (API.Store[id].options.queue === queueName && API.Store[id].killable) { + API.Store[id].close(); + } + } else if (API.Store[id].killable) { + API.Store[id].close(); + } + }); + return this; + } + + /** + * @param {Object} obj + * @return {Noty} + */ + + }, { + key: 'overrideDefaults', + value: function overrideDefaults(obj) { + API.Defaults = Utils.deepExtend({}, API.Defaults, obj); + return this; + } + + /** + * @param {int} amount + * @param {string} queueName + * @return {Noty} + */ + + }, { + key: 'setMaxVisible', + value: function setMaxVisible() { + var amount = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : API.DefaultMaxVisible; + var queueName = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'global'; + + if (!API.Queues.hasOwnProperty(queueName)) { + API.Queues[queueName] = { maxVisible: amount, queue: [] }; + } + + API.Queues[queueName].maxVisible = amount; + return this; + } + + /** + * @param {string} innerHtml + * @param {String} classes + * @param {Function} cb + * @param {Object} attributes + * @return {NotyButton} + */ + + }, { + key: 'button', + value: function button(innerHtml) { + var classes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null; + var cb = arguments[2]; + var attributes = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; + + return new _button.NotyButton(innerHtml, classes, cb, attributes); + } + + /** + * @return {string} + */ + + }, { + key: 'version', + value: function version() { + return "3.1.0"; + } + + /** + * @param {String} workerPath + * @return {Push} + */ + + }, { + key: 'Push', + value: function Push(workerPath) { + return new _push.Push(workerPath); + } + }]); + + return Noty; +}(); + +// Document visibility change controller + + +exports.default = Noty; +Utils.visibilityChangeFlow(); +module.exports = exports['default']; + +/***/ }), +/* 7 */ +/***/ (function(module, exports) { + +// shim for using process in browser +var process = module.exports = {}; + +// cached from whatever global is present so that test runners that stub it +// don't break things. But we need to wrap it in a try catch in case it is +// wrapped in strict mode code which doesn't define any globals. It's inside a +// function because try/catches deoptimize in certain engines. + +var cachedSetTimeout; +var cachedClearTimeout; + +function defaultSetTimout() { + throw new Error('setTimeout has not been defined'); +} +function defaultClearTimeout () { + throw new Error('clearTimeout has not been defined'); +} +(function () { + try { + if (typeof setTimeout === 'function') { + cachedSetTimeout = setTimeout; + } else { + cachedSetTimeout = defaultSetTimout; + } + } catch (e) { + cachedSetTimeout = defaultSetTimout; + } + try { + if (typeof clearTimeout === 'function') { + cachedClearTimeout = clearTimeout; + } else { + cachedClearTimeout = defaultClearTimeout; + } + } catch (e) { + cachedClearTimeout = defaultClearTimeout; + } +} ()) +function runTimeout(fun) { + if (cachedSetTimeout === setTimeout) { + //normal enviroments in sane situations + return setTimeout(fun, 0); + } + // if setTimeout wasn't available but was latter defined + if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { + cachedSetTimeout = setTimeout; + return setTimeout(fun, 0); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedSetTimeout(fun, 0); + } catch(e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedSetTimeout.call(null, fun, 0); + } catch(e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error + return cachedSetTimeout.call(this, fun, 0); + } + } + + +} +function runClearTimeout(marker) { + if (cachedClearTimeout === clearTimeout) { + //normal enviroments in sane situations + return clearTimeout(marker); + } + // if clearTimeout wasn't available but was latter defined + if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { + cachedClearTimeout = clearTimeout; + return clearTimeout(marker); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedClearTimeout(marker); + } catch (e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedClearTimeout.call(null, marker); + } catch (e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. + // Some versions of I.E. have different rules for clearTimeout vs setTimeout + return cachedClearTimeout.call(this, marker); + } + } + + + +} +var queue = []; +var draining = false; +var currentQueue; +var queueIndex = -1; + +function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } +} + +function drainQueue() { + if (draining) { + return; + } + var timeout = runTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + runClearTimeout(timeout); +} + +process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } +}; + +// v8 likes predictible objects +function Item(fun, array) { + this.fun = fun; + this.array = array; +} +Item.prototype.run = function () { + this.fun.apply(null, this.array); +}; +process.title = 'browser'; +process.browser = true; +process.env = {}; +process.argv = []; +process.version = ''; // empty string to avoid regexp issues +process.versions = {}; + +function noop() {} + +process.on = noop; +process.addListener = noop; +process.once = noop; +process.off = noop; +process.removeListener = noop; +process.removeAllListeners = noop; +process.emit = noop; + +process.binding = function (name) { + throw new Error('process.binding is not supported'); +}; + +process.cwd = function () { return '/' }; +process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); +}; +process.umask = function() { return 0; }; + + +/***/ }), +/* 8 */ +/***/ (function(module, exports) { + +var g; + +// This works in non-strict mode +g = (function() { + return this; +})(); + +try { + // This works if eval is allowed (see CSP) + g = g || Function("return this")() || (1,eval)("this"); +} catch(e) { + // This works if the window reference is available + if(typeof window === "object") + g = window; +} + +// g can still be undefined, but nothing to do about it... +// We return undefined, instead of nothing here, so it's +// easier to handle this case. if(!global) { ...} + +module.exports = g; + + +/***/ }), +/* 9 */ +/***/ (function(module, exports) { + +/* (ignored) */ + +/***/ }) +/******/ ]); +}); +//# sourceMappingURL=noty.js.map \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/pNotify/html/pNotify.js b/server-data/resources/[esx_addons]/pNotify/html/pNotify.js new file mode 100644 index 000000000..5fd44b55a --- /dev/null +++ b/server-data/resources/[esx_addons]/pNotify/html/pNotify.js @@ -0,0 +1,11 @@ +$(function(){ + window.addEventListener("message", function(event){ + if(event.data.options){ + var options = event.data.options; + new Noty(options).show(); + }else{ + var maxNotifications = event.data.maxNotifications; + Noty.setMaxVisible(maxNotifications.max, maxNotifications.queue); + }; + }); +}); \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/pNotify/html/themes.css b/server-data/resources/[esx_addons]/pNotify/html/themes.css new file mode 100644 index 000000000..a697b97b7 --- /dev/null +++ b/server-data/resources/[esx_addons]/pNotify/html/themes.css @@ -0,0 +1,131 @@ +.noty_theme__gta.noty_bar { + margin: 4px 0; + overflow: hidden; + border-radius: 1px; + font-family: sans-serif; + position: relative; + height: auto; + word-wrap: break-word; +} + +.noty_theme__gta.noty_bar .noty_body { + padding: 8px; + font-size: 14px; +} + +.noty_theme__gta.noty_bar .noty_buttons { + padding: 0px; +} + +.noty_theme__gta.noty_bar .noty_progressbar { + position: absolute; + left: 0; + bottom: 0; + height: 1px; + width: 100%; + background-color: white; + opacity: 0.8; + filter: alpha(opacity=80); +} + +.noty_theme__gta.noty_type__alert, +.noty_theme__gta.noty_type__notification { + background-color: rgb(40, 40, 40); + border-top: 2px solid #D1D1D1; + color: white; +} + +.noty_theme__gta.noty_type__warning { + background-color: rgb(40, 40, 40); + border-top: 2px solid #E89F3C; + color: white; +} + +.noty_theme__gta.noty_type__error { + background-color: rgb(40, 40, 40); + border-top: 2px solid #CA5A65; + color: #fff; +} + +.noty_theme__gta.noty_type__info, +.noty_theme__gta.noty_type__information { + background-color: rgb(40, 40, 40); + border-top: 2px solid #7473E8; + color: #fff; +} + +.noty_theme__gta.noty_type__success { + background-color: rgb(40, 40, 40); + border-top: 2px solid #A0B55C; + color: #fff; +} + +.gta_effects_open { + opacity: 0; + -webkit-transform: translate(50%); + -ms-transform: translate(50%); + transform: translate(50%); + -webkit-animation: noty_anim_in 0.5s cubic-bezier(0.215, 0.61, 0.355, 1); + animation: noty_anim_in 0.5s cubic-bezier(0.215, 0.61, 0.355, 1); + -webkit-animation-fill-mode: forwards; + animation-fill-mode: forwards; +} + +.gta_effects_close { + -webkit-animation: noty_anim_out 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55); + animation: noty_anim_out 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55); + -webkit-animation-fill-mode: forwards; + animation-fill-mode: forwards; +} + +@-webkit-keyframes noty_anim_out_left { + 100% { + -webkit-transform: translate(-50%); + transform: translate(-50%); + opacity: 0; } } + +@keyframes noty_anim_out_left { + 100% { + -webkit-transform: translate(-50%); + transform: translate(-50%); + opacity: 0; } } + +.gta_effects_open_left { + opacity: 0; + -webkit-transform: translate(-50%); + -ms-transform: translate(-50%); + transform: translate(-50%); + -webkit-animation: noty_anim_in 0.5s cubic-bezier(0.215, 0.61, 0.355, 1); + animation: noty_anim_in 0.5s cubic-bezier(0.215, 0.61, 0.355, 1); + -webkit-animation-fill-mode: forwards; + animation-fill-mode: forwards; +} + +.gta_effects_close_left { + -webkit-animation: noty_anim_out_left 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55); + animation: noty_anim_out_left 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55); + -webkit-animation-fill-mode: backwards; + animation-fill-mode: backwards; +} + +@-webkit-keyframes noty_anim_fade_in { + 100% { opacity: 1; } } + +@keyframes noty_anim_fade_in { + 100% { opacity: 1; } } + +@-webkit-keyframes noty_anim_fade_out { + 100% { opacity: 0; } } + +@keyframes noty_anim_fade_out { + 100% { opacity: 0; } } + +.gta_effects_fade_in { + opacity: 0; + animation: noty_anim_fade_in 0.5s; +} + +.gta_effects_fade_out { + opacity: 1; + animation: noty_anim_fade_out 0.5s; +} \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/seatbelt/fxmanifest.lua b/server-data/resources/[esx_addons]/seatbelt/fxmanifest.lua new file mode 100644 index 000000000..8d6c6855e --- /dev/null +++ b/server-data/resources/[esx_addons]/seatbelt/fxmanifest.lua @@ -0,0 +1,16 @@ +fx_version("adamant") +game("gta5") +author("bitpredator") +lua54("yes") +version("1.0.2") + +client_script "main.lua" + +ui_page 'html/ui.html' + +files { + 'html/ui.html', + 'html/app.js', + 'html/style.css', + 'img/seatbelt.png' +} \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/seatbelt/html/app.js b/server-data/resources/[esx_addons]/seatbelt/html/app.js new file mode 100644 index 000000000..1702342bc --- /dev/null +++ b/server-data/resources/[esx_addons]/seatbelt/html/app.js @@ -0,0 +1,26 @@ + +window.addEventListener('message', function(e) { + $("#container").stop(false, true); + if (e.data.displayWindow == 'true') { + $("#container").css('display', 'flex'); + + $("#container").animate({ + bottom: "25%", + opacity: "1.0" + }, + 700, function() { + + }); + + } else { + $("#container").animate({ + bottom: "-50%", + opacity: "0.0" + }, + 700, function() { + $("#container").css('display', 'none'); + + }); + } +}); + diff --git a/server-data/resources/[esx_addons]/seatbelt/html/style.css b/server-data/resources/[esx_addons]/seatbelt/html/style.css new file mode 100644 index 000000000..0d49af05a --- /dev/null +++ b/server-data/resources/[esx_addons]/seatbelt/html/style.css @@ -0,0 +1,91 @@ +html { + overflow: hidden; +} + +body { + margin: 0px; + padding: 0px; +} + +#container { + position: absolute; + /*top: 17vh;*/ + bottom: -100%; + left: 5%; /*25*/ + width: 80px; + height: 50px; + display: none; + flex-direction: column; +} + +header { + width: 80px; + height: 80px; + /*background-color: #a519ea;*/ + opacity: 0.8; + border-top-left-radius: 40px; + border-top-right-radius: 40px; + +} + +header img { + width: 60px; + height: 60px; + border-top-left-radius: 40px; + border-top-right-radius: 40px; +} + +/* Firefox old*/ +@-moz-keyframes blink { + 0% { + opacity:1; + } + 50% { + opacity:0; + } + 100% { + opacity:1; + } +} + +@-webkit-keyframes blink { + 0% { + opacity:1; + } + 50% { + opacity:0; + } + 100% { + opacity:1; + } +} +/* IE */ +@-ms-keyframes blink { + 0% { + opacity:1; + } + 50% { + opacity:0; + } + 100% { + opacity:1; + } +} +/* Opera and prob css3 final iteration */ +@keyframes blink { + 0% { + opacity:1; + } + 50% { + opacity:0; + } + 100% { + opacity:1; + } +} +.blink-image { + -moz-animation: blink normal 1s infinite ease-in-out; /* Firefox */ + -webkit-animation: blink normal 1s infinite ease-in-out; /* Webkit */ + -ms-animation: blink normal 1s infinite ease-in-out; /* IE */ + animation: blink normal 1s infinite ease-in-out; /* Opera and prob css3 final iteration */ +} \ No newline at end of file diff --git a/server-data/resources/[esx_addons]/seatbelt/html/ui.html b/server-data/resources/[esx_addons]/seatbelt/html/ui.html new file mode 100644 index 000000000..0b96dfe81 --- /dev/null +++ b/server-data/resources/[esx_addons]/seatbelt/html/ui.html @@ -0,0 +1,18 @@ + + + + + Document + + + + + +
+
+ header +
+
+ + + diff --git a/server-data/resources/[esx_addons]/seatbelt/img/seatbelt.png b/server-data/resources/[esx_addons]/seatbelt/img/seatbelt.png new file mode 100644 index 0000000000000000000000000000000000000000..08b51d747fa5316d4480ee17d8774e2728387f5a GIT binary patch literal 24990 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 literal 0 HcmV?d00001 diff --git a/server-data/resources/[esx_addons]/seatbelt/main.lua b/server-data/resources/[esx_addons]/seatbelt/main.lua new file mode 100644 index 000000000..f42c5e0e9 --- /dev/null +++ b/server-data/resources/[esx_addons]/seatbelt/main.lua @@ -0,0 +1,193 @@ +local isUiOpen = false +local speedBuffer = {} +local velBuffer = {} +local beltOn = false +local wasInCar = false +local notifIn = false +local notifOut = false + +function drawUI(x, y, width, height, scale, text, r, g, b, a, center) + SetTextFont(4) + SetTextProportional(0) + SetTextScale(scale, scale) + SetTextColour(r, g, b, a) + SetTextDropShadow(0, 0, 0, 0, 255) + SetTextEdge(1, 0, 0, 0, 255) + SetTextDropShadow() + SetTextCentre(center) + --SetTextOutline() + SetTextEntry("STRING") + AddTextComponentString(text) + DrawText(x - width / 2, y - height / 2 + 0.005) +end + +local function drawRect(x, y, width, height, color) + DrawRect(x, y, width, height, color.r, color.g, color.b, color.a) +end + +function IsCar(veh) + local vc = GetVehicleClass(veh) + return (vc >= 0 and vc <= 7) or (vc >= 9 and vc <= 12) or (vc >= 17 and vc <= 20) +end + +function Fwv(entity) + local hr = GetEntityHeading(entity) + 90.0 + if hr < 0.0 then + hr = 360.0 + hr + end + hr = hr * 0.0174533 + return { x = math.cos(hr) * 2.0, y = math.sin(hr) * 2.0 } +end + +Citizen.CreateThread(function() + while true do + Citizen.Wait(1500) + local ped = GetPlayerPed(-1) + local car = GetVehiclePedIsIn(ped) + + if IsPedSittingInAnyVehicle(ped) then + if not notifIn then + TriggerEvent("pNotify:SendNotification", { + text = "Premi B per allacciare la cintura", + type = "success", + queue = "global", + timeout = 4000, + layout = "bottomcenter", + close = "gta_effects_fade_out", + }) + notifIn = true + end + else + notifIn = false + end + + if car ~= 0 then + local speed = GetEntitySpeed(car) + if (speed >= 0 and speed <= 40) and not beltOn and IsCar(car) then + PlaySound(-1, "5_Second_Timer", "DLC_HEISTS_GENERAL_FRONTEND_SOUNDS", false, 0, true) + Citizen.Wait(500) + PlaySound(-1, "5_Second_Timer", "DLC_HEISTS_GENERAL_FRONTEND_SOUNDS", false, 0, true) + end + end + end +end) + +Citizen.CreateThread(function() + while true do + Citizen.Wait(0) + + local ped = GetPlayerPed(-1) + local car = GetVehiclePedIsIn(ped) + + if car ~= 0 and (wasInCar or IsCar(car)) then + wasInCar = true + if isUiOpen == false and not IsPlayerDead(PlayerId()) then + SendNUIMessage({ + displayWindow = "true", + }) + isUiOpen = true + end + + if beltOn then + DisableControlAction(0, 75) + end + + speedBuffer[2] = speedBuffer[1] + speedBuffer[1] = GetEntitySpeed(car) + + if speedBuffer[2] ~= nil and not beltOn and GetEntitySpeedVector(car, true).y > 1.0 and speedBuffer[1] > 19.25 and (speedBuffer[2] - speedBuffer[1]) > (speedBuffer[1] * 0.255) then + local co = GetEntityCoords(ped) + local fw = Fwv(ped) + SetEntityCoords(ped, co.x + fw.x, co.y + fw.y, co.z - 0.47, true, true, true) + SetEntityVelocity(ped, velBuffer[2].x, velBuffer[2].y, velBuffer[2].z) + Citizen.Wait(1) + SetPedToRagdoll(ped, 1000, 1000, 0, 0, 0, 0) + end + + velBuffer[2] = velBuffer[1] + velBuffer[1] = GetEntityVelocity(car) + local speed = GetEntitySpeed(car) + + if IsControlJustReleased(0, 29) and GetLastInputMethod(0) and speed < 10 then + beltOn = not beltOn + if beltOn then + ProgressBar("ALLACCIAMENTO CINTURA IN CORSO...", 30) + Wait(3750) + + TriggerEvent("pNotify:SendNotification", { text = "Cintura Allacciata", type = "success", timeout = 1400, layout = "centerLeft" }) + + SendNUIMessage({ + displayWindow = "false", + }) + isUiOpen = true + else + ProgressBar("SLACCIAMENTO CINTURA IN CORSO...", 30) + TriggerEvent("pNotify:SendNotification", { text = "Cintura Slacciata", type = "error", timeout = 1400, layout = "centerLeft" }) + + SendNUIMessage({ + displayWindow = "true", + }) + isUiOpen = true + end + end + elseif wasInCar then + wasInCar = false + beltOn = false + speedBuffer[1], speedBuffer[2] = 0.0, 0.0 + if isUiOpen == true and not IsPlayerDead(PlayerId()) then + SendNUIMessage({ + displayWindow = "false", + }) + isUiOpen = false + end + end + end +end) + +Citizen.CreateThread(function() + while true do + Citizen.Wait(100) + if IsPlayerDead(PlayerId()) and isUiOpen == true then + SendNUIMessage({ + displayWindow = "false", + }) + isUiOpen = false + end + end +end) + +local progress_time = 0.20 +local progress_bar = false +local progress_bar_duration = 20 +local progress_bar_text = "" + +Citizen.CreateThread(function() + while true do + Citizen.Wait(progress_bar_duration) + if progress_time > 0 then + progress_time = progress_time - 0.002 + end + end +end) + +Citizen.CreateThread(function() + while true do + Citizen.Wait(0) + if progress_bar then + DrawRect(0.50, 0.90, 0.20, 0.05, 0, 0, 0, 100) + drawUI(0.910, 1.375, 1.0, 1.0, 0.55, progress_bar_text, 255, 255, 255, 255, false) + if progress_time > 0 then + DrawRect(0.50, 0.90, 0.20 - progress_time, 0.05, 75, 156, 237, 225) + elseif progress_time < 1 and progress_bar then + progress_bar = false + end + end + end +end) + +function ProgressBar(text, time) + progress_bar_text = text + progress_bar_duration = time + progress_time = 0.20 + progress_bar = true +end