Advertisement
Kenix157

Untitled

Sep 23rd, 2016
441
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 15.63 KB | None | 0 0
  1. --
  2. -- Indicators script v1.1
  3. -- By Alberto "ryden" Alonso.
  4. -- Coded for Valhalla MTA Roleplay server.
  5. -- (Because Fenix can't code).
  6. --
  7. -- Licensed under the BSD license conditions.
  8. --
  9. -- Uses client-side synced element data to sync the
  10. -- indicators state. The element data names can be
  11. -- either 'i:left' and 'i:right', being possible to
  12. -- have both switched on. A true value on an element
  13. -- data means that the indicator is activated. False
  14. -- or nil means deactivated.
  15. --
  16. -- * Version 1.1:
  17. --   - Added tick sounds
  18. --   - Added auto switching off when taking a turn
  19. --
  20.  
  21. --[[ Configuration ]]--
  22. local INDICATOR_SIZE = 0.32                     -- Size for the corona markers
  23. local INDICATOR_COLOR = { 255, 100, 10, 255 }   -- Color in R G B A format
  24. local INDICATOR_FADE_MS = 150                   -- Miliseconds to fade out the indicators
  25. local INDICATOR_SWITCH_TIMES = { 300, 400 }     -- In miliseconds. First is time to switch them off, second to switch them on.
  26. local INDICATOR_AUTOSWITCH_OFF_THRESHOLD = 62   -- A value in degrees ranging (0, 90) preferibly far from the limits.
  27.  
  28.  
  29. --[[ Some globals to this context ]]--
  30. local root = getRootElement()
  31. local localPlayer = getLocalPlayer()
  32. local vehiclesWithIndicator = {}
  33.  
  34.  
  35. -- Precalculate some stuff
  36. INDICATOR_AUTOSWITCH_OFF_THRESHOLD = INDICATOR_AUTOSWITCH_OFF_THRESHOLD / 90
  37.  
  38.  
  39. --[[
  40. * vectorLength
  41. Gets the length of a vector.
  42. --]]
  43. local function vectorLength ( vector )
  44.     return math.sqrt ( vector[1]*vector[1] + vector[2]*vector[2] + vector[3]*vector[3] )
  45. end
  46.  
  47. --[[
  48. * normalizeVector
  49. Normalizes a vector, when possible, and returns the normalized vector plus the length.
  50. --]]
  51. local function normalizeVector ( vector )
  52.     local length = vectorLength ( vector )
  53.     if length > 0 then
  54.         local normalizedVector = {}
  55.         normalizedVector[1] = vector[1] / length
  56.         normalizedVector[2] = vector[2] / length
  57.         normalizedVector[3] = vector[3] / length
  58.         return normalizedVector, length
  59.     else
  60.         return nil, length
  61.     end
  62. end
  63.  
  64. --[[
  65. * crossProduct
  66. Calculates the cross product of two vectors.
  67. --]]
  68. local function crossProduct ( v, w )
  69.     local result = {}
  70.     result[1] = v[2]*w[3] - v[3]*w[2]
  71.     result[2] = w[1]*v[3] - w[3]*v[1]
  72.     result[3] = v[1]*w[2] - v[2]*w[1]
  73.     return result
  74. end
  75.  
  76. --[[
  77. * getFakeVelocity
  78. Gets a fake unitary velocity for a vehicle calculated using the current vehicle angle.
  79. --]]
  80. local function getFakeVelocity ( vehicle )
  81.     -- Get the angle around the Z axis
  82.     local _, _, angle = getElementRotation ( vehicle )
  83.     local velocity = { 0, 0, 0 }
  84.     velocity[1] = -math.sin ( angle )
  85.     velocity[2] = math.cos ( angle )
  86.     return velocity
  87. end
  88.  
  89. --[[
  90. * createIndicator
  91. Creates a marker for an indicator.
  92. --]]
  93. local function createIndicator ()
  94.     local x, y, z = getElementPosition(localPlayer)
  95.     local indicator = createMarker (    x, y, z+4, 'corona',
  96.                                         INDICATOR_SIZE,
  97.                                         INDICATOR_COLOR[1],
  98.                                         INDICATOR_COLOR[2],
  99.                                         INDICATOR_COLOR[3],
  100.                                         0
  101.                                     )
  102.     setElementStreamable ( indicator, false )
  103.     return indicator
  104. end
  105.  
  106. --[[
  107. * createIndicatorState
  108. Creates a table with information about the indicators state.
  109. --]]
  110. local function createIndicatorState ( vehicle, indicatorLeft, indicatorRight )
  111.     local t = { vehicle       = vehicle,        -- The vehicle that this state refers to
  112.                 left          = indicatorLeft,  -- The state of the left indicator
  113.                 right         = indicatorRight, -- The state of the right indicator
  114.                 coronaLeft    = nil,            -- The corona elements for the left indicator
  115.                 coronaRight   = nil,            -- The corona elements for the right indicator
  116.                 nextChange    = 0,              -- The time for the next change of the indicators
  117.                 timeElapsed   = 0,              -- Elapsed time since the last change
  118.                 currentState  = false,          -- If set to true, the coronas are activated.
  119.                 activationDir = nil,            -- Direction that the vehicle was following when the indicator got activated, for auto shut down.
  120.               }
  121.     return t
  122. end
  123.  
  124. --[[
  125. * updateIndicatorState
  126. Updates the indicator state (i.e. creates/destroys the coronas).
  127. --]]
  128. local function updateIndicatorState ( state )
  129.     if not state then return end
  130.  
  131.     -- Store the number of indicators activated
  132.     local numberOfIndicators = 0
  133.    
  134.     -- Get the vehicle bounding box
  135.     local xmin, ymin, zmin, xmax, ymax, zmax = getElementBoundingBox ( state.vehicle )
  136.  
  137.     -- Transform the bounding box positions to fit properly the vehicle
  138.     xmin = xmin + 0.2
  139.     xmax = xmax - 0.2
  140.     ymin = ymin + 0.2
  141.     ymax = ymax - 0.2
  142.     zmin = zmin + 0.6
  143.  
  144.     -- Check the left indicator
  145.     if state.left then
  146.         if not state.coronaLeft then
  147.             state.coronaLeft = { createIndicator (), createIndicator () }
  148.             attachElements ( state.coronaLeft[1], state.vehicle, xmin,  ymax, zmin )
  149.             attachElements ( state.coronaLeft[2], state.vehicle, xmin, -ymax, zmin )
  150.         end
  151.         numberOfIndicators = numberOfIndicators + 1
  152.     elseif state.coronaLeft then
  153.         destroyElement ( state.coronaLeft[1] )
  154.         destroyElement ( state.coronaLeft[2] )
  155.         state.coronaLeft = nil
  156.     end
  157.    
  158.     -- Check the right indicator
  159.     if state.right then
  160.         if not state.coronaRight then
  161.             state.coronaRight = { createIndicator (), createIndicator () }
  162.             attachElements ( state.coronaRight[1], state.vehicle, -xmin,  ymax, zmin )
  163.             attachElements ( state.coronaRight[2], state.vehicle, -xmin, -ymax, zmin )
  164.         end
  165.         numberOfIndicators = numberOfIndicators + 1
  166.     elseif state.coronaRight then
  167.         destroyElement ( state.coronaRight[1] )
  168.         destroyElement ( state.coronaRight[2] )
  169.         state.coronaRight = nil
  170.     end
  171.    
  172.     -- Check if this is the car that you are driving and that there is one and only one indicator
  173.     -- to enable auto switching off
  174.     if numberOfIndicators == 1 and getVehicleOccupant ( state.vehicle, 0 ) == localPlayer then
  175.         -- Store the current velocity, normalized, to check when will we have to switch it off.
  176.         state.activationDir = normalizeVector ( { getElementVelocity ( state.vehicle ) } )
  177.         if not state.activationDir then
  178.             -- The vehicle is stopped, get a fake velocity from the angle.
  179.             state.activationDir = getFakeVelocity ( state.vehicle )
  180.         end
  181.     else
  182.         state.activationDir = nil
  183.     end
  184. end
  185.  
  186. --[[
  187. * destroyIndicatorState
  188. Destroys an indicator state, deleting all its resources.
  189. --]]
  190. local function destroyIndicatorState ( state )
  191.     if not state then return end
  192.    
  193.     -- Destroy the left coronas
  194.     if state.coronaLeft then
  195.         destroyElement ( state.coronaLeft[1] )
  196.         destroyElement ( state.coronaLeft[2] )
  197.         state.coronaLeft = nil
  198.     end
  199.    
  200.     -- Destroy the right coronas
  201.     if state.coronaRight then
  202.         destroyElement ( state.coronaRight[1] )
  203.         destroyElement ( state.coronaRight[2] )
  204.         state.coronaRight = nil
  205.     end
  206.    
  207.     -- If I am the driver, reset the element data.
  208.     if getVehicleOccupant ( state.vehicle ) == localPlayer then
  209.         setElementData ( state.vehicle, 'i:left', false, true )
  210.         setElementData ( state.vehicle, 'i:right', false, true )
  211.     end
  212. end
  213.  
  214. --[[
  215. * performIndicatorChecks
  216. Checks how the indicators state should be: created, updated or destroyed.
  217. --]]
  218. local function performIndicatorChecks ( vehicle )
  219.     -- Get the current indicator states
  220.     local indicatorLeft = getElementData(vehicle, 'i:left')
  221.     local indicatorRight = getElementData(vehicle, 'i:right')
  222.  
  223.     -- Check if we at least have one indicator running
  224.     local anyIndicator = indicatorLeft or indicatorRight
  225.    
  226.     -- Grab the current indicators state in the flashing period.
  227.     local currentState = vehiclesWithIndicator [ vehicle ]
  228.  
  229.     -- If there's any indicator running, push it to the list of vehicles to draw the indicator.
  230.     -- Else, remove it from the list.
  231.     if anyIndicator then
  232.         -- Check if there is already a state for this vehicle
  233.         if currentState then
  234.             -- Update the state
  235.             currentState.left = indicatorLeft
  236.             currentState.right = indicatorRight
  237.         else
  238.             -- Create a new state
  239.             currentState = createIndicatorState ( vehicle, indicatorLeft, indicatorRight )
  240.             vehiclesWithIndicator [ vehicle ] = currentState
  241.         end
  242.         updateIndicatorState ( currentState )
  243.     elseif currentState then
  244.         -- Destroy the current state
  245.         destroyIndicatorState ( currentState )
  246.         vehiclesWithIndicator [ vehicle ] = nil
  247.     end
  248. end
  249.  
  250. --[[
  251. * setIndicatorsAlpha
  252. Sets all the active indicators alpha.
  253. --]]
  254. local function setIndicatorsAlpha ( state, alpha )
  255.     if state.coronaLeft then
  256.         setMarkerColor ( state.coronaLeft[1],   INDICATOR_COLOR[1],
  257.                                                 INDICATOR_COLOR[2],
  258.                                                 INDICATOR_COLOR[3],
  259.                                                 alpha )
  260.         setMarkerColor ( state.coronaLeft[2],   INDICATOR_COLOR[1],
  261.                                                 INDICATOR_COLOR[2],
  262.                                                 INDICATOR_COLOR[3],
  263.                                                 alpha )
  264.     end
  265.     if state.coronaRight then
  266.         setMarkerColor ( state.coronaRight[1],  INDICATOR_COLOR[1],
  267.                                                 INDICATOR_COLOR[2],
  268.                                                 INDICATOR_COLOR[3],
  269.                                                 alpha )
  270.         setMarkerColor ( state.coronaRight[2],  INDICATOR_COLOR[1],
  271.                                                 INDICATOR_COLOR[2],
  272.                                                 INDICATOR_COLOR[3],
  273.                                                 alpha )
  274.     end
  275. end
  276.  
  277. --[[
  278. * processIndicators
  279. Processes the indicators switching, and solves some MTA bugs.
  280. --]]
  281. local function processIndicators ( state )
  282.     -- Check first if the vehicle is blown up.
  283.     if getElementHealth ( state.vehicle ) == 0 then
  284.         -- Destroy the state.
  285.         destroyIndicatorState ( state )
  286.         vehiclesWithIndicator [ state.vehicle ] = nil
  287.         return
  288.     end
  289.    
  290.     -- Check if we must automatically deactivate the indicators.
  291.     if state.activationDir then
  292.         -- Get the current velocity and normalize it
  293.         local currentVelocity = normalizeVector ( { getElementVelocity ( state.vehicle ) } )
  294.        
  295.         -- If the vehicle is stopped, calculate a fake velocity from the angle.
  296.         if not currentVelocity then
  297.             currentVelocity = getFakeVelocity ( state.vehicle )
  298.         end
  299.        
  300.         -- Calculate the cross product between the velocities to get the angle and direction of any turn.
  301.         local cross = crossProduct ( state.activationDir, currentVelocity )
  302.            
  303.         -- Get the length of the resulting vector to calculate the "amount" of direction change [0..1].
  304.         local length = vectorLength ( cross )
  305.            
  306.         -- If the turn is over the threshold, deactivate the indicators
  307.         if length > INDICATOR_AUTOSWITCH_OFF_THRESHOLD then
  308.             -- Destroy the state
  309.             destroyIndicatorState ( state )
  310.             vehiclesWithIndicator [ state.vehicle ] = nil
  311.             return
  312.         end
  313.     end
  314.  
  315.     -- Check if we must switch the state
  316.     if state.nextChange <= state.timeElapsed then
  317.         -- Turn to switched on indicators, in both cases. When turning on,
  318.         -- it goes straight to the full alpha mode. When turning off, it
  319.         -- fades out from full alpha to full transparent.
  320.         setIndicatorsAlpha ( state, INDICATOR_COLOR[4] )
  321.        
  322.         -- Switch the state
  323.         state.currentState = not state.currentState
  324.    
  325.         -- Get the vehicle that we are in
  326.         local playerVehicle = getPedOccupiedVehicle ( localPlayer )
  327.        
  328.         -- Restart the timers and play a sound if we are in that vehicle
  329.         state.timeElapsed = 0
  330.         if state.currentState then
  331.             state.nextChange = INDICATOR_SWITCH_TIMES[1]
  332.             --if playerVehicle == state.vehicle then playSoundFrontEnd ( 37 ) end
  333.         else
  334.             state.nextChange = INDICATOR_SWITCH_TIMES[2]
  335.             --if playerVehicle == state.vehicle then playSoundFrontEnd ( 38 ) end
  336.         end
  337.        
  338.  
  339.     -- Check if we are turning them off
  340.     elseif state.currentState == false then
  341.         -- If the time elapsed is bigger than the time to fade out, then
  342.         -- just set the alpha to zero. Else, set it to the current alpha
  343.         -- value.
  344.         if state.timeElapsed >= INDICATOR_FADE_MS then
  345.             setIndicatorsAlpha ( state, 0 )
  346.         else
  347.             setIndicatorsAlpha ( state, (1 - (state.timeElapsed / INDICATOR_FADE_MS)) * INDICATOR_COLOR[4] )
  348.         end
  349.     end
  350. end
  351.  
  352. --[[
  353. * onClientElementDataChange
  354. Detects when the indicator state of a vehicle changes.
  355. --]]
  356. addEventHandler('onClientElementDataChange', root, function ( dataName, oldValue )
  357.     -- Check that the source is a vehicle and that the data name is what we are looking for.
  358.     if getElementType(source) == 'vehicle' and ( dataName == 'i:left' or dataName == 'i:right' ) then
  359.         -- If the vehicle is not streamed in, don't do anything.
  360.         if isElementStreamedIn(source) then
  361.             -- Perform the indicator checks for the new indicator states.
  362.             performIndicatorChecks ( source )
  363.         end
  364.     end
  365. end)
  366.  
  367. --[[
  368. * onClientElementStreamIn
  369. Detects when a vehicle streams in, to check if we must draw the indicators.
  370. --]]
  371. addEventHandler('onClientElementStreamIn', root, function ()
  372.     if getElementType(source) == 'vehicle' then
  373.         -- Perform the indicator checks for the just streamed in vehicle.
  374.         performIndicatorChecks ( source )
  375.     end
  376. end)
  377.  
  378. --[[
  379. * onClientElementStreamOut
  380. Detects when a vehicle streams out, to destroy its state.
  381. --]]
  382. addEventHandler('onClientElementStreamOut', root, function ()
  383.     if getElementType(source) == 'vehicle' then
  384.         -- Grab the current indicators state
  385.         local currentState = vehiclesWithIndicator [ source ]
  386.        
  387.         -- If it has a state, remove it.
  388.         if currentState then
  389.             destroyIndicatorState ( currentState )
  390.             vehiclesWithIndicator [ source ] = nil
  391.         end
  392.     end
  393. end)
  394.  
  395. --[[
  396. * indicator_left and indicator_right commands
  397. Changes the state of the indicators for the current vehicle.
  398. --]]
  399. local function switchIndicatorState ( indicator )
  400.     -- First check that we are in a vehicle.
  401.     local v = getPedOccupiedVehicle(localPlayer)
  402.     if v then
  403.         -- check for the correct vehicle type
  404.         if getVehicleType(v) == "Automobile" or getVehicleType(v) == "Bike" or getVehicleType(v) == "Quad" then
  405.             -- Check that we are the vehicle driver
  406.             if getVehicleOccupant(v, 0) == localPlayer then
  407.                 -- Switch the indicator state
  408.                 local dataName = 'i:' .. indicator
  409.                 local currentValue = getElementData(v, dataName) or false
  410.                 -- UNSAFE
  411.                 setElementData(v, dataName, not currentValue, true)
  412.             end
  413.         end
  414.     end
  415. end
  416. addCommandHandler('indicator_left', function () switchIndicatorState('left') end, false)
  417. addCommandHandler('indicator_right', function () switchIndicatorState('right') end, false)
  418.  
  419. --[[
  420. * onClientPreRender
  421. Calls processIndicators for every vehicle with the indicators activated.
  422. --]]
  423. addEventHandler('onClientPreRender', root, function ( timeSlice )
  424.     -- Process every vehicle with indicators
  425.     for vehicle, state in pairs(vehiclesWithIndicator) do
  426.         state.timeElapsed = state.timeElapsed + timeSlice
  427.         processIndicators ( state, state.lastChange )
  428.     end
  429. end)
  430.  
  431. function handleKeyBind( keyPressed, keyState )
  432.     if (keyPressed == "[") then
  433.         switchIndicatorState('left')
  434.     elseif (keyPressed == "]") then
  435.         switchIndicatorState('right')
  436.     end
  437. end
  438. --[[
  439. * onClientResourceStart
  440. Starts everything up.
  441. --]]
  442. addEventHandler('onClientResourceStart', getResourceRootElement(getThisResource()), function ()
  443.     -- Check all streamed in vehicles that have any indicator activated and create a state for them.
  444.     local vehicles = getElementsByType ( 'vehicle' )
  445.     for k, vehicle in ipairs(vehicles) do
  446.         if isElementStreamedIn ( vehicle ) then
  447.             local indicatorLeft = getElementData ( vehicle, 'i:left' )
  448.             local indicatorRight = getElementData ( vehicle, 'i:right' )
  449.             if indicatorLeft or indicatorRight then
  450.                 performIndicatorChecks ( vehicle )
  451.             end
  452.         end
  453.     end
  454.      
  455.     bindKey ( "[", "down", handleKeyBind )
  456.     bindKey ( "]", "down", handleKeyBind )
  457. end, false)
  458.  
  459. --[[
  460. * onClientVehicleRespawn
  461. Restore the state for vehicles respawning.
  462. --]]
  463. addEventHandler('onClientVehicleRespawn', root, function ()
  464.     if isElementStreamedIn ( source ) then
  465.         performIndicatorChecks ( source )
  466.     end
  467. end)
  468.  
  469. --[[
  470. * onClientElementDestroy
  471. Destroys the state for a vehicle when it's deleted.
  472. --]]
  473. addEventHandler('onClientElementDestroy', root, function ()
  474.     if getElementType ( source ) == 'vehicle' then
  475.         local currentState = vehiclesWithIndicator [ source ]
  476.         if currentState then
  477.             -- Destroy the state
  478.             destroyIndicatorState ( currentState )
  479.             vehiclesWithIndicator [ source ] = nil
  480.         end
  481.     end
  482. end)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement