local spawnedVehicles = {} local preExitCoords = nil currentlyInGarage = nil ShellGarages = {} existKey = nil CurrentShellGarage = nil local function ClearSpawnedVehicles() for _, vehicle in pairs(spawnedVehicles) do RemoveVehicle(vehicle) end spawnedVehicles = {} end CreateThread(function() while "activePlayers" do activePlayers = GetPlayers() Wait(2000) end end) RegisterNetEvent("advancedgarages:SetShellData") AddEventHandler("advancedgarages:SetShellData", function(shellData) currentlyShellData = shellData end) local function GetNextFreeVehicleSlot() if not currentlyShellData or not currentlyShellData.vehicleCoords then return false, 0 end for index, slot in pairs(currentlyShellData.vehicleCoords) do if not slot.vehicle then return slot.coords, index end end return false, 0 end local function DeleteVehicleAtSpawnCoords(coords) for _, vehicle in pairs(GetGamePool("CVehicle")) do local dist = #(GetEntityCoords(vehicle) - vec3(coords.x, coords.y, coords.z)) if dist < 1.0 then Debug("Found vehicle in spawn coords, deleting it") RemoveVehicle(vehicle) end end end local function SpawnShellVehicles(vehicleList) ClearSpawnedVehicles() local playerIdentifier = GetPlayerIdentifier() Debug("Spawning vehicles", json.encode(vehicleList)) if not currentlyShellData or not currentlyShellData.vehicleCoords or #currentlyShellData.vehicleCoords == 0 then return Warning("No vehicle coords found for this garage") end local firstSlotCoords = currentlyShellData.vehicleCoords[1].coords ClearAreaOfVehicles(firstSlotCoords.x, firstSlotCoords.y, firstSlotCoords.z, 1000.0, false, false, false, false, false) for _, vehicleEntry in pairs(vehicleList) do local slotCoords, slotIndex = GetNextFreeVehicleSlot() if not slotCoords then Notification(i18n.t("no_garage_slots"), "error") return end local vehicleData = json.decode(vehicleEntry.vehicle) if not vehicleData or not vehicleData.model then goto continue end DeleteVehicleAtSpawnCoords(slotCoords) local spawnPos = vec4(slotCoords.x, slotCoords.y, slotCoords.z, slotCoords.w) local spawnedVehicle = LocalSpawnVehicle(vehicleData, spawnPos) if vehicleEntry.owner ~= playerIdentifier then SetVehicleDoorsLocked(spawnedVehicle, 2) SetVehicleDoorsLockedForAllPlayers(spawnedVehicle, true) SetVehicleDoorsLockedForPlayer(spawnedVehicle, PlayerId(), true) SetVehicleDoorsLockedForNonScriptPlayers(spawnedVehicle, true) end if vehicleEntry.owner == playerIdentifier and Config.Vehiclekeys and Config.Vehiclekeys ~= "none" then local plate = GetPlate(spawnedVehicle) AddVehiclekeys(spawnedVehicle, plate, false) Debug("Vehiclekeys enabled on [SpawnVehicles], keys were delivered using:", Config.Vehiclekeys, "plate:", plate) end table.insert(spawnedVehicles, spawnedVehicle) if currentlyShellData then currentlyShellData.vehicleCoords[slotIndex].vehicle = spawnedVehicle end ::continue:: end for i, vehicleA in pairs(spawnedVehicles) do for j, vehicleB in pairs(spawnedVehicles) do if i ~= j then SetEntityNoCollisionEntity(vehicleA, vehicleB, false) SetEntityNoCollisionEntity(vehicleB, vehicleA, false) end end end end local function TeleportPlayerToCoords(coords, vehicleData, returnCoords) if not coords then return end local pedPos = GetEntityCoords(cache.ped) local pedHeading = GetEntityHeading(cache.ped) if returnCoords then preExitCoords = vec4(returnCoords.x, returnCoords.y, returnCoords.z, returnCoords.w) else preExitCoords = vec4(pedPos.x, pedPos.y, pedPos.z, pedHeading) end RequestCollisionAtCoord(coords.x, coords.y, coords.z) while not HasCollisionLoadedAroundEntity(cache.ped) do RequestCollisionAtCoord(coords.x, coords.y, coords.z) Wait(0) end DoScreenFadeOut(950) Wait(2000) SetEntityCoords(cache.ped, coords.x, coords.y, coords.z, false, false, false, false) SetEntityHeading(cache.ped, coords.w) if GARAGE_SHELL then DeleteEntity(GARAGE_SHELL) GARAGE_SHELL = nil if RemoveShellExit then RemoveShellExit() end end if not vehicleData then DoScreenFadeIn(3000) return end RemoveVehicle(cache.vehicle) local decodedVehicle = json.decode(vehicleData.vehicle) if not decodedVehicle then DoScreenFadeIn(3000) return end Wait(200) local spawnedVehicle = DriveVehicle(decodedVehicle, coords) if not spawnedVehicle then DoScreenFadeIn(3000) return end Wait(500) local vehicleModel = GetDisplayNameFromVehicleModel(GetEntityModel(cache.vehicle)) local vehiclePlate = GetVehicleNumberPlateText(cache.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(3000) end function ExitGarage(targetPlate) if not currentlyShellData then return Debug("No data found for that garage") end if currentlyShellData and currentlyShellData.vehicleCoords then for _, slot in pairs(currentlyShellData.vehicleCoords) do slot.vehicle = nil end end if CloseGarageMenu then CloseGarageMenu() end decorate:destroyObjects() Wait(500) if not preGarageCoords then local garageConfig = Config.Garages[currentlyInGarage] or Config.JobGarages[currentlyInGarage] if garageConfig then local mc = garageConfig.coords.menuCoords preGarageCoords = vec4(mc.x, mc.y, mc.z, 0.0) else -- Dernier recours : position par défaut sécurisée preGarageCoords = vec4(215.0, -1000.0, -99.0, 0.0) end end if not targetPlate then currentlyInGarage = nil currentlyShellData = nil ClearSpawnedVehicles() TeleportPlayerToCoords(preGarageCoords) return end local vehicleData = lib.callback.await("advancedgarages:GetVehicleFromPlate", false, targetPlate) TeleportPlayerToCoords(preGarageCoords, vehicleData) TriggerServerEvent("advancedgarages:leaveShellgarage", currentlyInGarage) preGarageCoords = nil currentlyInGarage = nil currentlyShellData = nil SetTimeout(1500, function() ClearSpawnedVehicles() end) if not vehicleData then Notification(i18n.t("no_vehicle_data"), "error") return end if Config.Vehiclekeys == "qs-vehiclekeys" then exports["qs-vehiclekeys"]:disableHotwire(false) end end function GotoGarage(garageId, spawnCoords, shellConfig) if not garageId then return Debug("No garage found") end if currentlyInGarage then return Notification(i18n.t("you_are_already_in_garage"), "error") end preGarageCoords = vec4(GetEntityCoords(cache.ped), GetEntityHeading(cache.ped)) if type(shellConfig) ~= "table" then local garageData = Config.Garages[garageId] if garageData then if garageData.shell then shellConfig = garageData.shell end else local shellGarage = ShellGarages[garageId] if shellGarage and shellGarage.shell then shellConfig = shellGarage.shell elseif spawnCoords then SetEntityCoords(cache.ped, spawnCoords.x, spawnCoords.y, spawnCoords.z, false, false, false, false) local heading = spawnCoords.w or 0.0 SetEntityHeading(cache.ped, heading) TriggerServerEvent("advancedgarages:RoutePlayerDefault", garageId) return end end end local interiorType = "ipl" if shellConfig and shellConfig.exitCoords ~= nil then interiorType = "shell" end Debug("You entered the shell id:", json.encode(shellConfig)) customGarageId = shellConfig and shellConfig.shell or 1 if interiorType == "shell" then local shellModelData = Config.Shells[shellConfig.shell] if not shellModelData then return Notification(i18n.t("shell_not_found"), "error") end end TriggerServerEvent("advancedgarages:RoutePlayer", garageId) Wait(150) currentlyInGarage = garageId if not nearbyGarageType then local gData = Config.Garages[garageId] nearbyGarageType = (gData and gData.type) or "vehicle" end if interiorType == "shell" then local shellModelData = Config.Shells[shellConfig.shell] if not shellModelData then Notification(i18n.t("shell_not_found"), "error") end currentlyShellData = { entry = shellConfig.exitCoords, vehicleCoords = shellConfig.vehicleCoords } local shellModel = shellModelData.model local shellObject = CreateShell(shellConfig.coords, shellConfig.exitCoords, shellModel) GARAGE_SHELL = shellObject CreateThread(function() while GARAGE_SHELL do Wait(0) end end) if InitShellExit then InitShellExit(shellConfig.exitCoords) else local exitText = i18n.t("drawtext.back_garage") CreateThread(function() while GARAGE_SHELL do if not shellConfig or not shellConfig.exitCoords then break end local waitTime = 500 local pedPos = GetEntityCoords(cache.ped) local exitCoords = vec3(shellConfig.exitCoords.x, shellConfig.exitCoords.y, shellConfig.exitCoords.z) local distToExit = #(pedPos - exitCoords) if distToExit <= 5 then waitTime = 0 DrawText3D(shellConfig.exitCoords.x, shellConfig.exitCoords.y, shellConfig.exitCoords.z + 0.5, exitText, "exit_garage", "E") -- Touche E (38), ESC (200) ou BACKSPACE (177) pour sortir if IsControlJustPressed(0, 38) or IsControlJustPressed(0, 200) or IsControlJustPressed(0, 177) then ExitGarage() end end Wait(waitTime) end end) end else local showroomData = Config.VehicleShowRooms[nearbyGarageType] currentlyShellData = showroomData[customGarageId] TeleportPlayerToCoords(currentlyShellData.entry, nil, spawnCoords) end TriggerServerEvent("advancedgarages:enterShellgarage", garageId, spawnCoords, customGarageId) local vehicleList = lib.callback.await("advancedgarages:GetShellVehicleList", false, garageId) if not vehicleList then vehicleList = {} end if Config.Vehiclekeys == "qs-vehiclekeys" then exports["qs-vehiclekeys"]:disableHotwire(true) end SpawnShellVehicles(vehicleList) decorate:getObjects(currentlyInGarage) end function GotoShellGarage(garageId, spawnCoords, shellConfig) if not garageId then return end if currentlyInGarage then return Notification(i18n.t("you_are_already_in_garage"), "error") end GotoGarage(garageId, spawnCoords, shellConfig) end RegisterNetEvent("advancedgarages:enterShellGarage") AddEventHandler("advancedgarages:enterShellGarage", GotoShellGarage) AddEventHandler("onResourceStop", function(resourceName) if resourceName ~= GetCurrentResourceName() then return end ClearPedTasks(cache.ped) ClearSpawnedVehicles() end) CreateThread(function() Wait(1251) if Config.Weather and Config.Weather ~= "none" then return end TriggerEvent("advancedgarages:GetWeatherSync", false) end) local function TriggerExitGarage(plate) ExitGarage(plate) end CreateThread(function() local function CheckVehicleExitTrigger(vehicle) if cache.vehicle ~= vehicle then return end if GetEntitySpeed(vehicle) > 1.0 then local plate = GetPlate(vehicle) if lib.callback.await("advancedgarages:checkTimeout", false) then Warning("You can not take out the vehicle, Another player is taking out a vehicle from the garage") return end TriggerExitGarage(plate) Wait(2000) end end while true do local waitTime = 500 if preExitCoords and cache.vehicle then waitTime = 20 for _, vehicle in pairs(spawnedVehicles) do CheckVehicleExitTrigger(vehicle) end end Wait(waitTime) end end) function GetNearbyShellGarage() local pedCoords = GetEntityCoords(cache.ped) for garageId, garageData in pairs(ShellGarages) do local dist = #(pedCoords - vec3(garageData.takeVehicle.x, garageData.takeVehicle.y, garageData.takeVehicle.z)) if dist < 5.0 then return garageId end end return false end local isInfoCardVisible = false local currentInfoVehicle = false function CloseUI() if not isInfoCardVisible then return end isInfoCardVisible = false currentInfoVehicle = false SendReactMessage("toggle_info_card", { visible = false }) end CreateThread(function() local function UpdateInfoCard(vehicle) if not DoesEntityExist(vehicle) or not cache.vehicle then CloseUI() return end local vehicleCoords = GetEntityCoords(vehicle) local pedCoords = GetEntityCoords(cache.ped) local dist = #(vehicleCoords - pedCoords) if dist > 2.5 then if isInfoCardVisible and currentInfoVehicle == vehicle then CloseUI() end return end if IsPauseMenuActive() then if isInfoCardVisible then CloseUI() end return end if isInfoCardVisible then return end local stats = GetVehicleStats(vehicle) if not isInfoCardVisible then isInfoCardVisible = true currentInfoVehicle = vehicle SendReactMessage("toggle_info_card", { visible = true, stats = stats }) end if currentInfoVehicle ~= vehicle then SendReactMessage("toggle_info_card", { visible = true, stats = stats }) Debug("Vehicle changed, sending new stats", "currentlyVisibleVehicle", currentInfoVehicle, "vehicle", vehicle) end currentInfoVehicle = vehicle end while true do local waitTime = 100 if #spawnedVehicles > 0 then for _, vehicle in pairs(spawnedVehicles) do UpdateInfoCard(vehicle) end end Wait(waitTime) end end) nearbyElevator = nil if not Config.UseTarget or Config.UseTarget == "none" or Config.UseTarget == "qb-radialmenu" then local exitText = i18n.t("drawtext.back_garage") CreateThread(function() local function CheckNearbyElevator() local waitTime = 500 if not nearbyGarageType or nearbyGarageType == "boat" then return waitTime end local showrooms = Config.VehicleShowRooms[nearbyGarageType] local pedCoords = GetEntityCoords(cache.ped) for _, showroom in pairs(showrooms) do local dist = #(pedCoords - vec3(showroom.entry.x, showroom.entry.y, showroom.entry.z)) if dist <= 20.0 then waitTime = 0 if dist <= 3.0 then if Config.UseTarget ~= "qb-radialmenu" then DrawText3D(showroom.entry.x, showroom.entry.y, showroom.entry.z + 0.5, exitText, "back_garage", "E") if IsControlJustPressed(0, Keys.E) then ExitGarage() end end end end end return waitTime end nearbyElevator = CheckNearbyElevator while true do local waitTime = nearbyElevator() Wait(waitTime) end end) end CreateThread(function() DoScreenFadeIn(500) end) RegisterNetEvent("advancedgarages:SetShellGarageData") AddEventHandler("advancedgarages:SetShellGarageData", function(garageId, hasKey) Debug("haskey", hasKey) CurrentShellGarage = garageId existKey = hasKey end) RegisterNetEvent("advancedgarages:GetShellGarageData") AddEventHandler("advancedgarages:GetShellGarageData", function(shellGarages) ShellGarages = shellGarages if Config.UseTarget and Config.UseTarget ~= "none" and Config.UseTarget ~= "qb-radialmenu" then InitShellGarages() end end) RegisterNetEvent("advancedgarages:AddShellGarage") AddEventHandler("advancedgarages:AddShellGarage", function(garageId, garageData) Debug("Housing shell added", json.encode(garageData)) ShellGarages[garageId] = garageData if Config.UseTarget and Config.UseTarget ~= "none" and Config.UseTarget ~= "qb-radialmenu" then InitShellGarages() end end) function CreateGarage(garageId, garageData) if not garageId or not garageData then return Warning("Invalid garage data") end if not garageData.coords then return Warning("You must create a coords for garage") end if not garageData.coords.menuCoords or not garageData.coords.spawnCoords then return Warning("Invalid garage data") end if not garageData.type then garageData.type = "vehicle" Warning("Garage type not found, setted to default (vehicle)") end if not garageData.shell then garageData.shell = { shell = 1 } Warning("Garage shell not found, setted to default (1)") end if not garageData.price then garageData.price = 0 Warning("Garage price not found, setted to default (0)") end if not garageData.isImpound then garageData.isImpound = false Warning("Garage isImpound not found, setted to default (false)") end if not garageData.available then garageData.available = true Warning("Garage available not found, setted to default (true)") end if Config.Garages[garageId] then return Error("Garage already exists") end Debug("Creating garage", garageData) TriggerServerEvent("advancedgarages:AddGarage", garageId, garageData) end exports("CreateGarage", CreateGarage) if not Config.UseTarget or Config.UseTarget == "none" or Config.UseTarget == "qb-radialmenu" then local storeText = i18n.t("drawtext.store_vehicle") local enterText = i18n.t("drawtext.enter_garage") CreateThread(function() local function CheckNearbyGarage() local waitTime = 500 if not CurrentShellGarage or not existKey then return waitTime end if not ShellGarages then return waitTime end local shellData = ShellGarages[CurrentShellGarage] if not shellData then return waitTime end local takeVehicleCoords = shellData.takeVehicle local shellId = shellData.shell if not takeVehicleCoords or not takeVehicleCoords.x then return waitTime end local pedCoords = GetEntityCoords(cache.ped) local dist = #(pedCoords - vec3(takeVehicleCoords.x, takeVehicleCoords.y, takeVehicleCoords.z)) if dist > 6.0 then return waitTime end waitTime = 0 if Config.UseTarget ~= "qb-radialmenu" then if cache.vehicle then DrawMarkerZone(takeVehicleCoords.x, takeVehicleCoords.y, takeVehicleCoords.z - 1) DrawText3D(takeVehicleCoords.x, takeVehicleCoords.y, takeVehicleCoords.z + 0.5, storeText, "store_vehicle", "E") if IsControlJustPressed(0, 38) then nearbyGarageType = "vehicle" SaveVehicle(CurrentShellGarage, true) end else DrawText3D(takeVehicleCoords.x, takeVehicleCoords.y, takeVehicleCoords.z + 0.5, enterText, "enter_garage", "E") if IsControlJustPressed(0, 38) then nearbyGarageType = "vehicle" local heading = takeVehicleCoords.h or takeVehicleCoords.w GotoGarage(CurrentShellGarage, vec4(takeVehicleCoords.x, takeVehicleCoords.y, takeVehicleCoords.z, heading), shellId) end end end return waitTime end CheckNearbyGarage = CheckNearbyGarage while true do local waitTime = CheckNearbyGarage() Wait(waitTime) end end) end