2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
26 ===========================================================================
32 Security camera that triggers targets when player is in view
36 #include "../idlib/precompiled.h"
39 #include "Game_local.h"
42 /***********************************************************************
46 ***********************************************************************/
48 const idEventDef EV_SecurityCam_ReverseSweep( "<reverseSweep>" );
49 const idEventDef EV_SecurityCam_ContinueSweep( "<continueSweep>" );
50 const idEventDef EV_SecurityCam_Pause( "<pause>" );
51 const idEventDef EV_SecurityCam_Alert( "<alert>" );
52 const idEventDef EV_SecurityCam_AddLight( "<addLight>" );
54 CLASS_DECLARATION( idEntity, idSecurityCamera )
55 EVENT( EV_SecurityCam_ReverseSweep, idSecurityCamera::Event_ReverseSweep )
56 EVENT( EV_SecurityCam_ContinueSweep, idSecurityCamera::Event_ContinueSweep )
57 EVENT( EV_SecurityCam_Pause, idSecurityCamera::Event_Pause )
58 EVENT( EV_SecurityCam_Alert, idSecurityCamera::Event_Alert )
59 EVENT( EV_SecurityCam_AddLight, idSecurityCamera::Event_AddLight )
64 idSecurityCamera::Save
67 void idSecurityCamera::Save( idSaveGame *savefile ) const {
68 savefile->WriteFloat( angle );
69 savefile->WriteFloat( sweepAngle );
70 savefile->WriteInt( modelAxis );
71 savefile->WriteBool( flipAxis );
72 savefile->WriteFloat( scanDist );
73 savefile->WriteFloat( scanFov );
75 savefile->WriteFloat( sweepStart );
76 savefile->WriteFloat( sweepEnd );
77 savefile->WriteBool( negativeSweep );
78 savefile->WriteBool( sweeping );
79 savefile->WriteInt( alertMode );
80 savefile->WriteFloat( stopSweeping );
81 savefile->WriteFloat( scanFovCos );
83 savefile->WriteVec3( viewOffset );
85 savefile->WriteInt( pvsArea );
86 savefile->WriteStaticObject( physicsObj );
87 savefile->WriteTraceModel( trm );
92 idSecurityCamera::Restore
95 void idSecurityCamera::Restore( idRestoreGame *savefile ) {
96 savefile->ReadFloat( angle );
97 savefile->ReadFloat( sweepAngle );
98 savefile->ReadInt( modelAxis );
99 savefile->ReadBool( flipAxis );
100 savefile->ReadFloat( scanDist );
101 savefile->ReadFloat( scanFov );
103 savefile->ReadFloat( sweepStart );
104 savefile->ReadFloat( sweepEnd );
105 savefile->ReadBool( negativeSweep );
106 savefile->ReadBool( sweeping );
107 savefile->ReadInt( alertMode );
108 savefile->ReadFloat( stopSweeping );
109 savefile->ReadFloat( scanFovCos );
111 savefile->ReadVec3( viewOffset );
113 savefile->ReadInt( pvsArea );
114 savefile->ReadStaticObject( physicsObj );
115 savefile->ReadTraceModel( trm );
120 idSecurityCamera::Spawn
123 void idSecurityCamera::Spawn( void ) {
126 sweepAngle = spawnArgs.GetFloat( "sweepAngle", "90" );
127 health = spawnArgs.GetInt( "health", "100" );
128 scanFov = spawnArgs.GetFloat( "scanFov", "90" );
129 scanDist = spawnArgs.GetFloat( "scanDist", "200" );
130 flipAxis = spawnArgs.GetBool( "flipAxis" );
132 modelAxis = spawnArgs.GetInt( "modelAxis" );
133 if ( modelAxis < 0 || modelAxis > 2 ) {
137 spawnArgs.GetVector( "viewOffset", "0 0 0", viewOffset );
139 if ( spawnArgs.GetBool( "spotLight" ) ) {
140 PostEventMS( &EV_SecurityCam_AddLight, 0 );
143 negativeSweep = ( sweepAngle < 0 ) ? true : false;
144 sweepAngle = abs( sweepAngle );
146 scanFovCos = cos( scanFov * idMath::PI / 360.0f );
148 angle = GetPhysics()->GetAxis().ToAngles().yaw;
150 SetAlertMode( SCANNING );
151 BecomeActive( TH_THINK );
154 fl.takedamage = true;
157 pvsArea = gameLocal.pvs.GetPVSArea( GetPhysics()->GetOrigin() );
158 // if no target specified use ourself
159 str = spawnArgs.GetString( "cameraTarget" );
160 if ( str.Length() == 0 ) {
161 spawnArgs.Set( "cameraTarget", spawnArgs.GetString( "name" ) );
164 // check if a clip model is set
165 spawnArgs.GetString( "clipmodel", "", str );
167 str = spawnArgs.GetString( "model" ); // use the visual model
170 if ( !collisionModelManager->TrmFromModel( str, trm ) ) {
171 gameLocal.Error( "idSecurityCamera '%s': cannot load collision model %s", name.c_str(), str.c_str() );
175 GetPhysics()->SetContents( CONTENTS_SOLID );
176 GetPhysics()->SetClipMask( MASK_SOLID | CONTENTS_BODY | CONTENTS_CORPSE | CONTENTS_MOVEABLECLIP );
178 UpdateChangeableSpawnArgs( NULL );
183 idSecurityCamera::Event_AddLight
186 void idSecurityCamera::Event_AddLight( void ) {
188 idVec3 right, up, target, temp;
195 dir.NormalVectors( right, up );
196 target = GetPhysics()->GetOrigin() + dir * scanDist;
198 radius = tan( scanFov * idMath::PI / 360.0f );
199 up = dir + up * radius;
201 up = GetPhysics()->GetOrigin() + up * scanDist;
204 right = dir + right * radius;
206 right = GetPhysics()->GetOrigin() + right * scanDist;
209 spawnArgs.GetVector( "lightOffset", "0 0 0", lightOffset );
211 args.Set( "origin", ( GetPhysics()->GetOrigin() + lightOffset ).ToString() );
212 args.Set( "light_target", target.ToString() );
213 args.Set( "light_right", right.ToString() );
214 args.Set( "light_up", up.ToString() );
215 args.SetFloat( "angle", GetPhysics()->GetAxis()[0].ToYaw() );
217 spotLight = static_cast<idLight *>( gameLocal.SpawnEntityType( idLight::Type, &args ) );
218 spotLight->Bind( this, true );
219 spotLight->UpdateVisuals();
224 idSecurityCamera::DrawFov
227 void idSecurityCamera::DrawFov( void ) {
229 float radius, a, s, c, halfRadius;
231 idVec4 color(1, 0, 0, 1), color2(0, 0, 1, 1);
232 idVec3 lastPoint, point, lastHalfPoint, halfPoint, center;
234 idVec3 dir = GetAxis();
235 dir.NormalVectors( right, up );
237 radius = tan( scanFov * idMath::PI / 360.0f );
238 halfRadius = radius * 0.5f;
239 lastPoint = dir + up * radius;
240 lastPoint.Normalize();
241 lastPoint = GetPhysics()->GetOrigin() + lastPoint * scanDist;
242 lastHalfPoint = dir + up * halfRadius;
243 lastHalfPoint.Normalize();
244 lastHalfPoint = GetPhysics()->GetOrigin() + lastHalfPoint * scanDist;
245 center = GetPhysics()->GetOrigin() + dir * scanDist;
246 for ( i = 1; i < 12; i++ ) {
247 a = idMath::TWO_PI * i / 12.0f;
248 idMath::SinCos( a, s, c );
249 point = dir + right * s * radius + up * c * radius;
251 point = GetPhysics()->GetOrigin() + point * scanDist;
252 gameRenderWorld->DebugLine( color, lastPoint, point );
253 gameRenderWorld->DebugLine( color, GetPhysics()->GetOrigin(), point );
256 halfPoint = dir + right * s * halfRadius + up * c * halfRadius;
257 halfPoint.Normalize();
258 halfPoint = GetPhysics()->GetOrigin() + halfPoint * scanDist;
259 gameRenderWorld->DebugLine( color2, point, halfPoint );
260 gameRenderWorld->DebugLine( color2, lastHalfPoint, halfPoint );
261 lastHalfPoint = halfPoint;
263 gameRenderWorld->DebugLine( color2, halfPoint, center );
269 idSecurityCamera::GetRenderView
272 renderView_t *idSecurityCamera::GetRenderView() {
273 renderView_t *rv = idEntity::GetRenderView();
276 rv->viewaxis = GetAxis().ToAngles().ToMat3();
277 rv->vieworg = GetPhysics()->GetOrigin() + viewOffset;
283 idSecurityCamera::CanSeePlayer
286 bool idSecurityCamera::CanSeePlayer( void ) {
294 handle = gameLocal.pvs.SetupCurrentPVS( pvsArea );
296 for ( i = 0; i < gameLocal.numClients; i++ ) {
297 ent = static_cast<idPlayer*>(gameLocal.entities[ i ]);
299 if ( !ent || ( ent->fl.notarget ) ) {
303 // if there is no way we can see this player
304 if ( !gameLocal.pvs.InCurrentPVS( handle, ent->GetPVSAreas(), ent->GetNumPVSAreas() ) ) {
308 dir = ent->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
309 dist = dir.Normalize();
311 if ( dist > scanDist ) {
315 if ( dir * GetAxis() < scanFovCos ) {
321 eye = ent->EyeOffset();
323 gameLocal.clip.TracePoint( tr, GetPhysics()->GetOrigin(), ent->GetPhysics()->GetOrigin() + eye, MASK_OPAQUE, this );
324 if ( tr.fraction == 1.0 || ( gameLocal.GetTraceEntity( tr ) == ent ) ) {
325 gameLocal.pvs.FreeCurrentPVS( handle );
330 gameLocal.pvs.FreeCurrentPVS( handle );
337 idSecurityCamera::SetAlertMode
340 void idSecurityCamera::SetAlertMode( int alert ) {
341 if (alert >= SCANNING && alert <= ACTIVATED) {
344 renderEntity.shaderParms[ SHADERPARM_MODE ] = alertMode;
350 idSecurityCamera::Think
353 void idSecurityCamera::Think( void ) {
357 if ( thinkFlags & TH_THINK ) {
358 if ( g_showEntityInfo.GetBool() ) {
363 BecomeInactive( TH_THINK );
371 if ( thinkFlags & TH_THINK ) {
372 if (CanSeePlayer()) {
373 if (alertMode == SCANNING) {
377 stopSweeping = gameLocal.time;
379 CancelEvents( &EV_SecurityCam_Pause );
381 CancelEvents( &EV_SecurityCam_ReverseSweep );
384 StopSound( SND_CHANNEL_ANY, false );
385 StartSound( "snd_sight", SND_CHANNEL_BODY, 0, false, NULL );
387 sightTime = spawnArgs.GetFloat( "sightTime", "5" );
388 PostEventSec(&EV_SecurityCam_Alert, sightTime);
391 if (alertMode == ALERT) {
394 SetAlertMode(LOSINGINTEREST);
395 CancelEvents( &EV_SecurityCam_Alert );
397 sightResume = spawnArgs.GetFloat( "sightResume", "1.5" );
398 PostEventSec( &EV_SecurityCam_ContinueSweep, sightResume );
402 idAngles a = GetPhysics()->GetAxis().ToAngles();
404 pct = ( gameLocal.time - sweepStart ) / ( sweepEnd - sweepStart );
405 travel = pct * sweepAngle;
406 if ( negativeSweep ) {
407 a.yaw = angle + travel;
409 a.yaw = angle - travel;
421 idSecurityCamera::GetAxis
424 const idVec3 idSecurityCamera::GetAxis( void ) const {
425 return (flipAxis) ? -GetPhysics()->GetAxis()[modelAxis] : GetPhysics()->GetAxis()[modelAxis];
430 idSecurityCamera::SweepSpeed
433 float idSecurityCamera::SweepSpeed( void ) const {
434 return spawnArgs.GetFloat( "sweepSpeed", "5" );
439 idSecurityCamera::StartSweep
442 void idSecurityCamera::StartSweep( void ) {
446 sweepStart = gameLocal.time;
447 speed = SEC2MS( SweepSpeed() );
448 sweepEnd = sweepStart + speed;
449 PostEventMS( &EV_SecurityCam_Pause, speed );
450 StartSound( "snd_moving", SND_CHANNEL_BODY, 0, false, NULL );
455 idSecurityCamera::Event_ContinueSweep
458 void idSecurityCamera::Event_ContinueSweep( void ) {
459 float pct = (stopSweeping - sweepStart) / (sweepEnd - sweepStart);
460 float f = gameLocal.time - (sweepEnd - sweepStart) * pct;
464 speed = MS2SEC( SweepSpeed() );
465 sweepEnd = sweepStart + speed;
466 PostEventMS( &EV_SecurityCam_Pause, speed * (1.0 - pct));
467 StartSound( "snd_moving", SND_CHANNEL_BODY, 0, false, NULL );
468 SetAlertMode(SCANNING);
474 idSecurityCamera::Event_Alert
477 void idSecurityCamera::Event_Alert( void ) {
480 SetAlertMode(ACTIVATED);
481 StopSound( SND_CHANNEL_ANY, false );
482 StartSound( "snd_activate", SND_CHANNEL_BODY, 0, false, NULL );
483 ActivateTargets(this);
484 CancelEvents( &EV_SecurityCam_ContinueSweep );
486 wait = spawnArgs.GetFloat( "wait", "20" );
487 PostEventSec( &EV_SecurityCam_ContinueSweep, wait );
492 idSecurityCamera::Event_ReverseSweep
495 void idSecurityCamera::Event_ReverseSweep( void ) {
496 angle = GetPhysics()->GetAxis().ToAngles().yaw;
497 negativeSweep = !negativeSweep;
503 idSecurityCamera::Event_Pause
506 void idSecurityCamera::Event_Pause( void ) {
509 sweepWait = spawnArgs.GetFloat( "sweepWait", "0.5" );
511 StopSound( SND_CHANNEL_ANY, false );
512 StartSound( "snd_stop", SND_CHANNEL_BODY, 0, false, NULL );
513 PostEventSec( &EV_SecurityCam_ReverseSweep, sweepWait );
518 idSecurityCamera::Killed
521 void idSecurityCamera::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
523 StopSound( SND_CHANNEL_ANY, false );
524 const char *fx = spawnArgs.GetString( "fx_destroyed" );
525 if ( fx[0] != '\0' ) {
526 idEntityFx::StartFx( fx, NULL, NULL, this, true );
529 physicsObj.SetSelf( this );
530 physicsObj.SetClipModel( new idClipModel( trm ), 0.02f );
531 physicsObj.SetOrigin( GetPhysics()->GetOrigin() );
532 physicsObj.SetAxis( GetPhysics()->GetAxis() );
533 physicsObj.SetBouncyness( 0.2f );
534 physicsObj.SetFriction( 0.6f, 0.6f, 0.2f );
535 physicsObj.SetGravity( gameLocal.GetGravity() );
536 physicsObj.SetContents( CONTENTS_SOLID );
537 physicsObj.SetClipMask( MASK_SOLID | CONTENTS_BODY | CONTENTS_CORPSE | CONTENTS_MOVEABLECLIP );
538 SetPhysics( &physicsObj );
539 physicsObj.DropToFloor();
545 idSecurityCamera::Pain
548 bool idSecurityCamera::Pain( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
549 const char *fx = spawnArgs.GetString( "fx_damage" );
550 if ( fx[0] != '\0' ) {
551 idEntityFx::StartFx( fx, NULL, NULL, this, true );
559 idSecurityCamera::Present
561 Present is called to allow entities to generate refEntities, lights, etc for the renderer.
564 void idSecurityCamera::Present( void ) {
565 // don't present to the renderer if the entity hasn't changed
566 if ( !( thinkFlags & TH_UPDATEVISUALS ) ) {
569 BecomeInactive( TH_UPDATEVISUALS );
571 // camera target for remote render views
572 if ( cameraTarget ) {
573 renderEntity.remoteRenderView = cameraTarget->GetRenderView();
576 // if set to invisible, skip
577 if ( !renderEntity.hModel || IsHidden() ) {
581 // add to refresh list
582 if ( modelDefHandle == -1 ) {
583 modelDefHandle = gameRenderWorld->AddEntityDef( &renderEntity );
585 gameRenderWorld->UpdateEntityDef( modelDefHandle, &renderEntity );