Emergency Lights Siren Feature (for vehicles) - user-grinch/ModelExtras GitHub Wiki

ℹ️ Overview

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.

📌 Getting Started

🔹 Siren Manifest Requirement

Every model with a siren must be configured with a JSONC manifest. Without a JSONC manifest, ModelExtras will not recognize the model as being adapted.

🔹 How the Siren Manifest Works

  • The siren manifest is an object where the keys represent different siren states, toggled using the R key. Groups can be switched using keys 1-9
  • The first key in the object is always the default state.
  • There is no limit to the number of states you can define.

🔹 Key Controls

  • H or CapsLock: Turn on siren (and turn on from muted state).
  • L: Toggles muted siren on/off.
  • R: Switch siren state forwards.
  • Shift+R: Switch siren state backwards.
  • Shift: Clear turn lights.

🗂️ Register Materials & Dummies

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 index it's connected to. With that said, there can be an infinitive amount of duplicates; e.g. you can have siren1, siren2, siren3...

📜 JSONC Configuration Guide

📂 File Location & Structure

📌 Path: ModelExtras/data/<FILENAME>.jsonc

Both model & modelname are valid FILENAME. 602.jsonc or alpha.jsonc both are valid (602 is the model index for the vehicle 'alpha`).
It is recommended to use modelnames instead of model index to avoid ID issues with other mods

1️⃣ references

Used to reference predefined configurations or templates stored elsewhere in the JSONC. This allows for reusability and modularity in siren configurations.

✨ Example

"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.

2️⃣ size

Defines the size of the corona or shadow. For comparison, a headlight corona is ~0.3.

"size": 1.5

3️⃣ diffuse

Controls the diffuse properties of the material.

🔧 Properties:

  • color: (boolean) Enable/disable color. Its true or false.
  • transparent: (boolean) Enable/disable transparency. Its true or false.

✨ Example

{
  "diffuse": {
    "color": true,
    "transparent": false
  }
}

4️⃣ radius

Defines the viewing angle of the corona

"radius": 270.0

270 deg - viewable from 3 sides

5️⃣ color

Defines the color of the siren corona or shadow.

🔧 Behavior:

  • If color is a string, it references a predefined color from references.
  • If color is an object, it specifies the color using red, green, blue, and alpha values.

✨ Example

"color": "lightBlue"

OR

"color": {
  "red": 255,
  "green": 0,
  "blue": 0,
  "alpha": 255
}

6️⃣ state

Defines the initial state of the siren (on/off). 1 make it start ON, 0 make it start OFF.

"state": 1

7️⃣ colors

Defines a sequence of colors and timing for dynamic siren effects.

🔧 Behavior:

  • Each entry consists of:
    • A delay (time in milliseconds).
    • A color (either a string reference or an object with red, green, blue, alpha values).

✨ Example

{
  "colors": [
    [1000, "lightBlue"],
    [1000, { "red": 255, "green": 0, "blue": 0, "alpha": 255 }]
  ]
}

8️⃣ pattern

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.

🔧 Behavior:

  • Array of numbers → Each number represents a delay in milliseconds.
  • Array of arrays → First element is the number of iterations, the rest are delays.

✨ Example

"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].

9️⃣ type

Defines the type of siren's corona or sets a special type 'rotator'.

🔧 Allowed Values: (Defaults to directional)

  • directional → Corona is directional IVF type 0
  • non-directional → Corona is omnidirectional IVF type 2 Can be seen from all angles.
  • inversed-directional → Directional corona, but inverted IVF type 1
  • rotator → This is a special type that alters the behavior of the shadow and the corona (enabling rotation) and allows the creation of the rotating parts! More information can be found in the 'Rotators' section.

🔟 shadow

Defines shadow properties for the siren.

🔧 Properties:

  • size: Shadow size.
  • type: Shadow type, texture name in "me_texdb.txd" file ("pointlight", "narrow", "round", "tightfocued" or any other similar texture).
  • offset: Shadow offset.
  • angleoffset: Adds rotation offset to the model value. Useful for ImVehFt vehicles.

✨ Example

  "shadow": {
    "size": 2.0,
    "type": "pointlight",
    "offset": 1.0,
    "angleoffset": 0.0
  }

1️⃣1️⃣ delay

Defines a delay before the siren effect starts.

"delay": 1000

1️⃣2️⃣ inertia

Defines the smoothness of transitions between siren states.

"inertia": 0.5

1️⃣3️⃣​ ImVehFt

Makes IVF siren dummies and materials compatible with JSON ModelExtras configurations. Use this only for ImVehFT-adapted vehicles.

"ImVehFt": true,

🚨 Rotators

Overview

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.

Rotator Specific Params

🔄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

✨ Example

  "type": "rotator",
  "rotator": {
    "time": 1000,
    "radius": 360,
    "direction": "clockwise"
    "type": "linear" 
  }

📄Complete Example

{
    "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": []
               }
            }
        }
    }
}
⚠️ **GitHub.com Fallback** ⚠️