Security Camera (2.10+)
This article documents the security camera as it will be from 2.10 onwards. See Security Camera if you're currently working with TDM 2.09 or older versions.
The security camera provides the following features:
- It can either sweep back and forth between two angles or remain stationary.
- If the camera sees the player, it initially plays a short alert sound that is silent to nearby guards. If the player is still in view several seconds later, another alarm sounds. This alarm will play intermittently for a while, even if the player moves out of sight. This second alarm will alert nearby guards.
- The camera is able to track the player, making it much harder to escape its view.
- An optional spotlight that points forward, lighting the area in the direction the camera faces.
- The ability to toggle the camera power on/off by triggering it. Alternatively, the 3 features above can be toggled individually.
- If the camera has targets, these will be activated when the camera is fully alerted. This gives the map author the ability to play a more powerful alarm than the one given off by the camera. (Or to do a variety of other things.)
- Sending what it sees to a separate, func_static display screen. Alternatively, it can send what another entity (typically a target_null) sees.
The Security Camera: Entities and Prefabs
Security camera entities can be found in AI/Machines/Security Camera and inherit from func_securitycamera. Prefabs can be found in AI/Machines.
Place the camera entity in your map, and orient it toward its starting direction. A rotating camera will sweep clockwise, halt a moment, then sweep back counter-clockwise.
Some cameras may be designed to use additional parts, i.e. a ceiling pivot entity with scripted gears. See the entity descriptions inside DR for more.
- "rotate" - If "1" (default) the camera will rotate. If "0", the camera is stationary.
- "sweepSpeed" - How many seconds it takes a rotating camera to complete a sweep in one direction.
- "sweepAngle" - The angle of sweep. You can cause a camera to initially sweep in the counter-clockwise direction by setting this to a negative number.
- "sweepWait" - How long the pause is after a sweep completes and starting the return sweep. Default is 0.5 seconds.
These spawnargs govern how the security camera detects and reacts to the player: up to what distance, at what light intensity, how long the alarm lasts, whether to trigger anything and so on.
- "scanDist" - The distance limit for spotting the player.
- "scanFov" - The camera's field of view.
- "seePlayer" - Whether the camera will react to the player.
- "sight_threshold" - From 0.0 to 1.0, how lit up the lightgem has to be for the player to be detected.
- "sightTime" - After seeing the player (partial alert), the camera will wait this amount of time. If it still sees the player, it will sound the alarm. This gives the player some time to hide.
- "sightResume" - If the camera can't see the player anymore after a partial alert, pause for this amount of time. When this expires, the camera will resume sweeping.
- "alarm_duration" - Minimum duration of the fully alert state. Will be extended by half if the player is still in view at the end or was recently seen.
- "alarm_interval" - Amount of time inbetween each activation of the alarm sound, when fully alerted.
- "trigger_alarm_start" - Trigger targets when an alarm begins.
- "trigger_alarm_end" - Trigger targets when an alarm ends.
The security camera can track the player if it catches sight of him. For as long as the security camera is partially or fully alerted, it will turn to wherever it can see the player. The models for the camera and mount should be able to support this without clipping into each other. A ceiling-mounted camera will generally have much more freedom to turn than a wall-mounted camera.
- "follow" - Enable to let the camera track the player horizontally.
- "follow_tolerance" - How far in degrees the player has to move before the camera readjusts its rotation, horizontally.
- "follow_speed_mult" - Speed up horizontal movements by this multiplier when the camera is following the player.
- "follow_incline" - The camera will also track the player vertically.
- "follow_incline_tolerance" - How far in degrees the player has to move before the camera readjusts its rotation, vertically.
- "follow_incline_speed" - Speed of vertical movements to track the player. Not affected by 'follow_speed_mult'.
- "follow_incline_max_up" - Limit how far the camera can turn upwards from its starting orientation.
- "follow_incline_max_down" - Limit how far the camera can turn downwards from its starting orientation.
If "spotLight" is set to 1, the security camera's code will spawn a spotlight and align it with its view. This dates back to the Doom3 days, so it follows different rules from the def_attach systems. If "useColors" is enabled, the spotlight's color will change depending on alert state. Otherwise it will look for a "_color" spawnarg on the camera.
Alternatively you can specify an existing light with the spawnarg "spotlight_custom". Apart from "spotLight" and "useColors", all other spotlight-relevant spawnargs are ignored. If no valid entity of spawnclass idLight is found, it'll spawn a spotlight instead.
- "spotLight" - If "1" the camera will use a spotlight.
- "spotlight_range" - Reach of the spotlight.
- "spotlight_diameter" - Diameter of the spotlight's projection. If 0, will automatically be calculated to match spotlight_range and scanFov, for scanFov up to 90°.
- "spotlight_texture" - Texture used by the spotlight. You should use a texture that's designed for projected lights: one that uses a gradient as its falloff image. This ensures that light intensity gradually fades to black, rather than abruptly cutting to black.
- "useColors" - If "1" the camera will change the colour of its model and spotlight depending on its alert state.
- "color_sweeping" - Color when the camera is sweeping = unalerted. Default green.
- "color_sighted" - Color when the camera has noticed the player = partially alerted. Default yellow.
- "color_alerted" - Color when the camera has sounded the alarm = fully alerted. Default red.
- "_color" - If "useColors" is disabled, the model and spotlight will always have this color.
As an alternative or complement to color spawnargs, you may make use of shaderParm7 in materials used by the security camera. shaderParm7 represents the alert state of the camera:
- 0 = unalerted
- 1 = was partially alerted for "sightTime" seconds but player has disappeared, now waiting "sightResume" seconds before resuming sweeping.
- 2 = partially alerted
- 3 = fully alerted
- "health" - Cameras can be 'killed', including by swords, broadhead arrows and fire arrows, but not blackjacks. Setting to 0 will make the camera invincible. Damage per hit, sword: 42; broadhead: 35; fire arrow direct hit: 400; fire arrow splash: 30.
- "fx_damage" - Fx to play whenever the camera is damaged while power is on.
- "fx_damage_nopower" - Fx to play whenever the camera is damaged while power is off.
- "fx_destroyed" - Fx to play whenever the camera is destroyed while power is on.
- "fx_destroyed_nopower" - Fx to play whenever the camera is destroyed while power is off.
- "break_up_script" - This script is called when the camera is destroyed.
- "break_up_target" - This entity is triggered if the camera is destroyed.
- "snd_sight" - Sound emitted when the camera notices the player.
- "snd_moving" - Sound emitted when the camera is rotating.
- "snd_stationary" - Sound emitted by a stationary type of camera.
- "snd_alert" - Alarm sound emitted when fully alerted.
- "snd_end" - Sound emitted when the camera is about to reach the end point of a sweep.
- "snd_death" - Sound emitted when the camera is destroyed.
- "snd_death_nopower" - Sound emitted when the camera is destroyed while power is off.
- "snd_sparks" - Sound emitted when the camera emits sparks after it was destroyed.
- "sprS_alert" - How far snd_alert propagates to AIs, at default this is a few rooms. Higher settings than mild can cause large framedrops.
The security camera spawns a particle emitter when it's destroyed. By default it uses a single-cycle spark particle which is periodically triggered at random intervals. When power to the camera is switched off, the sparks stop appearing.
- "sparks" - Whether to spawn a particle emitter at all.
- "sparks_particle" - Particle that is spawned.
- "sparks_delay" - Time taken for the particle emitter to spawn initially.
- "sparks_power_dependent" - Only show the particle if power to the (dead) camera is on.
- "sparks_periodic" - Set to '0' if you use a looping particle and sound. For single-cycle particles and sounds, set to '1' so that they are regularly triggered.
- "sparks_interval" - For non-looping particles, minimum time between triggers.
- "sparks_interval_rand" - For non-looping particles, additional random factor added to the time between triggers.
These spawnargs are important if you change the camera model:
- "broken" - Use this model when the camera is destroyed.
- "clipmodel" - Assign a simplified collision mesh to this camera. Should be under 32 tris, have only convex angles and use textures/common/collision.
- "viewOffset" - A vector that defines the offset of the camera's 'eye' from the camera's origin.
- "lightOffset" - A vector that defines the spotlight offset from the camera's origin.
- "flipAxis" - This and the next spawnarg can be used to turn a model such that it faces forwards. Not needed if the model was created forward-facing.
- "skin" - This is the "on" skin. It may contain materials that make use of the colored keyword or shaderParm7, to change depending on alert state.
- "skin_on_spotlight_off" - This is the "on" skin, but with the spotlight toggled off. This is useful if your model contains inbuilt lightrays that should represent the spotlight.
- "skin_off" - This is the "off" skin. It should not contain materials with the colored keyword or shaderParm7.
- "skin_broken" - When the camera is destroyed it will switch to this skin.
- "start_off" - Whether the camera starts powered on or off.
- "cameraTarget" - Use the view from this entity instead of from self if sending a view to the display screen. See section The Display Screen for more.
- "gearSpeed" - This spawnarg is set on the ceiling pivot of the 2nd security camera. It controls how fast the gears turn when the security camera turns.
The security camera uses spawnargs to place its 'eye' and spotlight in front of the model to ensure the view and spotlight aren't blocked by the model. If you use a different model, you might need to change these.
Damaging the Camera
By default the security camera is vulnerable to sword attacks, broadhead arrows and fire arrows. The player will receive feedback that a hit was successful in the form of spark effects, specified in spawnargs beginning with fx_. An .fx definiton is basically an instruction to emit a sequence of particles and sounds.
Setting "health" to 0 will make the camera totally invicible. Alternatively, you can make the camera immune to everything but fire arrow splash damage by binding a tight-fitting nodrawsolid_metal brush. Be sure to consider that cameras use a separate clipmodel with a different shape. Also note that fire arrows only do 30 splash damage throughout a large radius, so you may want to lower the camera's health.
A similar approach is to bind a brush that leaves parts of the camera exposed, i.e. the lens or face. Note that it will weaken fire arrows considerably if they hit from the wrong angle. A direct hit does up to 400 damage, while the splash only does 30 damage.
Unfortunately it does not appear to be possible to make water damage the camera, since TDM will crash upon load when a water response is applied to the camera.
In any case, be sure to communicate the camera's vulnerabilities to the player.
The security camera supports the following script events:
- getSecurityCameraState() - Returns the security camera's state. 1 = unalerted, 2 = partially alerted, 3 = fully alerted, 4 = inactive (power off), 5 = destroyed, 0 = not a security camera.
- getShaderParm(7) - Returns the current value of shaderParm7 on the camera model. 0 = unalerted, 1 = about to resume sweep after a partial alert, 2 = partially alerted, 3 = fully alerted.
- activate() and trigger() - These activate or deactivate the camera fully, representing switching power on or off.
- getHealth() and setHealth(float newHealth) - As per the name. Setting health to 0 or lower will destroy the security camera, which is irreversible. setHealth() will make an invincible camera vulnerable.
- setSightThreshold() - This changes how lit up the player's lightgem has to be for the security camera to see him, from 0.0 to 1.0.
- toggle_light() - Toggle the spotlight on/off, if the camera has one.
- toggle_sweep() - Toggle the camera's sweep on/off. This will also affect following.
- toggle_see_player() - Toggle whether the camera is able to detect the player. An alternative is just to set "seePlayer" to "1" or "0", since the code monitors this spawnarg.
The security camera's scriptobject contains the following object functions:
- toggleSCSpotlight() - calls toggle_light() on the camera if it has a spotlight (spawnarg "spotLight" = "1")
- toggleSCSweep() - calls toggle_sweep() on the camera
- toggleSCPlayer() - calls toggle_see_player() on the camera
As you can see, these object functions simply call the toggle script events on the camera they belong to. This allows you to call the script events from i.e. buttons without scripting. To set this up:
- create an atdm:target_callobjectfunction entity
- give it the spawnarg "call" with the name of the desired object function, i.e. "toggleSCSpotlight"
- let it target the security camera
- when desired, trigger this entity (from a script, from a button etc.).
The security camera's scriptobject only consists of the 3 object functions mentioned above. Beyond some added utility, this scriptobject has no importance for the security camera and you can easily give it your own scriptobject instead.
You may call a script when the camera is destroyed, as per the spawnarg "break_up_script". This script should be able to receive the name of the destroyed security camera as input, i.e.:
void camera_destroyed ( entity camera )
An alternative is to use the spawnarg "break_up_target" to name an entity that calls the script when triggered, but this will not pass the name of the destroyed security camera unless you use an atdm:target_callscriptfunction entity with the spawnarg "forEach" "1".
Sending the camera's view to a Display Screen
You can create a display screen that shows that the camera sees: all that's needed is a func_static patch that uses a texture like textures/common/camera/camera1. It's recommended to put another patch behind it, since this func_static will get hidden if it's switched off. The screen then needs to be given a spawnarg "cameraTarget" that names either a security camera or a different entity, typically a target_null. The screen will display what that entity sees.
To change the screen's field of view, apply the spawnargs "cameraFovX" and "cameraFovY" to the entity sending the view. Otherwise it will default to "scanFov" for security cameras or 120 for other entities.
The screen is hidden or shown automatically if the screen is displaying a security camera's view. The screen can also be switched on/off manually by triggering it, but this requires the screen to start with the spawnarg "hide" "0" or "hide" "1".
Multiple Display Screens / Reflective Water / Skybox
If you plan to have multiple security cameras sending to multiple display screens in your mission, or if the camera display will appear in the same player POV as the sky or reflective water surfaces, you'll need to use unique camera materials for each screen. You can find 9 additional camera materials in the textures/common/camera/ folder. If you should need even more, you can simply clone one of the materials and change the name after the map keyword.
Examining a Test Map
You can obtain a test map with sample cameras in it here: camerawiki2.pk4. Similar setups can be found as prefabs in AI/Machines.
Open the map camerawiki.map in Dark Radiant. In this map, we have examples of the different cameras.
A rotating camera that sweeps back and forth
This camera (cam1) starts its rotation at 135 degrees (assuming +X is 0 degrees), and sweeps clockwise until it reaches 45 degrees. It pauses for a moment, then return-sweeps back to 135 degrees. It has a spotlight.
The display for cam1 is on the wall behind it. (Don't worry about the material being displayed backward in the screenshot.)
The display patch uses the material camera1 (provided in the camerawiki/materials/tdm_camera.mtr file).
The four buttons below the display do the following (from left to right):
- Toggle Power - targets cam1 directly. When power is off, the display screen is hidden. You can simulate an "off" screen by making sure there's a black material behind the display. You could also place a glass material behind the display.
- Toggle Spotlight - calls the toggleSCSpotlight() object function in the camera's scriptobject, turning the spotlight on/off
- Toggle Player Sighting - calls the toggleSCPlayer() object function in the camera's scriptobject, turning Player detection on/off
- Toggle Sweep - calls the toggleSCSweep() object function in the camera's scriptobject, turning camera sweep on/off
A stationary camera that doesn't move
This camera (cam2) is stationary, w/o a spotlight.
Its display, to its left, uses the material camera2.
The two buttons below the display do the following (from left to right):
- Toggle Power - targets cam2 directly. When power is off, the display screen is hidden.
- Toggle Player Sighting - calls the toggleSCPlayer() object function in the camera's scriptobject, turning player detection on/off
A screen showing the view from another entity (a target_null)
This is simply a screen referencing a target_null as its cameraTarget. The target_null is in a separate room pointed at a guard.
If you look in the room where the guard is standing, you'll see that the target_null has the spawnargs cameraFovX and cameraFovY. These were set because the default field of view for a view from a non-camera entity is 120 by 120, which looks quite zoomed out. For a small screen like this one, 60 by 60 is much better.
Also notice that the screen itself has the spawnarg hide 0. This makes it respond to triggers by hiding or showing itself, allowing you to switch it on or off at the push of a button (see below).
The button below the display does the following:
- Toggle Power - targets the screen directly to show or hide it. This only works because the screen has a hide spawnarg.