DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

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 }