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 }