Files
wk_wars2x/cl_radar.lua
Dan 6877d61267 Radar menu now opens and works properly
Settings configured in the menu also have an effect on the radar's operation, e.g. the antenna sensitivities effect the range.
2019-12-01 16:56:20 +00:00

1050 lines
31 KiB
Lua

--[[------------------------------------------------------------------------
Wraith ARS 2X - v1.0.0
Created by WolfKnight
------------------------------------------------------------------------]]--
local next = next
local dot = dot
local table = table
local type = type
local tostring = tostring
local math = math
local pairs = pairs
--[[------------------------------------------------------------------------
Resource Rename Fix - for those muppets who rename the resource and
complain that the NUI aspect doesn't work!
------------------------------------------------------------------------]]--
Citizen.SetTimeout( 1000, function()
-- Get the name of the resource, for example the default name is 'wk_wrs2'
local name = GetCurrentResourceName()
print( "WK_WARS2X: Sending resource name (" .. name .. ") to JavaScript side." )
-- Send a message through the NUI system to the JavaScript file to give the name of the resource
SendNUIMessage( { _type = "updatePathName", pathName = name } )
end )
--[[------------------------------------------------------------------------
Player info variables
------------------------------------------------------------------------]]--
PLY = {}
PLY.ped = PlayerPedId()
PLY.veh = nil
PLY.inDriverSeat = false
PLY.vehClassValid = false
--[[------------------------------------------------------------------------
Radar variables
------------------------------------------------------------------------]]--
RADAR.vars =
{
-- The radar's power
power = false,
poweringUp = false,
-- These are the settings that are used in the operator menu
settings = {
["fastDisplay"] = true,
-- Sensitivty for each mode, 1-5
["same"] = 4,
["opp"] = 3,
["alert"] = true
},
menuActive = false,
currentOptionIndex = 1,
menuOptions = {
{ displayText = { "¦¦¦", "FAS" }, optionsText = { "On¦", "Off" }, options = { true, false }, optionIndex = 1, settingText = "fastDisplay" },
{ displayText = { "¦SL", "SEn" }, optionsText = { "¦1¦", "¦2¦", "¦3¦", "¦4¦", "¦5¦" }, options = { 1, 2, 3, 4, 5 }, optionIndex = 4, settingText = "same" },
{ displayText = { "¦OP", "SEn" }, optionsText = { "¦1¦", "¦2¦", "¦3¦", "¦4¦", "¦5¦" }, options = { 1, 2, 3, 4, 5 }, optionIndex = 3, settingText = "opp" },
},
-- Player's vehicle speed, this is used to update the patrol vehicle speed on the radar
patrolSpeed = 0,
patrolLocked = false,
psBlank = false,
-- The speed type, this is used when converting speeds to a readable format
-- Either "mph" or "kmh", can be toggle in-game
speedType = "mph",
-- Antennas, this table contains all of the data needed for operation of the front and rear antennas
antennas = {
-- Variables for the front antenna
[ "front" ] = {
xmit = false, -- Whether the antenna is on or off
mode = 0, -- Current antenna mode, 0 = none, 1 = same, 2 = opp, 3 = same and opp
--[[ speed = 0, -- Speed of the vehicle caught by the front antenna
dir = nil, -- Direction the caught vehicle is going, 0 = towards, 1 = away
fastMode = 1, -- Current fast mode, 1 = polling, 2 = lock on at first fast vehicle
fastSpeed = 0, -- Speed of the fastest vehicle caught by the front antenna
fastDir = nil, -- Direction the fastest vehicle is going, 0 = towards, 1 = away
fastLocked = false -- Whether the fast speed is locked or not ]]
},
[ "rear" ] = {
xmit = false, -- Whether the antenna is on or off
mode = 0, -- Current antenna mode, 0 = none, 1 = same, 2 = opp, 3 = same and opp
--[[ speed = 0, -- Speed of the vehicle caught by the front antenna
dir = nil, -- Direction the caught vehicle is going, 0 = towards, 1 = away
fastMode = 1, -- Current fast mode, 1 = polling, 2 = lock on at first fast vehicle
fastSpeed = 0, -- Speed of the fastest vehicle caught by the front antenna
fastDir = nil, -- Direction the fastest vehicle is going, 0 = towards, 1 = away
fastLocked = false -- Whether the fast speed is locked or not ]]
}
},
-- The maximum distance that the radar system's ray traces can go
maxCheckDist = 300.0,
-- Cached dynamic vehicle sphere sizes, automatically populated when the system is running
sphereSizes = {},
-- Table to store tables for hit entities of captured vehicles
capturedVehicles = {},
-- Table for temp id storage to stop unnecessary trace checks
tempVehicleIDs = {},
-- The current vehicle data for display
activeVehicles = {},
-- Vehicle pool, automatically populated when the system is running, holds all of the current
-- vehicle IDs for the player using entity enumeration (see cl_utils.lua)
vehiclePool = {},
-- Radar stage, this is used to tell the system what it should currently be doing, the stages are:
-- - 0 = Gathering vehicles hit by the rays
-- - 1 = Filtering the vehicles caught (removing duplicates, etc) and calculating what needs to be shown
-- to the user based on modes and settings
-- - 2 = Sending all required data across to the NUI system for display
radarStage = 0,
-- Ray trace state, this is used so the radar stage doesn't progress to the next stage unless
-- all of the ray trace threads have completed
rayTraceState = 0,
-- Number of ray traces, automatically cached when the system first runs
numberOfRays = 0
}
-- These vectors are used in the custom ray tracing system
RADAR.rayTraces = {
-- { startVec = { x = 0.0 }, endVec = { x = 0.0, y = 200.0 }, rayType = "same" },
-- { startVec = { x = -5.0 }, endVec = { x = -5.0, y = 200.0 }, rayType = "same" },
-- { startVec = { x = 5.0 }, endVec = { x = 5.0, y = 200.0 }, rayType = "same" },
{ startVec = { x = 3.0 }, endVec = { x = 3.0, y = 200.0 }, rayType = "same" },
{ startVec = { x = -3.0 }, endVec = { x = -3.0, y = 200.0 }, rayType = "same" },
{ startVec = { x = -10.0 }, endVec = { x = -10.0, y = 200.0 }, rayType = "opp" },
{ startVec = { x = -16.0 }, endVec = { x = -16.0, y = 200.0 }, rayType = "opp" }
}
-- Each of these are used for sorting the captured vehicle data, the 'strongest' filter is used for the main
-- target window of each antenna, whereas the 'fastest' filter is used for the fast target window of each antenna
RADAR.sorting = {
strongest = function( a, b ) return a.size > b.size end,
fastest = function( a, b ) return a.speed > b.speed end
}
--[[------------------------------------------------------------------------
Radar essentials functions
------------------------------------------------------------------------]]--
function RADAR:IsPowerOn()
return self.vars.power
end
function RADAR:IsPoweringUp()
return self.vars.poweringUp
end
function RADAR:SetPoweringUpState( state )
self.vars.poweringUp = state
end
function RADAR:TogglePower()
self.vars.power = not self.vars.power
SendNUIMessage( { _type = "radarPower", state = self:IsPowerOn() } )
-- Power is now turned on
if ( self:IsPowerOn() ) then
self:SetPoweringUpState( true )
Citizen.SetTimeout( 2000, function()
self:SetPoweringUpState( false )
SendNUIMessage( { _type = "poweredUp" } )
end )
else
self:ResetAntenna( "front" )
self:ResetAntenna( "rear" )
end
end
function RADAR:IsFastDisplayEnabled()
return self.vars.settings.fastDisplay
end
function RADAR:SetSettingValue( setting, value )
if ( value ~= nil ) then
self.vars.settings[setting] = value
end
end
function RADAR:GetSettingValue( setting )
return self.vars.settings[setting]
end
function RADAR:IsEitherAntennaOn()
return self:IsAntennaTransmitting( "front" ) or self:IsAntennaTransmitting( "rear" )
end
function RADAR:SendSettingUpdate()
local antennas = self.vars.antennas
local fast = self.vars.settings.fastDisplay
SendNUIMessage( { _type = "settingUpdate", antennaData = antennas, fast = fast } )
end
--[[------------------------------------------------------------------------
Radar menu functions
------------------------------------------------------------------------]]--
function RADAR:SetMenuState( state )
if ( self:IsPowerOn() ) then
self.vars.menuActive = state
if ( state ) then
self.vars.currentOptionIndex = 1
end
end
end
function RADAR:IsMenuOpen()
return self.vars.menuActive
end
function RADAR:ChangeMenuIndex()
local temp = self.vars.currentOptionIndex + 1
if ( temp > #self.vars.menuOptions ) then
temp = 1
end
self.vars.currentOptionIndex = temp
self:SendMenuUpdate()
end
function RADAR:GetMenuOptionTable()
return self.vars.menuOptions[self.vars.currentOptionIndex]
end
function RADAR:SetMenuOptionIndex( index )
self.vars.menuOptions[self.vars.currentOptionIndex].optionIndex = index
end
function RADAR:GetMenuOptionValue()
local opt = self:GetMenuOptionTable()
local index = opt.optionIndex
return opt.options[index]
end
function RADAR:ChangeMenuOption( dir )
local opt = self:GetMenuOptionTable()
local index = opt.optionIndex
if ( dir == "front" ) then
index = index + 1
if ( index > #opt.options ) then index = 1 end
elseif ( dir == "rear" ) then
index = index - 1
if ( index < 1 ) then index = #opt.options end
end
self:SetMenuOptionIndex( index )
self:SetSettingValue( opt.settingText, self:GetMenuOptionValue() )
self:SendMenuUpdate()
end
function RADAR:GetMenuOptionDisplayText()
return self:GetMenuOptionTable().displayText
end
function RADAR:GetMenuOptionText()
local opt = self:GetMenuOptionTable()
return opt.optionsText[opt.optionIndex]
end
function RADAR:SendMenuUpdate()
SendNUIMessage( { _type = "menu", text = self:GetMenuOptionDisplayText(), option = self:GetMenuOptionText() } )
end
--[[------------------------------------------------------------------------
Radar basics functions
------------------------------------------------------------------------]]--
function RADAR:GetPatrolSpeed()
return self.vars.patrolSpeed
end
function RADAR:GetVehiclePool()
return self.vars.vehiclePool
end
function RADAR:GetMaxCheckDist()
return self.vars.maxCheckDist
end
function RADAR:GetActiveVehicles()
return self.vars.activeVehicles
end
function RADAR:GetStrongestSortFunc()
return self.sorting.strongest
end
function RADAR:GetFastestSortFunc()
return self.sorting.fastest
end
function RADAR:SetPatrolSpeed( speed )
if ( type( speed ) == "number" ) then
self.vars.patrolSpeed = self:GetVehSpeedFormatted( speed )
end
end
function RADAR:SetVehiclePool( pool )
if ( type( pool ) == "table" ) then
self.vars.vehiclePool = pool
end
end
function RADAR:SetActiveVehicles( vehs )
if ( type( vehs ) == "table" ) then
self.vars.activeVehicles = vehs
end
end
--[[------------------------------------------------------------------------
Radar ray trace functions
------------------------------------------------------------------------]]--
function RADAR:GetRayTraceState()
return self.vars.rayTraceState
end
function RADAR:CacheNumRays()
self.vars.numberOfRays = #self.rayTraces
end
function RADAR:GetNumOfRays()
return self.vars.numberOfRays
end
function RADAR:IncreaseRayTraceState()
self.vars.rayTraceState = self.vars.rayTraceState + 1
end
function RADAR:ResetRayTraceState()
self.vars.rayTraceState = 0
end
function RADAR:GetIntersectedVehIsFrontOrRear( t )
if ( t > 10.0 ) then
return 1 -- vehicle is in front
elseif ( t < -10.0 ) then
return -1 -- vehicle is behind
end
return 0 -- vehicle is next to self
end
function RADAR:GetLineHitsSphereAndDir( centre, radius, rayStart, rayEnd )
-- First we get the normalised ray, this way we then know the direction the ray is going
local rayNorm = norm( rayEnd - rayStart )
-- Then we calculate the ray from the start point to the centre position of the sphere
local rayToCentre = centre - rayStart
-- Now that we have the ray to the centre of the sphere, and the normalised ray direction, we
-- can calculate the shortest point from the centre of the sphere onto the ray itself. This
-- would then give us the opposite side of the right angled triangle. All of the resulting
-- values are also in squared form, as performing square root functions is slower.
local tProj = dot( rayToCentre, rayNorm )
local oppLenSqr = dot( rayToCentre, rayToCentre ) - ( tProj * tProj )
-- Square the radius
local radiusSqr = radius * radius
local rayDist = #( rayEnd - rayStart )
local distToCentre = #( rayStart - centre ) - ( radius * 2 )
-- Now all we have to do is compare the squared opposite length and the radius squared, this
-- will then tell us if the ray intersects with the sphere.
-- if ( oppLenSqr < radiusSqr and ( t0 < ( dist + radius ) ) ) then
if ( oppLenSqr < radiusSqr and not ( distToCentre > rayDist ) ) then
return true, self:GetIntersectedVehIsFrontOrRear( tProj )
end
return false, nil
end
function RADAR:ShootCustomRay( plyVeh, veh, s, e )
local pos = GetEntityCoords( veh )
local dist = #( pos - s )
local key = tostring( veh )
if ( DoesEntityExist( veh ) and veh ~= plyVeh and dist < self:GetMaxCheckDist() and not self:HasVehicleAlreadyBeenHit( key ) ) then
local entSpeed = GetEntitySpeed( veh )
local visible = HasEntityClearLosToEntity( plyVeh, veh, 15 ) -- 13 seems okay, 15 too (doesn't grab ents through ents)
if ( entSpeed > 0.1 and visible ) then
local radius, size = self:GetDynamicRadius( veh )
local hit, relPos = self:GetLineHitsSphereAndDir( pos, radius, s, e )
if ( hit ) then
-- UTIL:DrawDebugSphere( pos.x, pos.y, pos.z, radius, { 255, 0, 0, 40 } )
self:SetVehicleHasBeenHit( key )
return true, relPos, dist, entSpeed, size
end
end
end
return false, nil, nil, nil, nil
end
function RADAR:GetVehsHitByRay( ownVeh, vehs, s, e )
local t = {}
local hasData = false
for _, veh in pairs( vehs ) do
local hit, relativePos, distance, speed, size = self:ShootCustomRay( ownVeh, veh, s, e )
if ( hit ) then
local d = {}
d.veh = veh
d.relPos = relativePos
-- d.dist = UTIL:Round( distance, 2 ) -- Possibly remove
-- d.speed = UTIL:Round( speed, 3 )
d.dist = distance
d.speed = speed
d.size = size
table.insert( t, d )
hasData = true
end
end
if ( hasData ) then return t end
end
function RADAR:CreateRayThread( vehs, from, startX, endX, endY, rayType )
local startPoint = GetOffsetFromEntityInWorldCoords( from, startX, 0.0, 0.0 )
local endPoint = GetOffsetFromEntityInWorldCoords( from, endX, endY, 0.0 )
local hitVehs = self:GetVehsHitByRay( from, vehs, startPoint, endPoint )
self:InsertCapturedVehicleData( hitVehs, rayType )
self:IncreaseRayTraceState()
end
function RADAR:CreateRayThreads( ownVeh, vehicles )
for _, v in pairs( self.rayTraces ) do
local endY = ( self:GetSettingValue( v.rayType ) * 0.2 ) * v.endVec.y
self:CreateRayThread( vehicles, ownVeh, v.startVec.x, v.endVec.x, endY, v.rayType )
end
end
--[[------------------------------------------------------------------------
Radar stage functions
------------------------------------------------------------------------]]--
function RADAR:GetRadarStage()
return self.vars.radarStage
end
function RADAR:IncreaseRadarStage()
self.vars.radarStage = self.vars.radarStage + 1
end
function RADAR:ResetRadarStage()
self.vars.radarStage = 0
end
--[[------------------------------------------------------------------------
Radar antenna functions
------------------------------------------------------------------------]]--
function RADAR:ToggleAntenna( ant, cb )
if ( self:IsPowerOn() ) then
self.vars.antennas[ant].xmit = not self.vars.antennas[ant].xmit
if ( cb ) then cb() end
end
end
function RADAR:IsAntennaTransmitting( ant )
return self.vars.antennas[ant].xmit
end
function RADAR:GetAntennaTextFromNum( relPos )
if ( relPos == 1 ) then
return "front"
elseif ( relPos == -1 ) then
return "rear"
end
end
function RADAR:GetAntennaMode( ant )
return self.vars.antennas[ant].mode
end
function RADAR:SetAntennaMode( ant, mode, cb )
if ( type( mode ) == "number" ) then
if ( mode >= 0 and mode <= 3 and self:IsPowerOn() ) then
self.vars.antennas[ant].mode = mode
if ( cb ) then cb() end
end
end
end
function RADAR:SetAntennaSpeed( ant, speed )
if ( type( speed ) == "number" ) then
if ( speed >= 0 and speed <= 999 ) then
self.vars.antennas[ant].speed = speed
end
end
end
function RADAR:SetAntennaDir( ant, dir )
if ( type( dir ) == "number" ) then
if ( dir == 0 or dir == 1 ) then
self.vars.antennas[ant].dir = dir
end
end
end
function RADAR:SetAntennaFastMode( ant, mode )
if ( type( mode ) == "number" ) then
if ( mode == 1 or mode == 2 ) then
self.vars.antennas[ant].fastMode = mode
end
end
end
function RADAR:SetAntennaFastSpeed( ant, speed )
if ( type( speed ) == "number" ) then
if ( speed >= 0 and speed <= 999 ) then
self.vars.antennas[ant].fastSpeed = speed
end
end
end
function RADAR:SetAntennaFastDir( ant, dir )
if ( type( dir ) == "number" ) then
if ( dir == 0 or dir == 1 ) then
self.vars.antennas[ant].fastDir = dir
end
end
end
function RADAR:SetAntennaFastLock( ant, state )
if ( type( state ) == "boolean" ) then
self.vars.antennas[ant].fastLocked = state
end
end
function RADAR:ResetAntenna( ant )
-- Overwrite default behaviour, this is because when the system is turned off, the temporary memory is
-- technically reset, as the setter functions require either the radar power to be on or the antenna to
-- be transmitting, this is the only way to reset the values
self.vars.antennas[ant].xmit = false
self.vars.antennas[ant].mode = 0
end
--[[------------------------------------------------------------------------
Radar captured vehicle functions
------------------------------------------------------------------------]]--
function RADAR:GetCapturedVehicles()
return self.vars.capturedVehicles
end
function RADAR:ResetCapturedVehicles()
self.vars.capturedVehicles = {}
end
function RADAR:InsertCapturedVehicleData( t, rt )
if ( type( t ) == "table" and not UTIL:IsTableEmpty( t ) ) then
for _, v in pairs( t ) do
v.rayType = rt
table.insert( self.vars.capturedVehicles, v )
end
end
end
function RADAR:HasVehicleAlreadyBeenHit( key )
return self.vars.tempVehicleIDs[key] == true
end
function RADAR:SetVehicleHasBeenHit( key )
self.vars.tempVehicleIDs[key] = true
end
function RADAR:ResetTempVehicleIDs()
self.vars.tempVehicleIDs = {}
end
--[[------------------------------------------------------------------------
Radar dynamic sphere radius functions
------------------------------------------------------------------------]]--
function RADAR:GetDynamicDataValue( key )
return self.vars.sphereSizes[key]
end
function RADAR:DoesDynamicRadiusDataExist( key )
return self:GetDynamicDataValue( key ) ~= nil
end
function RADAR:SetDynamicRadiusKey( key, t )
self.vars.sphereSizes[key] = t
end
function RADAR:InsertDynamicRadiusData( key, radius, actualSize )
if ( self:GetDynamicDataValue( key ) == nil ) then
local t = {}
t.radius = radius
t.actualSize = actualSize
self:SetDynamicRadiusKey( key, t )
end
end
function RADAR:GetRadiusData( key )
return self.vars.sphereSizes[key].radius or 5.0, self.vars.sphereSizes[key].actualSize
end
function RADAR:GetDynamicRadius( veh )
local mdl = GetEntityModel( veh )
local key = tostring( mdl )
local dataExists = self:DoesDynamicRadiusDataExist( key )
if ( not dataExists ) then
local min, max = GetModelDimensions( mdl )
local size = max - min
local numericSize = size.x + size.y + size.z
local dynamicRadius = UTIL:Clamp( ( numericSize * numericSize ) / 12, 4.0, 10.0 )
self:InsertDynamicRadiusData( key, dynamicRadius, numericSize )
return dynamicRadius, numericSize
end
return self:GetRadiusData( key )
end
--[[------------------------------------------------------------------------
Radar functions
------------------------------------------------------------------------]]--
function RADAR:GetVehSpeedFormatted( speed )
if ( self.vars.speedType == "mph" ) then
return UTIL:Round( math.ceil( speed * 2.236936 ), 0 )
else
return UTIL:Round( math.ceil( speed * 3.6 ), 0 )
end
end
function RADAR:GetAllVehicles()
local t = {}
for v in UTIL:EnumerateVehicles() do
table.insert( t, v )
end
return t
end
function RADAR:CheckVehicleDataFitsMode( ant, rt )
local mode = self:GetAntennaMode( ant )
if ( ( mode == 3 ) or ( mode == 1 and rt == "same" ) or ( mode == 2 and rt == "opp" ) ) then return true end
return false
end
function RADAR:GetVehiclesForAntenna()
local vehs = { ["front"] = {}, ["rear"] = {} }
local results = { ["front"] = { nil, nil }, ["rear"] = { nil, nil } }
-- Loop through and split up the vehicles based on front and rear, this is simply because the actual system
-- that gets all of the vehicles hit by the radar only has a relative position of either 1 or -1, which we
-- then convert below into an antenna string!
for ant in UTIL:Values( { "front", "rear" } ) do
if ( self:IsAntennaTransmitting( ant ) ) then
for k, v in pairs( self:GetCapturedVehicles() ) do
local antText = self:GetAntennaTextFromNum( v.relPos )
if ( ant == antText ) then
table.insert( vehs[ant], v )
end
end
-- As the radar is based on how the real Stalker DSR 2X works, we now sort the dataset by
-- the 'strongest' (largest) target, this way the first result for the front and rear data
-- will be the one that gets displayed in the target boxes.
table.sort( vehs[ant], self:GetStrongestSortFunc() )
end
end
for ant in UTIL:Values( { "front", "rear" } ) do
if ( not UTIL:IsTableEmpty( vehs[ant] ) ) then
-- Get the 'strongest' vehicle for the antenna
for k, v in pairs( vehs[ant] ) do
if ( self:CheckVehicleDataFitsMode( ant, v.rayType ) ) then
results[ant][1] = v
break
end
end
if ( self:IsFastDisplayEnabled() ) then
-- Get the 'fastest' vehicle for the antenna
table.sort( vehs[ant], self:GetFastestSortFunc() )
local temp = results[ant][1]
for k, v in pairs( vehs[ant] ) do
if ( self:CheckVehicleDataFitsMode( ant, v.rayType ) and v.veh ~= temp.veh and v.size < temp.size and v.speed > temp.speed ) then
results[ant][2] = v
break
end
end
end
end
end
return { ["front"] = { results["front"][1], results["front"][2] }, ["rear"] = { results["rear"][1], results["rear"][2] } }
end
-- Num4 = 108 - INPUT_VEH_FLY_ROLL_LEFT_ONLY
-- Num5 = 112 - INPUT_VEH_FLY_PITCH_DOWN_ONLY
-- Num6 = 109 - INPUT_VEH_FLY_ROLL_RIGHT_ONLY
-- Num7 = 117 - INPUT_VEH_FLY_SELECT_TARGET_LEFT
-- Num8 = 111 - INPUT_VEH_FLY_PITCH_UP_ONLY
-- Num9 = 118 - INPUT_VEH_FLY_SELECT_TARGET_RIGHT
-- F5 = 166 - INPUT_SELECT_CHARACTER_MICHAEL
function RADAR:RunControlManager()
-- 'Z' key, toggles debug mode
if ( IsDisabledControlJustPressed( 1, 20 ) ) then
self.config.debug_mode = not self.config.debug_mode
end
if ( IsDisabledControlJustPressed( 1, 166 ) ) then
SendNUIMessage( { _type = "openRemote" } )
SetNuiFocus( true, true )
end
if ( IsDisabledControlJustPressed( 1, 117 ) ) then
self:TogglePower()
UTIL:Notify( "Radar power toggled." )
end
if ( IsDisabledControlJustPressed( 1, 118 ) ) then
self:ToggleFastDisplay()
UTIL:Notify( "Fast display toggled." )
end
-- 'Num8' key, toggles front antenna
--[[ if ( IsDisabledControlJustPressed( 1, 111 ) ) then
self:ToggleAntenna( "front" )
UTIL:Notify( "Front antenna toggled." )
end
-- 'Num5' key, toggles rear antenna
if ( IsDisabledControlJustPressed( 1, 112 ) ) then
self:ToggleAntenna( "rear" )
UTIL:Notify( "Rear antenna toggled." )
end ]]
end
--[[------------------------------------------------------------------------
NUI callback
------------------------------------------------------------------------]]--
RegisterNUICallback( "togglePower", function()
RADAR:TogglePower()
end )
RegisterNUICallback( "closeRemote", function()
SetNuiFocus( false, false )
end )
RegisterNUICallback( "setAntennaMode", function( data )
if ( RADAR:IsPowerOn() and RADAR:IsMenuOpen() ) then
RADAR:SetMenuState( false )
RADAR:SendSettingUpdate()
else
RADAR:SetAntennaMode( data.value, tonumber( data.mode ), function()
SendNUIMessage( { _type = "antennaMode", ant = data.value, mode = tonumber( data.mode ) } )
end )
end
end )
RegisterNUICallback( "toggleAntenna", function( data )
if ( RADAR:IsPowerOn() and RADAR:IsMenuOpen() ) then
RADAR:ChangeMenuOption( data.value )
else
RADAR:ToggleAntenna( data.value, function()
SendNUIMessage( { _type = "antennaXmit", ant = data.value, on = RADAR:IsAntennaTransmitting( data.value ) } )
end )
end
end )
RegisterNUICallback( "menu", function()
if ( RADAR:IsMenuOpen() ) then
RADAR:ChangeMenuIndex()
else
-- Set the menu state to open, which will prevent anything else from working
RADAR:SetMenuState( true )
RADAR:SendMenuUpdate()
end
end )
--[[------------------------------------------------------------------------
Main function
------------------------------------------------------------------------]]--
function RADAR:Main()
-- Check to make sure the player is in the driver's seat, and also that the vehicle has a class of VC_EMERGENCY (18)
if ( DoesEntityExist( PLY.veh ) and PLY.inDriverSeat and PLY.vehClassValid and self:IsPowerOn() and not self:IsPoweringUp() and not self:IsMenuOpen() ) then
local plyVehPos = GetEntityCoords( PLY.veh )
-- First stage of the radar - get all of the vehicles hit by the radar
if ( self:GetRadarStage() == 0 ) then
if ( self:GetRayTraceState() == 0 ) then
local vehs = self:GetVehiclePool()
self:ResetCapturedVehicles()
self:ResetRayTraceState()
self:CreateRayThreads( PLY.veh, vehs )
elseif ( self:GetRayTraceState() == self:GetNumOfRays() ) then
self:IncreaseRadarStage()
end
elseif ( self:GetRadarStage() == 1 ) then
local data = {}
-- Get the player's vehicle speed
local entSpeed = GetEntitySpeed( PLY.veh )
if ( entSpeed == 0 ) then
data.patrolSpeed = "¦[]"
else
local speed = self:GetVehSpeedFormatted( entSpeed )
data.patrolSpeed = UTIL:FormatSpeed( speed )
end
-- Only grab data to send if there have actually been vehicles captured by the radar
if ( not UTIL:IsTableEmpty( self:GetCapturedVehicles() ) ) then
local vehsForDisplay = self:GetVehiclesForAntenna()
self:SetActiveVehicles( vehsForDisplay ) -- not really any point in setting this
else
self:SetActiveVehicles( { ["front"] = { nil, nil }, ["rear"] = { nil, nil } } )
end
-- Work out what has to be sent
local av = self:GetActiveVehicles()
data.antennas = { ["front"] = nil, ["rear"] = nil }
for ant in UTIL:Values( { "front", "rear" } ) do
if ( self:IsAntennaTransmitting( ant ) ) then
data.antennas[ant] = {}
for i = 1, 2 do
data.antennas[ant][i] = { speed = "¦¦¦", dir = 0 }
-- The vehicle data exists for this slot
if ( av[ant][i] ~= nil ) then
-- We already have the vehicle speed as we needed it earlier on for filtering
data.antennas[ant][i].speed = UTIL:FormatSpeed( self:GetVehSpeedFormatted( av[ant][i].speed ) )
-- Work out if the vehicle is closing or away
local ownH = GetEntityHeading( PLY.veh )
local tarH = GetEntityHeading( av[ant][i].veh )
data.antennas[ant][i].dir = UTIL:GetEntityRelativeDirection( ownH, tarH, 120 )
end
end
end
end
-- Send the update to the NUI side
SendNUIMessage( { _type = "update", speed = data.patrolSpeed, antennas = data.antennas } )
self:ResetTempVehicleIDs()
self:ResetRadarStage()
self:ResetRayTraceState()
end
end
end
-- Updates the local player information
Citizen.CreateThread( function()
while ( true ) do
PLY.ped = PlayerPedId()
PLY.veh = GetVehiclePedIsIn( PLY.ped, false )
PLY.inDriverSeat = GetPedInVehicleSeat( PLY.veh, -1 ) == PLY.ped
PLY.vehClassValid = GetVehicleClass( PLY.veh ) == 18
Citizen.Wait( 500 )
end
end )
-- Update the vehicle pool every 3 seconds
Citizen.CreateThread( function()
while ( true ) do
local vehs = RADAR:GetAllVehicles()
RADAR:SetVehiclePool( vehs )
Citizen.Wait( 3000 )
end
end )
-- Main thread
Citizen.CreateThread( function()
SetNuiFocus( false, false )
RADAR:CacheNumRays()
while ( true ) do
RADAR:Main()
Citizen.Wait( 50 )
end
end )
-- Control manager
Citizen.CreateThread( function()
while ( true ) do
RADAR:RunControlManager()
Citizen.Wait( 0 )
end
end )
------------------------------ DEBUG ------------------------------
Citizen.CreateThread( function()
while ( true ) do
-- Ray line drawing
-- local veh = GetVehiclePedIsIn( PlayerPedId(), false )
for k, v in pairs( RADAR.rayTraces ) do
local startP = GetOffsetFromEntityInWorldCoords( PLY.veh, v.startVec.x, 0.0, 0.0 )
local endP = GetOffsetFromEntityInWorldCoords( PLY.veh, v.endVec.x, v.endVec.y, 0.0 )
UTIL:DrawDebugLine( startP, endP )
end
local av = RADAR:GetActiveVehicles()
for ant in UTIL:Values( { "front", "rear" } ) do
for i = 1, 2, 1 do
if ( av[ant] ~= nil and av[ant][i] ~= nil ) then
local pos = GetEntityCoords( av[ant][i].veh )
local r = RADAR:GetDynamicRadius( av[ant][i].veh )
if ( i == 1 ) then
UTIL:DrawDebugSphere( pos.x, pos.y, pos.z, r, { 255, 127, 0, 100 } )
else
UTIL:DrawDebugSphere( pos.x, pos.y, pos.z, r, { 255, 0, 0, 100 } )
end
end
end
end
Citizen.Wait( 0 )
end
end )
--[[ local types = { "FRONT", "FRONT FAST", "REAR", "REAR FAST" }
Citizen.CreateThread( function()
while ( true ) do
-- Caught veh debug printing
local av = RADAR:GetActiveVehicles()
DrawRect( 0.500, 0.850, 0.400, 0.220, 0, 0, 0, 150 )
for i = 1, 4, 1 do
UTIL:DrawDebugText( 0.250 + ( 0.100 * i ), 0.750, 0.60, true, types[i] )
if ( av[i] ~= nil ) then
local pos = GetEntityCoords( av[i].veh )
local speed = RADAR:GetVehSpeedFormatted( GetEntitySpeed( av[i].veh ) )
local veh = av[i].veh
local rt = av[i].rayType
local dir = UTIL:GetEntityRelativeDirection( GetEntityHeading( GetVehiclePedIsIn( PlayerPedId(), false ) ), GetEntityHeading( veh ), 100 )
if ( dir == 1 ) then dir = "/\\" elseif ( dir == 2 ) then dir = "\\/" else dir = "none" end
DrawMarker( 2, pos.x, pos.y, pos.z + 3, 0.0, 0.0, 0.0, 0.0, 180.0, 0.0, 1.0, 1.0, 1.0, 255, 255, 0, 70, false, true, 2, nil, nil, false )
if ( i % 2 == 0 ) then
UTIL:DrawDebugText( 0.250 + ( 0.100 * i ), 0.800, 0.60, true, "Ent: " .. tostring( veh ) .. "\nSpeed: ~r~" .. tostring( speed ) .. "~s~mph" .. "\nRay type: " .. tostring( rt ) .. "\nDir: " .. tostring( dir ) )
else
UTIL:DrawDebugText( 0.250 + ( 0.100 * i ), 0.800, 0.60, true, "Ent: " .. tostring( veh ) .. "\nSpeed: " .. tostring( speed ) .. "mph" .. "\nRay type: " .. tostring( rt ) .. "\nDir: " .. tostring( dir ) )
end
else
UTIL:DrawDebugText( 0.250 + ( 0.100 * i ), 0.800, 0.60, true, "Ent: nil" .. "\nSpeed: nil" .. "\nRay type: nil" .. "\nDir: nil" )
end
end
-- Ray line drawing
local veh = GetVehiclePedIsIn( PlayerPedId(), false )
for k, v in pairs( RADAR.rayTraces ) do
local startP = GetOffsetFromEntityInWorldCoords( veh, v.startVec.x, 0.0, 0.0 )
local endP = GetOffsetFromEntityInWorldCoords( veh, v.endVec.x, v.endVec.y, 0.0 )
UTIL:DrawDebugLine( startP, endP )
end
Citizen.Wait( 0 )
end
end ) ]]
-- Commands for debugging
RegisterCommand( "rdr", function( src, args, raw )
if ( args[1] == "setlimit" ) then
RADAR:SetFastLimit( tonumber( args[2] ) )
elseif ( args[1] == "setmode" ) then
if ( args[2] == "front" or args[2] == "rear" ) then
RADAR:SetAntennaMode( args[2], tonumber( args[3] ) )
end
end
end, false )