forked from Simnation/Main
ed
This commit is contained in:
parent
6d22d5f77c
commit
63fbc60a00
86 changed files with 8352 additions and 3428 deletions
488
resources/[Developer]/mh_Parking/cl_utils.lua
Normal file
488
resources/[Developer]/mh_Parking/cl_utils.lua
Normal file
|
@ -0,0 +1,488 @@
|
|||
-- logs text to the console
|
||||
function Log(text)
|
||||
if (Config.isDebug) then
|
||||
Log(text)
|
||||
end
|
||||
end
|
||||
|
||||
-- Return all vehicle modifications in an array
|
||||
function GetVehicleModifications(vehicle)
|
||||
local color1, color2 = GetVehicleColours(vehicle)
|
||||
local pearlescentColor, wheelColor = GetVehicleExtraColours(vehicle)
|
||||
|
||||
local extras = {}
|
||||
for i = 0, 20, 1 do
|
||||
if (DoesExtraExist(vehicle, i)) then
|
||||
if (IsVehicleExtraTurnedOn(vehicle, i)) then
|
||||
table.insert(extras, { i, 0 })
|
||||
else
|
||||
table.insert(extras, { i, 1 })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local tiresBurst = {}
|
||||
for i = 0, 5, 1 do
|
||||
if (IsVehicleTyreBurst(vehicle, i, true)) then
|
||||
table.insert(tiresBurst, { i, true })
|
||||
elseif (IsVehicleTyreBurst(vehicle, i, false)) then
|
||||
table.insert(tiresBurst, { i, false })
|
||||
end
|
||||
end
|
||||
|
||||
local doorsMissing = {}
|
||||
for i = 0, 7, 1 do
|
||||
if (IsVehicleDoorDamaged(vehicle, i)) then
|
||||
table.insert(doorsMissing, i)
|
||||
end
|
||||
end
|
||||
|
||||
local windowsBroken = {}
|
||||
--for i = 0, 13, 1 do
|
||||
-- if (not IsVehicleWindowIntact(vehicle, i)) then
|
||||
-- table.insert(windowsBroken, i)
|
||||
-- end
|
||||
--end
|
||||
|
||||
-- custom colors
|
||||
local hasCustomPrimaryColor = GetIsVehiclePrimaryColourCustom(vehicle)
|
||||
local customPrimaryColor = nil
|
||||
if (hasCustomPrimaryColor) then
|
||||
local r, g, b = GetVehicleCustomPrimaryColour(vehicle)
|
||||
customPrimaryColor = { r, g, b }
|
||||
end
|
||||
|
||||
local hasCustomSecondaryColor = GetIsVehicleSecondaryColourCustom(vehicle)
|
||||
local customSecondaryColor = nil
|
||||
if (hasCustomSecondaryColor) then
|
||||
local r, g, b = GetVehicleCustomSecondaryColour(vehicle)
|
||||
customSecondaryColor = { r, g, b }
|
||||
end
|
||||
|
||||
return {
|
||||
-- 1 model
|
||||
GetEntityModel(vehicle),
|
||||
|
||||
-- 2 lockStatus
|
||||
GetVehicleDoorLockStatus(vehicle),
|
||||
|
||||
-- 3 health
|
||||
math.floor(GetEntityHealth(vehicle) * 10.0) / 10.0,
|
||||
-- 4 bodyHealth
|
||||
math.floor(GetVehicleBodyHealth(vehicle) * 10.0) / 10.0,
|
||||
-- 5 engineHealth
|
||||
math.floor(GetVehicleEngineHealth(vehicle) * 10.0) / 10.0,
|
||||
-- 6 petrolTankHealth
|
||||
math.floor(GetVehiclePetrolTankHealth(vehicle) * 10.0) / 10.0,
|
||||
|
||||
-- 7 dirtLevel
|
||||
math.floor(GetVehicleDirtLevel(vehicle) * 10.0) / 10.0,
|
||||
-- 8 fuelLevel
|
||||
math.floor(exports[Config.exports]:GetFuel(vehicle) * 10.0) / 10.0,
|
||||
|
||||
---------------------------------------------------------------------- Implementierung Miho
|
||||
--math.floor(DecorGetFloat(vehicle, Config.FuelDecor)*10) / 10.0,
|
||||
---------------------------------------------------------------------- Implementierung Miho ENDE
|
||||
|
||||
-- 9 plateIndex
|
||||
GetVehicleNumberPlateTextIndex(vehicle),
|
||||
|
||||
-- 10 primaryColor
|
||||
color1,
|
||||
-- 11 secondaryColor
|
||||
color2,
|
||||
-- 12 pearlescentColor
|
||||
pearlescentColor,
|
||||
-- 13 wheelColor
|
||||
wheelColor,
|
||||
|
||||
-- 14 wheelType
|
||||
GetVehicleWheelType(vehicle),
|
||||
-- 15 customWheelsFront
|
||||
GetVehicleModVariation(vehicle, 23);
|
||||
-- 16 customWheelsBack
|
||||
GetVehicleModVariation(vehicle, 24);
|
||||
-- 17 windowTint
|
||||
GetVehicleWindowTint(vehicle),
|
||||
-- 18 enabledNeon
|
||||
{
|
||||
IsVehicleNeonLightEnabled(vehicle, 0),
|
||||
IsVehicleNeonLightEnabled(vehicle, 1),
|
||||
IsVehicleNeonLightEnabled(vehicle, 2),
|
||||
IsVehicleNeonLightEnabled(vehicle, 3),
|
||||
},
|
||||
-- 19 neonColor
|
||||
table.pack(GetVehicleNeonLightsColour(vehicle)),
|
||||
-- 20 tireSmokeColor
|
||||
table.pack(GetVehicleTyreSmokeColor(vehicle)),
|
||||
|
||||
-- 21 extras
|
||||
extras,
|
||||
|
||||
-- 22-32 mods
|
||||
GetVehicleMod(vehicle, 0),
|
||||
GetVehicleMod(vehicle, 1),
|
||||
GetVehicleMod(vehicle, 2),
|
||||
GetVehicleMod(vehicle, 3),
|
||||
GetVehicleMod(vehicle, 4),
|
||||
GetVehicleMod(vehicle, 5),
|
||||
GetVehicleMod(vehicle, 6),
|
||||
GetVehicleMod(vehicle, 7),
|
||||
GetVehicleMod(vehicle, 8),
|
||||
GetVehicleMod(vehicle, 9),
|
||||
GetVehicleMod(vehicle, 10),
|
||||
|
||||
-- 33-38 mods
|
||||
GetVehicleMod(vehicle, 11),
|
||||
GetVehicleMod(vehicle, 12),
|
||||
GetVehicleMod(vehicle, 13),
|
||||
GetVehicleMod(vehicle, 14),
|
||||
GetVehicleMod(vehicle, 15),
|
||||
GetVehicleMod(vehicle, 16),
|
||||
|
||||
-- 39-41 mods
|
||||
IsToggleModOn(vehicle, 18),
|
||||
IsToggleModOn(vehicle, 20),
|
||||
IsToggleModOn(vehicle, 22),
|
||||
|
||||
-- 42-43 mods
|
||||
GetVehicleMod(vehicle, 23),
|
||||
GetVehicleMod(vehicle, 24),
|
||||
|
||||
-- 44-66 mods
|
||||
GetVehicleMod(vehicle, 25),
|
||||
GetVehicleMod(vehicle, 26),
|
||||
GetVehicleMod(vehicle, 27),
|
||||
GetVehicleMod(vehicle, 28),
|
||||
GetVehicleMod(vehicle, 29),
|
||||
GetVehicleMod(vehicle, 30),
|
||||
GetVehicleMod(vehicle, 31),
|
||||
GetVehicleMod(vehicle, 32),
|
||||
GetVehicleMod(vehicle, 33),
|
||||
GetVehicleMod(vehicle, 34),
|
||||
GetVehicleMod(vehicle, 35),
|
||||
GetVehicleMod(vehicle, 36),
|
||||
GetVehicleMod(vehicle, 37),
|
||||
GetVehicleMod(vehicle, 38),
|
||||
GetVehicleMod(vehicle, 39),
|
||||
GetVehicleMod(vehicle, 40),
|
||||
GetVehicleMod(vehicle, 41),
|
||||
GetVehicleMod(vehicle, 42),
|
||||
GetVehicleMod(vehicle, 43),
|
||||
GetVehicleMod(vehicle, 44),
|
||||
GetVehicleMod(vehicle, 45),
|
||||
GetVehicleMod(vehicle, 46),
|
||||
GetVehicleMod(vehicle, 48),
|
||||
|
||||
-- 67 livery
|
||||
GetVehicleLivery(vehicle),
|
||||
|
||||
-- 68 missingDoors
|
||||
doorsMissing,
|
||||
|
||||
-- 69 bulletproofTires
|
||||
not GetVehicleTyresCanBurst(vehicle),
|
||||
-- 70 tiresBurst
|
||||
tiresBurst,
|
||||
|
||||
-- 71 brokenWindows
|
||||
windowsBroken,
|
||||
|
||||
-- 72 xenon lights
|
||||
GetVehicleXenonLightsColour(vehicle),
|
||||
|
||||
-- 73 custom primary color
|
||||
customPrimaryColor,
|
||||
-- 74 custom secondary color
|
||||
customSecondaryColor,
|
||||
|
||||
-- 75 interior color
|
||||
GetVehicleInteriorColor(vehicle)
|
||||
}
|
||||
end
|
||||
|
||||
-- Apply all modifications to a vehicle entity
|
||||
function SetVehicleModifications(vehicle, plate, modifications)
|
||||
SetVehicleModKit(vehicle, 0)
|
||||
|
||||
-- plate
|
||||
SetVehicleNumberPlateText(vehicle, plate)
|
||||
SetVehicleNumberPlateTextIndex(vehicle, modifications[9])
|
||||
|
||||
-- lockStatus
|
||||
SetVehicleDoorsLocked(vehicle, modifications[2])
|
||||
|
||||
-- colours
|
||||
SetVehicleColours(vehicle, modifications[10], modifications[11])
|
||||
if (modifications[73]) then
|
||||
SetVehicleCustomPrimaryColour(vehicle, modifications[73][1], modifications[73][2], modifications[73][3])
|
||||
end
|
||||
if (modifications[74]) then
|
||||
SetVehicleCustomSecondaryColour(vehicle, modifications[74][1], modifications[74][2], modifications[74][3])
|
||||
end
|
||||
if (modifications[75]) then
|
||||
SetVehicleInteriorColor(vehicle, modifications[75])
|
||||
end
|
||||
|
||||
SetVehicleExtraColours(vehicle, modifications[12], modifications[13])
|
||||
|
||||
SetVehicleTyreSmokeColor(vehicle, modifications[20][1], modifications[20][2], modifications[20][3])
|
||||
|
||||
-- wheels
|
||||
SetVehicleWheelType(vehicle, modifications[14])
|
||||
|
||||
-- windows
|
||||
SetVehicleWindowTint(vehicle, modifications[17])
|
||||
|
||||
-- neonlight
|
||||
SetVehicleNeonLightEnabled(vehicle, 0, modifications[18][1])
|
||||
SetVehicleNeonLightEnabled(vehicle, 1, modifications[18][2])
|
||||
SetVehicleNeonLightEnabled(vehicle, 2, modifications[18][3])
|
||||
SetVehicleNeonLightEnabled(vehicle, 3, modifications[18][4])
|
||||
|
||||
SetVehicleNeonLightsColour(vehicle, modifications[19][1], modifications[19][2], modifications[19][3])
|
||||
|
||||
-- mods
|
||||
SetVehicleMod(vehicle, 0, modifications[22], modifications[15])
|
||||
SetVehicleMod(vehicle, 1, modifications[23], modifications[15])
|
||||
SetVehicleMod(vehicle, 2, modifications[24], modifications[15])
|
||||
SetVehicleMod(vehicle, 3, modifications[25], modifications[15])
|
||||
SetVehicleMod(vehicle, 4, modifications[26], modifications[15])
|
||||
SetVehicleMod(vehicle, 5, modifications[27], modifications[15])
|
||||
SetVehicleMod(vehicle, 6, modifications[28], modifications[15])
|
||||
SetVehicleMod(vehicle, 7, modifications[29], modifications[15])
|
||||
SetVehicleMod(vehicle, 8, modifications[30], modifications[15])
|
||||
SetVehicleMod(vehicle, 9, modifications[31], modifications[15])
|
||||
SetVehicleMod(vehicle, 10, modifications[32], modifications[15])
|
||||
|
||||
SetVehicleMod(vehicle, 11, modifications[33], modifications[15])
|
||||
SetVehicleMod(vehicle, 12, modifications[34], modifications[15])
|
||||
SetVehicleMod(vehicle, 13, modifications[35], modifications[15])
|
||||
|
||||
SetVehicleMod(vehicle, 14, modifications[36], modifications[15])
|
||||
|
||||
SetVehicleMod(vehicle, 15, modifications[37], modifications[15])
|
||||
SetVehicleMod(vehicle, 16, modifications[38], modifications[15])
|
||||
|
||||
ToggleVehicleMod(vehicle, 18, modifications[39])
|
||||
ToggleVehicleMod(vehicle, 20, modifications[40])
|
||||
ToggleVehicleMod(vehicle, 22, modifications[41])
|
||||
|
||||
SetVehicleMod(vehicle, 23, modifications[42], modifications[15])
|
||||
SetVehicleMod(vehicle, 24, modifications[43], modifications[16])
|
||||
|
||||
SetVehicleMod(vehicle, 25, modifications[44], modifications[15])
|
||||
SetVehicleMod(vehicle, 26, modifications[45], modifications[15])
|
||||
SetVehicleMod(vehicle, 27, modifications[46], modifications[15])
|
||||
SetVehicleMod(vehicle, 28, modifications[47], modifications[15])
|
||||
SetVehicleMod(vehicle, 29, modifications[48], modifications[15])
|
||||
SetVehicleMod(vehicle, 30, modifications[49], modifications[15])
|
||||
SetVehicleMod(vehicle, 31, modifications[50], modifications[15])
|
||||
SetVehicleMod(vehicle, 32, modifications[51], modifications[15])
|
||||
SetVehicleMod(vehicle, 33, modifications[52], modifications[15])
|
||||
SetVehicleMod(vehicle, 34, modifications[53], modifications[15])
|
||||
SetVehicleMod(vehicle, 35, modifications[54], modifications[15])
|
||||
SetVehicleMod(vehicle, 36, modifications[55], modifications[15])
|
||||
SetVehicleMod(vehicle, 37, modifications[56], modifications[15])
|
||||
SetVehicleMod(vehicle, 38, modifications[57], modifications[15])
|
||||
SetVehicleMod(vehicle, 39, modifications[58], modifications[15])
|
||||
SetVehicleMod(vehicle, 40, modifications[59], modifications[15])
|
||||
SetVehicleMod(vehicle, 41, modifications[60], modifications[15])
|
||||
SetVehicleMod(vehicle, 42, modifications[61], modifications[15])
|
||||
SetVehicleMod(vehicle, 43, modifications[62], modifications[15])
|
||||
SetVehicleMod(vehicle, 44, modifications[63], modifications[15])
|
||||
SetVehicleMod(vehicle, 45, modifications[64], modifications[15])
|
||||
SetVehicleMod(vehicle, 46, modifications[65], modifications[15])
|
||||
SetVehicleMod(vehicle, 48, modifications[66], modifications[15])
|
||||
|
||||
SetVehicleLivery(vehicle, modifications[67])
|
||||
|
||||
-- extras
|
||||
for i = 1, #modifications[21], 1 do
|
||||
SetVehicleExtra(vehicle, modifications[21][i][1], modifications[21][i][2])
|
||||
end
|
||||
|
||||
-- stats
|
||||
SetEntityHealth(vehicle, modifications[3])
|
||||
SetVehicleBodyHealth(vehicle, modifications[4])
|
||||
SetVehicleEngineHealth(vehicle, modifications[5])
|
||||
SetVehiclePetrolTankHealth(vehicle, modifications[6])
|
||||
if (Config.renderScorched and (modifications[5] < -3999.0 or modifications[6] < -999.0)) then
|
||||
TriggerServerEvent("mh_Parking:renderScorched", NetworkGetNetworkIdFromEntity(vehicle), true)
|
||||
end
|
||||
|
||||
SetVehicleDirtLevel(vehicle, modifications[7])
|
||||
exports[Config.exports]:SetFuel(vehicle, modifications[8])
|
||||
|
||||
---------------------------------------------------------------------- Implementierung Miho
|
||||
--[[
|
||||
fuel = modifications[8]
|
||||
if type(fuel) == 'number' and fuel >= 0 and fuel <= 100 then
|
||||
SetVehicleFuelLevel(vehicle, fuel + 0.0)
|
||||
DecorSetFloat(vehicle, Config.FuelDecor, GetVehicleFuelLevel(vehicle))
|
||||
end
|
||||
]]
|
||||
---------------------------------------------------------------------- Implementierung Miho ENDE
|
||||
|
||||
|
||||
-- doors
|
||||
for i = 1, #modifications[68], 1 do
|
||||
SetVehicleDoorBroken(vehicle, modifications[68][i], true)
|
||||
end
|
||||
|
||||
-- tires
|
||||
SetVehicleTyresCanBurst(vehicle, not modifications[69])
|
||||
if (not modifications[69]) then
|
||||
for i = 1, #modifications[70], 1 do
|
||||
SetVehicleTyreBurst(vehicle, modifications[70][i][1], modifications[70][i][2], 1000.0)
|
||||
end
|
||||
end
|
||||
|
||||
-- windows
|
||||
for i = 1, #modifications[71], 1 do
|
||||
SmashVehicleWindow(vehicle, modifications[71][i])
|
||||
end
|
||||
|
||||
-- xenon lights
|
||||
if (modifications[72]) then
|
||||
SetVehicleXenonLightsColour(vehicle, modifications[72])
|
||||
end
|
||||
end
|
||||
|
||||
-- Return closest loaded vehicle entity or nil if no vehicle is found
|
||||
function GetClosestVehicle(position, maxRadius)
|
||||
local vehicles = GetAllVehicles()
|
||||
local dist = maxRadius
|
||||
local closestVehicle = nil
|
||||
|
||||
for i=1, #vehicles, 1 do
|
||||
local vehicleCoords = GetEntityCoords(vehicles[i])
|
||||
local tempDist = Vdist(vehicleCoords.x, vehicleCoords.y, vehicleCoords.z, position.x, position.y, position.z)
|
||||
if (tempDist < dist) then
|
||||
dist = tempDist
|
||||
closestVehicle = vehicles[i]
|
||||
end
|
||||
end
|
||||
|
||||
if (closestVehicle ~= nil and DoesEntityExist(closestVehicle)) then
|
||||
return closestVehicle
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Returns all loaded vehicles on client side
|
||||
function GetAllVehicles()
|
||||
local vehicles = {}
|
||||
|
||||
for vehicle in EnumerateVehicles() do
|
||||
table.insert(vehicles, vehicle)
|
||||
end
|
||||
|
||||
return vehicles
|
||||
end
|
||||
|
||||
function IsVehicleBlacklisted(vehicle)
|
||||
-- check class
|
||||
local class = GetVehicleClass(vehicle)
|
||||
for i = 1, #Config.classesBlacklist, 1 do
|
||||
if (class == Config.classesBlacklist[i]) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- check model
|
||||
local modelHash = GetEntityModel(vehicle)
|
||||
for i = 1, #Config.vehiclesBlacklist, 1 do
|
||||
if (modelHash == Config.vehiclesBlacklist[i]) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function GetVehiclePosition(plate)
|
||||
if (GetResourceState("kimi_callbacks") ~= "started") then
|
||||
Log("^1[ERROR] \"kimi_callbacks\" is either not installed properly or hasn't been started!")
|
||||
Log("^1[ERROR] The export \"GetVehiclePosition\" can only be used when having this resource installed.")
|
||||
return nil
|
||||
end
|
||||
|
||||
if (plate == nil) then
|
||||
Log("^1[ERROR] \"plate\" was nil while trying to call export \"GetVehiclePosition\"!")
|
||||
return nil
|
||||
end
|
||||
|
||||
if (type(plate) ~= "string") then
|
||||
Log("^1[ERROR] \"plate\" (\"" .. plate .. "\") needs to be of type string when calling export \"GetVehiclePosition\"!")
|
||||
return nil
|
||||
end
|
||||
|
||||
local position = exports["kimi_callbacks"]:Trigger("AP:getVehiclePosition", plate:upper())
|
||||
|
||||
return position
|
||||
end
|
||||
|
||||
function GetVehiclePositions(...)
|
||||
if (GetResourceState("kimi_callbacks") ~= "started") then
|
||||
Log("^1[ERROR] \"kimi_callbacks\" is either not installed properly or hasn't been started!")
|
||||
Log("^1[ERROR] The export \"GetVehiclePositions\" can only be used when having this resource installed.")
|
||||
return nil
|
||||
end
|
||||
|
||||
local plates = { ... }
|
||||
|
||||
for i, plate in ipairs(plates) do
|
||||
if (plate == nil) then
|
||||
Log("^1[ERROR] \"plate\" (Index: \"" .. i .. "\") was nil while trying to call export \"GetVehiclePositions\"!")
|
||||
return nil
|
||||
end
|
||||
|
||||
if (type(plate) ~= "string") then
|
||||
Log("^1[ERROR] \"plate\" (\"" .. plate .. "\") (Index: " .. i .. ") needs to be of type string when calling export \"GetVehiclePositions\"!")
|
||||
return nil
|
||||
end
|
||||
|
||||
plates[i] = plates[i]:upper()
|
||||
end
|
||||
|
||||
local data = exports["kimi_callbacks"]:Trigger("AP:getVehiclePositions", plates)
|
||||
|
||||
return data
|
||||
end
|
||||
|
||||
-- getting all vehicles
|
||||
function EnumerateVehicles()
|
||||
return EnumerateEntities(FindFirstVehicle, FindNextVehicle, EndFindVehicle)
|
||||
end
|
||||
function EnumerateEntities(initFunc, moveFunc, disposeFunc)
|
||||
return coroutine.wrap(function()
|
||||
local iter, id = initFunc()
|
||||
if not id or id == 0 then
|
||||
disposeFunc(iter)
|
||||
return
|
||||
end
|
||||
|
||||
local enum = {handle = iter, destructor = disposeFunc}
|
||||
setmetatable(enum, entityEnumerator)
|
||||
|
||||
local next = true
|
||||
repeat
|
||||
coroutine.yield(id)
|
||||
next, id = moveFunc(iter)
|
||||
until not next
|
||||
|
||||
enum.destructor, enum.handle = nil, nil
|
||||
disposeFunc(iter)
|
||||
end)
|
||||
end
|
||||
local entityEnumerator = {
|
||||
__gc = function(enum)
|
||||
if enum.destructor and enum.handle then
|
||||
enum.destructor(enum.handle)
|
||||
end
|
||||
enum.destructor = nil
|
||||
enum.handle = nil
|
||||
end
|
||||
}
|
210
resources/[Developer]/mh_Parking/client.lua
Normal file
210
resources/[Developer]/mh_Parking/client.lua
Normal file
|
@ -0,0 +1,210 @@
|
|||
local enabled = true
|
||||
|
||||
Citizen.CreateThread(function()
|
||||
while (true) do
|
||||
local playerPed = PlayerPedId()
|
||||
if (DoesEntityExist(playerPed)) then
|
||||
TriggerServerEvent("mh_Parking:syncPlayerPosition", GetEntityCoords(playerPed))
|
||||
end
|
||||
|
||||
Citizen.Wait(3000)
|
||||
end
|
||||
end)
|
||||
|
||||
Citizen.CreateThread(function()
|
||||
while (true) do
|
||||
Citizen.Wait(5 * 60 * 1000)
|
||||
|
||||
local vehicle = GetVehiclePedIsIn(PlayerPedId())
|
||||
if (DoesEntityExist(vehicle)) then
|
||||
TriggerEvent("mh_Parking:updateVehicle", vehicle)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
Citizen.CreateThread(function()
|
||||
Citizen.Wait(3000)
|
||||
|
||||
local isInVehicle = false
|
||||
local currentVehiclePlate = nil
|
||||
local wasDead = false
|
||||
|
||||
while (true) do
|
||||
Citizen.Wait(50)
|
||||
|
||||
local playerPed = PlayerPedId()
|
||||
|
||||
if (not isInVehicle and IsPedInAnyVehicle(playerPed)) then
|
||||
-- entered vehicle
|
||||
|
||||
isInVehicle = true
|
||||
|
||||
if (enabled) then
|
||||
local vehicle = GetVehiclePedIsIn(playerPed, false)
|
||||
if (NetworkGetEntityIsNetworked(vehicle) and not IsVehicleBlacklisted(vehicle)) then
|
||||
local networkId = NetworkGetNetworkIdFromEntity(vehicle)
|
||||
SetNetworkIdCanMigrate(netId, true)
|
||||
local modifications = GetVehicleModifications(vehicle)
|
||||
|
||||
currentVehiclePlate = GetVehicleNumberPlateText(vehicle)
|
||||
|
||||
TriggerServerEvent("mh_Parking:enteredVehicle", networkId, currentVehiclePlate, modifications)
|
||||
|
||||
Log("Vehicle " .. currentVehiclePlate .. " entered")
|
||||
end
|
||||
end
|
||||
elseif (isInVehicle and not IsPedInAnyVehicle(playerPed, false)) then
|
||||
-- left vehicle
|
||||
|
||||
isInVehicle = false
|
||||
|
||||
if (enabled and not wasDead) then
|
||||
local vehicle = GetVehiclePedIsIn(playerPed, true)
|
||||
|
||||
if (vehicle ~= 0) then
|
||||
if (NetworkGetEntityIsNetworked(vehicle) and not IsVehicleBlacklisted(vehicle)) then
|
||||
local networkId = NetworkGetNetworkIdFromEntity(vehicle)
|
||||
SetNetworkIdCanMigrate(netId, true)
|
||||
local modifications = GetVehicleModifications(vehicle)
|
||||
|
||||
TriggerServerEvent("mh_Parking:leftVehicle", networkId, modifications)
|
||||
|
||||
Log("Vehicle " .. GetVehicleNumberPlateText(vehicle) .. " left")
|
||||
|
||||
-- if vehicle is deleted soon after
|
||||
Citizen.CreateThread(function()
|
||||
local veh = vehicle
|
||||
local plate = GetVehicleNumberPlateText(veh)
|
||||
local startTime = GetGameTimer()
|
||||
local playerPed = PlayerPedId()
|
||||
local playerPos
|
||||
|
||||
while (true) do
|
||||
Citizen.Wait(0)
|
||||
|
||||
if (GetGameTimer() - startTime >= 1000) then
|
||||
break
|
||||
end
|
||||
|
||||
if (not DoesEntityExist(veh)) then
|
||||
TriggerServerEvent("mh_Parking:deleteVehicle", plate, false)
|
||||
|
||||
Log("Vehicle " .. currentVehiclePlate .. " left and deleted after exit")
|
||||
|
||||
currentVehiclePlate = nil
|
||||
break
|
||||
end
|
||||
|
||||
playerPed = PlayerPedId()
|
||||
playerPos = GetEntityCoords(playerPed)
|
||||
vehPos = GetEntityCoords(veh)
|
||||
|
||||
if (Vdist(playerPos.x, playerPos.y, playerPos.z, vehPos.x, vehPos.y, vehPos.z) > 50.0) then
|
||||
currentVehiclePlate = nil
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
currentVehiclePlate = nil
|
||||
end)
|
||||
end
|
||||
elseif (currentVehiclePlate) then
|
||||
TriggerServerEvent("mh_Parking:deleteVehicle", currentVehiclePlate, false)
|
||||
|
||||
Log("Vehicle " .. currentVehiclePlate .. " left and deleted")
|
||||
|
||||
currentVehiclePlate = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (not wasDead and IsPlayerDead(PlayerId())) then
|
||||
wasDead = true
|
||||
elseif (wasDead and not IsPlayerDead(PlayerId())) then
|
||||
Citizen.CreateThread(function()
|
||||
Citizen.Wait(1000)
|
||||
|
||||
wasDead = false
|
||||
end)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterNetEvent("mh_Parking:setVehicleMods")
|
||||
AddEventHandler("mh_Parking:setVehicleMods", function(netId, plate, modifications)
|
||||
if (not IsModelInCdimage(modifications[1])) then
|
||||
Log("Setting mods for " .. plate .. " failed. The model does NOT exist.")
|
||||
TriggerServerEvent("mh_Parking:modelDoesNotExist", plate)
|
||||
return
|
||||
end
|
||||
|
||||
local timer = GetGameTimer()
|
||||
while (not NetworkDoesEntityExistWithNetworkId(netId)) do
|
||||
Citizen.Wait(0)
|
||||
|
||||
if (GetGameTimer() - 10000 > timer) then
|
||||
Log("Setting mods for " .. plate .. " failed after 10s")
|
||||
TriggerServerEvent("mh_Parking:setVehicleModsFailed", plate)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local vehicle = NetworkGetEntityFromNetworkId(netId)
|
||||
|
||||
if (DoesEntityExist(vehicle) and NetworkHasControlOfEntity(vehicle)) then
|
||||
Log("Setting modifications for vehicle " .. plate)
|
||||
|
||||
SetVehicleModifications(vehicle, plate, modifications)
|
||||
TriggerServerEvent("mh_Parking:setVehicleModsSuccess", plate)
|
||||
else
|
||||
Log("Setting mods failed for vehicle " .. plate .. ". Vehicle does not exist")
|
||||
TriggerServerEvent("mh_Parking:setVehicleModsFailed", plate)
|
||||
end
|
||||
end)
|
||||
|
||||
AddEventHandler("mh_Parking:updateVehicle", function(vehicle)
|
||||
if (vehicle == nil) then
|
||||
Log("^1[ERROR] \"vehicle\" was nil while trying to update a vehicle!")
|
||||
return
|
||||
end
|
||||
|
||||
if (DoesEntityExist(vehicle) and NetworkGetEntityIsNetworked(vehicle) and not IsVehicleBlacklisted(vehicle)) then
|
||||
Log("Triggering manual update of vehicle")
|
||||
|
||||
local networkId = NetworkGetNetworkIdFromEntity(vehicle)
|
||||
local modifications = GetVehicleModifications(vehicle)
|
||||
|
||||
TriggerServerEvent("mh_Parking:updateVehicle", networkId, modifications)
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterNetEvent("mh_Parking:enable")
|
||||
AddEventHandler("mh_Parking:enable", function(enable)
|
||||
if (enable == nil) then
|
||||
Log("^1[ERROR] \"enable\" was nil while trying to enable/disable script!")
|
||||
return
|
||||
end
|
||||
|
||||
enabled = enable
|
||||
|
||||
if (enabled) then
|
||||
Log("AdvancedParking enabled")
|
||||
else
|
||||
Log("AdvancedParking disabled")
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterNetEvent("mh_Parking:notification")
|
||||
AddEventHandler("mh_Parking:notification", function(msg)
|
||||
SetNotificationTextEntry('STRING')
|
||||
AddTextComponentSubstringPlayerName(msg)
|
||||
DrawNotification(false, true)
|
||||
end)
|
||||
|
||||
RegisterNetEvent("mh_Parking:renderScorched")
|
||||
AddEventHandler("mh_Parking:renderScorched", function(vehicleNetId, scorched)
|
||||
local vehicle = NetworkGetEntityFromNetworkId(vehicleNetId)
|
||||
if (DoesEntityExist(vehicle)) then
|
||||
SetEntityRenderScorched(vehicle, scorched)
|
||||
end
|
||||
end)
|
64
resources/[Developer]/mh_Parking/config.lua
Normal file
64
resources/[Developer]/mh_Parking/config.lua
Normal file
|
@ -0,0 +1,64 @@
|
|||
Config = {}
|
||||
|
||||
Config.exports = "lc_fuel" -- LegacyFuel
|
||||
|
||||
-- set this to true if you want to see debug messages on client and server side
|
||||
Config.isDebug = false
|
||||
|
||||
-- this controls the distance at which vehicles will spawn to the closest player
|
||||
-- (in meters)
|
||||
Config.spawnDistance = 200.0
|
||||
|
||||
-- this controls when a vehicle will be removed from the database table when
|
||||
-- calling the cleanup function (in hours; so 24 * 7 = one week)
|
||||
Config.cleanUpThresholdTime = 24 * 7
|
||||
|
||||
-- set this to false if you do not want entities render as scorched when they
|
||||
-- are completely broken
|
||||
Config.renderScorched = true
|
||||
|
||||
-- vehicle classes that you do not want to save go here (remove the -- in front
|
||||
-- of the number if you want it blacklisted)
|
||||
Config.classesBlacklist = {
|
||||
-- 0, -- Compacts
|
||||
-- 1, -- Sedans
|
||||
-- 2, -- SUVs
|
||||
-- 3, -- Coupes
|
||||
-- 4, -- Muscle
|
||||
-- 5, -- Sports Classics
|
||||
-- 6, -- Sports
|
||||
-- 7, -- Super
|
||||
-- 8, -- Motorcycles
|
||||
-- 9, -- Off-road
|
||||
--10, -- Industrial
|
||||
--11, -- Utility
|
||||
--12, -- Vans
|
||||
--13, -- Cycles
|
||||
--14, -- Boats
|
||||
--15, -- Helicopters
|
||||
--16, -- Planes
|
||||
--17, -- Service
|
||||
--18, -- Emergency
|
||||
--19, -- Military
|
||||
--20, -- Commercial
|
||||
21, -- Trains
|
||||
}
|
||||
-- other vehicles that you do not want to save can be inserted here (use `MODELNAME`
|
||||
-- when you put them in there)
|
||||
Config.vehiclesBlacklist = {
|
||||
--`blista`,
|
||||
}
|
||||
|
||||
-- this lets you control if vehicles should be deleted (in minutes; 0 if you do not
|
||||
-- want to use it; this is useful for large servers with a lot of players)
|
||||
Config.deleteTimer = 0
|
||||
-- despawns all vehicles that are more than x meters away from a player
|
||||
Config.deleteDistance = 25.0
|
||||
-- when the notifications should be shown before the despawning
|
||||
-- needs to be in descending order in minutes and lower than Config.deleteTimer
|
||||
Config.deleteNotificationTimes = { 5, 3, 2, 1 }
|
||||
-- notification to show players before deleting vehicles
|
||||
-- (use %s as placeholder for time left in minutes)
|
||||
Config.timeLeftNotification = "Fahrzeuge Despawn in %s Minuten."
|
||||
-- notification to show players when deleting vehicles
|
||||
Config.deleteNotification = "Lösche Fahrzeuge..."
|
24
resources/[Developer]/mh_Parking/fxmanifest.lua
Normal file
24
resources/[Developer]/mh_Parking/fxmanifest.lua
Normal file
|
@ -0,0 +1,24 @@
|
|||
fx_version 'cerulean'
|
||||
games { 'gta5' }
|
||||
|
||||
author 'Mîhó'
|
||||
description 'Park all your vehicles anywhere!'
|
||||
version '1.0.0'
|
||||
|
||||
server_scripts {
|
||||
'@oxmysql/lib/MySQL.lua',
|
||||
'config.lua',
|
||||
'sv_utils.lua',
|
||||
'server.lua'
|
||||
}
|
||||
|
||||
client_scripts {
|
||||
'config.lua',
|
||||
'cl_utils.lua',
|
||||
'client.lua'
|
||||
}
|
||||
|
||||
exports {
|
||||
'GetVehiclePosition',
|
||||
'GetVehiclePositions'
|
||||
}
|
32
resources/[Developer]/mh_Parking/mh_Parking.sql
Normal file
32
resources/[Developer]/mh_Parking/mh_Parking.sql
Normal file
|
@ -0,0 +1,32 @@
|
|||
-- --------------------------------------------------------
|
||||
-- Host: 127.0.0.1
|
||||
-- Server version: 10.1.28-MariaDB - mariadb.org binary distribution
|
||||
-- Server OS: Win32
|
||||
-- HeidiSQL Version: 11.0.0.5919
|
||||
-- --------------------------------------------------------
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET NAMES utf8 */;
|
||||
/*!50503 SET NAMES utf8mb4 */;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
|
||||
-- Dumping structure for table fivem_testserver.vehicle_parking
|
||||
CREATE TABLE IF NOT EXISTS `vehicle_parking` (
|
||||
`plate` varchar(8) NOT NULL,
|
||||
`modifications` text NOT NULL,
|
||||
`posX` float NOT NULL,
|
||||
`posY` float NOT NULL,
|
||||
`posZ` float NOT NULL,
|
||||
`rotX` float NOT NULL,
|
||||
`rotY` float NOT NULL,
|
||||
`rotZ` float NOT NULL,
|
||||
`lastUpdate` int(11) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`plate`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
|
||||
-- Data exporting was unselected.
|
||||
|
||||
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */;
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
770
resources/[Developer]/mh_Parking/server.lua
Normal file
770
resources/[Developer]/mh_Parking/server.lua
Normal file
|
@ -0,0 +1,770 @@
|
|||
local vehicles = {}
|
||||
local DB_vehicles = {}
|
||||
|
||||
-- Command to delete ALL vehicles from the database table. Needs to be executed twice for security reason.
|
||||
local deleteSavedVehicles = false
|
||||
RegisterCommand("deleteSavedVehicles", function(source, args, raw)
|
||||
if (deleteSavedVehicles) then
|
||||
MySQL.query("DELETE FROM vehicle_parking", { })
|
||||
|
||||
vehicles = {}
|
||||
|
||||
Log("Deleted all vehicles from the vehicle_parking table.")
|
||||
else
|
||||
Log("Are you sure that you want to delete all vehicles from the parking list?\nIf yes, type the command a second time!")
|
||||
end
|
||||
|
||||
deleteSavedVehicles = not deleteSavedVehicles
|
||||
end, true)
|
||||
|
||||
-- read all vehicles from the database on startup and do a cleanup check
|
||||
MySQL.ready(function()
|
||||
-- fetch all database results
|
||||
MySQL.query("SELECT plate, posX, posY, posZ, rotX, rotY, rotZ, modifications, lastUpdate FROM vehicle_parking", {}, function(results)
|
||||
Log("Found " .. #results .. " saved vehicles in vehicle_parking.")
|
||||
|
||||
for i = 1, #results do
|
||||
vehicles[results[i].plate] = {
|
||||
handle = nil,
|
||||
position = vector3(results[i].posX, results[i].posY, results[i].posZ),
|
||||
rotation = vector3(results[i].rotX, results[i].rotY, results[i].rotZ),
|
||||
modifications = json.decode(results[i].modifications),
|
||||
lastUpdate = results[i].lastUpdate,
|
||||
spawning = false,
|
||||
spawningPlayer = nil,
|
||||
faultyModel = false
|
||||
}
|
||||
end
|
||||
|
||||
--CleanUp()
|
||||
end)
|
||||
end)
|
||||
|
||||
-- Default CleanUp function. Deletes vehicles from the database after a specific amount of time has passed (configurable in config)
|
||||
function CleanUp()
|
||||
local currentTime = os.time()
|
||||
local threshold = 60 * 60 * Config.cleanUpThresholdTime
|
||||
|
||||
local toDelete = {}
|
||||
|
||||
for plate, vehicle in pairs(vehicles) do
|
||||
if (vehicle.lastUpdate < os.difftime(currentTime, threshold)) then
|
||||
|
||||
MySQL.query("DELETE FROM vehicle_parking WHERE plate = @plate",
|
||||
{
|
||||
["@plate"] = plate
|
||||
})
|
||||
|
||||
table.insert(toDelete, plate)
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1, #toDelete, 1 do
|
||||
local plate = toDelete[i]
|
||||
vehicles[plate] = nil
|
||||
end
|
||||
|
||||
Log("CleanUp complete. Deleted " .. #toDelete .. " entries.")
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- loop to spawn vehicles near players
|
||||
Citizen.CreateThread(function()
|
||||
while (true) do
|
||||
Citizen.Wait(5000)
|
||||
|
||||
if (GetActivePlayerCount() > 0) then
|
||||
TrySpawnVehicles()
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
Citizen.CreateThread(function()
|
||||
while (true) do
|
||||
CallDB_Vehicles()
|
||||
Citizen.Wait(60000 * 5)
|
||||
end
|
||||
end)
|
||||
|
||||
-- loop to delete vehicles
|
||||
Citizen.CreateThread(function()
|
||||
if (Config.deleteTimer <= 0) then
|
||||
return
|
||||
end
|
||||
|
||||
while (true) do
|
||||
Citizen.Wait(60000 * (Config.deleteTimer - Config.deleteNotificationTimes[1]))
|
||||
|
||||
TriggerClientEvent("mh_Parking:notification", -1, string.format(Config.timeLeftNotification, Config.deleteNotificationTimes[1]))
|
||||
for i = 2, #Config.deleteNotificationTimes, 1 do
|
||||
Citizen.Wait(60000 * (Config.deleteNotificationTimes[i - 1] - Config.deleteNotificationTimes[i]))
|
||||
TriggerClientEvent("mh_Parking:notification", -1, string.format(Config.timeLeftNotification, Config.deleteNotificationTimes[i]))
|
||||
end
|
||||
Citizen.Wait(60000 * Config.deleteNotificationTimes[#Config.deleteNotificationTimes])
|
||||
|
||||
DeleteAllVehicles()
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
|
||||
-- sync player position
|
||||
RegisterServerEvent("mh_Parking:syncPlayerPosition")
|
||||
AddEventHandler("mh_Parking:syncPlayerPosition", function(position)
|
||||
activePlayerPositions[source] = position
|
||||
end)
|
||||
|
||||
-- player disconnected
|
||||
AddEventHandler("playerDropped", function(disconnectReason)
|
||||
activePlayerPositions[source] = nil
|
||||
end)
|
||||
|
||||
-- player entered a vehicle
|
||||
RegisterServerEvent("mh_Parking:enteredVehicle")
|
||||
AddEventHandler("mh_Parking:enteredVehicle", function(networkId, plate, modifications)
|
||||
local vehicle = NetworkGetEntityFromNetworkId(networkId)
|
||||
|
||||
if (DoesEntityExist(vehicle)) then
|
||||
local currentTime = os.time()
|
||||
|
||||
local position = GetEntityCoords(vehicle)
|
||||
position = vector3(math.floor(position.x * 100.0) / 100.0, math.floor(position.y * 100.0) / 100.0, math.floor(position.z * 100.0) / 100.0)
|
||||
local rotation = GetEntityRotation(vehicle)
|
||||
rotation = vector3(math.floor(rotation.x * 100.0) / 100.0, math.floor(rotation.y * 100.0) / 100.0, math.floor(rotation.z * 100.0) / 100.0)
|
||||
|
||||
if (vehicles[plate]) then
|
||||
-- already on server list
|
||||
|
||||
Log("Entered vehicle " .. plate .. ". Updating...")
|
||||
|
||||
if (vehicles[plate].handle ~= vehicle) then
|
||||
if (DoesEntityExist(vehicles[plate].handle)) then
|
||||
DeleteEntity(vehicles[plate].handle)
|
||||
end
|
||||
|
||||
vehicles[plate].handle = vehicle
|
||||
end
|
||||
|
||||
vehicles[plate].position = position
|
||||
vehicles[plate].rotation = rotation
|
||||
vehicles[plate].modifications = modifications
|
||||
vehicles[plate].lastUpdate = currentTime
|
||||
|
||||
MySQL.query("UPDATE vehicle_parking SET posX = @posX, posY = @posY, posZ = @posZ, rotX = @rotX, rotY = @rotY, rotZ = @rotZ, modifications = @modifications, lastUpdate = @lastUpdate WHERE plate = @plate", {
|
||||
["@plate"] = plate,
|
||||
["@posX"] = vehicles[plate].position.x,
|
||||
["@posY"] = vehicles[plate].position.y,
|
||||
["@posZ"] = vehicles[plate].position.z,
|
||||
["@rotX"] = vehicles[plate].rotation.x,
|
||||
["@rotY"] = vehicles[plate].rotation.y,
|
||||
["@rotZ"] = vehicles[plate].rotation.z,
|
||||
["@modifications"] = json.encode(vehicles[plate].modifications),
|
||||
["@lastUpdate"] = vehicles[plate].lastUpdate
|
||||
})
|
||||
else
|
||||
-- insert in db
|
||||
|
||||
Log("Entered vehicle " .. plate .. ". Inserting new...")
|
||||
|
||||
vehicles[plate] = {
|
||||
handle = vehicle,
|
||||
position = position,
|
||||
rotation = rotation,
|
||||
modifications = modifications,
|
||||
lastUpdate = currentTime,
|
||||
spawning = false,
|
||||
spawningPlayer = nil
|
||||
}
|
||||
|
||||
MySQL.query('SELECT * FROM player_vehicles WHERE plate = @plate', { ['@plate'] = plate }, function(rs)
|
||||
Log(json.encode(rs))
|
||||
if rs[1] then
|
||||
Log("FOUND OWNER FROM VEHICLE")
|
||||
|
||||
MySQL.query("INSERT INTO vehicle_parking (plate, posX, posY, posZ, rotX, rotY, rotZ, modifications, lastUpdate) VALUES (@plate, @posX, @posY, @posZ, @rotX, @rotY, @rotZ, @modifications, @lastUpdate)", {
|
||||
["@plate"] = plate,
|
||||
["@posX"] = vehicles[plate].position.x,
|
||||
["@posY"] = vehicles[plate].position.y,
|
||||
["@posZ"] = vehicles[plate].position.z,
|
||||
["@rotX"] = vehicles[plate].rotation.x,
|
||||
["@rotY"] = vehicles[plate].rotation.y,
|
||||
["@rotZ"] = vehicles[plate].rotation.z,
|
||||
["@modifications"] = json.encode(vehicles[plate].modifications),
|
||||
["@lastUpdate"] = vehicles[plate].lastUpdate
|
||||
})
|
||||
else
|
||||
Log("1: NO VEHICLE FOUND")
|
||||
end
|
||||
end)
|
||||
end
|
||||
else
|
||||
Log("Vehicle does not exist. Might be a Onesync error.")
|
||||
end
|
||||
end)
|
||||
|
||||
-- player left a vehicle
|
||||
RegisterServerEvent("mh_Parking:leftVehicle")
|
||||
AddEventHandler("mh_Parking:leftVehicle", function(networkId, modifications)
|
||||
local vehicle = NetworkGetEntityFromNetworkId(networkId)
|
||||
|
||||
if (DoesEntityExist(vehicle)) then
|
||||
local currentTime = os.time()
|
||||
|
||||
local plate = GetVehicleNumberPlateText(vehicle)
|
||||
|
||||
local position = GetEntityCoords(vehicle)
|
||||
position = vector3(math.floor(position.x * 100.0) / 100.0, math.floor(position.y * 100.0) / 100.0, math.floor(position.z * 100.0) / 100.0)
|
||||
local rotation = GetEntityRotation(vehicle)
|
||||
rotation = vector3(math.floor(rotation.x * 100.0) / 100.0, math.floor(rotation.y * 100.0) / 100.0, math.floor(rotation.z * 100.0) / 100.0)
|
||||
|
||||
if (vehicles[plate]) then
|
||||
-- already on server list
|
||||
|
||||
Log("Left vehicle " .. plate .. ". Updating...")
|
||||
|
||||
if (vehicles[plate].handle ~= vehicle) then
|
||||
if (DoesEntityExist(vehicles[plate].handle)) then
|
||||
DeleteEntity(vehicles[plate].handle)
|
||||
end
|
||||
|
||||
vehicles[plate].handle = vehicle
|
||||
end
|
||||
|
||||
vehicles[plate].position = position
|
||||
vehicles[plate].rotation = rotation
|
||||
vehicles[plate].modifications = modifications
|
||||
vehicles[plate].lastUpdate = currentTime
|
||||
|
||||
MySQL.query("UPDATE vehicle_parking SET posX = @posX, posY = @posY, posZ = @posZ, rotX = @rotX, rotY = @rotY, rotZ = @rotZ, modifications = @modifications, lastUpdate = @lastUpdate WHERE plate = @plate",{
|
||||
["@plate"] = plate,
|
||||
["@posX"] = vehicles[plate].position.x,
|
||||
["@posY"] = vehicles[plate].position.y,
|
||||
["@posZ"] = vehicles[plate].position.z,
|
||||
["@rotX"] = vehicles[plate].rotation.x,
|
||||
["@rotY"] = vehicles[plate].rotation.y,
|
||||
["@rotZ"] = vehicles[plate].rotation.z,
|
||||
["@modifications"] = json.encode(vehicles[plate].modifications),
|
||||
["@lastUpdate"] = currentTime
|
||||
})
|
||||
else
|
||||
-- insert in db
|
||||
|
||||
Log("Left vehicle " .. plate .. ". Inserting new...")
|
||||
|
||||
vehicles[plate] = {
|
||||
handle = vehicle,
|
||||
position = position,
|
||||
rotation = rotation,
|
||||
modifications = modifications,
|
||||
lastUpdate = currentTime,
|
||||
spawning = false,
|
||||
spawningPlayer = nil
|
||||
}
|
||||
|
||||
MySQL.query('SELECT * FROM player_vehicles WHERE plate = @plate', { ['@plate'] = plate }, function(rs)
|
||||
if rs[1] then
|
||||
Log("FOUND OWNER FROM VEHICLE")
|
||||
|
||||
MySQL.query("INSERT INTO vehicle_parking (plate, posX, posY, posZ, rotX, rotY, rotZ, modifications, lastUpdate) VALUES (@plate, @posX, @posY, @posZ, @rotX, @rotY, @rotZ, @modifications, @lastUpdate)",{
|
||||
["@plate"] = plate,
|
||||
["@posX"] = vehicles[plate].position.x,
|
||||
["@posY"] = vehicles[plate].position.y,
|
||||
["@posZ"] = vehicles[plate].position.z,
|
||||
["@rotX"] = vehicles[plate].rotation.x,
|
||||
["@rotY"] = vehicles[plate].rotation.y,
|
||||
["@rotZ"] = vehicles[plate].rotation.z,
|
||||
["@modifications"] = json.encode(vehicles[plate].modifications),
|
||||
["@lastUpdate"] = vehicles[plate].lastUpdate
|
||||
})
|
||||
else
|
||||
Log("2: NO VEHICLE FOUND")
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- update a vehicle with its modifications
|
||||
RegisterServerEvent("mh_Parking:updateVehicle")
|
||||
AddEventHandler("mh_Parking:updateVehicle", function(networkId, modifications)
|
||||
local vehicle = NetworkGetEntityFromNetworkId(networkId)
|
||||
|
||||
if (DoesEntityExist(vehicle)) then
|
||||
local currentTime = os.time()
|
||||
|
||||
local plate = GetVehicleNumberPlateText(vehicle)
|
||||
|
||||
local position = GetEntityCoords(vehicle)
|
||||
position = vector3(math.floor(position.x * 100.0) / 100.0, math.floor(position.y * 100.0) / 100.0, math.floor(position.z * 100.0) / 100.0)
|
||||
local rotation = GetEntityRotation(vehicle)
|
||||
rotation = vector3(math.floor(rotation.x * 100.0) / 100.0, math.floor(rotation.y * 100.0) / 100.0, math.floor(rotation.z * 100.0) / 100.0)
|
||||
|
||||
if (vehicles[plate] ~= nil) then
|
||||
-- already on server list
|
||||
|
||||
Log("Updating vehicle " .. plate)
|
||||
|
||||
if (vehicles[plate].handle ~= vehicle) then
|
||||
if (DoesEntityExist(vehicles[plate].handle)) then
|
||||
DeleteEntity(vehicles[plate].handle)
|
||||
end
|
||||
|
||||
vehicles[plate].handle = vehicle
|
||||
end
|
||||
|
||||
vehicles[plate].position = position
|
||||
vehicles[plate].rotation = rotation
|
||||
vehicles[plate].modifications = modifications
|
||||
vehicles[plate].lastUpdate = currentTime
|
||||
|
||||
MySQL.query("UPDATE vehicle_parking SET posX = @posX, posY = @posY, posZ = @posZ, rotX = @rotX, rotY = @rotY, rotZ = @rotZ, modifications = @modifications, lastUpdate = @lastUpdate WHERE plate = @plate",{
|
||||
["@plate"] = plate,
|
||||
["@posX"] = vehicles[plate].position.x,
|
||||
["@posY"] = vehicles[plate].position.y,
|
||||
["@posZ"] = vehicles[plate].position.z,
|
||||
["@rotX"] = vehicles[plate].rotation.x,
|
||||
["@rotY"] = vehicles[plate].rotation.y,
|
||||
["@rotZ"] = vehicles[plate].rotation.z,
|
||||
["@modifications"] = json.encode(vehicles[plate].modifications),
|
||||
["@lastUpdate"] = vehicles[plate].lastUpdate
|
||||
})
|
||||
else
|
||||
-- insert in db
|
||||
|
||||
Log("Updating vehicle " .. plate .. ". Inserting new...")
|
||||
|
||||
vehicles[plate] = {
|
||||
handle = vehicle,
|
||||
position = position,
|
||||
rotation = rotation,
|
||||
modifications = modifications,
|
||||
lastUpdate = currentTime,
|
||||
spawning = false,
|
||||
spawningPlayer = nil
|
||||
}
|
||||
|
||||
MySQL.query('SELECT * FROM player_vehicles WHERE plate = @plate', { ['@plate'] = plate }, function(rs)
|
||||
if rs[1] then
|
||||
Log("FOUND OWNER FROM VEHICLE")
|
||||
|
||||
MySQL.query("INSERT INTO vehicle_parking (plate, posX, posY, posZ, rotX, rotY, rotZ, modifications, lastUpdate) VALUES (@plate, @posX, @posY, @posZ, @rotX, @rotY, @rotZ, @modifications, @lastUpdate)",{
|
||||
["@plate"] = plate,
|
||||
["@posX"] = vehicles[plate].position.x,
|
||||
["@posY"] = vehicles[plate].position.y,
|
||||
["@posZ"] = vehicles[plate].position.z,
|
||||
["@rotX"] = vehicles[plate].rotation.x,
|
||||
["@rotY"] = vehicles[plate].rotation.y,
|
||||
["@rotZ"] = vehicles[plate].rotation.z,
|
||||
["@modifications"] = json.encode(vehicles[plate].modifications),
|
||||
["@lastUpdate"] = vehicles[plate].lastUpdate
|
||||
})
|
||||
else
|
||||
Log("3: NO VEHICLE FOUND")
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- delete a vehicle from the table (and world) with its plate
|
||||
RegisterServerEvent("mh_Parking:deleteVehicle")
|
||||
AddEventHandler("mh_Parking:deleteVehicle", function(plate, deleteEntity)
|
||||
if (plate == nil) then
|
||||
Log("^1mh_Parking: \"plate\" was nil while trying to delete vehicle!")
|
||||
return
|
||||
end
|
||||
|
||||
Log("Trying to delete " .. tostring(plate))
|
||||
|
||||
if (vehicles[plate] ~= nil) then
|
||||
if (deleteEntity and DoesEntityExist(vehicles[plate].handle)) then
|
||||
DeleteEntity(vehicles[plate].handle)
|
||||
end
|
||||
|
||||
vehicles[plate] = nil
|
||||
|
||||
MySQL.query("DELETE FROM vehicle_parking WHERE plate = @plate",
|
||||
{
|
||||
["@plate"] = plate
|
||||
})
|
||||
else
|
||||
local loadedVehicles = GetAllVehicles()
|
||||
local loadedVehicle = TryGetLoadedVehicle(plate, loadedVehicles)
|
||||
|
||||
if (loadedVehicle ~= nil) then
|
||||
if (deleteEntity and DoesEntityExist(loadedVehicle)) then
|
||||
DeleteEntity(loadedVehicle)
|
||||
end
|
||||
|
||||
MySQL.query("DELETE FROM vehicle_parking WHERE plate = @plate",
|
||||
{
|
||||
["@plate"] = plate
|
||||
})
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- checks if vehicles have to be spawned and spawns them if necessary
|
||||
function TrySpawnVehicles()
|
||||
local loadedVehicles = GetAllVehicles()
|
||||
|
||||
local playerVehiclePlates = {}
|
||||
for id, position in pairs(activePlayerPositions) do
|
||||
local ped = GetPlayerPed(id)
|
||||
local veh = GetVehiclePedIsIn(ped, false)
|
||||
|
||||
if (DoesEntityExist(veh)) then
|
||||
table.insert(playerVehiclePlates, GetVehicleNumberPlateText(veh))
|
||||
end
|
||||
end
|
||||
|
||||
-- check, if vehicles need to be spawned
|
||||
for plate, vehicleData in pairs(vehicles) do
|
||||
local plateCheck = CheckPlate(plate)
|
||||
if (plateCheck ~= false) then
|
||||
|
||||
if (not vehicleData.faultyModel) then
|
||||
if (not vehicleData.spawning) then
|
||||
local closestPlayer, dist = GetClosestPlayerId(vehicleData.position)
|
||||
|
||||
if (closestPlayer ~= nil and dist < Config.spawnDistance and not ContainsPlate(plate, playerVehiclePlates)) then
|
||||
if (vehicleData.handle ~= nil and DoesEntityExist(vehicleData.handle)) then
|
||||
-- vehicle found on server side
|
||||
|
||||
UpdateVehicle(plate)
|
||||
else
|
||||
-- vehicle not found on server side
|
||||
-- check, if it is loaded differently
|
||||
|
||||
local loadedVehicle = TryGetLoadedVehicle(plate, loadedVehicles)
|
||||
if (loadedVehicle ~= nil) then
|
||||
-- vehicle found
|
||||
|
||||
vehicleData.handle = loadedVehicle
|
||||
|
||||
UpdateVehicle(plate)
|
||||
else
|
||||
-- vehicle not found
|
||||
-- try and spawn it
|
||||
|
||||
local playerId, distance = GetClosestPlayerId(vehicleData.position)
|
||||
if (playerId and distance < Config.spawnDistance) then
|
||||
vehicleData.spawning = true
|
||||
|
||||
Citizen.CreateThread(function()
|
||||
local vec4 = vector4(vehicleData.position.x, vehicleData.position.y, vehicleData.position.z, vehicleData.rotation.z)
|
||||
local vehicle = Citizen.InvokeNative(GetHashKey("CREATE_AUTOMOBILE"), vehicleData.modifications[1], vec4.xyzw)
|
||||
|
||||
while (not DoesEntityExist(vehicle)) do
|
||||
Citizen.Wait(0)
|
||||
end
|
||||
|
||||
SetEntityCoords(vehicle, vehicleData.position.x, vehicleData.position.y, vehicleData.position.z)
|
||||
SetEntityRotation(vehicle, vehicleData.rotation.x, vehicleData.rotation.y, vehicleData.rotation.z)
|
||||
|
||||
vehicleData.handle = vehicle
|
||||
|
||||
local networkOwner = -1
|
||||
while (networkOwner == -1) do
|
||||
Citizen.Wait(0)
|
||||
networkOwner = NetworkGetEntityOwner(vehicleData.handle)
|
||||
end
|
||||
|
||||
vehicleData.spawningPlayer = GetPlayerIdentifiersSorted(networkOwner)["license"]
|
||||
|
||||
Log("Creating vehicle " .. plate .. " at " .. tostring(vehicleData.position) .. " at entity owner " .. tostring(networkOwner))
|
||||
|
||||
TriggerClientEvent("mh_Parking:setVehicleMods", networkOwner, NetworkGetNetworkIdFromEntity(vehicleData.handle), plate, vehicleData.modifications)
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif (vehicleData.spawningPlayer) then
|
||||
-- if vehicle is currently spawning check if responsible player is still connected
|
||||
if (not IsPlayerWithLicenseActive(vehicleData.spawningPlayer)) then
|
||||
TriggerEvent("mh_Parking:setVehicleModsFailed", plate)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
RegisterServerEvent("mh_Parking:setVehicleModsSuccess")
|
||||
AddEventHandler("mh_Parking:setVehicleModsSuccess", function(plate)
|
||||
Log("Setting mods successful...")
|
||||
|
||||
if (vehicles[plate]) then
|
||||
vehicles[plate].spawning = false
|
||||
vehicles[plate].spawningPlayer = nil
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterServerEvent("mh_Parking:setVehicleModsFailed")
|
||||
AddEventHandler("mh_Parking:setVehicleModsFailed", function(plate)
|
||||
Log("Setting mods for " .. plate .. " failed... retrying...")
|
||||
|
||||
if (vehicles[plate] and vehicles[plate].handle and DoesEntityExist(vehicles[plate].handle)) then
|
||||
local networkOwner = -1
|
||||
while (networkOwner == -1) do
|
||||
Citizen.Wait(0)
|
||||
networkOwner = NetworkGetEntityOwner(vehicles[plate].handle)
|
||||
end
|
||||
|
||||
local netOwnerLicense = GetPlayerIdentifiersSorted(networkOwner)["license"]
|
||||
if (netOwnerLicense ~= nil) then
|
||||
vehicles[plate].spawningPlayer = GetPlayerIdentifiersSorted(networkOwner)["license"]
|
||||
|
||||
TriggerClientEvent("mh_Parking:setVehicleMods", networkOwner, NetworkGetNetworkIdFromEntity(vehicles[plate].handle), plate, vehicles[plate].modifications)
|
||||
else
|
||||
Log("Something went wrong while trying to get player license for spawning vehicle " .. tostring(plate) .. ". Despawning vehicle")
|
||||
|
||||
DeleteEntity(vehicles[plate].handle)
|
||||
|
||||
vehicles[plate].handle = nil
|
||||
vehicles[plate].spawningPlayer = nil
|
||||
vehicles[plate].spawning = false
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterServerEvent("mh_Parking:modelDoesNotExist")
|
||||
AddEventHandler("mh_Parking:modelDoesNotExist", function(plate)
|
||||
if (vehicles[plate] == nil) then
|
||||
return
|
||||
end
|
||||
|
||||
if (DoesEntityExist(vehicles[plate].handle)) then
|
||||
DeleteEntity(vehicles[plate].handle)
|
||||
end
|
||||
vehicles[plate].handle = nil
|
||||
vehicles[plate].spawningPlayer = nil
|
||||
vehicles[plate].spawning = false
|
||||
|
||||
Log("The model for vehicle " .. tostring(plate) .. " does not exist. Removing from spawn pool...")
|
||||
|
||||
vehicles[plate].faultyModel = true
|
||||
end)
|
||||
|
||||
RegisterServerEvent("mh_Parking:updatePlate")
|
||||
AddEventHandler("mh_Parking:updatePlate", function(oldPlate, newPlate)
|
||||
if (oldPlate == nil or newPlate == nil) then
|
||||
Log("^1mh_Parking: \"oldPlate\" or \"newPlate\" was nil while trying to update a plate!")
|
||||
return
|
||||
end
|
||||
|
||||
if (vehicles[oldPlate]) then
|
||||
if (vehicles[newPlate]) then
|
||||
Log("Updating plate failed. New plate was already there!")
|
||||
else
|
||||
vehicles[newPlate] = vehicles[oldPlate]
|
||||
vehicles[oldPlate] = nil
|
||||
|
||||
Log("Updating plate successful!")
|
||||
|
||||
MySQL.query("UPDATE vehicle_parking SET plate = @newPlate WHERE plate = @oldPlate",
|
||||
{
|
||||
["@newPlate"] = newPlate,
|
||||
["@oldPlate"] = oldPlate
|
||||
})
|
||||
end
|
||||
else
|
||||
Log("Updating plate failed or unnecessary. Old plate was not found!")
|
||||
end
|
||||
end)
|
||||
|
||||
function UpdateVehicle(plate)
|
||||
local newPos = GetEntityCoords(vehicles[plate].handle)
|
||||
local newRot = GetEntityRotation(vehicles[plate].handle)
|
||||
newPos = vector3(math.floor(newPos.x * 100.0) * 0.01, math.floor(newPos.y * 100.0) * 0.01, math.floor(newPos.z * 100.0) * 0.01)
|
||||
newRot = vector3(math.floor(newRot.x * 100.0) * 0.01, math.floor(newRot.y * 100.0) * 0.01, math.floor(newRot.z * 100.0) * 0.01)
|
||||
|
||||
local newLockStatus = GetVehicleDoorLockStatus(vehicles[plate].handle)
|
||||
local newEngineHealth = math.floor(GetVehicleEngineHealth(vehicles[plate].handle) * 10.0) * 0.1
|
||||
local newTankHealth = math.floor(GetVehiclePetrolTankHealth(vehicles[plate].handle) * 10.0) * 0.1
|
||||
if (Vector3DistFast(vehicles[plate].position, newPos) > 1.0
|
||||
or GetRotationDifference(vehicles[plate].rotation, newRot) > 15.0
|
||||
or newLockStatus ~= vehicles[plate].modifications[2]
|
||||
or math.abs(newEngineHealth - vehicles[plate].modifications[5]) > 5.0
|
||||
or math.abs(newTankHealth - vehicles[plate].modifications[6]) > 5.0
|
||||
) then
|
||||
vehicles[plate].modifications[2] = newLockStatus
|
||||
vehicles[plate].modifications[5] = newEngineHealth
|
||||
vehicles[plate].modifications[6] = newTankHealth
|
||||
|
||||
Log("Updating vehicle " .. plate)
|
||||
|
||||
vehicles[plate].position = newPos
|
||||
vehicles[plate].rotation = newRot
|
||||
|
||||
MySQL.query("UPDATE vehicle_parking SET posX = @posX, posY = @posY, posZ = @posZ, rotX = @rotX, rotY = @rotY, rotZ = @rotZ, modifications = @modifications WHERE plate = @plate",
|
||||
{
|
||||
["@posX"] = vehicles[plate].position.x,
|
||||
["@posY"] = vehicles[plate].position.y,
|
||||
["@posZ"] = vehicles[plate].position.z,
|
||||
["@rotX"] = vehicles[plate].rotation.x,
|
||||
["@rotY"] = vehicles[plate].rotation.y,
|
||||
["@rotZ"] = vehicles[plate].rotation.z,
|
||||
["@modifications"] = json.encode(vehicles[plate].modifications),
|
||||
["@plate"] = plate
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
function DeleteAllVehicles()
|
||||
TriggerClientEvent("mh_Parking:notification", -1, Config.deleteNotification)
|
||||
|
||||
local peds = GetAllPeds()
|
||||
local playerPeds = {}
|
||||
for i = 1, #peds, 1 do
|
||||
if (IsPedAPlayer(peds[i])) then
|
||||
table.insert(playerPeds, peds[i])
|
||||
end
|
||||
end
|
||||
|
||||
if (#playerPeds == 0) then
|
||||
return
|
||||
end
|
||||
|
||||
local time = GetGameTimer()
|
||||
|
||||
local vehs = GetAllVehicles()
|
||||
local deleted = 0
|
||||
for i = 1, #vehs, 1 do
|
||||
if (not IsAnyPlayerInsideVehicle(vehs[i], playerPeds)) then
|
||||
local closestPlayer, distance = GetClosestPlayerPed(GetEntityCoords(vehs[i]), playerPeds)
|
||||
if (closestPlayer ~= nil and distance > Config.deleteDistance) then
|
||||
local plate = GetVehicleNumberPlateText(vehs[i])
|
||||
if (vehicles[plate] ~= nil) then
|
||||
vehicles[plate] = nil
|
||||
|
||||
MySQL.query("DELETE FROM vehicle_parking WHERE plate = @plate",
|
||||
{
|
||||
["@plate"] = plate
|
||||
})
|
||||
end
|
||||
|
||||
DeleteEntity(vehs[i])
|
||||
|
||||
deleted = deleted + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Log("Deleted " .. tostring(deleted) .. "/" .. tostring(#vehs) .. " vehicles. Took " .. tostring((GetGameTimer() - time) / 1000.0) .. "sec")
|
||||
end
|
||||
|
||||
AddEventHandler("onResourceStop", function(name)
|
||||
if (name ~= GetCurrentResourceName()) then
|
||||
return
|
||||
end
|
||||
|
||||
-- delete vehicles that are still being spawned before actually stopping the resource
|
||||
for plate, vehicleData in pairs(vehicles) do
|
||||
if (vehicleData.spawning and DoesEntityExist(vehicleData.handle)) then
|
||||
DeleteEntity(vehicleData.handle)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- render entity scorched (trigger with netid of the vehicle and false when repairing)
|
||||
RegisterServerEvent("mh_Parking:renderScorched")
|
||||
AddEventHandler("mh_Parking:renderScorched", function(vehicleNetId, scorched)
|
||||
local vehicleHandle = NetworkGetEntityFromNetworkId(vehicleNetId)
|
||||
if (DoesEntityExist(vehicleHandle)) then
|
||||
TriggerClientEvent("mh_Parking:renderScorched", -1, vehicleNetId, scorched)
|
||||
end
|
||||
end)
|
||||
|
||||
if (GetResourceState("kimi_callbacks") == "started") then
|
||||
exports["kimi_callbacks"]:Register("AP:getVehiclePosition", function(source, plate)
|
||||
local vehs = GetAllVehicles()
|
||||
|
||||
for i, veh in ipairs(vehs) do
|
||||
local vehPlate = GetVehicleNumberPlateText(veh)
|
||||
if (plate == vehPlate or plate == string.gsub(vehPlate, "^%s*(.-)%s*$", "%1")) then
|
||||
return GetEntityCoords(veh)
|
||||
end
|
||||
end
|
||||
|
||||
for vehPlate, vehData in pairs(vehicles) do
|
||||
if (plate == vehPlate or plate == string.gsub(vehPlate, "^%s*(.-)%s*$", "%1")) then
|
||||
return vehData.position
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end)
|
||||
|
||||
exports["kimi_callbacks"]:Register("AP:getVehiclePositions", function(source, plates)
|
||||
local platePositions = {}
|
||||
|
||||
Log("Looking for plates:")
|
||||
for i, plate in ipairs(plates) do
|
||||
Log("\"" .. plate .. "\"")
|
||||
end
|
||||
|
||||
local vehs = GetAllVehicles()
|
||||
for i, veh in ipairs(vehs) do
|
||||
local vehPlate = GetVehicleNumberPlateText(veh)
|
||||
local trimmedVehPlate = string.gsub(vehPlate, "^%s*(.-)%s*$", "%1")
|
||||
|
||||
for j, plate in ipairs(plates) do
|
||||
if (plate == vehPlate or plate == trimmedVehPlate) then
|
||||
platePositions[plate] = GetEntityCoords(veh)
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for i, plate in ipairs(plates) do
|
||||
if (platePositions[plate] == nil) then
|
||||
for vehPlate, vehData in pairs(vehicles) do
|
||||
local trimmedVehPlate = string.gsub(vehPlate, "^%s*(.-)%s*$", "%1")
|
||||
|
||||
if (plate == vehPlate or plate == trimmedVehPlate) then
|
||||
platePositions[plate] = vehData.position
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return platePositions
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
RegisterNetEvent('mh_Parking:removeVehicle')
|
||||
AddEventHandler('mh_Parking:removeVehicle', function(plate)
|
||||
print("mh_parking: "..plate)
|
||||
vehicles[plate] = nil
|
||||
end)
|
||||
|
||||
function CallDB_Vehicles()
|
||||
DB_vehicles = {}
|
||||
MySQL.query('SELECT * FROM player_vehicles', {}, function(rs)
|
||||
for k, v in pairs(rs) do
|
||||
table.insert(DB_vehicles, v)
|
||||
end
|
||||
end) --DB_vehicles
|
||||
end
|
||||
|
||||
function CheckPlate(plate) -- DB_Players
|
||||
for k, v in pairs(DB_vehicles) do
|
||||
if Config.isDebug then
|
||||
Log(json.encode(v))
|
||||
Log(plate)
|
||||
Log(v.plate)
|
||||
end
|
||||
|
||||
if v.plate == plate then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
151
resources/[Developer]/mh_Parking/sv_utils.lua
Normal file
151
resources/[Developer]/mh_Parking/sv_utils.lua
Normal file
|
@ -0,0 +1,151 @@
|
|||
activePlayerPositions = {}
|
||||
|
||||
-- returns a loaded vehicled with a given number plate
|
||||
function TryGetLoadedVehicle(plate, loadedVehicles)
|
||||
for i = 1, #loadedVehicles, 1 do
|
||||
if (plate == GetVehicleNumberPlateText(loadedVehicles[i]) and DoesEntityExist(loadedVehicles[i])) then
|
||||
return loadedVehicles[i]
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
function ContainsPlate(plate, vehiclePlates)
|
||||
for i = 1, #vehiclePlates, 1 do
|
||||
if (plate == vehiclePlates[i]) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
-- Logs text to the server console
|
||||
function Log(text)
|
||||
if (Config.isDebug) then
|
||||
Print(GetCurrentResourceName() .. ": " .. text)
|
||||
end
|
||||
end
|
||||
|
||||
-- return the distance between two positions (Vector3)
|
||||
function Vector3Dist(v1, v2)
|
||||
return math.sqrt( (v2.x - v1.x) * (v2.x - v1.x) + (v2.y - v1.y) * (v2.y - v1.y) + (v2.z - v1.z) * (v2.z - v1.z) )
|
||||
end
|
||||
|
||||
-- return the distance between two positions without sqrt (Vector3)
|
||||
function Vector3DistFast(v1, v2)
|
||||
return (v2.x - v1.x) * (v2.x - v1.x) + (v2.y - v1.y) * (v2.y - v1.y) + (v2.z - v1.z) * (v2.z - v1.z)
|
||||
end
|
||||
|
||||
-- returns the difference in degrees from the axis with the highest difference
|
||||
function GetRotationDifference(r1, r2)
|
||||
local x = math.abs(r1.x - r2.x)
|
||||
local y = math.abs(r1.y - r2.y)
|
||||
local z = math.abs(r1.z - r2.z)
|
||||
|
||||
if (x > y and x > z) then
|
||||
return x
|
||||
elseif (y > z) then
|
||||
return y
|
||||
else
|
||||
return z
|
||||
end
|
||||
end
|
||||
|
||||
-- get the amount of currently active players
|
||||
function GetActivePlayerCount()
|
||||
local playerCount = 0
|
||||
for k, v in pairs(activePlayerPositions) do
|
||||
playerCount = playerCount + 1
|
||||
end
|
||||
return playerCount
|
||||
end
|
||||
|
||||
-- return the ID of the closest player
|
||||
function GetClosestPlayerId(position)
|
||||
local closestDistance = 1000000.0
|
||||
local closestPlayerID = nil
|
||||
local closestPos = nil
|
||||
|
||||
for playerID, pos in pairs(activePlayerPositions) do
|
||||
local distance = Vector3DistFast(position, pos)
|
||||
|
||||
if (distance < closestDistance) then
|
||||
closestDistance = distance
|
||||
closestPlayerID = playerID
|
||||
closestPos = pos
|
||||
end
|
||||
end
|
||||
|
||||
local distance = nil
|
||||
if (closestPlayerID ~= nil) then
|
||||
distance = Vector3Dist(position, closestPos)
|
||||
end
|
||||
|
||||
return closestPlayerID, distance
|
||||
end
|
||||
|
||||
function IsAnyPlayerInsideVehicle(vehicle, playerPeds)
|
||||
for i = 1, #playerPeds, 1 do
|
||||
local veh = GetVehiclePedIsIn(playerPeds[i], false)
|
||||
|
||||
if (DoesEntityExist(veh) and veh == vehicle) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
-- return the ped of the closest player
|
||||
function GetClosestPlayerPed(position, playerPeds)
|
||||
local closestDistance = 1000000.0
|
||||
local closestPlayerPed = nil
|
||||
local closestPos = nil
|
||||
|
||||
for k, playerPed in pairs(playerPeds) do
|
||||
local pos = GetEntityCoords(playerPed)
|
||||
local distance = Vector3DistFast(position, pos)
|
||||
|
||||
if (distance < closestDistance) then
|
||||
closestDistance = distance
|
||||
closestPlayerPed = playerPed
|
||||
closestPos = pos
|
||||
end
|
||||
end
|
||||
|
||||
local distance = 0.0
|
||||
if (closestPlayerPed ~= nil) then
|
||||
distance = Vector3Dist(position, closestPos)
|
||||
end
|
||||
|
||||
return closestPlayerPed, distance
|
||||
end
|
||||
|
||||
-- returns true if a player is active on the server with the specified license
|
||||
function IsPlayerWithLicenseActive(license)
|
||||
for playerId, playerPos in pairs(activePlayerPositions) do
|
||||
if (GetPlayerIdentifiersSorted(playerId)["license"] == license) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
-- Return an array with all identifiers - e.g. ids["license"] = license:xxxxxxxxxxxxxxxx
|
||||
function GetPlayerIdentifiersSorted(playerServerId)
|
||||
local ids = {}
|
||||
|
||||
local identifiers = GetPlayerIdentifiers(playerServerId)
|
||||
|
||||
for k, identifier in pairs (identifiers) do
|
||||
local i, j = string.find(identifier, ":")
|
||||
local idType = string.sub(identifier, 1, i-1)
|
||||
|
||||
ids[idType] = identifier
|
||||
end
|
||||
|
||||
return ids
|
||||
end
|
5
resources/[Developer]/mh_garage/client/debug.lua
Normal file
5
resources/[Developer]/mh_garage/client/debug.lua
Normal file
|
@ -0,0 +1,5 @@
|
|||
function Debug(text)
|
||||
if Config.Debug then
|
||||
print(text)
|
||||
end
|
||||
end
|
180
resources/[Developer]/mh_garage/client/main.lua
Normal file
180
resources/[Developer]/mh_garage/client/main.lua
Normal file
|
@ -0,0 +1,180 @@
|
|||
QBCore = exports['qb-core']:GetCoreObject()
|
||||
|
||||
Player = nil
|
||||
npcHandle = nil
|
||||
isNPCSpawned = false
|
||||
CurrentZone = nil
|
||||
|
||||
CurrentActionData = {}
|
||||
hasAlreadyEnteredMarker = false
|
||||
|
||||
Citizen.CreateThread(function()
|
||||
while Player == nil do
|
||||
Player = exports['qb-core']:GetPlayerData()
|
||||
Wait(0)
|
||||
end
|
||||
end)
|
||||
|
||||
Citizen.CreateThread(function()
|
||||
while true do
|
||||
Wait(15000)
|
||||
local ped = PlayerPedId()
|
||||
|
||||
if IsPedInAnyVehicle(ped, false) then
|
||||
local veh = GetVehiclePedIsIn(ped, false)
|
||||
local mods = QBCore.Functions.GetVehicleProperties(veh)
|
||||
print("Triggert setMods: "..json.encode(mods))
|
||||
TriggerServerEvent('mh_garage:setMods', mods)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- Funktion zum Spawnen des NPCs
|
||||
function SpawnGuardNPC(npc)
|
||||
-- Ped Model laden
|
||||
RequestModel(npc.model)
|
||||
local timeout = 0
|
||||
while not HasModelLoaded(npc.model) and timeout < 100 do
|
||||
timeout = timeout + 1
|
||||
Wait(100)
|
||||
end
|
||||
|
||||
if not HasModelLoaded(npc.model) then
|
||||
return
|
||||
end
|
||||
|
||||
-- NPC erstellen
|
||||
npcHandle = CreatePed(4, npc.model, npc.spawn.x, npc.spawn.y, npc.spawn.z, npc.spawn.w, false, true)
|
||||
if not DoesEntityExist(npcHandle) then
|
||||
return
|
||||
end
|
||||
|
||||
-- NPC Eigenschaften setzen
|
||||
SetEntityAsMissionEntity(npcHandle, true, true)
|
||||
SetBlockingOfNonTemporaryEvents(npcHandle, true)
|
||||
SetPedDiesWhenInjured(npcHandle, false)
|
||||
SetPedCanPlayAmbientAnims(npcHandle, true)
|
||||
SetPedCanRagdollFromPlayerImpact(npcHandle, false)
|
||||
SetEntityInvincible(npcHandle, true)
|
||||
FreezeEntityPosition(npcHandle, true)
|
||||
|
||||
-- Optional: Animation für den NPC
|
||||
TaskStartScenarioInPlace(npcHandle, "WORLD_HUMAN_GUARD_STAND", 0, true)
|
||||
|
||||
isNPCSpawned = true
|
||||
end
|
||||
|
||||
-- Funktion zum Entfernen des NPCs
|
||||
function RemoveGuardNPC()
|
||||
if DoesEntityExist(npcHandle) then
|
||||
DeleteEntity(npcHandle)
|
||||
isNPCSpawned = false
|
||||
end
|
||||
end
|
||||
|
||||
-- Hauptthread zum Überprüfen der Spieler-Position
|
||||
CreateThread(function()
|
||||
while true do
|
||||
Wait(0)
|
||||
local ped = PlayerPedId()
|
||||
local coords = GetEntityCoords(ped)
|
||||
|
||||
local isInMarker = false
|
||||
|
||||
for k, v in pairs(Config.Zonen) do
|
||||
local dist = #(coords - vector3(v.NPC.spawn.x, v.NPC.spawn.y, v.NPC.spawn.z))
|
||||
local spawnDistance = v.NPC.distance
|
||||
|
||||
if dist <= spawnDistance then
|
||||
isInMarker = true
|
||||
CurrentZone = v
|
||||
end
|
||||
end
|
||||
|
||||
if isInMarker and not hasAlreadyEnteredMarker then
|
||||
hasAlreadyEnteredMarker = true
|
||||
SpawnGuardNPC(CurrentZone.NPC)
|
||||
AddTargetOptions()
|
||||
end
|
||||
|
||||
if not isInMarker and hasAlreadyEnteredMarker then
|
||||
hasAlreadyEnteredMarker = false
|
||||
CurrentZone = nil
|
||||
exports['qb-target']:RemoveTargetEntity(npcHandle)
|
||||
RemoveGuardNPC()
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
function AddTargetOptions()
|
||||
|
||||
local opt = {
|
||||
{
|
||||
type = "client",
|
||||
event = "mh_garage:storeVehicle",
|
||||
icon = "fas fa-parking",
|
||||
label = "Fahrzeug einparken",
|
||||
},
|
||||
{
|
||||
type = "client",
|
||||
event = "mh_garage:retrieveOwnerVehicle",
|
||||
icon = "fas fa-car",
|
||||
label = "Eigene Fahrzeug ausparken",
|
||||
},
|
||||
{
|
||||
type = "client",
|
||||
event = "mh_garage:retrieveVehicle",
|
||||
icon = "key",
|
||||
label = "Schlüssel Fahrzeug ausparken",
|
||||
}
|
||||
}
|
||||
|
||||
if Config.Verwaltung.garage then
|
||||
table.insert(opt, {
|
||||
type = "client",
|
||||
event = "mh_garage:verwaltungVeh",
|
||||
icon = "hand",
|
||||
label = "Fahrzeuge Verwalten",
|
||||
})
|
||||
end
|
||||
|
||||
exports['qb-target']:AddTargetEntity(npcHandle, {
|
||||
options = opt,
|
||||
distance = 2.5
|
||||
})
|
||||
end
|
||||
|
||||
function Notification(text, type)
|
||||
lib.notify({
|
||||
title = "Garage - "..CurrentZone.name,
|
||||
description = text,
|
||||
type = type,
|
||||
position = 'top',
|
||||
})
|
||||
end
|
||||
|
||||
---------------------------- NetEvents
|
||||
RegisterNetEvent('mh_jobgarage:notify')
|
||||
AddEventHandler('mh_jobgarage:notify', function(title, text, type)
|
||||
Notification(text, type)
|
||||
end)
|
||||
|
||||
function GetVehicleDamagePercentage(vehicle)
|
||||
if not vehicle then return 0 end
|
||||
|
||||
-- Hole die verschiedenen Gesundheitswerte
|
||||
local engineHealth = GetVehicleEngineHealth(vehicle)
|
||||
local bodyHealth = GetVehicleBodyHealth(vehicle)
|
||||
local tankHealth = GetVehiclePetrolTankHealth(vehicle)
|
||||
|
||||
-- Normalisiere die Werte (Standard-Maximalwerte: 1000.0)
|
||||
local enginePercent = (engineHealth / 1000.0) * 100
|
||||
local bodyPercent = (bodyHealth / 1000.0) * 100
|
||||
local tankPercent = (tankHealth / 1000.0) * 100
|
||||
|
||||
-- Berechne Durchschnitt als Gesamtzustand
|
||||
local totalHealth = (enginePercent + bodyPercent + tankPercent) / 3
|
||||
|
||||
-- Runde auf ganze Zahlen
|
||||
return math.floor(totalHealth)
|
||||
end
|
152
resources/[Developer]/mh_garage/client/retrieve.lua
Normal file
152
resources/[Developer]/mh_garage/client/retrieve.lua
Normal file
|
@ -0,0 +1,152 @@
|
|||
RegisterNetEvent('mh_garage:retrieveOwnerVehicle')
|
||||
AddEventHandler('mh_garage:retrieveOwnerVehicle', function()
|
||||
local ped = PlayerPedId()
|
||||
local coords = GetEntityCoords(ped)
|
||||
local random = SelectName()
|
||||
|
||||
local opt = {}
|
||||
|
||||
QBCore.Functions.TriggerCallback('mh_garage:retrieveOwnerVehicle', function(cb)
|
||||
if cb == false then
|
||||
Notification("Es ist kein Fahrzeug hier!", "inform")
|
||||
return
|
||||
end
|
||||
|
||||
for i = 1, #cb, 1 do
|
||||
local mods = json.decode(cb[i].mods)
|
||||
table.insert(opt, {
|
||||
title = cb[i].name,
|
||||
description = "Kennzeichen: "..cb[i].plate, --[[ \nTankinhalt: "..math.round(mods.fuelLevel, 2).."%" ]]
|
||||
icon = "car",
|
||||
onSelect = function()
|
||||
cb[i].mods = mods
|
||||
SpawnThisVehicle(cb[i])
|
||||
end
|
||||
})
|
||||
end
|
||||
lib.registerContext({
|
||||
id = "retrieveVehicle",
|
||||
title = random.name,
|
||||
options = opt
|
||||
})
|
||||
|
||||
lib.showContext("retrieveVehicle")
|
||||
end, CurrentZone.name)
|
||||
end)
|
||||
|
||||
RegisterNetEvent('mh_garage:retrieveVehicle')
|
||||
AddEventHandler('mh_garage:retrieveVehicle', function()
|
||||
local ped = PlayerPedId()
|
||||
local coords = GetEntityCoords(ped)
|
||||
local random = SelectName()
|
||||
|
||||
local opt = {}
|
||||
|
||||
QBCore.Functions.TriggerCallback('mh_garage:retrieveKeyVehicle', function(cb)
|
||||
if cb == false then
|
||||
Notification("Es ist kein Fahrzeug hier!", "inform")
|
||||
return
|
||||
end
|
||||
|
||||
for i = 1, #cb, 1 do
|
||||
if cb[i].garage ~= "OUT" then
|
||||
local mods = json.decode(cb[i].mods)
|
||||
table.insert(opt, {
|
||||
title = cb[i].name,
|
||||
description = "Kennzeichen: "..cb[i].plate, --[[ \nTankinhalt: "..math.round(mods.fuelLevel, 2).."%" ]]
|
||||
icon = "car",
|
||||
onSelect = function()
|
||||
cb[i].mods = mods
|
||||
SpawnThisVehicle(cb[i])
|
||||
end
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
lib.registerContext({
|
||||
id = "retrieveVehicle",
|
||||
title = random.name,
|
||||
options = opt
|
||||
})
|
||||
|
||||
lib.showContext("retrieveVehicle")
|
||||
end, CurrentZone.name)
|
||||
end)
|
||||
|
||||
function SpawnThisVehicle(vehicle)
|
||||
local spawnPoint = nil
|
||||
|
||||
-- Freien Spawnpunkt suchen
|
||||
for _, spot in pairs(CurrentZone.vehicle_spawn) do
|
||||
if not IsAnyVehicleNearPoint(spot.x, spot.y, spot.z, 3.0) then
|
||||
spawnPoint = spot
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if spawnPoint then
|
||||
QBCore.Functions.SpawnVehicle(vehicle.vehicle, function(veh)
|
||||
-- Fahrzeug ID für Server
|
||||
local netId = NetworkGetNetworkIdFromEntity(veh)
|
||||
|
||||
-- Grundeinstellungen
|
||||
SetVehicleNumberPlateText(veh, vehicle.plate)
|
||||
SetVehicleDoorsLocked(veh, 0)
|
||||
SetEntityHeading(veh, spawnPoint.w)
|
||||
|
||||
-- Motor aus
|
||||
SetVehicleEngineOn(veh, false, true, true)
|
||||
|
||||
-- Fahrzeug Eigenschaften
|
||||
local mods = type(vehicle.mods) == 'string' and json.decode(vehicle.mods) or vehicle.mods
|
||||
|
||||
-- Grundwerte setzen
|
||||
SetVehicleFuelLevel(veh, mods.fuelLevel)
|
||||
exports["lc_fuel"]:SetFuel(veh, mods.fuelLevel)
|
||||
SetVehicleEngineHealth(veh, mods.engineHealth)
|
||||
SetVehicleBodyHealth(veh, mods.bodyHealth)
|
||||
SetVehicleDirtLevel(veh, mods.dirtLevel)
|
||||
|
||||
-- Türen Status
|
||||
if mods.doorStatus then
|
||||
for doorIndex = 0, 5 do
|
||||
if mods.doorStatus[tostring(doorIndex)] then
|
||||
SetVehicleDoorBroken(veh, doorIndex, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Fenster Status
|
||||
if mods.windowStatus then
|
||||
for windowIndex = 0, 7 do
|
||||
if not mods.windowStatus[tostring(windowIndex)] then
|
||||
SmashVehicleWindow(veh, windowIndex)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Alle Mods anwenden
|
||||
QBCore.Functions.SetVehicleProperties(veh, mods)
|
||||
|
||||
-- Fahrzeug auf den Boden setzen
|
||||
SetVehicleOnGroundProperly(veh)
|
||||
|
||||
-- Server über gespawntes Fahrzeug informieren
|
||||
TriggerServerEvent('mh_garage:spawnedVehicle', netId, vehicle.plate)
|
||||
|
||||
-- Optional: Erfolgsmeldung
|
||||
lib.notify({
|
||||
title = "Fahrzeug geparkt...",
|
||||
description = "Dein Fahrzeug steht auf Parkplatz "..math.random(1, 15),
|
||||
type = "success"
|
||||
})
|
||||
end, vector3(spawnPoint.x, spawnPoint.y, spawnPoint.z), true)
|
||||
else
|
||||
QBCore.Functions.Notify('Alle Parkplätze sind belegt!', 'error')
|
||||
lib.notify({
|
||||
title = "Fahrzeug nicht gefunden",
|
||||
description = "Alle Parkplätze sind belegt!",
|
||||
type = "success"
|
||||
})
|
||||
end
|
||||
end
|
131
resources/[Developer]/mh_garage/client/stored.lua
Normal file
131
resources/[Developer]/mh_garage/client/stored.lua
Normal file
|
@ -0,0 +1,131 @@
|
|||
RegisterNetEvent('mh_garage:storeVehicle')
|
||||
AddEventHandler('mh_garage:storeVehicle', function()
|
||||
local ped = PlayerPedId()
|
||||
local coords = GetEntityCoords(ped)
|
||||
local vehicles = GetGamePool('CVehicle')
|
||||
local random = SelectName()
|
||||
|
||||
local opt = {}
|
||||
for i = 1, #vehicles, 1 do
|
||||
local veh_coords = GetEntityCoords(vehicles[i])
|
||||
local distance = #(veh_coords - coords)
|
||||
if distance < 15.0 then
|
||||
local mods = QBCore.Functions.GetVehicleProperties(vehicles[i])
|
||||
local lc_fuelLevel = exports["lc_fuel"]:GetFuel(vehicles[i]) -- Get the fuel level
|
||||
|
||||
mods.fuelLevel = lc_fuelLevel
|
||||
table.insert(opt, {
|
||||
title = "Kennzeichen: "..mods.plate,
|
||||
description = GetRandomCarDescription(),
|
||||
icon = "car",
|
||||
onSelect = function()
|
||||
print(CurrentZone.price)
|
||||
if CurrentZone.price ~= nil then
|
||||
lib.hideContext("StoredVehicles")
|
||||
|
||||
lib.registerContext({
|
||||
id = "thisVehicle",
|
||||
title = random.name,
|
||||
options = {
|
||||
{title = "Kosten: "..CurrentZone.price.."$"},
|
||||
{title = "", disabled = true},
|
||||
{
|
||||
title = "Akzeptieren",
|
||||
description = "Geld wird vom Bankkonto abgebucht!",
|
||||
icon = "check",
|
||||
onSelect = function()
|
||||
lib.hideContext("thisVehicle")
|
||||
QBCore.Functions.TriggerCallback('mh_garage:storedVehicle', function(cb)
|
||||
if cb.status then
|
||||
TriggerServerEvent('mh_Parking:deleteVehicle', mods.plate, NetworkGetNetworkIdFromEntity(vehicles[i]))
|
||||
--DeleteVehicle(vehicles[i])
|
||||
Notification(cb.text, cb.type, CurrentZone.name)
|
||||
else
|
||||
if cb.police and Config.EnabledPolice then
|
||||
exports["roadphone"]:sendDispatch("Hier hat grade jemand versucht, ein Fahrzeug einzuparken.\nDas Zündschloss sah Beschädigt aus.\nKennzeichen: "..veh.plate, 'police', nil)
|
||||
end
|
||||
Notification(cb.text, cb.type)
|
||||
end
|
||||
end, mods, CurrentZone)
|
||||
end
|
||||
},
|
||||
{
|
||||
title = "Abbrechen",
|
||||
description = "Das ist sehr Schade,",
|
||||
icon = "close",
|
||||
onSelect = function()
|
||||
lib.hideContext("thisVehicle")
|
||||
end
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
lib.showContext("thisVehicle")
|
||||
else
|
||||
QBCore.Functions.TriggerCallback('mh_garage:storedVehicle', function(cb)
|
||||
if cb.status then
|
||||
TriggerServerEvent('mh_Parking:deleteVehicle', mods.plate, NetworkGetNetworkIdFromEntity(vehicles[i]))
|
||||
--DeleteVehicle(vehicles[i])
|
||||
Notification(cb.text, cb.type, CurrentZone.name)
|
||||
else
|
||||
if cb.police and Config.EnabledPolice then
|
||||
exports["roadphone"]:sendDispatch("Hier hat grade jemand versucht, ein Fahrzeug einzuparken.\nDas Zündschloss sah Beschädigt aus.\nKennzeichen: "..veh.plate, 'police', nil)
|
||||
end
|
||||
Notification(cb.text, cb.type)
|
||||
end
|
||||
end, mods, CurrentZone)
|
||||
end
|
||||
end
|
||||
})
|
||||
lib.registerContext({
|
||||
id = "StoredVehicles",
|
||||
title = random.name.."\n\n"..random.description,
|
||||
options = opt
|
||||
})
|
||||
lib.showContext("StoredVehicles")
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
function SelectName()
|
||||
local names = {
|
||||
{name = "Garagen-Guru",
|
||||
description = "passt auf, dass keiner falsch parkt, Schranken nicht durchdrehen und Autos nicht fliegen!"},
|
||||
{name = "Torflüsterer",
|
||||
description = "Redet Schranken gut zu wenn diese wieder nicht funktionieren."},
|
||||
{name = "Parkplatz-Papst",
|
||||
description = "segnet jedes Auto, das diese heilige Halle betritt. Kein Ölverlust ohne dein Amen!"},
|
||||
{name = "Schranken-Schamane",
|
||||
description = "Elektronik und Technik? Kein Problem! Ich kontrolliere den Strom der Schranken mit reiner Willenskraft und Kabelbinder!"},
|
||||
{name = "Chef vom Schuppen",
|
||||
description = "Wer falsch parkt, kriegt nicht den Abschleppdienst, sondern eine Ansprache. Kurz. Hart. Legendär!"}
|
||||
}
|
||||
|
||||
return names[math.random(1, #names)]
|
||||
end
|
||||
|
||||
function GetRandomCarDescription()
|
||||
local descriptions = {
|
||||
"Das Auto für alle, die beim Beschleunigen mehr Spaß haben als beim Ankommen.",
|
||||
"Wenn du ein Auto suchst, das schneller fährt als dein WiFi, bist du hier richtig.",
|
||||
"Nicht das schnellste Auto, aber hey, es hat vier Räder und eine Hupe!",
|
||||
"Das einzig wahre Fahren – keine GPS-Fehler, nur pure Straßenmagie.",
|
||||
"Mit diesem Auto wirst du die Straße genauso lieben wie das Benzin in deinem Tank.",
|
||||
"Fahr‘ einfach, und schau, wie viele Leute sich fragen, ob du gerade von der Rennstrecke kommst.",
|
||||
"Dieses Auto bringt mehr Nervenkitzel als ein Achterbahn-Abenteuer!",
|
||||
"Dieses Fahrzeug hat mehr Charakter als deine letzte Beziehung.",
|
||||
"Es ist nicht das teuerste Auto, aber es fährt immer noch besser als deine Laune nach dem Montagmorgen!",
|
||||
"Mit diesem Wagen wirst du zur Legende – oder zumindest zum König des Parkhauses.",
|
||||
"Schneller als dein Chef’s Auto, langsamer als dein Instagram-Feed.",
|
||||
"Wenn der Sound des Motors dich mehr motiviert als dein Wecker am Morgen!",
|
||||
"Sicheres Fahren ist wichtig. Aber Spaß haben – das ist die wahre Kunst!",
|
||||
"Nicht der neueste Sportwagen, aber dafür ein echter Klassiker auf der Straße.",
|
||||
"Weniger PS als dein Laptop, aber hey, er fährt!"
|
||||
}
|
||||
|
||||
-- Zufällige Beschreibung auswählen
|
||||
local randomIndex = math.random(1, #descriptions)
|
||||
|
||||
-- Beschreibung zurückgeben
|
||||
return descriptions[randomIndex]
|
||||
end
|
44
resources/[Developer]/mh_garage/client/vehicleadmin.lua
Normal file
44
resources/[Developer]/mh_garage/client/vehicleadmin.lua
Normal file
|
@ -0,0 +1,44 @@
|
|||
-- vehicleadmin.lua - Füge diese Datei in deinen client-Ordner ein
|
||||
|
||||
-- Füge einen Menüpunkt für das Fahrzeugadmin-System zum NPC-Menü hinzu
|
||||
-- Diese Funktion wird aufgerufen, wenn der NPC-Target erstellt wird
|
||||
local function AddVehicleAdminOption()
|
||||
-- Prüfe, ob der Spieler die Berechtigung hat
|
||||
QBCore.Functions.TriggerCallback('vehicleadmin:getPlayerJob', function(jobData)
|
||||
if jobData and jobData.hasPermission then
|
||||
-- Füge den Menüpunkt zum NPC hinzu, wenn der Spieler berechtigt ist
|
||||
exports['qb-target']:AddTargetEntity(npcHandle, {
|
||||
options = {
|
||||
{
|
||||
type = "client",
|
||||
event = "vehicleadmin:openMenu",
|
||||
icon = "fas fa-car-mechanic",
|
||||
label = "Fahrzeugverwaltung",
|
||||
}
|
||||
},
|
||||
distance = 2.5
|
||||
})
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Registriere einen Event-Handler, der nach dem Hinzufügen der Standard-Target-Optionen ausgeführt wird
|
||||
RegisterNetEvent('mh_garage:targetOptionsAdded')
|
||||
AddEventHandler('mh_garage:targetOptionsAdded', function()
|
||||
AddVehicleAdminOption()
|
||||
end)
|
||||
|
||||
-- Füge einen Hook in die bestehende AddTargetOptions-Funktion ein
|
||||
local originalAddTargetOptions = AddTargetOptions
|
||||
AddTargetOptions = function()
|
||||
originalAddTargetOptions()
|
||||
TriggerEvent('mh_garage:targetOptionsAdded')
|
||||
end
|
||||
|
||||
-- Registriere den Befehl für das Fahrzeugadmin-System
|
||||
RegisterCommand('vehicleadmin', function()
|
||||
TriggerEvent('vehicleadmin:openMenu')
|
||||
end, false)
|
||||
|
||||
-- Registriere die Tastenbelegung (optional)
|
||||
RegisterKeyMapping('vehicleadmin', 'Öffne Fahrzeug Admin Menu', 'keyboard', '')
|
|
@ -0,0 +1,56 @@
|
|||
-- vehicleadmin_integration.lua
|
||||
-- Diese Datei integriert das Fahrzeugadmin-System mit dem Garagensystem
|
||||
|
||||
-- Füge einen Menüpunkt für das Fahrzeugadmin-System zum NPC-Menü hinzu
|
||||
local function AddVehicleAdminOption()
|
||||
-- Prüfe, ob der Spieler die Berechtigung hat
|
||||
QBCore.Functions.TriggerCallback('vehicleadmin:getPlayerJob', function(jobData)
|
||||
if jobData and jobData.hasPermission then
|
||||
-- Füge den Menüpunkt zum NPC hinzu, wenn der Spieler berechtigt ist
|
||||
if npcHandle and DoesEntityExist(npcHandle) then
|
||||
exports['qb-target']:AddTargetEntity(npcHandle, {
|
||||
options = {
|
||||
{
|
||||
type = "client",
|
||||
event = "vehicleadmin:openMenu",
|
||||
icon = "fas fa-car-mechanic",
|
||||
label = "Fahrzeugverwaltung",
|
||||
}
|
||||
},
|
||||
distance = 2.5
|
||||
})
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Überschreibe die AddTargetOptions-Funktion, um unsere Option hinzuzufügen
|
||||
local originalAddTargetOptions = AddTargetOptions
|
||||
if originalAddTargetOptions then
|
||||
AddTargetOptions = function()
|
||||
originalAddTargetOptions()
|
||||
AddVehicleAdminOption()
|
||||
end
|
||||
end
|
||||
|
||||
-- Registriere einen Event-Handler für das Hinzufügen der Target-Optionen
|
||||
RegisterNetEvent('mh_garage:targetOptionsAdded')
|
||||
AddEventHandler('mh_garage:targetOptionsAdded', function()
|
||||
AddVehicleAdminOption()
|
||||
end)
|
||||
|
||||
-- Füge einen Event-Handler hinzu, der nach dem Spawnen des NPCs ausgeführt wird
|
||||
RegisterNetEvent('mh_garage:npcSpawned')
|
||||
AddEventHandler('mh_garage:npcSpawned', function()
|
||||
Wait(500) -- Warte kurz, damit der NPC vollständig gespawnt ist
|
||||
AddVehicleAdminOption()
|
||||
end)
|
||||
|
||||
-- Füge einen Hook in die SpawnGuardNPC-Funktion ein, um unseren Event auszulösen
|
||||
local originalSpawnGuardNPC = SpawnGuardNPC
|
||||
if originalSpawnGuardNPC then
|
||||
SpawnGuardNPC = function(npc)
|
||||
originalSpawnGuardNPC(npc)
|
||||
TriggerEvent('mh_garage:npcSpawned')
|
||||
end
|
||||
end
|
76
resources/[Developer]/mh_garage/client/verwaltung.lua
Normal file
76
resources/[Developer]/mh_garage/client/verwaltung.lua
Normal file
|
@ -0,0 +1,76 @@
|
|||
RegisterNetEvent('mh_garage:verwaltungVeh')
|
||||
AddEventHandler('mh_garage:verwaltungVeh', function()
|
||||
QBCore.TriggerCallback('mh_garage:verwaltung', function(cb)
|
||||
Debug("Verwaltung CB: "..json.encode(cb))
|
||||
if cb[1] ~= nil then
|
||||
local opt = {}
|
||||
|
||||
for i = 1, #cb, 1 do
|
||||
|
||||
local isingarage = cb[i].current_in_garage
|
||||
local des = ""
|
||||
|
||||
if isingarage then
|
||||
des = "Bearbeite das Fahrzeug\nKennzeichen: "..cb[i].current_plate.."\nGarage: "..cb[i].current_garage
|
||||
else
|
||||
des = "Bearbeite das Fahrzeug\nKennzeichen: "..cb[i].current_plate.."\nLetzte bekannte Garage: "..cb[i].current_garage
|
||||
end
|
||||
|
||||
table.insert(opt, {
|
||||
title = cb[i].current_name,
|
||||
description = des,
|
||||
icon = "car",
|
||||
onSelect = function()
|
||||
OpenVerwaltung(cb[i])
|
||||
end
|
||||
})
|
||||
end
|
||||
else
|
||||
lib.notify({
|
||||
title = "Fahrzeug Verwaltung",
|
||||
description = "Du bist nicht im Besitz eines Fahrzeuges!",
|
||||
type = "inform"
|
||||
})
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
function OpenVerwaltung(vehicleInfos)
|
||||
Debug("OpenVerwaltung: "..json.encode(vehicleInfos))
|
||||
local garages = {}
|
||||
for k, v in pairs(Config.Zonen) do
|
||||
table.insert(garages, v.name)
|
||||
end
|
||||
lib.registerMenu({
|
||||
id = 'some_menu_id',
|
||||
title = 'Menu title',
|
||||
position = 'top-right',
|
||||
onSideScroll = function(selected, scrollIndex, args)
|
||||
print("Scroll: ", selected, scrollIndex, args)
|
||||
end,
|
||||
onSelected = function(selected, secondary, args)
|
||||
if not secondary then
|
||||
print("Normal button")
|
||||
else
|
||||
if args.isCheck then
|
||||
print("Check button")
|
||||
end
|
||||
|
||||
if args.isScroll then
|
||||
print("Scroll button")
|
||||
end
|
||||
end
|
||||
print(selected, secondary, json.encode(args, {indent=true}))
|
||||
end,
|
||||
options = {
|
||||
{label = "Name ändern", icon = "paper"},
|
||||
{label = 'Transport to:', icon = 'arrows-up-down-left-right', values=garages},
|
||||
}
|
||||
}, function(selected, scrollIndex, args)
|
||||
print(selected, scrollIndex, args)
|
||||
end)
|
||||
|
||||
RegisterCommand('testmenu', function()
|
||||
lib.showMenu('some_menu_id')
|
||||
end)
|
||||
end
|
163
resources/[Developer]/mh_garage/config/config.lua
Normal file
163
resources/[Developer]/mh_garage/config/config.lua
Normal file
|
@ -0,0 +1,163 @@
|
|||
Config = {}
|
||||
|
||||
Config.Debug = true
|
||||
|
||||
Config.PriceModel = false
|
||||
|
||||
Config.CallKeyVehicles = true
|
||||
|
||||
Config.Verwaltung = {
|
||||
garage = false,
|
||||
garage_change = 1000, -- Kosten um das Fahrzeug in einer anderen Garage zu versetzen.
|
||||
garage_time = 10, -- Zeit in Minuten bis das Fahrzeug in der neuen Garage ist!
|
||||
|
||||
repair = false,
|
||||
repair_car = 600, -- Kosten wenn das Fahrzeug Repariert werden soll
|
||||
repair_time = 10, --Zeit in Minuten bis das Fahrzeug Repariert ist!
|
||||
|
||||
tank = false,
|
||||
tank_car = 300, -- Kosten wenn das Fahrzeug getankt werden soll
|
||||
tank_time = 3, -- Zeit in Minuten bis das Fahrzeug voll getankt ist
|
||||
}
|
||||
|
||||
Config.Zonen = {
|
||||
{
|
||||
name = "pillboxgarage",
|
||||
price = nil, --pro FZ
|
||||
NPC = {
|
||||
spawn = vector4(213.9830, -808.7876, 30.0149, 166.4462),
|
||||
model = "s_m_m_security_01",
|
||||
distance = 30.0
|
||||
},
|
||||
vehicle_spawn = {
|
||||
vector4(216.0978, -804.5941, 29.7967, 61.7316),
|
||||
vector4(216.4074, -801.9208, 29.7928, 63.4452),
|
||||
vector4(217.3208, -799.2861, 29.7802, 67.0612),
|
||||
vector4(218.2428, -796.9962, 29.7684, 86.0924)
|
||||
}
|
||||
},
|
||||
{
|
||||
name = "grovegarage",
|
||||
price = nil, --pro FZ
|
||||
NPC = {
|
||||
spawn = vector4(-74.8718, -1824.8325, 25.9420, 249.5276),
|
||||
model = "G_F_Y_Families_01",
|
||||
distance = 30.0
|
||||
},
|
||||
vehicle_spawn = {
|
||||
vector4(-62.5727, -1840.5316, 25.6792, 321.5169),
|
||||
vector4(-60.1089, -1842.8113, 25.5816, 323.8478),
|
||||
vector4(-60.1089, -1842.8113, 25.5816, 323.8478),
|
||||
vector4(-54.8264, -1847.2078, 25.3799, 317.6915)
|
||||
}
|
||||
},
|
||||
{
|
||||
name = "vespuccigarage",
|
||||
price = nil, --pro FZ
|
||||
NPC = {
|
||||
spawn = vector4(-1184.6665, -1509.8247, 3.6493, 312.6639),
|
||||
model = "s_m_m_security_01",
|
||||
distance = 30.0
|
||||
},
|
||||
vehicle_spawn = {
|
||||
vector4(-1191.6841, -1504.4095, 3.3688, 303.5921),
|
||||
vector4(-1194.9706, -1500.1947, 3.3634, 310.3644),
|
||||
vector4(-1196.7946, -1496.9948, 3.3652, 303.5580),
|
||||
vector4(-1198.0444, -1493.9160, 3.3705, 311.4306)
|
||||
}
|
||||
},
|
||||
{
|
||||
name = "vinewoodgarage",
|
||||
price = nil, --pro FZ
|
||||
NPC = {
|
||||
spawn = vector4(-340.6176, 266.3422, 84.6795, 22.1944),
|
||||
model = "s_m_m_security_01",
|
||||
distance = 30.0
|
||||
},
|
||||
vehicle_spawn = {
|
||||
vector4(-349.4257, 272.3725, 83.1077, 277.4619),
|
||||
vector4(-349.8885, 276.0246, 83.9945, 276.0025),
|
||||
vector4(-349.4035, 279.3595, 83.9495, 275.8749),
|
||||
vector4(-349.3278, 282.7364, 83.9431, 278.1651)
|
||||
}
|
||||
},
|
||||
{
|
||||
name = "sandygarage",
|
||||
price = nil, --pro FZ
|
||||
NPC = {
|
||||
spawn = vector4(1637.97, 3796.94, 34.21, 189.13),
|
||||
model = "s_m_m_security_01",
|
||||
distance = 30.0
|
||||
},
|
||||
vehicle_spawn = {
|
||||
vector4(1648.57, 3804.84, 33.81, 210.66),
|
||||
vector4(1651.82, 3806.69, 33.82, 209.34),
|
||||
vector4(1655.3, 3808.84, 33.83, 217.08),
|
||||
vector4(1659.01, 3810.84, 33.84, 216.15)
|
||||
|
||||
}
|
||||
},
|
||||
{
|
||||
name = "paletogarage",
|
||||
price = nil, --pro FZ
|
||||
NPC = {
|
||||
spawn = vector4(67.5230, 6412.9517, 30.4838, 230.1375),
|
||||
model = "s_m_m_security_01",
|
||||
distance = 30.0
|
||||
},
|
||||
vehicle_spawn = {
|
||||
vector4(64.5739, 6406.8584, 30.2258, 208.3274),
|
||||
vector4(61.9570, 6404.0493, 30.2258, 202.9158),
|
||||
vector4(59.0419, 6401.0122, 30.2258, 211.3612),
|
||||
vector4(73.0162, 6404.4741, 30.2258, 131.4993)
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
name = "eastsidegarage",
|
||||
price = nil, --pro FZ
|
||||
NPC = {
|
||||
spawn = vector4(1010.1598, -2289.5979, 29.5095, 186.2906),
|
||||
model = "s_m_m_security_01",
|
||||
distance = 30.0
|
||||
},
|
||||
vehicle_spawn = {
|
||||
vector4(1003.6630, -2299.5208, 29.5095, 266.1712),
|
||||
vector4(1005.3187, -2304.5198, 29.5095, 261.7787),
|
||||
vector4(1005.2725, -2308.4385, 29.5095, 277.7379),
|
||||
vector4(1004.2752, -2313.1870, 29.5095, 268.2005)
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
name = "cayogarage",
|
||||
price = nil, --pro FZ
|
||||
NPC = {
|
||||
spawn = vector4(4505.5659, -4549.3335, 3.0960, 69.6222),
|
||||
model = "IG_JuanStrickler",
|
||||
distance = 30.0
|
||||
},
|
||||
vehicle_spawn = {
|
||||
vector4(4501.0264, -4546.2852, 3.0278, 24.5729),
|
||||
vector4(4497.0498, -4536.6616, 3.1636, 276.0185),
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
}
|
||||
|
||||
Config.Log = {
|
||||
Webhook = "https://discord.com/api/webhooks/1366812966049288192/9ZjJC9_gLX6Fk-acf0YSW_haGWpCqG9zRGWaj0wCKLZefp8FX-GwNZBP77H6K93KfIw3",
|
||||
Color = {
|
||||
green = 56108,
|
||||
grey = 8421504,
|
||||
red = 16711680,
|
||||
orange = 16744192,
|
||||
blue = 2061822,
|
||||
purple = 11750815,
|
||||
},
|
||||
|
||||
SystemName = "Evolution_State_life Log [Garage]",
|
||||
UserAvatar = '',
|
||||
SystemAvatar = '',
|
||||
}
|
3
resources/[Developer]/mh_garage/config/price.lua
Normal file
3
resources/[Developer]/mh_garage/config/price.lua
Normal file
|
@ -0,0 +1,3 @@
|
|||
cprice = {
|
||||
["asea"] = 3,
|
||||
}
|
19
resources/[Developer]/mh_garage/fxmanifest.lua
Normal file
19
resources/[Developer]/mh_garage/fxmanifest.lua
Normal file
|
@ -0,0 +1,19 @@
|
|||
author 'Mîhó'
|
||||
|
||||
fx_version 'adamant'
|
||||
game 'gta5'
|
||||
lua54 'yes'
|
||||
|
||||
client_scripts {
|
||||
'@ox_lib/init.lua',
|
||||
'config/*.lua',
|
||||
'client/*.lua',
|
||||
'client/vehicleadmin.lua'
|
||||
}
|
||||
|
||||
server_scripts {
|
||||
'@oxmysql/lib/MySQL.lua',
|
||||
'config/*.lua',
|
||||
'server/*.lua',
|
||||
'server/vehicleadmin.lua'
|
||||
}
|
59
resources/[Developer]/mh_garage/server/log.lua
Normal file
59
resources/[Developer]/mh_garage/server/log.lua
Normal file
|
@ -0,0 +1,59 @@
|
|||
function sendToDiscord(Titel, Text, Color) ------ Sende Nachricht an Channel: Join/Left
|
||||
local embeds = {
|
||||
{
|
||||
["title"]=Text,
|
||||
["type"]="rich",
|
||||
["color"] =Config.Log.Color[Color],
|
||||
["footer"]= {
|
||||
["text"]= Config.Log.SystemName,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
PerformHttpRequest(Config.Log.Webhook, function(err, text, headers)
|
||||
if err and err ~= 204 and err ~= 200 then
|
||||
print("Fehler beim Discord Webhook [" .. tostring(err) .. "]: " .. tostring(text))
|
||||
end
|
||||
end, 'POST', json.encode({ username = Titel.." - Mîhó",avatar_url = Config.SystemAvatar, embeds = embeds, tts = TTS}), { ['Content-Type'] = 'application/json' })
|
||||
end
|
||||
|
||||
AddEventHandler('mh_garage:log')
|
||||
RegisterServerEvent('mh_garage:log', function(type)
|
||||
local Player = QBCore.Functions.GetPlayer(source)
|
||||
local PlyData = Player.PlayerData
|
||||
local Color = "purple"
|
||||
|
||||
Playerinfo = {
|
||||
name = PlyData.charinfo.firstname .. " ".. PlyData.charinfo.lastname,
|
||||
citizenid = PlyData.citizenid,
|
||||
}
|
||||
local user = Playerinfo.name.."[".. Playerinfo.citizenid .."]"
|
||||
|
||||
if type == "call" then
|
||||
Text = "Dr. Teddy wurde von " .. user .." gerufen."
|
||||
Color = "grey"
|
||||
elseif type == "cash" then
|
||||
Text = user.." hat Bar Bezahlt."
|
||||
Color = "green"
|
||||
elseif type == "bank" then
|
||||
Text = user .. "hat via Überweisung Bezahlt."
|
||||
Color = "green"
|
||||
elseif type == "noMoney" then
|
||||
Text = user .." Hatte nicht genug Geld."
|
||||
Color = "orange"
|
||||
elseif type == "noPlayer" then
|
||||
Text = user .. " Wurde nicht gefunden und abgebrochen!"
|
||||
Color = "red"
|
||||
elseif type == "storno" then
|
||||
Text = user .. " Hat die Behandlung doch Abgelehnt."
|
||||
Color = "red"
|
||||
elseif type == "heal" then
|
||||
Text = user.. " Wurde Geheilt."
|
||||
Color = "green"
|
||||
elseif type == "end" then
|
||||
Text = "NPC wurde wieder Entfernt..."
|
||||
Color = "blue"
|
||||
end
|
||||
|
||||
sendToDiscord("Garage", Text, Color)
|
||||
end)
|
159
resources/[Developer]/mh_garage/server/server.lua
Normal file
159
resources/[Developer]/mh_garage/server/server.lua
Normal file
|
@ -0,0 +1,159 @@
|
|||
QBCore = exports['qb-core']:GetCoreObject()
|
||||
local test_vari = {}
|
||||
|
||||
RegisterServerEvent('mh_garage:setMods')
|
||||
AddEventHandler('mh_garage:setMods', function(mods)
|
||||
if test_vari[mods.plate] == true then
|
||||
return
|
||||
else
|
||||
MySQL.query("SELECT mods FROM player_vehicles WHERE plate = ?", {mods.plate}, function(rs)
|
||||
-- Prüfen ob rs überhaupt Daten enthält
|
||||
if rs and rs[1] then
|
||||
-- Wenn mods ein String ist, konvertieren wir es zu einem Table
|
||||
local modsData = type(rs[1].mods) == "string" and json.decode(rs[1].mods) or rs[1].mods
|
||||
|
||||
if not modsData or next(modsData) == nil then
|
||||
-- Hier sollten Sie wahrscheinlich auch die mods-Daten übergeben, nicht nur plate
|
||||
MySQL.query("UPDATE player_vehicles SET mods = ? WHERE plate = ?", {json.encode(mods), mods.plate})
|
||||
test_vari[mods.plate] = true
|
||||
else
|
||||
test_vari[mods.plate] = true
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
end)
|
||||
|
||||
QBCore.Functions.CreateCallback('mh_garage:storedVehicle', function(source, cb, veh, zone)
|
||||
local Player = QBCore.Functions.GetPlayer(source)
|
||||
|
||||
if Player ~= nil then
|
||||
if Player.Functions.GetMoney('bank', zone.price) then
|
||||
MySQL.query("SELECT * FROM player_vehicles WHERE citizenid = ? AND plate = ?", {Player.PlayerData.citizenid, veh.plate}, function(rs)
|
||||
if rs[1] ~= nil then
|
||||
MySQL.query("UPDATE player_vehicles SET garage = ?, mods = ? WHERE plate = ?", {zone.name, json.encode(veh), veh.plate})
|
||||
DelVehParking(veh.plate)
|
||||
cb({
|
||||
status = true,
|
||||
text = "Fahrzeug erfolgreich eingeparkt!",
|
||||
type = "success",
|
||||
other = false,
|
||||
police = false
|
||||
})
|
||||
else
|
||||
MySQL.query("SELECT * FROM vehicle_keys WHERE owner = ? AND plate = ?", {Player.PlayerData.citizenid, veh.plate}, function(rs)
|
||||
if rs[1] ~= nil then
|
||||
MySQL.query("UPDATE player_vehicles SET garage = ?, mods = ? WHERE plate = ?", {zone.name, json.encode(veh), veh.plate})
|
||||
--TriggerEvent('mh_Parking:removeVehicle', source, veh.plate)
|
||||
--DelVehParking(veh.plate)
|
||||
cb({
|
||||
status = true,
|
||||
text = "Fahrzeug erfolgreich eingeparkt!",
|
||||
type = "success",
|
||||
other = true,
|
||||
police = false
|
||||
})
|
||||
else
|
||||
cb({
|
||||
status = false,
|
||||
text = "Du besitzt kein Schlüssel für dieses Fahrzeug.",
|
||||
type = "warning",
|
||||
other = false,
|
||||
police = true
|
||||
})
|
||||
end
|
||||
end)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
function DelVehParking(plate)
|
||||
MySQL.query("SELECT * FROM vehicle_parking WHERE plate = ?", {plate}, function(rs)
|
||||
if rs[1] ~= nil then
|
||||
MySQL.query("DELETE FROM vehicle_parking WHERE plate = ?", {plate})
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
QBCore.Functions.CreateCallback('mh_garage:retrieveOwnerVehicle', function(source, cb, zone)
|
||||
local _source = source
|
||||
local Player = QBCore.Functions.GetPlayer(_source)
|
||||
local pedid = Player.PlayerData.citizenid
|
||||
local veh = {}
|
||||
|
||||
MySQL.query("SELECT * FROM player_vehicles WHERE citizenid = ? and garage = ?", {pedid, zone}, function(rs)
|
||||
if rs ~= nil and rs[1] ~= nil then
|
||||
for k, v in pairs (rs) do
|
||||
table.insert(veh, {
|
||||
vehicle = v.vehicle,
|
||||
mods = v.mods,
|
||||
plate = v.plate,
|
||||
name = v.name
|
||||
})
|
||||
end
|
||||
Wait(100)
|
||||
cb(veh)
|
||||
else
|
||||
cb(false)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
QBCore.Functions.CreateCallback('mh_garage:retrieveKeyVehicle', function(source, cb, zone)
|
||||
local _source = source
|
||||
local Player = QBCore.Functions.GetPlayer(_source)
|
||||
local pedid = Player.PlayerData.citizenid
|
||||
local veh = {}
|
||||
|
||||
MySQL.query("SELECT pv.* FROM player_vehicles pv JOIN vehicle_keys vk ON pv.plate = vk.plate WHERE vk.owner = ? AND vk.count > 0 AND pv.garage = ?", {pedid, zone}, function(vehicles)
|
||||
if vehicles and #vehicles > 0 then
|
||||
for _, vehicle in pairs(vehicles) do
|
||||
table.insert(veh, {
|
||||
vehicle = vehicle.vehicle,
|
||||
mods = vehicle.mods,
|
||||
plate = vehicle.plate,
|
||||
name = vehicle.name
|
||||
})
|
||||
end
|
||||
Wait(100)
|
||||
cb(veh)
|
||||
else
|
||||
cb(false)
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
QBCore.Functions.CreateCallback('mh_garage:verwaltung', function(source, cb)
|
||||
local Player = QBCore.Functions.GetPlayer(source)
|
||||
local vehicles = {}
|
||||
MySQL.query("SELECT * FROM player_vehicles WHERE citizenid = ?", {Player.PlayerData.citizenid}, function(rs)
|
||||
if rs and rs[1] then
|
||||
for k, v in pairs(rs) do
|
||||
table.insert(vehicles, {
|
||||
current_garage = v.garage,
|
||||
current_in_garage = v.parking,
|
||||
current_name = v.name,
|
||||
current_plate = v.plate,
|
||||
})
|
||||
end
|
||||
end
|
||||
end)
|
||||
return vehicles
|
||||
end)
|
||||
|
||||
RegisterServerEvent('mh_garage:spawnedVehicle')
|
||||
AddEventHandler('mh_garage:spawnedVehicle', function(netID, plate)
|
||||
MySQL.query("UPDATE player_vehicles SET garage = ? WHERE plate = ?", {"OUT", plate})
|
||||
end)
|
||||
|
||||
QBCore.Functions.CreateCallback('mh_garage:CheckownerVehicles', function(source, cb, plate)
|
||||
MySQL.query("SELECT * FROM player_vehicles WHERE plate = ?", {plate}, function(rs)
|
||||
if rs ~= nil and rs[1] ~= nil then
|
||||
cb(true)
|
||||
else
|
||||
cb(false)
|
||||
end
|
||||
end)
|
||||
end)
|
181
resources/[Developer]/mh_garage/server/vehicleadmin.lua
Normal file
181
resources/[Developer]/mh_garage/server/vehicleadmin.lua
Normal file
|
@ -0,0 +1,181 @@
|
|||
-- vehicleadmin.lua - Füge diese Datei in deinen server-Ordner ein
|
||||
|
||||
-- Erweitere die Log-Funktion für das Fahrzeugadmin-System
|
||||
RegisterServerEvent('vehicleadmin:log')
|
||||
AddEventHandler('vehicleadmin:log', function(action, plate, garage)
|
||||
local src = source
|
||||
local Player = QBCore.Functions.GetPlayer(src)
|
||||
|
||||
if not Player then return end
|
||||
|
||||
local playerName = Player.PlayerData.charinfo.firstname .. " " .. Player.PlayerData.charinfo.lastname
|
||||
local citizenid = Player.PlayerData.citizenid
|
||||
local jobName = Player.PlayerData.job.name
|
||||
local jobLabel = Player.PlayerData.job.label
|
||||
|
||||
local logText = ""
|
||||
local logColor = "blue"
|
||||
|
||||
if action == "move" then
|
||||
logText = playerName .. " (" .. jobLabel .. ") hat Fahrzeug " .. plate .. " in Garage " .. garage .. " gestellt."
|
||||
elseif action == "delete" then
|
||||
logText = playerName .. " (" .. jobLabel .. ") hat Fahrzeug " .. plate .. " von der Map gelöscht."
|
||||
logColor = "orange"
|
||||
elseif action == "repair" then
|
||||
logText = playerName .. " (" .. jobLabel .. ") hat Fahrzeug " .. plate .. " repariert."
|
||||
logColor = "green"
|
||||
end
|
||||
|
||||
-- Verwende die bestehende Log-Funktion
|
||||
sendToDiscord("Fahrzeugadmin", logText, logColor)
|
||||
|
||||
-- Speichere den Log in der Datenbank (optional)
|
||||
MySQL.Async.execute('INSERT INTO admin_logs (action, player_name, citizenid, job, target_plate, target_garage, timestamp) VALUES (?, ?, ?, ?, ?, ?, NOW())',
|
||||
{action, playerName, citizenid, jobName, plate, garage or "none"})
|
||||
end)
|
||||
|
||||
-- Füge zusätzliche Callback-Funktionen für erweiterte Features hinzu
|
||||
QBCore.Functions.CreateCallback('vehicleadmin:getVehicleHistory', function(source, cb, plate)
|
||||
local src = source
|
||||
local Player = QBCore.Functions.GetPlayer(src)
|
||||
|
||||
if not Player then
|
||||
cb(false)
|
||||
return
|
||||
end
|
||||
|
||||
-- Prüfe, ob der Spieler die Berechtigung hat
|
||||
local hasPermission = false
|
||||
local playerJob = Player.PlayerData.job.name
|
||||
|
||||
if QBCore.Functions.HasPermission(src, 'admin') then
|
||||
hasPermission = true
|
||||
elseif playerJob == 'police' or playerJob == 'mechanic' then
|
||||
hasPermission = true
|
||||
end
|
||||
|
||||
if not hasPermission then
|
||||
cb(false)
|
||||
return
|
||||
end
|
||||
|
||||
-- Hole den Fahrzeugverlauf aus der Datenbank
|
||||
MySQL.Async.fetchAll('SELECT * FROM admin_logs WHERE target_plate = ? ORDER BY timestamp DESC LIMIT 20', {plate}, function(results)
|
||||
if results and #results > 0 then
|
||||
cb(results)
|
||||
else
|
||||
cb({})
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
-- Füge einen Event-Handler für das Löschen von Fahrzeugen hinzu
|
||||
RegisterServerEvent('vehicleadmin:deleteVehicle')
|
||||
AddEventHandler('vehicleadmin:deleteVehicle', function(plate)
|
||||
local src = source
|
||||
local Player = QBCore.Functions.GetPlayer(src)
|
||||
|
||||
if not Player then return end
|
||||
|
||||
-- Prüfe, ob der Spieler die Berechtigung hat
|
||||
local hasPermission = false
|
||||
local playerJob = Player.PlayerData.job.name
|
||||
|
||||
if QBCore.Functions.HasPermission(src, 'admin') then
|
||||
hasPermission = true
|
||||
elseif playerJob == 'police' or playerJob == 'mechanic' then
|
||||
hasPermission = true
|
||||
end
|
||||
|
||||
if not hasPermission then
|
||||
TriggerClientEvent('ox_lib:notify', src, {
|
||||
title = 'Keine Berechtigung',
|
||||
description = 'Du hast keine Berechtigung für diese Aktion',
|
||||
type = 'error'
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
-- Lösche das Fahrzeug aus der Datenbank
|
||||
MySQL.Async.execute('DELETE FROM player_vehicles WHERE plate = ?', {plate}, function(rowsChanged)
|
||||
if rowsChanged > 0 then
|
||||
-- Lösche auch alle zugehörigen Schlüssel
|
||||
MySQL.Async.execute('DELETE FROM vehicle_keys WHERE plate = ?', {plate})
|
||||
|
||||
-- Lösche das Fahrzeug aus dem Parking-System
|
||||
MySQL.Async.execute('DELETE FROM vehicle_parking WHERE plate = ?', {plate})
|
||||
|
||||
-- Log die Aktion
|
||||
TriggerEvent('vehicleadmin:log', "delete_permanent", plate, nil)
|
||||
|
||||
TriggerClientEvent('ox_lib:notify', src, {
|
||||
title = 'Fahrzeugverwaltung',
|
||||
description = 'Fahrzeug wurde permanent gelöscht',
|
||||
type = 'success'
|
||||
})
|
||||
else
|
||||
TriggerClientEvent('ox_lib:notify', src, {
|
||||
title = 'Fahrzeugverwaltung',
|
||||
description = 'Fahrzeug nicht gefunden',
|
||||
type = 'error'
|
||||
})
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|
||||
-- Füge einen Event-Handler für das Ändern des Fahrzeugbesitzers hinzu
|
||||
RegisterServerEvent('vehicleadmin:changeOwner')
|
||||
AddEventHandler('vehicleadmin:changeOwner', function(plate, newOwnerCID)
|
||||
local src = source
|
||||
local Player = QBCore.Functions.GetPlayer(src)
|
||||
|
||||
if not Player then return end
|
||||
|
||||
-- Prüfe, ob der Spieler die Berechtigung hat
|
||||
if not QBCore.Functions.HasPermission(src, 'admin') then
|
||||
TriggerClientEvent('ox_lib:notify', src, {
|
||||
title = 'Keine Berechtigung',
|
||||
description = 'Nur Admins können den Besitzer ändern',
|
||||
type = 'error'
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
-- Prüfe, ob der neue Besitzer existiert
|
||||
MySQL.Async.fetchAll('SELECT * FROM players WHERE citizenid = ?', {newOwnerCID}, function(players)
|
||||
if not players or #players == 0 then
|
||||
TriggerClientEvent('ox_lib:notify', src, {
|
||||
title = 'Fehler',
|
||||
description = 'Spieler mit dieser Citizen ID nicht gefunden',
|
||||
type = 'error'
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
-- Ändere den Besitzer des Fahrzeugs
|
||||
MySQL.Async.execute('UPDATE player_vehicles SET citizenid = ? WHERE plate = ?', {newOwnerCID, plate}, function(rowsChanged)
|
||||
if rowsChanged > 0 then
|
||||
-- Erstelle einen neuen Schlüssel für den neuen Besitzer
|
||||
MySQL.Async.execute('INSERT INTO vehicle_keys (plate, owner, count) VALUES (?, ?, 1) ON DUPLICATE KEY UPDATE count = 1', {plate, newOwnerCID})
|
||||
|
||||
-- Log die Aktion
|
||||
local newOwnerInfo = json.decode(players[1].charinfo)
|
||||
local newOwnerName = newOwnerInfo.firstname .. " " .. newOwnerInfo.lastname
|
||||
|
||||
TriggerEvent('vehicleadmin:log', "change_owner", plate, newOwnerName)
|
||||
|
||||
TriggerClientEvent('ox_lib:notify', src, {
|
||||
title = 'Fahrzeugverwaltung',
|
||||
description = 'Fahrzeugbesitzer wurde geändert',
|
||||
type = 'success'
|
||||
})
|
||||
else
|
||||
TriggerClientEvent('ox_lib:notify', src, {
|
||||
title = 'Fahrzeugverwaltung',
|
||||
description = 'Fahrzeug nicht gefunden',
|
||||
type = 'error'
|
||||
})
|
||||
end
|
||||
end)
|
||||
end)
|
||||
end)
|
954
resources/[Developer]/sn_vehicleKey/client.lua
Normal file
954
resources/[Developer]/sn_vehicleKey/client.lua
Normal file
|
@ -0,0 +1,954 @@
|
|||
-- error when both menus are enabled
|
||||
if (Config.useContextMenu and Config.useNativeUI) then
|
||||
print("^1[ERROR] You can use only one menu or no menu at all. You cannot use both menus."
|
||||
.. "\nMake sure to set at least one to false in the config and to edit the fxmanifest accordingly!")
|
||||
end
|
||||
|
||||
local keymakers = {}
|
||||
|
||||
local lockNotif = nil
|
||||
local createNewKeyNotif = nil
|
||||
|
||||
local LockStatus = {
|
||||
Unlocked = 1,
|
||||
Locked = 2
|
||||
}
|
||||
|
||||
local CB = exports["kimi_callbacks"]
|
||||
|
||||
local menuPoolNativeUI
|
||||
local keymakerMenuNativeUI
|
||||
local keyInvMenuNativeUI
|
||||
if (Config.useNativeUI) then
|
||||
if (NativeUI == nil) then
|
||||
print("^1[ERROR] NativeUI was not properly initialized! Make sure to install NativeUI and start it before this resource!")
|
||||
end
|
||||
|
||||
menuPoolNativeUI = NativeUI.CreatePool()
|
||||
keymakerMenuNativeUI = NativeUI.CreateMenu(Config.Strings.keymakerTitle, Config.Strings.keymakerSub)
|
||||
keyInvMenuNativeUI = NativeUI.CreateMenu(Config.Strings.keyInvTitle, Config.Strings.keyInvSub)
|
||||
end
|
||||
|
||||
local isAtKeymaker = false
|
||||
local menuOpen = false
|
||||
|
||||
-- create client side peds
|
||||
Citizen.CreateThread(function()
|
||||
for i, keymaker in ipairs(Config.Keymakers) do
|
||||
RequestModel(keymaker.model)
|
||||
while not HasModelLoaded(keymaker.model) do
|
||||
Citizen.Wait(0)
|
||||
end
|
||||
local ped = CreatePed(0, keymaker.model, keymaker.pos.x, keymaker.pos.y, keymaker.pos.z, keymaker.pos.w, false, true)
|
||||
|
||||
SetBlockingOfNonTemporaryEvents(ped, true)
|
||||
FreezeEntityPosition(ped, true)
|
||||
SetEntityInvincible(ped, true)
|
||||
|
||||
table.insert(keymakers, ped)
|
||||
|
||||
-- add blip
|
||||
local blip = AddBlipForCoord(keymaker.pos.x, keymaker.pos.y, keymaker.pos.z)
|
||||
|
||||
SetBlipSprite(blip, 134)
|
||||
SetBlipColour(blip, 0)
|
||||
|
||||
SetBlipScale(blip, 1.0)
|
||||
SetBlipDisplay(blip, 2)
|
||||
SetBlipAsShortRange(blip, true)
|
||||
|
||||
BeginTextCommandSetBlipName('STRING')
|
||||
AddTextComponentSubstringPlayerName(Config.Strings.keymaker)
|
||||
EndTextCommandSetBlipName(blip)
|
||||
end
|
||||
end)
|
||||
|
||||
-- main loop
|
||||
Citizen.CreateThread(function()
|
||||
while (true) do
|
||||
Citizen.Wait(0)
|
||||
|
||||
if (Config.useNativeUI) then
|
||||
if (menuOpen) then
|
||||
menuPoolNativeUI:ProcessMenus()
|
||||
end
|
||||
|
||||
if (isAtKeymaker) then
|
||||
if (not menuOpen) then
|
||||
ShowHelpText(Config.Strings.helpText)
|
||||
end
|
||||
|
||||
if (IsControlJustPressed(0, 51)) then
|
||||
if (menuPoolNativeUI:IsAnyMenuOpen()) then
|
||||
menuPoolNativeUI:CloseAllMenus()
|
||||
menuOpen = false
|
||||
else
|
||||
GenerateKeymakerMenuNativeUI()
|
||||
keymakerMenuNativeUI:Visible(true)
|
||||
menuOpen = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if (IsControlJustPressed(0, Config.keyMenuKey)) then
|
||||
if (menuPoolNativeUI:IsAnyMenuOpen()) then
|
||||
menuPoolNativeUI:CloseAllMenus()
|
||||
menuOpen = false
|
||||
else
|
||||
GenerateKeyInventoryNativeUI()
|
||||
keyInvMenuNativeUI:Visible(true)
|
||||
menuOpen = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- lock vehicle with key
|
||||
--[[ if (Config.lockKey and IsControlJustPressed(0, Config.lockKey)) then
|
||||
local vehicle = GetClosestVehicle(GetEntityCoords(PlayerPedId()), 10.0)
|
||||
if (DoesEntityExist(vehicle) and IsVehicleOrKeyOwner(vehicle)) then
|
||||
ToggleLock(vehicle, GetVehicleDoorLockStatus(vehicle) ~= LockStatus.Locked)
|
||||
end
|
||||
end ]]
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
|
||||
-- ContextMenu
|
||||
if (Config.useContextMenu) then
|
||||
local menuPool = MenuPool()
|
||||
menuPool.OnOpenMenu = function(screenPosition, hitSomething, worldPosition, hitEntity, normalDirection)
|
||||
local playerPos = GetEntityCoords(PlayerPedId())
|
||||
if (Vdist(worldPosition.x, worldPosition.y, worldPosition.z, playerPos.x, playerPos.y, playerPos.z) < Config.maxDistance) then
|
||||
CreateMenu(screenPosition, hitEntity)
|
||||
end
|
||||
end
|
||||
|
||||
function CreateMenu(screenPosition, hitEntity)
|
||||
menuPool:Reset()
|
||||
|
||||
if (hitEntity) then
|
||||
if (hitEntity == PlayerPedId()) then
|
||||
local vehicleData = CB:Trigger("VKC:getPlayerVehicleData")
|
||||
local ownedKeys = GetPlayerKeys()
|
||||
|
||||
menuPool:Reset()
|
||||
|
||||
local playerMenu = menuPool:AddMenu()
|
||||
|
||||
local ownVehMenu, ownVehMenuItem = menuPool:AddSubmenu(playerMenu, Config.Strings.CM.masterKeysTitle)
|
||||
for i, vehicle in ipairs(vehicleData) do
|
||||
local plate = vehicle[1]
|
||||
local model = GetLabelText(GetDisplayNameFromVehicleModel(vehicle[2]))
|
||||
if (model == "NULL") then
|
||||
model = GetDisplayNameFromVehicleModel(vehicle[2])
|
||||
end
|
||||
|
||||
local item = ownVehMenu:AddItem(model .. " " .. plate)
|
||||
end
|
||||
|
||||
local ownKeyMenu, ownKeyMenuItem = menuPool:AddSubmenu(playerMenu, Config.Strings.CM.keysTitle)
|
||||
for i, key in ipairs(ownedKeys) do
|
||||
local model = GetLabelText(GetDisplayNameFromVehicleModel(key.model))
|
||||
if (model == "NULL") then
|
||||
model = GetDisplayNameFromVehicleModel(key.model)
|
||||
end
|
||||
|
||||
local item = ownKeyMenu:AddItem(model .. " " .. key.plate)
|
||||
item.rightText = Text("x" .. key.count)
|
||||
end
|
||||
|
||||
-- Fahrzeugübergabe-Menü hinzufügen
|
||||
local transferVehMenu, transferVehMenuItem = menuPool:AddSubmenu(playerMenu, "Fahrzeug übergeben")
|
||||
for i, vehicle in ipairs(vehicleData) do
|
||||
local plate = vehicle[1]
|
||||
local model = GetLabelText(GetDisplayNameFromVehicleModel(vehicle[2]))
|
||||
if (model == "NULL") then
|
||||
model = GetDisplayNameFromVehicleModel(vehicle[2])
|
||||
end
|
||||
|
||||
local item = transferVehMenu:AddItem(model .. " " .. plate)
|
||||
item.closeMenuOnClick = true
|
||||
item.OnClick = function()
|
||||
-- Finde den nächsten Spieler
|
||||
local player = GetClosestPlayer(5.0)
|
||||
|
||||
if player then
|
||||
local targetPlayerId = GetPlayerServerId(player)
|
||||
local playerName = GetPlayerName(player)
|
||||
|
||||
local success = CB:Trigger("VKC:transferVehicleOwnership", plate, targetPlayerId)
|
||||
|
||||
if success then
|
||||
Notification(string.format("Du hast dein %s an %s übergeben", model, playerName))
|
||||
else
|
||||
Notification("Übergabe fehlgeschlagen")
|
||||
end
|
||||
else
|
||||
Notification("Kein Spieler in der Nähe gefunden")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
playerMenu:SetPosition(screenPosition)
|
||||
playerMenu:Visible(true)
|
||||
elseif (IsEntityAKeymaker(hitEntity)) then
|
||||
local vehicleData = GetPlayerVehicleData()
|
||||
local ownedKeys = GetPlayerKeys()
|
||||
|
||||
menuPool:Reset()
|
||||
|
||||
local keymakerMenu = menuPool:AddMenu()
|
||||
|
||||
local createKeyMenu, createKeyMenuItem = menuPool:AddSubmenu(keymakerMenu, Config.Strings.CM.createKeyTitle)
|
||||
for i, vehicle in ipairs(vehicleData) do
|
||||
local plate = vehicle[1]
|
||||
local model = GetLabelText(GetDisplayNameFromVehicleModel(vehicle[2]))
|
||||
if (model == "NULL") then
|
||||
model = GetDisplayNameFromVehicleModel(vehicle[2])
|
||||
end
|
||||
local keyCount = GetKeyCount(plate, ownedKeys)
|
||||
|
||||
local keyItem = createKeyMenu:AddItem(model .. " " .. plate)
|
||||
keyItem.rightText = Text("x" .. tostring(keyCount))
|
||||
keyItem.OnClick = function()
|
||||
local result = CB:Trigger("VKC:createNewKey", plate, 1)
|
||||
if (type(result) == "boolean" and result == true) then
|
||||
Notification(string.format(Config.Strings.createSuccess, model))
|
||||
|
||||
local oldCount = keyItem.rightText.title:gsub("x", "")
|
||||
keyItem.rightText.title = "x" .. tostring(tonumber(oldCount) + 1)
|
||||
elseif (type(result) == "string" and result == "noMoney") then
|
||||
Notification(string.format(Config.Strings.createNoMoney, model))
|
||||
else
|
||||
Notification(string.format(Config.Strings.createFailed, model))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local invalidateKeyMenu, invalidateKeyMenuItem = menuPool:AddSubmenu(keymakerMenu, Config.Strings.CM.invalKeyTitle)
|
||||
for i, vehicle in ipairs(vehicleData) do
|
||||
local plate = vehicle[1]
|
||||
local model = GetLabelText(GetDisplayNameFromVehicleModel(vehicle[2]))
|
||||
if (model == "NULL") then
|
||||
model = GetDisplayNameFromVehicleModel(vehicle[2])
|
||||
end
|
||||
|
||||
local item = invalidateKeyMenu:AddItem(model .. " " .. plate)
|
||||
item.closeMenuOnClick = true
|
||||
item.OnClick = function()
|
||||
local result = CB:Trigger("VKC:removeAllKeys", plate)
|
||||
if (type(result) == "boolean" and result == true) then
|
||||
Notification(string.format(Config.Strings.deleteKeys, model))
|
||||
elseif (type(result) == "string" and result == "noMoney") then
|
||||
Notification(string.format(Config.Strings.removeNoMoney, vehName))
|
||||
else
|
||||
Notification(string.format(Config.Strings.removeFailed, vehName))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
keymakerMenu:SetPosition(screenPosition)
|
||||
keymakerMenu:Visible(true)
|
||||
elseif (IsEntityAPed(hitEntity) and IsPedAPlayer(hitEntity)) then
|
||||
local ownedKeys = GetPlayerKeys()
|
||||
|
||||
menuPool:Reset()
|
||||
|
||||
local interactMenu = menuPool:AddMenu()
|
||||
|
||||
local giveKeyMenu, giveKeyMenuItem = menuPool:AddSubmenu(interactMenu, Config.Strings.CM.giveKey)
|
||||
for i, key in ipairs(ownedKeys) do
|
||||
local model = GetLabelText(GetDisplayNameFromVehicleModel(key.model))
|
||||
if (model == "NULL") then
|
||||
model = GetDisplayNameFromVehicleModel(key.model)
|
||||
end
|
||||
|
||||
local keyItem = giveKeyMenu:AddItem(model .. " " .. key.plate)
|
||||
keyItem.rightText = Text("x" .. key.count)
|
||||
keyItem.closeMenuOnClick = true
|
||||
keyItem.OnClick = function()
|
||||
local players = GetActivePlayers()
|
||||
for i, player in ipairs(players) do
|
||||
if (GetPlayerPed(player) == hitEntity) then
|
||||
local success = CB:Trigger("VKC:giveKeyToPlayer", key.plate, GetPlayerServerId(player))
|
||||
if (success) then
|
||||
Notification(string.format(Config.Strings.giveSuccess, model))
|
||||
else
|
||||
Notification(string.format(Config.Strings.giveFailed, model))
|
||||
end
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
interactMenu:SetPosition(screenPosition)
|
||||
interactMenu:Visible(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function IsEntityAKeymaker(entity)
|
||||
for i, keymaker in ipairs(keymakers) do
|
||||
if (entity == keymaker) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- NativeUI
|
||||
function GenerateKeymakerMenuNativeUI()
|
||||
keymakerMenuNativeUI:Clear()
|
||||
|
||||
local vehicleData = GetPlayerVehicleData()
|
||||
local ownedKeys = GetPlayerKeys()
|
||||
|
||||
keymakerMenuNativeUI = NativeUI.CreateMenu(Config.Strings.NUI.keymakerMenuTitle, Config.Strings.NUI.keymakerMenuSub)
|
||||
menuPoolNativeUI:Add(keymakerMenuNativeUI)
|
||||
|
||||
local submenuCreateKey = menuPoolNativeUI:AddSubMenu(keymakerMenuNativeUI, Config.Strings.NUI.createKeyTitle, Config.Strings.NUI.createKeyDesc)
|
||||
submenuCreateKey.ParentItem:RightLabel(">")
|
||||
submenuCreateKey.Subtitle.Text._Text = "~b~" .. Config.Strings.NUI.createKeyTitle
|
||||
for i, vehicle in ipairs(vehicleData) do
|
||||
local plate = vehicle[1]
|
||||
local model = GetLabelText(GetDisplayNameFromVehicleModel(vehicle[2]))
|
||||
if (model == "NULL") then
|
||||
model = GetDisplayNameFromVehicleModel(vehicle[2])
|
||||
end
|
||||
local keyCount = GetKeyCount(plate, ownedKeys)
|
||||
|
||||
local vehItem = NativeUI.CreateItem(model .. " " .. plate, string.format(Config.Strings.NUI.createVehicleKey, model, plate))
|
||||
vehItem:RightLabel("x" .. tostring(keyCount))
|
||||
|
||||
submenuCreateKey:AddItem(vehItem)
|
||||
end
|
||||
submenuCreateKey.OnItemSelect = function(menu, item, index)
|
||||
Citizen.CreateThread(function()
|
||||
local model = GetLabelText(GetDisplayNameFromVehicleModel(vehicleData[index][2]))
|
||||
if (model == "NULL") then
|
||||
model = GetDisplayNameFromVehicleModel(vehicleData[index][2])
|
||||
end
|
||||
|
||||
local result = CB:Trigger("VKC:createNewKey", vehicleData[index][1], 1)
|
||||
if (type(result) == "boolean" and result == true) then
|
||||
Notification(string.format(Config.Strings.createSuccess, model))
|
||||
|
||||
if (submenuCreateKey:Visible()) then
|
||||
local oldCount = item.Label.Text._Text:gsub("x", "")
|
||||
item:RightLabel("x" .. tostring(tonumber(oldCount) + 1))
|
||||
end
|
||||
elseif (type(result) == "string" and result == "noMoney") then
|
||||
Notification(string.format(Config.Strings.createNoMoney, model))
|
||||
else
|
||||
Notification(string.format(Config.Strings.createFailed, model))
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
local submenuInvalidateKey = menuPoolNativeUI:AddSubMenu(keymakerMenuNativeUI, Config.Strings.NUI.invalKeyTitle, Config.Strings.NUI.invalKeyDesc)
|
||||
submenuInvalidateKey.ParentItem:RightLabel(">")
|
||||
submenuInvalidateKey.Subtitle.Text._Text = "~b~" .. Config.Strings.NUI.invalKeyTitle
|
||||
for i, vehicle in ipairs(vehicleData) do
|
||||
local plate = vehicle[1]
|
||||
local model = GetLabelText(GetDisplayNameFromVehicleModel(vehicle[2]))
|
||||
if (model == "NULL") then
|
||||
model = GetDisplayNameFromVehicleModel(vehicle[2])
|
||||
end
|
||||
|
||||
local keyItem = NativeUI.CreateItem(model .. " " .. plate, string.format(Config.Strings.NUI.invalVehicleKey, model, plate))
|
||||
submenuInvalidateKey:AddItem(keyItem)
|
||||
end
|
||||
submenuInvalidateKey.OnItemSelect = function(menu, item, index)
|
||||
Citizen.CreateThread(function()
|
||||
local result = CB:Trigger("VKC:removeAllKeys", vehicleData[index][1])
|
||||
|
||||
if (type(result) == "boolean" and result == true) then
|
||||
local model = GetLabelText(GetDisplayNameFromVehicleModel(vehicleData[index][2]))
|
||||
if (model == "NULL") then
|
||||
model = GetDisplayNameFromVehicleModel(vehicleData[index][2])
|
||||
end
|
||||
Notification(string.format(Config.Strings.deleteKeys, model))
|
||||
elseif (type(result) == "string" and result == "noMoney") then
|
||||
Notification(string.format(Config.Strings.removeNoMoney, vehName))
|
||||
else
|
||||
Notification(string.format(Config.Strings.removeFailed, vehName))
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
keymakerMenuNativeUI.OnMenuClosed = function(menu)
|
||||
menuOpen = false
|
||||
end
|
||||
|
||||
menuPoolNativeUI:ControlDisablingEnabled(false)
|
||||
menuPoolNativeUI:MouseControlsEnabled(false)
|
||||
|
||||
menuPoolNativeUI:RefreshIndex()
|
||||
end
|
||||
|
||||
function GenerateKeyInventoryNativeUI()
|
||||
keyInvMenuNativeUI:Clear()
|
||||
|
||||
local vehicleData = GetPlayerVehicleData()
|
||||
local ownedKeys = GetPlayerKeys()
|
||||
|
||||
keyInvMenuNativeUI = NativeUI.CreateMenu(Config.Strings.NUI.keyInventoryTitle, Config.Strings.NUI.keyInventorySub)
|
||||
menuPoolNativeUI:Add(keyInvMenuNativeUI)
|
||||
|
||||
local submenuShowMasterKeys = menuPoolNativeUI:AddSubMenu(keyInvMenuNativeUI, Config.Strings.NUI.masterKeysTitle, Config.Strings.NUI.masterKeysDesc)
|
||||
submenuShowMasterKeys.ParentItem:RightLabel(">")
|
||||
submenuShowMasterKeys.Subtitle.Text._Text = "~b~" .. Config.Strings.NUI.masterKeysTitle
|
||||
for i, vehicle in ipairs(vehicleData) do
|
||||
local plate = vehicle[1]
|
||||
local model = GetLabelText(GetDisplayNameFromVehicleModel(vehicle[2]))
|
||||
if (model == "NULL") then
|
||||
model = GetDisplayNameFromVehicleModel(vehicle[2])
|
||||
end
|
||||
--local keyCount = GetKeyCount(plate, ownedKeys)
|
||||
|
||||
local vehItem = NativeUI.CreateItem(model .. " " .. plate, "")
|
||||
--vehItem:RightLabel("x" .. tostring(keyCount))
|
||||
submenuShowMasterKeys:AddItem(vehItem)
|
||||
end
|
||||
|
||||
-- Fahrzeugübergabe-Untermenü hinzufügen
|
||||
local submenuTransferVehicle = menuPoolNativeUI:AddSubMenu(keyInvMenuNativeUI, "Fahrzeug übergeben", "Übertrage ein Fahrzeug an einen Spieler in der Nähe")
|
||||
submenuTransferVehicle.ParentItem:RightLabel(">")
|
||||
submenuTransferVehicle.Subtitle.Text._Text = "~b~Fahrzeug übergeben"
|
||||
for i, vehicle in ipairs(vehicleData) do
|
||||
local plate = vehicle[1]
|
||||
local model = GetLabelText(GetDisplayNameFromVehicleModel(vehicle[2]))
|
||||
if (model == "NULL") then
|
||||
model = GetDisplayNameFromVehicleModel(vehicle[2])
|
||||
end
|
||||
|
||||
local vehItem = NativeUI.CreateItem(model .. " " .. plate, "Wähle dieses Fahrzeug zum Übergeben")
|
||||
submenuTransferVehicle:AddItem(vehItem)
|
||||
end
|
||||
|
||||
submenuTransferVehicle.OnItemSelect = function(menu, item, index)
|
||||
local selectedVehicle = vehicleData[index]
|
||||
local plate = selectedVehicle[1]
|
||||
local model = GetLabelText(GetDisplayNameFromVehicleModel(selectedVehicle[2]))
|
||||
if (model == "NULL") then
|
||||
model = GetDisplayNameFromVehicleModel(selectedVehicle[2])
|
||||
end
|
||||
|
||||
-- Finde den nächsten Spieler
|
||||
local player = GetClosestPlayer(5.0)
|
||||
|
||||
if player then
|
||||
local targetPlayerId = GetPlayerServerId(player)
|
||||
local playerName = GetPlayerName(player)
|
||||
|
||||
local success = CB:Trigger("VKC:transferVehicleOwnership", plate, targetPlayerId)
|
||||
|
||||
if success then
|
||||
lib.notify({
|
||||
title = "Fahrzeug übergeben",
|
||||
description = "Du hast dein " .. model .. " an " .. playerName .. " übergeben",
|
||||
position = "top",
|
||||
type = "success",
|
||||
icon = "car"
|
||||
})
|
||||
else
|
||||
lib.notify({
|
||||
title = "Fahrzeug übergeben",
|
||||
description = "Übergabe fehlgeschlagen",
|
||||
position = "top",
|
||||
type = "error",
|
||||
icon = "car"
|
||||
})
|
||||
end
|
||||
else
|
||||
lib.notify({
|
||||
title = "Fahrzeug übergeben",
|
||||
description = "Kein Spieler in der Nähe gefunden",
|
||||
position = "top",
|
||||
type = "error",
|
||||
icon = "car"
|
||||
})
|
||||
end
|
||||
|
||||
menuPoolNativeUI:CloseAllMenus()
|
||||
menuOpen = false
|
||||
end
|
||||
|
||||
local submenuShowKeys = menuPoolNativeUI:AddSubMenu(keyInvMenuNativeUI, Config.Strings.NUI.keysTitle, Config.Strings.NUI.keysDesc)
|
||||
submenuShowKeys.ParentItem:RightLabel(">")
|
||||
submenuShowKeys.Subtitle.Text._Text = "~b~" .. Config.Strings.NUI.keysTitle
|
||||
for i, key in ipairs(ownedKeys) do
|
||||
local model = GetLabelText(GetDisplayNameFromVehicleModel(key.model))
|
||||
if (model == "NULL") then
|
||||
model = GetDisplayNameFromVehicleModel(key.model)
|
||||
end
|
||||
|
||||
local submenuKey = menuPoolNativeUI:AddSubMenu(submenuShowKeys, model .. " " .. key.plate, "")
|
||||
submenuKey.ParentItem:RightLabel("x" .. tostring(key.count) .. " >")
|
||||
submenuKey.Subtitle.Text._Text = "~b~" .. model .. " " .. key.plate
|
||||
|
||||
local giveItem = NativeUI.CreateItem(Config.Strings.NUI.giveKeyTitle, Config.Strings.NUI.giveKeyDesc)
|
||||
submenuKey:AddItem(giveItem)
|
||||
|
||||
local removeItem = NativeUI.CreateItem(Config.Strings.NUI.removeKeyTitle, Config.Strings.NUI.removeKeyDesc)
|
||||
submenuKey:AddItem(removeItem)
|
||||
|
||||
submenuKey.OnItemSelect = function(menu, item, index)
|
||||
if (item == giveItem) then
|
||||
Citizen.CreateThread(function()
|
||||
local player = GetClosestPlayer(2.0)
|
||||
if (player) then
|
||||
local success = CB:Trigger("VKC:giveKeyToPlayer", key.plate, GetPlayerServerId(player))
|
||||
if (success) then
|
||||
Notification(string.format(Config.Strings.giveSuccess, key.plate))
|
||||
else
|
||||
Notification(string.format(Config.Strings.giveFailed, key.plate))
|
||||
end
|
||||
else
|
||||
Notification(string.format(Config.Strings.giveFailed, key.plate))
|
||||
end
|
||||
end)
|
||||
elseif (item == removeItem) then
|
||||
Citizen.CreateThread(function()
|
||||
local success = CB:Trigger("VKC:removeKey", key.plate, 1)
|
||||
if (success) then
|
||||
Notification(string.format(Config.Strings.removeSuccess, key.plate))
|
||||
|
||||
if (submenuShowKeys:Visible() or submenuKey:Visible()) then
|
||||
local oldCount = submenuKey.ParentItem.Label.Text._Text:gsub("x", "")
|
||||
oldCount = oldCount:gsub(" >", "")
|
||||
submenuKey.ParentItem:RightLabel("x" .. tostring(tonumber(oldCount) - 1) .. " >")
|
||||
end
|
||||
else
|
||||
Notification(string.format(Config.Strings.removeFailed, key.plate))
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
keyInvMenuNativeUI.OnMenuClosed = function(menu)
|
||||
menuOpen = false
|
||||
end
|
||||
|
||||
menuPoolNativeUI:ControlDisablingEnabled(false)
|
||||
menuPoolNativeUI:MouseControlsEnabled(false)
|
||||
|
||||
menuPoolNativeUI:RefreshIndex()
|
||||
end
|
||||
|
||||
if (Config.useNativeUI) then
|
||||
Citizen.CreateThread(function()
|
||||
while (true) do
|
||||
Citizen.Wait(250)
|
||||
|
||||
isAtKeymaker = false
|
||||
|
||||
local pos = GetEntityCoords(PlayerPedId())
|
||||
for i, keymaker in ipairs(Config.Keymakers) do
|
||||
if (Vdist(pos.x, pos.y, pos.z, keymaker.pos.x, keymaker.pos.y, keymaker.pos.z) < 2.0) then
|
||||
isAtKeymaker = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
if (Config.keyMenuCommand) then
|
||||
RegisterCommand(Config.keyMenuCommand, function(source, args, raw)
|
||||
if (menuPoolNativeUI:IsAnyMenuOpen()) then
|
||||
menuPoolNativeUI:CloseAllMenus()
|
||||
end
|
||||
|
||||
GenerateKeyInventoryNativeUI()
|
||||
keyInvMenuNativeUI:Visible(true)
|
||||
menuOpen = true
|
||||
end, false)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- lock vehicle
|
||||
if (Config.lockCommand) then
|
||||
RegisterCommand(Config.lockCommand, function(source, args, raw)
|
||||
local vehicle = GetClosestVehicle(GetEntityCoords(PlayerPedId()), 10.0)
|
||||
if (DoesEntityExist(vehicle) and IsVehicleOrKeyOwner(vehicle)) then
|
||||
ToggleLock(vehicle, GetVehicleDoorLockStatus(vehicle) ~= LockStatus.Locked)
|
||||
end
|
||||
end, false)
|
||||
end
|
||||
|
||||
function ToggleLock(vehicle, lock)
|
||||
local lockStatus = GetVehicleDoorLockStatus(vehicle)
|
||||
|
||||
if (NetworkHasControlOfEntity(vehicle)) then
|
||||
ToggleLockOnVehicle(vehicle, lock)
|
||||
else
|
||||
TriggerServerEvent("VKC:toggleLockNet", NetworkGetNetworkIdFromEntity(vehicle), lock)
|
||||
end
|
||||
|
||||
-- play sound
|
||||
TriggerServerEvent("VKC:playDoorLockSoundNet", NetworkGetNetworkIdFromEntity(vehicle), lock)
|
||||
|
||||
-- play remote animation
|
||||
if (not IsPedInAnyVehicle(PlayerPedId(), false)) then
|
||||
PlayRemoteAnimation()
|
||||
end
|
||||
|
||||
-- show notification
|
||||
--[[
|
||||
if (lockNotif) then
|
||||
RemoveNotification(lockNotif)
|
||||
end
|
||||
lockNotif = Notification(lockStatus == LockStatus.Locked and Config.Strings.unlockNotif or Config.Strings.lockNotif)
|
||||
]]
|
||||
end
|
||||
|
||||
function ToggleLockOnVehicle(vehicle, lock)
|
||||
if (lock) then
|
||||
SetVehicleDoorsShut(vehicle, false)
|
||||
SetVehicleDoorsLocked(vehicle, LockStatus.Locked)
|
||||
--exports['okokNotify']:Alert("Fahrzeug", "Du hast dein Fahrzeug Abgeschlossen", 3000, 'error')
|
||||
lib.notify({
|
||||
title = "Fahrzeug",
|
||||
description = "Du hast dein Fahrzeug Abgeschlossen",
|
||||
position = "top",
|
||||
type = "error",
|
||||
icon = "car"
|
||||
})
|
||||
|
||||
|
||||
SetVehicleLights(vehicle, 2)
|
||||
Citizen.Wait(150)
|
||||
SetVehicleLights(vehicle, 0)
|
||||
Citizen.Wait(150)
|
||||
SetVehicleLights(vehicle, 2)
|
||||
Citizen.Wait(150)
|
||||
SetVehicleLights(vehicle, 0)
|
||||
else
|
||||
SetVehicleDoorsLocked(vehicle, LockStatus.Unlocked)
|
||||
--exports['okokNotify']:Alert("Fahrzeug", "Du hast dein Fahrzeug Aufgeschlossen", 3000, 'success')
|
||||
lib.notify({
|
||||
title = "Fahrzeug",
|
||||
description = "Du hast dein Fahrzeug Aufgeschlossen",
|
||||
position = "top",
|
||||
type = "success",
|
||||
icon = "car"
|
||||
})
|
||||
|
||||
SetVehicleLights(vehicle, 2)
|
||||
Citizen.Wait(150)
|
||||
SetVehicleLights(vehicle, 0)
|
||||
Citizen.Wait(150)
|
||||
SetVehicleLights(vehicle, 2)
|
||||
Citizen.Wait(150)
|
||||
SetVehicleLights(vehicle, 0)
|
||||
Citizen.Wait(150)
|
||||
SetVehicleLights(vehicle, 2)
|
||||
Citizen.Wait(150)
|
||||
SetVehicleLights(vehicle, 0)
|
||||
Citizen.Wait(150)
|
||||
SetVehicleLights(vehicle, 2)
|
||||
Citizen.Wait(150)
|
||||
SetVehicleLights(vehicle, 0)
|
||||
Citizen.Wait(150)
|
||||
SetVehicleLights(vehicle, 2)
|
||||
Citizen.Wait(150)
|
||||
SetVehicleLights(vehicle, 0)
|
||||
end
|
||||
end
|
||||
|
||||
function PlayDoorLockSound(vehicle, lock)
|
||||
if (lock) then
|
||||
PlayVehicleDoorCloseSound(vehicle, 0)
|
||||
else
|
||||
PlayVehicleDoorOpenSound(vehicle, 0)
|
||||
end
|
||||
end
|
||||
|
||||
if (Config.lockCommand or Config.lockKey) then
|
||||
RegisterNetEvent("VKC:toggleLockOnPlayer")
|
||||
AddEventHandler("VKC:toggleLockOnPlayer", function(vehicleNetId, unlocked)
|
||||
local vehicle = NetworkGetEntityFromNetworkId(vehicleNetId)
|
||||
if (DoesEntityExist(vehicle) and NetworkHasControlOfEntity(vehicle)) then
|
||||
local lockStatus = GetVehicleDoorLockStatus(vehicle)
|
||||
ToggleLockOnVehicle(vehicle, unlocked)
|
||||
end
|
||||
end)
|
||||
|
||||
RegisterNetEvent("VKC:playDoorLockSound")
|
||||
AddEventHandler("VKC:playDoorLockSound", function(vehicleNetId, lock)
|
||||
local vehicle = NetworkGetEntityFromNetworkId(vehicleNetId)
|
||||
if (DoesEntityExist(vehicle)) then
|
||||
PlayDoorLockSound(vehicle, lock)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
|
||||
RegisterNetEvent("VKC:giveKeyNotif")
|
||||
AddEventHandler("VKC:giveKeyNotif", function(plate)
|
||||
Notification(string.format(Config.Strings.giveSuccessPly, plate))
|
||||
end)
|
||||
|
||||
-- Ereignis für Fahrzeugübergabe-Benachrichtigung
|
||||
RegisterNetEvent("VKC:vehicleTransferNotif")
|
||||
AddEventHandler("VKC:vehicleTransferNotif", function(plate, model)
|
||||
lib.notify({
|
||||
title = "Fahrzeug erhalten",
|
||||
description = "Du hast ein " .. model .. " mit dem Kennzeichen " .. plate .. " erhalten",
|
||||
position = "top",
|
||||
type = "success",
|
||||
icon = "car"
|
||||
})
|
||||
end)
|
||||
|
||||
|
||||
function IsVehicleOwner(vehicle)
|
||||
if (not DoesEntityExist(vehicle)) then
|
||||
print("^1[ERROR] Parameter \"vehicle\" was nil or vehicle did not exist while triggering export \"IsVehicleOwner\"!")
|
||||
return
|
||||
end
|
||||
|
||||
return CB:Trigger("VKC:isVehicleOwner", GetVehicleNumberPlateText(vehicle))
|
||||
end
|
||||
|
||||
function IsKeyOwner(vehicle)
|
||||
if (not DoesEntityExist(vehicle)) then
|
||||
print("^1[ERROR] Parameter \"vehicle\" was nil or vehicle did not exist while triggering export \"IsKeyOwner\"!")
|
||||
return
|
||||
end
|
||||
|
||||
return CB:Trigger("VKC:isKeyOwner", GetVehicleNumberPlateText(vehicle), GetEntityModel(vehicle))
|
||||
end
|
||||
|
||||
function IsVehicleOrKeyOwner(vehicle)
|
||||
if (not DoesEntityExist(vehicle)) then
|
||||
print("^1[ERROR] Parameter \"vehicle\" was nil or vehicle did not exist while triggering export \"IsVehicleOrKeyOwner\"!")
|
||||
return
|
||||
end
|
||||
|
||||
return CB:Trigger("VKC:isVehicleOrKeyOwner", GetVehicleNumberPlateText(vehicle), GetEntityModel(vehicle))
|
||||
end
|
||||
|
||||
function GetPlayerKeys()
|
||||
return CB:Trigger("VKC:getPlayerKeys")
|
||||
end
|
||||
|
||||
function GetPlayerVehicleData()
|
||||
return CB:Trigger("VKC:getPlayerVehicleData")
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- play lock animation
|
||||
function PlayRemoteAnimation()
|
||||
Citizen.CreateThread(function()
|
||||
RequestModel(keyPropHash)
|
||||
RequestAnimDict("anim@mp_player_intmenu@key_fob@")
|
||||
while (not HasModelLoaded(keyPropHash) and not HasAnimDictLoaded("anim@mp_player_intmenu@key_fob@")) do
|
||||
Citizen.Wait(0)
|
||||
end
|
||||
|
||||
local playerPed = PlayerPedId()
|
||||
local playerPos = GetEntityCoords(playerPed)
|
||||
|
||||
local keyObj = CreateObjectNoOffset(keyPropHash, playerPos.x, playerPos.y, playerPos.z, true, true, false)
|
||||
SetModelAsNoLongerNeeded(keyPropHash)
|
||||
|
||||
local boneIndex = GetEntityBoneIndexByName(playerPed, "IK_R_Hand")
|
||||
local offset = vector3(0.08, 0.025, -0.01)
|
||||
local rotOffset = vector3(0, 70, 140)
|
||||
AttachEntityToEntity(keyObj, playerPed, boneIndex, offset.x, offset.y, offset.z, rotOffset.x, rotOffset.y, rotOffset.z, false, false, true, false, 2, true)
|
||||
|
||||
TaskPlayAnim(playerPed, "anim@mp_player_intmenu@key_fob@", "fob_click_fp", 8.0, 8.0, -1, 48, 1, false, false, false)
|
||||
RemoveAnimDict("anim@mp_player_intmenu@key_fob@")
|
||||
|
||||
Citizen.Wait(1500)
|
||||
|
||||
DeleteEntity(keyObj)
|
||||
end)
|
||||
end
|
||||
|
||||
-- show text in upper left corner
|
||||
function ShowHelpText(text)
|
||||
BeginTextCommandDisplayHelp('STRING')
|
||||
AddTextComponentSubstringPlayerName(text)
|
||||
EndTextCommandDisplayHelp(0, false, true, -1)
|
||||
end
|
||||
|
||||
-- displays a notification and returns its handle
|
||||
function Notification(text)
|
||||
SetNotificationTextEntry('STRING')
|
||||
AddTextComponentSubstringPlayerName(text)
|
||||
return DrawNotification(false, true)
|
||||
end
|
||||
|
||||
-- get key count
|
||||
function GetKeyCount(plate, keyArray)
|
||||
for i, key in ipairs(keyArray) do
|
||||
if (plate == key.plate) then
|
||||
return key.count
|
||||
end
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
-- get all players
|
||||
function GetAllPlayers()
|
||||
local players = {}
|
||||
|
||||
for k, v in ipairs(GetActivePlayers()) do
|
||||
table.insert(players, v)
|
||||
end
|
||||
|
||||
return players
|
||||
end
|
||||
|
||||
-- get the closest player
|
||||
function GetClosestPlayer(maxRange)
|
||||
local playerPed = PlayerPedId()
|
||||
local players = GetAllPlayers()
|
||||
|
||||
local playerCoords = GetEntityCoords(playerPed)
|
||||
|
||||
local closestDistance = maxRange
|
||||
local closestPlayer = nil
|
||||
|
||||
for i=1, #players, 1 do
|
||||
local coords = GetEntityCoords(GetPlayerPed(players[i]))
|
||||
local dist = Vdist(playerCoords.x, playerCoords.y, playerCoords.z, coords.x, coords.y, coords.z)
|
||||
if (dist < closestDistance and players[i] ~= PlayerId()) then
|
||||
closestDistance = dist
|
||||
closestPlayer = players[i]
|
||||
end
|
||||
end
|
||||
|
||||
if (closestPlayer ~= nil and DoesEntityExist(GetPlayerPed(closestPlayer))) then
|
||||
return closestPlayer
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Return closest loaded vehicle entity or nil if no vehicle is found
|
||||
function GetClosestVehicle(position, maxRadius)
|
||||
local vehicles = GetAllVehicles()
|
||||
local dist = maxRadius
|
||||
local closestVehicle = nil
|
||||
|
||||
for i=1, #vehicles, 1 do
|
||||
local vehicleCoords = GetEntityCoords(vehicles[i])
|
||||
local tempDist = Vdist(vehicleCoords.x, vehicleCoords.y, vehicleCoords.z, position.x, position.y, position.z)
|
||||
if (tempDist < dist) then
|
||||
dist = tempDist
|
||||
closestVehicle = vehicles[i]
|
||||
end
|
||||
end
|
||||
|
||||
if (closestVehicle ~= nil and DoesEntityExist(closestVehicle)) then
|
||||
return closestVehicle
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Returns all loaded vehicles on client side
|
||||
function GetAllVehicles()
|
||||
local vehicles = {}
|
||||
|
||||
for vehicle in EnumerateVehicles() do
|
||||
table.insert(vehicles, vehicle)
|
||||
end
|
||||
|
||||
return vehicles
|
||||
end
|
||||
|
||||
-- getting all vehicles
|
||||
function EnumerateVehicles()
|
||||
return EnumerateEntities(FindFirstVehicle, FindNextVehicle, EndFindVehicle)
|
||||
end
|
||||
function EnumerateEntities(initFunc, moveFunc, disposeFunc)
|
||||
return coroutine.wrap(function()
|
||||
local iter, id = initFunc()
|
||||
if not id or id == 0 then
|
||||
disposeFunc(iter)
|
||||
return
|
||||
end
|
||||
|
||||
local enum = {handle = iter, destructor = disposeFunc}
|
||||
setmetatable(enum, entityEnumerator)
|
||||
|
||||
local next = true
|
||||
repeat
|
||||
coroutine.yield(id)
|
||||
next, id = moveFunc(iter)
|
||||
until not next
|
||||
|
||||
enum.destructor, enum.handle = nil, nil
|
||||
disposeFunc(iter)
|
||||
end)
|
||||
end
|
||||
local entityEnumerator = {
|
||||
__gc = function(enum)
|
||||
if enum.destructor and enum.handle then
|
||||
enum.destructor(enum.handle)
|
||||
end
|
||||
enum.destructor = nil
|
||||
enum.handle = nil
|
||||
end
|
||||
}
|
||||
|
||||
--[[
|
||||
RegisterCommand("plate", function(source, args, raw)
|
||||
local vehicle = GetClosestVehicle(GetEntityCoords(PlayerPedId()), 10.0)
|
||||
if (DoesEntityExist(vehicle)) then
|
||||
SetVehicleNumberPlateText(vehicle, args[1])
|
||||
end
|
||||
end, false)
|
||||
]]
|
||||
|
||||
|
||||
RegisterKeyMapping("vehicleLock", "Vehicle Lock", "keyboard", "PAGEUP")
|
||||
RegisterCommand("vehicleLock", function()
|
||||
local vehicle = GetClosestVehicle(GetEntityCoords(PlayerPedId()), 10.0)
|
||||
if (DoesEntityExist(vehicle) and IsVehicleOrKeyOwner(vehicle)) then
|
||||
ToggleLock(vehicle, GetVehicleDoorLockStatus(vehicle) ~= LockStatus.Locked)
|
||||
end
|
||||
end, false)
|
||||
|
||||
-- Integration mit dem Mietfahrzeug-System
|
||||
RegisterNetEvent('vehiclerental:client:checkRentalKeys')
|
||||
AddEventHandler('vehiclerental:client:checkRentalKeys', function()
|
||||
TriggerServerEvent('vehiclerental:server:checkRentalKeys')
|
||||
end)
|
||||
|
||||
-- Überprüfe Mietfahrzeug-Schlüssel beim Öffnen des Schlüssel-Menüs
|
||||
if Config.useNativeUI then
|
||||
-- Speichere die ursprüngliche Funktion
|
||||
local originalGenerateKeyInventoryNativeUI = GenerateKeyInventoryNativeUI
|
||||
|
||||
-- Überschreibe die Funktion
|
||||
GenerateKeyInventoryNativeUI = function()
|
||||
-- Überprüfe Mietfahrzeug-Schlüssel
|
||||
TriggerServerEvent('vehiclerental:server:checkRentalKeys')
|
||||
|
||||
-- Warte kurz, damit die Schlüssel aktualisiert werden können
|
||||
Citizen.Wait(100)
|
||||
|
||||
-- Rufe die ursprüngliche Funktion auf
|
||||
originalGenerateKeyInventoryNativeUI()
|
||||
end
|
||||
end
|
||||
|
149
resources/[Developer]/sn_vehicleKey/config.lua
Normal file
149
resources/[Developer]/sn_vehicleKey/config.lua
Normal file
|
@ -0,0 +1,149 @@
|
|||
|
||||
Config = {}
|
||||
|
||||
-- set at least one to false, otherwise both menus will clash
|
||||
-- and don't forget to comment the menu in the fxmanifest.lua if you don't use it!
|
||||
-- Setting both to true does not work!
|
||||
-- setting both to false results in no menu being usable at all
|
||||
-- you can then just include the exports in your own menu if you have one
|
||||
Config.useContextMenu = false
|
||||
Config.useNativeUI = true
|
||||
|
||||
-- set to nil if you don't want to use the command
|
||||
Config.lockCommand = "lock"
|
||||
-- set to nil if you don't want to use the button (303, U)
|
||||
Config.lockKey = 10 -- PageUp
|
||||
|
||||
-- set to nil if you don't want to use the command
|
||||
Config.keyMenuCommand = "keys"
|
||||
-- set to nil if you don't want to use the button (311, K)
|
||||
Config.keyMenuKey = 56 -- F9
|
||||
|
||||
-- maximum distance (in meters) between the player and ContextMenu interaction point
|
||||
Config.maxDistance = 5.0
|
||||
|
||||
-- you can define more keymakers by just adding additional entries
|
||||
Config.Keymakers = {
|
||||
{
|
||||
-- ped model (yes, the ` symbols are correct)
|
||||
model = `s_m_m_autoshop_01`,
|
||||
-- position and heading vector4(x, y, z, heading)
|
||||
pos = vector4(170.33, -1799.13, 28.32, 313.0)
|
||||
},
|
||||
}
|
||||
|
||||
-- define costs for creating a new key and exchanging locks
|
||||
Config.Costs = {
|
||||
GetMoney = "cash", -- cash or bank
|
||||
newKey = 150,
|
||||
exchangeLocks = 650
|
||||
}
|
||||
|
||||
-- define job vehicles like this (this acts as a key for those vehicles)
|
||||
-- models in ``
|
||||
-- either exact plates or just a string that should be in the vehicles plate
|
||||
-- e.g. "LSPD" will let a police officer lock/unlock any vehicle with the plate "LSPD1337" or "13LSPD37"
|
||||
Config.JobVehicles = {
|
||||
-- examples:
|
||||
--["police"] = {
|
||||
--models = {
|
||||
--`police`,
|
||||
--`policeb`,
|
||||
--`pbus`
|
||||
--},
|
||||
--plates = {
|
||||
--""
|
||||
--}
|
||||
--},
|
||||
--["mechanic"] = {
|
||||
-- models = {
|
||||
-- `flatbed`
|
||||
-- },
|
||||
-- plates = {
|
||||
-- "MECH"
|
||||
-- }
|
||||
--},
|
||||
}
|
||||
|
||||
Config.Strings = {
|
||||
keymaker = "Schlüsseldienst",
|
||||
|
||||
helpText = "~w~Drück ~g~E ~w~um mit den Schlüsseldienst zu reden",
|
||||
|
||||
lockNotif = "~r~Fahrzeug Abgeschlossen",
|
||||
unlockNotif = "~g~Fahrzeug Aufgeschlossen",
|
||||
|
||||
createSuccess = "~g~Ersatzschlüssel Nachgemacht %s.",
|
||||
createNoMoney = "~r~Schlüssel kann nicht nachgemacht werden, du hast zu wenig Geld dabei",
|
||||
createFailed = "~r~Ersatzschlüssel machen Fehlgeschlagen %s!",
|
||||
|
||||
giveSuccess = "~g~Schlüssel an %s Weitergegeben.",
|
||||
giveSuccessPly = "~g~Schlüssel von %s erhalten ",
|
||||
giveFailed = "~r~Schlüssel geben von %s Fehlgeschlagen",
|
||||
|
||||
removeSuccess = "~r~Schlüssel für ~g~%s ~r~aus deiner Tasche Entfernt",
|
||||
removeNoMoney = "~r~Schloss kann nicht bezahlt werden, du hast kein Geld dabei",
|
||||
removeFailed = "~r~Entfernen von Schlüssel für ~g~%s ~r~Fehgeschlagen!",
|
||||
|
||||
deleteKeys = "~r~Du hast für dein ~g~%s ~r~Das Schloss ausgetasucht, und somit sind jetzt alle Schlüssel nicht mehr Gültig.",
|
||||
|
||||
|
||||
-- NativeUI
|
||||
NUI = {
|
||||
-- keymaker menu
|
||||
keymakerMenuTitle = "Schlüssel-System",
|
||||
keymakerMenuSub = "~g~Erstelle Schlüssel und Tausche deine Fahrzeug Schlösser aus",
|
||||
|
||||
-- create key
|
||||
createKeyTitle = "~g~Ersatzschlüssel erstellen",
|
||||
createKeyDesc = "Erstelle einen neuen Schlüssel für eines Ihrer Fahrzeuge ",
|
||||
createVehicleKey = "Erstelle einen Schlüssel für Ihr %s mit den Kennzeichen %s. ~g~Preis "..Config.Costs.newKey.."$",
|
||||
|
||||
-- invalidate Key
|
||||
invalKeyTitle = "~r~Fahrzeug Schlöss Tauschen",
|
||||
invalKeyDesc = "Tausche die Schlösser von Ihren Auto aus, alle schlüssel sind damit nicht mehr Gültig!",
|
||||
invalVehicleKey = "Schloss Tauschen von %s mit den Kennzeichen %s. ~g~Preis "..Config.Costs.exchangeLocks.."$",
|
||||
|
||||
-- key Inventory
|
||||
keyInventoryTitle = "Schlüssel",
|
||||
keyInventorySub = "~g~Da sind alle deine Schlüssel",
|
||||
|
||||
-- master keys
|
||||
masterKeysTitle = "Hauptschlüssel",
|
||||
masterKeysDesc = "~g~Zeigt alle deine Hauptschlüssel an",
|
||||
|
||||
-- additional keys
|
||||
keysTitle = "Ersatzschlüssel",
|
||||
keysDesc = "~g~Zeigt alle deine Ersatzschlüssel an",
|
||||
giveKeyTitle = "~g~Ersatzschlüssel Geben",
|
||||
giveKeyDesc = "~g~Ersatzschlüssel Geben",
|
||||
removeKeyTitle = "~r~Schlüssel Entfernen",
|
||||
removeKeyDesc = "~r~Schlüssel Entfernen",
|
||||
transferVehicleTitle = "~g~Fahrzeug übergeben",
|
||||
transferVehicleDesc = "~g~Übertrage ein Fahrzeug an einen Spieler in der Nähe",
|
||||
transferVehicleSuccess = "~g~Fahrzeug erfolgreich übergeben an %s",
|
||||
transferVehicleFailed = "~r~Fahrzeugübergabe fehlgeschlagen",
|
||||
transferVehicleReceived = "~g~Du hast ein Fahrzeug erhalten",
|
||||
noPlayersNearby = "~r~Keine Spieler in der Nähe",
|
||||
|
||||
|
||||
},
|
||||
|
||||
-- ContextMenu
|
||||
CM = {
|
||||
-- keymaker menu
|
||||
createKeyTitle = "Ersatzschlüssel",
|
||||
invalKeyTitle = "Schlossaustauschen",
|
||||
|
||||
-- key inventory
|
||||
masterKeysTitle = "~g~Hauptschlüssel",
|
||||
keysTitle = "~b~Ersatzschlüssel",
|
||||
|
||||
-- other player
|
||||
giveKey = "~r~Gebe ein Schlüssel, an der Person",
|
||||
|
||||
transferVehicleTitle = "Fahrzeug übergeben",
|
||||
|
||||
}
|
||||
}
|
||||
|
57
resources/[Developer]/sn_vehicleKey/fxmanifest.lua
Normal file
57
resources/[Developer]/sn_vehicleKey/fxmanifest.lua
Normal file
|
@ -0,0 +1,57 @@
|
|||
fx_version 'cerulean'
|
||||
games { 'gta5' }
|
||||
|
||||
author 'Mîhó'
|
||||
description 'Manage vehicle keys!'
|
||||
version '1.1.1'
|
||||
|
||||
dependency 'kimi_callbacks'
|
||||
dependency 'NativeUI'
|
||||
|
||||
server_scripts {
|
||||
--'@mysql-async/lib/MySQL.lua',
|
||||
'@oxmysql/lib/MySQL.lua',
|
||||
|
||||
'config.lua',
|
||||
'server.lua'
|
||||
}
|
||||
|
||||
client_scripts {
|
||||
-- comment these lines if not using the ContextMenu
|
||||
--'@ContextMenu/screenToWorld.lua',
|
||||
--'@ContextMenu/Drawables/Color.lua',
|
||||
--'@ContextMenu/Drawables/Rect.lua',
|
||||
--'@ContextMenu/Drawables/Text.lua',
|
||||
--'@ContextMenu/Drawables/Sprite.lua',
|
||||
--'@ContextMenu/Menu/Item.lua',
|
||||
--'@ContextMenu/Menu/TextItem.lua',
|
||||
--'@ContextMenu/Menu/CheckboxItem.lua',
|
||||
--'@ContextMenu/Menu/SubmenuItem.lua',
|
||||
--'@ContextMenu/Menu/Separator.lua',
|
||||
--'@ContextMenu/Menu/Border.lua',
|
||||
--'@ContextMenu/Menu/Menu.lua',
|
||||
--'@ContextMenu/Menu/MenuPool.lua',
|
||||
|
||||
-- comment this line if not using NativeUILua
|
||||
'@NativeUI/NativeUI.lua',
|
||||
'@ox_lib/init.lua',
|
||||
|
||||
'config.lua',
|
||||
'client.lua'
|
||||
}
|
||||
|
||||
server_exports {
|
||||
'IsVehicleOwner',
|
||||
'IsKeyOwner',
|
||||
'IsVehicleOrKeyOwner',
|
||||
'GetPlayerKeys',
|
||||
'GetPlayerVehicles'
|
||||
}
|
||||
|
||||
exports {
|
||||
'IsVehicleOwner',
|
||||
'IsKeyOwner',
|
||||
'IsVehicleOrKeyOwner',
|
||||
'GetAllKeys',
|
||||
'GetAllVehicles'
|
||||
}
|
503
resources/[Developer]/sn_vehicleKey/server.lua
Normal file
503
resources/[Developer]/sn_vehicleKey/server.lua
Normal file
|
@ -0,0 +1,503 @@
|
|||
local CB = exports["kimi_callbacks"]
|
||||
local QBCore = exports['qb-core']:GetCoreObject()
|
||||
|
||||
|
||||
-- create a new key
|
||||
CB:Register("VKC:createNewKey", function(source, plate, count)
|
||||
local src = source
|
||||
|
||||
if (plate == nil or count == nil) then
|
||||
print("^1[ERROR] \"plate\" or \"count\" was nil while creating new key for id " .. tostring(src))
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local Player = QBCore.Functions.GetPlayer(src)
|
||||
if (Player) then
|
||||
local trimmedPlate = plate:gsub("^%s*(.-)%s*$", "%1"):upper()
|
||||
|
||||
if (Player.Functions.GetMoney('cash') >= Config.Costs.newKey) then
|
||||
local results = MySQL.Sync.fetchAll("SELECT count FROM vehicle_keys WHERE owner = @owner AND (plate = @plate OR plate = @trimmedPlate)", {
|
||||
["@owner"] = Player.PlayerData.citizenid,
|
||||
["@plate"] = plate,
|
||||
["@trimmedPlate"] = trimmedPlate
|
||||
})
|
||||
|
||||
local rows = 0
|
||||
if (#results > 0) then
|
||||
rows = MySQL.Sync.execute("UPDATE vehicle_keys SET count = count + @count WHERE owner = @owner AND (plate = @plate OR plate = @trimmedPlate)", {
|
||||
["@owner"] = Player.PlayerData.citizenid,
|
||||
["@plate"] = plate,
|
||||
["@trimmedPlate"] = trimmedPlate,
|
||||
["@count"] = count
|
||||
})
|
||||
else
|
||||
rows = MySQL.Sync.execute("INSERT INTO vehicle_keys (owner, plate, count) VALUES (@owner, @trimmedPlate, @count)", {
|
||||
["@owner"] = Player.PlayerData.citizenid,
|
||||
["@trimmedPlate"] = trimmedPlate,
|
||||
["@count"] = count
|
||||
})
|
||||
end
|
||||
|
||||
if (rows == 0) then
|
||||
return false
|
||||
end
|
||||
|
||||
Player.Functions.RemoveMoney('cash', Config.Costs.newKey)
|
||||
else
|
||||
return "noMoney"
|
||||
end
|
||||
else
|
||||
print("^1[ERROR] \"playerData\" was nil while creating new key for id " .. tostring(src))
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
-- remove a key from a plate
|
||||
CB:Register("VKC:removeKey", function(source, plate, num)
|
||||
local src = source
|
||||
|
||||
if (plate == nil or num == nil) then
|
||||
print("^1[ERROR] \"plate\" or \"num\" was nil while removing a key for id " .. tostring(src))
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local Player = QBCore.Functions.GetPlayer(src)
|
||||
if (Player) then
|
||||
local trimmedPlate = plate:gsub("^%s*(.-)%s*$", "%1"):upper()
|
||||
|
||||
local rows = MySQL.Sync.execute("DELETE FROM vehicle_keys WHERE owner = @owner and (plate = @plate OR plate = @trimmedPlate) and count = @num", {
|
||||
["@owner"] = Player.PlayerData.citizenid,
|
||||
["@plate"] = plate,
|
||||
["@trimmedPlate"] = trimmedPlate,
|
||||
["@num"] = num
|
||||
})
|
||||
|
||||
if (rows == 0) then
|
||||
rows = MySQL.Sync.execute("UPDATE vehicle_keys SET count = count - @num WHERE owner = @owner and (plate = @plate OR plate = @trimmedPlate)", {
|
||||
["@owner"] = Player.PlayerData.citizenid,
|
||||
["@plate"] = plate,
|
||||
["@trimmedPlate"] = trimmedPlate,
|
||||
["@num"] = num
|
||||
})
|
||||
end
|
||||
|
||||
if (rows == 0) then
|
||||
return false
|
||||
end
|
||||
else
|
||||
print("^1[ERROR] \"playerData\" was nil while removing a key for id " .. tostring(src))
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
-- remove a key from a plate
|
||||
CB:Register("VKC:giveKeyToPlayer", function(source, plate, playerId)
|
||||
local src = source
|
||||
|
||||
if (plate == nil or playerId == nil) then
|
||||
print("^1[ERROR] \"plate\" or \"playerId\" was nil while giving a key for id " .. tostring(src))
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local trimmedPlate = plate:gsub("^%s*(.-)%s*$", "%1"):upper()
|
||||
|
||||
local Playera = QBCore.Functions.GetPlayer(src) --MARK
|
||||
local Playerb = QBCore.Functions.GetPlayer(playerId)
|
||||
if (Playera and Playerb) then
|
||||
local rows = MySQL.Sync.execute("DELETE FROM vehicle_keys WHERE owner = @owner and (plate = @plate OR plate = @trimmedPlate) and count = 1", {
|
||||
["@owner"] = Playera.PlayerData.citizenid,
|
||||
["@plate"] = plate,
|
||||
["@trimmedPlate"] = trimmedPlate
|
||||
})
|
||||
|
||||
if (rows == 0) then
|
||||
rows = MySQL.Sync.execute("UPDATE vehicle_keys SET count = count - 1 WHERE owner = @owner and (plate = @plate OR plate = @trimmedPlate)", {
|
||||
["@owner"] = Playera.PlayerData.citizenid,
|
||||
["@plate"] = plate,
|
||||
["@trimmedPlate"] = trimmedPlate
|
||||
})
|
||||
end
|
||||
|
||||
if (rows == 0) then
|
||||
return false
|
||||
else
|
||||
local results = MySQL.Sync.fetchAll("SELECT count FROM vehicle_keys WHERE owner = @owner AND (plate = @plate OR plate = @trimmedPlate)", {
|
||||
["@owner"] = Playerb.PlayerData.citizenid,
|
||||
["@plate"] = plate,
|
||||
["@trimmedPlate"] = trimmedPlate
|
||||
})
|
||||
|
||||
rows = 0
|
||||
if (#results > 0) then
|
||||
rows = MySQL.Sync.execute("UPDATE vehicle_keys SET count = count + 1 WHERE owner = @owner AND (plate = @plate OR plate = @trimmedPlate)", {
|
||||
["@owner"] = Playerb.PlayerData.citizenid,
|
||||
["@plate"] = plate,
|
||||
["@trimmedPlate"] = trimmedPlate
|
||||
})
|
||||
else
|
||||
rows = MySQL.Sync.execute("INSERT INTO vehicle_keys (owner, plate, count) VALUES (@owner, @trimmedPlate, 1)", {
|
||||
["@owner"] = Playerb.PlayerData.citizenid,
|
||||
["@trimmedPlate"] = trimmedPlate
|
||||
})
|
||||
end
|
||||
|
||||
if (rows == 0) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
else
|
||||
print("^1[ERROR] \"playerData\" or \"playerData2\" was nil while giving a key for id " .. tostring(src))
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
TriggerClientEvent("VKC:giveKeyNotif", playerId, trimmedPlate)
|
||||
|
||||
return true
|
||||
end)
|
||||
|
||||
-- remove all keys from a plate
|
||||
CB:Register("VKC:removeAllKeys", function(source, plate)
|
||||
local src = source
|
||||
|
||||
if (plate == nil) then
|
||||
print("^1[ERROR] \"plate\" was nil while removing all keys for " .. tostring(src))
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
local Player = QBCore.Functions.GetPlayer(src)
|
||||
if (Player) then
|
||||
local trimmedPlate = plate:gsub("^%s*(.-)%s*$", "%1"):upper()
|
||||
|
||||
if (Player.Functions.GetMoney(Config.Costs.GetMoney) >= Config.Costs.exchangeLocks) then
|
||||
MySQL.Sync.execute("DELETE FROM vehicle_keys WHERE plate = @trimmedPlate", {
|
||||
["@trimmedPlate"] = trimmedPlate
|
||||
})
|
||||
|
||||
Player.Functions.RemoveMoney(Config.Costs.GetMoney, Config.Costs.exchangeLocks)
|
||||
|
||||
return true
|
||||
else
|
||||
return "noMoney"
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end)
|
||||
|
||||
-- get all owned vehicles from player
|
||||
function GetPlayerVehicleData(playerId)
|
||||
if (playerId == nil) then
|
||||
print("^1[ERROR] Parameter \"playerId\" was nil while triggering server export \"GetOwnedVehicles\"!")
|
||||
return
|
||||
end
|
||||
local Player = QBCore.Functions.GetPlayer(playerId)
|
||||
local vehicles = {}
|
||||
if (Player) then
|
||||
local results = MySQL.Sync.fetchAll("SELECT plate, vehicle FROM player_vehicles WHERE citizenid = @owner", {
|
||||
["@owner"] = Player.PlayerData.citizenid
|
||||
})
|
||||
|
||||
for i = 1, #results, 1 do
|
||||
print(json.encode(results[i]))
|
||||
table.insert(vehicles, {
|
||||
results[i].plate,
|
||||
results[i].vehicle})
|
||||
--json.decode(results[i].vehicles).model})
|
||||
end
|
||||
else
|
||||
print("^1[ERROR] \"playerData\" was nil while getting owned vehicles for id " .. tostring(playerId))
|
||||
end
|
||||
|
||||
print(json.encode(vehicles))
|
||||
return vehicles
|
||||
end
|
||||
CB:Register("VKC:getPlayerVehicleData", GetPlayerVehicleData)
|
||||
|
||||
-- get all owned keys from player
|
||||
function GetPlayerKeys(playerId)
|
||||
if (playerId == nil) then
|
||||
print("^1[ERROR] Parameter \"playerId\" was nil while triggering server export \"GetOwnedKeys\"!")
|
||||
return
|
||||
end
|
||||
|
||||
local keys = {}
|
||||
|
||||
local Player = QBCore.Functions.GetPlayer(playerId)
|
||||
if (Player) then
|
||||
local results = MySQL.Sync.fetchAll("SELECT vehicle_keys.plate, vehicle_keys.count, player_vehicles.vehicle FROM vehicle_keys INNER JOIN player_vehicles ON vehicle_keys.plate = player_vehicles.plate WHERE vehicle_keys.owner = @owner", {
|
||||
["@owner"] = Player.PlayerData.citizenid
|
||||
})
|
||||
|
||||
for i = 1, #results, 1 do
|
||||
table.insert(keys, {
|
||||
plate = results[i].plate,
|
||||
count = results[i].count,
|
||||
model = results[i].vehicle --json.decode(results[i].vehicle).model
|
||||
})
|
||||
end
|
||||
else
|
||||
print("^1[ERROR] \"playerData\" was nil while getting owned keys for id " .. tostring(playerId))
|
||||
end
|
||||
|
||||
return keys
|
||||
end
|
||||
CB:Register("VKC:getPlayerKeys", GetPlayerKeys)
|
||||
|
||||
-- return if playerId is owner of vehicle
|
||||
function IsVehicleOwner(playerId, plate)
|
||||
if (playerId == nil) then
|
||||
print("^1[ERROR] Parameter \"playerId\" was nil while triggering server export \"IsVehicleOwner\"!")
|
||||
return
|
||||
end
|
||||
if (plate == nil) then
|
||||
print("^1[ERROR] Parameter \"plate\" was nil while triggering server export \"IsVehicleOwner\"!")
|
||||
return
|
||||
end
|
||||
|
||||
local Player = QBCore.Functions.GetPlayer(playerId)
|
||||
if (Player) then
|
||||
local trimmedPlate = plate:gsub("^%s*(.-)%s*$", "%1"):upper()
|
||||
|
||||
local results = MySQL.Sync.fetchAll("SELECT plate FROM player_vehicles WHERE citizenid = @owner and (plate = @plate OR plate = @trimmedPlate)", {
|
||||
["@owner"] = Player.PlayerData.citizenid,
|
||||
["@plate"] = plate,
|
||||
["@trimmedPlate"] = trimmedPlate
|
||||
})
|
||||
|
||||
if (#results > 0) then
|
||||
return true
|
||||
end
|
||||
else
|
||||
print("^1[ERROR] \"playerData\" was nil while getting vehicle ownership for id " .. tostring(playerId))
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
CB:Register("VKC:isVehicleOwner", IsVehicleOwner)
|
||||
|
||||
-- return if playerId is owner of key
|
||||
function IsKeyOwner(playerId, plate, model)
|
||||
if (playerId == nil) then
|
||||
print("^1[ERROR] Parameter \"playerId\" was nil while triggering server export \"IsKeyOwner\"!")
|
||||
return
|
||||
end
|
||||
if (plate == nil) then
|
||||
print("^1[ERROR] Parameter \"plate\" was nil while triggering server export \"IsKeyOwner\"!")
|
||||
return
|
||||
end
|
||||
if (model == nil) then
|
||||
print("^1[ERROR] Parameter \"model\" was nil while triggering server export \"IsKeyOwner\"!")
|
||||
return
|
||||
end
|
||||
|
||||
local Player = QBCore.Functions.GetPlayer(playerId)
|
||||
if (Player) then
|
||||
local trimmedPlate = plate:gsub("^%s*(.-)%s*$", "%1"):upper()
|
||||
|
||||
if (IsJobVehicle(Player.PlayerData.job.name, trimmedPlate, model)) then
|
||||
return true
|
||||
end
|
||||
|
||||
local results = MySQL.Sync.fetchAll("SELECT plate FROM vehicle_keys WHERE owner = @owner and (plate = @plate OR plate = @trimmedPlate)", {
|
||||
["@owner"] = Player.PlayerData.citizenid,
|
||||
["@plate"] = plate,
|
||||
["@trimmedPlate"] = trimmedPlate
|
||||
})
|
||||
|
||||
if (#results > 0) then
|
||||
return true
|
||||
end
|
||||
else
|
||||
print("^1[ERROR] \"playerData\" was nil while getting key ownership for id " .. tostring(playerId))
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
exports["kimi_callbacks"]:Register("VKC:isKeyOwner", IsKeyOwner)
|
||||
|
||||
-- return if playerId is owner of vehicle or key
|
||||
function IsVehicleOrKeyOwner(playerId, plate, model)
|
||||
if (playerId == nil) then
|
||||
print("^1[ERROR] Parameter \"playerId\" was nil while triggering server export \"IsVehicleOrKeyOwner\"!")
|
||||
return
|
||||
end
|
||||
if (plate == nil) then
|
||||
print("^1[ERROR] Parameter \"plate\" was nil while triggering server export \"IsVehicleOrKeyOwner\"!")
|
||||
return
|
||||
end
|
||||
if (model == nil) then
|
||||
print("^1[ERROR] Parameter \"model\" was nil while triggering server export \"IsVehicleOrKeyOwner\"!")
|
||||
return
|
||||
end
|
||||
|
||||
local Player = QBCore.Functions.GetPlayer(playerId)
|
||||
if (Player) then
|
||||
local trimmedPlate = plate:gsub("^%s*(.-)%s*$", "%1"):upper()
|
||||
|
||||
if (IsJobVehicle(Player.PlayerData.job.name, trimmedPlate, model)) then
|
||||
return true
|
||||
end
|
||||
|
||||
local results = MySQL.Sync.fetchAll("SELECT plate FROM player_vehicles WHERE citizenid = @owner and (plate = @plate OR plate = @trimmedPlate)", {
|
||||
["@owner"] = Player.PlayerData.citizenid,
|
||||
["@plate"] = plate,
|
||||
["@trimmedPlate"] = trimmedPlate
|
||||
})
|
||||
|
||||
if (#results > 0) then
|
||||
return true
|
||||
end
|
||||
|
||||
results = MySQL.Sync.fetchAll("SELECT plate FROM vehicle_keys WHERE owner = @owner and (plate = @plate OR plate = @trimmedPlate)", {
|
||||
["@owner"] = Player.PlayerData.citizenid,
|
||||
["@plate"] = plate,
|
||||
["@trimmedPlate"] = trimmedPlate
|
||||
})
|
||||
|
||||
if (#results > 0) then
|
||||
return true
|
||||
end
|
||||
else
|
||||
print("^1[ERROR] \"playerData\" was nil while getting vehicle or key ownership for id " .. tostring(playerId))
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
CB:Register("VKC:isVehicleOrKeyOwner", IsVehicleOrKeyOwner)
|
||||
|
||||
|
||||
|
||||
-- if the given vehicle is a job vehicle
|
||||
function IsJobVehicle(job, plate, model)
|
||||
local jobData = Config.JobVehicles[job]
|
||||
|
||||
if (jobData == nil) then
|
||||
return false
|
||||
end
|
||||
|
||||
for i, m in ipairs(jobData.models) do
|
||||
if (m == model) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
for i, p in ipairs(jobData.plates) do
|
||||
if (plate:find(p:upper())) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- toggle door lock over network to ensure it always works
|
||||
RegisterServerEvent("VKC:toggleLockNet")
|
||||
AddEventHandler("VKC:toggleLockNet", function(vehicleNetId, unlocked)
|
||||
local vehicle = NetworkGetEntityFromNetworkId(vehicleNetId)
|
||||
if (DoesEntityExist(vehicle)) then
|
||||
local entityOwner = NetworkGetEntityOwner(vehicle)
|
||||
TriggerClientEvent("VKC:toggleLockOnPlayer", entityOwner, vehicleNetId, unlocked)
|
||||
end
|
||||
end)
|
||||
RegisterServerEvent("VKC:playDoorLockSoundNet")
|
||||
AddEventHandler("VKC:playDoorLockSoundNet", function(vehicleNetId, lock)
|
||||
TriggerClientEvent("VKC:playDoorLockSound", -1, vehicleNetId, lock)
|
||||
end)
|
||||
|
||||
|
||||
RegisterServerEvent('VKC:setvehkey')
|
||||
AddEventHandler('VKC:setvehkey', function(plate)
|
||||
local _source = source
|
||||
local xPlayer = QBCore.Functions.GetPlayer(_source)
|
||||
|
||||
MySQL.query("INSERT INTO vehicle_keys(owner, plate, count) VALUES (?, ?, ?)", {xPlayer.PlayerData.citizenid, plate, 1})
|
||||
end)
|
||||
|
||||
RegisterServerEvent('VKC:delvehkey')
|
||||
AddEventHandler('VKC:delvehkey', function(plate, owner)
|
||||
local _source = source
|
||||
if owner then
|
||||
local xPlayer = QBCore.Functions.GetPlayer(_source)
|
||||
|
||||
MySQL.query("DELETE FROM vehicle_keys WHERE plate = ? and owner = ?", {plate, xPlayer.PlayerData.citizenid})
|
||||
else
|
||||
MySQL.query("DELETE FROM vehicle_keys WHERE plate = ?", {plate})
|
||||
end
|
||||
end)
|
||||
|
||||
-- Fahrzeugbesitz an einen anderen Spieler übertragen
|
||||
CB:Register("VKC:transferVehicleOwnership", function(source, plate, targetPlayerId)
|
||||
local src = source
|
||||
|
||||
if (plate == nil or targetPlayerId == nil) then
|
||||
print("^1[ERROR] \"plate\" or \"targetPlayerId\" was nil while transferring vehicle ownership for id " .. tostring(src))
|
||||
return false
|
||||
end
|
||||
|
||||
local Player = QBCore.Functions.GetPlayer(src)
|
||||
local TargetPlayer = QBCore.Functions.GetPlayer(targetPlayerId)
|
||||
|
||||
if (not Player or not TargetPlayer) then
|
||||
print("^1[ERROR] Player or target player not found while transferring vehicle ownership")
|
||||
return false
|
||||
end
|
||||
|
||||
local trimmedPlate = plate:gsub("^%s*(.-)%s*$", "%1"):upper()
|
||||
|
||||
-- Überprüfen, ob der Spieler das Fahrzeug besitzt
|
||||
local isOwner = IsVehicleOwner(src, trimmedPlate)
|
||||
if not isOwner then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Fahrzeugmodell für die Benachrichtigung abrufen
|
||||
local vehicleModel = nil
|
||||
local results = MySQL.Sync.fetchAll("SELECT vehicle FROM player_vehicles WHERE plate = @plate AND citizenid = @owner", {
|
||||
["@plate"] = trimmedPlate,
|
||||
["@owner"] = Player.PlayerData.citizenid
|
||||
})
|
||||
|
||||
if #results > 0 then
|
||||
vehicleModel = results[1].vehicle
|
||||
end
|
||||
|
||||
-- Besitz in der Datenbank übertragen
|
||||
local success = MySQL.Sync.execute("UPDATE player_vehicles SET citizenid = @newOwner WHERE plate = @plate AND citizenid = @oldOwner", {
|
||||
["@newOwner"] = TargetPlayer.PlayerData.citizenid,
|
||||
["@plate"] = trimmedPlate,
|
||||
["@oldOwner"] = Player.PlayerData.citizenid
|
||||
})
|
||||
|
||||
if success > 0 then
|
||||
-- Alle Schlüssel auf den neuen Besitzer übertragen
|
||||
MySQL.Sync.execute("DELETE FROM vehicle_keys WHERE plate = @plate", {
|
||||
["@plate"] = trimmedPlate
|
||||
})
|
||||
|
||||
-- Einen Schlüssel dem neuen Besitzer geben
|
||||
MySQL.Sync.execute("INSERT INTO vehicle_keys (owner, plate, count) VALUES (@owner, @plate, 1)", {
|
||||
["@owner"] = TargetPlayer.PlayerData.citizenid,
|
||||
["@plate"] = trimmedPlate
|
||||
})
|
||||
|
||||
-- Den Zielspieler benachrichtigen
|
||||
TriggerClientEvent("VKC:vehicleTransferNotif", targetPlayerId, trimmedPlate, vehicleModel)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end)
|
27
resources/[Developer]/sn_vehicleKey/vehicle_keys.sql
Normal file
27
resources/[Developer]/sn_vehicleKey/vehicle_keys.sql
Normal file
|
@ -0,0 +1,27 @@
|
|||
-- --------------------------------------------------------
|
||||
-- Host: 127.0.0.1
|
||||
-- Server version: 10.1.28-MariaDB - mariadb.org binary distribution
|
||||
-- Server OS: Win32
|
||||
-- HeidiSQL Version: 11.0.0.5919
|
||||
-- --------------------------------------------------------
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET NAMES utf8 */;
|
||||
/*!50503 SET NAMES utf8mb4 */;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
|
||||
-- Dumping structure for table essentialmode.vehicle_keys
|
||||
CREATE TABLE IF NOT EXISTS `vehicle_keys` (
|
||||
`owner` varchar(60) NOT NULL,
|
||||
`plate` varchar(8) NOT NULL,
|
||||
`count` smallint(6) NOT NULL,
|
||||
KEY `plate` (`plate`),
|
||||
KEY `owner` (`owner`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
|
||||
-- Data exporting was unselected.
|
||||
|
||||
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */;
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
Loading…
Add table
Add a link
Reference in a new issue