Grabber.cpp (20267B)
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 #include "../idlib/precompiled.h" 29 #pragma hdrstop 30 31 32 #include "Game_local.h" 33 #include "Misc.h" 34 35 #define MAX_DRAG_TRACE_DISTANCE 384.0f 36 #define TRACE_BOUNDS_SIZE 3.f 37 #define HOLD_DISTANCE 72.f 38 #define FIRING_DELAY 1000.0f 39 #define DRAG_FAIL_LEN 64.f 40 #define THROW_SCALE 1000 41 #define MAX_PICKUP_VELOCITY 1500 * 1500 42 #define MAX_PICKUP_SIZE 96 43 44 /* 45 =============================================================================== 46 47 Allows entities to be dragged through the world with physics. 48 49 =============================================================================== 50 */ 51 52 CLASS_DECLARATION( idEntity, idGrabber ) 53 END_CLASS 54 55 /* 56 ============== 57 idGrabber::idGrabber 58 ============== 59 */ 60 idGrabber::idGrabber() { 61 dragEnt = NULL; 62 owner = NULL; 63 beam = NULL; 64 beamTarget = NULL; 65 oldImpulseSequence = 0; 66 shakeForceFlip = false; 67 holdingAF = false; 68 endTime = 0; 69 lastFiredTime = -FIRING_DELAY; 70 dragFailTime = 0; 71 startDragTime = 0; 72 warpId = -1; 73 dragTraceDist = MAX_DRAG_TRACE_DISTANCE; 74 } 75 76 /* 77 ============== 78 idGrabber::~idGrabber 79 ============== 80 */ 81 idGrabber::~idGrabber() { 82 StopDrag( true ); 83 if ( beam ) { 84 delete beam; 85 } 86 if ( beamTarget ) { 87 delete beamTarget; 88 } 89 } 90 91 /* 92 ============== 93 idGrabber::Save 94 ============== 95 */ 96 void idGrabber::Save( idSaveGame *savefile ) const { 97 98 dragEnt.Save( savefile ); 99 savefile->WriteStaticObject( drag ); 100 101 savefile->WriteVec3( saveGravity ); 102 savefile->WriteInt( id ); 103 104 savefile->WriteVec3( localPlayerPoint ); 105 106 owner.Save( savefile ); 107 108 savefile->WriteBool( holdingAF ); 109 savefile->WriteBool( shakeForceFlip ); 110 111 savefile->WriteInt( endTime ); 112 savefile->WriteInt( lastFiredTime ); 113 savefile->WriteInt( dragFailTime ); 114 savefile->WriteInt( startDragTime ); 115 savefile->WriteFloat( dragTraceDist ); 116 savefile->WriteInt( savedContents ); 117 savefile->WriteInt( savedClipmask ); 118 119 savefile->WriteObject( beam ); 120 savefile->WriteObject( beamTarget ); 121 122 savefile->WriteInt( warpId ); 123 } 124 125 /* 126 ============== 127 idGrabber::Restore 128 ============== 129 */ 130 void idGrabber::Restore( idRestoreGame *savefile ) { 131 //Spawn the beams 132 Initialize(); 133 134 dragEnt.Restore( savefile ); 135 savefile->ReadStaticObject( drag ); 136 137 savefile->ReadVec3( saveGravity ); 138 savefile->ReadInt( id ); 139 140 // Restore the drag force's physics object 141 if ( dragEnt.IsValid() ) { 142 drag.SetPhysics( dragEnt.GetEntity()->GetPhysics(), id, dragEnt.GetEntity()->GetPhysics()->GetOrigin() ); 143 } 144 145 savefile->ReadVec3( localPlayerPoint ); 146 147 owner.Restore( savefile ); 148 149 savefile->ReadBool( holdingAF ); 150 savefile->ReadBool( shakeForceFlip ); 151 152 savefile->ReadInt( endTime ); 153 savefile->ReadInt( lastFiredTime ); 154 savefile->ReadInt( dragFailTime ); 155 savefile->ReadInt( startDragTime ); 156 savefile->ReadFloat( dragTraceDist ); 157 savefile->ReadInt( savedContents ); 158 savefile->ReadInt( savedClipmask ); 159 160 savefile->ReadObject( reinterpret_cast<idClass *&>(beam) ); 161 savefile->ReadObject( reinterpret_cast<idClass *&>(beamTarget) ); 162 163 savefile->ReadInt( warpId ); 164 } 165 166 /* 167 ============== 168 idGrabber::Initialize 169 ============== 170 */ 171 void idGrabber::Initialize() { 172 if ( !common->IsMultiplayer() ) { 173 idDict args; 174 175 if ( !beamTarget ) { 176 args.SetVector( "origin", vec3_origin ); 177 args.SetBool( "start_off", true ); 178 beamTarget = ( idBeam * )gameLocal.SpawnEntityType( idBeam::Type, &args ); 179 } 180 181 if ( !beam ) { 182 args.Clear(); 183 args.Set( "target", beamTarget->name.c_str() ); 184 args.SetVector( "origin", vec3_origin ); 185 args.SetBool( "start_off", true ); 186 args.Set( "width", "6" ); 187 args.Set( "skin", "textures/smf/flareSizeable" ); 188 args.Set( "_color", "0.0235 0.843 0.969 0.2" ); 189 beam = ( idBeam * )gameLocal.SpawnEntityType( idBeam::Type, &args ); 190 beam->SetShaderParm( 6, 1.0f ); 191 } 192 193 endTime = 0; 194 dragTraceDist = MAX_DRAG_TRACE_DISTANCE; 195 } 196 else { 197 beam = NULL; 198 beamTarget = NULL; 199 endTime = 0; 200 dragTraceDist = MAX_DRAG_TRACE_DISTANCE; 201 }; 202 } 203 204 /* 205 ============== 206 idGrabber::SetDragDistance 207 ============== 208 */ 209 void idGrabber::SetDragDistance( float dist ) { 210 dragTraceDist = dist; 211 } 212 213 /* 214 ============== 215 idGrabber::StartDrag 216 ============== 217 */ 218 void idGrabber::StartDrag( idEntity *grabEnt, int id ) { 219 int clipModelId = id; 220 idPlayer *thePlayer = owner.GetEntity(); 221 222 holdingAF = false; 223 dragFailTime = gameLocal.slow.time; 224 startDragTime = gameLocal.slow.time; 225 226 oldImpulseSequence = thePlayer->usercmd.impulseSequence; 227 228 // set grabbed state for networking 229 grabEnt->SetGrabbedState( true ); 230 231 // This is the new object to drag around 232 dragEnt = grabEnt; 233 234 // Show the beams! 235 UpdateBeams(); 236 if ( beam ) { 237 beam->Show(); 238 } 239 if ( beamTarget ) { 240 beamTarget->Show(); 241 } 242 243 // Move the object to the fast group (helltime) 244 grabEnt->timeGroup = TIME_GROUP2; 245 246 // Handle specific class types 247 if ( grabEnt->IsType( idProjectile::Type ) ) { 248 idProjectile* p = (idProjectile*)grabEnt; 249 250 p->CatchProjectile( thePlayer, "_catch" ); 251 252 // Make the projectile non-solid to other projectiles/enemies (special hack for helltime hunter) 253 if ( !idStr::Cmp( grabEnt->GetEntityDefName(), "projectile_helltime_killer" ) ) { 254 savedContents = CONTENTS_PROJECTILE; 255 savedClipmask = MASK_SHOT_RENDERMODEL|CONTENTS_PROJECTILE; 256 } else { 257 savedContents = grabEnt->GetPhysics()->GetContents(); 258 savedClipmask = grabEnt->GetPhysics()->GetClipMask(); 259 } 260 grabEnt->GetPhysics()->SetContents( 0 ); 261 grabEnt->GetPhysics()->SetClipMask( CONTENTS_SOLID|CONTENTS_BODY ); 262 263 } else if ( grabEnt->IsType( idExplodingBarrel::Type ) ) { 264 idExplodingBarrel *ebarrel = static_cast<idExplodingBarrel*>(grabEnt); 265 266 ebarrel->StartBurning(); 267 268 } else if ( grabEnt->IsType( idAFEntity_Gibbable::Type ) ) { 269 holdingAF = true; 270 clipModelId = 0; 271 272 if ( grabbableAI( grabEnt->spawnArgs.GetString( "classname" ) ) ) { 273 idAI *aiEnt = static_cast<idAI*>(grabEnt); 274 275 aiEnt->StartRagdoll(); 276 } 277 } else if ( grabEnt->IsType( idMoveableItem::Type ) ) { 278 grabEnt->PostEventMS( &EV_Touch, 250, thePlayer, NULL ); 279 } 280 281 // Get the current physics object to manipulate 282 idPhysics *phys = grabEnt->GetPhysics(); 283 284 // Turn off gravity on object 285 saveGravity = phys->GetGravity(); 286 phys->SetGravity( vec3_origin ); 287 288 // hold it directly in front of player 289 localPlayerPoint = ( thePlayer->firstPersonViewAxis[0] * HOLD_DISTANCE ) * thePlayer->firstPersonViewAxis.Transpose(); 290 291 // Set the ending time for the hold 292 endTime = gameLocal.time + g_grabberHoldSeconds.GetFloat() * 1000; 293 294 // Start up the Force_Drag to bring it in 295 drag.Init( g_grabberDamping.GetFloat() ); 296 drag.SetPhysics( phys, clipModelId, thePlayer->firstPersonViewOrigin + localPlayerPoint * thePlayer->firstPersonViewAxis); 297 298 // start the screen warp 299 warpId = thePlayer->playerView.AddWarp( phys->GetOrigin(), SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 160, 2000 ); 300 } 301 302 /* 303 ============== 304 idGrabber::StopDrag 305 ============== 306 */ 307 void idGrabber::StopDrag( bool dropOnly ) { 308 idPlayer *thePlayer = owner.GetEntity(); 309 310 if ( beam ) { 311 beam->Hide(); 312 } 313 if ( beamTarget ) { 314 beamTarget->Hide(); 315 } 316 317 if ( dragEnt.IsValid() ) { 318 idEntity *ent = dragEnt.GetEntity(); 319 320 // set grabbed state for networking 321 ent->SetGrabbedState( false ); 322 323 // If a cinematic has started, allow dropped object to think in cinematics 324 if ( gameLocal.inCinematic ) { 325 ent->cinematic = true; 326 } 327 328 // Restore Gravity 329 ent->GetPhysics()->SetGravity( saveGravity ); 330 331 // Move the object back to the slow group (helltime) 332 ent->timeGroup = TIME_GROUP1; 333 334 if ( holdingAF ) { 335 idAFEntity_Gibbable *af = static_cast<idAFEntity_Gibbable *>(ent); 336 idPhysics_AF *af_Phys = static_cast<idPhysics_AF*>(af->GetPhysics()); 337 338 if ( grabbableAI( ent->spawnArgs.GetString( "classname" ) ) ) { 339 idAI *aiEnt = static_cast<idAI*>(ent); 340 341 aiEnt->Damage( thePlayer, thePlayer, vec3_origin, "damage_suicide", 1.0f, INVALID_JOINT ); 342 } 343 344 af->SetThrown( !dropOnly ); 345 346 // Reset timers so that it isn't forcibly put to rest in mid-air 347 af_Phys->PutToRest(); 348 af_Phys->Activate(); 349 350 af_Phys->SetTimeScaleRamp( MS2SEC(gameLocal.slow.time) - 1.5f, MS2SEC(gameLocal.slow.time) + 1.0f ); 351 } 352 353 // If the object isn't near its goal, just drop it in place. 354 if ( !ent->IsType( idProjectile::Type ) && ( dropOnly || drag.GetDistanceToGoal() > DRAG_FAIL_LEN ) ) { 355 ent->GetPhysics()->SetLinearVelocity( vec3_origin ); 356 thePlayer->StartSoundShader( declManager->FindSound( "grabber_maindrop" ), SND_CHANNEL_WEAPON, 0, false, NULL ); 357 358 if ( ent->IsType( idExplodingBarrel::Type ) ) { 359 idExplodingBarrel *ebarrel = static_cast<idExplodingBarrel*>(ent); 360 361 ebarrel->SetStability( true ); 362 ebarrel->StopBurning(); 363 } 364 } else { 365 // Shoot the object forward 366 ent->ApplyImpulse( thePlayer, 0, ent->GetPhysics()->GetOrigin(), thePlayer->firstPersonViewAxis[0] * THROW_SCALE * ent->GetPhysics()->GetMass() ); 367 thePlayer->StartSoundShader( declManager->FindSound( "grabber_release" ), SND_CHANNEL_WEAPON, 0, false, NULL ); 368 369 // Orient projectiles away from the player 370 if ( ent->IsType( idProjectile::Type ) ) { 371 idPlayer *player = owner.GetEntity(); 372 idAngles ang = player->firstPersonViewAxis[0].ToAngles(); 373 374 ang.pitch += 90.f; 375 ent->GetPhysics()->SetAxis( ang.ToMat3() ); 376 ent->GetPhysics()->SetAngularVelocity( vec3_origin ); 377 378 // Restore projectile contents 379 ent->GetPhysics()->SetContents( savedContents ); 380 ent->GetPhysics()->SetClipMask( savedClipmask ); 381 382 idProjectile *projectile = static_cast< idProjectile* >( ent ); 383 if ( projectile != NULL ) { 384 projectile->SetLaunchedFromGrabber( true ); 385 } 386 387 } else if ( ent->IsType( idMoveable::Type ) ) { 388 // Turn on damage for this object 389 idMoveable *obj = static_cast<idMoveable*>(ent); 390 obj->EnableDamage( true, 2.5f ); 391 obj->SetAttacker( thePlayer ); 392 393 if ( ent->IsType( idExplodingBarrel::Type ) ) { 394 idExplodingBarrel *ebarrel = static_cast<idExplodingBarrel*>(ent); 395 ebarrel->SetStability( false ); 396 } 397 398 } else if ( ent->IsType( idMoveableItem::Type ) ) { 399 ent->GetPhysics()->SetClipMask( MASK_MONSTERSOLID ); 400 } 401 } 402 403 // Remove the Force_Drag's control of the entity 404 drag.RemovePhysics( ent->GetPhysics() ); 405 } 406 407 if ( warpId != -1 ) { 408 thePlayer->playerView.FreeWarp( warpId ); 409 warpId = -1; 410 } 411 412 lastFiredTime = gameLocal.time; 413 dragEnt = NULL; 414 endTime = 0; 415 } 416 417 /* 418 ============== 419 idGrabber::Update 420 ============== 421 */ 422 int idGrabber::Update( idPlayer *player, bool hide ) { 423 trace_t trace; 424 idEntity *newEnt; 425 426 // pause before allowing refire 427 if ( lastFiredTime + FIRING_DELAY > gameLocal.time ) { 428 return 3; 429 } 430 431 // Dead players release the trigger 432 if ( hide || player->health <= 0 ) { 433 StopDrag( true ); 434 if ( hide ) { 435 lastFiredTime = gameLocal.time - FIRING_DELAY + 250; 436 } 437 return 3; 438 } 439 440 // Check if object being held has been removed (dead demon, projectile, etc.) 441 if ( endTime > gameLocal.time ) { 442 bool abort = !dragEnt.IsValid(); 443 444 if ( !abort && dragEnt.GetEntity()->IsType( idProjectile::Type ) ) { 445 idProjectile *proj = (idProjectile *)dragEnt.GetEntity(); 446 447 if ( proj->GetProjectileState() >= 3 ) { 448 abort = true; 449 } 450 } 451 if ( !abort && dragEnt.GetEntity() && dragEnt.GetEntity()->IsHidden() ) { 452 abort = true; 453 } 454 // Not in multiplayer :: Pressing "reload" lets you carefully drop an item 455 if ( !common->IsMultiplayer() && !abort && ( player->usercmd.impulseSequence != oldImpulseSequence ) && (player->usercmd.impulse == IMPULSE_13) ) { 456 abort = true; 457 } 458 459 if ( abort ) { 460 StopDrag( true ); 461 return 3; 462 } 463 } 464 465 owner = player; 466 467 // if no entity selected for dragging 468 if ( !dragEnt.GetEntity() ) { 469 idBounds bounds; 470 idVec3 end = player->firstPersonViewOrigin + player->firstPersonViewAxis[0] * dragTraceDist; 471 472 bounds.Zero(); 473 bounds.ExpandSelf( TRACE_BOUNDS_SIZE ); 474 475 gameLocal.clip.TraceBounds( trace, player->firstPersonViewOrigin, end, bounds, MASK_SHOT_RENDERMODEL|CONTENTS_PROJECTILE|CONTENTS_MOVEABLECLIP, player ); 476 // If the trace hit something 477 if ( trace.fraction < 1.0f ) { 478 newEnt = gameLocal.entities[ trace.c.entityNum ]; 479 480 // if entity is already being grabbed then bypass 481 if ( common->IsMultiplayer() && newEnt && newEnt->IsGrabbed() ) { 482 return 0; 483 } 484 485 // Check if this is a valid entity to hold 486 if ( newEnt && ( newEnt->IsType( idMoveable::Type ) || 487 newEnt->IsType( idMoveableItem::Type ) || 488 newEnt->IsType( idProjectile::Type ) || 489 newEnt->IsType( idAFEntity_Gibbable::Type ) 490 ) && 491 newEnt->noGrab == false && 492 newEnt->GetPhysics()->GetBounds().GetRadius() < MAX_PICKUP_SIZE && 493 newEnt->GetPhysics()->GetLinearVelocity().LengthSqr() < MAX_PICKUP_VELOCITY ) { 494 495 bool validAF = true; 496 497 if ( newEnt->IsType( idAFEntity_Gibbable::Type ) ) { 498 idAFEntity_Gibbable *afEnt = static_cast<idAFEntity_Gibbable*>(newEnt); 499 500 if ( grabbableAI( newEnt->spawnArgs.GetString( "classname" ) ) ) { 501 // Make sure it's also active 502 if ( !afEnt->IsActive() ) { 503 validAF = false; 504 } 505 } else if ( !afEnt->IsActiveAF() ) { 506 validAF = false; 507 } 508 } 509 510 if ( validAF && player->usercmd.buttons & BUTTON_ATTACK ) { 511 // Grab this entity and start dragging it around 512 StartDrag( newEnt, trace.c.id ); 513 } else if ( validAF ) { 514 // A holdable object is ready to be grabbed 515 return 1; 516 } 517 } 518 } 519 } 520 521 // check backwards server time in multiplayer 522 bool allow = true; 523 524 if ( common->IsMultiplayer() ) { 525 526 // if we've marched backwards 527 if ( gameLocal.slow.time < startDragTime ) { 528 allow = false; 529 } 530 } 531 532 533 // if there is an entity selected for dragging 534 if ( dragEnt.GetEntity() && allow ) { 535 idPhysics *entPhys = dragEnt.GetEntity()->GetPhysics(); 536 idVec3 goalPos; 537 538 // If the player lets go of attack, or time is up 539 if ( !( player->usercmd.buttons & BUTTON_ATTACK ) ) { 540 StopDrag( false ); 541 return 3; 542 } 543 if ( gameLocal.time > endTime ) { 544 StopDrag( true ); 545 return 3; 546 } 547 548 // Check if the player is standing on the object 549 if ( !holdingAF ) { 550 idBounds playerBounds; 551 idBounds objectBounds = entPhys->GetAbsBounds(); 552 idVec3 newPoint = player->GetPhysics()->GetOrigin(); 553 554 // create a bounds at the players feet 555 playerBounds.Clear(); 556 playerBounds.AddPoint( newPoint ); 557 newPoint.z -= 1.f; 558 playerBounds.AddPoint( newPoint ); 559 playerBounds.ExpandSelf( 8.f ); 560 561 // If it intersects the object bounds, then drop it 562 if ( playerBounds.IntersectsBounds( objectBounds ) ) { 563 StopDrag( true ); 564 return 3; 565 } 566 } 567 568 // Shake the object at the end of the hold 569 if ( g_grabberEnableShake.GetBool() && !common->IsMultiplayer() ) { 570 ApplyShake(); 571 } 572 573 // Set and evaluate drag force 574 goalPos = player->firstPersonViewOrigin + localPlayerPoint * player->firstPersonViewAxis; 575 576 drag.SetGoalPosition( goalPos ); 577 drag.Evaluate( gameLocal.time ); 578 579 // If an object is flying too fast toward the player, stop it hard 580 if ( g_grabberHardStop.GetBool() ) { 581 idPlane theWall; 582 idVec3 toPlayerVelocity, objectCenter; 583 float toPlayerSpeed; 584 585 toPlayerVelocity = -player->firstPersonViewAxis[0]; 586 toPlayerSpeed = entPhys->GetLinearVelocity() * toPlayerVelocity; 587 588 if ( toPlayerSpeed > 64.f ) { 589 objectCenter = entPhys->GetAbsBounds().GetCenter(); 590 591 theWall.SetNormal( player->firstPersonViewAxis[0] ); 592 theWall.FitThroughPoint( goalPos ); 593 594 if ( theWall.Side( objectCenter, 0.1f ) == PLANESIDE_BACK ) { 595 int i, num; 596 597 num = entPhys->GetNumClipModels(); 598 for ( i=0; i<num; i++ ) { 599 entPhys->SetLinearVelocity( vec3_origin, i ); 600 } 601 } 602 } 603 604 // Make sure the object isn't spinning too fast 605 const float MAX_ROTATION_SPEED = 12.f; 606 607 idVec3 angVel = entPhys->GetAngularVelocity(); 608 float rotationSpeed = angVel.LengthFast(); 609 610 if ( rotationSpeed > MAX_ROTATION_SPEED ) { 611 angVel.NormalizeFast(); 612 angVel *= MAX_ROTATION_SPEED; 613 entPhys->SetAngularVelocity( angVel ); 614 } 615 } 616 617 // Orient projectiles away from the player 618 if ( dragEnt.GetEntity()->IsType( idProjectile::Type ) ) { 619 idAngles ang = player->firstPersonViewAxis[0].ToAngles(); 620 ang.pitch += 90.f; 621 entPhys->SetAxis( ang.ToMat3() ); 622 } 623 624 // Some kind of effect from gun to object? 625 UpdateBeams(); 626 627 // If the object is stuck away from its intended position for more than 500ms, let it go. 628 if ( drag.GetDistanceToGoal() > DRAG_FAIL_LEN ) { 629 if ( dragFailTime < (gameLocal.slow.time - 500) ) { 630 StopDrag( true ); 631 return 3; 632 } 633 } else { 634 dragFailTime = gameLocal.slow.time; 635 } 636 637 // Currently holding an object 638 return 2; 639 } 640 641 // Not holding, nothing to hold 642 return 0; 643 } 644 645 /* 646 ====================== 647 idGrabber::UpdateBeams 648 ====================== 649 */ 650 void idGrabber::UpdateBeams() { 651 jointHandle_t muzzle_joint; 652 idVec3 muzzle_origin; 653 idMat3 muzzle_axis; 654 renderEntity_t *re; 655 656 if ( !beam ) { 657 return; 658 } 659 660 if ( dragEnt.IsValid() ) { 661 idPlayer *thePlayer = owner.GetEntity(); 662 663 if ( beamTarget ) { 664 beamTarget->SetOrigin( dragEnt.GetEntity()->GetPhysics()->GetAbsBounds().GetCenter() ); 665 } 666 667 muzzle_joint = thePlayer->weapon.GetEntity()->GetAnimator()->GetJointHandle( "particle_upper" ); 668 if ( muzzle_joint != INVALID_JOINT ) { 669 thePlayer->weapon.GetEntity()->GetJointWorldTransform( muzzle_joint, gameLocal.time, muzzle_origin, muzzle_axis ); 670 } else { 671 muzzle_origin = thePlayer->GetPhysics()->GetOrigin(); 672 } 673 674 beam->SetOrigin( muzzle_origin ); 675 re = beam->GetRenderEntity(); 676 re->origin = muzzle_origin; 677 678 beam->UpdateVisuals(); 679 beam->Present(); 680 } 681 } 682 683 /* 684 ============== 685 idGrabber::ApplyShake 686 ============== 687 */ 688 void idGrabber::ApplyShake() { 689 float u = 1 - (float)( endTime - gameLocal.time ) / ( g_grabberHoldSeconds.GetFloat() * 1000 ); 690 691 if ( u >= 0.8f ) { 692 idVec3 point, impulse; 693 float shakeForceMagnitude = 450.f; 694 float mass = dragEnt.GetEntity()->GetPhysics()->GetMass(); 695 696 shakeForceFlip = !shakeForceFlip; 697 698 // get point to rotate around 699 point = dragEnt.GetEntity()->GetPhysics()->GetOrigin(); 700 point.y += 1; 701 702 // Articulated figures get less violent shake 703 if ( holdingAF ) { 704 shakeForceMagnitude = 120.f; 705 } 706 707 // calc impulse 708 if ( shakeForceFlip ) { 709 impulse.Set( 0, 0, shakeForceMagnitude * u * mass ); 710 } 711 else { 712 impulse.Set( 0, 0, -shakeForceMagnitude * u * mass ); 713 } 714 715 dragEnt.GetEntity()->ApplyImpulse( NULL, 0, point, impulse ); 716 } 717 } 718 719 /* 720 ============== 721 idGrabber::grabbableAI 722 ============== 723 */ 724 bool idGrabber::grabbableAI( const char *aiName ) { 725 // skip "monster_" 726 aiName += 8; 727 728 if (!idStr::Cmpn( aiName, "flying_lostsoul", 15 ) || 729 !idStr::Cmpn( aiName, "demon_trite", 11 ) || 730 !idStr::Cmp( aiName, "flying_forgotten" ) || 731 !idStr::Cmp( aiName, "demon_cherub" ) || 732 !idStr::Cmp( aiName, "demon_tick" )) { 733 734 return true; 735 } 736 737 return false; 738 } 739