675 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			675 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| -- FiveM Heli Cam by mraes, version 1.3 (2017-06-12)
 | |
| -- Modified by rjross2013 (2017-06-23)
 | |
| -- Further modified by Loque (2017-08-15) with credits to the following for tips gleaned from their scripts: Guadmaz's Simple Police Searchlight, devilkkw's Speed Camera, nynjardin's Simple Outlaw Alert and IllidanS4's FiveM Entity Iterators.
 | |
| 
 | |
| ---=========================
 | |
| --- Config				====
 | |
| ---=========================
 | |
| local fov_max = 80.0
 | |
| local fov_min = 5.0 -- max zoom level (smaller fov is more zoom)
 | |
| local zoomspeed = 3.0 -- camera zoom speed
 | |
| local speed_lr = 4.0 -- speed by which the camera pans left-right 
 | |
| local speed_ud = 4.0 -- speed by which the camera pans up-down
 | |
| local toggle_helicam = 51 -- control id of the button by which to toggle the helicam mode. Default: INPUT_CONTEXT (E)
 | |
| local toggle_vision = 25 -- control id to toggle vision mode. Default: INPUT_AIM (Right mouse btn)
 | |
| local toggle_rappel = 154 -- control id to rappel out of the heli. Default: INPUT_DUCK (X)
 | |
| local toggle_spotlight = 183 -- control id to toggle the various spotlight states Default: INPUT_PhoneCameraGrid (G)
 | |
| local toggle_lock_on = 22 -- control id to lock onto a vehicle with the camera or unlock from vehicle (with or without camera). Default is INPUT_SPRINT (spacebar)
 | |
| local toggle_display = 44 -- control id to toggle vehicle info display. Default: INPUT_COVER (Q)
 | |
| local lightup_key = 246 -- control id to increase spotlight brightness. Default: INPUT_MP_TEXT_CHAT_TEAM (Y)
 | |
| local lightdown_key = 173 -- control id to decrease spotlight brightness. Default: INPUT_CELLPHONE_DOWN  (ARROW-DOWN)
 | |
| local radiusup_key = 137 -- control id to increase manual spotlight radius. Default: INPUT_VEH_PUSHBIKE_SPRINT (CAPSLOCK)
 | |
| local radiusdown_key = 21 -- control id to decrease spotlight radius. Default: INPUT_SPRINT (LEFT-SHIFT)
 | |
| local maxtargetdistance = 700 -- max distance at which target lock is maintained
 | |
| local brightness = 1.0 -- default spotlight brightness
 | |
| local spotradius = 4.0 -- default manual spotlight radius
 | |
| local speed_measure = "Km/h" -- default unit to measure vehicle speed but can be changed to "MPH". Use either exact string, "Km/h" or "MPH", or else functions break.
 | |
| 
 | |
| heli = {
 | |
| "polmav",
 | |
| "as350",
 | |
| "uh1",
 | |
| "uh2",
 | |
| "uh3",
 | |
| "uh4"
 | |
| 
 | |
| }
 | |
| 
 | |
| -- Script starts here
 | |
| local target_vehicle = nil
 | |
| local manual_spotlight = false
 | |
| local tracking spotlight = false
 | |
| local vehicle_display = 0 -- 0 is default full vehicle info display with speed/model/plate, 1 is model/plate, 2 turns off display
 | |
| local helicam = false
 | |
| local fov = (fov_max+fov_min)*0.5
 | |
| local vision_state = 0 -- 0 is normal, 1 is nightmode, 2 is thermal vision
 | |
| 
 | |
| Citizen.CreateThread(function() -- Register ped decorators used to pass some variables from heli pilot to other players (variable settings: 1=false, 2=true)
 | |
| 	while true do
 | |
| 	Citizen.Wait(0)
 | |
| 		if NetworkIsSessionStarted() then
 | |
| 			DecorRegister("SpotvectorX", 3) -- For direction of manual spotlight
 | |
| 			DecorRegister("SpotvectorY", 3)
 | |
| 			DecorRegister("SpotvectorZ", 3)
 | |
| 			DecorRegister("Target", 3) -- Backup method of target ID
 | |
| 			return
 | |
| 		end
 | |
| 	end
 | |
| end)
 | |
| 
 | |
