DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

Trigger.cpp (32938B)


      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 #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() {
     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 != NULL && 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() {
    122 	GetPhysics()->SetContents( CONTENTS_TRIGGER );
    123 	GetPhysics()->EnableClip();
    124 }
    125 
    126 /*
    127 ================
    128 idTrigger::Disable
    129 ================
    130 */
    131 void idTrigger::Disable() {
    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() 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() 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() {
    197 	Enable();
    198 }
    199 
    200 /*
    201 ================
    202 idTrigger::Event_Disable
    203 ================
    204 */
    205 void idTrigger::Event_Disable() {
    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() {
    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() {
    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() {
    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 		// If the player spawned inside the trigger, the player Spawn function called Think directly,
    403 		// allowing for multiple triggers on a trigger_once.  Increasing the nextTriggerTime prevents it.
    404 		nextTriggerTime = gameLocal.time + 99999;
    405 		PostEventMS( &EV_Remove, 0 );
    406 	}
    407 }
    408 
    409 /*
    410 ================
    411 idTrigger_Multi::Event_TriggerAction
    412 ================
    413 */
    414 void idTrigger_Multi::Event_TriggerAction( idEntity *activator ) {
    415 	TriggerAction( activator );
    416 }
    417 
    418 /*
    419 ================
    420 idTrigger_Multi::Event_Trigger
    421 
    422 the trigger was just activated
    423 activated should be the entity that originated the activation sequence (ie. the original target)
    424 activator should be set to the activator so it can be held through a delay
    425 so wait for the delay time before firing
    426 ================
    427 */
    428 void idTrigger_Multi::Event_Trigger( idEntity *activator ) {
    429 	if ( nextTriggerTime > gameLocal.time ) {
    430 		// can't retrigger until the wait is over
    431 		return;
    432 	}
    433 
    434 	// see if this trigger requires an item
    435 	if ( !gameLocal.RequirementMet( activator, requires, removeItem ) ) {
    436 		return;
    437 	}
    438 
    439 	if ( !CheckFacing( activator ) ) {
    440 		return;
    441 	}
    442 
    443 	if ( triggerFirst ) {
    444 		triggerFirst = false;
    445 		return;
    446 	}
    447 
    448 	// don't allow it to trigger twice in a single frame
    449 	nextTriggerTime = gameLocal.time + 1;
    450 
    451 	if ( delay > 0 ) {
    452 		// don't allow it to trigger again until our delay has passed
    453 		nextTriggerTime += SEC2MS( delay + random_delay * gameLocal.random.CRandomFloat() );
    454 		PostEventSec( &EV_TriggerAction, delay, activator );
    455 	} else {
    456 		TriggerAction( activator );
    457 	}
    458 }
    459 
    460 /*
    461 ================
    462 idTrigger_Multi::Event_Touch
    463 ================
    464 */
    465 void idTrigger_Multi::Event_Touch( idEntity *other, trace_t *trace ) {
    466 	if ( common->IsClient() ) {
    467 		return;
    468 	}
    469 
    470 	if( triggerFirst ) {
    471 		return;
    472 	}
    473 
    474 	bool player = other->IsType( idPlayer::Type );
    475 	if ( player ) {
    476 		if ( !touchClient ) {
    477 			return;
    478 		}
    479 		if ( static_cast< idPlayer * >( other )->spectating ) {
    480 			return;
    481 		}
    482 	} else if ( !touchOther ) {
    483 		return;
    484 	}
    485 
    486 	if ( nextTriggerTime > gameLocal.time ) {
    487 		// can't retrigger until the wait is over
    488 		return;
    489 	}
    490 
    491 	// see if this trigger requires an item
    492 	if ( !gameLocal.RequirementMet( other, requires, removeItem ) ) {
    493 		return;
    494 	}
    495 
    496 	if ( !CheckFacing( other ) ) {
    497 		return;
    498 	}
    499 
    500 	if ( spawnArgs.GetBool( "toggleTriggerFirst" ) ) {
    501 		triggerFirst = true;
    502 	}
    503 
    504 	nextTriggerTime = gameLocal.time + 1;
    505 	if ( delay > 0 ) {
    506 		// don't allow it to trigger again until our delay has passed
    507 		nextTriggerTime += SEC2MS( delay + random_delay * gameLocal.random.CRandomFloat() );
    508 		PostEventSec( &EV_TriggerAction, delay, other );
    509 	} else {
    510 		TriggerAction( other );
    511 	}
    512 }
    513 
    514 /*
    515 ===============================================================================
    516 
    517   idTrigger_EntityName
    518 	
    519 ===============================================================================
    520 */
    521 
    522 CLASS_DECLARATION( idTrigger, idTrigger_EntityName )
    523 	EVENT( EV_Touch,			idTrigger_EntityName::Event_Touch )
    524 	EVENT( EV_Activate,			idTrigger_EntityName::Event_Trigger )
    525 	EVENT( EV_TriggerAction,	idTrigger_EntityName::Event_TriggerAction )
    526 END_CLASS
    527 
    528 /*
    529 ================
    530 idTrigger_EntityName::idTrigger_EntityName
    531 ================
    532 */
    533 idTrigger_EntityName::idTrigger_EntityName() {
    534 	wait = 0.0f;
    535 	random = 0.0f;
    536 	delay = 0.0f;
    537 	random_delay = 0.0f;
    538 	nextTriggerTime = 0;
    539 	triggerFirst = false;
    540 	testPartialName = false;
    541 }
    542 
    543 /*
    544 ================
    545 idTrigger_EntityName::Save
    546 ================
    547 */
    548 void idTrigger_EntityName::Save( idSaveGame *savefile ) const {
    549 	savefile->WriteFloat( wait );
    550 	savefile->WriteFloat( random );
    551 	savefile->WriteFloat( delay );
    552 	savefile->WriteFloat( random_delay );
    553 	savefile->WriteInt( nextTriggerTime );
    554 	savefile->WriteBool( triggerFirst );
    555 	savefile->WriteString( entityName );
    556 	savefile->WriteBool( testPartialName );
    557 }
    558 
    559 /*
    560 ================
    561 idTrigger_EntityName::Restore
    562 ================
    563 */
    564 void idTrigger_EntityName::Restore( idRestoreGame *savefile ) {
    565 	savefile->ReadFloat( wait );
    566 	savefile->ReadFloat( random );
    567 	savefile->ReadFloat( delay );
    568 	savefile->ReadFloat( random_delay );
    569 	savefile->ReadInt( nextTriggerTime );
    570 	savefile->ReadBool( triggerFirst );
    571 	savefile->ReadString( entityName );
    572 	savefile->ReadBool( testPartialName );
    573 }
    574 
    575 /*
    576 ================
    577 idTrigger_EntityName::Spawn
    578 ================
    579 */
    580 void idTrigger_EntityName::Spawn() {
    581 	spawnArgs.GetFloat( "wait", "0.5", wait );
    582 	spawnArgs.GetFloat( "random", "0", random );
    583 	spawnArgs.GetFloat( "delay", "0", delay );
    584 	spawnArgs.GetFloat( "random_delay", "0", random_delay );
    585 	
    586 	if ( random && ( random >= wait ) && ( wait >= 0 ) ) {
    587 		random = wait - 1;
    588 		gameLocal.Warning( "idTrigger_EntityName '%s' at (%s) has random >= wait", name.c_str(), GetPhysics()->GetOrigin().ToString(0) );
    589 	}
    590 
    591 	if ( random_delay && ( random_delay >= delay ) && ( delay >= 0 ) ) {
    592 		random_delay = delay - 1;
    593 		gameLocal.Warning( "idTrigger_EntityName '%s' at (%s) has random_delay >= delay", name.c_str(), GetPhysics()->GetOrigin().ToString(0) );
    594 	}
    595 
    596 	spawnArgs.GetBool( "triggerFirst", "0", triggerFirst );
    597 
    598 	entityName = spawnArgs.GetString( "entityname" );
    599 	if ( !entityName.Length() ) {
    600 		gameLocal.Error( "idTrigger_EntityName '%s' at (%s) doesn't have 'entityname' key specified", name.c_str(), GetPhysics()->GetOrigin().ToString(0) );
    601 	}
    602 
    603 	nextTriggerTime = 0;
    604 
    605 	if ( !spawnArgs.GetBool( "noTouch" ) ) {
    606 		GetPhysics()->SetContents( CONTENTS_TRIGGER );
    607 	}
    608 
    609 	testPartialName = spawnArgs.GetBool( "testPartialName", testPartialName );
    610 }
    611 
    612 /*
    613 ================
    614 idTrigger_EntityName::TriggerAction
    615 ================
    616 */
    617 void idTrigger_EntityName::TriggerAction( idEntity *activator ) {
    618 	ActivateTargets( activator );
    619 	CallScript();
    620 
    621 	if ( wait >= 0 ) {
    622 		nextTriggerTime = gameLocal.time + SEC2MS( wait + random * gameLocal.random.CRandomFloat() );
    623 	} else {
    624 		// we can't just remove (this) here, because this is a touch function
    625 		// called while looping through area links...
    626 		nextTriggerTime = gameLocal.time + 1;
    627 		PostEventMS( &EV_Remove, 0 );
    628 	}
    629 }
    630 
    631 /*
    632 ================
    633 idTrigger_EntityName::Event_TriggerAction
    634 ================
    635 */
    636 void idTrigger_EntityName::Event_TriggerAction( idEntity *activator ) {
    637 	TriggerAction( activator );
    638 }
    639 
    640 /*
    641 ================
    642 idTrigger_EntityName::Event_Trigger
    643 
    644 the trigger was just activated
    645 activated should be the entity that originated the activation sequence (ie. the original target)
    646 activator should be set to the activator so it can be held through a delay
    647 so wait for the delay time before firing
    648 ================
    649 */
    650 void idTrigger_EntityName::Event_Trigger( idEntity *activator ) {
    651 	if ( nextTriggerTime > gameLocal.time ) {
    652 		// can't retrigger until the wait is over
    653 		return;
    654 	}
    655 
    656 	bool validEntity = false;
    657 	if ( activator ) {
    658 		if ( testPartialName ) {
    659 			if ( activator->name.Find( entityName, false ) >= 0 ) {
    660 				validEntity = true;
    661 			}
    662 		}
    663 		if ( activator->name == entityName ) {
    664 			validEntity = true;
    665 		}
    666 	}
    667 
    668 	if ( !validEntity ) {
    669 		return;
    670 	}
    671 
    672 	if ( triggerFirst ) {
    673 		triggerFirst = false;
    674 		return;
    675 	}
    676 
    677 	// don't allow it to trigger twice in a single frame
    678 	nextTriggerTime = gameLocal.time + 1;
    679 
    680 	if ( delay > 0 ) {
    681 		// don't allow it to trigger again until our delay has passed
    682 		nextTriggerTime += SEC2MS( delay + random_delay * gameLocal.random.CRandomFloat() );
    683 		PostEventSec( &EV_TriggerAction, delay, activator );
    684 	} else {
    685 		TriggerAction( activator );
    686 	}
    687 }
    688 
    689 /*
    690 ================
    691 idTrigger_EntityName::Event_Touch
    692 ================
    693 */
    694 void idTrigger_EntityName::Event_Touch( idEntity *other, trace_t *trace ) {
    695 	if ( common->IsClient() ) {
    696 		return;
    697 	}
    698 		
    699 	if( triggerFirst ) {
    700 		return;
    701 	}
    702 
    703 	if ( nextTriggerTime > gameLocal.time ) {
    704 		// can't retrigger until the wait is over
    705 		return;
    706 	}
    707 
    708 	bool validEntity = false;
    709 	if ( other ) {
    710 		if ( testPartialName ) {
    711 			if ( other->name.Find( entityName, false ) >= 0 ) {
    712 				validEntity = true;
    713 			}
    714 		}
    715 		if ( other->name == entityName ) {
    716 			validEntity = true;
    717 		}
    718 	}
    719 
    720 	if ( !validEntity ) {
    721 		return;
    722 	}
    723 
    724 	nextTriggerTime = gameLocal.time + 1;
    725 	if ( delay > 0 ) {
    726 		// don't allow it to trigger again until our delay has passed
    727 		nextTriggerTime += SEC2MS( delay + random_delay * gameLocal.random.CRandomFloat() );
    728 		PostEventSec( &EV_TriggerAction, delay, other );
    729 	} else {
    730 		TriggerAction( other );
    731 	}
    732 }
    733 
    734 /*
    735 ===============================================================================
    736 
    737   idTrigger_Timer
    738 	
    739 ===============================================================================
    740 */
    741 
    742 const idEventDef EV_Timer( "<timer>", NULL );
    743 
    744 CLASS_DECLARATION( idTrigger, idTrigger_Timer )
    745 	EVENT( EV_Timer,		idTrigger_Timer::Event_Timer )
    746 	EVENT( EV_Activate,		idTrigger_Timer::Event_Use )
    747 END_CLASS
    748 
    749 /*
    750 ================
    751 idTrigger_Timer::idTrigger_Timer
    752 ================
    753 */
    754 idTrigger_Timer::idTrigger_Timer() {
    755 	random = 0.0f;
    756 	wait = 0.0f;
    757 	on = false;
    758 	delay = 0.0f;
    759 }
    760 
    761 /*
    762 ================
    763 idTrigger_Timer::Save
    764 ================
    765 */
    766 void idTrigger_Timer::Save( idSaveGame *savefile ) const {
    767 	savefile->WriteFloat( random );
    768 	savefile->WriteFloat( wait );
    769 	savefile->WriteBool( on );
    770 	savefile->WriteFloat( delay );
    771 	savefile->WriteString( onName );
    772 	savefile->WriteString( offName );
    773 }
    774 
    775 /*
    776 ================
    777 idTrigger_Timer::Restore
    778 ================
    779 */
    780 void idTrigger_Timer::Restore( idRestoreGame *savefile ) {
    781 	savefile->ReadFloat( random );
    782 	savefile->ReadFloat( wait );
    783 	savefile->ReadBool( on );
    784 	savefile->ReadFloat( delay );
    785 	savefile->ReadString( onName );
    786 	savefile->ReadString( offName );
    787 }
    788 
    789 /*
    790 ================
    791 idTrigger_Timer::Spawn
    792 
    793 Repeatedly fires its targets.
    794 Can be turned on or off by using.
    795 ================
    796 */
    797 void idTrigger_Timer::Spawn() {
    798 	spawnArgs.GetFloat( "random", "1", random );
    799 	spawnArgs.GetFloat( "wait", "1", wait );
    800 	spawnArgs.GetBool( "start_on", "0", on );
    801 	spawnArgs.GetFloat( "delay", "0", delay );
    802 	onName = spawnArgs.GetString( "onName" );
    803 	offName = spawnArgs.GetString( "offName" );
    804 
    805 	if ( random >= wait && wait >= 0 ) {
    806 		random = wait - 0.001;
    807 		gameLocal.Warning( "idTrigger_Timer '%s' at (%s) has random >= wait", name.c_str(), GetPhysics()->GetOrigin().ToString(0) );
    808 	}
    809 
    810 	if ( on ) {
    811 		PostEventSec( &EV_Timer, delay );
    812 	}
    813 }
    814 
    815 /*
    816 ================
    817 idTrigger_Timer::Enable
    818 ================
    819 */
    820 void idTrigger_Timer::Enable() {
    821 	// if off, turn it on
    822 	if ( !on ) {
    823 		on = true;
    824 		PostEventSec( &EV_Timer, delay );
    825 	}
    826 }
    827 
    828 /*
    829 ================
    830 idTrigger_Timer::Disable
    831 ================
    832 */
    833 void idTrigger_Timer::Disable() {
    834 	// if on, turn it off
    835 	if ( on ) {
    836 		on = false;
    837 		CancelEvents( &EV_Timer );
    838 	}
    839 }
    840 
    841 /*
    842 ================
    843 idTrigger_Timer::Event_Timer
    844 ================
    845 */
    846 void idTrigger_Timer::Event_Timer() {
    847 	ActivateTargets( this );
    848 
    849 	// set time before next firing
    850 	if ( wait >= 0.0f ) {
    851 		PostEventSec( &EV_Timer, wait + gameLocal.random.CRandomFloat() * random );
    852 	}
    853 }
    854 
    855 /*
    856 ================
    857 idTrigger_Timer::Event_Use
    858 ================
    859 */
    860 void idTrigger_Timer::Event_Use( idEntity *activator ) {
    861 	// if on, turn it off
    862 	if ( on ) {
    863 		if ( offName.Length() && offName.Icmp( activator->GetName() ) ) {
    864 			return;
    865 		}
    866 		on = false;
    867 		CancelEvents( &EV_Timer );
    868 	} else {
    869 		// turn it on
    870 		if ( onName.Length() && onName.Icmp( activator->GetName() ) ) {
    871 			return;
    872 		}
    873 		on = true;
    874 		PostEventSec( &EV_Timer, delay );
    875 	}
    876 }
    877 
    878 /*
    879 ===============================================================================
    880 
    881   idTrigger_Count
    882 	
    883 ===============================================================================
    884 */
    885 
    886 CLASS_DECLARATION( idTrigger, idTrigger_Count )
    887 	EVENT( EV_Activate,	idTrigger_Count::Event_Trigger )
    888 	EVENT( EV_TriggerAction,	idTrigger_Count::Event_TriggerAction )
    889 END_CLASS
    890 
    891 /*
    892 ================
    893 idTrigger_Count::idTrigger_Count
    894 ================
    895 */
    896 idTrigger_Count::idTrigger_Count() {
    897 	goal = 0;
    898 	count = 0;
    899 	delay = 0.0f;
    900 }
    901 
    902 /*
    903 ================
    904 idTrigger_Count::Save
    905 ================
    906 */
    907 void idTrigger_Count::Save( idSaveGame *savefile ) const {
    908 	savefile->WriteInt( goal );
    909 	savefile->WriteInt( count );
    910 	savefile->WriteFloat( delay );
    911 }
    912 
    913 /*
    914 ================
    915 idTrigger_Count::Restore
    916 ================
    917 */
    918 void idTrigger_Count::Restore( idRestoreGame *savefile ) {
    919 	savefile->ReadInt( goal );
    920 	savefile->ReadInt( count );
    921 	savefile->ReadFloat( delay );
    922 }
    923 
    924 /*
    925 ================
    926 idTrigger_Count::Spawn
    927 ================
    928 */
    929 void idTrigger_Count::Spawn() {
    930 	spawnArgs.GetInt( "count", "1", goal );
    931 	spawnArgs.GetFloat( "delay", "0", delay );
    932 	count = 0;
    933 }
    934 
    935 /*
    936 ================
    937 idTrigger_Count::Event_Trigger
    938 ================
    939 */
    940 void idTrigger_Count::Event_Trigger( idEntity *activator ) {
    941 	// goal of -1 means trigger has been exhausted
    942 	if (goal >= 0) {
    943 		count++;
    944 		if ( count >= goal ) {
    945 			if (spawnArgs.GetBool("repeat")) {
    946 				count = 0;
    947 			} else {
    948 				goal = -1;
    949 			}
    950 			PostEventSec( &EV_TriggerAction, delay, activator );
    951 		}
    952 	}
    953 }
    954 
    955 /*
    956 ================
    957 idTrigger_Count::Event_TriggerAction
    958 ================
    959 */
    960 void idTrigger_Count::Event_TriggerAction( idEntity *activator ) {
    961 	ActivateTargets( activator );
    962 	CallScript();
    963 	if ( goal == -1 ) {
    964 		PostEventMS( &EV_Remove, 0 );
    965 	}
    966 }
    967 
    968 /*
    969 ===============================================================================
    970 
    971   idTrigger_Hurt
    972 	
    973 ===============================================================================
    974 */
    975 
    976 CLASS_DECLARATION( idTrigger, idTrigger_Hurt )
    977 	EVENT( EV_Touch,		idTrigger_Hurt::Event_Touch )
    978 	EVENT( EV_Activate,		idTrigger_Hurt::Event_Toggle )
    979 END_CLASS
    980 
    981 
    982 /*
    983 ================
    984 idTrigger_Hurt::idTrigger_Hurt
    985 ================
    986 */
    987 idTrigger_Hurt::idTrigger_Hurt() {
    988 	on = false;
    989 	delay = 0.0f;
    990 	nextTime = 0;
    991 }
    992 
    993 /*
    994 ================
    995 idTrigger_Hurt::Save
    996 ================
    997 */
    998 void idTrigger_Hurt::Save( idSaveGame *savefile ) const {
    999 	savefile->WriteBool( on );
   1000 	savefile->WriteFloat( delay );
   1001 	savefile->WriteInt( nextTime );
   1002 }
   1003 
   1004 /*
   1005 ================
   1006 idTrigger_Hurt::Restore
   1007 ================
   1008 */
   1009 void idTrigger_Hurt::Restore( idRestoreGame *savefile ) {
   1010 	savefile->ReadBool( on );
   1011 	savefile->ReadFloat( delay );
   1012 	savefile->ReadInt( nextTime );
   1013 }
   1014 
   1015 /*
   1016 ================
   1017 idTrigger_Hurt::Spawn
   1018 
   1019 	Damages activator
   1020 	Can be turned on or off by using.
   1021 ================
   1022 */
   1023 void idTrigger_Hurt::Spawn() {
   1024 	spawnArgs.GetBool( "on", "1", on );
   1025 	spawnArgs.GetFloat( "delay", "1.0", delay );
   1026 	nextTime = gameLocal.time;
   1027 	Enable();
   1028 }
   1029 
   1030 /*
   1031 ================
   1032 idTrigger_Hurt::Event_Touch
   1033 ================
   1034 */
   1035 void idTrigger_Hurt::Event_Touch( idEntity *other, trace_t *trace ) {
   1036 	const char *damage;
   1037 
   1038 	if ( common->IsClient() ) {
   1039 		return;
   1040 	}
   1041 
   1042 	if ( on && other && gameLocal.time >= nextTime ) {
   1043 		bool playerOnly = spawnArgs.GetBool( "playerOnly" );
   1044 		if ( playerOnly ) {
   1045 			if ( !other->IsType( idPlayer::Type ) ) {
   1046 				return;
   1047 			}
   1048 		}
   1049 		damage = spawnArgs.GetString( "def_damage", "damage_painTrigger" );
   1050 
   1051 		idVec3 dir = vec3_origin;
   1052 		if(spawnArgs.GetBool("kick_from_center", "0")) {
   1053 			dir = other->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
   1054 			dir.Normalize();
   1055 		}
   1056 		other->Damage( NULL, NULL, dir, damage, 1.0f, INVALID_JOINT );
   1057 
   1058 		ActivateTargets( other );
   1059 		CallScript();
   1060 
   1061 		nextTime = gameLocal.time + SEC2MS( delay );
   1062 	}
   1063 }
   1064 
   1065 /*
   1066 ================
   1067 idTrigger_Hurt::Event_Toggle
   1068 ================
   1069 */
   1070 void idTrigger_Hurt::Event_Toggle( idEntity *activator ) {
   1071 	on = !on;
   1072 }
   1073 
   1074 
   1075 /*
   1076 ===============================================================================
   1077 
   1078   idTrigger_Fade
   1079 
   1080 ===============================================================================
   1081 */
   1082 
   1083 CLASS_DECLARATION( idTrigger, idTrigger_Fade )
   1084 	EVENT( EV_Activate,		idTrigger_Fade::Event_Trigger )
   1085 END_CLASS
   1086 
   1087 /*
   1088 ================
   1089 idTrigger_Fade::Event_Trigger
   1090 ================
   1091 */
   1092 void idTrigger_Fade::Event_Trigger( idEntity *activator ) {
   1093 	idVec4		fadeColor;
   1094 	int			fadeTime;
   1095 	idPlayer	*player;
   1096 
   1097 	player = gameLocal.GetLocalPlayer();
   1098 	if ( player ) {
   1099 		fadeColor = spawnArgs.GetVec4( "fadeColor", "0, 0, 0, 1" );
   1100 		fadeTime = SEC2MS( spawnArgs.GetFloat( "fadeTime", "0.5" ) );
   1101 		player->playerView.Fade( fadeColor, fadeTime );
   1102 		PostEventMS( &EV_ActivateTargets, fadeTime, activator );
   1103 	}
   1104 }
   1105 
   1106 /*
   1107 ===============================================================================
   1108 
   1109   idTrigger_Touch
   1110 	
   1111 ===============================================================================
   1112 */
   1113 
   1114 CLASS_DECLARATION( idTrigger, idTrigger_Touch )
   1115 	EVENT( EV_Activate,		idTrigger_Touch::Event_Trigger )
   1116 END_CLASS
   1117 
   1118 
   1119 /*
   1120 ================
   1121 idTrigger_Touch::idTrigger_Touch
   1122 ================
   1123 */
   1124 idTrigger_Touch::idTrigger_Touch() {
   1125 	clipModel = NULL;
   1126 }
   1127 
   1128 /*
   1129 ================
   1130 idTrigger_Touch::Spawn
   1131 ================
   1132 */
   1133 void idTrigger_Touch::Spawn() {
   1134 	// get the clip model
   1135 	clipModel = new (TAG_THREAD) idClipModel( GetPhysics()->GetClipModel() );
   1136 
   1137 	// remove the collision model from the physics object
   1138 	GetPhysics()->SetClipModel( NULL, 1.0f );
   1139 
   1140 	if ( spawnArgs.GetBool( "start_on" ) ) {
   1141 		BecomeActive( TH_THINK );
   1142 	}
   1143 }
   1144 
   1145 /*
   1146 ================
   1147 idTrigger_Touch::Save
   1148 ================
   1149 */
   1150 void idTrigger_Touch::Save( idSaveGame *savefile ) {
   1151 	savefile->WriteClipModel( clipModel );
   1152 }
   1153 
   1154 /*
   1155 ================
   1156 idTrigger_Touch::Restore
   1157 ================
   1158 */
   1159 void idTrigger_Touch::Restore( idRestoreGame *savefile ) {
   1160 	savefile->ReadClipModel( clipModel );
   1161 }
   1162 
   1163 /*
   1164 ================
   1165 idTrigger_Touch::TouchEntities
   1166 ================
   1167 */
   1168 void idTrigger_Touch::TouchEntities() {
   1169 	int numClipModels, i;
   1170 	idBounds bounds;
   1171 	idClipModel *cm, *clipModelList[ MAX_GENTITIES ];
   1172 
   1173 	if ( clipModel == NULL || scriptFunction == NULL ) {
   1174 		return;
   1175 	}
   1176 
   1177 	bounds.FromTransformedBounds( clipModel->GetBounds(), clipModel->GetOrigin(), clipModel->GetAxis() );
   1178 	numClipModels = gameLocal.clip.ClipModelsTouchingBounds( bounds, -1, clipModelList, MAX_GENTITIES );
   1179 
   1180 	for ( i = 0; i < numClipModels; i++ ) {
   1181 		cm = clipModelList[ i ];
   1182 
   1183 		if ( !cm->IsTraceModel() ) {
   1184 			continue;
   1185 		}
   1186 
   1187 		idEntity *entity = cm->GetEntity();
   1188 
   1189 		if ( !entity ) {
   1190 			continue;
   1191 		}
   1192 		
   1193 		if ( !gameLocal.clip.ContentsModel( cm->GetOrigin(), cm, cm->GetAxis(), -1,
   1194 									clipModel->Handle(), clipModel->GetOrigin(), clipModel->GetAxis() ) ) {
   1195 			continue;
   1196 		}
   1197 
   1198 		ActivateTargets( entity );
   1199 
   1200 		idThread *thread = new idThread();
   1201 		thread->CallFunction( entity, scriptFunction, false );
   1202 		thread->DelayedStart( 0 );
   1203 	}
   1204 }
   1205 
   1206 /*
   1207 ================
   1208 idTrigger_Touch::Think
   1209 ================
   1210 */
   1211 void idTrigger_Touch::Think() {
   1212 	if ( thinkFlags & TH_THINK ) {
   1213 		TouchEntities();
   1214 	}
   1215 	idEntity::Think();
   1216 }
   1217 
   1218 /*
   1219 ================
   1220 idTrigger_Touch::Event_Trigger
   1221 ================
   1222 */
   1223 void idTrigger_Touch::Event_Trigger( idEntity *activator ) {
   1224 	if ( thinkFlags & TH_THINK ) {
   1225 		BecomeInactive( TH_THINK );
   1226 	} else {
   1227 		BecomeActive( TH_THINK );
   1228 	}
   1229 }
   1230 
   1231 /*
   1232 ================
   1233 idTrigger_Touch::Enable
   1234 ================
   1235 */
   1236 void idTrigger_Touch::Enable() {
   1237 	BecomeActive( TH_THINK );
   1238 }
   1239 
   1240 /*
   1241 ================
   1242 idTrigger_Touch::Disable
   1243 ================
   1244 */
   1245 void idTrigger_Touch::Disable() {
   1246 	BecomeInactive( TH_THINK );
   1247 }
   1248 
   1249 /*
   1250 ===============================================================================
   1251 
   1252   idTrigger_Flag
   1253 
   1254 ===============================================================================
   1255 */
   1256 
   1257 CLASS_DECLARATION( idTrigger_Multi, idTrigger_Flag )
   1258 	EVENT( EV_Touch, idTrigger_Flag::Event_Touch )
   1259 END_CLASS
   1260 
   1261 idTrigger_Flag::idTrigger_Flag() {
   1262 	team		= -1;
   1263 	player		= false;
   1264 	eventFlag	= NULL;
   1265 }
   1266 
   1267 void idTrigger_Flag::Spawn() {
   1268 	team = spawnArgs.GetInt( "team", "0" );
   1269 	player = spawnArgs.GetBool( "player", "0" );
   1270 
   1271 	idStr funcname = spawnArgs.GetString( "eventflag", "" );
   1272 	if ( funcname.Length() ) {
   1273 		eventFlag = idEventDef::FindEvent( funcname );// gameLocal.program.FindFunction( funcname );//, &idItemTeam::Type );
   1274 		if ( eventFlag == NULL ) {
   1275 			gameLocal.Warning( "trigger '%s' at (%s) event unknown '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), funcname.c_str() );
   1276 		}
   1277 	} else {
   1278 		eventFlag = NULL;
   1279 	}
   1280 
   1281 	idTrigger_Multi::Spawn();
   1282 }
   1283 
   1284 void idTrigger_Flag::Event_Touch( idEntity *other, trace_t *trace ) {
   1285 	idItemTeam * flag = NULL;
   1286 
   1287 	if ( common->IsClient() ) {
   1288 		return;
   1289 	}
   1290 
   1291 	if ( player ) {
   1292 		if ( !other->IsType( idPlayer::Type ) )
   1293 			return;
   1294 
   1295 		idPlayer * player = static_cast<idPlayer *>(other);
   1296 		if ( player->carryingFlag == false )
   1297 			return;
   1298 
   1299 		if ( team != -1 && ( player->team != team || (player->team != 0 && player->team != 1))  )
   1300 			return;
   1301 
   1302 		idItemTeam * flags[2];
   1303 
   1304 		flags[0] = gameLocal.mpGame.GetTeamFlag( 0 );
   1305 		flags[1] = gameLocal.mpGame.GetTeamFlag( 1 );
   1306 
   1307 		int iFriend = 1 - player->team;			// index to the flag player team wants
   1308 		int iOpp	= player->team;				// index to the flag opp team wants
   1309 
   1310 		// flag is captured if :
   1311 		// 1)flag is truely bound to the player
   1312 		// 2)opponent flag has been return
   1313 		if ( flags[iFriend]->carried && !flags[iFriend]->dropped && //flags[iFriend]->IsBoundTo( player ) &&
   1314 			!flags[iOpp]->carried && !flags[iOpp]->dropped )
   1315 			flag = flags[iFriend];
   1316 		else
   1317 			return;
   1318 	} else {
   1319 		if ( !other->IsType( idItemTeam::Type ) )
   1320 			return;
   1321 
   1322 		idItemTeam * item = static_cast<idItemTeam *>( other );
   1323 
   1324 		if ( item->team == team || team == -1 ) {
   1325 			flag = item;
   1326 		}
   1327 		else
   1328 			return;
   1329 	}
   1330 
   1331 	if ( flag ) {
   1332 		switch ( eventFlag->GetNumArgs() ) {
   1333 			default :
   1334 			case 0 :
   1335 				flag->PostEventMS( eventFlag, 0 );
   1336 			break;
   1337 			case 1 :
   1338 				flag->PostEventMS( eventFlag, 0, NULL );
   1339 			break;
   1340 			case 2 :
   1341 				flag->PostEventMS( eventFlag, 0, NULL, NULL );
   1342 			break;
   1343 		}
   1344 
   1345 /*
   1346 		ServerSendEvent( eventFlag->GetEventNum(), NULL, true );
   1347 
   1348 		idThread *thread;
   1349 		if ( scriptFlag ) {
   1350 			thread = new idThread();
   1351 			thread->CallFunction( flag, scriptFlag, false );
   1352 			thread->DelayedStart( 0 );
   1353 		}
   1354 */
   1355 		idTrigger_Multi::Event_Touch( other, trace );
   1356 	}
   1357 }