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

920 lines
32 KiB
Lua

local disablePlayerFiring = DisablePlayerFiring
local isControlJustPressed = IsControlJustPressed
local isDisabledControlJustPressed = IsDisabledControlJustPressed
local isControlJustReleased = IsControlJustReleased
local isDisabledControlJustReleased = IsDisabledControlJustReleased
local getCursorHitCoords = Utils.getCursorHitCoords
local decorateState = {
active = false,
currentObject = nil,
hide = false,
focus = false,
keepInput = false,
objects = {},
currentPage = "dynamic",
mode = "mgizmo",
freeCamera = false,
cameraFocus = false
}
local decorateMeta = {}
decorateMeta.__index = function(_, key) return decorateState[key] end
decorateMeta.__newindex = function(_, key, value)
decorateState[key] = value
if key == "currentObject" then
if value then
if decorateState.currentPage == "stash" then
SendReactMessage("select_stash_item", value.stashId)
end
if DoesEntityExist(value.handle) then
decorate:selectEntity(value.handle)
else
decorate:deselectEntity()
end
else
decorate:deselectEntity()
end
end
end
decorate = setmetatable({}, decorateMeta)
local CAMERA_LERP_SPEED = 0.01
function decorate.instructional(self, extraActions)
if not self.active then return end
if DrawingInstructional then
DrawingInstructional = false
end
local actions = {
{ key = "place_object_on_ground", label = "Place Object on Ground" },
{ key = "toggle_cursor", label = "Toggle Cursor" },
{ key = "toggle_free_mode", label = "Toggle Free Mode" },
{ key = "toggle_editor_mode", label = "Toggle Editor Mode" },
{ key = "toggle_gizmo_mode", label = "Toggle Gizmo Mode" },
{ key = "toggle_free_camera", label = "Toggle Free Camera" }
}
local currentObj = self.currentObject
if not (currentObj and currentObj.stashId) then
actions[#actions + 1] = { key = "done", label = "Buy Object" }
end
if extraActions then
for _, action in pairs(extraActions) do
actions[#actions + 1] = action
end
end
Utils.DrawInstructional(actions)
end
function decorate.open(self)
if self.active then
return Debug("decorate:open ::: decorate is already active")
end
if not currentlyInGarage then
return Notification(i18n.t("decorate.not_in_garage"), "error")
end
local isAvailable = lib.callback.await("garages:decorate:getDecorationAvailable", false, currentlyInGarage)
if not isAvailable then
return Notification(i18n.t("decorate.decoration_not_available"), "error")
end
TriggerServerEvent("garages:decorate:updateDecorationUsedBy", currentlyInGarage, true)
self.active = true
self:toggleFreeCamera(true)
self:setFocus(true)
DisableIdleCamera(true)
SendReactMessage("toggle_decorate_menu", {
visible = true,
navigation = Config.FurnitureNavigation,
furniture = Config.Furniture,
enableShop = Config.EnableF3Shop
})
ToggleHud(false)
TriggerServerEvent("garages:fiveguard:freecam", true)
self:instructional()
self:getObjects(currentlyInGarage)
gizmo:handleCameraUpdate()
mgizmo:loop()
self:handleControls()
self:checkDistance()
end
function decorate.close(self)
if not self.active then return end
self.active = false
ToggleHud(true)
DisableIdleCamera(false)
self:removeCurrentObject()
self.active = false
Utils.RemoveInstructional()
SetNuiFocus(false, false)
SetNuiFocusKeepInput(false)
SendReactMessage("toggle_decorate_menu", { visible = false })
self.focus = false
self.keepInput = false
TriggerServerEvent("garages:fiveguard:freecam", false)
TriggerServerEvent("garages:decorate:updateDecorationUsedBy", currentlyInGarage, false)
end
function decorate.checkDistance(self)
if not Config.MaximumDistanceForDecorate then return end
local startCoords = GetEntityCoords(cache.ped)
CreateThread(function()
while self.active and currentlyInGarage do
local camCoords = GetFinalRenderedCamCoord()
local distance = #(camCoords - startCoords)
if distance > Config.MaximumDistanceForDecorate then
Notification(i18n.t("decorate.too_far"), "error")
self:close()
end
Wait(500)
end
end)
end
function decorate.selectEntity(self, entityHandle)
local activeGizmo = (self.mode == "gizmo") and gizmo or mgizmo
if entityHandle then
if not DoesEntityExist(entityHandle) then
activeGizmo.entity = nil
activeGizmo.decorateData = nil
return
end
end
local hitEntity = entityHandle
if not hitEntity then
local hitCoords, hitEnt = getCursorHitCoords()
if hitCoords and hitEnt and hitEnt ~= 0 then
hitEntity = hitEnt
end
end
if not hitEntity then return end
if self.currentPage ~= "stash" and not entityHandle then
local currentHandle = self.currentObject and self.currentObject.handle
if currentHandle ~= hitEntity then
return Notification(i18n.t("decorate.you_cant_select_entity"), "error")
end
end
local objectData = self:getObjectData(hitEntity)
if not entityHandle and not objectData then return end
activeGizmo.entity = hitEntity
activeGizmo.decorateData = objectData
local currentHandle = self.currentObject and self.currentObject.handle
if currentHandle ~= hitEntity then
self.currentObject = {
handle = hitEntity,
modelName = objectData and objectData.modelName,
stashId = objectData and objectData.id
}
end
activeGizmo:selectEntity(hitEntity)
if self.mode == "gizmo" and self.freeCamera and not self.cameraFocus then
self.cameraFocus = true
end
self:instructional()
end
function decorate.deselectEntity(self)
gizmo:deselectEntity()
self:instructional()
end
function decorate.getCamCoords(self)
return GetFinalRenderedCamCoord()
end
function decorate.getCamRot(self)
return GetFinalRenderedCamRot(2)
end
function decorate.toggleHideDecorate(self)
self.hide = not self.hide
SendReactMessage("toggle_hide_decorate", self.hide)
if self.hide then
self:setFocus(true, true)
else
self:setFocus(true, false)
end
end
function decorate.setFocus(self, focusState, keepInputState)
if focusState == nil then
self.focus = not self.focus
else
self.focus = focusState
end
SetNuiFocus(self.focus, self.focus)
if keepInputState ~= nil then
self.keepInput = keepInputState
else
self.keepInput = not self.focus
end
self.keepInput = keepInputState
SetNuiFocusKeepInput(self.keepInput)
if not self.keepInput and self.mode == "mgizmo" then
self:toggleGizmoMode("gizmo")
Debug("setFocus ::: toggleGizmoMode to gizmo because keepInput is false")
end
end
function decorate.placeObjectOnGround(self)
local currentHandle = self.currentObject and self.currentObject.handle
if not currentHandle then return end
PlaceObjectOnGroundProperly(currentHandle)
gizmo:updateGizmoEntity()
end
function decorate.toggleGizmoMode(self, targetMode)
if self.mode == "gizmo" and not self.keepInput then
return Debug("toggleGizmoMode ::: mgizmo mode is enabled and keepInput is true, so we do not toggle mode")
end
if targetMode then
if targetMode == self.mode then
return Debug("toggleGizmoMode ::: mode is already same", "mode", targetMode)
end
self.mode = targetMode
else
self.mode = (self.mode == "gizmo") and "mgizmo" or "gizmo"
end
SendReactMessage("toggle_gizmo_mode", self.mode)
Notification(i18n.t("decorate.gizmo_mode_toggled", { mode = self.mode }), "info")
gizmo:deselectEntity()
mgizmo:deselectEntity()
if self.mode == "gizmo" then
gizmo:handleCameraUpdate()
else
mgizmo:loop()
end
end
LIGHT_ITEMS = Config.Furniture.light.items
local dynamicFurnituresBackup = table.deepclone(Config.DynamicFurnitures)
function InitializeFurnitures()
Config.DynamicFurnitures = table.deepclone(dynamicFurnituresBackup)
Config.DoorModels = {}
for categoryKey, category in pairs(Config.Furniture) do
if categoryKey ~= "navigation" then
for _, item in pairs(category.items) do
if item.type then
Config.DynamicFurnitures[item.object] = item
end
if item.isDoor then
Config.DoorModels[item.object] = item
end
if item.colors then
for _, colorVariant in pairs(item.colors) do
if colorVariant.type then
Config.DynamicFurnitures[colorVariant.object] = colorVariant
end
if item.isDoor then
Config.DoorModels[colorVariant.object] = colorVariant
end
end
end
end
end
end
end
CreateThread(InitializeFurnitures)
function decorate.handleControls(self)
local lastOutlinedHandle = 0
CreateThread(function()
while self.active do
disablePlayerFiring(cache.playerId, true)
local currentHandle = self.currentObject and self.currentObject.handle
if lastOutlinedHandle ~= currentHandle then
SetEntityDrawOutline(lastOutlinedHandle, false)
SetEntityDrawOutline(currentHandle, true)
SetEntityDrawOutlineColor(0, 180, 255, 255)
end
lastOutlinedHandle = currentHandle
if isControlJustPressed(0, Keys.F5) or isDisabledControlJustPressed(0, Keys.F5) then
self:setFocus(true)
end
if isControlJustPressed(0, Keys.F3) or isDisabledControlJustPressed(0, Keys.F3) then
self:toggleFreeCamera()
end
if isControlJustPressed(0, Keys.Enter) or isDisabledControlJustPressed(0, Keys.Enter) then
if not (self.currentObject and self.currentObject.stashId) then
self:openBuyObjectModal()
end
end
if currentHandle then
if isControlJustPressed(0, Keys.Delete) or isDisabledControlJustPressed(0, Keys.Delete) then
self:removeCurrentObject()
end
if isControlJustPressed(0, Keys.G) or isDisabledControlJustPressed(0, Keys.G) then
self:placeObjectOnGround()
end
end
Wait(0)
end
end)
end
RegisterNetEvent("garages:decorate:open")
AddEventHandler("garages:decorate:open", function()
if not currentlyInGarage then
return Notification(i18n.t("decorate.not_in_garage"), "error")
end
local garageData = Config.Garages[currentlyInGarage]
if not garageData then
return Notification(i18n.t("decorate.invalid_garage"), "error")
end
if garageData.available or garageData.isImpound then
return Notification(i18n.t("decorate.not_support_decoration"), "error")
end
local playerIdentifier = GetPlayerIdentifier()
local isOwner = garageData.owner == playerIdentifier
if Config.DecorateOnlyAccessForOwner and not isOwner then
return Notification(i18n.t("decorate.not_owner"), "error")
elseif not HasKey(currentlyInGarage) and not isOwner then
return Notification(i18n.t("decorate.not_key_holder"), "error")
end
decorate:open()
end)
function decorate.toggleFreeCamera(self, forceState)
if forceState ~= nil then
self.freeCamera = forceState
else
self.freeCamera = not self.freeCamera
end
if not self.freeCamera then return end
SetPlayerControl(cache.playerId, false, 0)
local _, _, pedForward, pedPos = GetEntityMatrix(cache.ped)
local cameraStartPos = pedPos + pedForward * 2
local pedRotation = GetEntityRotation(cache.ped)
local freeCam = Utils.CreateCamera("DEFAULT_SCRIPTED_CAMERA", cameraStartPos, pedRotation, true)
self:instructional({
{ key = "focus_free_camera", label = "Focus Object" }
})
self.cameraFocus = false
CreateThread(function()
local camPos = cameraStartPos
local camRot = pedRotation
while self.active and self.freeCamera do
local newPos, newRot = Utils.HandleFlyCam(freeCam, { mouse = not self.cameraFocus })
camRot = newRot
camPos = newPos
DisableAllControlActions(0)
if isDisabledControlJustPressed(0, Keys.F) then
if self.mode == "gizmo" then
self.cameraFocus = not self.cameraFocus
else
Notification(i18n.t("decorate.focus_object_not_supported"), "error")
end
end
if self.cameraFocus and self.mode == "mgizmo" then
self.cameraFocus = false
end
if self.cameraFocus then
local currentObjHandle = self.currentObject and self.currentObject.handle
if currentObjHandle and DoesEntityExist(currentObjHandle) then
local objCoords = GetEntityCoords(currentObjHandle)
local objModel = GetEntityModel(currentObjHandle)
local minDim, maxDim = GetModelDimensions(objModel)
local objSize = #(maxDim - minDim)
local idealDist = math.max(objSize * 2.0, 3.0)
local objHeight = maxDim.z - minDim.z
local camToObj = camPos - objCoords
local dist = #camToObj
local pitch = math.deg(math.asin(camToObj.z / dist))
local yaw = math.deg(math.atan(-camToObj.x, camToObj.y))
local targetRot = vec3(pitch, 0.0, yaw)
local lerpedRot = vec3(
camRot.x + (targetRot.x - camRot.x) * CAMERA_LERP_SPEED,
camRot.y + (targetRot.y - camRot.y) * CAMERA_LERP_SPEED,
camRot.z + (targetRot.z - camRot.z) * CAMERA_LERP_SPEED
)
SetCamRot(freeCam, lerpedRot.x, lerpedRot.y, lerpedRot.z, 2)
if dist > idealDist * 1.5 or dist < idealDist * 0.5 then
local dirToObj = camToObj / dist
local targetPos = objCoords - dirToObj * idealDist
local lerpedPos = vec3(
camPos.x + (targetPos.x - camPos.x) * CAMERA_LERP_SPEED * 0.5,
camPos.y + (targetPos.y - camPos.y) * CAMERA_LERP_SPEED * 0.5,
camPos.z + (targetPos.z - camPos.z) * CAMERA_LERP_SPEED * 0.5
)
SetCamCoord(freeCam, lerpedPos.x, lerpedPos.y, lerpedPos.z)
camPos = lerpedPos
end
if dist < idealDist * 0.7 then
local orbitAngle = GetGameTimer() / 1000.0 * 0.3
local orbitRadius = objSize * 0.8
local targetOrbit = vec3(
objCoords.x + math.cos(orbitAngle) * orbitRadius,
objCoords.y + math.sin(orbitAngle) * orbitRadius,
objCoords.z + objHeight
)
local lerpedOrbit = vec3(
camPos.x + (targetOrbit.x - camPos.x) * 0.05,
camPos.y + (targetOrbit.y - camPos.y) * 0.05,
camPos.z + (targetOrbit.z - camPos.z) * 0.05
)
SetCamCoord(freeCam, lerpedOrbit.x, lerpedOrbit.y, lerpedOrbit.z)
end
end
end
Wait(0)
end
Utils.DestroyFlyCam(freeCam, 1000)
SetPlayerControl(cache.playerId, true, 0)
self:instructional()
end)
end
function decorate.removeCurrentObject(self)
local currentObj = self.currentObject
if not currentObj then return end
if currentObj.handle and not currentObj.stashId then
DeleteObject(currentObj.handle)
end
gizmo:deselectEntity()
self.currentObject = nil
SendReactMessage("remove_current_object")
Debug("Removed current object", self.currentObject)
end
exports("inDecorate", function()
return decorate.active
end)
function decorate.getObjectData(self, entityHandle)
local currentHandle = self.currentObject and self.currentObject.handle
if currentHandle == entityHandle then
return self.currentObject
end
for _, objData in pairs(decorate.objects) do
if objData.handle and DoesEntityExist(objData.handle) and objData.handle == entityHandle then
return objData
end
end
return false
end
function decorate.saveCurrentObject(self)
Debug("saveCurrentObject", "Current object", decorate.currentObject)
if not self.currentObject then return end
local objToSave = {
modelName = self.currentObject.modelName,
coords = GetEntityCoords(self.currentObject.handle),
rotation = GetEntityRotation(self.currentObject.handle),
handle = self.currentObject.handle,
inStash = false,
inside = currentlyInGarage ~= nil,
insideId = currentlyInGarage
}
self:removeCurrentObject()
return lib.callback.await("garages:decorate:saveObject", false, currentlyInGarage, objToSave)
end
function decorate.destroyObjects(self)
local objectsCopy = table.deepclone(decorate.objects)
decorate.objects = {}
for _, objData in pairs(objectsCopy) do
RemoveSpawnedObject(objData)
end
Debug("Unloaded objects")
end
function decorate.refreshObjects(self)
for _, objData in pairs(decorate.objects) do
RemoveSpawnedObject(objData)
end
end
function decorate.saveObjects(self)
for _, objData in pairs(decorate.objects) do
if objData.spawned and DoesEntityExist(objData.handle) then
local newCoords = GetEntityCoords(objData.handle)
local newRotation = GetEntityRotation(objData.handle)
if newCoords.x ~= objData.coords.x or newRotation.x ~= objData.rotation.x then
objData.coords = newCoords
objData.rotation = newRotation
TriggerServerEvent("garages:decorate:updateObject", currentlyInGarage, objData.id, {
coords = json.encode(newCoords),
rotation = json.encode(newRotation)
})
end
end
end
end
function decorate.openBuyObjectModal(self)
if not self.currentObject then return end
SendReactMessage("open_buy_object_modal")
end
RegisterNetEvent("garages:decorate:updateObject")
AddEventHandler("garages:decorate:updateObject", function(garageId, objectId, newData)
if currentlyInGarage ~= garageId then
return Debug("garages:decorate:updateObject ::: insideID is not same", "currentlyInGarage", currentlyInGarage, "insideID", garageId)
end
local foundObject = table.find(decorate.objects, function(obj) return obj.id == objectId end)
if not foundObject then
Error("garages:decorate:updateObject :: Object not found", "id", objectId)
return
end
for key, value in pairs(newData) do
if key == "coords" and foundObject.spawned then
Debug("garages:decorate:updateObject ::: SetEntityCoords", "object", foundObject.handle, "coords", value)
SetEntityCoords(foundObject.handle, value.x, value.y, value.z, false, false, false, false)
elseif key == "rotation" and foundObject.spawned then
Debug("garages:decorate:updateObject ::: SetEntityRotation", "object", foundObject.handle, "rotation", value)
SetEntityRotation(foundObject.handle, value.x, value.y, value.z, 0, false)
end
foundObject[key] = value
Debug("Updated object", "object", foundObject.id, "key", key, "value", value)
end
end)
function RemoveSpawnedObject(objData)
if not objData.spawned then return false end
DeleteObject(objData.handle)
objData.spawned = false
end
RegisterNetEvent("garages:decorate:sellFurniture")
AddEventHandler("garages:decorate:sellFurniture", function(garageId, objectId)
if currentlyInGarage ~= garageId then
return Debug("garages:decorate:sellFurniture ::: decorateId is not same", "CurrentGarage", currentlyInGarage, "garage", garageId)
end
local foundObject = table.find(decorate.objects, function(obj) return obj.id == objectId end)
if not foundObject then
Error("garages:decorate:sellFurniture ::: Object not found", "id", objectId)
return
end
RemoveSpawnedObject(foundObject)
decorate.objects = table.filter(decorate.objects, function(obj) return obj.id ~= objectId end)
Debug("garages:decorate:sellFurniture", "object is deleted from cache", foundObject.id)
end)
RegisterNetEvent("garages:decorate:addObject")
AddEventHandler("garages:decorate:addObject", function(garageId, objectData)
if currentlyInGarage ~= garageId then
return Debug("garages:decorate:addObject ::: garage is not same", "Entered Garage", currentlyInGarage, "garage", garageId)
end
decorate.objects[#decorate.objects + 1] = objectData
Debug("Added object to data", "data", objectData)
end)
function SpawnObject(modelName, coords, rotation)
local modelHash = joaat(modelName)
lib.requestModel(modelHash)
local entity = CreateObject(modelHash, coords.x, coords.y, coords.z, false, false, false)
SetEntityAlpha(entity, 0, false)
CreateThread(function()
for alpha = 0, 255, 51 do
Wait(50)
SetEntityAlpha(entity, alpha, false)
end
end)
if rotation then
SetEntityRotation(entity, rotation.x, rotation.y, rotation.z, 0, false)
end
SetEntityCompletelyDisableCollision(entity, true, false)
if not (Config.DynamicDoors and Config.DoorModels[modelName]) then
FreezeEntityPosition(entity, true)
end
SetModelAsNoLongerNeeded(modelHash)
Wait(0)
SetEntityCoords(entity, coords.x, coords.y, coords.z, false, false, false, false)
return entity
end
function decorate.canAccessStash(self, stashUniq)
if not currentlyInGarage then return false end
return HasKey(currentlyInGarage)
end
function decorate.doorAnim(self)
lib.playAnim(cache.ped, "anim@heists@keycard@", "exit")
Wait(400)
ClearPedTasks(cache.ped)
end
local TEXT_DYNAMIC = i18n.t("drawtext.dynamic")
local TEXT_STASH = i18n.t("drawtext.stash")
local TEXT_GARDROBE = i18n.t("drawtext.gardrobe")
local TEXT_DELETE = i18n.t("drawtext.delete_illegal")
function decorate.checkNearObjects(self)
CreateThread(function()
while self.objects and #self.objects > 0 do
local waitTime = 500
local pedCoords = GetEntityCoords(cache.ped)
for _, objData in pairs(self.objects) do
local objCoords = vec3(objData.coords.x, objData.coords.y, objData.coords.z)
local distance = #(objCoords - pedCoords)
if distance <= 1.5 then
local dynamicConfig = Config.DynamicFurnitures[objData.modelName]
if dynamicConfig then
local interactPos = GetOffsetFromEntityInWorldCoords(
objData.handle,
dynamicConfig.offset.x, dynamicConfig.offset.y, dynamicConfig.offset.z
)
if dynamicConfig.event then
waitTime = 0
DrawText3D(interactPos.x, interactPos.y, interactPos.z, TEXT_DYNAMIC, "Store", "interact", "E")
if isControlJustPressed(0, Keys.E) then
TriggerEvent(dynamicConfig.event, objData.uniq)
end
elseif dynamicConfig.type == "stash" then
waitTime = 0
DrawText3D(interactPos.x, interactPos.y, interactPos.z, TEXT_STASH, "stash_access", "E")
if isControlJustPressed(0, Keys.E) then
if self:canAccessStash(objData.uniq) then
openStash(dynamicConfig.stash, objData.uniq)
end
end
elseif dynamicConfig.type == "gardrobe" then
waitTime = 0
DrawText3D(interactPos.x, interactPos.y, interactPos.z, TEXT_GARDROBE, "open_wardrobe", "E")
if isControlJustPressed(0, Keys.E) then
openWardrobe()
end
end
end
end
end
Wait(waitTime)
end
end)
end
function decorate.getObjects(self, garageId)
self:destroyObjects()
Debug("decorate:getObjects", "id", garageId)
local objects = lib.callback.await("garages:decorate:getDecorations", 0, garageId)
self.objects = objects
self:checkNearObjects()
Debug("Loaded objects", "data", self.objects)
end
local lightObjectModels = {}
local lightObjectsCache = {}
CreateThread(function()
for _, item in pairs(LIGHT_ITEMS) do
table.insert(lightObjectModels, item.object)
end
while true do
if not decorate.objects then
lightObjectsCache = {}
else
local lightObjects = table.deepclone(table.filter(decorate.objects, function(obj)
return table.includes(lightObjectModels, obj.modelName)
end))
lightObjectsCache = lightObjects
for idx, objData in pairs(lightObjectsCache) do
if objData.handle and DoesEntityExist(objData.handle) then
if not objData.inside or currentlyInGarage then
local rotation = GetEntityRotation(objData.handle)
local coords = GetEntityCoords(objData.handle)
local direction = RotationToDirection(rotation)
lightObjectsCache[idx].position = coords
lightObjectsCache[idx].direction = direction
end
end
end
end
Wait(500)
end
end)
CreateThread(function()
while true do
local waitTime = 1250
for _, objData in pairs(lightObjectsCache) do
if objData.handle and DoesEntityExist(objData.handle) then
local lightData = objData.lightData
if not lightData or not lightData.active then goto continue end
if objData.position then
waitTime = 0
local pos = objData.position
local dir = objData.direction
local rgb = (lightData and lightData.rgb) or { r = 255, g = 255, b = 255 }
local intensity = ((lightData and lightData.intensity) or Config.DefaultLightIntensity) + 0.0
DrawSpotLight(pos.x, pos.y, pos.z, dir.x, dir.y, dir.z,
rgb.r, rgb.g, rgb.b, 100.0, 20.0, 1.0, intensity, 0.0)
end
end
::continue::
end
Wait(waitTime)
end
end)
CreateThread(function()
while true do
local waitTime = decorate.active and 300 or 1250
local pedCoords = GetEntityCoords(cache.ped)
if not decorate.objects then
Wait(waitTime)
else
for _, objData in pairs(decorate.objects) do
if objData.inStash then
if objData.spawned then
DeleteObject(objData.handle)
objData.spawned = false
Debug("Deleted object because its setted to inStash", "object", objData.handle)
end
else
if not objData.coords then
Error("Object coords is nil we skipping it.", "object", objData)
else
objData.coords = vec3(objData.coords.x, objData.coords.y, objData.coords.z)
if objData.coords.x == 0.0 and objData.coords.y == 0.0 and objData.coords.z == 0.0 then
if EditorCamera then
local camData = Utils.GetCamera()
local camForward = Utils.GetForwardVector(camData.rotation) * 5.0
local camPos = camData.coords + camForward
Debug("Load Decorations : Object is from ikea. We setted it to camera center", "v", objData)
objData.coords = vec3(camPos.x, camPos.y, camPos.z)
decorate:saveObjects()
end
end
local distance = #(pedCoords - objData.coords)
if distance <= Config.SpawnDistance then
if not objData.spawned then
local entityHandle = SpawnObject(objData.modelName, objData.coords, objData.rotation)
if entityHandle then
objData.handle = entityHandle
local stashId = decorate.currentObject and decorate.currentObject.stashId
if stashId == objData.id then
decorate.currentObject.handle = entityHandle
decorate:selectEntity(entityHandle)
end
else
objData.handle = 0
Warning("This model is not loaded. Please check if the model is valid. if its not delete it from the list", objData.modelName)
end
objData.spawned = true
end
elseif distance > Config.SpawnDistance then
if objData.spawned then
RemoveSpawnedObject(objData)
Debug("Deleted object", "object", objData.handle)
end
end
end
end
end
Wait(waitTime)
end
end
end)
local function AddFurnitureToCategory(categoryName, itemData)
local category = Config.Furniture[categoryName]
if category then
table.insert(category.items, itemData)
InitializeFurnitures()
else
print("Error: Category " .. categoryName .. " does not exist in furniture settings")
end
end
exports("AddFurniture", function(categoryName, itemData)
return AddFurnitureToCategory(categoryName, itemData)
end)
exports("AddShell", function(shellData)
Config.Shells[#Config.Shells + 1] = shellData
Debug("Added shell", shellData)
end)
AddEventHandler("onResourceStop", function(resourceName)
if GetCurrentResourceName() ~= resourceName then return end
decorate:destroyObjects()
decorate:close()
end)
RegisterCommand("garagedecorate", function()
TriggerEvent("garages:decorate:open")
end)