Up to six siren tones (can be server sided as well)
Pattern Sync between vehicles (enabled with config)
Speed based patterns (enabled with config) (Faster you go the faster your lights flash)
Highly optimized (relative to other popular ELS scripts) (I’m activley working on more optimizations)
New Environmental Light System (Can be completely edited in the configuration) (Only draws one light per car, and colors the light by choosing the most prominent color out of the currently activated lights.)
Repairing the vehicle or spawning a car will not have extras pop up everywhere.
Responsive & Modern UI
Configuration
patternSpeed
Time in milliseconds between each pattern change. Lower values result in faster pattern changes.
speedAdjustFactor
Determines how much the vehicle's speed affects the pattern speed. Set to 0.0 to disable speed adjustment.
sirenParkKill
Specifies whether the sirens should be disabled when the driver exits the vehicle. Set to true to enable this feature.
hornInterrupt
Specifies whether the use of the horn should interrupt the siren. Set to true to enable this feature.
defaultKeybinds
Default keybinds for various actions. Replace the values with the desired keybinds. Set to "NONE" to disable a specific action.
vehicles
List of vehicles enabled for ELS. Each vehicle in the array should have the following properties:
name: The model name of the vehicle.
patternSet: The pattern set to use for this vehicle.
stages: Configuration for each stage of lighting. The stages object should include the following properties:
stageOneExtras: An array of extras used for stage one lighting.
stageTwoExtras: An array of extras used for stage two lighting.
stageThreeExtras: An array of extras used for stage three lighting.
takeDownExtras: An array of extras used for take down lights.
patternSync
Determines whether the patterns should be synchronized across all vehicles.
patterns
List of available patterns. Each pattern includes the following properties:
name: The name of the pattern set.
uiExtras: Extra numbers used for UI graphics.
Extras for each stage, numbered from 1 to 14. Each extra is represented by a string of 0s and 1s indicating its state.
changeStage
Defines how the stage lighting is changed based on the stage. Modify this function according to your requirements.
environmentalLightFunction
The function responsible for environmental lighting. Leave as is unless you're a developer intending to modify environmental lights' behavior.
Important Notes
Ensure that the provided Lua config aligns with your specific needs.
The values in the configuration can be adjusted as per your requirements.
config.lua
Config = {}--[[ Tommy's ELS Configuration (Developed by Tommy141x)
______ _ ________ _____
/_ __/___ ____ ___ ____ ___ __ _( )_____ / ____/ / / ___/
/ / / __ \/ __ `__ \/ __ `__ \/ / / /// ___/ / __/ / / \__ \
/ / / /_/ / / / / / / / / / / / /_/ / (__ ) / /___/ /______/ /
/_/ \____/_/ /_/ /_/_/ /_/ /_/\__, / /____/ /_____/_____/____/
/____/
[[-- Configuration --]]
-- Time in MS between each pattern change. (lower means faster)
Config.patternSpeed = 60
-- How much should vehicle speed affect the pattern speed (Higher means more)? Set to 0.0 to disable.
Config.speedAdjustFactor = 0.5
-- Should the sirens be disabled when the driver exits the vehicle?
Config.sirenParkKill = true
-- Should use of the horn interrupt the siren?
Config.hornInterrupt = false
-- Default Keybinds (https://docs.fivem.net/docs/game-references/input-mapper-parameter-ids/keyboard/) (Set to "NONE" to disable)
Config.defaultKeybinds = {
changeStage = "Q",
sirenTone1 = "1",
sirenTone2 = "2",
sirenTone3 = "3",
sirenTone4 = "4",
sirenTone5 = "5",
sirenTone6 = "6",
advLights = "7",
wrnLights = "8",
primLights = "9",
tkdLights = "0",
patternDown = "MINUS",
patternUp = "EQUALS",
}
-- List of vehicles that are ELS enabled (should correspond to the names of the vcf files in the vcf folder).
-- Only extras with IsElsControlled set to true in the vcf file will be controlled by this script.
Config.vehicles = {
[1] = {
name = "carOne", -- Model Name of the vehicle
patternSet = "patternSetOne", -- Pattern Set to use
stages = { -- Configure what extras are used for each stage, and what extras are used for the take down lights.
stageOneExtras = { 7, 8, 9 },
stageTwoExtras = { 5, 6 },
stageThreeExtras = { 1, 2, 3, 4 },
takeDownExtras = { 11 },
}
},
[2] = {
name = "carTwo", -- Model Name of the vehicle
patternSet = "patternSetOne", -- Pattern Set to use
stages = { -- Configure what extras are used for each stage, and what extras are used for the take down lights.
stageOneExtras = { 7, 8, 9 },
stageTwoExtras = { 5, 6 },
stageThreeExtras = { 1, 2, 3, 4 },
takeDownExtras = { 11 },
}
},
}
-- Sync patterns across all vehicles.
Config.patternSync = true
-- List of patterns that can be used. (Only extras with IsElsControlled in the VCF file will be used for patterns)
-- You can edit the colors of the UI in the html file.
Config.patterns = {
[1] = { -- Pattern Set (You can add as many of these as you want and reference them in the vehicles list)
name = "patternSetOne", -- Pattern Set Name
[1] = { -- Pattern
name = "NORMAL", -- Pattern Name
uiExtras = { prim1 = 1, prim2 = 2, wrn1 = 5, wrn2 = 6, adv1 = 7, adv2 = 8, adv3 = 9 }, -- Extra Numbers Used for UI Graphics
[1] = "000000111111000000111111000000111111", -- Extra 1
[2] = "111000111000111000111000111000111000", -- Extra 2
[3] = "000111000111000111000111000111000111", -- Extra 3
[4] = "111111000000111111000000111111000000", -- Extra 4
[5] = "111000111000111000111000111000111000", -- Extra 5
[6] = "000111000111000111000111000111000111", -- Extra 6
[7] = "100000110001100000000001100011000001", -- Extra 7
[8] = "011100000100000111111000001000001110", -- Extra 8
[9] = "100000110001100000000001100011000001", -- Extra 9
[10] = "111111000000111111000000111111000000", -- Extra 10
[11] = "111111000000111111000000111111000000", -- Extra 11
[12] = "111111000000111111000000111111000000", -- Extra 12
[13] = "111111000000111111000000111111000000", -- Extra 13
[14] = "111111000000111111000000111111000000", -- Extra 14
},
[2] = { -- Pattern
name = "LEFT", -- Pattern Name
uiExtras = { prim1 = 1, prim2 = 2, wrn1 = 5, wrn2 = 6, adv1 = 7, adv2 = 8, adv3 = 9 }, -- Extra Numbers Used for UI Graphics
[1] = "000000000111111", -- Extra 1
[2] = "000000111111000", -- Extra 2
[3] = "000111111000000", -- Extra 3
[4] = "111111000000000", -- Extra 4
[5] = "000001111100000", -- Extra 5
[6] = "111000111000111", -- Extra 6
[7] = "000000000111111", -- Extra 7
[8] = "000011111110000", -- Extra 8
[9] = "111111000000000", -- Extra 9
[10] = "101101001011010", -- Extra 10
[11] = "101101001011010", -- Extra 11
[12] = "101101001011010", -- Extra 12
[13] = "101101001011010", -- Extra 13
[14] = "101101001011010", -- Extra 14
},
[3] = { -- Pattern
name = "RIGHT", -- Pattern Name
uiExtras = { prim1 = 1, prim2 = 2, wrn1 = 5, wrn2 = 6, adv1 = 7, adv2 = 8, adv3 = 9 }, -- Extra Numbers Used for UI Graphics
[1] = "111111000000000", -- Extra 1
[2] = "000111111000000", -- Extra 2
[3] = "000000111111000", -- Extra 3
[4] = "000000000111111", -- Extra 4
[5] = "111000111000111", -- Extra 5
[6] = "000001111100000", -- Extra 6
[7] = "111111000000000", -- Extra 7
[8] = "000011111110000", -- Extra 8
[9] = "000000000111111", -- Extra 9
[10] = "101101001011010", -- Extra 10
[11] = "101101001011010", -- Extra 11
[12] = "101101001011010", -- Extra 12
[13] = "101101001011010", -- Extra 13
[14] = "101101001011010", -- Extra 14
},
[4] = { -- Pattern
name = "BURN", -- Pattern Name
uiExtras = { prim1 = 1, prim2 = 2, wrn1 = 5, wrn2 = 6, adv1 = 7, adv2 = 8, adv3 = 9 }, -- Extra Numbers Used for UI Graphics
[1] = "11111111111111111", -- Extra 1
[2] = "11111111111111111", -- Extra 2
[3] = "11111111111111111", -- Extra 3
[4] = "11111111111111111", -- Extra 4
[5] = "11111111111111111", -- Extra 5
[6] = "11111111111111111", -- Extra 6
[7] = "11111111111111111", -- Extra 7
[8] = "11111111111111111", -- Extra 8
[9] = "11111111111111111", -- Extra 9
[10] = "11111111111111111", -- Extra 10
[11] = "11111111111111111", -- Extra 11
[12] = "11111111111111111", -- Extra 12
[13] = "11111111111111111", -- Extra 13
[14] = "11111111111111111", -- Extra 14
},
},
}
-- Feel free to configure how the stage lighting is changed based on the stage
-- For example, in the way I currently have it below, stage one lighting is disabled in stages 2 and 3.
Config.changeStage = function(code, car)
if code == 1 then
car.stageOne = true
car.stageTwo = false
car.stageThree = false
elseif code == 2 then
car.stageOne = false
car.stageTwo = true
car.stageThree = false
elseif code == 3 then
car.stageOne = false
car.stageTwo = true
car.stageThree = true
else
car.stageOne = false
car.stageTwo = false
car.stageThree = false
end
end
-- The environmental light function, you don't need to edit this unless your a developer looking to change the way environmental lights work with this script.
Config.environmentalLightFunction = function(elsVehicle, netID)
if elsVehicle.stageOne or elsVehicle.stageTwo or elsVehicle.stageThree then
-- Loop through all vehicles
local toDraw = {}
local r = 0
local g = 0
local b = 0
for extra, color in pairs(elsVehicle.extraColors) do
-- Get all extras and colors from VCF File
if NetworkDoesNetworkIdExist(netID) and IsVehicleExtraTurnedOn(NetworkGetEntityFromNetworkId(netID), extra) then
table.insert(toDraw, color) -- If the extra is turned on then add the color to the table to draw
end
end
-- Loop through all colors to find the one with the most occurences.
-- I did this so that if you have 2 red and 1 blue, it will draw red.
-- I personally don't like drawing both red and blue at the same time because it looks funky.
-- Also only drawing one color / one light at a time is better for performance.
for k, v in pairs(toDraw) do
local redOccurences = 0
if v == "red" then
-- "red" is whats found in the VCF File
redOccurences = redOccurences + 1
end
local blueOccurences = 0
if v == "blue" then
-- "blue" is whats found in the VCF File
blueOccurences = blueOccurences + 1
end
local amberOccurences = 0
if v == "amber" then
-- "amber" is whats found in the VCF File
amberOccurences = amberOccurences + 1
end
local whiteOccurences = 0
if v == "white" then
-- "amber" is whats found in the VCF File
whiteOccurences = whiteOccurences + 1
end
-- If theres mostly red, draw red.
if redOccurences > blueOccurences and redOccurences > amberOccurences and redOccurences > whiteOccurences then
r = 255
g = 0
b = 0
-- If theres mostly blue, draw blue.
elseif blueOccurences > redOccurences and blueOccurences > amberOccurences and blueOccurences > whiteOccurences then
r = 0
g = 0
b = 255
-- If theres mostly amber, draw amber.
elseif amberOccurences > redOccurences and amberOccurences > blueOccurences and amberOccurences > whiteOccurences then
r = 255
g = 165
b = 0
-- Otherwise draw a random mix between red and blue.
elseif whiteOccurences > redOccurences and whiteOccurences > blueOccurences and whiteOccurences > amberOccurences then
r = 255
g = 255
b = 255
else
r = math.random(0, 255)
g = 0
b = math.random(0, 255)
end
end
-- Now that our rgb is picked out for this environmental light, let's draw it!
if NetworkDoesNetworkIdExist(netID) then
local coords = GetEntityCoords(NetworkGetEntityFromNetworkId(netID))
-- DrawLightWithRange(x, y, z, r, g, b, range, intensity)
DrawLightWithRange(coords.x, coords.y, coords.z, r, g, b, 15.0, 0.5) -- The 0.5 is the brightness of the environmental lights.
end
end
end
ELS In-Game UI
The ELS in-game UI allows for customization of the UI files without encryption. You have the freedom to make any desired changes. Additionally, the display of extras on the UI can be configured per pattern set.
Server Sided Sirens
Tommy's ELS provides an easy way to configure different sirens for various vehicles and enables server-sided sirens. An example VCF file configuration is provided below, utilizing server-sided sirens from Kwoks's free SAS script.
Resmon Usage: With 6 active vehicles in close proximity, achieving a resmon value of 0.62 is considered optimal. Ongoing optimizations and bug fixes are being implemented to further improve performance.
Known Bugs
Occasionally, after a few hours of roleplay, AI cars with ELS may toggle extras and environmental lighting unexpectedly, resulting in visually amusing situations.
Rarely, vehicles may have a ghost stock GTA siren while in Code 3 mode.
To-Do
Optimize RPC events for improved performance.
Add NetID checks for optimization and provide warnings in the console.
Tutorial Video: A tutorial video demonstrating the setup and configuration of Tommy's ELS is planned for future release.
Ox Inventory Mod (Optional)
If you wish to deactivate the hotbar shortcuts while in an emergency vehicle, modify the client.lua file on line 732.
Replace this:
if invOpen or IsNuiFocused() or not invHotkeys then return end
With this:
if invOpen or IsNuiFocused() or (GetVehicleClass(GetVehiclePedIsIn(GetPlayerPed(-1), false)) == 18) or not invHotkeys then return end
Known Issues w/ 3rd Party Chats
We use a new FiveM Native called RegisterKeyMapping, which is a more optimized way to detect key presses, this native calls a command when a key is pressed, and the way that it calls this command has caused some issues with outdated resources.
Unknown/Invalid Command ELS in Chat (ESX Solution)
For ESX users, go into es_extended/server/main.lua and find lines 324-331. Replace this:
AddEventHandler('chatMessage', function(playerId, author, message)
local xPlayer = ESX.GetPlayerFromId(playerId)
if message:sub(1, 1) == '/' and playerId > 0 then
CancelEvent()
local commandName = message:sub(1):gmatch("%w+")()
xPlayer.showNotification(TranslateCap('commanderror_invalidcommand', commandName))
end
end)
With this:
AddEventHandler('chatMessage', function(playerId, author, message)
local xPlayer = ESX.GetPlayerFromId(playerId)
if message:sub(1, 1) == '/' and (not string.lower(message):find("els", 1, true)) and playerId > 0 then
CancelEvent()
local commandName = message:sub(1):gmatch("%w+")()
xPlayer.showNotification(TranslateCap('commanderror_invalidcommand', commandName))
end
end)
Command Repeated in Chat (Standalone Solution)
For users seeing a /~els_ command in chat when using the keybinds, this means you do not have a chat script which hides chat messages that are commands. You can add this functionality with a script such as HideCMDs by FAXES, or you can implement it yourself by adding the below snippet to any server-side lua file:
AddEventHandler('chatMessage', function(Source, Name, Msg)
args = stringsplit(Msg, " ")
CancelEvent()
if string.find(args[1], "/") then
local cmd = args[1]
table.remove(args, 1)
else
TriggerClientEvent('chatMessage', -1, Name, { 255, 255, 255 }, Msg)
end
end)
function stringsplit(inputstr, sep)
if sep == nil then
sep = "%s"
end
local t={} ; i=1
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
t[i] = str
i = i + 1
end
return t
end
Please note: This documentation serves as a guide to understand and configure Tommy's ELS script effectively