| Citizen.CreateThread(function()
 | |
| 	while true do
 | |
|         Citizen.Wait(0)
 | |
| 		if IsPlayerInPolmav() then
 | |
| 			local lPed = GetPlayerPed(-1)
 | |
| 			local heli = GetVehiclePedIsIn(lPed)
 | |
| 			
 | |
| 			if IsHeliHighEnough(heli) then
 | |
| 				if IsControlJustPressed(0, toggle_helicam) then -- Toggle Helicam
 | |
| 					PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
 | |
| 					helicam = true
 | |
| 				end
 | |
| 				
 | |
| 				if IsControlJustPressed(0, toggle_rappel) then -- Initiate rappel
 | |
| 					Citizen.Trace("try to rappel")
 | |
| 					if GetPedInVehicleSeat(heli, 1) == lPed or GetPedInVehicleSeat(heli, 2) == lPed then
 | |
| 						PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
 | |
| 						TaskRappelFromHeli(GetPlayerPed(-1), 1)
 | |
| 					else
 | |
| 						SetNotificationTextEntry( "STRING" )
 | |
| 						AddTextComponentString("~r~Can't rappel from this seat")
 | |
| 						DrawNotification(false, false )
 | |
| 						PlaySoundFrontend(-1, "5_Second_Timer", "DLC_HEISTS_GENERAL_FRONTEND_SOUNDS", false) 
 | |
| 					end
 | |
| 				end
 | |
| 			end
 | |
| 			
 | |
| 			if IsControlJustPressed(0, toggle_spotlight) and GetPedInVehicleSeat(heli, -1) == lPed and not helicam then -- Toggle forward and tracking spotlight states
 | |
| 				if target_vehicle then
 | |
| 					if tracking_spotlight then
 | |
| 						if not pause_Tspotlight then
 | |
| 							pause_Tspotlight = true
 | |
| 							TriggerServerEvent("heli:pause.tracking.spotlight", pause_Tspotlight)
 | |
| 						else
 | |
| 							pause_Tspotlight = false
 | |
| 							TriggerServerEvent("heli:pause.tracking.spotlight", pause_Tspotlight)
 | |
| 						end
 | |
| 						PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
 | |
| 					else
 | |
| 						if Fspotlight_state then
 | |
| 							Fspotlight_state = false	
 | |
| 							TriggerServerEvent("heli:forward.spotlight", Fspotlight_state)
 | |
| 						end
 | |
| 						local target_netID = VehToNet(target_vehicle)
 | |
| 						local target_plate = GetVehicleNumberPlateText(target_vehicle)
 | |
| 						local targetposx, targetposy, targetposz = table.unpack(GetEntityCoords(target_vehicle))
 | |
| 						pause_Tspotlight = false
 | |
| 						tracking_spotlight = true
 | |
| 						TriggerServerEvent("heli:tracking.spotlight", target_netID, target_plate, targetposx, targetposy, targetposz)
 | |
| 						PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
 | |
| 					end				
 | |
| 				else
 | |
| 					if tracking_spotlight then
 | |
| 						pause_Tspotlight = false
 | |
| 						tracking_spotlight = false
 | |
| 						TriggerServerEvent("heli:tracking.spotlight.toggle")
 | |
| 					end
 | |
| 					Fspotlight_state = not Fspotlight_state
 | |
| 					TriggerServerEvent("heli:forward.spotlight", Fspotlight_state)
 | |
| 					PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
 | |
| 				end
 | |
| 			end
 | |
| 
 | |
| 			if IsControlJustPressed(0, toggle_display) and GetPedInVehicleSeat(heli, -1) == lPed then 
 | |
| 				ChangeDisplay()
 | |
| 			end
 | |
| 
 | |
| 			if target_vehicle and GetPedInVehicleSeat(heli, -1) == lPed then
 | |
| 				local coords1 = GetEntityCoords(heli)
 | |
| 				local coords2 = GetEntityCoords(target_vehicle)
 | |
| 				local target_distance = GetDistanceBetweenCoords(coords1.x, coords1.y, coords1.z, coords2.x, coords2.y, coords2.z, false)
 | |
| 				if IsControlJustPressed(0, toggle_lock_on) or target_distance > maxtargetdistance then
 | |
| 					--Citizen.Trace("Heli: target vehicle released or lost")
 | |
| 					DecorRemove(target_vehicle, "Target")
 | |
| 					if tracking_spotlight then
 | |
| 						TriggerServerEvent("heli:tracking.spotlight.toggle")
 | |
| 					end
 | |
| 					tracking_spotlight = false
 | |
| 					pause_Tspotlight = false
 | |
| 					target_vehicle = nil					
 | |
| 					PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
 | |
| 				end
 | |
| 			end
 | |
| 
 | |
| 		end
 | |
| 		
 | |
| 		if helicam then
 | |
| 			SetTimecycleModifier("heliGunCam")
 | |
| 			SetTimecycleModifierStrength(0.3)
 | |
| 			local scaleform = RequestScaleformMovie("HELI_CAM")
 | |
| 			while not HasScaleformMovieLoaded(scaleform) do
 | |
| 				Citizen.Wait(0)
 | |
| 			end
 | |
| 			local lPed = GetPlayerPed(-1)
 | |
| 			local heli = GetVehiclePedIsIn(lPed)
 | |
| 			local cam = CreateCam("DEFAULT_SCRIPTED_FLY_CAMERA", true)
 | |
| 			AttachCamToEntity(cam, heli, 0.0,0.0,-1.5, true)
 | |
| 			SetCamRot(cam, 0.0,0.0,GetEntityHeading(heli))
 | |
| 			SetCamFov(cam, fov)
 | |
| 			RenderScriptCams(true, false, 0, 1, 0)
 | |
| 			PushScaleformMovieFunction(scaleform, "SET_CAM_LOGO")
 | |
| 			PushScaleformMovieFunctionParameterInt(0) -- 0 for nothing, 1 for LSPD logo
 | |
| 			PopScaleformMovieFunctionVoid()
 | |
| 			local locked_on_vehicle = nil
 | |
| 			while helicam and not IsEntityDead(lPed) and (GetVehiclePedIsIn(lPed) == heli) and IsHeliHighEnough(heli) do
 | |
| 				if IsControlJustPressed(0, toggle_helicam) then -- Toggle Helicam
 | |
| 					PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
 | |
| 					if manual_spotlight and target_vehicle then -- If exiting helicam while manual spotlight is locked on a target, transition to non-helicam auto tracking spotlight
 | |
| 						TriggerServerEvent("heli:manual.spotlight.toggle")
 | |
| 						local target_netID = VehToNet(target_vehicle)
 | |
| 						local target_plate = GetVehicleNumberPlateText(target_vehicle)
 | |
| 						local targetposx, targetposy, targetposz = table.unpack(GetEntityCoords(target_vehicle))
 | |
| 						pause_Tspotlight = false
 | |
| 						tracking_spotlight = true
 | |
| 						TriggerServerEvent("heli:tracking.spotlight", target_netID, target_plate, targetposx, targetposy, targetposz)
 | |
| 						PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
 | |
| 					end
 | |
| 					manual_spotlight = false
 | |
| 					helicam = false
 | |
| 				end
 | |
| 
 | |
| 				if IsControlJustPressed(0, toggle_vision) then
 | |
| 					PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
 | |
| 					ChangeVision()
 | |
| 				end
 | |
| 
 | |
| 				if IsControlJustPressed(0, toggle_spotlight) then -- Spotlight_toggles within helicam
 | |
| 					if tracking_spotlight then -- If tracking spotlight active, pause it & toggle manual spotlight
 | |
| 						pause_Tspotlight = true
 | |
| 						TriggerServerEvent("heli:pause.tracking.spotlight", pause_Tspotlight)
 | |
| 						manual_spotlight = not manual_spotlight
 | |
| 						if manual_spotlight then
 | |
| 							local rotation = GetCamRot(cam, 2)
 | |
| 							local forward_vector = RotAnglesToVec(rotation)
 | |
| 							local SpotvectorX, SpotvectorY, SpotvectorZ = table.unpack(forward_vector)
 | |
| 							DecorSetInt(lPed, "SpotvectorX", SpotvectorX)
 | |
| 							DecorSetInt(lPed, "SpotvectorY", SpotvectorY)
 | |
| 							DecorSetInt(lPed, "SpotvectorZ", SpotvectorZ)
 | |
| 							PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
 | |
| 							TriggerServerEvent("heli:manual.spotlight")
 | |
| 						else
 | |
| 							TriggerServerEvent("heli:manual.spotlight.toggle")
 | |
| 						end
 | |
| 					elseif Fspotlight_state then -- If forward spotlight active, disable it & toggle manual spotlight
 | |
| 						Fspotlight_state = false
 | |
| 						TriggerServerEvent("heli:forward.spotlight", Fspotlight_state)
 | |
| 						manual_spotlight = not manual_spotlight
 | |
| 						if manual_spotlight then
 | |
| 							PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
 | |
| 							TriggerServerEvent("heli:manual.spotlight")
 | |
| 						else
 | |
| 							TriggerServerEvent("heli:manual.spotlight.toggle")
 | |
| 						end
 | |
| 					else -- If no other spotlight mode active, toggle manual spotlight
 | |
| 						manual_spotlight = not manual_spotlight
 | |
| 						if manual_spotlight then
 | |
| 							PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
 | |
| 							TriggerServerEvent("heli:manual.spotlight")
 | |
| 						else
 | |
| 							TriggerServerEvent("heli:manual.spotlight.toggle")
 | |
| 						end
 | |
| 					end
 | |
| 				end
 | |
| 
 | |
| 				if IsControlJustPressed(0, lightup_key) then
 | |
| 					TriggerServerEvent("heli:light.up")
 | |
| 				end
 | |
| 
 | |
| 				if IsControlJustPressed(0, lightdown_key) then
 | |
| 					TriggerServerEvent("heli:light.down")
 | |
| 				end
 | |
| 
 | |
| 				if IsControlJustPressed(0, radiusup_key) then
 | |
| 					TriggerServerEvent("heli:radius.up")
 | |
| 				end
 | |
| 
 | |
| 				if IsControlJustPressed(0, radiusdown_key) then
 | |
| 					TriggerServerEvent("heli:radius.down")
 | |
| 				end
 | |
| 
 | |
| 				if IsControlJustPressed(0, toggle_display) then 
 | |
| 					ChangeDisplay()
 | |
| 				end
 | |
| 
 | |
| 				if locked_on_vehicle then
 | |
| 					if DoesEntityExist(locked_on_vehicle) then
 | |
| 						PointCamAtEntity(cam, locked_on_vehicle, 0.0, 0.0, 0.0, true)
 | |
| 						RenderVehicleInfo(locked_on_vehicle)
 | |
| 						local coords1 = GetEntityCoords(heli)
 | |
| 						local coords2 = GetEntityCoords(locked_on_vehicle)
 | |
| 						local target_distance = GetDistanceBetweenCoords(coords1.x, coords1.y, coords1.z, coords2.x, coords2.y, coords2.z, false)
 | |
| 						if IsControlJustPressed(0, toggle_lock_on) or target_distance > maxtargetdistance then
 | |
| 							--Citizen.Trace("Heli: locked_on_vehicle unlocked or lost")
 | |
| 							PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
 | |
| 							DecorRemove(target_vehicle, "Target")
 | |
| 							if tracking_spotlight then
 | |
| 								TriggerServerEvent("heli:tracking.spotlight.toggle")
 | |
| 								tracking_spotlight = false
 | |
| 							end
 | |
| 							target_vehicle = nil
 | |
| 							locked_on_vehicle = nil
 | |
| 							local rot = GetCamRot(cam, 2) -- All this because I can't seem to get the camera unlocked from the entity
 | |
| 							local fov = GetCamFov(cam)
 | |
| 							local old cam = cam
 | |
| 							DestroyCam(old_cam, false)
 | |
| 							cam = CreateCam("DEFAULT_SCRIPTED_FLY_CAMERA", true)
 | |
| 							AttachCamToEntity(cam, heli, 0.0,0.0,-1.5, true)
 | |
| 							SetCamRot(cam, rot, 2)
 | |
| 							SetCamFov(cam, fov)
 | |
| 							RenderScriptCams(true, false, 0, 1, 0)
 | |
| 						end
 | |
| 					else
 | |
| 						locked_on_vehicle = nil -- Cam will auto unlock when entity doesn't exist anyway
 | |
| 						target_vehicle = nil
 | |
| 					end
 | |
| 				else
 | |
| 					local zoomvalue = (1.0/(fov_max-fov_min))*(fov-fov_min)
 | |
| 					CheckInputRotation(cam, zoomvalue)
 | |
| 					local vehicle_detected = GetVehicleInView(cam)
 | |
| 					if DoesEntityExist(vehicle_detected) then
 | |
| 						RenderVehicleInfo(vehicle_detected)
 | |
| 						if IsControlJustPressed(0, toggle_lock_on) then
 | |
| 							PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
 | |
| 							locked_on_vehicle = vehicle_detected
 | |
| 			
 | |
| 							if target_vehicle then -- If previous target exists, remove old target decorator before updating target vehicle
 | |
| 								DecorRemove(target_vehicle, "Target")
 | |
| 							end
 | |
| 							
 | |
| 							target_vehicle = vehicle_detected
 | |
| 							NetworkRequestControlOfEntity(target_vehicle)
 | |
| 							local target_netID = VehToNet(target_vehicle) 
 | |
| 							SetNetworkIdCanMigrate(target_netID, true)
 | |
| 							NetworkRegisterEntityAsNetworked(VehToNet(target_vehicle))
 | |
| 							SetNetworkIdExistsOnAllMachines(target_vehicle, true) 
 | |
| 							SetEntityAsMissionEntity(target_vehicle, true, true) 
 | |
| 							target_plate = GetVehicleNumberPlateText(target_vehicle)
 | |
| 							DecorSetInt(locked_on_vehicle, "Target", 2)
 | |
| 
 | |
| 							if tracking_spotlight then -- If tracking previous target, terminate and start tracking new target
 | |
| 								TriggerServerEvent("heli:tracking.spotlight.toggle")
 | |
| 								target_vehicle = locked_on_vehicle
 | |
| 								
 | |
| 								if not pause_Tspotlight then -- If spotlight was paused when tracking old target, 
 | |
| 									local target_netID = VehToNet(target_vehicle)
 | |
| 									local target_plate = GetVehicleNumberPlateText(target_vehicle)
 | |
| 									local targetposx, targetposy, targetposz = table.unpack(GetEntityCoords(target_vehicle))
 | |
| 									pause_Tspotlight = false
 | |
| 									tracking_spotlight = true
 | |
| 									TriggerServerEvent("heli:tracking.spotlight", target_netID, target_plate, targetposx, targetposy, targetposz)
 | |
| 									PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
 | |
| 								else
 | |
| 									tracking_spotlight = false
 | |
| 									pause_Tspotlight = false
 | |
| 								end
 | |
| 							end
 | |
| 						end
 | |
| 					end
 | |
| 				end
 | |
| 
 | |
| 				HandleZoom(cam)
 | |
| 				HideHUDThisFrame()
 | |
| 				PushScaleformMovieFunction(scaleform, "SET_ALT_FOV_HEADING")
 | |
| 				PushScaleformMovieFunctionParameterFloat(GetEntityCoords(heli).z)
 | |
| 				PushScaleformMovieFunctionParameterFloat(zoomvalue)
 | |
| 				PushScaleformMovieFunctionParameterFloat(GetCamRot(cam, 2).z)
 | |
| 				PopScaleformMovieFunctionVoid()
 | |
| 				DrawScaleformMovieFullscreen(scaleform, 255, 255, 255, 255)
 | |
| 				Citizen.Wait(0)
 | |
| 
 | |
| 				if manual_spotlight then -- Continuously update manual spotlight direction, sync client-client with decorators
 | |
| 					local rotation = GetCamRot(cam, 2)
 | |
| 					local forward_vector = RotAnglesToVec(rotation)
 | |
| 					local SpotvectorX, SpotvectorY, SpotvectorZ = table.unpack(forward_vector)
 | |
| 					local camcoords = GetCamCoord(cam)
 | |
| 
 | |
| 					DecorSetInt(lPed, "SpotvectorX", SpotvectorX)
 | |
| 					DecorSetInt(lPed, "SpotvectorY", SpotvectorY)
 | |
| 					DecorSetInt(lPed, "SpotvectorZ", SpotvectorZ)
 | |
| 					DrawSpotLight(camcoords, forward_vector, 255, 255, 255, 800.0, 10.0, brightness, spotradius, 1.0, 1.0)
 | |
| 				else
 | |
| 					TriggerServerEvent("heli:manual.spotlight.toggle")
 | |
| 				end
 | |
| 
 | |
| 			end
 | |
| 			if manual_spotlight then
 | |
| 				manual_spotlight = false
 | |
| 				TriggerServerEvent("heli:manual.spotlight.toggle")
 | |
| 			end
 | |
| 			helicam = false
 | |
| 			ClearTimecycleModifier()
 | |
| 			fov = (fov_max+fov_min)*0.5 -- reset to starting zoom level
 | |
| 			RenderScriptCams(false, false, 0, 1, 0) -- Return to gameplay camera
 | |
| 			SetScaleformMovieAsNoLongerNeeded(scaleform) -- Cleanly release the scaleform
 | |
| 			DestroyCam(cam, false)
 | |
| 			SetNightvision(false)
 | |
| 			SetSeethrough(false)
 | |
| 		end
 | |
| 
 | |
| 		if IsPlayerInPolmav() and target_vehicle and not helicam and vehicle_display ~=2 then
 | |
| 			RenderVehicleInfo(target_vehicle)
 | |
| 		end
 | |
| 	end
 | |
| end)
 | |
