367 lines
		
	
	
		
			No EOL
		
	
	
		
			12 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			367 lines
		
	
	
		
			No EOL
		
	
	
		
			12 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| local washingVehicle = false
 | |
| local WaitingForWash = false
 | |
| 
 | |
| local ptfxData = {
 | |
|     {
 | |
|         dict = 'cut_test',
 | |
|         name = 'exp_hydrant',
 | |
|         offset = {0.0,0.0,4.0},
 | |
|         rot = {0.0, 180.0, 0.0},
 | |
|         scale = 0.5,
 | |
|     },
 | |
| 
 | |
|     {
 | |
|         dict = 'cut_test',
 | |
|         name = 'exp_hydrant',
 | |
|         offset = {-0.5,0.0,4.0},
 | |
|         rot = {0.0, 180.0, 0.0},
 | |
|         scale = 0.5,
 | |
|     },
 | |
| 
 | |
|     {
 | |
|         dict = 'cut_test',
 | |
|         name = 'exp_hydrant',
 | |
|         offset = {-1.0,0.0,4.0},
 | |
|         rot = {0.0, 180.0, 0.0},
 | |
|         scale = 0.5,
 | |
|     },
 | |
| 
 | |
|     {
 | |
|         dict = 'cut_test',
 | |
|         name = 'exp_hydrant',
 | |
|         offset = {-1.5,0.0,4.0},
 | |
|         rot = {0.0, 180.0, 0.0},
 | |
|         scale = 0.5,
 | |
|     },
 | |
| 
 | |
|     {
 | |
|         dict = 'cut_test',
 | |
|         name = 'exp_hydrant',
 | |
|         offset = {-2.0,0.0,4.0},
 | |
|         rot = {0.0, 180.0, 0.0},
 | |
|         scale = 0.5,
 | |
|     },
 | |
| 
 | |
| 
 | |
|     {
 | |
|         dict = 'cut_test',
 | |
|         name = 'exp_hydrant',
 | |
|         offset = {0.5,0.0,4.0},
 | |
|         rot = {0.0, 180.0, 0.0},
 | |
|         scale = 0.5,
 | |
|     },
 | |
| 
 | |
|     {
 | |
|         dict = 'cut_test',
 | |
|         name = 'exp_hydrant',
 | |
|         offset = {1.0,0.0,4.0},
 | |
|         rot = {0.0, 180.0, 0.0},
 | |
|         scale = 0.5,
 | |
|     },
 | |
| 
 | |
|     {
 | |
|         dict = 'cut_test',
 | |
|         name = 'exp_hydrant',
 | |
|         offset = {1.5,0.0,4.0},
 | |
|         rot = {0.0, 180.0, 0.0},
 | |
|         scale = 0.5,
 | |
|     },
 | |
| 
 | |
|     {
 | |
|         dict = 'cut_test',
 | |
|         name = 'exp_hydrant',
 | |
|         offset = {2.0,0.0,4.0},
 | |
|         rot = {0.0, 180.0, 0.0},
 | |
|         scale = 0.5,
 | |
|     },
 | |
| 
 | |
|     {
 | |
|         dict = 'scr_fbi5a',
 | |
|         name = 'scr_tunnel_vent_bubbles',
 | |
|         offset = {0.0,0.0,0.0},
 | |
|         rot = {0.0,180.0,0.0},
 | |
|         scale = 2.0,
 | |
|     },
 | |
| 
 | |
|     {
 | |
|         dict = 'scr_fbi5a',
 | |
|         name = 'scr_tunnel_vent_bubbles',
 | |
|         offset = {0.0,-1.0,0.0},
 | |
|         rot = {0.0,180.0,0.0},
 | |
|         scale = 1.0,
 | |
|     },
 | |
| 
 | |
|     {
 | |
|         dict = 'scr_fbi5a',
 | |
|         name = 'scr_tunnel_vent_bubbles',
 | |
|         offset = {0.0,1.0,0.0},
 | |
|         rot = {0.0,180.0,0.0},
 | |
|         scale = 1.0,
 | |
|     },
 | |
| 
 | |
|     {
 | |
|         dict = 'scr_fbi5a',
 | |
|         name = 'scr_tunnel_vent_bubbles',
 | |
|         offset = {0.0,-2.0,0.0},
 | |
|         rot = {0.0,180.0,0.0},
 | |
|         scale = 1.0,
 | |
|     },
 | |
| 
 | |
|     {
 | |
|         dict = 'scr_fbi5a',
 | |
|         name = 'scr_tunnel_vent_bubbles',
 | |
|         offset = {0.0,2.0,0.0},
 | |
|         rot = {0.0,180.0,0.0},
 | |
|         scale = 1.0,
 | |
|     },
 | |
| }
 | |
