diff --git a/resources/[housing]/brutal_housing/config.lua b/resources/[housing]/brutal_housing/config.lua
index eaab6bdfa..5917b0e42 100644
--- a/resources/[housing]/brutal_housing/config.lua
+++ b/resources/[housing]/brutal_housing/config.lua
@@ -13,7 +13,7 @@ Config = {
Core = 'QBCORE', -- 'ESX' / 'QBCORE' | Other core setting on the 'core' folder.
VoiceSytem = 'yaca-voice', -- "pma-voice" / "mumble" / "SaltyChat"
Inventory = 'qb_inventory_new', -- 'ox_inventory' / 'qb_inventory_old'/ 'qb_inventory_new' / 'quasar_inventory' / 'chezza_inventory' / 'codem_inventory' / 'core_inventory' // Custom can be add in the cl_utils.lua!!!
- Wardrobe = 'default', -- 'default' / 'ak47_clothing' / 'codem_apperance' / 'fivem_appearance' / 'illenium_appearance' / 'qb_clothing' / 'raid_clothes' / 'rcore_clothes' / 'rcore_clothing' / 'sleek_clothestore' / 'tgiann_clothing' // Custom can be add in the cl_utils.lua!!!
+ Wardrobe = 'illenium_appearance', -- 'default' / 'ak47_clothing' / 'codem_apperance' / 'fivem_appearance' / 'illenium_appearance' / 'qb_clothing' / 'raid_clothes' / 'rcore_clothes' / 'rcore_clothing' / 'sleek_clothestore' / 'tgiann_clothing' // Custom can be add in the cl_utils.lua!!!
TextUI = 'ox_lib', -- false / 'brutal_textui' / 'ox_lib' / 'okokTextUI' / 'ESXTextUI' / 'QBDrawText' // Custom can be add in the cl_utils.lua!!!
BrutalKeys = false, -- Buy here: https://store.brutalscripts.com
BrutalPoliceJob = false, -- Buy here: https://store.brutalscripts.com | Better connection
@@ -36,14 +36,14 @@ Config = {
Blips = {
available = true,
- availableHouse = {label = "Available House", size = 0.7, sprite = 40, color = 2},
- availableGarage = {label = "Available Garage", size = 0.7, sprite = 357, color = 2},
+ availableHouse = {label = "Kaufbares Haus", size = 0.7, sprite = 40, color = 2},
+ availableGarage = {label = "Kaufbare Garage", size = 0.7, sprite = 357, color = 2},
owned = true,
- myHouse = {label = "My House", size = 0.7, sprite = 40, color = 53},
- myGarage = {label = "My Garage", size = 0.7, sprite = 357, color = 53},
- hasKeyHouse = {label = "House", size = 0.7, sprite = 40, color = 53},
- hasKeyGarage = {label = "House", size = 0.7, sprite = 40, color = 53},
- ownedHouse = {label = "House", size = 0.7, sprite = 40, color = 1},
+ myHouse = {label = "Mein Haus", size = 0.7, sprite = 40, color = 53},
+ myGarage = {label = "Meine Garage", size = 0.7, sprite = 357, color = 53},
+ hasKeyHouse = {label = "Haus", size = 0.7, sprite = 40, color = 53},
+ hasKeyGarage = {label = "Haus", size = 0.7, sprite = 40, color = 53},
+ ownedHouse = {label = "Haus", size = 0.7, sprite = 40, color = 1},
ownedGarage = {label = "Garage", size = 0.7, sprite = 357, color = 1},
police = true,
burglarAlarm = {label = "Einbruch Alarm", size = 1.0, sprite = 161, color = 1},
@@ -171,7 +171,7 @@ Config = {
MyProperties = {
Command = 'myproperties',
Suggestion = 'To manage your propertys',
- Control = '', -- Controls list: https://docs.fivem.net/docs/game-references/input-mapper-parameter-ids/keyboard/
+ Control = 'F7', -- Controls list: https://docs.fivem.net/docs/game-references/input-mapper-parameter-ids/keyboard/
},
ServerDestroy = {
diff --git a/resources/[housing]/brutal_keys/.fxap b/resources/[housing]/brutal_keys/.fxap
new file mode 100644
index 000000000..8d12bf19c
Binary files /dev/null and b/resources/[housing]/brutal_keys/.fxap differ
diff --git a/resources/[housing]/brutal_keys/README.md b/resources/[housing]/brutal_keys/README.md
new file mode 100644
index 000000000..cecf3b394
--- /dev/null
+++ b/resources/[housing]/brutal_keys/README.md
@@ -0,0 +1 @@
+Please follow the instructions: https://docs.brutalscripts.com
\ No newline at end of file
diff --git a/resources/[housing]/brutal_keys/cl_utils.lua b/resources/[housing]/brutal_keys/cl_utils.lua
new file mode 100644
index 000000000..36b8ece68
--- /dev/null
+++ b/resources/[housing]/brutal_keys/cl_utils.lua
@@ -0,0 +1,85 @@
+ESX = Core
+QBCore = Core
+
+-- Buy here: (4€+VAT) https://store.brutalscripts.com
+function notification(title, text, time, type)
+ if Config.BrutalNotify then
+ exports['brutal_notify']:SendAlert(title, text, time, type)
+ else
+ -- Put here your own notify and set the Config.BrutalNotify to false
+ SetNotificationTextEntry("STRING")
+ AddTextComponentString(text)
+ DrawNotification(0,1)
+
+ -- Default ESX Notify:
+ --TriggerEvent('esx:showNotification', text)
+
+ -- Default QB Notify:
+ --TriggerEvent('QBCore:Notify', text, 'info', 5000)
+
+ -- OKOK Notify:
+ -- exports['okokNotify']:Alert(title, text, time, type, false)
+
+ end
+end
+
+function TextUIFunction(type, text)
+ if type == 'open' then
+ if Config.TextUI:lower() == 'ox_lib' then
+ lib.showTextUI(text)
+ elseif Config.TextUI:lower() == 'okoktextui' then
+ exports['okokTextUI']:Open(text, 'darkblue', 'right')
+ elseif Config.TextUI:lower() == 'esxtextui' then
+ ESX.TextUI(text)
+ elseif Config.TextUI:lower() == 'qbdrawtext' then
+ exports['qb-core']:DrawText(text,'left')
+ elseif Config.TextUI:lower() == 'brutal_textui' then
+ exports['brutal_textui']:Open(text, "blue")
+ end
+ elseif type == 'hide' then
+ if Config.TextUI:lower() == 'ox_lib' then
+ lib.hideTextUI()
+ elseif Config.TextUI:lower() == 'okoktextui' then
+ exports['okokTextUI']:Close()
+ elseif Config.TextUI:lower() == 'esxtextui' then
+ ESX.HideUI()
+ elseif Config.TextUI:lower() == 'qbdrawtext' then
+ exports['qb-core']:HideText()
+ elseif Config.TextUI:lower() == 'brutal_textui' then
+ exports['brutal_textui']:Close()
+ end
+ end
+end
+
+function PoliceAlert(coords, plate)
+ local x,y,z = table.unpack(coords)
+ local streetLabel = GetStreetNameFromHashKey(GetStreetNameAtCoord(x,y,z))
+
+ if GetResourceState("brutal_policejob") == "started" then
+ TriggerServerEvent('brutal_policejob:server:citizencall', 'create', "Vehicle jacking", coords, streetLabel)
+ end
+end
+
+function OpenMenuUtil()
+ InMenu = true
+ SetNuiFocus(true, true)
+
+ Citizen.CreateThread(function()
+ while InMenu do
+ N_0xf4f2c0d4ee209e20() -- it's disable the AFK camera zoom
+ Citizen.Wait(15000)
+ end
+ end)
+
+ DisplayRadar(false)
+end
+
+function DisableMinimap()
+ DisplayRadar(false)
+ -- Here you can add a trigger to hide your HUD system
+end
+
+function EnableMinimap()
+ DisplayRadar(true)
+ -- Here you can add a trigger to enable your HUD system
+end
\ No newline at end of file
diff --git a/resources/[housing]/brutal_keys/client/client.lua b/resources/[housing]/brutal_keys/client/client.lua
new file mode 100644
index 000000000..863584375
Binary files /dev/null and b/resources/[housing]/brutal_keys/client/client.lua differ
diff --git a/resources/[housing]/brutal_keys/client/desktop.ini b/resources/[housing]/brutal_keys/client/desktop.ini
new file mode 100644
index 000000000..704fc3d2f
Binary files /dev/null and b/resources/[housing]/brutal_keys/client/desktop.ini differ
diff --git a/resources/[housing]/brutal_keys/client/nui.lua b/resources/[housing]/brutal_keys/client/nui.lua
new file mode 100644
index 000000000..92491eac1
Binary files /dev/null and b/resources/[housing]/brutal_keys/client/nui.lua differ
diff --git a/resources/[housing]/brutal_keys/config.lua b/resources/[housing]/brutal_keys/config.lua
new file mode 100644
index 000000000..df0eaaedd
--- /dev/null
+++ b/resources/[housing]/brutal_keys/config.lua
@@ -0,0 +1,155 @@
+----------------------------------------------------------------------------------------------
+--------------------------------------| BRUTAL KEYS :) |--------------------------------------
+----------------------------------------------------------------------------------------------
+
+--[[
+Hi, thank you for buying our script, We are very grateful!
+
+For help join our Discord server: https://discord.gg/85u2u5c8q9
+More informations about the script: https://docs.brutalscripts.com
+--]]
+
+Config = {
+ Core = 'QBCORE', -- 'ESX' / 'QBCORE' | Other core setting on the 'core' folder.
+ TextUI = 'ox_lib', -- false / 'brutal_textui' / 'ox_lib' / 'okokTextUI' / 'ESXTextUI' / 'QBDrawText' // Custom can be add in the cl_utils.lua!!!
+ BrutalNotify = true, -- Buy here: (4€+VAT) https://store.brutalscripts.com | Or set up your own notify >> cl_utils.lua
+ AdminGroups = {'superadmin', 'admin', 'mod', 'god'}, -- Admin groups
+
+ DataStorage = {
+ --[[
+ optimized = Saves the data when the script is restarted, the server is shut down or restarted, or during a scheduled server restart.
+ events: "txAdmin:events:serverShuttingDown" & "txAdmin:events:scheduledRestart" or fixed restart times.
+ resource_heavy = all data will be saved immediately, this is very stressful for the server so we don't recommend using this, but the choice is up to you.
+ ]]--
+
+ Type = "optimized", -- "optimized" / "resource_heavy"
+ Restarts = {}, -- example: "11:59" | Save the data 1 minute before restart. Save data at 15:59 instead of 16:00
+ },
+
+ CopyPrice = 500, -- Key copy price
+ UseVehicleKeySystem = false, -- true / false | false = the full vehicle key system is switched off.
+ DriveWithKey = true, -- If true, players can only drive the car if they have a key, IF false, they can drive the car anytime they find it unlocked.
+ LockPicking = {maxTry = 1, item = "lockpick"}, -- maxTry = maximum try per vehicle | item = item or ""
+ Hotwiring = {maxTry = 1, successrate = 25, item = "screwdriver"}, -- maxTry = maximum try per vehicle | successrate = 10 = 10% | item = item or ""
+ AlertPolice = {use = true, chance = 10}, -- chance 10 = 10% | Supports Brutal Policejob basically to modity it open the cl_utils.lua file
+ UseKeyProp = true, -- Use the key prop in the player's hand when closing or opening a vehicle
+ LostVehicleKeys = {
+ npc = vector4(-354.5593, -128.0446, 39.4307, 65.9746), -- npc VECTOR4! coords
+ model = "ig_benny", -- npc type
+ blip = {use = true, label = "Lost Vehicle Keys", size = 1.0, sprite = 186, color = 1},
+ price = 2500 -- if they lost all of their keys then the price is higher
+ },
+
+ QuickKeys = {
+ ['everyone'] = { -- no one needs a key to drive those vehicles
+ "adder", "bmx"
+ },
+
+ ['police'] = { -- player in the job do not need a key to drive the vehicle
+ "police", "police2", "police3", "policeb", "pbus"
+ },
+
+ ['ambulance'] = { -- player in the job do not need a key to drive the vehicle
+ "ambulance",
+ },
+ },
+
+ Commands = {
+ MyKeys = {
+ Command = 'mykeys',
+ Suggestion = 'To manage your keys',
+ Control = '', -- Controls list: https://docs.fivem.net/docs/game-references/input-mapper-parameter-ids/keyboard/
+ },
+
+ VehicleKey = {
+ Command = 'vkey',
+ Suggestion = 'To open the nearest vehicle',
+ Control = '', -- 'G' | Controls list: https://docs.fivem.net/docs/game-references/input-mapper-parameter-ids/keyboard/
+ },
+
+ Lockpicking = {
+ Command = 'lockpick',
+ Suggestion = 'Lockpicking the nearest vehicle',
+ Control = '', -- Controls list: https://docs.fivem.net/docs/game-references/input-mapper-parameter-ids/keyboard/
+ },
+
+ Hotwiring = {
+ Command = 'hotwiring',
+ Suggestion = 'To start the vehicle without keys',
+ Control = '', -- Controls list: https://docs.fivem.net/docs/game-references/input-mapper-parameter-ids/keyboard/
+ },
+
+ -- Admin --
+
+ StaffMode = {
+ Command = 'kstaff',
+ Suggestion = 'To switch Key System Staff mode ON/OFF',
+ },
+
+ AddKey = { -- /addkey [ID] [KEY-ID] [label]
+ Command = 'addkey',
+ Suggestion = 'To add key',
+ },
+
+ RemoveKey = { -- /removekey [ID] [KEY-ID]
+ Command = 'removekey',
+ Suggestion = 'To remove key',
+ },
+
+ AddVehicleKey = { -- /addvehiclekey [ID] [PLATE]
+ Command = 'addvehiclekey',
+ Suggestion = 'To add vehicle key',
+ },
+
+ AddVehicleTemporaryKey = { -- /addtempkey [ID] [PLATE]
+ Command = 'addtempkey',
+ Suggestion = 'To add vehicle temporary key',
+ },
+
+ RemoveVehicleKey = { -- /removevehiclekey [ID] [PLATE]
+ Command = 'removevehiclekey',
+ Suggestion = 'To remove vehicle key',
+ },
+ },
+
+ -----------------------------------------------------------
+ -----------------------| TRANSLATE |-----------------------
+ -----------------------------------------------------------
+
+ NUILanguage = "en", -- "en", "es", "fr", "de", "pt", "it", "pl", "nl", "ru", "tr", "hu", "ro", "cs", "sv", "ar"
+
+ MoneyForm = '$', -- Money form
+
+ Texts = {
+ [1] = {'[E] - Lost Key', 38, 'Open the lost key menu'},
+ },
+
+ -- Notify function EDITABLE >> cl_utils.lua
+ Notify = {
+ [1] = {"Keys", "Staff mode: ON", 5000, "success"},
+ [2] = {"Keys", "Staff mode: OFF", 5000, "error"},
+ [3] = {"Keys", "You got a new key!", 5000, "info"},
+ [4] = {"Keys", "A key has been deleted!", 5000, "success"},
+ [5] = {"Keys", "You gave them a key:", 5000, "info"},
+ [6] = {"Keys", "You copied a key!", 5000, "success"},
+ [7] = {"Keys", "You got a key from:", 5000, "info"},
+ [8] = {"Keys", "Successful lock change!", 5000, "success"},
+ [9] = {"Keys", "Only the vehicle owner can change!", 5000, "error"},
+ [10] = {"Keys", "You don't have enough money!", 5000, "error"},
+ [11] = {"Keys", "You have no car / you have keys to all your cars!", 5000, "error"},
+ [12] = {"Keys", "No vehicle near you!", 5000, "error"},
+ [13] = {"Keys", "The vehicle is open!", 5000, "error"},
+ [14] = {"Keys", "Someone is sitting in the car!", 5000, "error"},
+ [15] = {"Keys", "You have reached the maximum amount of keys!", 5000, "error"},
+ [16] = {"Keys", "This person already has a temporary key for this car!", 5000, "error"},
+ [17] = {"Keys", "To reduce the server load, you will have to wait a little!", 5000, "error"},
+ [18] = {"Keys", "Plate: 3-8 characters", 5000, "error"},
+ [19] = {"Keys", "Invalid Player ID!", 5000, "error"},
+ [20] = {"Keys", "You can't try again!", 5000, "error"},
+ [21] = {"Keys", "You have to sit in the driver's seat!", 5000, "error"},
+ [22] = {"Keys", "You did it! You've started the engine!", 5000, "success"},
+ [23] = {"Keys", "Unfortunately the safety lock would not let you start the engine!", 5000, "error"},
+ [24] = {"Keys", "You need: Lockpick", 5000, "error"},
+ [25] = {"Keys", "You need: Screwdriver", 5000, "error"},
+ },
+}
\ No newline at end of file
diff --git a/resources/[housing]/brutal_keys/core/client-core.lua b/resources/[housing]/brutal_keys/core/client-core.lua
new file mode 100644
index 000000000..939af1c6b
--- /dev/null
+++ b/resources/[housing]/brutal_keys/core/client-core.lua
@@ -0,0 +1,68 @@
+Core = nil
+
+if Config['Core']:upper() == 'ESX' then
+ local _esx_ = 'new' -- 'new' / 'old'
+
+ if _esx_ then
+ Core = exports['es_extended']:getSharedObject()
+ else
+ while Core == nil do
+ TriggerEvent('esx:getSharedObject', function(obj) Core = obj end)
+ Citizen.Wait(0)
+ end
+ end
+
+ LoadedEvent = 'esx:playerLoaded'
+ ReviveEvent = 'esx_ambulancejob:revive'
+ JobUpdateEvent = 'esx:setJob'
+ TSCB = Core.TriggerServerCallback
+
+ function GetPlayerJobDatas()
+ return Core.GetPlayerData().job
+ end
+
+ function GetPlayersFunction()
+ return Core.Game.GetPlayers()
+ end
+
+ function GetVehiclePropertiesFunction(vehicle)
+ return Core.Game.GetVehicleProperties(vehicle)
+ end
+
+ function SetVehiclePropertiesFunction(vehicle, properties)
+ return Core.Game.SetVehicleProperties(vehicle, properties)
+ end
+
+ function GetClosestVehicleFunction(coords, modelFilter)
+ return Core.Game.GetClosestVehicle(coords, modelFilter)
+ end
+
+elseif Config['Core']:upper() == 'QBCORE' then
+ Core = exports['qb-core']:GetCoreObject()
+
+ LoadedEvent = 'QBCore:Client:OnPlayerLoaded'
+ ReviveEvent = 'hospital:client:Revive'
+ JobUpdateEvent = 'QBCore:Client:OnJobUpdate'
+ TSCB = Core.Functions.TriggerCallback
+
+ function GetPlayerJobDatas()
+ return Core.Functions.GetPlayerData().job
+ end
+
+ function GetPlayersFunction()
+ return Core.Functions.GetPlayers()
+ end
+
+ function GetVehiclePropertiesFunction(vehicle)
+ return Core.Functions.GetVehicleProperties(vehicle)
+ end
+
+ function SetVehiclePropertiesFunction(vehicle, properties)
+ return Core.Functions.SetVehicleProperties(vehicle, properties)
+ end
+
+ function GetClosestVehicleFunction(coords, modelFilter)
+ return Core.Functions.GetClosestVehicle(coords, modelFilter)
+ end
+
+end
\ No newline at end of file
diff --git a/resources/[housing]/brutal_keys/core/desktop.ini b/resources/[housing]/brutal_keys/core/desktop.ini
new file mode 100644
index 000000000..704fc3d2f
Binary files /dev/null and b/resources/[housing]/brutal_keys/core/desktop.ini differ
diff --git a/resources/[housing]/brutal_keys/core/server-core.lua b/resources/[housing]/brutal_keys/core/server-core.lua
new file mode 100644
index 000000000..51c04ff5f
--- /dev/null
+++ b/resources/[housing]/brutal_keys/core/server-core.lua
@@ -0,0 +1,271 @@
+Core = nil
+
+if Config['Core']:upper() == 'ESX' then
+ local _esx_ = 'new' -- 'new' / 'old'
+
+ if _esx_ == 'new' then
+ Core = exports['es_extended']:getSharedObject()
+ else
+ Core = nil
+ TriggerEvent('esx:getSharedObject', function(obj) Core = obj end)
+ while Core == nil do
+ Citizen.Wait(0)
+ end
+ end
+
+ RESCB = Core.RegisterServerCallback
+ GETPFI = Core.GetPlayerFromId
+ RUI = Core.RegisterUsableItem
+ SetJobEvent = 'esx:setJob'
+ onPlayerDeath = 'esx:onPlayerDeath'
+ SQLData = {
+ player_vehicles = 'owned_vehicles',
+ }
+
+ function GetIdentifier(source)
+ local xPlayer = GETPFI(source)
+ while xPlayer == nil do
+ Citizen.Wait(1000)
+ xPlayer = GETPFI(source)
+ end
+ return xPlayer.identifier
+ end
+
+ function GetPlayerByIdentifier(identifier)
+ return Core.GetPlayerFromIdentifier(identifier)
+ end
+
+ function GetAccountMoney(source,account)
+ local xPlayer = GETPFI(source)
+ if account == 'bank' then
+ return xPlayer.getAccount(account).money
+ elseif account == 'money' then
+ return xPlayer.getMoney()
+ end
+ end
+
+ function AddMoneyFunction(source, account, amount)
+ local xPlayer = GETPFI(source)
+ if account == 'bank' then
+ xPlayer.addAccountMoney('bank', amount)
+ elseif account == 'money' then
+ xPlayer.addMoney(amount)
+ end
+ end
+
+ function RemoveAccountMoney(source, account, amount)
+ local xPlayer = GETPFI(source)
+ if account == 'bank' then
+ xPlayer.removeAccountMoney('bank', amount)
+ elseif account == 'money' then
+ xPlayer.removeMoney(amount)
+ end
+ end
+
+ function GetItemCount(source, item)
+ local xPlayer = GETPFI(source)
+
+ if _esx_ == 'new' then
+ return xPlayer.getInventoryItem(item).count
+ else
+ if string.sub(item, 0, 6):lower() == 'weapon' then
+ local loadoutNum, weapon = xPlayer.getWeapon(item:upper())
+
+ if weapon then
+ return true
+ else
+ return false
+ end
+ else
+ return xPlayer.getInventoryItem(item).count
+ end
+ end
+ end
+
+ function RemoveItem(source, item, amount)
+ local xPlayer = GETPFI(source)
+ if _esx_ == 'new' then
+ xPlayer.removeInventoryItem(item, amount)
+ else
+ if string.sub(item, 0, 6):lower() == 'weapon' then
+ xPlayer.removeWeapon(item)
+ else
+ xPlayer.removeInventoryItem(item, amount)
+ end
+ end
+ end
+
+ function AddItem(source, item, count, info)
+ local xPlayer = GETPFI(source)
+ if _esx_ == 'new' then
+ xPlayer.addInventoryItem(item, count, info)
+ else
+ if string.sub(item, 0, 6):lower() == 'weapon' then
+ xPlayer.addWeapon(item, 90)
+ else
+ xPlayer.addInventoryItem(item, count)
+ end
+ end
+ end
+
+ function GetPlayerNameFunction(source)
+ local name
+ if Config.SteamName then
+ name = GetPlayerName(source) or 'No Data'
+ else
+ local xPlayer = GETPFI(source)
+ name = xPlayer.getName() or 'No Data'
+ end
+ return name
+ end
+
+ function GetPlayerJob(source)
+ local xPlayer = GETPFI(source)
+ return xPlayer.job.name
+ end
+
+ function CreateCoreJob(name, label, grades)
+ Core.CreateJob(name, label, grades)
+ end
+
+ function SetCoreJob(source, job, grade)
+ local xPlayer = GETPFI(source)
+ xPlayer.setJob(job, grade)
+ end
+
+ function SetCoreJobOffline(identifier, job, grade)
+ MySQL.update('UPDATE users SET job = ?, job_grade = ? WHERE identifier = ?', {job, grade, identifier})
+ end
+
+ function GetPlayersFunction()
+ return Core.GetPlayers()
+ end
+
+elseif Config['Core']:upper() == 'QBCORE' then
+
+ Core = exports['qb-core']:GetCoreObject()
+
+ RESCB = Core.Functions.CreateCallback
+ GETPFI = Core.Functions.GetPlayer
+ RUI = Core.Functions.CreateUseableItem
+ SetJobEvent = 'QBCore:Server:SetGang'
+ onPlayerDeath = GetResourceState("brutal_ambulancejob") == "started" and 'onPlayerDeath' or 'hospital:server:SetDeathStatus'
+ SQLData = {
+ player_vehicles = 'player_vehicles',
+ }
+
+ function GetIdentifier(source)
+ local xPlayer = GETPFI(source)
+ while xPlayer == nil do
+ Citizen.Wait(1000)
+ xPlayer = GETPFI(source)
+ end
+ return xPlayer.PlayerData.citizenid
+ end
+
+ function GetPlayerByIdentifier(identifier)
+ return Core.Functions.GetPlayerByCitizenId(identifier)
+ end
+
+ function GetAccountMoney(source, account)
+ local xPlayer = GETPFI(source)
+ if account == 'bank' then
+ return xPlayer.PlayerData.money.bank
+ elseif account == 'money' then
+ return xPlayer.PlayerData.money.cash
+ end
+ end
+
+ function AddMoneyFunction(source, account, amount)
+ local xPlayer = GETPFI(source)
+ if account == 'bank' then
+ xPlayer.Functions.AddMoney('bank', amount)
+ elseif account == 'money' then
+ xPlayer.Functions.AddMoney('cash', amount)
+ end
+ end
+
+ function RemoveAccountMoney(source, account, amount)
+ local xPlayer = GETPFI(source)
+ if account == 'bank' then
+ xPlayer.Functions.RemoveMoney('bank', amount)
+ elseif account == 'money' then
+ xPlayer.Functions.RemoveMoney('cash', amount)
+ end
+ end
+
+ function GetItemCount(source, item)
+ local xPlayer = GETPFI(source)
+ local items = xPlayer.Functions.GetItemByName(item)
+ local item_count = 0
+ if items ~= nil then
+ item_count = items.amount
+ else
+ item_count = 0
+ end
+ return item_count
+ end
+
+ function RemoveItem(source, item, amount)
+ local xPlayer = GETPFI(source)
+ xPlayer.Functions.RemoveItem(item, amount)
+ end
+
+ function AddItem(source, item, count, info)
+ local xPlayer = GETPFI(source)
+ xPlayer.Functions.AddItem(item, count, nil, info)
+ end
+
+ function GetPlayerNameFunction(source)
+ local name
+ if Config.SteamName then
+ name = GetPlayerName(source)
+ else
+ local xPlayer = GETPFI(source)
+ name = xPlayer.PlayerData.charinfo.firstname..' '..xPlayer.PlayerData.charinfo.lastname
+ end
+ return name
+ end
+
+ function GetPlayerJob(source)
+ local xPlayer = GETPFI(source)
+ return xPlayer.PlayerData.gang.name
+ end
+
+ function CreateCoreJob(name, label, grades)
+ Core.Functions.AddGang(name,
+ {
+ label = label,
+ grades = grades,
+ })
+ end
+
+ function UpdateCoreJob(name, label, grades)
+ Core.Functions.UpdateGang(name, {
+ label = label,
+ grades = grades,
+ })
+ end
+
+ function RemoveCoreJob(name)
+ Core.Functions.RemoveGang(name)
+ end
+
+ function SetCoreJob(source, job, grade)
+ local xPlayer = GETPFI(source)
+ xPlayer.Functions.SetGang(job, grade)
+ end
+
+ function SetCoreJobOffline(identifier, job)
+ local joblabel = "None"
+ if Gangs[job] ~= nil and Gangs[job].label ~= nil then
+ joblabel = Gangs[job].label
+ end
+
+ MySQL.update('UPDATE players SET gang = ? WHERE citizenid = ?', {json.encode({grade = {level = 0, name = "Member"}, name = job, isboss = false, label = joblabel}), identifier})
+ end
+
+ function GetPlayersFunction()
+ return Core.Functions.GetPlayers()
+ end
+end
\ No newline at end of file
diff --git a/resources/[housing]/brutal_keys/desktop.ini b/resources/[housing]/brutal_keys/desktop.ini
new file mode 100644
index 000000000..704fc3d2f
Binary files /dev/null and b/resources/[housing]/brutal_keys/desktop.ini differ
diff --git a/resources/[housing]/brutal_keys/fxmanifest.lua b/resources/[housing]/brutal_keys/fxmanifest.lua
new file mode 100644
index 000000000..7c3739d50
--- /dev/null
+++ b/resources/[housing]/brutal_keys/fxmanifest.lua
@@ -0,0 +1,61 @@
+fx_version 'cerulean'
+games { 'gta5' }
+lua54 'yes'
+
+author 'Keres & Dév'
+description 'Brutal Keys - store.brutalscripts.com'
+version '1.0.5'
+
+client_scripts {
+ 'config.lua',
+ 'core/client-core.lua',
+ 'cl_utils.lua',
+ 'client/*.lua'
+}
+
+server_scripts {
+ '@mysql-async/lib/MySQL.lua',
+ 'config.lua',
+ 'core/server-core.lua',
+ 'sv_utils.lua',
+ 'server/*.lua',
+}
+
+shared_script {
+ '@ox_lib/init.lua'
+}
+
+export 'addKey'
+export 'removeKey'
+export 'addVehicleKey'
+export 'addVehicleTemporaryKey'
+export 'getPlayerKey'
+export 'OpenKeysMenu'
+
+
+ui_page "html/index.html"
+files {
+ "html/index.html",
+ "html/style.css",
+ "html/script.js",
+ "html/assets/**",
+ 'locales/*.json',
+}
+
+dependencies {
+ 'lockpick', -- https://github.com/baguscodestudio/lockpick
+ 'mx_fixwiring', -- https://github.com/mxlolshop/minigameFixWiring/tree/main/Fix%20Wiring/FiveM/mx_fixwiring
+ '/server:5181', -- ⚠️PLEASE READ⚠️; Requires at least SERVER build 5181
+ '/gameBuild:2189', -- ⚠️PLEASE READ⚠️; Requires at least GAME build 2189.
+}
+
+escrow_ignore {
+ 'config.lua',
+ 'cl_utils.lua',
+ 'sv_utils.lua',
+ 'core/client-core.lua',
+ 'core/server-core.lua',
+ 'stream/*.ydr',
+ 'stream/*.ytyp',
+}
+dependency '/assetpacks'
\ No newline at end of file
diff --git a/resources/[housing]/brutal_keys/html/assets/basic_key.svg b/resources/[housing]/brutal_keys/html/assets/basic_key.svg
new file mode 100644
index 000000000..fe41699d1
--- /dev/null
+++ b/resources/[housing]/brutal_keys/html/assets/basic_key.svg
@@ -0,0 +1,3 @@
+
diff --git a/resources/[housing]/brutal_keys/html/assets/basic_vehicle.svg b/resources/[housing]/brutal_keys/html/assets/basic_vehicle.svg
new file mode 100644
index 000000000..78b06cb77
--- /dev/null
+++ b/resources/[housing]/brutal_keys/html/assets/basic_vehicle.svg
@@ -0,0 +1,3 @@
+
diff --git a/resources/[housing]/brutal_keys/html/assets/bin.png b/resources/[housing]/brutal_keys/html/assets/bin.png
new file mode 100644
index 000000000..57b59d38c
Binary files /dev/null and b/resources/[housing]/brutal_keys/html/assets/bin.png differ
diff --git a/resources/[housing]/brutal_keys/html/assets/desktop.ini b/resources/[housing]/brutal_keys/html/assets/desktop.ini
new file mode 100644
index 000000000..704fc3d2f
Binary files /dev/null and b/resources/[housing]/brutal_keys/html/assets/desktop.ini differ
diff --git a/resources/[housing]/brutal_keys/html/assets/door_line.svg b/resources/[housing]/brutal_keys/html/assets/door_line.svg
new file mode 100644
index 000000000..7759b567d
--- /dev/null
+++ b/resources/[housing]/brutal_keys/html/assets/door_line.svg
@@ -0,0 +1,6 @@
+
diff --git a/resources/[housing]/brutal_keys/html/assets/key.svg b/resources/[housing]/brutal_keys/html/assets/key.svg
new file mode 100644
index 000000000..4b8d54f3d
--- /dev/null
+++ b/resources/[housing]/brutal_keys/html/assets/key.svg
@@ -0,0 +1,5 @@
+
diff --git a/resources/[housing]/brutal_keys/html/assets/panel_border.svg b/resources/[housing]/brutal_keys/html/assets/panel_border.svg
new file mode 100644
index 000000000..b93f7497a
--- /dev/null
+++ b/resources/[housing]/brutal_keys/html/assets/panel_border.svg
@@ -0,0 +1,3 @@
+
diff --git a/resources/[housing]/brutal_keys/html/assets/people.png b/resources/[housing]/brutal_keys/html/assets/people.png
new file mode 100644
index 000000000..eb9405158
Binary files /dev/null and b/resources/[housing]/brutal_keys/html/assets/people.png differ
diff --git a/resources/[housing]/brutal_keys/html/assets/selected_key.svg b/resources/[housing]/brutal_keys/html/assets/selected_key.svg
new file mode 100644
index 000000000..b0c77cc05
--- /dev/null
+++ b/resources/[housing]/brutal_keys/html/assets/selected_key.svg
@@ -0,0 +1,3 @@
+
diff --git a/resources/[housing]/brutal_keys/html/assets/vehicle_key.svg b/resources/[housing]/brutal_keys/html/assets/vehicle_key.svg
new file mode 100644
index 000000000..dee0095df
--- /dev/null
+++ b/resources/[housing]/brutal_keys/html/assets/vehicle_key.svg
@@ -0,0 +1,12 @@
+
diff --git a/resources/[housing]/brutal_keys/html/desktop.ini b/resources/[housing]/brutal_keys/html/desktop.ini
new file mode 100644
index 000000000..704fc3d2f
Binary files /dev/null and b/resources/[housing]/brutal_keys/html/desktop.ini differ
diff --git a/resources/[housing]/brutal_keys/html/index.html b/resources/[housing]/brutal_keys/html/index.html
new file mode 100644
index 000000000..ba146598f
--- /dev/null
+++ b/resources/[housing]/brutal_keys/html/index.html
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Brutal Keys
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/[housing]/brutal_keys/html/script.js b/resources/[housing]/brutal_keys/html/script.js
new file mode 100644
index 000000000..545c21980
--- /dev/null
+++ b/resources/[housing]/brutal_keys/html/script.js
@@ -0,0 +1,637 @@
+BGBlur = true
+ActionsOpen = false
+ChangedKeys = []
+DoorNotify = false
+CurrentKeyPage = 1
+
+let translations = {};
+
+async function loadTranslations(lang) {
+ try {
+ const response = await fetch(`../locales/${lang}.json`);
+ translations = await response.json();
+ updateText()
+ } catch (error) {
+ console.error("Error loading translation:", error);
+ }
+}
+
+function updateText() {
+ document.querySelectorAll("[data-i18n]").forEach(element => {
+ const key = element.getAttribute("data-i18n");
+
+ if (element.hasAttribute("placeholder")) {
+ element.setAttribute("placeholder", translations[key]);
+ } else {
+ element.innerHTML = translations[key];
+ }
+ });
+}
+
+function t(key) {
+ return translations[key] || key;
+}
+
+window.addEventListener('message', function(event) {
+ let data = event.data
+
+ if (data.action == "NUILanguage") {
+ loadTranslations(data.language);
+ }
+ else if (data.action === "OpenKeysMenu") {
+ MyKeys = data.mykeys
+ CreateKeyMenu()
+ BackgroundBlur("plugin_1", 'key_panel')
+ if (ActionsOpen){
+ BackgroundBlur("plugin_1", 'key_panel', true)
+ MakeKeysDraggable()
+ }
+ else{
+ BackgroundBlur("plugin_1", 'key_panel')
+ }
+ ChangedKeys = []
+ }
+ if (data.action === "OpenLostKeyMenu") {
+ MyVehicles = data.myvehicles
+ VehicleKeyPrice = data.price
+ Currency = data.currencyform
+ CreateKeyPurchaseMenu()
+ show('key_purchase_menu')
+ BackgroundBlur("plugin_2", 'key_purchase_panel')
+ }
+ if (data.action === "DoorState") {
+ DoorState = data.status
+ if (DoorState == 'unlocked'){
+ $(".key_notify .text").html(t('doors_unlocked'))
+ document.getElementById('line').classList.remove("red")
+ }
+ else{
+ $(".key_notify .text").html(t('doors_locked'))
+ document.getElementById('line').classList.add("red")
+ }
+ $('.key_notify').css('display', 'block')
+ $('.key_notify .container').css('animation', 'container 1s ease both')
+ if (DoorNotify){
+ clearTimeout(DoorNotify)
+ }
+ DoorNotify = setTimeout(() => {
+ $('.key_notify .container').css('animation', 'reverse_container 1s ease both')
+ setTimeout(() => {
+ $('.key_notify').css('display', 'none')
+ DoorNotify = false
+ }, 800);
+ }, 4000);
+ }
+ else if (data.action === "close") {
+ Close()
+ }
+})
+
+document.onkeydown = function(data) {
+ if (event.key == 'Escape') {
+ Close()
+ }
+}
+
+function Close(){
+ CloseKeyMenu()
+ hide('key_purchase_menu')
+ $.post('https://' + GetParentResourceName() + '/UseButton', JSON.stringify({action: "close", table:ChangedKeys}))
+ ChangedKeys = []
+}
+
+function CreateKeyPurchaseMenu(){
+ $('.vehicles_container').html('')
+ for (let i = 0; i < MyVehicles.length; i++) {
+ $('.vehicles_container').append(`
+
+
+
${MyVehicles[i].plate}
+
${VehicleKeyPrice.toLocaleString('hu-HU')+' '+Currency}
+
+
+ `)
+ }
+}
+
+function SendBuyKeyForVehicle(plate){
+ $.post('https://' + GetParentResourceName() + '/UseButton', JSON.stringify({action: "lostKey", plate}))
+}
+
+function CreateKeyMenu(){
+ $('.key_menu').css('display', 'block')
+ $('.key_menu .animation_element').css('animation', 'animation_element 1s ease both')
+ $('.key_menu .overflow_container').css('animation', 'overflow_container 1s ease both')
+ $('.key_menu .panel').css('animation', 'panel 1s ease both')
+
+ CreatePageSelector()
+ InsertDataToPanel()
+}
+
+function SendChangeLock(keyid){
+ $.post('https://' + GetParentResourceName() + '/UseButton', JSON.stringify({action: "changeLock", keyid, table:ChangedKeys}))
+ ChangedKeys = []
+}
+
+function SendCopy(keyid){
+ $.post('https://' + GetParentResourceName() + '/UseButton', JSON.stringify({action: "copyKey", keyid, table:ChangedKeys}))
+ ChangedKeys = []
+}
+
+function SendDeleteAll(keyid, plate){
+ $.post('https://' + GetParentResourceName() + '/UseButton', JSON.stringify({action: "deleteAll", keyid, table:ChangedKeys, plate}))
+ ChangedKeys = []
+}
+
+function CloseKeyMenu(){
+ $('.key_menu .animation_element').css('animation', 'reverse_animation_element 1s ease both')
+ $('.key_menu .overflow_container').css('animation', 'reverse_overflow_container 1s ease both')
+ $('.key_menu .panel').css('animation', 'reverse_panel 1s ease both')
+
+ if (ActionsOpen){
+ document.getElementById('actions_btn').classList.remove("active")
+ $('.key_menu .overflow_container').css('transform', 'translate(0px, -50%)')
+
+ $('.actions_section #actions_panel').css('animation', 'reverse_actions_panel 0.3s both')
+
+ MakeKeysNotDraggable()
+
+ setTimeout(() => {
+ ActionsOpen = false
+ BackgroundBlur("plugin_1", 'key_panel')
+ $('.actions_section').css('display', 'none')
+ }, 400);
+ }
+
+ setTimeout(() => {
+ $('.key_menu').css('display', 'none')
+ }, 700);
+}
+
+function ChooseKeyPage(element, number){
+ MakeKeysNotDraggable()
+ CurrentKeyPage = number+1
+ for (let i = 0; i < NumberOfPages; i++) {
+ document.getElementById("page_"+i).classList.remove("choosed")
+ }
+ element.classList.add("choosed")
+ InsertDataToPanel()
+ if (ActionsOpen){
+ MakeKeysDraggable()
+ }
+}
+
+function CreatePageSelector(){
+ BiggestSlot = 1
+ for (let i = 0; i < MyKeys.length; i++) {
+ if (MyKeys[i].slot > BiggestSlot){
+ BiggestSlot = MyKeys[i].slot
+ }
+ }
+ NumberOfPages = (BiggestSlot-(BiggestSlot%24))/24+1
+
+ $(".page_selector_container").html('')
+ if (NumberOfPages > 1){
+ for (let i = 0; i < NumberOfPages; i++) {
+ if (CurrentKeyPage-1 == i){
+ $(".page_selector_container").append(`
+
+ `)
+ }
+ else{
+ $(".page_selector_container").append(`
+
+ `)
+ }
+ }
+ }
+}
+
+function InsertDataToPanel(){
+ $('.keys_container').html('')
+ for (let i = (CurrentKeyPage*24)-24; i < CurrentKeyPage*24; i++) {
+ if (MyKeys.length != 0){
+ for (let _i = 0; _i < MyKeys.length; _i++) {
+ if (MyKeys[_i] != undefined && MyKeys[_i].slot-1 == i){
+ let options = {year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit"};
+ let date = new Date(MyKeys[_i].time * 1000);
+ $('.keys_container').append(`
+
+
+
${MyKeys[_i].quantity>1?MyKeys[_i].quantity:''}
+
${MyKeys[_i].label}
+ ${MyKeys[_i].type == 'temporary_key'?`
`:''}
+
+
+ `)
+ break
+ }
+ else if(_i+1 == MyKeys.length){
+ $('.keys_container').append(`
+
+ `)
+ break
+ }
+ }
+ }
+ else{
+ $('.keys_container').append(`
+
+ `)
+ }
+ }
+ document.querySelectorAll(".key_image").forEach(el => {
+ const dropdownMenu = new bootstrap.Dropdown(el);
+ el.addEventListener("contextmenu", function (event) {
+ event.preventDefault();
+ document.querySelectorAll(".key_image").forEach(otherEl => {
+ if (otherEl !== el) {
+ new bootstrap.Dropdown(otherEl).hide();
+ }
+ });
+ dropdownMenu.show();
+ });
+
+ document.addEventListener("click", function () {
+ dropdownMenu.hide();
+ });
+ });
+}
+
+function MakeKeysDraggable(){
+ for (let i = (CurrentKeyPage*24)-24; i < CurrentKeyPage*24; i++) {
+ for (let _i = 0; _i < MyKeys.length; _i++) {
+ if (MyKeys[_i] != undefined && MyKeys[_i].slot-1 == i){
+ dragElement(document.getElementById('key_'+i), 'key_'+i, MyKeys[_i].id, _i)
+ }
+ }
+ }
+}
+
+function MakeKeysNotDraggable(){
+ for (let i = (CurrentKeyPage*24)-24; i < CurrentKeyPage*24; i++) {
+ for (let _i = 0; _i < MyKeys.length; _i++) {
+ if (MyKeys[_i] != undefined && MyKeys[_i].slot-1 == i){
+ document.getElementById('key_'+i).onmousedown = null
+ }
+ }
+ }
+}
+
+function CreateActionPage(){
+ if (!ActionsOpen){
+ ActionsOpen = true
+ document.getElementById('actions_btn').classList.add("active")
+ $('.key_menu .overflow_container').css('transform', 'translate(-100px, -50%)')
+ BackgroundBlur("plugin_1", 'key_panel', true)
+
+ $('#actions_panel').css('height', $('.key_menu .panel').height()+'px')
+ $('.actions_section #actions_panel').css('animation', 'actions_panel 0.3s ease both')
+ $('.actions_section').css('display', 'block')
+
+ MakeKeysDraggable()
+
+ TriggerCallback('getClosestPlayers', {}).done((cb) => {
+ ClosestPlayers = cb
+ $('.people_container').html('')
+ if (ClosestPlayers.length > 0){
+ for (let i = 0; i < ClosestPlayers.length; i++) {
+ $('.people_container').append(`
+
+
+
+
ID ${ClosestPlayers[i].id}
+
+ `)
+ }
+ }
+ else{
+ $('.people_container').append(`
+
+ `)
+ }
+ });
+ }
+ else{
+ document.getElementById('actions_btn').classList.remove("active")
+ $('.key_menu .overflow_container').css('transform', 'translate(0px, -50%)')
+
+ $('.actions_section #actions_panel').css('animation', 'reverse_actions_panel 0.3s both')
+
+ MakeKeysNotDraggable()
+
+ setTimeout(() => {
+ ActionsOpen = false
+ BackgroundBlur("plugin_1", 'key_panel')
+ $('.actions_section').css('display', 'none')
+ }, 400);
+ }
+}
+
+function TriggerCallback(event, data) {
+ data.action = event;
+ return $.post('https://' + GetParentResourceName() + '/UseButton', JSON.stringify(data)).promise();
+}
+
+function BackgroundBlur(element, target, value) {
+ if (BGBlur){
+ var bodyRect = document.body.getBoundingClientRect();
+ let elemRect = getTotalBoundingBox(target);
+
+ if (!elemRect) return;
+
+ let offset = [];
+ if (value){
+ offset = [
+ (elemRect.top - bodyRect.top)-2,
+ ((elemRect.right - bodyRect.right) * (-1))-150,
+ (elemRect.bottom - bodyRect.bottom)+2,
+ (elemRect.left - bodyRect.left)-100
+ ];
+ }
+ else{
+ offset = [
+ (elemRect.top - bodyRect.top)-2,
+ ((elemRect.right - bodyRect.right) * (-1))-1,
+ (elemRect.bottom - bodyRect.bottom)+2,
+ (elemRect.left - bodyRect.left)-1
+ ];
+ }
+
+ $('#' + element).css('clip-path', `inset(${offset[0]}px ${offset[1]}px calc(100% - ${offset[2]}px) ${offset[3]}px)`);
+ }
+ else{
+ $('#' + element).css('display', 'none')
+ }
+
+}
+
+function getTotalBoundingBox(target) {
+ const elem = document.getElementById(target);
+ if (!elem) return null;
+
+ let elemRect = elem.getBoundingClientRect();
+ let minX = elemRect.left;
+ let minY = elemRect.top;
+ let maxX = elemRect.right;
+ let maxY = elemRect.bottom;
+
+
+ Array.from(elem.children).forEach(child => {
+ let childRect = child.getBoundingClientRect();
+ minX = Math.min(minX, childRect.left!=0?childRect.left:99999);
+ minY = Math.min(minY, childRect.top!=0?childRect.top:99999);
+ maxX = Math.max(maxX, childRect.right);
+ maxY = Math.max(maxY, childRect.bottom);
+ });
+
+
+ return {
+ width: maxX - minX,
+ height: maxY - minY,
+ left: minX,
+ top: minY,
+ right: maxX,
+ bottom: maxY
+ };
+}
+
+function show(element) {
+ $("#" + element).css("display", "block")
+ document.getElementById(element).style.animation = "showmenu 0.35s ease";
+}
+
+function hide(element) {
+ document.getElementById(element).style.animation = "hidemenu 0.3s ease";
+ setTimeout(function() {
+ $("#" + element).css("display", "none")
+ }, 300)
+}
+
+function isNumber(evt) {
+ evt = (evt) ? evt : window.event
+ var charCode = (evt.which) ? evt.which : evt.keyCode
+ if (charCode > 31 && (charCode < 48 || charCode > 57)) {
+ return false
+ }
+ return true
+}
+
+////////////////////////////////////////////// DRAGGING //////////////////////////////////////////////////
+
+OGPos = {}
+
+function dragElement(elmnt, item, keyid, index) {
+ var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
+
+ if (OGPos[item] == null){
+ OGPos[item] = { x: null, y: null }
+ OGPos[item].x = elmnt.offsetLeft
+ OGPos[item].y = elmnt.offsetTop
+ }
+
+ elmnt.onmousedown = dragMouseDown
+
+ function dragMouseDown(e) {
+ e = e || window.event;
+ e.preventDefault();
+
+ pos3 = e.clientX;
+ pos4 = e.clientY;
+ document.onmouseup = closeDragElement;
+ document.onmousemove = elementDrag;
+ }
+
+ function elementDrag(e) {
+ e = e || window.event
+ e.preventDefault()
+
+ pos1 = pos3 - e.clientX
+ pos2 = pos4 - e.clientY
+ pos3 = e.clientX
+ pos4 = e.clientY
+
+ elmnt.style.opacity = "0.8"
+
+ elmnt.style.top = (elmnt.offsetTop - pos2) + "px"
+ elmnt.style.left = (elmnt.offsetLeft - pos1) + "px"
+
+ if (elementsOverlap(elmnt, document.getElementById("key_panel"), true) == false && elementsOverlap(elmnt, document.getElementById("actions_panel")) == false){
+ closeDragElement(e)
+ }
+
+ for (let i = (CurrentKeyPage*24)-24; i < CurrentKeyPage*24; i++) {
+ if (document.getElementById("empty_"+i) != undefined){
+ if (elementsOverlapPrecise(document.getElementById("empty_"+i), e.clientX, e.clientY) == false){
+ document.getElementById('empty_'+i).classList.remove("hovered")
+ }
+ else{
+ document.getElementById('empty_'+i).classList.add("hovered")
+ }
+ }
+ }
+
+ for (let i = 0; i < ClosestPlayers.length; i++) {
+ if (elementsOverlapPrecise(document.getElementById("player_"+i), e.clientX, e.clientY) == false){
+ document.getElementById('player_'+i).classList.remove("hovered")
+ }
+ else{
+ document.getElementById('player_'+i).classList.add("hovered")
+ }
+ }
+
+ if (elementsOverlapPrecise(document.getElementById("key_delete"), e.clientX, e.clientY) == false){
+ document.getElementById('key_delete').classList.remove("hovered")
+ }
+ else{
+ document.getElementById('key_delete').classList.add("hovered")
+ }
+ }
+
+ function closeDragElement(e) {
+ UseElement(elmnt, item, e.clientX, e.clientY, keyid, index)
+ elmnt.style.opacity = "1"
+ document.onmouseup = null;
+ document.onmousemove = null;
+ }
+}
+
+function UseElement(elmnt, item, X, Y, keyid, index){
+ let elm = elmnt
+
+ for (let i = (CurrentKeyPage*24)-24; i < CurrentKeyPage*24; i++) {
+ if (document.getElementById("empty_"+i) != undefined){
+ if (elementsOverlapPrecise(document.getElementById("empty_"+i), X, Y) == false){
+ document.getElementById('empty_'+i).classList.remove("hovered")
+ }
+ else{
+ for (const key in ChangedKeys) {
+ if (ChangedKeys[key].keyid == keyid){
+ ChangedKeys.splice(key, 1)
+ }
+ }
+ for (const key in MyKeys) {
+ if (MyKeys[key].id == keyid){
+ if (MyKeys[key].oldSlot == undefined || MyKeys[key].oldSlot != i+1){
+ ChangedKeys.push({keyid: keyid, slot:i+1, plate:MyKeys[key].plate})
+ break
+ }
+ }
+ }
+
+ MakeKeysNotDraggable()
+ if (MyKeys[index].oldSlot == undefined){
+ MyKeys[index].oldSlot = MyKeys[index].slot
+ }
+ MyKeys[index].slot = i+1
+ InsertDataToPanel()
+ MakeKeysDraggable()
+ return
+ }
+ }
+ }
+
+ for (let i = 0; i < ClosestPlayers.length; i++) {
+ if (elementsOverlapPrecise(document.getElementById("player_"+i), X, Y)){
+ $.post('https://' + GetParentResourceName() + '/UseButton', JSON.stringify({action: "giveKey", playerid:ClosestPlayers[i].id, keyid, table:ChangedKeys}))
+ document.getElementById('player_'+i).classList.remove("hovered")
+ ChangedKeys = []
+ return
+ }
+ }
+
+ if (elementsOverlapPrecise(document.getElementById("key_delete"), X, Y)){
+ $.post('https://' + GetParentResourceName() + '/UseButton', JSON.stringify({action: "deleteKey", keyid, table:ChangedKeys, plate:MyKeys[index].plate}))
+ document.getElementById('key_delete').classList.remove("hovered")
+ ChangedKeys = []
+ return
+ }
+
+ /* if (elementsOverlap(elm, document.getElementById("head_box")) && Damages.head){
+ ItemAnim(elm, item)
+ }
+ else if(elementsOverlap(elm, document.getElementById("head_box")) && Damages.head == false){
+ WrongItemAnim(elm, item)
+ }
+ else{
+ elm.style.top = OGPos[item].y + "px"
+ elm.style.left = OGPos[item].x + "px"
+ }*/
+
+ elm.style.top = OGPos[item].y + "px"
+ elm.style.left = OGPos[item].x + "px"
+}
+
+function ItemAnim(elm, item, part){
+ elm.style.animation = 'none';
+ elm.offsetHeight;
+ elm.style.animation = "itemuseanim 1.2s";
+ setTimeout(function(){
+ elm.style.top = OGPos[item].y + "px"
+ elm.style.left = OGPos[item].x + "px"
+ elm.style.animation = 'none';
+ Close()
+ },1200)
+
+ $.post('https://'+GetParentResourceName()+'/UseButton', JSON.stringify({action:"MedicerMenu", type:"useitem", item, you:Mediceryou, part}))
+}
+
+function WrongItemAnim(elm, item){
+ elm.style.animation = 'none';
+ elm.offsetHeight;
+ elm.style.animation = "itemnotuseanim 0.8s";
+ setTimeout(function(){
+ elm.style.top = OGPos[item].y + "px"
+ elm.style.left = OGPos[item].x + "px"
+ elm.style.animation = 'none';
+ },800)
+}
+
+function elementsOverlap(el1, el2, value) {
+ const domRect1 = el1.getBoundingClientRect();
+ const domRect2 = el2.getBoundingClientRect();
+
+ if (value){
+ return !(
+ domRect1.top+50 > domRect2.bottom ||
+ domRect1.right-50 < domRect2.left ||
+ domRect1.bottom-50 < domRect2.top ||
+ domRect1.left-50 > domRect2.right
+ );
+ }
+ else{
+ return !(
+ domRect1.top+50 > domRect2.bottom ||
+ domRect1.right-50 < domRect2.left ||
+ domRect1.bottom-50 < domRect2.top ||
+ domRect1.left+50 > domRect2.right
+ );
+ }
+}
+
+function elementsOverlapPrecise(el1, X, Y) {
+ const domRect1 = el1.getBoundingClientRect();
+
+ return !(
+ domRect1.top > Y ||
+ domRect1.right < X ||
+ domRect1.bottom < Y ||
+ domRect1.left > X
+ );
+
+}
diff --git a/resources/[housing]/brutal_keys/html/style.css b/resources/[housing]/brutal_keys/html/style.css
new file mode 100644
index 000000000..f57b2f4f8
--- /dev/null
+++ b/resources/[housing]/brutal_keys/html/style.css
@@ -0,0 +1,1032 @@
+@import url('https://fonts.cdnfonts.com/css/futura-pt');
+
+body{
+ font-family: 'Oswald', sans-serif;
+ user-select: none;
+ background: none;
+ font-display: fallback;
+}
+
+:root{
+ --main_color: rgb(251, 133, 16);
+ --background_color: rgb(34, 34, 39);
+ --text_color: white;
+}
+
+button{
+ outline: none;
+ border: none;
+ cursor: pointer;
+ transition: all 0.2s;
+}
+
+button:disabled{
+ opacity: 0.6;
+ cursor: none;
+}
+
+button:hover{
+ cursor: pointer;
+ filter: brightness(110%);
+}
+
+button:disabled:hover{
+ filter: none;
+}
+
+button:active{
+ transition: all 0.05s;
+ filter: brightness(70%);
+ scale: 0.9;
+}
+
+button:disabled:active{
+ filter: none;
+ scale: 1;
+}
+
+::-webkit-scrollbar {
+ display: none;
+ width: 10px;
+}
+
+::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+::-webkit-scrollbar-thumb {
+ background: #2129326b;
+ border-radius: 3vh;
+ border: solid 1px rgba(255, 255, 255, 0.432);
+}
+
+hr{
+ height: 2px;
+ background-color: white;
+}
+
+.input-group input{
+ background-color: rgba(255, 255, 255, 0.137);
+ color: white;
+ border: solid 2px rgba(211, 211, 211, 0.705);
+ outline: none;
+ text-align: center;
+ font-weight: 400;
+ font-size: 22px !important;
+ padding-top: 10px !important;
+}
+
+.input-group input::placeholder{
+ color: #ffffff8e;
+}
+
+.input-group input:focus{
+ color: white;
+ background-color: rgba(255, 255, 255, 0.267);
+ border: solid 2px rgba(211, 211, 211, 0.959);
+ outline: none;
+ box-shadow: none;
+}
+
+.input-group input:disabled{
+ background-color: rgba(52, 63, 73, 0.295);
+ color: #b1b1b1;
+ border: none;
+ outline: none;
+ text-align: center;
+}
+
+.custom-checkbox .form-check-label{
+ color: white;
+ font-size: 24px;
+ font-weight: 500;
+}
+
+.custom-checkbox .form-check-input{
+ height: 25px;
+ width: 25px;
+ background-color: rgba(255, 255, 255, 0.205);
+ border: solid 2px #D3D3D3;
+}
+
+.custom-checkbox .form-check-input:checked{
+ border: solid 3px #ffffff;
+ background-color: #79cfe0b2;
+ background-image: none;
+}
+
+.range::-webkit-slider-thumb{
+ -webkit-appearance: none;
+ appearance: none;
+ height: 15px;
+ width: 15px;
+ background: rgba(255, 255, 255, 0.7);
+ transform: rotate(45deg);
+ cursor: pointer;
+ box-shadow: none;
+ border: solid 2px white;
+ transition: 0.3s;
+}
+
+.range::-webkit-slider-thumb:active{
+ background: #32EBBE;
+}
+
+.range{
+ -webkit-appearance: none;
+ width: 80%;
+ height: 3px;
+ border-radius: 10px;
+ outline: none;
+ background-color: #B7B7B7;
+}
+
+.custom-control.ios-switch {
+ scale: 1.6;
+ margin-bottom: 15px;
+}
+
+.custom-control.ios-switch .ios-switch-control-input {
+ display: none;
+}
+
+.custom-control.ios-switch .ios-switch-control-input:active~.ios-switch-control-indicator::after {
+ width: 20px;
+}
+
+.custom-control.ios-switch .ios-switch-control-input:checked~.ios-switch-control-indicator {
+ border: 10px solid #79cfe0;
+ background: #79cfe0;
+}
+
+.custom-control.ios-switch .ios-switch-control-input:checked~.ios-switch-control-indicator::after {
+ top: -8px;
+ left: 8px;
+}
+
+.custom-control.ios-switch .ios-switch-control-input:checked:active~.ios-switch-control-indicator::after {
+ left: 4px;
+}
+
+.custom-control.ios-switch .ios-switch-control-indicator {
+ display: inline-block;
+ position: relative;
+ margin: 0 10px;
+ top: 4px;
+ width: 36px;
+ height: 20px;
+ background: rgb(51, 49, 70);
+ border-radius: 16px;
+ transition: 0.3s;
+ border: 2px solid rgb(54, 72, 94);
+}
+
+.custom-control.ios-switch .ios-switch-control-indicator::after {
+ content: '';
+ display: block;
+ position: absolute;
+ width: 16px;
+ height: 16px;
+ border-radius: 16px;
+ transition: 0.3s;
+ top: 0px;
+ left: 0px;
+ background: rgb(236, 236, 236);
+}
+
+.dropdown .dropdown-menu{
+ position: absolute !important;
+ background-color: rgba(61, 61, 61, 0.705);
+ backdrop-filter: blur(20px);
+ border: none;
+ max-height: 200px;
+ overflow-y: auto;
+ border-radius: 0px;
+ padding: 10px;
+ z-index: 999999 !important;
+ padding-bottom: 20px;
+}
+
+.dropdown .dropdown-menu .dropdown-item{
+ color: white;
+ height: 40px;
+ transition: all 0.3s ease;
+ line-height: 30px;
+ font-size: 23px;
+ font-weight: 300;
+ border: solid 1px white;
+ text-align: center;
+ font-family: 'Futura PT', sans-serif;
+ text-shadow: 5px 1px 0px rgba(0, 0, 0, 0.479);
+}
+
+.dropdown .dropdown-menu .dropdown-item:hover{
+ background-color: rgba(192, 192, 192, 0.493);
+}
+
+.dropdown .dropdown-menu .dropdown-item img{
+ height: 35px;
+ width: 35px;
+ margin-top: -5px;
+ margin-left: -10px;
+ border-radius: 6px;
+}
+
+.dropdown .dropdown-menu .time{
+ position: absolute;
+ bottom: 0px;
+ right: 10px;
+ font-size: 15px;
+ font-family: 'Futura PT', sans-serif;
+ color: white;
+ font-weight: 300;
+}
+
+/* //////////////////////////////////////////////////////// MAIN THINGS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/
+
+.key_menu{
+ display: none;
+}
+
+.key_menu .animation_element{
+ position: absolute;
+ top: 150%;
+ left: -50%;
+ height: 150%;
+ width: 140%;
+ transform: translate(-50%, -50%) rotate(-45deg);
+ background-color: white;
+ animation: animation_element 1.5s ease both;
+ z-index: 10000;
+}
+
+@keyframes animation_element {
+ 99%{
+ opacity: 1;
+ }
+ 100%{
+ transform: translate(150%, -200%) rotate(-45deg);
+ opacity: 0;
+ }
+}
+
+@keyframes reverse_animation_element {
+ 0%{
+ transform: translate(150%, -200%) rotate(-45deg);
+ }
+ 99%{
+ opacity: 1;
+ }
+ 100%{
+ opacity: 0;
+ }
+}
+
+.key_menu .overflow_container{
+ position: absolute;
+ top: 50%;
+ right: 40px;
+ transform: translate(0%, -50%);
+ height: 690px;
+ width: 420px;
+ overflow: hidden;
+ transition: all 0.3s ease;
+ z-index: 100;
+ animation: overflow_container 2.5s both;
+}
+
+@keyframes overflow_container {
+ 99%{
+ overflow: hidden;
+ }
+ 100%{
+ overflow: visible;
+ }
+}
+
+@keyframes reverse_overflow_container {
+ 99%{
+ overflow: hidden;
+ }
+ 100%{
+ overflow: hidden;
+ }
+}
+
+.key_menu .panel{
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ height: 670px;
+ width: 400px;
+ transform: translate(-50%, -50%);
+ background-color: rgba(61, 61, 61, 0.425);
+ backdrop-filter: blur(25px);
+ font-family: 'Futura PT', sans-serif;
+ animation: panel 1.5s ease both;
+}
+
+@keyframes panel {
+ 0%{
+ opacity: 0;
+ }
+ 20%{
+ opacity: 0;
+ }
+ 40%{
+ opacity: 1;
+ }
+}
+
+@keyframes reverse_panel {
+ 0%{
+ opacity: 1;
+ }
+ 22%{
+ opacity: 1;
+ }
+ 50%{
+ opacity: 0;
+ }
+ 100%{
+ opacity: 0;
+ }
+}
+
+.panel .panel_border_top{
+ position: absolute;
+ top: -10px;
+ left: -10px;
+ height: 25px;
+ width: 25px;
+ background-image: url('assets/panel_border.svg');
+ background-repeat: no-repeat;
+ background-position: center;
+}
+
+.panel .panel_border_bottom{
+ position: absolute;
+ bottom: -10px;
+ right: -10px;
+ transform: rotate(180deg);
+ height: 25px;
+ width: 25px;
+ background-image: url('assets/panel_border.svg');
+ background-repeat: no-repeat;
+ background-position: center;
+}
+
+.panel .top_line{
+ position: absolute;
+ top: 16px;
+ left: 14px;
+ height: 1px;
+ width: 230px;
+ background-color: rgba(255, 255, 255, 0.37);
+ animation: top_line 4s ease;
+}
+
+@keyframes top_line {
+ 0%{
+ transform: scaleX(70%) translateX(-20%);
+ }
+ 100%{
+ transform: scaleX(100%);
+ }
+}
+
+.panel .top_box{
+ position: absolute;
+ top: 14px;
+ left: 14px;
+ height: 4px;
+ width: 4px;
+ background-color: rgba(255, 255, 255, 0.616);
+}
+
+.panel .title{
+ position: absolute;
+ top: 20px;
+ left: 20px;
+ font-weight: 400;
+ font-size: 43px;
+ line-height: 48px;
+ color: #FFFFFF;
+ text-shadow: 6px 1px 0px rgba(0, 0, 0, 0.30);
+}
+
+.panel .action_btn{
+ position: relative;
+ float: right;
+ height: 45px;
+ padding-inline: 15px;
+ font-weight: 300;
+ font-size: 22px;
+ line-height: 24px;
+ color: #FFFFFF;
+ text-shadow: 5px 1px 0px rgba(0, 0, 0, 0.40);
+ background-color: transparent;
+ filter: brightness(100%);
+ opacity: 1;
+ overflow: hidden;
+}
+
+.panel .action_btn:hover::before, .key_menu .panel .action_btn:hover::after{
+ filter: brightness(100%);
+}
+
+.action_btn::before, .action_btn::after{
+ content: "";
+ position: absolute;
+ height: 15px;
+ width: 15px;
+ background-image: url('assets/panel_border.svg');
+ background-size: cover;
+ background-repeat: no-repeat;
+ background-position: center;
+ filter: brightness(70%);
+ transition: all 0.3s ease;
+}
+
+.panel .action_btn::before{
+ top: 0px;
+ left: 0px;
+}
+
+.panel .action_btn::after{
+ bottom: 0px;
+ right: 0px;
+ transform: rotate(180deg);
+}
+
+.panel .action_btn .bg{
+ position: absolute;
+ top: 0;
+ left: 0;
+ height: 100%;
+ width: 100%;
+ background-color: #21cc9a;
+ box-shadow: 0px 0px 10px 0px #21cc99a8;
+ z-index: -10;
+ transform: scaleX(0.01) rotate(-55deg);
+ transition: all 0.6s ease;
+ opacity: 0;
+}
+
+.panel .action_btn.active .bg{
+ transform: scaleX(2) rotate(-55deg);
+ opacity: 1;
+}
+
+.panel .action_btn .line{
+ position: absolute;
+ bottom: 7px;
+ left: 10px;
+ height: 1px;
+ width: 60%;
+ background-color: white;
+ transition: all 0.6s ease;
+}
+
+.panel .action_btn:hover .line{
+ transform: scaleX(1.17) translateX(5px);
+}
+
+.panel .page_selector_container{
+ position: absolute;
+ top: 70px;
+ left: 20px;
+}
+
+.panel .page_selector_container .page_element{
+ display: inline-block;
+ width: 0px;
+ height: 0px;
+ border-style: solid;
+ border-width: 20px 25px 0 0;
+ border-color: #32ebbd4d #32ebbd transparent transparent;
+ margin-inline: 3px;
+ transition: all 0.3s ease;
+ background-color: transparent;
+ cursor: pointer;
+ outline: rgba(255, 255, 255, 0.747);
+}
+
+.panel .page_selector_container .page_element.choosed{
+ border-color: #32EBBE #32EBBE transparent transparent;
+ outline: solid 2px rgba(255, 255, 255, 0.747);
+ border-width: 20px 40px 0 0;
+}
+
+.page_element:hover{
+ filter: brightness(110%);
+}
+
+.page_element:active{
+ transition: all 0.05s;
+ filter: brightness(70%);
+ scale: 0.9;
+}
+
+.key_menu .panel .keys_container{
+ position: relative;
+ margin-top: 85px;
+ height: 580px;
+ width: 100%;
+ display: flex;
+ flex-wrap: wrap;
+ padding-inline: 10px;
+ margin-bottom: 20px;
+ padding-bottom: 8px;
+}
+
+.key_menu .panel .keys_container .key_element{
+ position: relative;
+ height: 78px;
+ width: 78px;
+ margin-top: 16px;
+ margin-inline: 8px;
+ transition: background-color 0.3s ease;
+ cursor: pointer;
+}
+
+.key_menu .panel .keys_container .key_element.hovered{
+ background-color: rgba(121, 215, 190, 0.349);
+ border: solid 1px rgba(255, 255, 255, 0.247);
+}
+
+.key_menu .panel .keys_container .key_element .key_border{
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ height: 80px;
+ width: 80px;
+ background-image: url('assets/basic_key.svg');
+ background-repeat: no-repeat;
+ background-position: center;
+}
+
+.key_menu .panel .keys_container .key_element.hovered .key_border{
+ background-image: url('assets/selected_key.svg');
+ filter: brightness(0) saturate(100%) invert(73%) sepia(96%) saturate(177%) hue-rotate(109deg) brightness(92%) contrast(86%);
+}
+
+.key_menu .panel .keys_container .key_element .number{
+ position: absolute;
+ top: 3px;
+ right: 5px;
+ font-style: normal;
+ font-weight: 400;
+ font-size: 20px;
+ line-height: 20px;
+ text-align: center;
+ color: rgba(255, 255, 255, 0.65);
+ text-shadow: 4px 1px 0px rgba(0, 0, 0, 0.24);
+}
+
+.key_menu .panel .keys_container .key_element.hovered .number{
+ color: white;
+}
+
+.key_menu .panel .keys_container .key_element .temporary{
+ position: absolute;
+ top: 5px;
+ left: 5px;
+ width: 0;
+ height: 0;
+ border-style: solid;
+ border-width: 10px 10px 0 0;
+ border-color: #32EBBE #32ebbd3d transparent transparent;
+ animation: temporary 4s ease-in-out infinite;
+}
+
+@keyframes temporary {
+ 50%{
+ border-color: #32ebbd3d #32EBBE transparent transparent;
+ }
+}
+
+.key_menu .panel .keys_container .key_element .name{
+ position: absolute;
+ bottom: 5px;
+ width: 100%;
+ font-style: normal;
+ font-weight: 400;
+ font-size: 14px;
+ line-height: 14px;
+ text-align: center;
+ color: rgba(255, 255, 255, 0.65);
+ text-shadow: 4px 1px 0px rgba(0, 0, 0, 0.24);
+}
+
+.key_menu .panel .keys_container .key_element .key_image{
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ height: 80%;
+ width: 80%;
+ background-image: url('assets/key.svg');
+ background-repeat: no-repeat;
+ background-position: center;
+}
+
+.key_menu .panel .keys_container .key_element .key_image.vehicle{
+ background-image: url('assets/vehicle_key.svg');
+}
+
+.key_menu .panel .keys_container .key_element .dropdown{
+ position: absolute;
+ height: 100%;
+ width: 100%;
+}
+
+.key_menu .panel .keys_container .key_element .dropdown .dropdown_btn{
+ position: absolute;
+ height: 100%;
+ width: 100%;
+ background-color: transparent;
+}
+
+.actions_section{
+ display: none;
+}
+
+.key_menu .actions_section #actions_panel{
+ position: absolute;
+ top: 50%;
+ right: 20px;
+ transform: translate(0%, -50%);
+ width: 110px;
+ background-color: rgba(61, 61, 61, 0.425);
+ backdrop-filter: blur(25px);
+ font-family: 'Futura PT', sans-serif;
+ z-index: 10;
+ overflow-y: auto;
+ border-top: solid white 3px;
+ transition: all 0.3s ease;
+}
+
+@keyframes actions_panel {
+ 0%{
+ transform: translate(0px, -50%) scale(1, 0.0);
+ }
+ 100%{
+ transform: translate(0px, -50%);
+ }
+}
+
+@keyframes reverse_actions_panel {
+ 0%{
+ transform: translate(0px, -50%);
+ }
+ 100%{
+ transform: translate(0px, -50%) scale(1, 0.0);
+ }
+}
+
+.key_menu .actions_section #actions_panel .key_deleting{
+ position: relative;
+ margin: 16px;
+ height: 80px;
+ width: 80px;
+ background-image: url('assets/selected_key.svg');
+ background-repeat: no-repeat;
+ background-position: center;
+ filter: brightness(0) saturate(100%) invert(49%) sepia(41%) saturate(4609%) hue-rotate(329deg) brightness(99%) contrast(97%);
+ transition: all 0.3s ease;
+}
+
+.key_menu .actions_section #actions_panel .key_deleting.hovered{
+ background-color: #f9545485;
+}
+
+.key_menu .actions_section #actions_panel .key_deleting .bg{
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ height: 78px;
+ width: 78px;
+ background-image: repeating-linear-gradient(45deg, #f95454 0, #f95454 11px, transparent 0, transparent 50%);
+ background-size: 30px 30px;
+ background-color: rgba(0, 0, 0, 0.342);
+ opacity: 0.3;
+}
+
+.key_menu .actions_section #actions_panel .key_deleting .bin{
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ height: 40%;
+ width: 40%;
+ background-image: url('assets/bin.png');
+ background-repeat: no-repeat;
+ background-position: center;
+ background-size: contain;
+}
+
+.key_menu .actions_section #actions_panel hr{
+ border: none;
+ height: 2px;
+ background-color: rgba(255, 255, 255, 0.671);
+ opacity: 1;
+ width: 70%;
+ margin-left: 15%;
+}
+
+.key_menu .actions_section #actions_panel .people_container{
+ position: relative;
+ width: 100%;
+}
+
+.key_menu .actions_section #actions_panel .people_container .people_element{
+ position: relative;
+ height: 78px;
+ width: 78px;
+ margin-bottom: 16px;
+ margin-inline: 16px;
+ transition: all 0.3s ease;
+ cursor: pointer;
+}
+
+.key_menu .actions_section #actions_panel .people_container .people_element.hovered{
+ background-color: rgba(255, 255, 255, 0.253);
+}
+
+.key_menu .actions_section #actions_panel .people_container .people_element .element_border{
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ height: 80px;
+ width: 80px;
+ background-image: url('assets/basic_key.svg');
+ background-repeat: no-repeat;
+ background-position: center;
+}
+
+.key_menu .actions_section #actions_panel .people_container .people_element.hovered .element_border{
+ background-image: url('assets/selected_key.svg');
+}
+
+.key_menu .actions_section #actions_panel .people_container .people_element .people_image{
+ position: absolute;
+ top: 40%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ height: 70%;
+ width: 70%;
+ background-image: url('assets/people.png');
+ background-size: contain;
+ background-repeat: no-repeat;
+ background-position: center;
+}
+
+.key_menu .actions_section #actions_panel .people_container .people_element .id{
+ position: absolute;
+ bottom: 0px;
+ width: 100%;
+ font-style: normal;
+ font-weight: 400;
+ font-size: 18px;
+ line-height: 24px;
+ text-align: center;
+ color: rgb(255, 255, 255);
+ text-shadow: 4px 1px 0px rgba(0, 0, 0, 0.24);
+}
+
+.key_menu .actions_section #actions_panel .people_container .people_element .text{
+ position: absolute;
+ top: 45%;
+ transform: translate(0%, -50%);
+ width: 100%;
+ font-style: normal;
+ font-weight: 400;
+ font-size: 65px;
+ text-align: center;
+ color: rgba(255, 255, 255, 0.404);
+}
+
+.key_purchase_menu{
+ display: none;
+}
+
+.key_purchase_menu .panel{
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 400px;
+ transform: translate(-50%, -50%);
+ background-color: rgba(61, 61, 61, 0.425);
+ backdrop-filter: blur(25px);
+ font-family: 'Futura PT', sans-serif;
+ animation: panel 1.5s ease both;
+}
+
+.key_purchase_menu .panel .description{
+ position: absolute;
+ top: 65px;
+ left: 20px;
+ font-weight: 200;
+ font-size: 15px;
+ line-height: 17px;
+ color: #ffffffb6;
+ width: 300px;
+}
+
+.key_purchase_menu .panel .vehicles_container{
+ position: relative;
+ margin-top: 40px;
+ max-height: 570px;
+ width: 100%;
+ display: flex;
+ flex-wrap: wrap;
+ padding-inline: 8px;
+ margin-bottom: 10px;
+ padding-bottom: 16px;
+ overflow-y: auto;
+}
+
+.key_purchase_menu .panel .vehicles_container .vehicle_element{
+ position: relative;
+ height: 60px;
+ width: 380px;
+ margin-top: 16px;
+ margin-inline: 8px;
+ background-color: rgba(255, 255, 255, 0.171);
+ backdrop-filter: blur(60px);
+}
+
+.key_purchase_menu .panel .vehicles_container .vehicle_element .plate{
+ position: absolute;
+ top: 50%;
+ left: 20px;
+ transform: translate(0%, -50%);
+ color: white;
+ font-size: 23px;
+ text-shadow: 5px 1px 0px rgba(0, 0, 0, 0.397);
+ font-weight: 300;
+}
+
+.key_purchase_menu .panel .vehicles_container .vehicle_element .price{
+ position: absolute;
+ top: 50%;
+ right: 120px;
+ transform: translate(0%, -50%);
+ color: #79D7BE;
+ font-size: 23px;
+ font-weight: 500;
+}
+
+.key_purchase_menu .panel .vehicles_container .vehicle_element .action_btn{
+ position: absolute;
+ top: 50%;
+ right: 15px;
+ transform: translate(0%, -50%);
+}
+
+.key_purchase_menu .panel .vehicles_container .vehicle_element .element_border{
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ height: 60px;
+ width: 370px;
+ background-image: url('assets/basic_vehicle.svg');
+ background-repeat: no-repeat;
+ background-position: center;
+ background-size: contain;
+}
+
+@keyframes showmenu {
+ 0%{
+ opacity: 0;
+ }
+}
+
+@keyframes hidemenu {
+ 100%{
+ opacity: 0;
+ }
+}
+
+.key_notify{
+ position: absolute;
+ bottom: 50px;
+ right: 200px;
+ height: 60px;
+ width: 220px;
+ font-family: 'Futura PT', sans-serif;
+ display: none;
+}
+
+.key_notify .container{
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ height: 100%;
+ width: 100%;
+ transform: translate(-50%, -50%);
+ background-color: #3d3d3dd8;
+ animation: container 1s ease both;
+ overflow: hidden;
+}
+
+@keyframes container {
+ 0%{
+ width: 0%;
+ transform: translate(-50%, -50%) scaleX(0);
+ }
+ 40%{
+ transform: translate(-50%, -50%) scaleX(1);
+ }
+ 100%{
+ width: 100%;
+ }
+}
+
+@keyframes reverse_container {
+ 0%{
+ width: 100%;
+ }
+ 60%{
+ transform: translate(-50%, -50%) scaleX(1);
+ }
+ 100%{
+ width: 0%;
+ transform: translate(-50%, -50%) scaleX(0);
+ }
+}
+
+
+.key_notify .container::before, .key_notify .container::after{
+ content: "";
+ position: absolute;
+ height: 20px;
+ width: 20px;
+ background-image: url('assets/panel_border.svg');
+ background-size: cover;
+ background-repeat: no-repeat;
+ background-position: center;
+}
+
+.key_notify .container::before{
+ top: 0px;
+ left: 0px;
+}
+
+.key_notify .container::after{
+ bottom: 0px;
+ right: 0px;
+ transform: rotate(180deg);
+}
+
+.key_notify .text{
+ position: absolute;
+ top: 35%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ text-align: center;
+ color: white;
+ font-size: 20px;
+ text-shadow: 5px 1px 0px rgba(0, 0, 0, 0.397);
+ white-space: nowrap;
+}
+
+.key_notify .line{
+ position: absolute;
+ bottom: 10px;
+ left: 50%;
+ transform: translate(-50%, 0%);
+ height: 10px;
+ width: 200px;
+ background-image: url('assets/door_line.svg');
+ background-size: cover;
+ background-repeat: no-repeat;
+ background-position: center;
+}
+
+.key_notify .line.red{
+ filter: brightness(0) saturate(100%) invert(52%) sepia(82%) saturate(3630%) hue-rotate(329deg) brightness(100%) contrast(97%);
+}
+
+.key_notify .line .prog_line{
+ position: absolute;
+ left: 7%;
+ width: 93%;
+ height: 100%;
+ background-color: #79D7BE;
+ animation: prog_line 2s ease both;
+}
+
+@keyframes prog_line {
+ 0%{
+ width: 0;
+ }
+ 100%{
+ width: 93%;
+ }
+}
\ No newline at end of file
diff --git a/resources/[housing]/brutal_keys/locales/ar.json b/resources/[housing]/brutal_keys/locales/ar.json
new file mode 100644
index 000000000..3e6da1d11
--- /dev/null
+++ b/resources/[housing]/brutal_keys/locales/ar.json
@@ -0,0 +1,13 @@
+{
+ "keys": "مفتاح",
+ "actions": "الإجراءات",
+ "back": "رجوع",
+ "buy_keys": "شراء مفتاح",
+ "buy_keys_des": "هنا يمكنك شراء مفتاح جديد إذا فقدت جميع المفاتيح.",
+ "doors_unlocked": "الباب مفتوح",
+ "doors_locked": "الباب مغلق",
+ "buy": "شراء",
+ "copy": "نسخ",
+ "change_lock": "تغيير القفل",
+ "delete_all": "حذف الكل"
+}
diff --git a/resources/[housing]/brutal_keys/locales/cs.json b/resources/[housing]/brutal_keys/locales/cs.json
new file mode 100644
index 000000000..5f4e28f3c
--- /dev/null
+++ b/resources/[housing]/brutal_keys/locales/cs.json
@@ -0,0 +1,13 @@
+{
+ "keys": "KLÍČ",
+ "actions": "AKCE",
+ "back": "ZPĚT",
+ "buy_keys": "KOUPIT KLÍČ",
+ "buy_keys_des": "Zde si můžeš koupit nový klíč, pokud jsi všechny ztratil.",
+ "doors_unlocked": "DVEŘE OTEVŘENÉ",
+ "doors_locked": "DVEŘE ZAVŘENÉ",
+ "buy": "KOUPIT",
+ "copy": "KOPÍROVAT",
+ "change_lock": "ZMĚNIT ZÁMEK",
+ "delete_all": "SMAZAT VŠE"
+}
diff --git a/resources/[housing]/brutal_keys/locales/de.json b/resources/[housing]/brutal_keys/locales/de.json
new file mode 100644
index 000000000..5a8448e71
--- /dev/null
+++ b/resources/[housing]/brutal_keys/locales/de.json
@@ -0,0 +1,13 @@
+{
+ "keys": "SCHLUSS",
+ "actions": "AKTIONEN",
+ "back": "ZURUCK",
+ "buy_keys": "KAUF SCHLUSS",
+ "buy_keys_des": "Hier kannst du einen neuen Schluss kaufen, wenn alle verloren sind.",
+ "doors_unlocked": "TUR OFFEN",
+ "doors_locked": "TUR ZU",
+ "buy": "KAUFEN",
+ "copy": "KOPIEREN",
+ "change_lock": "SCHLOSS WECHSEL",
+ "delete_all": "ALLES LOSCHEN"
+}
diff --git a/resources/[housing]/brutal_keys/locales/desktop.ini b/resources/[housing]/brutal_keys/locales/desktop.ini
new file mode 100644
index 000000000..704fc3d2f
Binary files /dev/null and b/resources/[housing]/brutal_keys/locales/desktop.ini differ
diff --git a/resources/[housing]/brutal_keys/locales/en.json b/resources/[housing]/brutal_keys/locales/en.json
new file mode 100644
index 000000000..836704e6f
--- /dev/null
+++ b/resources/[housing]/brutal_keys/locales/en.json
@@ -0,0 +1,13 @@
+{
+ "keys": "KEYS",
+ "actions": "ACTIONS",
+ "back": "BACK",
+ "buy_keys": "BUY KEYS",
+ "buy_keys_des": "Here you can buy keys for your vehicles if you have managed to lose all of them.",
+ "doors_unlocked": "DOORS UNLOCKED",
+ "doors_locked": "DOORS LOCKED",
+ "buy": "BUY",
+ "copy": "COPY",
+ "change_lock": "CHANGE LOCK",
+ "delete_all": "DELETE ALL"
+}
diff --git a/resources/[housing]/brutal_keys/locales/fr.json b/resources/[housing]/brutal_keys/locales/fr.json
new file mode 100644
index 000000000..cad03c324
--- /dev/null
+++ b/resources/[housing]/brutal_keys/locales/fr.json
@@ -0,0 +1,13 @@
+{
+ "keys": "CLE",
+ "actions": "ACTIONS",
+ "back": "RETOUR",
+ "buy_keys": "ACHAT CLE",
+ "buy_keys_des": "Ici, tu peux acheter une nouvelle cle si toutes sont perdues.",
+ "doors_unlocked": "PORTE OUVERTE",
+ "doors_locked": "PORTE FERME",
+ "buy": "ACHETER",
+ "copy": "COPIER",
+ "change_lock": "CHANGER SERRURE",
+ "delete_all": "SUPPRIMER TOUT"
+}
diff --git a/resources/[housing]/brutal_keys/locales/hu.json b/resources/[housing]/brutal_keys/locales/hu.json
new file mode 100644
index 000000000..8871af6c8
--- /dev/null
+++ b/resources/[housing]/brutal_keys/locales/hu.json
@@ -0,0 +1,13 @@
+{
+ "keys": "KULCS",
+ "actions": "MŰVELETEK",
+ "back": "VISSZA",
+ "buy_keys": "VÁSÁRLÁS",
+ "buy_keys_des": "Itt vásárolhatsz kulcsokat a járműveidhez, ha az összeset elvesztetted.",
+ "doors_unlocked": "AJTÓK NYITVA",
+ "doors_locked": "AJTÓK ZÁRVA",
+ "buy": "VÁSÁRLÁS",
+ "copy": "MÁSOLÁS",
+ "change_lock": "ZÁRCSERE",
+ "delete_all": "ÖSSZES TÖRLÉSE"
+}
diff --git a/resources/[housing]/brutal_keys/locales/it.json b/resources/[housing]/brutal_keys/locales/it.json
new file mode 100644
index 000000000..3273cdb10
--- /dev/null
+++ b/resources/[housing]/brutal_keys/locales/it.json
@@ -0,0 +1,13 @@
+{
+ "keys": "CHIAVE",
+ "actions": "AZIONI",
+ "back": "INDIETRO",
+ "buy_keys": "COMPRA CHIAVE",
+ "buy_keys_des": "Qui puoi comprare una nuova chiave se le hai perse tutte.",
+ "doors_unlocked": "PORTA APERTA",
+ "doors_locked": "PORTA CHIUSA",
+ "buy": "COMPRA",
+ "copy": "COPIA",
+ "change_lock": "CAMBIA SERRATURA",
+ "delete_all": "ELIMINA TUTTO"
+}
diff --git a/resources/[housing]/brutal_keys/locales/nl.json b/resources/[housing]/brutal_keys/locales/nl.json
new file mode 100644
index 000000000..8de839bcc
--- /dev/null
+++ b/resources/[housing]/brutal_keys/locales/nl.json
@@ -0,0 +1,13 @@
+{
+ "keys": "SLEUTEL",
+ "actions": "ACTIES",
+ "back": "TERUG",
+ "buy_keys": "KOOP SLEUTEL",
+ "buy_keys_des": "Hier kun je een nieuwe sleutel kopen als je alles kwijt bent.",
+ "doors_unlocked": "DEUR OPEN",
+ "doors_locked": "DEUR DICHT",
+ "buy": "KOPEN",
+ "copy": "KOPIEREN",
+ "change_lock": "VERVANG SLOT",
+ "delete_all": "VERWIJDER ALLES"
+}
diff --git a/resources/[housing]/brutal_keys/locales/pl.json b/resources/[housing]/brutal_keys/locales/pl.json
new file mode 100644
index 000000000..cabbde79c
--- /dev/null
+++ b/resources/[housing]/brutal_keys/locales/pl.json
@@ -0,0 +1,13 @@
+{
+ "keys": "KLUCZ",
+ "actions": "AKCJE",
+ "back": "WSTECZ",
+ "buy_keys": "KUP KLUCZ",
+ "buy_keys_des": "Tutaj mozesz kupic nowy klucz, jesli zgubiles wszystkie.",
+ "doors_unlocked": "DRZWI OTWARTE",
+ "doors_locked": "DRZWI ZAMKNIETE",
+ "buy": "KUP",
+ "copy": "KOPIUJ",
+ "change_lock": "ZMIEN ZAMEK",
+ "delete_all": "USUN WSZYSTKO"
+}
diff --git a/resources/[housing]/brutal_keys/locales/pt.json b/resources/[housing]/brutal_keys/locales/pt.json
new file mode 100644
index 000000000..25aead284
--- /dev/null
+++ b/resources/[housing]/brutal_keys/locales/pt.json
@@ -0,0 +1,13 @@
+{
+ "keys": "CHAVE",
+ "actions": "ACOES",
+ "back": "VOLTAR",
+ "buy_keys": "COMPRAR CHAVE",
+ "buy_keys_des": "Aqui voce pode comprar uma nova chave se perdeu todas.",
+ "doors_unlocked": "PORTA ABERTA",
+ "doors_locked": "PORTA FECHADA",
+ "buy": "COMPRAR",
+ "copy": "COPIAR",
+ "change_lock": "TROCAR FECHADURA",
+ "delete_all": "APAGAR TUDO"
+}
diff --git a/resources/[housing]/brutal_keys/locales/ro.json b/resources/[housing]/brutal_keys/locales/ro.json
new file mode 100644
index 000000000..30b149657
--- /dev/null
+++ b/resources/[housing]/brutal_keys/locales/ro.json
@@ -0,0 +1,13 @@
+{
+ "keys": "CHEIE",
+ "actions": "ACȚIUNI",
+ "back": "ÎNAPOI",
+ "buy_keys": "CUMPĂRĂ CHEIE",
+ "buy_keys_des": "Aici poți cumpăra o nouă cheie dacă le-ai pierdut pe toate.",
+ "doors_unlocked": "UȘA DESCHISĂ",
+ "doors_locked": "UȘA ÎNCHISĂ",
+ "buy": "CUMPĂRĂ",
+ "copy": "COPIAZĂ",
+ "change_lock": "SCHIMBĂ LACĂTUL",
+ "delete_all": "ȘTERGE TOT"
+}
diff --git a/resources/[housing]/brutal_keys/locales/ru.json b/resources/[housing]/brutal_keys/locales/ru.json
new file mode 100644
index 000000000..8f123132c
--- /dev/null
+++ b/resources/[housing]/brutal_keys/locales/ru.json
@@ -0,0 +1,13 @@
+{
+ "keys": "КЛЮЧ",
+ "actions": "ДЕЙСТВИЯ",
+ "back": "НАЗАД",
+ "buy_keys": "КУПИТЬ КЛЮЧ",
+ "buy_keys_des": "Здесь можно купить новый ключ, если все потеряны.",
+ "doors_unlocked": "ДВЕРЬ ОТКРЫТА",
+ "doors_locked": "ДВЕРЬ ЗАКРЫТА",
+ "buy": "КУПИТЬ",
+ "copy": "КОПИРОВАТЬ",
+ "change_lock": "СМЕНИТЬ ЗАМОК",
+ "delete_all": "УДАЛИТЬ ВСЕ"
+}
diff --git a/resources/[housing]/brutal_keys/locales/sp.json b/resources/[housing]/brutal_keys/locales/sp.json
new file mode 100644
index 000000000..e73d566ac
--- /dev/null
+++ b/resources/[housing]/brutal_keys/locales/sp.json
@@ -0,0 +1,13 @@
+{
+ "keys": "LLAVE",
+ "actions": "ACCIONES",
+ "back": "ATRAS",
+ "buy_keys": "COMPRAR LLAVE",
+ "buy_keys_des": "Aqui puedes comprar una nueva llave si perdiste todas.",
+ "doors_unlocked": "PUERTA ABIERTA",
+ "doors_locked": "PUERTA CERRADA",
+ "buy": "COMPRAR",
+ "copy": "COPIAR",
+ "change_lock": "CAMBIO CERRADURA",
+ "delete_all": "BORRAR TODO"
+}
diff --git a/resources/[housing]/brutal_keys/locales/sv.json b/resources/[housing]/brutal_keys/locales/sv.json
new file mode 100644
index 000000000..d5e80a192
--- /dev/null
+++ b/resources/[housing]/brutal_keys/locales/sv.json
@@ -0,0 +1,13 @@
+{
+ "keys": "NYCKEL",
+ "actions": "ÅTGÄRDER",
+ "back": "TILLBAKA",
+ "buy_keys": "KÖP NYCKEL",
+ "buy_keys_des": "Här kan du köpa en ny nyckel om du har tappat alla.",
+ "doors_unlocked": "DÖRR ÖPPEN",
+ "doors_locked": "DÖRR LÅST",
+ "buy": "KÖP",
+ "copy": "KOPIERA",
+ "change_lock": "BYT LÅS",
+ "delete_all": "RADERA ALLT"
+}
diff --git a/resources/[housing]/brutal_keys/locales/tr.json b/resources/[housing]/brutal_keys/locales/tr.json
new file mode 100644
index 000000000..b59364444
--- /dev/null
+++ b/resources/[housing]/brutal_keys/locales/tr.json
@@ -0,0 +1,13 @@
+{
+ "keys": "ANAHTAR",
+ "actions": "EYLEMLER",
+ "back": "GERI",
+ "buy_keys": "ANAHTAR AL",
+ "buy_keys_des": "Buradan tum anahtarlarini kaybettiysen yeni bir tane alabilirsin.",
+ "doors_unlocked": "KAPI ACIK",
+ "doors_locked": "KAPI KAPALI",
+ "buy": "AL",
+ "copy": "KOPYALA",
+ "change_lock": "KILIT DEGISTIR",
+ "delete_all": "HEPSINI SIL"
+}
diff --git a/resources/[housing]/brutal_keys/server/desktop.ini b/resources/[housing]/brutal_keys/server/desktop.ini
new file mode 100644
index 000000000..704fc3d2f
Binary files /dev/null and b/resources/[housing]/brutal_keys/server/desktop.ini differ
diff --git a/resources/[housing]/brutal_keys/server/server.lua b/resources/[housing]/brutal_keys/server/server.lua
new file mode 100644
index 000000000..16a27222f
Binary files /dev/null and b/resources/[housing]/brutal_keys/server/server.lua differ
diff --git a/resources/[housing]/brutal_keys/sv_utils.lua b/resources/[housing]/brutal_keys/sv_utils.lua
new file mode 100644
index 000000000..f69a65847
--- /dev/null
+++ b/resources/[housing]/brutal_keys/sv_utils.lua
@@ -0,0 +1,25 @@
+function StaffCheck(source)
+ local staff = false
+
+ if Config.Core:upper() == 'ESX'then
+ local player = Core.GetPlayerFromId(source)
+ local playerGroup = player.getGroup()
+
+ for i, Group in ipairs(Config.AdminGroups) do
+ if playerGroup == Group then
+ staff = true
+ break
+ end
+ end
+ elseif Config.Core:upper() == 'QBCORE' then
+
+ for i, Group in ipairs(Config.AdminGroups) do
+ if Core.Functions.HasPermission(source, Group) or IsPlayerAceAllowed(source, Group) or IsPlayerAceAllowed(source, 'command') then
+ staff = true
+ break
+ end
+ end
+ end
+
+ return staff
+end
\ No newline at end of file