| 
 | |
| RegisterNetEvent('heli:forward.spotlight')
 | |
| AddEventHandler('heli:forward.spotlight', function(serverID, state)
 | |
| 	local heli = GetVehiclePedIsIn(GetPlayerPed(GetPlayerFromServerId(serverID)), false)
 | |
| 	SetVehicleSearchlight(heli, state, false)
 | |
| end)
 | |
| 
 | |
| RegisterNetEvent('heli:Tspotlight')
 | |
| AddEventHandler('heli:Tspotlight', function(serverID, target_netID, target_plate, targetposx, targetposy, targetposz)
 | |
| 
 | |
| 	-- Client target identification and verification, with fail-safes until FiveM code around global networked entities is sorted out
 | |
| 	if GetVehicleNumberPlateText(NetToVeh(target_netID)) == target_plate then
 | |
| 		Tspotlight_target = NetToVeh(target_netID)
 | |
| 	elseif GetVehicleNumberPlateText(DoesVehicleExistWithDecorator("Target")) == target_plate then
 | |
| 		Tspotlight_target = DoesVehicleExistWithDecorator("Target")
 | |
| 		--Citizen.Trace("Client target ID by primary netID method failed! Secondary decorator-based method worked.")
 | |
| 	elseif GetVehicleNumberPlateText(GetClosestVehicle(targetposx, targetposy, targetposz, 25.0, 0, 70)) == target_plate then
 | |
| 		Tspotlight_target = GetClosestVehicle(targetposx, targetposy, targetposz, 25.0, 0, 70)
 | |
| 		--Citizen.Trace("Heli: client target ID methods based on netID and decorator both failed! Tertiary method using target coordinates worked.")
 | |
| 	else 
 | |
| 		vehicle_match = FindVehicleByPlate(target_plate)
 | |
| 		if vehicle_match then
 | |
| 			Tspotlight_target = vehicle_match
 | |
| 			--Citizen.Trace("Heli: client target ID methods based on netID, decorator and coords all failed! Final method of searching vehicles by plate worked.")
 | |
| 		else 
 | |
| 			Tspotlight_target = nil
 | |
| 			--Citizen.Trace("Heli: all methods of client target ID failed!!")
 | |
| 		end
 | |
| 	end
 | |
| 
 | |
| 	local heli = GetVehiclePedIsIn(GetPlayerPed(GetPlayerFromServerId(serverID)), false)
 | |
| 	local heliPed = GetPlayerPed(GetPlayerFromServerId(serverID))
 | |
| 	Tspotlight_toggle = true
 | |
| 	Tspotlight_pause = false
 | |
| 	tracking_spotlight = true
 | |
| 	while not IsEntityDead(heliPed) and (GetVehiclePedIsIn(heliPed) == heli) and Tspotlight_target and Tspotlight_toggle do
 | |
| 		Citizen.Wait(1)
 | |
| 		local helicoords = GetEntityCoords(heli)
 | |
| 		local targetcoords = GetEntityCoords(Tspotlight_target)
 | |
| 		local spotVector = targetcoords - helicoords
 | |
| 		local target_distance = Vdist(targetcoords, helicoords)
 | |
| 		if Tspotlight_target and Tspotlight_toggle and not Tspotlight_pause then -- Redundant condition seems needed here or a function breaks
 | |
| 			DrawSpotLight(helicoords['x'], helicoords['y'], helicoords['z'], spotVector['x'], spotVector['y'], spotVector['z'], 255, 255, 255, (target_distance+20), 10.0, brightness, 4.0, 1.0, 0.0)
 | |
| 		end
 | |
| 		if Tspotlight_target and Tspotlight_toggle and target_distance > maxtargetdistance then -- Ditto for this target loss section
 | |
| 			--Citizen.Trace("Heli: tracking spotlight target lost")
 | |
| 			DecorRemove(Tspotlight_target, "Target")			
 | |
| 			target_vehicle = nil
 | |
| 			tracking_spotlight = false
 | |
| 			TriggerServerEvent("heli:tracking.spotlight.toggle")
 | |
| 			Tspotlight_target = nil
 | |
| 			break
 | |
| 		end
 | |
| 	end
 | |
| 	Tspotlight_toggle = false
 | |
| 	Tspotlight_pause = false
 | |
| 	Tspotlight_target = nil
 | |
| 	tracking_spotlight = false
 | |
| end)
 | |