| 
 | |
| local function RequestNetworkControlOfEntity(entity)
 | |
|     NetworkRequestControlOfEntity(entity)
 | |
| 
 | |
|     local timeout = 2000
 | |
| 
 | |
|     while timeout > 0 and not NetworkHasControlOfEntity(entity) do
 | |
|         Wait(100)
 | |
|         timeout = timeout - 100
 | |
|     end
 | |
| 
 | |
|     timeout = 2000
 | |
| 
 | |
|     SetEntityAsMissionEntity(entity, true, true)
 | |
| 
 | |
|     while timeout > 0 and not IsEntityAMissionEntity(entity) do
 | |
|         Wait(100)
 | |
|         timeout = timeout - 100
 | |
|     end
 | |
| end
 | |
| 
 | |
| local function RequestParticleFX(dict)
 | |
|     RequestNamedPtfxAsset(dict)
 | |
|     while not HasNamedPtfxAssetLoaded(dict) do Wait(0) end
 | |
| end
 | |
| 
 | |
| local function LoadPropDict(model)
 | |
|     if not IsModelInCdimage(model) then
 | |
|         print('ERROR: The model that is required does not exist, Contact a developer of the server.')
 | |
|         return
 | |
|     end
 | |
|     local time = 30
 | |
|     while not HasModelLoaded(model) and time > 0 do RequestModel(model) Wait(100) time -= 1 end
 | |
|     if time <= 0 then print('ERROR: loading required model failed, Contact a server developer.') end
 | |
| end
 | |
| 
 | |
| local function CreateProp(model, coords)
 | |
|     if not HasModelLoaded(model) then; LoadPropDict(model); end
 | |
|     local prop = CreateObjectNoOffset(model, coords.x, coords.y, coords.z, false, false, false)
 | |
|     SetEntityAsMissionEntity(prop, true, true)
 | |
|     FreezeEntityPosition(prop, true)
 | |
|     SetEntityCollision(prop, false, true)
 | |
|     return prop
 | |
| end
 | |
| 
 | |
| RegisterNetEvent('carwash:DoVehicleWashParticles')
 | |
