Grabber.cpp 20 KB

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