local vehicleList = {} -- preGarageCoords est maintenant global dans main.lua local menuCamera = nil local currentGarage = nil local garageEventListener = nil local previewVehicle = nil local vehicleCamera = nil local dofLoopActive = false TempVehicle = nil IsCameraMoving = false InGarageMenu = false IsCameraAvailable = true RegisterNUICallback("existAvailableSlot", function(data, cb) local garageId = data.garage or currentGarage local usedSlots = lib.callback.await("advancedgarages:getGarageSlots", false, garageId) local garageConfig = Config.Garages[garageId] if not garageConfig then return cb(false) end local garageType = garageConfig.type local shellId = garageConfig.shell.shell local totalSlots = #Config.VehicleShowRooms[garageType][shellId].vehicleCoords if usedSlots >= totalSlots then return cb(false) end cb(true) end) function CheckJob(jobName, minGrade) local playerJob = GetJobName() local playerGang = GetGang() local playerGrade = GetJobGrade() local gradeOk = true if minGrade then gradeOk = minGrade <= playerGrade end if jobName == playerJob and gradeOk then return true end local gangName = playerGang and playerGang.name if gangName == jobName and gradeOk then return true end return false end function CheckGarageAuthorization(allowedJobs, allowedGangs) if not allowedJobs and not allowedGangs then return true end local merged = table.merge(allowedJobs or {}, allowedGangs or {}) if #merged == 0 then return true end local playerJob = GetJobName() local playerGrade = GetJobGrade() local playerGang = GetGang() for _, entry in pairs(merged) do if entry.name == playerJob then if entry.grades then if not table.contains(entry.grades, playerGrade) then goto continue end end return true, entry.name else if entry.name == playerGang.name then if entry.grades then if not table.contains(entry.grades, playerGang.grade) then goto continue end end return true, entry.name end end ::continue:: end return false, nil end RegisterNUICallback("getPlayerList", function(data, cb) local players = {} local allNames = lib.callback.await("advancedgarages:GetPlayersName", false) local myServerId = GetPlayerServerId(PlayerId()) for serverId, playerName in pairs(allNames) do if serverId ~= myServerId then table.insert(players, { id = serverId, name = playerName }) end end cb(players) end) RegisterNUICallback("getOutVehiclesList", function(data, cb) local rawList = lib.callback.await("advancedgarages:getOutVehicles", false, currentGarage) local result = {} for _, entry in pairs(rawList) do local vehicleData = json.decode(entry.vehicle) local modelName = vehicleData.model local brand = Config.VehicleBrands.brands[modelName] brand = brand and brand.brand or "Unknown" result[#result + 1] = { id = entry.plate, name = GetVehicleNameFromModel(modelName), brand = brand } end cb(result) end) RegisterNUICallback("getGarageList", function(data, cb) local garages = {} local myId = GetPlayerIdentifier() for garageId, garageData in pairs(Config.Garages) do local isOwned = not garageData.owner if isOwned ~= myId and garageId ~= ClosestGarage and garageData.type == nearbyGarageType then garages[#garages + 1] = garageId end end cb(garages) end) local function EquipFists() SetCurrentPedWeapon(cache.ped, -1569615261, true) end RegisterNUICallback("toggle_ped_visibility", function(visible, cb) cb(1) if not currentlyInGarage then return end if visible then SetEntityAlpha(cache.ped, 255, false) else SetEntityAlpha(cache.ped, 0, false) end end) local function StartDofLoop() dofLoopActive = true CreateThread(function() local lastCam = nil while dofLoopActive do local cam = GetRenderingCam() if cam ~= lastCam then lastCam = cam SetCamUseShallowDofMode(cam, true) SetCamNearDof(cam, 0.7) SetCamFarDof(cam, 5.0) SetCamDofStrength(cam, 1.0) end SetUseHiDof() Wait(0) end end) end local function FreezePlayerAtGarage(vehicle) local ped = cache.ped IsCameraAvailable = true inPlayerAnim = true IsCameraMoving = true FreezeEntityPosition(ped, false) StartIdleCam(vehicle, 1) if vehicle ~= 0 then TaskEnterVehicle(ped, vehicle, 3000, -1, 1.0, 0) end end function OpenGarageMenu(garageId, isImpound, garageVehicles, isBoat) if isImpound then TriggerServerEvent("advancedgarages:server:sentToImpound") Wait(500) end local garageConfig = Config.Garages[garageId] or Config.JobGarages[garageId] if not garageConfig then return Notification(i18n.t("garage_not_found"), "error") end if not garageVehicles then garageVehicles = lib.callback.await("advancedgarages:getGarageData", false, garageId) end if (not garageVehicles or (isImpound and #garageVehicles == 0)) then return Notification(i18n.t("garage_empty"), "error") end TriggerServerEvent("advancedgarages:RoutePlayer", garageId) currentlyInGarage = garageId local jobGarageConfig = Config.JobGarages[garageId] if garageVehicles then table.sort(garageVehicles, function(a, b) return (a.favorite or 0) > (b.favorite or 0) end) end currentGarage = garageId menuCamera = nil vehicleList = garageVehicles ToggleHud(false) Debug("Named garage activated:", json.encode(currentGarage)) Debug("Additional information of the current garage:", json.encode(Config.Garages[currentGarage], { indent = true })) TriggerEvent("advancedgarages:GetWeatherSync", true, 14) DoScreenFadeOut(300) Wait(500) preGarageCoords = GetEntityCoords(cache.ped) vehicleCamera = nil local garageType = garageConfig and garageConfig.type if Config.Garages[currentGarage] then if garageType == "vehicle" then vehicleCamera = garageConfig.vehicleCamera or Config.vehicleCamera elseif garageType == "boat" then if isBoat and not garageConfig.vehicleCamera then vehicleCamera = Config.BoatCamera else vehicleCamera = garageConfig.vehicleCamera end elseif garageType == "plane" then vehicleCamera = garageConfig.vehicleCamera or Config.PlaneCamera end else vehicleCamera = jobGarageConfig and (jobGarageConfig.vehicleCamera or Config.vehicleCamera) end local camCfg = vehicleCamera.camera SetEntityCoords(cache.ped, camCfg.ped.x, camCfg.ped.y, camCfg.ped.z, false, false, false, false) SetEntityHeading(cache.ped, camCfg.ped.w) lib.requestAnimDict("amb@world_human_leaning@male@wall@back@foot_up@idle_a") TaskPlayAnim(cache.ped, "amb@world_human_leaning@male@wall@back@foot_up@idle_a", "idle_a", 1.0, 1.0, -1, 1, 0, false, false, false) TaskLookAtCoord(cache.ped, camCfg.coords.x, camCfg.coords.y, camCfg.coords.z, 5000, 0, 2) menuCamera = CreateCamWithParams("DEFAULT_SCRIPTED_CAMERA", camCfg.coords.x, camCfg.coords.y, camCfg.coords.z, camCfg.rotation.x, camCfg.rotation.y, camCfg.rotation.z, 65.0, true, 0 ) RenderScriptCams(true, false, 1, true, true) FreezePlayerAtGarage(TempVehicle) EquipFists() TriggerEvent("advancedgarages:client:PlayOnOne") RemoveAnimDict("amb@world_human_leaning@male@wall@back@foot_up@idle_a") local playerBalance = lib.callback.await("advancedgarages:getBalance", false) or 0 local takeOutPrice = 0 if isImpound and Config.ImpoundPrice then takeOutPrice = Config.ImpoundPrice else takeOutPrice = (jobGarageConfig and jobGarageConfig.price) or 0 end local musicTrack = "" if SoundEnabled and Config.SoundFiles and #Config.SoundFiles > 0 then musicTrack = Config.SoundFiles[math.random(#Config.SoundFiles)] end -- Nous envoyons à la fois l'action moderne et l'action originale pour compatibilité local payload = { visible = true, vehicles = vehicleList or {}, isJobGarage = (garageVehicles ~= nil), isImpound = isImpound, takeOutPrice = takeOutPrice, balance = playerBalance, music = musicTrack, garageId = garageId, identifier = identifier, -- On envoie enfin QUI on est au NUI config = { enabledButtons = Config.EnabledButtons }, -- Version nested enabledButtons = Config.EnabledButtons -- Version root } SendReactMessage("open", payload) -- Action Quasar standard SendReactMessage("toggle_garage_menu", payload) -- Fallback Wait(700) SetNuiFocus(true, true) DoScreenFadeIn(400) end exports("OpenGarageMenu", OpenGarageMenu) RegisterNetEvent("advancedgarages:client:payVehiclePrice") AddEventHandler("advancedgarages:client:payVehiclePrice", function() vehicleList = lib.callback.await("advancedgarages:getGarageData", false, currentGarage) SendReactMessage("update_vehicles", vehicleList) end) local function RemoveVehicleKeys(vehicle) if not Config.Vehiclekeys or Config.Vehiclekeys == "none" then return end local vehicleModel = GetDisplayNameFromVehicleModel(GetEntityModel(vehicle)) local vehiclePlate = GetVehicleNumberPlateText(vehicle) RemoveVehiclekeys(vehicleModel, vehiclePlate) Wait(150) end function saveJobVehicle(garageData, vehicle) local jobOrGang = garageData.job or garageData.gang local plate = MathTrim(GetVehicleNumberPlateText(vehicle)) Debug("Is Vehicle Access Garage", "Job", jobOrGang, "Vehicle", vehicle, "Model", GetEntityModel(vehicle)) local isOwnedByJob = lib.callback.await("advancedgarages:isVehicleOwnedByJob", false, plate, jobOrGang) if isOwnedByJob then return true end local vehicleModel = GetEntityModel(vehicle) for _, model in pairs(garageData.vehicles) do if model == vehicleModel then return true end end return false end function StoreVehicle(garageId, isJobGarage, vehicleEntity) Debug("Store Vehicle", "Garage", garageId, "IsJob", isJobGarage, "Entity", vehicleEntity) local vehicle = vehicleEntity or cache.vehicle if not vehicle then return end local plate = GetPlate(vehicle) if isJobGarage then local garageData = Config.JobGarages[garageId] local isAllowed = saveJobVehicle(garageData, vehicle, GetEntityModel(vehicle)) if not isAllowed then return Notification(i18n.t("cannot_store_vehicle"), "error") end end local isOwned = isJobGarage if not isJobGarage then isOwned = lib.callback.await("advancedgarages:isVehicleOwned", false, plate) end if not isOwned then return Notification(i18n.t("no_vehicle_owner"), "error") end local driverSeatFree = IsVehicleSeatFree(vehicle, -1) local driverPed = GetPedInVehicleSeat(vehicle, -1) if driverPed ~= cache.ped and not driverSeatFree then return Notification(i18n.t("not_driver"), "error") end if isJobGarage then return saveJobVehicle(Config.JobGarages[garageId], vehicle) end SaveVehicle(garageId, nil, vehicle) end function CheckVehicleType(modelName, garageType) local vehicleClass = GetVehicleClassFromName(modelName) if garageType == "boat" then return vehicleClass == 14 elseif garageType == "plane" then return vehicleClass == 15 or vehicleClass == 16 elseif garageType == "vehicle" then return vehicleClass ~= 14 and vehicleClass ~= 15 and vehicleClass ~= 16 end return true end local function GetVehicleTypeFromClass(vehicle) local vehicleClass = GetVehicleClass(vehicle) if vehicleClass == 15 or vehicleClass == 16 then return "plane" elseif vehicleClass == 14 then return "boat" else return "vehicle" end end local storeVehicleText = i18n.t("drawtext.store_vehicle") local function HandleStoreVehicleInteraction(garageData, isJobGarage) local pedCoords = GetEntityCoords(cache.ped) local spawnCoords = vec3(garageData.coords.spawnCoords.x, garageData.coords.spawnCoords.y, garageData.coords.spawnCoords.z) local dist = #(pedCoords - spawnCoords) local waitTime = 500 if garageData.isImpound then return waitTime end local jobOrGang = garageData.job or garageData.gang if jobOrGang and dist < 10.0 then waitTime = 0 end if dist > 50.0 then return waitTime end waitTime = 0 DrawMarkerZone(garageData.coords.spawnCoords.x, garageData.coords.spawnCoords.y, garageData.coords.spawnCoords.z) if dist > 7.0 then return waitTime end DrawText3D( garageData.coords.spawnCoords.x, garageData.coords.spawnCoords.y, garageData.coords.spawnCoords.z + 0.3, storeVehicleText, "store_vehicle", "E" ) if IsControlJustPressed(0, Keys.E) then local vehicle = cache.vehicle local vehicleModel = GetDisplayNameFromVehicleModel(GetEntityModel(vehicle)) if not isJobGarage then local vehicleType = GetVehicleTypeFromClass(vehicle) if vehicleType ~= garageData.type then Debug("You can't store " .. vehicleModel .. " in [" .. garageData.type .. "] garage type") Notification(i18n.t("cannot_store_vehicle"), "error") return waitTime end end local targetGarage = ClosestGarage or garageData StoreVehicle(targetGarage, isJobGarage) return waitTime end return waitTime end if not Config.UseTarget or Config.UseTarget == "none" then local openGarageText = i18n.t("drawtext.open_garage") local publicGarageText = i18n.t("drawtext.public_garage") CreateThread(function() local function DrawGarageInteraction() local waitTime = 500 if not ClosestGarage then return waitTime end local garageData = Config.Garages[ClosestGarage] if not (IsGarageOwner or garageData.available or IsKeyHolder) then return waitTime end local authorized = CheckGarageAuthorization(garageData.jobs, garageData.gangs) if not authorized then return waitTime end if cache.vehicle then return HandleStoreVehicleInteraction(garageData) end local pedCoords = GetEntityCoords(cache.ped) local menuCoords = vec3(garageData.coords.menuCoords.x, garageData.coords.menuCoords.y, garageData.coords.menuCoords.z) local dist = #(pedCoords - menuCoords) if dist > 20.0 then return waitTime end waitTime = 0 if dist > 4.0 then return waitTime end if garageData.type ~= "plane" or garageData.isImpound then DrawText3D(menuCoords.x, menuCoords.y, menuCoords.z + 0.6, openGarageText, "open_garage", "E") if IsControlJustPressed(0, Keys.E) then OpenGarageMenu(ClosestGarage, garageData.isImpound, nil, garageData.type == "boat") return waitTime end end if garageData.type ~= "plane" then local showPublic = Config.EnablePublicInteriors or garageData.available or garageData.type == "boat" or garageData.isImpound if showPublic then DrawText3D(menuCoords.x, menuCoords.y, menuCoords.z + 0.2, publicGarageText, "public_garage", "G") if IsControlJustPressed(0, Keys.G) then GotoShellGarage(ClosestGarage, garageData.coords.spawnCoords, garageData.shell) return waitTime end end end return waitTime end while true do local waitTime = DrawGarageInteraction() Wait(waitTime) end end) end exports("getVehicleMenu", function() if not ClosestGarage then return CheckJobMenu() end local garageData = Config.Garages[ClosestGarage] if not (IsGarageOwner or garageData.available or IsKeyHolder) then return false end if cache.vehicle then return HandleStoreVehicleInteraction(garageData) end return "menu", { garage = ClosestGarage, available = garageData.available, isImpound = garageData.isImpound, type = garageData.type } end) RegisterNetEvent("advancedgarages:StoreVehicle") AddEventHandler("advancedgarages:StoreVehicle", function() if not ClosestGarage then return end StoreVehicle(ClosestGarage, false) end) RegisterNetEvent("advancedgarages:StoreJobVehicle") AddEventHandler("advancedgarages:StoreJobVehicle", function(data) StoreVehicle(data.garage, true) end) RegisterNetEvent("advancedgarages:OpenGarageMenu") AddEventHandler("advancedgarages:OpenGarageMenu", function() if not ClosestGarage then return end local garageData = Config.Garages[ClosestGarage] OpenGarageMenu(ClosestGarage, garageData.available, nil, garageData.type == "boat") end) RegisterNetEvent("advancedgarages:GotoShell") AddEventHandler("advancedgarages:GotoShell", function() if not ClosestGarage then return end local garageData = Config.Garages[ClosestGarage] GotoShellGarage(ClosestGarage, garageData.coords.spawnCoords, garageData.shell) end) if not Config.UseTarget or Config.UseTarget == "none" then local openGarageText = i18n.t("drawtext.open_garage") CreateThread(function() local function DrawJobGarageInteraction() local waitTime = 500 for garageIndex, garageData in ipairs(Config.JobGarages) do local jobOrGang = garageData.job or garageData.gang if not CheckJob(jobOrGang, garageData.grade) then goto continue end if cache.vehicle then HandleStoreVehicleInteraction(garageData, true) goto continue end local pedCoords = GetEntityCoords(cache.ped) local menuCoords = vec3(garageData.coords.menuCoords.x, garageData.coords.menuCoords.y, garageData.coords.menuCoords.z) local dist = #(pedCoords - menuCoords) if dist > 20.0 then goto continue end waitTime = 0 if dist > 3.0 then goto continue end DrawText3D(menuCoords.x, menuCoords.y, menuCoords.z + 0.3, openGarageText, "open_garage", "E") if IsControlJustPressed(0, Keys.E) then local isAvailable = lib.callback.await("advancedgarages:isGarageAvailable", false, garageIndex) if not isAvailable then Notification(i18n.t("garage_not_available"), "error") goto continue end local jobVehicles = lib.callback.await("advancedgarages:getJobVehicles", false, garageData.name, jobOrGang) for _, v in pairs(jobVehicles) do v.vehicle = json.encode(v.vehicle) end if not garageData.vehicle then for _, vehicleModel in ipairs(garageData.vehicles) do local plate = tostring(jobOrGang .. "_" .. math.random(111, 999)) table.insert(jobVehicles, { id = #jobVehicles + 1, vehicle = json.encode({ model = vehicleModel, plate = plate }), plate = plate }) end end TriggerServerEvent("advancedgarages:setInJobGarage", garageIndex, true) OpenGarageMenu(garageIndex, garageData.isImpound, jobVehicles) end ::continue:: end return waitTime end while true do local waitTime = DrawJobGarageInteraction() Wait(waitTime) end end) end if not Config.UseTarget or Config.UseTarget == "none" or Config.UseTarget == "qb-radialmenu" then CreateThread(function() local recoveryText = "" local function DrawRecoveryInteraction() local waitTime = 500 local pedCoords = GetEntityCoords(cache.ped) for _, recoveryCoords in pairs(Config.Recovery.coords) do local dist = #(pedCoords - vec3(recoveryCoords.x, recoveryCoords.y, recoveryCoords.z)) if dist <= 20.0 then waitTime = 0 if dist <= 3.0 then recoveryText = i18n.t("drawtext.recovery", { price = Config.Recovery.price }) DrawText3D(recoveryCoords.x, recoveryCoords.y, recoveryCoords.z + 0.3, recoveryText, "recovery", "E") if IsControlJustPressed(0, Keys.E) then local vehicles = lib.callback.await("advancedgarages:getRecoveryVehicles", false) if #vehicles == 0 then return Notification(i18n.t("keyholders.empty_out"), "info") end OpenRecoveryMenu(vehicles) return waitTime end end end end return waitTime end while true do local waitTime = DrawRecoveryInteraction() Wait(waitTime) end end) end RegisterNetEvent("advancedgarages:OpenJobMenu") AddEventHandler("advancedgarages:OpenJobMenu", function(data) garage = data.garage OpenGarageMenu(garage.garage, garage.isImpound, garage.vehicleList) end) function CheckJobMenu() local pedCoords = GetEntityCoords(cache.ped) local result = nil for garageIndex, garageData in pairs(Config.JobGarages) do local jobOrGang = garageData.job or garageData.gang if not CheckJob(jobOrGang, garageData.grade) then goto continue end local menuCoords = vec3(garageData.coords.menuCoords.x, garageData.coords.menuCoords.y, garageData.coords.menuCoords.z) local dist = #(pedCoords - menuCoords) if dist > 15.0 then goto continue end if cache.vehicle then return HandleStoreVehicleInteraction(garageData, true) end local vehicleEntries = {} for vehicleIdx, vehicleModel in ipairs(garageData.vehicles) do local plate = tostring(jobOrGang .. "_" .. garageIndex .. vehicleIdx) table.insert(vehicleEntries, { vehicle = json.encode({ model = vehicleModel, plate = plate }), plate = plate }) end result = { garage = garageIndex, vehicleList = vehicleEntries, available = garageData.available, isImpound = garageData.isImpound, type = garageData.type } ::continue:: end if not result then return false end return "job-menu", result end CreateThread(function() while true do if InGarageMenu or currentlyShellData then HideHudAndRadarThisFrame() end Wait(0) end end) exports("usingGarages", function() return InGarageMenu or currentlyShellData end) function CloseGarageMenu() -- On force la fermeture peu importe l'état InGarageMenu = false currentlyInGarage = false ToggleHud(true) TriggerEvent("advancedgarages:GetWeatherSync", false) TriggerServerEvent("advancedgarages:setInJobGarage", currentGarage, false) if garageEventListener then Citizen.RemoveEventListener(garageEventListener) garageEventListener = nil end DoScreenFadeOut(300) SetNuiFocus(false, false) ClearPedTasks(cache.ped) -- On force le déblocage du personnage FreezeEntityPosition(cache.ped, false) SetEntityAlpha(cache.ped, 255, false) dofLoopActive = false if menuCamera then Utils.DestroyFlyCam(menuCamera) menuCamera = nil end -- On détruit toutes les caméras résiduelles RenderScriptCams(false, false, 0, true, true) DestroyAllCams(true) Wait(700) if preGarageCoords then SetEntityCoords(cache.ped, preGarageCoords.x, preGarageCoords.y, preGarageCoords.z, false, false, false, false) end TriggerServerEvent("advancedgarages:RoutePlayerDefault") TriggerEvent("advancedgarages:client:Stop") openedManagementMenu = false Wait(500) DoScreenFadeIn(500) end exports("closeMenu", CloseGarageMenu) RegisterNUICallback("close_menu", function(data, cb) CloseGarageMenu() cb(1) end) RegisterNUICallback("toggle_inspect", function(active, cb) cb(1) IsCameraAvailable = not active if active then if menuCamera then DestroyCam(menuCamera, false) menuCamera = nil end menuCamera = CreateCamWithParams("DEFAULT_SCRIPTED_CAMERA", vehicleCamera.camera.coords.x, vehicleCamera.camera.coords.y, vehicleCamera.camera.coords.z, vehicleCamera.camera.rotation.x, vehicleCamera.camera.rotation.y, vehicleCamera.camera.rotation.z, 65.0, false, 0 ) SetCamActive(menuCamera, true) end end) RegisterNUICallback("takeVehicleFromOut", function(data, cb) local success = lib.callback.await("advancedgarages:takeVehicleFromOut", false, currentGarage, data.plate) vehicleList = lib.callback.await("advancedgarages:getGarageData", false, currentGarage) SendReactMessage("update_vehicles", vehicleList) cb(success) end) RegisterNUICallback("transferVehicle", function(data, cb) print("[GARAGES] NUI Callback: transferVehicle", json.encode(data)) local result = lib.callback.await("advancedgarages:transferVehicle", false, data.garage, data.plate) cb(result) end) RegisterNUICallback("transferVehicleToPlayer", function(data, cb) print("[GARAGES] NUI Callback: transferVehicleToPlayer", json.encode(data)) local result = lib.callback.await("advancedgarages:transferVehicleToPlayer", false, data.playerId, data.plate) cb(result) end) RegisterNUICallback("setVehicleTag", function(data, cb) print("[GARAGES] NUI Callback: setVehicleTag", data.tag, data.plate) TriggerServerEvent("advancedgarages:SetVehicleTag", data.tag, data.plate) cb(1) end) RegisterNUICallback("setVehicleFav", function(data, cb) print("[GARAGES] NUI Callback: setVehicleFav", data.fav, data.plate) TriggerServerEvent("advancedgarages:setVehicleFav", data.fav, data.plate) cb(1) end) function DriveVehicle(vehicleProps, spawnCoords) local hasTimeout = lib.callback.await("advancedgarages:DriveVehicle", false, vehicleProps, spawnCoords, vehicleCamera) if not hasTimeout then Notification(i18n.t("vehicle_not_spawned"), "error") return false end while not cache.vehicle do Debug("Waiting for in vehicle...") Wait(0) end local vehicle = cache.vehicle Wait(150) SetVehicleProperties(vehicle, vehicleProps) Wait(200) if DoesEntityExist(vehicle) then local currentPlate = MathTrim(GetVehicleNumberPlateText(vehicle)) local expectedPlate = MathTrim(vehicleProps.plate) if currentPlate ~= expectedPlate then Debug("Property verification failed, retrying. Expected:", expectedPlate, "Got:", currentPlate) SetVehicleProperties(vehicle, vehicleProps) end end return true end function ChangeVehicleExtra(vehicle, extraId, enable) if not DoesExtraExist(vehicle, extraId) then return end if enable then SetVehicleExtra(vehicle, extraId, false) if IsVehicleExtraTurnedOn(vehicle, extraId) then ChangeVehicleExtra(vehicle, extraId, enable) end else SetVehicleExtra(vehicle, extraId, true) if not IsVehicleExtraTurnedOn(vehicle, extraId) then ChangeVehicleExtra(vehicle, extraId, enable) end end end function SetDefaultVehicleExtras(vehicle, extrasMap) for extraId = 1, 20 do if DoesExtraExist(vehicle, extraId) then SetVehicleExtra(vehicle, extraId, 1) end end for extraId, enabled in pairs(extrasMap) do local shouldEnable = type(enabled) ~= "boolean" or enabled ChangeVehicleExtra(vehicle, tonumber(extraId), shouldEnable) end end local function FreezeBoatOnSpawn(vehicle) if GetVehicleClass(vehicle) ~= 14 then return end Wait(4000) Debug("The boat: " .. vehicle .. " froze so as not to be dragged by the tide!") FreezeEntityPosition(vehicle, true) end local function DeleteVehiclesNearCoords(coords) local refCoords = vec3(coords.x, coords.y, coords.z) for _, vehicle in ipairs(GetGamePool("CVehicle")) do local dist = #(refCoords - GetEntityCoords(vehicle)) if dist < 10.0 then RemoveVehicle(vehicle) Debug("Removed vehicle " .. vehicle .. " from " .. dist .. " meters") end end end RegisterNUICallback("spawnVehicle", function(data, cb) DeleteVehiclesNearCoords(vehicleCamera.vehicleCoords) local vehicleProps = json.decode(data.props) local jobGarageData = Config.JobGarages[currentGarage] local spawned = LocalSpawnVehicle(vehicleProps, vehicleCamera.vehicleCoords, previewVehicle) if jobGarageData then local modelName = vehicleProps.model local livery = jobGarageData.liveries and jobGarageData.liveries[modelName] SetVehicleLivery(spawned, livery) local tuning = jobGarageData.tuning and jobGarageData.tuning[modelName] or {} if tuning then SetVehicleProperties(spawned, tuning, true) end end SetEntityNoCollisionEntity(cache.ped, spawned, false) TempVehicle = spawned previewVehicle = spawned InGarageMenu = true local stats = GetVehicleStats(spawned) FreezeBoatOnSpawn(spawned) Wait(100) cb(stats) end) RegisterNUICallback("toggle_cinematic_cam", function(active, cb) cb(1) Debug("toggle_cinematic_cam", active) if not IsCameraAvailable then return end if active then IsCameraMoving = true StartIdleCam(TempVehicle) else if menuCamera then DestroyCam(menuCamera, false) menuCamera = nil end menuCamera = CreateCamWithParams("DEFAULT_SCRIPTED_CAMERA", vehicleCamera.camera.coords.x, vehicleCamera.camera.coords.y, vehicleCamera.camera.coords.z, vehicleCamera.camera.rotation.x, vehicleCamera.camera.rotation.y, vehicleCamera.camera.rotation.z, 65.0, false, 0 ) if LocalData.CurrentPlayerCamera then SetCamActiveWithInterp(menuCamera, LocalData.CurrentPlayerCamera, 1000, 100, 50) else SetCamActive(menuCamera, true) end IsCameraMoving = false end end) RegisterNUICallback("driveVehicle", function(data, cb) local hasTimeout = lib.callback.await("advancedgarages:checkTimeout", false) if hasTimeout then return Notification(i18n.t("timeout_expired"), "error") end if Config.PlayerToVehicleAnimation then local garageData = Config.Garages[currentGarage] if garageData and garageData.type == "vehicle" then FreezePlayerAtGarage(TempVehicle) Wait(3500) end end CloseGarageMenu() IsCameraMoving = false inPlayerAnim = false DoScreenFadeOut(150) Wait(500) SetNuiFocus(false, false) if previewVehicle then RemoveVehicle(previewVehicle) previewVehicle = nil end local garageConfig = (data.isJobGarage and Config.JobGarages[currentGarage]) or Config.Garages[currentGarage] local spawnCoords if garageConfig and garageConfig.coords then spawnCoords = garageConfig.coords.spawnCoords else -- Fallback de secours si jamais tout est nil spawnCoords = Config.Garages[currentGarage] and Config.Garages[currentGarage].coords.spawnCoords end if data.isJobGarage then local price = garageConfig.price or 0 if price > 0 then local hasMoney = lib.callback.await("advancedgarages:existMoney", false, price) if not hasMoney then DoScreenFadeIn(1000) return Notification(i18n.t("no_money", { price = price }), "error") end lib.callback.await("advancedgarages:removeMoney", false, price) end local livery = garageConfig.liveries and garageConfig.liveries[data.vehicle.model] local jobVehicle = SpawnVehicle(data.vehicle, spawnCoords) Wait(600) if Config.Vehiclekeys and Config.Vehiclekeys ~= "none" then local modelName = GetDisplayNameFromVehicleModel(GetEntityModel(jobVehicle)) local plate = GetVehicleNumberPlateText(jobVehicle) AddVehiclekeys(modelName, plate, true) end DoScreenFadeIn(1000) TaskWarpPedIntoVehicle(cache.ped, jobVehicle, -1) Wait(600) if livery then SetVehicleLivery(jobVehicle, livery) end local extras = garageConfig.extras and garageConfig.extras[data.vehicle.model] if extras then SetDefaultVehicleExtras(jobVehicle, extras) end local tuning = garageConfig.tuning and garageConfig.tuning[data.vehicle.model] or {} if tuning then SetVehicleProperties(jobVehicle, tuning) end TriggerServerEvent("advancedgarages:takeoutJobVehicle", data.vehicle.plate) return end SpawnVehicleEvents(data.vehicle, data.vehicle.plate) DriveVehicle(data.vehicle, spawnCoords) Wait(600) local vehicle = cache.vehicle local vehicleModel = GetDisplayNameFromVehicleModel(GetEntityModel(vehicle)) local vehiclePlate = GetVehicleNumberPlateText(vehicle) if Config.Vehiclekeys and Config.Vehiclekeys ~= "none" then AddVehiclekeys(vehicleModel, vehiclePlate, true) end if Config.Framework == "qb" then Debug("Vehicle goes to state 0 (OUT)") TriggerServerEvent("advancedgarages:server:updateVehicleState", 0, vehiclePlate) end DoScreenFadeIn(1000) cb(1) end) local function GetVehicleCorners(vehicle) local minDim, maxDim = GetModelDimensions(GetEntityModel(vehicle)) local padding = 0.0 local corners = {} corners[1] = GetOffsetFromEntityInWorldCoords(vehicle, minDim.x - padding, minDim.y - padding, minDim.z - padding) corners[2] = GetOffsetFromEntityInWorldCoords(vehicle, maxDim.x + padding, minDim.y - padding, minDim.z - padding) corners[3] = GetOffsetFromEntityInWorldCoords(vehicle, maxDim.x + padding, maxDim.y + padding, minDim.z - padding) corners[4] = GetOffsetFromEntityInWorldCoords(vehicle, minDim.x - padding, maxDim.y + padding, minDim.z - padding) corners[5] = GetOffsetFromEntityInWorldCoords(vehicle, minDim.x - padding, minDim.y - padding, maxDim.z + padding) corners[6] = GetOffsetFromEntityInWorldCoords(vehicle, maxDim.x + padding, minDim.y - padding, maxDim.z + padding) corners[7] = GetOffsetFromEntityInWorldCoords(vehicle, maxDim.x + padding, maxDim.y + padding, maxDim.z + padding) corners[8] = GetOffsetFromEntityInWorldCoords(vehicle, minDim.x - padding, maxDim.y + padding, maxDim.z + padding) return corners end local function MidPoint(a, b) return vector3((a.x + b.x) / 2.0, (a.y + b.y) / 2.0, (a.z + b.z) / 2.0) end local function OffsetCoords(origin, heading, distance) local angle = math.rad(heading - 90) return vector3( origin.x + distance * math.cos(angle), origin.y + distance * math.sin(angle), origin.z ) end function GetFrontOfVehicle(vehicle, heading) local corners = GetVehicleCorners(vehicle) local midBottom = MidPoint(corners[6], corners[7]) local midFront = MidPoint(corners[1], corners[4]) local center = MidPoint(midBottom, midFront) local vehiclePos = GetEntityCoords(vehicle) local elevated = vector3(vehiclePos.x, vehiclePos.y, center.z + 0.5) local offset = -(#(elevated - midFront)) + (-2.25) return OffsetCoords(elevated, heading, offset) end local currentVehicleRotation = nil RegisterNUICallback("RotateCam", function(data, cb) if not DoesEntityExist(TempVehicle) then return cb("ok") end if not currentVehicleRotation then currentVehicleRotation = GetEntityRotation(TempVehicle) end if not data.x then return cb("ok") end local newZ = currentVehicleRotation.z - data.x currentVehicleRotation = vec3(currentVehicleRotation.x, currentVehicleRotation.y, newZ) local camPos = GetFrontOfVehicle(TempVehicle, newZ) coords = camPos SetCamCoord(menuCamera, camPos.x, camPos.y, camPos.z) SetCamRot(menuCamera, 0, 0, newZ - 180, 2) cb("ok") end) local function GetNearbyJobGarageData() local pedCoords = GetEntityCoords(cache.ped) for _, garageData in ipairs(Config.JobGarages) do local menuCoords = vec3(garageData.coords.menuCoords.x, garageData.coords.menuCoords.y, garageData.coords.menuCoords.z) if #(pedCoords - menuCoords) < 15.0 then return garageData end end return false end RegisterCommand("deleteJobVehicle", function(source, args) local playerJob = GetJobName() local playerGrade = GetJobGrade() local nearbyGarage = GetNearbyJobGarageData() if not nearbyGarage then return Notification(i18n.t("garage_not_found"), "error") end local requiredJob = nearbyGarage.job or nearbyGarage.gang if not requiredJob then return Notification(i18n.t("garage_not_found_job"), "error") end if requiredJob ~= playerJob then return Notification(i18n.t("not_correct_job"), "error") end if playerGrade < nearbyGarage.grade then return Notification(i18n.t("not_correct_grade"), "error") end OpenDeleteJobVehicleMenu(nearbyGarage.name, requiredJob) end, false) RegisterCommand("addJobVehicle", function(source, args) local playerJob = GetJobName() local playerGrade = GetJobGrade() local nearbyGarage = GetNearbyJobGarageData() if not nearbyGarage then return Notification(i18n.t("garage_not_found"), "error") end local requiredJob = nearbyGarage.job or nearbyGarage.gang if not requiredJob then return Notification(i18n.t("garage_not_found_job"), "error") end if requiredJob ~= playerJob then return Notification(i18n.t("not_correct_job"), "error") end if nearbyGarage.grade and playerGrade < nearbyGarage.grade then return Notification(i18n.t("not_correct_grade"), "error") end local vehicle = cache.vehicle if not vehicle then return Notification(i18n.t("not_in_vehicle"), "error") end local plate = GetPlate(vehicle) local isOwned = lib.callback.await("advancedgarages:isVehicleOwned", false, plate) if not isOwned then return Notification(i18n.t("no_vehicle_owner"), "error") end TriggerServerEvent("advancedgarages:addJobVehicle", requiredJob, plate, nearbyGarage) Notification(i18n.t("job_vehicle_added"), "success") RemoveVehicle(vehicle) end, false)