Misc.cpp (92040B)
1 /* 2 =========================================================================== 3 4 Doom 3 BFG Edition GPL Source Code 5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 6 7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). 8 9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation, either version 3 of the License, or 12 (at your option) any later version. 13 14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>. 21 22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. 23 24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. 25 26 =========================================================================== 27 */ 28 /* 29 30 Various utility objects and functions. 31 32 */ 33 34 #include "../idlib/precompiled.h" 35 #pragma hdrstop 36 37 #include "Game_local.h" 38 39 /* 40 =============================================================================== 41 42 idSpawnableEntity 43 44 A simple, spawnable entity with a model and no functionable ability of it's own. 45 For example, it can be used as a placeholder during development, for marking 46 locations on maps for script, or for simple placed models without any behavior 47 that can be bound to other entities. Should not be subclassed. 48 =============================================================================== 49 */ 50 51 CLASS_DECLARATION( idEntity, idSpawnableEntity ) 52 END_CLASS 53 54 /* 55 ====================== 56 idSpawnableEntity::Spawn 57 ====================== 58 */ 59 void idSpawnableEntity::Spawn() { 60 // this just holds dict information 61 } 62 63 /* 64 =============================================================================== 65 66 idPlayerStart 67 68 =============================================================================== 69 */ 70 71 const idEventDef EV_TeleportStage( "<TeleportStage>", "e" ); 72 73 CLASS_DECLARATION( idEntity, idPlayerStart ) 74 EVENT( EV_Activate, idPlayerStart::Event_TeleportPlayer ) 75 EVENT( EV_TeleportStage, idPlayerStart::Event_TeleportStage ) 76 END_CLASS 77 78 /* 79 =============== 80 idPlayerStart::idPlayerStart 81 ================ 82 */ 83 idPlayerStart::idPlayerStart() { 84 teleportStage = 0; 85 } 86 87 /* 88 =============== 89 idPlayerStart::Spawn 90 ================ 91 */ 92 void idPlayerStart::Spawn() { 93 teleportStage = 0; 94 } 95 96 /* 97 ================ 98 idPlayerStart::Save 99 ================ 100 */ 101 void idPlayerStart::Save( idSaveGame *savefile ) const { 102 savefile->WriteInt( teleportStage ); 103 } 104 105 /* 106 ================ 107 idPlayerStart::Restore 108 ================ 109 */ 110 void idPlayerStart::Restore( idRestoreGame *savefile ) { 111 savefile->ReadInt( teleportStage ); 112 } 113 114 /* 115 ================ 116 idPlayerStart::ClientReceiveEvent 117 ================ 118 */ 119 bool idPlayerStart::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) { 120 int entityNumber; 121 122 switch( event ) { 123 case EVENT_TELEPORTPLAYER: { 124 entityNumber = msg.ReadBits( GENTITYNUM_BITS ); 125 idPlayer *player = static_cast<idPlayer *>( gameLocal.entities[entityNumber] ); 126 if ( player != NULL && player->IsType( idPlayer::Type ) ) { 127 Event_TeleportPlayer( player ); 128 } 129 return true; 130 } 131 default: { 132 return idEntity::ClientReceiveEvent( event, time, msg ); 133 } 134 } 135 } 136 137 /* 138 =============== 139 idPlayerStart::Event_TeleportStage 140 141 FIXME: add functionality to fx system ( could be done with player scripting too ) 142 ================ 143 */ 144 void idPlayerStart::Event_TeleportStage( idEntity *_player ) { 145 idPlayer *player; 146 if ( !_player->IsType( idPlayer::Type ) ) { 147 common->Warning( "idPlayerStart::Event_TeleportStage: entity is not an idPlayer\n" ); 148 return; 149 } 150 player = static_cast<idPlayer*>(_player); 151 float teleportDelay = spawnArgs.GetFloat( "teleportDelay" ); 152 switch ( teleportStage ) { 153 case 0: 154 player->playerView.Flash( colorWhite, 125 ); 155 player->SetInfluenceLevel( INFLUENCE_LEVEL3 ); 156 player->SetInfluenceView( spawnArgs.GetString( "mtr_teleportFx" ), NULL, 0.0f, NULL ); 157 gameSoundWorld->FadeSoundClasses( 0, -20.0f, teleportDelay ); 158 player->StartSound( "snd_teleport_start", SND_CHANNEL_BODY2, 0, false, NULL ); 159 teleportStage++; 160 PostEventSec( &EV_TeleportStage, teleportDelay, player ); 161 break; 162 case 1: 163 gameSoundWorld->FadeSoundClasses( 0, 0.0f, 0.25f ); 164 teleportStage++; 165 PostEventSec( &EV_TeleportStage, 0.25f, player ); 166 break; 167 case 2: 168 player->SetInfluenceView( NULL, NULL, 0.0f, NULL ); 169 TeleportPlayer( player ); 170 player->StopSound( SND_CHANNEL_BODY2, false ); 171 player->SetInfluenceLevel( INFLUENCE_NONE ); 172 teleportStage = 0; 173 break; 174 default: 175 break; 176 } 177 } 178 179 /* 180 =============== 181 idPlayerStart::TeleportPlayer 182 ================ 183 */ 184 void idPlayerStart::TeleportPlayer( idPlayer *player ) { 185 float pushVel = spawnArgs.GetFloat( "push", "300" ); 186 float f = spawnArgs.GetFloat( "visualEffect", "0" ); 187 const char *viewName = spawnArgs.GetString( "visualView", "" ); 188 idEntity *ent = viewName ? gameLocal.FindEntity( viewName ) : NULL; 189 190 SetTimeState ts( player->timeGroup ); 191 192 if ( f && ent != NULL ) { 193 // place in private camera view for some time 194 // the entity needs to teleport to where the camera view is to have the PVS right 195 player->Teleport( ent->GetPhysics()->GetOrigin(), ang_zero, this ); 196 player->StartSound( "snd_teleport_enter", SND_CHANNEL_ANY, 0, false, NULL ); 197 player->SetPrivateCameraView( static_cast<idCamera*>(ent) ); 198 // the player entity knows where to spawn from the previous Teleport call 199 if ( !common->IsClient() ) { 200 player->PostEventSec( &EV_Player_ExitTeleporter, f ); 201 } 202 } else { 203 // direct to exit, Teleport will take care of the killbox 204 player->Teleport( GetPhysics()->GetOrigin(), GetPhysics()->GetAxis().ToAngles(), NULL ); 205 206 // multiplayer hijacked this entity, so only push the player in multiplayer 207 if ( common->IsMultiplayer() ) { 208 player->GetPhysics()->SetLinearVelocity( GetPhysics()->GetAxis()[0] * pushVel ); 209 } 210 } 211 } 212 213 /* 214 =============== 215 idPlayerStart::Event_TeleportPlayer 216 ================ 217 */ 218 void idPlayerStart::Event_TeleportPlayer( idEntity *activator ) { 219 idPlayer *player; 220 221 if ( activator->IsType( idPlayer::Type ) ) { 222 player = static_cast<idPlayer*>( activator ); 223 } else { 224 player = gameLocal.GetLocalPlayer(); 225 } 226 if ( player ) { 227 if ( spawnArgs.GetBool( "visualFx" ) ) { 228 229 teleportStage = 0; 230 Event_TeleportStage( player ); 231 232 } else { 233 234 if ( common->IsServer() ) { 235 idBitMsg msg; 236 byte msgBuf[MAX_EVENT_PARAM_SIZE]; 237 238 msg.InitWrite( msgBuf, sizeof( msgBuf ) ); 239 msg.BeginWriting(); 240 msg.WriteBits( player->entityNumber, GENTITYNUM_BITS ); 241 ServerSendEvent( EVENT_TELEPORTPLAYER, &msg, false ); 242 } 243 244 TeleportPlayer( player ); 245 } 246 } 247 } 248 249 /* 250 =============================================================================== 251 252 idActivator 253 254 =============================================================================== 255 */ 256 257 CLASS_DECLARATION( idEntity, idActivator ) 258 EVENT( EV_Activate, idActivator::Event_Activate ) 259 END_CLASS 260 261 /* 262 =============== 263 idActivator::Save 264 ================ 265 */ 266 void idActivator::Save( idSaveGame *savefile ) const { 267 savefile->WriteBool( stay_on ); 268 } 269 270 /* 271 =============== 272 idActivator::Restore 273 ================ 274 */ 275 void idActivator::Restore( idRestoreGame *savefile ) { 276 savefile->ReadBool( stay_on ); 277 278 if ( stay_on ) { 279 BecomeActive( TH_THINK ); 280 } 281 } 282 283 /* 284 =============== 285 idActivator::Spawn 286 ================ 287 */ 288 void idActivator::Spawn() { 289 bool start_off; 290 291 spawnArgs.GetBool( "stay_on", "0", stay_on ); 292 spawnArgs.GetBool( "start_off", "0", start_off ); 293 294 GetPhysics()->SetClipBox( idBounds( vec3_origin ).Expand( 4 ), 1.0f ); 295 GetPhysics()->SetContents( 0 ); 296 297 if ( !start_off ) { 298 BecomeActive( TH_THINK ); 299 } 300 } 301 302 /* 303 =============== 304 idActivator::Think 305 ================ 306 */ 307 void idActivator::Think() { 308 RunPhysics(); 309 if ( thinkFlags & TH_THINK ) { 310 if ( TouchTriggers() ) { 311 if ( !stay_on ) { 312 BecomeInactive( TH_THINK ); 313 } 314 } 315 } 316 Present(); 317 } 318 319 /* 320 =============== 321 idActivator::Activate 322 ================ 323 */ 324 void idActivator::Event_Activate( idEntity *activator ) { 325 if ( thinkFlags & TH_THINK ) { 326 BecomeInactive( TH_THINK ); 327 } else { 328 BecomeActive( TH_THINK ); 329 } 330 } 331 332 333 /* 334 =============================================================================== 335 336 idPathCorner 337 338 =============================================================================== 339 */ 340 341 CLASS_DECLARATION( idEntity, idPathCorner ) 342 EVENT( AI_RandomPath, idPathCorner::Event_RandomPath ) 343 END_CLASS 344 345 /* 346 ===================== 347 idPathCorner::Spawn 348 ===================== 349 */ 350 void idPathCorner::Spawn() { 351 } 352 353 /* 354 ===================== 355 idPathCorner::DrawDebugInfo 356 ===================== 357 */ 358 void idPathCorner::DrawDebugInfo() { 359 idEntity *ent; 360 idBounds bnds( idVec3( -4.0, -4.0f, -8.0f ), idVec3( 4.0, 4.0f, 64.0f ) ); 361 362 for( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) { 363 if ( !ent->IsType( idPathCorner::Type ) ) { 364 continue; 365 } 366 367 idVec3 org = ent->GetPhysics()->GetOrigin(); 368 gameRenderWorld->DebugBounds( colorRed, bnds, org, 0 ); 369 } 370 } 371 372 /* 373 ============ 374 idPathCorner::RandomPath 375 ============ 376 */ 377 idPathCorner *idPathCorner::RandomPath( const idEntity *source, const idEntity *ignore ) { 378 int i; 379 int num; 380 int which; 381 idEntity *ent; 382 idPathCorner *path[ MAX_GENTITIES ]; 383 384 num = 0; 385 for( i = 0; i < source->targets.Num(); i++ ) { 386 ent = source->targets[ i ].GetEntity(); 387 if ( ent != NULL && ( ent != ignore ) && ent->IsType( idPathCorner::Type ) ) { 388 path[ num++ ] = static_cast<idPathCorner *>( ent ); 389 if ( num >= MAX_GENTITIES ) { 390 break; 391 } 392 } 393 } 394 395 if ( !num ) { 396 return NULL; 397 } 398 399 which = gameLocal.random.RandomInt( num ); 400 return path[ which ]; 401 } 402 403 /* 404 ===================== 405 idPathCorner::Event_RandomPath 406 ===================== 407 */ 408 void idPathCorner::Event_RandomPath() { 409 idPathCorner *path; 410 411 path = RandomPath( this, NULL ); 412 idThread::ReturnEntity( path ); 413 } 414 415 /* 416 =============================================================================== 417 418 idDamagable 419 420 =============================================================================== 421 */ 422 423 const idEventDef EV_RestoreDamagable( "<RestoreDamagable>" ); 424 425 CLASS_DECLARATION( idEntity, idDamagable ) 426 EVENT( EV_Activate, idDamagable::Event_BecomeBroken ) 427 EVENT( EV_RestoreDamagable, idDamagable::Event_RestoreDamagable ) 428 END_CLASS 429 430 /* 431 ================ 432 idDamagable::idDamagable 433 ================ 434 */ 435 idDamagable::idDamagable() { 436 count = 0; 437 nextTriggerTime = 0; 438 } 439 440 /* 441 ================ 442 idDamagable::Save 443 ================ 444 */ 445 void idDamagable::Save( idSaveGame *savefile ) const { 446 savefile->WriteInt( count ); 447 savefile->WriteInt( nextTriggerTime ); 448 } 449 450 /* 451 ================ 452 idDamagable::Restore 453 ================ 454 */ 455 void idDamagable::Restore( idRestoreGame *savefile ) { 456 savefile->ReadInt( count ); 457 savefile->ReadInt( nextTriggerTime ); 458 } 459 460 /* 461 ================ 462 idDamagable::Spawn 463 ================ 464 */ 465 void idDamagable::Spawn() { 466 idStr broken; 467 468 health = spawnArgs.GetInt( "health", "5" ); 469 spawnArgs.GetInt( "count", "1", count ); 470 nextTriggerTime = 0; 471 472 // make sure the model gets cached 473 spawnArgs.GetString( "broken", "", broken ); 474 if ( broken.Length() && !renderModelManager->CheckModel( broken ) ) { 475 gameLocal.Error( "idDamagable '%s' at (%s): cannot load broken model '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), broken.c_str() ); 476 } 477 478 fl.takedamage = true; 479 GetPhysics()->SetContents( CONTENTS_SOLID ); 480 } 481 482 /* 483 ================ 484 idDamagable::BecomeBroken 485 ================ 486 */ 487 void idDamagable::BecomeBroken( idEntity *activator ) { 488 float forceState; 489 int numStates; 490 int cycle; 491 float wait; 492 493 if ( gameLocal.time < nextTriggerTime ) { 494 return; 495 } 496 497 spawnArgs.GetFloat( "wait", "0.1", wait ); 498 nextTriggerTime = gameLocal.time + SEC2MS( wait ); 499 if ( count > 0 ) { 500 count--; 501 if ( !count ) { 502 fl.takedamage = false; 503 } else { 504 health = spawnArgs.GetInt( "health", "5" ); 505 } 506 } 507 508 idStr broken; 509 510 spawnArgs.GetString( "broken", "", broken ); 511 if ( broken.Length() ) { 512 SetModel( broken ); 513 } 514 515 // offset the start time of the shader to sync it to the gameLocal time 516 renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time ); 517 518 spawnArgs.GetInt( "numstates", "1", numStates ); 519 spawnArgs.GetInt( "cycle", "0", cycle ); 520 spawnArgs.GetFloat( "forcestate", "0", forceState ); 521 522 // set the state parm 523 if ( cycle ) { 524 renderEntity.shaderParms[ SHADERPARM_MODE ]++; 525 if ( renderEntity.shaderParms[ SHADERPARM_MODE ] > numStates ) { 526 renderEntity.shaderParms[ SHADERPARM_MODE ] = 0; 527 } 528 } else if ( forceState ) { 529 renderEntity.shaderParms[ SHADERPARM_MODE ] = forceState; 530 } else { 531 renderEntity.shaderParms[ SHADERPARM_MODE ] = gameLocal.random.RandomInt( numStates ) + 1; 532 } 533 534 renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time ); 535 536 ActivateTargets( activator ); 537 538 if ( spawnArgs.GetBool( "hideWhenBroken" ) ) { 539 Hide(); 540 PostEventMS( &EV_RestoreDamagable, nextTriggerTime - gameLocal.time ); 541 BecomeActive( TH_THINK ); 542 } 543 } 544 545 /* 546 ================ 547 idDamagable::Killed 548 ================ 549 */ 550 void idDamagable::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) { 551 if ( gameLocal.time < nextTriggerTime ) { 552 health += damage; 553 return; 554 } 555 556 BecomeBroken( attacker ); 557 } 558 559 /* 560 ================ 561 idDamagable::Hide 562 ================ 563 */ 564 void idDamagable::Hide() { 565 idEntity::Hide(); 566 GetPhysics()->SetContents( 0 ); 567 } 568 569 /* 570 ================ 571 idDamagable::Show 572 ================ 573 */ 574 void idDamagable::Show() { 575 idEntity::Show(); 576 GetPhysics()->SetContents( CONTENTS_SOLID ); 577 } 578 579 /* 580 ================ 581 idDamagable::Event_BecomeBroken 582 ================ 583 */ 584 void idDamagable::Event_BecomeBroken( idEntity *activator ) { 585 BecomeBroken( activator ); 586 } 587 588 /* 589 ================ 590 idDamagable::Event_RestoreDamagable 591 ================ 592 */ 593 void idDamagable::Event_RestoreDamagable() { 594 health = spawnArgs.GetInt( "health", "5" ); 595 Show(); 596 } 597 598 599 /* 600 =============================================================================== 601 602 idExplodable 603 604 =============================================================================== 605 */ 606 607 CLASS_DECLARATION( idEntity, idExplodable ) 608 EVENT( EV_Activate, idExplodable::Event_Explode ) 609 END_CLASS 610 611 /* 612 ================ 613 idExplodable::Spawn 614 ================ 615 */ 616 void idExplodable::Spawn() { 617 Hide(); 618 } 619 620 /* 621 ================ 622 idExplodable::Event_Explode 623 ================ 624 */ 625 void idExplodable::Event_Explode( idEntity *activator ) { 626 const char *temp; 627 628 if ( spawnArgs.GetString( "def_damage", "damage_explosion", &temp ) ) { 629 gameLocal.RadiusDamage( GetPhysics()->GetOrigin(), activator, activator, this, this, temp ); 630 } 631 632 StartSound( "snd_explode", SND_CHANNEL_ANY, 0, false, NULL ); 633 634 // Show() calls UpdateVisuals, so we don't need to call it ourselves after setting the shaderParms 635 renderEntity.shaderParms[SHADERPARM_RED] = 1.0f; 636 renderEntity.shaderParms[SHADERPARM_GREEN] = 1.0f; 637 renderEntity.shaderParms[SHADERPARM_BLUE] = 1.0f; 638 renderEntity.shaderParms[SHADERPARM_ALPHA] = 1.0f; 639 renderEntity.shaderParms[SHADERPARM_TIMEOFFSET] = -MS2SEC( gameLocal.time ); 640 renderEntity.shaderParms[SHADERPARM_DIVERSITY] = 0.0f; 641 Show(); 642 643 PostEventMS( &EV_Remove, 2000 ); 644 645 ActivateTargets( activator ); 646 } 647 648 649 /* 650 =============================================================================== 651 652 idSpring 653 654 =============================================================================== 655 */ 656 657 CLASS_DECLARATION( idEntity, idSpring ) 658 EVENT( EV_PostSpawn, idSpring::Event_LinkSpring ) 659 END_CLASS 660 661 /* 662 ================ 663 idSpring::Think 664 ================ 665 */ 666 void idSpring::Think() { 667 idVec3 start, end, origin; 668 idMat3 axis; 669 670 // run physics 671 RunPhysics(); 672 673 if ( thinkFlags & TH_THINK ) { 674 // evaluate force 675 spring.Evaluate( gameLocal.time ); 676 677 start = p1; 678 if ( ent1->GetPhysics() ) { 679 axis = ent1->GetPhysics()->GetAxis(); 680 origin = ent1->GetPhysics()->GetOrigin(); 681 start = origin + start * axis; 682 } 683 684 end = p2; 685 if ( ent2->GetPhysics() ) { 686 axis = ent2->GetPhysics()->GetAxis(); 687 origin = ent2->GetPhysics()->GetOrigin(); 688 end = origin + p2 * axis; 689 } 690 691 gameRenderWorld->DebugLine( idVec4(1, 1, 0, 1), start, end, 0, true ); 692 } 693 694 Present(); 695 } 696 697 /* 698 ================ 699 idSpring::Event_LinkSpring 700 ================ 701 */ 702 void idSpring::Event_LinkSpring() { 703 idStr name1, name2; 704 705 spawnArgs.GetString( "ent1", "", name1 ); 706 spawnArgs.GetString( "ent2", "", name2 ); 707 708 if ( name1.Length() ) { 709 ent1 = gameLocal.FindEntity( name1 ); 710 if ( ent1 == NULL ) { 711 gameLocal.Error( "idSpring '%s' at (%s): cannot find first entity '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), name1.c_str() ); 712 return; 713 } 714 } 715 else { 716 ent1 = gameLocal.entities[ENTITYNUM_WORLD]; 717 } 718 719 if ( name2.Length() ) { 720 ent2 = gameLocal.FindEntity( name2 ); 721 if ( ent2 == NULL ) { 722 gameLocal.Error( "idSpring '%s' at (%s): cannot find second entity '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), name2.c_str() ); 723 return; 724 } 725 } 726 else { 727 ent2 = gameLocal.entities[ENTITYNUM_WORLD]; 728 } 729 730 spring.SetPosition( ent1->GetPhysics(), id1, p1, ent2->GetPhysics(), id2, p2 ); 731 BecomeActive( TH_THINK ); 732 } 733 734 /* 735 ================ 736 idSpring::Spawn 737 ================ 738 */ 739 void idSpring::Spawn() { 740 float Kstretch, damping, restLength; 741 742 spawnArgs.GetInt( "id1", "0", id1 ); 743 spawnArgs.GetInt( "id2", "0", id2 ); 744 spawnArgs.GetVector( "point1", "0 0 0", p1 ); 745 spawnArgs.GetVector( "point2", "0 0 0", p2 ); 746 spawnArgs.GetFloat( "constant", "100.0f", Kstretch ); 747 spawnArgs.GetFloat( "damping", "10.0f", damping ); 748 spawnArgs.GetFloat( "restlength", "0.0f", restLength ); 749 750 spring.InitSpring( Kstretch, 0.0f, damping, restLength ); 751 752 ent1 = ent2 = NULL; 753 754 PostEventMS( &EV_PostSpawn, 0 ); 755 } 756 757 /* 758 =============================================================================== 759 760 idForceField 761 762 =============================================================================== 763 */ 764 765 const idEventDef EV_Toggle( "Toggle", NULL ); 766 767 CLASS_DECLARATION( idEntity, idForceField ) 768 EVENT( EV_Activate, idForceField::Event_Activate ) 769 EVENT( EV_Toggle, idForceField::Event_Toggle ) 770 EVENT( EV_FindTargets, idForceField::Event_FindTargets ) 771 END_CLASS 772 773 /* 774 =============== 775 idForceField::Toggle 776 ================ 777 */ 778 void idForceField::Toggle() { 779 if ( thinkFlags & TH_THINK ) { 780 BecomeInactive( TH_THINK ); 781 } else { 782 BecomeActive( TH_THINK ); 783 } 784 } 785 786 787 788 /* 789 ================ 790 idForceField::Think 791 ================ 792 */ 793 void idForceField::ClientThink( const int curTime, const float fraction, const bool predict ) { 794 795 // evaluate force 796 forceField.Evaluate( gameLocal.time ); 797 798 Present(); 799 } 800 801 /* 802 ================ 803 idForceField::Think 804 ================ 805 */ 806 void idForceField::Think() { 807 if ( thinkFlags & TH_THINK ) { 808 // evaluate force 809 forceField.Evaluate( gameLocal.time ); 810 } 811 Present(); 812 } 813 814 /* 815 ================ 816 idForceField::Save 817 ================ 818 */ 819 void idForceField::Save( idSaveGame *savefile ) const { 820 savefile->WriteStaticObject( forceField ); 821 } 822 823 /* 824 ================ 825 idForceField::Restore 826 ================ 827 */ 828 void idForceField::Restore( idRestoreGame *savefile ) { 829 savefile->ReadStaticObject( forceField ); 830 } 831 832 /* 833 ================ 834 idForceField::Spawn 835 ================ 836 */ 837 void idForceField::Spawn() { 838 idVec3 uniform; 839 float explosion, implosion, randomTorque; 840 841 if ( spawnArgs.GetVector( "uniform", "0 0 0", uniform ) ) { 842 forceField.Uniform( uniform ); 843 } else if ( spawnArgs.GetFloat( "explosion", "0", explosion ) ) { 844 forceField.Explosion( explosion ); 845 } else if ( spawnArgs.GetFloat( "implosion", "0", implosion ) ) { 846 forceField.Implosion( implosion ); 847 } 848 849 if ( spawnArgs.GetFloat( "randomTorque", "0", randomTorque ) ) { 850 forceField.RandomTorque( randomTorque ); 851 } 852 853 if ( spawnArgs.GetBool( "applyForce", "0" ) ) { 854 forceField.SetApplyType( FORCEFIELD_APPLY_FORCE ); 855 } else if ( spawnArgs.GetBool( "applyImpulse", "0" ) ) { 856 forceField.SetApplyType( FORCEFIELD_APPLY_IMPULSE ); 857 } else { 858 forceField.SetApplyType( FORCEFIELD_APPLY_VELOCITY ); 859 } 860 861 forceField.SetPlayerOnly( spawnArgs.GetBool( "playerOnly", "0" ) ); 862 forceField.SetMonsterOnly( spawnArgs.GetBool( "monsterOnly", "0" ) ); 863 864 // set the collision model on the force field 865 forceField.SetClipModel( new (TAG_PHYSICS_CLIP_ENTITY) idClipModel( GetPhysics()->GetClipModel() ) ); 866 867 // remove the collision model from the physics object 868 GetPhysics()->SetClipModel( NULL, 1.0f ); 869 870 if ( spawnArgs.GetBool( "start_on" ) ) { 871 BecomeActive( TH_THINK ); 872 } 873 } 874 875 /* 876 =============== 877 idForceField::Event_Toggle 878 ================ 879 */ 880 void idForceField::Event_Toggle() { 881 Toggle(); 882 } 883 884 /* 885 ================ 886 idForceField::Event_Activate 887 ================ 888 */ 889 void idForceField::Event_Activate( idEntity *activator ) { 890 float wait; 891 892 Toggle(); 893 if ( spawnArgs.GetFloat( "wait", "0.01", wait ) ) { 894 PostEventSec( &EV_Toggle, wait ); 895 } 896 } 897 898 /* 899 ================ 900 idForceField::Event_FindTargets 901 ================ 902 */ 903 void idForceField::Event_FindTargets() { 904 FindTargets(); 905 RemoveNullTargets(); 906 if ( targets.Num() ) { 907 forceField.Uniform( targets[0].GetEntity()->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin() ); 908 } 909 } 910 911 912 /* 913 =============================================================================== 914 915 idAnimated 916 917 =============================================================================== 918 */ 919 920 const idEventDef EV_Animated_Start( "<start>" ); 921 const idEventDef EV_LaunchMissiles( "launchMissiles", "ssssdf" ); 922 const idEventDef EV_LaunchMissilesUpdate( "<launchMissiles>", "dddd" ); 923 const idEventDef EV_AnimDone( "<AnimDone>", "d" ); 924 const idEventDef EV_StartRagdoll( "startRagdoll" ); 925 const idEventDef EV_SetAnimation( "setAnimation", "s" ); 926 const idEventDef EV_GetAnimationLength( "getAnimationLength", NULL, 'f' ); 927 928 CLASS_DECLARATION( idAFEntity_Gibbable, idAnimated ) 929 EVENT( EV_Activate, idAnimated::Event_Activate ) 930 EVENT( EV_Animated_Start, idAnimated::Event_Start ) 931 EVENT( EV_StartRagdoll, idAnimated::Event_StartRagdoll ) 932 EVENT( EV_AnimDone, idAnimated::Event_AnimDone ) 933 EVENT( EV_Footstep, idAnimated::Event_Footstep ) 934 EVENT( EV_FootstepLeft, idAnimated::Event_Footstep ) 935 EVENT( EV_FootstepRight, idAnimated::Event_Footstep ) 936 EVENT( EV_LaunchMissiles, idAnimated::Event_LaunchMissiles ) 937 EVENT( EV_LaunchMissilesUpdate, idAnimated::Event_LaunchMissilesUpdate ) 938 EVENT( EV_SetAnimation, idAnimated::Event_SetAnimation ) 939 EVENT( EV_GetAnimationLength, idAnimated::Event_GetAnimationLength ) 940 END_CLASS 941 942 /* 943 =============== 944 idAnimated::idAnimated 945 ================ 946 */ 947 idAnimated::idAnimated() { 948 anim = 0; 949 blendFrames = 0; 950 soundJoint = INVALID_JOINT; 951 activated = false; 952 combatModel = NULL; 953 activator = NULL; 954 current_anim_index = 0; 955 num_anims = 0; 956 achievement = -1; 957 958 } 959 960 /* 961 =============== 962 idAnimated::idAnimated 963 ================ 964 */ 965 idAnimated::~idAnimated() { 966 delete combatModel; 967 combatModel = NULL; 968 } 969 970 /* 971 =============== 972 idAnimated::Save 973 ================ 974 */ 975 void idAnimated::Save( idSaveGame *savefile ) const { 976 savefile->WriteInt( current_anim_index ); 977 savefile->WriteInt( num_anims ); 978 savefile->WriteInt( anim ); 979 savefile->WriteInt( blendFrames ); 980 savefile->WriteJoint( soundJoint ); 981 activator.Save( savefile ); 982 savefile->WriteBool( activated ); 983 } 984 985 /* 986 =============== 987 idAnimated::Restore 988 ================ 989 */ 990 void idAnimated::Restore( idRestoreGame *savefile ) { 991 savefile->ReadInt( current_anim_index ); 992 savefile->ReadInt( num_anims ); 993 savefile->ReadInt( anim ); 994 savefile->ReadInt( blendFrames ); 995 savefile->ReadJoint( soundJoint ); 996 activator.Restore( savefile ); 997 savefile->ReadBool( activated ); 998 } 999 1000 /* 1001 =============== 1002 idAnimated::Spawn 1003 ================ 1004 */ 1005 void idAnimated::Spawn() { 1006 idStr animname; 1007 int anim2; 1008 float wait; 1009 const char *joint; 1010 1011 joint = spawnArgs.GetString( "sound_bone", "origin" ); 1012 soundJoint = animator.GetJointHandle( joint ); 1013 if ( soundJoint == INVALID_JOINT ) { 1014 gameLocal.Warning( "idAnimated '%s' at (%s): cannot find joint '%s' for sound playback", name.c_str(), GetPhysics()->GetOrigin().ToString(0), joint ); 1015 } 1016 1017 LoadAF(); 1018 1019 // allow bullets to collide with a combat model 1020 if ( spawnArgs.GetBool( "combatModel", "0" ) ) { 1021 combatModel = new (TAG_PHYSICS_CLIP_ENTITY) idClipModel( modelDefHandle ); 1022 } 1023 1024 // allow the entity to take damage 1025 if ( spawnArgs.GetBool( "takeDamage", "0" ) ) { 1026 fl.takedamage = true; 1027 } 1028 1029 current_anim_index = 0; 1030 spawnArgs.GetInt( "num_anims", "0", num_anims ); 1031 1032 blendFrames = spawnArgs.GetInt( "blend_in" ); 1033 1034 animname = spawnArgs.GetString( num_anims ? "anim1" : "anim" ); 1035 if ( !animname.Length() ) { 1036 anim = 0; 1037 } else { 1038 anim = animator.GetAnim( animname ); 1039 if ( !anim ) { 1040 gameLocal.Error( "idAnimated '%s' at (%s): cannot find anim '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), animname.c_str() ); 1041 } 1042 } 1043 1044 if ( spawnArgs.GetBool( "hide" ) ) { 1045 Hide(); 1046 1047 if ( !num_anims ) { 1048 blendFrames = 0; 1049 } 1050 } else if ( spawnArgs.GetString( "start_anim", "", animname ) ) { 1051 anim2 = animator.GetAnim( animname ); 1052 if ( !anim2 ) { 1053 gameLocal.Error( "idAnimated '%s' at (%s): cannot find anim '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), animname.c_str() ); 1054 } 1055 animator.CycleAnim( ANIMCHANNEL_ALL, anim2, gameLocal.time, 0 ); 1056 } else if ( anim ) { 1057 // init joints to the first frame of the animation 1058 animator.SetFrame( ANIMCHANNEL_ALL, anim, 1, gameLocal.time, 0 ); 1059 1060 if ( !num_anims ) { 1061 blendFrames = 0; 1062 } 1063 } 1064 1065 spawnArgs.GetFloat( "wait", "-1", wait ); 1066 1067 if ( wait >= 0 ) { 1068 PostEventSec( &EV_Activate, wait, this ); 1069 } 1070 } 1071 1072 /* 1073 =============== 1074 idAnimated::LoadAF 1075 =============== 1076 */ 1077 bool idAnimated::LoadAF() { 1078 idStr fileName; 1079 1080 if ( !spawnArgs.GetString( "ragdoll", "*unknown*", fileName ) ) { 1081 return false; 1082 } 1083 af.SetAnimator( GetAnimator() ); 1084 return af.Load( this, fileName ); 1085 } 1086 1087 /* 1088 =============== 1089 idAnimated::GetPhysicsToSoundTransform 1090 =============== 1091 */ 1092 bool idAnimated::GetPhysicsToSoundTransform( idVec3 &origin, idMat3 &axis ) { 1093 animator.GetJointTransform( soundJoint, gameLocal.time, origin, axis ); 1094 axis = renderEntity.axis; 1095 return true; 1096 } 1097 1098 /* 1099 ================ 1100 idAnimated::StartRagdoll 1101 ================ 1102 */ 1103 bool idAnimated::StartRagdoll() { 1104 // if no AF loaded 1105 if ( !af.IsLoaded() ) { 1106 return false; 1107 } 1108 1109 // if the AF is already active 1110 if ( af.IsActive() ) { 1111 return true; 1112 } 1113 1114 // disable any collision model used 1115 GetPhysics()->DisableClip(); 1116 1117 // start using the AF 1118 af.StartFromCurrentPose( spawnArgs.GetInt( "velocityTime", "0" ) ); 1119 1120 return true; 1121 } 1122 1123 /* 1124 ===================== 1125 idAnimated::PlayNextAnim 1126 ===================== 1127 */ 1128 void idAnimated::PlayNextAnim() { 1129 const char *animname; 1130 int len; 1131 int cycle; 1132 1133 if ( current_anim_index >= num_anims ) { 1134 Hide(); 1135 if ( spawnArgs.GetBool( "remove" ) ) { 1136 PostEventMS( &EV_Remove, 0 ); 1137 } else { 1138 current_anim_index = 0; 1139 } 1140 return; 1141 } 1142 1143 Show(); 1144 current_anim_index++; 1145 1146 spawnArgs.GetString( va( "anim%d", current_anim_index ), NULL, &animname ); 1147 if ( !animname ) { 1148 anim = 0; 1149 animator.Clear( ANIMCHANNEL_ALL, gameLocal.time, FRAME2MS( blendFrames ) ); 1150 return; 1151 } 1152 1153 anim = animator.GetAnim( animname ); 1154 if ( !anim ) { 1155 gameLocal.Warning( "missing anim '%s' on %s", animname, name.c_str() ); 1156 return; 1157 } 1158 1159 if ( g_debugCinematic.GetBool() ) { 1160 gameLocal.Printf( "%d: '%s' start anim '%s'\n", gameLocal.framenum, GetName(), animname ); 1161 } 1162 1163 spawnArgs.GetInt( "cycle", "1", cycle ); 1164 if ( ( current_anim_index == num_anims ) && spawnArgs.GetBool( "loop_last_anim" ) ) { 1165 cycle = -1; 1166 } 1167 1168 animator.CycleAnim( ANIMCHANNEL_ALL, anim, gameLocal.time, FRAME2MS( blendFrames ) ); 1169 animator.CurrentAnim( ANIMCHANNEL_ALL )->SetCycleCount( cycle ); 1170 1171 len = animator.CurrentAnim( ANIMCHANNEL_ALL )->PlayLength(); 1172 if ( len >= 0 ) { 1173 PostEventMS( &EV_AnimDone, len, current_anim_index ); 1174 } 1175 1176 // offset the start time of the shader to sync it to the game time 1177 renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time ); 1178 1179 animator.ForceUpdate(); 1180 UpdateAnimation(); 1181 UpdateVisuals(); 1182 Present(); 1183 } 1184 1185 /* 1186 =============== 1187 idAnimated::Event_StartRagdoll 1188 ================ 1189 */ 1190 void idAnimated::Event_StartRagdoll() { 1191 StartRagdoll(); 1192 } 1193 1194 /* 1195 =============== 1196 idAnimated::Event_AnimDone 1197 ================ 1198 */ 1199 void idAnimated::Event_AnimDone( int animindex ) { 1200 if ( g_debugCinematic.GetBool() ) { 1201 const idAnim *animPtr = animator.GetAnim( anim ); 1202 gameLocal.Printf( "%d: '%s' end anim '%s'\n", gameLocal.framenum, GetName(), animPtr ? animPtr->Name() : "" ); 1203 } 1204 1205 if ( ( animindex >= num_anims ) && spawnArgs.GetBool( "remove" ) ) { 1206 Hide(); 1207 PostEventMS( &EV_Remove, 0 ); 1208 } else if ( spawnArgs.GetBool( "auto_advance" ) ) { 1209 PlayNextAnim(); 1210 } else { 1211 activated = false; 1212 } 1213 1214 ActivateTargets( activator.GetEntity() ); 1215 } 1216 1217 /* 1218 =============== 1219 idAnimated::Event_Activate 1220 ================ 1221 */ 1222 void idAnimated::Event_Activate( idEntity *_activator ) { 1223 if ( num_anims ) { 1224 PlayNextAnim(); 1225 activator = _activator; 1226 return; 1227 } 1228 1229 if ( activated ) { 1230 // already activated 1231 return; 1232 } 1233 1234 // achievement associated with this entity (given on activation) 1235 achievement = spawnArgs.GetInt( "achievement", "-1" ); 1236 if ( achievement != -1 ) { 1237 idPlayer *player = gameLocal.GetLocalPlayer(); 1238 if ( player != NULL ) { 1239 bool shouldCountAction = true; 1240 // only count unlocking lockers if we're in the base game 1241 if ( achievement == ACHIEVEMENT_OPEN_ALL_LOCKERS && player->GetExpansionType() != GAME_BASE ) { 1242 shouldCountAction = false; 1243 } 1244 1245 if ( shouldCountAction ) { 1246 player->GetAchievementManager().EventCompletesAchievement( (achievement_t)achievement ); 1247 } 1248 } 1249 } 1250 1251 activated = true; 1252 activator = _activator; 1253 ProcessEvent( &EV_Animated_Start ); 1254 } 1255 1256 /* 1257 =============== 1258 idAnimated::Event_Start 1259 ================ 1260 */ 1261 void idAnimated::Event_Start() { 1262 int cycle; 1263 int len; 1264 1265 Show(); 1266 1267 if ( num_anims ) { 1268 PlayNextAnim(); 1269 return; 1270 } 1271 1272 if ( anim ) { 1273 if ( g_debugCinematic.GetBool() ) { 1274 const idAnim *animPtr = animator.GetAnim( anim ); 1275 gameLocal.Printf( "%d: '%s' start anim '%s'\n", gameLocal.framenum, GetName(), animPtr ? animPtr->Name() : "" ); 1276 } 1277 spawnArgs.GetInt( "cycle", "1", cycle ); 1278 animator.CycleAnim( ANIMCHANNEL_ALL, anim, gameLocal.time, FRAME2MS( blendFrames ) ); 1279 animator.CurrentAnim( ANIMCHANNEL_ALL )->SetCycleCount( cycle ); 1280 1281 len = animator.CurrentAnim( ANIMCHANNEL_ALL )->PlayLength(); 1282 if ( len >= 0 ) { 1283 PostEventMS( &EV_AnimDone, len, 1 ); 1284 } 1285 } 1286 1287 // offset the start time of the shader to sync it to the game time 1288 renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time ); 1289 1290 animator.ForceUpdate(); 1291 UpdateAnimation(); 1292 UpdateVisuals(); 1293 Present(); 1294 } 1295 1296 /* 1297 =============== 1298 idAnimated::Event_Footstep 1299 =============== 1300 */ 1301 void idAnimated::Event_Footstep() { 1302 StartSound( "snd_footstep", SND_CHANNEL_BODY, 0, false, NULL ); 1303 } 1304 1305 /* 1306 ===================== 1307 idAnimated::Event_LaunchMissilesUpdate 1308 ===================== 1309 */ 1310 void idAnimated::Event_LaunchMissilesUpdate( int launchjoint, int targetjoint, int numshots, int framedelay ) { 1311 idVec3 launchPos; 1312 idVec3 targetPos; 1313 idMat3 axis; 1314 idVec3 dir; 1315 idEntity * ent; 1316 idProjectile * projectile; 1317 const idDict * projectileDef; 1318 const char * projectilename; 1319 1320 projectilename = spawnArgs.GetString( "projectilename" ); 1321 projectileDef = gameLocal.FindEntityDefDict( projectilename, false ); 1322 if ( !projectileDef ) { 1323 gameLocal.Warning( "idAnimated '%s' at (%s): 'launchMissiles' called with unknown projectile '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), projectilename ); 1324 return; 1325 } 1326 1327 StartSound( "snd_missile", SND_CHANNEL_WEAPON, 0, false, NULL ); 1328 1329 animator.GetJointTransform( ( jointHandle_t )launchjoint, gameLocal.time, launchPos, axis ); 1330 launchPos = renderEntity.origin + launchPos * renderEntity.axis; 1331 1332 animator.GetJointTransform( ( jointHandle_t )targetjoint, gameLocal.time, targetPos, axis ); 1333 targetPos = renderEntity.origin + targetPos * renderEntity.axis; 1334 1335 dir = targetPos - launchPos; 1336 dir.Normalize(); 1337 1338 gameLocal.SpawnEntityDef( *projectileDef, &ent, false ); 1339 if ( ent == NULL || !ent->IsType( idProjectile::Type ) ) { 1340 gameLocal.Error( "idAnimated '%s' at (%s): in 'launchMissiles' call '%s' is not an idProjectile", name.c_str(), GetPhysics()->GetOrigin().ToString(0), projectilename ); 1341 return; 1342 } 1343 projectile = ( idProjectile * )ent; 1344 projectile->Create( this, launchPos, dir ); 1345 projectile->Launch( launchPos, dir, vec3_origin ); 1346 1347 if ( numshots > 0 ) { 1348 PostEventMS( &EV_LaunchMissilesUpdate, FRAME2MS( framedelay ), launchjoint, targetjoint, numshots - 1, framedelay ); 1349 } 1350 } 1351 1352 /* 1353 ===================== 1354 idAnimated::Event_LaunchMissiles 1355 ===================== 1356 */ 1357 void idAnimated::Event_LaunchMissiles( const char *projectilename, const char *sound, const char *launchjoint, const char *targetjoint, int numshots, int framedelay ) { 1358 const idDict * projectileDef; 1359 jointHandle_t launch; 1360 jointHandle_t target; 1361 1362 projectileDef = gameLocal.FindEntityDefDict( projectilename, false ); 1363 if ( !projectileDef ) { 1364 gameLocal.Warning( "idAnimated '%s' at (%s): unknown projectile '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), projectilename ); 1365 return; 1366 } 1367 1368 launch = animator.GetJointHandle( launchjoint ); 1369 if ( launch == INVALID_JOINT ) { 1370 gameLocal.Warning( "idAnimated '%s' at (%s): unknown launch joint '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), launchjoint ); 1371 gameLocal.Error( "Unknown joint '%s'", launchjoint ); 1372 } 1373 1374 target = animator.GetJointHandle( targetjoint ); 1375 if ( target == INVALID_JOINT ) { 1376 gameLocal.Warning( "idAnimated '%s' at (%s): unknown target joint '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), targetjoint ); 1377 } 1378 1379 spawnArgs.Set( "projectilename", projectilename ); 1380 spawnArgs.Set( "missilesound", sound ); 1381 1382 CancelEvents( &EV_LaunchMissilesUpdate ); 1383 ProcessEvent( &EV_LaunchMissilesUpdate, launch, target, numshots - 1, framedelay ); 1384 } 1385 1386 /* 1387 ===================== 1388 idAnimated::Event_SetAnimation 1389 ===================== 1390 */ 1391 void idAnimated::Event_SetAnimation( const char *animName ) { 1392 1393 //BSM Nerve: Need to add some error checking so we don't change the animation 1394 //in the middle of the existing animation 1395 anim = animator.GetAnim( animName ); 1396 if ( !anim ) { 1397 gameLocal.Error( "idAnimated '%s' at (%s): cannot find anim '%s'", name.c_str(), GetPhysics()->GetOrigin().ToString(0), animName ); 1398 } 1399 1400 } 1401 1402 /* 1403 ===================== 1404 idAnimated::Event_GetAnimationLength 1405 ===================== 1406 */ 1407 void idAnimated::Event_GetAnimationLength() { 1408 float length = 0; 1409 1410 if(anim) { 1411 length = (float)(animator.AnimLength( anim )) / 1000.f; 1412 } 1413 1414 idThread::ReturnFloat(length); 1415 } 1416 1417 /* 1418 =============================================================================== 1419 1420 idStaticEntity 1421 1422 Some static entities may be optimized into inline geometry by dmap 1423 1424 =============================================================================== 1425 */ 1426 1427 CLASS_DECLARATION( idEntity, idStaticEntity ) 1428 EVENT( EV_Activate, idStaticEntity::Event_Activate ) 1429 END_CLASS 1430 1431 /* 1432 =============== 1433 idStaticEntity::idStaticEntity 1434 =============== 1435 */ 1436 idStaticEntity::idStaticEntity() { 1437 spawnTime = 0; 1438 active = false; 1439 fadeFrom.Set( 1, 1, 1, 1 ); 1440 fadeTo.Set( 1, 1, 1, 1 ); 1441 fadeStart = 0; 1442 fadeEnd = 0; 1443 runGui = false; 1444 } 1445 1446 /* 1447 =============== 1448 idStaticEntity::Save 1449 =============== 1450 */ 1451 void idStaticEntity::Save( idSaveGame *savefile ) const { 1452 savefile->WriteInt( spawnTime ); 1453 savefile->WriteBool( active ); 1454 savefile->WriteVec4( fadeFrom ); 1455 savefile->WriteVec4( fadeTo ); 1456 savefile->WriteInt( fadeStart ); 1457 savefile->WriteInt( fadeEnd ); 1458 savefile->WriteBool( runGui ); 1459 } 1460 1461 /* 1462 =============== 1463 idStaticEntity::Restore 1464 =============== 1465 */ 1466 void idStaticEntity::Restore( idRestoreGame *savefile ) { 1467 savefile->ReadInt( spawnTime ); 1468 savefile->ReadBool( active ); 1469 savefile->ReadVec4( fadeFrom ); 1470 savefile->ReadVec4( fadeTo ); 1471 savefile->ReadInt( fadeStart ); 1472 savefile->ReadInt( fadeEnd ); 1473 savefile->ReadBool( runGui ); 1474 } 1475 1476 /* 1477 =============== 1478 idStaticEntity::Spawn 1479 =============== 1480 */ 1481 void idStaticEntity::Spawn() { 1482 bool solid; 1483 bool hidden; 1484 1485 // an inline static model will not do anything at all 1486 if ( spawnArgs.GetBool( "inline" ) || gameLocal.world->spawnArgs.GetBool( "inlineAllStatics" ) ) { 1487 Hide(); 1488 return; 1489 } 1490 1491 solid = spawnArgs.GetBool( "solid" ); 1492 hidden = spawnArgs.GetBool( "hide" ); 1493 1494 if ( solid && !hidden ) { 1495 GetPhysics()->SetContents( CONTENTS_SOLID ); 1496 } else { 1497 GetPhysics()->SetContents( 0 ); 1498 } 1499 1500 spawnTime = gameLocal.time; 1501 active = false; 1502 1503 idStr model = spawnArgs.GetString( "model" ); 1504 if ( model.Find( ".prt" ) >= 0 ) { 1505 // we want the parametric particles out of sync with each other 1506 renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = gameLocal.random.RandomInt( 32767 ); 1507 } 1508 1509 fadeFrom.Set( 1, 1, 1, 1 ); 1510 fadeTo.Set( 1, 1, 1, 1 ); 1511 fadeStart = 0; 1512 fadeEnd = 0; 1513 1514 // NOTE: this should be used very rarely because it is expensive 1515 runGui = spawnArgs.GetBool( "runGui" ); 1516 if ( runGui ) { 1517 BecomeActive( TH_THINK ); 1518 } 1519 } 1520 1521 /* 1522 ================ 1523 idStaticEntity::ShowEditingDialog 1524 ================ 1525 */ 1526 void idStaticEntity::ShowEditingDialog() { 1527 } 1528 /* 1529 ================ 1530 idStaticEntity::Think 1531 ================ 1532 */ 1533 void idStaticEntity::Think() { 1534 idEntity::Think(); 1535 if ( thinkFlags & TH_THINK ) { 1536 if ( runGui && renderEntity.gui[0] ) { 1537 idPlayer *player = gameLocal.GetLocalPlayer(); 1538 if ( player ) { 1539 if ( !player->objectiveSystemOpen ) { 1540 renderEntity.gui[0]->StateChanged( gameLocal.time, true ); 1541 if ( renderEntity.gui[1] ) { 1542 renderEntity.gui[1]->StateChanged( gameLocal.time, true ); 1543 } 1544 if ( renderEntity.gui[2] ) { 1545 renderEntity.gui[2]->StateChanged( gameLocal.time, true ); 1546 } 1547 } 1548 } 1549 } 1550 if ( fadeEnd > 0 ) { 1551 idVec4 color; 1552 if ( gameLocal.time < fadeEnd ) { 1553 color.Lerp( fadeFrom, fadeTo, ( float )( gameLocal.time - fadeStart ) / ( float )( fadeEnd - fadeStart ) ); 1554 } else { 1555 color = fadeTo; 1556 fadeEnd = 0; 1557 BecomeInactive( TH_THINK ); 1558 } 1559 SetColor( color ); 1560 } 1561 } 1562 } 1563 1564 /* 1565 ================ 1566 idStaticEntity::Fade 1567 ================ 1568 */ 1569 void idStaticEntity::Fade( const idVec4 &to, float fadeTime ) { 1570 GetColor( fadeFrom ); 1571 fadeTo = to; 1572 fadeStart = gameLocal.time; 1573 fadeEnd = gameLocal.time + SEC2MS( fadeTime ); 1574 BecomeActive( TH_THINK ); 1575 } 1576 1577 /* 1578 ================ 1579 idStaticEntity::Hide 1580 ================ 1581 */ 1582 void idStaticEntity::Hide() { 1583 idEntity::Hide(); 1584 GetPhysics()->SetContents( 0 ); 1585 } 1586 1587 /* 1588 ================ 1589 idStaticEntity::Show 1590 ================ 1591 */ 1592 void idStaticEntity::Show() { 1593 idEntity::Show(); 1594 if ( spawnArgs.GetBool( "solid" ) ) { 1595 GetPhysics()->SetContents( CONTENTS_SOLID ); 1596 } 1597 } 1598 1599 /* 1600 ================ 1601 idStaticEntity::Event_Activate 1602 ================ 1603 */ 1604 void idStaticEntity::Event_Activate( idEntity *activator ) { 1605 idStr activateGui; 1606 1607 spawnTime = gameLocal.time; 1608 active = !active; 1609 1610 const idKeyValue *kv = spawnArgs.FindKey( "hide" ); 1611 if ( kv ) { 1612 if ( IsHidden() ) { 1613 Show(); 1614 } else { 1615 Hide(); 1616 } 1617 } 1618 1619 renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( spawnTime ); 1620 renderEntity.shaderParms[5] = active; 1621 // this change should be a good thing, it will automatically turn on 1622 // lights etc.. when triggered so that does not have to be specifically done 1623 // with trigger parms.. it MIGHT break things so need to keep an eye on it 1624 renderEntity.shaderParms[ SHADERPARM_MODE ] = ( renderEntity.shaderParms[ SHADERPARM_MODE ] ) ? 0.0f : 1.0f; 1625 BecomeActive( TH_UPDATEVISUALS ); 1626 } 1627 1628 /* 1629 ================ 1630 idStaticEntity::WriteToSnapshot 1631 ================ 1632 */ 1633 void idStaticEntity::WriteToSnapshot( idBitMsg &msg ) const { 1634 GetPhysics()->WriteToSnapshot( msg ); 1635 WriteBindToSnapshot( msg ); 1636 WriteColorToSnapshot( msg ); 1637 WriteGUIToSnapshot( msg ); 1638 msg.WriteBits( IsHidden()?1:0, 1 ); 1639 } 1640 1641 /* 1642 ================ 1643 idStaticEntity::ReadFromSnapshot 1644 ================ 1645 */ 1646 void idStaticEntity::ReadFromSnapshot( const idBitMsg &msg ) { 1647 bool hidden; 1648 1649 GetPhysics()->ReadFromSnapshot( msg ); 1650 ReadBindFromSnapshot( msg ); 1651 ReadColorFromSnapshot( msg ); 1652 ReadGUIFromSnapshot( msg ); 1653 hidden = msg.ReadBits( 1 ) == 1; 1654 if ( hidden != IsHidden() ) { 1655 if ( hidden ) { 1656 Hide(); 1657 } else { 1658 Show(); 1659 } 1660 } 1661 if ( msg.HasChanged() ) { 1662 UpdateVisuals(); 1663 } 1664 } 1665 1666 1667 /* 1668 =============================================================================== 1669 1670 idFuncEmitter 1671 1672 =============================================================================== 1673 */ 1674 1675 1676 CLASS_DECLARATION( idStaticEntity, idFuncEmitter ) 1677 EVENT( EV_Activate, idFuncEmitter::Event_Activate ) 1678 END_CLASS 1679 1680 /* 1681 =============== 1682 idFuncEmitter::idFuncEmitter 1683 =============== 1684 */ 1685 idFuncEmitter::idFuncEmitter() { 1686 hidden = false; 1687 } 1688 1689 /* 1690 =============== 1691 idFuncEmitter::Spawn 1692 =============== 1693 */ 1694 void idFuncEmitter::Spawn() { 1695 if ( spawnArgs.GetBool( "start_off" ) ) { 1696 hidden = true; 1697 renderEntity.shaderParms[SHADERPARM_PARTICLE_STOPTIME] = MS2SEC( 1 ); 1698 UpdateVisuals(); 1699 } else { 1700 hidden = false; 1701 } 1702 } 1703 1704 /* 1705 =============== 1706 idFuncEmitter::Save 1707 =============== 1708 */ 1709 void idFuncEmitter::Save( idSaveGame *savefile ) const { 1710 savefile->WriteBool( hidden ); 1711 } 1712 1713 /* 1714 =============== 1715 idFuncEmitter::Restore 1716 =============== 1717 */ 1718 void idFuncEmitter::Restore( idRestoreGame *savefile ) { 1719 savefile->ReadBool( hidden ); 1720 } 1721 1722 /* 1723 ================ 1724 idFuncEmitter::Event_Activate 1725 ================ 1726 */ 1727 void idFuncEmitter::Event_Activate( idEntity *activator ) { 1728 if ( hidden || spawnArgs.GetBool( "cycleTrigger" ) ) { 1729 renderEntity.shaderParms[SHADERPARM_PARTICLE_STOPTIME] = 0; 1730 renderEntity.shaderParms[SHADERPARM_TIMEOFFSET] = -MS2SEC( gameLocal.time ); 1731 hidden = false; 1732 } else { 1733 renderEntity.shaderParms[SHADERPARM_PARTICLE_STOPTIME] = MS2SEC( gameLocal.time ); 1734 hidden = true; 1735 } 1736 UpdateVisuals(); 1737 } 1738 1739 /* 1740 ================ 1741 idFuncEmitter::WriteToSnapshot 1742 ================ 1743 */ 1744 void idFuncEmitter::WriteToSnapshot( idBitMsg &msg ) const { 1745 msg.WriteBits( hidden ? 1 : 0, 1 ); 1746 msg.WriteFloat( renderEntity.shaderParms[ SHADERPARM_PARTICLE_STOPTIME ] ); 1747 msg.WriteFloat( renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] ); 1748 } 1749 1750 /* 1751 ================ 1752 idFuncEmitter::ReadFromSnapshot 1753 ================ 1754 */ 1755 void idFuncEmitter::ReadFromSnapshot( const idBitMsg &msg ) { 1756 hidden = msg.ReadBits( 1 ) != 0; 1757 renderEntity.shaderParms[ SHADERPARM_PARTICLE_STOPTIME ] = msg.ReadFloat(); 1758 renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = msg.ReadFloat(); 1759 if ( msg.HasChanged() ) { 1760 UpdateVisuals(); 1761 } 1762 } 1763 1764 /* 1765 =============================================================================== 1766 1767 idFuncShootProjectile 1768 1769 =============================================================================== 1770 */ 1771 1772 1773 CLASS_DECLARATION( idStaticEntity, idFuncShootProjectile ) 1774 EVENT( EV_Activate, idFuncShootProjectile::Event_Activate ) 1775 END_CLASS 1776 1777 /* 1778 =============== 1779 idFuncShootProjectile::idFuncShootProjectile 1780 =============== 1781 */ 1782 idFuncShootProjectile::idFuncShootProjectile() { 1783 mRespawnDelay = 1000; 1784 mRespawnTime = 0; 1785 mShootSpeed = 1000; 1786 mShootDir = idVec3( 0.0f, 0.0f, 1.0f ); 1787 } 1788 1789 /* 1790 =============== 1791 idFuncShootProjectile::Spawn 1792 =============== 1793 */ 1794 void idFuncShootProjectile::Spawn() { 1795 } 1796 1797 /* 1798 =============== 1799 idFuncShootProjectile::Think 1800 =============== 1801 */ 1802 void idFuncShootProjectile::Think() { 1803 if ( thinkFlags & TH_THINK ) { 1804 // time to spawn a new projectile? 1805 if ( mRespawnTime > 0 && mRespawnTime <= gameLocal.GetTime() ) { 1806 const idDict *dict = gameLocal.FindEntityDefDict( mEntityDefName ); 1807 idEntity *ent = NULL; 1808 gameLocal.SpawnEntityDef( *dict, &ent ); 1809 if ( ent != NULL ) { 1810 idProjectile *proj = static_cast<idProjectile *>(ent); 1811 1812 idVec3 pushVel = mShootDir * mShootSpeed; 1813 proj->Create( this, GetPhysics()->GetOrigin(), mShootDir ); 1814 proj->Launch( GetPhysics()->GetOrigin(), mShootDir, pushVel ); 1815 if ( mShootSpeed == 0.0f ) { 1816 proj->GetPhysics()->SetLinearVelocity( vec3_zero ); 1817 } else { 1818 proj->GetPhysics()->SetLinearVelocity( pushVel ); 1819 } 1820 1821 mLastProjectile = proj; 1822 } 1823 if ( mShootSpeed == 0.0f ) { 1824 mRespawnTime = 0; // stationary, respawn when triggered 1825 } else { 1826 mRespawnTime = gameLocal.GetTime() + mRespawnDelay; // moving, respawn after delay 1827 } 1828 } 1829 } 1830 } 1831 1832 /* 1833 =============== 1834 idFuncShootProjectile::Save 1835 =============== 1836 */ 1837 void idFuncShootProjectile::Save( idSaveGame *savefile ) const { 1838 savefile->WriteInt( mRespawnDelay ); 1839 savefile->WriteInt( mRespawnTime ); 1840 savefile->WriteFloat( mShootSpeed ); 1841 savefile->WriteVec3( mShootDir ); 1842 savefile->WriteString( mEntityDefName ); 1843 } 1844 1845 /* 1846 =============== 1847 idFuncShootProjectile::Restore 1848 =============== 1849 */ 1850 void idFuncShootProjectile::Restore( idRestoreGame *savefile ) { 1851 savefile->ReadInt( mRespawnDelay ); 1852 savefile->ReadInt( mRespawnTime ); 1853 savefile->ReadFloat( mShootSpeed ); 1854 savefile->ReadVec3( mShootDir ); 1855 savefile->ReadString( mEntityDefName ); 1856 } 1857 1858 /* 1859 ================ 1860 idFuncShootProjectile::Event_Activate 1861 ================ 1862 */ 1863 void idFuncShootProjectile::Event_Activate( idEntity *activator ) { 1864 if ( ( thinkFlags & TH_THINK ) != 0 ) { 1865 if ( mShootSpeed == 0.0f && mRespawnTime == 0 ) { 1866 mRespawnTime = gameLocal.GetTime(); 1867 return; 1868 } 1869 } 1870 1871 mRespawnDelay = spawnArgs.GetInt( "spawn_delay_ms" ); 1872 mShootSpeed = spawnArgs.GetFloat( "speed" ); 1873 mEntityDefName = spawnArgs.GetString( "def_projectile" ); 1874 if ( targets.Num() > 0 && targets[0].IsValid() ) { 1875 mShootDir = targets[0]->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin(); 1876 mShootDir.Normalize(); 1877 } else { 1878 // stationary projectile, doesn't move and only respawns when triggered 1879 mShootSpeed = 0.0f; 1880 mRespawnTime = 0; 1881 } 1882 1883 if ( ( thinkFlags & TH_THINK ) != 0 ) { 1884 // currently active, deactivate 1885 BecomeInactive( TH_THINK ); 1886 } else { 1887 // currently inactive, activate 1888 BecomeActive( TH_THINK ); 1889 mRespawnTime = gameLocal.GetTime(); 1890 } 1891 } 1892 1893 /* 1894 ================ 1895 idFuncShootProjectile::WriteToSnapshot 1896 ================ 1897 */ 1898 void idFuncShootProjectile::WriteToSnapshot( idBitMsg &msg ) const { 1899 // msg.WriteBits( hidden ? 1 : 0, 1 ); 1900 // msg.WriteFloat( renderEntity.shaderParms[ SHADERPARM_PARTICLE_STOPTIME ] ); 1901 // msg.WriteFloat( renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] ); 1902 } 1903 1904 /* 1905 ================ 1906 idFuncShootProjectile::ReadFromSnapshot 1907 ================ 1908 */ 1909 void idFuncShootProjectile::ReadFromSnapshot( const idBitMsg &msg ) { 1910 // hidden = msg.ReadBits( 1 ) != 0; 1911 // renderEntity.shaderParms[ SHADERPARM_PARTICLE_STOPTIME ] = msg.ReadFloat(); 1912 // renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = msg.ReadFloat(); 1913 // if ( msg.HasChanged() ) { 1914 // UpdateVisuals(); 1915 // } 1916 } 1917 1918 1919 /* 1920 =============================================================================== 1921 1922 idFuncSplat 1923 1924 =============================================================================== 1925 */ 1926 1927 1928 const idEventDef EV_Splat( "<Splat>" ); 1929 CLASS_DECLARATION( idFuncEmitter, idFuncSplat ) 1930 EVENT( EV_Activate, idFuncSplat::Event_Activate ) 1931 EVENT( EV_Splat, idFuncSplat::Event_Splat ) 1932 END_CLASS 1933 1934 /* 1935 =============== 1936 idFuncSplat::idFuncSplat 1937 =============== 1938 */ 1939 idFuncSplat::idFuncSplat() { 1940 } 1941 1942 /* 1943 =============== 1944 idFuncSplat::Spawn 1945 =============== 1946 */ 1947 void idFuncSplat::Spawn() { 1948 } 1949 1950 /* 1951 ================ 1952 idFuncSplat::Event_Splat 1953 ================ 1954 */ 1955 void idFuncSplat::Event_Splat() { 1956 const char *splat = NULL; 1957 int count = spawnArgs.GetInt( "splatCount", "1" ); 1958 for ( int i = 0; i < count; i++ ) { 1959 splat = spawnArgs.RandomPrefix( "mtr_splat", gameLocal.random ); 1960 if ( splat != NULL && *splat != NULL ) { 1961 float size = spawnArgs.GetFloat( "splatSize", "128" ); 1962 float dist = spawnArgs.GetFloat( "splatDistance", "128" ); 1963 float angle = spawnArgs.GetFloat( "splatAngle", "0" ); 1964 gameLocal.ProjectDecal( GetPhysics()->GetOrigin(), GetPhysics()->GetAxis()[2], dist, true, size, splat, angle ); 1965 } 1966 } 1967 StartSound( "snd_splat", SND_CHANNEL_ANY, 0, false, NULL ); 1968 } 1969 1970 /* 1971 ================ 1972 idFuncSplat::Event_Activate 1973 ================ 1974 */ 1975 void idFuncSplat::Event_Activate( idEntity *activator ) { 1976 idFuncEmitter::Event_Activate( activator ); 1977 PostEventSec( &EV_Splat, spawnArgs.GetFloat( "splatDelay", "0.25" ) ); 1978 StartSound( "snd_spurt", SND_CHANNEL_ANY, 0, false, NULL ); 1979 } 1980 1981 /* 1982 =============================================================================== 1983 1984 idFuncSmoke 1985 1986 =============================================================================== 1987 */ 1988 1989 CLASS_DECLARATION( idEntity, idFuncSmoke ) 1990 EVENT( EV_Activate, idFuncSmoke::Event_Activate ) 1991 END_CLASS 1992 1993 /* 1994 =============== 1995 idFuncSmoke::idFuncSmoke 1996 =============== 1997 */ 1998 idFuncSmoke::idFuncSmoke() { 1999 smokeTime = 0; 2000 smoke = NULL; 2001 restart = false; 2002 } 2003 2004 /* 2005 =============== 2006 idFuncSmoke::Save 2007 =============== 2008 */ 2009 void idFuncSmoke::Save( idSaveGame *savefile ) const { 2010 savefile->WriteInt( smokeTime ); 2011 savefile->WriteParticle( smoke ); 2012 savefile->WriteBool( restart ); 2013 } 2014 2015 /* 2016 =============== 2017 idFuncSmoke::Restore 2018 =============== 2019 */ 2020 void idFuncSmoke::Restore( idRestoreGame *savefile ) { 2021 savefile->ReadInt( smokeTime ); 2022 savefile->ReadParticle( smoke ); 2023 savefile->ReadBool( restart ); 2024 } 2025 2026 /* 2027 =============== 2028 idFuncSmoke::Spawn 2029 =============== 2030 */ 2031 void idFuncSmoke::Spawn() { 2032 const char *smokeName = spawnArgs.GetString( "smoke" ); 2033 if ( *smokeName != '\0' ) { 2034 smoke = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) ); 2035 } else { 2036 smoke = NULL; 2037 } 2038 if ( spawnArgs.GetBool( "start_off" ) ) { 2039 smokeTime = 0; 2040 restart = false; 2041 } else if ( smoke ) { 2042 smokeTime = gameLocal.time; 2043 BecomeActive( TH_UPDATEPARTICLES ); 2044 restart = true; 2045 } 2046 GetPhysics()->SetContents( 0 ); 2047 } 2048 2049 /* 2050 ================ 2051 idFuncSmoke::Event_Activate 2052 ================ 2053 */ 2054 void idFuncSmoke::Event_Activate( idEntity *activator ) { 2055 if ( thinkFlags & TH_UPDATEPARTICLES ) { 2056 restart = false; 2057 return; 2058 } else { 2059 BecomeActive( TH_UPDATEPARTICLES ); 2060 restart = true; 2061 smokeTime = gameLocal.time; 2062 } 2063 } 2064 2065 /* 2066 =============== 2067 idFuncSmoke::Think 2068 ================ 2069 */ 2070 void idFuncSmoke::Think() { 2071 2072 // if we are completely closed off from the player, don't do anything at all 2073 if ( CheckDormant() || smoke == NULL || smokeTime == -1 ) { 2074 return; 2075 } 2076 2077 if ( ( thinkFlags & TH_UPDATEPARTICLES) && !IsHidden() ) { 2078 if ( !gameLocal.smokeParticles->EmitSmoke( smoke, smokeTime, gameLocal.random.CRandomFloat(), GetPhysics()->GetOrigin(), GetPhysics()->GetAxis(), timeGroup /*_D3XP*/ ) ) { 2079 if ( restart ) { 2080 smokeTime = gameLocal.time; 2081 } else { 2082 smokeTime = 0; 2083 BecomeInactive( TH_UPDATEPARTICLES ); 2084 } 2085 } 2086 } 2087 2088 } 2089 2090 2091 /* 2092 =============================================================================== 2093 2094 idTextEntity 2095 2096 =============================================================================== 2097 */ 2098 2099 CLASS_DECLARATION( idEntity, idTextEntity ) 2100 END_CLASS 2101 2102 /* 2103 ================ 2104 idTextEntity::Spawn 2105 ================ 2106 */ 2107 void idTextEntity::Spawn() { 2108 // these are cached as the are used each frame 2109 text = spawnArgs.GetString( "text" ); 2110 playerOriented = spawnArgs.GetBool( "playerOriented" ); 2111 bool force = spawnArgs.GetBool( "force" ); 2112 if ( developer.GetBool() || force ) { 2113 BecomeActive(TH_THINK); 2114 } 2115 } 2116 2117 /* 2118 ================ 2119 idTextEntity::Save 2120 ================ 2121 */ 2122 void idTextEntity::Save( idSaveGame *savefile ) const { 2123 savefile->WriteString( text ); 2124 savefile->WriteBool( playerOriented ); 2125 } 2126 2127 /* 2128 ================ 2129 idTextEntity::Restore 2130 ================ 2131 */ 2132 void idTextEntity::Restore( idRestoreGame *savefile ) { 2133 savefile->ReadString( text ); 2134 savefile->ReadBool( playerOriented ); 2135 } 2136 2137 /* 2138 ================ 2139 idTextEntity::Think 2140 ================ 2141 */ 2142 void idTextEntity::Think() { 2143 if ( thinkFlags & TH_THINK ) { 2144 gameRenderWorld->DrawText( text, GetPhysics()->GetOrigin(), 0.25, colorWhite, playerOriented ? gameLocal.GetLocalPlayer()->viewAngles.ToMat3() : GetPhysics()->GetAxis().Transpose(), 1 ); 2145 for ( int i = 0; i < targets.Num(); i++ ) { 2146 if ( targets[i].GetEntity() ) { 2147 gameRenderWorld->DebugArrow( colorBlue, GetPhysics()->GetOrigin(), targets[i].GetEntity()->GetPhysics()->GetOrigin(), 1 ); 2148 } 2149 } 2150 } else { 2151 BecomeInactive( TH_ALL ); 2152 } 2153 } 2154 2155 2156 /* 2157 =============================================================================== 2158 2159 idVacuumSeperatorEntity 2160 2161 Can be triggered to let vacuum through a portal (blown out window) 2162 2163 =============================================================================== 2164 */ 2165 2166 CLASS_DECLARATION( idEntity, idVacuumSeparatorEntity ) 2167 EVENT( EV_Activate, idVacuumSeparatorEntity::Event_Activate ) 2168 END_CLASS 2169 2170 2171 /* 2172 ================ 2173 idVacuumSeparatorEntity::idVacuumSeparatorEntity 2174 ================ 2175 */ 2176 idVacuumSeparatorEntity::idVacuumSeparatorEntity() { 2177 portal = 0; 2178 } 2179 2180 /* 2181 ================ 2182 idVacuumSeparatorEntity::Save 2183 ================ 2184 */ 2185 void idVacuumSeparatorEntity::Save( idSaveGame *savefile ) const { 2186 savefile->WriteInt( (int)portal ); 2187 savefile->WriteInt( gameRenderWorld->GetPortalState( portal ) ); 2188 } 2189 2190 /* 2191 ================ 2192 idVacuumSeparatorEntity::Restore 2193 ================ 2194 */ 2195 void idVacuumSeparatorEntity::Restore( idRestoreGame *savefile ) { 2196 int state; 2197 2198 savefile->ReadInt( (int &)portal ); 2199 savefile->ReadInt( state ); 2200 2201 gameLocal.SetPortalState( portal, state ); 2202 } 2203 2204 /* 2205 ================ 2206 idVacuumSeparatorEntity::Spawn 2207 ================ 2208 */ 2209 void idVacuumSeparatorEntity::Spawn() { 2210 idBounds b; 2211 2212 b = idBounds( spawnArgs.GetVector( "origin" ) ).Expand( 16 ); 2213 portal = gameRenderWorld->FindPortal( b ); 2214 if ( !portal ) { 2215 gameLocal.Warning( "VacuumSeparator '%s' didn't contact a portal", spawnArgs.GetString( "name" ) ); 2216 return; 2217 } 2218 gameLocal.SetPortalState( portal, PS_BLOCK_AIR | PS_BLOCK_LOCATION ); 2219 } 2220 2221 /* 2222 ================ 2223 idVacuumSeparatorEntity::Event_Activate 2224 ================ 2225 */ 2226 void idVacuumSeparatorEntity::Event_Activate( idEntity *activator ) { 2227 if ( !portal ) { 2228 return; 2229 } 2230 gameLocal.SetPortalState( portal, PS_BLOCK_NONE ); 2231 } 2232 2233 2234 /* 2235 =============================================================================== 2236 2237 idLocationSeparatorEntity 2238 2239 =============================================================================== 2240 */ 2241 2242 CLASS_DECLARATION( idEntity, idLocationSeparatorEntity ) 2243 END_CLASS 2244 2245 /* 2246 ================ 2247 idLocationSeparatorEntity::Spawn 2248 ================ 2249 */ 2250 void idLocationSeparatorEntity::Spawn() { 2251 idBounds b; 2252 2253 b = idBounds( spawnArgs.GetVector( "origin" ) ).Expand( 16 ); 2254 qhandle_t portal = gameRenderWorld->FindPortal( b ); 2255 if ( !portal ) { 2256 gameLocal.Warning( "LocationSeparator '%s' didn't contact a portal", spawnArgs.GetString( "name" ) ); 2257 } 2258 gameLocal.SetPortalState( portal, PS_BLOCK_LOCATION ); 2259 } 2260 2261 2262 /* 2263 =============================================================================== 2264 2265 idVacuumEntity 2266 2267 Levels should only have a single vacuum entity. 2268 2269 =============================================================================== 2270 */ 2271 2272 CLASS_DECLARATION( idEntity, idVacuumEntity ) 2273 END_CLASS 2274 2275 /* 2276 ================ 2277 idVacuumEntity::Spawn 2278 ================ 2279 */ 2280 void idVacuumEntity::Spawn() { 2281 if ( gameLocal.vacuumAreaNum != -1 ) { 2282 gameLocal.Warning( "idVacuumEntity::Spawn: multiple idVacuumEntity in level" ); 2283 return; 2284 } 2285 2286 idVec3 org = spawnArgs.GetVector( "origin" ); 2287 2288 gameLocal.vacuumAreaNum = gameRenderWorld->PointInArea( org ); 2289 } 2290 2291 2292 /* 2293 =============================================================================== 2294 2295 idLocationEntity 2296 2297 =============================================================================== 2298 */ 2299 2300 CLASS_DECLARATION( idEntity, idLocationEntity ) 2301 END_CLASS 2302 2303 /* 2304 ====================== 2305 idLocationEntity::Spawn 2306 ====================== 2307 */ 2308 void idLocationEntity::Spawn() { 2309 idStr realName; 2310 2311 // this just holds dict information 2312 2313 // if "location" not already set, use the entity name. 2314 if ( !spawnArgs.GetString( "location", "", realName ) ) { 2315 spawnArgs.Set( "location", name ); 2316 } 2317 } 2318 2319 /* 2320 ====================== 2321 idLocationEntity::GetLocation 2322 ====================== 2323 */ 2324 const char *idLocationEntity::GetLocation() const { 2325 return spawnArgs.GetString( "location" ); 2326 } 2327 2328 /* 2329 =============================================================================== 2330 2331 idBeam 2332 2333 =============================================================================== 2334 */ 2335 2336 CLASS_DECLARATION( idEntity, idBeam ) 2337 EVENT( EV_PostSpawn, idBeam::Event_MatchTarget ) 2338 EVENT( EV_Activate, idBeam::Event_Activate ) 2339 END_CLASS 2340 2341 /* 2342 =============== 2343 idBeam::idBeam 2344 =============== 2345 */ 2346 idBeam::idBeam() { 2347 target = NULL; 2348 master = NULL; 2349 } 2350 2351 /* 2352 =============== 2353 idBeam::Save 2354 =============== 2355 */ 2356 void idBeam::Save( idSaveGame *savefile ) const { 2357 target.Save( savefile ); 2358 master.Save( savefile ); 2359 } 2360 2361 /* 2362 =============== 2363 idBeam::Restore 2364 =============== 2365 */ 2366 void idBeam::Restore( idRestoreGame *savefile ) { 2367 target.Restore( savefile ); 2368 master.Restore( savefile ); 2369 } 2370 2371 /* 2372 =============== 2373 idBeam::Spawn 2374 =============== 2375 */ 2376 void idBeam::Spawn() { 2377 float width; 2378 2379 if ( spawnArgs.GetFloat( "width", "0", width ) ) { 2380 renderEntity.shaderParms[ SHADERPARM_BEAM_WIDTH ] = width; 2381 } 2382 2383 SetModel( "_BEAM" ); 2384 Hide(); 2385 PostEventMS( &EV_PostSpawn, 0 ); 2386 } 2387 2388 /* 2389 ================ 2390 idBeam::Think 2391 ================ 2392 */ 2393 void idBeam::Think() { 2394 idBeam *masterEnt; 2395 2396 if ( !IsHidden() && !target.GetEntity() ) { 2397 // hide if our target is removed 2398 Hide(); 2399 } 2400 2401 RunPhysics(); 2402 2403 masterEnt = master.GetEntity(); 2404 if ( masterEnt ) { 2405 const idVec3 &origin = GetPhysics()->GetOrigin(); 2406 masterEnt->SetBeamTarget( origin ); 2407 } 2408 Present(); 2409 } 2410 2411 /* 2412 ================ 2413 idBeam::SetMaster 2414 ================ 2415 */ 2416 void idBeam::SetMaster( idBeam *masterbeam ) { 2417 master = masterbeam; 2418 } 2419 2420 /* 2421 ================ 2422 idBeam::SetBeamTarget 2423 ================ 2424 */ 2425 void idBeam::SetBeamTarget( const idVec3 &origin ) { 2426 if ( ( renderEntity.shaderParms[ SHADERPARM_BEAM_END_X ] != origin.x ) || ( renderEntity.shaderParms[ SHADERPARM_BEAM_END_Y ] != origin.y ) || ( renderEntity.shaderParms[ SHADERPARM_BEAM_END_Z ] != origin.z ) ) { 2427 renderEntity.shaderParms[ SHADERPARM_BEAM_END_X ] = origin.x; 2428 renderEntity.shaderParms[ SHADERPARM_BEAM_END_Y ] = origin.y; 2429 renderEntity.shaderParms[ SHADERPARM_BEAM_END_Z ] = origin.z; 2430 UpdateVisuals(); 2431 } 2432 } 2433 2434 /* 2435 ================ 2436 idBeam::Show 2437 ================ 2438 */ 2439 void idBeam::Show() { 2440 idBeam *targetEnt; 2441 2442 idEntity::Show(); 2443 2444 targetEnt = target.GetEntity(); 2445 if ( targetEnt ) { 2446 const idVec3 &origin = targetEnt->GetPhysics()->GetOrigin(); 2447 SetBeamTarget( origin ); 2448 } 2449 } 2450 2451 /* 2452 ================ 2453 idBeam::Event_MatchTarget 2454 ================ 2455 */ 2456 void idBeam::Event_MatchTarget() { 2457 int i; 2458 idEntity *targetEnt; 2459 idBeam *targetBeam; 2460 2461 if ( !targets.Num() ) { 2462 return; 2463 } 2464 2465 targetBeam = NULL; 2466 for( i = 0; i < targets.Num(); i++ ) { 2467 targetEnt = targets[ i ].GetEntity(); 2468 if ( targetEnt != NULL && targetEnt->IsType( idBeam::Type ) ) { 2469 targetBeam = static_cast<idBeam *>( targetEnt ); 2470 break; 2471 } 2472 } 2473 2474 if ( targetBeam == NULL ) { 2475 gameLocal.Error( "Could not find valid beam target for '%s'", name.c_str() ); 2476 return; 2477 } 2478 2479 target = targetBeam; 2480 targetBeam->SetMaster( this ); 2481 if ( !spawnArgs.GetBool( "start_off" ) ) { 2482 Show(); 2483 } 2484 } 2485 2486 /* 2487 ================ 2488 idBeam::Event_Activate 2489 ================ 2490 */ 2491 void idBeam::Event_Activate( idEntity *activator ) { 2492 if ( IsHidden() ) { 2493 Show(); 2494 } else { 2495 Hide(); 2496 } 2497 } 2498 2499 /* 2500 ================ 2501 idBeam::WriteToSnapshot 2502 ================ 2503 */ 2504 void idBeam::WriteToSnapshot( idBitMsg &msg ) const { 2505 GetPhysics()->WriteToSnapshot( msg ); 2506 WriteBindToSnapshot( msg ); 2507 WriteColorToSnapshot( msg ); 2508 msg.WriteFloat( renderEntity.shaderParms[SHADERPARM_BEAM_END_X] ); 2509 msg.WriteFloat( renderEntity.shaderParms[SHADERPARM_BEAM_END_Y] ); 2510 msg.WriteFloat( renderEntity.shaderParms[SHADERPARM_BEAM_END_Z] ); 2511 } 2512 2513 /* 2514 ================ 2515 idBeam::ReadFromSnapshot 2516 ================ 2517 */ 2518 void idBeam::ReadFromSnapshot( const idBitMsg &msg ) { 2519 GetPhysics()->ReadFromSnapshot( msg ); 2520 ReadBindFromSnapshot( msg ); 2521 ReadColorFromSnapshot( msg ); 2522 renderEntity.shaderParms[SHADERPARM_BEAM_END_X] = msg.ReadFloat(); 2523 renderEntity.shaderParms[SHADERPARM_BEAM_END_Y] = msg.ReadFloat(); 2524 renderEntity.shaderParms[SHADERPARM_BEAM_END_Z] = msg.ReadFloat(); 2525 if ( msg.HasChanged() ) { 2526 UpdateVisuals(); 2527 } 2528 } 2529 2530 2531 /* 2532 =============================================================================== 2533 2534 idLiquid 2535 2536 =============================================================================== 2537 */ 2538 2539 CLASS_DECLARATION( idEntity, idLiquid ) 2540 EVENT( EV_Touch, idLiquid::Event_Touch ) 2541 END_CLASS 2542 2543 /* 2544 ================ 2545 idLiquid::Save 2546 ================ 2547 */ 2548 void idLiquid::Save( idSaveGame *savefile ) const { 2549 // Nothing to save 2550 } 2551 2552 /* 2553 ================ 2554 idLiquid::Restore 2555 ================ 2556 */ 2557 void idLiquid::Restore( idRestoreGame *savefile ) { 2558 //FIXME: NO! 2559 Spawn(); 2560 } 2561 2562 /* 2563 ================ 2564 idLiquid::Spawn 2565 ================ 2566 */ 2567 void idLiquid::Spawn() { 2568 /* 2569 model = dynamic_cast<idRenderModelLiquid *>( renderEntity.hModel ); 2570 if ( !model ) { 2571 gameLocal.Error( "Entity '%s' must have liquid model", name.c_str() ); 2572 } 2573 model->Reset(); 2574 GetPhysics()->SetContents( CONTENTS_TRIGGER ); 2575 */ 2576 } 2577 2578 /* 2579 ================ 2580 idLiquid::Event_Touch 2581 ================ 2582 */ 2583 void idLiquid::Event_Touch( idEntity *other, trace_t *trace ) { 2584 // FIXME: for QuakeCon 2585 /* 2586 if ( common->IsClient() ) { 2587 return; 2588 } 2589 2590 idVec3 pos; 2591 2592 pos = other->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin(); 2593 model->IntersectBounds( other->GetPhysics()->GetBounds().Translate( pos ), -10.0f ); 2594 */ 2595 } 2596 2597 2598 /* 2599 =============================================================================== 2600 2601 idShaking 2602 2603 =============================================================================== 2604 */ 2605 2606 CLASS_DECLARATION( idEntity, idShaking ) 2607 EVENT( EV_Activate, idShaking::Event_Activate ) 2608 END_CLASS 2609 2610 /* 2611 =============== 2612 idShaking::idShaking 2613 =============== 2614 */ 2615 idShaking::idShaking() { 2616 active = false; 2617 } 2618 2619 /* 2620 =============== 2621 idShaking::Save 2622 =============== 2623 */ 2624 void idShaking::Save( idSaveGame *savefile ) const { 2625 savefile->WriteBool( active ); 2626 savefile->WriteStaticObject( physicsObj ); 2627 } 2628 2629 /* 2630 =============== 2631 idShaking::Restore 2632 =============== 2633 */ 2634 void idShaking::Restore( idRestoreGame *savefile ) { 2635 savefile->ReadBool( active ); 2636 savefile->ReadStaticObject( physicsObj ); 2637 RestorePhysics( &physicsObj ); 2638 } 2639 2640 /* 2641 =============== 2642 idShaking::Spawn 2643 =============== 2644 */ 2645 void idShaking::Spawn() { 2646 physicsObj.SetSelf( this ); 2647 physicsObj.SetClipModel( new (TAG_PHYSICS_CLIP_ENTITY) idClipModel( GetPhysics()->GetClipModel() ), 1.0f ); 2648 physicsObj.SetOrigin( GetPhysics()->GetOrigin() ); 2649 physicsObj.SetAxis( GetPhysics()->GetAxis() ); 2650 physicsObj.SetClipMask( MASK_SOLID ); 2651 SetPhysics( &physicsObj ); 2652 2653 active = false; 2654 if ( !spawnArgs.GetBool( "start_off" ) ) { 2655 BeginShaking(); 2656 } 2657 } 2658 2659 /* 2660 ================ 2661 idShaking::BeginShaking 2662 ================ 2663 */ 2664 void idShaking::BeginShaking() { 2665 int phase; 2666 idAngles shake; 2667 int period; 2668 2669 active = true; 2670 phase = gameLocal.random.RandomInt( 1000 ); 2671 shake = spawnArgs.GetAngles( "shake", "0.5 0.5 0.5" ); 2672 period = spawnArgs.GetFloat( "period", "0.05" ) * 1000; 2673 physicsObj.SetAngularExtrapolation( extrapolation_t(EXTRAPOLATION_DECELSINE|EXTRAPOLATION_NOSTOP), phase, period * 0.25f, GetPhysics()->GetAxis().ToAngles(), shake, ang_zero ); 2674 } 2675 2676 /* 2677 ================ 2678 idShaking::Event_Activate 2679 ================ 2680 */ 2681 void idShaking::Event_Activate( idEntity *activator ) { 2682 if ( !active ) { 2683 BeginShaking(); 2684 } else { 2685 active = false; 2686 physicsObj.SetAngularExtrapolation( EXTRAPOLATION_NONE, 0, 0, physicsObj.GetAxis().ToAngles(), ang_zero, ang_zero ); 2687 } 2688 } 2689 2690 /* 2691 =============================================================================== 2692 2693 idEarthQuake 2694 2695 =============================================================================== 2696 */ 2697 2698 CLASS_DECLARATION( idEntity, idEarthQuake ) 2699 EVENT( EV_Activate, idEarthQuake::Event_Activate ) 2700 END_CLASS 2701 2702 /* 2703 =============== 2704 idEarthQuake::idEarthQuake 2705 =============== 2706 */ 2707 idEarthQuake::idEarthQuake() { 2708 wait = 0.0f; 2709 random = 0.0f; 2710 nextTriggerTime = 0; 2711 shakeStopTime = 0; 2712 triggered = false; 2713 playerOriented = false; 2714 disabled = false; 2715 shakeTime = 0.0f; 2716 } 2717 2718 /* 2719 =============== 2720 idEarthQuake::Save 2721 =============== 2722 */ 2723 void idEarthQuake::Save( idSaveGame *savefile ) const { 2724 savefile->WriteInt( nextTriggerTime ); 2725 savefile->WriteInt( shakeStopTime ); 2726 savefile->WriteFloat( wait ); 2727 savefile->WriteFloat( random ); 2728 savefile->WriteBool( triggered ); 2729 savefile->WriteBool( playerOriented ); 2730 savefile->WriteBool( disabled ); 2731 savefile->WriteFloat( shakeTime ); 2732 } 2733 2734 /* 2735 =============== 2736 idEarthQuake::Restore 2737 =============== 2738 */ 2739 void idEarthQuake::Restore( idRestoreGame *savefile ) { 2740 savefile->ReadInt( nextTriggerTime ); 2741 savefile->ReadInt( shakeStopTime ); 2742 savefile->ReadFloat( wait ); 2743 savefile->ReadFloat( random ); 2744 savefile->ReadBool( triggered ); 2745 savefile->ReadBool( playerOriented ); 2746 savefile->ReadBool( disabled ); 2747 savefile->ReadFloat( shakeTime ); 2748 2749 if ( shakeStopTime > gameLocal.time ) { 2750 BecomeActive( TH_THINK ); 2751 } 2752 } 2753 2754 /* 2755 =============== 2756 idEarthQuake::Spawn 2757 =============== 2758 */ 2759 void idEarthQuake::Spawn() { 2760 nextTriggerTime = 0; 2761 shakeStopTime = 0; 2762 wait = spawnArgs.GetFloat( "wait", "15" ); 2763 random = spawnArgs.GetFloat( "random", "5" ); 2764 triggered = spawnArgs.GetBool( "triggered" ); 2765 playerOriented = spawnArgs.GetBool( "playerOriented" ); 2766 disabled = false; 2767 shakeTime = spawnArgs.GetFloat( "shakeTime", "0" ); 2768 2769 if ( !triggered ){ 2770 PostEventSec( &EV_Activate, spawnArgs.GetFloat( "wait" ), this ); 2771 } 2772 BecomeInactive( TH_THINK ); 2773 } 2774 2775 /* 2776 ================ 2777 idEarthQuake::Event_Activate 2778 ================ 2779 */ 2780 void idEarthQuake::Event_Activate( idEntity *activator ) { 2781 2782 if ( nextTriggerTime > gameLocal.time ) { 2783 return; 2784 } 2785 2786 if ( disabled && activator == this ) { 2787 return; 2788 } 2789 2790 idPlayer *player = gameLocal.GetLocalPlayer(); 2791 if ( player == NULL ) { 2792 return; 2793 } 2794 2795 nextTriggerTime = 0; 2796 2797 if ( !triggered && activator != this ){ 2798 // if we are not triggered ( i.e. random ), disable or enable 2799 disabled ^= 1; 2800 if (disabled) { 2801 return; 2802 } else { 2803 PostEventSec( &EV_Activate, wait + random * gameLocal.random.CRandomFloat(), this ); 2804 } 2805 } 2806 2807 ActivateTargets( activator ); 2808 2809 const idSoundShader *shader = declManager->FindSound( spawnArgs.GetString( "snd_quake" ) ); 2810 if ( playerOriented ) { 2811 player->StartSoundShader( shader, SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL ); 2812 } else { 2813 StartSoundShader( shader, SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL ); 2814 } 2815 2816 if ( shakeTime > 0.0f ) { 2817 shakeStopTime = gameLocal.time + SEC2MS( shakeTime ); 2818 BecomeActive( TH_THINK ); 2819 } 2820 2821 if ( wait > 0.0f ) { 2822 if ( !triggered ) { 2823 PostEventSec( &EV_Activate, wait + random * gameLocal.random.CRandomFloat(), this ); 2824 } else { 2825 nextTriggerTime = gameLocal.time + SEC2MS( wait + random * gameLocal.random.CRandomFloat() ); 2826 } 2827 } else if ( shakeTime == 0.0f ) { 2828 PostEventMS( &EV_Remove, 0 ); 2829 } 2830 } 2831 2832 2833 /* 2834 =============== 2835 idEarthQuake::Think 2836 ================ 2837 */ 2838 void idEarthQuake::Think() { 2839 if ( thinkFlags & TH_THINK ) { 2840 if ( gameLocal.time > shakeStopTime ) { 2841 BecomeInactive( TH_THINK ); 2842 if ( wait <= 0.0f ) { 2843 PostEventMS( &EV_Remove, 0 ); 2844 } 2845 return; 2846 } 2847 float shakeVolume = gameSoundWorld->CurrentShakeAmplitude(); 2848 gameLocal.RadiusPush( GetPhysics()->GetOrigin(), 256, 1500 * shakeVolume, this, this, 1.0f, true ); 2849 } 2850 BecomeInactive( TH_UPDATEVISUALS ); 2851 } 2852 2853 /* 2854 =============================================================================== 2855 2856 idFuncPortal 2857 2858 =============================================================================== 2859 */ 2860 2861 CLASS_DECLARATION( idEntity, idFuncPortal ) 2862 EVENT( EV_Activate, idFuncPortal::Event_Activate ) 2863 END_CLASS 2864 2865 /* 2866 =============== 2867 idFuncPortal::idFuncPortal 2868 =============== 2869 */ 2870 idFuncPortal::idFuncPortal() { 2871 portal = 0; 2872 state = false; 2873 } 2874 2875 /* 2876 =============== 2877 idFuncPortal::Save 2878 =============== 2879 */ 2880 void idFuncPortal::Save( idSaveGame *savefile ) const { 2881 savefile->WriteInt( (int)portal ); 2882 savefile->WriteBool( state ); 2883 } 2884 2885 /* 2886 =============== 2887 idFuncPortal::Restore 2888 =============== 2889 */ 2890 void idFuncPortal::Restore( idRestoreGame *savefile ) { 2891 savefile->ReadInt( (int &)portal ); 2892 savefile->ReadBool( state ); 2893 gameLocal.SetPortalState( portal, state ? PS_BLOCK_ALL : PS_BLOCK_NONE ); 2894 } 2895 2896 /* 2897 =============== 2898 idFuncPortal::Spawn 2899 =============== 2900 */ 2901 void idFuncPortal::Spawn() { 2902 portal = gameRenderWorld->FindPortal( GetPhysics()->GetAbsBounds().Expand( 32.0f ) ); 2903 if ( portal > 0 ) { 2904 state = spawnArgs.GetBool( "start_on" ); 2905 gameLocal.SetPortalState( portal, state ? PS_BLOCK_ALL : PS_BLOCK_NONE ); 2906 } 2907 } 2908 2909 /* 2910 ================ 2911 idFuncPortal::Event_Activate 2912 ================ 2913 */ 2914 void idFuncPortal::Event_Activate( idEntity *activator ) { 2915 if ( portal > 0 ) { 2916 state = !state; 2917 gameLocal.SetPortalState( portal, state ? PS_BLOCK_ALL : PS_BLOCK_NONE ); 2918 } 2919 } 2920 2921 /* 2922 =============================================================================== 2923 2924 idFuncAASPortal 2925 2926 =============================================================================== 2927 */ 2928 2929 CLASS_DECLARATION( idEntity, idFuncAASPortal ) 2930 EVENT( EV_Activate, idFuncAASPortal::Event_Activate ) 2931 END_CLASS 2932 2933 /* 2934 =============== 2935 idFuncAASPortal::idFuncAASPortal 2936 =============== 2937 */ 2938 idFuncAASPortal::idFuncAASPortal() { 2939 state = false; 2940 } 2941 2942 /* 2943 =============== 2944 idFuncAASPortal::Save 2945 =============== 2946 */ 2947 void idFuncAASPortal::Save( idSaveGame *savefile ) const { 2948 savefile->WriteBool( state ); 2949 } 2950 2951 /* 2952 =============== 2953 idFuncAASPortal::Restore 2954 =============== 2955 */ 2956 void idFuncAASPortal::Restore( idRestoreGame *savefile ) { 2957 savefile->ReadBool( state ); 2958 gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL, state ); 2959 } 2960 2961 /* 2962 =============== 2963 idFuncAASPortal::Spawn 2964 =============== 2965 */ 2966 void idFuncAASPortal::Spawn() { 2967 state = spawnArgs.GetBool( "start_on" ); 2968 gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL, state ); 2969 } 2970 2971 /* 2972 ================ 2973 idFuncAASPortal::Event_Activate 2974 ================ 2975 */ 2976 void idFuncAASPortal::Event_Activate( idEntity *activator ) { 2977 state ^= 1; 2978 gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_CLUSTERPORTAL, state ); 2979 } 2980 2981 /* 2982 =============================================================================== 2983 2984 idFuncAASObstacle 2985 2986 =============================================================================== 2987 */ 2988 2989 CLASS_DECLARATION( idEntity, idFuncAASObstacle ) 2990 EVENT( EV_Activate, idFuncAASObstacle::Event_Activate ) 2991 END_CLASS 2992 2993 /* 2994 =============== 2995 idFuncAASObstacle::idFuncAASObstacle 2996 =============== 2997 */ 2998 idFuncAASObstacle::idFuncAASObstacle() { 2999 state = false; 3000 } 3001 3002 /* 3003 =============== 3004 idFuncAASObstacle::Save 3005 =============== 3006 */ 3007 void idFuncAASObstacle::Save( idSaveGame *savefile ) const { 3008 savefile->WriteBool( state ); 3009 } 3010 3011 /* 3012 =============== 3013 idFuncAASObstacle::Restore 3014 =============== 3015 */ 3016 void idFuncAASObstacle::Restore( idRestoreGame *savefile ) { 3017 savefile->ReadBool( state ); 3018 gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_OBSTACLE, state ); 3019 } 3020 3021 /* 3022 =============== 3023 idFuncAASObstacle::Spawn 3024 =============== 3025 */ 3026 void idFuncAASObstacle::Spawn() { 3027 state = spawnArgs.GetBool( "start_on" ); 3028 gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_OBSTACLE, state ); 3029 } 3030 3031 /* 3032 ================ 3033 idFuncAASObstacle::Event_Activate 3034 ================ 3035 */ 3036 void idFuncAASObstacle::Event_Activate( idEntity *activator ) { 3037 state ^= 1; 3038 gameLocal.SetAASAreaState( GetPhysics()->GetAbsBounds(), AREACONTENTS_OBSTACLE, state ); 3039 } 3040 3041 3042 3043 /* 3044 =============================================================================== 3045 3046 idFuncRadioChatter 3047 3048 =============================================================================== 3049 */ 3050 3051 const idEventDef EV_ResetRadioHud( "<resetradiohud>", "e" ); 3052 3053 3054 CLASS_DECLARATION( idEntity, idFuncRadioChatter ) 3055 EVENT( EV_Activate, idFuncRadioChatter::Event_Activate ) 3056 EVENT( EV_ResetRadioHud, idFuncRadioChatter::Event_ResetRadioHud ) 3057 END_CLASS 3058 3059 /* 3060 =============== 3061 idFuncRadioChatter::idFuncRadioChatter 3062 =============== 3063 */ 3064 idFuncRadioChatter::idFuncRadioChatter() { 3065 time = 0.0; 3066 } 3067 3068 /* 3069 =============== 3070 idFuncRadioChatter::Save 3071 =============== 3072 */ 3073 void idFuncRadioChatter::Save( idSaveGame *savefile ) const { 3074 savefile->WriteFloat( time ); 3075 } 3076 3077 /* 3078 =============== 3079 idFuncRadioChatter::Restore 3080 =============== 3081 */ 3082 void idFuncRadioChatter::Restore( idRestoreGame *savefile ) { 3083 savefile->ReadFloat( time ); 3084 } 3085 3086 /* 3087 =============== 3088 idFuncRadioChatter::Spawn 3089 =============== 3090 */ 3091 void idFuncRadioChatter::Spawn() { 3092 time = spawnArgs.GetFloat( "time", "5.0" ); 3093 } 3094 3095 /* 3096 ================ 3097 idFuncRadioChatter::Event_Activate 3098 ================ 3099 */ 3100 void idFuncRadioChatter::Event_Activate( idEntity *activator ) { 3101 idPlayer * player = gameLocal.GetLocalPlayer(); 3102 3103 if ( player != NULL && player->hudManager ) { 3104 player->hudManager->SetRadioMessage( true ); 3105 } 3106 3107 const char * sound = spawnArgs.GetString( "snd_radiochatter", "" ); 3108 if ( sound != NULL && *sound != NULL ) { 3109 const idSoundShader * shader = declManager->FindSound( sound ); 3110 int length = 0; 3111 player->StartSoundShader( shader, SND_CHANNEL_RADIO, SSF_GLOBAL, false, &length ); 3112 time = MS2SEC( length + 150 ); 3113 } 3114 // we still put the hud up because this is used with no sound on 3115 // certain frame commands when the chatter is triggered 3116 PostEventSec( &EV_ResetRadioHud, time, player ); 3117 } 3118 3119 /* 3120 ================ 3121 idFuncRadioChatter::Event_ResetRadioHud 3122 ================ 3123 */ 3124 void idFuncRadioChatter::Event_ResetRadioHud( idEntity *activator ) { 3125 idPlayer *player = ( activator->IsType( idPlayer::Type ) ) ? static_cast<idPlayer *>( activator ) : gameLocal.GetLocalPlayer(); 3126 3127 if ( player != NULL && player->hudManager ) { 3128 player->hudManager->SetRadioMessage( false ); 3129 } 3130 3131 ActivateTargets( activator ); 3132 } 3133 3134 3135 /* 3136 =============================================================================== 3137 3138 idPhantomObjects 3139 3140 =============================================================================== 3141 */ 3142 3143 CLASS_DECLARATION( idEntity, idPhantomObjects ) 3144 EVENT( EV_Activate, idPhantomObjects::Event_Activate ) 3145 END_CLASS 3146 3147 /* 3148 =============== 3149 idPhantomObjects::idPhantomObjects 3150 =============== 3151 */ 3152 idPhantomObjects::idPhantomObjects() { 3153 target = NULL; 3154 end_time = 0; 3155 throw_time = 0.0f; 3156 shake_time = 0.0f; 3157 shake_ang.Zero(); 3158 speed = 0.0f; 3159 min_wait = 0; 3160 max_wait = 0; 3161 fl.neverDormant = false; 3162 } 3163 3164 /* 3165 =============== 3166 idPhantomObjects::Save 3167 =============== 3168 */ 3169 void idPhantomObjects::Save( idSaveGame *savefile ) const { 3170 int i; 3171 3172 savefile->WriteInt( end_time ); 3173 savefile->WriteFloat( throw_time ); 3174 savefile->WriteFloat( shake_time ); 3175 savefile->WriteVec3( shake_ang ); 3176 savefile->WriteFloat( speed ); 3177 savefile->WriteInt( min_wait ); 3178 savefile->WriteInt( max_wait ); 3179 target.Save( savefile ); 3180 3181 savefile->WriteInt( targetTime.Num() ); 3182 for( i = 0; i < targetTime.Num(); i++ ) { 3183 savefile->WriteInt( targetTime[ i ] ); 3184 } 3185 for( i = 0; i < lastTargetPos.Num(); i++ ) { 3186 savefile->WriteVec3( lastTargetPos[ i ] ); 3187 } 3188 } 3189 3190 /* 3191 =============== 3192 idPhantomObjects::Restore 3193 =============== 3194 */ 3195 void idPhantomObjects::Restore( idRestoreGame *savefile ) { 3196 int num; 3197 int i; 3198 3199 savefile->ReadInt( end_time ); 3200 savefile->ReadFloat( throw_time ); 3201 savefile->ReadFloat( shake_time ); 3202 savefile->ReadVec3( shake_ang ); 3203 savefile->ReadFloat( speed ); 3204 savefile->ReadInt( min_wait ); 3205 savefile->ReadInt( max_wait ); 3206 target.Restore( savefile ); 3207 3208 savefile->ReadInt( num ); 3209 targetTime.SetGranularity( 1 ); 3210 targetTime.SetNum( num ); 3211 lastTargetPos.SetGranularity( 1 ); 3212 lastTargetPos.SetNum( num ); 3213 3214 for( i = 0; i < num; i++ ) { 3215 savefile->ReadInt( targetTime[ i ] ); 3216 } 3217 for( i = 0; i < num; i++ ) { 3218 savefile->ReadVec3( lastTargetPos[ i ] ); 3219 } 3220 } 3221 3222 /* 3223 =============== 3224 idPhantomObjects::Spawn 3225 =============== 3226 */ 3227 void idPhantomObjects::Spawn() { 3228 throw_time = spawnArgs.GetFloat( "time", "5" ); 3229 speed = spawnArgs.GetFloat( "speed", "1200" ); 3230 shake_time = spawnArgs.GetFloat( "shake_time", "1" ); 3231 throw_time -= shake_time; 3232 if ( throw_time < 0.0f ) { 3233 throw_time = 0.0f; 3234 } 3235 min_wait = SEC2MS( spawnArgs.GetFloat( "min_wait", "1" ) ); 3236 max_wait = SEC2MS( spawnArgs.GetFloat( "max_wait", "3" ) ); 3237 3238 shake_ang = spawnArgs.GetVector( "shake_ang", "65 65 65" ); 3239 Hide(); 3240 GetPhysics()->SetContents( 0 ); 3241 } 3242 3243 /* 3244 ================ 3245 idPhantomObjects::Event_Activate 3246 ================ 3247 */ 3248 void idPhantomObjects::Event_Activate( idEntity *activator ) { 3249 int i; 3250 float time; 3251 float frac; 3252 float scale; 3253 3254 if ( thinkFlags & TH_THINK ) { 3255 BecomeInactive( TH_THINK ); 3256 return; 3257 } 3258 3259 RemoveNullTargets(); 3260 if ( !targets.Num() ) { 3261 return; 3262 } 3263 3264 if ( !activator || !activator->IsType( idActor::Type ) ) { 3265 target = gameLocal.GetLocalPlayer(); 3266 } else { 3267 target = static_cast<idActor *>( activator ); 3268 } 3269 3270 end_time = gameLocal.time + SEC2MS( spawnArgs.GetFloat( "end_time", "0" ) ); 3271 3272 targetTime.SetNum( targets.Num() ); 3273 lastTargetPos.SetNum( targets.Num() ); 3274 3275 const idVec3 &toPos = target.GetEntity()->GetEyePosition(); 3276 3277 // calculate the relative times of all the objects 3278 time = 0.0f; 3279 for( i = 0; i < targetTime.Num(); i++ ) { 3280 targetTime[ i ] = SEC2MS( time ); 3281 lastTargetPos[ i ] = toPos; 3282 3283 frac = 1.0f - ( float )i / ( float )targetTime.Num(); 3284 time += ( gameLocal.random.RandomFloat() + 1.0f ) * 0.5f * frac + 0.1f; 3285 } 3286 3287 // scale up the times to fit within throw_time 3288 scale = throw_time / time; 3289 for( i = 0; i < targetTime.Num(); i++ ) { 3290 targetTime[ i ] = gameLocal.time + SEC2MS( shake_time )+ targetTime[ i ] * scale; 3291 } 3292 3293 BecomeActive( TH_THINK ); 3294 } 3295 3296 /* 3297 =============== 3298 idPhantomObjects::Think 3299 ================ 3300 */ 3301 void idPhantomObjects::Think() { 3302 int i; 3303 int num; 3304 float time; 3305 idVec3 vel; 3306 idVec3 ang; 3307 idEntity *ent; 3308 idActor *targetEnt; 3309 idPhysics *entPhys; 3310 trace_t tr; 3311 3312 // if we are completely closed off from the player, don't do anything at all 3313 if ( CheckDormant() ) { 3314 return; 3315 } 3316 3317 if ( !( thinkFlags & TH_THINK ) ) { 3318 BecomeInactive( thinkFlags & ~TH_THINK ); 3319 return; 3320 } 3321 3322 targetEnt = target.GetEntity(); 3323 if ( targetEnt == NULL || ( targetEnt->health <= 0 ) || ( end_time && ( gameLocal.time > end_time ) ) || gameLocal.inCinematic ) { 3324 BecomeInactive( TH_THINK ); 3325 return; 3326 } 3327 3328 const idVec3 &toPos = targetEnt->GetEyePosition(); 3329 3330 num = 0; 3331 for ( i = 0; i < targets.Num(); i++ ) { 3332 ent = targets[ i ].GetEntity(); 3333 if ( !ent ) { 3334 continue; 3335 } 3336 3337 if ( ent->fl.hidden ) { 3338 // don't throw hidden objects 3339 continue; 3340 } 3341 3342 if ( !targetTime[ i ] ) { 3343 // already threw this object 3344 continue; 3345 } 3346 3347 num++; 3348 3349 time = MS2SEC( targetTime[ i ] - gameLocal.time ); 3350 if ( time > shake_time ) { 3351 continue; 3352 } 3353 3354 entPhys = ent->GetPhysics(); 3355 const idVec3 &entOrg = entPhys->GetOrigin(); 3356 3357 gameLocal.clip.TracePoint( tr, entOrg, toPos, MASK_OPAQUE, ent ); 3358 if ( tr.fraction >= 1.0f || ( gameLocal.GetTraceEntity( tr ) == targetEnt ) ) { 3359 lastTargetPos[ i ] = toPos; 3360 } 3361 3362 if ( time < 0.0f ) { 3363 idAI::PredictTrajectory( entPhys->GetOrigin(), lastTargetPos[ i ], speed, entPhys->GetGravity(), 3364 entPhys->GetClipModel(), entPhys->GetClipMask(), 256.0f, ent, targetEnt, ai_debugTrajectory.GetBool() ? 1 : 0, vel ); 3365 vel *= speed; 3366 entPhys->SetLinearVelocity( vel ); 3367 if ( !end_time ) { 3368 targetTime[ i ] = 0; 3369 } else { 3370 targetTime[ i ] = gameLocal.time + gameLocal.random.RandomInt( max_wait - min_wait ) + min_wait; 3371 } 3372 if ( ent->IsType( idMoveable::Type ) ) { 3373 idMoveable *ment = static_cast<idMoveable*>( ent ); 3374 ment->EnableDamage( true, 2.5f ); 3375 } 3376 } else { 3377 // this is not the right way to set the angular velocity, but the effect is nice, so I'm keeping it. :) 3378 ang.Set( gameLocal.random.CRandomFloat() * shake_ang.x, gameLocal.random.CRandomFloat() * shake_ang.y, gameLocal.random.CRandomFloat() * shake_ang.z ); 3379 ang *= ( 1.0f - time / shake_time ); 3380 entPhys->SetAngularVelocity( ang ); 3381 } 3382 } 3383 3384 if ( !num ) { 3385 BecomeInactive( TH_THINK ); 3386 } 3387 } 3388 3389 /* 3390 =============================================================================== 3391 3392 idShockwave 3393 3394 =============================================================================== 3395 */ 3396 CLASS_DECLARATION( idEntity, idShockwave ) 3397 EVENT( EV_Activate, idShockwave::Event_Activate ) 3398 END_CLASS 3399 3400 /* 3401 =============== 3402 idShockwave::idShockwave 3403 =============== 3404 */ 3405 idShockwave::idShockwave() { 3406 isActive = false; 3407 startTime = 0; 3408 duration = 0; 3409 startSize = 0.f; 3410 endSize = 0.f; 3411 currentSize = 0.f; 3412 magnitude = 0.f; 3413 3414 height = 0.0f; 3415 playerDamaged = false; 3416 playerDamageSize = 0.0f; 3417 } 3418 3419 /* 3420 =============== 3421 idShockwave::~idShockwave 3422 =============== 3423 */ 3424 idShockwave::~idShockwave() { 3425 } 3426 3427 /* 3428 =============== 3429 idShockwave::Save 3430 =============== 3431 */ 3432 void idShockwave::Save( idSaveGame *savefile ) const { 3433 savefile->WriteBool( isActive ); 3434 savefile->WriteInt( startTime ); 3435 savefile->WriteInt( duration ); 3436 3437 savefile->WriteFloat( startSize ); 3438 savefile->WriteFloat( endSize ); 3439 savefile->WriteFloat( currentSize ); 3440 3441 savefile->WriteFloat( magnitude ); 3442 3443 savefile->WriteFloat( height ); 3444 savefile->WriteBool( playerDamaged ); 3445 savefile->WriteFloat( playerDamageSize ); 3446 } 3447 3448 /* 3449 =============== 3450 idShockwave::Restore 3451 =============== 3452 */ 3453 void idShockwave::Restore( idRestoreGame *savefile ) { 3454 savefile->ReadBool( isActive ); 3455 savefile->ReadInt( startTime ); 3456 savefile->ReadInt( duration ); 3457 3458 savefile->ReadFloat( startSize ); 3459 savefile->ReadFloat( endSize ); 3460 savefile->ReadFloat( currentSize ); 3461 3462 savefile->ReadFloat( magnitude ); 3463 3464 savefile->ReadFloat( height ); 3465 savefile->ReadBool( playerDamaged ); 3466 savefile->ReadFloat( playerDamageSize ); 3467 3468 } 3469 3470 /* 3471 =============== 3472 idShockwave::Spawn 3473 =============== 3474 */ 3475 void idShockwave::Spawn() { 3476 3477 spawnArgs.GetInt( "duration", "1000", duration ); 3478 spawnArgs.GetFloat( "startsize", "8", startSize ); 3479 spawnArgs.GetFloat( "endsize", "512", endSize ); 3480 spawnArgs.GetFloat( "magnitude", "100", magnitude ); 3481 3482 spawnArgs.GetFloat( "height", "0", height); 3483 spawnArgs.GetFloat( "player_damage_size", "20", playerDamageSize); 3484 3485 if ( spawnArgs.GetBool( "start_on" ) ) { 3486 ProcessEvent( &EV_Activate, this ); 3487 } 3488 } 3489 3490 /* 3491 =============== 3492 idShockwave::Think 3493 =============== 3494 */ 3495 void idShockwave::Think() { 3496 int endTime; 3497 3498 if ( !isActive ) { 3499 BecomeInactive( TH_THINK ); 3500 return; 3501 } 3502 3503 endTime = startTime + duration; 3504 3505 if ( gameLocal.time < endTime ) { 3506 float u; 3507 float newSize; 3508 3509 // Expand shockwave 3510 u = (float)(gameLocal.time - startTime) / (float)duration; 3511 newSize = startSize + u * (endSize - startSize); 3512 3513 // Find all clipmodels between currentSize and newSize 3514 idVec3 pos, end; 3515 idClipModel *clipModelList[ MAX_GENTITIES ]; 3516 idClipModel *clip; 3517 idEntity *ent; 3518 int i, listedClipModels; 3519 3520 // Set bounds 3521 pos = GetPhysics()->GetOrigin(); 3522 3523 float zVal; 3524 if(!height) { 3525 zVal = newSize; 3526 } else { 3527 zVal = height/2.0f; 3528 } 3529 3530 //Expand in a sphere 3531 end = pos + idVec3( newSize, newSize, zVal ); 3532 idBounds bounds( end ); 3533 end = pos + idVec3( -newSize, -newSize, -zVal ); 3534 bounds.AddPoint( end ); 3535 3536 if(g_debugShockwave.GetBool()) { 3537 gameRenderWorld->DebugBounds(colorRed, bounds, vec3_origin); 3538 } 3539 3540 listedClipModels = gameLocal.clip.ClipModelsTouchingBounds( bounds, -1, clipModelList, MAX_GENTITIES ); 3541 3542 for ( i = 0; i < listedClipModels; i++ ) { 3543 clip = clipModelList[ i ]; 3544 ent = clip->GetEntity(); 3545 3546 if ( ent->IsHidden() ) { 3547 continue; 3548 } 3549 3550 if ( !ent->IsType( idMoveable::Type ) && !ent->IsType( idAFEntity_Base::Type ) && !ent->IsType( idPlayer::Type )) { 3551 continue; 3552 } 3553 3554 idVec3 point = ent->GetPhysics()->GetOrigin(); 3555 idVec3 force = point - pos; 3556 3557 float dist = force.Normalize(); 3558 3559 if(ent->IsType( idPlayer::Type )) { 3560 3561 if(ent->GetPhysics()->GetAbsBounds().IntersectsBounds(bounds)) { 3562 3563 //For player damage we check the current radius and a specified player damage ring size 3564 if ( dist <= newSize && dist > newSize-playerDamageSize ) { 3565 3566 idStr damageDef = spawnArgs.GetString("def_player_damage", ""); 3567 if(damageDef.Length() > 0 && !playerDamaged) { 3568 3569 playerDamaged = true; //Only damage once per shockwave 3570 idPlayer* player = static_cast< idPlayer* >( ent ); 3571 idVec3 dir = ent->GetPhysics()->GetOrigin() - pos; 3572 dir.NormalizeFast(); 3573 player->Damage(NULL, NULL, dir, damageDef, 1.0f, INVALID_JOINT); 3574 } 3575 } 3576 } 3577 3578 } else { 3579 3580 // If the object is inside the current expansion... 3581 if ( dist <= newSize && dist > currentSize ) { 3582 force.z += 4.f; 3583 force.NormalizeFast(); 3584 3585 if ( ent->IsType( idAFEntity_Base::Type ) ) { 3586 force = force * (ent->GetPhysics()->GetMass() * magnitude * 0.01f); 3587 } else { 3588 force = force * ent->GetPhysics()->GetMass() * magnitude; 3589 } 3590 3591 // Kick it up, move force point off object origin 3592 float rad = ent->GetPhysics()->GetBounds().GetRadius(); 3593 point.x += gameLocal.random.CRandomFloat() * rad; 3594 point.y += gameLocal.random.CRandomFloat() * rad; 3595 3596 int j; 3597 for( j=0; j < ent->GetPhysics()->GetNumClipModels(); j++ ) { 3598 ent->GetPhysics()->AddForce( j, point, force ); 3599 } 3600 } 3601 } 3602 } 3603 3604 // Update currentSize for next frame 3605 currentSize = newSize; 3606 3607 } else { 3608 3609 // turn off 3610 isActive = false; 3611 } 3612 } 3613 3614 /* 3615 =============== 3616 idShockwave::Event_Activate 3617 =============== 3618 */ 3619 void idShockwave::Event_Activate( idEntity *activator ) { 3620 3621 isActive = true; 3622 startTime = gameLocal.time; 3623 playerDamaged = false; 3624 3625 BecomeActive( TH_THINK ); 3626 } 3627 3628 3629 /* 3630 =============================================================================== 3631 3632 idFuncMountedObject 3633 3634 =============================================================================== 3635 */ 3636 3637 CLASS_DECLARATION( idEntity, idFuncMountedObject ) 3638 EVENT( EV_Touch, idFuncMountedObject::Event_Touch ) 3639 EVENT( EV_Activate, idFuncMountedObject::Event_Activate ) 3640 END_CLASS 3641 3642 /* 3643 =============== 3644 idFuncMountedObject::idFuncMountedObject 3645 =============== 3646 */ 3647 idFuncMountedObject::idFuncMountedObject() { 3648 isMounted = false; 3649 scriptFunction = NULL; 3650 mountedPlayer = NULL; 3651 harc = 0; 3652 varc = 0; 3653 } 3654 3655 /* 3656 =============== 3657 idFuncMountedObject::idFuncMountedObject 3658 =============== 3659 */ 3660 idFuncMountedObject::~idFuncMountedObject() { 3661 } 3662 3663 /* 3664 =============== 3665 idFuncMountedObject::Spawn 3666 =============== 3667 */ 3668 void idFuncMountedObject::Spawn() { 3669 // Get viewOffset 3670 spawnArgs.GetInt( "harc", "45", harc ); 3671 spawnArgs.GetInt( "varc", "30", varc ); 3672 3673 // Get script function 3674 idStr funcName = spawnArgs.GetString( "call", "" ); 3675 if ( funcName.Length() ) { 3676 scriptFunction = gameLocal.program.FindFunction( funcName ); 3677 if ( scriptFunction == NULL ) { 3678 gameLocal.Warning( "idFuncMountedObject '%s' at (%s) calls unknown function '%s'\n", name.c_str(), GetPhysics()->GetOrigin().ToString(0), funcName.c_str() ); 3679 } 3680 } 3681 3682 BecomeActive( TH_THINK ); 3683 } 3684 3685 /* 3686 ================ 3687 idFuncMountedObject::Think 3688 ================ 3689 */ 3690 void idFuncMountedObject::Think() { 3691 3692 idEntity::Think(); 3693 } 3694 3695 /* 3696 ================ 3697 idFuncMountedObject::GetViewInfo 3698 ================ 3699 */ 3700 void idFuncMountedObject::GetAngleRestrictions( int &yaw_min, int &yaw_max, int &pitch ) { 3701 idMat3 axis; 3702 idAngles angs; 3703 3704 axis = GetPhysics()->GetAxis(); 3705 angs = axis.ToAngles(); 3706 3707 yaw_min = angs.yaw - harc; 3708 yaw_min = idMath::AngleNormalize180( yaw_min ); 3709 3710 yaw_max = angs.yaw + harc; 3711 yaw_max = idMath::AngleNormalize180( yaw_max ); 3712 3713 pitch = varc; 3714 } 3715 3716 /* 3717 ================ 3718 idFuncMountedObject::Event_Touch 3719 ================ 3720 */ 3721 void idFuncMountedObject::Event_Touch( idEntity *other, trace_t *trace ) { 3722 if ( common->IsClient() ) { 3723 return; 3724 } 3725 3726 ProcessEvent( &EV_Activate, other ); 3727 } 3728 3729 /* 3730 ================ 3731 idFuncMountedObject::Event_Activate 3732 ================ 3733 */ 3734 void idFuncMountedObject::Event_Activate( idEntity *activator ) { 3735 if ( !isMounted && activator->IsType( idPlayer::Type ) ) { 3736 idPlayer *client = (idPlayer *)activator; 3737 3738 mountedPlayer = client; 3739 3740 /* 3741 // Place player at path_corner targeted by mounted object 3742 int i; 3743 idPathCorner *spot; 3744 3745 for ( i = 0; i < targets.Num(); i++ ) { 3746 if ( targets[i]->IsType( idPathCorner::Type ) ) { 3747 spot = (idPathCorner*)targets[i]; 3748 break; 3749 } 3750 } 3751 3752 mountedPlayer->GetPhysics()->SetOrigin( spot->GetPhysics()->GetOrigin() ); 3753 mountedPlayer->GetPhysics()->SetAxis( spot->GetPhysics()->GetAxis() ); 3754 */ 3755 3756 mountedPlayer->Bind( this, true ); 3757 mountedPlayer->mountedObject = this; 3758 3759 // Call a script function 3760 idThread *mountthread; 3761 if ( scriptFunction ) { 3762 mountthread = new idThread( scriptFunction ); 3763 mountthread->DelayedStart( 0 ); 3764 } 3765 3766 isMounted = true; 3767 } 3768 } 3769 3770 /* 3771 =============================================================================== 3772 3773 idFuncMountedWeapon 3774 3775 =============================================================================== 3776 */ 3777 CLASS_DECLARATION( idFuncMountedObject, idFuncMountedWeapon ) 3778 EVENT( EV_PostSpawn, idFuncMountedWeapon::Event_PostSpawn ) 3779 END_CLASS 3780 3781 idFuncMountedWeapon::idFuncMountedWeapon() { 3782 turret = NULL; 3783 weaponLastFireTime = 0; 3784 weaponFireDelay = 0; 3785 projectile = NULL; 3786 } 3787 3788 idFuncMountedWeapon::~idFuncMountedWeapon() { 3789 } 3790 3791 3792 void idFuncMountedWeapon::Spawn() { 3793 3794 // Get projectile info 3795 projectile = gameLocal.FindEntityDefDict( spawnArgs.GetString( "def_projectile" ), false ); 3796 if ( !projectile ) { 3797 gameLocal.Warning( "Invalid projectile on func_mountedweapon." ); 3798 } 3799 3800 float firerate; 3801 spawnArgs.GetFloat( "firerate", "3", firerate ); 3802 weaponFireDelay = 1000.f / firerate; 3803 3804 // Get the firing sound 3805 idStr fireSound; 3806 spawnArgs.GetString( "snd_fire", "", fireSound ); 3807 soundFireWeapon = declManager->FindSound( fireSound ); 3808 3809 PostEventMS( &EV_PostSpawn, 0 ); 3810 } 3811 3812 void idFuncMountedWeapon::Think() { 3813 3814 if ( isMounted && turret ) { 3815 idVec3 vec = mountedPlayer->viewAngles.ToForward(); 3816 idAngles ang = mountedPlayer->GetLocalVector( vec ).ToAngles(); 3817 3818 turret->GetPhysics()->SetAxis( ang.ToMat3() ); 3819 turret->UpdateVisuals(); 3820 3821 // Check for firing 3822 if ( mountedPlayer->usercmd.buttons & BUTTON_ATTACK && ( gameLocal.time > weaponLastFireTime + weaponFireDelay ) ) { 3823 // FIRE! 3824 idEntity *ent; 3825 idProjectile *proj; 3826 idBounds projBounds; 3827 idVec3 dir; 3828 3829 gameLocal.SpawnEntityDef( *projectile, &ent ); 3830 if ( !ent || !ent->IsType( idProjectile::Type ) ) { 3831 const char *projectileName = spawnArgs.GetString( "def_projectile" ); 3832 gameLocal.Error( "'%s' is not an idProjectile", projectileName ); 3833 } 3834 3835 mountedPlayer->GetViewPos( muzzleOrigin, muzzleAxis ); 3836 3837 muzzleOrigin += ( muzzleAxis[0] * 128 ); 3838 muzzleOrigin -= ( muzzleAxis[2] * 20 ); 3839 3840 dir = muzzleAxis[0]; 3841 3842 proj = static_cast<idProjectile *>(ent); 3843 proj->Create( this, muzzleOrigin, dir ); 3844 3845 projBounds = proj->GetPhysics()->GetBounds().Rotate( proj->GetPhysics()->GetAxis() ); 3846 3847 proj->Launch( muzzleOrigin, dir, vec3_origin ); 3848 StartSoundShader( soundFireWeapon, SND_CHANNEL_WEAPON, SSF_GLOBAL, false, NULL ); 3849 3850 weaponLastFireTime = gameLocal.time; 3851 } 3852 } 3853 3854 idFuncMountedObject::Think(); 3855 } 3856 3857 void idFuncMountedWeapon::Event_PostSpawn() { 3858 3859 if ( targets.Num() >= 1 ) { 3860 for ( int i=0; i < targets.Num(); i++ ) { 3861 if ( targets[i].GetEntity()->IsType( idStaticEntity::Type ) ) { 3862 turret = targets[i].GetEntity(); 3863 break; 3864 } 3865 } 3866 } else { 3867 gameLocal.Warning( "idFuncMountedWeapon::Spawn: Please target one model for a turret\n" ); 3868 } 3869 } 3870 3871 3872 3873 3874 3875 3876 /* 3877 =============================================================================== 3878 3879 idPortalSky 3880 3881 =============================================================================== 3882 */ 3883 3884 CLASS_DECLARATION( idEntity, idPortalSky ) 3885 EVENT( EV_PostSpawn, idPortalSky::Event_PostSpawn ) 3886 EVENT( EV_Activate, idPortalSky::Event_Activate ) 3887 END_CLASS 3888 3889 /* 3890 =============== 3891 idPortalSky::idPortalSky 3892 =============== 3893 */ 3894 idPortalSky::idPortalSky() { 3895 3896 } 3897 3898 /* 3899 =============== 3900 idPortalSky::~idPortalSky 3901 =============== 3902 */ 3903 idPortalSky::~idPortalSky() { 3904 3905 } 3906 3907 /* 3908 =============== 3909 idPortalSky::Spawn 3910 =============== 3911 */ 3912 void idPortalSky::Spawn() { 3913 if ( !spawnArgs.GetBool( "triggered" ) ) { 3914 PostEventMS( &EV_PostSpawn, 1 ); 3915 } 3916 } 3917 3918 /* 3919 ================ 3920 idPortalSky::Event_PostSpawn 3921 ================ 3922 */ 3923 void idPortalSky::Event_PostSpawn() { 3924 gameLocal.SetPortalSkyEnt( this ); 3925 } 3926 3927 /* 3928 ================ 3929 idPortalSky::Event_Activate 3930 ================ 3931 */ 3932 void idPortalSky::Event_Activate( idEntity *activator ) { 3933 gameLocal.SetPortalSkyEnt( this ); 3934 }