| 
 | |
| RegisterNetEvent('heli:Tspotlight.toggle')
 | |
| AddEventHandler('heli:Tspotlight.toggle', function(serverID)
 | |
| 	Tspotlight_toggle = false
 | |
| 	tracking_spotlight = false
 | |
| end)
 | |
| 
 | |
| RegisterNetEvent('heli:pause.Tspotlight')
 | |
| AddEventHandler('heli:pause.Tspotlight', function(serverID, pause_Tspotlight)
 | |
| 	if pause_Tspotlight then
 | |
| 		Tspotlight_pause = true
 | |
| 	else
 | |
| 		Tspotlight_pause = false
 | |
| 	end
 | |
| end)
 | |
| 
 | |
| RegisterNetEvent('heli:Mspotlight')
 | |
| AddEventHandler('heli:Mspotlight', function(serverID)
 | |
| 	if GetPlayerServerId(PlayerId()) ~= serverID then -- Skip event for the source, since heli pilot already sees a more responsive manual spotlight
 | |
| 		local heli = GetVehiclePedIsIn(GetPlayerPed(GetPlayerFromServerId(serverID)), false)
 | |
| 		local heliPed = GetPlayerPed(GetPlayerFromServerId(serverID))
 | |
| 		Mspotlight_toggle = true
 | |
| 		while not IsEntityDead(heliPed) and (GetVehiclePedIsIn(heliPed) == heli) and Mspotlight_toggle do
 | |
| 			Citizen.Wait(0) 
 | |
| 			local helicoords = GetEntityCoords(heli)
 | |
| 			spotoffset = helicoords + vector3(0.0, 0.0, -1.5)
 | |
| 			SpotvectorX = DecorGetInt(heliPed, "SpotvectorX")
 | |
| 			SpotvectorY = DecorGetInt(heliPed, "SpotvectorY")
 | |
| 			SpotvectorZ = DecorGetInt(heliPed, "SpotvectorZ")
 | |
| 			if SpotvectorX then
 | |
| 				DrawSpotLight(spotoffset['x'], spotoffset['y'], spotoffset['z'], SpotvectorX, SpotvectorY, SpotvectorZ, 255, 255, 255, 800.0, 10.0, brightness, spotradius, 1.0, 1.0)
 | |
| 			end
 | |
| 		end
 | |
| 		Mspotlight_toggle = false
 | |
| 		DecorSetInt(heliPed, "SpotvectorX", nil)
 | |
| 		DecorSetInt(heliPed, "SpotvectorY", nil)
 | |
| 		DecorSetInt(heliPed, "SpotvectorZ", nil)
 | |
| 	end
 | |
| end)
 | |
