Emergency Lights Siren Feature (for vehicles) - user-grinch/ModelExtras GitHub Wiki
The Emergency Lights Feature allows vehicles in the game to have customizable emergency, hazard, and steady burn lights. This documentation will guide you through the process of adapting vehicles to utilize this feature effectively.
Additionally, this update introduces the long-awaited support for rotating siren parts, which become active when the sirens are turned on—bringing a new level of realism and authenticity to emergency vehicles.
Every model with a siren must be configured with a JSONC manifest (JSON with Comments). Without a JSONC manifest, ModelExtras will not recognize the model as being adapted.
-
The siren manifest is an object where the keys represent different siren states, toggled using the
Rkey. Groups can be switched using keys1-9 -
State objects are traversed by key in lexicographic order; the first valid state in that order is used first. JSON object source order does not define the order.
-
There is no limit to the number of states you can define.
-
H or CapsLock: Turn on siren (and turn on from muted state).
-
L: Toggles muted siren on/off.
-
R: Switch siren state forwards (accompanied by an authentic cabin switch click sound).
-
Shift+R: Switch siren state backwards.
-
1 - 9: Directly switch to the corresponding siren state number (if
SirenStateNumbersis enabled). -
Ctrl + 1 - 9: Switch custom siren audio tone / sound effect on the fly.
-
Shift: Clear turn lights.
In order for a material to be recognized as a siren material:
🔴 Red: 1-255
🟢 Green always 255
🔵 Blue always 255
Texture: Name should start with siren. For example sirenlight128
In order for dummies to be recognized, they must begin with the name sirenX, X standing for the material ID index it's connected to. With that said, there can be an infinitive amount of duplicates; e.g. you can have siren1, siren1.01, siren1.02...
✨ Material-less Coronas:
If you only want to create flashing light coronas from dummies without illuminating any 3D mesh material on the model, simply set the mesh material color to pure white, any color other than the siren detection colors or set the sirenX dummy and JSONC index to any number above 255 (e.g. "siren256", "siren257"). The coronas will illuminate based on dummy positions while mesh materials remain untouched.
📌 Path: ModelExtras/data/<FILENAME>.jsonc or inside a modloader folder where your model is located
Both ID or modelname are valid FILENAME. Example: 602.jsonc or alpha.jsonc both are valid (602 is ID of the model index for the vehicle 'alpha`).
It is recommended to use modelnames instead of ID of the model index to avoid ID issues with other mods
Every JSONC configuration requires a metadata specification before any other settings. See here: METADATA
CRLO777 created an excellent, highly intuitive tool for creating siren patterns. You might prefer using it instead of creating your JSONC manually. CHECK IT OUT HERE!
Used to reference predefined configurations or templates stored elsewhere in the JSONC. This allows for reusability and modularity in siren configurations.
"colors": {
"nearlyRed": { "red": 248, "green": 95, "blue": 95, "alpha": 160 },
"OffWhite": { "red": 255, "green": 255, "blue": 255, "alpha": 160 },
"lightblue": { "red": 52, "green": 255, "blue": 221, "alpha": 160 }
},
"customProperties": {
"size": 0.3,
"state": 1,
"type": "rotator",
"inertia": 1,
"rotator": { "type": "linear" }
}Now, these colors can be referenced anywhere in the JSONC:
"color": "lightblue"Or even use the customProperties reference:
"reference": "customProperties"You can put this line inside any corona or shadow property to apply properties of this object to them.
Defines the size of the corona or shadow. For comparison, a headlight corona is ~0.3.
"size": 1.5Defines the viewing angle of the corona
"radius": 270.0270 deg - viewable from 3 sides
Defines the color of the siren material, corona and shadow in ON state.
- If
coloris a string, it references a predefined color fromreferences. - If
coloris an object, it specifies the color usingred,green,blue, andalphavalues.
"color": "lightBlue"OR
"color": {
"red": 255,
"green": 0,
"blue": 0,
"alpha": 255
}Defines the color of the siren material when its in OFF state.
- This configuration is not mandatory. If not configured, for legacy IVF materials, the "off" state color will be the same as the "on" state color but less intense. For AVS and ME materials, the "off" state color will be white.
- If
color_offis a string, it references a predefined color fromreferences. - If
color_offis an object, it specifies the color usingred,green,blue, andalphavalues.
"color_off": "nearlyRed"OR
"color_off": {
"red": 0,
"green": 0,
"blue": 255,
"alpha": 255
}Defines the initial state of the siren (on/off). 1 make it start ON, 0 make it start OFF.
"state": 1Defines a sequence of colors and timing for dynamic siren effects.
- Each entry consists of:
- A delay (time in milliseconds).
- A color (either a string reference or an object with
red,green,blue,alphavalues).
{
"colors": [
[1000, "lightBlue"],
[1000, { "red": 255, "green": 0, "blue": 0, "alpha": 255 }]
]
}The pattern property is an array of integers that represent milliseconds, leaving the array empty ([ ]) makes it a steady burn; the milliseconds does not stack, unlike IVF. They execute after each other.
- Array of numbers → Each number represents a delay in milliseconds.
- Array of arrays → First element is the number of iterations, the rest are delays.
"pattern": [500, 500, 1000]If you have multiple sirens, make sure the total time specified in their patterns is the same. This ensures they synchronize and reset together after completing one cycle.
When a pattern resets, the siren state also resets to its initial setting, regardless of where it was in the pattern. To prevent this, you can add a 0 millisecond stage at the end of the pattern. To clarify, if a state consists of a single interval in the pattern, it will continuously reset. Thus, each interval must be repeated at least twice. For instance, to toggle states every 500 milliseconds, the pattern would be [500, 500].
Defines the type of siren's corona or sets a special type 'rotator'.
-
directional→ Corona is directional (faces forward,_prmtype0/ IVF type0). -
inversed-directional→ Directional corona, but inverted 180° (faces backward,_prmtype1/ IVF type1). -
non-directional→ Corona is omnidirectional (360° visible from all angles,_prmtype2/ IVF type4). -
rotator→ This is a special type that alters the behavior of the shadow and the corona (enabling rotation) and allows the creation of rotating parts! More information can be found in the 'Rotators' section.
Defines shadow properties for the siren.
-
size: Shadow size. -
type: Shadow type, texture name inme_texdb.txd(for example,"pointlight","narrow","round", or"tightfocused"). -
offset: Shadow offset. -
angleoffset: Adds rotation offset to the model value. Useful for ImVehFt vehicles.
"shadow": {
"size": 2.0,
"type": "pointlight",
"offset": 1.0,
"angleoffset": 0.0
}Defines a delay before the siren effect starts.
"delay": 1000Defines the smoothness of transitions between siren states (simulating halogen bulb fade-out).
"inertia": 0.5You can bind specific siren states to specific vehicle paintjobs (liveries/remaps) by placing "paintjob": <index> inside the state object.
"paintjob": 0When toggling states with R or 1-9, ModelExtras checks the vehicle's current remap index (GetRemapIndex()). States assigned to other paintjob indices are automatically skipped, allowing a single vehicle model to support distinct siren lightbars for LSPD (Paintjob 0), LSSD (Paintjob 1), or State Patrol (Paintjob 2).
Enables compatibility with ImVehFt-adapted siren dummies and materials while keeping the vehicle configuration in JSONC.
"imvehft": true💡 IVF Material IDs (256 - IVF_ID):
When configuring individual lights on an IVF-adapted vehicle, reference the material index calculated as 256 - IVF_ID:
- IVF ID 1
$\rightarrow$ "255" - IVF ID 2
$\rightarrow$ "254" - IVF ID 3
$\rightarrow$ "253"
Example:
{
"sirens": {
"imvehft": true,
"states": {
"1": {
"255": {
"size": 0.35,
"color": { "red": 255, "green": 0, "blue": 0, "alpha": 255 },
"type": "non-directional"
}
}
}
}
}Defines a custom 3D audio file for the siren state. When playing, the sound continues to play in 3D space around the vehicle even if the player exits the car with the engine off.
"1. Code 3 Emergency": {
"sound": "audio/siren3.wav",
"1": { "reference": "customProperties" }
}Players can also cycle through siren sound tones on the fly using Ctrl + 1..9.
The rotator works by creating a node named after the siren (e.g., siren1) and assigning a geometry (such as a rotating mirror used in classic police beacons) as its child object. When siren1 is defined as a rotator in the JSON configuration, the child geometry will perform a continuous rotation when the sirens are activated, accurately simulating the behavior of real-world rotating emergency lights.
Additionally, the rotation is visually applied to the light shadow texture on the ground.
🔄direction
Defines the rotation direction of the rotator:
- clockwise (or 0) – rotates in a clockwise direction,
- counter-clockwise (or 1) – rotates in a counter-clockwise direction,
- switch (or 2) – alternates the rotation direction every few seconds, simulating more dynamic light patterns (e.g., spinning one way, then reversing).
⚙️type
Defines the style of movement:
- linear (or 0) – instead of rotating, the part moves back and forth along an axis (e.g., left to right across the vehicle). Useful for strobe or scanning light effects.
- ease (or 1) – performs a smooth 360° continuous rotation. This is ideal for recreating traditional rotating beacons used on classic emergency vehicles.
🔄radius
Defines rotation angle of the shadow.
🔄time
Time for one rotation of the rotator/shadow.
🔄Offset
Angle from where start the rotation
"type": "rotator",
"rotator": {
"time": 1000,
"radius": 360,
"direction": "clockwise",
"type": "linear"
}{
"sirens": { // Start of siren configuration
"references": { // Start of reference settings
"colors": { // Defines a set of named colors with RGBA values
"red": { "red": 248, "green": 95, "blue": 95, "alpha": 160 },
"white": { "red": 255, "green": 255, "blue": 255, "alpha": 160 },
"yellow": { "red": 255, "green": 149, "blue": 0, "alpha": 160 },
"blueTint": { "red": 98, "green": 156, "blue": 236, "alpha": 160 },
"lightblue": { "red": 52, "green": 255, "blue": 221, "alpha": 160 }
},
"myReference": { // Defines a reusable myReference object
"size": 0.3, // Defines the size of the rotator
"state": 1, // Initial state of the rotator
"type": "rotator", // Type identifier
"rotator": { "type": "linear" }, // Specifies the rotation type
"inertia": 1 // Defines how smoothly it rotates
}
},
"states": { // Defines each siren group, you can rotate between which group is active using hotkeys
"FirstGroup": { // Defines group 1 of sirens, can be named anything
"1": { // These siren numbers need to match the dummies inside the model
"color": "lightblue", // Use predefined color in the "colors" reference
"reference": "myReference", // References the myReference definition
"rotator": { "time": 800 } // Defines rotation speed
},
"2": {
"color": "lightblue",
"size": 0.3,
"inertia": 1.5, // Higher inertia for smoother transitions
"state": 1, // Active state
"pattern": [ 300,300 ] // Defines the blink pattern (in milliseconds)
},
"3": {
"color": "white",
"reference": "myReference",
"rotator": {
"direction": 0,
"radius": 70.0, // Defines rotation radius
"offset": -35.0, // Offset angle
"time": 600 // Time duration for one full cycle
}
},
"4": {
"color": "blueTint",
"size": 0.3,
"state": 1,
"pattern": [ 50,50,50,50,50,300,50,50,50,50,50,300 ] // Complex blink pattern
}
},
"Group 2": { // This is a totally separate group
"15": {
"size": 0.4,
"color": {
"red": 255,
"green": 255,
"blue": 255,
"alpha": 255
},
"shadow": {
"offset": 0.0,
"size": 1.0,
"type": 11
},
"state": 1,
"pattern": []
},
"16": {
"size": 0.4,
"color": {
"red": 255,
"green": 255,
"blue": 255,
"alpha": 255
},
"shadow": {
"offset": 0.0,
"size": 1.0,
"type": "pointlight"
},
"state": 1,
"pattern": []
}
}
}
}
}