Grabber.cpp 20 KB

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