| 
 | |
| RegisterNetEvent('heli:Mspotlight.toggle')
 | |
| AddEventHandler('heli:Mspotlight.toggle', function(serverID)
 | |
| 	Mspotlight_toggle = false
 | |
| end)
 | |
| 
 | |
| RegisterNetEvent('heli:light.up')
 | |
| AddEventHandler('heli:light.up', function(serverID)
 | |
| 	if brightness < 10 then
 | |
| 		brightness = brightness + 1.0
 | |
| 		PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
 | |
| 	end
 | |
| end)
 | |
| 
 | |
| RegisterNetEvent('heli:light.down')
 | |
| AddEventHandler('heli:light.down', function(serverID)
 | |
| 	if brightness > 1.0 then
 | |
| 		brightness = brightness - 1.0
 | |
| 		PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
 | |
| 	end
 | |
| end)
 | |
| 
 | |
| RegisterNetEvent('heli:radius.up')
 | |
| AddEventHandler('heli:radius.up', function(serverID)
 | |
| 	if spotradius < 10.0 then
 | |
| 		spotradius = spotradius + 1.0
 | |
| 		PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
 | |
| 	end
 | |
| end)
 | |
| 
 | |
| RegisterNetEvent('heli:radius.down')
 | |
| AddEventHandler('heli:radius.down', function(serverID)
 | |
| 	if spotradius > 4.0 then
 | |
| 		spotradius = spotradius - 1.0
 | |
| 		PlaySoundFrontend(-1, "SELECT", "HUD_FRONTEND_DEFAULT_SOUNDSET", false)
 | |
| 	end
 | |
| end)
 | |
