.
\ No newline at end of file
diff --git a/[esx_addons]/esx_property/README.md b/[esx_addons]/esx_property/README.md
new file mode 100644
index 00000000..b83c47e1
--- /dev/null
+++ b/[esx_addons]/esx_property/README.md
@@ -0,0 +1,94 @@
+[ESX] Property
Discord - Website - Documentation
+
+## Features
+
+* 95 Premade Properties
+* Dynamic Json Storage
+* Key Management System
+* Garage System
+* Furniture System
+ * Can configure everything in the config
+ * Editing system
+ * Easy to use controls
+ * Furniture Stores
+ * Keymapped quick access menu
+* Shell / IPL support
+* Police Raiding
+* In-Game property Creator
+* Routing Buckets for instancing
+* Wardrobe functionality
+* OX Inventory Support
+* CCTV Camera System
+ * Night Vision
+ * Cool HUD overlay
+ * ability to zoom in/out
+ * Discord webhook support (copies link to clipboard aswell)
+* Admin Menu
+ * **EVERY** part of the property can be changed!
+ * Real estate Job Can also access
+* Real Estate Job
+* Built in Discord Logs
+* Highly Configurable
+
+## Dependencies
+
+* es_extended
+* esx_society *for Player Management* *
+* [OX-inventory](https://github.com/overextended/ox_inventory) *For Property Stashes* *
+* ESX Context
+* ESX Notify
+* ESX Progressbar *for Raiding* *
+* ESX Datastore *for wardrobe* *
+* ESX Clotheshop *for wardrobe* *
+* [k4mb1 Starter Shells](https://www.k4mb1maps.com/package/5015840) *only for shell use* *
+* [bob74_ipl](https://github.com/Bob74/bob74_ipl) *only for IPL use* *
+
+> `*` = Optional
+
+## Create A Property
+
+* [Menu Preview](https://prnt.sc/Xczb5Fs9xW-t)
+* Use the command `property:create` while an admin
+* Set Street Number
+* Set Price of Property
+* Select an Interior
+ > Note: To Use the Default Shells, you **NEED** [this Shell Pack](https://www.k4mb1maps.com/package/5015840)
+
+ > [Catagory Selection](https://prnt.sc/3RGaIEVTL6zw)
+
+ > [Interior Selection](https://prnt.sc/za9aOSsuqkce)
+* Set The Entrance Position
+ > Note: Uses Player`s Current Coordinates
+
+## Getting Shell Offset
+
+* To get the spawn Offset of the shell, use [Loaf`s Offset Finder](https://github.com/loaf-scripts/loaf_offsetfinder)
+
+* To Get the Wardrobe or Storage offset, go inside the Property, goto where u want them to be and then use the `getoffset` command and it will print the offset in F8
+
+## Admin Menu
+
+* As an Admin, use the command, `property:admin`
+* From There, you can select A property
+* You Can then either `Manage`, `Teleport To Entrance` or `Delete`
+* Allows you to edit all options and evict users
+* [Menu Preview](https://prnt.sc/Jpxox2ZcKn9G)
+* [Menu Preview 2](https://prnt.sc/IbP-ynHgYSMA)
+
+# Copyright
+
+ ESX Property - Properties Made Right!
+ Copyright (C) 2022 ESX-Framework
+
+ 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 .
diff --git a/[esx_addons]/esx_property/client/cctv.lua b/[esx_addons]/esx_property/client/cctv.lua
new file mode 100644
index 00000000..5585c71e
--- /dev/null
+++ b/[esx_addons]/esx_property/client/cctv.lua
@@ -0,0 +1,277 @@
+--[[
+ ESX Property - Properties Made Right!
+ Copyright (C) 2022 ESX-Framework
+
+ 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 .
+]]
+function CCTV(PropertyID)
+ DoScreenFadeOut(500)
+ Wait(500)
+ local PlyCoordsBefore = GetEntityCoords(PlayerPedId())
+ local Property = Properties[PropertyID]
+ local CamTakePic = true
+ if Property.cctv.enabled then
+ ESX.TriggerServerCallback("esx_property:CCTV", function(CanCCTV)
+ if CanCCTV then
+ InCCTV = true
+ local NightVision = false
+ local function InstructionButtonMessage(text)
+ BeginTextCommandScaleformString("STRING")
+ AddTextComponentScaleform(text)
+ EndTextCommandScaleformString()
+ end
+
+ local function CreateInstuctionScaleform(scaleform)
+ local scaleform = RequestScaleformMovie(scaleform)
+ while not HasScaleformMovieLoaded(scaleform) do
+ Wait(0)
+ end
+ PushScaleformMovieFunction(scaleform, "CLEAR_ALL")
+ PopScaleformMovieFunctionVoid()
+
+ PushScaleformMovieFunction(scaleform, "SET_CLEAR_SPACE")
+ PushScaleformMovieFunctionParameterInt(200)
+ PopScaleformMovieFunctionVoid()
+
+ if Config.CCTV.PictureWebook ~= "" then
+ PushScaleformMovieFunction(scaleform, "SET_DATA_SLOT")
+ PushScaleformMovieFunctionParameterInt(1)
+ N_0xe83a3e3557a56640(GetControlInstructionalButton(1, Config.CCTV.Controls.Screenshot, true))
+ InstructionButtonMessage("Take Picture")
+ PopScaleformMovieFunctionVoid()
+ end
+
+ PushScaleformMovieFunction(scaleform, "SET_DATA_SLOT")
+ PushScaleformMovieFunctionParameterInt(2)
+ N_0xe83a3e3557a56640(GetControlInstructionalButton(1, Config.CCTV.Controls.Right, true))
+ N_0xe83a3e3557a56640(GetControlInstructionalButton(1, Config.CCTV.Controls.Left, true))
+ InstructionButtonMessage("Left/Right")
+ PopScaleformMovieFunctionVoid()
+
+ PushScaleformMovieFunction(scaleform, "SET_DATA_SLOT")
+ PushScaleformMovieFunctionParameterInt(3)
+ N_0xe83a3e3557a56640(GetControlInstructionalButton(1, Config.CCTV.Controls.Down, true))
+ N_0xe83a3e3557a56640(GetControlInstructionalButton(1, Config.CCTV.Controls.Up, true))
+ InstructionButtonMessage("Up/Down")
+ PopScaleformMovieFunctionVoid()
+
+ PushScaleformMovieFunction(scaleform, "SET_DATA_SLOT")
+ PushScaleformMovieFunctionParameterInt(4)
+ N_0xe83a3e3557a56640(GetControlInstructionalButton(1, Config.CCTV.Controls.ZoomOut, true))
+ N_0xe83a3e3557a56640(GetControlInstructionalButton(1, Config.CCTV.Controls.ZoomIn, true))
+ InstructionButtonMessage("Zoom In/Out")
+ PopScaleformMovieFunctionVoid()
+
+ PushScaleformMovieFunction(scaleform, "SET_DATA_SLOT")
+ PushScaleformMovieFunctionParameterInt(5)
+ N_0xe83a3e3557a56640(GetControlInstructionalButton(1, Config.CCTV.Controls.NightVision, true))
+ InstructionButtonMessage("Toggle Night Vision")
+ PopScaleformMovieFunctionVoid()
+
+ PushScaleformMovieFunction(scaleform, "SET_DATA_SLOT")
+ PushScaleformMovieFunctionParameterInt(0)
+ N_0xe83a3e3557a56640(GetControlInstructionalButton(1, Config.CCTV.Controls.Exit, true))
+ InstructionButtonMessage("Exit Camera")
+ PopScaleformMovieFunctionVoid()
+
+ PushScaleformMovieFunction(scaleform, "DRAW_INSTRUCTIONAL_BUTTONS")
+ PopScaleformMovieFunctionVoid()
+
+ PushScaleformMovieFunction(scaleform, "SET_BACKGROUND_COLOUR")
+ PushScaleformMovieFunctionParameterInt(0)
+ PushScaleformMovieFunctionParameterInt(0)
+ PushScaleformMovieFunctionParameterInt(0)
+ PushScaleformMovieFunctionParameterInt(80)
+ PopScaleformMovieFunctionVoid()
+
+ return scaleform
+ end
+ ESX.CloseContext()
+ local cctvcam = nil
+ local angleZ = 0.0
+ ClearFocus()
+ local playerPed = PlayerPedId()
+ cctvcam = CreateCamWithParams("DEFAULT_SCRIPTED_CAMERA",
+ vector3(Property.Entrance.x, Property.Entrance.y, Property.Entrance.z + Config.CCTV.HeightAboveDoor), 0, 0, 0, Config.CCTV.FOV)
+ SetCamRot(cctvcam, Property.cctv.rot.x, Property.cctv.rot.y, Property.cctv.rot.z, 2)
+ SetCamActive(cctvcam, true)
+ SetTimecycleModifier("scanline_cam_cheap")
+ TriggerServerEvent("p_instance:s:leave")
+ DisableAllControlActions(0)
+ FreezeEntityPosition(playerPed, true)
+ SetEntityCollision(playerPed, false, true)
+ local ShowButtons = true
+ SetEntityVisible(playerPed, false)
+ SetTimecycleModifierStrength(2.0)
+ SetFocusArea(Property.Entrance.x, Property.Entrance.y, Property.Entrance.z, 0.0, 0.0, 0.0)
+ PointCamAtCoord(cctvcam, vector3(Property.Entrance.x, Property.Entrance.y, Property.Entrance.z + Config.CCTV.HeightAboveDoor))
+ RenderScriptCams(true, false, 1, true, false)
+ Wait(1000)
+ DoScreenFadeIn(500)
+ RequestAmbientAudioBank("Phone_Soundset_Franklin", 0, 0)
+ RequestAmbientAudioBank("HintCamSounds", 0, 0)
+ while IsCamActive(cctvcam) do
+ Wait(5)
+ DisableAllControlActions(0)
+ EnableControlAction(0, 245, true)
+ EnableControlAction(0, 246, true)
+ EnableControlAction(0, 249, true)
+ HideHudComponentThisFrame(7)
+ HideHudComponentThisFrame(8)
+ HideHudComponentThisFrame(9)
+ HideHudComponentThisFrame(6)
+ HideHudComponentThisFrame(19)
+ HideHudAndRadarThisFrame()
+
+ if ShowButtons then
+ local instructions = CreateInstuctionScaleform("instructional_buttons")
+ DrawScaleformMovieFullscreen(instructions, 255, 255, 255, 255, 0)
+ end
+ -- ROTATE LEFT
+ local getCameraRot = GetCamRot(cctvcam, 2)
+
+ if IsDisabledControlPressed(0, Config.CCTV.Controls.Left) and getCameraRot.z < Property.cctv.maxleft then
+ PlaySoundFrontend(-1, " FocusIn", "HintCamSounds", false)
+ SetCamRot(cctvcam, getCameraRot.x, 0.0, getCameraRot.z + Config.CCTV.RotateSpeed, 2)
+ end
+ -- ROTATE RIGHT
+ if IsDisabledControlPressed(0, Config.CCTV.Controls.Right) and getCameraRot.z > Property.cctv.maxright then
+ PlaySoundFrontend(-1, " FocusIn", "HintCamSounds", false)
+ SetCamRot(cctvcam, getCameraRot.x, 0.0, getCameraRot.z - Config.CCTV.RotateSpeed, 2)
+ end
+
+ -- ROTATE UP
+ if IsDisabledControlPressed(0, Config.CCTV.Controls.Up) and getCameraRot.x < Config.CCTV.MaxUpRotation then
+ PlaySoundFrontend(-1, " FocusIn", "HintCamSounds", false)
+ SetCamRot(cctvcam, getCameraRot.x + Config.CCTV.RotateSpeed, 0.0, getCameraRot.z, 2)
+ end
+
+ if IsDisabledControlPressed(0, Config.CCTV.Controls.Down) and getCameraRot.x > Config.CCTV.MaxDownRotation then
+ PlaySoundFrontend(-1, " FocusIn", "HintCamSounds", false)
+ SetCamRot(cctvcam, getCameraRot.x - Config.CCTV.RotateSpeed, 0.0, getCameraRot.z, 2)
+ end
+
+ if IsDisabledControlPressed(0, Config.CCTV.Controls.ZoomIn) and GetCamFov(cctvcam) > Config.CCTV.MaxZoom then
+ SetCamFov(cctvcam, GetCamFov(cctvcam) - 1.0)
+ end
+
+ if IsDisabledControlPressed(0, Config.CCTV.Controls.ZoomOut) and GetCamFov(cctvcam) < Config.CCTV.MinZoom then
+ SetCamFov(cctvcam, GetCamFov(cctvcam) + 1.0)
+ end
+
+ if IsDisabledControlPressed(0, Config.CCTV.Controls.Down) and getCameraRot.x > Config.CCTV.MaxDownRotation then
+ PlaySoundFrontend(-1, " FocusIn", "HintCamSounds", false)
+ SetCamRot(cctvcam, getCameraRot.x - Config.CCTV.RotateSpeed, 0.0, getCameraRot.z, 2)
+ end
+
+ SetTextFont(4)
+ SetTextScale(0.8, 0.8)
+ SetTextColour(255, 255, 255, 255)
+ SetTextDropshadow(0.1, 3, 27, 27, 255)
+ BeginTextCommandDisplayText('STRING')
+ AddTextComponentSubstringPlayerName(Property.setName ~= "" and Property.setName or Property.Name)
+ EndTextCommandDisplayText(0.01, 0.01)
+
+ SetTextFont(4)
+ SetTextScale(0.7, 0.7)
+ SetTextColour(255, 255, 255, 255)
+ SetTextDropshadow(0.1, 3, 27, 27, 255)
+ BeginTextCommandDisplayText('STRING')
+ local year --[[ integer ]] , month --[[ integer ]] , day --[[ integer ]] , hour --[[ integer ]] , minute --[[ integer ]] , second --[[ integer ]] =
+ GetPosixTime()
+ AddTextComponentSubstringPlayerName("" .. day .. "/" .. month .. "/" .. year .. " " .. hour .. ":" .. minute .. ":" .. second)
+ EndTextCommandDisplayText(0.01, 0.055)
+
+ SetTextFont(4)
+ SetTextScale(0.6, 0.6)
+ SetTextColour(255, 255, 255, 255)
+ SetTextDropshadow(0.1, 3, 27, 27, 255)
+ BeginTextCommandDisplayText('STRING')
+ local Zoom = ((Config.CCTV.FOV - GetCamFov(cctvcam)) / GetCamFov(cctvcam)) * 100
+ AddTextComponentSubstringPlayerName("Zoom Level: " .. math.floor(Zoom) .. "%")
+ EndTextCommandDisplayText(0.01, 0.09)
+
+ SetTextFont(4)
+ SetTextScale(0.6, 0.6)
+ SetTextColour(255, 255, 255, 255)
+ SetTextDropshadow(0.1, 3, 27, 27, 255)
+ BeginTextCommandDisplayText('STRING')
+ AddTextComponentSubstringPlayerName(NightVision and "Night Vision: Active" or "CCTV System: Active")
+ EndTextCommandDisplayText(0.01, 0.12)
+
+ if IsDisabledControlPressed(0, Config.CCTV.Controls.Down) and getCameraRot.x > Config.CCTV.MaxDownRotation then
+ PlaySoundFrontend(-1, " FocusIn", "HintCamSounds", false)
+ SetCamRot(cctvcam, getCameraRot.x - Config.CCTV.RotateSpeed, 0.0, getCameraRot.z, 2)
+ end
+
+ if IsDisabledControlJustPressed(0, 38) then
+ NightVision = not NightVision
+ SetNightvision(NightVision)
+ SetTimecycleModifier("scanline_cam")
+ end
+
+ if Config.CCTV.PictureWebook ~= "" and IsDisabledControlJustPressed(0, 201) then
+ if CamTakePic then
+ ShowButtons = false
+ Wait(1)
+ PlaySoundFrontend(-1, "Camera_Shoot", "Phone_Soundset_Franklin", 1)
+ ESX.TriggerServerCallback("esx_property:GetWebhook", function(hook)
+ if hook then
+ exports['screenshot-basic']:requestScreenshotUpload(hook, "files[]", function(data)
+ local image = json.decode(data)
+ print(ESX.DumpTable(image))
+ ESX.ShowNotification("Picture Taken!", "success")
+ SendNUIMessage({link = image.attachments[1].proxy_url})
+ ESX.ShowNotification("Link Copied To ~b~Cipboard", "success")
+ ShowButtons = true
+ CamTakePic = false
+ SetTimeout(5000, function()
+ CamTakePic = true
+ end)
+ end)
+ end
+ end)
+ else
+ ESX.ShowNotification("Please Wait Before taking another ~b~Picture", "error")
+ end
+ end
+
+ if IsDisabledControlPressed(1, Config.CCTV.Controls.Exit) then
+ DoScreenFadeOut(1000)
+ ESX.TriggerServerCallback("esx_property:ExitCCTV", function(CanExit)
+ if CanExit then
+ InCCTV = false
+ Wait(1000)
+ ClearFocus()
+ ClearTimecycleModifier()
+ ClearExtraTimecycleModifier()
+ RenderScriptCams(false, false, 0, true, false)
+ DestroyCam(cctvcam, false)
+ SetFocusEntity(playerPed)
+ SetNightvision(false)
+ SetSeethrough(false)
+ SetEntityCollision(playerPed, true, true)
+ FreezeEntityPosition(playerPed, false)
+ SetEntityVisible(playerPed, true)
+ Wait(1500)
+ DoScreenFadeIn(1000)
+ end
+ end, PropertyID)
+ break
+ end
+ end
+ end
+ end, PropertyID)
+ end
+end
diff --git a/[esx_addons]/esx_property/client/furniture.lua b/[esx_addons]/esx_property/client/furniture.lua
new file mode 100644
index 00000000..4e5ee7c3
--- /dev/null
+++ b/[esx_addons]/esx_property/client/furniture.lua
@@ -0,0 +1,407 @@
+--[[
+ ESX Property - Properties Made Right!
+ Copyright (C) 2022 ESX-Framework
+
+ 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 .
+]]
+if Config.Furniture.Enabled then
+ SpawnedFurniture = {}
+ RegisterNetEvent("esx_property:syncFurniture", function(PropertyId, Furniture)
+ if Properties[PropertyId] and Furniture then
+ Properties[PropertyId].furniture = Furniture
+ end
+ end)
+
+ RegisterNetEvent("esx_property:placeFurniture", function(Id, furniture, findex)
+ if Properties[Id] and InProperty and CurrentId == Id then
+ ESX.Game.SpawnLocalObject(furniture.Name, vector3(furniture.Pos.x, furniture.Pos.y, furniture.Pos.z - 0.1), function(object)
+ SetEntityCoordsNoOffset(object, furniture.Pos.x, furniture.Pos.y, furniture.Pos.z)
+ SetEntityHeading(object, furniture.Heading)
+ SetEntityAsMissionEntity(object, true, true)
+ FreezeEntityPosition(object, true)
+ SpawnedFurniture[findex] = {obj = object, data = furniture}
+ end)
+ end
+ end)
+
+ RegisterNetEvent("esx_property:removeFurniture", function(Id, furniture)
+ if Properties[Id] and InProperty and CurrentId == Id then
+ if SpawnedFurniture[furniture] then
+ DeleteEntity(SpawnedFurniture[furniture].obj)
+ SpawnedFurniture[furniture] = nil
+ end
+ end
+ end)
+
+ RegisterNetEvent("esx_property:editFurniture", function(Id, furniture, Pos, Heading)
+ if Properties[Id] and InProperty and CurrentId == Id then
+
+ if SpawnedFurniture[furniture] then
+ local obj = SpawnedFurniture[furniture].obj
+ SetEntityCoordsNoOffset(obj, Pos.x, Pos.y, Pos.z)
+ SetEntityHeading(obj, Heading)
+ SpawnedFurniture[furniture].data.Pos = Pos
+ SpawnedFurniture[furniture].data.Heading = Heading
+ end
+ end
+ end)
+
+ ------------------------ Furniture Start -------------------------------
+ local inFurniture = false
+ local CurrentlyEditing = nil
+
+ function TempFurniturePlacement(PropertyId, PropName, PropIndex, PropCatagory, PropPrice, Existing, Pos, Heading)
+ ESX.CloseContext()
+ if CurrentlyEditing then
+ DeleteEntity(CurrentlyEditing)
+ CurrentlyEditing = nil
+ end
+ local function InstructionButtonMessage(text)
+ BeginTextCommandScaleformString("STRING")
+ AddTextComponentScaleform(text)
+ EndTextCommandScaleformString()
+ end
+
+ local function CreateInstuctionScaleform(scaleform)
+ local scaleform = RequestScaleformMovie(scaleform)
+ while not HasScaleformMovieLoaded(scaleform) do
+ Wait(0)
+ end
+ PushScaleformMovieFunction(scaleform, "CLEAR_ALL")
+ PopScaleformMovieFunctionVoid()
+
+ PushScaleformMovieFunction(scaleform, "SET_CLEAR_SPACE")
+ PushScaleformMovieFunctionParameterInt(200)
+ PopScaleformMovieFunctionVoid()
+
+ PushScaleformMovieFunction(scaleform, "SET_DATA_SLOT")
+ PushScaleformMovieFunctionParameterInt(1)
+ N_0xe83a3e3557a56640(GetControlInstructionalButton(1, Config.Furniture.Controls.MinusX, true))
+ N_0xe83a3e3557a56640(GetControlInstructionalButton(1, Config.Furniture.Controls.PlusX, true))
+ InstructionButtonMessage("X Axis")
+ PopScaleformMovieFunctionVoid()
+
+ PushScaleformMovieFunction(scaleform, "SET_DATA_SLOT")
+ PushScaleformMovieFunctionParameterInt(3)
+ N_0xe83a3e3557a56640(GetControlInstructionalButton(1, Config.Furniture.Controls.MinusY, true))
+ N_0xe83a3e3557a56640(GetControlInstructionalButton(1, Config.Furniture.Controls.PlusY, true))
+ InstructionButtonMessage("Y axis")
+ PopScaleformMovieFunctionVoid()
+
+ PushScaleformMovieFunction(scaleform, "SET_DATA_SLOT")
+ PushScaleformMovieFunctionParameterInt(5)
+ N_0xe83a3e3557a56640(GetControlInstructionalButton(1, Config.Furniture.Controls.Up, true))
+ N_0xe83a3e3557a56640(GetControlInstructionalButton(1, Config.Furniture.Controls.Down, true))
+ InstructionButtonMessage("Height")
+ PopScaleformMovieFunctionVoid()
+
+ PushScaleformMovieFunction(scaleform, "SET_DATA_SLOT")
+ PushScaleformMovieFunctionParameterInt(7)
+ N_0xe83a3e3557a56640(GetControlInstructionalButton(1, Config.Furniture.Controls.RotateLeft, true))
+ N_0xe83a3e3557a56640(GetControlInstructionalButton(1, Config.Furniture.Controls.RotateRight, true))
+ InstructionButtonMessage("Rotation")
+ PopScaleformMovieFunctionVoid()
+
+ PushScaleformMovieFunction(scaleform, "SET_DATA_SLOT")
+ PushScaleformMovieFunctionParameterInt(9)
+ N_0xe83a3e3557a56640(GetControlInstructionalButton(1, Config.Furniture.Controls.Confirm, true))
+ InstructionButtonMessage("Place Furniture")
+ PopScaleformMovieFunctionVoid()
+
+ PushScaleformMovieFunction(scaleform, "SET_DATA_SLOT")
+ PushScaleformMovieFunctionParameterInt(0)
+ N_0xe83a3e3557a56640(GetControlInstructionalButton(1, Config.Furniture.Controls.Exit, true))
+ InstructionButtonMessage("Delete")
+ PopScaleformMovieFunctionVoid()
+
+ PushScaleformMovieFunction(scaleform, "DRAW_INSTRUCTIONAL_BUTTONS")
+ PopScaleformMovieFunctionVoid()
+
+ PushScaleformMovieFunction(scaleform, "SET_BACKGROUND_COLOUR")
+ PushScaleformMovieFunctionParameterInt(0)
+ PushScaleformMovieFunctionParameterInt(0)
+ PushScaleformMovieFunctionParameterInt(0)
+ PushScaleformMovieFunctionParameterInt(80)
+ PopScaleformMovieFunctionVoid()
+
+ return scaleform
+ end
+ function GetCameraDirection()
+ local rotation = GetGameplayCamRot() * (math.pi / 180)
+
+ return vector3(-math.sin(rotation.z), math.cos(rotation.z), math.sin(rotation.x))
+ end
+ local Position = GetEntityCoords(ESX.PlayerData.ped)
+ local PropSpawn = Position - vector3(0.5, 0.5, 1.0)
+ ESX.Game.SpawnLocalObject(PropName, PropSpawn, function(CurrentlyEditing)
+ CurrentlyEditing = CurrentlyEditing
+ NetworkSetObjectForceStaticBlend(CurrentlyEditing, true)
+ SetEntityAsMissionEntity(CurrentlyEditing, true, true)
+ SetEntityCollision(CurrentlyEditing, false, true)
+ if Existing then
+ SetEntityCoordsNoOffset(CurrentlyEditing, vector3(Pos.x, Pos.y, Pos.z))
+ SetEntityHeading(CurrentlyEditing, Heading)
+ else
+ PlaceObjectOnGroundProperly(CurrentlyEditing)
+ end
+ FreezeEntityPosition(CurrentlyEditing, true)
+ while true do
+ HudForceWeaponWheel(false)
+ HideHudComponentThisFrame(19)
+ HideHudComponentThisFrame(20)
+ local CurrentPos = GetEntityCoords(CurrentlyEditing)
+ local CurrentHeading = GetEntityHeading(CurrentlyEditing)
+ local Rotation = GetEntityRotation(CurrentlyEditing, 2)
+ SetEntityDrawOutline(CurrentlyEditing, true)
+ SetEntityDrawOutlineColor(55, 140, 191, 200)
+ SetEntityDrawOutlineShader(1)
+ local instructions = CreateInstuctionScaleform("instructional_buttons")
+ DrawScaleformMovieFullscreen(instructions, 255, 255, 255, 255, 0)
+ Wait(0)
+ if IsControlJustPressed(0, 202) then
+ DeleteObject(CurrentlyEditing)
+ end
+
+ if IsControlPressed(0, Config.Furniture.Controls.MinusY) then
+ local direction = GetCameraDirection()
+ CurrentPos = CurrentPos - vector3(direction.x * Config.Furniture.MovementSpeed, direction.y * Config.Furniture.MovementSpeed, 0)
+ SetEntityCoordsNoOffset(CurrentlyEditing, CurrentPos.x, CurrentPos.y, CurrentPos.z)
+ end
+
+ if IsControlPressed(0, Config.Furniture.Controls.PlusY) then
+ local direction = GetCameraDirection()
+ CurrentPos = CurrentPos + vector3(direction.x * Config.Furniture.MovementSpeed, direction.y * Config.Furniture.MovementSpeed, 0)
+ SetEntityCoordsNoOffset(CurrentlyEditing, CurrentPos)
+ end
+
+ if IsControlPressed(0, Config.Furniture.Controls.Up) then
+ SetEntityCoordsNoOffset(CurrentlyEditing, CurrentPos.x, CurrentPos.y, CurrentPos.z + Config.Furniture.MovementZspeed)
+ end
+
+ if IsControlPressed(0, Config.Furniture.Controls.Down) then
+ SetEntityCoordsNoOffset(CurrentlyEditing, CurrentPos.x, CurrentPos.y, CurrentPos.z - Config.Furniture.MovementZspeed)
+ end
+ if IsControlPressed(0, Config.Furniture.Controls.RotateLeft) then
+ CurrentHeading = CurrentHeading + Config.Furniture.RotationSpeed
+ SetEntityRotation(CurrentlyEditing, 0, 0, CurrentHeading, 1, true)
+ -- SetEntityRotation(CurrentlyEditing, Rotation.x, Rotation.y, Rotation.z + Config.Furniture.RotationSpeed, 2, false)
+ -- SetEntityHeading(CurrentlyEditing, CurrentHeading + Config.Furniture.RotationSpeed)
+ end
+
+ if IsControlPressed(0, Config.Furniture.Controls.RotateRight) then
+ CurrentHeading = CurrentHeading - Config.Furniture.RotationSpeed
+ SetEntityRotation(CurrentlyEditing, 0, 0, CurrentHeading, 1, true)
+ -- SetEntityRotation(CurrentlyEditing, Rotation.x, Rotation.y, Rotation.z - Config.Furniture.RotationSpeed, 2, false)
+ -- SetEntityHeading(CurrentlyEditing, CurrentHeading - Config.Furniture.RotationSpeed)
+ end
+
+ if IsControlPressed(0, Config.Furniture.Controls.PlusX) then
+ local direction = GetCameraDirection()
+ CurrentPos = CurrentPos + vector3(-direction.y * Config.Furniture.MovementSpeed, direction.x * Config.Furniture.MovementSpeed, 0)
+ SetEntityCoordsNoOffset(CurrentlyEditing, CurrentPos)
+ end
+
+ if IsControlPressed(0, Config.Furniture.Controls.MinusX) then
+ local direction = GetCameraDirection()
+ CurrentPos = CurrentPos - vector3(-direction.y * Config.Furniture.MovementSpeed, direction.x * Config.Furniture.MovementSpeed, 0)
+ SetEntityCoordsNoOffset(CurrentlyEditing, CurrentPos)
+ end
+
+ if IsControlJustPressed(0, Config.Furniture.Controls.Confirm) then
+ if not Existing then
+ ESX.OpenContext("right",
+ {{unslectable = true, title = "Do you want to buy " .. Config.FurnitureCatagories[PropCatagory][PropIndex].title .. "?",
+ description = "Price: $" .. Config.FurnitureCatagories[PropCatagory][PropIndex].price, icon = "fas fa-shopping-cart"},
+ {title = "Yes", value = "buy", icon = "fas fa-check"}, {title = "No", icon = "fas fa-minus"}}, function(menu, element)
+ if element.value and element.value == "buy" then
+ ESX.TriggerServerCallback("esx_property:buyFurniture", function(response)
+ if response then
+ DeleteEntity(CurrentlyEditing)
+ ESX.ShowNotification("You Bought ~b~" .. Config.FurnitureCatagories[PropCatagory][PropIndex].title .. "~s~!", "success")
+ ESX.CloseContext()
+ else
+ ESX.ShowNotification("You ~r~Cannot~s~ Buy This Furniture", "error")
+ end
+ end, CurrentId, PropName, PropIndex, PropCatagory, vector3(CurrentPos.x, CurrentPos.y, CurrentPos.z), CurrentHeading)
+ end
+ end)
+ else
+ ESX.TriggerServerCallback("esx_property:editFurniture", function(response)
+ if response then
+ DeleteEntity(CurrentlyEditing)
+ ESX.ShowNotification("You Edited ~b~" .. Config.FurnitureCatagories[PropCatagory][PropPrice].title .. "~s~!", "success")
+ ESX.CloseContext()
+ else
+ ESX.ShowNotification("You ~r~Cannot~s~ Edit This Furniture", "error")
+ end
+ end, CurrentId, PropIndex, vector3(CurrentPos.x, CurrentPos.y, CurrentPos.z), CurrentHeading)
+ end
+ end
+
+ if DoesEntityExist(CurrentlyEditing) then
+ inFurniture = true
+ else
+ inFurniture = false
+ for _, Control in pairs(Config.Furniture.Controls) do
+ EnableControlAction(0, Control, true)
+ end
+ break
+ end
+ end
+ end)
+ end
+
+ function FurnitureItems(house, Store, StoreIndex, Catagory)
+ local Items = {{unselectable = true, title = Store .. " Store - " .. Catagory, icon = "fas fa-store"},
+ {title = "Go Back", value = "go-back", icon = "fas fa-arrow-left"}}
+
+ for k, v in pairs(Config.FurnitureCatagories[Catagory]) do
+ table.insert(Items, {title = v.title, value = v.name, index = k, description = "Price: " .. v.price, icon = "fas fa-shopping-cart"})
+ end
+
+ ESX.OpenContext("right", Items, function(data, element)
+ if element.value == "go-back" then
+ FurnitureCatagories(house, Store, StoreIndex)
+ else
+ TempFurniturePlacement(house, element.value, element.index, Catagory)
+ end
+ end)
+ end
+
+ function FurnitureCatagories(house, Store, StoreIndex)
+ local Catagories = {{unselectable = true, title = Store .. " Store", icon = "fas fa-store"},
+ {title = "Go Back", value = "go-back", icon = "fas fa-arrow-left"}}
+
+ for k, v in pairs(Config.FurnitureStores[StoreIndex].Catagories) do
+ table.insert(Catagories, {title = v})
+ end
+
+ ESX.OpenContext("right", Catagories, function(data, element)
+ if element.value == "go-back" then
+ FurnitureStores(house)
+ else
+ FurnitureItems(house, Store, StoreIndex, element.title)
+ end
+ end)
+ end
+
+ function FurnitureEdit(propertyId, furniture)
+ local furni = SpawnedFurniture[furniture]
+ local Funiture = Config.FurnitureCatagories[furni.data.Catagory][furni.data.Index]
+ local Catagories = {{unselectable = true, title = "Edit " .. Funiture.title, icon = "fas fa-store"},
+ {title = "Go Back", value = "go-back", icon = "fas fa-arrow-left"},
+ {title = "Move", value = "rotate", icon = "fas fa-sync-alt"}, {title = "Delete", value = "delete", icon = "fas fa-trash-alt"}}
+
+ ESX.OpenContext("right", Catagories, function(data, element)
+ if element.value == "go-back" then
+ FurnitureEditSelect(propertyId)
+ end
+ if element.value == "delete" then
+ ESX.TriggerServerCallback("esx_property:deleteFurniture", function(response)
+ if response then
+ ESX.ShowNotification("You Deleted ~b~" .. Funiture.title .. "~s~!", "success")
+ FurnitureEditSelect(propertyId)
+ else
+ ESX.ShowNotification("You ~r~Cannot~s~ Delete This Furniture", "error")
+ end
+ end, propertyId, furniture)
+ end
+ if element.value == "rotate" then
+ ESX.CloseContext()
+ TempFurniturePlacement(propertyId, furni.data.Name, furniture, furni.data.Catagory, furni.data.Index, true, furni.data.Pos, furni.data.Heading)
+ end
+ end)
+ end
+
+ function FurnitureEditSelect(propertyId)
+ local Furni = {{unselectable = true, title = "Owned Furniture", icon = "far fa-edit"},
+ {title = "Go Back", value = "go-back", icon = "fas fa-arrow-left"}}
+
+ for k, v in pairs(SpawnedFurniture) do
+ local Funiture = Config.FurnitureCatagories[v.data.Catagory][v.data.Index]
+ table.insert(Furni, {title = Funiture.title, value = k})
+ end
+
+ ESX.OpenContext("right", Furni, function(data, element)
+ if element.value == "go-back" then
+ FurnitureMenu(propertyId)
+ else
+ FurnitureEdit(propertyId, element.value)
+ end
+ end)
+ end
+
+ function FurnitureStores(house)
+ local Options = {{unselectable = true, title = "Furniture Stores", icon = "fas fa-store"},
+ {title = "Go Back", value = "go-back", icon = "fas fa-arrow-left"}}
+
+ for k, v in pairs(Config.FurnitureStores) do
+ table.insert(Options, {title = v.title, Store = k})
+ end
+
+ ESX.OpenContext("right", Options, function(data, element)
+ if element.Store then
+ FurnitureCatagories(house, element.title, element.Store)
+ elseif element.value == "go-back" then
+ FurnitureMenu(house)
+ end
+ end)
+ end
+
+ function FurnitureMenu(PropertyId)
+ local Options = {{unselectable = true, title = "Furniture Management", icon = "fas fa-lamp"},
+ {title = "Furniture Shop", description = "Buy Furniture", icon = "fas fa-dollar-sign", value = "store"}}
+
+ if #SpawnedFurniture > 0 then
+ Options[#Options + 1] = {title = "Reset", description = "Clears All Furniture", icon = "fas fa-trash", value = "reset"}
+ Options[#Options + 1] = {title = "Edit", description = "Move and/or Delete furniture items", icon = "fas fa-archive", value = "edit"}
+
+ end
+ ESX.OpenContext("right", Options, function(data, element)
+ if element.value then
+ if element.value == "store" then
+ FurnitureStores(PropertyId)
+ elseif element.value == "edit" then
+ FurnitureEditSelect(PropertyId)
+ elseif element.value == "reset" then
+ ESX.TriggerServerCallback("esx_property:RemoveAllfurniture", function(Removed)
+ if Removed then
+ ESX.ShowNotification("Furniture Reset!", "success")
+ else
+ ESX.ShowNotification("You Cannot Reset This Property!", "error")
+ end
+ end, PropertyId)
+ end
+ end
+ end)
+ end
+
+ ESX.RegisterInput("furniture", "(ESX Property) Open Furniture Menu", "keyboard", "M", function()
+ if InProperty then
+ ESX.TriggerServerCallback('esx_property:CanOpenFurniture', function(Access)
+ if Access then
+ if not inFurniture then
+ FurnitureMenu(CurrentId)
+ else
+ FurnitureStores(CurrentId)
+ end
+ else
+ ESX.ShowNotification("You ~r~Cannot~s~ Access This Menu!", 5000, "error")
+ end
+ end, CurrentId)
+ else
+ return
+ end
+ end)
+end
diff --git a/[esx_addons]/esx_property/client/html/copy.html b/[esx_addons]/esx_property/client/html/copy.html
new file mode 100644
index 00000000..5d62698d
--- /dev/null
+++ b/[esx_addons]/esx_property/client/html/copy.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/[esx_addons]/esx_property/client/main.lua b/[esx_addons]/esx_property/client/main.lua
new file mode 100644
index 00000000..a682cd3c
--- /dev/null
+++ b/[esx_addons]/esx_property/client/main.lua
@@ -0,0 +1,1527 @@
+--[[
+ ESX Property - Properties Made Right!
+ Copyright (C) 2022 ESX-Framework
+
+ 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 .
+]] local GameBuild = GetGameBuildNumber()
+Properties = {}
+CurrentId = 0
+local CurrentDrawing = {}
+InProperty = false
+PlayerKeys = {}
+InCCTV = false
+local Blips = {}
+local Shell = nil
+local PM = Config.PlayerManagement
+local CreateThread = CreateThread
+local Wait = Wait
+local ShowingUIs = {Exit = false, Wardrobe = false, Storage = false, ExitShell = false}
+local ox_inventory = exports.ox_inventory
+local DoScreenFadeIn = DoScreenFadeIn
+local PlayerPedId = PlayerPedId
+local GetEntityCoords = GetEntityCoords
+local GetEntityHeading = GetEntityHeading
+local IsControlJustPressed = IsControlJustPressed
+local IsControlPressed = IsControlPressed
+local DoScreenFadeOut = DoScreenFadeOut
+
+DoScreenFadeIn(3000)
+function RefreshBlips()
+ for i = 1, #Blips, 1 do
+ RemoveBlip(Blips[i])
+ Blips[i] = nil
+ end
+
+ for k, v in pairs(Properties) do
+ if v.Owned and Config.OwnedBlips then
+ if v.Owner == ESX.PlayerData.identifier then
+ local Blip = AddBlipForCoord(v.Entrance.x, v.Entrance.y, v.Entrance.z)
+ SetBlipSprite(Blip, 40)
+ SetBlipAsShortRange(Blip, true)
+ SetBlipScale(Blip, 0.8)
+ SetBlipColour(Blip, 0)
+ BeginTextCommandSetBlipName("STRING")
+ AddTextComponentString(v.Name)
+ EndTextCommandSetBlipName(Blip)
+ SetBlipCategory(Blip, 11)
+ Blips[#Blips + 1] = Blip
+ elseif PlayerKeys[k] then
+ local Blip = AddBlipForCoord(v.Entrance.x, v.Entrance.y, v.Entrance.z)
+ SetBlipSprite(Blip, GameBuild >= 2699 and 811 or 134)
+ SetBlipAsShortRange(Blip, true)
+ SetBlipScale(Blip, 0.9)
+ SetBlipColour(Blip, 26)
+ BeginTextCommandSetBlipName("STRING")
+ AddTextComponentString(v.Name)
+ EndTextCommandSetBlipName(Blip)
+ SetBlipCategory(Blip, 11)
+ Blips[#Blips + 1] = Blip
+ end
+ elseif not v.Owned and Config.ForSaleBlips then
+ local Blip = AddBlipForCoord(v.Entrance.x, v.Entrance.y, v.Entrance.z)
+ SetBlipSprite(Blip, 350)
+ SetBlipAsShortRange(Blip, true)
+ SetBlipScale(Blip, 0.7)
+ BeginTextCommandSetBlipName("STRING")
+ AddTextComponentString(v.Name)
+ EndTextCommandSetBlipName(Blip)
+ SetBlipCategory(Blip, 10)
+ Blips[#Blips + 1] = Blip
+ end
+ end
+
+ if PM.Enabled then
+ local Blip = AddBlipForCoord(PM.Locations.Entrance)
+ SetBlipSprite(Blip, 374)
+ SetBlipColour(Blip, 45)
+ SetBlipAsShortRange(Blip, true)
+ SetBlipScale(Blip, 0.7)
+ BeginTextCommandSetBlipName("STRING")
+ AddTextComponentString(PM.joblabel .. " Office")
+ EndTextCommandSetBlipName(Blip)
+ Blips[#Blips + 1] = Blip
+ end
+end
+
+RegisterNetEvent("esx_property:syncProperties", function(properties, lastProperty)
+ while not ESX.PlayerLoaded and not ESX.PlayerData.identifier do
+ Wait(0)
+ end
+ Properties = properties
+ for house, data in pairs(Properties) do
+ if data.Keys then
+ for ident, vaues in pairs(data.Keys) do
+ if vaues and ident == ESX.PlayerData.identifier then
+ PlayerKeys[house] = true
+ end
+ end
+ end
+ end
+ if lastProperty then
+ if Properties[lastProperty.id] and (Properties[lastProperty.id].Owner == ESX.PlayerData.identifier or PlayerKeys[lastProperty.id]) then
+ AttemptHouseEntry(lastProperty.id)
+ else
+ ESX.TriggerServerCallback('esx_property:RemoveLastProperty', function()
+ SetEntityCoords(PlayerPedId(), vector3(lastProperty.coords.x, lastProperty.coords.y, lastProperty.coords.z))
+ end)
+ end
+ end
+ RefreshBlips()
+end)
+
+RegisterNetEvent("esx_property:giveKeyAccess", function(Property)
+ ESX.TriggerServerCallback("esx_property:ShouldHaveKey", function(Should)
+ if Should then
+ PlayerKeys[Property] = true
+ end
+ end, Property)
+end)
+
+RegisterNetEvent("esx_property:RemoveKey", function(Property)
+ PlayerKeys[Property] = false
+end)
+
+RegisterNetEvent('esx:setJob')
+AddEventHandler('esx:setJob', function(job)
+ RefreshBlips()
+end)
+
+exports("GetProperties", function()
+ return Properties
+end)
+
+function OpenInteractionMenu(PropertyId, Interaction)
+ local Property = Properties[PropertyId]
+ if Property.Owned then
+ if Interaction == "Wardrobe" then
+ Config.WardrobeInteraction(PropertyId, Interaction)
+ elseif Interaction == "Storage" then
+ if Config.OxInventory then
+ exports.ox_inventory:openInventory('stash', {id = 'property-' .. PropertyId, owner = Property.Owner})
+ end
+ end
+ end
+end
+
+function GiveKeysMenu(Property)
+ ESX.TriggerServerCallback("esx_property:GetInsidePlayers", function(Players)
+ local Elements = {{unselectable = true, title = "Nearby Players", icon = "fas fa-user-plus"},
+ {title = "Go Back", icon = "fas fa-arrow-left", value = "go_back"}}
+ for i = 1, #Players, 1 do
+ Elements[#Elements + 1] = {title = Players[i].Name, icon = "far fa-user", index = Players[i].Id, value = "user"}
+ end
+ ESX.OpenContext("right", Elements, function(menu, element)
+ if element.value == "go_back" then
+ ManageKeys(Property)
+ elseif element.value == "user" then
+ ESX.TriggerServerCallback("esx_property:GiveKey", function(KeyGiven)
+ if KeyGiven then
+ ESX.ShowNotification("You gave a key to ~b~" .. element.title .. "~s~.", "success")
+ ESX.CloseContext()
+ else
+ ESX.ShowNotification("You Cannot Give This Player A ~r~Key", "error")
+ end
+ end, Property, element.index)
+ end
+ end)
+ end, Property)
+end
+
+function RemoveKeysMenu(Property)
+ ESX.TriggerServerCallback("esx_property:GetPlayersWithKeys", function(Players)
+ local Elements = {{unselectable = true, title = "Remove Key From Player", icon = "fas fa-user-plus"},
+ {title = "Go Back", icon = "fas fa-arrow-left", value = "go_back"}}
+ for k, v in pairs(Players) do
+ local name = v.name
+ local Id = k
+ Elements[#Elements + 1] = {title = name, icon = "far fa-user", value = "user", id = Id}
+ end
+ ESX.OpenContext("right", Elements, function(menu, element)
+ if element.value == "go_back" then
+ ManageKeys(Property)
+ elseif element.value == "user" then
+ ESX.TriggerServerCallback("esx_property:RemoveKey", function(KeyGiven)
+ if KeyGiven then
+ ESX.ShowNotification("You Revoked ~b~" .. element.title .. "~s~`s Property Access.", "success")
+ ESX.CloseContext()
+ else
+ ESX.ShowNotification("You Cannot ~b~Remove~s~ A ~r~Key~s~ From this Person", "error")
+ end
+ end, Property, element.id)
+ end
+ end)
+ end, Property)
+end
+
+function ManageKeys(Property)
+ ESX.HideUI()
+ local Elements = {{unselectable = true, title = "Key Management", icon = "fas fa-key"},
+ {title = "Go Back", icon = "fas fa-arrow-left", value = "go_back"},
+ {title = "Give Keys", icon = "fas fa-plus", value = "give_keys"},
+ {title = "Remove Keys", icon = "fas fa-minus", value = "remove_keys"}}
+
+ ESX.OpenContext("right", Elements, function(menu, element)
+ if element.value == "go_back" then
+ OpenPropertyMenu(Property)
+ end
+ if element.value == "give_keys" then
+ GiveKeysMenu(Property)
+ end
+ if element.value == "remove_keys" then
+ RemoveKeysMenu(Property)
+ end
+ end)
+end
+
+function SetPropertyName(Property)
+ local Name = Properties[Property].setName ~= "" and Properties[Property].setName or Properties[Property].Name
+ local Elements = {{unselectable = true, title = "Edit Property Name", icon = "fa-solid fa-signature"},
+ {icon = "", title = "Name", input = true, inputType = "text", inputValue = Name, inputPlaceholder = Properties[Property].Name,
+ name = "setName"}, {icon = "fa-solid fa-check", title = "Confirm", name = "confirm"}}
+
+ ESX.OpenContext("right", Elements, function(menu, element)
+ if element.name == "confirm" then
+ ESX.TriggerServerCallback("esx_property:SetPropertyName", function(Set)
+ if Set then
+ ESX.ShowNotification("You Have Set The Property Name To ~b~" .. menu.eles[2].inputValue .. "~s~.", "success")
+ ESX.CloseContext()
+ else
+ ESX.ShowNotification("You Cannot Set The Property Name To ~r~" .. menu.eles[2].inputValue .. "~s~.", "error")
+ end
+ end, Property, menu.eles[2].inputValue)
+ end
+ end)
+end
+
+local SettingValue = ""
+
+function PropertyMenuElements(PropertyId)
+ local Property = Properties[PropertyId]
+ local elements = {{unselectable = true, title = Property.setName ~= "" and Property.setName or Property.Name, icon = "fas fa-home"}}
+ if Property.Owned then
+ if Property.Locked then
+ table.insert(elements, {title = "Door: Locked", icon = "fas fa-lock", value = 'property_unlock'})
+ else
+ table.insert(elements, {title = "Door: Unlocked", icon = "fas fa-unlock", value = 'property_lock'})
+ end
+ if ESX.PlayerData.identifier == Property.Owner then
+ table.insert(elements,
+ {title = "Manage Name", description = "Change The Name of the Property.", icon = "fa-solid fa-signature", value = 'property_name'})
+ end
+ if not InProperty then
+ if ESX.PlayerData.identifier == Property.Owner then
+ table.insert(elements, {title = "Manage Keys", description = "Give And Remove Property Access.", icon = "fas fa-key", value = 'property_keys'})
+ table.insert(elements,
+ {title = "Sell", description = "Sell This Property For $" .. ESX.Math.GroupDigits(ESX.Round(Property.Price * 0.6)),
+ icon = "fas fa-dollar-sign", value = 'property_sell'})
+ end
+ if Config.Raiding.Enabled and Property.Locked and ESX.PlayerData.job and ESX.GetPlayerData().job.name == "police" then
+ table.insert(elements, {title = "Raid", description = "Burst Through The Door", icon = "fas fa-bomb", value = 'property_raid'})
+ end
+ else
+ if (Config.CCTV.Enabled and Properties[PropertyId].cctv.enabled) and (ESX.PlayerData.identifier == Property.Owner or PlayerKeys[PropertyId]) then
+ table.insert(elements, {title = "CCTV", description = "View The Property Camera.", icon = "fas fa-video", value = 'property_cctv'})
+ end
+ if (ESX.PlayerData.identifier == Property.Owner or PlayerKeys[PropertyId]) then
+ if Config.CanCustomiseInventoryAndWardrobePositions then
+ if Config.OxInventory then
+ table.insert(elements, {title = "Inventory", description = "Change Position of Property Storage.",
+ icon = "fa-solid fa-up-down-left-right", value = 'property_inventory'})
+ end
+ table.insert(elements, {title = "Wardrobe", description = "Change Position of Property Wardrobe.", icon = "fa-solid fa-up-down-left-right",
+ value = 'property_wardrobe'})
+ end
+ table.insert(elements,
+ {title = "Furniture", description = "Open Furniture Menu.", icon = "fas fa-boxes-stacked", value = 'property_furniture'})
+ end
+ end
+ if (not Property.Locked or Config.OwnerCanAlwaysEnter and ESX.PlayerData.identifier == Property.Owner) and not InProperty then
+ table.insert(elements, {title = "Enter", icon = "fas fa-door-open", value = 'property_enter'})
+ end
+ if Property.Locked and not InProperty and
+ not (Config.OwnerCanAlwaysEnter and ESX.PlayerData.identifier == Property.Owner or PlayerKeys[PropertyId]) then
+ table.insert(elements, {title = "Knock On Door", icon = "fa-solid fa-hand-sparkles", value = 'property_knock'})
+ end
+ else
+ if not PM.Enabled then
+ table.insert(elements,
+ {title = "Buy", description = "Buy This Property For $" .. ESX.Math.GroupDigits(ESX.Round(Property.Price)), icon = "fas fa-shopping-cart",
+ value = 'property_buy'})
+ else
+ if ESX.PlayerData.job.name == PM.job and ESX.PlayerData.job.grade >= PM.Permissions.SellProperty then
+ table.insert(elements, {title = "Sell To Player", description = "Sell This Property For $" .. ESX.Math.GroupDigits(ESX.Round(Property.Price)),
+ icon = "fas fa-shopping-cart", value = 'property_sell_re'})
+ end
+ end
+ if not Property.Locked and not InProperty then
+ table.insert(elements, {title = "View", icon = "fas fa-door-open", value = 'property_enter'})
+ end
+ end
+
+ if InProperty and (not Property.Locked or Config.CanAlwaysExit) then
+ table.insert(elements, {title = "Exit", icon = "fas fa-sign-out-alt", value = 'property_exit'})
+ end
+
+ return elements
+end
+
+function OpenPropertyMenu(PropertyId)
+ if SettingValue ~= "" then
+ ESX.ShowNotification("You Are Currently Editing The Property.")
+ return
+ end
+ ESX.HideUI()
+ local Property = Properties[PropertyId]
+ local elements = PropertyMenuElements(PropertyId)
+
+ ESX.OpenContext("right", elements, function(menu, element)
+ if element.value == "property_unlock" then
+ ESX.TriggerServerCallback("esx_property:toggleLock", function(IsUnlocked)
+ if IsUnlocked then
+ local eles = PropertyMenuElements(PropertyId)
+ exports["esx_context"]:Refresh(eles)
+ else
+ ESX.ShowNotification("You Cannot ~b~Unlock~s~ This Property", "error")
+ end
+ end, PropertyId)
+ end
+ if element.value == "property_lock" then
+ ESX.TriggerServerCallback("esx_property:toggleLock", function(IsUnlocked)
+ if IsUnlocked then
+ local eles = PropertyMenuElements(PropertyId)
+ exports["esx_context"]:Refresh(eles)
+ else
+ ESX.ShowNotification("You Cannot ~b~Lock~s~ This Property", "error")
+ end
+ end, PropertyId)
+ end
+ if element.value == "property_cctv" then
+ CCTV(PropertyId)
+ end
+ if element.value == "property_raid" then
+ ESX.TriggerServerCallback("esx_property:CanRaid", function(CanRaid)
+ if CanRaid then
+ ESX.Progressbar("~b~Preparing~s~ Raid!", 15000, {FreezePlayer = true, animation = Config.Raiding.Animation, onFinish = function()
+ ESX.ShowNotification("Raiding...", "success")
+ AttemptHouseEntry(PropertyId)
+ end, onCancel = function()
+ ESX.ShowNotification("~r~Cancelled~s~ Raid", "error")
+ end})
+ else
+ ESX.ShowNotification("You Cannot ~b~Raid~s~ This Property.", "error")
+ end
+ end, PropertyId)
+ end
+ if element.value == "property_enter" then
+ ESX.CloseContext()
+ AttemptHouseEntry(PropertyId)
+ end
+ if element.value == "property_keys" then
+ ESX.CloseContext()
+ ManageKeys(PropertyId)
+ end
+ if element.value == "property_name" then
+ ESX.CloseContext()
+ SetPropertyName(PropertyId)
+ end
+ if element.value == "property_furniture" then
+ ESX.CloseContext()
+ FurnitureMenu(PropertyId)
+ end
+ if element.value == "property_inventory" then
+ ESX.CloseContext()
+ ShowingUIs.Exit = false
+ SettingValue = "Storage"
+ Wait(20)
+ ESX.TextUI("Press ~b~[G]~s~ To Set Storage Position")
+ while SettingValue ~= "" do
+ Wait(1)
+ if IsControlJustPressed(0, 47) then
+ SettingValue = ""
+ ESX.HideUI()
+ ESX.TriggerServerCallback("esx_property:SetInventoryPosition", function(Success)
+ if Success then
+ ESX.ShowNotification("~b~Storage~s~ Position Set.", "success")
+ else
+ ESX.ShowNotification("~r~Cannot~s~ Set ~b~Storage~s~ Position.", "error")
+ end
+ end, PropertyId, GetEntityCoords(PlayerPedId()))
+ break
+ end
+ end
+ end
+ if element.value == "property_wardrobe" then
+ ESX.CloseContext()
+ ShowingUIs.Exit = false
+ SettingValue = "Wardrobe"
+ Wait(20)
+ ESX.TextUI("Press ~b~[G]~s~ To Set Wardrobe Position")
+ while SettingValue ~= "" do
+ Wait(1)
+ if IsControlJustPressed(0, 47) then
+ SettingValue = ""
+ ESX.HideUI()
+ ESX.TriggerServerCallback("esx_property:SetWardrobePosition", function(Success)
+ if Success then
+ ESX.ShowNotification("~b~Wardrobe~s~ Position Set.", "success")
+ else
+ ESX.ShowNotification("~r~Cannot~s~ Set ~b~Wardrobe~s~ Position.", "error")
+ end
+ end, PropertyId, GetEntityCoords(PlayerPedId()))
+ break
+ end
+ end
+ end
+ if element.value == "property_exit" then
+ ESX.CloseContext()
+ if SettingValue == "" then
+ AttemptHouseExit(PropertyId)
+ else
+ ESX.ShowNotification("Please Finish Setting ~b~" .. SettingValue .. "~s~ Position")
+ end
+ end
+ if element.value == "property_buy" then
+ ESX.TriggerServerCallback("esx_property:buyProperty", function(IsBought)
+ if IsBought then
+ local eles = PropertyMenuElements(PropertyId)
+ exports["esx_context"]:Refresh(eles)
+ else
+ ESX.ShowNotification("You don't have enough money!", "error")
+ end
+ end, PropertyId)
+ end
+ if element.value == "property_sell_re" then
+ local Elements = {{unselectable = true, title = "Select Player"}}
+ ESX.TriggerServerCallback("esx_property:GetNearbyPlayers", function(Players)
+ if Players then
+ for i = 1, #Players do
+ Elements[#Elements + 1] = {title = Players[i].name, value = Players[i].source}
+ end
+
+ ESX.OpenContext("right", Elements, function(menu, element)
+ if element.value then
+ ESX.TriggerServerCallback("esx_property:attemptSellToPlayer", function(IsBought)
+ if IsBought then
+ local eles = PropertyMenuElements(PropertyId)
+ exports["esx_context"]:Refresh(eles)
+ else
+ ESX.ShowNotification("Cannot Sell To This Player!", "error")
+ end
+ end, PropertyId, element.value)
+ end
+ end)
+ end
+ end, PropertyId)
+ end
+ if element.value == "property_knock" then
+ ESX.ShowNotification("Knocking On Door...", "success")
+ ESX.TriggerServerCallback("esx_property:KnockOnDoor", function(HasKnocked)
+ if not HasKnocked then
+ ESX.ShowNotification("It Seems Nobody is home...", "error")
+ end
+ end, PropertyId)
+ end
+ if element.value == "property_sell" then
+ ESX.TriggerServerCallback("esx_property:sellProperty", function(IsSold)
+ if IsSold then
+ local eles = PropertyMenuElements(PropertyId)
+ exports["esx_context"]:Refresh(eles)
+ else
+ ESX.ShowNotification("You ~r~Cannot~s~ Sell this Property!", "error")
+ end
+ end, PropertyId)
+ end
+ end, function(menu)
+ CurrentDrawing.Showing = false
+ end)
+end
+
+function AttemptHouseExit(PropertyId)
+ local Property = Properties[PropertyId]
+
+ ESX.ShowNotification("Exiting property...", "success")
+ FreezeEntityPosition(PlayerPedId(), true)
+ InProperty = false
+ CurrentId = 0
+ SettingValue = ""
+ ESX.HideUI()
+ ESX.UI.Menu.CloseAll()
+ SetRainLevel(-1)
+ DoScreenFadeOut(1500)
+ Wait(1500)
+ TriggerServerEvent("esx_property:leave", PropertyId)
+ if Shell then
+ DeleteObject(Shell)
+ Shell = nil
+ end
+ if Config.Furniture.Enabled then
+ for k, v in pairs(SpawnedFurniture) do
+ DeleteObject(v.obj)
+ end
+ SpawnedFurniture = {}
+ end
+ Wait(1500)
+ FreezeEntityPosition(PlayerPedId(), false)
+ DoScreenFadeIn(1500)
+end
+
+RegisterCommand("getoffset", function(source)
+ if InProperty then
+ local PlayerPed = PlayerPedId()
+ local Pcoords = GetEntityCoords(PlayerPed)
+ local Property = Properties[CurrentId]
+ local Interior = GetInteriorValues(Property.Interior)
+ print(vector3(Property.Entrance.x, Property.Entrance.y, 2000) - Pcoords)
+ end
+end)
+
+function AttemptHouseEntry(PropertyId)
+ local Property = Properties[PropertyId]
+ local Interior = GetInteriorValues(Property.Interior)
+ if Interior.type == "shell" and not Config.Shells then
+ ESX.ShowNotification("This Interior Uses Shells, which are disabled!.", "error")
+ return
+ end
+ if Property.Locked and not ESX.GetPlayerData().job.name == "police" then
+ ESX.ShowNotification("The door is ~r~locked.", "error")
+ return
+ end
+ ESX.ShowNotification("Entering property...", "success")
+ CurrentId = PropertyId
+ ESX.UI.Menu.CloseAll()
+ local Property = Properties[CurrentId]
+ FreezeEntityPosition(PlayerPedId(), true)
+ DoScreenFadeOut(1500)
+ Wait(1500)
+ if Interior.type == "shell" then
+ ESX.Streaming.RequestModel(GetHashKey(Property.Interior), function()
+ if Shell then
+ DeleteObject(Shell)
+ Shell = nil
+ end
+ local Pos = vector3(Property.Entrance.x, Property.Entrance.y, 2000)
+ Shell = CreateObjectNoOffset(GetHashKey(Property.Interior), Pos + Interior.pos, false, false, false)
+ SetEntityHeading(Shell, 0.0)
+ while not DoesEntityExist(Shell) do
+ Wait(1)
+ end
+ FreezeEntityPosition(Shell, true)
+ end)
+ end
+ if Properties[PropertyId].furniture then
+ for k, v in pairs(Properties[PropertyId].furniture) do
+ ESX.Game.SpawnLocalObject(v.Name, v.Pos, function(object)
+ SetEntityCoordsNoOffset(object, v.Pos.x, v.Pos.y, v.Pos.z)
+ SetEntityHeading(object, v.Heading)
+ SetEntityAsMissionEntity(object, true, true)
+ FreezeEntityPosition(object, true)
+ SpawnedFurniture[k] = {obj = object, data = v}
+ end)
+ end
+ end
+ local ShowingTextUI2 = false
+ FreezeEntityPosition(PlayerPedId(), false)
+ TriggerServerEvent("esx_property:enter", PropertyId)
+ Wait(1500)
+ DoScreenFadeIn(1800)
+ InProperty = true
+ if not Config.OxInventory then
+ Interior.positions.Storage = nil
+ Properties[CurrentId].positions.Storage = nil
+ end
+
+ CreateThread(function()
+ while InProperty do
+ local Property = Properties[CurrentId]
+ local Sleep = 1000
+ local Near = false
+ SetRainLevel(0.0)
+ local PlayerPed = PlayerPedId()
+ local PlayerCoords = GetEntityCoords(PlayerPed)
+ if Interior.type == "shell" then
+ if #(PlayerCoords - vector3(Property.Entrance.x, Property.Entrance.y, 1999)) < 5.0 then
+ Sleep = 0
+ DrawMarker(27, vector3(Property.Entrance.x, Property.Entrance.y, 2000.2), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 50, 50, 200, 200,
+ false, false, 2, true, nil, nil, false)
+ if #(PlayerCoords.xy - vector2(Property.Entrance.x, Property.Entrance.y)) <= 2.5 then
+ Near = true
+ if not ShowingUIs.Exit then
+
+ local Pname = Properties[CurrentId].setName ~= "" and Properties[CurrentId].setName or Properties[CurrentId].Name
+ ESX.TextUI("Press ~b~[E]~s~ to Access ~b~" .. Pname)
+ ShowingUIs.Exit = true
+ end
+ if IsControlJustPressed(0, 38) then
+ OpenPropertyMenu(CurrentId)
+ end
+ else
+ if not Near and ShowingUIs.Exit and SettingValue == "" then
+ ShowingUIs.Exit = false
+ ESX.HideUI()
+ ESX.CloseContext()
+ end
+ end
+ end
+
+ if Property.Owned then
+ for k, v in pairs(Properties[CurrentId].positions) do
+ local v = vector3(v.x, v.y, v.z)
+ local CanDo = true
+ if CanDo then
+ local Poss = vector3(Property.Entrance.x, Property.Entrance.y, 1999) - v
+ if #(PlayerCoords - Poss) < 5.0 then
+ Sleep = 0
+ DrawMarker(27, Poss, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 200, 50, 50, 200, false, false, 2, true, nil, nil, false)
+ if #(PlayerCoords - Poss) < 2.0 and SettingValue == "" then
+ Near = true
+ if not ShowingUIs[k] then
+ ShowingUIs[k] = true
+
+ ESX.TextUI("Press ~b~[E]~s~ to Access ~b~" .. k)
+ end
+ if IsControlJustPressed(0, 38) then
+ OpenInteractionMenu(CurrentId, k)
+ end
+ else
+ if not Near and ShowingUIs[k] and SettingValue == "" then
+ ShowingUIs[k] = false
+ ESX.HideUI()
+ end
+ end
+ end
+ end
+ end
+ if not Near and ShowingUIs and SettingValue == "" then
+ ShowingTextUI2 = false
+ ESX.HideUI()
+ end
+ end
+ else
+ if #(PlayerCoords - Interior.pos) < 5.0 then
+ Sleep = 0
+ DrawMarker(27, vector3(Interior.pos.xy, Interior.pos.z - 0.98), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 50, 50, 200, 200, false, false,
+ 2, true, nil, nil, false)
+ if #(PlayerCoords - Interior.pos) < 2.0 then
+ if not ShowingUIs.Exit then
+ ShowingUIs.Exit = true
+ local Pname = Properties[CurrentId].setName ~= "" and Properties[CurrentId].setName or Properties[CurrentId].Name
+ ESX.TextUI("Press ~b~[E]~s~ to Access ~b~" .. Pname)
+ end
+ if IsControlJustPressed(0, 38) then
+ OpenPropertyMenu(CurrentId)
+ end
+ else
+ if ShowingUIs.Exit and SettingValue == "" then
+ ShowingUIs.Exit = false
+ ESX.HideUI()
+ ESX.CloseContext()
+ end
+ end
+ end
+
+ if Property.Owned then
+ for k, v in pairs(Properties[CurrentId].positions) do
+ v = vector3(v.x, v.y, v.z)
+ local CanDo = true
+
+ if CanDo then
+ if #(PlayerCoords - v) < 5.0 then
+ Sleep = 0
+ DrawMarker(27, vector3(v.xy, v.z - 0.98), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 200, 50, 50, 200, false, false, 2, true, nil,
+ nil, false)
+ if #(PlayerCoords - v) < 2.0 then
+ if not ShowingUIs[k] then
+ ShowingUIs[k] = true
+ ESX.TextUI("Press ~b~[E]~s~ to Access ~b~" .. k)
+ end
+ if IsControlJustPressed(0, 38) then
+ OpenInteractionMenu(CurrentId, k)
+ end
+ else
+ if ShowingUIs[k] and SettingValue == "" then
+ ShowingUIs[k] = false
+ ESX.HideUI()
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ Wait(Sleep)
+ end
+ end)
+end
+
+function StoreVehicle(PropertyId)
+ local Vehicle = GetVehiclePedIsIn(PlayerPedId(), false)
+ if Vehicle then
+ local VehProperties = ESX.Game.GetVehicleProperties(Vehicle)
+ VehProperties.DisplayName = GetLabelText(GetDisplayNameFromVehicleModel(VehProperties.model))
+ ESX.TriggerServerCallback("esx_property:StoreVehicle", function(result)
+ if result then
+ SetEntityAsMissionEntity(Vehicle, true, true)
+ DeleteVehicle(Vehicle)
+ ESX.ShowNotification("Vehicle ~b~Stored!", "success")
+ return
+ else
+ ESX.ShowNotification("You Cannot Store This Vehicle!", "error")
+ return
+ end
+ end, PropertyId, VehProperties)
+ end
+end
+
+function AccessGarage(PropertyId)
+ ESX.TriggerServerCallback("esx_property:AccessGarage", function(Vehicles)
+ if Vehicles then
+ local elements = {{unselectable = true, icon = "fas fa-warehouse", title = "Property Garage"}}
+ for k, v in pairs(Vehicles) do
+ elements[#elements + 1] = {title = v.vehicle.DisplayName .. " | " .. v.vehicle.plate, Properties = v.vehicle, index = k}
+ end
+ ESX.OpenContext("right", elements, function(menu, element)
+ if element.Properties then
+ ESX.CloseContext()
+ ESX.ShowNotification("Retrieving ~b~" .. element.Properties.DisplayName .. "~s~ ...")
+ if ESX.Game.IsSpawnPointClear(vector3(Properties[PropertyId].garage.pos.x, Properties[PropertyId].garage.pos.y,
+ Properties[PropertyId].garage.pos.z), 3.0) then
+ ESX.Game.SpawnVehicle(element.Properties.model, Properties[PropertyId].garage.pos, Properties[PropertyId].garage.Heading,
+ function(vehicle)
+ SetEntityAsMissionEntity(vehicle, true, true)
+ ESX.Game.SetVehicleProperties(vehicle, element.Properties)
+ TaskWarpPedIntoVehicle(PlayerPedId(), vehicle, -1)
+ SetModelAsNoLongerNeeded(element.Properties.model)
+ TriggerServerEvent("esx_property:SetVehicleOut", PropertyId, element.index)
+ end)
+ end
+ end
+ end)
+ else
+ ESX.ShowNotification("You Cannot Access This Garage!", "error")
+ return
+ end
+ end, PropertyId)
+end
+
+CreateThread(function()
+ local ShowingTextUI = false
+ while true do
+ local Sleep = 2000
+
+ if #(Properties) > 0 then
+ Sleep = 800
+ local nearby = false
+ for i = 1, #(Properties) do
+ if Properties[i].Entrance then
+ local GetEntityCoords = GetEntityCoords
+ local IsControlJustPressed = IsControlJustPressed
+ local DrawMarker = DrawMarker
+ local Coords = GetEntityCoords(PlayerPedId())
+ local Entrance = vector3(Properties[i].Entrance.x, Properties[i].Entrance.y, Properties[i].Entrance.z)
+ if not InCCTV then
+ if #(Coords - Entrance) < 10.0 then
+ Sleep = 0
+ DrawMarker(27, Entrance, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 50, 200, 50, 200, false, false, 2, true, nil, nil, false)
+ if #(Coords - Entrance) < 1.5 then
+ nearby = true
+ local PropertyName = Properties[i].setName ~= "" and Properties[i].setName or Properties[i].Name
+ if not CurrentDrawing.Showing or CurrentDrawing.Name ~= PropertyName then
+ CurrentDrawing.Name = PropertyName
+ CurrentDrawing.Showing = true
+ ESX.TextUI("Press ~b~[E]~s~ to Access ~b~" .. PropertyName)
+ end
+ if IsControlJustPressed(0, 38) then
+ if Properties[i].Locked and Properties[i].Owned and Properties[i].Owner ~= ESX.PlayerData.identifier and not ESX.PlayerData.job.name ==
+ "police" and not PlayerKeys[i] then
+ ESX.ShowNotification("This property is ~r~locked.", "error")
+ else
+ OpenPropertyMenu(i)
+ end
+ end
+ end
+ end
+ end
+ if Properties[i].garage.enabled and Properties[i].garage.pos and (ESX.PlayerData.identifier == Properties[i].Owner or PlayerKeys[i]) then
+ local Garage = vector3(Properties[i].garage.pos.x, Properties[i].garage.pos.y, Properties[i].garage.pos.z)
+ if #(Coords - Garage) < 10.0 then
+ Sleep = 0
+ DrawMarker(36, Garage, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.5, 1.5, 1.5, 50, 200, 50, 200, false, false, 2, true, nil, nil, false)
+ if #(Coords - Garage) < 3.0 then
+ nearby = true
+ if GetVehiclePedIsUsing(ESX.PlayerData.ped) ~= 0 then
+ local veh = GetVehiclePedIsIn(ESX.PlayerData.ped, false)
+ local vehmodel = GetEntityModel(veh)
+ local DisplayName = GetLabelText(GetDisplayNameFromVehicleModel(vehmodel))
+ if not CurrentDrawing.Showing or CurrentDrawing.Name ~= DisplayName then
+ CurrentDrawing.Name = DisplayName
+ CurrentDrawing.Showing = true
+ ESX.TextUI("Press ~b~[E]~s~ to Store ~b~" .. DisplayName)
+ end
+ else
+ if not CurrentDrawing.Showing or CurrentDrawing.Name ~= "Garage" then
+ CurrentDrawing.Name = "Garage"
+ CurrentDrawing.Showing = true
+ ESX.TextUI("Press ~b~[E]~s~ to Access ~b~ Garage")
+ end
+ end
+ if IsControlJustPressed(0, 38) then
+ if GetVehiclePedIsUsing(ESX.PlayerData.ped) ~= 0 then
+ StoreVehicle(i)
+ else
+ AccessGarage(i)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ if not nearby and CurrentDrawing.Showing and SettingValue == "" then
+ ESX.HideUI()
+ CurrentDrawing.Showing = false
+ ESX.CloseContext()
+ end
+ end
+ Wait(Sleep)
+ end
+end)
+
+function OpenPMQuickMenu(Action)
+ if Action == "Entrance" then
+ DoScreenFadeOut(1500)
+ Wait(1500)
+ ESX.TriggerServerCallback("esx_property:PMenterOffice", function(HasEntered)
+ if HasEntered then
+ ESX.ShowNotification("~b~Entering~s~ Office.", "success")
+ else
+ ESX.ShowNotification("You ~r~Cannot~s~ Enter This Office!", "error")
+ end
+ Wait(1500)
+ DoScreenFadeIn(1500)
+ end)
+ elseif Action == "Exit" then
+ DoScreenFadeOut(1500)
+ Wait(1500)
+ ESX.TriggerServerCallback("esx_property:PMexitOffice", function(HasExited)
+ if HasExited then
+ ESX.ShowNotification("~b~Exiting~s~ Office.", "success")
+ else
+ ESX.ShowNotification("You ~r~Cannot~s~ Exit This Office!", "error")
+ end
+ Wait(1500)
+ DoScreenFadeIn(1500)
+ end)
+ elseif Action == "Properties" then
+ TriggerEvent("esx_property:AdminMenu")
+
+ elseif Action == "ActionsMenu" then
+ local elements = {{unselectable = true, title = "Real Estate Actions", value = "RealEstateActions"}}
+
+ if ESX.PlayerData.job.name == PM.job then
+ if ESX.PlayerData.job.grade >= PM.Permissions.CreateProperty then
+ elements[#elements + 1] = {title = "Create Property", value = "CreateProperty"}
+ end
+ if ESX.PlayerData.job.grade >= PM.Permissions.BillPlayers then
+ elements[#elements + 1] = {title = "Create Bill", value = "bill"}
+ end
+ if ESX.PlayerData.job.grade >= PM.Permissions.ManagePropertiesFromQuickActions then
+ elements[#elements + 1] = {title = "Manage Properties", value = "manage"}
+ end
+ end
+ ESX.OpenContext("right", elements, function(menu, element)
+ if element.value == "CreateProperty" then
+ TriggerEvent("esx_property:CreateProperty")
+ end
+ if element.value == "manage" then
+ TriggerEvent("esx_property:AdminMenu")
+ end
+ end)
+ end
+end
+
+ESX.RegisterInput("realestatequickmenu", "(ESX Property) Open Real Estate Quick Actions", "keyboard", "F5", function()
+ ESX.TriggerServerCallback('esx_property:IsAdmin', function(Access)
+ if Access then
+ OpenPMQuickMenu("ActionsMenu")
+ end
+ end)
+end)
+
+local PMdrawing = {Entrance = false, Exit = false}
+CreateThread(function()
+ while PM.Enabled do
+ local Sleep = 2500
+ local DrawMarker = DrawMarker
+ if ESX.IsPlayerLoaded() then
+ if ESX.PlayerData.job and ESX.PlayerData.job.name == PM.job then
+ Sleep = 1500
+
+ local Coords = GetEntityCoords(ESX.PlayerData.ped)
+ local nearby = false
+
+ for k, v in pairs(PM.Locations) do
+ local Dist = #(Coords - v)
+ if Dist <= 10.0 then
+ nearby = true
+ Sleep = 0
+ DrawMarker(27, v, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 50, 200, 50, 200, false, false, 2, true, nil, nil, false)
+ if Dist <= 1.5 then
+ if not PMdrawing[k] then
+ PMdrawing[k] = true
+ ESX.TextUI("Press ~b~[E]~s~ to Access ~b~" .. k)
+ end
+ if IsControlJustPressed(0, 38) then
+ OpenPMQuickMenu(k)
+ end
+ end
+ else
+ if not nearby and PMdrawing[k] then
+ ESX.HideUI()
+ PMdrawing[k] = false
+ ESX.CloseContext()
+ end
+ end
+ end
+ end
+ end
+ Wait(Sleep)
+ end
+end)
+
+local HouseData = {}
+
+RegisterNetEvent("esx_property:CreateProperty", function()
+ ESX.TriggerServerCallback('esx_property:CanCreateProperty', function(data)
+ if data then
+ local GetEntityCoords = GetEntityCoords
+ local GetStreetNameAtCoord = GetStreetNameAtCoord
+ local GetStreetNameFromHashKey = GetStreetNameFromHashKey
+ local Pcoords = GetEntityCoords(ESX.PlayerData.ped)
+ local StreetHash = GetStreetNameAtCoord(Pcoords.x, Pcoords.y, Pcoords.z)
+ local StreetName = GetStreetNameFromHashKey(StreetHash)
+ HouseData = {}
+ local Property = {{unselectable = true, icon = "fas fa-plus", title = "Property Creation"},
+ {value = 0, title = "Street Number", icon = "fas fa-list-ol", description = "Set the property Street Number.", input = true,
+ inputType = "number", inputPlaceholder = "Number...", inputValue = nil, inputMin = 1, inputMax = 90000, index = "hnumber"},
+ {title = "Price", icon = "fas fa-dollar-sign", input = true, inputType = "number", description = "Set the property Price.",
+ inputPlaceholder = "Price...", inputValue = nil, inputMin = 1, inputMax = 50000000, index = "price"},
+ {title = "Select Interior", description = "Set the property Interior.", icon = "fas fa-home", index = "interior"},
+ {title = "Garage", description = "Set Garage settings (optional)", icon = "fas fa-warehouse", value = {enabled = false},
+ index = "garage", disabled = not (Config.Garage.Enabled)},
+ {title = "CCTV", description = "Set CCTV settings (optional)", icon = "fas fa-video",
+ value = {enabled = false, rot = GetGameplayCamRot(2), maxleft = 80, maxright = -20}, index = "cctv",
+ disabled = not (Config.CCTV.Enabled)},
+ {title = "Entrance:", description = "Set the entrance of the property.", icon = "fas fa-map-marker-alt", index = "entrance"},
+ {title = "Create Property", icon = "fas fa-check", description = "All Inputs Required to continue.", index = "creation"}}
+ local function OpenCreate()
+ local opos = {}
+ ESX.OpenContext("right", Property, function(menu, element)
+ if menu.eles[2] and menu.eles[2].inputValue and menu.eles[1].title == "Property Creation" then
+ Property[2].inputValue = menu.eles[2].inputValue
+ HouseData.name = menu.eles[2].inputValue .. " " .. StreetName
+ end
+ if menu.eles[3] and menu.eles[3].inputValue and menu.eles[1].title == "Property Creation" then
+ Property[3].inputValue = menu.eles[3].inputValue
+ HouseData.price = menu.eles[3].inputValue
+ end
+ if element.index then
+ if element.index == "entrance" then
+ local PlayerPos = GetEntityCoords(ESX.PlayerData.ped)
+ HouseData.entrance = {x = ESX.Math.Round(PlayerPos.x, 2), y = ESX.Math.Round(PlayerPos.y, 2), z = ESX.Math.Round(PlayerPos.z, 2) - 0.98}
+ Property[7].title = "Entrance Set."
+ Property[7].description = "Entrance: " .. HouseData.entrance.x .. ", " .. HouseData.entrance.y .. ", " .. HouseData.entrance.z
+ OpenCreate()
+ end
+ if element.index == "selectedinterior" then
+ HouseData.interior = element.value
+ Property[4].title = "Interior Set."
+ Property[4].description = "Interior: " .. element.title
+ OpenCreate()
+ end
+ if element.index == "IPL" then
+ local ints = {{unselectable = true, icon = "fas fa-warehouse", title = "IPL Interiors"}}
+ for i = 1, #(Config.Interiors.IPL) do
+ ints[#ints + 1] = {title = Config.Interiors.IPL[i].label, index = "selectedinterior", value = Config.Interiors.IPL[i].value}
+ exports["esx_context"]:Refresh(ints, "right")
+ end
+ end
+ if element.index == "Shells" then
+ local ints = {{unselectable = true, icon = "fas fa-warehouse", title = "Custom Interiors"}}
+ for i = 1, #(Config.Interiors.Shells) do
+ ints[#ints + 1] = {title = Config.Interiors.Shells[i].label, index = "selectedinterior", value = Config.Interiors.Shells[i].value}
+ exports["esx_context"]:Refresh(ints, "right")
+ end
+ end
+ if element.index == "interior" then
+ local catsa = {{unselectable = true, icon = "fas fa-warehouse", title = "Interior Types"},
+ {title = "IPL Interiors", description = "Native GTA Interiors, Made by R*", index = "IPL"}}
+ if Config.Shells then
+ catsa[3] = {title = "Custom Interiors", description = "Custom Interiors, Made by You", index = "Shells"}
+ end
+ exports["esx_context"]:Refresh(catsa, "right")
+ end
+ if element.index == "return" then
+ OpenCreate()
+ end
+ if element.index == "cctv" then
+ local status = Property[6].value.enabled and "Enabled" or "Disabled"
+ opos = {{unselectable = true, icon = "fas fa-video", title = "CCTV Settings"},
+ {title = "Toggle Usage", icon = status == "Enabled" and "fas fa-eye" or "fas fa-eye-slash",
+ description = "Current Status: " .. status, index = "ToggleCCTV"},
+ {title = "Set Angle", icon = "fas fa-rotate", disabled = not Property[6].value.enabled,
+ description = "Sets the Camera angle to your Cameras Direction", index = "SetCCTVangle"},
+ {title = "Go Back", icon = "fas fa-arrow-left", description = "return to property creation.", index = "return"}}
+ exports["esx_context"]:Refresh(opos, "right")
+ end
+ if element.index == "ToggleCCTV" then
+ Property[6].value.enabled = not Property[6].value.enabled
+ local status = Property[6].value.enabled and "Enabled" or "Disabled"
+ opos = {{unselectable = true, icon = "fas fa-video", title = "CCTV Settings"},
+ {title = "Toggle Usage", icon = status == "Enabled" and "fas fa-eye" or "fas fa-eye-slash",
+ description = "Current Status: " .. status, index = "ToggleCCTV"},
+ {title = "Set Angle", icon = "fas fa-rotate", disabled = not Property[6].value.enabled,
+ description = "Sets the Camera angle to your Cameras Direction", index = "SetCCTVangle"},
+ {title = "Go Back", icon = "fas fa-arrow-left", description = "return to property creation.", index = "return"}}
+ exports["esx_context"]:Refresh(opos, "right")
+ end
+ if Config.Garage.Enabled and element.index == "garage" then
+ local status = Property[5].value.enabled and "Enabled" or "Disabled"
+ opos = {{unselectable = true, icon = "fas fa-warehouse", title = "Garage Settings"},
+ {title = "Toggle Usage", icon = status == "Enabled" and "fa-solid fa-toggle-on" or "fa-solid fa-toggle-off",
+ description = "Current Status: " .. status, index = "ToggleGarage"}}
+ if Property[5].value.enabled then
+ opos[#opos + 1] = {title = "Set Position", icon = "fas fa-map-marker-alt", disabled = not Property[5].value.enabled,
+ description = "Sets the Camera angle to your Cameras Direction", index = "SetGaragePos"}
+ if Property[5].value.pos then
+ opos[#opos + 1] = {title = "Go Back", icon = "fas fa-arrow-left", description = "return to property creation.", index = "return"}
+ end
+ else
+ opos[#opos + 1] = {title = "Go Back", icon = "fas fa-arrow-left", description = "return to property creation.", index = "return"}
+ end
+ exports["esx_context"]:Refresh(opos, "right")
+ end
+ if element.index == "ToggleGarage" then
+ Property[5].value.enabled = not Property[5].value.enabled
+ local status = Property[5].value.enabled and "Enabled" or "Disabled"
+ opos = {{unselectable = true, icon = "fas fa-warehouse", title = "Garage Settings"},
+ {title = "Toggle Usage", icon = status == "Enabled" and "fa-solid fa-toggle-on" or "fa-solid fa-toggle-off",
+ description = "Current Status: " .. status, index = "ToggleGarage"}}
+ if Property[5].value.enabled then
+ opos[#opos + 1] = {title = "Set Position", icon = "fas fa-map-marker-alt", disabled = not Property[5].value.enabled,
+ description = "Sets the Camera angle to your Cameras Direction", index = "SetGaragePos"}
+ else
+ opos[#opos + 1] = {title = "Go Back", icon = "fas fa-arrow-left", description = "return to property creation.", index = "return"}
+ end
+ exports["esx_context"]:Refresh(opos, "right")
+ end
+ if element.index == "SetGaragePos" then
+ ESX.CloseContext()
+ ESX.TextUI("Press ~b~[E]~s~ to Set Position")
+ while true do
+ Wait(0)
+ if IsControlJustPressed(0, 38) then
+ local PlayerPos = GetEntityCoords(ESX.PlayerData.ped)
+ Property[5].value.pos = GetEntityCoords(PlayerPedId())
+ Property[5].value.heading = GetEntityHeading(PlayerPedId())
+ ESX.HideUI()
+ OpenCreate()
+ break
+ end
+ end
+ end
+ if element.index == "SetCCTVangle" then
+ ESX.CloseContext()
+ ESX.TextUI("Press ~b~[E]~s~ to Set Angle")
+ local stage = "angle"
+ while true do
+ Wait(0)
+ if IsControlJustPressed(0, 38) then
+ if stage == "angle" then
+ Property[6].value.rot = GetGameplayCamRot(2)
+ ESX.TextUI("Press ~b~[E]~s~ to Set Max Right Roation")
+ stage = "maxright"
+ elseif stage == "maxright" then
+ Property[6].value.maxright = GetGameplayCamRot(2).z
+ ESX.TextUI("Press ~b~[E]~s~ to Set Max Left Roation")
+ stage = "maxleft"
+ elseif stage == "maxleft" then
+ Property[6].value.maxleft = GetGameplayCamRot(2).z
+ ESX.HideUI()
+ OpenCreate()
+ break
+ end
+ end
+ end
+ end
+ if element.index == "creation" then
+ if HouseData.price and HouseData.name and HouseData.entrance and HouseData.interior then
+ local newProperty = {name = HouseData.name, price = HouseData.price, interior = HouseData.interior, entrance = HouseData.entrance,
+ cctv = Property[6].value, garage = Property[5].value}
+ TriggerServerEvent("esx_property:server:createProperty", newProperty)
+ ESX.ShowNotification("Property created!", "success")
+ ESX.CloseContext()
+ HouseData = {}
+ else
+ ESX.ShowNotification("Please fill out all fields!", "error")
+ end
+ end
+ end
+ end)
+ end
+ OpenCreate()
+ end
+ end)
+end)
+
+RegisterNetEvent("esx_property:AdminMenu", function()
+ ESX.TriggerServerCallback('esx_property:IsAdmin', function(data)
+ if data then
+
+ function ManageProperty(currentProperty)
+ local Interior = GetInteriorValues(Properties[currentProperty].Interior)
+ ESX.TriggerServerCallback('esx_property:IsAdmin', function(data)
+ if data then
+ local opos = {}
+ local function GetData()
+ local elements = {{unselectable = true, icon = "fas fa-cogs", title = "Property Management"},
+ {title = "Go Back", icon = "fas fa-arrow-left", value = "back"},
+ {title = "Toggle Lock", icon = (Properties[currentProperty].Locked and "fas fa-lock") or "fas fa-unlock",
+ description = "Lock/Unlock The Property.", value = "lock"},
+ {title = "Enter", description = "Force Entry Into The Property.", icon = "fas fa-door-open", value = "enter"},
+ {title = "Price", icon = "fas fa-dollar-sign", description = "Alter The Price Of The Property.", value = "price"},
+ {title = "Set Interior", description = "Renovate The Property`s Interior.", icon = "fas fa-home", value = "interior"},
+ {title = "Entrance", description = "Set The Entrance As Your Position.", icon = "fas fa-map-marker-alt",
+ value = "entrance"}}
+ if Properties[currentProperty].setName ~= "" then
+ elements[#elements + 1] = {title = "Clear Custom Name", icon = "fa-solid fa-ban",
+ description = "Current Name: " .. Properties[currentProperty].setName, value = "remove_custom_name"}
+ end
+ if Config.Furniture.Enabled and #(Properties[currentProperty].furniture) > 0 then
+ elements[#elements + 1] = {title = "Reset Furniture", description = "Delete All Property Furniture", icon = "fas fa-eraser",
+ value = "refurni"}
+ end
+ if Config.Garage.Enabled then
+ elements[#elements + 1] = {title = "Garage", description = "Change Garage Settings", icon = "fa-solid fa-warehouse", value = "garage"}
+ end
+ if Config.CCTV.Enabled then
+ elements[#elements + 1] = {title = "CCTV", description = "Change CCTV Settings", icon = "fa-solid fa-camera", value = "cctv"}
+ end
+ if (Config.OxInventory) and Properties[currentProperty].positions.Storage and
+ (ESX.Round(Interior.positions.Storage.x, 2) ~= Properties[currentProperty].positions.Storage.x or
+ ESX.Round(Interior.positions.Storage.y, 2) ~= Properties[currentProperty].positions.Storage.y) then
+ elements[#elements + 1] = {title = "Reset Storage Position", description = "Set Storage Position To Interior Default.",
+ icon = "fas fa-eraser", value = "restorage"}
+ end
+ if Interior.positions.Wardrobe then
+ if ESX.Round(Interior.positions.Wardrobe.x, 2) ~= ESX.Round(Properties[currentProperty].positions.Wardrobe.x, 2) or
+ ESX.Round(Interior.positions.Wardrobe.y, 2) ~= ESX.Round(Properties[currentProperty].positions.Wardrobe.y, 2) then
+ elements[#elements + 1] = {title = "Reset Wardrobe Position", description = "Set Wardrobe Position To Interior Default.",
+ icon = "fas fa-eraser", value = "rewardrobe"}
+ end
+ end
+ if Properties[currentProperty].Owned then
+ elements[#elements + 1] = {title = "Remove Owner", icon = "fas fa-user-times", description = "Evict The Owner Of The Property.",
+ value = "removeowner"}
+ end
+ return elements
+ end
+ ESX.OpenContext("right", GetData(), function(menu, element)
+ if element.value then
+ if element.value == "lock" then
+ ESX.TriggerServerCallback("esx_property:toggleLock", function(IsUnlocked)
+ if IsUnlocked then
+ ESX.ShowNotification("Lock Toggled!", "success")
+ exports["esx_context"]:Refresh(GetData())
+ else
+ ESX.ShowNotification("You Cannot Lock This Property", "error")
+ end
+ end, currentProperty)
+ end
+ if element.value == "enter" then
+ AttemptHouseEntry(currentProperty)
+ end
+ if element.value == "removeowner" then
+ ESX.TriggerServerCallback("esx_property:evictOwner", function(Evicted)
+ if Evicted then
+ ESX.ShowNotification("Owner Evicted!", "success")
+ exports["esx_context"]:Refresh(GetData())
+ else
+ ESX.ShowNotification("You Cannot Evict This Owner!", "error")
+ end
+ end, currentProperty)
+ end
+ if element.value == "garage" then
+ local status = Properties[currentProperty].garage.enabled and "Enabled" or "Disabled"
+ opos = {{unselectable = true, icon = "fas fa-warehouse", title = "Garage Settings"},
+ {title = "Toggle Usage", icon = status == "Enabled" and "fa-solid fa-toggle-on" or "fa-solid fa-toggle-off",
+ description = "Current Status: " .. status, value = "ToggleGarage"}}
+ if Properties[currentProperty].garage.enabled then
+ opos[#opos + 1] = {title = "Set Position", icon = "fas fa-map-marker-alt",
+ disabled = not Properties[currentProperty].garage.enabled,
+ description = "Sets the Garage Position to your Players Position", value = "SetGaragePos"}
+ if Properties[currentProperty].garage.pos then
+ opos[#opos + 1] = {title = "Go Back", icon = "fas fa-arrow-left", description = "return to Property Management.",
+ value = "return"}
+ end
+ else
+ opos[#opos + 1] = {title = "Go Back", icon = "fas fa-arrow-left", description = "return to Property Management", value = "return"}
+ end
+ exports["esx_context"]:Refresh(opos, "right")
+ end
+ if element.value == "cctv" then
+ local status = Properties[currentProperty].cctv.enabled and "Enabled" or "Disabled"
+ opos = {{unselectable = true, icon = "fas fa-warehouse", title = "CCTV Settings"},
+ {title = "Toggle Usage", icon = status == "Enabled" and "fa-solid fa-toggle-on" or "fa-solid fa-toggle-off",
+ description = "Current Status: " .. status, value = "ToggleCCTV"}}
+ if Properties[currentProperty].cctv.enabled then
+ opos[#opos + 1] = {title = "Set Angle", icon = "fas fa-map-marker-alt", disabled = not Properties[currentProperty].cctv.enabled,
+ description = "Sets the Angle of the Camera.", value = "SetCCTVangle"}
+ if Properties[currentProperty].cctv.rot then
+ opos[#opos + 1] = {title = "Go Back", icon = "fas fa-arrow-left", description = "return to Property Management.",
+ value = "return"}
+ end
+ else
+ opos[#opos + 1] = {title = "Go Back", icon = "fas fa-arrow-left", description = "return to Property Management", value = "return"}
+ end
+ exports["esx_context"]:Refresh(opos, "right")
+ end
+ if element.value == "ToggleGarage" then
+ ESX.TriggerServerCallback("esx_property:toggleGarage", function(IsUnlocked, enabled)
+ if IsUnlocked then
+ ESX.ShowNotification("Garage Toggled!", "success")
+ local status = enabled and "Enabled" or "Disabled"
+ opos = {{unselectable = true, icon = "fas fa-warehouse", title = "Garage Settings"},
+ {title = "Toggle Usage", icon = status == "Enabled" and "fa-solid fa-toggle-on" or "fa-solid fa-toggle-off",
+ description = "Current Status: " .. status, value = "ToggleGarage"}}
+ if enabled then
+ opos[#opos + 1] = {title = "Set Position", icon = "fas fa-map-marker-alt", disabled = not enabled,
+ description = "Sets the Garage Position to your Players Position", value = "SetGaragePos"}
+ if Properties[currentProperty].garage.pos then
+ opos[#opos + 1] = {title = "Go Back", icon = "fas fa-arrow-left", description = "return to Property Management.",
+ value = "return"}
+ end
+ else
+ opos[#opos + 1] = {title = "Go Back", icon = "fas fa-arrow-left", description = "return to Property Management",
+ value = "return"}
+ end
+ exports["esx_context"]:Refresh(opos, "right")
+ else
+ ESX.ShowNotification("You ~r~Cannot~s~ Toggle This Option!", "error")
+ end
+ end, currentProperty)
+ end
+ if element.value == "ToggleCCTV" then
+ ESX.TriggerServerCallback("esx_property:toggleCCTV", function(IsUnlocked, enabled)
+ if IsUnlocked then
+ ESX.ShowNotification("CCTV Toggled!", "success")
+ local status = enabled and "Enabled" or "Disabled"
+ opos = {{unselectable = true, icon = "fas fa-warehouse", title = "CCTV Settings"},
+ {title = "Toggle Usage", icon = status == "Enabled" and "fa-solid fa-toggle-on" or "fa-solid fa-toggle-off",
+ description = "Current Status: " .. status, value = "ToggleCCTV"}}
+ if enabled then
+ opos[#opos + 1] = {title = "Set Angle", icon = "fas fa-map-marker-alt", disabled = not enabled,
+ description = "Sets the Angle of the Camera.", value = "SetCCTVangle"}
+ if Properties[currentProperty].cctv.rot then
+ opos[#opos + 1] = {title = "Go Back", icon = "fas fa-arrow-left", description = "return to Property Management.",
+ value = "return"}
+ end
+ else
+ opos[#opos + 1] = {title = "Go Back", icon = "fas fa-arrow-left", description = "return to Property Management",
+ value = "return"}
+ end
+ exports["esx_context"]:Refresh(opos, "right")
+ else
+ ESX.ShowNotification("You ~r~Cannot~s~ Toggle This Option!", "error")
+ end
+ end, currentProperty)
+ end
+ if element.value == "SetGaragePos" then
+ ESX.CloseContext()
+ ESX.TextUI("Press ~b~[E]~s~ to Set Position")
+ SettingValue = "Garage"
+ while SettingValue ~= "" do
+ Wait(0)
+ if IsControlJustPressed(0, 38) then
+ ESX.TriggerServerCallback("esx_property:SetGaragePos", function(IsChanged)
+ if IsChanged then
+ ESX.HideUI()
+ SettingValue = ""
+ ESX.ShowNotification("Position Changed!", "success")
+ ManageProperty(currentProperty)
+ else
+ ESX.ShowNotification("You ~r~Cannot~s~ Change This Option!", "error")
+ end
+ end, currentProperty, GetEntityHeading(PlayerPedId()))
+ break
+ end
+ end
+ end
+ if element.value == "SetCCTVangle" then
+ ESX.CloseContext()
+ ESX.TextUI("Press ~b~[E]~s~ to Set Angle")
+ local stage = "angle"
+ SettingValue = "cctv"
+ local Angles = {}
+ while stage do
+ Wait(0)
+ if IsControlJustPressed(0, 38) then
+ if stage == "angle" then
+ Angles.rot = GetGameplayCamRot(2)
+ ESX.TextUI("Press ~b~[E]~s~ to Set Max Right Roation")
+ stage = "maxright"
+ elseif stage == "maxright" then
+ Angles.maxright = GetGameplayCamRot(2).z
+ ESX.TextUI("Press ~b~[E]~s~ to Set Max Left Roation")
+ stage = "maxleft"
+ elseif stage == "maxleft" then
+ Angles.maxleft = GetGameplayCamRot(2).z
+ ESX.TriggerServerCallback("esx_property:SetCCTVangle", function(IsChanged)
+ if IsChanged then
+ SettingValue = ""
+ stage = nil
+ ESX.HideUI()
+ ESX.ShowNotification("Angle Changed!", "success")
+ ManageProperty(currentProperty)
+ else
+ ESX.ShowNotification("You ~r~Cannot~s~ Change This Option!", "error")
+ end
+ end, currentProperty, Angles)
+ break
+ end
+ end
+ end
+ end
+ if element.value == "remove_custom_name" then
+ ESX.TriggerServerCallback("esx_property:RemoveCustomName", function(Cleared)
+ if Cleared then
+ ESX.ShowNotification("Property Name Reset!", "success")
+ exports["esx_context"]:Refresh(GetData())
+ else
+ ESX.ShowNotification("You Cannot Reset This Property`s Name!", "error")
+ end
+ end, currentProperty)
+ end
+ if element.value == "refurni" then
+ ESX.TriggerServerCallback("esx_property:RemoveAllfurniture", function(Removed)
+ if Removed then
+ ESX.ShowNotification("Furniture Reset!", "success")
+ exports["esx_context"]:Refresh(GetData())
+ else
+ ESX.ShowNotification("You Cannot Reset This Property!", "error")
+ end
+ end, currentProperty)
+ end
+ if element.value == "restorage" then
+ ESX.TriggerServerCallback("esx_property:SetInventoryPosition", function(Reset)
+ if Reset then
+ ESX.ShowNotification("~b~Storage~s~ Position Reset!", "success")
+ exports["esx_context"]:Refresh(GetData())
+ else
+ ESX.ShowNotification("You Cannot Reset This Property!", "error")
+ end
+ end, currentProperty, Interior.positions.Storage, true)
+ end
+ if element.value == "rewardrobe" then
+ ESX.TriggerServerCallback("esx_property:SetWardrobePosition", function(Reset)
+ if Reset then
+ ESX.ShowNotification("~b~Wardrobe~s~ Position Reset!", "success")
+ exports["esx_context"]:Refresh(GetData())
+ else
+ ESX.ShowNotification("You Cannot Reset This Property!", "error")
+ end
+ end, currentProperty, Interior.positions.Wardrobe, true)
+ end
+ if element.value == "back" then
+ AdminOptions(currentProperty)
+ end
+ if element.value == "return" then
+ exports["esx_context"]:Refresh(GetData())
+ end
+ if element.value == "price" then
+ ESX.UI.Menu.Open('dialog', GetCurrentResourceName(), 'PropertyPrice', {title = "Property Price"}, function(data4, menu4)
+ if data4.value then
+ ESX.TriggerServerCallback("esx_property:ChangePrice", function(IsChanged)
+ if IsChanged then
+ ESX.ShowNotification("Price Changed!", "success")
+ menu4.close()
+ else
+ ESX.ShowNotification("You Cannot Change this property!", "error")
+ end
+ end, currentProperty, tonumber(data4.value))
+ end
+ end, function(data4, menu4)
+ menu4.close()
+ end)
+ end
+ if element.value == "interior" then
+ local elements = {{unselectable = true, icon = "fas fa-warehouse", title = "Interior Types"},
+ {title = "IPL Interiors", description = "Native GTA Interiors, Made by R*", value = "IPL"}}
+ if Config.Shells then
+ elements[3] = {title = "Custom Interiors", description = "Custom Interiors, Made by You", value = "Shells"}
+ end
+ ESX.OpenContext("right", elements, function(menu, element)
+ if element.value then
+ local elements = {{unselectable = true, icon = "fas fa-warehouse", title = "Interiors"}}
+ for i = 1, #(Config.Interiors[element.value]) do
+ elements[#elements + 1] = {title = Config.Interiors[element.value][i].label, value = Config.Interiors[element.value][i].value}
+ end
+ ESX.OpenContext("right", elements, function(menu, element)
+ if element.value then
+ ESX.TriggerServerCallback("esx_property:ChangeInterior", function(IsChanged)
+ if IsChanged then
+ ESX.ShowNotification("Interior Changed!", "success")
+ ESX.CloseContext()
+ else
+ ESX.ShowNotification("You Cannot Change this property!", "error")
+ end
+ end, currentProperty, element.value)
+ end
+ end)
+ end
+ end, function()
+ ManageProperty(currentProperty)
+ end)
+ end
+ if element.value == "entrance" then
+ ESX.TriggerServerCallback("esx_property:ChangeEntrance", function(IsChanged)
+ if IsChanged then
+ ESX.ShowNotification("Entrance Changed!", "success")
+ else
+ ESX.ShowNotification("You Cannot Change this property!", "error")
+ end
+ end, currentProperty, GetEntityCoords(ESX.PlayerData.ped))
+ end
+ end
+ end)
+ end
+ end)
+ end
+
+ function AdminOptions(currentProperty)
+ ESX.TriggerServerCallback('esx_property:IsAdmin', function(data)
+ if data then
+ local elements = {{unselectable = true, icon = "fas fa-home", title = "Property Options"},
+ {title = "Go Back", icon = "fas fa-arrow-left", value = "back"},
+ {title = "Manage", icon = "fas fa-cogs", description = "Alter This Property's Settings.", value = "manage"},
+ {title = "Teleport", description = "Teleport To This Property.", icon = "fas fa-map-marker-alt", value = "goto"},
+ {title = "Set GPS", description = "Set GPS position To Property.", icon = "fa-solid fa-location-dot", value = "gps"},
+ {title = "Delete", icon = "fas fa-trash-alt", description = "Remove Current Property.", value = "delete"}}
+
+ ESX.OpenContext("right", elements, function(menu, element)
+ if element.value then
+ if element.value == "goto" then
+ SetEntityCoords(ESX.PlayerData.ped, Properties[currentProperty].Entrance.x, Properties[currentProperty].Entrance.y,
+ Properties[currentProperty].Entrance.z)
+ ESX.ShowNotification("Teleported to Property!")
+ end
+ if element.value == "gps" then
+ SetNewWaypoint(Properties[currentProperty].Entrance.x, Properties[currentProperty].Entrance.y)
+ ESX.ShowNotification("GPS Set!")
+ end
+ if element.value == "back" then
+ AdminMenu()
+ end
+ if element.value == "delete" then
+ ESX.TriggerServerCallback("esx_property:deleteProperty", function(response)
+ if response then
+ ESX.ShowNotification("Property Deleted!", "success")
+ ESX.CloseContext()
+ else
+ ESX.ShowNotification("You Cannot Delete This Property", "error")
+ end
+ end, currentProperty)
+ end
+
+ if element.value == "manage" then
+ ManageProperty(currentProperty)
+ end
+ end
+ end)
+ end
+ end)
+ end
+
+ function AdminMenu()
+ ESX.TriggerServerCallback('esx_property:IsAdmin', function(data)
+ if data then
+ local elements = {{unselectable = true, icon = "fas fa-home", title = "Properties Management"}}
+ for i = 1, #(Properties) do
+ if Properties[i].Entrance then
+ local description = ""
+ if Properties[i].setName ~= "" then
+ description = description .. "\nName: " .. Properties[i].setName
+ end
+ if Properties[i].Owned then
+ description = description .. "\nOwner: " .. Properties[i].OwnerName
+ end
+ table.insert(elements, {title = Properties[i].Name, value = i, description = description, icon = "fas fa-home"})
+ end
+ end
+ ESX.OpenContext("right", elements, function(menu, element)
+ if element.value then
+ ESX.CloseContext()
+ AdminOptions(element.value)
+ end
+ end)
+ end
+ end)
+ end
+
+ AdminMenu()
+ else
+ ESX.ShowNotification("You ~r~Cannot~s~ Access This Menu!", 5000, "error")
+ end
+ end)
+end)
+
diff --git a/[esx_addons]/esx_property/config.lua b/[esx_addons]/esx_property/config.lua
new file mode 100644
index 00000000..b8d22423
--- /dev/null
+++ b/[esx_addons]/esx_property/config.lua
@@ -0,0 +1,644 @@
+Config = {}
+
+-------- Note: Can Cause quite abit of clutter ---------------
+Config.ForSaleBlips = false -- Add Blips to For Sale Properties
+Config.OwnedBlips = true -- Add blips for Owned Properties
+---------------------------------------------------------------
+
+--------------------- General Settings ---------------------------------
+Config.Shells = false -- Enable/Disable Shell Interiors Default: false
+Config.CanAlwaysExit = true -- Disabling this allows players to be locked in
+Config.OwnerCanAlwaysEnter = true -- Disabling this means the owner can't enter the property unless door is unlocked
+Config.MaxNameLength = 20 -- Max Name Length for Owned Properties Default: 20
+Config.CanCustomiseInventoryAndWardrobePositions = true -- Allow users to customise inventory and wardrobe positions Default: true
+Config.WipeCustomNameOnSell = true -- Wipe custom name on sell Default: true
+Config.WipeFurnitureOnSell = true -- Wipe custom name on sell Default: true
+Config.Raiding = {
+ Enabled = true, -- Enable/Disable Raiding Default: true
+ CanAdminsRaid = true, -- Can Admins Raid Houses Default: true
+ ItemRequired = { -- Item Required to Raid -- Remove To Disable
+ RemoveItem = true, -- Remove Item from Inventory Default: true
+ name = "bread", -- Item Name Default: "bread"
+ label = "Bread", -- Item label Default: "Bread"
+ ItemCount = 1, -- Item Count Default: 1
+ },
+ Animation = {
+ type = "Scenario",
+ Scenario = "WORLD_HUMAN_HAMMERING",
+ },
+}
+
+
+Config.Garage = {
+ Enabled = true, -- Enable/Disable Garage Default: true
+ OwnedVehiclesOnly = true, -- Only allow owned vehicles to be stored Default: true
+}
+
+Config.Logs ={
+ Webhook = "",
+ LogLevel = 1
+
+ ----------- Log Levels ------------
+ -- 0 = No Logs
+ -- 1 = Logs Major Actions
+ -- 2 = Logs Major + Minor Actions
+ -- 3 = Logs All Actions
+ -------------------------------------
+}
+
+Config.Furniture = {
+ Enabled = true,
+ RotationSpeed = 0.4, -- Object Rotation Speed
+ MovementSpeed = 0.01, -- Object Movement Speed
+ MovementZspeed = 0.05, -- Object Z Movement Speed
+ WipeFurnitureOnSell = true, -- Wipe Furniture On Sell Default: true
+ Controls = {
+ PlusX = 174, -- lEFT Arrow
+ MinusX = 175, -- RIGHT Arrow
+ RotateRight = 19, -- Alt,
+ RotateLeft = 21, -- Shift,
+ Up = 96, -- UP Arrow
+ Down = 97, -- DOWN Arrow
+ Confirm = 201, -- ENTER
+ Exit = 194, -- BACKSPACE
+ PlusY = 172, -- SHIFT
+ MinusY = 173, -- SHIFT
+ }
+}
+
+Config.CCTV = {
+ Enabled = true,
+ PictureWebook = "", -- Set a discord webhook here to enable taking pictures of the CCTV (link is copied to user`s clipboard)
+ HeightAboveDoor = 2.5, -- Height above the door to place the cctv camera
+ FOV = 80.0, -- Camera Field of View
+ MaxLeftRotation = 80,
+ MaxZoom = 30,
+ MinZoom = 100,
+ MaxRightRotation = -50,
+ MaxUpRotation = 10,
+ MaxDownRotation = -45,
+ RotateSpeed = 0.3, -- Camera Rotation Speed
+ Controls = {
+ Left = 34, -- lEFT Arrow
+ Right = 35, -- RIGHT Arrow
+ Screenshot = 201, -- ENTER
+ NightVision = 38, -- E
+ ZoomIn = 96, -- UP Arrow
+ ZoomOut = 97, -- DOWN Arrow
+ Up = 32, -- UP Arrow
+ Down = 33, -- DOWN Arrow
+ Exit = 194, -- BACKSPACE
+ }
+}
+
+
+-------- Groups allowed to use Admin Functions -------------------
+Config.AllowedGroups = {
+ -- Note: Superadmin is not a group In ESX
+ "admin"
+}
+
+------------------Interacting With Wardrobe Markers ------------------------------
+Config.WardrobeInteraction = function(PropertyId, Interaction)
+ ESX.TriggerServerCallback('esx_property:getPlayerDressing', function(dressing)
+ local elements = {{unselectable = true, icon = "fas fa-tshirt", title = "Wardrobe"}}
+
+ for i=1, #dressing, 1 do
+ elements[#elements + 1] = {
+ title = dressing[i],
+ value = i
+ }
+ end
+
+ ESX.OpenContext("left", elements, function(menu, element)
+ TriggerEvent('skinchanger:getSkin', function(skin)
+ ESX.TriggerServerCallback('esx_property:getPlayerOutfit', function(clothes)
+ TriggerEvent('skinchanger:loadClothes', skin, clothes)
+ TriggerEvent('esx_skin:setLastSkin', skin)
+
+ TriggerEvent('skinchanger:getSkin', function(skin)
+ TriggerServerEvent('esx_skin:save', skin)
+ end)
+ end, element.value)
+ end)
+ end)
+end)
+end
+
+--------------------- Real Estate Settings ----------
+Config.PlayerManagement = {
+ Enabled = false, -- Enable/Disable Player Management Default: true
+ job = "realestateagent", -- Job Required to Manage Players Default: "realestateagent"
+ joblabel = "Estate Agent", -- Job Label Default: "Estate Agent"
+ society = "society_realestateagent", -- Society Required to Manage Players Default: "society_realestateagent"
+ SalePercentage = 0.25, -- Sale Percentage Default: 0.25 -- Note: This is a percentage of the price of the house
+ jobRanks = {
+ {
+ grade = 0,
+ name = "trainee",
+ label = "Trainee",
+ salary = 15,
+ },
+ {
+ grade = 1,
+ name = "agent",
+ label = "Experienced Agent",
+ salary = 30,
+ },
+ {
+ grade = 2,
+ name = "boss",
+ label = "Chief Agent",
+ salary = 45,
+ },
+ },
+ Permissions = { -- Minimum Grade Required for Action
+ CreateProperty = 0, -- Create Property
+ BillPlayers = 1, -- Send Players A Bill
+ DeleteProperty = 2, -- Delete Property
+ ViewProperties = 0, -- View All Properties
+ SellProperty = 0, -- Sell Property to players
+ ToggleCCTV = 1, -- Change CCTV settings
+ ToggleLock = 0, -- Change Lock Settings
+ ToggleGarage = 1, -- Change Garage Settings
+ SetPropertyName =1, -- Change Property Name
+ RemovePropertyName = 1, -- Remove Property Name
+ SetPropertyPrice = 0, -- Change Property Price
+ ChangeInterior = 1, -- Change Interior
+ ResetFurniture = 0, -- Reset Furniture
+ EvictOwner = 0, -- Evict Owners
+ ChangeEntrance = 0, -- Change Entrance
+ EditInteriorPositions = 1, -- Edit Interior Positions
+ ManagePropertiesFromQuickActions = 1, -- Manage Properties From Quick Actions (F6)
+ },
+ Locations = {
+ Entrance = vector3( -199.151, -575.000, 39.489 ), -- Entrance Location Default: vector3( -199.151, -575.000, 39.489 )
+ Exit = vector3( -141.226, -614.166, 167.820 ), -- Exit Location (Interior Location) Default: vector3( -141.226, -614.166, 167.820 )
+ Properties = vector3(-138.9228, -634.1255, 167.8504), -- Property Management Menu Default: vector3(-138.9228, -634.1255, 167.8504)
+ BossMenu = vector3(-126.0031, -641.1303, 167.8504), -- Boss Menu Default: vector3(-126.0031, -641.1303, 167.8504)
+ }
+}
+
+----------------------Long Lists----------------------------------------
+Config.FurnitureStores = {
+ {
+ title = "Ikea",
+ Catagories = {"Decorations", "Tables", "Sofas", "Bedroom"}
+ },
+ {
+ title = "Electronics Store",
+ Catagories = {"Electronics","Kitchen"}
+ },
+}
+
+Config.Interiors = {
+ IPL = {
+ {
+ label = "Modern Apartment",
+ value = "apa_v_mp_h_01_a",
+ positions = {
+ Wardrobe = vec3(-797.72, 328.03, 220.42),
+ Storage = vec3(-795.67, 326.67, 217.037), -- Only Works with Ox Inventory
+ },
+ type = "ipl",
+ pos = vector3(-786.8663, 315.7642, 217.6385)
+ },
+ {
+ label = "Mody Apartment",
+ value = "apa_v_mp_h_02_a",
+ positions = {
+ Wardrobe = vec3(-797.591187, 327.995605, 220.424194),
+ Storage = vec3(-795.441772, 326.307678, 217.037354), -- Only Works with Ox Inventory
+ },
+ type = "ipl",
+ pos = vector3(-787.0749, 315.8198, 217.6386)
+ },
+ {
+ label = "Vibrant Apartment",
+ value = "apa_v_mp_h_03_a",
+ positions = {
+ Wardrobe = vec3(-798.131897, 328.417572, 220.424194),
+ Storage = vec3(-796.786804, 327.164825, 217.037354),-- Only Works with Ox Inventory
+ },
+ type = "ipl",
+ pos = vector3(-786.6245, 315.6175, 217.6385)
+ },
+ {
+ label = "Regal Apartment",
+ value = " apa_v_mp_h_07_c",
+ positions = {
+ Wardrobe = vector3(-797.6594, 328.4619, 190.5122),
+ Storage = vector3(-796.0188, 326.7346, 187.3131), -- Only Works with Ox Inventory
+ },
+ type = "ipl",
+ pos = vector3(-787.0574, 315.6567, 187.9135)
+ },
+ {
+ label = "Tinsel Towers",
+ value = "tinsel_towers",
+ positions = {
+ Wardrobe = vector3(-594.5938, 55.6803, 96.9996),
+ Storage = vector3(-622.8556, 54.7504, 97.5995), -- Only Works with Ox Inventory
+ },
+ type = "ipl",
+ pos = vector3(-603.7109, 59.0251, 98.2002)
+ },
+ {
+ label = "Dell Perro Heights",
+ value = "dell_perro_heights",
+ positions = {
+ Wardrobe = vector3(-1467.2574, -536.9599, 50.7325),
+ Storage = vector3(-1457.0121, -531.0082, 56.9373), -- Only Works with Ox Inventory
+ },
+ type = "ipl",
+ pos = vector3(-1449.7212, -526.1263, 56.9290)
+ },
+ {
+ label = "Mid-Tier Apartment",
+ value = "mid-end",
+ positions = {
+ Wardrobe = vector3(350.7425, -994.2987, -99.1472),
+ Storage = vector3(343.8686, -1001.1404, -99.1962), -- Only Works with Ox Inventory
+ },
+ type = "ipl",
+ pos = vector3(346.5085, -1012.7023, -99.1962)
+ },
+ {
+ label = "Low-End Apartment",
+ value = "low-end",
+ positions = {
+ Wardrobe = vector3(259.9943, -1003.4595, -99.0086),
+ Storage = vector3(265.9162, -999.3954, -99.0086), -- Only Works with Ox Inventory
+ },
+ type = "ipl",
+ pos = vector3(266.0285, -1007.4129, -101.0086)
+ },
+ {
+ label = "High-End House 1",
+ value = "wild_oats",
+ positions = {
+ Wardrobe = vector3(-167.4227, 487.7173, 133.0),
+ Storage = vector3(-174.4463, 492.5583, 130.0436), -- Only Works with Ox Inventory
+ },
+ type = "ipl",
+ pos = vector3(-174.3548, 497.7895, 137.6538)
+ },
+ {
+ label = "Clubhouse",
+ value = "bkr_biker_interior_placement_interior_0_biker_dlc_int_01_milo",
+ positions = {
+ Wardrobe = vector3(1116.9374, -3162.8467, -36.8705),
+ Storage = vector3(1112.0820, -3151.4019, -37.5186), -- Only Works with Ox Inventory
+ },
+ type = "ipl",
+ pos = vector3(1121.1871, -3152.6177, -37.0628)
+ },
+ },
+ Shells = {
+ {
+ label = "Michael House",
+ value = "shell_michael",
+ positions = {
+ Wardrobe = vector3(-12.71,8.36,-5.8), -- Wardrobe Location
+ Storage = vector3(-177.77,-2.05,-1.44), -- Only Works with Ox Inventory
+ },
+ type = "shell",
+ pos = vector3(9.290245, -5.563965, 5.053642)
+ },
+ {
+ label = "Standard Motel",
+ value = "standardmotel_shell",
+ positions = {
+ Wardrobe = vector3(-1.435, -4.74, -1.0),
+ Storage = vector3(2.45, -4.37, -1.0), -- Only Works with Ox Inventory
+ },
+ type = "shell",
+ pos = vector3(0.054924, 2.459473, 1.546776)
+ },
+ {
+ label = "Mid-Tier apartment",
+ value = "furnitured_midapart",
+ positions = {
+ Wardrobe = vector3(-4.5, -18.99, -1.2),
+ Storage = vector3(-5.1, -13.68, -1.2), -- Only Works with Ox Inventory
+ },
+ type = "shell",
+ pos = vector3(-1.434044, 9.802368, 1.511887)
+ },
+ {
+ label = "Trever`s Trailer",
+ value = "shell_trevor",
+ positions = {
+ Wardrobe = vector3(-0.58, -4.85, -1.0),
+ Storage = vector3(3.02, -1.467, -1.0), -- Only Works with Ox Inventory
+ },
+ type = "shell",
+ pos = vector3(-0.156189, 3.585266, 1.398056)
+ },
+ {
+ label = "Trailer",
+ value = "shell_trailer",
+ positions = {
+ Wardrobe = vector3(3.8, -2.92, -1.0),
+ Storage = vector3(-5.69, -0.57, -1.12), -- Only Works with Ox Inventory
+ },
+ type = "shell",
+ pos = vector3(1.282555, 1.713379, 1.469521)
+ },
+ {
+ label = "Low-End Apartment",
+ value = "shell_v16low",
+ positions = {
+ Wardrobe = vector3(7.31, -6.71, -3.0),
+ Storage = vector3(0.61, -8.30, -3.0), -- Only Works with Ox Inventory
+ },
+ type = "shell",
+ pos = vector3(-4.798767, 6.197479, 2.644226)
+ },
+ {
+ label = "Store",
+ value = "shell_store1",
+ positions = {
+ Storage = vector3(0.03, -8.4, -1.1), -- Only Works with Ox Inventory
+ },
+ type = "shell",
+ pos = vector3(2.666809, 4.603638, 1.609573)
+ },
+ {
+ label = "Ranch",
+ value = "shell_ranch",
+ positions = {
+ Wardrobe = vector3(-2.78, -10.76, -1.0),
+ Storage = vector3(3.1, 8.04, -1.0), -- Only Works with Ox Inventory
+ },
+ type = "shell",
+ pos = vector3(0.873413, -5.749237, 2.416748)
+ },
+ }
+}
+
+Config.FurnitureCatagories =
+{Decorations = {{name = "apa_mp_h_acc_bottle_01", title = "Bottle", price = 700},
+ {name = "apa_mp_h_acc_candles_01", title = "Candles", price = 700},
+ {name = "p_int_jewel_mirror", title = "Mirror", price = 700},
+ {name = "apa_mp_h_acc_dec_plate_01", title = "Decorative Plate", price = 700},
+ {name = "apa_mp_h_acc_vase_01", title = "Vase", price = 700},
+ {name = "v_res_desktidy", title = "Desk Supplies", price = 700},
+ {name = "ex_prop_ashtray_luxe_02", title = "Ashtray", price = 700},
+ {name = "v_res_mp_ashtrayb", title = "Ashtray 2", price = 300},
+ {name = "prop_bong_01", title = "Bong", price = 700},
+ {name = "prop_mr_rasberryclean", title = "Mr Rasberry Clean", price = 700},
+ {name = "prop_acc_guitar_01", title = "Guitar", price = 1000},
+ {name = "p_planning_board_04", title = "Planning Board", price = 500},
+ {name = "prop_hotel_clock_01", title = "Hotel Clock", price = 500},
+ {name = "p_cs_pamphlet_01_s", title = "Pamphlet", price = 700},
+ {name = "prop_big_clock_01", title = "Big Clock", price = 500},
+ {name = "prop_egg_clock_01", title = "Egg Clock", price = 500},
+ {name = "prop_ld_greenscreen_01", title = "Green Screen", price = 100},
+ {name = "prop_dart_bd_cab_01", title = "Dart", price = 500},
+ {name = "prop_dart_bd_01", title = "Dart 2", price = 500},
+ {name = "prop_exercisebike", title = "Exercise Bike", price = 500},
+ {name = "p_laz_j02_s", title = "Laz", price = 500},
+ {name = "v_res_cherubvase", title = "White Vase", price = 500},
+ {name = "v_res_d_paddedwall", title = "Padded Wall", price = 500},
+ {name = "prop_dummy_01", title = "Dummy", price = 100},
+ {name = "prop_el_guitar_01", title = "E Guitar 1", price = 100},
+ {name = "prop_el_guitar_02", title = "E Guitar 2", price = 100},
+ {name = "prop_el_guitar_03", title = "E Guitar 2", price = 100},
+ {name = "prop_ld_greenscreen_01", title = "Green Screen", price = 100},
+ {name = "v_res_mbowlornate", title = "Ornate Bowl", price = 300},
+ {name = "v_res_mbronzvase", title = "Bronze Vase", price = 300},
+ {name = "prop_ceramic_jug_01", title = "Ceramic Jug", price = 100},
+ {name = "v_res_m_candle", title = "Candle Large 1", price = 300},
+ {name = "v_res_m_candlelrg", title = "Candle Large 2", price = 300},
+ {name = "apa_mp_h_acc_candles_06", title = "Candles 1", price = 50},
+ {name = "apa_mp_h_acc_candles_05", title = "Candles 2", price = 50},
+ {name = "apa_mp_h_acc_candles_04", title = "Candles 3", price = 50},
+ {name = "apa_mp_h_acc_rugwools_01", title = "Rug 1", price = 300},
+ {name = "apa_mp_h_acc_rugwoolm_01", title = "Rug 2", price = 300},
+ {name = "apa_mp_h_acc_rugwooll_04", title = "Rug 3", price = 300},
+ {name = "apa_mp_h_acc_rugwooll_03", title = "Rug 4", price = 300},
+ {name = "apa_mp_h_acc_rugwoolm_04", title = "Rug 5", price = 300},
+ {name = "apa_mp_h_acc_rugwools_03", title = "Rug 6", price = 300},
+ {name = "v_res_fh_pouf", title = "Pouf", price = 300},
+ {name = "v_res_fh_sculptmod", title = "Sculpture", price = 300},
+ {name = "prop_ceramic_jug_01", title = "Ceramic Jug", price = 100},
+ {name = "prop_v_5_bclock", title = "Vintage Clock", price = 300},
+ {name = "prop_v_15_cars_clock", title = "American Flag Clock", price = 300},
+ {name = "prop_sm_19_clock", title = "Modern Clock", price = 300},
+ {name = "prop_sports_clock_01", title = "Sports Clock", price = 300},
+ {name = "prop_mem_candle_01", title = "Candle 1", price = 300},
+ {name = "prop_game_clock_01", title = "Crown Clock", price = 300},
+ {name = "prop_game_clock_02", title = "Kronos Clock", price = 300},
+ {name = "prop_id2_20_clock", title = "Modern Clock 2", price = 300},
+ {name = "ex_office_citymodel_01", title = "CIty name", price = 300},
+ {name = "apa_mp_h_acc_dec_head_01", title = "Mask", price = 300},
+ {name = "ex_mp_h_acc_vase_06", title = "Vase 1", price = 300},
+ {name = "ex_mp_h_acc_vase_02", title = "Red Vase", price = 300},
+ {name = "hei_prop_hei_bust_01", title = "Bust", price = 300},
+ {name = "prop_arcade_01", title = "Arcade Machine", price = 300},
+ {price = 6500, name = 'prop_beer_neon_01', title = 'Neon Sign 1'},
+ {price = 6500, name = 'prop_beer_neon_02', title = 'Neon Sign 2'},
+ {price = 6500, name = 'prop_beer_neon_03', title = 'Neon Sign 3'},
+ {price = 6500, name = 'prop_beer_neon_04', title = 'Neon Sign 3'},
+ {price = 6500, name = 'prop_patriotneon', title = 'Neon Sign Patriot'},
+ {price = 6500, name = 'prop_barrachneon', title = 'Neon Sign Pussy Beer'}},
+ Electronics = {{price = 0, title = 'Lamp', name = 'prop_cd_lamp'},
+ {price = 1800, title = 'Shredder', name = 'v_ret_gc_shred'},
+ {price = 800, title = 'Antique telephone', name = 'apa_mp_h_acc_phone_01'},
+ {price = 14700, title = 'TV wall white-gray with electronics', name = 'apa_mp_h_str_avunitl_04'},
+ {price = 6700, title = 'TV wooden wall with television', name = 'apa_mp_h_str_avunitl_01_b'},
+ {price = 14300, title = 'Television with yellow speakers', name = 'apa_mp_h_str_avunitm_01'},
+ {price = 12000, title = 'TV with white speakers', name = 'apa_mp_h_str_avunitm_03'},
+ {price = 12900, title = 'Television with accessories', name = 'apa_mp_h_str_avunits_01'},
+ {price = 6900, title = 'TV on a metal table', name = 'apa_mp_h_str_avunits_01'},
+ {price = 5500, title = 'Notebook', name = 'bkr_prop_clubhouse_laptop_01a'},
+ {price = 4400, title = 'Notebook', name = 'bkr_prop_clubhouse_laptop_01b'},
+ {price = 12400, title = 'Money counter', name = 'bkr_prop_money_counter'},
+ {price = 700, title = 'Large upright fan', name = 'bkr_prop_weed_fan_floor_01a'},
+ {price = 2400, title = 'Tvsmash', name = 'des_tvsmash_start'},
+ {price = 2700, title = 'Wall TV', name = 'ex_prop_ex_tv_flat_01'},
+ {price = 3500, title = 'Desktop monitor with keyboard', name = 'ex_prop_monitor_01_ex'},
+ {price = 4700, title = 'Desktop monitor with keyboard', name = 'ex_prop_trailer_monitor_01'},
+ {price = 200, title = 'TV driver', name = 'ex_prop_tv_settop_remote'},
+ {price = 2200, title = 'TV box', name = 'ex_prop_tv_settop_box'},
+ {price = 1000, title = 'Table fan', name = 'gr_prop_bunker_deskfan_01a'},
+ {price = 8400, title = 'Television with speakers and all equipment', name = 'hei_heist_str_avunitl_03'},
+ {price = 1700, title = 'telephone landline', name = 'hei_prop_hei_bank_phone_01'},
+ {price = 3800, title = 'Alarm', name = 'hei_prop_hei_bio_panel'},
+ {price = 2100, title = 'Keyboard', name = 'hei_prop_hei_cs_keyboard'},
+ {price = 8900, title = 'Project board', name = 'hei_prop_hei_muster_01'},
+ {price = 1800, title = 'WIFI', name = 'hei_prop_server_piece_01'},
+ {price = 3300, title = 'White notebook', name = 'p_laptop_02_s'},
+ {price = 10500, title = 'Safe', name = 'p_v_43_safe_s'},
+ {price = 7900, title = 'Table Hockey', name = 'prop_airhockey_01'},
+ {price = 2400, title = 'Portable Radio', name = 'prop_boombox_01'},
+ {price = 2500, title = 'DVD player', name = 'prop_cctv_cont_03'},
+ {price = 3300, title = 'CD player', name = 'prop_cctv_cont_04'},
+ {price = 900, title = 'PC Mouse', name = 'prop_cs_mouse_01'},
+ {price = 900, title = 'Wall clock', name = 'prop_game_clock_01'},
+ {price = 600, title = 'Wall clock black', name = 'prop_game_clock_02'},
+ {price = 3400, title = 'HiFi system', name = 'prop_hifi_01'},
+ {price = 1500, title = 'square clock', name = 'prop_hotel_clock_01'},
+ {price = 1500, title = 'Clock', name = 'prop_id2_20_clock'},
+ {price = 3400, title = 'Nikon Handheld Camera', name = 'prop_ing_camera_01'},
+ {price = 1700, title = 'White keyboard', name = 'prop_keyboard_01a'},
+ {price = 1600, title = 'Router', name = 'prop_ld_armour'},
+ {price = 1700, title = 'Old monitor', name = 'prop_ld_monitor_01'},
+ {price = 2300, title = 'Small old monitor', name = 'prop_monitor_01b'},
+ {price = 400, title = 'RETRO monitor', name = 'prop_monitor_03b'},
+ {price = 5900, title = 'Monitor', name = 'prop_monitor_w_large'},
+ {price = 4800, title = 'Repráček', name = 'prop_mp3_dock'},
+ {price = 4000, title = 'White PC', name = 'prop_pc_01a'},
+ {price = 4800, title = 'Black PC', name = 'prop_pc_02a'},
+ {price = 2500, title = 'Mobile', name = 'prop_phone_ing'},
+ {price = 2100, title = 'Mobile', name = 'prop_phone_ing_02'},
+ {price = 2000, title = 'Mobile', name = 'prop_phone_ing_02_lod'},
+ {price = 2300, title = 'Mobile', name = 'prop_phone_ing_03'},
+ {price = 1000, title = 'RETRO radio', name = 'prop_radio_01'},
+ {price = 1100, title = 'Small speaker', name = 'prop_speaker_05'},
+ {price = 1000, title = 'Speaker', name = 'prop_speaker_06'},
+ {price = 1900, title = 'Speaker', name = 'prop_speaker_08'},
+ {price = 2000, title = 'Old TV', name = 'prop_trev_tv_01'},
+ {price = 500, title = 'Old TV', name = 'prop_tv_03'},
+ {price = 300, title = 'Old TV', name = 'prop_tv_01'},
+ {price = 400, title = 'RETRO TV', name = 'prop_tv_04'},
+ {price = 1700, title = 'Old TV', name = 'prop_tv_06'},
+ {price = 6500, title = 'Plasma big screen', name = 'prop_tv_flat_01'},
+ {price = 12500, title = 'Plasma thin TV', name = 'prop_tv_flat_01_screen'},
+ {price = 1600, title = 'Plasma small television', name = 'prop_tv_flat_02'},
+ {price = 2100, title = 'Plasma small television', name = 'prop_tv_flat_02b'},
+ {price = 900, title = 'Small TV with stand', name = 'prop_tv_flat_03'},
+ {price = 300, title = 'Small TV on the wall', name = 'prop_tv_flat_03b'},
+ {price = 9200, title = 'Television Michael 50cm', name = 'prop_tv_flat_michael'},
+ {price = 1000, title = 'Little US Clock', name = 'prop_v_15_cars_clock'},
+ {price = 4000, title = 'Professional camera', name = 'prop_v_cam_01'},
+ {price = 4700, title = 'VET player RETRO', name = 'prop_vcr_01'},
+ {price = 9900, title = 'Mixing desk', name = 'v_club_vu_deckcase'},
+ {price = 1500, title = 'RETRO Laptop', name = 'v_ind_ss_laptop'},
+ {price = 2500, title = 'CD seda', name = 'v_res_cdstorage'},
+ {price = 3300, title = 'Ipod', name = 'v_res_ipoddock'},
+ {price = 2800, title = 'Silver Monitor', name = 'v_res_monitorwidelarge'},
+ {price = 2400, title = 'Mouse and as a gift pad', name = 'v_res_mousemat'},
+ {price = 100, title = 'PC Headphones', name = 'v_res_pcheadset'},
+ {price = 1200, title = 'PC speaker', name = 'v_res_pcspeaker'},
+ {price = 5000, title = 'VHS white RETRO player', name = 'v_res_vhsplayer'},
+ {price = 2200, title = 'Vacuum Cleaner', name = 'v_res_vacuum'},
+ {price = 5400, title = 'Shredder', name = 'v_ret_gc_shred'},
+ {price = 100, title = 'Stapler', name = 'v_ret_gc_staple'},
+ {price = 1900, title = 'Hardisk', name = 'xm_prop_x17_harddisk_01a'},
+ {price = 5300, title = 'Computer', name = 'xm_prop_x17_res_pctower'},
+ {price = 12900, title = 'Plasma TV', name = 'xm_prop_x17_tv_flat_01'},
+ {price = 6600, title = 'Plasma TV', name = 'xm_prop_x17_tv_flat_02'},
+ {price = 14000, title = 'Jukebox', name = 'bkr_prop_clubhouse_jukebox_01b'},
+ {price = 1700, title = 'USB', name = 'hei_prop_hst_usb_drive'},
+ {price = 700, title = 'Flashlight', name = 'p_cs_police_torch_s'},
+ {price = 1000, title = 'Mikofon', name = 'p_ing_microphonel_01'},
+ {price = 1600, title = 'Radio', name = 'prop_tapeplayer_01'},
+ {price = 4500, title = 'Multifunction Laser Printer', name = 'prop_printer_01'},
+ {price = 11200, title = 'Multifunction Laser Printer', name = 'prop_printer_02'},
+ {price = 20300, title = 'Jukebox 2', name = 'prop_50s_jukebox'},
+ {price = 21200, title = 'Arcade games', name = 'prop_arcade_01'},
+ {price = 19400, title = 'Safe', name = 'prop_ld_int_safe_01'},
+ {price = 3800, title = 'Astronomical Clock', name = 'prop_v_5_bclock'},
+ {price = 22200, title = 'Table with three monitors', name = 'xm_prop_base_staff_desk_01'},
+ {price = 17900, title = 'Table with three monitors', name = 'xm_prop_base_staff_desk_02'},
+ {price = 1500, title = 'Napkin machine', name = 'prop_handdry_01'},
+ {price = 2700, title = 'Washing Machine', name = 'prop_washer_02'},
+ {price = 600, title = 'Washing machine with its years', name = 'prop_washer_03'},
+ {price = 700, title = 'RETRO washing machine', name = 'v_ret_fh_dryer'}},
+ Tables = {{price = 5600, title = 'Glass table', name = 'apa_mp_h_din_table_04'},
+ {price = 5000, title = 'Glass table', name = 'apa_mp_h_din_table_11'},
+ {price = 5300, title = 'Glass table', name = 'apa_mp_h_tab_sidelrg_07'},
+ {price = 1200, title = 'Black Round Table', name = 'apa_mp_h_tab_sidelrg_04'},
+ {price = 1600, title = 'Glass table', name = 'apa_mp_h_tab_sidelrg_01'},
+ {price = 5700, title = 'Decorative glass table', name = 'apa_mp_h_tab_sidelrg_02'},
+ {price = 2600, title = 'Black coffee table', name = 'apa_mp_h_yacht_coffee_table_01'},
+ {price = 4200, title = 'Square table', name = 'apa_mp_h_yacht_side_table_01'},
+ {price = 3400, title = 'Small table', name = 'gr_dlc_gr_yacht_props_table_01'},
+ {price = 4700, title = 'Long table', name = 'gr_dlc_gr_yacht_props_table_02'},
+ {price = 1000, title = 'Particleboard table', name = 'prop_rub_table_01'},
+ {price = 700, title = 'Particleboard dining table', name = 'prop_rub_table_02'},
+ {price = 700, title = 'TV table', name = 'prop_tv_cabinet_03'},
+ {price = 300, title = 'TV table', name = 'prop_tv_cabinet_04'},
+ {price = 800, title = 'TV table', name = 'prop_tv_cabinet_05'},
+ {price = 5300, title = 'Black modern table', name = 'v_ilev_liconftable_sml'},
+ {price = 4100, title = 'Bar', name = 'xm_prop_lab_desk_01'}},
+ Sofas = {{price = 11500, title = 'Couch with pillows', name = 'prop_couch_01'},
+ {price = 6500, title = 'Padded bench', name = 'prop_wait_bench_01'},
+ {price = 4400, title = 'White leather sofa', name = 'xm_lab_sofa_01'}},
+ Kitchen = {{price = 800, title = 'Kitchen scale', name = 'bkr_prop_coke_scale_01'},
+ {price = 1000, title = 'Home coffee maker', name = 'prop_coffee_mac_02'},
+ {price = 5200, title = 'Automatic juice mixer', name = 'prop_juice_dispenser'},
+ {price = 200, title = 'White wall phone', name = 'prop_office_phone_tnt'},
+ {price = 2300, title = 'Fruit Blender', name = 'p_kitch_juicer_s'},
+ {price = 1500, title = 'Kettle', name = 'prop_kettle_01'},
+ {price = 2600, title = 'Crushing machine', name = 'prop_slush_dispenser'},
+ {price = 700, title = 'Coffee pot', name = 'xm_prop_x17_coffee_jug'},
+ {price = 3700, title = 'Mini bar fridge', name = 'prop_bar_fridge_03'},
+ {price = 800, title = 'Plastic red cup', name = 'apa_prop_cs_plastic_cup_01'},
+ {price = 1000, title = 'Trash', name = 'prop_bin_10a'},
+ {price = 400, title = 'Universal Cleaner', name = 'prop_blox_spray'},
+ {price = 200, title = 'Green bucket', name = 'prop_buck_spade_05'},
+ {price = 400, title = 'Blue bucket', name = 'prop_buck_spade_06'},
+ {price = 200, title = 'Red bucket', name = 'prop_buck_spade_07'},
+ {price = 700, title = 'Cups', name = 'prop_food_cups2'},
+ {price = 400, title = 'White Cup', name = 'prop_mug_02'},
+ {price = 800, title = 'Bowl with donut lid', name = 'v_res_cakedome'},
+ {price = 1000, title = 'Loose tea container', name = 'v_res_fa_pottea'},
+ {price = 300, title = 'Deep Plate', name = 'v_res_mbowl'},
+ {price = 500, title = 'Paper napkins', name = 'v_ret_ta_paproll'},
+ {price = 400, title = 'Glasses', name = 'p_w_grass_gls_s'},
+ {price = 300, title = 'Glasses', name = 'prop_cocktail'},
+ {price = 600, title = 'Knife', name = 'prop_cs_bowie_knife'}},
+ Bedroom = {{price = 3300, title = 'Single bed', name = 'v_res_msonbed'},
+ {price = 100, title = 'Double bed', name = 'p_lestersbed_s'},
+ {price = 600, title = 'Double bed', name = 'p_v_res_tt_bed_s'},
+ {price = 400, title = 'Double modern bed', name = 'apa_mp_h_bed_double_08'},
+ {price = 1100, title = 'Double modern bed', name = 'apa_mp_h_bed_wide_05'},
+ {price = 2200, title = 'Double modern bed', name = 'apa_mp_h_yacht_bed_02'},
+ {price = 700, title = 'Single sofa bed', name = 'ex_prop_exec_bed_01'},
+ {price = 3100, title = 'Double modern bed', name = 'hei_heist_bed_double_08'},
+ {price = 4400, title = 'Chest of drawers', name = 'apa_mp_h_bed_chestdrawer_02'},
+ {price = 400, title = 'RETRO wardrobe', name = 'apa_mp_h_str_shelffloorm_02'},
+ {price = 100, title = 'RETRO chest of drawers low', name = 'apa_mp_h_str_sideboardl_11'},
+ {price = 2500, title = 'Gray-white low cabinet', name = 'apa_mp_h_str_sideboardl_13'},
+ {price = 700, title = 'Wooden chest of drawers', name = 'apa_mp_h_str_sideboardl_14'},
+ {price = 500, title = 'Wood brindle feather duster', name = 'apa_mp_h_str_sideboardm_02'},
+ {price = 900, title = 'Iron open cabinet', name = 'p_cs_locker_01'},
+ {price = 300, title = 'Iron used closed cabinet', name = 'p_cs_locker_01_s'}}}
+
+
+
+-------------------DONT TOUCH -------------------------
+Config.OxInventory = ESX.GetConfig().OxInventory --
+ --
+function GetInteriorValues(Interior) --
+ for _,type in pairs(Config.Interiors) do --
+ for _, interior in pairs(type) do --
+ if interior.value == Interior then --
+ return interior --
+ end --
+ end --
+ end --
+end --
+--------------------------------------------------------
+
+
+--[[
+ ESX Property - Properties Made Right!
+ Copyright (C) 2022 ESX-Framework
+
+ 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 .
+]]
\ No newline at end of file
diff --git a/[esx_addons]/esx_property/fxmanifest.lua b/[esx_addons]/esx_property/fxmanifest.lua
new file mode 100644
index 00000000..285cf170
--- /dev/null
+++ b/[esx_addons]/esx_property/fxmanifest.lua
@@ -0,0 +1,51 @@
+--[[
+ ESX Property - Properties Made Right!
+ Copyright (C) 2022 ESX-Framework
+
+ 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 .
+]]
+fx_version 'adamant'
+
+game 'gta5'
+lua54 'yes'
+
+author 'ESX-Framework'
+description 'Official ESX-Legacy Property System'
+
+version '1.8.0'
+
+shared_script '@es_extended/imports.lua'
+file "client/html/copy.html"
+ui_page "client/html/copy.html"
+
+server_scripts {
+ '@oxmysql/lib/MySQL.lua',
+ '@es_extended/locale.lua',
+ 'locales/*.lua',
+ 'config.lua',
+ 'server/*.lua'
+}
+
+client_scripts {
+ '@es_extended/locale.lua',
+ 'locales/*.lua',
+ 'config.lua',
+ 'client/cctv.lua',
+ 'client/main.lua',
+ 'client/furniture.lua'
+}
+
+dependencies {
+ 'es_extended'
+}
diff --git a/[esx_addons]/esx_property/properties.json b/[esx_addons]/esx_property/properties.json
new file mode 100644
index 00000000..a99deb4e
--- /dev/null
+++ b/[esx_addons]/esx_property/properties.json
@@ -0,0 +1 @@
+[{"Locked":false,"Price":25000,"Entrance":{"z":28.57,"y":6206.16,"x":-467.88},"furniture":[],"plysinside":[],"Keys":[],"Owned":false,"Owner":"","OwnerName":"","setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":90.71163940429688,"maxright":-88.02642059326172,"enabled":true,"rot":{"z":2.7542073726654,"y":2.7045992112562097e-8,"x":-9.43104267120361}},"positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"Name":"1076 Procopio Dr","Interior":"low-end"},{"Locked":false,"Price":25000,"Entrance":{"z":29.07,"y":6260.23,"x":-448.02},"furniture":[],"plysinside":[],"Keys":[],"Owned":false,"Owner":"","OwnerName":"","setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":-113.61099243164064,"maxright":70.65039825439453,"enabled":true,"rot":{"z":162.9940643310547,"y":-0.0,"x":3.75689458847045}},"positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"Name":"1075 Procopio Dr","Interior":"low-end"},{"Locked":false,"Owned":false,"Entrance":{"z":27.96,"y":6314.13,"x":-407.35},"furniture":[],"plysinside":[],"Price":25000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":-54.77568435668945,"maxright":135.13072204589845,"enabled":true,"rot":{"z":-138.95835876464845,"y":-0.0,"x":-1.16786921024322}},"positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"Name":"1074 Procopio Dr","Interior":"low-end"},{"Locked":false,"Owned":false,"Entrance":{"z":28.86,"y":6341.62,"x":-368.23},"furniture":[],"plysinside":[],"Price":25000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":-51.56189727783203,"maxright":132.70008850097657,"enabled":true,"rot":{"z":-139.94163513183598,"y":-0.0,"x":-2.56643605232238}},"positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"Name":"1073 Procopio Dr","Interior":"low-end"},{"Locked":false,"Owned":false,"Entrance":{"z":30.52,"y":6400.88,"x":-272.61},"furniture":[],"plysinside":[],"Price":25000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":-65.34387969970703,"maxright":123.2501983642578,"enabled":true,"rot":{"z":-152.39810180664066,"y":-0.0,"x":-7.54235982894897}},"positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"Name":"1071 Procopio Dr","Interior":"low-end"},{"Locked":false,"Owned":false,"Entrance":{"z":30.48,"y":6414.46,"x":-246.14},"furniture":[],"plysinside":[],"Price":25000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":-148.38644409179688,"maxright":38.33782577514648,"enabled":true,"rot":{"z":128.27305603027345,"y":4.379791960218428e-7,"x":-12.92249584197998}},"positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"Name":"1071 Procopio Dr","Interior":"low-end"},{"Locked":false,"Owned":false,"Entrance":{"z":30.22,"y":6445.48,"x":-229.63},"furniture":[],"plysinside":[],"Price":27500,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":-135.48313903808598,"maxright":48.30059432983398,"enabled":true,"rot":{"z":137.8870849609375,"y":2.1444458297992242e-7,"x":-5.53862285614013}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"1070 Procopio Dr","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":28.89,"y":6551.76,"x":-130.8},"furniture":[],"plysinside":[],"Price":27500,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":-46.79578399658203,"maxright":138.85032653808598,"enabled":true,"rot":{"z":-134.93411254882813,"y":1.0691521623584777e-7,"x":3.44796895980834}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"1067 Procopio Dr","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":30.11,"y":6637.3,"x":-41.72},"furniture":[],"plysinside":[],"Price":25000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":-56.39479827880859,"maxright":129.07382202148438,"enabled":true,"rot":{"z":-144.0503692626953,"y":-1.3340336835199196e-8,"x":-0.24611411988735}},"positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"Name":"1065 Procopio Dr","Interior":"low-end"},{"Locked":false,"Owned":false,"Entrance":{"z":30.72,"y":6654.13,"x":-9.62},"furniture":[],"plysinside":[],"Price":25000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":-68.07413482666016,"maxright":116.67019653320313,"enabled":true,"rot":{"z":-155.01153564453126,"y":-1.068413766347477e-7,"x":-2.71219563484191}},"positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"Name":"1064 Procopio Dr","Interior":"low-end"},{"Locked":false,"Owned":false,"Entrance":{"z":30.86,"y":6207.33,"x":-356.82},"furniture":[],"plysinside":[],"Price":25000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":-45.94052505493164,"maxright":135.7574005126953,"enabled":true,"rot":{"z":-133.8831787109375,"y":-0.0,"x":-2.77191710472106}},"positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"Name":"1034 Paleto Blvd","Interior":"low-end"},{"Locked":false,"Owned":false,"Entrance":{"z":30.87,"y":6252.75,"x":-379.92},"furniture":[],"plysinside":[],"Price":25000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":42.09032440185547,"maxright":-131.2773895263672,"enabled":true,"rot":{"z":-40.4444465637207,"y":4.32035335506953e-7,"x":-8.85426330566406}},"positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"Name":"1034 Procopio Dr","Interior":"low-end"},{"Locked":false,"Owned":false,"Entrance":{"z":31.91,"y":6326.99,"x":-302.19},"furniture":[],"plysinside":[],"Price":25000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":133.55870056152345,"maxright":-42.54082870483398,"enabled":true,"rot":{"z":44.16030502319336,"y":-9.440947223993136e-7,"x":-25.26673126220703}},"positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"Name":"1047 Paleto Blvd","Interior":"low-end"},{"Locked":false,"Owned":false,"Entrance":{"z":30.9,"y":6225.21,"x":-347.3},"furniture":[],"plysinside":[],"Price":25000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":-43.60107040405273,"maxright":134.7738800048828,"enabled":true,"rot":{"z":-137.5096435546875,"y":-0.0,"x":-1.57497417926788}},"positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"Name":"1043 Paleto Blvd","Interior":"low-end"},{"Locked":false,"Owned":false,"Entrance":{"z":32.26,"y":6557.45,"x":-15.3},"furniture":[],"plysinside":[],"Price":25000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":43.89247512817383,"maxright":-133.83499145507813,"enabled":true,"rot":{"z":-45.87245941162109,"y":-0.0,"x":-11.80154037475586}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"1059 Paleto Blvd","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":32.1,"y":6568.19,"x":4.43},"furniture":[],"plysinside":[],"Price":27500,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":-139.30752563476566,"maxright":44.53319549560547,"enabled":true,"rot":{"z":129.4863433837891,"y":2.144746105159357e-7,"x":-5.62087059020996}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"1061 Paleto Blvd","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":31.84,"y":6596.46,"x":31.0},"furniture":[],"plysinside":[],"Price":27500,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":-43.43285751342773,"maxright":-159.96804809570313,"enabled":true,"rot":{"z":-94.7632293701172,"y":-0.0,"x":-5.83577966690063}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"1061 Paleto Blvd","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":42.9,"y":4642.33,"x":1725.06},"furniture":[],"plysinside":[],"Price":12500,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-63.00457000732422,"y":-0.0,"x":-6.35934734344482}},"positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"Name":"2016 Grapeseed Main St","Interior":"low-end"},{"Locked":false,"Owned":false,"Entrance":{"z":42.39,"y":4658.22,"x":1674.01},"furniture":[],"plysinside":[],"Price":12500,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":91.53390502929688,"y":2.7858778395284394e-8,"x":-16.72389602661132}},"positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"Name":"2015 Grapeseed Main St","Interior":"low-end"},{"Locked":false,"Owned":false,"Entrance":{"z":42.68,"y":4677.34,"x":1718.91},"furniture":[],"plysinside":[],"Price":7500,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-93.13695526123049,"y":-0.0,"x":-29.83987236022949}},"positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"Name":"2014 Grapeseed Main St","Interior":"low-end"},{"Locked":false,"Owned":false,"Entrance":{"z":42.09,"y":4689.52,"x":1682.85},"furniture":[],"plysinside":[],"Price":7500,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":91.7155303955078,"y":-0.0,"x":-6.32857131958007}},"positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"Name":"2013 Grapeseed Main St","Interior":"low-end"},{"Locked":false,"Owned":false,"Entrance":{"z":41.03,"y":4739.5,"x":1664.0},"furniture":[],"plysinside":[],"Price":10000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":110.03758239746094,"y":-0.0,"x":-18.96443367004394}},"positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"Name":"2011 Grapeseed Main St","Interior":"low-end"},{"Locked":false,"Owned":false,"Entrance":{"z":32.23,"y":3920.59,"x":1880.27},"furniture":[],"plysinside":[],"Price":10000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-77.30774688720703,"y":-0.0,"x":-14.36374378204345}},"positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"Name":"3012 Niland Ave","Interior":"low-end"},{"Locked":false,"Owned":false,"Entrance":{"z":32.48,"y":3914.59,"x":1846.06},"furniture":[],"plysinside":[],"Price":9000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":103.60260009765624,"y":-2.1725810483985698e-7,"x":-10.75266551971435}},"positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"Name":"3013 Niland Ave","Interior":"low-end"},{"Locked":false,"Owned":false,"Entrance":{"z":36.08,"y":3913.8,"x":1803.57},"furniture":[],"plysinside":[],"Price":11500,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-76.59026336669922,"y":-0.0,"x":-20.23343658447265}},"positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"Name":"3013 Marina Dr","Interior":"low-end"},{"Locked":false,"Owned":false,"Entrance":{"z":33.45,"y":3657.13,"x":1435.31},"furniture":[],"plysinside":[],"Price":11500,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":95.02767944335938,"y":-0.0,"x":-5.87784767150878}},"positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"Name":"3025 Lesbos Ln","Interior":"low-end"},{"Locked":false,"Owned":false,"Entrance":{"z":43.5,"y":2607.8,"x":471.15},"furniture":[],"plysinside":[],"Price":12500,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-166.2356719970703,"y":-0.0,"x":-20.86568069458007}},"positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"Name":"4018 Route 68","Interior":"low-end"},{"Locked":false,"Owned":false,"Entrance":{"z":42.28,"y":3087.1,"x":181.03},"furniture":[],"plysinside":[],"Price":7500,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-93.07178497314452,"y":-0.0,"x":-11.2086534500122}},"positions":{"Wardrobe":{"z":-99.00859832763672,"y":-1003.45947265625,"x":259.9942932128906}},"Name":"4013 Joshua Rd","Interior":"low-end"},{"Locked":false,"Owned":false,"Entrance":{"z":73.50999999999999,"y":-593.38,"x":1386.22},"furniture":[],"plysinside":[],"Price":45000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-127.72049713134766,"y":-0.0,"x":-9.48588275909423}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7340 Nikola Pl","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":73.52,"y":-569.49,"x":1388.94},"furniture":[],"plysinside":[],"Price":45000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-69.5700912475586,"y":-0.0,"x":-9.84068393707275}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7341 Nikola Pl","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":73.71,"y":-555.82,"x":1373.19},"furniture":[],"plysinside":[],"Price":4500,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-114.24230194091796,"y":-0.0,"x":-12.52571487426757}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7341 Nikola Pl","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":73.72999999999999,"y":-606.62,"x":1367.33},"furniture":[],"plysinside":[],"Price":45000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-177.74884033203126,"y":-0.0,"x":-10.93433380126953}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7340 Nikola Pl","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":73.72,"y":-597.26,"x":1341.64},"furniture":[],"plysinside":[],"Price":45000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":62.37366104125976,"y":-0.0,"x":-17.39539337158203}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7339 Nikola Pl","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":72.91,"y":-546.97,"x":1348.31},"furniture":[],"plysinside":[],"Price":45000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-27.96370315551757,"y":4.342483350683324e-7,"x":-10.56496334075927}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7342 Nikola Pl","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":72.27,"y":-583.06,"x":1323.3},"furniture":[],"plysinside":[],"Price":45000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":152.2796173095703,"y":4.441767487151084e-7,"x":-16.03893852233886}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7339 Nikola Pl","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":71.46,"y":-535.91,"x":1328.57},"furniture":[],"plysinside":[],"Price":45000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-111.59012603759766,"y":-0.00000144736395,"x":-53.85140228271484}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7342 Nikola Pl","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":70.75,"y":-574.08,"x":1301.02},"furniture":[],"plysinside":[],"Price":45000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":164.71957397460938,"y":4.6078031346041828e-7,"x":-22.11302947998047}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7339 Nikola Pl","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":70.47999999999999,"y":-527.3,"x":1303.1},"furniture":[],"plysinside":[],"Price":45000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-19.26140213012695,"y":4.363083689895575e-7,"x":-11.92860603332519}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7342 Nikola Pl","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":56.84,"y":-729.5,"x":996.89},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":125.62109375,"y":-0.0,"x":-58.0705451965332}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7322 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":57.24,"y":-716.31,"x":979.11},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":131.13587951660157,"y":-0.0,"x":-15.06083393096923}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7320 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":57.5,"y":-701.3,"x":970.88},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":170.76788330078126,"y":3.3350637806961464e-9,"x":-0.14884614944458}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7320 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":57.47,"y":-669.85,"x":960.15},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":115.8887939453125,"y":4.346124455878453e-7,"x":-10.81925582885742}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7319 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":57.45,"y":-653.51,"x":943.45},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":38.34314727783203,"y":0.00000168464464,"x":-59.54927444458008}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7319 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":58.26,"y":-627.65,"x":980.25},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-144.15000915527345,"y":-0.0,"x":-36.16230392456055}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7318 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":57.26,"y":-639.71,"x":928.82},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":137.4777984619141,"y":-0.0,"x":-15.65773868560791}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7317 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":57.47,"y":-615.51,"x":902.95},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":46.20460510253906,"y":-0.00000160100864,"x":-57.77318572998047}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7317 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":57.47,"y":-608.21,"x":886.77},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":138.07872009277345,"y":-8.718344588487525e-7,"x":-11.68268966674804}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7316 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":57.18,"y":-583.65,"x":861.7},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-176.59844970703126,"y":-0.0,"x":-3.38041257858276}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7316 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":57.01,"y":-562.66,"x":844.12},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":12.50613498687744,"y":-2.1754524937023245e-7,"x":-11.14390087127685}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7312 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":56.95,"y":-532.68,"x":850.25},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":85.02394104003906,"y":-5.362977262279856e-8,"x":-5.74017667770385}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7314 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":56.74,"y":-508.98,"x":861.51},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":46.49221038818359,"y":0.00000154111978,"x":-56.35844421386719}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7313 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":57.11000000000001,"y":-497.87,"x":878.44},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":44.91729354858398,"y":-0.0,"x":-64.80823516845703}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7313 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":58.46,"y":-489.76,"x":906.46},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":19.29909515380859,"y":-0.0,"x":-14.44456386566162}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7311 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":60.1,"y":-477.8,"x":921.78},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-8.25692367553711,"y":-0.0,"x":-19.33738899230957}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7311 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":60.57,"y":-463.17,"x":944.54},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-55.23443603515625,"y":-8.953837777880835e-7,"x":-17.53591918945312}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7309 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":61.81,"y":-451.65,"x":967.16},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":33.32376480102539,"y":8.936335120779404e-7,"x":-17.17726707458496}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7309 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":63.06000000000001,"y":-433.03,"x":987.57},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":34.80197525024414,"y":8.771137913754501e-7,"x":-13.24740600585937}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7307 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":64.36999999999999,"y":-423.38,"x":1010.38},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":123.14452362060549,"y":-4.402644719903037e-7,"x":-14.16050624847412}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7307 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":65.36,"y":-408.36,"x":1028.78},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":41.270263671875,"y":-4.386202476780454e-7,"x":-13.28245830535888}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7305 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":67.25,"y":-378.13,"x":1060.52},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":54.3464241027832,"y":-8.817532943794504e-7,"x":-14.47243404388427}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7305 Bridge St","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":63.52,"y":-469.3,"x":1014.67},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-144.52737426757813,"y":-9.047982416632294e-7,"x":-19.33366966247558}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7306 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":61.16,"y":-502.36,"x":970.55},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-107.44925689697266,"y":-0.0,"x":-8.07564449310302}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7308 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":59.64,"y":-518.94,"x":945.9},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":119.70294189453124,"y":-0.0,"x":-61.49639892578125}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7310 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":58.81,"y":-526.05,"x":924.39},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-151.43536376953126,"y":-0.0,"x":-17.66978073120117}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7312 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":57.53,"y":-540.58,"x":893.24},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-69.45809936523438,"y":-9.24586970540986e-7,"x":-22.57007217407226}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7312 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":57.39,"y":-569.56,"x":919.7},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":28.74713134765625,"y":4.412431167111208e-7,"x":-14.65564727783203}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7312 Nikola Ave","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":58.75,"y":-541.93,"x":965.25},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":32.70853805541992,"y":-4.381802227726439e-7,"x":-13.03651428222656}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7310 Nikola Ave","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":59.71,"y":-525.76,"x":987.84},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":30.89822769165039,"y":9.366781910102872e-7,"x":-24.28778457641601}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7310 Nikola Ave","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":60.01,"y":-510.98,"x":1006.48},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-65.29899597167969,"y":-0.0,"x":-16.08703422546386}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7308 Nikola Ave","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":63.1,"y":-498.06,"x":1046.29},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":144.13137817382813,"y":9.091479000744585e-7,"x":-20.10037040710449}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7306 Bridge St","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":63.32,"y":-470.58,"x":1051.04},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":76.4620132446289,"y":-0.0,"x":-11.98972129821777}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7306 Bridge St","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":65.28,"y":-448.93,"x":1056.24},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":162.82366943359376,"y":-0.0,"x":-8.78071689605712}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7306 Bridge St","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":64.67999999999999,"y":-484.37,"x":1090.46},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-102.9858856201172,"y":-4.4310172597761268e-7,"x":-15.54805183410644}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7344 Bridge St","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":66.33999999999999,"y":-464.52,"x":1098.58},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-15.62946987152099,"y":2.17306606487e-7,"x":-10.81984233856201}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7346 Bridge St","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":66.81,"y":-438.69,"x":1099.44},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":179.49822998046876,"y":-0.0,"x":-11.32894706726074}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7346 Bridge St","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":66.58,"y":-411.33,"x":1101.06},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-93.88529205322266,"y":-5.391471091797939e-8,"x":-8.21971225738525}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7348 Bridge St","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":67.97,"y":-391.27,"x":1114.44},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-112.49811553955078,"y":-0.0,"x":-24.5419979095459}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7348 West Mirror Drive","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":69.03999999999999,"y":-429.86,"x":1262.35},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":112.37248229980468,"y":-2.144359569911103e-7,"x":-5.51487922668457}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7345 East Mirror Dr","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":69.53999999999999,"y":-458.06,"x":1265.74},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":94.98734283447266,"y":-0.0,"x":-14.86165904998779}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7945 East Mirror Dr","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":69.21,"y":-480.18,"x":1259.51},"furniture":[],"plysinside":[],"garage":{"enabled":false,"StoredVehicles":[]},"Owner":"","Price":40000,"setName":"","Keys":[],"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":125.15465545654296,"y":-0.0,"x":-10.50986766815185}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7343 East Mirror Dr","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":68.92999999999999,"y":-494.1,"x":1251.53},"furniture":[],"plysinside":[],"garage":{"enabled":false,"StoredVehicles":[]},"Owner":"","Price":40000,"setName":"","Keys":[],"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":63.34117126464844,"y":-0.0,"x":-16.31183433532715}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7343 East Mirror Dr","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":68.36999999999999,"y":-515.5,"x":1250.9},"furniture":[],"plysinside":[],"garage":{"enabled":false,"StoredVehicles":[]},"Owner":"","Price":40000,"setName":"","Keys":[],"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":77.79755401611328,"y":-0.0,"x":-17.28229522705078}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7343 East Mirror Dr","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":68.67999999999999,"y":-566.43,"x":1241.53},"furniture":[],"plysinside":[],"garage":{"enabled":false,"StoredVehicles":[]},"Owner":"","Price":40000,"setName":"","Keys":[],"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":130.3302459716797,"y":-0.0,"x":-22.43321228027343}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7338 East Mirror Dr","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":68.8,"y":-601.65,"x":1240.54},"furniture":[],"plysinside":[],"garage":{"enabled":false,"StoredVehicles":[]},"Owner":"","Price":40000,"setName":"","Keys":[],"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":91.53223419189452,"y":-1.3435412782314417e-8,"x":-6.82489442825317}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7338 East Mirror Dr","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":68.58999999999999,"y":-620.99,"x":1250.79},"furniture":[],"plysinside":[],"garage":{"enabled":false,"StoredVehicles":[]},"Price":40000,"Owner":"","OwnerName":"","setName":"","Keys":[],"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":27.77294158935547,"y":-2.145688853261163e-7,"x":-5.87094974517822}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7336 East Mirror Dr","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":67.14,"y":-648.63,"x":1265.69},"furniture":[],"plysinside":[],"garage":{"enabled":false,"StoredVehicles":[]},"Owner":"","Price":40000,"setName":"","Keys":[],"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-152.5341949462891,"y":-0.0,"x":-14.65502071380615}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7333 East Mirror Dr","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":65.05,"y":-683.53,"x":1270.98},"furniture":[],"plysinside":[],"garage":{"enabled":false,"StoredVehicles":[]},"Owner":"","Price":40000,"setName":"","Keys":[],"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-171.3050994873047,"y":-0.0,"x":-12.5356969833374}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7333 East Mirror Dr","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":63.93,"y":-702.82,"x":1264.75},"furniture":[],"plysinside":[],"garage":{"enabled":false,"StoredVehicles":[]},"Owner":"","Price":40000,"setName":"","Keys":[],"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":58.4541015625,"y":0.00000175048376,"x":-12.71638584136962}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7328 East Mirror Dr","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":59.98,"y":-725.27,"x":1229.58},"furniture":[],"plysinside":[],"garage":{"enabled":false,"StoredVehicles":[]},"Owner":"","Price":40000,"setName":"","Keys":[],"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-88.46087646484375,"y":-0.0,"x":-11.09668827056884}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7328 East Mirror Dr","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":59.82,"y":-696.93,"x":1223.0},"furniture":[],"plysinside":[],"garage":{"enabled":false,"StoredVehicles":[]},"Owner":"","Price":40000,"setName":"","Keys":[],"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-81.23240661621094,"y":-0.0,"x":-17.37718200683593}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7328 Mirror Park Blvd","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":62.71,"y":-669.31,"x":1221.46},"furniture":[],"plysinside":[],"garage":{"enabled":false,"StoredVehicles":[]},"Owner":"","Price":40000,"setName":"","Keys":[],"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-164.38833618164066,"y":-0.0,"x":-11.55613040924072}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7333 Mirror Park Blvd","Interior":"mid-end"},{"Locked":false,"Owned":false,"Entrance":{"z":65.46,"y":-620.26,"x":1207.46},"furniture":[],"plysinside":[],"Price":40000,"Owner":"","Keys":[],"setName":"","garage":{"enabled":false,"StoredVehicles":[]},"cctv":{"maxleft":80,"maxright":-20,"enabled":false,"rot":{"z":-87.679443359375,"y":1.3399391818325056e-8,"x":-5.38702487945556}},"positions":{"Wardrobe":{"z":-99.14720153808594,"y":-994.2987060546876,"x":350.7424926757813}},"Name":"7336 Mirror Park Blvd","Interior":"mid-end"},{"Locked":false,"Price":40000,"Entrance":{"z":67.08,"y":-598.41,"x":1203.75},"furniture":[],"setName":"","Owned":false,"Owner":"","garage":{"enabled":false},"cctv":{"enabled":false,"maxright":-20,"maxleft":80,"rot":{"x":-16.58763313293457,"y":-0.0,"z":3.90023970603942}},"Keys":[],"Interior":"mid-end","positions":{"Storage":{"x":343.86859130859377,"y":-1001.140380859375,"z":-99.19619750976563},"Wardrobe":{"x":350.74249267578127,"y":-994.2987060546875,"z":-99.14720153808594}},"Name":"7338 Mirror Park Blvd","plysinside":[]},{"Locked":false,"Price":40000,"Entrance":{"z":68.16,"y":-575.59,"x":1201.01},"furniture":[],"setName":"","Owned":false,"Owner":"","garage":{"enabled":false},"cctv":{"enabled":false,"maxright":-20,"maxleft":80,"rot":{"x":-20.23956108093261,"y":9.099596240957908e-7,"z":-37.31895446777344}},"Keys":[],"Interior":"mid-end","positions":{"Storage":{"x":343.86859130859377,"y":-1001.140380859375,"z":-99.19619750976563},"Wardrobe":{"x":350.74249267578127,"y":-994.2987060546875,"z":-99.14720153808594}},"Name":"7338 Mirror Park Blvd","plysinside":[]}]
\ No newline at end of file
diff --git a/[esx_addons]/esx_property/server/main.lua b/[esx_addons]/esx_property/server/main.lua
new file mode 100644
index 00000000..0082eee3
--- /dev/null
+++ b/[esx_addons]/esx_property/server/main.lua
@@ -0,0 +1,1088 @@
+--[[
+ ESX Property - Properties Made Right!
+ Copyright (C) 2022 ESX-Framework
+
+ 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 .
+]] local function Log(title, color, fields, Level)
+ if Level <= Config.Logs.LogLevel then
+ local webHook = Config.Logs.Webhook
+ local embedData = {{['title'] = title, ['color'] = color, ['footer'] = {['text'] = "ESX-Property | " .. os.date(),
+ ['icon_url'] = 'https://cdn.discordapp.com/attachments/944789399852417096/1004915039414788116/imageedit_1_2564956129.png'},
+ ['fields'] = fields, ['description'] = "", ['author'] = {['name'] = "ESX-Framework | Log Level " .. Level,
+ ['icon_url'] = 'https://cdn.discordapp.com/emojis/939245183621558362.webp?size=128&quality=lossless'}}}
+ PerformHttpRequest(webHook, function(err, text, headers)
+ end, 'POST', json.encode({username = 'ESX-Framework', embeds = embedData}), {['Content-Type'] = 'application/json'})
+ end
+end
+
+local PM = Config.PlayerManagement
+local Properties = {}
+
+function PropertiesRefresh()
+ Properties = {}
+ local PropertiesList = LoadResourceFile(GetCurrentResourceName(), 'properties.json')
+ if PropertiesList then
+ Properties = json.decode(PropertiesList)
+ Wait(10)
+ for i = 1, #Properties do
+ if not Properties[i].furniture then
+ Properties[i].furniture = {}
+ end
+ if not Properties[i].cctv then
+ Properties[i].cctv = {enabled = false}
+ end
+ if not Properties[i].garage then
+ Properties[i].garage = {enabled = false}
+ end
+ if not Properties[i].garage.StoredVehicles then
+ Properties[i].garage.StoredVehicles = {}
+ end
+ if not Properties[i].setName then
+ Properties[i].setName = ""
+ end
+ if not Properties[i].positions then
+ local Interior = GetInteriorValues(Properties[i].Interior)
+ Properties[i].positions = Interior.positions
+ end
+ Properties[i].plysinside = {}
+ if Config.OxInventory then
+ if Properties[i].Owned then
+ exports.ox_inventory:RegisterStash("property-" .. i, Properties[i].Name, 15, 100000, Properties[i].Owner)
+ end
+ else
+ Properties[i].positions.Storage = nil
+ end
+ end
+ local Players = ESX.GetExtendedPlayers()
+ Log("ESX-Property Beta Loaded", 11141375, {{name = "Property Count", value = #Properties, inline = true},
+ {name = "OX Inventory", value = Config.OxInventory and "Enabled" or "Disabled", inline = true}}, 1)
+ for _, xPlayer in pairs(Players) do
+ TriggerClientEvent("esx_property:syncProperties", xPlayer.source, Properties, xPlayer.get("lastProperty"))
+ end
+ else
+ Properties = {}
+ print("[ERROR]: Properties.json Not Found!")
+ end
+end
+
+function IsPlayerAdmin(player, action)
+ local xPlayer = ESX.GetPlayerFromId(player)
+
+ for i = 1, #Config.AllowedGroups do
+ if xPlayer.group == Config.AllowedGroups[i] then
+ return true
+ end
+ end
+
+ if Config.PlayerManagement.Enabled and action then
+ if xPlayer.job.name == Config.PlayerManagement.job and xPlayer.job.grade >= Config.PlayerManagement.Permissions[action] then
+ return true
+ end
+ end
+
+ return false
+end
+
+CreateThread(function()
+ Wait(3000)
+ PropertiesRefresh()
+ MySQL.query("ALTER TABLE `users` ADD COLUMN IF NOT EXISTS `last_property` LONGTEXT NULL", function(result)
+ if result then
+ print("[^2INFO^7] Adding ^5last_property^7 column to users table")
+ end
+ end)
+ if PM.Enabled then
+ MySQL.insert("INSERT IGNORE INTO `addon_account` (name, label, shared) VALUES (?, ? , 1)", {PM.society, PM.joblabel}, function(result)
+ if result then
+ print("[^2INFO^7] Adding ^5" .. PM.society .. " - " .. PM.joblabel .. "^7 into ^5addon_account^7 table")
+ end
+ end)
+ local Existance = ESX.DoesJobExist(PM.job, 0)
+ if not Existance then
+ MySQL.query("INSERT INTO `jobs` VALUES (?, ?, 1)", {PM.job, PM.joblabel}, function(result)
+ if result then
+ print("[^2INFO^7] Inserting ^5" .. PM.job .. " - " .. PM.joblabel .. "^7 into ^5jobs^7 table")
+ end
+ end)
+ end
+ for i = 1, #PM.jobRanks do
+ local Existance = ESX.DoesJobExist(PM.job, PM.jobRanks[i].grade)
+ if not Existance then
+ MySQL.query("INSERT INTO `job_grades` (job_name, grade, name, label, salary, skin_male, skin_female) VALUES (?, ?,?, ?, ?,'{}','{}')",
+ {PM.job, PM.jobRanks[i].grade, PM.jobRanks[i].name, PM.jobRanks[i].label, PM.jobRanks[i].salary}, function(result)
+ if result then
+ print("[^2INFO^7] Inserting ^5" .. PM.job .. " - " .. PM.jobRanks[i].grade .. " - " .. PM.jobRanks[i].label ..
+ "^7 into ^5job_grades^7 table")
+ end
+ end)
+ end
+ end
+ Wait(10)
+ ESX.RefreshJobs()
+ TriggerEvent('esx_society:registerSociety', 'realestateagent', 'realestateagent', 'society_realestateagent', 'society_realestateagent',
+ 'society_realestateagent', {type = 'private'})
+ end
+end)
+
+AddEventHandler("esx:playerLoaded", function(playerId, xPlayer)
+ Wait(1000)
+ local lastProperty = nil
+ MySQL.query("SELECT last_property FROM users WHERE identifier = ?", {xPlayer.identifier}, function(result)
+ if result then
+ if result[1].last_property then
+ local Data = json.decode(result[1].last_property)
+ xPlayer.set("lastProperty", Data)
+ lastProperty = Data
+ end
+ end
+ TriggerClientEvent("esx_property:syncProperties", playerId, Properties, lastProperty)
+ end)
+end)
+
+--- Commands
+ESX.RegisterCommand("property:refresh", Config.AllowedGroups, function(xPlayer)
+ PropertiesRefresh()
+end, false, {help = "Refresh Properties"})
+
+ESX.RegisterCommand("property:create", "user", function(xPlayer)
+ if IsPlayerAdmin(xPlayer.source) or (PM.Enabled and xPlayer.job.name == PM.job) then
+ xPlayer.triggerEvent("esx_property:CreateProperty")
+ end
+end, false, {help = "Refresh Properties"})
+
+ESX.RegisterCommand("property:admin", Config.AllowedGroups, function(xPlayer)
+ xPlayer.triggerEvent("esx_property:AdminMenu")
+end, false, {help = "Refresh Properties"})
+
+-- Buy Property
+ESX.RegisterServerCallback("esx_property:buyProperty", function(source, cb, PropertyId)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local Price = Properties[PropertyId].Price
+ if xPlayer.getAccount("bank").money >= Price then
+ xPlayer.removeAccountMoney("bank", Price)
+ Properties[PropertyId].Owner = xPlayer.identifier
+ Properties[PropertyId].OwnerName = xPlayer.getName()
+ Properties[PropertyId].Owned = true
+ Log("Property Bought", 65280, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
+ {name = "**Price**", value = ESX.Math.GroupDigits(Price), inline = true},
+ {name = "**Player**", value = xPlayer.getName(), inline = true}}, 1)
+ TriggerClientEvent("esx_property:syncProperties", -1, Properties)
+ if Config.OxInventory then
+ exports.ox_inventory:RegisterStash("property-" .. PropertyId, Properties[PropertyId].Name, 15, 100000, xPlayer.identifier)
+ end
+ end
+ cb(xPlayer.getAccount("bank").money >= Price)
+end)
+
+ESX.RegisterServerCallback("esx_property:attemptSellToPlayer", function(source, cb, PropertyId, PlayerId)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local xTarget = ESX.GetPlayerFromId(PlayerId)
+ local Price = Properties[PropertyId].Price
+ if xTarget and (xTarget.getAccount("bank").money >= Price) and (xPlayer.job.name == PM.job) then
+ xPlayer.removeAccountMoney("bank", Price)
+ Properties[PropertyId].Owner = xTarget.identifier
+ Properties[PropertyId].OwnerName = xTarget.getName()
+ Properties[PropertyId].Owned = true
+ Log("Property Sold To Player", 65280, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
+ {name = "**Price**", value = ESX.Math.GroupDigits(Price), inline = true},
+ {name = "**Player**", value = xTarget.getName(), inline = true},
+ {name = "**Agent**", value = xPlayer.getName(), inline = true}}, 1)
+ TriggerClientEvent("esx_property:syncProperties", -1, Properties)
+ if Config.OxInventory then
+ exports.ox_inventory:RegisterStash("property-" .. PropertyId, Properties[PropertyId].Name, 15, 100000, xTarget.identifier)
+ end
+ if PM.Enabled then
+ local PlayerPrice = Price * PM.SalePercentage
+ local SocietyPrice = Price - PlayerPrice
+ TriggerEvent('esx_addonaccount:getSharedAccount', PM.society, function(account)
+ account.addMoney(SocietyPrice)
+ end)
+ xPlayer.addAccountMoney("bank", PlayerPrice)
+ end
+ end
+ cb(xPlayer.getAccount("bank").money >= Price)
+end)
+
+-- Buy Property
+ESX.RegisterServerCallback("esx_property:buyFurniture", function(source, cb, PropertyId, PropName, PropIndex, PropCatagory, pos, heading)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local Owner = Properties[PropertyId].Owner
+ if xPlayer.identifier == Owner or IsPlayerAdmin(source) or (Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier]) then
+ local Price = Config.FurnitureCatagories[PropCatagory][PropIndex].price
+ if xPlayer.getAccount("bank").money >= Price then
+ xPlayer.removeAccountMoney("bank", Price)
+ cb(true)
+ local furniture = {Name = PropName, Index = PropIndex, Catagory = PropCatagory, Pos = pos, Heading = heading, Price = Price}
+ table.insert(Properties[PropertyId].furniture, furniture)
+ for i = 1, #Properties[PropertyId].plysinside do
+ TriggerClientEvent("esx_property:placeFurniture", Properties[PropertyId].plysinside[i], PropertyId, furniture,
+ #Properties[PropertyId].furniture)
+ end
+ TriggerClientEvent("esx_property:syncFurniture", -1, PropertyId, Properties[PropertyId].furniture)
+ else
+ cb(false)
+ ESX.ShowNotification("You ~r~Cannot~s~ Afford This Furniture.")
+ end
+ else
+ cb(false)
+ end
+ Log("Furniture Bought", 3640511,
+ {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
+ {name = "**Player**", value = xPlayer.getName(), inline = true}, {name = "**Has Access**",
+ value = (xPlayer.identifier == Owner or IsPlayerAdmin(source) or
+ (Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier])) and "Yes" or "No", inline = true},
+ {name = "**Prop Name**", value = Config.FurnitureCatagories[PropCatagory][PropIndex].title, inline = true},
+ {name = "**Price**", value = tostring(Config.FurnitureCatagories[PropCatagory][PropIndex].price), inline = true},
+ {name = "**Can Afford**",
+ value = xPlayer.getAccount("bank").money >= Config.FurnitureCatagories[PropCatagory][PropIndex].price and "Yes" or "No", inline = true}}, 1)
+end)
+
+-- Selling Property
+
+ESX.RegisterServerCallback("esx_property:sellProperty", function(source, cb, PropertyId)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local Owner = Properties[PropertyId].Owner
+ if xPlayer.identifier == Owner then
+ local Price = ESX.Math.Round(Properties[PropertyId].Price * 0.6)
+ xPlayer.addAccountMoney("bank", Price)
+ Properties[PropertyId].Owner = ""
+ Properties[PropertyId].OwnerName = ""
+ Properties[PropertyId].Owned = false
+ Properties[PropertyId].Locked = false
+ Properties[PropertyId].Keys = {}
+ local furn = #(Properties[PropertyId].furniture)
+ if Config.WipeFurnitureOnSell then
+ Properties[PropertyId].furniture = {}
+ end
+ if Config.WipeCustomNameOnSell then
+ Properties[PropertyId].setName = ""
+ end
+ Properties[PropertyId].plysinside = {}
+ Log("Property Sold", 16711680, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
+ {name = "**Price**", value = ESX.Math.GroupDigits(Price), inline = true},
+ {name = "**Owner**", value = xPlayer.getName(), inline = true},
+ {name = "**Furniture Count**", value = tostring(furn), inline = true},
+ {name = "**Vehicle Count**", value = tostring(#Properties[PropertyId].garage.StoredVehicles), inline = true}}, 1)
+ if Properties[PropertyId].garage.StoredVehicles then
+ Properties[PropertyId].garage.StoredVehicles = {}
+ end
+ TriggerClientEvent("esx_property:syncProperties", -1, Properties)
+ if Config.OxInventory then
+ exports.ox_inventory:ClearInventory("property-" .. PropertyId)
+ end
+ end
+ cb(xPlayer.identifier == Owner)
+end)
+
+-- Admin Menu Options
+
+ESX.RegisterServerCallback("esx_property:toggleLock", function(source, cb, PropertyId)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local Owner = Properties[PropertyId].Owner
+ if xPlayer.identifier == Owner or IsPlayerAdmin(source, "ToggleLock") or
+ (Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier]) then
+ Properties[PropertyId].Locked = not Properties[PropertyId].Locked
+ TriggerClientEvent("esx_property:syncProperties", -1, Properties)
+ end
+ Log("Lock Toggled", 3640511, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
+ {name = "**Owner**", value = Properties[PropertyId].OwnerName, inline = true},
+ {name = "**Executing User**", value = xPlayer.getName(), inline = true},
+ {name = "**Status**", value = Properties[PropertyId].Locked and "Locked" or "Unlocked", inline = true}}, 3)
+ cb(xPlayer.identifier == Owner or IsPlayerAdmin(source, "ToggleLock") or
+ (Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier]))
+end)
+
+ESX.RegisterServerCallback("esx_property:toggleGarage", function(source, cb, PropertyId)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ if IsPlayerAdmin(source, "ToggleGarage") then
+ Properties[PropertyId].garage.enabled = not Properties[PropertyId].garage.enabled
+ TriggerClientEvent("esx_property:syncProperties", -1, Properties)
+ Log("Property Garage Toggled", 3640511, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
+ {name = "**Owner**", value = Properties[PropertyId].OwnerName, inline = true},
+ {name = "**Admin**", value = xPlayer.getName(), inline = true},
+ {name = "**Status**", value = Properties[PropertyId].garage.enabled and "Enabled" or "Disabled",
+ inline = true}}, 2)
+ cb(true, Properties[PropertyId].garage.enabled)
+ else
+ cb(false)
+ end
+end)
+
+ESX.RegisterServerCallback("esx_property:toggleCCTV", function(source, cb, PropertyId)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ if IsPlayerAdmin(source, "ToggleCCTV") then
+ Properties[PropertyId].cctv.enabled = not Properties[PropertyId].cctv.enabled
+ TriggerClientEvent("esx_property:syncProperties", -1, Properties)
+ cb(true, Properties[PropertyId].cctv.enabled)
+ Log("Property CCTV Toggled", 3640511, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
+ {name = "**Owner**", value = Properties[PropertyId].OwnerName, inline = true},
+ {name = "**Admin**", value = xPlayer.getName(), inline = true},
+ {name = "**Status**", value = Properties[PropertyId].cctv.enabled and "Enabled" or "Disabled",
+ inline = true}}, 2)
+ else
+ cb(false)
+ end
+end)
+
+ESX.RegisterServerCallback("esx_property:SetGaragePos", function(source, cb, PropertyId, heading)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ if IsPlayerAdmin(source, "ToggleGarage") then
+ local PlayerPed = GetPlayerPed(source)
+ local PlayerPos = GetEntityCoords(PlayerPed)
+ local Property = Properties[PropertyId]
+ local Original = vector3(Properties[PropertyId].garage.pos.x, Properties[PropertyId].garage.pos.y, Properties[PropertyId].garage.pos.z)
+ Properties[PropertyId].garage.pos = PlayerPos
+ Properties[PropertyId].garage.Heading = heading
+ TriggerClientEvent("esx_property:syncProperties", -1, Properties)
+ Log("Property Garage Location Changed", 3640511, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
+ {name = "**Owner**", value = Properties[PropertyId].OwnerName, inline = true},
+ {name = "**Admin**", value = xPlayer.getName(), inline = true},
+ {name = "**Original Position**", value = tostring(Original), inline = true},
+ {name = "**New Position**", value = tostring(PlayerPos), inline = true}}, 2)
+ cb(true)
+ else
+ cb(false)
+ end
+end)
+
+ESX.RegisterServerCallback("esx_property:SetCCTVangle", function(source, cb, PropertyId, angles)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ if IsPlayerAdmin(source, "ToggleCCTV") then
+ local Property = Properties[PropertyId]
+ Properties[PropertyId].cctv.rot = angles.rot
+ Properties[PropertyId].cctv.maxleft = angles.maxleft
+ Properties[PropertyId].cctv.maxright = angles.maxright
+ TriggerClientEvent("esx_property:syncProperties", -1, Properties)
+ cb(true)
+ Log("Property CCTV Angle Changed", 3640511, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
+ {name = "**Owner**", value = Properties[PropertyId].OwnerName, inline = true},
+ {name = "**Admin**", value = xPlayer.getName(), inline = true}}, 2)
+ else
+ cb(false)
+ end
+end)
+
+ESX.RegisterServerCallback("esx_property:CCTV", function(source, cb, PropertyId)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local Owner = Properties[PropertyId].Owner
+ local Property = Properties[PropertyId]
+ if xPlayer.identifier == Owner or IsPlayerAdmin(source) or (Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier]) then
+ SetEntityCoords(GetPlayerPed(source), vector3(Property.Entrance.x, Property.Entrance.y, Property.Entrance.z + 5.0))
+ SetPlayerRoutingBucket(source, 0)
+ end
+ Log("Player Entered CCTV", 3640511, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
+ {name = "**Player**", value = xPlayer.getName(), inline = true}}, 3)
+ cb(xPlayer.identifier == Owner or IsPlayerAdmin(source) or (Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier]))
+end)
+
+ESX.RegisterServerCallback("esx_property:ExitCCTV", function(source, cb, PropertyId)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local Owner = Properties[PropertyId].Owner
+ local Property = Properties[PropertyId]
+ if xPlayer.identifier == Owner or IsPlayerAdmin(source) or (Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier]) then
+ local Interior = GetInteriorValues(Property.Interior)
+ if Interior.type == "shell" then
+ SetEntityCoords(GetPlayerPed(source), vector3(Property.Entrance.x, Property.Entrance.y, 2001))
+ else
+ SetEntityCoords(GetPlayerPed(source), Interior.pos)
+ end
+ SetPlayerRoutingBucket(source, PropertyId + 1)
+ end
+ Log("Player Exited CCTV", 3640511, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
+ {name = "**Player**", value = xPlayer.getName(), inline = true}}, 3)
+ cb(xPlayer.identifier == Owner or IsPlayerAdmin(source) or (Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier]))
+end)
+
+ESX.RegisterServerCallback("esx_property:SetPropertyName", function(source, cb, PropertyId, name)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local Owner = Properties[PropertyId].Owner
+ local Property = Properties[PropertyId]
+ if xPlayer.identifier == Owner or IsPlayerAdmin(source) then
+ if name and #name <= Config.MaxNameLength then
+ Properties[PropertyId].setName = name
+ TriggerClientEvent("esx_property:syncProperties", -1, Properties)
+ Log("Property Name Changed", 3640511,
+ {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
+ {name = "**Player**", value = xPlayer.getName(), inline = true}, {name = "**New Name**", value = name, inline = true}}, 2)
+ end
+ end
+ cb((xPlayer.identifier == Owner or IsPlayerAdmin(source, "SetPropertyName")) and name and #name <= Config.MaxNameLength)
+end)
+
+ESX.RegisterServerCallback("esx_property:KnockOnDoor", function(source, cb, PropertyId, name)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local Property = Properties[PropertyId]
+ local Owner = ESX.GetPlayerFromIdentifier(Property.Owner)
+ if Owner then
+ for i = 1, #(Property.plysinside) do
+ if Property.plysinside[i] == Owner.source then
+ Owner.showNotification("Someone Is ~b~Knocking~s~ On The Door.", "info")
+ cb(true)
+ Log("Player Knocked On Door", 3640511, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
+ {name = "**Owner**", value = Properties[PropertyId].OwnerName, inline = true},
+ {name = "**Player**", value = xPlayer.getName(), inline = true}}, 3)
+ end
+ end
+ cb(false)
+ else
+ cb(false)
+ end
+end)
+
+ESX.RegisterServerCallback("esx_property:RemoveCustomName", function(source, cb, PropertyId, name)
+ local Property = Properties[PropertyId]
+ local xPlayer = ESX.GetPlayerFromId(source)
+ if IsPlayerAdmin(source, "RemovePropertyName") then
+ local n = Properties[PropertyId].setName
+ Properties[PropertyId].setName = ""
+ TriggerClientEvent("esx_property:syncProperties", -1, Properties)
+ Log("Property Name Reset", 3640511, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
+ {name = "**Owner**", value = Properties[PropertyId].OwnerName, inline = true},
+ {name = "**Admin**", value = xPlayer.getName(), inline = true},
+ {name = "**Removed Name**", value = n, inline = true}}, 3)
+ cb(true)
+ else
+ cb(false)
+ end
+end)
+
+ESX.RegisterServerCallback("esx_property:deleteProperty", function(source, cb, PropertyId)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ if IsPlayerAdmin(source, "DeleteProperty") then
+ Log("Property Deleted", 16711680,
+ {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
+ {name = "**Admin**", value = xPlayer.getName(), inline = true},
+ {name = "**Owner**", value = Properties[PropertyId].OwnerName ~= "" and Properties[PropertyId].OwnerName or "N/A", inline = true},
+ {name = "**Furniture Count**", value = #(Properties[PropertyId].furniture), inline = true},
+ {name = "**Vehicle Count**", value = #(Properties[PropertyId].garage.StoredVehicles), inline = true}}, 1)
+ table.remove(Properties, PropertyId)
+ TriggerClientEvent("esx_property:syncProperties", -1, Properties)
+ if Config.OxInventory then
+ exports.ox_inventory:ClearInventory("property-" .. PropertyId)
+ end
+ end
+ cb(IsPlayerAdmin(source, "DeleteProperty"))
+end)
+
+ESX.RegisterServerCallback("esx_property:ChangePrice", function(source, cb, PropertyId, NewPrice)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ if IsPlayerAdmin(source, "SetPropertyPrice") then
+ local Original = Properties[PropertyId].Price
+ Properties[PropertyId].Price = NewPrice
+ TriggerClientEvent("esx_property:syncProperties", -1, Properties)
+ Log("Property Price Changed", 3640511,
+ {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
+ {name = "**Admin**", value = xPlayer.getName(), inline = true}, {name = "**Original Price**", value = tostring(Original), inline = true},
+ {name = "**New Price**", value = tostring(NewPrice), inline = true}}, 2)
+ end
+ cb(IsPlayerAdmin(source, "SetPropertyPrice"))
+end)
+
+ESX.RegisterServerCallback("esx_property:ChangeInterior", function(source, cb, PropertyId, Interior)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ if IsPlayerAdmin(source, "ChangeInterior") then
+ local Original = GetInteriorValues(Properties[PropertyId].Interior).label
+ Properties[PropertyId].Interior = Interior
+ Properties[PropertyId].furniture = {}
+ Properties[PropertyId].positions = GetInteriorValues(Interior).positions
+ if not Config.OxInventory then
+ Properties[PropertyId].positions.Storage = nil
+ end
+ TriggerClientEvent("esx_property:syncProperties", -1, Properties)
+ Log("Property Interior Changed", 3640511,
+ {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
+ {name = "**Admin**", value = xPlayer.getName(), inline = true}, {name = "**Original**", value = tostring(Original), inline = true},
+ {name = "**New Interior**", value = GetInteriorValues(Properties[PropertyId].Interior).label, inline = true}}, 2)
+ end
+ cb(IsPlayerAdmin(source, "ChangeInterior"))
+end)
+
+ESX.RegisterServerCallback("esx_property:RemoveAllfurniture", function(source, cb, PropertyId)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local Owner = Properties[PropertyId].Owner
+ if xPlayer.identifier == Owner or IsPlayerAdmin(source, "ResetFurniture") or
+ (Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier]) then
+ for i = 1, #Properties[PropertyId].plysinside do
+ for furniture = 1, #Properties[PropertyId].furniture do
+ TriggerClientEvent("esx_property:removeFurniture", Properties[PropertyId].plysinside[i], PropertyId, furniture)
+ end
+ end
+ Properties[PropertyId].furniture = {}
+ TriggerClientEvent("esx_property:syncFurniture", -1, PropertyId, Properties[PropertyId].furniture)
+ end
+ Log("Property Furniture Reset", 16711680, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
+ {name = "**Admin**", value = xPlayer.getName(), inline = true}}, 1)
+ cb(xPlayer.identifier == Owner or IsPlayerAdmin(source, "ResetFurniture") or
+ (Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier]))
+end)
+
+ESX.RegisterServerCallback("esx_property:deleteFurniture", function(source, cb, PropertyId, furnitureIndex)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local Owner = Properties[PropertyId].Owner
+ if xPlayer.identifier == Owner or IsPlayerAdmin(source) or (Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier]) then
+ if Properties[PropertyId].furniture[furnitureIndex] then
+ Properties[PropertyId].furniture[furnitureIndex] = nil
+ TriggerClientEvent("esx_property:syncFurniture", -1, PropertyId, Properties[PropertyId].furniture)
+ end
+ for i = 1, #Properties[PropertyId].plysinside do
+ TriggerClientEvent("esx_property:removeFurniture", Properties[PropertyId].plysinside[i], PropertyId, furnitureIndex)
+ end
+ end
+ Log("Property Furniture Deleted", 16711680,
+ {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true}, {name = "**Admin**", value = xPlayer.getName(), inline = true},
+ {name = "**Furniture Name**", value = xPlayer.getName(), inline = true}}, 3)
+ cb(xPlayer.identifier == Owner or IsPlayerAdmin(source) or (Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier]))
+end)
+
+ESX.RegisterServerCallback("esx_property:editFurniture", function(source, cb, PropertyId, furnitureIndex, Pos, Heading)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local Owner = Properties[PropertyId].Owner
+ if xPlayer.identifier == Owner or IsPlayerAdmin(source) or (Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier]) then
+ if Properties[PropertyId].furniture[furnitureIndex] then
+ Properties[PropertyId].furniture[furnitureIndex].Pos = Pos
+ Properties[PropertyId].furniture[furnitureIndex].Heading = Heading
+ TriggerClientEvent("esx_property:syncFurniture", -1, PropertyId, Properties[PropertyId].furniture)
+ for i = 1, #Properties[PropertyId].plysinside do
+ TriggerClientEvent("esx_property:editFurniture", Properties[PropertyId].plysinside[i], PropertyId, furnitureIndex, Pos, Heading)
+ end
+ end
+ end
+ cb(xPlayer.identifier == Owner or IsPlayerAdmin(source) or (Properties[PropertyId].Keys and Properties[PropertyId].Keys[xPlayer.identifier]))
+end)
+
+ESX.RegisterServerCallback("esx_property:evictOwner", function(source, cb, PropertyId, Interior)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ if IsPlayerAdmin(source, "EvictOwner") then
+ local xOwner = ESX.GetPlayerFromIdentifier(Properties[PropertyId].Owner)
+ if xOwner then
+ xOwner.showNotification("Your Have Been ~r~Evicted~s~.", "error")
+ end
+ local pName = Properties[PropertyId].OwnerName
+ Properties[PropertyId].Owner = ""
+ Properties[PropertyId].Owned = false
+ Properties[PropertyId].OwnerName = ""
+ Properties[PropertyId].Locked = false
+ Properties[PropertyId].Keys = {}
+ if Config.WipeFurnitureOnSell then
+ Properties[PropertyId].furniture = {}
+ end
+ Properties[PropertyId].setName = ""
+ Properties[PropertyId].plysinside = {}
+ if Properties[PropertyId].garage.StoredVehicles then
+ Properties[PropertyId].garage.StoredVehicles = {}
+ end
+ TriggerClientEvent("esx_property:syncProperties", -1, Properties)
+ if Config.OxInventory then
+ exports.ox_inventory:ClearInventory("property-" .. PropertyId)
+ end
+ Log("Property Owner Evicted", 3640511,
+ {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true}, {name = "**Owner**", value = pName, inline = true},
+ {name = "**Admin**", value = xPlayer.getName(), inline = true},
+ {name = "**Has Access**", value = IsPlayerAdmin(source, "EvictOwner") and "Yes" or "No", inline = true}}, 1)
+ end
+ cb(IsPlayerAdmin(source, "EvictOwner"))
+end)
+
+ESX.RegisterServerCallback("esx_property:CanRaid", function(source, cb, PropertyId, Interior)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local Can = false
+ if Config.Raiding.Enabled then
+ if (Config.CanAdminsRaid and IsPlayerAdmin(source)) or xPlayer.job.name == "police" then
+ if Config.Raiding.ItemRequired then
+ local itemCount = xPlayer.getInventoryItem(Config.Raiding.ItemRequired.name).count
+ if itemCount >= Config.Raiding.ItemRequired.ItemCount then
+ if Config.Raiding.ItemRequired.RemoveItem then
+ xPlayer.removeInventoryItem(Config.Raiding.ItemRequired.name, Config.Raiding.ItemRequired.ItemCount)
+ end
+ Can = true
+ else
+ xPlayer.showNotification("You need ~b~" .. Config.Raiding.ItemRequired.ItemCount .. "x " .. Config.Raiding.ItemRequired.label ..
+ "~s~ to Raid.", "error")
+ end
+ else
+ Can = true
+ end
+ end
+ end
+ cb(Can)
+ if Can then
+ local xOwner = ESX.GetPlayerFromIdentifier(Properties[PropertyId].Owner)
+ if xOwner then
+ xOwner.showNotification("Your Property is Currently Being ~b~Raided!", "error")
+ end
+ Wait(15000)
+ Properties[PropertyId].Locked = false
+ TriggerClientEvent("esx_property:syncProperties", -1, Properties)
+ end
+end)
+
+ESX.RegisterServerCallback("esx_property:ChangeEntrance", function(source, cb, PropertyId, Coords)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ if IsPlayerAdmin(source, "ChangeEntrance") then
+ local Origonal = Properties[PropertyId].Entrance.x .. "," .. Properties[PropertyId].Entrance.y .. "," .. Properties[PropertyId].Entrance.z
+ Properties[PropertyId].Entrance = {x = ESX.Math.Round(Coords.x, 2), y = ESX.Math.Round(Coords.y, 2), z = ESX.Math.Round(Coords.z, 2) - 0.8}
+ TriggerClientEvent("esx_property:syncProperties", -1, Properties)
+ Log("Property Entrance Changed", 3640511, {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
+ {name = "**Owner**", value = Properties[PropertyId].OwnerName, inline = true},
+ {name = "**Admin**", value = xPlayer.getName(), inline = true},
+ {name = "**Has Access**", value = IsPlayerAdmin(source, "ChangeEntrance") and "Yes" or "No",
+ inline = true}, {name = "**Original**", value = Origonal, inline = true},
+ {name = "**New**",
+ value = Properties[PropertyId].Entrance.x .. "," .. Properties[PropertyId].Entrance.y .. "," ..
+ Properties[PropertyId].Entrance.z, inline = true}}, 1)
+ end
+ cb(IsPlayerAdmin(source, "ChangeEntrance"))
+end)
+
+ESX.RegisterServerCallback("esx_property:SetInventoryPosition", function(source, cb, PropertyId, Coords, Reset)
+ if Config.OxInventory then
+ local Property = Properties[PropertyId]
+ local xPlayer = ESX.GetPlayerFromId(source)
+ if IsPlayerAdmin(source, "EditInteriorPositions") or (Property.Owner == xPlayer.identifier or Properties[PropertyId].Keys[xPlayer.identifier]) then
+ local Interior = GetInteriorValues(Property.Interior)
+ if Reset then
+ Properties[PropertyId].positions.Storage = {x = ESX.Math.Round(Coords.x, 2), y = ESX.Math.Round(Coords.y, 2), z = ESX.Math.Round(Coords.z, 2)}
+ else
+ if Interior.type == "ipl" then
+ Properties[PropertyId].positions.Storage = {x = ESX.Math.Round(Coords.x, 2), y = ESX.Math.Round(Coords.y, 2),
+ z = ESX.Math.Round(Coords.z, 2)}
+ else
+ Properties[PropertyId].positions.Storage = vector3(Property.Entrance.x, Property.Entrance.y, 2000) - Coords
+ end
+ end
+ Log("Property Storage Location Set", 3640511,
+ {{name = "**Property Name**", value = Property.Name, inline = true}, {name = "**Owner**", value = Property.OwnerName, inline = true},
+ {name = "**Player**", value = xPlayer.getName(), inline = true}, {name = "**Has Access**",
+ value = (IsPlayerAdmin(source, "EditInteriorPositions") or
+ (Property.Owner == xPlayer.identifier or Properties[PropertyId].Keys[xPlayer.identifier])) and "Yes" or "No", inline = true},
+ {name = "**Reset?**", value = Reset and "Yes" or "No", inline = true}}, 1)
+ TriggerClientEvent("esx_property:syncProperties", -1, Properties)
+ end
+ cb(IsPlayerAdmin(source, "EditInteriorPositions") or (Property.Owner == xPlayer.identifier or Properties[PropertyId].Keys[xPlayer.identifier]))
+ else
+ cb(false)
+ end
+end)
+-- Wardrobe
+ESX.RegisterServerCallback("esx_property:SetWardrobePosition", function(source, cb, PropertyId, Coords, Reset)
+ local Property = Properties[PropertyId]
+ local xPlayer = ESX.GetPlayerFromId(source)
+ if IsPlayerAdmin(source, "EditInteriorPositions") or (Property.Owner == xPlayer.identifier or Properties[PropertyId].Keys[xPlayer.identifier]) then
+ local Interior = GetInteriorValues(Property.Interior)
+ if Reset then
+ Properties[PropertyId].positions.Wardrobe = Interior.positions.Wardrobe
+ else
+ if Interior.type == "ipl" then
+ Properties[PropertyId].positions.Wardrobe =
+ {x = ESX.Math.Round(Coords.x, 2), y = ESX.Math.Round(Coords.y, 2), z = ESX.Math.Round(Coords.z, 2)}
+ else
+ Properties[PropertyId].positions.Wardrobe = vector3(Property.Entrance.x, Property.Entrance.y, 1999.8) - Coords
+ end
+ Log("Property Wardrobe Location Set", 3640511,
+ {{name = "**Property Name**", value = Property.Name, inline = true}, {name = "**Owner**", value = Property.OwnerName, inline = true},
+ {name = "**Player**", value = xPlayer.getName(), inline = true}, {name = "**Has Access**",
+ value = (IsPlayerAdmin(source, "EditInteriorPositions") or
+ (Property.Owner == xPlayer.identifier or Properties[PropertyId].Keys[xPlayer.identifier])) and "Yes" or "No", inline = true},
+ {name = "**Reset?**", value = Reset and "Yes" or "No", inline = true}}, 1)
+ end
+ TriggerClientEvent("esx_property:syncProperties", -1, Properties)
+ end
+ cb(IsPlayerAdmin(source, "EditInteriorPositions") or (Property.Owner == xPlayer.identifier or Properties[PropertyId].Keys[xPlayer.identifier]))
+end)
+
+ESX.RegisterServerCallback('esx_property:getPlayerDressing', function(source, cb)
+ local xPlayer = ESX.GetPlayerFromId(source)
+
+ TriggerEvent('esx_datastore:getDataStore', 'property', xPlayer.identifier, function(store)
+ local count = store.count('dressing')
+ local labels = {}
+
+ for i = 1, count, 1 do
+ local entry = store.get('dressing', i)
+ table.insert(labels, entry.label)
+ end
+
+ cb(labels)
+ end)
+end)
+
+ESX.RegisterServerCallback('esx_property:GetInsidePlayers', function(source, cb, property)
+ local Property = Properties[property]
+ local Players = {}
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local NearbyPlayers = Property.plysinside
+
+ for k, v in pairs(NearbyPlayers) do
+ local xPlayer = ESX.GetPlayerFromId(v)
+ if not Properties[property].Keys then
+ Properties[property].Keys = {}
+ end
+ if xPlayer.identifier ~= Property.Owner and not Properties[property].Keys[xPlayer.identifier] then
+ Players[#Players + 1] = {Name = xPlayer.getName(), Id = xPlayer.source}
+ end
+ end
+ cb(Players)
+end)
+
+ESX.RegisterServerCallback('esx_property:GetNearbyPlayers', function(source, cb, property)
+ local Property = Properties[property]
+ local Players = {}
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local NearbyPlayers = ESX.OneSync.GetPlayersInArea(vector3(Property.Entrance.x, Property.Entrance.y, Property.Entrance.z), 5.0)
+ Wait(100)
+ for k, v in pairs(NearbyPlayers) do
+ local xTarget = ESX.GetPlayerFromId(v.id)
+ ---if xPlayer.identifier ~= xTarget.identifier then
+ Players[#Players + 1] = {name = xTarget.getName(), source = xTarget.source}
+ -- end
+ end
+ cb(Players)
+end)
+
+ESX.RegisterServerCallback('esx_property:GetPlayersWithKeys', function(source, cb, property)
+ local Property = Properties[property]
+ local Players = {}
+ local xPlayer = ESX.GetPlayerFromId(source)
+ if xPlayer.identifier == Property.Owner then
+ cb(Property.Keys or {})
+ end
+end)
+
+ESX.RegisterServerCallback('esx_property:ShouldHaveKey', function(source, cb, property)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ cb(Properties[property].Keys[xPlayer.identifier])
+end)
+
+ESX.RegisterServerCallback('esx_property:GetWebhook', function(source, cb, property)
+ cb(Config.CCTV.PictureWebook)
+end)
+
+ESX.RegisterServerCallback('esx_property:RemoveLastProperty', function(source, cb, property)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ MySQL.query("UPDATE `users` SET `last_property` = NULL WHERE `identifier` = ?", {xPlayer.identifier}) -- Remove Saved Data
+ SetPlayerRoutingBucket(source, 0) -- Reset Routing Bucket
+ xPlayer.set("lastProperty", nil)
+ cb()
+end)
+
+ESX.RegisterServerCallback('esx_property:GiveKey', function(source, cb, property, player)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local xTarget = ESX.GetPlayerFromId(player)
+ local Property = Properties[property]
+
+ if Property.Owner == xPlayer.identifier then
+ if not Property.Keys then
+ Properties[property].Keys = {}
+ end
+
+ local id = xTarget.identifier
+ if not Properties[property].Keys[id] then
+ Property.Keys[id] = {name = xTarget.getName(), identifier = id}
+ xTarget.showNotification('You Have Been Granted Keys To ~b~' .. Property.Name .. '~s~.', 'success')
+ xTarget.triggerEvent("esx_property:giveKeyAccess")
+ cb(true)
+ else
+ xPlayer.showNotification('This Player Already Has Keys To ~b~' .. Property.Name .. '~s~.', 'error')
+ cb(false)
+ end
+ else
+ xPlayer.showNotification('You do ~r~not~s~ own this property.', 'error')
+ cb(false)
+ end
+ Log("Property Key Given", 3640511,
+ {{name = "**Property Name**", value = Property.Name, inline = true}, {name = "**Owner**", value = Property.OwnerName, inline = true},
+ {name = "**Player**", value = xPlayer.getName(), inline = true},
+ {name = "**Has Access**", value = Property.Owner == xPlayer.identifier and "Yes" or "No", inline = true}}, 1)
+end)
+
+ESX.RegisterServerCallback('esx_property:StoreVehicle', function(source, cb, PropertyId, VehicleProperties)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local Property = Properties[PropertyId]
+
+ if Property.Owner == xPlayer.identifier or Properties[PropertyId].Keys[xPlayer.identifier] then
+ if Property.garage.enabled then
+ if Config.Garage.OwnedVehiclesOnly then
+ MySQL.scalar("SELECT `owner` FROM `owned_vehicles` WHERE `plate` = ?", {VehicleProperties.plate}, function(result)
+ if result then
+ if result == xPlayer.identifier then
+ Properties[PropertyId].garage.StoredVehicles[#Properties[PropertyId].garage.StoredVehicles + 1] = {owner = xPlayer.identifier,
+ vehicle = VehicleProperties}
+ cb(true)
+ elseif (Properties[PropertyId].Keys[result] or Property.Owner == result) then
+ Properties[PropertyId].garage.StoredVehicles[#Properties[PropertyId].garage.StoredVehicles + 1] = {owner = xPlayer.identifier,
+ vehicle = VehicleProperties}
+ cb(true)
+ else
+ cb(false)
+ end
+ else
+ cb(false)
+ end
+ end)
+ else
+ Properties[PropertyId].garage.StoredVehicles[#Properties[PropertyId].garage.StoredVehicles + 1] = {owner = xPlayer.identifier,
+ vehicle = VehicleProperties}
+ cb(true)
+ end
+ else
+ xPlayer.showNotification('Garage Not Enabled On This Property.', 'error')
+ cb(false)
+ end
+ else
+ xPlayer.showNotification('You ~r~Cannot~s~ Access this property.', 'error')
+ cb(false)
+ end
+ Log("User Attempted To Store Vehicle", 3640511,
+ {{name = "**Property Name**", value = Property.Name, inline = true}, {name = "**Owner**", value = Property.OwnerName, inline = true},
+ {name = "**Player**", value = xPlayer.getName(), inline = true},
+ {name = "**Has Access**", value = (Property.Owner == xPlayer.identifier or Properties[PropertyId].Keys[xPlayer.identifier]) and "Yes" or "No",
+ inline = true}, {name = "**Garage Status**", value = Property.garage.enabled and "Enabled" or "Disabled", inline = true},
+ {name = "**Vehicle Name**", value = VehicleProperties.DisplayName, inline = true}}, 2)
+end)
+
+ESX.RegisterServerCallback('esx_property:AccessGarage', function(source, cb, PropertyId, VehicleProperties)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local Property = Properties[PropertyId]
+
+ if Property.Owner == xPlayer.identifier or Properties[PropertyId].Keys[xPlayer.identifier] then
+ if Property.garage.enabled then
+ cb(Property.garage.StoredVehicles)
+ else
+ xPlayer.showNotification('Garage Not Enabled On This Property.', 'error')
+ cb(false)
+ end
+ else
+ xPlayer.showNotification('You ~r~Cannot~s~ Access this property.', 'error')
+ cb(false)
+ end
+
+ Log("User Opened Garage Menu", 3640511,
+ {{name = "**Property Name**", value = Property.Name, inline = true}, {name = "**Owner**", value = Property.OwnerName, inline = true},
+ {name = "**Player**", value = xPlayer.getName(), inline = true},
+ {name = "**Garage Status**", value = Property.garage.enabled and "Enabled" or "Disabled", inline = true}}, 2)
+end)
+
+ESX.RegisterServerCallback('esx_property:RemoveKey', function(source, cb, property, player)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local xTarget = ESX.GetPlayerFromIdentifier(player)
+ local Property = Properties[property]
+
+ if Property.Owner == xPlayer.identifier then
+ if Property.Keys then
+ if Properties[property].Keys[player] then
+ Log("Property Key Revoked", 3640511,
+ {{name = "**Property Name**", value = Property.Name, inline = true}, {name = "**Owner**", value = xPlayer.getName(), inline = true},
+ {name = "**Removed From**", value = tostring(Properties[property].Keys[player].name), inline = true}}, 3)
+ Properties[property].Keys[player] = nil
+ xTarget.showNotification('Your Key Access To ~b~' .. Property.Name .. '~s~. Has Been ~r~Revoked~s~', 'error')
+ xTarget.triggerEvent("esx_property:RemoveKeyAccess", property)
+ cb(true)
+ else
+ xPlayer.showNotification('This Player Does Not Have Keys To ~b~' .. Property.Name .. '~s~.', 'error')
+ cb(false)
+ end
+ else
+ cb(false)
+ end
+ else
+ xPlayer.showNotification('You do ~r~not~s~ own this property.', 'error')
+ cb(false)
+ end
+end)
+
+ESX.RegisterServerCallback('esx_property:CanOpenFurniture', function(source, cb, property)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local Property = Properties[property]
+ cb(Property.Owner == xPlayer.identifier or (Property.Keys and Properties[property].Keys[xPlayer.identifier]))
+end)
+
+ESX.RegisterServerCallback('esx_property:getPlayerOutfit', function(source, cb, num)
+ local xPlayer = ESX.GetPlayerFromId(source)
+
+ TriggerEvent('esx_datastore:getDataStore', 'property', xPlayer.identifier, function(store)
+ local outfit = store.get('dressing', num)
+ cb(outfit.skin)
+ end)
+end)
+
+-- Player Management
+if PM.Enabled then
+ ESX.RegisterServerCallback('esx_property:PMenterOffice', function(source, cb)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local PlayerPed = GetPlayerPed(source)
+
+ if xPlayer.job.name == PM.job then
+ SetEntityCoords(PlayerPed, PM.Locations.Exit)
+ SetPlayerRoutingBucket(source, 1)
+ cb(true)
+ else
+ cb(false)
+ end
+ end)
+
+ ESX.RegisterServerCallback('esx_property:PMexitOffice', function(source, cb)
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local PlayerPed = GetPlayerPed(source)
+
+ if xPlayer.job.name == PM.job then
+ SetEntityCoords(PlayerPed, PM.Locations.Entrance)
+ SetPlayerRoutingBucket(source, 0)
+ cb(true)
+ else
+ cb(false)
+ end
+ end)
+end
+
+-- Enter/leave Events
+
+RegisterNetEvent('esx_property:enter', function(PropertyId)
+ local player = source
+ local PlayerPed = GetPlayerPed(player)
+ local xPlayer = ESX.GetPlayerFromId(player)
+ local Property = Properties[PropertyId]
+ local Interior = GetInteriorValues(Property.Interior)
+ if not Properties[PropertyId].plysinside then
+ Properties[PropertyId].plysinside = {}
+ end
+ table.insert(Properties[PropertyId].plysinside, player)
+
+ local PropertyData = {id = PropertyId, coords = Property.Entrance} -- Save the property data to the table
+ MySQL.query("UPDATE `users` SET `last_property` = ? WHERE `identifier` = ?", {json.encode(PropertyData), xPlayer.identifier}) -- Save the property data to the database
+ xPlayer.set("lastProperty", PropertyData)
+ if Interior.type == "shell" then
+ SetEntityCoords(PlayerPed, vector3(Property.Entrance.x, Property.Entrance.y, 2001))
+ else
+ SetEntityCoords(PlayerPed, Interior.pos)
+ SetEntityHeading(PlayerPed, 0.0)
+ end
+ SetPlayerRoutingBucket(player, PropertyId + 1)
+ Log("Player Entered Property", 3640511,
+ {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
+ {name = "**Player**", value = xPlayer.getName(), inline = true},
+ {name = "**Property Player Count**", value = tostring(#(Properties[PropertyId].plysinside)), inline = true}}, 3)
+end)
+
+RegisterNetEvent('esx_property:leave', function(PropertyId)
+ local player = source
+ local Property = Properties[PropertyId]
+ local xPlayer = ESX.GetPlayerFromId(player)
+ MySQL.query("UPDATE `users` SET `last_property` = NULL WHERE `identifier` = ?", {xPlayer.identifier}) -- Remove Saved Data
+ xPlayer.set("lastProperty", nil)
+ SetEntityCoords(player, vector3(Property.Entrance.x, Property.Entrance.y, Property.Entrance.z))
+ SetEntityHeading(player, 0.0)
+ SetPlayerRoutingBucket(player, 0)
+ for i = 1, #(Properties[PropertyId].plysinside) do
+ if Properties[PropertyId].plysinside[i] == player then
+ table.remove(Properties[PropertyId].plysinside, i)
+ break
+ end
+ end
+ Log("Player Left Property", 3640511,
+ {{name = "**Property Name**", value = Properties[PropertyId].Name, inline = true},
+ {name = "**Player**", value = xPlayer.getName(), inline = true},
+ {name = "**Property Player Count**", value = tostring(#(Properties[PropertyId].plysinside)), inline = true}}, 3)
+end)
+
+RegisterNetEvent('esx_property:SetVehicleOut', function(PropertyId, VehIndex)
+ local source = source
+ local Property = Properties[PropertyId]
+ local xPlayer = ESX.GetPlayerFromId(source)
+ table.remove(Properties[PropertyId].garage.StoredVehicles, VehIndex)
+end)
+
+AddEventHandler('playerDropped', function()
+ local source = source
+ for PropertyId = 1, #Properties do
+ for i = 1, #(Properties[PropertyId].plysinside) do
+ if Properties[PropertyId].plysinside[i] == source then
+ table.remove(Properties[PropertyId].plysinside, i)
+ break
+ end
+ end
+ end
+end)
+
+ESX.RegisterServerCallback('esx_property:CanCreateProperty', function(source, cb, num)
+ local Re = false
+ local xPlayer = ESX.GetPlayerFromId(source)
+
+ if xPlayer then
+ if IsPlayerAdmin(source, "CreateProperty") then
+ Re = true
+ end
+ end
+
+ cb(Re)
+end)
+
+ESX.RegisterServerCallback('esx_property:IsAdmin', function(source, cb, num)
+ cb(IsPlayerAdmin(source, "ViewProperties"))
+end)
+
+RegisterNetEvent('esx_property:server:createProperty', function(Property)
+ local source = source
+ local xPlayer = ESX.GetPlayerFromId(source)
+ local Interior = GetInteriorValues(Property.interior)
+ print(ESX.DumpTable(Property))
+ local garageData =
+ Property.garage.enabled and {enabled = true, pos = Property.garage.pos, Heading = Property.garage.heading, StoredVehicles = {}} or
+ {enabled = false}
+ if IsPlayerAdmin(source, "CreateProperty") then
+ local ActualProperty = {Name = Property.name, setName = "", Price = Property.price, furniture = {}, plysinside = {}, Interior = Property.interior,
+ Entrance = Property.entrance, Owner = "", Keys = {}, positions = Interior.positions, cctv = Property.cctv,
+ garage = garageData, Owned = false, Locked = false}
+ Properties[#Properties + 1] = ActualProperty
+ end
+ Log("Property Created", 65280,
+ {{name = "**Admin**", value = xPlayer.getName(), inline = true}, {name = "**Name**", value = Property.name, inline = true},
+ {name = "**Price**", value = ESX.Math.GroupDigits(Property.price), inline = true},
+ {name = "**Interior**", value = Interior.label, inline = true},
+ {name = "**Garage Status**", value = Property.garage.enabled and "Enabled" or "Disabled", inline = true},
+ {name = "**CCTV Status**", value = Property.cctv.enabled and "Enabled" or "Disabled", inline = true},
+ {name = "**Entrance**", value = tostring(Property.entrance.x .. ", " .. Property.entrance.y .. ", " .. Property.entrance.z), inline = true}}, 1)
+ TriggerClientEvent("esx_property:syncProperties", -1, Properties)
+end)
+
+-- Json File Saving
+
+--- Save Properties On Server Scheduled Restart
+AddEventHandler('txAdmin:events:scheduledRestart', function(eventData)
+ if eventData.secondsRemaining == 60 then
+ CreateThread(function()
+ Wait(50000)
+ SaveResourceFile(GetCurrentResourceName(), 'properties.json', json.encode(Properties))
+ Log("Properties Saving", 11141375, {{name = "**Reason**", value = "Scheduled Server Restart", inline = true},
+ {name = "**Property Count**", value = tostring(#Properties), inline = true}}, 1)
+ end)
+ end
+end)
+
+--- Save Properties On Server Stop/Restart
+AddEventHandler('txAdmin:events:serverShuttingDown', function()
+ SaveResourceFile(GetCurrentResourceName(), 'properties.json', json.encode(Properties))
+ Log("Properties Saving", 11141375,
+ {{name = "**Reason**", value = "Server Shutdown", inline = true}, {name = "**Property Count**", value = tostring(#Properties), inline = true}}, 1)
+end)
+
+--- Save Properties On Resource Stop/Restart
+
+AddEventHandler('onResourceStop', function(ResourceName)
+ if ResourceName == GetCurrentResourceName() then
+ SaveResourceFile(GetCurrentResourceName(), 'properties.json', json.encode(Properties))
+ Log("Properties Saving", 11141375, {{name = "**Reason**", value = "Resource Restart", inline = true},
+ {name = "**Property Count**", value = tostring(#Properties), inline = true}}, 1)
+ end
+end)
+