| AddEventHandler('carwash:DoVehicleWashParticles', function(vehNet, washer, use_props)
 | |
|     if NetworkDoesEntityExistWithNetworkId(vehNet) then
 | |
|         if washer == GetPlayerServerId(PlayerId()) then
 | |
|             WaitingForWash = true
 | |
|             washer = true 
 | |
|         end
 | |
| 
 | |
|         local vehicle = NetworkGetEntityFromNetworkId(vehNet)
 | |
|         local ptfxHandles = {}
 | |
|         local side_props = nil
 | |
|         local min_offsets, max_offsets = GetModelDimensions(GetEntityModel(vehicle))
 | |
| 
 | |
|         if use_props then
 | |
|             local _, max_prop_dim = GetModelDimensions(`prop_carwash_roller_vert`)
 | |
|             local left_offest = GetOffsetFromEntityInWorldCoords(vehicle, min_offsets.x, min_offsets.y, min_offsets.z - 0.5);left_offest = vector3(left_offest.x + max_prop_dim.x, left_offest.y, left_offest.z)
 | |
|             local right_offset = GetOffsetFromEntityInWorldCoords(vehicle, max_offsets.x, min_offsets.y, min_offsets.z - 0.5);right_offset = vector3(right_offset.x - max_prop_dim.x, right_offset.y, right_offset.z)
 | |
| 
 | |
|             side_props = {
 | |
|                 {prop = CreateProp(`prop_carwash_roller_vert`, left_offest), offset = vector3(min_offsets.x - (max_prop_dim.x - 0.2), min_offsets.y, max_prop_dim.z/2)},
 | |
|                 {prop = CreateProp(`prop_carwash_roller_vert`, right_offset), offset = vector3(max_offsets.x + (max_prop_dim.x - 0.2), min_offsets.y, max_prop_dim.z/2)},
 | |
|             }
 | |
| 
 | |
|             for i =1, #side_props, 1 do
 | |
|                 Citizen.CreateThread(function()
 | |
|                     while side_props and side_props[i] and DoesEntityExist(side_props[i].prop) do
 | |
|                         if i == 1 then
 | |
|                             SetEntityHeading(side_props[i].prop, ((GetEntityHeading(side_props[i].prop) + 0.75) + 360) %360)
 | |
|                         elseif i == 2 then
 | |
|                             SetEntityHeading(side_props[i].prop, ((GetEntityHeading(side_props[i].prop) - 0.75) + 360) %360)
 | |
|                         end
 | |
|                         Citizen.Wait(0)
 | |
|                     end
 | |
|                 end)
 | |
|             end
 | |
|         end
 | |
| 
 | |
|         for index, ptfx in pairs(ptfxData) do
 | |
|             RequestParticleFX(ptfx.dict)
 | |
|             UseParticleFxAssetNextCall(ptfx.dict)
 | |
|             local CreatedParticle = StartNetworkedParticleFxLoopedOnEntity(ptfx.name, vehicle, ptfx.offset[1], ptfx.offset[2], ptfx.offset[3], ptfx.rot[1], ptfx.rot[2], ptfx.rot[3], ptfx.scale, false, false, false)
 | |
|             table.insert(ptfxHandles, CreatedParticle)
 | |
|         end
 | |
| 
 | |
|         local offset = min_offsets.y
 | |
|         local prop_offset = min_offsets.y
 | |
| 
 | |
|         while offset < max_offsets.y and DoesEntityExist(vehicle) do
 | |
|             for i = 1, #ptfxHandles do
 | |
|                 SetParticleFxLoopedOffsets(ptfxHandles[i], ptfxData[i].offset[1], offset, ptfxData[i].offset[3], ptfxData[i].rot[1], ptfxData[i].rot[2], ptfxData[i].rot[3])
 | |
|             end
 | |
| 
 | |
|             if side_props ~= nil then
 | |
|                 for i = 1, #side_props, 1 do
 | |
|                     SetEntityCoordsNoOffset(side_props[i].prop, GetOffsetFromEntityInWorldCoords(vehicle, side_props[i].offset.x, prop_offset, side_props[i].offset.z))
 | |
|                 end
 | |
|                 prop_offset += 0.0055
 | |
|             end
 | |
| 
 | |
|             offset += 0.0055
 | |
|             Wait(0)
 | |
|         end
 | |
| 
 | |
|         if Config.double_clean == true then
 | |
|             while min_offsets.y < offset and DoesEntityExist(vehicle) do
 | |
|                 for i = 1, #ptfxHandles, 1 do
 | |
|                     SetParticleFxLoopedOffsets(ptfxHandles[i], ptfxData[i].offset[1], offset, ptfxData[i].offset[3], ptfxData[i].rot[1], ptfxData[i].rot[2], ptfxData[i].rot[3])
 | |
|                 end
 | |
|                 if side_props ~= nil then
 | |
|                     for i = 1, #side_props, 1 do
 | |
|                         SetEntityCoordsNoOffset(side_props[i].prop, GetOffsetFromEntityInWorldCoords(vehicle, side_props[i].offset.x, prop_offset, side_props[i].offset.z))
 | |
|                     end
 | |
|                     prop_offset -= 0.0055
 | |
|                 end
 | |
|                 offset -= 0.0055
 | |
|                 Wait(0)
 | |
|             end
 | |
|         end
 | |
| 
 | |
|         for i = 1, #ptfxHandles do StopParticleFxLooped(ptfxHandles[i], false) end
 | |
| 
 | |
|         if side_props ~= nil then
 | |
|             for i = 1, #side_props, 1 do DeleteEntity(side_props[i].prop) end
 | |
|             side_props = nil
 | |
|         end
 | |
|         
 | |
|         if washer == true then
 | |
|             RequestNetworkControlOfEntity(vehicle)
 | |
|             SetVehicleDirtLevel(vehicle, 0.0)
 | |
|             WashDecalsFromVehicle(vehicle, 1.0)
 | |
|             Wait(1000)
 | |
|             FreezeEntityPosition(vehicle, false)
 | |
|             Notify('Vehicle Washed', 'success')
 | |
|             WaitingForWash = false
 | |
|             washingVehicle = false
 | |
|         end
 | |
|     end
 | |
| end)
 | |
| 
 | |
| local function WashVehicle(vehicle, use_props)
 | |
|     if washingVehicle then return end
 | |
|     washingVehicle = true
 | |
|     TriggerCallback("carwash:CanPurchaseCarWash", function(paid)
 | |
|         if paid then
 | |
|             FreezeEntityPosition(vehicle, true)
 | |
|             TriggerServerEvent('carwash:DoVehicleWashParticles', VehToNet(vehicle), use_props)
 | |
|         else
 | |
|             washingVehicle = false
 | |
|         end
 | |
|     end)
 | |