| 
 | |
| function IsPlayerInPolmav()
 | |
| 	local lPed = GetPlayerPed(-1)
 | |
| 	local vehicle = GetVehiclePedIsIn(lPed)
 | |
| 	--return IsVehicleModel(vehicle, polmav_hash)
 | |
| for i = 1,#heli do
 | |
| rightveh = IsVehicleModel(vehicle, heli[i])
 | |
| if rightveh then
 | |
| return IsVehicleModel(vehicle, heli[i])
 | |
|  end
 | |
| end
 | |
| end
 | |
| 
 | |
| function IsHeliHighEnough(heli)
 | |
| 	return GetEntityHeightAboveGround(heli) > 1.5
 | |
| end
 | |
| 
 | |
| function ChangeVision()
 | |
| 	if vision_state == 0 then
 | |
| 		SetNightvision(true)
 | |
| 		vision_state = 1
 | |
| 	elseif vision_state == 1 then
 | |
| 		SetNightvision(false)
 | |
| 		SetSeethrough(true)
 | |
| 		vision_state = 2
 | |
| 	else
 | |
| 		SetSeethrough(false)
 | |
| 		vision_state = 0
 | |
| 	end
 | |
| end
 | |
| 
 | |
| function ChangeDisplay()
 | |
| 	if vehicle_display == 0 then
 | |
