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

266 lines
8.8 KiB
Lua

local pendingScreenshots = {}
local uploadedResults = {}
furnitureCreator = {
visible = false,
token = nil
}
function furnitureCreator.updateUI(self)
if not self.visible then return end
local furnitureCopy = table.deepclone(Config.Furniture)
for _, category in pairs(furnitureCopy) do
category.items = table.filter(category.items, function(item)
return item.creator
end)
end
SendReactMessage("toggle_furniture_creator", {
visible = true,
furniture = furnitureCopy
})
end
function furnitureCreator.open(self)
ToggleHud(false)
self.visible = true
SetNuiFocus(true, true)
self:updateUI()
Debug("Furniture Creator opened")
end
function furnitureCreator.close(self)
if not self.visible then return end
self.visible = false
SendReactMessage("toggle_furniture_creator", { visible = false })
ToggleHud(true)
SetNuiFocus(false, false)
Debug("Furniture Creator closed")
end
RegisterNetEvent("garages:syncFurnitureData")
AddEventHandler("garages:syncFurnitureData", function(furnitureData)
Config.Furniture = furnitureData
InitializeFurnitures()
end)
RegisterCommand(Config.FurniCreatorCommand, function()
local hasPermission = lib.callback.await("garages:hasPermission", 0)
if not hasPermission then
return Notification(i18n.t("no_permission"), "error")
end
furnitureCreator:open()
end)
function furnitureCreator.takeScreenshot(self, requestId)
Wait(Config.FurniCreator.interval)
if not self.token then
self.token = lib.callback.await("garages:getFiveManageToken", false)
end
if not self.token then
return Notification(i18n.t("furniture_creator.token_not_set"), "error")
end
pendingScreenshots[requestId] = {
name = requestId,
timestamp = GetGameTimer()
}
Notification(i18n.t("furniture_creator.uploading_image"), "info")
exports["screenshot-basic"]:requestScreenshot({ encoding = "png" }, function(imageData)
SendReactMessage("upload_image", {
image = imageData,
fiveManageToken = self.token,
requestId = requestId
})
end)
Debug("Screenshot Request Sent", requestId)
return requestId
end
function furnitureCreator.hasPendingScreenshots(self)
return next(pendingScreenshots) ~= nil
end
RegisterNUICallback("handle_uploaded_image", function(data, cb)
local requestId = data.requestId
local pending = pendingScreenshots[requestId]
if not pending then
Error("No data found for requestId", data.requestId)
return cb("error")
end
uploadedResults[requestId] = {
filePath = data.url,
name = pending.name
}
Debug("Screenshot Results", uploadedResults[requestId], "data", data)
pendingScreenshots[requestId] = nil
Notification(i18n.t("furniture_creator.image_uploaded"), "success")
cb("ok")
end)
local GREEN_BOX_ORIGIN = vec3(-2407.29736328125, -2023.800537109375, 690.6831665039062)
local GREEN_BOX_OFFSET = vec3(0.0, 0.0, 0.5)
local MIN_CAMERA_DIST = 3.0
local ROTATE_SPEED = 100.0
local VERTICAL_SPEED = 0.5
local ZOOM_SPEED = 15.0
local MAX_ZOOM = 10.0
function furnitureCreator.takeObjectScreenshot(self, objectModelName)
if self.inGreenBox then return end
self.inGreenBox = true
self.initialPlayerCoords = GetEntityCoords(cache.ped)
FreezeEntityPosition(cache.ped, true)
SetEntityAlpha(cache.ped, 0, false)
local greenBoxModel = joaat("qs_gradient_032")
lib.requestModel(greenBoxModel)
local greenBoxEntity = CreateObject(greenBoxModel, GREEN_BOX_ORIGIN.x, GREEN_BOX_ORIGIN.y, GREEN_BOX_ORIGIN.z, false, false, false)
self.greenBox = greenBoxEntity
FreezeEntityPosition(greenBoxEntity, true)
local objectModel = joaat(objectModelName)
lib.requestModel(objectModel)
local spawnPos = GREEN_BOX_ORIGIN + GREEN_BOX_OFFSET
local objectEntity = CreateObject(objectModel, spawnPos.x, spawnPos.y, spawnPos.z, false, false, false)
self.object = objectEntity
FreezeEntityPosition(objectEntity, true)
SetEntityHeading(objectEntity, 0.0)
Wait(100)
local objectCoords = GetEntityCoords(objectEntity)
local minDim, maxDim = GetModelDimensions(objectModel)
local objectSize = #(maxDim - minDim)
local cameraOffset = math.min(objectSize * 2.5, MIN_CAMERA_DIST)
local verticalMidpoint = (maxDim.z - minDim.z) / 2
local cameraPos = vec3(objectCoords.x + cameraOffset, objectCoords.y, objectCoords.z + verticalMidpoint)
local greenBoxCamera = Utils.CreateCamera("DEFAULT_SCRIPTED_CAMERA", cameraPos, vec3(0, 0, 0), true, nil, 0)
self.greenBoxCamera = greenBoxCamera
SetCamCoord(greenBoxCamera, cameraPos.x, cameraPos.y, cameraPos.z)
PointCamAtCoord(greenBoxCamera, objectCoords.x, objectCoords.y, objectCoords.z)
Utils.DrawInstructional({ "zoom", "done", "cancel", "up", "rotate_z" })
local heading = 0.0
local verticalOffset = verticalMidpoint
local currentZoom = cameraOffset
local confirmed = false
while self.inGreenBox do
DisableAllControlActions(0)
SetEntityHeading(objectEntity, heading)
SetEntityCoords(objectEntity, objectCoords.x, objectCoords.y, objectCoords.z + verticalOffset, false, false, false, false)
if IsDisabledControlPressed(0, ActionControls.rotate_z.codes[1]) then
heading = (heading + ROTATE_SPEED * GetFrameTime()) % 360
elseif IsDisabledControlPressed(0, ActionControls.rotate_z.codes[2]) then
heading = (heading - ROTATE_SPEED * GetFrameTime()) % 360
end
if IsDisabledControlPressed(0, ActionControls.up.codes[1]) then
verticalOffset = verticalOffset + VERTICAL_SPEED * GetFrameTime()
elseif IsDisabledControlPressed(0, ActionControls.up.codes[2]) then
verticalOffset = verticalOffset - VERTICAL_SPEED * GetFrameTime()
end
if IsDisabledControlPressed(0, ActionControls.rotate_z_scroll.codes[1]) then
currentZoom = math.max(MIN_CAMERA_DIST / 3, currentZoom - ZOOM_SPEED * GetFrameTime())
local newCamPos = vec3(objectCoords.x + currentZoom, objectCoords.y, objectCoords.z + verticalMidpoint)
cameraPos = newCamPos
SetCamCoord(greenBoxCamera, newCamPos.x, newCamPos.y, newCamPos.z)
elseif IsDisabledControlPressed(0, ActionControls.rotate_z_scroll.codes[2]) then
currentZoom = math.min(MAX_ZOOM, currentZoom + ZOOM_SPEED * GetFrameTime())
local newCamPos = vec3(objectCoords.x + currentZoom, objectCoords.y, objectCoords.z + verticalMidpoint)
cameraPos = newCamPos
SetCamCoord(greenBoxCamera, newCamPos.x, newCamPos.y, newCamPos.z)
end
if IsDisabledControlJustPressed(0, ActionControls.done.codes[1]) then
confirmed = true
self.inGreenBox = false
break
end
if IsDisabledControlJustPressed(0, ActionControls.cancel.codes[1]) then
self.inGreenBox = false
Notification(i18n.t("furniture_creator.screenshot_cancelled"), "error")
break
end
Wait(0)
end
Utils.RemoveInstructional()
if confirmed then
local screenshotId = self:takeScreenshot(objectModelName)
if not screenshotId then return end
while self:hasPendingScreenshots() do
Wait(0)
end
local resultData = uploadedResults[screenshotId]
self:destroyScreenshot()
uploadedResults[screenshotId] = nil
return resultData and resultData.filePath
else
self:destroyScreenshot()
return nil
end
end
RegisterCommand("test", function()
local url = furnitureCreator:takeObjectScreenshot("prop_rub_washer_01")
Debug("Screenshot URL", url)
end)
function furnitureCreator.destroyScreenshot(self)
if self.greenBox and DoesEntityExist(self.greenBox) then
DeleteEntity(self.greenBox)
self.greenBox = nil
end
if self.object and DoesEntityExist(self.object) then
DeleteEntity(self.object)
self.object = nil
end
if self.greenBoxCamera then
Utils.DestroyFlyCam(self.greenBoxCamera)
self.greenBoxCamera = nil
end
if self.initialPlayerCoords then
local coords = self.initialPlayerCoords
SetEntityCoords(cache.ped, coords.x, coords.y, coords.z, false, false, false, false)
FreezeEntityPosition(cache.ped, false)
SetEntityAlpha(cache.ped, 255, false)
self.initialPlayerCoords = nil
end
self.inGreenBox = false
end
AddEventHandler("onResourceStop", function(resourceName)
if resourceName == GetCurrentResourceName() then
furnitureCreator:destroyScreenshot()
end
end)