| end
 | |
| 
 | |
| local function ShowHelpNotification(msg, thisFrame, beep, duration)
 | |
| 	AddTextEntry('HelpNotification', msg)
 | |
| 
 | |
| 	if thisFrame then
 | |
| 		DisplayHelpTextThisFrame('HelpNotification', false)
 | |
| 	else
 | |
| 		if (beep == nil or beep == false) then beep = false else beep = true end
 | |
| 		BeginTextCommandDisplayHelp('HelpNotification')
 | |
| 		EndTextCommandDisplayHelp(0, false, beep, duration or -1)
 | |
| 	end
 | |
| end
 | |
| 
 | |
| Citizen.CreateThread(function()
 | |
|     while true do
 | |
|         local sleep = 1000
 | |
|         local PlayerPed = PlayerPedId()
 | |
|         if IsPedInAnyVehicle(PlayerPed, false) and not isWashingVehicle then
 | |
|             local myVehicle = GetVehiclePedIsIn(PlayerPed)
 | |
|             if GetPedInVehicleSeat(myVehicle, -1) == PlayerPed and (Config.only_dirty_vehicles == true and GetVehicleDirtLevel(myVehicle) >= 0.1 or Config.only_dirty_vehicles == false) then
 | |
|                 local coords = GetEntityCoords(PlayerPed)
 | |
|                 for _, carwash in pairs(Config.locations) do
 | |
|                     local dist = #(coords - carwash.location)
 | |
|                     if dist <= 10.0 then
 | |
|                         sleep = 100
 | |
|                         if dist <= 2.0 then
 | |
|                             ShowHelpNotification((Config.button[1]):format(Config.cost), true, false, -1)
 | |
|                             sleep = 0
 | |
|                             if IsControlJustPressed(Config.button[2], Config.button[3]) then
 | |
|                                 WashVehicle(myVehicle, carwash.use_props)
 | |
|                             end
 | |
|                         end
 | |
|                     end
 | |
|                 end
 | |
|             end
 | |
|         end
 | |
|         Citizen.Wait(sleep)
 | |
|     end
 | |
| end)
 | |
| 
 | |
| local function CreateCarwashBlip(coords, name)
 | |
|     local blip = AddBlipForCoord(coords)
 | |
| 
 | |
|     SetBlipSprite(blip, 100)
 | |
|     SetBlipScale(blip, 0.8)
 | |
|     SetBlipColour(blip, 2)
 | |
|     SetBlipDisplay(blip, 4)
 | |
|     SetBlipAsShortRange(blip, true)
 | |
| 
 | |
|     BeginTextCommandSetBlipName("STRING")
 | |
|     AddTextComponentString(name)
 | |
|     EndTextCommandSetBlipName(blip)
 | |
| 
 | |
|     return blip
 | |
| end
 | |
| 
 | |
| if Config.show_all_blips then
 | |
|     local blips = {}
 | |
| 
 | |
|     for _, carwash in pairs(Config.locations) do
 | |
|         if carwash.show_blip == true then
 | |
|             blips[#blips +1] = CreateCarwashBlip(carwash.location, carwash.name)
 | |
|         end
 | |
|     end
 | |
| else
 | |
|     Citizen.CreateThread(function()
 | |
|         local currentCarWashBlip = nil
 | |
|         local currentCarWashBlipLocation = nil
 | |
|     
 | |
|         while true do
 | |
|             local coords = GetEntityCoords(PlayerPedId())
 | |
|             local closest = 999999
 | |
|             local closestCoords, closestName 
 | |
|     
 | |
|             for _, carwash in pairs(Config.locations) do
 | |
|                 local dstcheck = #(coords.xy - carwash.location.xy)
 | |
|     
 | |
|                 if dstcheck < closest and carwash.show_blip == true then
 | |
|                     closest = dstcheck
 | |
|                     closestCoords = carwash.location
 | |
|                     closestName = carwash.name
 | |
|                 end
 | |
|             end
 | |
|     
 | |
|             if currentCarWashBlipLocation ~= closestCoords then
 | |
|                 if DoesBlipExist(currentCarWashBlip) then RemoveBlip(currentCarWashBlip) end
 | |
|                 currentCarWashBlip = CreateCarwashBlip(closestCoords, closestName)
 | |
|                 currentCarWashBlipLocation = closestCoords
 | |
|             end
 | |
|     
 | |
|             Citizen.Wait(10000)
 | |
|         end
 | |
|     end)
 | |
| end | 