| 		vehicle_display = 1
 | |
| 	elseif vehicle_display == 1 then
 | |
| 		vehicle_display = 2
 | |
| 	else
 | |
| 		vehicle_display = 0
 | |
| 	end
 | |
| end
 | |
| 
 | |
| function HideHUDThisFrame()
 | |
| 	HideHelpTextThisFrame()
 | |
| 	HideHudAndRadarThisFrame()
 | |
| 	HideHudComponentThisFrame(19) -- weapon wheel
 | |
| 	HideHudComponentThisFrame(1) -- Wanted Stars
 | |
| 	HideHudComponentThisFrame(2) -- Weapon icon
 | |
| 	HideHudComponentThisFrame(3) -- Cash
 | |
| 	HideHudComponentThisFrame(4) -- MP CASH
 | |
| 	HideHudComponentThisFrame(13) -- Cash Change
 | |
| 	HideHudComponentThisFrame(11) -- Floating Help Text
 | |
| 	HideHudComponentThisFrame(12) -- more floating help text
 | |
| 	HideHudComponentThisFrame(15) -- Subtitle Text
 | |
| 	HideHudComponentThisFrame(18) -- Game Stream
 | |
| end
 | |
| 
 | |
| function CheckInputRotation(cam, zoomvalue)
 | |
| 	local rightAxisX = GetDisabledControlNormal(0, 220)
 | |
| 	local rightAxisY = GetDisabledControlNormal(0, 221)
 | |
| 	local rotation = GetCamRot(cam, 2)
 | |
| 	if rightAxisX ~= 0.0 or rightAxisY ~= 0.0 then
 | |
| 		new_z = rotation.z + rightAxisX*-1.0*(speed_ud)*(zoomvalue+0.1)
 | |
