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

644 lines
21 KiB
Lua

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