SecurityCamera.cpp (15926B)
1 /* 2 =========================================================================== 3 4 Doom 3 BFG Edition GPL Source Code 5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 6 7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). 8 9 Doom 3 BFG Edition 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. 13 14 Doom 3 BFG Edition 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. 18 19 You should have received a copy of the GNU General Public License 20 along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>. 21 22 In addition, the Doom 3 BFG Edition 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 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. 23 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. 25 26 =========================================================================== 27 */ 28 /* 29 30 SecurityCamera.cpp 31 32 Security camera that triggers targets when player is in view 33 34 */ 35 36 #include "../idlib/precompiled.h" 37 #pragma hdrstop 38 39 #include "Game_local.h" 40 41 42 /*********************************************************************** 43 44 idSecurityCamera 45 46 ***********************************************************************/ 47 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>" ); 53 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 ) 60 END_CLASS 61 62 /* 63 ================ 64 idSecurityCamera::Save 65 ================ 66 */ 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 ); 74 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 ); 82 83 savefile->WriteVec3( viewOffset ); 84 85 savefile->WriteInt( pvsArea ); 86 savefile->WriteStaticObject( physicsObj ); 87 savefile->WriteTraceModel( trm ); 88 } 89 90 /* 91 ================ 92 idSecurityCamera::Restore 93 ================ 94 */ 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 ); 102 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 ); 110 111 savefile->ReadVec3( viewOffset ); 112 113 savefile->ReadInt( pvsArea ); 114 savefile->ReadStaticObject( physicsObj ); 115 savefile->ReadTraceModel( trm ); 116 } 117 118 /* 119 ================ 120 idSecurityCamera::Spawn 121 ================ 122 */ 123 void idSecurityCamera::Spawn() { 124 idStr str; 125 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" ); 131 132 modelAxis = spawnArgs.GetInt( "modelAxis" ); 133 if ( modelAxis < 0 || modelAxis > 2 ) { 134 modelAxis = 0; 135 } 136 137 spawnArgs.GetVector( "viewOffset", "0 0 0", viewOffset ); 138 139 if ( spawnArgs.GetBool( "spotLight" ) ) { 140 PostEventMS( &EV_SecurityCam_AddLight, 0 ); 141 } 142 143 negativeSweep = ( sweepAngle < 0 ) ? true : false; 144 sweepAngle = abs( sweepAngle ); 145 146 scanFovCos = cos( scanFov * idMath::PI / 360.0f ); 147 148 angle = GetPhysics()->GetAxis().ToAngles().yaw; 149 StartSweep(); 150 SetAlertMode( SCANNING ); 151 BecomeActive( TH_THINK ); 152 153 if ( health ) { 154 fl.takedamage = true; 155 } 156 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" ) ); 162 } 163 164 // check if a clip model is set 165 spawnArgs.GetString( "clipmodel", "", str ); 166 if ( !str[0] ) { 167 str = spawnArgs.GetString( "model" ); // use the visual model 168 } 169 170 if ( !collisionModelManager->TrmFromModel( str, trm ) ) { 171 gameLocal.Error( "idSecurityCamera '%s': cannot load collision model %s", name.c_str(), str.c_str() ); 172 return; 173 } 174 175 GetPhysics()->SetContents( CONTENTS_SOLID ); 176 GetPhysics()->SetClipMask( MASK_SOLID | CONTENTS_BODY | CONTENTS_CORPSE | CONTENTS_MOVEABLECLIP ); 177 // setup the physics 178 UpdateChangeableSpawnArgs( NULL ); 179 } 180 181 /* 182 ================ 183 idSecurityCamera::Event_AddLight 184 ================ 185 */ 186 void idSecurityCamera::Event_AddLight() { 187 idDict args; 188 idVec3 right, up, target, temp; 189 idVec3 dir; 190 float radius; 191 idVec3 lightOffset; 192 idLight *spotLight; 193 194 dir = GetAxis(); 195 dir.NormalVectors( right, up ); 196 target = GetPhysics()->GetOrigin() + dir * scanDist; 197 198 radius = tan( scanFov * idMath::PI / 360.0f ); 199 up = dir + up * radius; 200 up.Normalize(); 201 up = GetPhysics()->GetOrigin() + up * scanDist; 202 up -= target; 203 204 right = dir + right * radius; 205 right.Normalize(); 206 right = GetPhysics()->GetOrigin() + right * scanDist; 207 right -= target; 208 209 spawnArgs.GetVector( "lightOffset", "0 0 0", lightOffset ); 210 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() ); 216 217 spotLight = static_cast<idLight *>( gameLocal.SpawnEntityType( idLight::Type, &args ) ); 218 spotLight->Bind( this, true ); 219 spotLight->UpdateVisuals(); 220 } 221 222 /* 223 ================ 224 idSecurityCamera::DrawFov 225 ================ 226 */ 227 void idSecurityCamera::DrawFov() { 228 int i; 229 float radius, a, s, c, halfRadius; 230 idVec3 right, up; 231 idVec4 color(1, 0, 0, 1), color2(0, 0, 1, 1); 232 idVec3 lastPoint, point, lastHalfPoint, halfPoint, center; 233 234 idVec3 dir = GetAxis(); 235 dir.NormalVectors( right, up ); 236 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; 250 point.Normalize(); 251 point = GetPhysics()->GetOrigin() + point * scanDist; 252 gameRenderWorld->DebugLine( color, lastPoint, point ); 253 gameRenderWorld->DebugLine( color, GetPhysics()->GetOrigin(), point ); 254 lastPoint = point; 255 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; 262 263 gameRenderWorld->DebugLine( color2, halfPoint, center ); 264 } 265 } 266 267 /* 268 ================ 269 idSecurityCamera::GetRenderView 270 ================ 271 */ 272 renderView_t *idSecurityCamera::GetRenderView() { 273 renderView_t *rv = idEntity::GetRenderView(); 274 rv->fov_x = scanFov; 275 rv->fov_y = scanFov; 276 rv->viewaxis = GetAxis().ToAngles().ToMat3(); 277 rv->vieworg = GetPhysics()->GetOrigin() + viewOffset; 278 return rv; 279 } 280 281 /* 282 ================ 283 idSecurityCamera::CanSeePlayer 284 ================ 285 */ 286 bool idSecurityCamera::CanSeePlayer() { 287 int i; 288 float dist; 289 idPlayer *ent; 290 trace_t tr; 291 idVec3 dir; 292 pvsHandle_t handle; 293 294 handle = gameLocal.pvs.SetupCurrentPVS( pvsArea ); 295 296 for ( i = 0; i < gameLocal.numClients; i++ ) { 297 ent = static_cast<idPlayer*>(gameLocal.entities[ i ]); 298 299 if ( !ent || ( ent->fl.notarget ) ) { 300 continue; 301 } 302 303 // if there is no way we can see this player 304 if ( !gameLocal.pvs.InCurrentPVS( handle, ent->GetPVSAreas(), ent->GetNumPVSAreas() ) ) { 305 continue; 306 } 307 308 dir = ent->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin(); 309 dist = dir.Normalize(); 310 311 if ( dist > scanDist ) { 312 continue; 313 } 314 315 if ( dir * GetAxis() < scanFovCos ) { 316 continue; 317 } 318 319 idVec3 eye; 320 321 eye = ent->EyeOffset(); 322 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 ); 326 return true; 327 } 328 } 329 330 gameLocal.pvs.FreeCurrentPVS( handle ); 331 332 return false; 333 } 334 335 /* 336 ================ 337 idSecurityCamera::SetAlertMode 338 ================ 339 */ 340 void idSecurityCamera::SetAlertMode( int alert ) { 341 if (alert >= SCANNING && alert <= ACTIVATED) { 342 alertMode = alert; 343 } 344 renderEntity.shaderParms[ SHADERPARM_MODE ] = alertMode; 345 UpdateVisuals(); 346 } 347 348 /* 349 ================ 350 idSecurityCamera::Think 351 ================ 352 */ 353 void idSecurityCamera::Think() { 354 float pct; 355 float travel; 356 357 if ( thinkFlags & TH_THINK ) { 358 if ( g_showEntityInfo.GetBool() ) { 359 DrawFov(); 360 } 361 362 if (health <= 0) { 363 BecomeInactive( TH_THINK ); 364 return; 365 } 366 } 367 368 // run physics 369 RunPhysics(); 370 371 if ( thinkFlags & TH_THINK ) { 372 if (CanSeePlayer()) { 373 if (alertMode == SCANNING) { 374 float sightTime; 375 376 SetAlertMode(ALERT); 377 stopSweeping = gameLocal.time; 378 if (sweeping) { 379 CancelEvents( &EV_SecurityCam_Pause ); 380 } else { 381 CancelEvents( &EV_SecurityCam_ReverseSweep ); 382 } 383 sweeping = false; 384 StopSound( SND_CHANNEL_ANY, false ); 385 StartSound( "snd_sight", SND_CHANNEL_BODY, 0, false, NULL ); 386 387 sightTime = spawnArgs.GetFloat( "sightTime", "5" ); 388 PostEventSec(&EV_SecurityCam_Alert, sightTime); 389 } 390 } else { 391 if (alertMode == ALERT) { 392 float sightResume; 393 394 SetAlertMode(LOSINGINTEREST); 395 CancelEvents( &EV_SecurityCam_Alert ); 396 397 sightResume = spawnArgs.GetFloat( "sightResume", "1.5" ); 398 PostEventSec( &EV_SecurityCam_ContinueSweep, sightResume ); 399 } 400 401 if ( sweeping ) { 402 idAngles a = GetPhysics()->GetAxis().ToAngles(); 403 404 pct = ( gameLocal.time - sweepStart ) / ( sweepEnd - sweepStart ); 405 travel = pct * sweepAngle; 406 if ( negativeSweep ) { 407 a.yaw = angle + travel; 408 } else { 409 a.yaw = angle - travel; 410 } 411 412 SetAngles( a ); 413 } 414 } 415 } 416 Present(); 417 } 418 419 /* 420 ================ 421 idSecurityCamera::GetAxis 422 ================ 423 */ 424 const idVec3 idSecurityCamera::GetAxis() const { 425 return (flipAxis) ? -GetPhysics()->GetAxis()[modelAxis] : GetPhysics()->GetAxis()[modelAxis]; 426 }; 427 428 /* 429 ================ 430 idSecurityCamera::SweepSpeed 431 ================ 432 */ 433 float idSecurityCamera::SweepSpeed() const { 434 return spawnArgs.GetFloat( "sweepSpeed", "5" ); 435 } 436 437 /* 438 ================ 439 idSecurityCamera::StartSweep 440 ================ 441 */ 442 void idSecurityCamera::StartSweep() { 443 int speed; 444 445 sweeping = true; 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 ); 451 } 452 453 /* 454 ================ 455 idSecurityCamera::Event_ContinueSweep 456 ================ 457 */ 458 void idSecurityCamera::Event_ContinueSweep() { 459 float pct = (stopSweeping - sweepStart) / (sweepEnd - sweepStart); 460 float f = gameLocal.time - (sweepEnd - sweepStart) * pct; 461 int speed; 462 463 sweepStart = f; 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); 469 sweeping = true; 470 } 471 472 /* 473 ================ 474 idSecurityCamera::Event_Alert 475 ================ 476 */ 477 void idSecurityCamera::Event_Alert() { 478 float wait; 479 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 ); 485 486 wait = spawnArgs.GetFloat( "wait", "20" ); 487 PostEventSec( &EV_SecurityCam_ContinueSweep, wait ); 488 } 489 490 /* 491 ================ 492 idSecurityCamera::Event_ReverseSweep 493 ================ 494 */ 495 void idSecurityCamera::Event_ReverseSweep() { 496 angle = GetPhysics()->GetAxis().ToAngles().yaw; 497 negativeSweep = !negativeSweep; 498 StartSweep(); 499 } 500 501 /* 502 ================ 503 idSecurityCamera::Event_Pause 504 ================ 505 */ 506 void idSecurityCamera::Event_Pause() { 507 float sweepWait; 508 509 sweepWait = spawnArgs.GetFloat( "sweepWait", "0.5" ); 510 sweeping = false; 511 StopSound( SND_CHANNEL_ANY, false ); 512 StartSound( "snd_stop", SND_CHANNEL_BODY, 0, false, NULL ); 513 PostEventSec( &EV_SecurityCam_ReverseSweep, sweepWait ); 514 } 515 516 /* 517 ============ 518 idSecurityCamera::Killed 519 ============ 520 */ 521 void idSecurityCamera::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) { 522 sweeping = false; 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 ); 527 } 528 529 physicsObj.SetSelf( this ); 530 physicsObj.SetClipModel( new (TAG_PHYSICS_CLIP_ENTITY) 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(); 540 } 541 542 543 /* 544 ============ 545 idSecurityCamera::Pain 546 ============ 547 */ 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 ); 552 } 553 return true; 554 } 555 556 557 /* 558 ================ 559 idSecurityCamera::Present 560 561 Present is called to allow entities to generate refEntities, lights, etc for the renderer. 562 ================ 563 */ 564 void idSecurityCamera::Present() { 565 // don't present to the renderer if the entity hasn't changed 566 if ( !( thinkFlags & TH_UPDATEVISUALS ) ) { 567 return; 568 } 569 BecomeInactive( TH_UPDATEVISUALS ); 570 571 // camera target for remote render views 572 if ( cameraTarget ) { 573 renderEntity.remoteRenderView = cameraTarget->GetRenderView(); 574 } 575 576 // if set to invisible, skip 577 if ( !renderEntity.hModel || IsHidden() ) { 578 return; 579 } 580 581 // add to refresh list 582 if ( modelDefHandle == -1 ) { 583 modelDefHandle = gameRenderWorld->AddEntityDef( &renderEntity ); 584 } else { 585 gameRenderWorld->UpdateEntityDef( modelDefHandle, &renderEntity ); 586 } 587 }