2026-04-14 17:41:39 +02:00

408 lines
18 KiB
Lua

--[[
ESX Property - Properties Made Right!
Copyright (C) 2025 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 <http://www.gnu.org/licenses/>.
]]
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(TranslateCap("rot_left_right"))
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(TranslateCap("rot_up_down"))
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(TranslateCap("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(TranslateCap("rotate"))
PopScaleformMovieFunctionVoid()
PushScaleformMovieFunction(scaleform, "SET_DATA_SLOT")
PushScaleformMovieFunctionParameterInt(9)
N_0xe83a3e3557a56640(GetControlInstructionalButton(1, Config.Furniture.Controls.Confirm, true))
InstructionButtonMessage(TranslateCap("place"))
PopScaleformMovieFunctionVoid()
PushScaleformMovieFunction(scaleform, "SET_DATA_SLOT")
PushScaleformMovieFunctionParameterInt(0)
N_0xe83a3e3557a56640(GetControlInstructionalButton(1, Config.Furniture.Controls.Exit, true))
InstructionButtonMessage(TranslateCap("delete_furni"))
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 = TranslateCap("confirm_buy",Config.FurnitureCatagories[PropCatagory][PropIndex].title),
description = TranslateCap("price",Config.FurnitureCatagories[PropCatagory][PropIndex].price), icon = "fas fa-shopping-cart"},
{title = TranslateCap("yes"), value = "buy", icon = "fas fa-check"}, {title = TranslateCap("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(TranslateCap("bought_furni", Config.FurnitureCatagories[PropCatagory][PropIndex].title), "success")
ESX.CloseContext()
else
ESX.ShowNotification(TranslateCap("cannot_buy"), "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(TranslateCap("edited_furni"), "success")
ESX.CloseContext()
else
ESX.ShowNotification(TranslateCap("cannot_edit"), "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 = TranslateCap("store_title", Catagory), icon = "fas fa-store"},
{title = TranslateCap("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 = _U("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 = TranslateCap("store_title", Store), icon = "fas fa-store"},
{title = TranslateCap("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 = TranslateCap("edit_title",Funiture.title), icon = "fas fa-store"},
{title = TranslateCap("back"), value = "go-back", icon = "fas fa-arrow-left"},
{title = TranslateCap("move_title"), value = "rotate", icon = "fas fa-sync-alt"}, {title = TranslateCap("delete_furni"), 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(TranslateCap("delete_confirm", Funiture.title), "success")
FurnitureEditSelect(propertyId)
else
ESX.ShowNotification(TranslateCap("delete_error", Funiture.title), "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 = TranslateCap("owned_furni"), icon = "far fa-edit"},
{title = TranslateCap("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 = TranslateCap("menu_stores"), icon = "fas fa-store"},
{title = TranslateCap("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 = TranslateCap("furni_management"), icon = "fas fa-lamp"},
{title = TranslateCap("menu_stores"), description = TranslateCap("menu_stores_desc"), icon = "fas fa-dollar-sign", value = "store"}}
if #SpawnedFurniture > 0 then
Options[#Options + 1] = {title = TranslateCap("menu_reset"), description = TranslateCap("menu_reset_desc"), icon = "fas fa-trash", value = "reset"}
Options[#Options + 1] = {title = TranslateCap("menu_edit"), description = TranslateCap("menu_edit_desc"), 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(TranslateCap("furni_reset_success"), "success")
else
ESX.ShowNotification(TranslateCap("furni_reset_error"), "error")
end
end, PropertyId)
end
end
end)
end
ESX.RegisterInput(TranslateCap("furni_command"), TranslateCap("furni_command_desc"), "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(TranslateCap("furni_command_permission"), 5000, "error")
end
end, CurrentId)
else
return
end
end)
end