| 		new_x = math.max(math.min(20.0, rotation.x + rightAxisY*-1.0*(speed_lr)*(zoomvalue+0.1)), -89.5) -- Clamping at top (cant see top of heli) and at bottom (doesn't glitch out in -90deg)
 | |
| 		SetCamRot(cam, new_x, 0.0, new_z, 2)
 | |
| 	end
 | |
| end
 | |
| 
 | |
| function HandleZoom(cam)
 | |
| 	if IsControlJustPressed(0,241) then -- Scrollup
 | |
| 		fov = math.max(fov - zoomspeed, fov_min)
 | |
| 	end
 | |
| 	if IsControlJustPressed(0,242) then
 | |
| 		fov = math.min(fov + zoomspeed, fov_max) -- ScrollDown		
 | |
| 	end
 | |
| 	local current_fov = GetCamFov(cam)
 | |
| 	if math.abs(fov-current_fov) < 0.1 then -- the difference is too small, just set the value directly to avoid unneeded updates to FOV of order 10^-5
 | |
| 		fov = current_fov
 | |
| 	end
 | |
| 	SetCamFov(cam, current_fov + (fov - current_fov)*0.05) -- Smoothing of camera zoom
 | |
| end
 | |
| 
 | |
| function GetVehicleInView(cam)
 | |
| 	local coords = GetCamCoord(cam)
 | |
| 	local forward_vector = RotAnglesToVec(GetCamRot(cam, 2))
 | |
| 	--DrawLine(coords, coords+(forward_vector*100.0), 255,0,0,255) -- debug line to show LOS of cam
 | |
| 	local rayhandle = CastRayPointToPoint(coords, coords+(forward_vector*200.0), 10, GetVehiclePedIsIn(GetPlayerPed(-1)), 0)
 | |
| 	local _, _, _, _, entityHit = GetRaycastResult(rayhandle)
 | |
| 	if entityHit>0 and IsEntityAVehicle(entityHit) then
 | |
| 		return entityHit
 | |
| 	else
 | |
| 		return nil
 | |
| 	end
 | |
| end
 | |
| 
 | |
| function RenderVehicleInfo(vehicle)
 | |
| 	if DoesEntityExist(vehicle) then
 | |
| 		local model = GetEntityModel(vehicle)
 | |
| 		local vehname = GetLabelText(GetDisplayNameFromVehicleModel(model))
 | |
| 		local licenseplate = GetVehicleNumberPlateText(vehicle)
 | |
| 		if speed_measure == "MPH" then
 | |
| 			vehspeed = GetEntitySpeed(vehicle)*2.236936
 | |
| 		else
 | |
| 			vehspeed = GetEntitySpeed(vehicle)*3.6
 | |
| 		end
 | |
| 		SetTextFont(0)
 | |
| 		SetTextProportional(1)
 | |
| 		if vehicle_display == 0 then
 | |
| 			SetTextScale(0.0, 0.49)
 | |
| 		elseif vehicle_display == 1 then
 | |
| 			SetTextScale(0.0, 0.55)
 | |
| 		end
 | |
| 		SetTextColour(255, 255, 255, 255)
 | |
| 		SetTextDropshadow(0, 0, 0, 0, 255)
 | |
| 		SetTextEdge(1, 0, 0, 0, 255)
 | |
| 		SetTextDropShadow()
 | |
| 		SetTextOutline()
 | |
| 		SetTextEntry("STRING")
 | |
| 		if vehicle_display == 0 then
 | |
| 			AddTextComponentString("Speed: " .. math.ceil(vehspeed) .. " " .. speed_measure .. "\nModel: " .. vehname .. "\nPlate: " .. licenseplate)
 | |
| 		elseif vehicle_display == 1 then
 | |
| 			AddTextComponentString("Model: " .. vehname .. "\nPlate: " .. licenseplate)
 | |
| 		end
 | |
| 		DrawText(0.45, 0.9)
 | |
| 	end
 | |
| end
 | |
| 
 | |
| function RotAnglesToVec(rot) -- input vector3
 | |
| 	local z = math.rad(rot.z)
 | |
| 	local x = math.rad(rot.x)
 | |
| 	local num = math.abs(math.cos(x))
 | |
| 	return vector3(-math.sin(z)*num, math.cos(z)*num, math.sin(x))
 | |
| end
 | |
| 
 | |
| -- Following two functions from IllidanS4's entity enuerator script:  https://gist.github.com/IllidanS4/9865ed17f60576425369fc1da70259b2
 | |
| local entityEnumerator = {
 | |
|   __gc = function(enum)
 | |
|     if enum.destructor and enum.handle then
 | |
|       enum.destructor(enum.handle)
 | |
|     end
 | |
|     enum.destructor = nil
 | |
|     enum.handle = nil
 | |
|   end
 | |
| }
 | |
| 
 | |
| local 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
 | |
| 
 | |
| function EnumerateVehicles()
 | |
|   return EnumerateEntities(FindFirstVehicle, FindNextVehicle, EndFindVehicle)
 | |
| end
 | |
| 
 | |
| function FindVehicleByPlate(plate) -- Search existing vehicles enumerated above for target plate and return the matching vehicle
 | |
| 	for vehicle in EnumerateVehicles() do
 | |
| 		if GetVehicleNumberPlateText(vehicle) == plate then
 | |
| 			return vehicle
 | |
| 		end
 | |
| 	end
 | |
| end
 | 
