]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/game/Trigger.cpp
Use the same OpenAL headers on all platforms.
[icculus/iodoom3.git] / neo / game / Trigger.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 #include "../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "Game_local.h"
33
34
35 /*
36 ===============================================================================
37
38   idTrigger
39         
40 ===============================================================================
41 */
42
43 const idEventDef EV_Enable( "enable", NULL );
44 const idEventDef EV_Disable( "disable", NULL );
45
46 CLASS_DECLARATION( idEntity, idTrigger )
47         EVENT( EV_Enable,       idTrigger::Event_Enable )
48         EVENT( EV_Disable,      idTrigger::Event_Disable )
49 END_CLASS
50
51 /*
52 ================
53 idTrigger::DrawDebugInfo
54 ================
55 */
56 void idTrigger::DrawDebugInfo( void ) {
57         idMat3          axis = gameLocal.GetLocalPlayer()->viewAngles.ToMat3();
58         idVec3          up = axis[ 2 ] * 5.0f;
59         idBounds        viewTextBounds( gameLocal.GetLocalPlayer()->GetPhysics()->GetOrigin() );
60         idBounds        viewBounds( gameLocal.GetLocalPlayer()->GetPhysics()->GetOrigin() );
61         idBounds        box( idVec3( -4.0f, -4.0f, -4.0f ), idVec3( 4.0f, 4.0f, 4.0f ) );
62         idEntity        *ent;
63         idEntity        *target;
64         int                     i;
65         bool            show;
66         const function_t *func;
67
68         viewTextBounds.ExpandSelf( 128.0f );
69         viewBounds.ExpandSelf( 512.0f );
70         for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
71                 if ( ent->GetPhysics()->GetContents() & ( CONTENTS_TRIGGER | CONTENTS_FLASHLIGHT_TRIGGER ) ) {
72                         show = viewBounds.IntersectsBounds( ent->GetPhysics()->GetAbsBounds() );
73                         if ( !show ) {
74                                 for( i = 0; i < ent->targets.Num(); i++ ) {
75                                         target = ent->targets[ i ].GetEntity();
76                                         if ( target && viewBounds.IntersectsBounds( target->GetPhysics()->GetAbsBounds() ) ) {
77                                                 show = true;
78                                                 break;
79                                         }
80                                 }
81                         }
82
83                         if ( !show ) {
84                                 continue;
85                         }
86
87                         gameRenderWorld->DebugBounds( colorOrange, ent->GetPhysics()->GetAbsBounds() );
88                         if ( viewTextBounds.IntersectsBounds( ent->GetPhysics()->GetAbsBounds() ) ) {
89                                 gameRenderWorld->DrawText( ent->name.c_str(), ent->GetPhysics()->GetAbsBounds().GetCenter(), 0.1f, colorWhite, axis, 1 );
90                                 gameRenderWorld->DrawText( ent->GetEntityDefName(), ent->GetPhysics()->GetAbsBounds().GetCenter() + up, 0.1f, colorWhite, axis, 1 );
91                                 if ( ent->IsType( idTrigger::Type ) ) {
92                                         func = static_cast<idTrigger *>( ent )->GetScriptFunction();
93                                 } else {
94                                         func = NULL;
95                                 }
96
97                                 if ( func ) {
98                                         gameRenderWorld->DrawText( va( "call script '%s'", func->Name() ), ent->GetPhysics()->GetAbsBounds().GetCenter() - up, 0.1f, colorWhite, axis, 1 );
99                                 }
100                         }
101
102                         for( i = 0; i < ent->targets.Num(); i++ ) {
103                                 target = ent->targets[ i ].GetEntity();
104                                 if ( target ) {
105                                         gameRenderWorld->DebugArrow( colorYellow, ent->GetPhysics()->GetAbsBounds().GetCenter(), target->GetPhysics()->GetOrigin(), 10, 0 );
106                                         gameRenderWorld->DebugBounds( colorGreen, box, target->GetPhysics()->GetOrigin() );
107                                         if ( viewTextBounds.IntersectsBounds( target->GetPhysics()->GetAbsBounds() ) ) {
108                                                 gameRenderWorld->DrawText( target->name.c_str(), target->GetPhysics()->GetAbsBounds().GetCenter(), 0.1f, colorWhite, axis, 1 );
109                                         }
110                                 }
111                         }
112                 }
113         }
114 }
115
116 /*
117 ================
118 idTrigger::Enable
119 ================
120 */
121 void idTrigger::Enable( void ) {
122         GetPhysics()->SetContents( CONTENTS_TRIGGER );
123         GetPhysics()->EnableClip();
124 }
125
126 /*
127 ================
128 idTrigger::Disable
129 ================
130 */
131 void idTrigger::Disable( void ) {
132         // we may be relinked if we're bound to another object, so clear the contents as well
133         GetPhysics()->SetContents( 0 );
134         GetPhysics()->DisableClip();
135 }
136
137 /*
138 ================
139 idTrigger::CallScript
140 ================
141 */
142 void idTrigger::CallScript( void ) const {
143         idThread *thread;
144
145         if ( scriptFunction ) {
146                 thread = new idThread( scriptFunction );
147                 thread->DelayedStart( 0 );
148         }
149 }
150
151 /*
152 ================
153 idTrigger::GetScriptFunction
154 ================
155 */
156 const function_t *idTrigger::GetScriptFunction( void ) const {
157         return scriptFunction;
158 }
159
160 /*
161 ================
162 idTrigger::Save
163 ================
164 */
165 void idTrigger::Save( idSaveGame *savefile ) const {
166         if ( scriptFunction ) {
167                 savefile->WriteString( scriptFunction->Name() );
168         } else {
169                 savefile->WriteString( "" );
170         }
171 }
172
173 /*
174 ================
175 idTrigger::Restore
176 ================
177 */
178 void idTrigger::Restore( idRestoreGame *savefile ) {
179         idStr funcname;
180         savefile->ReadString( funcname );
181         if ( funcname.Length() ) {
182                 scriptFunction = gameLocal.program.FindFunction( funcname );
183                 if ( scriptFunction == NULL ) {
184                         gameLocal.Warning( "idTrigger_Multi '%s' at (%s) calls unknown function '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), funcname.c_str() );
185                 }
186         } else {
187                 scriptFunction = NULL;
188         }
189 }
190
191 /*
192 ================
193 idTrigger::Event_Enable
194 ================
195 */
196 void idTrigger::Event_Enable( void ) {
197         Enable();
198 }
199
200 /*
201 ================
202 idTrigger::Event_Disable
203 ================
204 */
205 void idTrigger::Event_Disable( void ) {
206         Disable();
207 }
208
209 /*
210 ================
211 idTrigger::idTrigger
212 ================
213 */
214 idTrigger::idTrigger() {
215         scriptFunction = NULL;
216 }
217
218 /*
219 ================
220 idTrigger::Spawn
221 ================
222 */
223 void idTrigger::Spawn( void ) {
224         GetPhysics()->SetContents( CONTENTS_TRIGGER );
225
226         idStr funcname = spawnArgs.GetString( "call", "" );
227         if ( funcname.Length() ) {
228                 scriptFunction = gameLocal.program.FindFunction( funcname );
229                 if ( scriptFunction == NULL ) {
230                         gameLocal.Warning( "trigger '%s' at (%s) calls unknown function '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), funcname.c_str() );
231                 }
232         } else {
233                 scriptFunction = NULL;
234         }
235 }
236
237
238 /*
239 ===============================================================================
240
241   idTrigger_Multi
242         
243 ===============================================================================
244 */
245
246 const idEventDef EV_TriggerAction( "<triggerAction>", "e" );
247
248 CLASS_DECLARATION( idTrigger, idTrigger_Multi )
249         EVENT( EV_Touch,                        idTrigger_Multi::Event_Touch )
250         EVENT( EV_Activate,                     idTrigger_Multi::Event_Trigger )
251         EVENT( EV_TriggerAction,        idTrigger_Multi::Event_TriggerAction )
252 END_CLASS
253
254
255 /*
256 ================
257 idTrigger_Multi::idTrigger_Multi
258 ================
259 */
260 idTrigger_Multi::idTrigger_Multi( void ) {
261         wait = 0.0f;
262         random = 0.0f;
263         delay = 0.0f;
264         random_delay = 0.0f;
265         nextTriggerTime = 0;
266         removeItem = 0;
267         touchClient = false;
268         touchOther = false;
269         triggerFirst = false;
270         triggerWithSelf = false;
271 }
272
273 /*
274 ================
275 idTrigger_Multi::Save
276 ================
277 */
278 void idTrigger_Multi::Save( idSaveGame *savefile ) const {
279         savefile->WriteFloat( wait );
280         savefile->WriteFloat( random );
281         savefile->WriteFloat( delay );
282         savefile->WriteFloat( random_delay );
283         savefile->WriteInt( nextTriggerTime );
284         savefile->WriteString( requires );
285         savefile->WriteInt( removeItem );
286         savefile->WriteBool( touchClient );
287         savefile->WriteBool( touchOther );
288         savefile->WriteBool( triggerFirst );
289         savefile->WriteBool( triggerWithSelf );
290 }
291
292 /*
293 ================
294 idTrigger_Multi::Restore
295 ================
296 */
297 void idTrigger_Multi::Restore( idRestoreGame *savefile ) {
298         savefile->ReadFloat( wait );
299         savefile->ReadFloat( random );
300         savefile->ReadFloat( delay );
301         savefile->ReadFloat( random_delay );
302         savefile->ReadInt( nextTriggerTime );
303         savefile->ReadString( requires );
304         savefile->ReadInt( removeItem );
305         savefile->ReadBool( touchClient );
306         savefile->ReadBool( touchOther );
307         savefile->ReadBool( triggerFirst );
308         savefile->ReadBool( triggerWithSelf );
309 }
310
311 /*
312 ================
313 idTrigger_Multi::Spawn
314
315 "wait" : Seconds between triggerings, 0.5 default, -1 = one time only.
316 "call" : Script function to call when triggered
317 "random"        wait variance, default is 0
318 Variable sized repeatable trigger.  Must be targeted at one or more entities.
319 so, the basic time between firing is a random time between
320 (wait - random) and (wait + random)
321 ================
322 */
323 void idTrigger_Multi::Spawn( void ) {
324         spawnArgs.GetFloat( "wait", "0.5", wait );
325         spawnArgs.GetFloat( "random", "0", random );
326         spawnArgs.GetFloat( "delay", "0", delay );
327         spawnArgs.GetFloat( "random_delay", "0", random_delay );
328         
329         if ( random && ( random >= wait ) && ( wait >= 0 ) ) {
330                 random = wait - 1;
331                 gameLocal.Warning( "idTrigger_Multi '%s' at (%s) has random >= wait", name.c_str(), GetPhysics()->GetOrigin().ToString(0) );
332         }
333
334         if ( random_delay && ( random_delay >= delay ) && ( delay >= 0 ) ) {
335                 random_delay = delay - 1;
336                 gameLocal.Warning( "idTrigger_Multi '%s' at (%s) has random_delay >= delay", name.c_str(), GetPhysics()->GetOrigin().ToString(0) );
337         }
338
339         spawnArgs.GetString( "requires", "", requires );
340         spawnArgs.GetInt( "removeItem", "0", removeItem );
341         spawnArgs.GetBool( "triggerFirst", "0", triggerFirst );
342         spawnArgs.GetBool( "triggerWithSelf", "0", triggerWithSelf );
343
344         if ( spawnArgs.GetBool( "anyTouch" ) ) {
345                 touchClient = true;
346                 touchOther = true;
347         } else if ( spawnArgs.GetBool( "noTouch" ) ) {
348                 touchClient = false;
349                 touchOther = false;
350         } else if ( spawnArgs.GetBool( "noClient" ) ) {
351                 touchClient = false;
352                 touchOther = true;
353         } else {
354                 touchClient = true;
355                 touchOther = false;
356         }
357
358         nextTriggerTime = 0;
359
360         if ( spawnArgs.GetBool( "flashlight_trigger" ) ) {
361                 GetPhysics()->SetContents( CONTENTS_FLASHLIGHT_TRIGGER );
362         } else {
363                 GetPhysics()->SetContents( CONTENTS_TRIGGER );
364         }
365 }
366
367 /*
368 ================
369 idTrigger_Multi::CheckFacing
370 ================
371 */
372 bool idTrigger_Multi::CheckFacing( idEntity *activator ) {
373         if ( spawnArgs.GetBool( "facing" ) ) {
374                 if ( !activator->IsType( idPlayer::Type ) ) {
375                         return true;
376                 }
377                 idPlayer *player = static_cast< idPlayer* >( activator );
378                 float dot = player->viewAngles.ToForward() * GetPhysics()->GetAxis()[0];
379                 float angle = RAD2DEG( idMath::ACos( dot ) );
380                 if ( angle  > spawnArgs.GetFloat( "angleLimit", "30" ) ) {
381                         return false;
382                 }
383         }
384         return true;
385 }
386
387
388 /*
389 ================
390 idTrigger_Multi::TriggerAction
391 ================
392 */
393 void idTrigger_Multi::TriggerAction( idEntity *activator ) {
394         ActivateTargets( triggerWithSelf ? this : activator );
395         CallScript();
396
397         if ( wait >= 0 ) {
398                 nextTriggerTime = gameLocal.time + SEC2MS( wait + random * gameLocal.random.CRandomFloat() );
399         } else {
400                 // we can't just remove (this) here, because this is a touch function
401                 // called while looping through area links...
402                 nextTriggerTime = gameLocal.time + 1;
403                 PostEventMS( &EV_Remove, 0 );
404         }
405 }
406
407 /*
408 ================
409 idTrigger_Multi::Event_TriggerAction
410 ================
411 */
412 void idTrigger_Multi::Event_TriggerAction( idEntity *activator ) {
413         TriggerAction( activator );
414 }
415
416 /*
417 ================
418 idTrigger_Multi::Event_Trigger
419
420 the trigger was just activated
421 activated should be the entity that originated the activation sequence (ie. the original target)
422 activator should be set to the activator so it can be held through a delay
423 so wait for the delay time before firing
424 ================
425 */
426 void idTrigger_Multi::Event_Trigger( idEntity *activator ) {
427         if ( nextTriggerTime > gameLocal.time ) {
428                 // can't retrigger until the wait is over
429                 return;
430         }
431
432         // see if this trigger requires an item
433         if ( !gameLocal.RequirementMet( activator, requires, removeItem ) ) {
434                 return;
435         }
436
437         if ( !CheckFacing( activator ) ) {
438                 return;
439         }
440
441         if ( triggerFirst ) {
442                 triggerFirst = false;
443                 return;
444         }
445
446         // don't allow it to trigger twice in a single frame
447         nextTriggerTime = gameLocal.time + 1;
448
449         if ( delay > 0 ) {
450                 // don't allow it to trigger again until our delay has passed
451                 nextTriggerTime += SEC2MS( delay + random_delay * gameLocal.random.CRandomFloat() );
452                 PostEventSec( &EV_TriggerAction, delay, activator );
453         } else {
454                 TriggerAction( activator );
455         }
456 }
457
458 /*
459 ================
460 idTrigger_Multi::Event_Touch
461 ================
462 */
463 void idTrigger_Multi::Event_Touch( idEntity *other, trace_t *trace ) {
464         if( triggerFirst ) {
465                 return;
466         }
467
468         bool player = other->IsType( idPlayer::Type );
469         if ( player ) {
470                 if ( !touchClient ) {
471                         return;
472                 }
473                 if ( static_cast< idPlayer * >( other )->spectating ) {
474                         return;
475                 }
476         } else if ( !touchOther ) {
477                 return;
478         }
479
480         if ( nextTriggerTime > gameLocal.time ) {
481                 // can't retrigger until the wait is over
482                 return;
483         }
484
485         // see if this trigger requires an item
486         if ( !gameLocal.RequirementMet( other, requires, removeItem ) ) {
487                 return;
488         }
489
490         if ( !CheckFacing( other ) ) {
491                 return;
492         }
493
494         if ( spawnArgs.GetBool( "toggleTriggerFirst" ) ) {
495                 triggerFirst = true;
496         }
497
498         nextTriggerTime = gameLocal.time + 1;
499         if ( delay > 0 ) {
500                 // don't allow it to trigger again until our delay has passed
501                 nextTriggerTime += SEC2MS( delay + random_delay * gameLocal.random.CRandomFloat() );
502                 PostEventSec( &EV_TriggerAction, delay, other );
503         } else {
504                 TriggerAction( other );
505         }
506 }
507
508 /*
509 ===============================================================================
510
511   idTrigger_EntityName
512         
513 ===============================================================================
514 */
515
516 CLASS_DECLARATION( idTrigger, idTrigger_EntityName )
517         EVENT( EV_Touch,                        idTrigger_EntityName::Event_Touch )
518         EVENT( EV_Activate,                     idTrigger_EntityName::Event_Trigger )
519         EVENT( EV_TriggerAction,        idTrigger_EntityName::Event_TriggerAction )
520 END_CLASS
521
522 /*
523 ================
524 idTrigger_EntityName::idTrigger_EntityName
525 ================
526 */
527 idTrigger_EntityName::idTrigger_EntityName( void ) {
528         wait = 0.0f;
529         random = 0.0f;
530         delay = 0.0f;
531         random_delay = 0.0f;
532         nextTriggerTime = 0;
533         triggerFirst = false;
534 }
535
536 /*
537 ================
538 idTrigger_EntityName::Save
539 ================
540 */
541 void idTrigger_EntityName::Save( idSaveGame *savefile ) const {
542         savefile->WriteFloat( wait );
543         savefile->WriteFloat( random );
544         savefile->WriteFloat( delay );
545         savefile->WriteFloat( random_delay );
546         savefile->WriteInt( nextTriggerTime );
547         savefile->WriteBool( triggerFirst );
548         savefile->WriteString( entityName );
549 }
550
551 /*
552 ================
553 idTrigger_EntityName::Restore
554 ================
555 */
556 void idTrigger_EntityName::Restore( idRestoreGame *savefile ) {
557         savefile->ReadFloat( wait );
558         savefile->ReadFloat( random );
559         savefile->ReadFloat( delay );
560         savefile->ReadFloat( random_delay );
561         savefile->ReadInt( nextTriggerTime );
562         savefile->ReadBool( triggerFirst );
563         savefile->ReadString( entityName );
564 }
565
566 /*
567 ================
568 idTrigger_EntityName::Spawn
569 ================
570 */
571 void idTrigger_EntityName::Spawn( void ) {
572         spawnArgs.GetFloat( "wait", "0.5", wait );
573         spawnArgs.GetFloat( "random", "0", random );
574         spawnArgs.GetFloat( "delay", "0", delay );
575         spawnArgs.GetFloat( "random_delay", "0", random_delay );
576         
577         if ( random && ( random >= wait ) && ( wait >= 0 ) ) {
578                 random = wait - 1;
579                 gameLocal.Warning( "idTrigger_EntityName '%s' at (%s) has random >= wait", name.c_str(), GetPhysics()->GetOrigin().ToString(0) );
580         }
581
582         if ( random_delay && ( random_delay >= delay ) && ( delay >= 0 ) ) {
583                 random_delay = delay - 1;
584                 gameLocal.Warning( "idTrigger_EntityName '%s' at (%s) has random_delay >= delay", name.c_str(), GetPhysics()->GetOrigin().ToString(0) );
585         }
586
587         spawnArgs.GetBool( "triggerFirst", "0", triggerFirst );
588
589         entityName = spawnArgs.GetString( "entityname" );
590         if ( !entityName.Length() ) {
591                 gameLocal.Error( "idTrigger_EntityName '%s' at (%s) doesn't have 'entityname' key specified", name.c_str(), GetPhysics()->GetOrigin().ToString(0) );
592         }
593
594         nextTriggerTime = 0;
595
596         if ( !spawnArgs.GetBool( "noTouch" ) ) {
597                 GetPhysics()->SetContents( CONTENTS_TRIGGER );
598         }
599 }
600
601 /*
602 ================
603 idTrigger_EntityName::TriggerAction
604 ================
605 */
606 void idTrigger_EntityName::TriggerAction( idEntity *activator ) {
607         ActivateTargets( activator );
608         CallScript();
609
610         if ( wait >= 0 ) {
611                 nextTriggerTime = gameLocal.time + SEC2MS( wait + random * gameLocal.random.CRandomFloat() );
612         } else {
613                 // we can't just remove (this) here, because this is a touch function
614                 // called while looping through area links...
615                 nextTriggerTime = gameLocal.time + 1;
616                 PostEventMS( &EV_Remove, 0 );
617         }
618 }
619
620 /*
621 ================
622 idTrigger_EntityName::Event_TriggerAction
623 ================
624 */
625 void idTrigger_EntityName::Event_TriggerAction( idEntity *activator ) {
626         TriggerAction( activator );
627 }
628
629 /*
630 ================
631 idTrigger_EntityName::Event_Trigger
632
633 the trigger was just activated
634 activated should be the entity that originated the activation sequence (ie. the original target)
635 activator should be set to the activator so it can be held through a delay
636 so wait for the delay time before firing
637 ================
638 */
639 void idTrigger_EntityName::Event_Trigger( idEntity *activator ) {
640         if ( nextTriggerTime > gameLocal.time ) {
641                 // can't retrigger until the wait is over
642                 return;
643         }
644
645         if ( !activator || ( activator->name != entityName ) ) {
646                 return;
647         }
648
649         if ( triggerFirst ) {
650                 triggerFirst = false;
651                 return;
652         }
653
654         // don't allow it to trigger twice in a single frame
655         nextTriggerTime = gameLocal.time + 1;
656
657         if ( delay > 0 ) {
658                 // don't allow it to trigger again until our delay has passed
659                 nextTriggerTime += SEC2MS( delay + random_delay * gameLocal.random.CRandomFloat() );
660                 PostEventSec( &EV_TriggerAction, delay, activator );
661         } else {
662                 TriggerAction( activator );
663         }
664 }
665
666 /*
667 ================
668 idTrigger_EntityName::Event_Touch
669 ================
670 */
671 void idTrigger_EntityName::Event_Touch( idEntity *other, trace_t *trace ) {
672         if( triggerFirst ) {
673                 return;
674         }
675
676         if ( nextTriggerTime > gameLocal.time ) {
677                 // can't retrigger until the wait is over
678                 return;
679         }
680
681         if ( !other || ( other->name != entityName ) ) {
682                 return;
683         }
684
685         nextTriggerTime = gameLocal.time + 1;
686         if ( delay > 0 ) {
687                 // don't allow it to trigger again until our delay has passed
688                 nextTriggerTime += SEC2MS( delay + random_delay * gameLocal.random.CRandomFloat() );
689                 PostEventSec( &EV_TriggerAction, delay, other );
690         } else {
691                 TriggerAction( other );
692         }
693 }
694
695 /*
696 ===============================================================================
697
698   idTrigger_Timer
699         
700 ===============================================================================
701 */
702
703 const idEventDef EV_Timer( "<timer>", NULL );
704
705 CLASS_DECLARATION( idTrigger, idTrigger_Timer )
706         EVENT( EV_Timer,                idTrigger_Timer::Event_Timer )
707         EVENT( EV_Activate,             idTrigger_Timer::Event_Use )
708 END_CLASS
709
710 /*
711 ================
712 idTrigger_Timer::idTrigger_Timer
713 ================
714 */
715 idTrigger_Timer::idTrigger_Timer( void ) {
716         random = 0.0f;
717         wait = 0.0f;
718         on = false;
719         delay = 0.0f;
720 }
721
722 /*
723 ================
724 idTrigger_Timer::Save
725 ================
726 */
727 void idTrigger_Timer::Save( idSaveGame *savefile ) const {
728         savefile->WriteFloat( random );
729         savefile->WriteFloat( wait );
730         savefile->WriteBool( on );
731         savefile->WriteFloat( delay );
732         savefile->WriteString( onName );
733         savefile->WriteString( offName );
734 }
735
736 /*
737 ================
738 idTrigger_Timer::Restore
739 ================
740 */
741 void idTrigger_Timer::Restore( idRestoreGame *savefile ) {
742         savefile->ReadFloat( random );
743         savefile->ReadFloat( wait );
744         savefile->ReadBool( on );
745         savefile->ReadFloat( delay );
746         savefile->ReadString( onName );
747         savefile->ReadString( offName );
748 }
749
750 /*
751 ================
752 idTrigger_Timer::Spawn
753
754 Repeatedly fires its targets.
755 Can be turned on or off by using.
756 ================
757 */
758 void idTrigger_Timer::Spawn( void ) {
759         spawnArgs.GetFloat( "random", "1", random );
760         spawnArgs.GetFloat( "wait", "1", wait );
761         spawnArgs.GetBool( "start_on", "0", on );
762         spawnArgs.GetFloat( "delay", "0", delay );
763         onName = spawnArgs.GetString( "onName" );
764         offName = spawnArgs.GetString( "offName" );
765
766         if ( random >= wait && wait >= 0 ) {
767                 random = wait - 0.001;
768                 gameLocal.Warning( "idTrigger_Timer '%s' at (%s) has random >= wait", name.c_str(), GetPhysics()->GetOrigin().ToString(0) );
769         }
770
771         if ( on ) {
772                 PostEventSec( &EV_Timer, delay );
773         }
774 }
775
776 /*
777 ================
778 idTrigger_Timer::Enable
779 ================
780 */
781 void idTrigger_Timer::Enable( void ) {
782         // if off, turn it on
783         if ( !on ) {
784                 on = true;
785                 PostEventSec( &EV_Timer, delay );
786         }
787 }
788
789 /*
790 ================
791 idTrigger_Timer::Disable
792 ================
793 */
794 void idTrigger_Timer::Disable( void ) {
795         // if on, turn it off
796         if ( on ) {
797                 on = false;
798                 CancelEvents( &EV_Timer );
799         }
800 }
801
802 /*
803 ================
804 idTrigger_Timer::Event_Timer
805 ================
806 */
807 void idTrigger_Timer::Event_Timer( void ) {
808         ActivateTargets( this );
809
810         // set time before next firing
811         if ( wait >= 0.0f ) {
812                 PostEventSec( &EV_Timer, wait + gameLocal.random.CRandomFloat() * random );
813         }
814 }
815
816 /*
817 ================
818 idTrigger_Timer::Event_Use
819 ================
820 */
821 void idTrigger_Timer::Event_Use( idEntity *activator ) {
822         // if on, turn it off
823         if ( on ) {
824                 if ( offName.Length() && offName.Icmp( activator->GetName() ) ) {
825                         return;
826                 }
827                 on = false;
828                 CancelEvents( &EV_Timer );
829         } else {
830                 // turn it on
831                 if ( onName.Length() && onName.Icmp( activator->GetName() ) ) {
832                         return;
833                 }
834                 on = true;
835                 PostEventSec( &EV_Timer, delay );
836         }
837 }
838
839 /*
840 ===============================================================================
841
842   idTrigger_Count
843         
844 ===============================================================================
845 */
846
847 CLASS_DECLARATION( idTrigger, idTrigger_Count )
848         EVENT( EV_Activate,     idTrigger_Count::Event_Trigger )
849         EVENT( EV_TriggerAction,        idTrigger_Count::Event_TriggerAction )
850 END_CLASS
851
852 /*
853 ================
854 idTrigger_Count::idTrigger_Count
855 ================
856 */
857 idTrigger_Count::idTrigger_Count( void ) {
858         goal = 0;
859         count = 0;
860         delay = 0.0f;
861 }
862
863 /*
864 ================
865 idTrigger_Count::Save
866 ================
867 */
868 void idTrigger_Count::Save( idSaveGame *savefile ) const {
869         savefile->WriteInt( goal );
870         savefile->WriteInt( count );
871         savefile->WriteFloat( delay );
872 }
873
874 /*
875 ================
876 idTrigger_Count::Restore
877 ================
878 */
879 void idTrigger_Count::Restore( idRestoreGame *savefile ) {
880         savefile->ReadInt( goal );
881         savefile->ReadInt( count );
882         savefile->ReadFloat( delay );
883 }
884
885 /*
886 ================
887 idTrigger_Count::Spawn
888 ================
889 */
890 void idTrigger_Count::Spawn( void ) {
891         spawnArgs.GetInt( "count", "1", goal );
892         spawnArgs.GetFloat( "delay", "0", delay );
893         count = 0;
894 }
895
896 /*
897 ================
898 idTrigger_Count::Event_Trigger
899 ================
900 */
901 void idTrigger_Count::Event_Trigger( idEntity *activator ) {
902         // goal of -1 means trigger has been exhausted
903         if (goal >= 0) {
904                 count++;
905                 if ( count >= goal ) {
906                         if (spawnArgs.GetBool("repeat")) {
907                                 count = 0;
908                         } else {
909                                 goal = -1;
910                         }
911                         PostEventSec( &EV_TriggerAction, delay, activator );
912                 }
913         }
914 }
915
916 /*
917 ================
918 idTrigger_Count::Event_TriggerAction
919 ================
920 */
921 void idTrigger_Count::Event_TriggerAction( idEntity *activator ) {
922         ActivateTargets( activator );
923         CallScript();
924         if ( goal == -1 ) {
925                 PostEventMS( &EV_Remove, 0 );
926         }
927 }
928
929 /*
930 ===============================================================================
931
932   idTrigger_Hurt
933         
934 ===============================================================================
935 */
936
937 CLASS_DECLARATION( idTrigger, idTrigger_Hurt )
938         EVENT( EV_Touch,                idTrigger_Hurt::Event_Touch )
939         EVENT( EV_Activate,             idTrigger_Hurt::Event_Toggle )
940 END_CLASS
941
942
943 /*
944 ================
945 idTrigger_Hurt::idTrigger_Hurt
946 ================
947 */
948 idTrigger_Hurt::idTrigger_Hurt( void ) {
949         on = false;
950         delay = 0.0f;
951         nextTime = 0;
952 }
953
954 /*
955 ================
956 idTrigger_Hurt::Save
957 ================
958 */
959 void idTrigger_Hurt::Save( idSaveGame *savefile ) const {
960         savefile->WriteBool( on );
961         savefile->WriteFloat( delay );
962         savefile->WriteInt( nextTime );
963 }
964
965 /*
966 ================
967 idTrigger_Hurt::Restore
968 ================
969 */
970 void idTrigger_Hurt::Restore( idRestoreGame *savefile ) {
971         savefile->ReadBool( on );
972         savefile->ReadFloat( delay );
973         savefile->ReadInt( nextTime );
974 }
975
976 /*
977 ================
978 idTrigger_Hurt::Spawn
979
980         Damages activator
981         Can be turned on or off by using.
982 ================
983 */
984 void idTrigger_Hurt::Spawn( void ) {
985         spawnArgs.GetBool( "on", "1", on );
986         spawnArgs.GetFloat( "delay", "1.0", delay );
987         nextTime = gameLocal.time;
988         Enable();
989 }
990
991 /*
992 ================
993 idTrigger_Hurt::Event_Touch
994 ================
995 */
996 void idTrigger_Hurt::Event_Touch( idEntity *other, trace_t *trace ) {
997         const char *damage;
998
999         if ( on && other && gameLocal.time >= nextTime ) {
1000                 damage = spawnArgs.GetString( "def_damage", "damage_painTrigger" );
1001                 other->Damage( NULL, NULL, vec3_origin, damage, 1.0f, INVALID_JOINT );
1002
1003                 ActivateTargets( other );
1004                 CallScript();
1005
1006                 nextTime = gameLocal.time + SEC2MS( delay );
1007         }
1008 }
1009
1010 /*
1011 ================
1012 idTrigger_Hurt::Event_Toggle
1013 ================
1014 */
1015 void idTrigger_Hurt::Event_Toggle( idEntity *activator ) {
1016         on = !on;
1017 }
1018
1019
1020 /*
1021 ===============================================================================
1022
1023   idTrigger_Fade
1024
1025 ===============================================================================
1026 */
1027
1028 CLASS_DECLARATION( idTrigger, idTrigger_Fade )
1029         EVENT( EV_Activate,             idTrigger_Fade::Event_Trigger )
1030 END_CLASS
1031
1032 /*
1033 ================
1034 idTrigger_Fade::Event_Trigger
1035 ================
1036 */
1037 void idTrigger_Fade::Event_Trigger( idEntity *activator ) {
1038         idVec4          fadeColor;
1039         int                     fadeTime;
1040         idPlayer        *player;
1041
1042         player = gameLocal.GetLocalPlayer();
1043         if ( player ) {
1044                 fadeColor = spawnArgs.GetVec4( "fadeColor", "0, 0, 0, 1" );
1045                 fadeTime = SEC2MS( spawnArgs.GetFloat( "fadeTime", "0.5" ) );
1046                 player->playerView.Fade( fadeColor, fadeTime );
1047                 PostEventMS( &EV_ActivateTargets, fadeTime, activator );
1048         }
1049 }
1050
1051 /*
1052 ===============================================================================
1053
1054   idTrigger_Touch
1055         
1056 ===============================================================================
1057 */
1058
1059 CLASS_DECLARATION( idTrigger, idTrigger_Touch )
1060         EVENT( EV_Activate,             idTrigger_Touch::Event_Trigger )
1061 END_CLASS
1062
1063
1064 /*
1065 ================
1066 idTrigger_Touch::idTrigger_Touch
1067 ================
1068 */
1069 idTrigger_Touch::idTrigger_Touch( void ) {
1070         clipModel = NULL;
1071 }
1072
1073 /*
1074 ================
1075 idTrigger_Touch::Spawn
1076 ================
1077 */
1078 void idTrigger_Touch::Spawn( void ) {
1079         // get the clip model
1080         clipModel = new idClipModel( GetPhysics()->GetClipModel() );
1081
1082         // remove the collision model from the physics object
1083         GetPhysics()->SetClipModel( NULL, 1.0f );
1084
1085         if ( spawnArgs.GetBool( "start_on" ) ) {
1086                 BecomeActive( TH_THINK );
1087         }
1088 }
1089
1090 /*
1091 ================
1092 idTrigger_Touch::Save
1093 ================
1094 */
1095 void idTrigger_Touch::Save( idSaveGame *savefile ) {
1096         savefile->WriteClipModel( clipModel );
1097 }
1098
1099 /*
1100 ================
1101 idTrigger_Touch::Restore
1102 ================
1103 */
1104 void idTrigger_Touch::Restore( idRestoreGame *savefile ) {
1105         savefile->ReadClipModel( clipModel );
1106 }
1107
1108 /*
1109 ================
1110 idTrigger_Touch::TouchEntities
1111 ================
1112 */
1113 void idTrigger_Touch::TouchEntities( void ) {
1114         int numClipModels, i;
1115         idBounds bounds;
1116         idClipModel *cm, *clipModelList[ MAX_GENTITIES ];
1117
1118         if ( clipModel == NULL || scriptFunction == NULL ) {
1119                 return;
1120         }
1121
1122         bounds.FromTransformedBounds( clipModel->GetBounds(), clipModel->GetOrigin(), clipModel->GetAxis() );
1123         numClipModels = gameLocal.clip.ClipModelsTouchingBounds( bounds, -1, clipModelList, MAX_GENTITIES );
1124
1125         for ( i = 0; i < numClipModels; i++ ) {
1126                 cm = clipModelList[ i ];
1127
1128                 if ( !cm->IsTraceModel() ) {
1129                         continue;
1130                 }
1131
1132                 idEntity *entity = cm->GetEntity();
1133
1134                 if ( !entity ) {
1135                         continue;
1136                 }
1137                 
1138                 if ( !gameLocal.clip.ContentsModel( cm->GetOrigin(), cm, cm->GetAxis(), -1,
1139                                                                         clipModel->Handle(), clipModel->GetOrigin(), clipModel->GetAxis() ) ) {
1140                         continue;
1141                 }
1142
1143                 ActivateTargets( entity );
1144
1145                 idThread *thread = new idThread();
1146                 thread->CallFunction( entity, scriptFunction, false );
1147                 thread->DelayedStart( 0 );
1148         }
1149 }
1150
1151 /*
1152 ================
1153 idTrigger_Touch::Think
1154 ================
1155 */
1156 void idTrigger_Touch::Think( void ) {
1157         if ( thinkFlags & TH_THINK ) {
1158                 TouchEntities();
1159         }
1160         idEntity::Think();
1161 }
1162
1163 /*
1164 ================
1165 idTrigger_Touch::Event_Trigger
1166 ================
1167 */
1168 void idTrigger_Touch::Event_Trigger( idEntity *activator ) {
1169         if ( thinkFlags & TH_THINK ) {
1170                 BecomeInactive( TH_THINK );
1171         } else {
1172                 BecomeActive( TH_THINK );
1173         }
1174 }
1175
1176 /*
1177 ================
1178 idTrigger_Touch::Enable
1179 ================
1180 */
1181 void idTrigger_Touch::Enable( void ) {
1182         BecomeActive( TH_THINK );
1183 }
1184
1185 /*
1186 ================
1187 idTrigger_Touch::Disable
1188 ================
1189 */
1190 void idTrigger_Touch::Disable( void ) {
1191         BecomeInactive( TH_THINK );
1192 }