From d6511b8b49d9be49ac189b735ce50de1ffac813a Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 18 Feb 2020 14:04:52 +0000 Subject: [PATCH] Comments and formatting --- cl_plate_reader.lua | 78 +++++++++++++++---- cl_radar.lua | 177 ++++++++++++++++++++++++------------------- cl_utils.lua | 42 +++++----- config.lua | 4 +- nui/radar.css | 37 +-------- nui/radar.html | 2 +- nui/radar.js | 143 ++++++++++++++++++++++++++++------ sv_saving.lua | 159 ++++++++++++++++++++------------------ sv_version_check.lua | 36 ++++++--- 9 files changed, 418 insertions(+), 260 deletions(-) diff --git a/cl_plate_reader.lua b/cl_plate_reader.lua index aad7af7..53d332a 100644 --- a/cl_plate_reader.lua +++ b/cl_plate_reader.lua @@ -15,26 +15,30 @@ READER = {} ----------------------------------------------------------------------------------]]-- READER.vars = { - -- Whether or not the radar's UI is visible + -- Whether or not the plate reader's UI is visible displayed = false, - -- Whether or not the radar should be hidden, e.g. the display is active but the player then steps + -- Whether or not the plate reader should be hidden, e.g. the display is active but the player then steps -- out of their vehicle hidden = false, + -- The BOLO plate boloPlate = "", + -- Cameras, this table contains all of the data needed for operation of the front and rear plate reader cams = { + -- Variables for the front camera ["front"] = { - plate = "", - index = "", - locked = false + plate = "", -- The current plate caught by the reader + index = "", -- The index of the current plate + locked = false -- If the reader is locked }, + -- Variables for the rear camera ["rear"] = { - plate = "", - index = "", - lockec = false + plate = "", -- The current plate caught by the reader + index = "", -- The index of the current plate + locked = false -- If the reader is locked } } } @@ -44,7 +48,7 @@ function READER:GetDisplayState() return self.vars.displayed end --- Toggles the display state of the radar system +-- Toggles the display state of the plate reader system function READER:ToggleDisplayState() -- Toggle the display variable self.vars.displayed = not self.vars.displayed @@ -63,48 +67,63 @@ function READER:GetDisplayHidden() return self.vars.hidden end +-- Returns the stored plate for the given reader function READER:GetPlate( cam ) return self.vars.cams[cam].plate end +-- Sets the plate for the given reader to the given plate function READER:SetPlate( cam, plate ) self.vars.cams[cam].plate = plate end +-- Returns the stored plate index for the given reader function READER:GetIndex( cam ) return self.vars.cams[cam].index end +-- Sets the plate index for the given reader to the given index function READER:SetIndex( cam, index ) self.vars.cams[cam].index = index end +-- Returns the bolo plate function READER:GetBoloPlate() return self.vars.boloPlate end +-- Sets the bolo plate to the given plate function READER:SetBoloPlate( plate ) self.vars.boloPlate = plate UTIL:Notify( "BOLO plate set to: " .. plate ) end +-- Returns if the given reader is locked function READER:GetCamLocked( cam ) return self.vars.cams[cam].locked end +-- Locks the given reader function READER:LockCam( cam ) + -- Check that plate readers can actually be locked if ( PLY:VehicleStateValid() and self:CanPerformMainTask() ) then + -- Toggle the lock state self.vars.cams[cam].locked = not self.vars.cams[cam].locked + -- Tell the NUI side to show/hide the lock icon SendNUIMessage( { _type = "lockPlate", cam = cam, state = self:GetCamLocked( cam ) } ) + + -- Play a beep SendNUIMessage( { _type = "audio", name = "beep", vol = RADAR:GetSettingValue( "beep" ) } ) end end +-- Returns if the plate reader system can perform tasks function READER:CanPerformMainTask() return self.vars.displayed and not self.vars.hidden end +-- Returns if the given relative position value is for front or rear function READER:GetCamFromNum( relPos ) if ( relPos == 1 ) then return "front" @@ -119,32 +138,56 @@ RegisterNUICallback( "togglePlateReaderDisplay", function() READER:ToggleDisplayState() end ) --- Runs when the "Toggle Display" button is pressed on the plate reder box +-- Runs when the "Set BOLO Plate" button is pressed on the plate reader box RegisterNUICallback( "setBoloPlate", function( plate, cb ) + -- Set the BOLO plate READER:SetBoloPlate( plate ) end ) +-- This is the main function that runs and scans all vehicles in front and behind the patrol vehicle function READER:Main() + -- Check that the system can actually run if ( PLY:VehicleStateValid() and self:CanPerformMainTask() ) then + -- Loop through front (1) and rear (-1) for i = 1, -1, -2 do - local start = GetEntityCoords( PLY.veh ) - local offset = GetOffsetFromEntityInWorldCoords( PLY.veh, 0.0, ( 40.0 * i ), 0.0 ) + -- Get the world position of the player's vehicle + local pos = GetEntityCoords( PLY.veh ) + + -- Get a start position 5m in front/behind the player's vehicle + local start = GetOffsetFromEntityInWorldCoords( PLY.veh, 0.0, ( 5.0 * i ), 0.0 ) + + -- Get the end position 40m in front/behind the player's vehicle + local offset = GetOffsetFromEntityInWorldCoords( PLY.veh, -2.5, ( 50.0 * i ), 0.0 ) + + -- Run the ray trace to get a vehicle local veh = UTIL:GetVehicleInDirection( PLY.veh, start, offset ) + -- Get the plate reader text for front/rear local cam = self:GetCamFromNum( i ) + -- Only proceed to read a plate if the hit entity is a valid vehicle and the current camera isn't locked if ( DoesEntityExist( veh ) and IsEntityAVehicle( veh ) and not self:GetCamLocked( cam ) ) then + -- Get the licence plate text from the vehicle local plate = GetVehicleNumberPlateText( veh ) + + -- Get the licence plate index from the vehicle local index = GetVehicleNumberPlateTextIndex( veh ) + -- Only update the stored plate if it's different, otherwise we'd keep sending a NUI message to update the displayed + -- plate and image even though they're the same if ( self:GetPlate( cam ) ~= plate ) then + -- Set the plate for the current reader self:SetPlate( cam, plate ) + + -- Set the plate index for the current reader self:SetIndex( cam, index ) + -- Automatically lock the plate if the scanned plate matches the BOLO if ( plate == self:GetBoloPlate() ) then self:LockCam( cam ) end + -- Send the plate information to the NUI side to update the UI SendNUIMessage( { _type = "changePlate", cam = cam, plate = plate, index = index } ) end end @@ -152,14 +195,20 @@ function READER:Main() end end +-- Main thread Citizen.CreateThread( function() while ( true ) do + -- Run the main plate reader function READER:Main() + -- Wait half a second Citizen.Wait( 500 ) end end ) +-- This function is pretty much straight from WraithRS, it does the job so I didn't see the point in not +-- using it. Hides the radar UI when certain criteria is met, e.g. in pause menu or stepped out ot the +-- patrol vehicle function READER:RunDisplayValidationCheck() if ( ( ( PLY.veh == 0 or ( PLY.veh > 0 and not PLY.vehClassValid ) ) and self:GetDisplayState() and not self:GetDisplayHidden() ) or IsPauseMenuActive() and self:GetDisplayState() ) then self:SetDisplayHidden( true ) @@ -170,12 +219,15 @@ function READER:RunDisplayValidationCheck() end end +-- Runs the display validation check for the radar Citizen.CreateThread( function() Citizen.Wait( 100 ) - while ( true ) do + while ( true ) do + -- Run the check READER:RunDisplayValidationCheck() + -- Wait half a second Citizen.Wait( 500 ) end end ) \ No newline at end of file diff --git a/cl_radar.lua b/cl_radar.lua index ecd2c2e..82d9b7d 100644 --- a/cl_radar.lua +++ b/cl_radar.lua @@ -35,16 +35,20 @@ end ) ----------------------------------------------------------------------------------]]-- local spawned = false +-- Runs every time the player spawns, but the additional check means it only runs the first time +-- the player spawns AddEventHandler( "playerSpawned", function() - if ( not spawned ) then - TriggerServerEvent( "wk:getUiData" ) - spawned = true - end + if ( not spawned ) then + -- Ask the server to get the player's saved UI data + TriggerServerEvent( "wk:getUiData" ) + spawned = true + end end ) +-- Grabs the saved UI data sent by the server and forwards it to the NUI side RegisterNetEvent( "wk:loadUiData" ) AddEventHandler( "wk:loadUiData", function( data ) - SendNUIMessage( { _type = "loadUiSettings", data = data } ) + SendNUIMessage( { _type = "loadUiSettings", data = data } ) end ) --[[---------------------------------------------------------------------------------- @@ -52,10 +56,10 @@ end ) ----------------------------------------------------------------------------------]]-- PLY = { - ped = PlayerPedId(), - veh = nil, - inDriverSeat = false, - vehClassValid = false + ped = PlayerPedId(), + veh = nil, + inDriverSeat = false, + vehClassValid = false } -- Used to check if the player is in a position where the radar should be allowed operation @@ -109,10 +113,10 @@ RADAR.vars = ["opp"] = 3, -- The volume of the audible beep - ["beep"] = 1.0, - - -- The volume of the verbal lock confirmation - ["voice"] = 1.0, + ["beep"] = 1.0, + + -- The volume of the verbal lock confirmation + ["voice"] = 1.0, -- The speed unit used in conversions ["speedType"] = "mph" @@ -126,8 +130,8 @@ RADAR.vars = { displayText = { "¦¦¦", "FAS" }, optionsText = { "On¦", "Off" }, options = { true, false }, optionIndex = 1, settingText = "fastDisplay" }, { displayText = { "¦SL", "SEn" }, optionsText = { "¦1¦", "¦2¦", "¦3¦", "¦4¦", "¦5¦" }, options = { 0.2, 0.4, 0.6, 0.8, 1.0 }, optionIndex = 3, settingText = "same" }, { displayText = { "¦OP", "SEn" }, optionsText = { "¦1¦", "¦2¦", "¦3¦", "¦4¦", "¦5¦" }, options = { 0.2, 0.4, 0.6, 0.8, 1.0 }, optionIndex = 3, settingText = "opp" }, - { displayText = { "bEE", "P¦¦" }, optionsText = { "Off", "¦1¦", "¦2¦", "¦3¦", "¦4¦", "¦5¦" }, options = { 0.0, 0.2, 0.4, 0.6, 0.8, 1.0 }, optionIndex = 6, settingText = "beep" }, - { displayText = { "VOI", "CE¦" }, optionsText = { "Off", "¦1¦", "¦2¦", "¦3¦", "¦4¦", "¦5¦" }, options = { 0.0, 0.2, 0.4, 0.6, 0.8, 1.0 }, optionIndex = 6, settingText = "voice" }, + { displayText = { "bEE", "P¦¦" }, optionsText = { "Off", "¦1¦", "¦2¦", "¦3¦", "¦4¦", "¦5¦" }, options = { 0.0, 0.2, 0.4, 0.6, 0.8, 1.0 }, optionIndex = 6, settingText = "beep" }, + { displayText = { "VOI", "CE¦" }, optionsText = { "Off", "¦1¦", "¦2¦", "¦3¦", "¦4¦", "¦5¦" }, options = { 0.0, 0.2, 0.4, 0.6, 0.8, 1.0 }, optionIndex = 6, settingText = "voice" }, { displayText = { "Uni", "tS¦" }, optionsText = { "USA", "INT" }, options = { "mph", "kmh" }, optionIndex = 1, settingText = "speedType" } }, @@ -193,10 +197,10 @@ RADAR.vars = -- The wait time for the ray trace system, this changes dynamically based on if the player's vehicle is stationary -- or not - threadWaitTime = 500, - - -- Key lock, when true, prevents any of the radar's key events from working, like the ELS key lock - keyLock = false + threadWaitTime = 500, + + -- Key lock, when true, prevents any of the radar's key events from working, like the ELS key lock + keyLock = false } -- Speed conversion values @@ -411,19 +415,19 @@ end -- Toggles the internal key lock state, which stops any of the radar's key binds from working function RADAR:ToggleKeyLock() - -- Check the player state is valid - if ( PLY:VehicleStateValid() ) then - -- Toggle the key lock variable - self.vars.keyLock = not self.vars.keyLock + -- Check the player state is valid + if ( PLY:VehicleStateValid() ) then + -- Toggle the key lock variable + self.vars.keyLock = not self.vars.keyLock - -- Tell the NUI side to display the key lock message - SendNUIMessage( { _type = "displayKeyLock", state = self:GetKeyLockState() } ) - end + -- Tell the NUI side to display the key lock message + SendNUIMessage( { _type = "displayKeyLock", state = self:GetKeyLockState() } ) + end end -- Returns the key lock state function RADAR:GetKeyLockState() - return self.vars.keyLock + return self.vars.keyLock end @@ -626,12 +630,12 @@ end -- much further away (400+ units). Also, as my system uses sphere intersections, each sphere can have a different -- radius, which means that larger vehicles can have larger spheres, and smaller vehicles can have smaller spheres. function RADAR:GetLineHitsSphereAndDir( c, radius, rs, re ) - -- Take the vector3's and turn them into vector2's, this way all of the calculations below are for an - -- infinite cylinder rather than a sphere, which also means that vehicles can be detected even when on - -- an incline! - local rayStart = vector2( rs.x, rs.y ) - local rayEnd = vector2( re.x, re.y ) - local centre = vector2( c.x, c.y ) + -- Take the vector3's and turn them into vector2's, this way all of the calculations below are for an + -- infinite cylinder rather than a sphere, which also means that vehicles can be detected even when on + -- an incline! + local rayStart = vector2( rs.x, rs.y ) + local rayEnd = vector2( re.x, re.y ) + local centre = vector2( c.x, c.y ) -- First we get the normalised ray, this way we then know the direction the ray is going local rayNorm = norm( rayEnd - rayStart ) @@ -674,7 +678,7 @@ function RADAR:ShootCustomRay( plyVeh, veh, s, e ) -- Calculate the distance between the target vehicle and the start point of the ray trace, note how we don't -- use GetDistanceBetweenCoords or Vdist, the method below still returns the same result with less cpu time - local dist = #( pos - s ) + local dist = #( pos - s ) -- We only perform a trace on the target vehicle if it exists, isn't the player's vehicle, and the distance is -- less than the max distance defined by the system @@ -683,10 +687,10 @@ function RADAR:ShootCustomRay( plyVeh, veh, s, e ) local entSpeed = GetEntitySpeed( veh ) -- Check that the target vehicle is within the line of sight of the player's vehicle - local visible = HasEntityClearLosToEntity( plyVeh, veh, 15 ) -- 13 seems okay, 15 too (doesn't grab ents through ents) - - -- Get the pitch of the player's vehicle - local pitch = GetEntityPitch( plyVeh ) + local visible = HasEntityClearLosToEntity( plyVeh, veh, 15 ) -- 13 seems okay, 15 too (doesn't grab ents through ents) + + -- Get the pitch of the player's vehicle + local pitch = GetEntityPitch( plyVeh ) -- Now we check that the target vehicle is moving and is visible if ( entSpeed > 0.1 and ( pitch > -35 and pitch < 35 ) and visible ) then @@ -928,10 +932,10 @@ function RADAR:SetAntennaSpeedLock( ant, speed, dir, lockType ) self:SetAntennaSpeedIsLocked( ant, true ) -- Send a message to the NUI side to play the beep sound with the current volume setting - SendNUIMessage( { _type = "audio", name = "beep", vol = RADAR:GetSettingValue( "beep" ) } ) - - -- Send a message to the NUI side to play the lock audio with the current voice volume setting - SendNUIMessage( { _type = "lockAudio", ant = ant, dir = dir, vol = RADAR:GetSettingValue( "voice" ) } ) + SendNUIMessage( { _type = "audio", name = "beep", vol = RADAR:GetSettingValue( "beep" ) } ) + + -- Send a message to the NUI side to play the lock audio with the current voice volume setting + SendNUIMessage( { _type = "lockAudio", ant = ant, dir = dir, vol = RADAR:GetSettingValue( "voice" ) } ) end end @@ -966,10 +970,10 @@ end function RADAR:LockAntennaSpeed( ant ) -- Only lock a speed if the antenna is on and the UI is displayed if ( self:IsPowerOn() and self:GetDisplayState() and not self:GetDisplayHidden() and self:IsAntennaTransmitting( ant ) ) then - -- Check if the antenna doesn't have a locked speed, if it doesn't then we lock in the speed, otherwise we - -- reset the lock + -- Check if the antenna doesn't have a locked speed, if it doesn't then we lock in the speed, otherwise we + -- reset the lock if ( not self:IsAntennaSpeedLocked( ant ) ) then - -- Set up a temporary table with 3 nil values, this way if the system isn't able to get a speed or + -- Set up a temporary table with 3 nil values, this way if the system isn't able to get a speed or -- direction, the speed lock function won't work local data = { nil, nil, nil } @@ -986,7 +990,7 @@ function RADAR:LockAntennaSpeed( ant ) end -- Lock in the speed data for the antenna - self:SetAntennaSpeedLock( ant, data[1], data[2], data[3] ) + self:SetAntennaSpeedLock( ant, data[1], data[2], data[3] ) else self:ResetAntennaSpeedLock( ant ) end @@ -1039,8 +1043,8 @@ function RADAR:InsertCapturedVehicleData( t, rt ) end --[[ - These need to be changed so a ray type can be set too, otherwise in its current state, messes up vehicle - detection. + These need to be changed so a ray type can be set too, otherwise in its current state, messes up vehicle + detection. -- Sets the given value to true in the temp vehicles table, it is a test system used to reduce ray traces -- on vehicles that have already been hit by another trace. Currently not implemented fully, as it doesn't @@ -1342,7 +1346,7 @@ end ) -- Runs when the JavaScript side sends the UI data for saving RegisterNUICallback( "saveUiData", function( data, cb ) - TriggerServerEvent( "wk:saveUiData", data ) + TriggerServerEvent( "wk:saveUiData", data ) end ) @@ -1526,15 +1530,21 @@ end -- Main thread Citizen.CreateThread( function() + -- Remove the NUI focus just in case SetNuiFocus( false, false ) + -- Run the function to cache the number of rays, this way a hard coded number is never needed RADAR:CacheNumRays() + + -- Update the end coordinates for the ray traces based on the config, again, reduced hard coding RADAR:UpdateRayEndCoords() + -- If the fast limit feature is allowed, create the config in the radar variables if ( RADAR:IsFastLimitAllowed() ) then RADAR:CreateFastLimitConfig() end + -- Run the main radar function while ( true ) do RADAR:Main() @@ -1542,6 +1552,9 @@ Citizen.CreateThread( function() end end ) +-- This function is pretty much straight from WraithRS, it does the job so I didn't see the point in not +-- using it. Hides the radar UI when certain criteria is met, e.g. in pause menu or stepped out ot the +-- patrol vehicle function RADAR:RunDisplayValidationCheck() if ( ( ( PLY.veh == 0 or ( PLY.veh > 0 and not PLY.vehClassValid ) ) and self:GetDisplayState() and not self:GetDisplayHidden() ) or IsPauseMenuActive() and self:GetDisplayState() ) then self:SetDisplayHidden( true ) @@ -1552,69 +1565,79 @@ function RADAR:RunDisplayValidationCheck() end end +-- Runs the display validation check for the radar Citizen.CreateThread( function() Citizen.Wait( 100 ) while ( true ) do + -- Run the check RADAR:RunDisplayValidationCheck() + -- Wait half a second Citizen.Wait( 500 ) end end ) -- Update the vehicle pool every 3 seconds function RADAR:UpdateVehiclePool() + -- Only update the vehicle pool if we need to if ( PLY:VehicleStateValid() and self:CanPerformMainTask() and self:IsEitherAntennaOn() ) then + -- Get the active vehicle set local vehs = self:GetAllVehicles() + + -- Update the vehicle pool self:SetVehiclePool( vehs ) end end +-- Runs the vehicle pool updater Citizen.CreateThread( function() while ( true ) do + -- Update the vehicle pool RADAR:UpdateVehiclePool() + -- Wait 3 seconds Citizen.Wait( 3000 ) end end ) function RunControlManager() - if ( not RADAR:GetKeyLockState() ) then - -- Opens the remote control - if ( IsDisabledControlJustPressed( 1, CONFIG.remote_control_key ) ) then - RADAR:OpenRemote() - end + if ( not RADAR:GetKeyLockState() ) then + -- Opens the remote control + if ( IsDisabledControlJustPressed( 1, CONFIG.remote_control_key ) ) then + RADAR:OpenRemote() + end - -- Locks speed from front antenna - if ( IsDisabledControlJustPressed( 1, CONFIG.front_lock_key ) ) then - RADAR:LockAntennaSpeed( "front" ) - end + -- Locks speed from front antenna + if ( IsDisabledControlJustPressed( 1, CONFIG.front_lock_key ) ) then + RADAR:LockAntennaSpeed( "front" ) + end - -- Locks speed from rear antenna - if ( IsDisabledControlJustPressed( 1, CONFIG.rear_lock_key ) ) then - RADAR:LockAntennaSpeed( "rear" ) - end + -- Locks speed from rear antenna + if ( IsDisabledControlJustPressed( 1, CONFIG.rear_lock_key ) ) then + RADAR:LockAntennaSpeed( "rear" ) + end - -- Locks front plate reader - if ( IsDisabledControlJustPressed( 1, CONFIG.plate_front_lock_key ) ) then - READER:LockCam( "front" ) - end + -- Locks front plate reader + if ( IsDisabledControlJustPressed( 1, CONFIG.plate_front_lock_key ) ) then + READER:LockCam( "front" ) + end - -- Locks front plate reader - if ( IsDisabledControlJustPressed( 1, CONFIG.plate_rear_lock_key ) ) then - READER:LockCam( "rear" ) - end - end - - -- Toggles the key lock state - if ( IsDisabledControlJustPressed( 1, CONFIG.key_lock_key ) ) then + -- Locks front plate reader + if ( IsDisabledControlJustPressed( 1, CONFIG.plate_rear_lock_key ) ) then + READER:LockCam( "rear" ) + end + end + + -- Toggles the key lock state + if ( IsDisabledControlJustPressed( 1, CONFIG.key_lock_key ) ) then RADAR:ToggleKeyLock() - end + end -- Shortcut to restart the resource - if ( IsDisabledControlJustPressed( 1, 167 ) ) then + --[[ if ( IsDisabledControlJustPressed( 1, 167 ) ) then ExecuteCommand( "restart wk_wars2x" ) - end + end ]] end -- Control manager diff --git a/cl_utils.lua b/cl_utils.lua index e65fc67..84a9ef8 100644 --- a/cl_utils.lua +++ b/cl_utils.lua @@ -7,33 +7,45 @@ UTIL = {} +-- Returns a number to a set number of decimal places function UTIL:Round( num, numDecimalPlaces ) return tonumber( string.format( "%." .. ( numDecimalPlaces or 0 ) .. "f", num ) ) end +-- The custom font used for the digital displays have the ¦ symbol as an empty character, this function +-- takes a speed and returns a formatted speed that can be displayed on the radar function UTIL:FormatSpeed( speed ) + -- Return "Err" (Error) if the given speed is outside the 0-999 range if ( speed < 0 or speed > 999 ) then return "Err" end + -- Convert the speed to a string local text = tostring( speed ) local pipes = "" + -- Create a string of pipes (¦) for the number of spaces for i = 1, 3 - string.len( text ) do pipes = pipes .. "¦" end + -- Return the formatted speed return pipes .. text end +-- Returns a clamped numerical value based on the given parameters function UTIL:Clamp( val, min, max ) + -- Return the min value if the given value is less than the min if ( val < min ) then return min + -- Return the max value if the given value is larger than the max elseif ( val > max ) then return max end + -- Return the given value if it's between the min and max return val end +-- Returns if the given table is empty, includes numerical and non-numerical key values function UTIL:IsTableEmpty( t ) local c = 0 @@ -52,12 +64,15 @@ function UTIL:Values( xs ) end end +-- Old ray trace function for getting a vehicle in a specific direction from a start point function UTIL:GetVehicleInDirection( entFrom, coordFrom, coordTo ) - local rayHandle = StartShapeTestCapsule( coordFrom.x, coordFrom.y, coordFrom.z, coordTo.x, coordTo.y, coordTo.z, 5.0, 10, entFrom, 7 ) - local _, hitEntity, endCoords, surfaceNormal, vehicle = GetShapeTestResult( rayHandle ) + local rayHandle = StartShapeTestCapsule( coordFrom.x, coordFrom.y, coordFrom.z, coordTo.x, coordTo.y, coordTo.z, 8.0, 10, entFrom, 7 ) + local _, _, _, _, vehicle = GetShapeTestResult( rayHandle ) return vehicle end +-- Returns if a target vehicle is coming towards or going away from the patrol vehicle, it has a range +-- so if a vehicle is sideways compared to the patrol vehicle, the directional arrows won't light up function UTIL:GetEntityRelativeDirection( myAng, tarAng ) local angleDiff = math.abs( ( myAng - tarAng + 180 ) % 360 - 180 ) @@ -70,6 +85,7 @@ function UTIL:GetEntityRelativeDirection( myAng, tarAng ) return 0 end +-- Your everyday GTA notification function function UTIL:Notify( text ) SetNotificationTextEntry( "STRING" ) AddTextComponentSubstringPlayerName( text ) @@ -91,28 +107,6 @@ function UTIL:DrawDebugText( x, y, scale, centre, text ) DrawText( x, y ) end -function UTIL:DrawDebugSphere( x, y, z, r, col ) - if ( CONFIG.debug_mode ) then - local col = col or { 255, 255, 255, 255 } - - DrawMarker( 28, x, y, z, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, r, r, r, col[1], col[2], col[3], col[4], false, true, 2, false, false, false, false ) - end -end - -function UTIL:DrawDebugLine( startP, endP, col ) - if ( CONFIG.debug_mode ) then - local col = col or { 255, 255, 255, 255 } - - DrawLine( startP, endP, col[1], col[2], col[3], col[4] ) - end -end - -function UTIL:DebugPrint( text ) - if ( CONFIG.debug_mode ) then - print( text ) - end -end - --[[The MIT License (MIT) Copyright (c) 2017 IllidanS4 diff --git a/config.lua b/config.lua index 59cb669..ee7510b 100644 --- a/config.lua +++ b/config.lua @@ -8,8 +8,8 @@ -- Do not touch this CONFIG = {} --- Radar Control Panel key --- The default key to open the radar control panel is 166 (F5 - INPUT_SELECT_CHARACTER_MICHAEL) +-- Remote control key +-- The default key to open the remote control is 166 (F5 - INPUT_SELECT_CHARACTER_MICHAEL) CONFIG.remote_control_key = 166 -- Radar front antenna lock/unlock Key diff --git a/nui/radar.css b/nui/radar.css index e5b7bba..af18424 100644 --- a/nui/radar.css +++ b/nui/radar.css @@ -88,7 +88,6 @@ button:focus { outline: none; } width: 675px; height: 200px; - /* Temp centre */ position: absolute; margin: auto; top: 0; @@ -160,8 +159,6 @@ button:focus { outline: none; } grid-template-columns: 45px 50px 150px 35px 50px 135px 35px 135px; gap: 0 0; - /* z-index: 2; */ - box-shadow: inset 0px 20px 20px -15px rgba( 0, 0, 0, 0.4 ); } #radar .label { @@ -194,7 +191,6 @@ button:focus { outline: none; } background: linear-gradient( to bottom, rgba( 230, 230, 230, 0.8 ), rgb( 40, 168, 40 ) 10%, rgb( 0, 134, 0 ) ); box-shadow: 0px 0px 3px 0px rgb( 80, 80, 80 ); text-align: center; - /* font-family: 'Heebo-Regular'; */ font-size: 14px; color: rgb( 34, 34, 34 ); line-height: 45px; @@ -372,20 +368,13 @@ button:focus { outline: none; } } #rc { - /* width: 275px; - height: 750px; */ width: 315px; height: 800px; position: absolute; - /* top: 0; - bottom: 0; - left: 0; - right: 0; */ top: calc( 50% - ( 800px / 2 ) ); left: calc( 50% - ( 315px / 2 ) ); margin: auto; - /* padding-top: 25px; */ padding: 65px 30px 50px 30px; display: grid; @@ -394,12 +383,9 @@ button:focus { outline: none; } align-items: center; color: white; - /* background-color: rgb( 50, 50, 50 ); */ background-image: url( "images/rc_bg.png" ); transition: transform 0.5s; - - /* clip-path: polygon( 5% 0, 95% 0, 100% 25%, 90% 100%, 10% 100%, 0 25% ); */ /* Settings for scaling */ transform: scale( 1.0 ); @@ -414,8 +400,6 @@ button:focus { outline: none; } padding: 0; background-color: rgb( 200, 200, 200 ); - - /* font-family: 'Heebo-Regular'; */ box-shadow: 2px 3px rgb( 100, 100, 100 ); } @@ -495,10 +479,8 @@ button:focus { outline: none; } } #rc .label { - /* font-family: 'Heebo-Regular'; */ font-size: 20px; letter-spacing: 1px; - /* margin: 5px 0; */ } #rc .antenna_btns_container { @@ -625,9 +607,6 @@ button:focus { outline: none; } transform: scale( 1.0 ); transform-origin: 0 0; - /* background-color: rgb(70, 70, 70); - box-shadow: inset 0px 20px 20px -15px rgba( 0, 0, 0, 0.4 ); */ - z-index: 1; } #plateReaderFrame .frame_border { @@ -644,8 +623,6 @@ button:focus { outline: none; } background-color: rgb( 20, 22, 18 ); border-radius: 5px; - - /* clip-path: polygon( 20px 2px, 665px 2px, 682px 30%, 682px 70%, 665px 208px, 20px 208px, 3px 70%, 3px 30% ); */ } #plateReader { @@ -721,7 +698,6 @@ button:focus { outline: none; } font-size: 58px; text-align: center; letter-spacing: -3px; - /* color: rgb(0, 0, 163); */ padding-top: 5px; margin: 0; grid-column: 1; @@ -754,11 +730,9 @@ button:focus { outline: none; } bottom: 0; left: 0; - /* background-color: rgb( 50, 54, 45 ); */ background: linear-gradient( to bottom, rgb( 70, 70, 70 ), rgb( 45, 45, 45 ) ); border: 3px solid rgb( 0, 0, 0 ); - /* for testing */ z-index: 4; } #plateReaderBox .title { @@ -805,10 +779,10 @@ button:focus { outline: none; } font-family: "Plate-Font"; text-align: center; font-size: 38px; + text-transform: uppercase; padding: 0; padding-bottom: 15px; margin-bottom: -15px; - /* border: 3px solid rgb( 0, 0, 0 ); */ } #plateReaderBox .close { @@ -843,9 +817,8 @@ button:focus { outline: none; } top: 0; right: 0; bottom: 0; - left: 0; - - /* background-color: rgb( 50, 54, 45 ); */ + left: 0; + background: linear-gradient( to bottom, rgb( 70, 70, 70 ), rgb( 45, 45, 45 ) ); border: 3px solid rgb( 0, 0, 0 ); @@ -872,10 +845,6 @@ button:focus { outline: none; } #uiSettingsBox .scaling { height: 70px; - - /* display: flex; - justify-content: space-evenly; - align-items: center; */ display: grid; grid-template-columns: 1fr 2fr 1fr; diff --git a/nui/radar.html b/nui/radar.html index b628418..0fbe564 100644 --- a/nui/radar.html +++ b/nui/radar.html @@ -16,7 +16,7 @@
FRONT ANTENNA
-
PWR
+
PWR
diff --git a/nui/radar.js b/nui/radar.js index 2c49bce..bf18263 100644 --- a/nui/radar.js +++ b/nui/radar.js @@ -14,6 +14,7 @@ var resourceName; var uiEdited = false; +// All of the audio file names const audioNames = { // Beeps @@ -29,6 +30,7 @@ const audioNames = away: "away.ogg" } +// Defines which audio needs to play for which direction const lockAudio = { front: { @@ -50,7 +52,6 @@ const elements = remote: $( "#rc" ), plateReader: $( "#plateReaderFrame" ), - // toggleDisplay: $( "#toggleDisplay" ), pwrBtn: $( "#pwrBtn" ), uiSettingsBtn: $( "#uiSettings" ), @@ -163,6 +164,7 @@ const elements = } } +// Antenna mode values const modes = { off: 0, @@ -171,6 +173,7 @@ const modes = both: 3 } +// Antenna direction values const dirs = { none: 0, @@ -190,45 +193,45 @@ elements.uiSettingsBox.hide(); elements.keyLock.label.hide(); elements.helpWindow.hide(); +// Sets the action for the "UI SETTINGS" button on the remote to open the UI settings box elements.uiSettingsBtn.click( function() { setEleVisible( elements.uiSettingsBox, true ); } ) +// Sets the action for the "PLATE READER" button on the remote to open the plate reader box elements.plateReaderBtn.click( function() { setEleVisible( elements.plateReaderBox, true ); } ) +// Sets the action for the "HELP" button on the remote to open the help window and load the web page elements.openHelp.click( function() { setEleVisible( elements.helpWindow, true ); loadHelp( true ); } ) +// Sets the action for the "Close Help" button under the help window to close the help window and unload the web page elements.closeHelp.click( function() { setEleVisible( elements.helpWindow, false ); loadHelp( false ); } ) -elements.pwrBtn.click( function() { - togglePower(); -} ) - /*------------------------------------------------------------------------------------ Setters ------------------------------------------------------------------------------------*/ +// Sets the visibility of an element to the given state function setEleVisible( ele, state ) { state ? ele.fadeIn() : ele.fadeOut(); - - if ( state ) { - ele.blur(); - } } +// Changes the class of the given element so it looks lit up function setLight( ant, cat, item, state ) { + // Grab the obj element from the elements table let obj = elements.antennas[ant][cat][item]; + // Either add the active class or remove it if ( state ) { obj.addClass( cat == "dirs" ? "active_arrow" : "active" ); } else { @@ -236,48 +239,72 @@ function setLight( ant, cat, item, state ) } } +// Sets the XMIT state of an antenna based on the passed state, makes the fast box display "HLd" +// when the state is false function setAntennaXmit( ant, state ) { + // Set the light state of the antenna's XMIT icon setLight( ant, "modes", "xmit", state ); + // Clear the antenna's directional arrows and speeds, display "HLd" in the fast box if ( !state ) { clearDirs( ant ); elements.antennas[ant].targetSpeed.html( "¦¦¦" ); - elements.antennas[ant].fastSpeed.html( "HLd" ); + elements.antennas[ant].fastSpeed.html( "HLd" ); + + // Blank the fast box when the antenna is set to transmit } else { elements.antennas[ant].fastSpeed.html( "¦¦¦" ); } } +// Sets the mode lights for the given antenna function setAntennaMode( ant, mode ) { - setLight( ant, "modes", "same", mode == modes.same ); + // Light up the 'same' led if the given mode is the same mode, otherwise blank it + setLight( ant, "modes", "same", mode == modes.same ); + + // Light up the 'opp' led if the given mode is the opp mode, otherwise blank it setLight( ant, "modes", "opp", mode == modes.opp ); } +// Sets the fast light for the given antenna function setAntennaFastMode( ant, state ) { + // Lighten or dull the fast led based on the given state setLight( ant, "fast", "fastLabel", state ); } +// Sets the lock light for the given antenna function setAntennaLock( ant, state ) { + // Lighten or dull the lock led based on the given state setLight( ant, "fast", "lockLabel", state ); } +// Sets the directional arrows light for the given antenna function setAntennaDirs( ant, dir, fastDir ) { - setLight( ant, "dirs", "fwd", dir == dirs.closing ); + // Target forward + setLight( ant, "dirs", "fwd", dir == dirs.closing ); + + // Target backward setLight( ant, "dirs", "bwd", dir == dirs.away ); - setLight( ant, "dirs", "fwdFast", fastDir == dirs.closing ); + // Fast forward + setLight( ant, "dirs", "fwdFast", fastDir == dirs.closing ); + + // Fast backward setLight( ant, "dirs", "bwdFast", fastDir == dirs.away ); } +// sets the plate lock light for the given plate reader function setPlateLock( cam, state ) { + // Get the plate reader lock object let obj = elements.plates[cam].lock; + // Add or remove the active class if ( state ) { obj.addClass( "active" ); } else { @@ -285,6 +312,7 @@ function setPlateLock( cam, state ) } } +// Sets the license text and plate image of the given plate reader function setPlate( cam, plate, index ) { // Get the plate items @@ -309,40 +337,55 @@ function setPlate( cam, plate, index ) /*------------------------------------------------------------------------------------ Clearing functions ------------------------------------------------------------------------------------*/ +// Clears the same, opp, fast, and lock leds for the given antenna function clearModes( ant ) { + // Iterate through the modes and clear them for ( let i in elements.antennas[ant].modes ) { elements.antennas[ant].modes[i].removeClass( "active" ); } + // Iterate through the fast leds and clear them for ( let a in elements.antennas[ant].fast ) { elements.antennas[ant].fast[a].removeClass( "active" ); } } +// Clears the directional arrows for the given antenna function clearDirs( ant ) { + // Iterate through the directional arrows and clear them for ( let i in elements.antennas[ant].dirs ) { elements.antennas[ant].dirs[i].removeClass( "active_arrow" ); } } +// Clears all of the elements of the given antenna function clearAntenna( ant ) { - clearModes( ant ); + // Clear the modes + clearModes( ant ); + + // Clear the directional arrows clearDirs( ant ); - elements.antennas[ant].targetSpeed.html( "¦¦¦" ); + // Blank the target speed box + elements.antennas[ant].targetSpeed.html( "¦¦¦" ); + + // Blank the fast speed box elements.antennas[ant].fastSpeed.html( "¦¦¦" ); } +// Clears all the elements on the radar's UI function clearEverything() { + // Blank the patrol speed elements.patrolSpeed.html( "¦¦¦" ); + // Blank both the antennas for ( let i in elements.antennas ) { clearAntenna( i ); @@ -353,24 +396,26 @@ function clearEverything() /*------------------------------------------------------------------------------------ Radar power functions ------------------------------------------------------------------------------------*/ -function togglePower() -{ - sendData( "togglePower", null ); -} - +// Simulates the radar unit powering up by lighting all of the elements function poweringUp() { + // Set the patrol speed container to be fully lit elements.patrolSpeed.html( "888" ); + // Iterate through the front and rear antenna elements for ( let i of [ "front", "rear" ] ) { + // Get the antenna object to shorten the target reference let e = elements.antennas[i]; + // Set the target and fast speed box to be fully lit e.targetSpeed.html( "888" ); e.fastSpeed.html( "888" ); + // Iterate through the rest of the antenna's elements for ( let a of [ "dirs", "modes", "fast" ] ) { + // Iterate through the objects for the current category and add the active class for ( let obj in e[a] ) { a == "dirs" ? e[a][obj].addClass( "active_arrow" ) : e[a][obj].addClass( "active" ); @@ -379,17 +424,23 @@ function poweringUp() } } +// Simulates the 'fully powered' state of the radar unit function poweredUp() { + // Completely clear everything clearEverything(); + // Activate the 'fast' led for both antennas, and make sure the xmit led is off for ( let ant of [ "front", "rear" ] ) { + // Even though the clearEverything() function is called above, we run this so the fast window + // displays 'HLd' setAntennaXmit( ant, false ); setAntennaFastMode( ant, true ); } } +// Runs the startup process or clears everything, the Lua side calls for the full powered up state function radarPower( state ) { state ? poweringUp() : clearEverything(); @@ -399,17 +450,26 @@ function radarPower( state ) /*------------------------------------------------------------------------------------ Audio ------------------------------------------------------------------------------------*/ +// Plays the given audio file name from the audioNames list at the given volume function playAudio( name, vol ) { - let audio = new Audio( "sounds/" + audioNames[name] ); - audio.volume = vol; + // Create the new audio object + let audio = new Audio( "sounds/" + audioNames[name] ); + + // Set the volume + audio.volume = vol; + + // Play the audio clip audio.play(); } +// Plays the verbal lock, this is a separate from the function above as it plays two sounds with a delay function playLockAudio( ant, dir, vol ) { + // Play the front/rear sound playAudio( ant, vol ); + // If the vehicle was closing or away, play that sound too if ( dir > 0 ) { setTimeout( function() { @@ -422,23 +482,31 @@ function playLockAudio( ant, dir, vol ) /*------------------------------------------------------------------------------------ Radar updating ------------------------------------------------------------------------------------*/ +// Updates patrol speed as well as the speeds and directional arrows for the given antenna function updateDisplays( ps, ants ) { + // Update the patrol speed elements.patrolSpeed.html( ps ); + // Iterate through the antenna data for ( let ant in ants ) { + // Make sure there is actually data for the current antenna data if ( ants[ant] != null ) { + // Grab the antenna element from the elements table let e = elements.antennas[ant]; + // Update the target and fast speeds e.targetSpeed.html( ants[ant][0].speed ); e.fastSpeed.html( ants[ant][1].speed ); + // Update the directional arrows setAntennaDirs( ant, ants[ant][0].dir, ants[ant][1].dir ); } } } +// Updates all of the mode leds on the radar interface function settingUpdate( ants ) { for ( let ant in ants ) @@ -454,22 +522,30 @@ function settingUpdate( ants ) /*------------------------------------------------------------------------------------ Misc ------------------------------------------------------------------------------------*/ +// Displays the given option text and current option value on the radar function menu( optionText, option ) { + // Clear everything clearEverything(); + // Set the target and fast box to the option text elements.antennas.front.targetSpeed.html( optionText[0] ); elements.antennas.front.fastSpeed.html( optionText[1] ); + // Set the patrol speed to the value elements.patrolSpeed.html( option ); } +// Makes the key lock label fade in then fade out after 2 seconds function displayKeyLock( state ) { + // Set the state label text to enabled or disabled elements.keyLock.stateLabel.html( state ? "enabled" : "disabled" ); + // Fade in the label elements.keyLock.label.fadeIn(); + // Make the label fade out after 2 seconds setTimeout( function() { elements.keyLock.label.fadeOut(); }, 2000 ); @@ -484,18 +560,22 @@ function sendData( name, data ) { } ); } +// Sets the ui edited variable to the given state, this is used in the UI save system function setUiHasBeenEdited( state ) { uiEdited = state; } +// Returns if the UI has been edited function hasUiBeenEdited() { return uiEdited; } +// Gathers the UI data and sends it to the Lua side function sendSaveData() { + // Make sure we only collect and send the UI data if it has been edited if ( hasUiBeenEdited() ) { let data = { @@ -520,10 +600,12 @@ function sendSaveData() safezone: safezone } + // Send the data sendData( "saveUiData", data ); } } +// Loads the UI settings function loadUiSettings( data ) { // Iterate through "remote", "radar" and "plateReader" @@ -550,20 +632,28 @@ function loadUiSettings( data ) elements.safezoneSlider.trigger( "input" ); } +// Sets the on click function for the set BOLO plate button elements.setBoloBtn.click( function() { + // Grab the value of the text input box let plate = elements.boloText.val(); + + // Send the plate to the Lua side sendData( "setBoloPlate", plate ); } ) +// Checks what the user is typing into the plate box function checkPlateInput( event ) { - let valid = /[A-Z0-9"?!£$%^&*()+\=\-_\[\]\{\};:'@#~,<.>/?|\\ ]/g.test( event.key ); + // See if what has been typed is a valid key, GTA only seems to like A-Z and 0-9 + let valid = /[a-zA-Z0-9 ]/g.test( event.key ); + // If the key is not valid, prevent the key from being input into the box if ( !valid ) { event.preventDefault(); } } +// Sets the src of the in-game help element, when true it loads the manual, when false it blanks the element function loadHelp( state ) { if ( state ) { @@ -575,7 +665,10 @@ function loadHelp( state ) /*------------------------------------------------------------------------------------ - UI scaling and positioning + UI scaling and positioning + + This whole bit could most likely be streamlined and made more efficient, it + works for now though. Redo it at a later date. ------------------------------------------------------------------------------------*/ var remoteScale = 1.0; var remoteMoving = false; @@ -794,7 +887,7 @@ function clamp( num, min, max ) ------------------------------------------------------------------------------------*/ // This runs when the JS file is loaded, loops through all of the remote buttons and assigns them an onclick function // elements.remote.find( "button" ).each( function( i, obj ) { -$( "body" ).find( "button" ).each( function( i, obj ) { +$( "body" ).find( "button, div" ).each( function( i, obj ) { if ( $( this ).attr( "data-nuitype" ) ) { $( this ).click( function() { let type = $( this ).data( "nuitype" ); diff --git a/sv_saving.lua b/sv_saving.lua index 4935d8f..f7cf773 100644 --- a/sv_saving.lua +++ b/sv_saving.lua @@ -21,117 +21,130 @@ DATASAVE.idType = "license" -- Saves the data for the given player into the saves folder within the resource function DATASAVE:SavePlayerData( src, data ) - -- Get the player's identifier - local id = self:GetIdentifier( src ) + -- Get the player's identifier + local id = self:GetIdentifier( src ) - -- Save the JSON file into the saves folder - SaveResourceFile( GetCurrentResourceName(), self.dir .. "/" .. id .. ".json", json.encode( data ), -1 ) - - -- Print out a message in the console to say the player's UI data has been saved - self:Print( "Saved UI data for " .. GetPlayerName( src ) .. " (ID: " .. src .. ")" ) + -- Save the JSON file into the saves folder + SaveResourceFile( GetCurrentResourceName(), self.dir .. "/" .. id .. ".json", json.encode( data ), -1 ) + + -- Print out a message in the console to say the player's UI data has been saved + self:Print( "Saved UI data for " .. GetPlayerName( src ) .. " (ID: " .. src .. ")" ) end -- Attempts to retrieve the UI data for the given player function DATASAVE:GetPlayerData( src ) - -- Get the player's identifier - local id = self:GetIdentifier( src ) - - -- Try to grab the raw data from the player's JSON file - local rawData = LoadResourceFile( GetCurrentResourceName(), self.dir .. "/" .. id .. ".json" ) + -- Get the player's identifier + local id = self:GetIdentifier( src ) + + -- Try to grab the raw data from the player's JSON file + local rawData = LoadResourceFile( GetCurrentResourceName(), self.dir .. "/" .. id .. ".json" ) - -- In the event there is no file for the player, return nil - if ( rawData == nil ) then - return nil - end + -- In the event there is no file for the player, return nil + if ( rawData == nil ) then + return nil + end - -- Decode the JSON data into a Lua table - local data = json.decode( rawData ) + -- Decode the JSON data into a Lua table + local data = json.decode( rawData ) - -- Return the data - return data + -- Return the data + return data end -- Checks that the given data is valid, helps to stop modified data from being sent through the save system function DATASAVE:CheckDataIsValid( data ) - -- First we check to make sure the data being passed is actually a table - if ( type( data ) ~= "table" ) then return false end + -- First we check to make sure the data being passed is actually a table + if ( type( data ) ~= "table" ) then return false end - -- Then we check to make sure that the data has only 3 elements, "remote", "radar", "reader" and "safezone" - local c = 0 - for _ in pairs( data ) do c = c + 1 end + -- Then we check to make sure that the data has only 3 elements, "remote", "radar", "reader" and "safezone" + local c = 0 + for _ in pairs( data ) do c = c + 1 end - -- If there isn't 4 elements, then the data isn't valid - if ( c ~= 4 ) then return false end + -- If there isn't 4 elements, then the data isn't valid + if ( c ~= 4 ) then return false end - return true + return true end -- Gets the identifier for the given player based on the identifier type specified at the top function DATASAVE:GetIdentifier( src ) - -- Get the number of identifiers the player has - local max = GetNumPlayerIdentifiers( src ) + -- Get the number of identifiers the player has + local max = GetNumPlayerIdentifiers( src ) - -- Iterate through the identifier numerical range - for i = 0, max do - -- Get the current identifier - local id = GetPlayerIdentifier( src, i ) + -- Iterate through the identifier numerical range + for i = 0, max do + -- Get the current identifier + local id = GetPlayerIdentifier( src, i ) - -- In the event the identifier is nil, report it to the server console and return nil - if ( id == nil ) then - self:Print( "^1It appears there was an error trying to find the specified ID (" .. self.idType .. ") for player " .. GetPlayerName( source ) ) - return nil - end - - -- - if ( string.find( id, self.idType, 1 ) ) then - local split = self:SplitString( id, ":" ) - return split[2] - end - end + -- In the event the identifier is nil, report it to the server console and return nil + if ( id == nil ) then + self:Print( "^1It appears there was an error trying to find the specified ID (" .. self.idType .. ") for player " .. GetPlayerName( source ) ) + return nil + end + + -- If we find the identifier type set in DATASAVE.idType, then we have the identifier + if ( string.find( id, self.idType, 1 ) ) then + -- Split the identifier so we just get the actual identifier + local split = self:SplitString( id, ":" ) - return nil + -- Return the identifier + return split[2] + end + end + + -- In the event we get nothing, return nil + return nil end +-- Your typical split string function! function DATASAVE:SplitString( inputstr, sep ) - if ( sep == nil ) then - sep = "%s" - end + if ( sep == nil ) then + sep = "%s" + end - local t = {} - local i = 1 - - for str in string.gmatch( inputstr, "([^" .. sep .. "]+)" ) do - t[i] = str - i = i + 1 - end + local t = {} + local i = 1 + + for str in string.gmatch( inputstr, "([^" .. sep .. "]+)" ) do + t[i] = str + i = i + 1 + end - return t + return t end +-- Prints the given message with the resource name attached function DATASAVE:Print( msg ) - print( "^3[wk_wars2x] ^0" .. msg .. "^0" ) + print( "^3[wk_wars2x] ^0" .. msg .. "^0" ) end +-- Serverside event for saving a player's UI data RegisterServerEvent( "wk:saveUiData" ) AddEventHandler( "wk:saveUiData", function( data ) - -- Check to make sure that the data being sent by the client is valid - local valid = DATASAVE:CheckDataIsValid( data ) + -- Check to make sure that the data being sent by the client is valid + local valid = DATASAVE:CheckDataIsValid( data ) - -- Only proceed if the data is actually valid - if ( valid ) then - DATASAVE:SavePlayerData( source, data ) - else - DATASAVE:Print( "^1Save data being sent from " .. GetPlayerName( source ) .. " (ID: " .. source .. ") is not valid, either something went wrong, or the player has modified the data being sent." ) - end + -- Only proceed if the data is actually valid + if ( valid ) then + DATASAVE:SavePlayerData( source, data ) + else + -- Print a message to the console saying the data sent by the client is not valid, this should rarely occur but it could be because + -- the client has modified the data being sent (mods/injections) + DATASAVE:Print( "^1Save data being sent from " .. GetPlayerName( source ) .. " (ID: " .. source .. ") is not valid, either something went wrong, or the player has modified the data being sent." ) + end end ) +-- Serverside event for when the player loads in and the system needs to get their saved UI data RegisterServerEvent( "wk:getUiData" ) AddEventHandler( "wk:getUiData", function() - local data = DATASAVE:GetPlayerData( source ) + -- Try and get the player's data + local data = DATASAVE:GetPlayerData( source ) - if ( data ) then - TriggerClientEvent( "wk:loadUiData", source, data ) - else - DATASAVE:Print( "Player " .. GetPlayerName( source ) .. " (ID: " .. source .. ") doesn't have a UI settings file." ) - end + -- Send the client the data if we get any + if ( data ) then + TriggerClientEvent( "wk:loadUiData", source, data ) + else + -- When player's first use the radar, they won't have saved UI data + DATASAVE:Print( "Player " .. GetPlayerName( source ) .. " (ID: " .. source .. ") doesn't have a UI settings file." ) + end end ) \ No newline at end of file diff --git a/sv_version_check.lua b/sv_version_check.lua index 5bec3ce..399f4f0 100644 --- a/sv_version_check.lua +++ b/sv_version_check.lua @@ -5,6 +5,7 @@ -----------------------------------------------------------------------]]-- +-- Branding! local label = [[ // @@ -13,25 +14,38 @@ local label = || \ \ /\ / / __ __ _ _| |_| |__ / \ | |__) | (___ ) |\ V / || \ \/ \/ / '__/ _` | | __| '_ \ / /\ \ | _ / \___ \ / / > < || \ /\ /| | | (_| | | |_| | | | / ____ \| | \ \ ____) | / /_ / . \ - || \/ \/ |_| \__,_|_|\__|_| |_| /_/ \_\_| \_\_____/ |____/_/ \_\]] + || \/ \/ |_| \__,_|_|\__|_| |_| /_/ \_\_| \_\_____/ |____/_/ \_\ + ||]] +-- Returns the current version set in fxmanifest.lua function GetCurrentVersion() return GetResourceMetadata( GetCurrentResourceName(), "version" ) end +-- Grabs the latest version number from the web GitHub PerformHttpRequest( "https://wolfknight98.github.io/wk_wars2x_web/version.txt", function( err, text, headers ) - Citizen.Wait( 2000 ) + -- Wait to reduce spam + Citizen.Wait( 2000 ) - print( label ) + -- Print the branding! + print( label ) + -- Get the current resource version local curVer = GetCurrentVersion() - print( " ||\n || Current version: " .. curVer ) - print( " || Latest version: " .. text .."\n ||" ) - - if ( text ~= curVer ) then - print( " || ^1Your Wraith ARS 2X version is outdated, visit the FiveM forum post to get the latest version.\n^0 \\\\\n" ) - else - print( " || ^2Wraith ARS 2X is up to date!\n^0 ||\n \\\\\n" ) - end + if ( text ~= nil ) then + -- Print out the current and latest version + print( " || Current version: " .. curVer ) + print( " || Latest version: " .. text .."\n ||" ) + + -- If the versions are different, print it out + if ( text ~= curVer ) then + print( " || ^1Your Wraith ARS 2X version is outdated, visit the FiveM forum post to get the latest version.\n^0 \\\\\n" ) + else + print( " || ^2Wraith ARS 2X is up to date!\n^0 ||\n \\\\\n" ) + end + else + -- In case the version can not be requested, print out an error message + print( " || ^1There was an error getting the latest version information, if the issue persists contact WolfKnight#8586 on Discord.\n^0 ||\n \\\\\n" ) + end end ) \ No newline at end of file