]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/game/SecurityCamera.cpp
Use the same OpenAL headers on all platforms.
[icculus/iodoom3.git] / neo / game / SecurityCamera.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
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.
13
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.
18
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/>.
21
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.
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( void ) {
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( void ) {
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( void ) {
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( void ) {
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( void ) {
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( void ) const {
425         return (flipAxis) ? -GetPhysics()->GetAxis()[modelAxis] : GetPhysics()->GetAxis()[modelAxis];
426 };
427
428 /*
429 ================
430 idSecurityCamera::SweepSpeed
431 ================
432 */
433 float idSecurityCamera::SweepSpeed( void ) const {
434         return spawnArgs.GetFloat( "sweepSpeed", "5" );
435 }
436
437 /*
438 ================
439 idSecurityCamera::StartSweep
440 ================
441 */
442 void idSecurityCamera::StartSweep( void ) {
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( void ) {
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( void ) {
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( void ) {
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( void ) {
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 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( void ) {
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 }